Showing posts with label Visual Studio. Show all posts
Showing posts with label Visual Studio. Show all posts

Tuesday, April 21, 2015

Small helper tool for configuration transformations

In my daily work I often use configuration transformations for different settings for the different environment and often I need to add a new transformation for a new environment. So I've decided to create a small tool that can automate adding new transformations to all the projects in a solution. In addition to this I've added functionality for applying config transformations as this can be useful if you want to test the transformations from command line. Here's a short description of the tool:

TransformHelper

Small tool helping with the usage of the configuration transformations

It has two

  • Add - passing solution, existing transformation and the new transformation the tool will go through all the projects in the solution and check for configuration files having the existing transformation and will add new transformation copying the files from the existing
  • Remove - passing solution, and existing transformation the tool will go through all the projects in the solution and check for configuration files having the existing transformation and will delete them
  • Apply - passing source file, transformation file and optional target file (if not passed the source file content will be replaced)
  • ApplySLN - passing solution, and existing transformation the tool will go through all the projects in the solution and apply the transformation on the original file
Usage
  • Add mode (you may not specify the mode here as the Add mode is the default behavior)
TransformHelper.exe [-mode add] -solution C:\Work\MySolution.sln -existing Dev -new Live

This will check all the projects in the solution for files like [fileName].Dev.config and will add the corresponding [fileName].Live.config files copying the content from the existing ones
  • Remove mode
TransformHelper.exe -mode remove -solution C:\Work\MySolution.sln -existing Dev

This will check all the projects in the solution for files like [fileName].Dev.config and will delete them and remove them from the project
  • Apply mode
TransformHelper.exe -mode apply -source "C:\Work\MySolution\WebProject\web.config" -transformFile "C:\Work\MySolution\WebProject\web.Dev.config" -target "C:\Temp\result.web.config"

This will apply the transformation file C:\Work\MySolution\WebProject\web.Dev.config on C:\Work\MySolution\WebProject\web.config and the result content will be saved in C:\Temp\result.web.config file
  • ApplySLN mode
TransformHelper.exe -mode applySLN -solution C:\Work\MySolution.sln -existing Dev

This will check all the projects in the solution for files like [fileName].Dev.config and if there are files like [fileName].config as well in the project they will be transformed using the Dev transformations

You can find the tool on github here

Tuesday, December 18, 2012

Using WIX with managed custom action

WIX is a great toolset for creating installers. In most of the cases when you need an installer you need some custom logic to be executed. It's great that WIX supports managed custom actions. Anyway it wasn't so easy for me to make it work, so I want to share my experience.
I will demonstrate it using the WIX Visual Studio add-in (version v3.7.1217.0).

We will create WIX Setup project and C# Custom Action Project.
We will add a dummy text file in the setup project to be used as installation content and will change a little bit the auto created Product.wxs file.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="*" Name="WixWithCustomAction" Language="1033" Version="1.0.0.0" Manufacturer="Trifonov" UpgradeCode="60468a7d-6485-4e7e-bf82-503213bc43a8">
    <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

    <Media Id='1' Cabinet='Dummy.cab' EmbedCab='yes' />

    <Directory Id='TARGETDIR' Name='SourceDir'>
      <Directory Id='ProgramFilesFolder'>
        <Directory Id='WixWithCustomAction' Name='WixWithCustomAction'>
          <Component Id="DummyContent" Guid="ba9028ae-0d3b-4b66-8560-f53330736265">
            <!-- Add the dummy file as content. -->
            <File Id="DummyFile" KeyPath="yes" Source="Dummy.txt" Vital="yes" />
          </Component>
        </Directory>
      </Directory>
    </Directory>

    <Feature Id="Complete" Title="WixWithCustomAction" Level="1">
      <ComponentRef Id='DummyContent' />
    </Feature>
  </Product>
</Wix>
That's how our solution looks like:

If we build the WixWithCustomAction project, WixWithCustomAction.msi will be created. If we run it WixWithCustomAction folder will be created in program files with Dummy.txt file inside.
But now we want to add a custom action which will create a file in C:\Temp folder. We will use the MyCustomActionProject for this. Let's change the CustomAction class a little bit:
using Microsoft.Deployment.WindowsInstaller;
using System.IO;

namespace MyCustomActionProject
{
    public class CustomActions
    {
        [CustomAction]
        public static ActionResult MyCustomAcion(Session session)
        {
            session.Log("Executing MyCustomAcion");

            File.CreateText(@"c:\temp\installed.txt");

            return ActionResult.Success;
        }
    }
}

Now we just need to call this custom action from the installer. To do this we will add a reference to this project in the setup project.
Now let's add the custom action in the Product.wxs file.
Adding the project as a reference allows as to use these variables.
But adding custom action is a little bit complicated. After building the MyCustomActionProject.dll file we will need a call to MakeSfxCA.exe and sfxca.dll in your installed WiX toolset as the dll need a reference to Microsoft.Deployment.WindowsInstaller.dll and has CustomAction.config attached. Calling the MakeSfxCA.exe tool will package the project output to MyCustomActionProject.CA.dll(here you can find some additional information about this).
As we use "C# Custom Action Project" there is an import added to $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.CA.targets file which will create this package on build. To check this you can build the custom action project and see the output:
So the custom action in the Product.wxs needs to reference the .CA.dll file. Thats why we cannot use
$(var.MyCustomActionProject.TargetPath) as a source for the custom action binary, but we will have to construct the source path like this:
$(var.MyCustomActionProject.TargetDir)$(var.MyCustomActionProject.TargetName).CA.dll
The other option is not to use the project reference but add the full path to the custom action output.
So we will add the following rows to the wxs file
<!--The source file should be the MyCustomActionProject.CA.dll file, that's why it's constructed this way-->
<Binary Id='CustomActionBinary' SourceFile='$(var.MyCustomActionProject.TargetDir)$(var.MyCustomActionProject.TargetName).CA.dll' />

<!--The DllEntry must be the name of the method to be called from the custom action project, in our case - MyCustomActionMethod 
http://wix.tramontana.co.hu/tutorial/events-and-actions/at-a-later-stage 
The Execute attribute will specify the deferred status of our custom action.
And finally, HideTarget will allow us to disable logging the parameteres passed to this custom action if security considerations so dictate.-->
<CustomAction Id='CustomActionId' BinaryKey='CustomActionBinary' DllEntry='MyCustomActionMethod' Execute="deferred" HideTarget="yes"/>

<InstallExecuteSequence>
  <!--We want to call the custom action before the install finalizes-->
  <Custom Action='CustomActionId' Before='InstallFinalize'/>
</InstallExecuteSequence>
And that's it. Now if we build the setup project and run the created msi installer, c:\temp\installed.txt will be created as a part of the installation process.

You can find the solution file here - WixWithCustomAction.zip

Thursday, November 15, 2012

Testing private methods with Visual Studio

When writing unit tests in most of the cases we have to test private methods. Let's see how this can be achieved using Visual Studio. I will give you three options:

  1. You can test private methods using Reflection, but this is not always a good option, as when you change the name or input arguments of a Method, you won't get any error when building, but you will get exceptions run time.
    We will use the project from my previous post Getting console output within a unit test.
    We will add a private method to the DummyClass:
    using System;
    
    namespace ConsoleLogger
    {
        public class DummyClass
        {
            public void WriteToConsole(string text)
            {
                Console.Write(text);
            }
    
            private void PrivateWriteToConsole(string text)
            {
                Console.Write("Private: " + text);
            }
        }
    }
    
    Now we can add a unit test to test the method using reflection:
    [TestMethod]
    public void PrivateWriteToConsoleReflection()
    {
        var currentConsoleOut = Console.Out;
    
        DummyClass target = new DummyClass();
        Type type = typeof(DummyClass);
    
        string text = "Hello";
    
        using (var consoleOutput = new ConsoleOutput())
        {
            var method = type.GetMethod("PrivateWriteToConsole", 
                BindingFlags.NonPublic | BindingFlags.Instance);
            method.Invoke(target, new object[1] { text });
    
            Assert.AreEqual(string.Format("Private: {0}", text), 
                consoleOutput.GetOuput());
        }
    
        Assert.AreEqual(currentConsoleOut, Console.Out);
    }
    We get and invoke the method using reflection.

  2. Another options is to use PrivateObject class. Using it you can easily call private method, but you have the same problem, you won't get compile exception when name or parameters are changed. Here is the same test written using PrivateObject:
    [TestMethod]
    public void PrivateWriteToConsolePrivateObject()
    {
        var currentConsoleOut = Console.Out;
    
        PrivateObject target = new PrivateObject(typeof(DummyClass));
    
        string text = "Hello";
    
        using (var consoleOutput = new ConsoleOutput())
        {
            target.Invoke("PrivateWriteToConsole", text);
    
            Assert.AreEqual(string.Format("Private: {0}", text), 
                consoleOutput.GetOuput());
        }
    
        Assert.AreEqual(currentConsoleOut, Console.Out);
    }
    
  3. And here is the third option which I think is the best. You can add .accessor file containing the name of the assembly whose private methods you want to see, or you can use Visual Studio "Create Unit Tests..." wizard to do this for you:

    this will add the following unit test:
    /// 
    ///A test for PrivateWriteToConsole
    ///
    [TestMethod]
    [DeploymentItem("ConsoleLogger.exe")]
    public void PrivateWriteToConsoleTest()
    {
        DummyClass_Accessor target = new DummyClass_Accessor(); // TODO: Initialize to an appropriate value
        string text = string.Empty; // TODO: Initialize to an appropriate value
        target.PrivateWriteToConsole(text);
        Assert.Inconclusive("A method that does not return a value cannot be verified.");
    }
    
    we will modify it for our needs:
    /// 
    ///A test for PrivateWriteToConsole
    ///
    [TestMethod]
    [DeploymentItem("ConsoleLogger.exe")]
    public void PrivateWriteToConsoleTest()
    {
        var currentConsoleOut = Console.Out;
    
        DummyClass_Accessor target = new DummyClass_Accessor();
    
        string text = "Hello";
    
        using (var consoleOutput = new ConsoleOutput())
        {
            target.PrivateWriteToConsole(text);
    
            Assert.AreEqual(string.Format("Private: {0}", text),
                consoleOutput.GetOuput());
        }
    
        Assert.AreEqual(currentConsoleOut, Console.Out);
    }
    
    When Visual Studio builds the project it will generate ConsoleLogger_Accessor.exe assembly, containing DummyClass_Accessor class with public methods only.
  4. Just to mention that you can test internal methods using the same approaches, but you will have one more option - in the AssemblyInfo file of the assembly being tested, you can add  InternalsVisibleTo attribute to specify which assembly will see the internal methods, in our case:
    [assembly: InternalsVisibleTo("ConsoleLogger.Tests")]
    
    now we will add an internal method to the same class:
    using System;
    
    namespace ConsoleLogger
    {
        public class DummyClass
        {
            public void WriteToConsole(string text)
            {
                Console.Write(text);
            }
    
            private void PrivateWriteToConsole(string text)
            {
                Console.Write("Private: " + text);
            }
    
            internal void InternalWriteToConsole(string text)
            {
                Console.Write("Internal: " + text);
            }
        }
    }
    
    and here is the working test method:
    [TestMethod]
    public void InternalWriteToConsoleTest()
    {
        var currentConsoleOut = Console.Out;
    
        DummyClass target = new DummyClass();
    
        string text = "Hello";
    
        using (var consoleOutput = new ConsoleOutput())
        {
            target.InternalWriteToConsole(text);
    
            Assert.AreEqual(string.Format("Internal: {0}", text),
                consoleOutput.GetOuput());
        }
    
        Assert.AreEqual(currentConsoleOut, Console.Out);
    }
    
And here's the code - ConsoleLogger.zip

Getting console output within a unit test

Today I needed to test a method which writes to the Console to validate the ouput. It is not hard to change the default console output and check the result. However you may forget to return the original output at the end. So let's take a look at my solution.

Let say we have the following class we want to test:
using System;

namespace ConsoleLogger
{
    public class DummyClass
    {
        public void WriteToConsole(string text)
        {
            Console.Write(text);
        }
    }
}

I have created a small helper class to redirect the output to a StringWriter:
using System;
using System.IO;

namespace ConsoleLogger.Tests
{
    public class ConsoleOutput : IDisposable
    {
        private StringWriter stringWriter;
        private TextWriter originalOutput;

        public ConsoleOutput()
        {
            stringWriter = new StringWriter();
            originalOutput = Console.Out;
            Console.SetOut(stringWriter);
        }

        public string GetOuput()
        {
            return stringWriter.ToString();
        }

        public void Dispose()
        {
            Console.SetOut(originalOutput);
            stringWriter.Dispose();
        }
    }
}
Now let's write the unit test:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ConsoleLogger.Tests
{
    [TestClass]
    public class DummyClassTest
    {
        [TestMethod]
        public void WriteToConsoleTest()
        {
            var currentConsoleOut = Console.Out;

            DummyClass target = new DummyClass(); 
            
            string text = "Hello";

            using (var consoleOutput = new ConsoleOutput())
            {
                target.WriteToConsole(text);

                Assert.AreEqual(text, consoleOutput.GetOuput());
            }

            Assert.AreEqual(currentConsoleOut, Console.Out);
        }
    }
}
This way we are sure that the original output will be restored and it's easy to get the output from the console.

You can find the sample here ConsoleLogger.zip.

Thursday, November 8, 2012

How to configure local Nuget Repository

After my last posts about Nuget packaging I wanted to share another useful experience with Nuget.
You can create a local repository to store all the packages you need and not to download those every time.
  1. To do this I have created a folder C:\NugetConfig\Repo and I have copied there the Newtonsoft.Json.4.5.10.nupkg package file
  2. To make the both solutions use this local repository all I have to do is to change the following settings in the NuGet.targets file:
    <ItemGroup Condition=" '$(PackageSources)' == '' ">
        <!-- Package sources used to restore packages. By default will used the registered sources under %APPDATA%\NuGet\NuGet.Config -->
        <!--
            <PackageSource Include="https://nuget.org/api/v2/" />
            <PackageSource Include="https://my-nuget-source/nuget/" />
        -->
    </ItemGroup>
    
    and adding s new PackageSource location
    <ItemGroup Condition=" '$(PackageSources)' == '' ">
        <!-- Package sources used to restore packages. By default will used the registered sources under %APPDATA%\NuGet\NuGet.Config -->
        <!--
            <PackageSource Include="https://nuget.org/api/v2/" />
            <PackageSource Include="https://my-nuget-source/nuget/" />
        -->
        <PackageSource Include="C:\NugetConfig\Repo" />
    </ItemGroup>
    
    And that's it. This will make the solution search for the used packages in the given folder and you will get а meaningful error if the package could not be found.
  3. Furthermore you can add you local repository to the visual studio package sources
    so that you will be able to search and add packages from it to any new solution:
As usual you can find the code here NugetConfig-Local-Repo.zip.

Share Nuget packages between solutions part 2

Let's see how we can fix the problem explained here.
According to the latest release notes by now you should be able to change the packages folder by adding this setting in the Nuget.config file:
<configuration>
  <config>
    <add key="repositoryPath" value="C:\myteam\teampackages"></add>
  </config>
  ... 
</configuration>
However unfortunately I couldn't make this work :( So I found a workaround. I want to make the Second.sln use the package folder of First.sln. So I have made the following changes to the NuGet.targets file.
  1. I this section:
    <PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
        <!-- Windows specific commands -->
        <NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
        <PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
    </PropertyGroup>
    
    I have added the following row:
    <PackagesDir>$(SolutionDir)..\First\packages</PackagesDir> 
  2. Change the arguments of the NewGetCommand from:
    <RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(RequireConsentSwitch) -solutionDir "$(SolutionDir)</RestoreCommand>
    
    to:
    <RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" -o "$(PackagesDir)" $(RequireConsentSwitch) -solutionDir "$(SolutionDir)</RestoreCommand>
    
    I have added -o "$(PackagesDir)" which should make the packages to be downloaded to the PackagesDir folder
  3. To make the configuration more flexible we can change the PackagesDir definition to
    <PackagesDir Condition="'$(PackagesDir)' == ''">$(SolutionDir)..\First\packages</PackagesDir> 
    this will allow us to predefine the PackagesDir value in the .csproj.user files.
  4. To test this we can add the C:\ NugetConfig\First\Dummy\Dummy.csproj.user with the following content:
    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <PropertyGroup>
            <PackagesDir>C:\NugetConfig\packages</PackagesDir>
        </PropertyGroup>
    </Project>
    
    This will make the Nuget to get files in the C:\NugetConfig\packages folder.
And here is the code NugetConfig-part-2.zip

Share Nuget packages between solutions part 1

Many of you may use Nuget to add reference assemblies to your projects. Anyway sometimes we need to share a project between solutions. In this case if the relative path to this project from the different solutions is different you won't be able to use the default nuget packaging functionality.
Let me explain this with samples.
  1. Let's create an empty Console application named First in folder C:\NugetConfig\First
  2. Add a dummy class library project to this solution in the same folder.
  3. Add reference in the console application to the class library. You should have the following structure now:
  4. Now add Newtonsoft.Json package to the solution:
  5. Install the package to the Dummy project:
  6. Add a dummy class to the dummy project which references Newton Json:
    namespace Dummy
    {
        public class Dummy
        {
            public Newtonsoft.Json.Required Required { get; set; }
    
            public Dummy()
            {
                this.Required = Newtonsoft.Json.Required.Always;
            }
    
            public override string ToString()
            {
                return this.Required.ToString();
            }
        }
    } 
  7. Add a call to the dummy project in Program.cs
    using System;
    
    namespace First
    {
        class Program
        {
            public static void Main(string[] args)
            {
                var dummy = new Dummy.Dummy();
                Console.WriteLine(dummy);
            }
        }
    }
    
  8. Now enable Nuget Package Restore on this solution:

    Now if we delete the Newtonsoft.Json folder from the packages folder and run the build the package is downloaded and the solution builds successfully
  9. Let's create a new project in another solution named Second in folder in folder C:\NugetConfig\Second
  10. Add reference to the Dummy project 
  11. Add a call to the dummy project in Program.cs
    using System;
    
    namespace Second
    {
        class Program
        {
            public static void Main(string[] args)
            {
                var dummy = new Dummy.Dummy();
                Console.WriteLine(dummy);
            }
        }
    }
    
  12. We can build the second solution now as we have already downloaded the needed nuget package. But let's delete the package from C:\NugetConfig\First\packages folder. 
  13. Now we won't be able to build the solution. Let's enable Nuget Package Restore on this solution as well.
  14. Trying to build the solution leads to this error:
So here is the actual problem:
In the Dummy project there is a reference to the NewtonJson
 <Reference Include="Newtonsoft.Json">
      <HintPath>..\packages\Newtonsoft.Json.4.5.10\lib\net40\Newtonsoft.Json.dll</HintPath>
 </Reference>
But our folder looks like this:

So we search for the Newtonsoft.Json.dll in folder C:\NugetConfig\First\packages folder as the HintPath is relative from the Dummy.csproj file.
But the build downloads the package to C:\NugetConfig\Second\packages

You can find the code here NugetConfig-part-1.zip

To see how we can resolve this issue go to the part 2.