Skip to content

Commit 154a6dc

Browse files
[W3C] Refactor W3C integration tests (#6328)
1 parent 21993b0 commit 154a6dc

File tree

1 file changed

+100
-14
lines changed

1 file changed

+100
-14
lines changed

test/OpenTelemetry.Instrumentation.W3cTraceContext.Tests/W3CTraceContextTests.cs

Lines changed: 100 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ public W3CTraceContextTests(ITestOutputHelper output)
3333
[SkipUnlessEnvVarFoundTheory(W3CTraceContextEnvVarName)]
3434
[InlineData("placeholder")]
3535
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "xUnit1026:Theory methods should use all of their parameters", Justification = "Need to use SkipUnlessEnvVarFoundTheory")]
36-
public void W3CTraceContextTestSuiteAsync(string value)
36+
public async Task W3CTraceContextTestSuiteAsync(string value)
3737
{
3838
// configure SDK
3939
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
40-
.AddAspNetCoreInstrumentation()
41-
.Build();
40+
.AddAspNetCoreInstrumentation()
41+
.Build();
4242

4343
var builder = WebApplication.CreateBuilder();
4444
using var app = builder.Build();
@@ -66,14 +66,16 @@ public void W3CTraceContextTestSuiteAsync(string value)
6666
return result;
6767
});
6868

69-
app.RunAsync("http://localhost:5000/");
69+
_ = app.RunAsync("http://localhost:5000/");
7070

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

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

76-
this.output.WriteLine("result:" + result);
77+
this.output.WriteLine("[stderr]" + stderr);
78+
this.output.WriteLine("[stdout]" + stdout);
7779

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

87-
private static string RunCommand(string command, string args)
89+
private static async Task<(string StdOut, string StdErr)> RunCommand(string command, string args)
8890
{
89-
using var proc = new Process
91+
using var process = new Process
9092
{
9193
StartInfo = new ProcessStartInfo
9294
{
@@ -99,12 +101,43 @@ private static string RunCommand(string command, string args)
99101
WorkingDirectory = ".",
100102
},
101103
};
102-
proc.Start();
104+
process.Start();
103105

104-
// TODO: after W3C Trace Context test suite passes, it might go in standard output
105-
var results = proc.StandardError.ReadToEnd();
106-
proc.WaitForExit();
107-
return results;
106+
// See https://stackoverflow.com/a/16326426/1064169 and
107+
// https://learn.microsoft.com/dotnet/api/system.diagnostics.processstartinfo.redirectstandardoutput.
108+
using var outputTokenSource = new CancellationTokenSource();
109+
110+
var readOutput = ReadOutputAsync(process, outputTokenSource.Token);
111+
112+
try
113+
{
114+
await process.WaitForExitAsync();
115+
}
116+
catch (OperationCanceledException)
117+
{
118+
try
119+
{
120+
process.Kill(entireProcessTree: true);
121+
}
122+
catch (Exception)
123+
{
124+
// Ignore
125+
}
126+
}
127+
finally
128+
{
129+
await outputTokenSource.CancelAsync();
130+
}
131+
132+
try
133+
{
134+
return await readOutput;
135+
}
136+
finally
137+
{
138+
process.Dispose();
139+
outputTokenSource.Dispose();
140+
}
108141
}
109142

110143
private static string ParseLastLine(string output)
@@ -119,6 +152,59 @@ private static string ParseLastLine(string output)
119152
return output.Substring(lastNewLineCharacterPos + 1);
120153
}
121154

155+
private static async Task<(string Output, string Error)> ReadOutputAsync(
156+
Process process,
157+
CancellationToken cancellationToken)
158+
{
159+
var processErrors = ConsumeStreamAsync(process.StandardError, process.StartInfo.RedirectStandardError, cancellationToken);
160+
var processOutput = ConsumeStreamAsync(process.StandardOutput, process.StartInfo.RedirectStandardOutput, cancellationToken);
161+
162+
await Task.WhenAll(processErrors, processOutput);
163+
164+
string error = string.Empty;
165+
string output = string.Empty;
166+
167+
if (processErrors.Status == TaskStatus.RanToCompletion)
168+
{
169+
error = (await processErrors).ToString();
170+
}
171+
172+
if (processOutput.Status == TaskStatus.RanToCompletion)
173+
{
174+
output = (await processOutput).ToString();
175+
}
176+
177+
return (output, error);
178+
}
179+
180+
private static Task<StringBuilder> ConsumeStreamAsync(
181+
StreamReader reader,
182+
bool isRedirected,
183+
CancellationToken cancellationToken)
184+
{
185+
return isRedirected ?
186+
Task.Run(() => ProcessStream(reader, cancellationToken), cancellationToken) :
187+
Task.FromResult(new StringBuilder(0));
188+
189+
static async Task<StringBuilder> ProcessStream(
190+
StreamReader reader,
191+
CancellationToken cancellationToken)
192+
{
193+
var builder = new StringBuilder();
194+
195+
try
196+
{
197+
builder.Append(await reader.ReadToEndAsync(cancellationToken));
198+
}
199+
catch (OperationCanceledException)
200+
{
201+
// Ignore
202+
}
203+
204+
return builder;
205+
}
206+
}
207+
122208
#pragma warning disable CA1812 // Avoid uninstantiated internal classes
123209
internal sealed class Data
124210
#pragma warning restore CA1812 // Avoid uninstantiated internal classes

0 commit comments

Comments
 (0)