我为ReSharper编写了xunit.net测试运行器,我在8.0版本中遇到了一个问题 - 我看到程序集无法加载阴影复制的AppDomain。 7.1版本中相同的代码和测试项目工作正常。但我不明白为什么。
运行测试时,ReSharper会生成一个可加载我的插件的可执行文件。我使用xunit.net的API来创建一个启用了卷影复制的AppDomain。测试项目程序集将复制到卷影副本缓存中,并开始加载。它将依赖项复制到缓存中,并加载它 - 一个较旧版本的FakeItEasy,它使用Assembly.LoadFile加载当前目录中的所有程序集,这是测试项目的bin \ Debug文件夹。因此,FakeItEasy将这些程序集加载到Neither load上下文中。由于它使用的是LoadFile,它会绕过卷影副本缓存,文件直接从bin \ Debug文件夹加载。
在此之后,测试项目的依赖项无法加载,从而导致FileNotFoundException。 Fusion绑定日志显示它尝试加载它们,但它们不会被复制到卷影副本缓存中,并且无法加载。我不明白为什么。这是绑定失败:
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/temp/todonancy/TodoNancyTests/bin/Debug/Nancy.Testing.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:\temp\todonancy\TodoNancyTests\bin\Debug\Nancy.Testing.dll
LOG: Entering download cache setup phase.
LOG: Assembly Name is: Nancy.Testing, Version=0.17.1.0, Culture=neutral, PublicKeyToken=null
ERR: Setup failed with hr = 0x80070003.
ERR: Failed to complete setup of assembly (hr = 0x80070003). Probing terminated.
如果我禁用阴影副本缓存,或者使用不使用LoadFile的较新版本的FakeItEasy,则一切正常。但是,我不能责怪FakeItEasy的旧版本 - 我让用户看到与其他项目和程序集相同的错误,所有这些都通过禁用卷影副本缓存来解决。
此外,此方案适用于ReSharper 7.1 - 相同的插件代码和相同的测试项目。唯一的区别是主机应用程序,但我看不到它做了什么不同 - 例如,没有订阅其他程序集解析事件处理程序。唯一真正的区别是7.1主机使用远程处理与Visual Studio应用程序通信,而8.0使用简单的TCP套接字。
有没有人知道为什么这个版本在8.0版本中出现故障,然后用7.1运行?
编辑(2013年8月7日):
我设法通过简单的测试使其失败:
[Fact]
public void Thing()
{
Assert.NotNull(Nancy.Bootstrapper.AppDomainAssemblyTypeScanner.Assemblies);
}
使用copy of the Nancy class直接添加到项目中(使用引用的ScanMode和AssemblyExtensions类)。项目中唯一的其他内容是对xunit.dll和xunit.extensions.dll的引用。
它每次都不会失败,令人沮丧地断断续续,但我可以让它在this call to Assembly.ReflectionOnlyLoadFrom
中尝试从bin \ Debug文件夹加载测试程序集时抛出FileNotFoundException
。
以下是异常的融合日志:
Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
Running under executable C:\Program Files (x86)\JetBrains\ReSharper\v8.0\Bin\JetBrains.ReSharper.TaskRunner.CLR4.MSIL.exe
--- A detailed error log follows.
=== Pre-bind state information ===
LOG: Where-ref bind. Location = C:\Users\Matt\Code\scratch\WeirdXunitFailures\WeirdXunitFailures\bin\Debug\WeirdXunitFailures.dll
LOG: Appbase = file:///C:/Users/Matt/Code/scratch/WeirdXunitFailures/WeirdXunitFailures/bin/Debug
LOG: Initial PrivatePath = NULL
Calling assembly : (Unknown).
===
LOG: This is an inspection only bind.
LOG: No application configuration file found.
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Attempting download of new URL file:///C:/Users/Matt/Code/scratch/WeirdXunitFailures/WeirdXunitFailures/bin/Debug/WeirdXunitFailures.dll.
ERR: Failed to complete setup of assembly (hr = 0x80070003). Probing terminated.
不幸的是,这没有告诉我 - 文件:// url是有效的,而卷影副本缓存包含xunit.dll和WeirdXunitFailures.DLL(测试项目)。此外,调试器中的“模块”窗口向我显示已从阴影副本缓存位置加载WeitdXunitFailures.dll。
再一次,真正奇怪的是7.1跑步者的工作正常。
修改
事实上,我只需致电:
即可让它失败[Fact]
public void Thing()
{
Assembly.ReflectionOnlyLoadFrom(@"C:\Users\Matt\Code\scratch\WeirdXunitFailures\WeirdXunitFailures\bin\Debug\xunit.dll");
Assembly.ReflectionOnlyLoadFrom(@"C:\Users\Matt\Code\scratch\WeirdXunitFailures\WeirdXunitFailures\bin\Debug\xunit.extensions.dll");
Assembly.ReflectionOnlyLoadFrom(@"C:\Users\Matt\Code\scratch\WeirdXunitFailures\WeirdXunitFailures\bin\Debug\WeirdXunitFailures.dll");
}
这些是项目dll和两个xunit dll。它仍然是非常间歇性的,但在完全重建之后似乎最容易被复制,尽管在几次成功运行之后它仍然会发生(所以这不是重建的错误)
答案 0 :(得分:2)
呼。问题(自然地)是ReSharper 8中行为的微妙变化。
测试运行器进程API有一种方法可以告诉主ReSharper进程(即devenv.exe)用于测试运行的临时文件夹的位置 - 卷影副本缓存。这是因为测试运行程序通常无法删除缓存文件夹,因为它仍在使用中。然后,ReSharper将尝试多次尝试为您删除该文件夹,从而留出时间让该过程正常消失。
ReSharper 7.1会在测试运行结束时删除此文件夹,或者运行中止。
ReSharper 8会在您调用方法后立即删除该文件夹。 nunit测试运行器在测试运行的 end 告诉ReSharper它。我是在开始告诉它的。因此,当我的测试运行时,ReSharper会愉快地出现,并从阴影副本缓存中删除任何可能的内容,使其看起来像阴影副本缓存已正确破坏。
我想我会提交一个错误:)