Skip to content

Commit

Permalink
[mt] Add signalR WBT on WASM browser app (#100723)
Browse files Browse the repository at this point in the history
* WIP, fixing net::ERR_CONNECTION_REFUSED.

* Change of setup - aspnet project serving WASM static files.

* Enable SharedArrayBuffer.

* WIP, investigating `JsonSerializerIsReflectionDisabled`

* Fix serialization issue.

* Fixed

* Cleanup.

* Trying to fix integrity check issue.

* Async interop + more logging.

* Move test preparation code to a method.

* Remove buttons, match other TestAppScenarios style.

* Moved the test out of TestAppScenarios. Standalone version works.

* Working version of test.

* Cleanup.

* Cleanup.

* @maraf's feedback - simplify the prebuild steps.

* WIP - merging blazor and wasmapp into one proj.

* Missing change in a non-related test.

* Do not pack blazor into a subfolder.

* Remove old app.

* Connect test logic, simplify.

* Not intentional.

* AspServer serves only "standard content types"

* Prevent loosing `InnerException` on exceptions thrown during `TaskCompletionSource` completion.

* Revert.

* Cleanup.

* @maraf's cleanup

* SignalR hub is located in a url with path.

* Fix "GET http://localhost:5000/wasmclient, Response status code: 404"
  • Loading branch information
ilonatommy authored Apr 22, 2024
1 parent 7a5542d commit 66ce263
Show file tree
Hide file tree
Showing 37 changed files with 528 additions and 371 deletions.
3 changes: 2 additions & 1 deletion eng/testing/scenarios/BuildWasmAppsJobsList.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Wasm.Build.Tests.Blazor.NoopNativeRebuildTest
Wasm.Build.Tests.Blazor.WorkloadRequiredTests
Wasm.Build.Tests.Blazor.IcuTests
Wasm.Build.Tests.Blazor.IcuShardingTests
Wasm.Build.Tests.Blazor.SignalRClientTests
Wasm.Build.Tests.BuildPublishTests
Wasm.Build.Tests.ConfigSrcTests
Wasm.Build.Tests.HybridGlobalizationTests
Expand All @@ -37,7 +38,7 @@ Wasm.Build.Tests.TestAppScenarios.LazyLoadingTests
Wasm.Build.Tests.TestAppScenarios.LibraryInitializerTests
Wasm.Build.Tests.TestAppScenarios.SatelliteLoadingTests
Wasm.Build.Tests.TestAppScenarios.ModuleConfigTests
Wasm.Build.Tests.TestAppScenarios.SignalRClientTests
Wasm.Build.Tests.AspNetCore.SignalRClientTests
Wasm.Build.Tests.WasmBuildAppTest
Wasm.Build.Tests.WasmNativeDefaultsTests
Wasm.Build.Tests.WasmRunOutOfAppBundleTests
Expand Down
27 changes: 27 additions & 0 deletions src/mono/wasm/Wasm.Build.Tests/AspNetCore/SignalRClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Threading.Tasks;
using Xunit.Abstractions;
using Xunit;

#nullable enable

namespace Wasm.Build.Tests.AspNetCore;

public class SignalRClientTests : SignalRTestsBase
{
public SignalRClientTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
: base(output, buildContext)
{
}

[ConditionalTheory(typeof(BuildTestBase), nameof(IsWorkloadWithMultiThreadingForDefaultFramework))]
[InlineData("Debug", "LongPolling")]
[InlineData("Release", "LongPolling")]
[InlineData("Debug", "WebSockets")]
[InlineData("Release", "WebSockets")]
public async Task SignalRPassMessageWasmBrowser(string config, string transport) =>
await SignalRPassMessage("wasmclient", config, transport);
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public async Task BlazorRunTest(string runArgs,
onConsoleMessage: OnConsoleMessage,
onServerMessage: runOptions.OnServerMessage,
onError: OnErrorMessage,
modifyBrowserUrl: browserUrl => browserUrl + runOptions.BrowserPath + runOptions.QueryString);
modifyBrowserUrl: browserUrl => new Uri(new Uri(browserUrl), runOptions.BrowserPath + runOptions.QueryString).ToString());

_testOutput.WriteLine("Waiting for page to load");
await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded, new () { Timeout = 1 * 60 * 1000 });
Expand Down
30 changes: 30 additions & 0 deletions src/mono/wasm/Wasm.Build.Tests/Blazor/SignalRClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

using System.Threading.Tasks;
using Xunit.Abstractions;
using Xunit;

#nullable enable

namespace Wasm.Build.Tests.Blazor;

public class SignalRClientTests : SignalRTestsBase
{
public SignalRClientTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
: base(output, buildContext)
{
}

[ConditionalTheory(typeof(BuildTestBase), nameof(IsWorkloadWithMultiThreadingForDefaultFramework))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/100445")] // to be fixed by: "https://github.com/dotnet/aspnetcore/issues/54365"
[InlineData("Debug", "LongPolling")]
[InlineData("Release", "LongPolling")]
[InlineData("Debug", "WebSockets")]
[InlineData("Release", "WebSockets")]
public async Task SignalRPassMessageBlazor(string config, string transport) =>
await SignalRPassMessage("blazorclient", config, transport);
}

49 changes: 49 additions & 0 deletions src/mono/wasm/Wasm.Build.Tests/SignalRTestsBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Collections.Generic;
using Wasm.Build.Tests.TestAppScenarios;
using Xunit.Abstractions;
using Xunit;
#nullable enable

namespace Wasm.Build.Tests;

public class SignalRTestsBase : AppTestBase
{
public SignalRTestsBase(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
: base(output, buildContext)
{
}

protected async Task SignalRPassMessage(string staticWebAssetBasePath, string config, string transport)
{
CopyTestAsset("WasmOnAspNetCore", "SignalRClientTests", "AspNetCoreServer");
PublishProject(config, runtimeType: RuntimeVariant.MultiThreaded, assertAppBundle: false);

var result = await RunSdkStyleAppForBuild(new(
Configuration: config,
ServerEnvironment: new Dictionary<string, string> { ["ASPNETCORE_ENVIRONMENT"] = "Development" },
BrowserPath: staticWebAssetBasePath,
BrowserQueryString: new Dictionary<string, string> { ["transport"] = transport, ["message"] = "ping" } ));

string testOutput = string.Join("\n", result.TestOutput) ?? "";
Assert.NotEmpty(testOutput);
// check sending and receiving threadId
string threadIdUsedForSending = GetThreadOfAction(testOutput, @"SignalRPassMessages was sent by CurrentManagedThreadId=(\d+)", "signalR message was sent");
string threadIdUsedForReceiving = GetThreadOfAction(testOutput, @"ReceiveMessage from server on CurrentManagedThreadId=(\d+)", "signalR message was received");
string consoleOutput = string.Join("\n", result.ConsoleOutput);
Assert.True("1" != threadIdUsedForSending || "1" != threadIdUsedForReceiving,
$"Expected to send/receive with signalR in non-UI threads, instead only CurrentManagedThreadId=1 was used. ConsoleOutput: {consoleOutput}.");
}

private string GetThreadOfAction(string testOutput, string pattern, string actionDescription)
{
Match match = Regex.Match(testOutput, pattern);
Assert.True(match.Success, $"Expected to find a log that {actionDescription}. TestOutput: {testOutput}.");
return match.Groups[1].Value ?? "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public AppSettingsTests(ITestOutputHelper output, SharedBuildPerTestClassFixture
[InlineData("Production")]
public async Task LoadAppSettingsBasedOnApplicationEnvironment(string applicationEnvironment)
{
CopyTestAsset("WasmBasicTestApp", "AppSettingsTests");
CopyTestAsset("WasmBasicTestApp", "AppSettingsTests", "App");
PublishProject("Debug");

var result = await RunSdkStyleAppForPublish(new(
Expand Down
37 changes: 20 additions & 17 deletions src/mono/wasm/Wasm.Build.Tests/TestAppScenarios/AppTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,29 @@ protected AppTestBase(ITestOutputHelper output, SharedBuildPerTestClassFixture b
protected string Id { get; set; }
protected string LogPath { get; set; }

protected void CopyTestAsset(string assetName, string generatedProjectNamePrefix = null)
protected void CopyTestAsset(string assetName, string generatedProjectNamePrefix = null, string? projectDirSuffix = null)
{
Id = $"{generatedProjectNamePrefix ?? assetName}_{GetRandomId()}";
InitBlazorWasmProjectDir(Id);

LogPath = Path.Combine(s_buildEnv.LogRootPath, Id);
Utils.DirectoryCopy(Path.Combine(BuildEnvironment.TestAssetsPath, assetName), Path.Combine(_projectDir!));

switch(assetName)
if (!string.IsNullOrEmpty(projectDirSuffix))
{
case "WasmBasicTestApp":
// WasmBasicTestApp consists of App + Library projects
_projectDir = Path.Combine(_projectDir!, "App");
break;
case "BlazorHostedApp":
// BlazorHostedApp consists of BlazorHosted.Client and BlazorHosted.Server projects
_projectDir = Path.Combine(_projectDir!, "BlazorHosted.Server");
break;
_projectDir = Path.Combine(_projectDir, projectDirSuffix);
}
}

protected void BlazorHostedBuild(
string config,
string assetName,
string projectDirSuffix,
string clientDirRelativeToProjectDir = "",
string? generatedProjectNamePrefix = null,
RuntimeVariant runtimeType = RuntimeVariant.SingleThreaded)
{
CopyTestAsset(assetName, generatedProjectNamePrefix);
CopyTestAsset(assetName, generatedProjectNamePrefix, projectDirSuffix);
string frameworkDir = FindBlazorHostedBinFrameworkDir(config,
forPublish: false,
clientDirRelativeToProjectDir: clientDirRelativeToProjectDir);
Expand All @@ -76,9 +70,17 @@ protected void BuildProject(
result.EnsureSuccessful();
}

protected void PublishProject(string configuration, params string[] extraArgs)
protected void PublishProject(
string configuration,
RuntimeVariant runtimeType = RuntimeVariant.SingleThreaded,
bool assertAppBundle = true,
params string[] extraArgs)
{
(CommandResult result, _) = BlazorPublish(new BlazorBuildOptions(Id, configuration), extraArgs);
(CommandResult result, _) = BlazorPublish(new BlazorBuildOptions(
Id: Id,
Config: configuration,
RuntimeType: runtimeType,
AssertAppBundle: assertAppBundle), extraArgs);
result.EnsureSuccessful();
}

Expand All @@ -99,12 +101,11 @@ private async Task<RunResult> RunSdkStyleApp(RunOptions options, BlazorRunHost h
query.Add("test", options.TestScenario);

var queryString = query.Any() ? "?" + string.Join("&", query.Select(kvp => $"{kvp.Key}={kvp.Value}")) : "";

var tcs = new TaskCompletionSource<int>();
List<string> testOutput = new();
List<string> consoleOutput = new();
List<string> serverOutput = new();
Regex exitRegex = new Regex("(WASM EXIT (?<exitCode>[0-9]+)$)|(Program terminated with exit\\((?<exitCode>[0-9]+)\\))");
Regex exitRegex = new Regex("WASM EXIT (?<exitCode>[0-9]+)$");

BlazorRunOptions blazorRunOptions = new(
CheckCounter: false,
Expand All @@ -114,7 +115,8 @@ private async Task<RunResult> RunSdkStyleApp(RunOptions options, BlazorRunHost h
OnServerMessage: OnServerMessage,
BrowserPath: options.BrowserPath,
QueryString: queryString,
Host: host);
Host: host,
ExtraArgs: options.ExtraArgs);

await BlazorRunTest(blazorRunOptions);

Expand Down Expand Up @@ -171,7 +173,8 @@ protected record RunOptions(
Dictionary<string, string> ServerEnvironment = null,
Action<IPage, IConsoleMessage> OnConsoleMessage = null,
Action<string> OnServerMessage = null,
int? ExpectedExitCode = 0
int? ExpectedExitCode = 0,
string? ExtraArgs = null
);

protected record RunResult(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ private void AssertDebugLevel(RunResult result, int value)
[InlineData("Release")]
public async Task BuildWithDefaultLevel(string configuration)
{
CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_BuildWithDefaultLevel_{configuration}");
CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_BuildWithDefaultLevel_{configuration}", "App");
BuildProject(configuration);

var result = await RunSdkStyleAppForBuild(new(
Expand All @@ -50,7 +50,7 @@ public async Task BuildWithDefaultLevel(string configuration)
[InlineData("Release", 0)]
public async Task BuildWithExplicitValue(string configuration, int debugLevel)
{
CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_BuildWithExplicitValue_{configuration}");
CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_BuildWithExplicitValue_{configuration}", "App");
BuildProject(configuration: configuration, extraArgs: $"-p:WasmDebugLevel={debugLevel}");

var result = await RunSdkStyleAppForBuild(new(
Expand All @@ -65,7 +65,7 @@ public async Task BuildWithExplicitValue(string configuration, int debugLevel)
[InlineData("Release")]
public async Task PublishWithDefaultLevel(string configuration)
{
CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_PublishWithDefaultLevel_{configuration}");
CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_PublishWithDefaultLevel_{configuration}", "App");
PublishProject(configuration);

var result = await RunSdkStyleAppForPublish(new(
Expand All @@ -82,8 +82,8 @@ public async Task PublishWithDefaultLevel(string configuration)
[InlineData("Release", -1)]
public async Task PublishWithExplicitValue(string configuration, int debugLevel)
{
CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_PublishWithExplicitValue_{configuration}");
PublishProject(configuration, $"-p:WasmDebugLevel={debugLevel}");
CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_PublishWithExplicitValue_{configuration}", "App");
PublishProject(configuration, RuntimeVariant.SingleThreaded, assertAppBundle: true, $"-p:WasmDebugLevel={debugLevel}");

var result = await RunSdkStyleAppForPublish(new(
Configuration: configuration,
Expand All @@ -97,8 +97,8 @@ public async Task PublishWithExplicitValue(string configuration, int debugLevel)
[InlineData("Release")]
public async Task PublishWithDefaultLevelAndPdbs(string configuration)
{
CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_PublishWithDefaultLevelAndPdbs_{configuration}");
PublishProject(configuration, $"-p:CopyOutputSymbolsToPublishDirectory=true");
CopyTestAsset("WasmBasicTestApp", $"DebugLevelTests_PublishWithDefaultLevelAndPdbs_{configuration}", "App");
PublishProject(configuration, RuntimeVariant.SingleThreaded, assertAppBundle: true, $"-p:CopyOutputSymbolsToPublishDirectory=true");

var result = await RunSdkStyleAppForPublish(new(
Configuration: configuration,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public LazyLoadingTests(ITestOutputHelper output, SharedBuildPerTestClassFixture
[Fact]
public async Task LoadLazyAssemblyBeforeItIsNeeded()
{
CopyTestAsset("WasmBasicTestApp", "LazyLoadingTests");
CopyTestAsset("WasmBasicTestApp", "LazyLoadingTests", "App");
PublishProject("Debug");

var result = await RunSdkStyleAppForPublish(new(Configuration: "Debug", TestScenario: "LazyLoadingTest"));
Expand All @@ -33,7 +33,7 @@ public async Task LoadLazyAssemblyBeforeItIsNeeded()
[Fact]
public async Task FailOnMissingLazyAssembly()
{
CopyTestAsset("WasmBasicTestApp", "LazyLoadingTests");
CopyTestAsset("WasmBasicTestApp", "LazyLoadingTests", "App");
PublishProject("Debug");

var result = await RunSdkStyleAppForPublish(new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public LibraryInitializerTests(ITestOutputHelper output, SharedBuildPerTestClass
[Fact]
public async Task LoadLibraryInitializer()
{
CopyTestAsset("WasmBasicTestApp", "LibraryInitializerTests_LoadLibraryInitializer");
CopyTestAsset("WasmBasicTestApp", "LibraryInitializerTests_LoadLibraryInitializer", "App");
PublishProject("Debug");

var result = await RunSdkStyleAppForPublish(new(Configuration: "Debug", TestScenario: "LibraryInitializerTest"));
Expand All @@ -39,7 +39,7 @@ public async Task LoadLibraryInitializer()
[Fact]
public async Task AbortStartupOnError()
{
CopyTestAsset("WasmBasicTestApp", "LibraryInitializerTests_AbortStartupOnError");
CopyTestAsset("WasmBasicTestApp", "LibraryInitializerTests_AbortStartupOnError", "App");
PublishProject("Debug");

var result = await RunSdkStyleAppForPublish(new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public ModuleConfigTests(ITestOutputHelper output, SharedBuildPerTestClassFixtur
[InlineData(true)]
public async Task DownloadProgressFinishes(bool failAssemblyDownload)
{
CopyTestAsset("WasmBasicTestApp", $"ModuleConfigTests_DownloadProgressFinishes_{failAssemblyDownload}");
CopyTestAsset("WasmBasicTestApp", $"ModuleConfigTests_DownloadProgressFinishes_{failAssemblyDownload}", "App");
PublishProject("Debug");

var result = await RunSdkStyleAppForPublish(new(
Expand Down Expand Up @@ -58,7 +58,7 @@ public async Task DownloadProgressFinishes(bool failAssemblyDownload)
[Fact]
public async Task OutErrOverrideWorks()
{
CopyTestAsset("WasmBasicTestApp", $"ModuleConfigTests_OutErrOverrideWorks");
CopyTestAsset("WasmBasicTestApp", $"ModuleConfigTests_OutErrOverrideWorks", "App");
PublishProject("Debug");

var result = await RunSdkStyleAppForPublish(new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public SatelliteLoadingTests(ITestOutputHelper output, SharedBuildPerTestClassFi
[Fact]
public async Task LoadSatelliteAssembly()
{
CopyTestAsset("WasmBasicTestApp", "SatelliteLoadingTests");
CopyTestAsset("WasmBasicTestApp", "SatelliteLoadingTests", "App");
BuildProject("Debug");

var result = await RunSdkStyleAppForBuild(new(Configuration: "Debug", TestScenario: "SatelliteAssembliesTest"));
Expand Down
Loading

0 comments on commit 66ce263

Please sign in to comment.