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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="https://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

    Private m_Clock As String

    Private m_nTestPass As Integer

    Friend StopWatch As Diagnostics.Stopwatch

    Public Property Clock() As String

        Get

            Return m_Clock

        End Get

        Set(ByVal value As String)

            If m_Clock <> value Then

                m_Clock = value

                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Clock"))

            End If

        End Set

    End Property

    Public Property IsTestRunning() As Boolean

        Get

            Return m_IsTestRunning

        End Get

        Set(ByVal value As Boolean)

            m_IsTestRunning = value

            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("IsTestRunning"))

            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("NotIsTestRunning"))

        End Set

    End Property

    Public ReadOnly Property NotIsTestRunning() As Boolean

        Get

            Return Not m_IsTestRunning

        End Get

    End Property

    Public Property StatusText() As String

        Get

            Return m_StatusText

        End Get

        Set(ByVal value As String)

            Dim strTitle = "TestHost "

            If IntPtr.Size > 4 Then

                strTitle += "amd64 64 bit"

            Else

                strTitle += "x86 32 bit"

            End If

            strTitle += " Total Tests = " + TestMethods.Count.ToString

            If m_nTestPass > 0 Then

                strTitle += " Test Pass #" + m_nTestPass.ToString + " PeakWorkingSet =" + Process.GetCurrentProcess.PeakWorkingSet64.ToString("n0")

            End If

      m_StatusText = strTitle + " " + value

            Me.Title = m_StatusText ' in case of process crash, we can still see window title

            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("StatusText"))

        End Set

    End Property

    Public ReadOnly Property TestMethods() As ReadOnlyCollection(Of TestItem)

        Get

            Return m_TestMethods

        End Get

    End Property

    Sub New(ByVal TestMethods As ReadOnlyCollection(Of TestItem))

        InitializeComponent()

        m_TestMethods = TestMethods

        StatusText = "" ' init window title

        Me.ProgressBar1.Visibility = Windows.Visibility.Hidden

        Me.ProgressBar1.Minimum = 0

        Me.ProgressBar1.Maximum = 100

        Me.ProgressBar1.Value = 0

        Me.DataContext = Me ' so XAML can databind to our members

        AddHandler Me.Closed, AddressOf OnWindowClosed

        Me.AddHandler(System.Windows.Controls.GridViewColumnHeader.ClickEvent, New RoutedEventHandler(AddressOf OnHeaderClicked))

    End Sub

    Sub OnWindowClosed(ByVal sender As Object, ByVal e As EventArgs)

        TestItem.ShutDown() ' abort thread

    End Sub

    Private Sub btnClicked(ByVal sender As System.Windows.Controls.Button, ByVal e As RoutedEventArgs)

        Debug.WriteLine((New StackTrace).GetFrame(0).GetMethod.Name + " " + sender.Name)

        Select Case CStr(sender.Name)

            Case "btnSelectAll"

                For Each lstItem In ListView1.Items

                    Dim tstItem = CType(lstItem, TestItem)

                    If Not tstItem.Selected Then

                        tstItem.Selected = True

                    End If

                Next

            Case "btnInvertSelection"

                For Each lstItem In ListView1.Items

                    Dim tstItem = CType(lstItem, TestItem)

                    tstItem.Selected = Not tstItem.Selected

                Next

            Case "btnRunSelected"

                m_SelectedTests.Clear()

                m_nTestPass = 1

                For Each lstItem In ListView1.Items ' Get the user selected items in display order

                    Dim tstItem = CType(lstItem, TestItem)

                    tstItem.Reset() ' reset all to not run yet (doesn't reset checkbox)

                    If tstItem.Selected Then

                        m_SelectedTests.Add(tstItem)

                        tstItem.Status = TestItem.TstStatus.Pending

                    End If

                Next

                If Me.chkLoopForever.IsChecked Then

                    Do While True

                        DoRunSelected()

                        For Each lstItem In ListView1.Items

                            Dim tstItem = CType(lstItem, TestItem)

                            If tstItem.Status = TestItem.TstStatus.Aborted OrElse tstItem.Status = TestItem.TstStatus.Failed Then

                                Me.StatusText = String.Format(" Failed {0}", DateTime.Now.ToString)

                                Exit Do

                            End If

                            tstItem.Reset()

                        Next

                        m_nTestPass += 1

                    Loop

                Else

                    DoRunSelected()

                End If

            Case "btnAbortRun"

                TestItem.ShutDown()

   For Each tstItem In m_SelectedTests

                    If tstItem.Status = TestItem.TstStatus.Pending Then

                        tstItem.Status = TestItem.TstStatus.NotRun

                    End If

                Next

            Case "btnQuit"

                TestItem.ShutDown()

                Me.Close()

            Case Else

                Debug.Assert(False, "Unhandled button")

        End Select

    End Sub

    Private Sub DoRunSelected()

        IsTestRunning = True

        Dim nTestsRun = 0

        Dim nTestsFailed = 0

        StopWatch = Diagnostics.Stopwatch.StartNew

        Me.StatusText = String.Format("{0} Test run ({1}/{2}) ", DateTime.Now.ToString, nTestsRun, m_SelectedTests.Count)

        Try

            For Each lstItem In ListView1.Items

                Dim tstItem = CType(lstItem, TestItem)

                If tstItem.Selected Then

                    Me.ProgressBar1.Visibility = Windows.Visibility.Visible

                    tstItem.RunTest(Me) ' actually run the test

                    Me.ProgressBar1.Visibility = Windows.Visibility.Hidden

                    nTestsRun += 1

                    If tstItem.Status = TestItem.TstStatus.Failed Then

                        nTestsFailed += 1

                    End If

                    Me.StatusText = String.Format("{0} Test run {1}/{2} #Failed = {3}", DateTime.Now.ToString, nTestsRun, m_SelectedTests.Count, nTestsFailed)

                    If tstItem.Status = TestItem.TstStatus.Aborted Then

                        Exit For

                    End If

                End If

            Next

        Catch ex As Exception

        End Try

        Me.StatusText = String.Format(" Done: {0:f2} secs", StopWatch.ElapsedMilliseconds / 1000)

        IsTestRunning = False

    End Sub

    Private Sub ListBox1_KeyUp(ByVal sender As Object, ByVal args As System.Windows.Input.KeyEventArgs) Handles ListView1.KeyUp

        If args.Key = Key.Space Then

            Dim lb As System.Windows.Controls.ListBox = CType(sender, System.Windows.Controls.ListBox)

            If lb.SelectedIndex >= 0 Then

                Dim lbi = CType(lb.ItemContainerGenerator.ContainerFromIndex(lb.SelectedIndex), System.Windows.Controls.ListBoxItem)

                If lbi IsNot Nothing Then

                    Dim tstItem = CType(lbi.Content, TestItem)

                    tstItem.Selected = Not tstItem.Selected

                End If

            End If

        End If

    End Sub

    'see https://blogs.msdn.com/calvin\_hsia/archive/2007/11/29/6600915.aspx

    Dim m_LastDir As System.ComponentModel.ListSortDirection = System.ComponentModel.ListSortDirection.Ascending

    Dim m_LastHeaderClicked As System.Windows.Controls.GridViewColumnHeader = Nothing

    Private Sub OnHeaderClicked(ByVal sender As Object, ByVal e As RoutedEventArgs)

        If e.OriginalSource.GetType Is GetType(System.Windows.Controls.GridViewColumnHeader) Then

            Dim direction = System.ComponentModel.ListSortDirection.Ascending

            Dim gvHdr = CType(e.OriginalSource, System.Windows.Controls.GridViewColumnHeader)

            If gvHdr IsNot Nothing AndAlso gvHdr.Column IsNot Nothing Then

                Dim hdr As String

                hdr = CStr(gvHdr.Column.Header)

                If gvHdr Is m_LastHeaderClicked Then

                    If m_LastDir = System.ComponentModel.ListSortDirection.Ascending Then

                        direction = System.ComponentModel.ListSortDirection.Descending

                    End If

                End If

   Sort(hdr, direction)

                m_LastHeaderClicked = gvHdr

                m_LastDir = direction

            End If

        End If

    End Sub

    Private Sub Sort(ByVal sortby As String, ByVal dir As System.ComponentModel.ListSortDirection)

        Me.ListView1.Items.SortDescriptions.Clear()

        Dim sd = New System.ComponentModel.SortDescription(sortby, dir)

        Me.ListView1.Items.SortDescriptions.Add(sd)

        Me.ListView1.Items.Refresh()

    End Sub

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) _

        Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

End Class

</Window1.XAML.vb code to paste >

<MyTestHost.vb code to paste >

Imports System.Reflection

Imports System.Windows

Imports System.Collections.ObjectModel

Imports System.Windows.Controls

Imports System.ComponentModel

Imports Microsoft.VisualStudio.TestTools.UnitTesting

Public Class MyTestHost

    Private Shared m_TestMethods As ReadOnlyCollection(Of TestItem)

    Shared Sub Main()

        m_TestMethods = GetTestMethods()

        If System.Environment.GetCommandLineArgs.Count > 1 Then ' arg 0 is the fullpath name of the app

            Dim testname = System.Environment.GetCommandLineArgs(1)

        Else

            ' show UI

            Dim wpfApplicationWindow As System.Windows.Window = New Window1(m_TestMethods)

            wpfApplicationWindow.ShowDialog()

        End If

    End Sub

    Public Shared Function GetTestMethods() As ReadOnlyCollection(Of TestItem) ' can be called from other assemblies

        Dim retval As New Collection(Of TestItem)

        Dim MyAsm = Assembly.GetExecutingAssembly

        Dim TestClasses = From typ In MyAsm.GetTypes Where typ.GetCustomAttributes(GetType(TestClassAttribute), True).Length > 0

        For Each TestClassName In TestClasses

            Debug.WriteLine("Test class " + TestClassName.Name)

            Dim TestClassInstance = MyAsm.CreateInstance(TestClassName.FullName)

            Dim TestMethods = From meth In TestClassInstance.GetType.GetMethods _

                        Where meth.GetCustomAttributes(GetType(TestMethodAttribute), True).Length > 0 _

                            AndAlso Not meth.GetCustomAttributes(GetType(IgnoreAttribute), True).Length > 0

            Debug.WriteLine("Count=" + TestMethods.Count.ToString)

            ' you can define your own TestContext class

            'Dim tstContext = New VBTest64TestContext

            'Dim TestContext_Property As PropertyInfo = TestClassInstance.GetType.GetProperty("TestContext")

            'If TestContext_Property IsNot Nothing Then

            ' TestContext_Property.SetValue(TestClassInstance, tstContext, BindingFlags.SetProperty, Nothing, Nothing, Nothing)

            'End If

            Dim TestInitialize_method As MethodInfo = TestClassInstance.GetType.GetMethod("TestInitialize")

            Dim TestCleanup_method As MethodInfo = TestClassInstance.GetType.GetMethod("TestCleanup")

            For Each meth In TestMethods ' In From dd In meths Where dd.Name.StartsWith("PepperQu")

                Debug.WriteLine(meth.Name)

                Dim TestDesc = ""

                Dim TestOwner = ""

                Dim TestPriority = ""

                Dim Tmp = meth.GetCustomAttributes(GetType(Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute), True)

                If Tmp.Length > 0 Then

                    TestDesc = CType(Tmp(0), Microsoft.VisualStudio.TestTools.UnitTesting.DescriptionAttribute).Description

                End If

                Tmp = meth.GetCustomAttributes(GetType(OwnerAttribute), True)

                If Tmp.Length > 0 Then

                    TestOwner = CType(Tmp(0), OwnerAttribute).Owner

                End If

  Tmp = meth.GetCustomAttributes(GetType(PriorityAttribute), True)

                If Tmp.Length > 0 Then

                    TestPriority = CType(Tmp(0), PriorityAttribute).Priority.ToString

                End If

                retval.Add(New TestItem(meth.Name, TestClassName.FullName, TestDesc, _

                                            TestOwner, TestPriority, meth, _

                                            TestClassInstance, TestInitialize_method, TestCleanup_method))

            Next

        Next

        Return New ReadOnlyCollection(Of TestItem)(retval)

    End Function

End Class

Public Class TestItem

    Implements INotifyPropertyChanged

    Enum TstStatus

        Idle

        Pending

        Running

        Passed

        Aborted

        NotRun

        Failed

    End Enum

    Private m_TestName As String

    Private m_TestClass As String

    Private m_Description As String

    Private m_TestOwner As String

    Private m_TestPriority As String

    Private m_Selected As Boolean = True ' default to checkbox selected

    Private m_TestStatus As TstStatus = TstStatus.Idle

    Private m_TestInitialize_method As MethodInfo

    Private m_TestCleanup_method As MethodInfo

    Private m_TestMethodInfo As MethodInfo ' the actual test to run

    Private m_TestClassInstance As Object

    Private m_ElapsedTime As String

    Private m_ErrorMessage As String

    Private m_Stopwatch As Diagnostics.Stopwatch

    Private Shared m_bgThread As System.Threading.Thread

    Private Shared m_threadRunning As Boolean

    Delegate Sub delThreadDone()

    Private m_ThreadDoneEvent As System.Threading.ManualResetEvent

    Sub New(ByVal TestName As String, ByVal TestClass As String, ByVal testDesc As String, _

            ByVal TestOwner As String, ByVal TestPriority As String, _

            ByVal meth As MethodInfo, ByVal TestClassInstance As Object, _

            ByVal TestInitialize_method As MethodInfo, ByVal TestCleanup_method As MethodInfo)

        m_TestName = TestName

        m_TestClass = TestClass

        m_Description = testDesc

        m_TestOwner = TestOwner

        m_TestPriority = TestPriority

        m_TestMethodInfo = meth

        m_TestInitialize_method = TestInitialize_method

        m_TestCleanup_method = TestCleanup_method

  m_TestClassInstance = TestClassInstance

    End Sub

    Shared Sub ShutDown()

        If m_bgThread IsNot Nothing Then

            m_bgThread.Abort()

            Do While m_threadRunning

                System.Windows.Forms.Application.DoEvents()

            Loop

        End If

    End Sub

    Sub BgThreadProc(ByVal disp As Object)

        m_Stopwatch = Stopwatch.StartNew

        Dim stat As TstStatus = TstStatus.Running

        Status = TstStatus.Running

        Dim deldone As New delThreadDone(AddressOf BgThreadDone)

        Dim fDidRunCleanup = False

        Debug.WriteLine(TestName)

        Try

            If m_TestInitialize_method IsNot Nothing Then

                m_TestInitialize_method.Invoke(m_TestClassInstance, BindingFlags.InvokeMethod, Nothing, Nothing, Nothing)

            End If

            ' run the test

            m_TestMethodInfo.Invoke(m_TestClassInstance, Reflection.BindingFlags.InvokeMethod, Nothing, Nothing, Nothing)

            If m_TestCleanup_method IsNot Nothing Then

  m_TestCleanup_method.Invoke(m_TestClassInstance, BindingFlags.InvokeMethod, Nothing, Nothing, Nothing)

                fDidRunCleanup = True

            End If

            stat = TstStatus.Passed

        Catch ex As Exception 'When ex.GetType IsNot GetType(System.Threading.ThreadAbortException)

            If ex.GetType Is GetType(System.Threading.ThreadAbortException) Then

                If Not fDidRunCleanup Then

                    If m_TestCleanup_method IsNot Nothing Then

                        m_TestCleanup_method.Invoke(m_TestClassInstance, BindingFlags.InvokeMethod, Nothing, Nothing, Nothing)

                        fDidRunCleanup = True

                    End If

                End If

                Status = TstStatus.Aborted

                ErrorMessage = "User aborted test run"

                CType(disp, System.Windows.Threading.Dispatcher).Invoke(Threading.DispatcherPriority.Background, deldone)

                Throw ex

            End If

            Debug.WriteLine(String.Format("Got exception {0} {1}", m_TestMethodInfo.Name, ex.InnerException.Message))

            ErrorMessage = ex.InnerException.Message

            stat = TstStatus.Failed

        End Try

        Me.Status = stat

       mSecs = m_Stopwatch.ElapsedMilliseconds.ToString

        m_Stopwatch.Stop()

        Debug.WriteLine(TestName + " Done")

        CType(disp, System.Windows.Threading.Dispatcher).Invoke(Threading.DispatcherPriority.Background, deldone)

    End Sub

    Sub BgThreadDone() ' runs on main thread

        m_bgThread = Nothing

        m_threadRunning = False

    End Sub

    Friend Sub RunTest(ByVal mWindow As Window1)

        m_ThreadDoneEvent = New System.Threading.ManualResetEvent(False)

        m_bgThread = New System.Threading.Thread(AddressOf BgThreadProc) ' cant' use BackgroundWorker because need STA

        m_bgThread.Name = "MyTestHost runtests"

        m_bgThread.SetApartmentState(System.Threading.ApartmentState.STA)

        m_threadRunning = True

     m_bgThread.Start(System.Windows.Threading.Dispatcher.CurrentDispatcher)

        Do While m_threadRunning

            If mWindow IsNot Nothing Then

                Dim pbar As ProgressBar = mWindow.ProgressBar1

                pbar.Value = DateTime.Now.Millisecond / 10

                mWindow.Clock = (mWindow.StopWatch.ElapsedMilliseconds / 1000).ToString("f1")

            End If

            System.Windows.Forms.Application.DoEvents()

        Loop

    End Sub

  Public Sub Reset()

        mSecs = ""

        ErrorMessage = ""

        Status = TstStatus.Idle

    End Sub

    Public Property ErrorMessage() As String

        Get

            Return m_ErrorMessage

        End Get

        Set(ByVal value As String)

    m_ErrorMessage = value

            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("ErrorMessage"))

        End Set

    End Property

    Public Property mSecs() As String

        Get

            Return m_ElapsedTime

        End Get

        Set(ByVal value As String)

            m_ElapsedTime = value

            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("mSecs"))

        End Set

    End Property

    Public Property Status() As TstStatus

        Get

            Return m_TestStatus

        End Get

        Set(ByVal value As TstStatus)

            If value <> m_TestStatus Then

                m_TestStatus = value

                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Status"))

                RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("StatusColor"))

            End If

        End Set

    End Property

    Public ReadOnly Property StatusColor() As String

        Get

            Select Case m_TestStatus

                Case TstStatus.Failed

                    Return "Red"

                Case TstStatus.Idle

                    Return "LightBlue"

                Case TstStatus.Pending

                    Return "Purple"

                Case TstStatus.Aborted

                Return "Red"

                Case TstStatus.Passed

                    Return "Green"

                Case TstStatus.NotRun

                    Return "Black"

                Case Else

                    Return "Blue"

            End Select

        End Get

    End Property

    Public ReadOnly Property TestName() As String

        Get

            Return m_TestName

        End Get

    End Property

    Public ReadOnly Property TestClass() As String

        Get

            Return m_TestClass

        End Get

    End Property

    Public ReadOnly Property Description() As String

        Get

            Return m_Description

        End Get

    End Property

    Public ReadOnly Property Owner() As String

        Get

            Return m_TestOwner

        End Get

    End Property

    Public ReadOnly Property Pri() As String

        Get

            Return m_TestPriority

        End Get

    End Property

    Public Property Selected() As Boolean

        Get

            Return m_Selected

        End Get

        Set(ByVal value As Boolean)

            m_Selected = value

            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Selected"))

        End Set

    End Property

    Public Overrides Function ToString() As String

        Return String.Format("{0,-20} {1}", m_TestName.PadRight(20), m_TestClass)

    End Function

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged

End Class

</MyTestHost.vb code to paste >

 

 

 

End of blog entry