Give Me Everything (part 2)

(Continued...)

Let's instead add each test to its own project file. Hopefully then the Items scope won't end up clashing with each other. And let's be a little more careful about the side-effects of the tests. Instead of always working with the master MSI, I will be careful to always copy a fresh database in and work against that. Then run these tests using MSBuild.

Let's create a new file and add it to our setup project, "SelectFromEmpty.test". I can copy a lot of the useful stuff for this test from the old PostBuild.proj:

 <Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="SelectFromEmpty">
    <Import Project="$(MSBuildExtensionsPath)\SetupProjects\SetupProjects.Targets" />

    <PropertyGroup>
        <SourceMsiPath>$(BuiltOutputPath)</SourceMsiPath>
        <TestMsiPath>$(ProjectDir)TestResults\Test.Msi</TestMsiPath>
    </PropertyGroup>

    <Target Name="SelectFromEmpty">
        <Message Text="********** Begin SelectFromEmpty Test **********" />
        <Copy SourceFiles="$(SourceMsiPath)" DestinationFiles="$(TestMsiPath)" />

        <Select MsiFileName="$(TestMsiPath)"
                TableName="Error" 
                Columns="Error;Message">
            <Output TaskParameter="Records" ItemName="ErrorRecords" />
            <Output TaskParameter="ColumnInfo" ItemName="ErrorColumnInfo" />
        </Select>
            
        <Message Text="---------- Error Table Information ------------" />
        <Message Text="Table: %(ErrorColumnInfo.Identity)   Name: %(ErrorColumnInfo.Name)    Type: %(ErrorColumnInfo.Type)   Index: %(ErrorColumnInfo.Index)" />
        <Message Text="---------- Error Records ------------" />
        <Message Text="Table: %(ErrorRecords.Identity)   Error: %(ErrorRecords.Error)    Message: %(ErrorRecords.Message)" 
                 Condition="'%(ErrorRecords.Identity)'!=''"/>
    </Target>
</Project>

Items of note:

  • The DefaultTargets is "SelectFromEmpty"
  • I added some new properties: SourceMsiPath and TestMsiPath.
  • These are the arguments to the Copy task to avoid side-effects

How to run the test? It would be simple to add this test to the command-line, but that will likely get unwieldy as more tests are added. Instead, let's have MSBuild take care of all of these by changing our PostBuild.proj contents (which, by the way, now sounds like it's no longer named appropriately).

 <Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="RunTests">

    <ItemGroup>
        <TestFiles Include="*.test" />
    </ItemGroup>

    <Target Name="RunTests">
        <MSBuild
            Projects="@(TestFiles)">
        </MSBuild>
    </Target>

</Project>

Yes, that project is using the MSBuild task. The projects to run are found by having an Item find all files that end with ".test". It would also be possible to explicitly list all of the tests. The advantage of listing them all is that it is easier to specify test order (which shouldn't matter because hopefully all side-effects have been eliminated). The disadvantage is that it becomes necessary to update this file every time a new test is created. I prefer this way, and if I wanted to exclude a test it's possible to do so using the Excludes attribute.

The new system is working so far:

 Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" (default targets):

Target RunTests:
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectFromEmpty.test" (default targets):

    Target SelectFromEmpty:
        ********** Begin SelectFromEmpty Test **********
        Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        ---------- Error Table Information ------------
        Table: Error   Name: Error    Type: Integer   Index: 1
        Table: Error   Name: Message    Type: String   Index: 2
        ---------- Error Records ------------

Build succeeded.
    0 Warning(s)
    0 Error(s)

Let's add the next test for selecting all columns, "SelectAllColumns.test":

 <Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="SelectAllColumnsTest">
    <Import Project="$(MSBuildExtensionsPath)\SetupProjects\SetupProjects.Targets" />

    <PropertyGroup>
        <SourceMsiPath>$(BuiltOutputPath)</SourceMsiPath>
        <TestMsiPath>$(ProjectDir)TestResults\Test.Msi</TestMsiPath>
    </PropertyGroup>

    <Target Name="SelectAllColumnsTest">
        <Message Text="********** Begin SelectAllColumns Test **********" />

        <Copy SourceFiles="$(SourceMsiPath)" DestinationFiles="$(TestMsiPath)" />

        <ExecuteSql MsiFileName="$(TestMsiPath)" 
                    Sql="INSERT INTO `Error` (`Error`, `Message`) VALUES ('1', 'One')"
        />
        <ExecuteSql MsiFileName="$(TestMsiPath)"
                    Sql="INSERT INTO `Error` (`Error`, `Message`) VALUES ('2', 'Two')"
        />

        <Select MsiFileName="$(TestMsiPath)"
                TableName="Error" 
                Columns="Error;Message">
            <Output TaskParameter="Records" ItemName="ErrorRecords" />
            <Output TaskParameter="ColumnInfo" ItemName="ErrorColumnInfo" />
        </Select>

        <Message Text="---------- Error Table Information ------------" />
        <Message Text="Table: %(ErrorColumnInfo.Identity)   Name: %(ErrorColumnInfo.Name)    Type: 

%(ErrorColumnInfo.Type)   Index: %(ErrorColumnInfo.Index)" />
        <Message Text="---------- Error Records ------------" />
        <Message Text="Table: %(ErrorRecords.Identity)   Error: %(ErrorRecords.Error)    Message: 

%(ErrorRecords.Message)" 
                 Condition="'%(ErrorRecords.Identity)'!=''"/>
    </Target>
</Project>

This is just a combination of the old SelectAllColumnsTest combined with the new changes from SelectFromEmpty.test. Now running the tests shows:

 Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" (default targets):

Target RunTests:
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectAllColumns.test" (default targets):

    Target SelectAllColumnsTest:
        ********** Begin SelectAllColumns Test **********
        Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        ---------- Error Table Information ------------
        Table: Error   Name: Error    Type: Integer   Index: 1
        Table: Error   Name: Message    Type: String   Index: 2
        ---------- Error Records ------------
        Table: Error   Error: 1    Message: One
        Table: Error   Error: 2    Message: Two
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectFromEmpty.test" (default targets):

    Target SelectFromEmpty:
        ********** Begin SelectFromEmpty Test **********
        Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        ---------- Error Table Information ------------
        Table: Error   Name: Error    Type: Integer   Index: 1
        Table: Error   Name: Message    Type: String   Index: 2
        ---------- Error Records ------------

Build succeeded.

Those seem to be working, and also note that the side-effects appear to have been eliminated, as the SelectFromEmpty ran after SelectAllColumns.

Of course, there isn't anything working here that didn't work before (except for the side-effects problem). the tests really ran into problems when trying to cope with both SelectAllColumns and SelectAllColumnsWithWhere tests. Let's add SelectAllColumnsWithWhere and see what happens.

First, the test looks very similar to SelectAllColumns:

 <Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="SelectAllColumnsWithWhere">
    <Import Project="$(MSBuildExtensionsPath)\SetupProjects\SetupProjects.Targets" />

    <PropertyGroup>
        <SourceMsiPath>$(BuiltOutputPath)</SourceMsiPath>
        <TestMsiPath>$(ProjectDir)TestResults\Test.Msi</TestMsiPath>
    </PropertyGroup>

    <Target Name="SelectAllColumnsWithWhere">
        <Message Text="********** Begin SelectAllColumnsWithWhere Test **********" />

        <Copy SourceFiles="$(SourceMsiPath)" DestinationFiles="$(TestMsiPath)" />

        <ExecuteSql MsiFileName="$(TestMsiPath)" 
                    Sql="INSERT INTO `Error` (`Error`, `Message`) VALUES ('1', 'One')"
        />
        <ExecuteSql MsiFileName="$(TestMsiPath)"
                    Sql="INSERT INTO `Error` (`Error`, `Message`) VALUES ('2', 'Two')"
        />

        <Select MsiFileName="$(TestMsiPath)"
                TableName="Error" 
                Columns="Error;Message"
                Where="`Error`=1">
            <Output TaskParameter="Records" ItemName="ErrorRecords" />
            <Output TaskParameter="ColumnInfo" ItemName="ErrorColumnInfo" />
        </Select>

        <Message Text="---------- Error Table Information ------------" />
        <Message Text="Table: %(ErrorColumnInfo.Identity)   Name: %(ErrorColumnInfo.Name)    Type: %(ErrorColumnInfo.Type)   Index: %(ErrorColumnInfo.Index)" />
        <Message Text="---------- Error Records ------------" />
        <Message Text="Table: %(ErrorRecords.Identity)   Error: %(ErrorRecords.Error)    Message: %(ErrorRecords.Message)" 
                 Condition="'%(ErrorRecords.Identity)'!=''"/>
    </Target>
</Project>

Running shows:

 Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" (default targets):


Target RunTests:
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectAllColumns.test" (default targets):

    Target SelectAllColumnsTest:
        ********** Begin SelectAllColumns Test **********
        Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        ---------- Error Table Information ------------
        Table: Error   Name: Error    Type: Integer   Index: 1
        Table: Error   Name: Message    Type: String   Index: 2
        ---------- Error Records ------------
        Table: Error   Error: 1    Message: One
        Table: Error   Error: 2    Message: Two
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectAllColumnsWithWhere.test" (default targets):

    Target SelectAllColumnsWithWhere:
        ********** Begin SelectAllColumnsWithWhere Test **********
        Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        ---------- Error Table Information ------------
        Table: Error   Name: Error    Type: Integer   Index: 1
        Table: Error   Name: Message    Type: String   Index: 2
        ---------- Error Records ------------
        Table: Error   Error: 1    Message: One
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectFromEmpty.test" (default targets):

    Target SelectFromEmpty:
        ********** Begin SelectFromEmpty Test **********
        Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        ---------- Error Table Information ------------
        Table: Error   Name: Error    Type: Integer   Index: 1
        Table: Error   Name: Message    Type: String   Index: 2
        ---------- Error Records ------------

Build succeeded.

It looks like this new scheme worked (schwoo: I spent almost 2 hours writing all this stuff; I would have been very upset had this not worked).

Now the question is, what to do next? Write the rest of the tests? Not quite yet. Let's clean these up a little bit by refactoring. Notice that all three tests:

  • Define the same properties
  • Perform the same copy
  • Populate the Error table in the exact same way (okay, only 2 of the tests do this)
  • Display things in the same way

It should be possible to move these commonalities into a shared location, right? It sure is, by using a targets file just like last time around.

Start by creating a new file in our project directory, SelectTests.targets, and move these commonalities into there:

 <Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003">
    <Import Project="$(MSBuildExtensionsPath)\SetupProjects\SetupProjects.Targets" />
    <Import Project="$(MSBuildExtensionsPath)\SetupProjects\SetupProjects.Targets" />

    <PropertyGroup>
        <SourceMsiPath Condition="'$(SourceMsiPath)'==''">$(BuiltOutputPath)</SourceMsiPath>
        <TestMsiPath Condition="'$(TestMsiPath)'==''">$(ProjectDir)TestResults\Test.Msi</TestMsiPath>
    </PropertyGroup>

    <Target Name="CopyMsi">
        <Copy SourceFiles="$(SourceMsiPath)" DestinationFiles="$(TestMsiPath)" />
    </Target>

    <Target Name="FillErrorTable">
        <ExecuteSql MsiFileName="$(TestMsiPath)" 
                    Sql="INSERT INTO `Error` (`Error`, `Message`) VALUES ('1', 'One')"
        />
        <ExecuteSql MsiFileName="$(TestMsiPath)"
                    Sql="INSERT INTO `Error` (`Error`, `Message`) VALUES ('2', 'Two')"
        />
    </Target>

    <Target Name="DisplayOutputs">
        <Message Text="---------- Error Table Information ------------" />
        <Message Text="Table: %(ErrorColumnInfo.Identity)   Name: %(ErrorColumnInfo.Name)    Type: %(ErrorColumnInfo.Type)   Index: %(ErrorColumnInfo.Index)" />
        <Message Text="---------- Error Records ------------" />
        <Message Text="Table: %(ErrorRecords.Identity)   Error: %(ErrorRecords.Error)    Message: %(ErrorRecords.Message)" 
                 Condition="'%(ErrorRecords.Identity)'!=''"/>
    </Target>
</Project>

All the individual tasks were grouped into their own target. Also notice that a Condition was added to the Properties. This pattern was mentioned before when I was discussing public/private properties: its considered impolite to overwrite a property value explicitly set elsewhere.

Having one the tests use these new targets is (seemingly) pretty easy. Let's start with SelectFromEmpty:

 <Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="SelectFromEmpty">
    <Import Project="SelectTests.Targets" />
    <Import Project="$(MSBuildExtensionsPath)\SetupProjects\SetupProjects.Targets" />

    <Target Name="SelectFromEmpty">
        <Message Text="********** Begin SelectFromEmpty Test **********" />
        
        <CallTarget Targets="CopyMsi" />

        <Select MsiFileName="$(TestMsiPath)"
                TableName="Error" 
                Columns="Error;Message">
            <Output TaskParameter="Records" ItemName="ErrorRecords" />
            <Output TaskParameter="ColumnInfo" ItemName="ErrorColumnInfo" />
        </Select>
            
        <CallTarget Targets="DisplayOutputs" />
    </Target>
</Project>

The test uses the "CallTarget" task to run the refactored targets.

Unfortunately, there is a slight problem here. When this task gets run the outputs are not propagated:

     Target SelectFromEmpty:
        ********** Begin SelectFromEmpty Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table:    Name:     Type:    Index: 
            ---------- Error Records ------------

I actually came across this problem in my "real" job a year and a half ago, and asked the experts why this was. It turns out that these items only get propagated to DisplayOutputs if the Target they were defined in has run to completion. So, what really has to happen is that all of these Targets have to be called separately:

 <Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="RunTest">
    <Import Project="SelectTests.Targets" />
    <Import Project="$(MSBuildExtensionsPath)\SetupProjects\SetupProjects.Targets" />

    <Target Name="SelectFromEmpty">
        <Select MsiFileName="$(TestMsiPath)"
                TableName="Error" 
                Columns="Error;Message">
            <Output TaskParameter="Records" ItemName="ErrorRecords" />
            <Output TaskParameter="ColumnInfo" ItemName="ErrorColumnInfo" />
        </Select>
    </Target>

    <Target Name="RunTest">
        <Message Text="********** Begin SelectFromEmpty Test **********" />
        <CallTarget Targets="CopyMsi" />
        <CallTarget Targets="SelectFromEmpty" />
        <CallTarget Targets="DisplayOutputs" />        
    </Target>
</Project>

This uses an explict call to CallTarget (Need link) for all the targets to call. It also would have been possible to use the DependsOn construct that was discussed before. This change seems to work much better:

     Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectFromEmpty.test" (default targets):

    Target RunTest:
        ********** Begin SelectFromEmpty Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table: Error   Name: Error    Type: Integer   Index: 1
            Table: Error   Name: Message    Type: String   Index: 2
            ---------- Error Records ------------

Build succeeded.

Propagating this style to the other tests shows that the tests continue to work, but I won't bother to show them or the results (the tests will be in the zip in the attachment).

Continuing with all the other tests is pretty easy. Unfortunately, when running the test which uses "SELECT *" (which was the entire point of this entry) causes an exception:

     Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectStar.test" (default targets):

    Target RunTest:
        ********** Begin SelectStar Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target SelectStar:
            E:\MWadeBlog\src\Tests\SetupSelectTests\SelectStar.test(5,9): error : Object reference not set to an instance of an object.
        Done building target "SelectStar" in project "SelectStar.test" -- FAILED.
    Done building target "RunTest" in project "SelectStar.test" -- FAILED.

Inspecting the code showed that the Records ItemSpec was being set based on the Columns property. In this case, however, the Columns property is null. I caught this in a couple of other places, but not here. The fix is easy: use the ColumnInfo instead of Columns:

 protected override bool ExecuteTask()
{
    string sql = CreateSelectStatement();

    View view = Msi.OpenView(sql);
    view.Execute(null);

    FillColumnInfo(view);

    Record record = view.Fetch();
    while (record != null)
    {
        ITaskItem item = new TaskItem(TableName);
        for (int i = 0; i < ColumnInfo.Length; i++) 
        {
            item.SetMetadata(ColumnInfo[i].GetMetadata("Name"), record.get_StringData(i + 1)); 
        }

        records.Add(item);
        record = view.Fetch();
    }

    view.Close();
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(view);

    return true;
}

Here are the results of all tests:

 Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" (default targets):


Target RunTests:
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectAllColumns.test" (default targets):

    Target RunTest:
        ********** Begin SelectAllColumns Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table: Error   Name: Error    Type: Integer   Index: 1
            Table: Error   Name: Message    Type: String   Index: 2
            ---------- Error Records ------------
            Table: Error   Error: 1    Message: One
            Table: Error   Error: 2    Message: Two
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectAllColumnsOutOfOrder.test" (default targets):

    Target RunTest:
        ********** Begin SelectAllColumnsOutOfOrder Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table: Error   Name: Message    Type: String   Index: 1
            Table: Error   Name: Error    Type: Integer   Index: 2
            ---------- Error Records ------------
            Table: Error   Error: 1    Message: One
            Table: Error   Error: 2    Message: Two
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectAllColumnsOutOfOrderWithWhere.test" (default targets):

    Target RunTest:
        ********** Begin SelectAllColumnsOutOfOrderWithWhere Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table: Error   Name: Message    Type: String   Index: 1
            Table: Error   Name: Error    Type: Integer   Index: 2
            ---------- Error Records ------------
            Table: Error   Error: 1    Message: One
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectAllColumnsWithWhere.test" (default targets):

    Target RunTest:
        ********** Begin SelectAllColumnsWithWhere Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table: Error   Name: Error    Type: Integer   Index: 1
            Table: Error   Name: Message    Type: String   Index: 2
            ---------- Error Records ------------
            Table: Error   Error: 1    Message: One
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectFromEmpty.test" (default targets):

    Target RunTest:
        ********** Begin SelectFromEmpty Test **********

        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".
        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table: Error   Name: Error    Type: Integer   Index: 1
            Table: Error   Name: Message    Type: String   Index: 2
            ---------- Error Records ------------
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectSomeColumns.test" (default targets):

    Target RunTest:
        ********** Begin SelectSomeColumns Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table: Error   Name: Message    Type: String   Index: 1
            ---------- Error Records ------------
            Table: Error   Error:     Message: One
            Table: Error   Error:     Message: Two
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectSomeColumnsWithWhere.test" (default targets):

    Target RunTest:
        ********** Begin SelectSomeColumnsWithWhere Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table: Error   Name: Message    Type: String   Index: 1
            ---------- Error Records ------------
            Table: Error   Error:     Message: One
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectStar.test" (default targets):

    Target RunTest:
        ********** Begin SelectStar Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table: Error   Name: Error    Type: Integer   Index: 1
            Table: Error   Name: Message    Type: String   Index: 2
            ---------- Error Records ------------
            Table: Error   Error: 1    Message: One
            Table: Error   Error: 2    Message: Two
    __________________________________________________
    Project "E:\MWadeBlog\src\Tests\SetupSelectTests\PostBuild.proj" is building "E:\MWadeBlog\src\Tests\SetupSelectTests\SelectStarWithWhere.test" (default targets):

    Target RunTest:
        ********** Begin SelectStarWithWhere Test **********
        Target CopyMsi:
            Copying file from "E:\MWadeBlog\src\Tests\SetupSelectTests\Debug\SetupSelectTests.msi" to "E:\MWadeBlog\src\Tests\SetupSelectTests\TestResults\Test.Msi".

        Target DisplayOutputs:
            ---------- Error Table Information ------------
            Table: Error   Name: Error    Type: Integer   Index: 1
            Table: Error   Name: Message    Type: String   Index: 2
            ---------- Error Records ------------
            Table: Error   Error: 1    Message: One

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:03.46

A visual inspection shows that the results are what were expected.

Conclusion

In this entry, the Select task was improved by:

  • Allowing for "SELECT *" type statements
  • Provide more complete information about the selected columns
  • Tests were added to provide mental security

Along the way, some new MSBuild topics were broached:

  • Using DependsOnTargets
  • The MSBuild and CallTarget tasks

SetupProjects.Tasks-1.0.20531.0-src.zip