Wednesday, April 17, 2013

Unit testing a Powershell Commandlet

I chose to write Powershell commandlets in C#. I created a class library (DLL) project and inherited a class from Cmdlet base class. I created a snapin class derived from PSSnapIn to be able to install my commandlets from powershell.

Unit testing

Writing a unit test

[TestClass]
    public class UnitTest1
    {
        private static RunspaceConfiguration config;
        private static Runspace runspace;
        private static Pipeline pipe;
        private static Command command;

        [ClassInitialize]
        public static void TestFixtureSetup(TestContext context)
        {
            config = RunspaceConfiguration.Create();

            PSSnapInException warning;
            config.AddPSSnapIn("SnapinName", out warning);

            runspace = RunspaceFactory.CreateRunspace(config);
            runspace.Open();
        }

        [ClassCleanup]
        public static void TestFixtureTeardown()
        {
            runspace.Close();
        }

        [TestInitialize]
        public void Setup()
        {
            pipe = runspace.CreatePipeline();
            command = new Command("CommandName");
            pipe.Commands.Add(command);
        }

        [TestMethod]
        public void TestMethod1()
        {
            command.Parameters.Add(new CommandParameter("ParameterName", "Value"));

            var psObject = pipe.Invoke();
        }
    }

Snapin couldn't be found

I wrote a unit test against one of my commands but it couldn't find my snapin, I got the following exception:
System.Management.Automation.PSArgumentException: System.Management.Automation.PSArgumentException: The Windows PowerShell snap-in '...' is not installed on this computer..

I could use my snapin from Powershell command line.

Solution

Add the following lines as post-build events:
c:\Windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe $(TargetPath)
c:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe  $(TargetPath)

Both 32-bit and 64-bit installation will happen this way.

Cause

Visual Studio 2012 is a 32-bit process. I registered with 64-bit install util only. Now I register both versions.

Monday, April 15, 2013

XAML Resource Loading

Visual Studio 2012 kept locking my .DLLs. It happened mostly after starting a debugger session. I could trace back the problem to a GetManifestResourceStream() call.

Solution

This is my solution that seems to be working:

public Stream GetResourceStreamByType(Type type, string resourceName)
{
  var location = Assembly.GetAssembly(type).Location;
  var assemblyBytes = File.ReadAllBytes(location);

  var appDomain = AppDomain.CreateDomain("Resource domain");
  var assembly = appDomain.Load(assemblyBytes);

  return assembly.GetManifestResourceStream(resourceName);
}

public BitmapImage GetBitmapByType(Type type, string resourceName)
{
  var bmp = new BitmapImage();
  using (var source = GetResourceStreamByType(type, resourceName))
  {
    bmp.BeginInit();
    bmp.StreamSource = source;
    bmp.EndInit();
  }

  return bmp;
}

Reasons

Loading the resource by calling the GetManifestResourceStream() on the current assembly loads the assembly into the debugger's app domain which is Visual Studio in this case. Reading the bytes and loading them manually prevents this behavior.

I couldn't reload the form again after loading the resources this way so I had to create a separate app domain as well. Without separate app domain the assembly was already loaded and the reloading didn't happen with an error message like 'does not have a resource identified by the URI'.