VSTS test deployment and invalid assembly culture
I have a DLL that I'm testing, which links to a DLL that has what I think is an invalid value for AssemblyCulture. The value is "Neutral" (notice the upper-case "N"), whereas the DLL I'm testing, and every other DLL in my project, has a value of "neutral" (because they specify AssemblyCulture("")).
When I try to deploy the DLL that links to the problem DLL, I get this error in VSTS:
Failed to queue test run '...': Culture is not supported. Parameter name: name Neutral is an invalid culture identifier. <Exception>System.Globalization.CultureNotFoundException: Culture is not supported. Parameter name: name Neutral is an invalid culture identifier. at System.Globalization.CultureInfo..ctor(String name, Boolean useUserOverride) at System.Globalization.CultureInfo..ctor(String name) at System.Reflection.RuntimeAssembly.GetReferencedAssemblies(RuntimeAssembly assembly) at System.Reflection.RuntimeAssembly.GetReferencedAssemblies() at Microsoft.VisualStudio.TestTools.Utility.AssemblyLoadWorker.ProcessChildren(Assembly assembly) at Microsoft.VisualStudio.TestTools.Utility.AssemblyLoadWorker.GetDependentAssemblies(String path) at Microsoft.VisualStudio.TestTools.Utility.AssemblyLoadWorker.GetDependentAssemblies(String path) at Microsoft.VisualStudio.TestTools.Utility.AssemblyLoadStrategy.GetDependentAssemblies(String path) at Microsoft.VisualStudio.TestTools.Utility.AssemblyHelper.GetDependentAssemblies(String path, DependentAssemblyOptions options, String configFile) at Microsoft.VisualStudio.TestTools.TestManagement.DeploymentManager.GetDependencies(String master, String configFile, TestRunConfiguration runConfig, DeploymentItemOrigin dependencyOrigin, List`1 dependencyDeploymentItems, Dictionary`2 missingDependentAssemblies) at Microsoft.VisualStudio.TestTools.TestManagement.DeploymentManager.DoDeployment(TestRun run, FileCopyService fileCopyService) at Microsoft.VisualStudio.TestTools.TestManagement.ControllerProxy.SetupTestRun(TestRun run, Boolean isNewTestRun, FileCopyService fileCopyService, DeploymentManager deploymentManager) at Microsoft.VisualStudio.TestTools.TestManagement.ControllerProxy.SetupRunAndListener(TestRun run, FileCopyService fileCopyService, DeploymentManager deploymentManager) at Microsoft.VisualStudio.TestTools.TestManagement.ControllerProxy.QueueTestRunWorker(Object state)</Exception>
Even if I don't link to the DLL (in my VSTS wrapper test, or in the NUnit test), as soon as I add it in my GenericTest file (I'm wrapping NUnit tests), I get that exception.
We don't have the source for the problem DLL, and it is also code signed, so I can't solve this by recompiling.
Is there a way to skip deploying the dependencies of a DLL DeploymentItem, to fix or disable the culture check, or to work around this by convoluted means (maybe somehow embed the assembly)? Is there a way to override the value for the culture, short of hacking the DLL (and removing code signing so the hack works)? Maybe with an external manifest?
Any correct solution must work without weird changes to production code. We can't deploy a hacked DLL, for example. It also must allow the DLL to be instrumented for code coverage.
Additional note: I do get a linker warning when compiling the DLL under test that links to the problem DLL, but this hasn't broken anything but VSTS, and multiple versions have shipped.
I'd appreciate an alternative to this, but I found a solution that works for me.
- Compile the code of the project under test into the unit test DLL, instead of referencing the built DLL
- Hack the problem DLL to remove code signing, and change "Neutral" to "neutral" (manually modified the binary)
- Check in the problem DLL to the unit test project directory. Don't overwrite the production version
- Link the unit test project to this DLL instead of the original problem DLL
This doesn't involve changes to production code, but does duplicate effort. To add/remove source files from the project under test, you must also modify the unit test project, and add/remove a link to those files. It doesn't duplicate code, though, since we use a link instead of duplicating the source file.
Edit: Some variation of this worked for me at one point, but I am completely lost as to how it worked, now. The problem I have with this solution is that I have a mixed-mode assembly, so I can't do a proper hack to complete remove the culture setting. An explicitly set value of "neutral" only works in some places.
See my other answer for my final work-arounds. They should work in all cases, though they're convoluted as well.
I found out that if I do instrumentation (which was the point of me even bothering with the NUnit VS integration), then I manage to avoid these problems. Not sure which exact combination of settings did the trick, but these are the settings I am using:
- In-place instrumentation
- The MSTest test project only has a dependency on the NUnit test project
- The generic test deploys the NUnit test (from the MSTest bin output folder), the NUnit test app.config (from the NUnit test bin output folder), and the problem DLL. The rest of the dependencies, including the DLL under test, get deployed by code coverage.
If you don't need instrumentation, I found this work-around:
- Don't link to the top-level DLL that's causing a problem (in this case, the DLL under test)
- Modify the solution build setting to make your project dependent on that project
- Embed all DLLs in the dependency chain that are causing problems as Embedded Resources
- During runtime, grab the resource stream for each DLL, create a file, and copy the stream across. Basically, extract the resources and output them again as files
Then, there is no link-time dependency, so MSTest won't see those DLLs. This won't work well with instrumentation because the DLLs only appear at runtime, and MSTest wants to instrument during deploy time. It also won't work well if you actually have to link to those DLLs :) In my case, they derived from an abstract interface, which was free from the problem.