Skip to content

Commit eca4234

Browse files
authored
Added more testing and updated code based on results (#40)
* Task now validates the pre-release number/fix ONLY when a pre-release name is provided. - Otherwise the number/fix are irrelevant. * Adjusted error codes to include the validation of the Fix as it accidentally used the wrong value and therefore, did not have a distinct code. * Added error code for Missing BuildVersionData element in BuildVersionXml file - This resulted in an adjustment of error codes. * Moved clearing of CI info properties to targets file - This is required to have the correct impact as the props file is imported BEFORE any project file. Thus, anything set in a project file would re-set the value to non-empty which isn't the intended state. Co-authored-by: smaillet <25911635+smaillet@users.noreply.github.com>
1 parent 53c8bae commit eca4234

16 files changed

+939
-100
lines changed

docfx/build-tasks/index.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ If not explicitly set this is determined by the automated build flags as describ
218218
section of this document.
219219

220220
## Detected Error Conditions
221+
222+
### Targets file
221223
|Code |Description|
222224
|--------|-----------|
223225
| CSM001 | BuildMajor is a required property, either set it as a global or in the build version XML |
@@ -227,14 +229,32 @@ section of this document.
227229
| CSM005 | FileVersion property not provided AND FileVersionMinor property not found to create it from |
228230
| CSM006 | FileVersion property not provided AND FileVersionBuild property not found to create it from |
229231
| CSM007 | FileVersion property not provided AND FileVersionRevision property not found to create it from |
232+
233+
### CreateVersionInfo Task
234+
|Code |Description|
235+
|--------|-----------|
230236
| CSM100 | BuildMajor value must be in range [0-99999] |
231237
| CSM101 | BuildMinor value must be in range [0-49999] |
232238
| CSM102 | BuildPatch value must be in range [0-9999] |
233-
| CSM103 | PreRelease Name is unknown |
239+
| CSM103 | PreReleaseName is unknown |
234240
| CSM104 | PreReleaseNumber value must be in range [0-99] |
235-
| CSM105 | If CiBuildIndex is set then CiBuildName must also be set; If CiBuildIndex is NOT set then CiBuildName must not be set. |
236-
| CSM106 | CiBuildIndex does not match syntax defined by CSemVer |
237-
| CSM107 | CiBuildName does not match syntax defined by CSemVer |
241+
| CSM105 | PreReleaseFix value must be in range [0-99] |
242+
| CSM106^1^ | If CiBuildIndex is set then CiBuildName must also be set; If CiBuildIndex is NOT set then CiBuildName must not be set. |
243+
| CSM107 | CiBuildIndex does not match syntax defined by CSemVer |
244+
| CSM108 | CiBuildName does not match syntax defined by CSemVer |
245+
246+
### ParseBuildVersionXml Task
247+
|Code |Description|
248+
|--------|-----------|
238249
| CSM200 | BuildVersionXml is required and must not be all whitespace |
239250
| CSM201 | Specified BuildVersionXml does not exist `$(BuildVersionXml)`|
240-
| CSM202 | [Warning] Unexpected attribute on BuildVersionData Element |
251+
| CSM202 | BuildVersionData element does not exist in `$(BuildVersionXml)`|
252+
| CSM203 | [Warning] Unexpected attribute on BuildVersionData Element |
253+
| CSM204 | XML format of file specified by `$(BuildVersionXml)' is invalid |
254+
255+
----
256+
^1^ CSM106 is essentially an internal sanity test. The props/targets files ensure that
257+
`$(CiBuildIndex)` and `$(CiBuildName)` have a value unless $(IsReleaseBuild) is set. In that case
258+
the targets file will force them to empty. So, there's no way to test for or hit this condition
259+
without completely replacing/bypassing the props/targets files for the task. Which is obviously,
260+
an unsupported scenario :grin:.

src/Ubiquity.NET.Versioning.Build.Tasks/CreateVersionInfo.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -221,35 +221,35 @@ private bool ValidateInput( )
221221
hasInputError = true;
222222
}
223223
}
224-
}
225224

226-
if(PreReleaseNumber < 0 || PreReleaseNumber > 99)
227-
{
228-
LogError("CSM104", "PreReleaseNumber value must be in range [0-99]" );
229-
hasInputError = true;
230-
}
225+
if(PreReleaseNumber < 0 || PreReleaseNumber > 99)
226+
{
227+
LogError("CSM104", "PreReleaseNumber value must be in range [0-99]" );
228+
hasInputError = true;
229+
}
231230

232-
if(PreReleaseFix < 0 || PreReleaseFix > 99)
233-
{
234-
LogError("CSM104", "PreReleaseFix value must be in range [0-99]" );
235-
hasInputError = true;
231+
if(PreReleaseNumber != 0 && (PreReleaseFix < 0 || PreReleaseFix > 99))
232+
{
233+
LogError("CSM105", "PreReleaseFix value must be in range [0-99]" );
234+
hasInputError = true;
235+
}
236236
}
237237

238238
if(string.IsNullOrWhiteSpace( CiBuildIndex ) != string.IsNullOrWhiteSpace( CiBuildName ))
239239
{
240-
LogError("CSM105", "If CiBuildIndex is set then CiBuildName must also be set; If CiBuildIndex is NOT set then CiBuildName must not be set." );
240+
LogError("CSM106", "If CiBuildIndex is set then CiBuildName must also be set; If CiBuildIndex is NOT set then CiBuildName must not be set." );
241241
hasInputError = true;
242242
}
243243

244244
if(CiBuildIndex != null && !CiBuildIdRegEx.IsMatch( CiBuildIndex ))
245245
{
246-
LogError("CSM106", "CiBuildIndex does not match syntax defined by CSemVer" );
246+
LogError("CSM107", "CiBuildIndex does not match syntax defined by CSemVer" );
247247
hasInputError = true;
248248
}
249249

250250
if(CiBuildName != null && !CiBuildIdRegEx.IsMatch( CiBuildName ))
251251
{
252-
LogError("CSM107", "CiBuildName does not match syntax defined by CSemVer" );
252+
LogError("CSM108", "CiBuildName does not match syntax defined by CSemVer" );
253253
hasInputError = true;
254254
}
255255

src/Ubiquity.NET.Versioning.Build.Tasks/ParseBuildVersionXml.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using System;
88
using System.Diagnostics.CodeAnalysis;
99
using System.IO;
10+
using System.Xml.Linq;
11+
1012
using Microsoft.Build.Framework;
1113
using Microsoft.Build.Utilities;
1214

@@ -55,8 +57,13 @@ public override bool Execute( )
5557
}
5658

5759
using var stream = File.OpenText( BuildVersionXml );
58-
var xdoc = System.Xml.Linq.XDocument.Load( stream, System.Xml.Linq.LoadOptions.None );
60+
var xdoc = XDocument.Load( stream, System.Xml.Linq.LoadOptions.None );
5961
var data = xdoc.Element( "BuildVersionData" );
62+
if (data is null)
63+
{
64+
LogError("CSM202", $"BuildVersionData element does not exist in '{BuildVersionXml}'");
65+
return false;
66+
}
6067

6168
foreach( var attrib in data.Attributes( ) )
6269
{
@@ -93,7 +100,7 @@ public override bool Execute( )
93100
break;
94101

95102
default:
96-
LogWarning( "CSM202", "Unexpected attribute {0}", attrib.Name.LocalName );
103+
LogWarning( "CSM203", "Unexpected attribute {0}", attrib.Name.LocalName );
97104
break;
98105
}
99106
}
@@ -115,6 +122,11 @@ public override bool Execute( )
115122
Log.LogMessage(MessageImportance.Low, $"-{nameof(ParseBuildVersionXml)} Task");
116123
return true;
117124
}
125+
catch(System.Xml.XmlException)
126+
{
127+
LogError("CSM204", "XML format of '{0}' is invalid", BuildVersionXml!);
128+
return false;
129+
}
118130
catch(Exception ex)
119131
{
120132
Log.LogErrorFromException(ex, showStackTrace: true);

src/Ubiquity.NET.Versioning.Build.Tasks/build/Ubiquity.NET.Versioning.Build.Tasks.props

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project>
1+
<Project TreatAsLocalProperty="BuildTime;CiBuildName;IsPullRequestBuild;IsAutomatedBuild;IsReleaseBuild">
22
<PropertyGroup>
33
<!--
44
Force the time-stamp into ISO 8601 format so it is locale neutral
@@ -21,11 +21,4 @@
2121
<CiBuildName Condition="'$(CiBuildName)'=='' AND $(IsAutomatedBuild) AND !$(IsReleaseBuild)">BLD</CiBuildName>
2222
<CiBuildName Condition="'$(CiBuildName)'=='' AND !$(IsReleaseBuild)">ZZZ</CiBuildName>
2323
</PropertyGroup>
24-
25-
<!-- Force empty values for CI Build info in a release build -->
26-
<PropertyGroup Condition="$(IsReleaseBuild)">
27-
<CiBuildName/>
28-
<CiBuildIndex />
29-
<BuildTime />
30-
</PropertyGroup>
3124
</Project>

src/Ubiquity.NET.Versioning.Build.Tasks/build/Ubiquity.NET.Versioning.Build.Tasks.targets

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
1-
<Project TreatAsLocalProperty="_TaskFolder;_Ubiquity_NET_Versioning_Build_Tasks;__FileVersion">
1+
<Project TreatAsLocalProperty="_TaskFolder;_Ubiquity_NET_Versioning_Build_Tasks;__FileVersion;BuildTime;CiBuildIndex;CiBuildName">
22
<PropertyGroup>
33
<_TaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' ">tasks\netstandard2.0\</_TaskFolder>
44
<_TaskFolder Condition=" '$(MSBuildRuntimeType)' != 'Core' ">tasks\net48\</_TaskFolder>
55
<_Ubiquity_NET_Versioning_Build_Tasks>$([MSBuild]::NormalizePath($(MSBuildThisFileDirectory)..\$(_TaskFolder)Ubiquity.NET.Versioning.Build.Tasks.dll))</_Ubiquity_NET_Versioning_Build_Tasks>
66
</PropertyGroup>
77

8+
<!--
9+
Set empty values for CI Build info in a release build.
10+
This must appear in the targets file to ensure that the properties are reset
11+
AFTER any project might set them.
12+
-->
13+
<PropertyGroup Condition="$(IsReleaseBuild)">
14+
<BuildTime/>
15+
<CiBuildIndex/>
16+
<CiBuildName/>
17+
</PropertyGroup>
18+
819
<UsingTask TaskName="CreateVersionInfo" AssemblyFile="$(_Ubiquity_NET_Versioning_Build_Tasks)"/>
920
<UsingTask TaskName="GetBuildIndexFromTime" AssemblyFile="$(_Ubiquity_NET_Versioning_Build_Tasks)"/>
1021
<UsingTask TaskName="ParseBuildVersionXml" AssemblyFile="$(_Ubiquity_NET_Versioning_Build_Tasks)"/>

src/Ubiquity.Versioning.Build.Tasks.UT/AssemblyValidationTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ public void ValidateRepoAssemblyVersion( string targetFramework)
8282

8383
using var collection = new ProjectCollection(globalProperties);
8484

85-
var (buildResults, props) = Context.CreateTestProjectAndInvokeTestedPackage(targetFramework, collection);
85+
using var fullResults = Context.CreateTestProjectAndInvokeTestedPackage(targetFramework, collection);
86+
var (buildResults, props) = fullResults;
8687
LogBuildMessages(buildResults.Output);
8788
Assert.IsTrue(buildResults.Success);
8889

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="BuildTaskErrorTests.cs" company="Ubiquity.NET Contributors">
3+
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
4+
// </copyright>
5+
// -----------------------------------------------------------------------
6+
7+
using System;
8+
using System.Collections.Generic;
9+
using System.Linq;
10+
11+
using Microsoft.Build.Evaluation;
12+
using Microsoft.VisualStudio.TestTools.UnitTesting;
13+
14+
namespace Ubiquity.Versioning.Build.Tasks.UT
15+
{
16+
[TestClass]
17+
[TestCategory("Error Validation")]
18+
public class BuildTaskErrorTests
19+
{
20+
public BuildTaskErrorTests( TestContext ctx )
21+
{
22+
ArgumentNullException.ThrowIfNull(ctx);
23+
ArgumentException.ThrowIfNullOrWhiteSpace(ctx.TestResultsDirectory);
24+
25+
Context = ctx;
26+
}
27+
28+
public TestContext Context { get; }
29+
30+
[TestMethod]
31+
public void CSM001_Missing_BuildMajor_should_fail( )
32+
{
33+
// use a build version XML that has no attributes to get the expected error
34+
// Otherwise, MSBUILD kicks in and complains about missing required param
35+
string buildVersionXml = Context.CreateEmptyBuildVersionXmlWithRandomName();
36+
var globalProperties = new Dictionary<string, string>
37+
{
38+
["BuildVersionXml"] = buildVersionXml
39+
};
40+
41+
using var collection = new ProjectCollection(globalProperties);
42+
using var fullResults = Context.CreateTestProjectAndInvokeTestedPackage("net8.0", collection);
43+
var (buildResults, props) = fullResults;
44+
Assert.IsFalse(buildResults.Success);
45+
var errors = buildResults.Output.ErrorEvents.Where(evt=>evt.Code == "CSM001").ToList();
46+
Assert.AreEqual(1, errors.Count);
47+
}
48+
49+
[TestMethod]
50+
public void CSM002_Missing_BuildMinor_should_fail( )
51+
{
52+
var globalProperties = new Dictionary<string, string>
53+
{
54+
["BuildMajor"] = "10"
55+
};
56+
57+
using var collection = new ProjectCollection(globalProperties);
58+
using var fullResults = Context.CreateTestProjectAndInvokeTestedPackage("net8.0", collection);
59+
var (buildResults, props) = fullResults;
60+
Assert.IsFalse(buildResults.Success);
61+
var errors = buildResults.Output.ErrorEvents.Where(evt=>evt.Code == "CSM002").ToList();
62+
Assert.AreEqual(1, errors.Count);
63+
}
64+
65+
[TestMethod]
66+
public void CSM003_Missing_BuildPatch_should_fail( )
67+
{
68+
var globalProperties = new Dictionary<string, string>
69+
{
70+
["BuildMajor"] = "10",
71+
["BuildMinor"] = "1",
72+
};
73+
74+
using var collection = new ProjectCollection(globalProperties);
75+
using var fullResults = Context.CreateTestProjectAndInvokeTestedPackage("net8.0", collection);
76+
var (buildResults, props) = fullResults;
77+
Assert.IsFalse(buildResults.Success);
78+
var errors = buildResults.Output.ErrorEvents.Where(evt=>evt.Code == "CSM003").ToList();
79+
Assert.AreEqual(1, errors.Count);
80+
}
81+
82+
[TestMethod]
83+
public void CSM004_Missing_FileVersionMajor_should_fail( )
84+
{
85+
var globalProperties = new Dictionary<string, string>
86+
{
87+
["BuildMajor"] = "10",
88+
["BuildMinor"] = "1",
89+
["BuildPatch"] = "2",
90+
["FullBuildNumber"] = "\t", // Present but all whitespace; Presence of this skips the CreateVersionInfoTask
91+
};
92+
93+
using var collection = new ProjectCollection(globalProperties);
94+
using var fullResults = Context.CreateTestProjectAndInvokeTestedPackage("net8.0", collection);
95+
var (buildResults, props) = fullResults;
96+
Assert.IsFalse(buildResults.Success);
97+
var errors = buildResults.Output.ErrorEvents.Where(evt=>evt.Code == "CSM004").ToList();
98+
Assert.AreEqual(1, errors.Count);
99+
}
100+
101+
[TestMethod]
102+
public void CSM006_Missing_FileVersionMinor_should_fail( )
103+
{
104+
var globalProperties = new Dictionary<string, string>
105+
{
106+
["BuildMajor"] = "10",
107+
["BuildMinor"] = "1",
108+
["BuildPatch"] = "2",
109+
["FullBuildNumber"] = "\t", // Present but all whitespace; Presence of this skips the CreateVersionInfoTask
110+
["FileVersionMajor"] = "1", // avoid CSM004 to allow testing of next field requirement
111+
};
112+
113+
using var collection = new ProjectCollection(globalProperties);
114+
using var fullResults = Context.CreateTestProjectAndInvokeTestedPackage("net8.0", collection);
115+
var (buildResults, props) = fullResults;
116+
Assert.IsFalse(buildResults.Success);
117+
var errors = buildResults.Output.ErrorEvents.Where(evt=>evt.Code == "CSM005").ToList();
118+
Assert.AreEqual(1, errors.Count);
119+
}
120+
121+
[TestMethod]
122+
public void CSM006_Missing_FileVersionBuild_should_fail( )
123+
{
124+
var globalProperties = new Dictionary<string, string>
125+
{
126+
["BuildMajor"] = "10",
127+
["BuildMinor"] = "1",
128+
["BuildPatch"] = "2",
129+
["FullBuildNumber"] = "\t", // Present but all whitespace; Presence of this skips the CreateVersionInfoTask
130+
["FileVersionMajor"] = "1", // avoid CSM004 to allow testing of next field requirement
131+
["FileVersionMinor"] = "2", // avoid CSM005 to allow testing of next field requirement
132+
};
133+
134+
using var collection = new ProjectCollection(globalProperties);
135+
using var fullResults = Context.CreateTestProjectAndInvokeTestedPackage("net8.0", collection);
136+
var (buildResults, props) = fullResults;
137+
Assert.IsFalse(buildResults.Success);
138+
var errors = buildResults.Output.ErrorEvents.Where(evt=>evt.Code == "CSM006").ToList();
139+
Assert.AreEqual(1, errors.Count);
140+
}
141+
142+
[TestMethod]
143+
public void CSM007_Missing_FileVersionRevision_should_fail( )
144+
{
145+
var globalProperties = new Dictionary<string, string>
146+
{
147+
["BuildMajor"] = "10",
148+
["BuildMinor"] = "1",
149+
["BuildPatch"] = "2",
150+
["FullBuildNumber"] = "\t", // Present but all whitespace; Presence of this skips the CreateVersionInfoTask
151+
["FileVersionMajor"] = "1", // avoid CSM004 to allow testing of next field requirement
152+
["FileVersionMinor"] = "2", // avoid CSM005 to allow testing of next field requirement
153+
["FileVersionBuild"] = "3", // avoid CSM006 to allow testing of next field requirement
154+
};
155+
156+
using var collection = new ProjectCollection(globalProperties);
157+
using var fullResults = Context.CreateTestProjectAndInvokeTestedPackage("net8.0", collection);
158+
var (buildResults, props) = fullResults;
159+
Assert.IsFalse(buildResults.Success);
160+
var errors = buildResults.Output.ErrorEvents.Where(evt=>evt.Code == "CSM007").ToList();
161+
Assert.AreEqual(1, errors.Count);
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)