Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,13 @@ private Dictionary<string, string> GetEnvVariables(
variables.Add(EnviromentVariables.AppEndTag, appEndTag);
}

// Propagate DOTNET_CI environment variable if it's set on the host
var dotnetCI = Environment.GetEnvironmentVariable(EnviromentVariables.DotnetCI);
if (!string.IsNullOrEmpty(dotnetCI))
{
variables.Add(EnviromentVariables.DotnetCI, dotnetCI);
}

AddExtraEnvVars(variables, extraEnvVariables);

return variables;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.XHarness.Android;
using Microsoft.DotNet.XHarness.CLI.Android;
Expand Down Expand Up @@ -61,11 +63,19 @@ protected override ExitCode InvokeCommand(ILogger logger)
{
runner.ClearAdbLog();

// Propagate DOTNET_CI environment variable if it's set on the host
var instrumentationArguments = new Dictionary<string, string>(Arguments.InstrumentationArguments.Value);
var dotnetCI = System.Environment.GetEnvironmentVariable("DOTNET_CI");
if (!string.IsNullOrEmpty(dotnetCI) && !instrumentationArguments.ContainsKey("DOTNET_CI"))
{
instrumentationArguments.Add("DOTNET_CI", dotnetCI);
}

var instrumentationRunner = new InstrumentationRunner(logger, runner);
exitCode = instrumentationRunner.RunApkInstrumentation(
Arguments.PackageName,
Arguments.InstrumentationName,
Arguments.InstrumentationArguments,
instrumentationArguments,
Arguments.OutputDirectory,
Arguments.DeviceOutputFolder,
Arguments.Timeout,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ protected override async Task<ExitCode> InvokeInternal(ILogger logger)
}
}

// Propagate DOTNET_CI environment variable if it's set on the host
var dotnetCI = System.Environment.GetEnvironmentVariable("DOTNET_CI");
if (!string.IsNullOrEmpty(dotnetCI))
{
engineArgs.Add("--env");
engineArgs.Add($"DOTNET_CI={dotnetCI}");
}

engineArgs.AddRange(PassThroughArguments);

var xmlResultsFilePath = Path.Combine(Arguments.OutputDirectory, "testResults.xml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,16 @@ private string BuildUrl(ServerURLs serverURLs)
sb.Append($"arg={HttpUtility.UrlEncode(arg)}");
}

// Propagate DOTNET_CI environment variable if it's set on the host
var dotnetCI = System.Environment.GetEnvironmentVariable("DOTNET_CI");
if (!string.IsNullOrEmpty(dotnetCI))
{
if (sb.Length > 0)
sb.Append('&');

sb.Append($"arg={HttpUtility.UrlEncode($"--setenv=DOTNET_CI={dotnetCI}")}");
}

if (sb.Length > 0)
sb.Append('&');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,17 +135,33 @@ protected override async Task<ExitCode> InvokeInternal(ILogger logger)
symbolicator);
var logProcessorTask = Task.Run(() => logProcessor.RunAsync(cts.Token));

// Prepare environment variables
Dictionary<string, string>? environmentVariables = null;
if (Arguments.Engine.Value == JavaScriptEngine.NodeJS)
{
// Node respects LANG only, ignores LANGUAGE
environmentVariables = new Dictionary<string, string>() { {"LANG", Arguments.Locale} };
}

// Propagate DOTNET_CI environment variable if it's set on the host
var dotnetCI = System.Environment.GetEnvironmentVariable("DOTNET_CI");
if (!string.IsNullOrEmpty(dotnetCI))
{
environmentVariables ??= new Dictionary<string, string>();
if (!environmentVariables.ContainsKey("DOTNET_CI"))
{
environmentVariables.Add("DOTNET_CI", dotnetCI);
}
}

var processTask = processManager.ExecuteCommandAsync(
engineBinary,
engineArgs,
log: new CallbackLog(m => logger.LogInformation(m)),
stdoutLog: new CallbackLog(msg => logProcessor.Invoke(msg)),
stderrLog: new CallbackLog(logProcessor.ProcessErrorMessage),
Arguments.Timeout,
// Node respects LANG only, ignores LANGUAGE
environmentVariables: Arguments.Engine.Value == JavaScriptEngine.NodeJS ?
new Dictionary<string, string>() { {"LANG", Arguments.Locale} } :
null);
environmentVariables);

TaskCompletionSource wasmExitReceivedTcs = logProcessor.WasmExitReceivedTcs;
var tasks = new Task[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,10 @@ public static class EnviromentVariables
/// Env var uses to notify the test application which test classes will be excluded.
/// </summary>
public const string SkippedClasses = "NUNIT_SKIPPED_CLASSES";

/// <summary>
/// Env var that indicates the test is running in CI environment.
/// This is used by SkipOnCIAttribute and other test attributes to skip tests that shouldn't run in CI.
/// </summary>
public const string DotnetCI = "DOTNET_CI";
}
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,156 @@ public async Task TestOnDeviceWithAppEndSignalTest()
deviceSystemLog.Verify(x => x.Dispose(), Times.AtLeastOnce);
}

[Fact]
public async Task TestOnSimulator_PropagatesDotnetCIEnvVar()
{
// Setup DOTNET_CI environment variable
var originalValue = Environment.GetEnvironmentVariable("DOTNET_CI");
Environment.SetEnvironmentVariable("DOTNET_CI", "true");

try
{
var testResultFilePath = Path.GetTempFileName();
var listenerLogFile = Mock.Of<IFileBackedLog>(x => x.FullPath == testResultFilePath);
File.WriteAllLines(testResultFilePath, new[] { "Some result here", "Tests run: 124", "Some result there" });

_logs
.Setup(x => x.Create("test-ios-simulator-64-mocked_timestamp.log", "TestLog", It.IsAny<bool?>()))
.Returns(listenerLogFile);

var captureLog = new Mock<ICaptureLog>();
captureLog.SetupGet(x => x.FullPath).Returns(_simulatorLogPath);

var captureLogFactory = new Mock<ICaptureLogFactory>();
captureLogFactory
.Setup(x => x.Create(
Path.Combine(_logs.Object.Directory, _mockSimulator.Name + ".log"),
_mockSimulator.SystemLog,
false,
It.IsAny<LogType>()))
.Returns(captureLog.Object);

var appTester = new AppTester(
_processManager.Object,
_listenerFactory.Object,
_snapshotReporterFactory,
captureLogFactory.Object,
Mock.Of<IDeviceLogCapturerFactory>(),
_testReporterFactory,
new XmlResultParser(),
_mainLog.Object,
_logs.Object,
_helpers.Object);

// Act
var (result, resultMessage) = await appTester.TestApp(
_appBundleInfo,
new TestTargetOs(TestTarget.Simulator_tvOS, null),
_mockSimulator,
null,
TimeSpan.FromSeconds(30),
TimeSpan.FromSeconds(30),
signalAppEnd: false,
extraAppArguments: Array.Empty<string>(),
extraEnvVariables: Array.Empty<(string, string)>());

// Verify
Assert.Equal(TestExecutingResult.Succeeded, result);

// Verify DOTNET_CI was passed as an environment variable
_processManager
.Verify(
x => x.ExecuteCommandAsync(
It.Is<MlaunchArguments>(args => args.AsCommandLine().Contains("-setenv=DOTNET_CI=true")),
_mainLog.Object,
It.IsAny<TimeSpan>(),
It.IsAny<Dictionary<string, string>>(),
It.IsAny<int>(),
It.IsAny<CancellationToken>()),
Times.Once);
}
finally
{
// Restore original value
Environment.SetEnvironmentVariable("DOTNET_CI", originalValue);
}
}

[Fact]
public async Task TestOnSimulator_DoesNotPropagateDotnetCIWhenNotSet()
{
// Ensure DOTNET_CI environment variable is not set
var originalValue = Environment.GetEnvironmentVariable("DOTNET_CI");
Environment.SetEnvironmentVariable("DOTNET_CI", null);

try
{
var testResultFilePath = Path.GetTempFileName();
var listenerLogFile = Mock.Of<IFileBackedLog>(x => x.FullPath == testResultFilePath);
File.WriteAllLines(testResultFilePath, new[] { "Some result here", "Tests run: 124", "Some result there" });

_logs
.Setup(x => x.Create("test-ios-simulator-64-mocked_timestamp.log", "TestLog", It.IsAny<bool?>()))
.Returns(listenerLogFile);

var captureLog = new Mock<ICaptureLog>();
captureLog.SetupGet(x => x.FullPath).Returns(_simulatorLogPath);

var captureLogFactory = new Mock<ICaptureLogFactory>();
captureLogFactory
.Setup(x => x.Create(
Path.Combine(_logs.Object.Directory, _mockSimulator.Name + ".log"),
_mockSimulator.SystemLog,
false,
It.IsAny<LogType>()))
.Returns(captureLog.Object);

var appTester = new AppTester(
_processManager.Object,
_listenerFactory.Object,
_snapshotReporterFactory,
captureLogFactory.Object,
Mock.Of<IDeviceLogCapturerFactory>(),
_testReporterFactory,
new XmlResultParser(),
_mainLog.Object,
_logs.Object,
_helpers.Object);

// Act
var (result, resultMessage) = await appTester.TestApp(
_appBundleInfo,
new TestTargetOs(TestTarget.Simulator_tvOS, null),
_mockSimulator,
null,
TimeSpan.FromSeconds(30),
TimeSpan.FromSeconds(30),
signalAppEnd: false,
extraAppArguments: Array.Empty<string>(),
extraEnvVariables: Array.Empty<(string, string)>());

// Verify
Assert.Equal(TestExecutingResult.Succeeded, result);

// Verify DOTNET_CI was NOT passed as an environment variable
_processManager
.Verify(
x => x.ExecuteCommandAsync(
It.Is<MlaunchArguments>(args => !args.AsCommandLine().Contains("-setenv=DOTNET_CI")),
_mainLog.Object,
It.IsAny<TimeSpan>(),
It.IsAny<Dictionary<string, string>>(),
It.IsAny<int>(),
It.IsAny<CancellationToken>()),
Times.Once);
}
finally
{
// Restore original value
Environment.SetEnvironmentVariable("DOTNET_CI", originalValue);
}
}

private string GetExpectedDeviceMlaunchArgs(string? skippedTests = null, bool useTunnel = false, string? extraArgs = null) =>
"-setenv=NUNIT_AUTOEXIT=true " +
$"-setenv=NUNIT_HOSTPORT={Port} " +
Expand Down
Loading