Skip to content

Instantly share code, notes, and snippets.

@urasandesu
Created March 29, 2016 11:56
Show Gist options
  • Save urasandesu/f3c84f4b7c7bbb4b632e to your computer and use it in GitHub Desktop.
Save urasandesu/f3c84f4b7c7bbb4b632e to your computer and use it in GitHub Desktop.
Prototype: AutoFixture with auto mocking using Moq and Prig
using Moq;
using Moq.Language;
using NUnit.Framework;
using Ploeh.AutoFixture;
using Ploeh.AutoFixture.Kernel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Prig;
using System.Linq;
using System.Linq.Expressions;
using System.Prig;
using System.Reflection;
using Urasandesu.Prig.Framework;
namespace ClassLibrary1
{
public static class ULProcessMixin
{
const int ErrorCancelled = 1223;
public static bool RestartCurrentProcess()
{
return RestartCurrentProcessWith(null);
}
public static bool RestartCurrentProcessWith(Action<ProcessStartInfo> additionalSetup)
{
var curProc = Process.GetCurrentProcess();
var startInfo = curProc.StartInfo;
startInfo.UseShellExecute = true;
startInfo.WorkingDirectory = Environment.CurrentDirectory;
startInfo.FileName = curProc.MainModule.FileName;
var commandLineArgs = Environment.GetCommandLineArgs().Skip(1).ToArray();
if (commandLineArgs.Any())
startInfo.Arguments = "\"" + string.Join("\" \"", commandLineArgs.Select(_ => _.Replace("\"", "\\\"")).ToArray()) + "\"";
if (additionalSetup != null)
additionalSetup(startInfo);
try
{
Process.Start(startInfo);
}
catch (Win32Exception ex)
{
if (ex.NativeErrorCode == ErrorCancelled)
return false;
throw;
}
curProc.CloseMainWindow();
return true;
}
}
[TestFixture]
public class ULProcessMixinTest
{
[Test]
public void RestartCurrentProcess_should_return_false_if_user_cancelled_starting_process()
{
using (new IndirectionsContext())
{
// Arrange
var ms = new MockStorage(MockBehavior.Strict);
var fixture = new Fixture().Customize(new MyCustomization(ms));
fixture.Create<PEnvironment>();
var curProcMainMod = fixture.Create<PProxyProcessModule>();
var curProc = fixture.Create<PProxyProcess>();
curProc.MainModuleGet().Body = @this => curProcMainMod;
fixture.Create<PProcess>();
ms.Customize(m => m.
Do(PProcess.StartProcessStartInfo).Setup(_ => _(It.IsAny<ProcessStartInfo>())).Throws(new Win32Exception(1223))
);
PProcess.GetCurrentProcess().Body = () => curProc;
// Act
var result = ULProcessMixin.RestartCurrentProcess();
// Assert
Assert.IsFalse(result);
}
}
}
public class MockStorage : MockRepository
{
Dictionary<Delegate, Mock> m_storage = new Dictionary<Delegate, Mock>();
public MockStorage(MockBehavior defaultBehavior)
: base(defaultBehavior)
{ }
public void Assign(Delegate dlgt, Mock m)
{
m_storage[dlgt] = m;
}
public void Assign<T>(Func<TypedBehaviorPreparable<T>> func, Mock<T> m) where T : class
{
Assign((Delegate)func, (Mock)m);
}
public MockStorage Customize(Action<MockStorage> exp)
{
exp(this);
return this;
}
public Mock<T> Do<T>(Func<TypedBehaviorPreparable<T>> func) where T : class
{
return (Mock<T>)m_storage[func];
}
}
public class MyCustomization : ICustomization
{
readonly MockStorage m_ms;
public MyCustomization(MockStorage ms)
{
m_ms = ms;
}
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new IntPtrInitializer(new PrigTypeMocker(m_ms)));
}
}
public class IntPtrInitializer : ISpecimenBuilder
{
private ISpecimenBuilder m_builder;
public IntPtrInitializer(ISpecimenBuilder builder)
{
m_builder = builder;
}
public object Create(object request, ISpecimenContext context)
{
if (context == null)
throw new ArgumentNullException("context");
var result = m_builder.Create(request, context);
if (!(result is NoSpecimen))
return result;
var requestedType = request as Type;
if (requestedType == null)
return new NoSpecimen();
{
if (requestedType == typeof(IntPtr))
return IntPtr.Zero;
}
return new NoSpecimen();
}
}
public class PrigTypeMocker : ISpecimenBuilder
{
readonly MockStorage m_ms;
public PrigTypeMocker(MockStorage ms)
{
m_ms = ms;
}
public object Create(object request, ISpecimenContext context)
{
if (context == null)
throw new ArgumentNullException("context");
var requestedType = request as Type;
if (requestedType == null)
return new NoSpecimen();
{
var result = CreatePrigType(requestedType, context);
if (!(result is NoSpecimen))
return result;
}
{
var result = CreateInstancePrigType(requestedType, context);
if (!(result is NoSpecimen))
return result;
}
return new NoSpecimen();
}
const BindingFlags PublicStatic = BindingFlags.Public | BindingFlags.Static;
object CreatePrigType(Type type, ISpecimenContext context)
{
var methods = type.GetMethods(PublicStatic).Where(_ => typeof(IBehaviorPreparable).IsAssignableFrom(_.ReturnType)).ToArray();
if (methods.Length == 0)
return new NoSpecimen();
foreach (var method in methods)
SetAutoBody(null, method, context);
return null;
}
const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;
object CreateInstancePrigType(Type type, ISpecimenContext context)
{
type = ReplaceInstancePrigType(type);
var methods = type.GetMethods(PublicInstance).Where(_ => typeof(IBehaviorPreparable).IsAssignableFrom(_.ReturnType)).ToArray();
if (methods.Length == 0)
return new NoSpecimen();
var obj = Activator.CreateInstance(type);
foreach (var method in methods)
SetAutoBody(obj, method, context);
return obj;
}
Type ReplaceInstancePrigType(Type type)
{
var instPrigTypeName = string.Format("{0}.Prig.PProxy{1}", type.Namespace, type.Name);
return GetInstancePrigType(instPrigTypeName) ?? type;
}
static Dictionary<string, Type> ms_instPrigTypes;
static Dictionary<string, Type> InstancePrigTypes
{
get
{
if (ms_instPrigTypes == null)
{
var repository = new IndirectionAssemblyRepository();
var instPrigTypes = repository.FindAll().SelectMany(_ => _.GetTypes()).Where(_ => _.Name.StartsWith("PProxy")).ToDictionary(_ => _.FullName);
ms_instPrigTypes = new Dictionary<string, Type>(instPrigTypes);
}
return ms_instPrigTypes;
}
}
Type GetInstancePrigType(string fullName)
{
var result = default(Type);
InstancePrigTypes.TryGetValue(fullName, out result);
return result;
}
void SetAutoBody(object obj, MethodInfo method, ISpecimenContext context)
{
var preparable = method.Invoke(obj, null);
var preparableType = preparable.GetType();
var bodyProp = preparableType.GetProperty("Body");
var m = GetAutoMock(bodyProp.PropertyType, context);
bodyProp.SetValue(preparable, m.Object, null);
var funcType = typeof(Func<>);
var funcTypeInst = funcType.MakeGenericType(method.ReturnType);
m_ms.Assign(Delegate.CreateDelegate(funcTypeInst, obj, method), m);
}
Mock GetAutoMock(Type dlgtType, ISpecimenContext context)
{
Debug.Assert(dlgtType.IsSubclassOf(typeof(Delegate)));
var invokeMethod = dlgtType.GetMethod("Invoke");
var createMethod = m_ms.GetType().GetMethod("Create", Type.EmptyTypes);
var createMethodInst = createMethod.MakeGenericMethod(dlgtType);
var m = (Mock)createMethodInst.Invoke(m_ms, null);
DoSetupForAutoMock(m, invokeMethod, context);
return m;
}
void DoSetupForAutoMock(Mock m, MethodInfo invokeMethod, ISpecimenContext context)
{
var mockType = m.GetType();
var setupMethod = GetSetupMethod(mockType, invokeMethod);
var expression = GetExpression(setupMethod.GetParameters()[0].ParameterType, invokeMethod);
var setup = setupMethod.Invoke(m, new object[] { expression });
DoReturnsForAutoMock(setup, context);
}
MethodInfo GetSetupMethod(Type mockType, MethodInfo invokeMethod)
{
var setupMethods = mockType.GetMethods(PublicInstance).Where(_ => _.Name == "Setup");
if (invokeMethod.ReturnType == typeof(void))
return GetActionSetupMethod(setupMethods);
else
return GetFuncSetupMethod(setupMethods, invokeMethod.ReturnType);
}
MethodInfo GetActionSetupMethod(IEnumerable<MethodInfo> setupMethods)
{
return setupMethods.First(_ => _.GetParameters().ContainsInGenericArguments(typeof(Action<>)));
}
MethodInfo GetFuncSetupMethod(IEnumerable<MethodInfo> setupMethods, Type resultType)
{
return setupMethods.First(_ => _.GetParameters().ContainsInGenericArguments(typeof(Func<,>))).MakeGenericMethod(resultType);
}
Expression GetExpression(Type setupParamType, MethodInfo invokeMethod)
{
var lambdaMethod = typeof(Expression).GetMethods(PublicStatic).
Where(_ => _.Name == "Lambda").
Where(_ => _.IsGenericMethod).
Where(_ => _.GetParameters().Length == 2).
Single(_ => _.GetParameters()[1].ParameterType.IsArray);
var lambdaType = setupParamType.GetGenericArguments()[0];
var dlgtType = lambdaType.GetGenericArguments()[0];
var lambdaMethodInst = lambdaMethod.MakeGenericMethod(lambdaType);
var paramExp = Expression.Parameter(dlgtType, "_");
var anyAccedptableParamExps = GetAnyAccedptableParameterExpressions(invokeMethod);
var bodyExp = Expression.Invoke(paramExp, anyAccedptableParamExps);
var paramExps = new[] { paramExp };
return (Expression)lambdaMethodInst.Invoke(null, new object[] { bodyExp, paramExps });
}
class ValueHolder<T>
{
public T Value;
}
IEnumerable<Expression> GetAnyAccedptableParameterExpressions(MethodInfo invokeMethod)
{
return invokeMethod.GetParameters().Select(ToAnyAccedptableParameterExpression);
}
Expression ToAnyAccedptableParameterExpression(ParameterInfo paramInfo)
{
var paramType = paramInfo.ParameterType;
if (paramType.IsByRef)
return ToAnyAccedptableRefParameterExpression(paramType);
else
return ToAnyAccedptableParameterExpression(paramType);
}
Expression ToAnyAccedptableRefParameterExpression(Type type)
{
var valHolderType = typeof(ValueHolder<>).MakeGenericType(type);
var valHolder = Activator.CreateInstance(valHolderType);
var valField = valHolderType.GetField("Value");
return Expression.Field(Expression.Constant(valHolder), valField);
}
Expression ToAnyAccedptableParameterExpression(Type type)
{
var isAnyMethod = typeof(It).GetMethod("IsAny");
var isAnyMethodInst = isAnyMethod.MakeGenericMethod(type);
return Expression.Call(null, isAnyMethodInst);
}
void DoReturnsForAutoMock(object setup, ISpecimenContext context)
{
var setupType = setup.GetType();
if (!setupType.GetInterfaces().Where(_ => _.IsGenericType).Any(_ => _.GetGenericTypeDefinition() == typeof(IReturns<,>)))
return;
var returnsMethod = setupType.GetMethods(PublicInstance).Single(ReturnsImmediateValue);
var result = context.Resolve(setupType.GetGenericArguments()[1]);
if (result is NoSpecimen)
return;
var resultType = result.GetType();
var paramType = returnsMethod.GetParameters()[0].ParameterType;
if (!paramType.IsAssignableFrom(resultType))
{
var opImplicitMethod = resultType.GetMethods(PublicStatic).
Where(_ => _.Name == "op_Implicit").
SingleOrDefault(_ => _.ReturnType == paramType);
if (opImplicitMethod == null)
throw new InvalidOperationException(string.Format("{0} doesn't have implicit conversion to {1}.", resultType, paramType));
result = opImplicitMethod.Invoke(null, new object[] { result });
}
returnsMethod.Invoke(setup, new object[] { result });
}
bool ReturnsImmediateValue(MethodInfo method)
{
if (method.Name != "Returns")
return false;
return !method.GetParameters().Any(_ => typeof(Delegate).IsAssignableFrom(_.ParameterType));
}
}
static class MyEnumerable
{
public static bool ContainsInGenericArguments(this IEnumerable<ParameterInfo> paramInfos, Type typeDefinition)
{
return paramInfos.Any(_ => _.ContainsInGenericArguments(typeDefinition));
}
public static bool ContainsInGenericArguments(this ParameterInfo paramInfo, Type typeDefinition)
{
return paramInfo.ParameterType.ContainsInGenericArguments(typeDefinition);
}
public static bool ContainsInGenericArguments(this Type t, Type typeDefinition)
{
if (typeDefinition.IsGenericType && !typeDefinition.IsGenericTypeDefinition)
throw new ArgumentException("The parameter must be a type definition.", "typeDefinition");
return t.EnumerateGenericArgument().Contains(typeDefinition);
}
public static IEnumerable<Type> EnumerateGenericArgument(this Type t)
{
if (t.IsGenericType)
yield return t.GetGenericTypeDefinition();
else
yield return t;
foreach (var genericArg in t.GetGenericArguments())
foreach (var chidGenericArg in genericArg.EnumerateGenericArgument())
yield return chidGenericArg;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment