我为 ReSharper 编写了 xunit.net 测试运行程序,并且在 8.0 版本中遇到了问题 - 我看到程序集无法加载到影子复制的 AppDomain 中。相同的代码和测试项目在 7.1 版本中运行良好。不过,我不明白为什么。
运行测试时,ReSharper 会生成一个可执行文件来加载我的插件。我使用 xunit.net 的 API 创建启用了卷影复制的 AppDomain。测试项目程序集被复制到卷影副本缓存中,并开始加载。它将依赖项复制到缓存中,并加载它 - FakeItEasy 的旧版本,它使用 Assembly.LoadFile 加载当前目录中的所有程序集,即测试项目的 bin\Debug 文件夹。因此,FakeItEasy 将这些程序集加载到既不加载上下文中。由于它使用 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 年 7 月 8 日):
我设法通过一个简单的测试使其失败:
[Fact]
public void Thing()
{
Assert.NotNull(Nancy.Bootstrapper.AppDomainAssemblyTypeScanner.Assemblies);
}
使用直接添加到项目中的 Nancy 类的副本(使用引用的ScanMode和AssemblyExtensions类)。项目中唯一的其他内容是对 xunit.dll 和 xunit.extensions.dll 的引用。
它不会每次都失败,它令人沮丧地断断续续,但我可以让它FileNotFoundException
在这个调用中Assembly.ReflectionOnlyLoadFrom
抛出一个,同时尝试从 bin\Debug 文件夹加载测试程序集。
这是来自异常的融合日志:
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.
不幸的是,这并没有告诉我——file:// 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。它仍然很断断续续,但似乎在完全重建后最容易重现,尽管在几次成功运行后仍然会发生(所以不是重建有问题)