March 2010 - Posts

Running Fitnesse.NET tests using MSTest - Revisited
Mon, Mar 29 2010 16:35

In 2008 I wrote a post Running Fitnesse.NET tests using MSTest.  Recently I had need to us this technique on a VS2010 project, and as is so often the issue the code that worked then does not seem to work now. All I can assume is that the Fitnesse API had altered, but I thought I was using the same assemblies!

So I pulled down the code from http://sourceforge.net/projects/fitnessedotnet/ and had a poke about. Basically I seemed to be using the command line switches wrong. The listing below shows what I found is the working usage:

[TestMethod]
    public void WorkFlowSwitchOnName_DataViaFitnesse_Success()
    {
        fit.Runner.FolderRunner runner = new fit.Runner.FolderRunner(new fit.Runner.ConsoleReporter());
        var errorCount = runner.Run(new string[] {
                "-i",@"WF Workflow", // the directory that holds the HTM test files
                "-a",@"TestProject.dll",  //we have the fit fascard in this assembly
                "-o",@"results"}); // the directory the results are dumped into as HTML
        // fit can fail silently giving no failures as no test are run, so check for exceptions
        Assert.AreEqual(false, Regex.IsMatch(runner.Results, "^0.+?0.+?0.+?0.+?$"), "No tests appear to have been run");
        // look for expected errors
        Assert.AreEqual(0, errorCount, runner.Results);
        
    }

The –i option is the one that was wrong. I had been specifying a single HTM file. What I should have done was specify a directory that contains one or more HTM files. I handled this as a sub directory in my project with its contents set to copy to the output directory.

Once I made these edits I had my tests running again as expect.

And finally my personal domains
Mon, Mar 29 2010 9:59

When I said will our internet bits were back I was not quite right. I had forgotten that I had not re-pointed m own richardfennell domains to put to my blog. These are now done, so I hope all search results to my blog should resolve OK.

Mixed mode assembly is built against version 'v2.0.50727' error using .NET 4 Development Web Server
Sat, Mar 27 2010 7:38

If your application has a dependency on an assembly built in .NET 2 you will see the error below if you try to run your application when it has been built in.NET 4.

Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.

This can be important in VS2010 testing, as test projects must be built as .NET 4, there is no option to build with an older runtime. I suffered this problem when trying to do some development where I hosted a webpart that make calls into SharePoint (that was faked out with Typemock Isolator) inside a ASP.NET 4.0 test harness

The answer to this problem is well documented, you need to add the useLegacyV2RuntimeActivationPolicy attribute to a .CONFIG file, but which one? It is not the web.config file you might suspect, but the C:\Program Files (x86)\Common Files\microsoft shared\DevServer\10.0\WebDev.WebServer40.exe.config file. The revised config file should read as follows (new bits in red)

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup useLegacyV2RuntimeActivationPolicy="true">
            <supportedRuntime version="v4.0" />   
  </startup>

  <runtime>
    <generatePublisherEvidence enabled="false" />
  </runtime>
</configuration>

Note: Don’t add the following <supportedRuntime version="v2.0.50727" />  this cause the web server to crash on start-up.

TFS 2010 Database Label not use for Team Project Collection DBs
Thu, Mar 25 2010 12:54

Found out something I did not know today, the TFS 2010 database label is only used for the server’s own primary configuration databases not for the DBs for the TPC it creates.

For example on a 2010 RC install with the database label was set to RC during the installation. When I try to create a new TPC (called ABC) it tries to create a Db named TFS_ABC, even though the database label for TFS server can be seen to TFS_RC_  on the admin console and the instance’s primary databases are called TFS_RC_Configuration, TFS_RC_Warehouse and TFS_RC_Analysis.

image

I would have expect the new db to be call TFS_RC_ABC.

This can become interesting if like me you are working with a number of TFS instances that all share the same central SQL server instance. So if you want a TPC of the same name on two or more TFS servers that share a DT you must manually create the empty DBs to avoid a name clash.

So not a major issue but confusing if you don’t know it is a problem.

 

Announcing Typemock TMockRunner Custom Activity for Team Build 2010
Thu, Mar 25 2010 12:18

My Team Build 2010 custom activity for Typemock Isolator is now available at http://site.typemock.com/add-ons/.

It is packaged as a zip which include all the source and a compiled assembly as well a document detailing usage and why the solution is constructed as it is.

Hope you find it useful

A fix to run Typemock Isolator inside the Page_Load for an ASPX page on VS2010/.NET4
Thu, Mar 25 2010 10:23

I recently tried to use Typemock Isolator inside a ASP.NET page load event, to fake out some SharePoint SPLists e.g.

protected void Page_Load(object sender, EventArgs e)
{
     SPSite fakeSite = Isolate.Fake.Instance<SPSite>();
     ……..
}

This has worked in the past but using VS2010RC and Isolator 6.0.1.0 it failed.

  • If running VS2010 as Admin I got the error "Could not load file or assembly 'TypeMock, Version=0.12900.25133.13153, Culture=neutral, PublicKeyToken=37342d316331342d' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)":"TypeMock, Version=0.12900.25133.13153, Culture=neutral, PublicKeyToken=37342d316331342d"}"
  • If running as a non admin user I got the error "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

Typemock Support told me that I needed to edit the Typemock Isolator’s namespaces.dat file (in its programs directory), this is the file that lists valid test runners. I needed to add WebDev.WebServer40.exe to this file as this is program that makes the call to Isolator to do the faking.

So this is an immediate fix, it is unclear at the moment if this filename WebDev.WebServer40.exe will be the final one for the RTM of 2010, so be prepared for another edit in the future.

Internet problems should be over
Thu, Mar 25 2010 2:10

Our new Internet line has now been commissioned fully and hopefully any problems you may have had accessing my blog via the blackmarble.com or co.uk domains should be over.

TF254024 error when upgrading TFS 2010 Beta2 to RC
Wed, Mar 24 2010 9:04

Whilst upgrading a single server instance of TFS 2010 Beta2 to the RC I got a TF254024 error. This occurred at the point in the upgrade wizard where it tries to list the databases available to upgrade.

The reason for the error was the account I was logged in as (the domain administrator in my case) did not have rights on the SQL instance to see any TFS DBs. Once this was sorted, by granting owner rights to all the TFS DBs, all was OK.

I think it had got into this state as this was a ‘standard’ TFS install using an automatically installed SQLExpress instance; so rights had not been explicitly assigned during the TFS setup. By installing SQL 2008 Management Studio Express and logging in as the servers local administrator I was able to grant the rights needed.

Speaking at NDC in Oslo in June
Mon, Mar 22 2010 4:55

I have just heard that I had two sessions accepted for the Norwegian Developers Conference. They are Putting Some Testing Into Your TFS Build and Developing Testable Web Parts for SharePoint.

So off to Oslo, never been there before, looking forward to it already

clip_image002

VHD boot and c00002e2 Errors
Thu, Mar 18 2010 14:58

For some reason that is beyond me now I did not setup my Lab Manager test system to be a VHD boot. So before installing the 2010 RC version I decided to P2V this system (on the same hardware) to make backups easier whilst testing. All seemed to go well

  1. I used IMAGEX to create a WIM of the disk
  2. Created an empty VHD
  3. Used IMAGEX to apply the WIM to the VHD
  4. Formatted the PC with a default Windows 7 install
  5. Added a VHD boot Windows Server 2008R2 to the PC, tested this all booted OK
  6. Replaced the test VHD with my own and rebooted

…. and it just went into a reboot cycle. Pressing F8 and stopping the reboot on error I saw I had a “c00002e2 Directory Services could not start” error. I managed to get into the PC by pressing F8 and using the AD recovery mode (safe mode did not work). After much fiddling around I eventually noticed that my boot drive was drive D: not C: as I would have expected. My VHD and parent drive had reversed letter assignments. So when the AD services tried to start they look on the parent Windows 7 partition (C:) for their data and hence failed.

I think the root cause was the way I had attached the empty VHD to used IMAGEX. I had not done it using WINPE, but just created in my Windows 7 instance and attached the VHD as drive D: before copying on the WIM

So my revised method was

  1. I used IMAGEX to create a WIM of the disk (actually used the one I already had as there was nothing wrong with it, which was a good job as I had formatted the disk)
  2. Formatted the PC with a default Windows 7 install
  3. Added a VHD boot Windows Server 2008R2 to the PC, tested this all booted OK
  4. Copied my WIM file to the same directory as my newly created W2k8R2.VHD
  5. Copied IMAGEX to this directory
  6. Booted of a Win7 DVD
  7. Pressed Shift F10 to get a prompt at the first opportunity
    1. Ran DISKPART
    2. Select Disk 1
    3. Select Part 1
    4. Detail Part – this was the 100Mb system partition Windows 7 creates and was assigned as drive C: (note when you boots Windows 7 the drive letters get reassigned just to confuse you, as to look at this you would expect your Windows 7 boot drive to be D:)
    5. Assign Letter = Q – this set the system partition to be drive Q, but any unused letter would do
    6. Select vdisk file:d:\vhd\w2k8r2.vhd
    7. attach vdisk – this loaded the VHD and assigned it the letter C: as this was now not in use
    8. list disk
    9. Select disk 2
    10. Select Part 1
    11. detail Part – checked the drive letter was correct
    12. I then exited DISKPART and from the same command prompt ran IMAGEX to put the WIM on this new drive C:
  8. Rebooted and it worked

So the technical tip is make sure your drive letter assignments are what you think they are, it may not be as obvious as you expect.

NEBytes last night
Thu, Mar 18 2010 3:24

Hope everyone enjoyed my session VS2010 at NEBytes last night. I don't now about you but I think that quick end to end demo of build, manual test and Intellitrace debug work very nicely. That was the first time I have done it as a single demo and I think it works better than three smaller ones. Truely shows the intergrated team store for VS2010

Anyway as the session was demo lead there are no slides to download, but if you have follow up questions post a comment on this post or email me 

 

Speaking at NEBytes tomorrow
Tue, Mar 16 2010 5:43

Just a reminder I am speaking at NEBytes in Newcastle tomorrow on the new features of VS2010. I will be covering project management, architect, dev and test features so something there for everyone.

Cannot access TFS over HTTPS after upgrade from 2010 Beta 2 to RC
Mon, Mar 15 2010 8:13

I upgraded our Beta2 2010 TFS last week, and after a quick local test all appeared to be working OK, so I rushed out the office like you do. However, whilst I have been out the office it was spotted that though it was working within the office using HTTP and the NETBIOS\Domain server name (TFSSERVER) it could not be accessed outside the firewall or over HTTPS inside the office, so https://tfsserver.mydomain.com:8443/tfs did not work

Turns out the problem was twofold, both it seems caused by the TFS in place upgrade process.

IIS7 Bindings

The 2010 upgrade configuration wizard appeared to removed the non Port 8080 bindings on the IIS7 TFS site instance. I had to re-add the binding on 8443 that we use to access the TFS web services (this did not happen with the related SharePoint web site, this was still happily on ports 80 and 443)

image

One this was re-added I could access the server from the console on port 8443 using the Url https://tfsserver.mydomain.com:8443/tfs, however other clients still could not access it

Windows Firewall with Advanced Security

One the Firewall I noticed that though the rule to allow port 8443 was there it’s profile was set to only public/private (can’t remember how it used to be set).

image image

Once I added domain (or set it to any) I was able to access the upgraded TFS server from other clients using the Url https://tfsserver.mydomain.com:8443/tfs

Post QCon thoughts
Sat, Mar 13 2010 6:10

Interesting time at QCon yesterday,  shame I was only there one day, I do like the events that are not limited to a single vendor or technology. The multi presenter session I was involved in on Microsoft interoperability seemed to go well, there is talk of repeating at other events or podcasting. It is a nice format if you can get the sub-sessions linking nicely, like themed grok talks. 

Due to chatting to people (but that why you go really isn't it?), I only managed to get to one other session, but I was the one I wanted to see, Roy Osherove’s on using CThru to enable testing of monolithic frameworks such as Silverlight. It got a few things clearer in my mind over using CThu, a tool I have tried to use in the past but not had as much success as I hoped. So I think I will have another go at trying to build a SharePoint workflow testing framework, the problem has rested on the back burner too long. I think I just need to persist longer in digging to the eventing model to see why my workflows under test do not start. Roy’s comment that there is no short cut for this type of problem to avoid an archaeological excavation into the framework under test, I think is the key here.

Speaking at SharePoint Evolution Conference
Tue, Mar 9 2010 14:35

I am please to be able to announce that I am speaking at the SharePoint 2010 Evolution Conference in London on the 21st of April.

I will be talking about my experiences during the Prontaprint project developing testable components for SharePoint using TypeMock Isolator and other techniques to speed development and improve quality.

 

Lessons learnt building a custom activity to run Typemock Isolator in VS2010 Team Build
Mon, Mar 8 2010 3:46

I have previously posted on how you can run Typemock Isolator based tests within a VS2010 using the InvokeMethod activity. After this post Gian Maria Ricci, a fellow Team System MVP suggested it would be better to put this functionality in a custom code activity, and provided the basis of the solution. I have taken this base sample and worked it up to be a functional activity, and boy have I learnt a few things doing it.

Getting the custom activity into a team build

Coding up a custom Team Build activity is not easy, there are a good few posts on the subject (Jim Lamb’s is a good place to start). The problem is not writing the code but getting the activity into the VS toolbox. All documentation gives basically the same complex manual process, there is no way of avoiding it. Hopefully this will be addressed in a future release of Visual Studio. But for now the basic process is this:

  1. Create a Class Library project in your language of choice
  2. Code up your activity inheriting it from the CodeActivity<T> class
  3. Branch the build workflow, that you wish to use for testing, into the folder of the class library project
  4. Add the build workflow’s .XAML file to the class library project then set it’s properties: “build action” to none and “copy to output directory” to do not copy
  5. Open the .XAML file (in VS2010), the new activity should appear in the toolbox, it can be dropped onto the workflow. Set the properties required.
  6. Check in the file .XAML file
  7. Merge the .XAML file to the original location, if you get conflicts simply tell merge to use the new version discarding the original version, so effectively overwriting the original version with the version edited in the project.
  8. Check in the merged original .XAML file that now contains the modifications.
  9. Take the .DLL containing the new activity and place it in a folder under source control (usually under the BuildProcessTemplates folder)
  10. Set the Build Controller’s custom assemblies path to point to this folder (so your custom activity can be loaded) 

    image
  11. Run the build and all should be fine

But of course is wasn’t. I kept getting the error when I ran a build

TF215097: An error occurred while initializing a build for build definition \Typemock Test\BuildTest Branch: Cannot create unknown type '{clr-namespace:TypemockBuildActivity}ExternalTestRunner'.

This was because I had not followed the procedure correctly. I had tried to be clever. Instead of step 6 and onwards I had had an idea. I created a new build that referenced the branched copy of the .XAML file in the class library project directly. I thought this would save me a good deal of tedious merging while I was debugged my process. It did do this but introduced other issues

The problem was when I inspected the .XAML in my trusty copy of Notepad, I saw that there was no namespace declared for my assembly (as the TF21509 error suggested). If I looked at the actual activity call in the file it was declared as <local:ExternalTestRunner  …… />, the local: replacing the namespace reference I would expect. This is obviously down to the way I was editing the .XAML file in the VS2010.

The fix is easy, using Notepad I added a namespace declaration to the Activity block

<Activity ……    xmlns:t="clr-namespace:TypemockBuildActivity;assembly=TypemockBuildActivity" >

and then edited the references from local: to t: (the alias for my namespace) for any classes called from the custom assembly e.g.

<t:ExternalTestRunner ResultsFileRoot="{x:Null}" BuildNumber="[BuildDetail.Uri.ToString()]" Flavor="[platformConfiguration.Configuration]" sap:VirtualizedContainerService.HintSize="200,22" MsTestExecutable="C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe" Platform="[platformConfiguration.Platform]" ProjectCollection="http://typhoon:8080/tfs/DefaultCollection" Result="[ExternalTestRunnerResult]" ResultsFile="ExternalTestRunner.Trx" SearchPathRoot="[outputDirectory]" TeamProjectName="[BuildDetail.TeamProject]" TestAssemblyNames="[testAssemblies.ToArray()]" TestRunnerExecutable="C:\Program Files (x86)\Typemock\Isolator\6.0\TMockRunner.exe" TestSettings="[localTestSettings]" />

Once this was done I could use my custom activity in a Team Build, though I had to make this manual edit every time I edited the branched .XAML file in VS2010 IDE. So I had swapped repeated merges with repeated editing, you take your view as to which is worst.

So what is in my Typemock external test runner custom activity?

The activity is basically the same as the one suggest by Gian Maria, it takes all the same parameters as the MSTest team build activity and then executes the TMockRunner to wrapper MSTest. What I have done is add a couple of parameters that were missing in the original sample and also added some more error traps and logging.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.IO;
using System.Diagnostics;
using Microsoft.TeamFoundation.Build.Workflow.Activities;
using Microsoft.TeamFoundation.Build.Client;
using System.Text.RegularExpressions;
 
namespace TypemockBuildActivity
{
    public enum ExternalTestRunnerReturnCode { Unknown =0 , NotRun, Passed, Failed };
 
    [BuildExtension(HostEnvironmentOption.Agent)]
    [BuildActivity(HostEnvironmentOption.All)]
    public sealed class ExternalTestRunner : CodeActivity<ExternalTestRunnerReturnCode>
    {
        // Define an activity input argument of type string  
 
        /// <summary>
        /// The name of the wrapper application, usually tmockrunner.exe
        /// </summary>
        public InArgument<string> TestRunnerExecutable { get; set; }
        
        /// <summary>
        /// The name of the application that actually runs the test, defaults to MSTest.exe if not set
        /// </summary>
        public InArgument<string> MsTestExecutable { get; set; }
 
        /// <summary>
        /// The project collection to publish to e.g. http://tfs2010:8080/tfs/DefaultCollection
        /// </summary>
        public InArgument<string> ProjectCollection { get; set; }
 
        /// <summary>
        /// The build ID to to publish to e.g. vstfs:///Build/Build/91
        /// </summary>
        public InArgument<string> BuildNumber { get; set; }
 
        /// <summary>
        /// The project name to publish to e.g: "Typemock Test"
        /// </summary>
        public InArgument<string> TeamProjectName { get; set; }
 
        /// <summary>
        /// The platform name to publish to e.g. Any CPU
        /// </summary>
        public InArgument<string> Platform { get; set; }
 
        /// <summary>
        /// The flavour (configuration) to publish to e.g. "Debug"
        /// </summary>
        public InArgument<string> Flavor { get; set; }
 
 
        /// <summary>
        /// Array of assembly names to test
        /// </summary>
        public InArgument<string[]> TestAssemblyNames { get; set; }
        
        /// <summary>
        /// Where to search for assemblies under test
        /// </summary>
        public InArgument<string> SearchPathRoot { get; set; }
        
        /// <summary>
        /// A single name result file
        /// </summary>
        public InArgument<string> ResultsFile { get; set; }
 
        /// <summary>
        /// A directory to store results in (tends not be used if the ResultFile is set)
        /// </summary>
        public InArgument<string> ResultsFileRoot { get; set; }
 
        /// <summary>
        /// The file that list as to how test should be run
        /// </summary>
        public InArgument<string> TestSettings { get; set; }
 
 
        // If your activity returns a value, derive from CodeActivity<TResult> 
        // and return the value from the Execute method. 
        protected override ExternalTestRunnerReturnCode Execute(CodeActivityContext context)
        {
            String msTestOutput = string.Empty;
            ExternalTestRunnerReturnCode exitMessage = ExternalTestRunnerReturnCode.NotRun;
 
            if (CheckFileExists(TestRunnerExecutable.Get(context)) == false)
            {
                LogError(context, string.Format("TestRunner not found {0}", TestRunnerExecutable.Get(context)));
            }
            else
            {
                String mstest = MsTestExecutable.Get(context);
                if (CheckFileExists(mstest) == false)
                {
                    mstest = GetDefaultMsTestPath();
                }
 
                String testrunner = TestRunnerExecutable.Get(context);
 
                var arguments = new StringBuilder();
                arguments.Append(string.Format("\"{0}\"", mstest));
                arguments.Append(" /nologo ");
 
                // the files to test
                foreach (string name in TestAssemblyNames.Get(context))
                {
                    arguments.Append(AddParameterIfNotNull("testcontainer", name));
                }
 
                // settings about what to test
                arguments.Append(AddParameterIfNotNull("searchpathroot", SearchPathRoot.Get(context)));
                arguments.Append(AddParameterIfNotNull("testSettings", TestSettings.Get(context)));
                
                // now the publish bits
                if (string.IsNullOrEmpty(ProjectCollection.Get(context)) == false)
                {
                    arguments.Append(AddParameterIfNotNull("publish", ProjectCollection.Get(context)));
                    arguments.Append(AddParameterIfNotNull("publishbuild", BuildNumber.Get(context)));
                    arguments.Append(AddParameterIfNotNull("teamproject", TeamProjectName.Get(context)));
                    arguments.Append(AddParameterIfNotNull("platform", Platform.Get(context)));
                    arguments.Append(AddParameterIfNotNull("flavor", Flavor.Get(context)));
                }
 
                // where do the results go, tend to use one of these not both
                arguments.Append(AddParameterIfNotNull("resultsfile", ResultsFile.Get(context)));
                arguments.Append(AddParameterIfNotNull("resultsfileroot", ResultsFileRoot.Get(context)));
 
                LogMessage(context, string.Format("Call Mstest With Wrapper [{0}] and arguments [{1}]", testrunner, arguments.ToString()), BuildMessageImportance.Normal);
 
                using (System.Diagnostics.Process process = new System.Diagnostics.Process())
                {
                    process.StartInfo.FileName = testrunner;
                    process.StartInfo.WorkingDirectory = SearchPathRoot.Get(context);
                    process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
                    process.StartInfo.UseShellExecute = false;
                    process.StartInfo.ErrorDialog = false;
                    process.StartInfo.CreateNoWindow = true;
                    process.StartInfo.RedirectStandardOutput = true;
                    process.StartInfo.Arguments = arguments.ToString();
                    try
                    {
                        process.Start();
                        msTestOutput = process.StandardOutput.ReadToEnd();
                        process.WaitForExit();
                        // for TypemockRunner and MSTest this is alway seems to be 1 so does not help tell if test passed or not
                        //  In general you can detect test failures by simply checking whether mstest.exe returned 0 or not.  
                        // I say in general because there is a known bug where on certain OSes mstest.exe sometimes returns 128 whether 
                        // successful or not, so mstest.exe 10.0 added a new command-line option /usestderr which causes it to write 
                        // something to standard error on failure.
 
                        // If (error data received)
                        //    FAIL
                        // Else If (exit code != 0 AND exit code != 128)
                        //    FAIL
                        // Else If (exit code == 128)
                        //    Write Warning about weird error code, but SUCCEED
                        // Else
                        //   SUCCEED
 
                        ///int exitCode = process.ExitCode;
                        LogMessage(context, string.Format("Output of ExternalTestRunner: {0}", msTestOutput), BuildMessageImportance.High);
                    }
                    catch (InvalidOperationException ex)
                    {
                        LogError(context, "ExternalTestRunner InvalidOperationException :" + ex.Message);
                    }
 
                    exitMessage = ParseResultsForSummary(msTestOutput);
                }
            }
            LogMessage(context, string.Format("ExternaTestRunner exiting with message [{0}]", exitMessage), BuildMessageImportance.High);
            return exitMessage;
        }
 
        /// <summary>
        /// Adds a parameter to the MSTest line, it has been extracted to allow us to do a isEmpty chekc in one place
        /// </summary>
        /// <param name="parameterName">The name of the parameter</param>
        /// <param name="value">The string value</param>
        /// <returns>If the value is present a formated block is return</returns>
        private static string AddParameterIfNotNull(string parameterName, string value)
        {
            var returnValue = string.Empty;
            if (string.IsNullOrEmpty(value) == false)
            {
                returnValue = string.Format(" /{0}:\"{1}\"", parameterName, value);
            }
            return returnValue;
       }
 
        /// <summary>
        /// A handler to check the results for the success or failure message
        /// This is a rough way to do it, but is more reliable than the MSTest exit codes
        /// It returns a string as opposed to an  exit code so that it 
        /// Note this will not work of the /usestderr flag is used
        /// </summary>
        /// <param name="output">The output from the test run</param>
        /// <returns>A single line summary</returns>
        private static ExternalTestRunnerReturnCode ParseResultsForSummary(String output)
        {
            ExternalTestRunnerReturnCode exitMessage = ExternalTestRunnerReturnCode.NotRun;
            if (Regex.IsMatch(output, "Test Run Failed"))
            {
                exitMessage = ExternalTestRunnerReturnCode.Failed;
            }
            else if (Regex.IsMatch(output, "Test Run Completed"))
            {
                exitMessage = ExternalTestRunnerReturnCode.Passed;
            }
            else
            {
                exitMessage = ExternalTestRunnerReturnCode.Unknown;
            }
 
            return exitMessage;
        }
 
        /// <summary>
        /// Handles finding MSTest, checking both the 32 and 64 bit paths
        /// </summary>
        /// <returns></returns>
        private static string GetDefaultMsTestPath()
        {
            String mstest = @"C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\mstest.exe";
            if (CheckFileExists(mstest) == false)
            {
                mstest = @"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe";
                if (CheckFileExists(mstest) == false)
                {
                    throw new System.IO.FileNotFoundException("MsTest file cannot be found");
                }
            }
            return mstest;
        }
 
        /// <summary>
        /// Helper method so we log in both the VS Build and Debugger modes
        /// </summary>
        /// <param name="context">The workflow context</param>
        /// <param name="message">Our message</param>
        /// <param name="logLevel">Team build importance level</param>
        private static void LogMessage(CodeActivityContext context, string message, BuildMessageImportance logLevel)
        {
            TrackingExtensions.TrackBuildMessage(context, message, logLevel);
            Debug.WriteLine(message);
        }
 
        /// <summary>
        /// Helper method so we log in both the VS Build and Debugger modes
        /// </summary>
        /// <param name="context">The workflow context</param>
        /// <param name="message">Our message</param>
        private static void LogError(CodeActivityContext context, string message)
        {
            TrackingExtensions.TrackBuildError(context, message);
            Debug.WriteLine(message);
        }
 
        /// <summary>
        /// Helper to check a file name to make sure it not null and that the file it name is present
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private static bool CheckFileExists(string fileName)
        {
            return !((string.IsNullOrEmpty(fileName) == true) || (File.Exists(fileName) == false));
        }
    }
}

This activity does need a good bit of configuring to use it in a real build. However, as said previously, the options it takes are basically those needed for the MSTest activity, so you just replace the existing calls to the MSTest activities as shown in the graph below should be enough.

image

Note: The version of the ExternalTestRunner activity in this post does not handle tests based on Metadata parameters (blue box above), but should be OK for all other usages (it is just that these parameters have not been wired through yet). The red box show the new activity in place (this is the path taken if the tests are controlled by a test setting file) and the green box contains an MSTest activity waiting to be swapped out (this is the path taken if no test setting or metadata files are provided).

The parameters on the activity in the red box are as follows, as said before they are basically the same as parameters for the standard MSTest activity.

image

The Result parameter (the Execute() method return value) does need to be associated with a variable declared in the workflow, in my case ExternalTestRunnerResult. This is defined at the sequence scope, the scope it is defined at must be such that it can be read by any other steps in the workflow that require the value. It is declared as being of the enum type ExternalTestRunnerReturnCode defined in the custom activity.

image

Further on in the workflow you need to edit the if statement that branches on whether the tests passed or not to use this ExtenalTestRunnerResult value

image

Once all this is done you should have all your MSTests running inside a Typemock’ed wrapper and all the results should be shown correctly in the build summary

image

And the log of the build should show you all the parameters that got passed through to the MSTest program.

image

Is there a better way to test a custom activity project?

Whilst sorting out the logic for the custom activity I did not want to have to go through the whole process of running the team build to test the activity, it just took too long. To speed this process I did the following

  1. In my solution I created a new Console Workflow project
  2. I referenced my custom activity project from this new workflow project
  3. I added my custom activity as the only item in my workflow
  4. For each parameter of the custom activity I created a matching argument for the workflow and wired the two together.

    image
  5. I then created a Test Project that referenced the workflow project and custom activity project.
  6. In this I could write unit tests (well more integration tests really) that exercise many of the options in the custom activity. To help in this process I created some simple Test Projects assemblies that contained just passing tests, just failing test and a mixture of both.
  7. A sample test is shown below
    [TestMethod]
    public void RunTestWithTwoNamedAssembly_OnePassingOneFailingTestsNoPublishNoMstestSpecified_FailMessage()
    {
     
        // make sure we have no results file, MSTest fails if the file is present
        File.Delete(Directory.GetCurrentDirectory() + @"\TestResult.trx");
     
        var wf = new Workflow1();
     
        Dictionary<string, object> wfParams = new Dictionary<string, object>
        {
            { "BuildNumber", string.Empty },
            { "Flavour", "Debug" },
            { "MsTestExecutable", string.Empty },
            { "Platform", "Any CPU" },
            { "ProjectCollection",string.Empty },
            { "TeamProjectName", string.Empty },
            { "TestAssemblyNames", new string[] { 
                Directory.GetCurrentDirectory() + @"\TestProjectWithPassingTest.dll",
                Directory.GetCurrentDirectory() + @"\TestProjectWithfailingTest.dll"
            }},
            { "TestRunnerExecutable", @"C:\Program Files (x86)\Typemock\Isolator\6.0\TMockRunner.exe" },
            { "ResultsFile", "TestResult.trx" }
        };
     
     
        var results = WorkflowInvoker.Invoke(wf, wfParams);
     
        Assert.AreEqual(TypemockBuildActivity.ExternalTestRunnerReturnCode.Failed, results["ResultSummary"]);
    }
  8. The only real limit here is that some of the options (the publishing ones) need a TFS server to be tested. You have to make a choice as to whether this type of publishing test is worth the effort of filling your local TFS server with test runs  from the test project or whether you want to test these features manually in a real build environment, especially give the issues I mention in my past post

Summary

So I have a working implementation of a custom activity that makes it easy to run Typemock based tests without losing any of the other features of a Team Build. Butt as I learnt getting around the deployment issues can be a real pain.

Getting sick of seeing the Vodafone error “HTTP Error 403: The service you requested is restricted”
Mon, Mar 8 2010 3:26

Of late I keep getting ‘HTTP Error 403: The service you requested is restricted’ when I try to use my HTC Diamond2 mobile phone on Vodafone. I see the problem whether browsing the internet with mobile IE or Opera and also when using the phone as a 3G modem  from my Windows 7 laptop. Seems to happen every other day of late.

image

Interestingly, when I got the error shown above whilst trying to browse to Bing, a connection, on the same 3G link, from my PC to our Exchange server was working fine.

This error seems to be a reoccurring problem judging from posts on the various Vodafone forums. In most cases the problem just seems to go away if you wait long enough, which is my experience. I can find nothing I can do or set on the phone that makes a difference. My guess is a proxy error in the Vodafone cloud. They really need to get it sorted out.

I have been a happy Vodafone customer for years, since I first got a mobile phone in the early 90s, but this is happening too often and it is seriously making me think of moving. Mobile internet is becoming too important to be to have it as such an unreliable service.

The Teamprise Eclipse plug in for TFS gets a new name
Mon, Mar 8 2010 3:22

As I am sure you remember a few months ago Microsoft bought Teamprise and their Java clients for TFS. Well the team has got out their first Microsoft branded release, details can be found on Martin Woodward’s and Brian Harry’s blogs. This beta provides the first support for TFS2010

This release is very timely as I will be talking on the Java integration via the Eclipse plug-in at QCON next week and at the Architect Insight Conference at the end of the month. This  “Eaglestone” release means I can hopefully do my demos against TFS2010.