Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wasm] Wasm.Build.Tests - some refactoring, and rationalizing #88357

Merged
merged 12 commits into from
Jul 12, 2023
61 changes: 61 additions & 0 deletions src/mono/wasm/Wasm.Build.Tests/BlazorWasmProjectProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// 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.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using Xunit;
using Xunit.Abstractions;
using System.Runtime.Serialization.Json;
using Microsoft.NET.Sdk.WebAssembly;
using System.Text.Json.Nodes;

#nullable enable

namespace Wasm.Build.Tests;

public class BlazorWasmProjectProvider(string projectDir, ITestOutputHelper _testOutput)
: ProjectProviderBase(projectDir, _testOutput)
{
public void AssertBlazorBootJson(string config, bool isPublish, bool isNet7AndBelow, string targetFramework, string? binFrameworkDir)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blazor boot json schema is now shared with WasmAppBuilder. We should do the same check for every app bundle

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that will in the follow up PRs. I'm trying to keep the changes to a minimum, like here the essential change is the check for dotnet files. The next one will add some new test classes and mostly move code around. And the one after that can then cleanly move the templates to wasm-sdk. Followed up multithreaded tests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the same thing is needed for checking icu files.

binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish, targetFramework);

string bootJsonPath = Path.Combine(binFrameworkDir, "blazor.boot.json");
Assert.True(File.Exists(bootJsonPath), $"Expected to find {bootJsonPath}");

string bootJson = File.ReadAllText(bootJsonPath);
var bootJsonNode = JsonNode.Parse(bootJson);
var runtimeObj = bootJsonNode?["resources"]?["runtime"]?.AsObject();
Assert.NotNull(runtimeObj);

string msgPrefix = $"[{(isPublish ? "publish" : "build")}]";
Assert.True(runtimeObj!.Where(kvp => kvp.Key == (isNet7AndBelow ? "dotnet.wasm" : "dotnet.native.wasm")).Any(), $"{msgPrefix} Could not find dotnet.native.wasm entry in blazor.boot.json");
Assert.True(runtimeObj!.Where(kvp => kvp.Key.StartsWith("dotnet.", StringComparison.OrdinalIgnoreCase) &&
kvp.Key.EndsWith(".js", StringComparison.OrdinalIgnoreCase)).Any(),
$"{msgPrefix} Could not find dotnet.*js in {bootJson}");
}

public static BootJsonData ParseBootData(Stream stream)
{
stream.Position = 0;
var serializer = new DataContractJsonSerializer(
typeof(BootJsonData),
new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true });

var config = (BootJsonData?)serializer.ReadObject(stream);
Assert.NotNull(config);
return config;
}

public string FindBlazorBinFrameworkDir(string config, bool forPublish, string framework)
{
string basePath = Path.Combine(ProjectDir, "bin", config, framework);
if (forPublish)
basePath = FindSubDirIgnoringCase(basePath, "publish");

return Path.Combine(basePath, "wwwroot", "_framework");
}
}
66 changes: 15 additions & 51 deletions src/mono/wasm/Wasm.Build.Tests/BuildTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,12 @@ protected void AssertBlazorBundle(string config, bool isPublish, bool dotnetWasm
{
binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish, targetFramework);

AssertBlazorBootJson(config, isPublish, targetFramework != DefaultTargetFrameworkForBlazor, targetFramework, binFrameworkDir: binFrameworkDir);
new BlazorWasmProjectProvider(_projectDir!, _testOutput)
.AssertBlazorBootJson(config,
isPublish,
targetFramework != DefaultTargetFrameworkForBlazor,
targetFramework,
binFrameworkDir: binFrameworkDir);
AssertFile(Path.Combine(s_buildEnv.GetRuntimeNativeDir(targetFramework), "dotnet.native.wasm"),
Path.Combine(binFrameworkDir, "dotnet.native.wasm"),
"Expected dotnet.native.wasm to be same as the runtime pack",
Expand All @@ -904,7 +909,7 @@ protected void AssertBlazorBundle(string config, bool isPublish, bool dotnetWasm

using (var bootConfigContent = File.OpenRead(bootConfigPath))
{
var bootConfig = ParseBootData(bootConfigContent);
var bootConfig = BlazorWasmProjectProvider.ParseBootData(bootConfigContent);
var dotnetJsEntries = bootConfig.resources.runtime.Keys.Where(k => k.StartsWith("dotnet.") && k.EndsWith(".js")).ToArray();

void AssertFileExists(string fileName)
Expand Down Expand Up @@ -932,58 +937,17 @@ void AssertFileExists(string fileName)
}
}

private static BootJsonData ParseBootData(Stream stream)
{
stream.Position = 0;
var serializer = new DataContractJsonSerializer(
typeof(BootJsonData),
new DataContractJsonSerializerSettings { UseSimpleDictionaryFormat = true });

var config = (BootJsonData?)serializer.ReadObject(stream);
Assert.NotNull(config);
return config;
}

protected void AssertBlazorBootJson(string config, bool isPublish, bool isNet7AndBelow, string targetFramework = DefaultTargetFrameworkForBlazor, string? binFrameworkDir = null)
{
binFrameworkDir ??= FindBlazorBinFrameworkDir(config, isPublish, targetFramework);

string bootJsonPath = Path.Combine(binFrameworkDir, "blazor.boot.json");
Assert.True(File.Exists(bootJsonPath), $"Expected to find {bootJsonPath}");

string bootJson = File.ReadAllText(bootJsonPath);
var bootJsonNode = JsonNode.Parse(bootJson);
var runtimeObj = bootJsonNode?["resources"]?["runtime"]?.AsObject();
Assert.NotNull(runtimeObj);

string msgPrefix = $"[{(isPublish ? "publish" : "build")}]";
Assert.True(runtimeObj!.Where(kvp => kvp.Key == (isNet7AndBelow ? "dotnet.wasm" : "dotnet.native.wasm")).Any(), $"{msgPrefix} Could not find dotnet.native.wasm entry in blazor.boot.json");
Assert.True(runtimeObj!.Where(kvp => kvp.Key.StartsWith("dotnet.", StringComparison.OrdinalIgnoreCase) &&
kvp.Key.EndsWith(".js", StringComparison.OrdinalIgnoreCase)).Any(),
$"{msgPrefix} Could not find dotnet.*js in {bootJson}");
}
=> new BlazorWasmProjectProvider(_projectDir!, _testOutput)
.AssertBlazorBootJson(config: config,
isPublish: isPublish,
isNet7AndBelow: isNet7AndBelow,
targetFramework: targetFramework,
binFrameworkDir: binFrameworkDir);

protected string FindBlazorBinFrameworkDir(string config, bool forPublish, string framework = DefaultTargetFrameworkForBlazor)
{
string basePath = Path.Combine(_projectDir!, "bin", config, framework);
if (forPublish)
basePath = FindSubDirIgnoringCase(basePath, "publish");

return Path.Combine(basePath, "wwwroot", "_framework");
}

private string FindSubDirIgnoringCase(string parentDir, string dirName)
{
IEnumerable<string> matchingDirs = Directory.EnumerateDirectories(parentDir,
dirName,
new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive });

string? first = matchingDirs.FirstOrDefault();
if (matchingDirs.Count() > 1)
throw new Exception($"Found multiple directories with names that differ only in case. {string.Join(", ", matchingDirs.ToArray())}");

return first ?? Path.Combine(parentDir, dirName);
}
=> new BlazorWasmProjectProvider(_projectDir!, _testOutput)
.FindBlazorBinFrameworkDir(config, forPublish, framework);

protected string GetBinDir(string config, string targetFramework = DefaultTargetFramework, string? baseDir = null)
{
Expand Down
40 changes: 40 additions & 0 deletions src/mono/wasm/Wasm.Build.Tests/ProjectProviderBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;

namespace Wasm.Build.Tests;

public abstract class ProjectProviderBase(string projectDir, ITestOutputHelper output)
{
protected const string s_dotnetVersionHashRegex = @"\.(?<version>.+)\.(?<hash>[a-zA-Z0-9]+)\.";

public ITestOutputHelper TestOutput { get; init; } = output;
public string ProjectDir { get; init; } = projectDir;

public static string FindSubDirIgnoringCase(string parentDir, string dirName)
{
IEnumerable<string> matchingDirs = Directory.EnumerateDirectories(parentDir,
dirName,
new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive });

string? first = matchingDirs.FirstOrDefault();
if (matchingDirs.Count() > 1)
throw new Exception($"Found multiple directories with names that differ only in case. {string.Join(", ", matchingDirs.ToArray())}");

return first ?? Path.Combine(parentDir, dirName);
}

public static bool ShouldCheckFingerprint(string expectedFilename, bool expectFingerprintOnDotnetJs, bool expectFingerprintForThisFile) =>
(expectedFilename == "dotnet.js" && expectFingerprintOnDotnetJs) || expectFingerprintForThisFile;
}
3 changes: 2 additions & 1 deletion src/mono/wasm/wasm.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"settings": {
"omnisharp.enableMsBuildLoadProjectsOnDemand": true,
"omnisharp.defaultLaunchSolution": "${workspaceFolder}sln/WasmBuild.sln",
"omnisharp.enableRoslynAnalyzers": true
"omnisharp.enableRoslynAnalyzers": true,
"cSpell.enabled": false
}
}