2013-08-06 48 views
4

我为ReSharper编写了xunit.net测试运行器,并且我在8.0版本中遇到了一个问题 - 我看到程序集无法加载到复制了影子的AppDomain中。相同的代码和测试项目在7.1版本中正常工作。但我不明白为什么。Assemblies无法加载阴影复制AppDomain

运行测试时,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. 

如果我禁用卷影副本高速缓存,或使用FakeItEasy的较新版本不使用的LoadFile,它所有的作品。然而,我不能责怪FakeItEasy的老版本 - 我有用户看到与其他项目和程序集相同的错误,所有这些都通过禁用卷影副本缓存来解决。

此外,这种情况下使用ReSharper 7.1 - 相同的插件代码和相同的测试项目。唯一的区别是主机应用程序,但是我看不出有什么不同 - 例如,没有其他程序集解析事件处理程序订阅。唯一真正的区别是7.1主机使用远程处理与Visual Studio应用程序交谈,8.0使用简单的TCP套接字。

有没有人有任何想法,为什么这是在8.0版本失败,并与7.1运行?

EDIT(2013年7月8日):

我已经成功地使它失败,一个简单的测试:

[Fact] 
public void Thing() 
{ 
    Assert.NotNull(Nancy.Bootstrapper.AppDomainAssemblyTypeScanner.Assemblies); 
} 

使用copy of the Nancy class加入直入项目(与参考ScanModeAssemblyExtensions类)。该项目中唯一的其他内容是对xunit.dll和xunit.extensions.dll的引用。

它不会每次都失败,这是令人沮丧的间歇性,但我可以在尝试从bin \ Debug文件夹中加载测试程序集时,在this call to Assembly.ReflectionOnlyLoadFrom中引发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(中测试项目)。另外,调试器中的Modules窗口向我显示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

对不起,没意识到你已经检查fuslog;这是我的睡眠模式大脑! –

+0

只是想通过这个;据谷歌 - 80070003是目录未找到,没有找到文件 - 这是否改变了什么? –

+0

它没有帮助 - 我不知道它为什么找不到它。它具有实际文件的路径,它知道影子副本缓存目录的位置,所以我不知道它找不到。 – citizenmatt

回答

2

Phew。问题是(自然地)ReSharper 8中行为的微妙变化。

测试运行器进程API有一个方法来告诉主要ReSharper进程(即devenv.exe)用于测试运行的temp文件夹的位置 - 卷影副本缓存。这是因为测试运行程序进程通常无法删除缓存文件夹,因为它仍在使用中。然后ReSharper会尝试多次尝试删除文件夹,这样可以让流程优雅地死去。

ReSharper 7.1将在测试运行结束时删除该文件夹,或者运行被中止。

ReSharper 8在您给该方法打电话时立即删除该文件夹。 nunit测试跑步者在测试运行的结束处告知ReSharper。我是在开始告诉它的。因此,当我的测试正在运行时,ReSharper会高兴地走过来,并从影子副本缓存中删除任何可能的东西,使得它看起来像影子副本缓存被正确地破坏了。

我想我会提交一个bug :)