Sunday, April 18, 2010

Getting coverage.exe (trunk) to work with nUnit and .net 4

Following up from yesterday, about an instrumenting coverage tool that I'd spotted on Googlecode (Apr 2015 : rescued to GitHub), what I needed to do to get the instrumenting coverage tool working under .net 3.5sp1 with nUnit 2.5.2 driving unit tests.

First, the command line looks like

[path to]\ClassLibrary1.dll [path to]\UnitTestClassLibrary1.dll /x coverage.xml /r /exe [tool path to]\nunit-console.exe [path to]\UnitTestClassLibrary1.dll

where the /r (backup and replace) flag is essential; so you need to work on a copy directory with your assemblies and their .pdb files.

Second, you need to make the following change to the coverage main program in Runner.cs, line 92 from

to be

The hard-coded relative path could be added as yet another argument instead; and I've not played around with the shadow-copy parameter.

But that at least -- with a totally trivial test set -- provided me with a clean run and non-empty coverage data.

So the next thing to do will be to port the whole lot to .net 4 and see what gives. Also, it will be worth trying the teamcity branch (faking NCover 1.x) to see whether the different AppDomain usage there means we don't have to resort to this trick.

Later: doing a rebuild of Coverage and the sample test under .net 4, with command line

[path to]\ClassLibrary1.dll [path to]\UnitTestClassLibrary1.dll /x coverage.xml /r /exe [tool path to nunint 2.5.4]\nunit-console.exe /framework=net-4.0.30319 [path to]\UnitTestClassLibrary1.dll

where the nunit-console.exe.config has been adjusted appropriately, I get

Copyright (C) 2002-2004 James W. Newkirk, Michael C. Two, lexei A. Vorontsov.
Copyright (C) 2000-2002 Philip Craig.
All Rights Reserved.

Runtime Environment -
   OS Version: Microsoft Windows NT 6.0.6002 Service Pack 2
  CLR Version: 2.0.50727.4200 ( Net 2.0 )

ProcessModel: Default    DomainUsage: Multiple
Execution Runtime: net-4.0.30319
Unhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object.

Server stack trace:
   at NUnit.Util.ProcessRunner.Load(TestPackage package)
   at NUnit.Core.ProxyTestRunner.Load(TestPackage package)
   at NUnit.Util.RemoteTestAgent.AgentRunner.Load(TestPackage package)
   at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
   at System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(RuntimeMethodHandle md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
   at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg, Int32 methodPtr, Boolean fExecuteInContext)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at NUnit.Core.TestRunner.Load(TestPackage package)
   at NUnit.Core.ProxyTestRunner.Load(TestPackage package)
   at NUnit.Util.ProcessRunner.Load(TestPackage package)
   at NUnit.ConsoleRunner.ConsoleUi.Execute(ConsoleOptions options)
   at NUnit.ConsoleRunner.Runner.Main(String[] args)

However, if I do this as a two-stage operation

[path to]\ClassLibrary1.dll [path to]\UnitTestClassLibrary1.dll /x coverage.xml /r

to instrument the code and create a coverage file with zero visit counts throughout -- and then run nunit separately with rest of the command line

[tool path to nunint 2.5.4]\nunit-console.exe /framework=net-4.0.30319 [path to]\UnitTestClassLibrary1.dll

this then fills in the visit counts as expected.

We have code coverage for .net 4 -- at least under some conditions! And without having to rebuild any tools apart from coverage.exe!

Later yet (refrigerator logic) -- of course when working coverage in two passes, I don't need to do the fiddle to change the settings for the AppDomain. And I don't need to have coverage built in the same .net version as I want to run nUnit in, either. I can use the code as synched from Googlecode built under .net 3.5 even for .net 4 codebases; all I have to do now is fix the problems with running it over F# code.


Ryan said...

What do you mean by two stage process? I am trying to get this working, no F# here but would sure like to get some coverage out of my unit tests.

Steve Gilham said...

With old NCover you went something like

%NCOVER% %NUNIT% $(TargetFileName) /noshadow //reg //a "Tinesware.Infrastructure;Tinesware.InfrastructureTests;Tinesware.Rules;Tinesware.TestData;CSharpTests"

where the coverage tool listened to the implied instrumentation in the CLR itself. With coverage.exe you would do

coverage [list of assemblies] /r /x
%NUNIT% [unit test assembly]

which instruments the code first, then in the second step, executes it.

Notionally, you can concatenate the two steps with coverage's /exe command line switch, but I find that doing so can cause nUnit to fault (pretty much every time when running as a post-build step inside Visual Studio, less often at the command line).