Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,5 @@ public static void AddTestRunParametersService(this ITestApplicationBuilder buil
/// <param name="extension">The extension that will be used as the source of registration for this helper service.</param>
public static void AddRunSettingsEnvironmentVariableProvider(this ITestApplicationBuilder builder, IExtension extension)
=> builder.TestHostControllers.AddEnvironmentVariableProvider(serviceProvider
=> new RunSettingsEnvironmentVariableProvider(extension, serviceProvider.GetCommandLineOptions(), serviceProvider.GetFileSystem()));
=> new RunSettingsEnvironmentVariableProvider(extension, serviceProvider.GetCommandLineOptions(), serviceProvider.GetFileSystem(), serviceProvider.GetEnvironment()));
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.Testing.Extensions.VSTestBridge.CommandLine;
using Microsoft.Testing.Platform;
using Microsoft.Testing.Platform.CommandLine;
using Microsoft.Testing.Platform.Extensions;
using Microsoft.Testing.Platform.Extensions.TestHostControllers;
Expand All @@ -14,13 +15,15 @@ internal sealed class RunSettingsEnvironmentVariableProvider : ITestHostEnvironm
private readonly IExtension _extension;
private readonly ICommandLineOptions _commandLineOptions;
private readonly IFileSystem _fileSystem;
private readonly IEnvironment _environment;
private XDocument? _runSettings;

public RunSettingsEnvironmentVariableProvider(IExtension extension, ICommandLineOptions commandLineOptions, IFileSystem fileSystem)
public RunSettingsEnvironmentVariableProvider(IExtension extension, ICommandLineOptions commandLineOptions, IFileSystem fileSystem, IEnvironment environment)
{
_extension = extension;
_commandLineOptions = commandLineOptions;
_fileSystem = fileSystem;
_environment = environment;
}

public string Uid => _extension.Uid;
Expand All @@ -33,23 +36,56 @@ public RunSettingsEnvironmentVariableProvider(IExtension extension, ICommandLine

public async Task<bool> IsEnabledAsync()
{
if (!_commandLineOptions.TryGetOptionArgumentList(RunSettingsCommandLineOptionsProvider.RunSettingsOptionName, out string[]? runsettings))
string? runSettingsFilePath = null;
string? runSettingsContent = null;

// Try to get runsettings from command line
if (_commandLineOptions.TryGetOptionArgumentList(RunSettingsCommandLineOptionsProvider.RunSettingsOptionName, out string[]? runsettings)
&& runsettings.Length > 0)
{
return false;
if (_fileSystem.ExistFile(runsettings[0]))
{
runSettingsFilePath = runsettings[0];
}
}

if (!_fileSystem.ExistFile(runsettings[0]))
// If not from command line, try environment variable with content
if (runSettingsFilePath is null)
{
return false;
runSettingsContent = _environment.GetEnvironmentVariable("TESTINGPLATFORM_EXPERIMENTAL_VSTEST_RUNSETTINGS");
}

using IFileStream fileStream = _fileSystem.NewFileStream(runsettings[0], FileMode.Open, FileAccess.Read);
// If not from content env var, try environment variable with file path
if (runSettingsFilePath is null && RoslynString.IsNullOrEmpty(runSettingsContent))
{
string? envVarFilePath = _environment.GetEnvironmentVariable("TESTINGPLATFORM_VSTESTBRIDGE_RUNSETTINGS_FILE");
if (!RoslynString.IsNullOrEmpty(envVarFilePath) && _fileSystem.ExistFile(envVarFilePath))
{
runSettingsFilePath = envVarFilePath;
}
}

// If we have a file path, read from file
if (runSettingsFilePath is not null)
{
using IFileStream fileStream = _fileSystem.NewFileStream(runSettingsFilePath, FileMode.Open, FileAccess.Read);
#if NETCOREAPP
_runSettings = await XDocument.LoadAsync(fileStream.Stream, LoadOptions.None, CancellationToken.None).ConfigureAwait(false);
_runSettings = await XDocument.LoadAsync(fileStream.Stream, LoadOptions.None, CancellationToken.None).ConfigureAwait(false);
#else
using StreamReader streamReader = new(fileStream.Stream);
_runSettings = XDocument.Parse(await streamReader.ReadToEndAsync().ConfigureAwait(false));
using StreamReader streamReader = new(fileStream.Stream);
_runSettings = XDocument.Parse(await streamReader.ReadToEndAsync().ConfigureAwait(false));
#endif
}
else if (!RoslynString.IsNullOrEmpty(runSettingsContent))
{
// If we have content, parse it directly
_runSettings = XDocument.Parse(runSettingsContent);
}
else
{
return false;
}

return _runSettings.Element("RunSettings")?.Element("RunConfiguration")?.Element("EnvironmentVariables") is not null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
// 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.Extensions.VSTestBridge.TestHostControllers;
using Microsoft.Testing.Extensions.VSTestBridge.UnitTests.Helpers;
using Microsoft.Testing.Platform.CommandLine;
using Microsoft.Testing.Platform.Extensions.TestHostControllers;
using Microsoft.Testing.Platform.Helpers;

using Moq;

namespace Microsoft.Testing.Extensions.VSTestBridge.UnitTests.TestHostControllers;

[TestClass]
public sealed class RunSettingsEnvironmentVariableProviderTests
{
private const string RunSettingsWithEnvironmentVariables = """
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<RunConfiguration>
<EnvironmentVariables>
<TEST_ENV>TestValue</TEST_ENV>
<ANOTHER_VAR>AnotherValue</ANOTHER_VAR>
</EnvironmentVariables>
</RunConfiguration>
</RunSettings>
""";

private const string RunSettingsWithoutEnvironmentVariables = """
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<RunConfiguration>
</RunConfiguration>
</RunSettings>
""";

[TestMethod]
public async Task IsEnabledAsync_WhenCommandLineOptionProvided_ReturnsTrue()
{
// Arrange
const string filePath = "test.runsettings";
var commandLineOptions = new Mock<ICommandLineOptions>();
commandLineOptions.Setup(x => x.TryGetOptionArgumentList("settings", out It.Ref<string[]?>.IsAny))
.Returns((string optionName, out string[]? value) =>
{
value = [filePath];
return true;
});

var fileSystem = new Mock<IFileSystem>();
fileSystem.Setup(x => x.ExistFile(filePath)).Returns(true);
var fileStream = new Mock<IFileStream>();
var stream = new MemoryStream(Encoding.UTF8.GetBytes(RunSettingsWithEnvironmentVariables));
fileStream.Setup(x => x.Stream).Returns(stream);
fileSystem.Setup(x => x.NewFileStream(filePath, FileMode.Open, FileAccess.Read)).Returns(fileStream.Object);

var environment = new Mock<IEnvironment>();

var provider = new RunSettingsEnvironmentVariableProvider(new TestExtension(), commandLineOptions.Object, fileSystem.Object, environment.Object);

// Act
bool result = await provider.IsEnabledAsync();

// Assert
Assert.IsTrue(result);
}

[TestMethod]
public async Task IsEnabledAsync_WhenCommandLineOptionProvidedButNoEnvironmentVariables_ReturnsFalse()
{
// Arrange
const string filePath = "test.runsettings";
var commandLineOptions = new Mock<ICommandLineOptions>();
commandLineOptions.Setup(x => x.TryGetOptionArgumentList("settings", out It.Ref<string[]?>.IsAny))
.Returns((string optionName, out string[]? value) =>
{
value = [filePath];
return true;
});

var fileSystem = new Mock<IFileSystem>();
fileSystem.Setup(x => x.ExistFile(filePath)).Returns(true);
var fileStream = new Mock<IFileStream>();
var stream = new MemoryStream(Encoding.UTF8.GetBytes(RunSettingsWithoutEnvironmentVariables));
fileStream.Setup(x => x.Stream).Returns(stream);
fileSystem.Setup(x => x.NewFileStream(filePath, FileMode.Open, FileAccess.Read)).Returns(fileStream.Object);

var environment = new Mock<IEnvironment>();

var provider = new RunSettingsEnvironmentVariableProvider(new TestExtension(), commandLineOptions.Object, fileSystem.Object, environment.Object);

// Act
bool result = await provider.IsEnabledAsync();

// Assert
Assert.IsFalse(result);
}

[TestMethod]
public async Task IsEnabledAsync_WhenEnvironmentVariableWithContentProvided_ReturnsTrue()
{
// Arrange
var commandLineOptions = new Mock<ICommandLineOptions>();
commandLineOptions.Setup(x => x.TryGetOptionArgumentList("settings", out It.Ref<string[]?>.IsAny))
.Returns((string optionName, out string[]? value) =>
{
value = null;
return false;
});

var fileSystem = new Mock<IFileSystem>();

var environment = new Mock<IEnvironment>();
environment.Setup(x => x.GetEnvironmentVariable("TESTINGPLATFORM_EXPERIMENTAL_VSTEST_RUNSETTINGS"))
.Returns(RunSettingsWithEnvironmentVariables);

var provider = new RunSettingsEnvironmentVariableProvider(new TestExtension(), commandLineOptions.Object, fileSystem.Object, environment.Object);

// Act
bool result = await provider.IsEnabledAsync();

// Assert
Assert.IsTrue(result);
}

[TestMethod]
public async Task IsEnabledAsync_WhenEnvironmentVariableWithContentProvidedButNoEnvironmentVariables_ReturnsFalse()
{
// Arrange
var commandLineOptions = new Mock<ICommandLineOptions>();
commandLineOptions.Setup(x => x.TryGetOptionArgumentList("settings", out It.Ref<string[]?>.IsAny))
.Returns((string optionName, out string[]? value) =>
{
value = null;
return false;
});

var fileSystem = new Mock<IFileSystem>();

var environment = new Mock<IEnvironment>();
environment.Setup(x => x.GetEnvironmentVariable("TESTINGPLATFORM_EXPERIMENTAL_VSTEST_RUNSETTINGS"))
.Returns(RunSettingsWithoutEnvironmentVariables);

var provider = new RunSettingsEnvironmentVariableProvider(new TestExtension(), commandLineOptions.Object, fileSystem.Object, environment.Object);

// Act
bool result = await provider.IsEnabledAsync();

// Assert
Assert.IsFalse(result);
}

[TestMethod]
public async Task IsEnabledAsync_WhenEnvironmentVariableWithFilePathProvided_ReturnsTrue()
{
// Arrange
const string filePath = "test.runsettings";
var commandLineOptions = new Mock<ICommandLineOptions>();
commandLineOptions.Setup(x => x.TryGetOptionArgumentList("settings", out It.Ref<string[]?>.IsAny))
.Returns((string optionName, out string[]? value) =>
{
value = null;
return false;
});

var fileSystem = new Mock<IFileSystem>();
fileSystem.Setup(x => x.ExistFile(filePath)).Returns(true);
var fileStream = new Mock<IFileStream>();
var stream = new MemoryStream(Encoding.UTF8.GetBytes(RunSettingsWithEnvironmentVariables));
fileStream.Setup(x => x.Stream).Returns(stream);
fileSystem.Setup(x => x.NewFileStream(filePath, FileMode.Open, FileAccess.Read)).Returns(fileStream.Object);

var environment = new Mock<IEnvironment>();
environment.Setup(x => x.GetEnvironmentVariable("TESTINGPLATFORM_VSTESTBRIDGE_RUNSETTINGS_FILE"))
.Returns(filePath);

var provider = new RunSettingsEnvironmentVariableProvider(new TestExtension(), commandLineOptions.Object, fileSystem.Object, environment.Object);

// Act
bool result = await provider.IsEnabledAsync();

// Assert
Assert.IsTrue(result);
}

[TestMethod]
public async Task IsEnabledAsync_WhenNoRunsettingsProvided_ReturnsFalse()
{
// Arrange
var commandLineOptions = new Mock<ICommandLineOptions>();
commandLineOptions.Setup(x => x.TryGetOptionArgumentList("settings", out It.Ref<string[]?>.IsAny))
.Returns((string optionName, out string[]? value) =>
{
value = null;
return false;
});

var fileSystem = new Mock<IFileSystem>();

var environment = new Mock<IEnvironment>();

var provider = new RunSettingsEnvironmentVariableProvider(new TestExtension(), commandLineOptions.Object, fileSystem.Object, environment.Object);

// Act
bool result = await provider.IsEnabledAsync();

// Assert
Assert.IsFalse(result);
}

[TestMethod]
public async Task UpdateAsync_SetsEnvironmentVariablesFromRunsettings()
{
// Arrange
var commandLineOptions = new Mock<ICommandLineOptions>();
commandLineOptions.Setup(x => x.TryGetOptionArgumentList("settings", out It.Ref<string[]?>.IsAny))
.Returns((string optionName, out string[]? value) =>
{
value = null;
return false;
});

var fileSystem = new Mock<IFileSystem>();

var environment = new Mock<IEnvironment>();
environment.Setup(x => x.GetEnvironmentVariable("TESTINGPLATFORM_EXPERIMENTAL_VSTEST_RUNSETTINGS"))
.Returns(RunSettingsWithEnvironmentVariables);

var provider = new RunSettingsEnvironmentVariableProvider(new TestExtension(), commandLineOptions.Object, fileSystem.Object, environment.Object);

var environmentVariables = new Mock<IEnvironmentVariables>();
var capturedVariables = new List<EnvironmentVariable>();
environmentVariables.Setup(x => x.SetVariable(It.IsAny<EnvironmentVariable>()))
.Callback<EnvironmentVariable>(capturedVariables.Add);

// Act
await provider.IsEnabledAsync();
await provider.UpdateAsync(environmentVariables.Object);

// Assert
Assert.HasCount(2, capturedVariables);
Assert.Contains(v => v.Variable == "TEST_ENV" && v.Value == "TestValue", capturedVariables);
Assert.Contains(v => v.Variable == "ANOTHER_VAR" && v.Value == "AnotherValue", capturedVariables);
}
}
Loading