Wednesday, May 30, 2007

Preventing new Remote Desktop sessions in Server 2003

I have both a laptop and a desktop, and it's fairly often that I remote into my desktop to do development.  Although my laptop is no slouch, you really can't beat a desktop dev experience.  However, my dev machine is running Server 2003, which allows multiple remote desktop sessions at once.  Windows XP only allows one user session at a time, whether it's a console (I'm physically at the machine) or remote session.  That was nice, because I could have a bunch of applications open at once, lock the machine, then remote into it and have all of my applications up and running.

When I try and remote into my Server 2003 dev box, by default remote desktop starts a new session.  Poof, all of my applications are gone (in a another session at least).  I'd like to mimic the behavior of Windows XP, and continue the console session I already had going for me.  Luckily for me, it's pretty easy to accomplish this.  I actually have two options:

  • Using /console command-line switch to mstsc.exe (not very user-friendly)
  • Edit a saved remote desktop connection file (.RDP file)

I like the second option, since I often save connections to known machines.  I can never remember any of the machine names anyway.  Just edit a saved remote desktop connection (RDP file) in Notepad or another text editor and add the following line at the end of the file:

connect to console:i:1

Save the file, and close Notepad.  When you run the RDP file, you will connect to the console session, and you'll have all of the programs you had when you were logged in to the console session.

Tuesday, May 29, 2007

Refactoring NAnt and MSBuild build scripts

A while back, I talked about the harmful effects of "Copy Paste".  While editing some NAnt and MSBuild build scripts, I forgot about the evil twin of "Copy Paste", which is "Find and Replace" (I guess both twins are evil).  I needed to update an MSBuild script to have the correct version numbers of an application we're starting on.  Here's what the abridged MSBuild script looked like before any modifications:

<Project>
  <PropertyGroup>
    <LocalPath1>E:\builds\V2.1\US\ecomm\CoreBusinessObjectsDistribution</LocalPath1>
    <LocalPath2>E:\builds\V2.1\US\ecomm\OrderWorkflowDistribution</LocalPath2>
    <LocalPath3>E:\builds\V2.1\US\ecomm\Store.BusinessObjects.Ecommerce</LocalPath3>
    <LocalPath4>E:\builds\V2.1\US\ecomm\Store.UI</LocalPath4>
    <LocalPath5>E:\builds\V2.1\US\ecomm\Store.Utilities.Ecommerce</LocalPath5>
    <LocalPath6>E:\builds\V2.1\US\ecomm\MyCompany.Store.UI.Ecommerce</LocalPath6>
    <LocalPath7>E:\builds\V2.1\US\ecomm\MyCompany.Store.UI</LocalPath7>
    <LocalPath8>E:\builds\V2.1\US\ecomm\store</LocalPath8>
    <LocalPath9>E:\builds\V2.1\US\ecomm</LocalPath9>
    <LocalPath10>E:\builds\V2.1\US\ecomm\Deploy</LocalPath10>
  </PropertyGroup>
</Project>

This file was targeting our "V2.1" release, but I needed to update it to "V2.1.5", so all of the directory names had to be changed.  I started to whip out the ever-faithful "Ctrl-H" to perform a "Find and Replace", but I stopped myself.  This was a great opportunity for a refactoring.

Eliminating duplication

One of the major code smells is duplicated code.  But duplications don't always have to occur in code, as the previous MSBuild script showed.  I needed to change all of the references of "V2.1" to "V2.1.5", and there were two dozen examples of these, which I would need to change through "Find and Replace".

The problem with "Find and Replace" is that it can be error-prone.  The search can be case-sensitive, I might pick "Search entire word", etc.  There are so many options, I would need to try several combinations to make sure I found all of the instances I wanted to replace.  Instead of wallowing through the "Find and Replace" mud, can't I just eliminate the duplication so I only need to make one change?  Why don't we take a look at our catalog of refactorings to see if one fits for MSBuild.

Refactoring the script

Detailed in Martin Fowler's refactoring book and website, I can look up a specific code smell and find an appropriate refactoring.  There are some websites that also list out "smells to refactorings".  The one that looks the most promising is Extract Method.  MSBuild scripts don't exactly have methods, but they do have the concepts of properties and tasks.

I can introduce a property that encapsulates the commonality between all of the "LocalPathXxx" properties, which is namely the root directory.  I'll give the extracted property a good name, and then make all properties and tasks that use the root directory use my new property instead of hard-coding the path.  Here's the final script:

<Project>
  <PropertyGroup>
    <LocalPathRoot>E:\builds\V2.1.5\ecomm</LocalPathRoot>
    <LocalPath1>$(LocalPathRoot)\CoreBusinessObjectsDistribution</LocalPath1>
    <LocalPath2>$(LocalPathRoot)\OrderWorkflowDistribution</LocalPath2>
    <LocalPath3>$(LocalPathRoot)\Store.BusinessObjects.Ecommerce</LocalPath3>
    <LocalPath4>$(LocalPathRoot)\Store.UI</LocalPath4>
    <LocalPath5>$(LocalPathRoot)\Store.Utilities.Ecommerce</LocalPath5>
    <LocalPath6>$(LocalPathRoot)\MyCompany.Store.UI.Ecommerce</LocalPath6>
    <LocalPath7>$(LocalPathRoot)\MyCompany.Store.UI</LocalPath7>
    <LocalPath8>$(LocalPathRoot)\store</LocalPath8>
    <LocalPath9>$(LocalPathRoot)</LocalPath9>
    <LocalPath10>$(LocalPathRoot)\Deploy</LocalPath10>
  </PropertyGroup>
</Project>

Now in future versions (V2.2 maybe?) we'll only need to make one change, instead of several dozen.  Any time I eliminate duplication, I greatly reduce the chances for error.

So where are we?

The code smells laid out in Martin Fowler's book don't apply only to code.  As we've seen with this MSBuild script, they can apply to all sorts of other domains where duplication causes problems.  All we have to do is find appropriate mappings to the new domain for the refactorings laid out for that particular smell.  Of course, if you don't know about code smells and how to recognize them, the duplication will probably continue to live on and wreak havoc on your productivity.

My next step is to replace these horrible "LocalPathXxx" property names with intention-revealing names.  Originally, this script had comments around each property explaining what it meant.  There's nothing like using intention-revealing names to eliminate the need for comments.

Thursday, May 24, 2007

TFS Guide now available

One thing I always thought was missing from MSDN regarding TFS was any kind of guidance or best practices.  Just released was a Beta 1 of a TFS Best Practices guide, and after taking a quick look at the contents (over 300 pages) it has a ton of great information.  The section on Team Build is worth its weight in gold, as it not only covers good build practices, but relates them to Team Build.

TFS Guide homepage

Latest Release

This document was put out by the Microsoft Patterns and Practices team, and while the document is a Beta, and it's easy to go straight to the guidance you need.  It even has a whole chapter dedicated to Continuous Integration, though it does have the caveat "Team Foundation Server 2005 does not provide a CI solution out of box".  TFS does provide the framework to support it, so you have tools like TeamCI, TFS Integrator, and Automaton.  Also nice is a chapter on Large Project Considerations, and many other chapters have sections dedicated to large project considerations.

The book is laid out into separate parts:

  • Fundamentals
  • Source Control
  • Builds
  • Large Project Consideration
  • Project Management
  • Process Templates
  • Reporting
  • Setting Up and Maintaining

It finishes out with a list of:

  • Guidelines
  • Practices
  • Questions and Answers
  • How Tos

All in all, a pretty nice reference, and something I really wish I had a year ago.

Team Foundation Build, Part 3: Creating a Build

In part 1 and 2 of this series, I gave an overview of Team Foundation Build and discussed installation and configuration options.  One thing I should note is that if a team needs to add custom tasks to the build that are in separate assemblies, these assemblies need to be copied to the build machine.  That implies that the dev team probably needs administrator access to the build machine.

In VSTS, build definitions are called Build Types, and are created through the Team Explorer.  Creating a Build Type is accomplished through a wizard, which will walk you through the steps of defining the build.  So what does Team Build provide out of the box?  Namely, what are the build steps involved?  First, Team Build will:

  • Synchronize with source control
  • Compile the application
  • Run unit tests
  • Perform code analysis
  • Release builds on a file server
  • Publish build reports

So out of the box, we don't have to worry about configuring source control, compiling, and other common tasks that we would otherwise need to define ourselves.  When I launch the New Team Build Type Creation Wizard from Team Explorer, the wizard walks me through the following steps:

  • Create a new build type
  • Select the solutions to build
  • Select a configuration and platforms for build
  • Select a build machine and drop location
  • Select build options

I'll walk through each of these steps one by one.

Step 1: Create a new build type

In the first screen, you need to specify the name of your Build Type.  Unfortunately, all Build Types are grouped in one folder in source control, so we have to use names instead of folders to distinguish different builds.  Naming conventions can help that situation, so something like <Application>_<Version>_<Region>_<BuildType> would work.  In the past, I've defined several builds for the same application, like "Deploy", "Nightly", "CI", etc.  Build Type names can be a pain to change, so choose your Build Type names carefully.

Step 2: Select the solutions to build

Build Type definitions allow you to select one or more Visual Studio solutions to build.  In most cases, you would have only one solution to build, but if there are more than one solution to build, you can select multiple and specify the order that each solution will be compiled.  If SolutionA depends on SolutionB, just have SolutionB build before SolutionA.

Step 3: Select a configuration and platforms for build

In this screen, you can specify the project configuration you would like to build with.  Typically this could be "DEBUG", "RELEASE", or any custom project configurations you might have.  Typically, I might have a separate project configuration like "AUTOMATEDDEBUG" that might add code analysis.  I usually leave the platform to "Any CPU", but if you have specific platform requirements, this is where you would specify that.

Step 4: Select a build machine and drop location

When specifying the build machine, Team Build needs two pieces of information:

  • What is the name of the build machine?
  • What directory on the build machine should I build in?

The build machine is the machine that has Team Build Service installed on it.  The directory can be anything, but keep in mind that you don't necessarily want all builds being built in the same directory.  Team Build is good about separating builds in the file system, but I've had hard drives fill up when I had too many builds going on the same machine

The other piece of information in this step is the drop location.  When Team Build finishes compiling and testing, it will copy the files to a UNC share you specify here.  Don't worry, if you need additional files dropped, you can customize this later in the Build Type definition.

Step 5: Select build options

This step is entirely optional (but strongly recommended).  You can specify that you would like this build to run tests and perform code analysis.  If you select "Run test", you will need to specify the test metadata file (*.vsmdi) and the test list to run.  In my last project, we had over 1300 unit tests when I left, which was absolutely impossible to manage in a test list.  We used custom task to specify our tests to run, which would use reflection to load the tests dynamically.

The other option available is code analysis, which is important for enforcing coding guidelines and standards.  Without code analysis turned on, you'll probably have a different coding standard for every developer who touched the code.

Step 6: Finish

When you complete the wizard, two new files are created in source control:

  • TfsBuild.proj - this is the Build Type definition, where you'd put any customization
  • WorkspaceMapping.xml - definition of the source control workspace, where you can change the build directory

You can find these files in the source control explorer in $/[Team Project]/TeamBuildType/[Build type name].  Manually going through source control is a little bit of a pain if I want to edit the TfsBuild.proj file, so I use Attrice's Team Foundation Sidekick add-in, which lets my right-click and check out and check in directly from Team Explorer.

So that's it!  To start a new build just right-click the Build Type in Team Explorer and select "Build".  Double-clicking the Build Type will bring up a list of all of the builds with their statuses.  This is also where you can view the details of an individual build.

In the next posts, I'll detail some values, principles, and practices when it comes to automated builds, as well as some discussion on customizing and extending a Build Type definition.

Wednesday, May 23, 2007

Unit testing with stubs and Rhino Mocks

I've been using Rhino Mocks for about a year now, and Oren has never failed to impress me with the features he keeps adding on a regular basis.  I needed to test a particular method that accepted an IProfile interface as an argument.  I didn't want to use an existing IProfile implementation I found, I was really interested in just sending the method a stub.  If I used Rhino Mocks to create a mock, I'd have to set a bunch of expectations to get everything set up, but I really just want a stub.  It's a huge pain to set up a stub manually right now, as this would entail creating your own class that implemented IProfile with a basic implementation, etc.  For more information about mocks, stubs, dummy objects and fake objects, check out Fowler's paper on the subject.  Here's the test I created:

[TestMethod]
public void SetPaymentType_WithValidPayment_AddsPaymentFieldToPaymentFields()
{
    MockRepository repo = new MockRepository();
    IProfile profile = repo.Stub<IProfile>();
    IPayment payment = repo.Stub<IPayment>();

    using (repo.Record())
    {
        profile.Payments = new IPayment[] {payment};
        payment.PaymentCode = "CC";
    }

    using (repo.Playback())
    {
        bool result = ProfileHelper.SetPaymentType("TestValue", profile);

        Assert.AreEqual(true, result);
        Assert.AreEqual(1, payment.PaymentFields.Length);

        IField paymentField = payment.PaymentFields[0];

        Assert.AreEqual("PaymentType", paymentField.FieldKey);
        Assert.AreEqual("TestValue", paymentField.FieldValue);
    }
}

The MockRepository object is from Rhino Mocks.  I call the Stub method to generate a stub object for the interfaces I'm interested in, which are specifically the IProfile and IPayment types.  I set the MockRepository to Record to put in the initial values for my stubs.  Note that Rhino Mocks creates the interface types, and nowhere in my code will I create an implementation of IProfile or IPayment.  Rhino Mocks does this for me.  I set the MockRepository back to Playback mode and call the method I wanted to test (ProfileHelper.SetPaymentType).  Notice that the SetPaymentType method modifies the PaymentFields property on the IProfile object, and does it correctly.  I finish out the test making assertions about the values that should be set in the IProfile object.

What's clear from looking at this test is that I'm only concerned about testing the interaction between the ProfileHelper.SetPaymentType method and the IProfile object, but I don't care about the specific implementation of the IProfile object.  If I passed in a specific implementation of an IProfile object, there may be some unwanted side effects that might cause some false positives or false negatives.  Using stubs makes sure I limit the scope of what's being tested only to the method I'm calling.

Team Foundation Build, Part 2: Installation and Configuration

So now that we have some understanding of what the components of Team Build are from Part 1, where should these components be installed? Luckily, there's some pretty good documentation on Team Foundation Server components and topologies on MSDN.

Lots of arrows and boxes, but the main point of this diagram is that Team Build is installed on a separate box from the Application Tier (Team Foundation Server or TFS Proxy) and from any client machines. A build machine should only have software installed to support the execution of a build. You shouldn't install:

  • Third-party control packages
  • Database client tools (Toad, SQL Server Client Tools, etc.)
  • Anything that would push assemblies into the GAC

Ideally, all you would have installed would be:

  • Team Build
  • Team Edition for Developers (for static analysis)
  • Team Edition for Testers (for running tests during a build)

Anything else installed could potentially cause build errors because the build might use incorrect versions of third party libraries when compiling. That's why it's always best to check in all third-party libraries into source control, instead of relying on installers to get them to work. For a detailed installation guide, check out the Team Foundation Installation Guide.

Another piece to note on the diagram above is the upper-right hand corner, noted as the "Build Drop site". This could be a file server or a share on the buildserver, where the compiled assemblies, log files, etc. are dropped. In the next post, I'll discuss creating a Team Build definition and an introduction into extending the build.

Tuesday, May 22, 2007

Team Foundation Build, Part 1: Introduction

There's been some interest recently for our team to utilize more features of Team System, including Team Foundation Build.  Rather than send out a blanket email, I'm following Jon Udell's advice and maximizing the value of my keystrokes by posting a series of blog entries on this topic.

Visual Studio Team System introduced quite a few productivity enhancements for development teams including work items, process templates, reporting, source control, and builds.  Team Foundation Build is the build server component of VSTS.  Build definitions in VSTS are:

  • Managed in Team Explorer
  • Represented by MSBuild scripts
  • Stored in Team Foundation Source Control
  • Executed on a build machine by the Team Build Service
  • Can be initiated through Team Explorer
  • Report results to Team System

So why should we use Team Build over a home grown solution like batch files, Nant scripts, etc.?

Centralized management

All builds are defined, managed, and viewed through Team Explorer.  Since builds are stored in source control, we get all of the benefits source control provides, such as versioning, security, etc.  We also have one central repository to view and edit builds.  I can double-click a build definition to view all of the executed builds with status (success/failure), and drill down into a single build to view more details.  If I'm using ReSharper, I get IntelliSense and refactoring tools for MSBuild.

Defined with MSBuild

MSBuild is the new build platform for Visual Studio.  Project files (.vsproj, .vbproj, etc.) are now defined as MSBuild scripts.  Tasks in MSBuild are customizable and extensible, so I can define new tasks and use community built tasks.  Team Build definitions also allow extensibility points, similar to the ASP.NET page event model, by extending certain targets such as "BeforeGet", "AfterTest", and "AfterDropBuild".

Status and reporting

There are usually two pieces of information I'm curious about when looking at builds:

  • What is the status of the current build? (In progress, successful, failed)
  • Is there a trend in the build statuses?

All of this information can be seen through Team Explorer.  Additionally, I've seen tray icon applications that will display a red, yellow, or green light indicating the status of a certain build definition.

Where do we go from here?

In coming posts, I'll discuss installation and configuration, defining builds, and outlining a set of values, principles, and practices Team Build can be used to encourage and enforce.  I'll also outline some ideas on what kinds of build definitions are good to have, and what kinds of activities we might want to accomplish as part of our builds.