1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 using System.Runtime.InteropServices;
10 using Xunit;
11 
12 namespace System.Tests
13 {
14     public partial class GetEnvironmentVariable
15     {
16         [Fact]
InvalidArguments_ThrowsExceptions()17         public void InvalidArguments_ThrowsExceptions()
18         {
19             AssertExtensions.Throws<ArgumentNullException>("variable", () => Environment.GetEnvironmentVariable(null));
20             AssertExtensions.Throws<ArgumentNullException>("variable", () => Environment.SetEnvironmentVariable(null, "test"));
21             AssertExtensions.Throws<ArgumentException>("variable", () => Environment.SetEnvironmentVariable("", "test"));
22 
23             AssertExtensions.Throws<ArgumentException>("variable", () => Environment.SetEnvironmentVariable("", "test", EnvironmentVariableTarget.Machine));
24             AssertExtensions.Throws<ArgumentNullException>("variable", () => Environment.SetEnvironmentVariable(null, "test", EnvironmentVariableTarget.User));
25             AssertExtensions.Throws<ArgumentNullException>("variable", () => Environment.GetEnvironmentVariable(null, EnvironmentVariableTarget.Process));
26             AssertExtensions.Throws<ArgumentOutOfRangeException, ArgumentException>("target", null, () => Environment.GetEnvironmentVariable("test", (EnvironmentVariableTarget)42));
27             AssertExtensions.Throws<ArgumentOutOfRangeException, ArgumentException>("target", null, () => Environment.SetEnvironmentVariable("test", "test", (EnvironmentVariableTarget)(-1)));
28             AssertExtensions.Throws<ArgumentOutOfRangeException, ArgumentException>("target", null, () => Environment.GetEnvironmentVariables((EnvironmentVariableTarget)(3)));
29             if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && System.Tests.SetEnvironmentVariable.IsSupportedTarget(EnvironmentVariableTarget.User))
30             {
31                 AssertExtensions.Throws<ArgumentException>("variable", null, () => Environment.SetEnvironmentVariable(new string('s', 256), "value", EnvironmentVariableTarget.User));
32             }
33         }
34 
35         [Fact]
EmptyVariableReturnsNull()36         public void EmptyVariableReturnsNull()
37         {
38             Assert.Null(Environment.GetEnvironmentVariable(String.Empty));
39         }
40 
41         [Fact]
42         [PlatformSpecific(TestPlatforms.Windows)] // GetEnvironmentVariable by design doesn't respect changes via setenv
RandomLongVariableNameCanRoundTrip()43         public void RandomLongVariableNameCanRoundTrip()
44         {
45             // NOTE: The limit of 32766 characters enforced by desktop
46             // SetEnvironmentVariable is antiquated. I was
47             // able to create ~1GB names and values on my Windows 8.1 box. On
48             // desktop, GetEnvironmentVariable throws OOM during its attempt to
49             // demand huge EnvironmentPermission well before that. Also, the old
50             // test for long name case wasn't very good: it just checked that an
51             // arbitrary long name > 32766 characters returned null (not found), but
52             // that had nothing to do with the limit, the variable was simply not
53             // found!
54 
55             string variable = "LongVariable_" + new string('@', 33000);
56             const string value = "TestValue";
57 
58             try
59             {
60                 SetEnvironmentVariableWithPInvoke(variable, value);
61 
62                 Assert.Equal(value, Environment.GetEnvironmentVariable(variable));
63             }
64             finally
65             {
66                 SetEnvironmentVariableWithPInvoke(variable, null);
67             }
68         }
69 
70         [Fact]
RandomVariableThatDoesNotExistReturnsNull()71         public void RandomVariableThatDoesNotExistReturnsNull()
72         {
73             string variable = "TestVariable_SurelyThisDoesNotExist";
74             Assert.Null(Environment.GetEnvironmentVariable(variable));
75         }
76 
77         [Fact]
VariableNamesAreCaseInsensitiveAsAppropriate()78         public void VariableNamesAreCaseInsensitiveAsAppropriate()
79         {
80             string value = "TestValue";
81 
82             try
83             {
84                 Environment.SetEnvironmentVariable("ThisIsATestEnvironmentVariable", value);
85 
86                 Assert.Equal(value, Environment.GetEnvironmentVariable("ThisIsATestEnvironmentVariable"));
87 
88                 if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
89                 {
90                     value = null;
91                 }
92 
93                 Assert.Equal(value, Environment.GetEnvironmentVariable("thisisatestenvironmentvariable"));
94                 Assert.Equal(value, Environment.GetEnvironmentVariable("THISISATESTENVIRONMENTVARIABLE"));
95                 Assert.Equal(value, Environment.GetEnvironmentVariable("ThISISATeSTENVIRoNMEnTVaRIABLE"));
96             }
97             finally
98             {
99                 Environment.SetEnvironmentVariable("ThisIsATestEnvironmentVariable", null);
100             }
101         }
102 
103         [Fact]
CanGetAllVariablesIndividually()104         public void CanGetAllVariablesIndividually()
105         {
106             Random r = new Random();
107             string envVar1 = "TestVariable_CanGetVariablesIndividually_" + r.Next().ToString();
108             string envVar2 = "TestVariable_CanGetVariablesIndividually_" + r.Next().ToString();
109 
110             try
111             {
112                 Environment.SetEnvironmentVariable(envVar1, envVar1);
113                 Environment.SetEnvironmentVariable(envVar2, envVar2);
114 
115                 IDictionary envBlock = Environment.GetEnvironmentVariables();
116 
117                 // Make sure the environment variables we set are part of the dictionary returned.
118                 Assert.True(envBlock.Contains(envVar1));
119                 Assert.True(envBlock.Contains(envVar1));
120 
121                 // Make sure the values match the expected ones.
122                 Assert.Equal(envVar1, envBlock[envVar1]);
123                 Assert.Equal(envVar2, envBlock[envVar2]);
124 
125                 // Make sure we can read the individual variables as well
126                 Assert.Equal(envVar1, Environment.GetEnvironmentVariable(envVar1));
127                 Assert.Equal(envVar2, Environment.GetEnvironmentVariable(envVar2));
128             }
129             finally
130             {
131                 // Clear the variables we just set
132                 Environment.SetEnvironmentVariable(envVar1, null);
133                 Environment.SetEnvironmentVariable(envVar2, null);
134             }
135         }
136 
137         [Fact]
EnumerateYieldsDictionaryEntryFromIEnumerable()138         public void EnumerateYieldsDictionaryEntryFromIEnumerable()
139         {
140             // GetEnvironmentVariables has always yielded DictionaryEntry from IEnumerable
141             IDictionary vars = Environment.GetEnvironmentVariables();
142             IEnumerator enumerator = ((IEnumerable)vars).GetEnumerator();
143             if (enumerator.MoveNext())
144             {
145                 Assert.IsType<DictionaryEntry>(enumerator.Current);
146             }
147             else
148             {
149                 Assert.Throws<InvalidOperationException>(() => enumerator.Current);
150             }
151         }
152 
153         [Fact]
GetEnumerator_IDictionaryEnumerator_YieldsDictionaryEntries()154         public void GetEnumerator_IDictionaryEnumerator_YieldsDictionaryEntries()
155         {
156             // GetEnvironmentVariables has always yielded DictionaryEntry from IDictionaryEnumerator
157             IDictionary vars = Environment.GetEnvironmentVariables();
158             IDictionaryEnumerator enumerator = vars.GetEnumerator();
159             if (enumerator.MoveNext())
160             {
161                 Assert.IsType<DictionaryEntry>(enumerator.Current);
162             }
163             else
164             {
165                 Assert.Throws<InvalidOperationException>(() => enumerator.Current);
166             }
167         }
168 
169         [Theory]
170         [InlineData(null)]
171         [MemberData(nameof(EnvironmentTests.EnvironmentVariableTargets), MemberType = typeof(EnvironmentTests))]
172         [ActiveIssue("https://github.com/dotnet/corefx/issues/23003", TargetFrameworkMonikers.NetFramework)]
173         public void GetEnumerator_LinqOverDictionaryEntries_Success(EnvironmentVariableTarget? target)
174         {
175             IDictionary envVars = target != null ?
176                 Environment.GetEnvironmentVariables(target.Value) :
177                 Environment.GetEnvironmentVariables();
178 
179             Assert.IsType<Hashtable>(envVars);
180 
181             foreach (KeyValuePair<string, string> envVar in envVars.Cast<DictionaryEntry>().Select(de => new KeyValuePair<string, string>((string)de.Key, (string)de.Value)))
182             {
183                 Assert.NotNull(envVar.Key);
184             }
185         }
186 
EnvironmentVariablesAreHashtable()187         public void EnvironmentVariablesAreHashtable()
188         {
189             // On NetFX, the type returned was always Hashtable
190             Assert.IsType<Hashtable>(Environment.GetEnvironmentVariables());
191         }
192 
193         [Theory]
194         [MemberData(nameof(EnvironmentTests.EnvironmentVariableTargets), MemberType = typeof(EnvironmentTests))]
EnvironmentVariablesAreHashtable(EnvironmentVariableTarget target)195         public void EnvironmentVariablesAreHashtable(EnvironmentVariableTarget target)
196         {
197             // On NetFX, the type returned was always Hashtable
198             Assert.IsType<Hashtable>(Environment.GetEnvironmentVariables(target));
199         }
200 
201         [Theory]
202         [MemberData(nameof(EnvironmentTests.EnvironmentVariableTargets), MemberType = typeof(EnvironmentTests))]
EnumerateYieldsDictionaryEntryFromIEnumerable(EnvironmentVariableTarget target)203         public void EnumerateYieldsDictionaryEntryFromIEnumerable(EnvironmentVariableTarget target)
204         {
205             // GetEnvironmentVariables has always yielded DictionaryEntry from IEnumerable
206             IDictionary vars = Environment.GetEnvironmentVariables(target);
207             IEnumerator enumerator = ((IEnumerable)vars).GetEnumerator();
208             if (enumerator.MoveNext())
209             {
210                 Assert.IsType<DictionaryEntry>(enumerator.Current);
211             }
212             else
213             {
214                 Assert.Throws<InvalidOperationException>(() => enumerator.Current);
215             }
216         }
217 
218         [Theory]
219         [MemberData(nameof(EnvironmentTests.EnvironmentVariableTargets), MemberType = typeof(EnvironmentTests))]
EnumerateEnvironmentVariables(EnvironmentVariableTarget target)220         public void EnumerateEnvironmentVariables(EnvironmentVariableTarget target)
221         {
222             bool lookForSetValue = (target == EnvironmentVariableTarget.Process) ||
223                                     // On the Project N corelib, it doesn't attempt to set machine/user environment variables;
224                                     // it just returns silently. So don't try.
225                                     (PlatformDetection.IsWindowsAndElevated && !PlatformDetection.IsNetNative);
226 
227 
228             string key = $"EnumerateEnvironmentVariables ({target})";
229             string value = Path.GetRandomFileName();
230 
231             try
232             {
233                 if (lookForSetValue)
234                 {
235                     Environment.SetEnvironmentVariable(key, value, target);
236                     Assert.Equal(value, Environment.GetEnvironmentVariable(key, target));
237                 }
238 
239                 IDictionary results = Environment.GetEnvironmentVariables(target);
240 
241                 // Ensure we can walk through the results
242                 IDictionaryEnumerator enumerator = results.GetEnumerator();
243                 while (enumerator.MoveNext())
244                 {
245                     Assert.NotNull(enumerator.Entry);
246                 }
247 
248                 if (lookForSetValue)
249                 {
250                     // Ensure that we got our flagged value out
251                     Assert.Equal(value, results[key]);
252                 }
253             }
254             finally
255             {
256                 if (lookForSetValue)
257                 {
258                     Environment.SetEnvironmentVariable(key, null, target);
259                     Assert.Null(Environment.GetEnvironmentVariable(key, target));
260                 }
261             }
262         }
263 
SetEnvironmentVariableWithPInvoke(string name, string value)264         private static void SetEnvironmentVariableWithPInvoke(string name, string value)
265         {
266             bool success =
267 #if !Unix
268                     SetEnvironmentVariable(name, value);
269 #else
270                     (value != null ? setenv(name, value, 1) : unsetenv(name)) == 0;
271 #endif
272             Assert.True(success);
273         }
274 
275         [DllImport("kernel32.dll", EntryPoint = "SetEnvironmentVariableW" , CharSet = CharSet.Unicode, SetLastError = true)]
SetEnvironmentVariable(string lpName, string lpValue)276         private static extern bool SetEnvironmentVariable(string lpName, string lpValue);
277 
278 #if Unix
279         [DllImport("libc")]
setenv(string name, string value, int overwrite)280         private static extern int setenv(string name, string value, int overwrite);
281 
282         [DllImport("libc")]
unsetenv(string name)283         private static extern int unsetenv(string name);
284 #endif
285     }
286 }
287