Determining Whether Tests Passed in Team Build

In a forum post a while back, I laid out a method for determining whether tests had passed during a build.  More recently, I have linked to this forum post in advising others on similar problems.  Unfortunately, as a sharp user pointed out in this same thread, my solution doesn't actually work, since it relies on a property that is not accessible in Team Build v1!

So - to remedy the situation I have written a custom task which can be used to determine whether tests have succeeded or not.  This task takes advantage of the GetTestResultsForBuild method of the Microsoft.TeamFoundation.Build.Proxy.BuildStore class. 

As always, this task is provided as a sample only; its awesomeness cannot be guaranteed, etc.  The task inherits from the TeamBuildTask class I presented in an earlier post.

 using System;
using System.Web.Services;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Build.Common;
using Microsoft.TeamFoundation.Build.Proxy;

namespace MyNamespace
{
    public class CheckForTestSuccess : TeamBuildTask
    {
        /// <summary>
        /// The ConfigurationToBuild Item Group for the Build.
        /// </summary>
        [Required]
        public ITaskItem[] ConfigurationToBuild
        {
            get
            {
                return m_configurationToBuild;
            }
            set
            {
                m_configurationToBuild = value;
            }
        }

        /// <summary>
        /// An output property which will be true if all test runs succeeded and false otherwise.
        /// </summary>
        [Output]
        public bool TestSuccess
        {
            get
            {
                return m_testSuccess;
            }
            set
            {
                m_testSuccess = value;
            }
        }

        /// <summary>
        /// Returns the name of the build step to be added for this task.
        /// </summary>
        /// <returns>Name of the build step to be added.</returns>
        protected override string GetBuildStepName()
        {
            return "CheckForTestSuccess";
        }

        /// <summary>
        /// Returns the message of the build step to be added for this task - this is the
        /// string displayed in the Team Build GUI.
        /// </summary>
        /// <returns>Message of the build step to be added.</returns>
        protected override string GetBuildStepMessage()
        {
            return "Checking for test success.";
        }

        /// <summary>
        /// Put real task logic in this method.
        /// </summary>
        /// <returns>True if task is successful, otherwise false.</returns>
        protected override bool ExecuteInternal()
        {
            string platform;
            string flavor;
            TestResultData[] testResults;

            m_testSuccess = true;

            foreach (ITaskItem configuration in ConfigurationToBuild)
            {
                platform = configuration.GetMetadata("PlatformToBuild");
                flavor   = configuration.GetMetadata("FlavorToBuild");

                testResults = BuildStore.GetTestResultsForBuild(BuildUri, platform, flavor);

                foreach (TestResultData testResult in testResults)
                {
                    if (!testResult.RunPassed)
                    {
                        m_testSuccess = false;
                        break;
                    }
                }
                if (!m_testSuccess)
                {
                    break;
                }
            }

            return true;
        }

        private ITaskItem[] m_configurationToBuild;
        private bool m_testSuccess;
    }
}

To use this task, you'll need to do something like the following in TfsBuild.proj:

 <UsingTask TaskName="CheckForTestSuccess" AssemblyFile="CustomTasks.dll" />

<Target Name="AfterTest">
 <CheckForTestSuccess TeamFoundationServerUrl="$(TeamFoundationServerUrl)" 
       BuildUri="$(BuildUri)" 
     ConfigurationToBuild="@(ConfigurationToBuild)">
      <Output TaskParameter="TestSuccess" PropertyName="TestSuccess" />
 </CheckForTestSuccess>
</Target>

The property TestSuccess would then be 'true' if and only if all test runs succeeded for the build.