コード例 #1
0
        private MethodReference GetGenericMethod(GenericInstanceMethod genericInstanceMethod)
        {
            string value = Method.GetLabel(this, genericInstanceMethod);

            if (this.methodsDictionary.ContainsKey(value))
            {
                return(this.methodsDictionary [value].MethodDefinition);
            }

            foreach (Method method in this.methods)
            {
                if (!method.IsGenericType)
                {
                    continue;
                }

                if (method.Name != genericInstanceMethod.Name)
                {
                    continue;
                }

                if (method.MethodDefinition.Parameters.Count != genericInstanceMethod.Parameters.Count)
                {
                    continue;
                }

                bool ok = true;

                for (int i = 0; i < method.MethodDefinition.Parameters.Count; i++)
                {
                    if (method.MethodDefinition.Parameters [i].ParameterType != genericInstanceMethod.Parameters [i].ParameterType)
                    {
                        if (!(method.MethodDefinition.Parameters [i].ParameterType is GenericParameter) ||
                            !(genericInstanceMethod.Parameters [i].ParameterType is GenericParameter))
                        {
                            ok = false;
                            break;
                        }
                    }
                }

                if (ok)
                {
                    this.methodsDictionary [value] = method;

                    return(method.MethodDefinition);
                }
            }

            throw new EngineException(string.Format("Method '{0}' not found.", genericInstanceMethod.ToString()));
        }
コード例 #2
0
        /// <summary>
        /// Executes.
        /// </summary>
        ///
        /// <param name="job"> The parameter. </param>
        ///
        /// <returns>
        /// A Dictionary&lt;string,bool&gt;
        /// </returns>
        public Boolean Execute(IJob job)
        {
            currentJob = job;

            host.AddResult(Severity.Debug, true, $"{GetType().Name}.Execute('{job.Parm}')");

            host.AddResult(Severity.Info, true, $"[Examining and Executing Unit Test in {Path.GetFileNameWithoutExtension(job.Parm)} Project]");

            Utils.TestTypes tt = Utils.TestTypes.Unknown;

            if (File.Exists(job.Parm))
            {
                host.AddResult(Severity.Info, true, $"'{job.Parm}' exists.");

                //! Only needed for Microsoft Test.
                List <String> Unpatched = new List <String>();

                //! Only needed for Microsoft Test.
                AssemblyDefinition assertAsmDef = AssemblyDefinition.ReadAssembly(GetType().Assembly.Location);

                AssemblyDefinition testAsmDef = AssemblyDefinition.ReadAssembly(job.Parm);

                //! ------------------------------------------------------------
                //! Enumerate and process all classes with a TestClassAttribute.
                //! ------------------------------------------------------------
                //
                foreach (TypeDefinition td in testAsmDef.MainModule.Types)
                {
                    //! ------------------------------------------------------------
                    //! Microsoft Test.
                    //
                    //! Needs a fixup for references to the Assert class that resides in a pair of non-distributable assemblies.
                    //! Here the references are changed to a Assert class defined in RQAT.
                    //
                    // [TestClass]
                    // [TestInitialize]
                    // [TestMethod]
                    // [TestCleanup]
                    //! ------------------------------------------------------------
                    //
                    if (td.CustomAttributes.Any(q => q.AttributeType.Name.Equals("TestClassAttribute")) ||
                        td.Methods.Any(q => q.CustomAttributes.Any(p => p.AttributeType.Name.Equals("TestMethodAttribute"))))
                    {
                        tt = Utils.TestTypes.Microsoft;

                        //! ------------------------------------------------------------
                        //! Weave Start...
                        //! ------------------------------------------------------------
                        //
                        //! https://www.simplethread.com/static-method-interception-in-net-with-c-and-monocecil/
                        //
                        //! Patch all methods where the Assert class is used (so these end up in this code).
                        //
                        foreach (MethodDefinition foundMethod in td.Methods.OfType <MethodDefinition>())
                        {
                            //! https://stackoverflow.com/questions/25077830/replace-references-to-a-type-namespace-using-mono-cecil
                            //! https://blog.elishalom.com/2012/02/04/monitoring-execution-using-mono-cecil/
                            //! https://gist.github.com/7H3LaughingMan/311662c07b8bf8f8d2c6
                            //
                            ILProcessor ilProcessor = foundMethod.Body.GetILProcessor();

                            Debug.WriteLine($"\r\n-----------------");
                            Debug.WriteLine($"Weaving: {foundMethod.Name}");

                            //! Examine and patch Call statements to the Visual Studio UnitTesting.Assert class.
                            //
                            for (Int32 pc = 0; pc < ilProcessor.Body.Instructions.Count; pc++)
                            {
                                Instruction il = ilProcessor.Body.Instructions[pc];

                                if (il.OpCode.Code == Code.Call)
                                {
                                    Debug.WriteLine($"Examening: {il.OpCode.Code} {il.Operand}");

                                    //! Methods with generics need special handling.
                                    //
                                    if (il.Operand is GenericInstanceMethod && ((GenericInstanceMethod)il.Operand).DeclaringType.FullName.Equals("Microsoft.VisualStudio.TestTools.UnitTesting.Assert"))
                                    {
                                        Debug.WriteLine(il.Operand.ToString());
                                        GenericInstanceMethod gim = ((GenericInstanceMethod)il.Operand);

                                        Debug.WriteLine($"Method: {gim.Name}");
                                        Debug.WriteLine($"Signature: {gim}");

                                        //! Check if there is a matching non-generic method.
                                        //
                                        MethodDefinition md = FindReplacementMethod(assertAsmDef, gim.ToString());

                                        if (md != null)
                                        {
                                            //! Replace generic method call by a non generic one.
                                            //
                                            Debug.WriteLine($"Replacement Definition: {md}");

                                            //! Find MethodInfo from MethodDefinition by matching MetadataToken value.
                                            //
                                            MethodInfo mi = typeof(Assert).GetMethods().First(p => p.MetadataToken == md.MetadataToken.ToInt32());
                                            Debug.WriteLine($"Replacement Info: {mi}");

                                            MethodReference mri = testAsmDef.MainModule.ImportReference(mi);
                                            Debug.WriteLine($"Replacement Import: {mri}");

                                            //! Patch.
                                            //
                                            ilProcessor.Replace(foundMethod.Body.Instructions[pc], ilProcessor.Create(OpCodes.Call, mri));
                                        }
                                        else
                                        {
                                            //! Check if there is a matching generic method.
                                            //
                                            md = FindGenericReplacementMethod(assertAsmDef, gim.ToString());

                                            if (md != null)
                                            {
                                                //! Replace generic method call by a generic one.
                                                //
                                                Debug.WriteLine($"Replacement Definition: {md}");

                                                //! Find MethodInfo from MethodDefinition by matching MetadataToken value.
                                                //
                                                MethodInfo mi = typeof(Assert).GetMethods().First(p => p.MetadataToken == md.MetadataToken.ToInt32());
                                                Debug.WriteLine($"Replacement Info: {mi}");

                                                //! Adjust Generic method to match the one to be replaced.
                                                //
                                                GenericInstanceMethod mri = new GenericInstanceMethod(testAsmDef.MainModule.ImportReference(mi));
                                                foreach (TypeReference tr in gim.GenericArguments)
                                                {
                                                    mri.GenericArguments.Add(tr);
                                                }

                                                Debug.WriteLine($"Replacement Import: {mri}");

                                                //! Patch.
                                                //
                                                ilProcessor.Replace(foundMethod.Body.Instructions[pc], ilProcessor.Create(OpCodes.Call, mri));
                                            }
                                            else
                                            {
                                                if (!Unpatched.Contains(foundMethod.Name))
                                                {
                                                    Unpatched.Add(foundMethod.Name);
                                                }
                                                Debug.WriteLine($"Failed to weave: {gim}");
                                            }
                                        }
                                    }
                                    else if (il.Operand is MethodReference && ((MethodReference)il.Operand).DeclaringType.FullName.Equals("Microsoft.VisualStudio.TestTools.UnitTesting.Assert"))
                                    {
                                        Debug.WriteLine(il.Operand.ToString());
                                        MethodReference mr = ((MethodReference)il.Operand);

                                        Debug.WriteLine($"Method: {mr.Name}");
                                        Debug.WriteLine($"Signature: {mr}");

                                        //! Check if there is a matching non-generic method.
                                        //
                                        MethodDefinition md = FindReplacementMethod(assertAsmDef, mr.ToString());

                                        if (md != null)
                                        {
                                            //! Replace non-generic method call by a non-generic one.
                                            //
                                            Debug.WriteLine($"Replacement Definition: {md}");

                                            //! Find MethodInfo from MethodDefinition by matching MetadataToken value.
                                            //
                                            MethodInfo mi = typeof(Assert).GetMethods().First(p => p.MetadataToken == md.MetadataToken.ToInt32());
                                            Debug.WriteLine($"Replacement Info: {mi}");

                                            MethodReference mri = testAsmDef.MainModule.ImportReference(mi);
                                            Debug.WriteLine($"Replacement Import: {mri}");

                                            //! Patch.
                                            //
                                            ilProcessor.Replace(foundMethod.Body.Instructions[pc], ilProcessor.Create(OpCodes.Call, mri));
                                        }
                                        else
                                        {
                                            if (!Unpatched.Contains(foundMethod.Name))
                                            {
                                                Unpatched.Add(foundMethod.Name);
                                            }
                                            Debug.WriteLine($"Failed to weave: {mr}");
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else if (td.CustomAttributes.Any(q => q.AttributeType.Name.Equals("TestFixtureAttribute")) ||
                             td.Methods.Any(q => q.CustomAttributes.Any(p => p.AttributeType.Name.Equals("TestAttribute"))))
                    {
                        tt = Utils.TestTypes.Nunit;
                    }
                }

                //! ------------------------------------------------------------
                //! Finish up and save the resulting Weaved Assembly.
                //! ------------------------------------------------------------
                //
                //https://stackoverflow.com/questions/13499384/is-it-possible-to-debug-assemblies-compiled-with-mono-xbuild-with-visual-studi

                switch (tt)
                {
                case Utils.TestTypes.Microsoft:
                {
                    CustomAttribute debuggableAttribute = new CustomAttribute(testAsmDef.MainModule.ImportReference(
                                                                                  typeof(DebuggableAttribute).GetConstructor(new[] { typeof(bool), typeof(bool) })));

                    debuggableAttribute.ConstructorArguments.Add(new CustomAttributeArgument(
                                                                     testAsmDef.MainModule.ImportReference(typeof(bool)), true));

                    debuggableAttribute.ConstructorArguments.Add(new CustomAttributeArgument(
                                                                     testAsmDef.MainModule.ImportReference(typeof(bool)), true));

                    // !Only one DebuggableAttribute is allowed, so replace an existing one.

                    Int32 ndx = testAsmDef.CustomAttributes.ToList().FindIndex(p => p.AttributeType.Name.Equals("DebuggableAttribute"));
                    if (ndx != -1)
                    {
                        testAsmDef.CustomAttributes.RemoveAt(ndx);
                    }
                    testAsmDef.CustomAttributes.Add(debuggableAttribute);

                    //!Remove references to Visual Studio Assembly.
                    //!Removing leads to failing to enumerate the test methods further along.
                    //ndx = assemblyDef.MainModule.AssemblyReferences.ToList().FindIndex(p => p.Name.Equals("Microsoft.VisualStudio.QualityTools.UnitTestFramework"));
                    //if (ndx != -1)
                    //{
                    //    assemblyDef.MainModule.AssemblyReferences.RemoveAt(ndx);
                    //}

                    //! Create a method call:
                    // See https://stackoverflow.com/questions/35948733/mono-cecil-method-and-instruction-insertion
                    //
                    testAsmDef.Write(Path.Combine(Path.GetDirectoryName(job.Parm), "weaved.dll"),
                                     new WriterParameters()
                        {
                            SymbolWriterProvider = new PdbWriterProvider(),
                            WriteSymbols         = true
                        });

                    //! ------------------------------------------------------------
                    //! Weave End
                    //! ------------------------------------------------------------
                }
                break;

                case Utils.TestTypes.Nunit:
                {
                    File.Copy(job.Parm, Path.Combine(Path.GetDirectoryName(job.Parm), "weaved.dll"));
                }
                break;

                default:
                    return(false);
                }

                String dll = Path.Combine(Path.GetDirectoryName(job.Parm), "weaved.dll");
                String pdb = Path.ChangeExtension(dll, ".pdb");

                //AppDomain currentDomain = AppDomain.CurrentDomain;
                //currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
                //tests.TestAssembly = Assembly.Load(File.ReadAllBytes(dll), File.ReadAllBytes(pdb));

                testAsm.TestAssembly = Assembly.LoadFrom(dll);

                host.AddResult(Severity.Info, true, $"{tt} test class found in {Path.GetFileNameWithoutExtension(job.Parm)} assembly.", 1);

                //! ------------------------------------------------------------
                //! Enumerate all patched tests and build a list.
                //! ------------------------------------------------------------
                //
                foreach (Type type in testAsm.TestAssembly.GetTypes())
                {
                    switch (tt)
                    {
                    case Utils.TestTypes.Microsoft:
                    {
                        //! Microsoft Test needs a TestClass Attribute.
                        //
                        foreach (Attribute att1 in type.GetCustomAttributes(false))
                        {
                            if (att1.GetType().Name.Equals("TestClassAttribute"))
                            {
                                testAsm.Add(new TestClass());

                                testAsm.Last().TestType = type;

                                host.AddResult(Severity.Info, true, $"{att1.GetType().Name} found on {testAsm.Last().TestType.Name} class.", 1);

                                foreach (MethodInfo method in type.GetMethods())
                                {
                                    foreach (Attribute att2 in method.GetCustomAttributes(false))
                                    {
                                        String attname = att2.GetType().Name;

                                        //! See https://stackoverflow.com/questions/933613/how-do-i-use-assert-to-verify-that-an-exception-has-been-thrown
                                        // [ExpectedException(typeof(ArgumentException), "A userId of null was inappropriately allowed.")]

                                        if (attname.Equals("TestInitializeAttribute"))
                                        {
                                            testAsm.Last().Initialize = method;
                                            host.AddResult(Severity.Info, true, $"Initialize found in {testAsm.Last().TestType.Name}: {method.Name}() is {att2.GetType().Name}", 2);
                                        }
                                        else if (attname.Equals("TestCleanupAttribute"))
                                        {
                                            testAsm.Last().Cleanup = method;
                                            host.AddResult(Severity.Info, true, $"Cleanup found in {testAsm.Last().TestType.Name}: {method.Name}() is {att2.GetType().Name}", 2);
                                        }
                                        else if (attname.Equals("TestMethodAttribute"))
                                        {
                                            //! Skip unpatched methods
                                            //
                                            if (!Unpatched.Contains(method.Name))
                                            {
                                                testAsm.Last().Methods.Add(method);
                                            }
                                        }
                                    }
                                }

                                host.AddResult(Severity.Info, true, $"Patched Assert Class {testAsm.Last().TestType.Name} method calls in TestMethods: {testAsm.Last().Methods.Count}", 2);

                                host.AddResult(Severity.Info, true, $"Unpatched TestMethods in {testAsm.Last().TestType.Name}: {Unpatched.Count}", 2);
                            }
                        }
                    }
                    break;

                    case Utils.TestTypes.Nunit:
                    {
                        //! Nunit Test might lack a TestFixture Attribute.
                        //
                        if (type.GetCustomAttributes(false).Any(q => q.GetType().Name.Equals("TestFixtureAttribute")) ||
                            type.GetMethods().Any(q => q.GetCustomAttributes(false).Any(p => p.GetType().Name.Equals("TestAttribute"))))
                        {
                            testAsm.Add(new TestClass());

                            testAsm.Last().TestType = type;

                            foreach (MethodInfo method in type.GetMethods())
                            {
                                foreach (Attribute att2 in method.GetCustomAttributes(false))
                                {
                                    String attname = att2.GetType().Name;

                                    //! See https://stackoverflow.com/questions/933613/how-do-i-use-assert-to-verify-that-an-exception-has-been-thrown
                                    // [ExpectedException(typeof(ArgumentException), "A userId of null was inappropriately allowed.")]

                                    if (attname.Equals("SetupAttribute"))
                                    {
                                        testAsm.Last().Initialize = method;
                                        host.AddResult(Severity.Info, true, $"Setup found in {testAsm.Last().TestType.Name}: {method.Name}() is {att2.GetType().Name}", 2);
                                    }
                                    else if (attname.Equals("TearDownAttribute"))
                                    {
                                        testAsm.Last().Cleanup = method;
                                        host.AddResult(Severity.Info, true, $"TearDown found in {testAsm.Last().TestType.Name}: {method.Name}() is {att2.GetType().Name}", 2);
                                    }
                                    else if (attname.Equals("TestAttribute"))
                                    {
                                        //! Skip unpatched methods
                                        //
                                        if (!Unpatched.Contains(method.Name))
                                        {
                                            testAsm.Last().Methods.Add(method);
                                        }
                                    }
                                }
                            }
                        }
                    }
                    break;
                    }
                }

                //! ------------------------------------------------------------
                //! Run Patched Method from (last added) Weaved Test Class.
                //! ------------------------------------------------------------
                //
                foreach (TestClass testclass in testAsm)
                {
                    foreach (MethodInfo test in testclass.Methods)
                    {
                        host.AddResult(Severity.Info, true, $"Invoking Test: {testclass.TestType.Name}.{test.Name}", 1);

                        Object cls = Activator.CreateInstance(testclass.TestType);

                        //! Initialize Test
                        //
                        testclass.Initialize?.Invoke(cls, new Object[] { });

                        //! Execute Test
                        //
                        try
                        {
                            test?.Invoke(cls, new Object[] { });
                        }
                        catch (Exception e)
                        {
                            host.AddResult(Severity.Error, false, $"{test.Name} - {e.GetType().Name} - {e.Message}.");
                        }

                        //! Cleanup Test
                        //
                        testclass.Cleanup?.Invoke(cls, new Object[] { });
                    }
                }

                testAsm.TestAssembly = null;
            }
            else
            {
                host.AddResult(Severity.Warning, false, $"Failed to Locate UnitTest Assembly.");
            }

            return(true);
        }