// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Testing.Platform.CommandLine; using Microsoft.Testing.Platform.Configurations; using Microsoft.Testing.Platform.Extensions.TestHostControllers; using Microsoft.Testing.Platform.Helpers; using Microsoft.Testing.Platform.Logging; using Microsoft.Testing.Platform.Services; using Microsoft.Testing.Platform.TestHostControllers; using Moq; namespace Microsoft.Testing.Platform.UnitTests; [TestClass] [UnsupportedOSPlatform("browser")] public sealed class TestConfigurationEnvironmentVariableProviderTests { [TestMethod] public async Task IsEnabledAsync_AbsentSection_ReturnsFalse() { TestConfigurationEnvironmentVariableProvider provider = await CreateProviderAsync("{\"other\": {}}"); Assert.IsFalse(await provider.IsEnabledAsync()); } [TestMethod] public async Task IsEnabledAsync_EmptySection_ReturnsFalse() { TestConfigurationEnvironmentVariableProvider provider = await CreateProviderAsync("{\"environmentVariables\": {}}"); Assert.IsFalse(await provider.IsEnabledAsync()); } [TestMethod] public async Task IsEnabledAsync_PopulatedSection_ReturnsTrue() { TestConfigurationEnvironmentVariableProvider provider = await CreateProviderAsync( "{\"environmentVariables\": {\"DOTNET_ENVIRONMENT\": \"Development\"}}"); Assert.IsTrue(await provider.IsEnabledAsync()); } [TestMethod] public async Task IsEnabledAsync_InvalidVariableName_Throws() { TestConfigurationEnvironmentVariableProvider provider = await CreateProviderAsync( "{\"environmentVariables\": {\"FOO=BAR\": \"value\"}}"); FormatException exception = await Assert.ThrowsAsync(() => provider.IsEnabledAsync()); Assert.Contains("FOO=BAR", exception.Message); } [TestMethod] public async Task UpdateAsync_AppliesAllEntries() { TestConfigurationEnvironmentVariableProvider provider = await CreateProviderAsync( "{\"environmentVariables\": {\"DOTNET_ENVIRONMENT\": \"Development\", \"HEADED\": \"1\"}}"); Assert.IsTrue(await provider.IsEnabledAsync()); EnvironmentVariables environmentVariables = new(new TestLoggerFactory()) { CurrentProvider = provider, }; await provider.UpdateAsync(environmentVariables); Assert.IsTrue(environmentVariables.TryGetVariable("DOTNET_ENVIRONMENT", out OwnedEnvironmentVariable? envVar)); Assert.AreEqual("Development", envVar!.Value); Assert.IsFalse(envVar.IsLocked, "Entry must not be locked so other providers (e.g. runsettings) can override."); Assert.IsFalse(envVar.IsSecret); Assert.IsTrue(environmentVariables.TryGetVariable("HEADED", out envVar)); Assert.AreEqual("1", envVar!.Value); } [TestMethod] public async Task ValidateAsync_AlwaysSucceeds() { TestConfigurationEnvironmentVariableProvider provider = await CreateProviderAsync( "{\"environmentVariables\": {\"FOO\": \"bar\"}}"); ValidationResult result = await provider.ValidateTestHostEnvironmentVariablesAsync( new EnvironmentVariables(new TestLoggerFactory())); Assert.IsTrue(result.IsValid); } [TestMethod] public async Task SystemProviderLocksDeadlineSnapshotAgainstChildOnlyOverrides() { var sourceVariables = new Hashtable { [EnvironmentVariableConstants.TESTINGPLATFORM_DEADLINE] = "2030-01-01T00:00:00Z", }; Mock environment = new(); environment.Setup(x => x.GetEnvironmentVariables()).Returns(sourceVariables); var systemProvider = new SystemEnvironmentVariableProvider(environment.Object); EnvironmentVariables environmentVariables = new(new TestLoggerFactory()) { CurrentProvider = systemProvider, }; await systemProvider.UpdateAsync(environmentVariables); Assert.IsTrue(environmentVariables.TryGetVariable( EnvironmentVariableConstants.TESTINGPLATFORM_DEADLINE, out OwnedEnvironmentVariable? deadline)); Assert.AreEqual("2030-01-01T00:00:00Z", deadline!.Value); Assert.IsTrue(deadline.IsLocked); Assert.IsTrue(environmentVariables.TryGetVariable( EnvironmentVariableConstants.TESTINGPLATFORM_DEADLINE_STOP_MARGIN, out OwnedEnvironmentVariable? stopMargin)); Assert.AreEqual(string.Empty, stopMargin!.Value); Assert.IsTrue(stopMargin.IsLocked); TestConfigurationEnvironmentVariableProvider testConfigurationProvider = await CreateProviderAsync( $"{{\"environmentVariables\": {{\"{EnvironmentVariableConstants.TESTINGPLATFORM_DEADLINE}\": \"2040-01-01T00:00:00Z\"}}}}"); Assert.IsTrue(await testConfigurationProvider.IsEnabledAsync()); environmentVariables.CurrentProvider = testConfigurationProvider; InvalidOperationException exception = await Assert.ThrowsAsync( () => testConfigurationProvider.UpdateAsync(environmentVariables)); Assert.Contains(EnvironmentVariableConstants.TESTINGPLATFORM_DEADLINE, exception.Message); } [TestMethod] [OSCondition(ConditionMode.Include, OperatingSystems.Windows)] public async Task SystemProviderLocksDeadlineSnapshotCaseInsensitivelyOnWindows() { string lowerCaseDeadline = EnvironmentVariableConstants.TESTINGPLATFORM_DEADLINE.ToLowerInvariant(); var sourceVariables = new Hashtable { [lowerCaseDeadline] = "2030-01-01T00:00:00Z", }; Mock environment = new(); environment.Setup(x => x.GetEnvironmentVariables()).Returns(sourceVariables); var systemProvider = new SystemEnvironmentVariableProvider(environment.Object); EnvironmentVariables environmentVariables = new(new TestLoggerFactory()) { CurrentProvider = systemProvider, }; await systemProvider.UpdateAsync(environmentVariables); Assert.IsTrue(environmentVariables.TryGetVariable( EnvironmentVariableConstants.TESTINGPLATFORM_DEADLINE, out OwnedEnvironmentVariable? deadline)); Assert.AreEqual("2030-01-01T00:00:00Z", deadline!.Value); Assert.IsTrue(deadline.IsLocked); TestConfigurationEnvironmentVariableProvider testConfigurationProvider = await CreateProviderAsync( $"{{\"environmentVariables\": {{\"{EnvironmentVariableConstants.TESTINGPLATFORM_DEADLINE}\": \"2040-01-01T00:00:00Z\"}}}}"); Assert.IsTrue(await testConfigurationProvider.IsEnabledAsync()); environmentVariables.CurrentProvider = testConfigurationProvider; await Assert.ThrowsAsync( () => testConfigurationProvider.UpdateAsync(environmentVariables)); } private static async Task CreateProviderAsync(string jsonFileContent) { Mock fileSystem = new(); fileSystem.Setup(x => x.ExistFile(It.IsAny())).Returns(true); fileSystem.Setup(x => x.NewFileStream(It.IsAny(), FileMode.Open, FileAccess.Read)) .Returns(() => new ConfigurationManagerTests.MemoryFileStream(Encoding.UTF8.GetBytes(jsonFileContent))); CurrentTestApplicationModuleInfo testApplicationModuleInfo = new(new SystemEnvironment(), new SystemProcessHandler()); ConfigurationManager configurationManager = new(fileSystem.Object, testApplicationModuleInfo, new SystemEnvironment()); configurationManager.AddConfigurationSource(() => new JsonConfigurationSource(testApplicationModuleInfo, fileSystem.Object, null)); IConfiguration configuration = await configurationManager.BuildAsync(null, new CommandLineParseResult(null, new List(), [])); return new TestConfigurationEnvironmentVariableProvider(configuration); } private sealed class TestLoggerFactory : ILoggerFactory { public ILogger CreateLogger(string categoryName) => new TestLogger(); private sealed class TestLogger : ILogger { public bool IsEnabled(LogLevel logLevel) => false; public Task LogAsync(LogLevel logLevel, TState state, Exception? exception, Func formatter) => Task.CompletedTask; public void Log(LogLevel logLevel, TState state, Exception? exception, Func formatter) { } } } }