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)