But it works on my PC!

The random thoughts of Richard Fennell on technology and software development

Fixed numerous issues with VS2008 using a /resetskippkgs

I am doing some work on VS2008 at present and I when I started my VS2008, which I had not used for a while, I was plagued by errors along the lines of "The operation could not be completed". These occurred when running major features such as:

  • Loading LINQ to SQL Designer
  • Running the SQL 2008 Project Wizard

The fix turned out to be resetting the package skip loading flag. It seems a number of add-ins were not being loaded on startup. This command is run from the "Visual Studio 2008 Command Prompt" by typing

devenv /resetskippkgs

I also had problems trying to run tests using the standard Microsoft Test tools within Visual Studio. I got the error “Exception has been thrown by the target of an invocation”, but running tests using TestDriven.NET worked fine. This problem was not fixed with the /resetskippkgs. However, turns out it is a known issues if VS2008 SP1 and TFS, KB980216. The quick fix is to make sure I was connected to a TFS server. Once this was done the test could be run. There is a hotfix, but I can live without I think for now.

So the moral is, even if you don’t use an IDE everyday you can still break it with all the patching you do around it for other IDEs and underlying frameworks. A reset to defaults can often by just the kick it needs to get it working.

A day of new releases and announcements in Visual Studio 2010 land

Today we have seen the release of the Visual Studio 2010 SP1 and the TFS-Project Server Integration Feature Pack. Both are available on the MSDN download site.

As well was the new downloads they have announced a change to licensing over Load Test Agent. This gives Visual Studio Ultimate with MSDN users the ability to do unlimited load testing. No longer do you need to purchase Load Test Packs, thus making load testing an option for more teams.

For more details see Brian Harry’s blog post on the new downloads and load testing

What to do with project dependencies?

Many development teams hit the problem that they have dependencies on libraries that they do not want to have as part of their solutions. If these dependencies are open source projects then there are options using technologies like NuGet or OpenWrap. However, in many cases the dependency is to an internal project, such as the company standard logging library, which it is never going to put up into a centralised repository. So normally you end up with either:

  1. Adding the project for the shared assembly to the solution and rebuilding with the solution, probably via some branching model in source control to allow fixes to be merged between projects.
  2. Adding the assembly from a known location (maybe under source control)  that the team responsible for then shared library publish the current version to.

Both solutions can work, but they both have their pros and cons.

A different and interesting approach has been proposed in Sven Hubert’s post “Extended Dependency Management with Team Foundation Server” on http://www.TFSBlog.de. He suggests using a custom MSBuild task and .Targets files that allows you to cause an external project to be rebuild from source control (addressing option 1 without the need to add the actual VS project to the solutions) or to pick the built assemblies from the a given Team Build (addressing option 2).

If this problem is one you have come across this post makes for interesting reading.

Mocking out calls in unit tests to a TFS Server using Typemock

If you are developing custom application using the TFS API then there is a good chance you will want to mock out the calls to your TFS server to enable better testing of the business logic in your application. The architecture of the TFS API does not lend itself to mocking using the standard means provided in most auto-mocking frameworks i.e there is not an interface for all the objects you care about. However, with Typemock Isolator you can fake the classes required, as Isolator can fake an instance of virtually any class.

So say we wanted to write a simple build monitor application for TFS Team Build system, we need to connect to a TFS server, get a list of historic builds, then select the last successful one. So our business logic method is as follows

/// <summary>
///  Gets the last successful build
/// </summary>
public static IBuildDetail GetLastNonFailingBuildDetails(string url, string projectName, string buildName)
{
    using (TeamFoundationServer tfs = new TeamFoundationServer(url))
    {
        IBuildServer buildServer = (IBuildServer)tfs.GetService(typeof(IBuildServer));
        return buildServer.QueryBuilds(projectName, buildName).Last(b => b.Status == BuildStatus.Succeeded || b.Status == BuildStatus.PartiallySucceeded);
    }
}

To test this, you would usually need a TFS server, with a set of historic build data already on it, but with Typemock you can avoid this requirement. OK we have to write bit of supporting code, but most of it would be common to a suite of tests, so the effort will not be too high overall and by doing it you get a test that can be run as part of the build process.

To be able to unit test our business logic (the last line of code in reality in this sample) we need to mock the call to the TeamFoundationServer (which is usually the blocking point for most mocking frameworks) and then mock the call to get the IBuildServer and return a set of data (which is usually possible with mocking frameworks).

Using Typemock we can get around these problems, the comments for each step are inline with the code.

[TestClass]
public class TFSTests
{
    [TestMethod]
    public void The_last_completed_and_non_failed_build_can_be_found()
    {
        // Arrange
        // Create the fake TFS server
        var fakeTfsServer = Isolate.Fake.Instance<TeamFoundationServer>();
        // Swap it in the next time the constructor is run
        Isolate.Swap.NextInstance<TeamFoundationServer>().With(fakeTfsServer);
 
        // Create a fake build server instance
        var fakeBuildServer = Isolate.Fake.Instance<IBuildServer>();
        // Set the behaviour on the TFS server to return the build server
        Isolate.WhenCalled(() => fakeTfsServer.GetService(typeof(IBuildServer))).WillReturn(fakeBuildServer);
        
        // Create some test data for the build server to return
        var fakeBuildDetails = CreateResultSet(new List<BuildTestData>() {
              new BuildTestData() {BuildName ="Build1", BuildStatus = BuildStatus.Failed},
              new BuildTestData() {BuildName ="Build2", BuildStatus = BuildStatus.PartiallySucceeded},
              new BuildTestData() {BuildName ="Build3", BuildStatus = BuildStatus.Failed},
              new BuildTestData() {BuildName ="Build4", BuildStatus = BuildStatus.Succeeded},
              new BuildTestData() {BuildName ="Build5", BuildStatus = BuildStatus.PartiallySucceeded},
              new BuildTestData() {BuildName ="Build6", BuildStatus = BuildStatus.Failed}
        });
        // Set the behaviour on the build server to return the test data, the nulls mean we don’t care about parameters passed
        Isolate.WhenCalled(() => fakeBuildServer.QueryBuilds(null, null)).WillReturn(fakeBuildDetails);
        
        // Act
        // Call the method we want to test, as we are using a fake server the parameters are actually ignored
        var actual = TFSMocking.BuildDetails.GetLastNonFailingBuildDetails("http://FakeURL:8080/tfs", "FakeTeamProject", "FakeBuildName");
 
        // Assert
        Assert.AreEqual("Build5", actual.BuildNumber);
    }
 
    /// <summary>
    /// A helper method to hide the Typemock code used to create each build results set
    /// </summary>
    /// <param name="builds">The parameters to populate into the build results</param>
    /// <returns>A set of build results</returns>
    private IBuildDetail[] CreateResultSet(List<BuildTestData> builds)
    {
        var fakeBuilds = new List<IBuildDetail>();
        foreach (var build in builds)
        {
            // Create a fake build result instance
            var fakeBuildDetails = Isolate.Fake.Instance<IBuildDetail>();
            // Set the properties, in this sample we only set a couple of properties, but this can be extended
            fakeBuildDetails.BuildNumber = build.BuildName;
            fakeBuildDetails.Status = build.BuildStatus;
            fakeBuilds.Add(fakeBuildDetails);
        }
        return fakeBuilds.ToArray();
    }
}
 
/// <summary>
/// A holding class for the build data we are interested in faking
/// </summary>
public class BuildTestData
{
    public string BuildName {get;set;}
    public BuildStatus BuildStatus {get;set;}
}

This sample is obviously fairly simple, but not that unrealistic. I have certainly written simple logic like this for build status applications. You could of course use some different architecture to make the business logic a bit more testable, but for such as basic requirement it is very tempting to keep it simple.

What I hope this post shows is that there is a way to test this type of logic without the need for a TFS server that has a suitable set of pre-created data and that the basic technique can be extended as much as is required to provide a mocked framework to allow unit testing of more complicated business logic.

Alpha release of TFS 2010 Build Extensions

Back in September I asked the question Does anyone need a VS2010 Custom Build Activity for StyleCop? and a good few people said yes and asked me when the activity would be released.

Well I had forgotten to say that the Codeplex TFS Build Extensions project, which the activity code got included into, has made it’s first public alpha release i.e. a release that means you don’t have to download the source and build it yourself. There is now a downloadable ZIP will just the built assemblies.

But the StyleCop activity is not the only one in releases, the others are:

  • StyleCop
  • Email
  • Twitter
  • Zip
  • File Attributes
  • DateTime
  • Guid
  • Run PowerShell Script
  • Run SQL Command
  • Numerous TFS Build management activities
  • Hyper-V Management
  • Virtual PC Management
  • IIS Website Management

So if you think any of these would be useful in your TFS 2010 Team build please download have a look and feedback any issues you find

TF30040 error when attaching a team project collection

Whilst doing some tidying on a multi-server TFS 2010 system I needed to move a team project collection from one Application Tier (AT) to another. Both ATs (which were not a load balanced pair) shared the same SQL server data tier (DT). It should have been easy using the TFS Administration Console.

I backed up the TPC database using SQL Management tools, the TPC was then detached without any issue from the first AT. I then backed up the SQL DB again in the detached state.

I then I tried to attach the TPC on the second AT. I entered the SQL DT instance name to get a list of TPCs available for attachment and I got the error

TF30040: The database is not correctly configured.
Contact your Team Foundation Server administrator.

So I went back to the original AT and tried a re-attach and got the same error message. Strange I had not changed accounts, the TFSSetup account in use had enough rights to detach the collection but not list one to be attached, strange!

A quick chat with the DBA found the problem, the TFSSetup account in use on both ATs had had its rights trimmed on the SQL server since the system was installed. As soon as it was granted admin rights to the SQL server all was fine with the listing TPCs available for attachment and the actually attaching the TPC on the new server.

Though I did not try it I suspect that as soon as I had the list of available TPCs in the TFS Administration Console I could have removed the extra SQL rights. The TFSService account would be doing the actual attachment, as it had done the detach, the TFSSetup account only need to be used to list the available TPCs.

My experiences getting started with writing custom adaptors for TFS Integration Platform

The TFS Integration Platform is an ALM Rangers project that provides an excellent set of tools to migrate or synchronise source code and/or work items between different TFS servers or TFS server and third party platforms. For many people the supported release on Code Gallery will do all they need. However if you have a need to connect to a system that there is no adaptor for you need the Codeplex version so you can write it yourself. To get the environment up and running, not unsurprisingly, the best place to start is the Getting Started documents.

I did this, I got all the pre-requisites (or so I thought), I download the code and unpack the zip. I ran the  extract_tfs_assemblies.bat to get the local copies of the TFS API assemblies and loaded the MigrationTools.sln in VS2010.

First thing I noted was that I was asked to convert the solution and project files to 2010 format, though they appeared to be the right format to start with. I did this but the report showed no changes, strange!

On loading the solution, other than the ‘TFS offline’ dialog the instructions mention, it also reported it could not load the InstallationCS.csproj file because

C:\Projects\ITIS\IntegrationPlatform\Setup\InstallationCA\InstallationCA.csproj(133,3): The imported project "C:\Program Files (x86)\MSBuild\Microsoft\WiX\v3.5\Wix.CA.targets" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.

The Wix directory for the current distribution is C:\Program Files (x86)\MSBuild\Microsoft\WiX\v3.x, a quick edit of the InstallationCA.csproj in notepad to correct the path fixed this load problem (wonder if that was why VS2010 though it needed to do a solution upgrade?, I could not be bother to roll back to find out.)

I then tried to build the solution, and got around 150 errors, starting with the error

C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppBuild.targets(292,5): error MSB8009: .NET Framework 2.0/3.0/3.5 target the v90 platform toolset. Please make sure that Visual Studio 2008 is installed on the machine.

I checked and this targets file was there. I had VS2010 and VS2008 installed what could be the problem?

Well it turned out that though I had VS2008 installed I had (for some strange reason lost in the mists of time) not selected the C++ components. Easy to fix you would think. Just go into control panel, into installed products and add the missing features, so I tried to add C++ and got the message

A selected drive is no longer valid. Please review your installation path settings before continuing with setup

I tried inserting DVDs, mounting ISO etc. all to no avail. The found on this forum post, turns out you have to remove VS2008 SP1, add the feature and then re-patch.Once this was done I could build the C++ projects.

As a side note here, I went through this process a couple of time, the first time I also managed to get the TFS adaptor projects references a bit confused. The TFS2008 adaptor projects had ended up referencing the TFS2010 assemblies. This caused a build error due to obsolete calls. This was easily fixed by repointing the references to the copies of the assemblies the extract_tfs_assemblies.bat creates. However if you remember to run extract_tfs_assemblies.bat before opening the solution for the first time this should not be an issue, as all the right assemblies will be on the correct hint paths.

So I now had 36 build errors. The next one I tackled was

Unable to copy file "C:\Projects\ITIS\TestEnv\MigrationTestEnvironment.xml" to "..\..\..\Binaries\Debug\\Test\MigrationTestEnvironment.xml". Could not find a part of the path 'C:\Projects\ITIS\TestEnv\MigrationTestEnvironment.xml'.    MigrationTestLibrary

This  was because the file was actually missing, it is not under the Codeplex source control. This is the XML file defines the adaptors under test. You need to create it based on the test you wish to run, or that is my current understanding. To get around the error (as a start) I just set it ‘copy to output directory’ property to ‘do not copy’ – knowing I would switch this back later

I was now down to 16 errors, all related to the Subversion related projects. These requires, fairly obviously, some Subversion libraries I did not have on my PC. As I did not need to work with Subversion I could have chosen to just remove these projects from the solution, but I thought why not fix it it all. They key again was to follow the readme.txt in the Interop.Subversion project folder. You need to download and unzip the two sets of source for Subversion and Apr.

I was then left with a missing reference to SharpSvn. This I downloaded from http://www.open.collab.net/files/documents/180/2861/SSvn-1.6006.1373.zip and unpacked to the {solution root}/binaries/external/sharpsvn  and made sure the SubversionTCAdapter project reference pointed to this location.

Once all this was done all the projects in the solution built. So nothing too complex really, especially when you follow the instructions in the right order!

So to develop some custom adaptors now then……

Renaming branches in TFS2010

I recently was asked why a client had experienced some unexpected results when merging a development branch back into the main trunk on a TFS 2010 installation.

Turns out the issue was that during some tests that the both the Main and Dev branches had been renamed, and new branches of the same names created. So they had a structure like this:

$/ProjectX    
  Dev Newly created after the rename
  Main Newly created after the rename
  Old_Dev Renamed of Dev
  Old_Main          Renamed of Main

 

In TFS 2010 behind the scenes a rename is actually a branch and delete process, this meant we ended up with the new branch, but also a deleted branch of the old name. This is not obvious unless you have ‘show deleted items in source control explorer’ enabled in the Visual Studio option

image

Once you recreate a new branch with the same name as one of the old named branches this deleted branch is replaced by the newly created one, It has taken up the old ‘slot’ (see links at end).This means that when you try to do a merge the merge tools sees this recreated branch as a potential target, and so shows it in the merge dialog. With all the potential confusion that might cause.

image

So the simple answer is try to avoid renames, and especially try to avoid creating new branches in the same ‘slot’ as deleted/renamed ones.

For a more detailed explanations of that is going on here have a look at this post on renaming branches in TFS and this one on ‘slot mode’ in 2010 version.