GenericMockFactory.cs :  » Development » StyleCop » Microsoft » VsSDK » UnitTestLibrary » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Development » StyleCop 
StyleCop » Microsoft » VsSDK » UnitTestLibrary » GenericMockFactory.cs
/***************************************************************************

Copyright (c) Microsoft Corporation. All rights reserved.
This code is licensed under the Visual Studio SDK license terms.
THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

***************************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Reflection.Emit;

namespace Microsoft.VsSDK.UnitTestLibrary{
    /// <summary>
    /// This class creates Mock object classes implementing specific interfaces.
    /// </summary>
    public class GenericMockFactory
    {
        private static AssemblyBuilder dynamicAssembly;
        private static ModuleBuilder dynamicModule;
        private static Dictionary<String, Type> cachedTypes;

        private Type generatedType;
        private string className;
        private Type[] interfaces;

        // Static constructor used to initilize the static variables.
        [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
        static GenericMockFactory()
        {
            AppDomain domain = System.Threading.Thread.GetDomain();
            AssemblyName assemblyName = new AssemblyName("MockFactoryAssembly");
            dynamicAssembly = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
            dynamicModule = dynamicAssembly.DefineDynamicModule("MockFactoryModule", "MockFactoryAssembly.dll", true);
            cachedTypes = new Dictionary<String, Type>();
        }

        static public void Save()
        {
            dynamicAssembly.Save("MockFactoryAssembly.dll");
        }

        /// <summary>
        /// Creates an instance of the factory for a specific class.
        /// </summary>
        /// <param name="className">The name of the class that this factory will create.</param>
        /// <param name="interfaces">The interfaces implemented by the generated class.</param>
        public GenericMockFactory(string className, Type[] interfaces)
        {
            // Initialize the instance's variables.
            this.className = className;
            this.interfaces = interfaces;
        }

        /// <summary>
        /// This is similar to GetGeneratedType, but instead of returning the Type
        /// it returns an instance of that type
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public BaseMock GetInstance()
        {
            return (BaseMock)Activator.CreateInstance(GetGeneratedType());
        }

        /// <summary>
        /// Create a new class type dynamicly.
        /// The type will be derived from BaseMock.
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public Type GetGeneratedType()
        {
            // Check if we have created this type before;
            if (null == generatedType)
                generatedType = CreateType(className, interfaces);

            return generatedType;
        }

        private static void AddTypeToList(Type t, List<Type> typeList)
        {
            if (typeList.Contains(t))
            {
                return;
            }
            typeList.Add(t);
            foreach (Type i in t.GetInterfaces())
            {
                AddTypeToList(i, typeList);
            }
        }

        /// <summary>
        /// Creates a new class with a given name derived from BaseMock and implementing 
        /// a specific set of interfaces.
        /// </summary>
        /// <param name="className">The name of the class to create.</param>
        /// <param name="interfaces">The interfaces implemented by the generated class.</param>
        /// <returns></returns>
        [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        public static Type CreateType(string className, Type[] interfaces)
        {
            if (cachedTypes.ContainsKey(GetHashForInterfaces(interfaces)))
            {
                return cachedTypes[GetHashForInterfaces(interfaces)];
            }

            // Get some information about the base type
            Type baseType = typeof(BaseMock);

            // Methods and fields about the callbacks
            FieldInfo callbackFieldInfo = baseType.GetField("callbacks", BindingFlags.Instance | BindingFlags.NonPublic);
            MethodInfo callbacksContainsKey = callbackFieldInfo.FieldType.GetMethod("ContainsKey");
            MethodInfo callbackGetItem = callbackFieldInfo.FieldType.GetMethod("get_Item");
            ConstructorInfo callbackArgsCtr = typeof(CallbackArgs).GetConstructor(new Type[] { typeof(object[]) });
            MethodInfo callbackInvoke = typeof(EventHandler<CallbackArgs>).GetMethod("Invoke");
            MethodInfo callbackArgsGetParam = typeof(CallbackArgs).GetMethod("GetParameter");
            MethodInfo callbackArgsGetRetVal = typeof(CallbackArgs).GetMethod("get_ReturnValue");

            // Utility functions exposed by the base class
            MethodInfo incrementCallCount = baseType.GetMethod("IncrementFunctionCalls", BindingFlags.Instance | BindingFlags.NonPublic);

            // Methods and fields about the return values.
            FieldInfo retValuesFieldInfo = baseType.GetField("returnValues", BindingFlags.Instance | BindingFlags.NonPublic);
            MethodInfo retValuesContainsKey = retValuesFieldInfo.FieldType.GetMethod("ContainsKey");
            MethodInfo retValuesGetItem = retValuesFieldInfo.FieldType.GetMethod("get_Item");
            MethodInfo arrayGetLength = typeof(object[]).GetMethod("get_Length");

            // Build the list of the interfaces to implement.
            // Note that if we have a COM interface I2 that derives from the COM interface I1
            // we have to implement also I1, even if I2 implements all the methods of the base
            // interface.
            List<Type> typeList = new List<Type>();
            foreach (Type t in interfaces)
            {
                AddTypeToList(t, typeList);
            }

            // Create the type builder for this type; the new type will be a class derived
            // from the BaseType class.
            TypeBuilder newType = dynamicModule.DefineType(className, TypeAttributes.Class | TypeAttributes.Public, baseType);

            // Set the ComVisible attribute for the type. This will enable unit testing of interop scenarios where the
            // mock objetcs are supposed to implement IDispatch.
            ConstructorInfo comVisibleAttributeCtr = typeof(System.Runtime.InteropServices.ComVisibleAttribute).GetConstructor(new Type[] { typeof(bool) });
            CustomAttributeBuilder customAttribute = new CustomAttributeBuilder(comVisibleAttributeCtr, new object[] { true });
            newType.SetCustomAttribute(customAttribute);

            foreach (Type t in typeList)
            {
                // Add the informations about the implemented interface. We don't check if the type
                // is an interface because the check is performed by AddInterfaceImplementation.
                newType.AddInterfaceImplementation(t);

                // Now we have to create the interface's methods. 
                foreach (MethodInfo method in t.GetMethods())
                {
                    // Build the full name of this method; this name will also be used as index
                    // in the Disctionary of callbaks.
                    string fullMethodName = string.Format(System.Globalization.CultureInfo.InvariantCulture,
                                                          "{0}.{1}", t.FullName, method.Name);
                    Trace.WriteLine("Building method: " + fullMethodName);

                    // Get the information about parameters.
                    ParameterInfo[] parameters = method.GetParameters();
                    // Now build the array with the type of the parameters.
                    Type[] paramTypes = new Type[parameters.Length];
                    for (int i = 0; i < parameters.Length; ++i)
                    {
                        paramTypes[i] = parameters[i].ParameterType;
                    }

                    // Create a the method build with the informations about the parameter's type
                    // and the return value.
                    MethodBuilder methodBuilder = newType.DefineMethod(
                                                    fullMethodName,
                                                    MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.NewSlot | MethodAttributes.HideBySig,
                                                    method.ReturnType,
                                                    paramTypes);
                    methodBuilder.SetImplementationFlags(MethodImplAttributes.IL | MethodImplAttributes.Managed);

                    // Add all the attributes to the parameters (e.g. 'out', 'in', ...).
                    foreach (ParameterInfo param in parameters)
                    {
                        methodBuilder.DefineParameter(param.Position + 1, param.Attributes, param.Name);
                    }


                    // Now we have to add the code for the method.
                    // The implementation of the method is simple: it will check if there is a
                    // callback defined for it; if there is no callback then it will return,
                    // otherwise it will call the event handler and return.
                    ILGenerator methodCode = methodBuilder.GetILGenerator();

                    // Define the local variables.
                    LocalBuilder paramValues = methodCode.DeclareLocal(typeof(object[]));
                    LocalBuilder callbackArgs = methodCode.DeclareLocal(typeof(CallbackArgs));
                    LocalBuilder retValuesArray = methodCode.DeclareLocal(typeof(object[]));

                    // Define the labels needed inside the method.
                    Label callbackNotDefined = methodCode.DefineLabel();
                    Label retValuesNotDefined = methodCode.DefineLabel();

                    // The first operation is to increment the counter for the calls to this function
                    methodCode.Emit(OpCodes.Ldarg_0);                   // this
                    methodCode.Emit(OpCodes.Ldstr, fullMethodName);     // Method name
                    methodCode.Emit(OpCodes.Call, incrementCallCount);  // Call the function
                    // IncrementFunctionCalls is a void function, so now the stack is clear.

                    // Check if there is a callback defined for this method
                    methodCode.Emit(OpCodes.Ldarg_0);           // "this"
                    methodCode.Emit(OpCodes.Ldfld, callbackFieldInfo);  // get the dictionary of callbacks
                    methodCode.Emit(OpCodes.Ldstr, fullMethodName);   // name of this method.
                    methodCode.Emit(OpCodes.Callvirt, callbacksContainsKey);

                    // Check the return code
                    methodCode.Emit(OpCodes.Ldc_I4_0);
                    methodCode.Emit(OpCodes.Beq, callbackNotDefined);

                    // Here the callback function is defined

                    // Create an array of objects with the values of the parameters.
                    methodCode.Emit(OpCodes.Ldc_I4_S, parameters.Length);
                    methodCode.Emit(OpCodes.Newarr, typeof(object));
                    methodCode.Emit(OpCodes.Stloc, paramValues);

                    // Set the values of the parameters.
                    foreach (ParameterInfo paramInfo in parameters)
                    {
                        methodCode.Emit(OpCodes.Ldloc, paramValues);
                        methodCode.Emit(OpCodes.Ldc_I4, paramInfo.Position);

                        methodCode.Emit(OpCodes.Ldarg, paramInfo.Position + 1);   // Parameter 0 is "this"
                        // If this parameter is a reference we have to get the referenced value.
                        if (paramInfo.ParameterType.IsByRef)
                        {
                            Type elemType = paramInfo.ParameterType.GetElementType();
                            if (elemType.IsValueType)
                            {
                                if (elemType.IsPrimitive)
                                {
                                    // Here we assume that element types are I4, but actually we should
                                    // consider I1,...,I8
                                    methodCode.Emit(OpCodes.Ldind_I4);
                                }
                                else
                                {
                                    methodCode.Emit(OpCodes.Ldobj, elemType);
                                }
                                methodCode.Emit(OpCodes.Box, elemType);
                            }
                            else
                            {
                                methodCode.Emit(OpCodes.Ldind_Ref);
                            }
                        }
                        if (paramInfo.ParameterType.IsValueType)
                        {
                            methodCode.Emit(OpCodes.Box, paramInfo.ParameterType);
                        }

                        methodCode.Emit(OpCodes.Stelem_Ref);
                    }

                    // Create the CallbackArgs variable using the array of parameter's values
                    methodCode.Emit(OpCodes.Ldloc, paramValues);
                    methodCode.Emit(OpCodes.Newobj, callbackArgsCtr);
                    methodCode.Emit(OpCodes.Stloc, callbackArgs);

                    // Get the callback function.
                    methodCode.Emit(OpCodes.Ldarg_0);
                    methodCode.Emit(OpCodes.Ldfld, callbackFieldInfo);
                    methodCode.Emit(OpCodes.Ldstr, fullMethodName);
                    methodCode.Emit(OpCodes.Callvirt, callbackGetItem);

                    // Keep the callback function on the stack as "this" pointer and
                    // add the other parameters.
                    methodCode.Emit(OpCodes.Ldarg_0);         // Use this object as sender.
                    methodCode.Emit(OpCodes.Ldloc, callbackArgs);   // The arguments to the callback.
                    methodCode.Emit(OpCodes.Callvirt, callbackInvoke);

                    // After calling the callback we should set the values of the out parameters.
                    foreach (ParameterInfo param in parameters)
                    {
                        if (!param.ParameterType.IsByRef)
                        {
                            continue;
                        }
                        methodCode.Emit(OpCodes.Ldarg, param.Position + 1);
                        methodCode.Emit(OpCodes.Ldloc, callbackArgs);
                        methodCode.Emit(OpCodes.Ldc_I4, param.Position);
                        methodCode.Emit(OpCodes.Callvirt, callbackArgsGetParam);
                        if (param.ParameterType.GetElementType().IsValueType)
                        {
                            methodCode.Emit(OpCodes.Unbox_Any, param.ParameterType.GetElementType());
                            if (param.ParameterType.GetElementType().IsPrimitive)
                            {
                                // Right now we assume this is I4, but actually we should check
                                // for I1,...,I8
                                methodCode.Emit(OpCodes.Stind_I4);
                            }
                            else
                            {
                                methodCode.Emit(OpCodes.Stobj, param.ParameterType.GetElementType());
                            }
                        }
                        else
                        {
                            methodCode.Emit(OpCodes.Stind_Ref);
                        }
                    }

                    // The last step is to set the return value
                    if (method.ReturnType != typeof(void))
                    {
                        // Get the return code from the callback arguments
                        methodCode.Emit(OpCodes.Ldloc, callbackArgs);
                        methodCode.Emit(OpCodes.Callvirt, callbackArgsGetRetVal);
                        // If the return code of this method is object, no conversion is needed,
                        // otherwise we have to cast this value to the right type.
                        if (method.ReturnType != typeof(object))
                        {
                            // Here we have to switch on three case: a value type that needs unboxing,
                            // strings and any other type.
                            if (method.ReturnType.IsValueType)
                            {
                                // Unbox the type; notice that this will throw if the object is null
                                // or there is no conversion to the requested type.
                                methodCode.Emit(OpCodes.Unbox_Any, method.ReturnType);
                            }
                            else
                            {
                                // In all the other cases call the cast function.
                                methodCode.Emit(OpCodes.Castclass, method.ReturnType);
                            }
                        }
                    }
                    // Now the return value (if needed) is on the stack, so we can exit.
                    methodCode.Emit(OpCodes.Ret);

                    // ============================================================================
                    // Here we add the code for the case the callback is not defined.
                    methodCode.MarkLabel(callbackNotDefined);

                    // Check if there is an array of return values for this method.
                    // Check if there is a callback defined for this method
                    methodCode.Emit(OpCodes.Ldarg_0);           // "this"
                    methodCode.Emit(OpCodes.Ldfld, retValuesFieldInfo); // get the dictionary of return values
                    methodCode.Emit(OpCodes.Ldstr, fullMethodName);   // name of this method.
                    methodCode.Emit(OpCodes.Callvirt, retValuesContainsKey);

                    // Check the return code
                    methodCode.Emit(OpCodes.Ldc_I4_0);
                    methodCode.Emit(OpCodes.Beq, retValuesNotDefined);

                    // There is an entry for this method in the dictionary of return values.
                    // Get the array of values.
                    methodCode.Emit(OpCodes.Ldarg_0);
                    methodCode.Emit(OpCodes.Ldfld, retValuesFieldInfo);
                    methodCode.Emit(OpCodes.Ldstr, fullMethodName);
                    methodCode.Emit(OpCodes.Callvirt, retValuesGetItem);
                    methodCode.Emit(OpCodes.Stloc, retValuesArray);

                    // The array of return values contains in the first element the return value,
                    // then a value to assign to each parameter. The first step is to set the value
                    // of the ByRef parameters.
                    int offset = (method.ReturnType == typeof(void)) ? 0 : 1;
                    foreach (ParameterInfo paramInfo in parameters)
                    {
                        // If this parameter is not ByRef we can skip it.
                        if (!paramInfo.ParameterType.IsByRef)
                            continue;

                        // Put the parameter on the stack (add 1 because 0 is "this")
                        methodCode.Emit(OpCodes.Ldarg, paramInfo.Position + 1);

                        // Get the element in the array of values.
                        // Notice that we want an exception here if the element is not present.
                        methodCode.Emit(OpCodes.Ldloc, retValuesArray);
                        methodCode.Emit(OpCodes.Ldc_I4, paramInfo.Position + offset);
                        methodCode.Emit(OpCodes.Ldelem_Ref);

                        // Now the element is the first on the stack, so we can convert it to
                        // the type of the parameter.
                        Type elemType = paramInfo.ParameterType.GetElementType();
                        if (elemType.IsValueType)
                        {
                            methodCode.Emit(OpCodes.Unbox_Any, elemType);
                            if (elemType.IsPrimitive)
                            {
                                // As always we assume this is I4, but we should check for I1,...,I8
                                methodCode.Emit(OpCodes.Stind_I4);
                            }
                            else
                            {
                                methodCode.Emit(OpCodes.Stobj, elemType);
                            }
                        }
                        else
                        {
                            methodCode.Emit(OpCodes.Stind_Ref);
                        }
                    }

                    // Now that the parameters are done we have to set the return value.
                    if (method.ReturnType != typeof(void))
                    {
                        // Get the first element of the array
                        methodCode.Emit(OpCodes.Ldloc, retValuesArray);
                        methodCode.Emit(OpCodes.Ldc_I4_0);
                        methodCode.Emit(OpCodes.Ldelem_Ref);

                        // If the return type is object we don't need any conversion.
                        if (method.ReturnType != typeof(object))
                        {
                            if (method.ReturnType.IsValueType)
                            {
                                methodCode.Emit(OpCodes.Unbox_Any, method.ReturnType);
                            }
                            else
                            {
                                methodCode.Emit(OpCodes.Castclass, method.ReturnType);
                            }
                        }
                    }
                    methodCode.Emit(OpCodes.Ret);

                    // ============================================================================
                    // Here there is no return code defined and no callback, so we return a defaul
                    methodCode.MarkLabel(retValuesNotDefined);

                    // Simply return setting a return code if the method does not return void.
                    if (method.ReturnType != typeof(void))
                    {
                        // The first check should be if the return type is object because in this
                        // case it is assignable from any other type and this can cause unexpected
                        // results.
                        if (method.ReturnType == typeof(object))
                        {
                            methodCode.Emit(OpCodes.Ldnull);
                        }
                        else if (method.ReturnType.IsAssignableFrom(typeof(int)))
                        {
                            methodCode.Emit(OpCodes.Ldc_I4_0);
                        }
                        else if (method.ReturnType.IsValueType)
                        {
                            // Return a not initialized instance of the value type.
                            LocalBuilder localRetVal = methodCode.DeclareLocal(method.ReturnType);
                            methodCode.Emit(OpCodes.Ldloc, localRetVal);
                        }
                        else
                        {
                            methodCode.Emit(OpCodes.Ldnull);
                        }
                    }
                    methodCode.Emit(OpCodes.Ret);

                    // Now that we have the method implementation, let's declare it as the
                    // override of the interface's method.
                    newType.DefineMethodOverride(methodBuilder, method);
                }

            }
            cachedTypes.Add(GetHashForInterfaces(interfaces), newType);
            return newType.CreateType();
        }

        private static String GetHashForInterfaces(Type[] interfaces)
        {
            SortedList<String, Guid> sortedInterfaces = new SortedList<String, Guid>();
            foreach (Type type in interfaces)
            {
                sortedInterfaces.Add(type.GUID.ToString(), type.GUID);
            }

            String hash = "";
            foreach (String guid in sortedInterfaces.Keys)
            {
                hash = hash + guid;
            }
            return hash;
        }

    }


}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.