diff --git a/Microsoft.Testing.Platform.slnf b/Microsoft.Testing.Platform.slnf index 0951decf55..e9b9342fe6 100644 --- a/Microsoft.Testing.Platform.slnf +++ b/Microsoft.Testing.Platform.slnf @@ -4,6 +4,7 @@ "projects": [ "samples\\FSharpPlayground\\FSharpPlayground.fsproj", "samples\\Playground\\Playground.csproj", + "samples\\WasiPlayground\\WasiPlayground.csproj", "src\\Platform\\Microsoft.Testing.Extensions.AzureDevOpsReport\\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj", "src\\Platform\\Microsoft.Testing.Extensions.AzureFoundry\\Microsoft.Testing.Extensions.AzureFoundry.csproj", "src\\Platform\\Microsoft.Testing.Extensions.CrashDump\\Microsoft.Testing.Extensions.CrashDump.csproj", diff --git a/TestFx.slnx b/TestFx.slnx index a0fb468aec..006f9525f5 100644 --- a/TestFx.slnx +++ b/TestFx.slnx @@ -3,6 +3,7 @@ + @@ -72,6 +73,7 @@ + diff --git a/samples/WasiPlayground/Program.cs b/samples/WasiPlayground/Program.cs new file mode 100644 index 0000000000..049b9e41d4 --- /dev/null +++ b/samples/WasiPlayground/Program.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Extensions; +using Microsoft.Testing.Platform.Builder; +using Microsoft.Testing.Platform.Capabilities.TestFramework; +using Microsoft.Testing.Platform.Extensions.Messages; +using Microsoft.Testing.Platform.Extensions.TestFramework; + +ITestApplicationBuilder testApplicationBuilder = await TestApplication.CreateBuilderAsync(args); + +testApplicationBuilder.RegisterTestFramework(_ => new TestFrameworkCapabilities(), (_, _) => new DummyFramework()); + +testApplicationBuilder.AddTrxReportProvider(); +testApplicationBuilder.AddAppInsightsTelemetryProvider(); +testApplicationBuilder.AddCrashDumpProvider(); +testApplicationBuilder.AddHangDumpProvider(); +testApplicationBuilder.AddAzureDevOpsProvider(); +using ITestApplication testApplication = await testApplicationBuilder.BuildAsync(); +return await testApplication.RunAsync(); + +internal sealed class DummyFramework : ITestFramework, IDataProducer +{ + public string Uid => nameof(DummyFramework); + + public string Version => "1.0.0"; + + public string DisplayName => "DummyFramework"; + + public string Description => DisplayName; + + public Type[] DataTypesProduced => [typeof(TestNodeUpdateMessage)]; + + public Task CloseTestSessionAsync(CloseTestSessionContext context) + => Task.FromResult(new CloseTestSessionResult() { IsSuccess = true }); + + public Task CreateTestSessionAsync(CreateTestSessionContext context) + => Task.FromResult(new CreateTestSessionResult() { IsSuccess = true }); + + public async Task ExecuteRequestAsync(ExecuteRequestContext context) + { + await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid, new TestNode() + { + DisplayName = "Test display name", + Uid = "Uid1", + Properties = new PropertyBag(PassedTestNodeStateProperty.CachedInstance), + })); + context.Complete(); + } + + public Task IsEnabledAsync() => Task.FromResult(true); +} diff --git a/samples/WasiPlayground/README.md b/samples/WasiPlayground/README.md new file mode 100644 index 0000000000..fb6789b966 --- /dev/null +++ b/samples/WasiPlayground/README.md @@ -0,0 +1,21 @@ +# WasiPlayground + +To run this project: + +1. Run `dotnet workload install wasi-experimental` +2. Run `dotnet build` +3. Install wasmtime. See docs at . +4. Open command-line in AppBundle directory (`artifacts\bin\WasiPlayground\Debug\net10.0\wasi-wasm\AppBundle`) +5. Run `wasmtime run --wasi http --dir . -- dotnet.wasm WasiPlayground` + +## Status + +As of today, this will produce this exception (it's not yet supported by MTP). + +```console +Unhandled Exception: +System.PlatformNotSupportedException: Arg_PlatformNotSupported + at System.Threading.Tasks.Task.InternalWaitCore(Int32 millisecondsTimeout, CancellationToken cancellationToken) + at System.Threading.Tasks.Task.InternalWait(Int32 millisecondsTimeout, CancellationToken cancellationToken) + at Program.
(String[] args) +``` diff --git a/samples/WasiPlayground/WasiPlayground.csproj b/samples/WasiPlayground/WasiPlayground.csproj new file mode 100644 index 0000000000..ed0972d7ce --- /dev/null +++ b/samples/WasiPlayground/WasiPlayground.csproj @@ -0,0 +1,29 @@ + + + + Exe + net10.0 + wasi-wasm + true + + enable + Exe + true + + + + + + + + + + + + + + + + + + diff --git a/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs index 41d78f5eaa..97723c4681 100644 --- a/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs +++ b/src/Platform/Microsoft.Testing.Extensions.HotReload/HotReloadHandler.cs @@ -46,6 +46,7 @@ public HotReloadHandler(IConsole console, IOutputDevice outputDevice, IOutputDev [SupportedOSPlatformGuard("android")] [SupportedOSPlatformGuard("ios")] [SupportedOSPlatformGuard("tvos")] + [SupportedOSPlatformGuard("wasi")] [SupportedOSPlatformGuard("browser")] private static bool IsCancelKeyPressNotSupported() => RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID")) || diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/Sha256Hasher.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/Sha256Hasher.cs index 955ee51d9a..019ce3a00d 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/Sha256Hasher.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/Sha256Hasher.cs @@ -10,36 +10,12 @@ namespace Microsoft.Testing.Platform.Helpers; [ExcludeFromCodeCoverage] internal static class Sha256Hasher { + // https://github.com/dotnet/runtime/issues/99126 + [UnsupportedOSPlatform("wasi")] public static string HashWithNormalizedCasing(string text) { - try - { - byte[] bytes = Encoding.UTF8.GetBytes(text.ToUpperInvariant()); - byte[] hash = SHA256.HashData(bytes); - return Convert.ToHexStringLower(hash); - } - catch (PlatformNotSupportedException) - { - // SHA256 is not supported on WASM WASI and similar platforms. - // Fall back to a simple non-cryptographic hash for telemetry purposes. - return ComputeNonCryptographicHash(text.ToUpperInvariant()); - } - } - - private static string ComputeNonCryptographicHash(string text) - { - // Use a simple deterministic hash for platforms without SHA256 support. - // This is sufficient for telemetry correlation purposes. - int hash = text.GetHashCode(); - byte[] hashBytes = BitConverter.GetBytes(hash); - - // Expand to 32 bytes (SHA256 size) for consistency by repeating the pattern - byte[] expandedHash = new byte[32]; - for (int i = 0; i < expandedHash.Length; i++) - { - expandedHash[i] = hashBytes[i % hashBytes.Length]; - } - - return Convert.ToHexStringLower(expandedHash); + byte[] bytes = Encoding.UTF8.GetBytes(text.ToUpperInvariant()); + byte[] hash = SHA256.HashData(bytes); + return Convert.ToHexStringLower(hash); } } diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/ITask.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/ITask.cs index f3f4fabdb9..24e0173805 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/ITask.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/ITask.cs @@ -12,6 +12,8 @@ internal interface ITask Task Run(Func?> function, CancellationToken cancellationToken); + [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("wasi")] Task RunLongRunning(Func action, string name, CancellationToken cancellationToken); Task WhenAll(params Task[] tasks); diff --git a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemTask.cs b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemTask.cs index 144c75dec5..bd0f72b394 100644 --- a/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemTask.cs +++ b/src/Platform/Microsoft.Testing.Platform/Helpers/System/SystemTask.cs @@ -15,6 +15,7 @@ public Task Run(Func?> function, CancellationToken cancellationTok => Task.Run(function, cancellationToken); [UnsupportedOSPlatform("browser")] + [UnsupportedOSPlatform("wasi")] public Task RunLongRunning(Func action, string name, CancellationToken cancellationToken) { // We create custom thread so we can assign the name that will help us to identify the thread in the dump diff --git a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs index 3ef086d477..beb17eeeea 100644 --- a/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs +++ b/src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.cs @@ -648,7 +648,11 @@ private void AddApplicationTelemetryMetadata(IServiceProvider serviceProvider, D ?? _testApplicationModuleInfo.TryGetAssemblyName() ?? "unknown"; - builderMetadata[TelemetryProperties.HostProperties.TestHostPropertyName] = Sha256Hasher.HashWithNormalizedCasing(moduleName); + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Create("WASI"))) + { + builderMetadata[TelemetryProperties.HostProperties.TestHostPropertyName] = Sha256Hasher.HashWithNormalizedCasing(moduleName); + } + builderMetadata[TelemetryProperties.HostProperties.FrameworkDescriptionPropertyName] = RuntimeInformation.FrameworkDescription; builderMetadata[TelemetryProperties.HostProperties.ProcessArchitecturePropertyName] = RuntimeInformation.ProcessArchitecture; builderMetadata[TelemetryProperties.HostProperties.OSArchitecturePropertyName] = RuntimeInformation.OSArchitecture; diff --git a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs index 78b26ac13d..f37c052bf6 100644 --- a/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs +++ b/src/Platform/Microsoft.Testing.Platform/OutputDevice/Terminal/NonAnsiTerminal.cs @@ -41,6 +41,7 @@ public override void ResetColor() [SupportedOSPlatformGuard("android")] [SupportedOSPlatformGuard("ios")] [SupportedOSPlatformGuard("tvos")] + [SupportedOSPlatformGuard("wasi")] [SupportedOSPlatformGuard("browser")] private bool IsForegroundColorNotSupported() { diff --git a/src/Platform/Microsoft.Testing.Platform/Services/CTRLPlusCCancellationTokenSource.cs b/src/Platform/Microsoft.Testing.Platform/Services/CTRLPlusCCancellationTokenSource.cs index 3521080157..5b06bec21f 100644 --- a/src/Platform/Microsoft.Testing.Platform/Services/CTRLPlusCCancellationTokenSource.cs +++ b/src/Platform/Microsoft.Testing.Platform/Services/CTRLPlusCCancellationTokenSource.cs @@ -24,6 +24,7 @@ public CTRLPlusCCancellationTokenSource(IConsole? console = null, ILogger? logge [SupportedOSPlatformGuard("android")] [SupportedOSPlatformGuard("ios")] [SupportedOSPlatformGuard("tvos")] + [SupportedOSPlatformGuard("wasi")] [SupportedOSPlatformGuard("browser")] private static bool IsCancelKeyPressNotSupported() => RuntimeInformation.IsOSPlatform(OSPlatform.Create("ANDROID")) || diff --git a/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs b/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs index 12c9196b28..5519cf09b5 100644 --- a/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs +++ b/src/Platform/Microsoft.Testing.Platform/Telemetry/ExtensionInformationCollector.cs @@ -16,8 +16,15 @@ namespace Microsoft.Testing.Platform.Telemetry; internal static class ExtensionInformationCollector { - public static async Task CollectAndSerializeToJsonAsync(ServiceProvider serviceProvider) + public static async Task CollectAndSerializeToJsonAsync(ServiceProvider serviceProvider) { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("WASI"))) + { + // HashWithNormalizedCasing not supported on WASI at time of writing. + // https://github.com/dotnet/runtime/issues/99126 + return null; + } + HashSet extensionsInformation = []; foreach (object service in serviceProvider.Services)