2727public class BuildIntegrationTests : RepoTestBase
2828{
2929 private const string GitVersioningTargetsFileName = "NerdBank.GitVersioning.targets" ;
30+ private const string UnitTestCloudBuildPrefix = "UnitTest: " ;
3031 private static readonly string [ ] ToxicEnvironmentVariablePrefixes = new string [ ]
3132 {
3233 "APPVEYOR" ,
@@ -42,7 +43,6 @@ public class BuildIntegrationTests : RepoTestBase
4243 // Set global properties to neutralize environment variables
4344 // that might actually be defined by a CI that is building and running these tests.
4445 { "PublicRelease" , string . Empty } ,
45- { "_NBGV_UnitTest" , "true" }
4646 } ;
4747 private Random random ;
4848
@@ -59,6 +59,7 @@ public BuildIntegrationTests(ITestOutputHelper logger)
5959 this . LoadTargetsIntoProjectCollection ( ) ;
6060 this . testProject = this . CreateProjectRootElement ( this . projectDirectory , "test.proj" ) ;
6161 this . globalProperties . Add ( "NerdbankGitVersioningTasksPath" , Environment . CurrentDirectory + "\\ " ) ;
62+ Environment . SetEnvironmentVariable ( "_NBGV_UnitTest" , "true" ) ;
6263
6364 // Sterilize the test of any environment variables.
6465 foreach ( System . Collections . DictionaryEntry variable in Environment . GetEnvironmentVariables ( ) )
@@ -71,6 +72,12 @@ public BuildIntegrationTests(ITestOutputHelper logger)
7172 }
7273 }
7374
75+ protected override void Dispose ( bool disposing )
76+ {
77+ Environment . SetEnvironmentVariable ( "_NBGV_UnitTest" , string . Empty ) ;
78+ base . Dispose ( disposing ) ;
79+ }
80+
7481 [ Fact ]
7582 public async Task GetBuildVersion_Returns_BuildVersion_Property ( )
7683 {
@@ -104,7 +111,7 @@ public async Task GetBuildVersion_OutsideGit_PointingToGit()
104111
105112 // Write the same version file to the 'real' repo
106113 this . WriteVersionFile ( version ) ;
107-
114+
108115 // Point the project to the 'real' repo
109116 this . testProject . AddProperty ( "GitRepoRoot" , this . RepoPath ) ;
110117
@@ -362,8 +369,8 @@ public static IEnumerable<object[]> CloudBuildOfBranch(string branchName)
362369 {
363370 return new object [ ] [ ]
364371 {
365- new object [ ] { CloudBuild . AppVeyor . Add ( "APPVEYOR_REPO_BRANCH" , branchName ) } ,
366- new object [ ] { CloudBuild . VSTS . Add ( "BUILD_SOURCEBRANCH" , $ "refs/heads/{ branchName } ") } ,
372+ new object [ ] { CloudBuild . AppVeyor . SetItem ( "APPVEYOR_REPO_BRANCH" , branchName ) } ,
373+ new object [ ] { CloudBuild . VSTS . SetItem ( "BUILD_SOURCEBRANCH" , $ "refs/heads/{ branchName } ") } ,
367374 } ;
368375 }
369376
@@ -382,14 +389,12 @@ public async Task PublicRelease_RegEx_SatisfiedByCI(IReadOnlyDictionary<string,
382389 // Don't actually switch the checked out branch in git. CI environment variables
383390 // should take precedence over actual git configuration. (Why? because these variables may
384391 // retain information about which tag was checked out on a detached head).
385- foreach ( var property in serverProperties )
392+ using ( ApplyEnvironmentVariables ( serverProperties ) )
386393 {
387- this . globalProperties [ property . Key ] = property . Value ;
394+ var buildResult = await this . BuildAsync ( ) ;
395+ Assert . True ( buildResult . PublicRelease ) ;
396+ AssertStandardProperties ( versionOptions , buildResult ) ;
388397 }
389-
390- var buildResult = await this . BuildAsync ( ) ;
391- Assert . True ( buildResult . PublicRelease ) ;
392- AssertStandardProperties ( versionOptions , buildResult ) ;
393398 }
394399
395400 public static object [ ] [ ] CloudBuildVariablesData
@@ -407,44 +412,42 @@ public static object[][] CloudBuildVariablesData
407412 [ MemberData ( nameof ( CloudBuildVariablesData ) ) ]
408413 public async Task CloudBuildVariables_SetInCI ( IReadOnlyDictionary < string , string > properties , string expectedMessage )
409414 {
410- foreach ( var property in properties )
411- {
412- this . globalProperties [ property . Key ] = property . Value ;
413- }
414-
415- string keyName = "n1" ;
416- string value = "v1" ;
417- this . testProject . AddItem ( "CloudBuildVersionVars" , keyName , new Dictionary < string , string > { { "Value" , value } } ) ;
418-
419- string alwaysExpectedMessage = expectedMessage
420- . Replace ( "{NAME}" , keyName )
421- . Replace ( "{VALUE}" , value ) ;
422-
423- var versionOptions = new VersionOptions
415+ using ( ApplyEnvironmentVariables ( properties ) )
424416 {
425- Version = SemanticVersion . Parse ( "1.0" ) ,
426- CloudBuild = new VersionOptions . CloudBuildOptions { SetVersionVariables = true } ,
427- } ;
428- this . WriteVersionFile ( versionOptions ) ;
429- this . InitializeSourceControl ( ) ;
417+ string keyName = "n1" ;
418+ string value = "v1" ;
419+ this . testProject . AddItem ( "CloudBuildVersionVars" , keyName , new Dictionary < string , string > { { "Value" , value } } ) ;
430420
431- var buildResult = await this . BuildAsync ( ) ;
432- AssertStandardProperties ( versionOptions , buildResult ) ;
433- string conditionallyExpectedMessage = expectedMessage
434- . Replace ( "{NAME}" , "GitBuildVersion" )
435- . Replace ( "{VALUE}" , buildResult . BuildVersion ) ;
436- Assert . Contains ( alwaysExpectedMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
437- Assert . Contains ( conditionallyExpectedMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
421+ string alwaysExpectedMessage = UnitTestCloudBuildPrefix + expectedMessage
422+ . Replace ( "{NAME}" , keyName )
423+ . Replace ( "{VALUE}" , value ) ;
438424
439- versionOptions . CloudBuild . SetVersionVariables = false ;
440- this . WriteVersionFile ( versionOptions ) ;
441- buildResult = await this . BuildAsync ( ) ;
442- AssertStandardProperties ( versionOptions , buildResult ) ;
443- conditionallyExpectedMessage = expectedMessage
444- . Replace ( "{NAME}" , "GitBuildVersion" )
445- . Replace ( "{VALUE}" , buildResult . BuildVersion ) ;
446- Assert . Contains ( alwaysExpectedMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
447- Assert . DoesNotContain ( conditionallyExpectedMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
425+ var versionOptions = new VersionOptions
426+ {
427+ Version = SemanticVersion . Parse ( "1.0" ) ,
428+ CloudBuild = new VersionOptions . CloudBuildOptions { SetVersionVariables = true } ,
429+ } ;
430+ this . WriteVersionFile ( versionOptions ) ;
431+ this . InitializeSourceControl ( ) ;
432+
433+ var buildResult = await this . BuildAsync ( ) ;
434+ AssertStandardProperties ( versionOptions , buildResult ) ;
435+ string conditionallyExpectedMessage = UnitTestCloudBuildPrefix + expectedMessage
436+ . Replace ( "{NAME}" , "GitBuildVersion" )
437+ . Replace ( "{VALUE}" , buildResult . BuildVersion ) ;
438+ Assert . Contains ( alwaysExpectedMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
439+ Assert . Contains ( conditionallyExpectedMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
440+
441+ versionOptions . CloudBuild . SetVersionVariables = false ;
442+ this . WriteVersionFile ( versionOptions ) ;
443+ buildResult = await this . BuildAsync ( ) ;
444+ AssertStandardProperties ( versionOptions , buildResult ) ;
445+ conditionallyExpectedMessage = UnitTestCloudBuildPrefix + expectedMessage
446+ . Replace ( "{NAME}" , "GitBuildVersion" )
447+ . Replace ( "{VALUE}" , buildResult . BuildVersion ) ;
448+ Assert . Contains ( alwaysExpectedMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
449+ Assert . DoesNotContain ( conditionallyExpectedMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
450+ }
448451 }
449452
450453 private static VersionOptions BuildNumberVersionOptionsBasis
@@ -483,16 +486,23 @@ public async Task BuildNumber_SetInCI(VersionOptions versionOptions, IReadOnlyDi
483486 {
484487 this . WriteVersionFile ( versionOptions ) ;
485488 this . InitializeSourceControl ( ) ;
486-
487- foreach ( var property in properties )
489+ using ( ApplyEnvironmentVariables ( properties ) )
488490 {
489- this . globalProperties [ property . Key ] = property . Value ;
491+ var buildResult = await this . BuildAsync ( ) ;
492+ AssertStandardProperties ( versionOptions , buildResult ) ;
493+ expectedBuildNumberMessage = expectedBuildNumberMessage . Replace ( "{CLOUDBUILDNUMBER}" , buildResult . CloudBuildNumber ) ;
494+ Assert . Contains ( UnitTestCloudBuildPrefix + expectedBuildNumberMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
490495 }
491496
492- var buildResult = await this . BuildAsync ( ) ;
493- AssertStandardProperties ( versionOptions , buildResult ) ;
494- expectedBuildNumberMessage = expectedBuildNumberMessage . Replace ( "{CLOUDBUILDNUMBER}" , buildResult . CloudBuildNumber ) ;
495- Assert . Contains ( expectedBuildNumberMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
497+ versionOptions . CloudBuild . BuildNumber . Enabled = false ;
498+ this . WriteVersionFile ( versionOptions ) ;
499+ using ( ApplyEnvironmentVariables ( properties ) )
500+ {
501+ var buildResult = await this . BuildAsync ( ) ;
502+ AssertStandardProperties ( versionOptions , buildResult ) ;
503+ expectedBuildNumberMessage = expectedBuildNumberMessage . Replace ( "{CLOUDBUILDNUMBER}" , buildResult . CloudBuildNumber ) ;
504+ Assert . DoesNotContain ( UnitTestCloudBuildPrefix + expectedBuildNumberMessage , buildResult . LoggedEvents . Select ( e => e . Message . TrimEnd ( ) ) ) ;
505+ }
496506 }
497507
498508 [ Theory ]
@@ -526,12 +536,15 @@ public async Task PublicRelease_RegEx_SatisfiedByCheckedOutBranch()
526536 this . WriteVersionFile ( versionOptions ) ;
527537 this . InitializeSourceControl ( ) ;
528538
529- // Check out a branch that conforms.
530- var releaseBranch = this . Repo . CreateBranch ( "release" ) ;
531- this . Repo . Checkout ( releaseBranch ) ;
532- var buildResult = await this . BuildAsync ( ) ;
533- Assert . True ( buildResult . PublicRelease ) ;
534- AssertStandardProperties ( versionOptions , buildResult ) ;
539+ using ( ApplyEnvironmentVariables ( CloudBuild . SuppressEnvironment ) )
540+ {
541+ // Check out a branch that conforms.
542+ var releaseBranch = this . Repo . CreateBranch ( "release" ) ;
543+ this . Repo . Checkout ( releaseBranch ) ;
544+ var buildResult = await this . BuildAsync ( ) ;
545+ Assert . True ( buildResult . PublicRelease ) ;
546+ AssertStandardProperties ( versionOptions , buildResult ) ;
547+ }
535548 }
536549
537550 [ Theory ]
@@ -705,6 +718,20 @@ private static Version GetExpectedAssemblyVersion(VersionOptions versionOptions,
705718 return assemblyVersion ;
706719 }
707720
721+ private static RestoreEnvironmentVariables ApplyEnvironmentVariables ( IReadOnlyDictionary < string , string > variables )
722+ {
723+ Requires . NotNull ( variables , nameof ( variables ) ) ;
724+
725+ var oldValues = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
726+ foreach ( var variable in variables )
727+ {
728+ oldValues [ variable . Key ] = Environment . GetEnvironmentVariable ( variable . Key ) ;
729+ Environment . SetEnvironmentVariable ( variable . Key , variable . Value ) ;
730+ }
731+
732+ return new RestoreEnvironmentVariables ( oldValues ) ;
733+ }
734+
708735 private void AssertStandardProperties ( VersionOptions versionOptions , BuildResults buildResult , string relativeProjectDirectory = null )
709736 {
710737 int versionHeight = this . Repo . GetVersionHeight ( relativeProjectDirectory ) ;
@@ -728,9 +755,6 @@ private void AssertStandardProperties(VersionOptions versionOptions, BuildResult
728755 Assert . Equal ( $ "{ assemblyVersion . Major } .{ assemblyVersion . Minor } .{ assemblyVersion . Build } .{ assemblyVersion . Revision } ", buildResult . AssemblyVersion ) ;
729756
730757 Assert . Equal ( idAsVersion . Build . ToString ( ) , buildResult . BuildNumber ) ;
731- Assert . Equal ( idAsVersion . Build . ToString ( ) , buildResult . BuildNumberFirstAndSecondComponentsIfApplicable ) ;
732- Assert . Equal ( idAsVersion . Build . ToString ( ) , buildResult . BuildNumberFirstComponent ) ;
733- Assert . Equal ( string . Empty , buildResult . BuildNumberSecondComponent ) ;
734758 Assert . Equal ( $ "{ version } ", buildResult . BuildVersion ) ;
735759 Assert . Equal ( $ "{ idAsVersion . Major } .{ idAsVersion . Minor } .{ idAsVersion . Build } ", buildResult . BuildVersion3Components ) ;
736760 Assert . Equal ( idAsVersion . Build . ToString ( ) , buildResult . BuildVersionNumberComponent ) ;
@@ -846,12 +870,36 @@ private void MakeItAVBProject()
846870 csharpImport . Project = @"$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" ;
847871 }
848872
873+ private struct RestoreEnvironmentVariables : IDisposable
874+ {
875+ private readonly IReadOnlyDictionary < string , string > applyVariables ;
876+
877+ internal RestoreEnvironmentVariables ( IReadOnlyDictionary < string , string > applyVariables )
878+ {
879+ this . applyVariables = applyVariables ;
880+ }
881+
882+ public void Dispose ( )
883+ {
884+ ApplyEnvironmentVariables ( this . applyVariables ) ;
885+ }
886+ }
887+
849888 private static class CloudBuild
850889 {
851- public static readonly ImmutableDictionary < string , string > VSTS = ImmutableDictionary < string , string > . Empty
852- . Add ( "SYSTEM_TEAMPROJECTID" , "1" ) ;
853- public static readonly ImmutableDictionary < string , string > AppVeyor = ImmutableDictionary < string , string > . Empty
854- . Add ( "APPVEYOR" , "True" ) ;
890+ public static readonly ImmutableDictionary < string , string > SuppressEnvironment = ImmutableDictionary < string , string > . Empty
891+ // AppVeyor
892+ . Add ( "APPVEYOR" , string . Empty )
893+ . Add ( "APPVEYOR_REPO_TAG" , string . Empty )
894+ . Add ( "APPVEYOR_REPO_TAG_NAME" , string . Empty )
895+ . Add ( "APPVEYOR_PULL_REQUEST_NUMBER" , string . Empty )
896+ // VSTS
897+ . Add ( "SYSTEM_TEAMPROJECTID" , string . Empty )
898+ . Add ( "BUILD_SOURCEBRANCH" , string . Empty ) ;
899+ public static readonly ImmutableDictionary < string , string > VSTS = SuppressEnvironment
900+ . SetItem ( "SYSTEM_TEAMPROJECTID" , "1" ) ;
901+ public static readonly ImmutableDictionary < string , string > AppVeyor = SuppressEnvironment
902+ . SetItem ( "APPVEYOR" , "True" ) ;
855903 }
856904
857905 private static class Targets
@@ -882,9 +930,6 @@ internal BuildResults(BuildResult buildResult, IReadOnlyList<BuildEventArgs> log
882930 public string PrereleaseVersion => this . BuildResult . ProjectStateAfterBuild . GetPropertyValue ( "PrereleaseVersion" ) ;
883931 public string MajorMinorVersion => this . BuildResult . ProjectStateAfterBuild . GetPropertyValue ( "MajorMinorVersion" ) ;
884932 public string BuildVersionNumberComponent => this . BuildResult . ProjectStateAfterBuild . GetPropertyValue ( "BuildVersionNumberComponent" ) ;
885- public string BuildNumberFirstComponent => this . BuildResult . ProjectStateAfterBuild . GetPropertyValue ( "BuildNumberFirstComponent" ) ;
886- public string BuildNumberSecondComponent => this . BuildResult . ProjectStateAfterBuild . GetPropertyValue ( "BuildNumberSecondComponent" ) ;
887- public string BuildNumberFirstAndSecondComponentsIfApplicable => this . BuildResult . ProjectStateAfterBuild . GetPropertyValue ( "BuildNumberFirstAndSecondComponentsIfApplicable" ) ;
888933 public string GitCommitIdShort => this . BuildResult . ProjectStateAfterBuild . GetPropertyValue ( "GitCommitIdShort" ) ;
889934 public string GitVersionHeight => this . BuildResult . ProjectStateAfterBuild . GetPropertyValue ( "GitVersionHeight" ) ;
890935 public string SemVerBuildSuffix => this . BuildResult . ProjectStateAfterBuild . GetPropertyValue ( "SemVerBuildSuffix" ) ;
0 commit comments