Friday, March 16, 2012

Telerik JustMock enabling the profiler from the command line

I recently ran into the issue of enabling the JustMock profiler for nUnit console on our build server, this is needed to mock statics and concrete classes. Telerik points to a solution but this is over a year old and no longer works, also all of their descriptions of how to enable this programatically also do not work. Telerik also decided to include the JustMockRunner from the mentioned blog in their newer releases in Program Files (x86)\Telerik\JustMock\Libraries\ This actually does work, so Telerik must have modified their version but there's a huge problem with this version, it doesn't allow the passing of any arguments to nunit or allow you to supply more than one test dll. This is a big problem for us so after some digging and reverse engineering I've re-written the JustMockRunner application to just pass all the arguments supplied to nUnit. Actually you don't even have to use nUnit, it should work with any command line unit tester.

Telerik documents 2 or 3 environment variables depending on where you read it that are involved in enabling the profiler, they added another one recently which causes all the old documentation and programs to fail. This new variable is JUSTMOCK_INSTANCE which needs to be set to the process ID of the running program.

I also noticed that the old JustMockRunner was capturing the output into a string and when everything finishes it all gets dumped to the screen at once. I find this behavior to be very annoying and results in a "is it actually running?" moment and with our 1,000+ unit tests there's quite a while for any feedback about what is going on. I have no idea why it was done this way as the default behavior of running another process is to display its output in the current console window. So my version removes it and makes it as simple and flexible as possible.

Without further adieu here's my code:

Update: There's some interest in this solution so I have updated to my code to a later version I came up with that preserves all arguments that are passed to it in their original form. This was a problem when trying to pass arguments with spaces in them like /loc="c:\Program Files\"
using System;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;

namespace JustMockRunner
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Requires at least 1 argument, nunit-console.exe location");
                return;
            }
            string command, arguments;
            ParseCommand(Environment.CommandLine.Trim(), out command, out arguments);
            
            var process = new Process();
            var processStartInfo = new ProcessStartInfo(command)
                                       {
                                           Arguments = arguments,
                                           UseShellExecute = false
                                       };

            processStartInfo.EnvironmentVariables["COR_ENABLE_PROFILING"] = "0x1";
            processStartInfo.EnvironmentVariables["COR_PROFILER"] = "{D1087F67-BEE8-4f53-B27A-4E01F64F3DA8}";
            processStartInfo.EnvironmentVariables["COMPLUS_ProfAPI_ProfilerCompatibilitySetting"] = "EnableV2Profiler";
            processStartInfo.EnvironmentVariables["JUSTMOCK_INSTANCE"] = Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture);


            process.StartInfo = processStartInfo;
            process.Start();
            process.WaitForExit();
        }

        private static void ParseCommand(string input, out string command, out string arguments)
        {
            var reg = new Regex(@"(^""[^""]*(?:\.[^""]*)*""|^\s*\S*)(.*)");
            var parts = reg.Match(reg.Match(input).Groups[2].Value.Trim());

            command = parts.Groups[1].Value.Trim();
            arguments = parts.Groups[2].Value.Trim();
        }
    }
}