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 a92612ca0b0f4..224b134a8a147 100644
--- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs
+++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs
@@ -92,7 +92,23 @@ private ClientConfigPaths(string exePath, bool includeUserConfig)
}
}
- 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);
+ if (externalConfigUri.IsFile)
+ {
+ ApplicationConfigUri = externalConfigUri.LocalPath;
+ }
+ }
+ else
+ {
+ 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 94670bcb997df..816d4ca00292e 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 0000000000000..5d2442dffbfe0
--- /dev/null
+++ b/src/libraries/System.Configuration.ConfigurationManager/tests/System/Configuration/ConfigurationPathTests.cs
@@ -0,0 +1,92 @@
+// 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 Microsoft.DotNet.RemoteExecutor;
+using Xunit;
+
+namespace System.ConfigurationTests
+{
+ public class ConfigurationPathTests
+ {
+ private const string ConfigName = "APP_CONFIG_FILE";
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void CustomAppConfigIsUsedWhenSpecifiedAsRelativePath()
+ {
+ const string SettingName = "test_CustomAppConfigIsUsedWhenSpecified";
+ string expectedSettingValue = Guid.NewGuid().ToString();
+ string configFilePath = CreateAppConfigFileWithSetting(SettingName, expectedSettingValue);
+
+ RemoteExecutor.Invoke((string configFilePath, string expectedSettingValue) => {
+ AppDomain.CurrentDomain.SetData(ConfigName, 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.GetFullPath(CreateAppConfigFileWithSetting(SettingName, expectedSettingValue));
+
+ RemoteExecutor.Invoke((string configFilePath, string expectedSettingValue) => {
+ AppDomain.CurrentDomain.SetData(ConfigName, 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.GetFullPath(CreateAppConfigFileWithSetting(SettingName, expectedSettingValue))).ToString();
+
+ RemoteExecutor.Invoke((string configFilePath, string expectedSettingValue) => {
+ AppDomain.CurrentDomain.SetData(ConfigName, configFilePath);
+ Assert.Equal(expectedSettingValue, ConfigurationManager.AppSettings[SettingName]);
+ }, configFilePath, expectedSettingValue).Dispose();
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public void NoErrorWhenCustomAppConfigIsSpecifiedAndItDoesNotExist()
+ {
+ RemoteExecutor.Invoke(() =>
+ {
+ AppDomain.CurrentDomain.SetData(ConfigName, "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 = CreateAppConfigFileWithSetting(SettingName, "\"");
+
+ RemoteExecutor.Invoke((string configFilePath) => {
+ AppDomain.CurrentDomain.SetData(ConfigName, configFilePath);
+ Assert.Throws(() => ConfigurationManager.AppSettings[SettingName]);
+ }, configFilePath).Dispose();
+ }
+
+ private static string CreateAppConfigFileWithSetting(string key, string rawUnquotedValue)
+ {
+ string fileName = Path.GetRandomFileName() + ".config";
+ File.WriteAllText(fileName,
+ @$"
+
+
+
+
+");
+
+ return fileName;
+ }
+ }
+}