Skip to content

Commit f51faec

Browse files
authored
Refactoring tests and additional test cases (#27)
1 parent 4cc535e commit f51faec

File tree

6 files changed

+555
-345
lines changed

6 files changed

+555
-345
lines changed

src/Ubiquity.NET.Versioning/FileVersionQuad.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ public Version ToVersion( )
8080
return new Version( Major, Minor, Build, Revision );
8181
}
8282

83+
/// <summary>Converts this instance to a dotted string form</summary>
84+
/// <returns>Formatted string</returns>
85+
public override string ToString( ) => $"{Major}.{Minor}.{Build}.{Revision}";
86+
8387
/// <summary>Converts a <see cref="Version"/> value to a <see cref="FileVersionQuad"/> if possible</summary>
8488
/// <param name="v">Version to convert</param>
8589
/// <returns>Resulting <see cref="FileVersionQuad"/></returns>
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="AssemblyValidationTests.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.Globalization;
10+
using System.IO;
11+
using System.Linq;
12+
using System.Runtime.Loader;
13+
14+
using Microsoft.Build.Evaluation;
15+
using Microsoft.VisualStudio.TestTools.UnitTesting;
16+
17+
using Ubiquity.NET.Versioning;
18+
19+
namespace Ubiquity.Versioning.Build.Tasks.UT
20+
{
21+
[TestClass]
22+
[TestCategory("Build Task Assembly")]
23+
public class AssemblyValidationTests
24+
{
25+
public AssemblyValidationTests( TestContext ctx )
26+
{
27+
ArgumentNullException.ThrowIfNull(ctx);
28+
ArgumentException.ThrowIfNullOrWhiteSpace( ctx.TestResultsDirectory );
29+
30+
Context = ctx;
31+
}
32+
33+
public TestContext Context { get; }
34+
35+
// Repo assembly versions are defined via PowerShell (Or hard coded in the project file
36+
// for IDE builds) as the build task is not usable for itself. This verifies the build
37+
// versioning used in the scripts matches what is expected for an end-consumer by testing
38+
// the assemblies versioning information against the output from THAT task assembly. This
39+
// should be identical for IDE builds AND command line builds. Basically this verifies the
40+
// manual IDE properties as well as the automated build scripting matches what the task
41+
// itself produces. If anything is out of whack, this will complain.
42+
[TestMethod]
43+
[DataRow("netstandard2.0")]
44+
[DataRow("net48")]
45+
[DataRow("net8.0")]
46+
public void ValidateRepoAssemblyVersion( string targetFramework)
47+
{
48+
var globalProperties = new Dictionary<string,string>
49+
{
50+
["BuildVersionXml"] = Path.Combine(Context.GetRepoRoot(), "BuildVersion.xml"),
51+
};
52+
53+
// For a CI build load the ciBuildIndex and ciBuildName from the generatedversion.props file
54+
// so the test knows what to expect. This does NOT verify the behavior of the tasks exactly unfortunately.
55+
// There is a non-determinism in computing the index based on a time stamp in particular a single date/time
56+
// string is converted based on seconds since midnight today (in UTC) so if two different implementations
57+
// compute a value at a different time that varies by as much as 2 seconds, then they will produce different
58+
// results even if behaving correctly. The use of seconds since midnight today makes it non-deterministic...
59+
// Unfortunately that's the algorithm chosen, though since this is a major release (and a full rename that
60+
// is something to re-visit) Until, that is deterministic, use the generated CI info all up. Other tests will
61+
// need to validate the behavior of the task.
62+
var (ciBuildIndex, ciBuildName, buildTime) = TestUtils.GetGeneratedCiBuildInfo();
63+
64+
// Build name depends on context of the build (Local, PR, CI, Release)
65+
// and therefore is NOT hard-coded in the tests.
66+
if(!string.IsNullOrWhiteSpace(ciBuildName))
67+
{
68+
globalProperties["CiBuildName"] = ciBuildName;
69+
}
70+
71+
if(!string.IsNullOrWhiteSpace(buildTime))
72+
{
73+
// NOT using exact parsing as that's 'flaky' at best and doesn't actually handle all ISO-8601 formats
74+
// Also, NOT using assumption of UTC as commit dates from repo are local time based. ToBuildIndex() will
75+
// convert to UTC so that the resulting index is still consistent.
76+
var parsedBuildTime = DateTime.Parse(buildTime, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
77+
string indexFromLib = parsedBuildTime.ToBuildIndex();
78+
Assert.AreEqual(indexFromLib, ciBuildIndex, "Index computed with versioning library should match the index computed by scripts");
79+
80+
globalProperties["BuildTime"] = buildTime;
81+
}
82+
83+
using var collection = new ProjectCollection(globalProperties);
84+
85+
var (buildResults, props) = Context.CreateTestProjectAndInvokeTestedPackage(targetFramework, collection);
86+
87+
string? taskAssembly = buildResults.Creator.ProjectInstance.GetOptionalProperty("_Ubiquity_NET_Versioning_Build_Tasks");
88+
Assert.IsNotNull( taskAssembly, "Task assembly property should contain full path to the task DLL (Not NULL)" );
89+
Context.WriteLine( $"Task Assembly: '{taskAssembly}'" );
90+
91+
Assert.IsFalse( string.IsNullOrWhiteSpace( taskAssembly ), "Task assembly property should contain full path to the task DLL (Not Whitespace)" );
92+
Assert.IsNotNull( props.FileVersion, "Generated properties should have a 'FileVersion'" );
93+
Context.WriteLine( $"Generated FileVersion: {props.FileVersion}" );
94+
95+
var alc = new AssemblyLoadContext("TestALC", isCollectible: true);
96+
try
97+
{
98+
var asm = alc.LoadFromAssemblyPath(taskAssembly);
99+
Assert.IsNotNull( asm, "should be able to load task assembly" );
100+
var asmName = asm.GetName();
101+
Version? asmVer = asmName.Version;
102+
Assert.IsNotNull( asmVer, "Task assembly should have a version" );
103+
Context.WriteLine( $"TaskAssemblyVersion: {asmVer}" );
104+
Context.WriteLine( $"AssemblyName: {asmName}" );
105+
106+
Assert.IsNotNull( props.FileVersionMajor, "Property value for Major should exist" );
107+
Assert.AreEqual( (int)props.FileVersionMajor, asmVer.Major, "Major value of assembly version should match" );
108+
109+
Assert.IsNotNull( props.FileVersionMinor, "Property value for Minor should exist" );
110+
Assert.AreEqual( (int)props.FileVersionMinor, asmVer.Minor, "Minor value of assembly version should match" );
111+
112+
Assert.IsNotNull( props.FileVersionBuild, "Property value for Build should exist" );
113+
Assert.AreEqual( (int)props.FileVersionBuild, asmVer.Build, "Build value of assembly version should match" );
114+
115+
Assert.IsNotNull( props.FileVersionRevision, "Property value for Revision should exist" );
116+
Assert.AreEqual( (int)props.FileVersionRevision, asmVer.Revision, "Revision value of assembly version should match" );
117+
118+
// Release builds won't have a CI component by definition so nothing to validate for those
119+
// Should get local, PR and CI builds before that to hit this case though.
120+
if(!string.IsNullOrWhiteSpace(ciBuildIndex))
121+
{
122+
Assert.AreEqual( ciBuildIndex, props.CiBuildIndex, "BuildIndex computed in scripts should match computed value from task");
123+
}
124+
125+
// Test that AssemblyFileVersion on the task assembly matches expected value
126+
string fileVersion = ( from attr in asm.CustomAttributes
127+
where attr.AttributeType.FullName == "System.Reflection.AssemblyFileVersionAttribute"
128+
let val = attr.ConstructorArguments.Single().Value as string
129+
where val is not null
130+
select val
131+
).Single();
132+
133+
Assert.AreEqual(props.FileVersion, fileVersion);
134+
135+
// Test that AssemblyInformationalVersion on the task assembly matches expected value
136+
string informationalVersion = ( from attr in asm.CustomAttributes
137+
where attr.AttributeType.FullName == "System.Reflection.AssemblyInformationalVersionAttribute"
138+
let val = attr.ConstructorArguments.Single().Value as string
139+
where val is not null
140+
select val
141+
).Single();
142+
143+
Assert.AreEqual(props.InformationalVersion, informationalVersion);
144+
}
145+
finally
146+
{
147+
alc.Unload();
148+
}
149+
}
150+
}
151+
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@ namespace Ubiquity.Versioning.Build.Tasks.UT
1818
/// </remarks>
1919
internal readonly record struct BuildProperties
2020
{
21-
public BuildProperties( BuildResult result )
21+
public BuildProperties( ProjectInstance inst )
2222
{
23-
ArgumentNullException.ThrowIfNull(result.ProjectStateAfterBuild);
24-
var inst = result.ProjectStateAfterBuild;
23+
ArgumentNullException.ThrowIfNull(inst);
2524

2625
// Manually or from Ubiquity.NET.Versioning.Build.Tasks.props
2726
BuildTime = inst.GetPropertyValue("BuildTime");
@@ -46,6 +45,8 @@ public BuildProperties( BuildResult result )
4645
FileVersionRevision = inst.GetPropertyAs<UInt16>("FileVersionRevision");
4746
PackageVersion = inst.GetOptionalProperty("PackageVersion");
4847

48+
BuildMeta = inst.GetOptionalProperty("BuildMeta");
49+
4950
// from Ubiquity.NET.Versioning.Build.Tasks.targets/SetVersionDependentProperties target
5051
FileVersion = inst.GetOptionalProperty("FileVersion");
5152
AssemblyVersion = inst.GetOptionalProperty("AssemblyVersion");

0 commit comments

Comments
 (0)