SharpDevelop Community

Get your problems solved!
Welcome to SharpDevelop Community Sign in | Join | Help
in Search

Matt Ward

January 2006 - Posts

  • Code Coverage with NCover

    Revision 1057 of SharpDevelop2 sees support for code coverage.  You can now run your unit tests and check how much of your code is covered.  To use this feature NCover needs to be installed, the latest release, at the time of this post, was 1.5.2 (beta 3).

    Issues

    Currently NCover 1.5.2 can only be run under an account with administrator rights.  There is a workaround for this problem which you can find at the end of this post. 

    NCover 1.5.3 no longer has this problem and can be run from a non-administrator account without requiring a workaround.

    Running Tests with Code Coverage

    To see how to use the code coverage feature in SharpDevelop we will use the Money test project that ships with NUnit.  First thing to do is to open it up and build it.  Then open up the Unit Tests pad (View | Tools | Unit Tests) and load the unit tests in to the tree view by clicking the Reload button.  Then select the tree node, right click and select "Run tests with code coverage".



    You can also run the code coverage by right clicking a test fixture name or test method name and selecting "Run with code coverage", which is under the "Unit Testing" menu.



    After this menu option has been selected, the output window should be displayed and you should see the NCover output as it profiles your assemblies.

    If any errors were found in your tests, after the profiling has finished, then the Errors window will be displayed.

    Code Coverage Results

    To see the code coverage results open the Code Coverage window (View | Tools | Code Coverage).



    The tree view shows the classes that have been profiled along with the percentage of the code covered. The list view, on the right hand side, shows more detailed output on the lines visited.



    Double clicking a class or method in the tree view will open up the corresponding source file.  Double clicking a row in the list view will open up the corresponding source file and move the cursor to the appropriate line.

    The toolbar button at the top left corner of the Code Coverage results allows you to enable/disable the highlighting of the covered source code. 



    When this option is enabled, opening a source file will highlight the lines of code that have been covered in green, and uncovered lines in red.



    Code Coverage Options

    The colours used to highlight the covered code can be changed in the options page (Tools | Options | Tools | Code Coverage).



    This options page also allows you to specify the location of the NCover console application.  By default SharpDevelop assumes it is installed in "C:\Program Files\NCover".

    Code Coverage Project Options

    By default all assemblies that can be profiled will be, this means you will see code coverage for tests as well as your other classes.  To specify which assemblies you want to be profiled, open up the project options (Project | Project Options), select the Code Coverage tab and enter the names of the assemblies you want to be profiled, without the file extension, each delimited by a semi-colon.



    Issues
    • There is currently no way to stop a code coverage run.
    • Can only get code coverage for the entire assembly or assemblies, not individual test fixtures or test methods.
    • Running code coverage from the Unit Tests pad does not update the results in the tree.
    • The path to the test assembly cannot contain any spaces otherwise you will see the message "No MbUnit results file generated" in the Errors window.  NCover seems to strip quotation marks from any arguments passed to the profiled application.

    Running NCover as non-admin

    The only way I could get NCover 1.5.2 to work as non-admin was to re-register the CoverLib COM component.

    Under an Administrator account, run "regsvr32 coverlib.dll" from the directory where NCover is installed (C:\Program Files\NCover).

    NCover 1.5.3 no longer has this issue and installs for all users, making it possible to run NCover from a non-admin account without needing this workaround.
     
    Links

    NCover
    NCover Explorer
    Code Coverage in Visual Studio with TestDriven.Net

    [Update 1st Feb]

    Added issue, found by Daniel, about the test assembly's path containing any spaces.

    [Update 15th Feb]

    Peter Waldschmidt has released NCover 1.5.3 which now installs for all users, so it can be run as non-admin without any workaround.  Added this information to the post.
  • MSBuild and Mono's GAC

    SharpDevelop2 now supports Mono's GAC, so this post takes a low level look into how it uses MSBuild to do this.

    Our starting point is a previous Custom MSBuild Targets for Mono post, which introduced an MSBuild task (Mcs), which was used to compile the code using Mono's Mcs compiler, and an MSBuild targets file (Mono.Mcs.targets).  We will be using the ICSharpCode.Build.Tasks assembly that ships with SharpDevelop2 which contains the custom MSBuild tasks.  We will not be using the SharpDevelop2's target files since these target more than just the Mono framework.

    Mono GAC References

    The original custom MSBuild task and targets file did not support Mono GAC references.  In fact they were the bare minimum required to compile code using Mcs.  The only MSBuild target that was overridden was the CoreCompile target, which was changed to use the Mcs task instead of Microsoft's Csc task.  Without support for Mono's GAC any assembly references needed to have a HintPath

    <Reference Include="gtk-sharp">
       <HintPath>..\..\Program Files\Mono\lib\mono\gtk-sharp\gtk-sharp.dll</HintPath>
    </Reference>

    In order to add support for Mono's GAC we will need to look at the following Microsoft targets and properties:
    • AssemblySearchPaths
    • GetFrameworkPaths
    • ResolveAssemblyReferences
    First let us see if we can get a simple GAC reference to System.Xml to work.  With the old tasks and targets a reference of the form

    <Reference Include="System.Xml"/>
       
    is resolved to "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll".

    To fix this we need to override the GetFrameworkPaths target and set the TargetFrameworkDirectory property  correctly.  This can be done by adding the following to the Mono.Mcs.targets file

    <Target Name="GetFrameworkPaths">
        <!-- Get the path to the target Mono Framework directory. -->
        <GetMonoFrameworkPath TargetFrameworkVersion="Mono v1.1">
            <Output TaskParameter="Path" PropertyName="TargetFrameworkDirectory"/>
            <Output TaskParameter="Path" ItemName="_TargetFrameworkDirectoryItem"/>
        </GetMonoFrameworkPath>

        <!-- Get the path to the target the Mono SDK directory. -->
        <GetMonoFrameworkSDKPath>
            <Output TaskParameter="Path" PropertyName="TargetFrameworkSDKDirectory"/>
            <Output TaskParameter="Path" ItemName="_TargetFrameworkSDKDirectoryItem"/>
        </GetMonoFrameworkSDKPath>
    </Target>

        
    This override must come after the

    <Import Project="$(MSBuildBinPath)\Microsoft.Common.targets" />

    statement, so this should be moved to the beginning of the Mono.Mcs.targets file.  It also uses two custom  tasks that need to be made known to MSBuild

    <UsingTask TaskName="ICSharpCode.Build.Tasks.GetMonoFrameworkPath"
               AssemblyFile="ICSharpCode.Build.Tasks.dll"/>
    <UsingTask TaskName="ICSharpCode.Build.Tasks.GetMonoFrameworkSdkPath"
               AssemblyFile="ICSharpCode.Build.Tasks.dll"/>
     
     
    The GetFrameworkPaths target sets a few more things than just the TargetFrameworkDirectory, this is done for completeness based on what the Microsoft.Common.targets file does, but only the TargetFrameworkDirectory property needs to be set.  The TargetFrameworkDirectory is set by calling the GetMonoFrameworkPath task and passing in the target framework version, which in this case is "Mono v1.1".  This task uses the registry to locate the Mono framework directory (e.g. "C:\Program Files\Mono\lib\mono\1.0").

    AssemblySearchPaths and ResolveAssemblyReference

    Now what about an assembly that only exists in Mono's GAC and cannot be found in Mono framework's directory?  A reference to glib-sharp is a good place to start.

    <Reference Include="glib-sharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f"/>

    We need to modify the AssemblySearchPaths which is being passed to the ResolveAssemblyReference task.  In the Microsoft's target files this is defined as:

    <PropertyGroup>
        <!--
        The SearchPaths property is set to find assemblies in the following order:

            (1) Files from current project - indicated by {CandidateAssemblyFiles}
            (2) $(ReferencePath) - the reference path property, which comes from the .USER file.
            (3) The hintpath from the referenced item itself, indicated by {HintPathFromItem}.
            (4) The directory of MSBuild's "target" runtime from GetFrameworkPath.
                The "target" runtime folder is the folder of the runtime that MSBuild is a part of.
            (5) Registered assembly folders, indicated by {Registry:*,*,*}
            (6) Legacy registered assembly folders, indicated by {AssemblyFolders}
            (7) Look in the application's output folder (like bin\debug)
            (8) Resolve to the GAC.
            (9) Treat the reference's Include as if it were a real file name.
        -->        
        <AssemblySearchPaths Condition=" '$(AssemblySearchPaths)' == '' ">
            {CandidateAssemblyFiles};
            $(ReferencePath);
            {HintPathFromItem};
            {TargetFrameworkDirectory};
            {Registry:$(FrameworkRegistryBase),$(TargetFrameworkVersion),$(AssemblyFoldersSuffix)$(AssemblyFoldersExConditions)};
            {AssemblyFolders};
            {GAC};
            {RawFileName};
            $(OutputPath)
        </AssemblySearchPaths>                       
    </PropertyGroup>

    The {Registry} part is Microsoft specific and can be removed for Mono.  The {GAC} part is also Microsoft specific.  When the ResolveAssemblyReference task encounters this string it knows to look in Microsoft's GAC.  We will replace this with {MonoGAC} which Microsoft's ResolveAssemblyReference task knows nothing about.

    <PropertyGroup>
        <AssemblySearchPaths>
            {CandidateAssemblyFiles};
            $(ReferencePath);
            {HintPathFromItem};
            {TargetFrameworkDirectory};
            {AssemblyFolders};
            {MonoGAC};
            {RawFileName};
            $(OutputPath)
        </AssemblySearchPaths> 
    </PropertyGroup>

    Now we need to somehow replace the {MonoGAC} with a set of directories.  We could write our own ResolveAssemblyReference task, but this seems like a lot of work.  Instead let us update the AssemblySearchPaths just before the ResolveAssemblyReference task is called.  We do this by overriding the ResolveAssemblyReferenceDependsOn target and making it call our custom AddMonoAssemblySearchPaths target.

    <PropertyGroup>
        <ResolveAssemblyReferencesDependsOn>
            GetFrameworkPaths;
            GetRedistLists;
            PrepareForBuild;
            AddMonoAssemblySearchPaths
        </ResolveAssemblyReferencesDependsOn>
    </PropertyGroup>
    <Target Name="AddMonoAssemblySearchPaths">
        <AddMonoAssemblySearchPaths 
            Assemblies="@(Reference)"
            Paths="$(AssemblySearchPaths)">
            <Output TaskParameter="Paths" PropertyName="AssemblySearchPaths"/>
        </AddMonoAssemblySearchPaths>
    </Target>

    Again this custom task needs to be included via

    <UsingTask TaskName="ICSharpCode.Build.Tasks.AddMonoAssemblySearchPaths"
               AssemblyFile="ICSharpCode.Build.Tasks.dll"/>

    The AddMonoAssemblySearchPath task looks for the {MonoGAC} item in the AssemblySearchPaths property and replaces it with any Mono GAC directories that need to be searched.  The GAC directories are determined by looking at the assembly references passed in via the Assemblies property.  If multiple GAC references are located then multiple directories are inserted into the AssemblySearchPaths property.

    Now we can use MSBuild to build a project with references to assemblies in Mono's GAC using SharpDevelop2's ICSharpCode.Build.Tasks assembly and the Mono.Mcs.targets file.

    Mono.Mcs.targets

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">

        <Import Project="$(MSBuildBinPath)\Microsoft.Common.targets" />

        <UsingTask TaskName="ICSharpCode.Build.Tasks.Mcs"
                   AssemblyFile="ICSharpCode.Build.Tasks.dll"/>
         <UsingTask TaskName="ICSharpCode.Build.Tasks.GetMonoFrameworkPath"
                   AssemblyFile="ICSharpCode.Build.Tasks.dll"/>
         <UsingTask TaskName="ICSharpCode.Build.Tasks.GetMonoFrameworkSdkPath"
                   AssemblyFile="ICSharpCode.Build.Tasks.dll"/>
         <UsingTask TaskName="ICSharpCode.Build.Tasks.AddMonoAssemblySearchPaths"
                   AssemblyFile="ICSharpCode.Build.Tasks.dll"/>

       <PropertyGroup>
            <MSBuildAllProjects>$(MSBuildAllProjects);Mono.Mcs.targets</MSBuildAllProjects>
            <DefaultLanguageSourceExtension>.cs</DefaultLanguageSourceExtension>
            <Language>C#</Language>
        </PropertyGroup>
        
        <Target Name="CreateManifestResourceNames"/>

        <PropertyGroup>
            <DebugSymbols Condition=" '$(DebugType)' == 'none' ">false</DebugSymbols>
            <DebugType    Condition=" '$(DebugType)' == 'none' "></DebugType>    
        </PropertyGroup>

        <ItemGroup>
            <DocFileItem Include="$(DocumentationFile)" Condition="'$(DocumentationFile)'!=''">
                <InProject>false</InProject>
            </DocFileItem>
        </ItemGroup>

        <PropertyGroup>
            <CoreCompileDependsOn>_ComputeNonExistentFileProperty</CoreCompileDependsOn>
        </PropertyGroup>
        
       <!-- Override AssemblySearchPaths property and remove Microsoft specific search paths -->
       <PropertyGroup>
            <AssemblySearchPaths>
                {CandidateAssemblyFiles};
                $(ReferencePath);
                {HintPathFromItem};
                {TargetFrameworkDirectory};
                {AssemblyFolders};
                {MonoGAC};
                {RawFileName};
                $(OutputPath)
            </AssemblySearchPaths> 
        </PropertyGroup>
        
        <!-- Modify what the ResolveAssemblyReferences tasks depends on so the
             AssemblySearchPaths can be modified to use the Mono GAC -->
        <PropertyGroup>
            <ResolveAssemblyReferencesDependsOn>
                GetFrameworkPaths;
                GetRedistLists;
                PrepareForBuild;
                AddMonoAssemblySearchPaths
            </ResolveAssemblyReferencesDependsOn>
        </PropertyGroup>
        <Target Name="AddMonoAssemblySearchPaths">
            <AddMonoAssemblySearchPaths 
                Assemblies="@(Reference)"
                Paths="$(AssemblySearchPaths)">
                
                <Output TaskParameter="Paths" PropertyName="AssemblySearchPaths"/>
            </AddMonoAssemblySearchPaths>
        </Target>
        
        <Target    Name="GetFrameworkPaths">
            <!-- Get the path to the target Mono Framework directory. -->
            <GetMonoFrameworkPath TargetFrameworkVersion="Mono v1.1">
                <Output TaskParameter="Path" PropertyName="TargetFrameworkDirectory"/>
                <Output TaskParameter="Path" ItemName="_TargetFrameworkDirectoryItem"/>
            </GetMonoFrameworkPath>
        
            <!-- Get the path to the target the Mono SDK directory. -->
            <GetMonoFrameworkSDKPath>
                <Output TaskParameter="Path" PropertyName="TargetFrameworkSDKDirectory"/>
                <Output TaskParameter="Path" ItemName="_TargetFrameworkSDKDirectoryItem"/>
            </GetMonoFrameworkSDKPath>
        </Target>
        
        <Target
            Name="CoreCompile"
            Inputs="$(MSBuildAllProjects);
                    @(Compile);
                    @(ManifestResourceWithNoCulture);
                    $(ApplicationIcon);
                    $(AssemblyOriginatorKeyFile);
                    @(ManifestNonResxWithNoCultureOnDisk);
                    @(ReferencePath);
                    @(CompiledLicenseFile)"
            Outputs="@(DocFileItem);
                     @(IntermediateAssembly);
                     $(NonExistentFile)"
            DependsOnTargets="$(CoreCompileDependsOn)"
        >
           
            <Mcs
                  AdditionalLibPaths="$(AdditionalLibPaths)"
                  AddModules="@(AddModules)"
                  AllowUnsafeBlocks="$(AllowUnsafeBlocks)"
                  CheckForOverflowUnderflow="$(CheckForOverflowUnderflow)"
                  CodePage="$(CodePage)"
                  DebugType="$(DebugType)"
                  DefineConstants="$(DefineConstants)"
                  DelaySign="$(DelaySign)"
                  DisabledWarnings="$(NoWarn)"
                  DocumentationFile="@(DocFileItem)"
                  EmitDebugInformation="$(DebugSymbols)"
                  KeyContainer="$(KeyContainerName)"
                  KeyFile="$(KeyOriginatorFile)"
                  LangVersion="$(LangVersion)"
                  MainEntryPoint="$(StartupObject)"
                  NoConfig="true"
                  NoLogo="$(NoLogo)"
                  NoStandardLib="$(NoStdLib)"
                  Optimize="$(Optimize)"
                  OutputAssembly="@(IntermediateAssembly)"
                  References="@(ReferencePath)"
                  Resources="@(ManifestResourceWithNoCulture);@(ManifestNonResxWithNoCultureOnDisk);@(CompiledLicenseFile)"
                  ResponseFiles="$(CompilerResponseFile)"
                  Sources="@(Compile)"
                  TargetType="$(OutputType)"
                  ToolPath="$(McsToolPath)"
                  TreatWarningsAsErrors="$(TreatWarningsAsErrors)"
                  WarningLevel="$(WarningLevel)"
                  Win32Icon="$(ApplicationIcon)"
                  Win32Resource="$(Win32Resource)" />
        </Target>
    </Project>

    Posted Jan 11 2006, 09:13 PM by MattWard with no comments
    Filed under: ,
  • SharpDevelop2 and Mono's GAC

    In revision 983 SharpDevelop2 has support for Mono's GAC.  It also includes new Gtk# and Glade# project templates, and a Gtk# Window file template.  Mono can be downloaded from here

    Creating a new Glade# Project
    1. Select the menu option File | New | Solution to open the "New Project" dialog.
    2. Select the C# category.
    3. There are two new projects here, a Gtk# project and a Glade# project.  Select the Glade# project.



    4. Enter a name for the project and select the "Create" button to generate the project.



    The generated project will target Mono 1.1 by default, and will use Mono's Mcs compiler.

    Running the Glade# Application

    If you have the Mono bin directory (e.g. C:\Program Files\Mono\bin) added to your Path environment variable, the project can be started by selecting the menu option Debug | Run or Debug | Run without debugger. This will run the application under the Microsoft .NET framework and not Mono itself, to run under Mono see this post.

    Note that you cannot debug the application when it is built with either of the Mono compilers.

    Adding a Mono GAC Reference
    1. Open the Project Browser via View | Project.
    2. Select the "References" node, right click and select "Add Mono Reference". 



    3. Select the GAC entry that will be added to the project, click the Select button and then click the OK button to add the reference to the project.


    Code Completion


    There is a small problem with code auto-completion.  In order for auto-completion to work the reference must be added via the "Add Mono Reference" dialog.  So the first time a Glade# or Gtk# project has been created you will have to remove the GAC references and then re-add them to get code auto-completion working.  This only needs to be done once, not every time a Gtk# or Glade# project is created.

    Debugging a Mono Application

    You cannot debug a Mono app built using Mcs or Gmcs.  The Mono compilers create their own open source debugging symbols binary (.mdb) which SharpDevelop and Microsoft's DbgCLR do not support.  There is also no Mono debugger available for Windows.  In order to debug a Mono app it needs to be built using the Microsoft Csc compiler.  The general guidelines to be able to debug your Mono app using the SharpDevelop debugger are:
    1. Make sure all Mono specific GAC references have "Local Copy" set to true.  Select the reference in the Project Browser and select Properties to set this.
    2. The Mono bin directory (e.g. C:\Program Files\Mono\bin) must be added to the Path environment variable.
    3. Change the "Target Framework" (Project | Project Options | Compiling) to one of the Microsoft .NET frameworks.
    4. Remove and re-add your Mono GAC references using the "Add Mono Reference" dialog.  You should only need to do this once and only if you are using a project created from the Glade# or Gtk# templates.  Doing this will add a HintPath to the references in your MSBuild project so the Microsoft compilers can locate the assemblies in the Mono GAC.  These HintPaths are not currently added to a newly created Glade/Gtk# project.
    5. Rebuild your application.
    Once the HintPaths have been added to your MSBuild project you should be able to switch between the Microsoft and Mono frameworks as required, and build your code using the Microsoft or Mono compilers.

    If you are going to debug a Gtk# or Glade# application you will need to add extra GAC references.  Mcs and Gmcs do not need all Gtk# references to be added, but Microsoft's Csc compiler does.  For a Gtk# app you will need at least:
    • atk-sharp
    • gdk-sharp
    • glib-sharp
    • gtk-sharp
    • pango-sharp
    Also add glade-sharp if you are developing a Glade# app.

    Links

    Mono Downloads
    Targeting Mono with SharpDevelop2
    SharpDevelop Daily Builds
    Posted Jan 10 2006, 01:43 PM by MattWard with 3 comment(s)
    Filed under:
Powered by Community Server (Commercial Edition), by Telligent Systems
Don't contact us via this (fleischfalle@alphasierrapapa.com) email address.