Welcome to MSDN Blogs Sign in | Join | Help

Create your own Test Host using XAML to run your unit tests

A few days ago, somebody came into my office and plopped down a box. It seemed very light. He said that it was a new PC. I thought hmmm…. The box seems empty…Why am I getting a new PC?.


Apparently an inventory was made and my current hardware was at the lower end of the list.

 

So I started up the Dell Optiplex 755 with 4 gigs of RAM, and it was running Vista 64. Super: I hadn’t done much with 64 bit code yet.

 

Sure enough, debugging 64 bit native applications shows that the CPU has 64 bit wide registers (and more registers!). If I attach to a 64 bit process, the Debug->Windows->Registers looks like:

 

RAX = 0000000000000000 RBX = 000000001DEB7818 RCX = 000000000F99A027

RDX = 0000000000000000 RSI = 0000000000000001 RDI = 00000000242E5454

R8  = 000000001FD3FCB8 R9  = 0000000000000000 R10 = 0000000000000000

R11 = 0000000000000206 R12 = 0000000000000000 R13 = 0000000000000000

R14 = 0000000000000000 R15 = 0000000000000000 RIP = 000007FEF0FD3D29

RSP = 000000001FD3FCF0 RBP = 0000000000000000 EFL = 00000246

 

If I attach to a 32 bit process:

 

EAX = 00000000 EBX = 006AC138 ECX = 79E74400 EDX = 00000000 ESI = 00DF6700

EDI = 00000000 EIP = 59B80265 ESP = 001DDEE8 EBP = 001DDEE8 EFL = 00000206

 

001DDEFC = 06CD35C8

 

 

(The debugger is pretty amazing!)

 

The sheer size of the registers means instead of a maximum 2^32 =4 gig virtual address space, we have 2^64

To calculate that, type this in the Fox command window:

?LOG10(2^64)

?2^64

Which shows 19.27, 1.844674E+19

 

That means 19.27 zeros: 18,000,000,000,000,000,000 which is 18 billion billion or 18 exabytes!

 

Coincidentally, we have some new 64 bit code that was recently created by “patching” the 32 bit version, and I wanted some test tools for it.

 

I have a VS Test Project with many (32 bit) tests. (Use Visual Studio Test framework to create tests for your code)

 

So I created my own Test Host in a day:

 

This Test Host:

·         Uses the existing 32 bit test source code as linked files: if you make changes to the tests, they will automatically be updated in both 32 and 64 bit versions

·         Can be built as 32 bit or 64 bit (Project->Properties->Compile->Advanced Compile Options->Target CPU->AnyCPU or x86), so it can run tests in either 32 or 64 bits.  When set as AnyCPU and running on 64 bit OS like Vista 64, then it will run as 64 bit.

·         Does not require test deployment: you can test in place. VSTestHost requires deployment.

·         Uses reflection on itself to get and run the TestInitialize, TestCleanup and TestMethods.

·         Can run selected tests in a loop for stress tests, like leak detection.

·         Uses XAML and Data Binding (see Create your own media browser: Display your pictures, music, movies in a XAML tooltip)

o   Notice how the enabling and disabling of buttons is done via data binding.

o   The data binding updates the status (Success,Failure/Pending) and its color in the ListView.

o   Shows the test methods in a ListView with click/sort column headers.

·         The tests run on a worker thread. The UI is thus responsive and not blocked.

·         A progress bar and stopwatch also show

·         Can be run without UI: another assembly can call it to run tests

·         Can determine if it’s running 64 or 32:  IntPtr.Size=8 bytes on 64, 4 bytes on 32.

 

 

I’m thinking about adding a new 32 bit test case (for running in the VS IDE) that will launch all the 64 bit tests, or a new one for each of the 32 bit test cases, but that’s lower priority.

 

This new test host has already helped me find several 64 bit issues.

 

Follow my prior post: Use Visual Studio Test framework to create tests for your code to create a project.  (If you don’t have Fox or Excel, you can either get them or remove the Fox/Excel code in the test)

 

If you add these lines to the test

        System.Diagnostics.Debug.WriteLine("Sizeof IntPtr = " + IntPtr.Size.ToString)

        System.Diagnostics.Debug.WriteLine(System.Diagnostics.Process.GetCurrentProcess.MainModule.FileName)

 

You’ll get the output:

Sizeof IntPtr = 4

C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\vstesthost.exe

 

Nothing unexpected. VSTestHost is a 32 bit EXE, which means only 32 bit code is run.

 

 

To make the test host more interesting, add a few more tests to the bottom of UnitTests1:

    Shared oRandom As New Random

 

    <TestMethod()> _

    Sub EmptyTest()

 

    End Sub

 

    <TestMethod()> _

    Sub AlwaysFail()

        Assert.IsTrue(False, "True is never false!")

    End Sub

    <TestMethod()> _

    Sub SometimesFail()

        If oRandom.NextDouble < 0.4 Then

            Assert.IsTrue(False, "True is never false!")

        End If

    End Sub

    <TestMethod()> _

    Sub RarelyFails()

        If oRandom.NextDouble < 0.02 Then

            Assert.IsTrue(False, "True is rarely false!")

        End If

    End Sub

 

 

 

 

 

To create the Test Host:

 

Right Click on the Solution from above in Solution explorer, choose Add->New Project. This time choose Windows->WPF Application. Put in a folder next to the folder of the existing test project. I called mine MyTestHost

 

Right Click on MyTestHost in solution explorer, choose Set As Startup Project. That way, hitting F5 will run this new project.

Delete the Window1.XAML in the solution explorer. We’ll create our own.

 

Delete MyTestHost->Application.xaml  (we have our own Sub Main)

 

Right Click on MyTestHost in solution explorer, choose Add New Item, Code->Class. Name it MyTestHost.vb

 

Right Click on MyTestHost in solution explorer, choose Add Existing Item, navigate to the UnitTest1.vb file in the other project. Make sure you add as a Lnked File. The “Add” button on the dialog has a little down arrow that allows you to choose Add As Link.

 

Note: the Fox code in the unit test TestMethod1 will fail in 64 because there is no 64 bit version of it.

 

Add references to:

Microsoft.VisualStudio.QualityTools.UnitTestFramework

System.Windows.Forms

 

There are 3 sections of code to paste below: Window1.XAML, Window1.XAML.VB (the code behind file), and MyTestHost.vb

 

Hit F5, click on the button  to run the tests.

You can force the Testmethod1 to fail simply by forcing Excel to close before it’s done.

 

 

See also:

Remove double spaces from pasted code samples in blog

 

 

 

<Window1.XAML code to paste>

<Window x:Class="Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

       

    Title="Window1" Height="711" Width="1043">

    <Grid DataContext="{Binding}">

 

        <TextBlock Height="21" HorizontalAlignment="Right" Margin="0,12,16,0" Name="tbClock" VerticalAlignment="Top" Width="81" Text="{Binding Path=Clock}"/>

        <ListView Margin="22,49,54,90" Name="ListView1" ItemsSource="{Binding Path=TestMethods}" >

            <ListView.View>

            <GridView >

               

            <GridViewColumn>Selected

                <GridViewColumn.CellTemplate>

                    <DataTemplate>

                        <CheckBox Name="ChkBox" IsChecked="{Binding Path=Selected}"></CheckBox>

                    </DataTemplate>

                </GridViewColumn.CellTemplate>

            </GridViewColumn>

            <GridViewColumn>Status

                <GridViewColumn.CellTemplate>

                    <DataTemplate>

                        <TextBlock Width="50" Text="{Binding Path=Status}"  Foreground="{Binding Path=StatusColor}"></TextBlock>

                    </DataTemplate>

                </GridViewColumn.CellTemplate>

            </GridViewColumn>

            <GridViewColumn>TestName

                <GridViewColumn.CellTemplate>

                    <DataTemplate>

                        <TextBlock Text="{Binding Path=TestName}"></TextBlock>

                    </DataTemplate>

                </GridViewColumn.CellTemplate>

            </GridViewColumn>

            <GridViewColumn>Description

                <GridViewColumn.CellTemplate>

                    <DataTemplate>

                        <TextBlock Text="{Binding Path=Description}"></TextBlock>

                    </DataTemplate>

                </GridViewColumn.CellTemplate>

            </GridViewColumn>

            <GridViewColumn>TestClass

                <GridViewColumn.CellTemplate>

                    <DataTemplate>

                        <TextBlock Text="{Binding Path=TestClass}"></TextBlock>

                    </DataTemplate>

                </GridViewColumn.CellTemplate>

            </GridViewColumn>

            <GridViewColumn>mSecs

                <GridViewColumn.CellTemplate>

                    <DataTemplate>

                        <TextBlock Text="{Binding Path=mSecs}" Width="40" HorizontalAlignment="Right"></TextBlock>

                    </DataTemplate>

                </GridViewColumn.CellTemplate>

            </GridViewColumn>

            <GridViewColumn>ErrorMessage

                <GridViewColumn.CellTemplate>

                    <DataTemplate>

                        <TextBlock Text="{Binding Path=ErrorMessage}" ToolTip="{Binding Path=ErrorMessage}"></TextBlock>

                    </DataTemplate>

                </GridViewColumn.CellTemplate>

            </GridViewColumn>

            <GridViewColumn>Owner

                <GridViewColumn.CellTemplate>

                    <DataTemplate>

                        <TextBlock Text="{Binding Path=Owner}"></TextBlock>

                    </DataTemplate>

                </GridViewColumn.CellTemplate>

            </GridViewColumn>

            <GridViewColumn>Pri

                <GridViewColumn.CellTemplate>

                    <DataTemplate>

                        <TextBlock Text="{Binding Path=Pri}"></TextBlock>

                    </DataTemplate>

                </GridViewColumn.CellTemplate>

            </GridViewColumn>

               

            </GridView>

            </ListView.View>

 

        </ListView>

        <Button Height="23" HorizontalAlignment="Left" Margin="22,0,0,35"

                Name="btnSelectAll" VerticalAlignment="Bottom" Width="85" ButtonBase.Click="btnClicked">Select_All</Button>

        <Button Height="23" HorizontalAlignment="Left" Margin="117,0,0,35"

                Name="btnInvertSelection" VerticalAlignment="Bottom" Width="85" ButtonBase.Click="btnClicked">_InvertSelection</Button>

        <CheckBox Height="16" HorizontalAlignment="Left" Margin="215,0,0,65"

                  Name="chkLoopForever" VerticalAlignment="Bottom" Width="120" IsEnabled="{Binding Path=NotIsTestRunning}">_LoopForever</CheckBox>

        <Button Height="23" HorizontalAlignment="Left" Margin="215,0,0,35"

                Name="btnRunSelected" VerticalAlignment="Bottom" ButtonBase.Click="btnClicked"  Width="85" IsEnabled="{Binding Path=NotIsTestRunning}">_RunSelected</Button>

        <Button Height="23" HorizontalAlignment="Left" Margin="315,0,0,35"

                Name="btnAbortRun" VerticalAlignment="Bottom" ButtonBase.Click="btnClicked" IsEnabled="{Binding Path=IsTestRunning}" Width="56.82">_AbortRun</Button>

        <Button Height="23" HorizontalAlignment="Right" Margin="0,0,54,35"

                Name="btnQuit" VerticalAlignment="Bottom" Width="85" ButtonBase.Click="btnClicked" IsCancel="True">_Quit</Button>

        <ProgressBar Height="14" HorizontalAlignment="Right" Margin="0,0,16,0"

                Name="ProgressBar1" VerticalAlignment="Top" Width="123"  />

 

    </Grid>

</Window>

 

</Window1.XAML code to paste >

 

 

<Window1.XAML.vb code to paste >

Imports System.ComponentModel

Imports System.Collections.ObjectModel

 

Class Window1

    Implements INotifyPropertyChanged   ' so XAML UI updates

 

    Private m_TestMethods As ReadOnlyCollection(Of TestItem)

    Private m_SelectedTests As New Collection(Of TestItem)

    Private m_StatusText As String

    Private m_IsTestRunning As Boolean = False