Mono で NUnit と TPL を組み合わせると、奇妙なクラッシュが発生します。コードは次のとおりです。
using System;
using NUnit.Engine;
namespace BadMono
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Mono is bad.");
DryRun();
}
private static void DryRun()
{
// Code based on:
// http://stackoverflow.com/questions/33826500/how-to-run-a-nunit-test
string path = @"<snip>/bin/Debug/TestChannelA.dll";
TestPackage package = new TestPackage(path);
package.AddSetting("WorkDirectory", Environment.CurrentDirectory);
// prepare the engine
ITestEngine engine = TestEngineActivator.CreateInstance();
// ERROR happens on this line
var _filterService = engine.Services.GetService<ITestFilterService>();
}
}
}
次の NuGet パッケージを追加しました。
NUnit.Engine v3.2.0
NUnit v3.2.0
Microsoft.Tpl.Dataflow v4.5.24
したがって、このコードを Windows 10 (.NET 4.6.1) で実行すると、プログラムは「Mono is bad.」と出力します。クラッシュすることなく。
Visual Studio 2015 でコンパイルし、モノを使用して Linux でテストしています。これを Linux (OpenSUSE 13.2) で実行すると、次のスタック トレースが表示されます。
BadMono/bin/Debug $ mono BadMono.exe
Mono is bad.
Unhandled Exception:
Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
at Mono.Cecil.BaseAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name, Mono.Cecil.ReaderParameters parameters) <0x41f5df10 + 0x00333> in <filename unknown>:0
at Mono.Cecil.BaseAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name) <0x41f5dec0 + 0x0003e> in <filename unknown>:0
at Mono.Cecil.DefaultAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name) <0x41f5d890 + 0x00073> in <filename unknown>:0
at Mono.Cecil.MetadataResolver.Resolve (Mono.Cecil.TypeReference type) <0x41f5d530 + 0x000f3> in <filename unknown>:0
at Mono.Cecil.ModuleDefinition.Resolve (Mono.Cecil.TypeReference type) <0x41f5d2a0 + 0x0002a> in <filename unknown>:0
at Mono.Cecil.TypeReference.Resolve () <0x41f5d240 + 0x00033> in <filename unknown>:0
at NUnit.Engine.Internal.CecilExtensions.GetAttribute (Mono.Cecil.TypeDefinition type, System.String fullName) <0x41f5a300 + 0x000b6> in <filename unknown>:0
at NUnit.Engine.Services.ExtensionService.FindExtensionsInAssembly (System.String assemblyName, Boolean specifiedInConfig) <0x41f46f80 + 0x0032f> in <filename unknown>:0
at NUnit.Engine.Services.ExtensionService.FindExtensionsInDirectory (System.IO.DirectoryInfo startDir) <0x41f46b10 + 0x00177> in <filename unknown>:0
at NUnit.Engine.Services.ExtensionService.StartService () <0x41f45640 + 0x000ab> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
at Mono.Cecil.BaseAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name, Mono.Cecil.ReaderParameters parameters) <0x41f5df10 + 0x00333> in <filename unknown>:0
at Mono.Cecil.BaseAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name) <0x41f5dec0 + 0x0003e> in <filename unknown>:0
at Mono.Cecil.DefaultAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name) <0x41f5d890 + 0x00073> in <filename unknown>:0
at Mono.Cecil.MetadataResolver.Resolve (Mono.Cecil.TypeReference type) <0x41f5d530 + 0x000f3> in <filename unknown>:0
at Mono.Cecil.ModuleDefinition.Resolve (Mono.Cecil.TypeReference type) <0x41f5d2a0 + 0x0002a> in <filename unknown>:0
at Mono.Cecil.TypeReference.Resolve () <0x41f5d240 + 0x00033> in <filename unknown>:0
at NUnit.Engine.Internal.CecilExtensions.GetAttribute (Mono.Cecil.TypeDefinition type, System.String fullName) <0x41f5a300 + 0x000b6> in <filename unknown>:0
at NUnit.Engine.Services.ExtensionService.FindExtensionsInAssembly (System.String assemblyName, Boolean specifiedInConfig) <0x41f46f80 + 0x0032f> in <filename unknown>:0
at NUnit.Engine.Services.ExtensionService.FindExtensionsInDirectory (System.IO.DirectoryInfo startDir) <0x41f46b10 + 0x00177> in <filename unknown>:0
at NUnit.Engine.Services.ExtensionService.StartService () <0x41f45640 + 0x000ab> in <filename unknown>:0
私が使用しているMonoのバージョンは次のとおりです。
$ mono --version
Mono JIT compiler version 4.4.0 (Stable 4.4.0.40/f8474c4 Wed Mar 30 18:05:27 PDT 2016)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
TLS: __thread
SIGSEGV: altstack
Notifications: epoll
Architecture: amd64
Disabled: none
Misc: softdebug
LLVM: supported, not enabled.
GC: sgen
奇妙な修正 #1 次の行をコメントアウトすると、プログラムは Linux/mono 上で正常に動作します。
private static void DryRun()
{
... // other code the same
// If I comment out this SINGLE LINE, the program works fine.
// var _filterService = engine.Services.GetService<ITestFilterService>();
}
奇妙な修正#2
ここで、コードを元のままにしておくとしましょう (_filterService
行はコメントされていません)。作業ディレクトリ内の単一の .dll ファイルを削除すると、クラッシュは発生しません。元のファイルをお見せしましょう:
<snip>BadMono/bin/Debug $ ls
.
..
BadMono.exe
BadMono.exe.config
BadMono.pdb
BadMono.vshost.exe
BadMono.vshost.exe.config
Mono.Cecil.dll
System.Threading.Tasks.Dataflow.dll
System.Threading.Tasks.Dataflow.xml
nunit-agent-x86.exe
nunit-agent.exe
nunit.engine.api.dll
nunit.engine.dll
nunit.framework.dll
nunit.framework.xml
を削除するSystem.Threading.Tasks.Dataflow.dll
と、プログラムは再び正常に動作します。Windows ではこの問題は発生せず、私が説明するどのような状況でも問題なく動作します。問題があるのはmono / Linuxだけです。
このサンプル コードでは Tasks.Dataflow (TPL) を実際に呼び出しているわけではないので、これは私にとって非常に奇妙です。誰かがここで何が起こっているのかを明らかにできますか?