Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.DotNet.XHarness.CLI.CommandArguments;

internal class WebServerUploadResults : SwitchArgument
{
public WebServerUploadResults()
: base("web-server-upload-results", "Enable uploading XML test results", true)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ internal class WasiTestCommandArguments : XHarnessCommandArguments, IWebServerAr
public WebServerUseCrossOriginPolicyArguments WebServerUseCrossOriginPolicy { get; } = new();
public WebServerUseDefaultFilesArguments WebServerUseDefaultFiles { get; } = new();
public bool IsWebServerEnabled => WebServerMiddlewarePathsAndTypes.Value.Count > 0;
public WebServerUploadResults WebServerUploadResults { get; } = new();

protected override IEnumerable<Argument> GetArguments() => new Argument[]
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ internal interface IWebServerArguments
WebServerUseCorsArguments WebServerUseCors { get; }
WebServerUseCrossOriginPolicyArguments WebServerUseCrossOriginPolicy { get; }
WebServerUseDefaultFilesArguments WebServerUseDefaultFiles { get; }
WebServerUploadResults WebServerUploadResults { get; }
OutputDirectoryArgument OutputDirectory { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ internal class WasmTestBrowserCommandArguments : XHarnessCommandArguments, IWebS
public WebServerUseCorsArguments WebServerUseCors { get; } = new();
public WebServerUseCrossOriginPolicyArguments WebServerUseCrossOriginPolicy { get; } = new();
public WebServerUseDefaultFilesArguments WebServerUseDefaultFiles { get; } = new();
public WebServerUploadResults WebServerUploadResults { get; } = new();
public bool IsWebServerEnabled => WebServerMiddlewarePathsAndTypes.Value.Count > 0;

protected override IEnumerable<Argument> GetArguments() => new Argument[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal class WasmTestCommandArguments : XHarnessCommandArguments, IWebServerAr
public WebServerUseCorsArguments WebServerUseCors { get; } = new();
public WebServerUseCrossOriginPolicyArguments WebServerUseCrossOriginPolicy { get; } = new();
public WebServerUseDefaultFilesArguments WebServerUseDefaultFiles { get; } = new();
public WebServerUploadResults WebServerUploadResults { get; } = new();
public bool IsWebServerEnabled => WebServerMiddlewarePathsAndTypes.Value.Count > 0;

protected override IEnumerable<Argument> GetArguments() => new Argument[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ internal class WebServerCommandArguments : XHarnessCommandArguments, IWebServerA
public WebServerUseCorsArguments WebServerUseCors { get; } = new();
public WebServerUseCrossOriginPolicyArguments WebServerUseCrossOriginPolicy { get; } = new();
public WebServerUseDefaultFilesArguments WebServerUseDefaultFiles { get; } = new();
public WebServerUploadResults WebServerUploadResults { get; } = new();
public OutputDirectoryArgument OutputDirectory { get; } = new();

public bool IsWebServerEnabled => WebServerMiddlewarePathsAndTypes.Value.Count > 0;

public TimeoutArgument Timeout { get; } = new(TimeSpan.FromMinutes(15));
Expand Down
21 changes: 21 additions & 0 deletions src/Microsoft.DotNet.XHarness.CLI/Commands/WebServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using System.IO;

namespace Microsoft.DotNet.XHarness.CLI.Commands;

Expand Down Expand Up @@ -175,6 +176,20 @@ public void Configure(IApplicationBuilder app, IOptionsMonitor<TestWebServerOpti
});
});
}
if (options.WebServerUploadResults)
{
app.UseRouter(router =>
{
router.MapPost("/test-results", async context =>
{
var xmlResultsFilePath = Path.Combine(options.OutputDirectory!, "testResults.xml");

await using var fileStream = File.Create(xmlResultsFilePath);
await context.Request.BodyReader.CopyToAsync(fileStream);
_logger.LogInformation($"Stored {xmlResultsFilePath} results {fileStream.Position} bytes");
});
});
}

foreach (var middleware in options.EchoServerMiddlewares)
{
Expand All @@ -192,6 +207,8 @@ internal class TestWebServerOptions
public bool UseHttps { get; set; }
public bool UseCrossOriginPolicy { get; set; }
public bool UseDefaultFiles { get; set; }
public bool WebServerUploadResults { get; set; }
public string? OutputDirectory { get; set; }
public string? ContentRoot { get; set; }

public void CopyTo(TestWebServerOptions otherOptions)
Expand All @@ -202,6 +219,8 @@ public void CopyTo(TestWebServerOptions otherOptions)
otherOptions.UseHttps = UseHttps;
otherOptions.UseCrossOriginPolicy = UseCrossOriginPolicy;
otherOptions.UseDefaultFiles = UseDefaultFiles;
otherOptions.WebServerUploadResults = WebServerUploadResults;
otherOptions.OutputDirectory = OutputDirectory;
otherOptions.ContentRoot = ContentRoot;
}

Expand All @@ -212,6 +231,8 @@ public static TestWebServerOptions FromArguments(IWebServerArguments arguments)
options.UseHttps = arguments.WebServerUseHttps;
options.UseCrossOriginPolicy = arguments.WebServerUseCrossOriginPolicy;
options.UseDefaultFiles = arguments.WebServerUseDefaultFiles;
options.WebServerUploadResults = arguments.WebServerUploadResults;
options.OutputDirectory = arguments.OutputDirectory;
foreach (var middlewareType in arguments.WebServerMiddlewarePathsAndTypes.GetLoadedTypes())
{
options.EchoServerMiddlewares.Add(middlewareType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ internal static void ConfigureRunnerFilters(TestRunner runner, ApplicationOption
}
}

private static void WriteResults(TestRunner runner, ApplicationOptions options, LogWriter logger, TextWriter writer)
private static async Task WriteResults(TestRunner runner, ApplicationOptions options, LogWriter logger, TextWriter writer)
{
if (options.EnableXml && writer == null)
{
Expand All @@ -182,12 +182,12 @@ private static void WriteResults(TestRunner runner, ApplicationOptions options,

if (options.EnableXml)
{
runner.WriteResultsToFile(writer, options.XmlVersion);
await runner.WriteResultsToFile(writer, options.XmlVersion);
logger.Info("Xml file was written to the provided writer.");
}
else
{
string resultsFilePath = runner.WriteResultsToFile(options.XmlVersion);
string resultsFilePath = await runner.WriteResultsToFile(options.XmlVersion);
logger.Info($"XML results can be found in '{resultsFilePath}'");
}
}
Expand Down Expand Up @@ -227,7 +227,7 @@ protected async Task<TestRunner> InternalRunAsync(ApplicationOptions options, Te
logger.MinimumLogLevel = MinimumLogLevel.Info;
var runner = await InternalRunAsync(logger);

WriteResults(runner, options, logger, resultsFile ?? Console.Out);
await WriteResults(runner, options, logger, resultsFile ?? Console.Out);

logger.Info($"{Environment.NewLine}=== TEST EXECUTION SUMMARY ==={Environment.NewLine}Tests run: {runner.TotalTests} Passed: {runner.PassedTests} Inconclusive: {runner.InconclusiveTests} Failed: {runner.FailedTests} Ignored: {runner.FilteredTests} Skipped: {runner.SkippedTests}{Environment.NewLine}");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ protected TestRunner(LogWriter logger)
}

public abstract Task Run(IEnumerable<TestAssemblyInfo> testAssemblies);
public abstract string WriteResultsToFile(XmlResultJargon xmlResultJargon);
public abstract void WriteResultsToFile(TextWriter writer, XmlResultJargon jargon);
public abstract Task<string> WriteResultsToFile(XmlResultJargon xmlResultJargon);
public abstract Task WriteResultsToFile(TextWriter writer, XmlResultJargon jargon);
public abstract void SkipTests(IEnumerable<string> tests);
public abstract void SkipCategories(IEnumerable<string> categories);
public abstract void SkipMethod(string method, bool isExcluded);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,32 +158,34 @@ private bool ReportFilteredAssembly(TestAssemblyInfo assemblyInfo, bool include)
return include;
}

public override string WriteResultsToFile(XmlResultJargon jargon)
public override Task<string> WriteResultsToFile(XmlResultJargon jargon)
{
if (_results == null)
{
return string.Empty;
return Task.FromResult(string.Empty);
}

string ret = GetResultsFilePath();
if (string.IsNullOrEmpty(ret))
{
return string.Empty;
return Task.FromResult(string.Empty);
}

jargon.GetWriter().WriteResultFile(_results, ret);

return ret;
return Task.FromResult(ret);
}

public override void WriteResultsToFile(TextWriter writer, XmlResultJargon jargon)
public override Task WriteResultsToFile(TextWriter writer, XmlResultJargon jargon)
{
if (_results == null)
{
return;
return Task.CompletedTask;
}

jargon.GetWriter().WriteResultFile(_results, writer);

return Task.CompletedTask;
}

public override void SkipTests(IEnumerable<string> tests)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,14 @@ namespace Microsoft.DotNet.XHarness.TestRunners.Xunit;

internal class ThreadlessXunitTestRunner : XunitTestRunnerBase
{
public ThreadlessXunitTestRunner(LogWriter logger, bool oneLineResults = false) : base(logger)
public ThreadlessXunitTestRunner(LogWriter logger) : base(logger)
{
_oneLineResults = oneLineResults;
ShowFailureInfos = false;
}

protected override string ResultsFileName { get => string.Empty; set => throw new InvalidOperationException("This runner outputs its results to stdout."); }

private readonly XElement _assembliesElement = new XElement("assemblies");
private readonly bool _oneLineResults;

public override async Task Run(IEnumerable<TestAssemblyInfo> testAssemblies)
{
Expand Down Expand Up @@ -110,26 +108,16 @@ private ExecutionSummary Combine(ExecutionSummary aggregateSummary, ExecutionSum
};
}

public override string WriteResultsToFile(XmlResultJargon xmlResultJargon)
public override async Task<string> WriteResultsToFile(XmlResultJargon xmlResultJargon)
{
Debug.Assert(xmlResultJargon == XmlResultJargon.xUnit);
WriteResultsToFile(Console.Out, xmlResultJargon);
await WriteResultsToFile(Console.Out, xmlResultJargon);
return "";
}

public override void WriteResultsToFile(TextWriter writer, XmlResultJargon jargon)
public override async Task WriteResultsToFile(TextWriter writer, XmlResultJargon jargon)
{
if (_oneLineResults)
{
WasmXmlResultWriter.WriteOnSingleLine(_assembliesElement);
}
else
{
writer.WriteLine($"STARTRESULTXML");
_assembliesElement.Save(writer);
writer.WriteLine();
writer.WriteLine($"ENDRESULTXML");
}
await WasmXmlResultWriter.WriteResultsToFile(_assembliesElement);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public abstract class WasmApplicationEntryPoint : WasmApplicationEntryPointBase
protected override TestRunner GetTestRunner(LogWriter logWriter)
{
XunitTestRunnerBase runner = IsThreadless
? new ThreadlessXunitTestRunner(logWriter, true)
? new ThreadlessXunitTestRunner(logWriter)
: new WasmThreadedTestRunner(logWriter) { MaxParallelThreads = MaxParallelThreads };

ConfigureRunnerFilters(runner, ApplicationOptions.Current);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ public override Task Run(IEnumerable<TestAssemblyInfo> testAssemblies)
return base.Run(testAssemblies);
}

public override void WriteResultsToFile(TextWriter writer, XmlResultJargon jargon)
=> WasmXmlResultWriter.WriteOnSingleLine(AssembliesElement);
public override Task WriteResultsToFile(TextWriter writer, XmlResultJargon jargon)
=> WasmXmlResultWriter.WriteResultsToFile(AssembliesElement);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.InteropServices.JavaScript;
using System.Threading.Tasks;
using System.Xml.Linq;

#nullable enable
Expand All @@ -12,10 +16,45 @@ namespace Microsoft.DotNet.XHarness.TestRunners.Xunit;

internal class WasmXmlResultWriter
{
public static void WriteOnSingleLine(XElement assembliesElement)
public async static Task WriteResultsToFile(XElement assembliesElement)
{
using var ms = new MemoryStream();
assembliesElement.Save(ms);

if (OperatingSystem.IsBrowser())
{
try
{
using JSObject globalThis = JSHost.GlobalThis;
if (globalThis.HasProperty("fetch") && globalThis.HasProperty("location") && globalThis.HasProperty("document"))
{
ms.Position = 0;

// globalThis.location.origin
using JSObject location = globalThis.GetPropertyAsJSObject("location")!;
var originURL = location.GetPropertyAsString("origin");

using var req = new HttpRequestMessage(HttpMethod.Post, originURL + "/test-results");
req.Content = new StreamContent(ms);
req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
req.Content.Headers.ContentLength = ms.Length;

using var httpClient = new HttpClient();
using var response = await httpClient.SendAsync(req);
if (response.IsSuccessStatusCode)
{
Console.WriteLine($"Finished uploading {ms.Length} bytes of RESULTXML");
return;
}
// otherwise fall back to the console output
}
}
catch (Exception)
{
// fall back to the console output
}
}

ms.TryGetBuffer(out var bytes);
var base64 = Convert.ToBase64String(bytes, Base64FormattingOptions.None);
Console.WriteLine($"STARTRESULTXML {bytes.Count} {base64} ENDRESULTXML");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -901,11 +901,11 @@ public override async Task Run(IEnumerable<TestAssemblyInfo> testAssemblies)
TotalTests += FilteredTests; // ensure that we do have in the total run the excluded ones.
}

public override string WriteResultsToFile(XmlResultJargon jargon)
public override Task<string> WriteResultsToFile(XmlResultJargon jargon)
{
if (_assembliesElement == null)
{
return string.Empty;
return Task.FromResult(string.Empty);
}
// remove all the empty nodes
_assembliesElement.Descendants().Where(e => e.Name == "collection" && !e.Descendants().Any()).Remove();
Expand All @@ -926,15 +926,15 @@ public override string WriteResultsToFile(XmlResultJargon jargon)
_assembliesElement.Save(xmlWriter);
break;
}
}

return outputFilePath;
}
return Task.FromResult(outputFilePath);
}
public override void WriteResultsToFile(TextWriter writer, XmlResultJargon jargon)
public override Task WriteResultsToFile(TextWriter writer, XmlResultJargon jargon)
{
if (_assembliesElement == null)
{
return;
return Task.CompletedTask;
}
// remove all the empty nodes
_assembliesElement.Descendants().Where(e => e.Name == "collection" && !e.Descendants().Any()).Remove();
Expand Down Expand Up @@ -969,6 +969,7 @@ public override void WriteResultsToFile(TextWriter writer, XmlResultJargon jargo
break;
}
}
return Task.CompletedTask;
}

private void Transform_Results(string xsltResourceName, XElement element, XmlWriter writer)
Expand Down
Loading