Respect AppContext.SetData with APP_CONFIG_FILE key (#56748)
authorKrzysztof Wicher <kwicher@microsoft.com>
Thu, 5 Aug 2021 16:14:46 +0000 (18:14 +0200)
committerGitHub <noreply@github.com>
Thu, 5 Aug 2021 16:14:46 +0000 (18:14 +0200)
* Respect AppContext.SetData with APP_CONFIG_FILE key

* apply feedback (use BaseDirectory and FileCleanupTestBase)

* avoid using GetFullPath with 2 arguments as it's not available everywhere

* trim all directory separators rather than single

* cleanupRootDirectory -> tempDirectory

* fix absolute paths on linux

src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs
src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs
src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj
src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/ConfigurationPathTests.cs [new file with mode: 0644]

index a45aab1..04b2788 100644 (file)
@@ -18,8 +18,10 @@ namespace System.IO
         protected static bool IsProcessElevated => s_isElevated.Value;
 
         /// <summary>Initialize the test class base.  This creates the associated test directory.</summary>
-        protected FileCleanupTestBase()
+        protected FileCleanupTestBase(string tempDirectory = null)
         {
+            tempDirectory ??= Path.GetTempPath();
+
             // Use a unique test directory per test class.  The test directory lives in the user's temp directory,
             // and includes both the name of the test class and a random string.  The test class name is included
             // so that it can be easily correlated if necessary, and the random string to helps avoid conflicts if
@@ -31,7 +33,7 @@ namespace System.IO
             string failure = string.Empty;
             for (int i = 0; i <= 2; i++)
             {
-                TestDirectory = Path.Combine(Path.GetTempPath(), GetType().Name + "_" + Path.GetRandomFileName());
+                TestDirectory = Path.Combine(tempDirectory, GetType().Name + "_" + Path.GetRandomFileName());
                 try
                 {
                     Directory.CreateDirectory(TestDirectory);
index 747815c..5d25b97 100644 (file)
@@ -94,7 +94,28 @@ namespace System.Configuration
                 }
             }
 
-            if (!string.IsNullOrEmpty(ApplicationUri))
+            string externalConfigPath = AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE") as string;
+            if (!string.IsNullOrEmpty(externalConfigPath))
+            {
+                if (Uri.IsWellFormedUriString(externalConfigPath, UriKind.Absolute))
+                {
+                    Uri externalConfigUri = new Uri(externalConfigPath, UriKind.Absolute);
+                    if (externalConfigUri.IsFile)
+                    {
+                        ApplicationConfigUri = externalConfigUri.LocalPath;
+                    }
+                }
+                else
+                {
+                    if (!Path.IsPathRooted(externalConfigPath))
+                    {
+                        externalConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, externalConfigPath);
+                    }
+
+                    ApplicationConfigUri = Path.GetFullPath(externalConfigPath);
+                }
+            }
+            else if (!string.IsNullOrEmpty(ApplicationUri))
             {
                 string applicationPath = ApplicationUri;
                 if (isSingleFile)
index c5e101e..9751595 100644 (file)
@@ -65,6 +65,7 @@
     <Compile Include="System\Configuration\ConfigurationElementCollectionTests.cs" />
     <Compile Include="System\Configuration\ConfigurationElementTests.cs" />
     <Compile Include="System\Configuration\ConfigurationPropertyAttributeTests.cs" />
+    <Compile Include="System\Configuration\ConfigurationPathTests.cs" />
     <Compile Include="System\Configuration\CustomHostTests.cs" />
     <Compile Include="System\Configuration\ConfigurationPropertyTests.cs" />
     <Compile Include="System\Configuration\ConfigurationTests.cs" />
diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/ConfigurationPathTests.cs b/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/ConfigurationPathTests.cs
new file mode 100644 (file)
index 0000000..a802ccc
--- /dev/null
@@ -0,0 +1,105 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Configuration;
+using System.IO;
+using System.Runtime.CompilerServices;
+using Microsoft.DotNet.RemoteExecutor;
+using Xunit;
+
+namespace System.ConfigurationTests
+{
+    public class ConfigurationPathTests : FileCleanupTestBase
+    {
+        public ConfigurationPathTests() : base(AppDomain.CurrentDomain.BaseDirectory) // We do not want the files go to temporary directory as that will not test the relative paths correctly
+        {
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void CustomAppConfigIsUsedWhenSpecifiedAsRelativePath()
+        {
+            const string SettingName = "test_CustomAppConfigIsUsedWhenSpecified";
+            string expectedSettingValue = Guid.NewGuid().ToString();
+            string configFilePath = Path.Combine(GetTestDirectoryName(), CreateAppConfigFileWithSetting(SettingName, expectedSettingValue));
+
+            RemoteExecutor.Invoke((string configFilePath, string expectedSettingValue) => {
+                // We change directory so that if product tries to read from the current directory which usually happens to be same as BaseDirectory the test will fail
+                Environment.CurrentDirectory = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ".."));
+                AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", configFilePath);
+                Assert.Equal(expectedSettingValue, ConfigurationManager.AppSettings[SettingName]);
+            }, configFilePath, expectedSettingValue).Dispose();
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void CustomAppConfigIsUsedWhenSpecifiedAsAbsolutePath()
+        {
+            const string SettingName = "test_CustomAppConfigIsUsedWhenSpecified";
+            string expectedSettingValue = Guid.NewGuid().ToString();
+            string configFilePath = Path.Combine(TestDirectory, CreateAppConfigFileWithSetting(SettingName, expectedSettingValue));
+
+            RemoteExecutor.Invoke((string configFilePath, string expectedSettingValue) => {
+                AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", configFilePath);
+                Assert.Equal(expectedSettingValue, ConfigurationManager.AppSettings[SettingName]);
+            }, configFilePath, expectedSettingValue).Dispose();
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void CustomAppConfigIsUsedWhenSpecifiedAsAbsoluteUri()
+        {
+            const string SettingName = "test_CustomAppConfigIsUsedWhenSpecified";
+            string expectedSettingValue = Guid.NewGuid().ToString();
+            string configFilePath = new Uri(Path.Combine(TestDirectory, CreateAppConfigFileWithSetting(SettingName, expectedSettingValue))).ToString();
+
+            RemoteExecutor.Invoke((string configFilePath, string expectedSettingValue) => {
+                AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", configFilePath);
+                Assert.Equal(expectedSettingValue, ConfigurationManager.AppSettings[SettingName]);
+            }, configFilePath, expectedSettingValue).Dispose();
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void NoErrorWhenCustomAppConfigIsSpecifiedAndItDoesNotExist()
+        {
+            RemoteExecutor.Invoke(() =>
+            {
+                AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "non-existing-file.config");
+                Assert.Null(ConfigurationManager.AppSettings["AnySetting"]);
+            }).Dispose();
+        }
+
+        [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+        public void MalformedAppConfigCausesException()
+        {
+            const string SettingName = "AnySetting";
+
+            // Following will cause malformed config file
+            string configFilePath = Path.Combine(TestDirectory, CreateAppConfigFileWithSetting(SettingName, "\""));
+
+            RemoteExecutor.Invoke((string configFilePath) => {
+                AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", configFilePath);
+                Assert.Throws<ConfigurationErrorsException>(() => ConfigurationManager.AppSettings[SettingName]);
+            }, configFilePath).Dispose();
+        }
+
+        private string GetTestDirectoryName()
+        {
+            string dir = TestDirectory;
+            if (dir.EndsWith("\\") || dir.EndsWith("/"))
+                dir = dir.Substring(0, dir.Length - 1);
+
+            return Path.GetFileName(dir);
+        }
+
+        private string CreateAppConfigFileWithSetting(string key, string rawUnquotedValue, [CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0)
+        {
+            string fileName = GetTestFileName(null, memberName, lineNumber) + ".config";
+            File.WriteAllText(Path.Combine(TestDirectory, fileName),
+                @$"<?xml version=""1.0"" encoding=""utf-8"" ?>
+<configuration>
+  <appSettings>
+    <add key=""{key}"" value=""{rawUnquotedValue}""/>
+  </appSettings>
+</configuration>");
+            return fileName;
+        }
+    }
+}