You still can't run compact framework unit tests in Team Foundation Build very easily. <ShamelessPlug>You'll be able to with TFS Advantage - Compact Framework Edition</ShamelessPlug> Anyway, you would think you could compile them consistently though, right?
Well, that's not always the case. I was helping out Chris Tacke of OpenNETCF with a problem he was having with build failures on solutions that contained:
-
Compact Framework (CF) projects
-
Compact Framework unit test projects
-
MSTest private accessors
-
Were participating in a continuous integration process under Team Foundation Build using Microsoft Team Foundation Server.
He has had success with all of these technologies independently, but when combined together, it went boom! You know when a problem mentions four different technologies it's always a fun one to solve. ;-)
The Setup
Chris has CF projects, CF unit test assemblies, some private accessors and he submits to the build. The build fails with "API restriction".
Here's the error:
Using "BuildShadowTask" task from assembly "C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\TeamTest\Microsoft.VisualStudio.TestTools.BuildShadowsTask.dll".
Task "BuildShadowTask"
C:\Program Files\MSBuild\Microsoft\VisualStudio\v9.0\TeamTest\Microsoft.TeamTest.targets(14,5): error : API restriction: The assembly 'file:///C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.SmartDevice.UnitTestFramework.dll' has already loaded from a different location. It cannot be loaded from a new location within the same appdomain.
Done executing task "BuildShadowTask" -- FAILED.
Done building target "ResolveTestReferences" in project "TestProject1.csproj" -- FAILED.
What's going on here? I don't know exactly, but I've got some bits and a workaround.
The BuildShadowTask is a component used by the Microsoft.TeamTest.targets used to build the private accessor DLL (e.g. myproject_accessor.dll) that a unit test uses to get at private and protected members of a test target.
It in turn calls the Publicize.exe executable to do the actual work.
A bit more on Chris Tacke's scenario. He's taken one of my patterns and created a TestClassBase that does some common initialization, cleanup and provides some test specific helper functions. This has been put into a CF unit test assembly that has no real unit tests in it. Since it has no real unit tests in it, it has no private accessors in it either.
This was essentially the item that triggers the downfall, albeit, he wasn't doing anything really wrong.
It appears that when MSBuild, BuildShadowsTask and Publicize do their work, they're pulling in some copy/version of Microsoft.VisualStudio.SmartDevices.UnitTestFramework from somewhere else besides where the rest of the MSBuild process is going to get it from. And, somehow it is being kept in the same AppDomain as the rest of the MSBuild process. (So, I doubt that it is publicize.exe that's the problem). Or vice versa, MSBuild is pulling one in and then BuildShadowsTask is attempting too as well.
The Workaround
If you are going to use private accessors in any compact framework unit test assembly in your solution, use it in all compact framework unit test assemblies. Even if you have to refer to something you don't need to make a private accessor. Even if you create a "shill target" assembly, you need to have in it in all of them.
Also, we had previously attempted to overcome the inability of Team Build to find the Microsoft.VisualStudio.SmartDevices.UnitTestFramework.dll by registering it in the GAC. As always, when you mess with fire you get burnt. At this point, I have had Chris has add the following to his build scripts (update the path as appropriate to find Microsoft.VisualStudio.SmartDevices.UnitTestFramework.dll)
<ItemGroup>
<AdditionalReferencePath Include="c:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\" />
</ItemGroup>