1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.IO;
5 using System.Linq;
6 using System.Reflection;
7 using System.Text;
8 using System.Threading.Tasks;
9 using Microsoft.Build.Evaluation;
10 using Microsoft.Build.Shared;
11 using Shouldly;
12 using Xunit;
13 
14 namespace Microsoft.Build.Engine.UnitTests
15 {
16     public class BuildEnvironmentHelper_Tests
17     {
18 #if USE_MSBUILD_DLL_EXTN
19         private const string MSBuildExeName = "MSBuild.dll";
20 #else
21         private const string MSBuildExeName = "MSBuild.exe";
22 #endif
23 
24         [Fact]
GetExecutablePath()25         public void GetExecutablePath()
26         {
27             var msbuildPath = Path.GetDirectoryName(FileUtilities.ExecutingAssemblyPath);
28             string expectedMSBuildPath = Path.Combine(msbuildPath, MSBuildExeName).ToLowerInvariant();
29 
30             string configFilePath = BuildEnvironmentHelper.Instance.CurrentMSBuildConfigurationFile.ToLowerInvariant();
31             string toolsDirectoryPath = BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory.ToLowerInvariant();
32             string actualMSBuildPath = BuildEnvironmentHelper.Instance.CurrentMSBuildExePath.ToLowerInvariant();
33 
34             configFilePath.ShouldBe($"{actualMSBuildPath}.config");
35             actualMSBuildPath.ShouldBe(expectedMSBuildPath);
36             Path.GetDirectoryName(expectedMSBuildPath).ShouldBe(toolsDirectoryPath);
37             BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.Standalone);
38         }
39 
40         [Fact]
FindBuildEnvironmentByEnvironmentVariable()41         public void FindBuildEnvironmentByEnvironmentVariable()
42         {
43             using (var env = new EmptyStandaloneEnviroment(MSBuildExeName))
44             {
45                 var path = env.BuildDirectory;
46                 var msBuildPath = Path.Combine(path, MSBuildExeName);
47                 var msBuildConfig = Path.Combine(path, $"{MSBuildExeName}.config");
48 
49                 env.WithEnvironment("MSBUILD_EXE_PATH", env.MSBuildExePath);
50                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
51 
52                 BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory.ShouldBe(path);
53                 BuildEnvironmentHelper.Instance.CurrentMSBuildExePath.ShouldBe(msBuildPath);
54                 BuildEnvironmentHelper.Instance.CurrentMSBuildConfigurationFile.ShouldBe(msBuildConfig);
55                 BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeFalse();
56                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBeNull();
57                 BuildEnvironmentHelper.Instance.RunningTests.ShouldBeFalse();
58                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.Standalone);
59             }
60         }
61 
62         [Fact]
63         [Trait("Category", "nonlinuxtests")]
64         [Trait("Category", "nonosxtests")]
FindBuildEnvironmentFromCommandLineVisualStudio()65         public void FindBuildEnvironmentFromCommandLineVisualStudio()
66         {
67             using (var env = new EmptyVSEnviroment())
68             {
69                 // All we know about is path to msbuild.exe as the command-line arg[0]
70                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => env.MSBuildExePath, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
71 
72                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
73                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory64);
74                 BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeFalse();
75                 BuildEnvironmentHelper.Instance.RunningTests.ShouldBeFalse();
76                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
77             }
78         }
79 
80         [Fact]
FindBuildEnvironmentFromCommandLineStandalone()81         public void FindBuildEnvironmentFromCommandLineStandalone()
82         {
83             // Path will not be under a Visual Studio install like path.
84             using (var env = new EmptyStandaloneEnviroment(MSBuildExeName))
85             {
86                 // All we know about is path to msbuild.exe as the command-line arg[0]
87                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => env.MSBuildExePath, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
88 
89                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
90                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory);
91                 BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeFalse();
92                 BuildEnvironmentHelper.Instance.RunningTests.ShouldBeFalse();
93                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.Standalone);
94             }
95         }
96 
97         [Fact]
98         [Trait("Category", "nonlinuxtests")]
99         [Trait("Category", "nonosxtests")]
FindBuildEnvironmentFromRunningProcessVisualStudio()100         public void FindBuildEnvironmentFromRunningProcessVisualStudio()
101         {
102             using (var env = new EmptyVSEnviroment())
103             {
104                 // All we know about is path to msbuild.exe as the current process
105                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, () => env.MSBuildExePath, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
106 
107                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
108                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory64);
109                 BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeFalse();
110                 BuildEnvironmentHelper.Instance.RunningTests.ShouldBeFalse();
111                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
112             }
113         }
114 
115         [Fact]
FindBuildEnvironmentFromRunningProcessStandalone()116         public void FindBuildEnvironmentFromRunningProcessStandalone()
117         {
118             // Path will not be under a Visual Studio install like path.
119             using (var env = new EmptyStandaloneEnviroment(MSBuildExeName))
120             {
121                 // All we know about is path to msbuild.exe as the current process
122                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, () => env.MSBuildExePath, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
123 
124                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
125                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory);
126                 BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeFalse();
127                 BuildEnvironmentHelper.Instance.RunningTests.ShouldBeFalse();
128                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.Standalone);
129             }
130         }
131 
132         [Fact]
FindBuildEnvironmentFromExecutingAssemblyAsDll()133         public void FindBuildEnvironmentFromExecutingAssemblyAsDll()
134         {
135             // Ensure the correct file is found (.dll not .exe)
136             using (var env = new EmptyStandaloneEnviroment("MSBuild.dll"))
137             {
138                 // All we know about is path to msbuild.exe as the current process
139                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, () => env.MSBuildExePath, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
140 
141                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
142                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory);
143                 BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeFalse();
144                 BuildEnvironmentHelper.Instance.RunningTests.ShouldBeFalse();
145                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.Standalone);
146             }
147         }
148 
149         [Fact]
FindBuildEnvironmentFromAppContextDirectory()150         public void FindBuildEnvironmentFromAppContextDirectory()
151         {
152             using (var env = new EmptyStandaloneEnviroment(MSBuildExeName))
153             {
154                 // Only the app base directory will be available
155                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, ReturnNull, () => env.BuildDirectory, env.VsInstanceMock, env.EnvironmentMock, () => false);
156 
157                 // Make sure we get the right MSBuild entry point. On .NET Core this will be MSBuild.dll, otherwise MSBuild.exe
158                 Path.GetFileName(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath).ShouldBe(MSBuildExeName);
159 
160                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
161                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory);
162                 BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeFalse();
163                 BuildEnvironmentHelper.Instance.RunningTests.ShouldBeFalse();
164                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.Standalone);
165             }
166         }
167 
168         [Fact]
169         [Trait("Category", "nonlinuxtests")]
170         [Trait("Category", "nonosxtests")]
FindBuildEnvironmentFromVisualStudioRoot()171         public void FindBuildEnvironmentFromVisualStudioRoot()
172         {
173             using (var env = new EmptyVSEnviroment())
174             {
175                 // All we know about is path to DevEnv.exe
176                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => env.DevEnvPath, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
177 
178                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
179                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory64);
180                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot);
181                 BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeTrue();
182                 BuildEnvironmentHelper.Instance.RunningTests.ShouldBeFalse();
183                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
184             }
185         }
186 
187         [Fact]
188         [Trait("Category", "nonlinuxtests")]
189         [Trait("Category", "nonosxtests")]
BuildEnvironmentDetectsVisualStudioByEnvironment()190         public void BuildEnvironmentDetectsVisualStudioByEnvironment()
191         {
192             using (var env = new EmptyVSEnviroment())
193             {
194                 env.WithEnvironment("VSINSTALLDIR", env.TempFolderRoot);
195                 env.WithEnvironment("VisualStudioVersion", "15.0");
196                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
197 
198                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot);
199                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
200             }
201         }
202 
203         [Fact]
204         [Trait("Category", "nonlinuxtests")]
205         [Trait("Category", "nonosxtests")]
BuildEnvironmentDetectsVisualStudioByMSBuildProcess()206         public void BuildEnvironmentDetectsVisualStudioByMSBuildProcess()
207         {
208             using (var env = new EmptyVSEnviroment())
209             {
210                 // We only know we're in msbuild.exe, we should still be able to attempt to find Visual Studio
211                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => env.MSBuildExePath, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
212 
213                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot);
214                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
215             }
216         }
217 
218         [Fact]
219         [Trait("Category", "nonlinuxtests")]
220         [Trait("Category", "nonosxtests")]
BuildEnvironmentDetectsVisualStudioByMSBuildProcessAmd64()221         public void BuildEnvironmentDetectsVisualStudioByMSBuildProcessAmd64()
222         {
223             using (var env = new EmptyVSEnviroment())
224             {
225                 // We only know we're in amd64\msbuild.exe, we should still be able to attempt to find Visual Studio
226                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => env.MSBuildExePath64, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
227 
228                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot);
229                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
230             }
231         }
232 
233         [Theory]
234         [Trait("Category", "nonlinuxtests")]
235         [Trait("Category", "nonosxtests")]
236         [InlineData("15.0")]
237         [InlineData("15.3")]
BuildEnvironmentDetectsVisualStudioFromSetupInstance(string visualStudioVersion)238         public void BuildEnvironmentDetectsVisualStudioFromSetupInstance(string visualStudioVersion)
239         {
240             using (var env = new EmptyVSEnviroment())
241             {
242                 env.WithVsInstance(new VisualStudioInstance("Invalid path", @"c:\_doesnotexist", new Version(visualStudioVersion)));
243                 env.WithVsInstance(new VisualStudioInstance("VS", env.TempFolderRoot, new Version(visualStudioVersion)));
244 
245                 // This test has no context to find MSBuild other than Visual Studio root.
246                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
247 
248                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot);
249                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
250             }
251         }
252 
253         [Fact]
BuildEnvironmentVisualStudioNotFoundWhenVersionMismatch()254         public void BuildEnvironmentVisualStudioNotFoundWhenVersionMismatch()
255         {
256             using (var env = new EmptyVSEnviroment())
257             {
258                 env.WithVsInstance(new VisualStudioInstance("Invalid path", @"c:\_doesnotexist", new Version("15.0")));
259                 env.WithVsInstance(new VisualStudioInstance("VS", env.TempFolderRoot, new Version("14.0")));
260 
261                 // This test has no context to find MSBuild other than Visual Studio root.
262                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
263 
264                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBeNull();
265                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.None);
266             }
267         }
268 
269         [Fact]
BuildEnvironmentDetectsRunningTests()270         public void BuildEnvironmentDetectsRunningTests()
271         {
272             BuildEnvironmentHelper.Instance.RunningTests.ShouldBeTrue();
273             BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeFalse();
274         }
275 
276         [Fact]
277         [Trait("Category", "nonlinuxtests")]
278         [Trait("Category", "nonosxtests")]
BuildEnvironmentDetectsVisualStudioByProcessName()279         public void BuildEnvironmentDetectsVisualStudioByProcessName()
280         {
281             using (var env = new EmptyVSEnviroment())
282             {
283                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => env.DevEnvPath, () => env.MSBuildExePath, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
284 
285                 BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeTrue();
286                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot);
287                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
288             }
289         }
290 
291         [Fact]
292         [Trait("Category", "nonlinuxtests")]
293         [Trait("Category", "nonosxtests")]
BuildEnvironmentDetectsVisualStudioByBlendProcess()294         public void BuildEnvironmentDetectsVisualStudioByBlendProcess()
295         {
296             using (var env = new EmptyVSEnviroment())
297             {
298                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => env.BlendPath, () => env.MSBuildExePath, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
299 
300                 BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeTrue();
301                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot);
302                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
303             }
304         }
305 
306         [Fact]
307         [Trait("Category", "nonlinuxtests")]
308         [Trait("Category", "nonosxtests")]
BuildEnvironmentFindsAmd64()309         public void BuildEnvironmentFindsAmd64()
310         {
311             using (var env = new EmptyVSEnviroment())
312             {
313                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => env.DevEnvPath, ReturnNull,
314                     ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
315 
316                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
317                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory64);
318                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
319             }
320         }
321 
322         [Fact]
323         [Trait("Category", "nonlinuxtests")]
324         [Trait("Category", "nonosxtests")]
BuildEnvironmentFindsAmd64RunningInAmd64NoVS()325         public void BuildEnvironmentFindsAmd64RunningInAmd64NoVS()
326         {
327             using (var env = new EmptyStandaloneEnviroment(MSBuildExeName, writeFakeFiles:true, includeAmd64Folder:true))
328             {
329                 var msBuild64Exe = Path.Combine(env.BuildDirectory, "amd64", MSBuildExeName);
330                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => msBuild64Exe, ReturnNull, ReturnNull,
331                     env.VsInstanceMock, env.EnvironmentMock, () => false);
332 
333                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
334                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(Path.Combine(env.BuildDirectory, "amd64"));
335                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBeNull();
336                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.Standalone);
337             }
338         }
339 
340         [Fact]
341         [Trait("Category", "nonlinuxtests")]
342         [Trait("Category", "nonosxtests")]
BuildEnvironmentFindsAmd64NoVS()343         public void BuildEnvironmentFindsAmd64NoVS()
344         {
345             using (var env = new EmptyStandaloneEnviroment(MSBuildExeName, writeFakeFiles: true, includeAmd64Folder: true))
346             {
347                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => env.MSBuildExePath, ReturnNull,
348                     ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
349 
350                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
351                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(Path.Combine(env.BuildDirectory, "amd64"));
352                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.Standalone);
353             }
354         }
355 
356         [Fact]
357         [Trait("Category", "nonlinuxtests")]
358         [Trait("Category", "nonosxtests")]
BuildEnvironmentFindsAmd64RunningInAmd64()359         public void BuildEnvironmentFindsAmd64RunningInAmd64()
360         {
361             using (var env = new EmptyVSEnviroment())
362             {
363                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => env.MSBuildExePath64, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false);
364 
365                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
366                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory64);
367                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot);
368                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
369             }
370         }
371 
372         [Fact]
BuildEnvironmentNoneWhenNotAvailable()373         public void BuildEnvironmentNoneWhenNotAvailable()
374         {
375             using (var env = new EmptyStandaloneEnviroment(MSBuildExeName))
376             {
377                 var entryProcess = Path.Combine(Path.GetTempPath(), "foo.exe");
378                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(() => entryProcess, ReturnNull, ReturnNull,
379                     env.VsInstanceMock, env.EnvironmentMock, () => false);
380 
381                 BuildEnvironmentHelper.Instance.CurrentMSBuildExePath.ShouldBe(entryProcess);
382                 BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory.ShouldBe(Path.GetDirectoryName(entryProcess));
383                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBeNull();
384                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.None);
385             }
386         }
387 
388         [Fact]
389         [Trait("Category", "nonlinuxtests")]
390         [Trait("Category", "nonosxtests")]
BuildEnvironmentVSFromMSBuildAssembly()391         public void BuildEnvironmentVSFromMSBuildAssembly()
392         {
393             using (var env = new EmptyVSEnviroment())
394             {
395                 var msBuildAssembly = Path.Combine(env.BuildDirectory, "Microsoft.Build.dll");
396 
397                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, () => msBuildAssembly, ReturnNull,
398                     env.VsInstanceMock, env.EnvironmentMock, () => false);
399 
400                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
401                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory64);
402                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot);
403                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
404             }
405         }
406 
407         [Fact]
408         [Trait("Category", "nonlinuxtests")]
409         [Trait("Category", "nonosxtests")]
BuildEnvironmentVSFromMSBuildAssemblyAmd64()410         public void BuildEnvironmentVSFromMSBuildAssemblyAmd64()
411         {
412             using (var env = new EmptyVSEnviroment())
413             {
414                 var msBuildAssembly = Path.Combine(env.BuildDirectory64, "Microsoft.Build.dll");
415 
416                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, () => msBuildAssembly, ReturnNull,
417                     env.VsInstanceMock, env.EnvironmentMock, () => false);
418 
419                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory32.ShouldBe(env.BuildDirectory);
420                 BuildEnvironmentHelper.Instance.MSBuildToolsDirectory64.ShouldBe(env.BuildDirectory64);
421                 BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot);
422                 BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio);
423             }
424         }
425 
ReturnNull()426         private static string ReturnNull()
427         {
428             return null;
429         }
430 
431         private class EmptyVSEnviroment : EmptyStandaloneEnviroment
432         {
433             public string DevEnvPath { get; }
434 
435             public string BlendPath { get; }
436 
437             public string BuildDirectory64 { get; }
438 
439             public string MSBuildExePath64 => Path.Combine(BuildDirectory64, MSBuildExeName);
440 
EmptyVSEnviroment()441             public EmptyVSEnviroment() : base("msbuild.exe", false)
442             {
443                 try
444                 {
445                     var files = new[] { "msbuild.exe", "msbuild.exe.config" };
446                     BuildDirectory = Path.Combine(TempFolderRoot, "MSBuild", "15.0", "Bin");
447                     BuildDirectory64 = Path.Combine(BuildDirectory, "amd64");
448                     DevEnvPath = Path.Combine(TempFolderRoot, "Common7", "IDE", "devenv.exe");
449                     BlendPath = Path.Combine(TempFolderRoot, "Common7", "IDE", "blend.exe");
450 
451                     Directory.CreateDirectory(BuildDirectory);
452                     foreach (var file in files)
453                     {
454                         File.WriteAllText(Path.Combine(BuildDirectory, file), string.Empty);
455                     }
456 
457                     Directory.CreateDirectory(BuildDirectory64);
458                     foreach (var file in files)
459                     {
460                         File.WriteAllText(Path.Combine(BuildDirectory64, file), string.Empty);
461                     }
462 
463                     Directory.CreateDirectory(Path.Combine(TempFolderRoot, "Common7", "IDE"));
464                     File.WriteAllText(DevEnvPath, string.Empty);
465                 }
466                 catch (Exception)
467                 {
468                     FileUtilities.DeleteDirectoryNoThrow(BuildDirectory, true);
469                     throw;
470                 }
471             }
472         }
473 
474         private class EmptyStandaloneEnviroment : IDisposable
475         {
476             public string TempFolderRoot { get; }
477 
478             public string BuildDirectory { get; protected set; }
479 
480             public string MSBuildExeName { get; }
481 
482             public string MSBuildExePath => Path.Combine(BuildDirectory, MSBuildExeName);
483 
484             private readonly Dictionary<string, string> _mockEnvironment = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
485 
486             private readonly List<VisualStudioInstance> _mockInstances = new List<VisualStudioInstance>();
487 
EmptyStandaloneEnviroment(string msBuildExeName, bool writeFakeFiles = true, bool includeAmd64Folder = false)488             public EmptyStandaloneEnviroment(string msBuildExeName, bool writeFakeFiles = true, bool includeAmd64Folder = false)
489             {
490                 try
491                 {
492                     MSBuildExeName = msBuildExeName;
493                     TempFolderRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
494                     BuildDirectory = Path.Combine(TempFolderRoot, "MSBuild");
495 
496                     Directory.CreateDirectory(BuildDirectory);
497                     if (writeFakeFiles)
498                     {
499                         File.WriteAllText(MSBuildExePath, string.Empty);
500                         File.WriteAllText($"{MSBuildExePath}.config", string.Empty);
501 
502                         if (includeAmd64Folder)
503                         {
504                             Directory.CreateDirectory(Path.Combine(BuildDirectory, "amd64"));
505                             File.WriteAllText(Path.Combine(BuildDirectory, "amd64", msBuildExeName), string.Empty);
506                             File.WriteAllText(Path.Combine(BuildDirectory, "amd64", $"{MSBuildExePath}.config"), string.Empty);
507                         }
508                     }
509                 }
510                 catch (Exception)
511                 {
512                     FileUtilities.DeleteDirectoryNoThrow(BuildDirectory, true);
513                     throw;
514                 }
515             }
516 
WithEnvironment(string variable, string value)517             public void WithEnvironment(string variable, string value)
518             {
519                 _mockEnvironment.Add(variable, value);
520             }
521 
WithVsInstance(VisualStudioInstance instance)522             public void WithVsInstance(VisualStudioInstance instance)
523             {
524                 _mockInstances.Add(instance);
525             }
526 
EnvironmentMock(string variable)527             public string EnvironmentMock(string variable)
528             {
529                 return _mockEnvironment.ContainsKey(variable) ? _mockEnvironment[variable] : null;
530             }
531 
VsInstanceMock()532             public IEnumerable<VisualStudioInstance> VsInstanceMock()
533             {
534                 return _mockInstances;
535             }
536 
Dispose()537             public void Dispose()
538             {
539                 FileUtilities.DeleteDirectoryNoThrow(TempFolderRoot, true);
540                 BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(runningTests: () => true);
541             }
542         }
543     }
544 }
545