From d329350d64e2b9f843f6581759eb5524a0612452 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Thu, 5 Aug 2021 18:14:46 +0200 Subject: [PATCH] Respect AppContext.SetData with APP_CONFIG_FILE key (#56748) * 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 --- .../TestUtilities/System/IO/FileCleanupTestBase.cs | 6 +- .../src/System/Configuration/ClientConfigPaths.cs | 23 ++++- ...Configuration.ConfigurationManager.Tests.csproj | 1 + .../System/Configuration/ConfigurationPathTests.cs | 105 +++++++++++++++++++++ 4 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/ConfigurationPathTests.cs diff --git a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs index a45aab1..04b2788 100644 --- a/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs +++ b/src/libraries/Common/tests/TestUtilities/System/IO/FileCleanupTestBase.cs @@ -18,8 +18,10 @@ namespace System.IO protected static bool IsProcessElevated => s_isElevated.Value; /// Initialize the test class base. This creates the associated test directory. - 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); diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs index 747815c..5d25b97 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs @@ -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) diff --git a/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj b/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj index c5e101e..9751595 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/System.Configuration.ConfigurationManager.Tests.csproj @@ -65,6 +65,7 @@ + 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 index 0000000..a802ccc --- /dev/null +++ b/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/ConfigurationPathTests.cs @@ -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(() => 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), + @$" + + + + +"); + return fileName; + } + } +} -- 2.7.4