Skip to content
Merged
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 @@ -33,12 +33,12 @@ public W3CTraceContextTests(ITestOutputHelper output)
[SkipUnlessEnvVarFoundTheory(W3CTraceContextEnvVarName)]
[InlineData("placeholder")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters", Justification = "Need to use SkipUnlessEnvVarFoundTheory")]
public void W3CTraceContextTestSuiteAsync(string value)
public async Task W3CTraceContextTestSuiteAsync(string value)
{
// configure SDK
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddAspNetCoreInstrumentation()
.Build();
.AddAspNetCoreInstrumentation()
.Build();

var builder = WebApplication.CreateBuilder();
using var app = builder.Build();
Expand Down Expand Up @@ -66,14 +66,16 @@ public void W3CTraceContextTestSuiteAsync(string value)
return result;
});

app.RunAsync("http://localhost:5000/");
_ = app.RunAsync("http://localhost:5000/");

string result = RunCommand("python", "trace-context/test/test.py http://localhost:5000/");
(var stdout, var stderr) = await RunCommand("python", "-W ignore trace-context/test/test.py http://localhost:5000/");

// Assert
string lastLine = ParseLastLine(result);
// TODO: after W3C Trace Context test suite passes, it might go in standard output
string lastLine = ParseLastLine(stderr);

this.output.WriteLine("result:" + result);
this.output.WriteLine("[stderr]" + stderr);
this.output.WriteLine("[stdout]" + stdout);

// Assert on the last line
Assert.StartsWith("OK", lastLine, StringComparison.Ordinal);
Expand All @@ -84,9 +86,9 @@ public void Dispose()
this.httpClient.Dispose();
}

private static string RunCommand(string command, string args)
private static async Task<(string StdOut, string StdErr)> RunCommand(string command, string args)
{
using var proc = new Process
using var process = new Process
{
StartInfo = new ProcessStartInfo
{
Expand All @@ -99,12 +101,43 @@ private static string RunCommand(string command, string args)
WorkingDirectory = ".",
},
};
proc.Start();
process.Start();

// TODO: after W3C Trace Context test suite passes, it might go in standard output
var results = proc.StandardError.ReadToEnd();
proc.WaitForExit();
return results;
// See https://stackoverflow.com/a/16326426/1064169 and
// https://learn.microsoft.com/dotnet/api/system.diagnostics.processstartinfo.redirectstandardoutput.
using var outputTokenSource = new CancellationTokenSource();

var readOutput = ReadOutputAsync(process, outputTokenSource.Token);

try
{
await process.WaitForExitAsync();
}
catch (OperationCanceledException)
{
try
{
process.Kill(entireProcessTree: true);
}
catch (Exception)
{
// Ignore
}
}
finally
{
await outputTokenSource.CancelAsync();
}

try
{
return await readOutput;
}
finally
{
process.Dispose();
outputTokenSource.Dispose();
}
}

private static string ParseLastLine(string output)
Expand All @@ -119,6 +152,59 @@ private static string ParseLastLine(string output)
return output.Substring(lastNewLineCharacterPos + 1);
}

private static async Task<(string Output, string Error)> ReadOutputAsync(
Process process,
CancellationToken cancellationToken)
{
var processErrors = ConsumeStreamAsync(process.StandardError, process.StartInfo.RedirectStandardError, cancellationToken);
var processOutput = ConsumeStreamAsync(process.StandardOutput, process.StartInfo.RedirectStandardOutput, cancellationToken);

await Task.WhenAll(processErrors, processOutput);

string error = string.Empty;
string output = string.Empty;

if (processErrors.Status == TaskStatus.RanToCompletion)
{
error = (await processErrors).ToString();
}

if (processOutput.Status == TaskStatus.RanToCompletion)
{
output = (await processOutput).ToString();
}

return (output, error);
}

private static Task<StringBuilder> ConsumeStreamAsync(
StreamReader reader,
bool isRedirected,
CancellationToken cancellationToken)
{
return isRedirected ?
Task.Run(() => ProcessStream(reader, cancellationToken), cancellationToken) :
Task.FromResult(new StringBuilder(0));

static async Task<StringBuilder> ProcessStream(
StreamReader reader,
CancellationToken cancellationToken)
{
var builder = new StringBuilder();

try
{
builder.Append(await reader.ReadToEndAsync(cancellationToken));
}
catch (OperationCanceledException)
{
// Ignore
}

return builder;
}
}

#pragma warning disable CA1812 // Avoid uninstantiated internal classes
internal sealed class Data
#pragma warning restore CA1812 // Avoid uninstantiated internal classes
Expand Down