Fix threading issue in ExecuteHelper (#13643)

This commit is contained in:
Michael Simons 2022-04-15 15:24:48 -05:00 committed by GitHub
parent 5609d7db88
commit 21c7bb3abb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 104 additions and 98 deletions

View file

@ -5,7 +5,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace Microsoft.DotNet.SourceBuild.SmokeTests; namespace Microsoft.DotNet.SourceBuild.SmokeTests;
@ -81,15 +80,6 @@ internal class DotNetHelper
public void ExecuteCmd(string args, string? workingDirectory = null, Action<Process>? additionalProcessConfigCallback = null, int expectedExitCode = 0, int millisecondTimeout = -1) public void ExecuteCmd(string args, string? workingDirectory = null, Action<Process>? additionalProcessConfigCallback = null, int expectedExitCode = 0, int millisecondTimeout = -1)
{ {
Action<Process, string?> configureProcess = (Process process, string? workingDirectory) => {
ConfigureProcess(process, workingDirectory);
if (additionalProcessConfigCallback != null)
{
additionalProcessConfigCallback(process);
}
};
(Process Process, string StdOut, string StdErr) executeResult = ExecuteHelper.ExecuteProcess( (Process Process, string StdOut, string StdErr) executeResult = ExecuteHelper.ExecuteProcess(
DotNetPath, DotNetPath,
args, args,
@ -98,6 +88,13 @@ internal class DotNetHelper
millisecondTimeout: millisecondTimeout); millisecondTimeout: millisecondTimeout);
ExecuteHelper.ValidateExitCode(executeResult, expectedExitCode); ExecuteHelper.ValidateExitCode(executeResult, expectedExitCode);
void configureProcess(Process process, string? workingDirectory)
{
ConfigureProcess(process, workingDirectory);
additionalProcessConfigCallback?.Invoke(process);
}
} }
public static void ConfigureProcess(Process process, string? workingDirectory, bool setPath = false) public static void ConfigureProcess(Process process, string? workingDirectory, bool setPath = false)

View file

@ -44,10 +44,24 @@ internal static class ExecuteHelper
configure?.Invoke(process); configure?.Invoke(process);
StringBuilder stdOutput = new(); StringBuilder stdOutput = new();
process.OutputDataReceived += new DataReceivedEventHandler((sender, e) => stdOutput.AppendLine(e.Data)); process.OutputDataReceived += new DataReceivedEventHandler(
(sender, e) =>
{
lock (stdOutput)
{
stdOutput.AppendLine(e.Data);
}
});
StringBuilder stdError = new(); StringBuilder stdError = new();
process.ErrorDataReceived += new DataReceivedEventHandler((sender, e) => stdError.AppendLine(e.Data)); process.ErrorDataReceived += new DataReceivedEventHandler(
(sender, e) =>
{
lock (stdError)
{
stdError.AppendLine(e.Data);
}
});
process.Start(); process.Start();
process.BeginOutputReadLine(); process.BeginOutputReadLine();

View file

@ -5,35 +5,34 @@
using System; using System;
using Xunit; using Xunit;
namespace Microsoft.DotNet.SourceBuild.SmokeTests namespace Microsoft.DotNet.SourceBuild.SmokeTests;
/// <summary>
/// A Fact that will be skipped based on the specified environment variable's value.
/// </summary>
internal class SkippableFactAttribute : FactAttribute
{ {
/// <summary> public SkippableFactAttribute(string envName, bool skipOnNullOrWhiteSpace = false, bool skipOnTrue = false) =>
/// A Fact that will be skipped based on the specified environment variable's value. CheckEnvs(skipOnNullOrWhiteSpace, skipOnTrue, (skip) => Skip = skip, envName);
/// </summary>
internal class SkippableFactAttribute : FactAttribute public SkippableFactAttribute(string[] envNames, bool skipOnNullOrWhiteSpace = false, bool skipOnTrue = false) =>
CheckEnvs(skipOnNullOrWhiteSpace, skipOnTrue, (skip) => Skip = skip, envNames);
public static void CheckEnvs(bool skipOnNullOrWhiteSpace, bool skipOnTrue, Action<string> setSkip, params string[] envNames)
{ {
public SkippableFactAttribute(string envName, bool skipOnNullOrWhiteSpace = false, bool skipOnTrue = false) => foreach (string envName in envNames)
CheckEnvs(skipOnNullOrWhiteSpace, skipOnTrue, (skip) => Skip = skip, envName);
public SkippableFactAttribute(string[] envNames, bool skipOnNullOrWhiteSpace = false, bool skipOnTrue = false) =>
CheckEnvs(skipOnNullOrWhiteSpace, skipOnTrue, (skip) => Skip = skip, envNames);
public static void CheckEnvs(bool skipOnNullOrWhiteSpace, bool skipOnTrue, Action<string> setSkip, params string[] envNames)
{ {
foreach (string envName in envNames) string? envValue = Environment.GetEnvironmentVariable(envName);
{
string? envValue = Environment.GetEnvironmentVariable(envName);
if (skipOnNullOrWhiteSpace && string.IsNullOrWhiteSpace(envValue)) if (skipOnNullOrWhiteSpace && string.IsNullOrWhiteSpace(envValue))
{ {
setSkip($"Skipping because `{envName}` is null or whitespace"); setSkip($"Skipping because `{envName}` is null or whitespace");
break; break;
} }
else if (skipOnTrue && bool.TryParse(envValue, out bool boolValue) && boolValue) else if (skipOnTrue && bool.TryParse(envValue, out bool boolValue) && boolValue)
{ {
setSkip($"Skipping because `{envName}` is set to True"); setSkip($"Skipping because `{envName}` is set to True");
break; break;
}
} }
} }
} }

View file

@ -4,17 +4,16 @@
using Xunit; using Xunit;
namespace Microsoft.DotNet.SourceBuild.SmokeTests namespace Microsoft.DotNet.SourceBuild.SmokeTests;
{
/// <summary>
/// A Theory that will be skipped based on the specified environment variable's value.
/// </summary>
internal class SkippableTheoryAttribute : TheoryAttribute
{
public SkippableTheoryAttribute(string envName, bool skipOnNullOrWhiteSpace = false, bool skipOnTrue = false) =>
SkippableFactAttribute.CheckEnvs(skipOnNullOrWhiteSpace, skipOnTrue, (skip) => Skip = skip, envName);
public SkippableTheoryAttribute(string[] envNames, bool skipOnNullOrWhiteSpace = false, bool skipOnTrue = false) => /// <summary>
SkippableFactAttribute.CheckEnvs(skipOnNullOrWhiteSpace, skipOnTrue, (skip) => Skip = skip, envNames); /// A Theory that will be skipped based on the specified environment variable's value.
} /// </summary>
internal class SkippableTheoryAttribute : TheoryAttribute
{
public SkippableTheoryAttribute(string envName, bool skipOnNullOrWhiteSpace = false, bool skipOnTrue = false) =>
SkippableFactAttribute.CheckEnvs(skipOnNullOrWhiteSpace, skipOnTrue, (skip) => Skip = skip, envName);
public SkippableTheoryAttribute(string[] envNames, bool skipOnNullOrWhiteSpace = false, bool skipOnTrue = false) =>
SkippableFactAttribute.CheckEnvs(skipOnNullOrWhiteSpace, skipOnTrue, (skip) => Skip = skip, envNames);
} }

View file

@ -2,66 +2,63 @@
// The .NET Foundation licenses this file to you under the MIT license. // The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; namespace Microsoft.DotNet.SourceBuild.SmokeTests;
namespace Microsoft.DotNet.SourceBuild.SmokeTests public class TestScenario
{ {
public class TestScenario public DotNetActions Commands { get; }
public DotNetLanguage Language { get; }
public bool NoHttps { get; set; } = Config.TargetRid.Contains("osx");
public string ScenarioName { get; }
public DotNetTemplate Template { get; }
public TestScenario(string scenarioName, DotNetLanguage language, DotNetTemplate template, DotNetActions commands = DotNetActions.None)
{ {
public DotNetActions Commands { get; } ScenarioName = scenarioName;
public DotNetLanguage Language { get; } Template = template;
public bool NoHttps { get; set; } = Config.TargetRid.Contains("osx"); Language = language;
public string ScenarioName { get; } Commands = commands;
public DotNetTemplate Template { get; } }
public TestScenario(string scenarioName, DotNetLanguage language, DotNetTemplate template, DotNetActions commands = DotNetActions.None) internal void Execute(DotNetHelper dotNetHelper)
{
// Don't use the cli language name in the project name because it may contain '#': https://github.com/dotnet/roslyn/issues/51692
string projectName = $"{ScenarioName}_{Template}_{Language}";
string customNewArgs = Template.IsAspNetCore() && NoHttps ? "--no-https" : string.Empty;
dotNetHelper.ExecuteNew(Template.GetName(), projectName, Language.ToCliName(), customArgs: customNewArgs);
if (Commands.HasFlag(DotNetActions.Build))
{ {
ScenarioName = scenarioName; dotNetHelper.ExecuteBuild(projectName);
Template = template;
Language = language;
Commands = commands;
} }
if (Commands.HasFlag(DotNetActions.Run))
internal void Execute(DotNetHelper dotNetHelper)
{ {
// Don't use the cli language name in the project name because it may contain '#': https://github.com/dotnet/roslyn/issues/51692 if (Template.IsAspNetCore())
string projectName = $"{ScenarioName}_{Template}_{Language}";
string customNewArgs = Template.IsAspNetCore() && NoHttps ? "--no-https" : string.Empty;
dotNetHelper.ExecuteNew(Template.GetName(), projectName, Language.ToCliName(), customArgs: customNewArgs);
if (Commands.HasFlag(DotNetActions.Build))
{ {
dotNetHelper.ExecuteBuild(projectName); dotNetHelper.ExecuteRunWeb(projectName);
} }
if (Commands.HasFlag(DotNetActions.Run)) else
{ {
if (Template.IsAspNetCore()) dotNetHelper.ExecuteRun(projectName);
{
dotNetHelper.ExecuteRunWeb(projectName);
}
else
{
dotNetHelper.ExecuteRun(projectName);
}
}
if (Commands.HasFlag(DotNetActions.Publish))
{
dotNetHelper.ExecutePublish(projectName);
}
if (Commands.HasFlag(DotNetActions.PublishComplex))
{
dotNetHelper.ExecutePublish(projectName, selfContained: false);
dotNetHelper.ExecutePublish(projectName, selfContained: true, Config.TargetRid);
dotNetHelper.ExecutePublish(projectName, selfContained: true, "linux-x64");
}
if (Commands.HasFlag(DotNetActions.PublishR2R))
{
dotNetHelper.ExecutePublish(projectName, selfContained: true, "linux-x64", trimmed: true, readyToRun: true);
}
if (Commands.HasFlag(DotNetActions.Test))
{
dotNetHelper.ExecuteTest(projectName);
} }
} }
if (Commands.HasFlag(DotNetActions.Publish))
{
dotNetHelper.ExecutePublish(projectName);
}
if (Commands.HasFlag(DotNetActions.PublishComplex))
{
dotNetHelper.ExecutePublish(projectName, selfContained: false);
dotNetHelper.ExecutePublish(projectName, selfContained: true, Config.TargetRid);
dotNetHelper.ExecutePublish(projectName, selfContained: true, "linux-x64");
}
if (Commands.HasFlag(DotNetActions.PublishR2R))
{
dotNetHelper.ExecutePublish(projectName, selfContained: true, "linux-x64", trimmed: true, readyToRun: true);
}
if (Commands.HasFlag(DotNetActions.Test))
{
dotNetHelper.ExecuteTest(projectName);
}
} }
} }