Merge remote-tracking branch 'origin/mockCtorIntegration'
This commit is contained in:
Коммит
6943026714
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Mono.Cecil;
|
||||
using NSubstitute.Core;
|
||||
using NSubstitute.Elevated.WeaverInternals;
|
||||
using NSubstitute.Exceptions;
|
||||
|
@ -9,6 +10,7 @@ using NSubstitute.Proxies;
|
|||
using NSubstitute.Proxies.CastleDynamicProxy;
|
||||
using NSubstitute.Proxies.DelegateProxy;
|
||||
using Unity.Core;
|
||||
using TypeAttributes = Mono.Cecil.TypeAttributes;
|
||||
|
||||
namespace NSubstitute.Elevated
|
||||
{
|
||||
|
@ -23,6 +25,15 @@ namespace NSubstitute.Elevated
|
|||
m_CallFactory = new CallFactory(substitutionContext);
|
||||
}
|
||||
|
||||
void AddMockPlaceholderToAssembly(AssemblyDefinition targetAssembly)
|
||||
{
|
||||
var mockPlaceholder = new TypeDefinition("NSubstitute.Elevated.WeaverInternals", "MockPlaceholderType", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit)
|
||||
{
|
||||
BaseType = targetAssembly.MainModule.TypeSystem.Object
|
||||
};
|
||||
targetAssembly.MainModule.Types.Add(mockPlaceholder);
|
||||
}
|
||||
|
||||
object IProxyFactory.GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, object[] constructorArguments)
|
||||
{
|
||||
// TODO:
|
||||
|
@ -58,18 +69,32 @@ namespace NSubstitute.Elevated
|
|||
if (additionalInterfaces.Any())
|
||||
throw new SubstituteException("Cannot add interfaces at runtime to patched types");
|
||||
|
||||
if (substituteConfig == SubstituteConfig.OverrideAllCalls)
|
||||
{
|
||||
// overriding all calls includes the ctor, so it makes no sense for the user to pass in ctor args
|
||||
if (constructorArguments.Any())
|
||||
throw new SubstituteException("Do not pass ctor args when substituting with elevated mocks (or did you mean to use ForPartsOf?)");
|
||||
switch (substituteConfig) {
|
||||
case SubstituteConfig.OverrideAllCalls:
|
||||
|
||||
// but we use a ctor arg to select the special empty ctor that we patched in
|
||||
constructorArguments = k_MockedCtorParams;
|
||||
// overriding all calls includes the ctor, so it makes no sense for the user to pass in ctor args
|
||||
if (constructorArguments != null && constructorArguments.Any())
|
||||
throw new SubstituteException("Do not pass ctor args when substituting with elevated mocks (or did you mean to use ForPartsOf?)");
|
||||
|
||||
// but we use a ctor arg to select the special empty ctor that we patched in
|
||||
constructorArguments = k_MockedCtorParams;
|
||||
break;
|
||||
case SubstituteConfig.CallBaseByDefault:
|
||||
var castleDynamicProxyFactory = new CastleDynamicProxyFactory();
|
||||
return castleDynamicProxyFactory.GenerateProxy(callRouter, typeToProxy, additionalInterfaces, constructorArguments);
|
||||
case null:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
// var proxyWrap = Activator.CreateInstanceFrom(patchAllDependentAssemblies[1].Path, typeToProxy.FullName, false,
|
||||
// BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance, null,
|
||||
// constructorArguments, null, null);
|
||||
// proxy = proxyWrap.Unwrap();
|
||||
|
||||
proxy = Activator.CreateInstance(typeToProxy, constructorArguments);
|
||||
GetRouterField(typeToProxy).SetValue(proxy, callRouter);
|
||||
GetRouterField(proxy.GetType()).SetValue(proxy, callRouter);
|
||||
}
|
||||
|
||||
return proxy;
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using NSubstitute.Core;
|
||||
using NSubstitute.Core.Arguments;
|
||||
using NSubstitute.Elevated.Weaver;
|
||||
using NSubstitute.Exceptions;
|
||||
using NSubstitute.Routing;
|
||||
using Unity.Core;
|
||||
|
@ -28,12 +31,14 @@ namespace NSubstitute.Elevated
|
|||
new ElevatedCallRouterFactory(), ElevatedSubstituteManager, new CallRouterResolver());
|
||||
}
|
||||
|
||||
public static IDisposable AutoHook()
|
||||
public static IDisposable AutoHook(string assemblyLocation, IEnumerable<string> assemblyPath = null)
|
||||
{
|
||||
var hookedContext = SubstitutionContext.Current;
|
||||
var thisContext = new ElevatedSubstitutionContext(hookedContext);
|
||||
SubstitutionContext.Current = thisContext;
|
||||
|
||||
var patchAllDependentAssemblies = ElevatedWeaver.PatchAllDependentAssemblies(assemblyLocation, PatchTestAssembly.Yes, assemblyPath).ToList();
|
||||
|
||||
return new DelegateDisposable(() =>
|
||||
{
|
||||
if (SubstitutionContext.Current != thisContext)
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../common.targets" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net461</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations">
|
||||
<Version>11.1.0</Version>
|
||||
|
@ -16,10 +13,9 @@
|
|||
<PackageReference Include="Mono.Cecil">
|
||||
<Version>0.10.0-beta6</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Shouldly" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Unity.Core\Unity.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using Mono.Cecil;
|
||||
using Shouldly;
|
||||
using Unity.Core;
|
||||
|
||||
namespace NSubstitute.Elevated.Weaver
|
||||
|
@ -19,9 +20,8 @@ namespace NSubstitute.Elevated.Weaver
|
|||
public static string GetPatchBackupPathFor(string path)
|
||||
=> path + k_PatchBackupExtension;
|
||||
|
||||
public static IReadOnlyCollection<PatchResult> PatchAllDependentAssemblies(
|
||||
[NotNull] string testAssemblyPath,
|
||||
PatchTestAssembly patchTestAssembly = PatchTestAssembly.No) // typically we don't want to patch the test assembly itself, only the systems under test
|
||||
public static IReadOnlyCollection<PatchResult> PatchAllDependentAssemblies([NotNull] string testAssemblyPath,
|
||||
PatchTestAssembly patchTestAssembly = PatchTestAssembly.No, IEnumerable<string> assemblyPath = null) // typically we don't want to patch the test assembly itself, only the systems under test
|
||||
{
|
||||
var testAssemblyFolder = Path.GetDirectoryName(testAssemblyPath);
|
||||
if (testAssemblyFolder.IsNullOrEmpty())
|
||||
|
@ -75,21 +75,27 @@ namespace NSubstitute.Elevated.Weaver
|
|||
patchResult = new PatchResult(assemblyToPatchPath, null, PatchState.IgnoredTestAssembly);
|
||||
else if (MockInjector.IsPatched(assemblyToPatch))
|
||||
patchResult = new PatchResult(assemblyToPatchPath, null, PatchState.AlreadyPatched);
|
||||
else
|
||||
else if (assemblyPath.Contains(assemblyToPatch.Name.Name))
|
||||
{
|
||||
mockInjector.Patch(assemblyToPatch);
|
||||
|
||||
// atomic write of file with backup
|
||||
var tmpPath = assemblyToPatchPath + ".tmp";
|
||||
var tmpPath = assemblyToPatchPath.Split(new[] {".dll"}, StringSplitOptions.None)[0] +
|
||||
".tmp";
|
||||
File.Delete(tmpPath);
|
||||
assemblyToPatch.Write(tmpPath);//$$$$, new WriterParameters { WriteSymbols = true }); // getting exception, haven't looked into it yet
|
||||
assemblyToPatch.Write(tmpPath); //$$$$, new WriterParameters { WriteSymbols = true }); // getting exception, haven't looked into it yet
|
||||
assemblyToPatch.Dispose();
|
||||
var originalPath = GetPatchBackupPathFor(assemblyToPatchPath);
|
||||
File.Replace(tmpPath, assemblyToPatchPath, originalPath);
|
||||
Verify(assemblyToPatchPath);
|
||||
// $$$ TODO: move pdb file too
|
||||
|
||||
patchResult = new PatchResult(assemblyToPatchPath, originalPath, PatchState.Patched);
|
||||
}
|
||||
else
|
||||
{ // TODO: Nope
|
||||
patchResult = default(PatchResult);
|
||||
}
|
||||
|
||||
patchResults.Add(assemblyToPatchPath, patchResult);
|
||||
}
|
||||
|
@ -98,6 +104,83 @@ namespace NSubstitute.Elevated.Weaver
|
|||
return patchResults.Values;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fix
|
||||
const string peVerifyLocation = @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\x64\PEVerify.exe";
|
||||
static void Verify(string assemblyName)
|
||||
{
|
||||
var p = new Process
|
||||
{
|
||||
StartInfo =
|
||||
{
|
||||
Arguments = $"/nologo \"{assemblyName}\"",
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
FileName = peVerifyLocation,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true
|
||||
}
|
||||
};
|
||||
|
||||
var error = "";
|
||||
var output = "";
|
||||
|
||||
p.OutputDataReceived += (_, e) => output += $"{e.Data}\n";
|
||||
p.ErrorDataReceived += (_, e) => error += $"{e.Data}\n";
|
||||
|
||||
p.Start();
|
||||
p.BeginOutputReadLine();
|
||||
p.BeginErrorReadLine();
|
||||
|
||||
p.WaitForExit();
|
||||
|
||||
Console.WriteLine(assemblyName);
|
||||
p.ExitCode.ShouldBe(0, () => $"{error}\n{output}");
|
||||
}
|
||||
|
||||
public static IReadOnlyCollection<PatchResult> PatchAssemblies(
|
||||
[NotNull] List<string> testAssemblyPaths)
|
||||
{
|
||||
var testAssemblyPath = testAssemblyPaths[0];
|
||||
var testAssemblyFolder = Path.GetDirectoryName(testAssemblyPath);
|
||||
if (testAssemblyFolder.IsNullOrEmpty())
|
||||
throw new Exception("Unable to find folder for test assembly");
|
||||
testAssemblyFolder = Path.GetFullPath(testAssemblyFolder);
|
||||
|
||||
// scope
|
||||
{
|
||||
var thisAssemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
if (thisAssemblyFolder.IsNullOrEmpty())
|
||||
throw new Exception("Can only patch assemblies on disk");
|
||||
thisAssemblyFolder = Path.GetFullPath(thisAssemblyFolder);
|
||||
|
||||
// keep things really simple, at least for now
|
||||
if (string.Compare(testAssemblyFolder, thisAssemblyFolder, StringComparison.OrdinalIgnoreCase) != 0)
|
||||
throw new Exception("All assemblies must be in the same folder");
|
||||
}
|
||||
|
||||
var nsubElevatedPath = Path.Combine(testAssemblyFolder, "NSubstitute.Elevated.dll");
|
||||
using (var nsubElevatedAssembly = AssemblyDefinition.ReadAssembly(nsubElevatedPath))
|
||||
{
|
||||
var mockInjector = new MockInjector(nsubElevatedAssembly);
|
||||
|
||||
foreach (var assemblyPath in testAssemblyPaths)
|
||||
{
|
||||
var assemblyDefinition = AssemblyDefinition.ReadAssembly(assemblyPath);
|
||||
mockInjector.Patch(assemblyDefinition);
|
||||
// atomic write of file with backup
|
||||
var tmpPath = assemblyPath.Split(new[] { ".dll" }, StringSplitOptions.None)[0] + ".tmp";
|
||||
File.Delete(tmpPath);
|
||||
assemblyDefinition.Write(tmpPath);//$$$$, new WriterParameters { WriteSymbols = true }); // getting exception, haven't looked into it yet
|
||||
assemblyDefinition.Dispose();
|
||||
/*var originalPath = GetPatchBackupPathFor(assemblyToPatchPath);
|
||||
File.Replace(tmpPath, assemblyToPatchPath, originalPath);*/
|
||||
// $$$ TODO: move pdb file too
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum PatchState
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Policy;
|
||||
|
@ -52,7 +53,7 @@ namespace NSubstitute.Elevated.Weaver
|
|||
.ToList(); // copy to a list in case patch work we do would invalidate the enumerator
|
||||
|
||||
foreach (var type in typesToProcess)
|
||||
Patch(type);
|
||||
Patch(type, assembly);
|
||||
|
||||
// add an attr to mark the assembly as patched
|
||||
|
||||
|
@ -87,7 +88,7 @@ namespace NSubstitute.Elevated.Weaver
|
|||
return IsPatched(assembly);
|
||||
}
|
||||
|
||||
void Patch(TypeDefinition type)
|
||||
void Patch(TypeDefinition type, AssemblyDefinition assembly)
|
||||
{
|
||||
if (type.IsInterface)
|
||||
return;
|
||||
|
@ -103,12 +104,12 @@ namespace NSubstitute.Elevated.Weaver
|
|||
try
|
||||
{
|
||||
foreach (var method in type.Methods)
|
||||
Patch(method);
|
||||
Patch(method, assembly);
|
||||
|
||||
void AddField(string fieldName, FieldAttributes fieldAttributes)
|
||||
{
|
||||
type.Fields.Add(new FieldDefinition(fieldName,
|
||||
FieldAttributes.Private | FieldAttributes.NotSerialized | fieldAttributes,
|
||||
FieldAttributes.Private | fieldAttributes,
|
||||
type.Module.TypeSystem.Object));
|
||||
}
|
||||
|
||||
|
@ -175,12 +176,72 @@ namespace NSubstitute.Elevated.Weaver
|
|||
|
||||
type.Methods.Add(ctor);
|
||||
}
|
||||
void Patch(MethodDefinition method)
|
||||
|
||||
void Patch(MethodDefinition method, AssemblyDefinition assembly)
|
||||
{
|
||||
if (method.IsCompilerControlled || method.IsConstructor || method.IsAbstract)
|
||||
return;
|
||||
|
||||
// $$$ DOWIT
|
||||
method.Body.InitLocals = true;
|
||||
var originalType = assembly.MainModule.ImportReference(Type.GetType("System.Type"));
|
||||
var getTypeFromHandle = assembly.MainModule.ImportReference(originalType.Resolve().Methods.Single(m => m.Name == "GetTypeFromHandle"));
|
||||
//var getTypeFromHandle = assembly.MainModule.Import(new MethodReference("GetTypeFromHandle", type, type) { Parameters = { new ParameterDefinition(runtimeTypeHandle) } });
|
||||
var emptyTypes = assembly.MainModule.ImportReference(originalType.Resolve().Fields.Single(f => f.Name == "EmptyTypes"));
|
||||
|
||||
var v1 = new VariableDefinition(assembly.MainModule.TypeSystem.Object);
|
||||
method.Body.Variables.Add(v1);
|
||||
var bodyInstructions = new List<Instruction>(method.Body.Instructions);
|
||||
method.Body.Instructions.Clear();
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldtoken, method.DeclaringType));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, getTypeFromHandle));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldtoken, method.ReturnType));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, getTypeFromHandle));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloca_S, v1));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldsfld, emptyTypes));
|
||||
|
||||
// TODO: Parameter specific
|
||||
if (method.Parameters.Count > 0)
|
||||
{
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_1));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Newarr, assembly.MainModule.TypeSystem.Object));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Dup));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_0));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1)); // arg
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Box, method.Parameters[0].ParameterType));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Stelem_Ref));
|
||||
}
|
||||
else
|
||||
{
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_0));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Newarr, assembly.MainModule.TypeSystem.Object));
|
||||
}
|
||||
|
||||
// End of parameter include
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(m_PatchedAssemblyBridgeTryMock)));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Nop));
|
||||
|
||||
var count = method.Body.Instructions.Count;
|
||||
|
||||
var hasReturnValue = method.ReturnType != assembly.MainModule.TypeSystem.Void;
|
||||
if (hasReturnValue)
|
||||
{
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc_S, v1));
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Unbox_Any, method.ReturnType));
|
||||
}
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
|
||||
|
||||
foreach (var instruction in bodyInstructions)
|
||||
{
|
||||
method.Body.Instructions.Add(instruction);
|
||||
}
|
||||
method.Body.Instructions[count - 1] = Instruction.Create(OpCodes.Brfalse_S, method.Body.Instructions[count + (hasReturnValue ? 3 : 1)]);
|
||||
|
||||
/*method.Body.Instructions.Clear();
|
||||
|
||||
|
||||
ConvertReturnTypeToDefault(method.ReturnType, method.Body.Instructions);
|
||||
method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using SystemUnderTest;
|
||||
using NSubstitute.Elevated.WeaverInternals;
|
||||
using NSubstitute.Exceptions;
|
||||
using NUnit.Framework;
|
||||
using Shouldly;
|
||||
|
@ -15,8 +14,7 @@ namespace NSubstitute.Elevated.Tests
|
|||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
m_Dispose = ElevatedSubstitutionContext.AutoHook();
|
||||
PatchedAssemblyBridgeX.TryMock = PatchedAssemblyBridge.TryMock;
|
||||
m_Dispose = ElevatedSubstitutionContext.AutoHook(typeof(BasicTests).Assembly.Location, new [] {"SystemUnderTest"});
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
|
@ -60,6 +58,16 @@ namespace NSubstitute.Elevated.Tests
|
|||
sub.Value.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClassWithDefaultCtor_MockedMethod_ReturnsOverriddenValue()
|
||||
{
|
||||
var sub = Substitute.For<ClassWithDefaultCtor>();
|
||||
|
||||
sub.Value.Returns(24);
|
||||
|
||||
sub.Value.ShouldBe(24);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClassWithNoDefaultCtor_TypeDoesNotChange()
|
||||
{
|
||||
|
@ -92,7 +100,7 @@ namespace NSubstitute.Elevated.Tests
|
|||
[Test]
|
||||
public void ClassWithCtorParams_WhenMocked_ShouldThrow()
|
||||
{
|
||||
Should.Throw<SubstituteException>(() => Substitute.For<ClassWithNoDefaultCtor>(null));
|
||||
// Should.Throw<MissingMethodException>(() => Substitute.For<ClassWithNoDefaultCtor>());
|
||||
Should.Throw<SubstituteException>(() => Substitute.For<ClassWithNoDefaultCtor>("test"));
|
||||
Should.Throw<SubstituteException>(() => Substitute.For<ClassWithNoDefaultCtor>(null, null));
|
||||
}
|
||||
|
@ -104,16 +112,16 @@ namespace NSubstitute.Elevated.Tests
|
|||
// if unpatched, then mocking will run standard nsubstitute behavior (i.e. proxying done via dynamicproxy generator, which inherits proxy type from the real type).
|
||||
|
||||
var subEmpty = Substitute.For<EmptyClass>();
|
||||
subEmpty.GetType().BaseType.ShouldBe(typeof(EmptyClass));
|
||||
subEmpty.GetType().ShouldBe(typeof(EmptyClass));
|
||||
|
||||
var subNoCtor1 = Substitute.For<ClassWithNoDefaultCtorNoMethods>(null);
|
||||
subNoCtor1.GetType().BaseType.ShouldBe(typeof(ClassWithNoDefaultCtorNoMethods));
|
||||
subNoCtor1.GetType().ShouldBe(typeof(ClassWithNoDefaultCtorNoMethods));
|
||||
|
||||
var subNoCtor2 = Substitute.For<ClassWithNoDefaultCtorNoMethods>("test");
|
||||
subNoCtor2.GetType().BaseType.ShouldBe(typeof(ClassWithNoDefaultCtorNoMethods));
|
||||
// var subNoCtor2 = Substitute.For<ClassWithNoDefaultCtorNoMethods>("test"); TODO: This will cause an exception as ForPartsOf should be used. Maybe do that here instead?
|
||||
// subNoCtor2.GetType().ShouldBe(typeof(ClassWithNoDefaultCtorNoMethods));
|
||||
|
||||
var subNoCtor3 = Substitute.For<ClassWithNoDefaultCtorNoMethods>(null, null);
|
||||
subNoCtor3.GetType().BaseType.ShouldBe(typeof(ClassWithNoDefaultCtorNoMethods));
|
||||
// var subNoCtor3 = Substitute.For<ClassWithNoDefaultCtorNoMethods>(null, null);
|
||||
// subNoCtor3.GetType().ShouldBe(typeof(ClassWithNoDefaultCtorNoMethods));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -131,7 +139,7 @@ namespace NSubstitute.Elevated.Tests
|
|||
var sub = Substitute.For<ClassWithDependency>();
|
||||
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
sub.GetType().GetMethod("Dummy").ReturnType.FullName.ShouldBe("mycodedep.DependentType");
|
||||
sub.GetType().GetMethod("Dummy").ReturnType.FullName.ShouldBe("DependentAssembly.DependentType");
|
||||
|
||||
// $$$ TODO: test that the type is itself patched (look for __mockthingy)
|
||||
}
|
||||
|
@ -163,7 +171,7 @@ namespace NSubstitute.Elevated.Tests
|
|||
sub.ReturnMethod(3).ShouldBe(8);
|
||||
sub.Modified.ShouldBe(8);
|
||||
|
||||
sub.ReturnMethod(Arg.Is(4)).Returns(10);
|
||||
sub.ReturnMethod(4).Returns(10);
|
||||
sub.ReturnMethod(4).ShouldBe(10);
|
||||
sub.Modified.ShouldBe(8);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Build.Utilities;
|
||||
using Shouldly;
|
||||
|
||||
namespace NSubstitute.Elevated.Tests.Utilities
|
||||
{
|
||||
|
@ -24,29 +24,37 @@ namespace NSubstitute.Elevated.Tests.Utilities
|
|||
|
||||
public static class PeVerify
|
||||
{
|
||||
public static void Verify(string assemblyToTestPath)
|
||||
// TODO: Fix
|
||||
const string k_PeVerifyLocation = @"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\x64\PEVerify.exe";
|
||||
public static void Verify(string assemblyName)
|
||||
{
|
||||
var peVerifyPath = ToolLocationHelper.GetPathToDotNetFrameworkSdkFile("peverify.exe", TargetDotNetFrameworkVersion.Version45);
|
||||
|
||||
var psi = new ProcessStartInfo(peVerifyPath, $"/nologo \"{assemblyToTestPath}\"")
|
||||
var p = new Process
|
||||
{
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false
|
||||
StartInfo =
|
||||
{
|
||||
Arguments = $"/nologo \"{assemblyName}\"",
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
FileName = k_PeVerifyLocation,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true
|
||||
}
|
||||
};
|
||||
|
||||
using (var process = Process.Start(psi))
|
||||
{
|
||||
if (process != null)
|
||||
{
|
||||
process.WaitForExit();
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
var stdout = process.StandardOutput.ReadToEnd(); // peverify apparently doesn't write to stderr..
|
||||
throw new PeVerifyException($"Failure during PEVerify of {assemblyToTestPath}", process.ExitCode, stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
var error = "";
|
||||
var output = "";
|
||||
|
||||
p.OutputDataReceived += (_, e) => output += $"{e.Data}\n";
|
||||
p.ErrorDataReceived += (_, e) => error += $"{e.Data}\n";
|
||||
|
||||
p.Start();
|
||||
p.BeginOutputReadLine();
|
||||
p.BeginErrorReadLine();
|
||||
|
||||
p.WaitForExit();
|
||||
|
||||
Console.WriteLine(assemblyName);
|
||||
p.ExitCode.ShouldBe(0, () => $"{error}\n{output}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
@ -47,7 +48,7 @@ namespace NSubstitute.Elevated.Tests.Utilities
|
|||
|
||||
PeVerify.Verify(m_TestAssemblyPath); // pre-check..sometimes we can compile code that doesn't verify
|
||||
|
||||
var results = ElevatedWeaver.PatchAllDependentAssemblies(m_TestAssemblyPath, PatchTestAssembly.Yes);
|
||||
var results = ElevatedWeaver.PatchAllDependentAssemblies(m_TestAssemblyPath, PatchTestAssembly.Yes, new [] { new FileInfo(m_TestAssemblyPath).Name.Replace(".dll", string.Empty) });
|
||||
results.Count.ShouldBe(2);
|
||||
results.ShouldContain(new PatchResult("mscorlib", null, PatchState.IgnoredOutsideAllowedPaths));
|
||||
results.ShouldContain(new PatchResult(m_TestAssemblyPath, ElevatedWeaver.GetPatchBackupPathFor(m_TestAssemblyPath), PatchState.Patched));
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using NSubstitute.Elevated.WeaverInternals;
|
||||
//using NSubstitute.Elevated.WeaverInternals;
|
||||
|
||||
//using NSubstitute.Elevated.WeaverInternals;
|
||||
|
||||
#if TEST_ICALLS
|
||||
using System.Runtime.CompilerServices;
|
||||
|
@ -33,7 +35,7 @@ namespace SystemUnderTest
|
|||
|
||||
public int Value = 234;
|
||||
|
||||
void Dummy() {}
|
||||
public void Dummy() {}
|
||||
}
|
||||
|
||||
public class ClassWithNoDefaultCtor
|
||||
|
@ -87,36 +89,32 @@ namespace SystemUnderTest
|
|||
|
||||
public class SimpleClass
|
||||
{
|
||||
static object __mock__staticData;
|
||||
object __mock__data;
|
||||
|
||||
public int Modified;
|
||||
|
||||
// actual
|
||||
//public void VoidMethod() => ++Modified;
|
||||
//public int ReturnMethod() => ++Modified;
|
||||
|
||||
// hack until patching works
|
||||
//actual
|
||||
public void VoidMethod() => ++Modified;
|
||||
public int ReturnMethod() => ++Modified;
|
||||
|
||||
//hack until patching works
|
||||
public void VoidMethod(int count)
|
||||
{
|
||||
if (PatchedAssemblyBridgeX.TryMock(typeof(SimpleClass), this, typeof(void), out var _, Type.EmptyTypes, new object[] { count }))
|
||||
return;
|
||||
/*if (PatchedAssemblyBridgeX.TryMock(typeof(SimpleClass), this, typeof(void), out var _, Type.EmptyTypes, new object[] { count }))
|
||||
return;*/
|
||||
|
||||
Modified += count;
|
||||
}
|
||||
|
||||
public int ReturnMethod(int count)
|
||||
{
|
||||
if (PatchedAssemblyBridgeX.TryMock(typeof(SimpleClass), this, typeof(int), out var returnValue, Type.EmptyTypes, new object[] { count }))
|
||||
return (int)returnValue;
|
||||
/*if (PatchedAssemblyBridgeX.TryMock(typeof(SimpleClass), this, typeof(int), out var returnValue, Type.EmptyTypes, new object[] { count }))
|
||||
return (int)returnValue;#1#*/
|
||||
|
||||
return Modified += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace NSubstitute.Elevated.WeaverInternals
|
||||
/*namespace NSubstitute.Elevated.WeaverInternals
|
||||
{
|
||||
public static class PatchedAssemblyBridgeX
|
||||
{
|
||||
|
@ -124,4 +122,4 @@ namespace NSubstitute.Elevated.WeaverInternals
|
|||
|
||||
public static TryMockProc TryMock;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
|
Загрузка…
Ссылка в новой задаче