|
12 | 12 | using System.Runtime.Loader; |
13 | 13 |
|
14 | 14 | using Microsoft.Build.Evaluation; |
| 15 | +using Microsoft.Build.Utilities.ProjectCreation; |
15 | 16 | using Microsoft.VisualStudio.TestTools.UnitTesting; |
16 | 17 |
|
17 | 18 | using Ubiquity.NET.Versioning; |
@@ -51,100 +52,110 @@ public void ValidateRepoAssemblyVersion( string targetFramework) |
51 | 52 | }; |
52 | 53 |
|
53 | 54 | // 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)) |
| 55 | + // so the test knows what to expect. |
| 56 | + var (ciBuildIndex, ciBuildName, buildTime, envControl) = TestUtils.GetGeneratedBuildInfo(); |
| 57 | + using(envControl) |
67 | 58 | { |
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); |
| 59 | + if(!string.IsNullOrWhiteSpace(ciBuildIndex)) |
| 60 | + { |
| 61 | + globalProperties["CiBuildIndex"] = ciBuildIndex; |
| 62 | + } |
86 | 63 |
|
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}'" ); |
| 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 | + } |
90 | 70 |
|
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}" ); |
| 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 | + } |
94 | 82 |
|
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}" ); |
| 83 | + using var collection = new ProjectCollection(globalProperties); |
105 | 84 |
|
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" ); |
| 85 | + var (buildResults, props) = Context.CreateTestProjectAndInvokeTestedPackage(targetFramework, collection); |
108 | 86 |
|
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" ); |
| 87 | + LogBuildMessages(buildResults.Output); |
111 | 88 |
|
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" ); |
| 89 | + string? taskAssembly = buildResults.Creator.ProjectInstance.GetOptionalProperty("_Ubiquity_NET_Versioning_Build_Tasks"); |
| 90 | + Assert.IsNotNull( taskAssembly, "Task assembly property should contain full path to the task DLL (Not NULL)" ); |
| 91 | + Context.WriteLine( $"Task Assembly: '{taskAssembly}'" ); |
114 | 92 |
|
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" ); |
| 93 | + Assert.IsFalse( string.IsNullOrWhiteSpace( taskAssembly ), "Task assembly property should contain full path to the task DLL (Not Whitespace)" ); |
| 94 | + Assert.IsNotNull( props.FileVersion, "Generated properties should have a 'FileVersion'" ); |
| 95 | + Context.WriteLine( $"Generated FileVersion: {props.FileVersion}" ); |
117 | 96 |
|
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)) |
| 97 | + var alc = new AssemblyLoadContext("TestALC", isCollectible: true); |
| 98 | + try |
121 | 99 | { |
122 | | - Assert.AreEqual( ciBuildIndex, props.CiBuildIndex, "BuildIndex computed in scripts should match computed value from task"); |
| 100 | + var asm = alc.LoadFromAssemblyPath(taskAssembly); |
| 101 | + Assert.IsNotNull( asm, "should be able to load task assembly" ); |
| 102 | + var asmName = asm.GetName(); |
| 103 | + Version? asmVer = asmName.Version; |
| 104 | + Assert.IsNotNull( asmVer, "Task assembly should have a version" ); |
| 105 | + Context.WriteLine( $"TaskAssemblyVersion: {asmVer}" ); |
| 106 | + Context.WriteLine( $"AssemblyName: {asmName}" ); |
| 107 | + |
| 108 | + Assert.IsNotNull( props.FileVersionMajor, "Property value for Major should exist" ); |
| 109 | + Assert.AreEqual( (int)props.FileVersionMajor, asmVer.Major, "Major value of assembly version should match" ); |
| 110 | + |
| 111 | + Assert.IsNotNull( props.FileVersionMinor, "Property value for Minor should exist" ); |
| 112 | + Assert.AreEqual( (int)props.FileVersionMinor, asmVer.Minor, "Minor value of assembly version should match" ); |
| 113 | + |
| 114 | + Assert.IsNotNull( props.FileVersionBuild, "Property value for Build should exist" ); |
| 115 | + Assert.AreEqual( (int)props.FileVersionBuild, asmVer.Build, "Build value of assembly version should match" ); |
| 116 | + |
| 117 | + Assert.IsNotNull( props.FileVersionRevision, "Property value for Revision should exist" ); |
| 118 | + Assert.AreEqual( (int)props.FileVersionRevision, asmVer.Revision, "Revision value of assembly version should match" ); |
| 119 | + |
| 120 | + // Release builds won't have a CI component by definition so nothing to validate for those |
| 121 | + // Should get local, PR and CI builds before that to hit this case though. |
| 122 | + if(!string.IsNullOrWhiteSpace(ciBuildIndex)) |
| 123 | + { |
| 124 | + Assert.AreEqual( ciBuildIndex, props.CiBuildIndex, "BuildIndex computed in scripts should match computed value from task"); |
| 125 | + } |
| 126 | + |
| 127 | + // Test that AssemblyFileVersion on the task assembly matches expected value |
| 128 | + string fileVersion = ( from attr in asm.CustomAttributes |
| 129 | + where attr.AttributeType.FullName == "System.Reflection.AssemblyFileVersionAttribute" |
| 130 | + let val = attr.ConstructorArguments.Single().Value as string |
| 131 | + where val is not null |
| 132 | + select val |
| 133 | + ).Single(); |
| 134 | + |
| 135 | + Assert.AreEqual(props.FileVersion, fileVersion); |
| 136 | + |
| 137 | + // Test that AssemblyInformationalVersion on the task assembly matches expected value |
| 138 | + string informationalVersion = ( from attr in asm.CustomAttributes |
| 139 | + where attr.AttributeType.FullName == "System.Reflection.AssemblyInformationalVersionAttribute" |
| 140 | + let val = attr.ConstructorArguments.Single().Value as string |
| 141 | + where val is not null |
| 142 | + select val |
| 143 | + ).Single(); |
| 144 | + |
| 145 | + Assert.AreEqual(props.InformationalVersion, informationalVersion); |
| 146 | + } |
| 147 | + finally |
| 148 | + { |
| 149 | + alc.Unload(); |
123 | 150 | } |
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 | 151 | } |
145 | | - finally |
| 152 | + } |
| 153 | + |
| 154 | + private void LogBuildMessages( BuildOutput output ) |
| 155 | + { |
| 156 | + foreach(string msg in output.Messages.Low) |
146 | 157 | { |
147 | | - alc.Unload(); |
| 158 | + Context.WriteLine("MSBUILD: {0}", msg); |
148 | 159 | } |
149 | 160 | } |
150 | 161 | } |
|
0 commit comments