Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions Microsoft.Testing.Platform.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions TestFx.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<Project Path="samples/FSharpPlayground/FSharpPlayground.fsproj" />
<Project Path="samples/FxExtensibility/FxExtensibility.csproj" />
<Project Path="samples/Playground/Playground.csproj" />
<Project Path="samples/WasiPlayground/WasiPlayground.csproj" />
</Folder>
<Folder Name="/Solution Items/">
<File Path=".editorconfig" />
Expand Down Expand Up @@ -72,6 +73,7 @@
<Project Path="src/Package/MSTest.Sdk/MSTest.Sdk.csproj" />
<Project Path="src/Package/MSTest/MSTest.csproj" />
</Folder>
<Folder Name="/src/Platform/" />
<Folder Name="/test/">
<File Path="test/.editorconfig" />
<File Path="test/Directory.Build.props" />
Expand Down
52 changes: 52 additions & 0 deletions samples/WasiPlayground/Program.cs
Original file line number Diff line number Diff line change
@@ -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<CloseTestSessionResult> CloseTestSessionAsync(CloseTestSessionContext context)
=> Task.FromResult(new CloseTestSessionResult() { IsSuccess = true });

public Task<CreateTestSessionResult> 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<bool> IsEnabledAsync() => Task.FromResult(true);
}
21 changes: 21 additions & 0 deletions samples/WasiPlayground/README.md
Original file line number Diff line number Diff line change
@@ -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 <https://docs.wasmtime.dev/cli-install.html>.
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.<Main>(String[] args)
```
29 changes: 29 additions & 0 deletions samples/WasiPlayground/WasiPlayground.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
<PublishTrimmed>true</PublishTrimmed>

<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<IsTestingPlatformApplication>true</IsTestingPlatformApplication>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="$(RepoRoot)src\Platform\Microsoft.Testing.Platform\Microsoft.Testing.Platform.csproj" />
<ProjectReference Include="$(RepoRoot)src\Platform\Microsoft.Testing.Extensions.CrashDump\Microsoft.Testing.Extensions.CrashDump.csproj" />
<ProjectReference Include="$(RepoRoot)src\Platform\Microsoft.Testing.Extensions.HangDump\Microsoft.Testing.Extensions.HangDump.csproj" />
<ProjectReference Include="$(RepoRoot)src\Platform\Microsoft.Testing.Extensions.TrxReport\Microsoft.Testing.Extensions.TrxReport.csproj" />
<ProjectReference Include="$(RepoRoot)src\Platform\Microsoft.Testing.Extensions.Telemetry\Microsoft.Testing.Extensions.Telemetry.csproj" />
<ProjectReference Include="$(RepoRoot)src\Platform\Microsoft.Testing.Extensions.AzureDevOpsReport\Microsoft.Testing.Extensions.AzureDevOpsReport.csproj" />
</ItemGroup>

<ItemGroup>
<ProjectCapability Include="DiagnoseCapabilities" />
<ProjectCapability Include="TestingPlatformServer" />
<ProjectCapability Include="TestContainer" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -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")) ||
Expand Down
34 changes: 5 additions & 29 deletions src/Platform/Microsoft.Testing.Platform/Helpers/Sha256Hasher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ internal interface ITask

Task<T> Run<T>(Func<Task<T>?> function, CancellationToken cancellationToken);

[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("wasi")]
Task RunLongRunning(Func<Task> action, string name, CancellationToken cancellationToken);

Task WhenAll(params Task[] tasks);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public Task<T> Run<T>(Func<Task<T>?> function, CancellationToken cancellationTok
=> Task.Run(function, cancellationToken);

[UnsupportedOSPlatform("browser")]
[UnsupportedOSPlatform("wasi")]
public Task RunLongRunning(Func<Task> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public override void ResetColor()
[SupportedOSPlatformGuard("android")]
[SupportedOSPlatformGuard("ios")]
[SupportedOSPlatformGuard("tvos")]
[SupportedOSPlatformGuard("wasi")]
[SupportedOSPlatformGuard("browser")]
private bool IsForegroundColorNotSupported()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ namespace Microsoft.Testing.Platform.Telemetry;

internal static class ExtensionInformationCollector
{
public static async Task<string> CollectAndSerializeToJsonAsync(ServiceProvider serviceProvider)
public static async Task<string?> 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<ExtensionInformation> extensionsInformation = [];

foreach (object service in serviceProvider.Services)
Expand Down
Loading