# Friday, April 22, 2005

Andrew Stopford has agreed to take over the MbUnit and keep it running. This is a very good news for the project. Hopefully, MbUnit can recover a short bug fix cycle and shape up its integration with CruiseControl.NET or Draco.

Good luck Andrew !

 

posted on Friday, April 22, 2005 11:25:00 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [2]
# Friday, April 15, 2005

To whom it may concern:


As many of you probably know, I am now working at Microsoft in the CLR JIT testing team.  As my job load and duties are filling up more and more, I am unable to continue to contribute at a rate that best serves this project.  From this point forward please consider Jamie Cansdale (www.testdriven.net) as the sole owner (work/responsibilities/fun) for the MbUnit project.  I’ll of course be around occasionally.


Cheers,
Peli

posted on Friday, April 15, 2005 9:30:00 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [15]
# Sunday, January 09, 2005

This is a rather funny application of unit testing. Let me state the problem:

We need to write a wrapper for every type that inherits from C. To make sure we don't miss any, we start by writing a combinatorial test that makes sure every type is mapped with a wrapper type*.

[TestFixture]
public class WrapperTest
{
    // get all the types assignable to C, from the assembly SomeAssembly
    [Factory(typeof(Type))]
    public IEnumerable<Type> Types()
    {
        foreach(Type type in SomeAssembly.GetExportedTypes())
        { 
            if (typeof(C).IsAssignableFrom(type))
               yield return type;
        }
    } 

    // tests that all types have a wrapper
    [CombinatorialTest]
    public void EnsureWrapper(
        [UsingFactories("Types")]Type type
    )
    {
        Assert.IsTrue( IsWrapped(type) );
    }
    
    // returns true of the wrapper exists
    private bool IsWrapped(Type type)
    {...}
}

Ok, we can run this test and see a lot of failures. So now we know wich wrapper has to be written.... but wait, why not making the test generate the wrapper as well.

    [CombinatorialTest]
    public void EnsureWrapper(
        [UsingFactories("Types")]Type type
    )
    {
        if (IsWrapped(type))
            return;

        // wrapper not found, 
        // generating the code and dumping it to Console.Out
        ...

        Assert.Fail("Wrapper not found");
    }

Now, we have test cases that give us the fix in case of a failure :)

*: this could be done by a FxCop rule as well since it is pure static analysis.

posted on Sunday, January 09, 2005 4:13:00 PM (Pacific Standard Time, UTC-08:00)  #    Comments [5]
# Saturday, January 08, 2005

Updated Reflector.Framework for Reflector 4.1.27.0.

This version has an automatic *non-intrusive* checking for available updates... based on ProjectDistributor web services :) 

Download it here

posted on Saturday, January 08, 2005 8:44:00 AM (Pacific Standard Time, UTC-08:00)  #    Comments [3]
# Friday, January 07, 2005

ProjectDistributor is a new actor on the scene of inline project managment (thanks for Darren). It provides a simple web site for publishing your little cool applications that have been sleeping on your desktop.

Unlike the big guns like sourceforge, tigris or gotdotnet, ProjectDistributor is simple simple simple. With RSS and webservices, it has the vitual but no more features, anyway who uses all the feature from sf or tigris ?

I've decided to use ProjectDistributor to publish my snippets and other useless samples. Currently, the following applciations are already there:

ps: ProjectDistributor.AutoUpdate is a micro autoupdate framework for your application based on projectdistributor. The next release of the addins with support that .... and have their “Check for Addin update” button :)

posted on Friday, January 07, 2005 5:57:00 AM (Pacific Standard Time, UTC-08:00)  #    Comments [4]
# Monday, January 03, 2005

Mike Gunderloy has posted a nice introductory article on using TD.NET and MbUnit in Visual Studio. Check it out at http://www.developer.com/net/net/article.php/3453121

This article is really about TD.NET as Mike emphasis on one of the zillions of feature of TD.NET. There are a lot of very nice feature in TD.NET, just take a moment to discover them.

Going beyond Mike sample with MbUnit

Mike illustrates the article with a little class that stores an integer and returns information such as IsPositive, etc... In the article, Mike uses the canonical fixture. Let's if we can do better. (disclaimer: my VB is really bad)

RowFixture

Typically, you want to test this for 'interresting' values such 0,-1,1,MaxInt,etc... This example really fits well into the example of the RowFixture:

Imports System
Imports MbUnit.Core.Framework
Imports MbUnit.Framework

<TestFixture()> _
Public Class NumberTests
    ...

    <RowTest()> _
    <Row(5,FALSE)> _
    <Row(8,TRUE)> _
    Public Sub TestIsEven(int value, Boolean isEven)
        ' Make sure even numbers are property identified
        Dim num As New Number
        num.Value = value;
        Assert.AreEqual(isEven, num.IsEven)
    End Sub

End Class

This will create a test case for each RowAttribute. Better readibabilty, better test separation. Now can we do better ?

Combinatorial test

Let's try another approach. We could create a table of 'interresting' number with their properties (is positive, is even). Once this is done, we can feed this table to combinatorial tests.

<TestFixture()> _
Public Class NumberTests

    Public Class NumberRow
        Public int Value
        Public bool IsPositive
        Public bool IsEven

        Public New(int value, bool isPositive, bool isEven)
            Me.Value = value
            Me.IsPositive = IsPositive
            Me.IsEven = isEven
        End New
    End Class

    <Factory()> _    
    Public Function Numbers() as Array Of NumberRow
        Dim numbers As New Array Of NumberRow[4];

        numbers[0] = New NumberRow(-1, False, False)
        numbers[1] = New NumberRow(0, False, True)
        numbers[2] = New NumberRow(1, True, False)
        numbers[3] = New NumberRow(2, True, True)

        Return numbers
    End Function
    
    <CombinatorialTest()> _
    Public Sub TestIsEven(<UsingFactories("NUMBERS")> _ NumberRow row)
        ' Make sure even numbers are property identified
        Dim num As New Number
        num.Value = row.Value;
        Assert.AreEqual(row.IsEven, num.IsEven)
    End Sub

End Class

This will create 4 test cases, one per element of the array returned by Numbers. Moreover, we could reuse Numbers in another test to verify IsPositive.

[Update]
If you plan to reuse the factory, you can extract it into a separate class and pass the facotry type of the UsingFactories parameter.

Public Class NumberRow
    Public int Value
    Public bool IsPositive
    Public bool IsEven

    Public New(int value, bool isPositive, bool isEven)
        Me.Value = value
        Me.IsPositive = IsPositive
        Me.IsEven = isEven
    End New
End Class

Public Class NumberFactory
    <Factory()> _    
    Public Function Numbers() as Array Of NumberRow
        Dim numbers As New Array Of NumberRow[4];

        numbers[0] = New NumberRow(-1, False, False)
        numbers[1] = New NumberRow(0, False, True)
        numbers[2] = New NumberRow(1, True, False)
        numbers[3] = New NumberRow(2, True, True)

        Return numbers
    End Function
End Class

<TestFixture()> _
Public Class NumberTests
    
    <CombinatorialTest()> _
    Public Sub TestIsEven(<UsingFactories(CType(NumberFactory))> _ NumberRow row)
        ' Make sure even numbers are property identified
        Dim num As New Number
        num.Value = row.Value;
        Assert.AreEqual(row.IsEven, num.IsEven)
    End Sub

End Class
posted on Monday, January 03, 2005 10:48:00 PM (Pacific Standard Time, UTC-08:00)  #    Comments [11]

The Reflector addin download have been down for a long time and I apologize for that (vacations).

Released in 3 flavors

There has been a significant refactoring of the addins so stay tuned: the addins are now separated in 3 assemblies: Reflector.CodeMetrics, Reflector.Graph and Reflector.Framework

  • Reflector.CodeMetrics has no dependency on NGraphviz,
  • Reflector.Graph contains graph stuff only, dependency on NGraphviz
  • Reflector.Framework contains CodeMetrics, Graph, and the all the rest. It is compiled against 1.1
  • you should not mix those 3 builds. Pick the one you want but don't mix them!

New project web site

The addins are now hosted on the excellent http://projectdistributor.net/Groups/Group.aspx?groupId=34. Make sure you monitor the rss to be notified for the updates.

posted on Monday, January 03, 2005 5:06:00 PM (Pacific Standard Time, UTC-08:00)  #    Comments [3]
# Thursday, December 16, 2004

MSN Desktop search (or other search engine) is definitely the new must-have tool for developers, specially if you work with a laaaaaaarge code base. The title is self-explaining:

  •  install MSN Desktop search,
  • set up the indexing to point the source code folders
  • wait a few hours for indexing depending on the size of your project
  • pick any class or method and write it in the search bar...

The results are just lovely! It is easy to browse, relevant and fast! 

posted on Thursday, December 16, 2004 1:24:00 PM (Pacific Standard Time, UTC-08:00)  #    Comments [9]
# Thursday, December 09, 2004

I have finally deciced to merge MbUnit.Core.dll into MbUnit.Framework.dll. This will be a minor breaking change in the next release because you will have to remove the reference to MbUnit.Core.dll.

All the attributes also have moved to MbUnit.Framework, so you don't need to use MbUnit.Core.Framework. I did this change mainly to simplify the morphing NUnitForms or NUnitAsp to MbUnit: a Replace All in Files will be sufficient to move them to MbUnit.

 

posted on Thursday, December 09, 2004 9:30:00 PM (Pacific Standard Time, UTC-08:00)  #    Comments [12]
# Sunday, December 05, 2004

Reflector.CodeMetrics dynamically loads code metrics tabs at runtime by using Reflection. This means that anybody can write his own metric whithout recompiling CodeMetrics.... an Addin API inside another AddIn api :)

Here's a quick review of the steps to get your new cool code metric started. In this example, we will display the number of arguments per method (simple example).

Setting up the project

Start a new assembly project, add references to Reflector.exe, Reflector.CodeMetrics.dll and Reflector.Helpers.dll

Creating a new CodeMetric

All code metrics implement the ICodeMetric interface (this what the CodeMetricManager looks for):

public interface ICodeMetric : IServiceComponent

{

string Name { get;}

string FullName { get;}

ComputationState State { get;}

DataTable Result { get;}

Object FindItem(DataRow row);

bool Enabled { get;set;}

CodeMetricLevel Level { get;}

event ComputationProgressEventHandler Progress;

void Compute();

void Abort();

}

Of course, you don't have to implement it from scratch. You can use the abstract base class CodeMetricBase to get started:

public class ArgumentCountCodeMetric : CodeMetricBase
{
    // setting up name, full name and level of metric
    public ArgumentCountCodeMetric()
    :base("AC","Argument Count",CodeMetricLevel.Method)
    {}

    // adding columns
    protected void AddColumns()
    {
       this.AddColumn("Argument Count");
    }

    ...
}

It is important to note that we specify that the metrics target Method (can also target Type, Module). The AddColumns method is used to all the columns you want to show in the grid.

Add the metric computation

Time to get to the real fun, computing the metric... First, we provide a method GetTestCount that returns the number of steps. This number is used to scale the progress bar:

private int GetStepCount()
{
    int count = 0;
    foreach (IAssembly assembly in this.Services.AssemblyLoader.Assemblies)
    {
       if (this.Services.CodeMetricManager.IsIgnored(assembly))
           continue;
       foreach (IModule module in assembly.Modules)
            count += module.Types.Count;    
    }
    return count;
}

It is important to note that we check for each IAssembly if it has to be ignored. This is directly linked to the checkbox control on the CodeMetrics page. Note that we coun the types only, if you show progress on methods, almost all the CPU time is spent to update the controls. Next, we write the method that create a row in the grid from a IMethodDeclaration:

private void ComputeMethod(IMethodDeclaration method)
{
    IMethodBody body = method as IMethodBody;
    if (body == null)
        return;
    this.AddRow(
        method,
        body.Parameters.Count 
        );
}

The AddRow methods will add a row in the underlying DataTable, etc...

Implementing Compute

We have all the pieces, we just need to implement Compute:

public override void Compute()
{
    base.Compute();
    int stepCount = GetStepCount();
    int count = 0;
    foreach (IAssembly assembly in this.Services.AssemblyLoader.Assemblies)
    {
        if (this.Services.CodeMetricManager.IsIgnored(assembly))
            continue;
        foreach (IModule module in assembly.Modules)
        {
            foreach (ITypeDeclaration type in module.Types)
            {
                this.OnProgress(
                    new ComputationProgressEventArgs(
                        count++,
                        stepCount,
                        "Analysing {0}",
                        type
                        )
                     );
                foreach (IMethodDeclaration method in type.Methods)
                {
                    if (this.CheckForAbort())
                        return;
                    ComputeMethod(method);
                }
            }
        }
    }
    this.Finished();
}

The metric is ready, we just need to register it to the manager when the Addin is loaded.

Setting up a package and registering CodeMetrics

Reflector looks for IPackage class when loading Addins. Reflector.Helpers contains a smart implementation of that addin that will automatically load the code metrics the assembly contains, so all you have to do is to inherit from PackagePage (well almost):

namespace Reflector.CodeMetrics
{
    public sealed class CodeMetricPackage : PackageBase
    {
        [ReflectorWindow(
        Name = "Dummy control to make Reflector happy",
        Caption = "Dummy control to make Reflector happy"
        )]
       [ReflectorCommandBar(CommandBarTarget.Tools)]
       private UserControl dummy = new UserControl();
    }
}

If you do not actually load a new window, Reflector gets pissed and unload the addin. Therefore, just add a dummy window. At this point, recompile (makes sure you target the right .Net framework), load and enjoy.

Debugging

To debug, just start Reflector as external process and break point on your code.

posted on Sunday, December 05, 2004 2:48:00 PM (Pacific Standard Time, UTC-08:00)  #    Comments [11]