/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Components/Testing/test/GenerateE2EManifestTaskTests.cs
469 строк
17 KB
Javier Calvarro Nelson
[Blazor] Add Native AOT Components testing harness (#68289)
10 авг 2026, 15:11
Не верифицирован
10 авг 2026, 15:11
330e89c
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.InteropServices; using System.Text.Json; using Microsoft.AspNetCore.Components.Testing.Infrastructure; using Microsoft.AspNetCore.Components.Testing.Tasks; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; namespace Microsoft.AspNetCore.Components.Testing.Tests; public class GenerateE2EManifestTaskTests : IDisposable { private readonly string _tempDir; public GenerateE2EManifestTaskTests() { _tempDir = Path.Combine(Path.GetTempPath(), "E2EManifestTests_" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(_tempDir); } public void Dispose() { if (Directory.Exists(_tempDir)) { Directory.Delete(_tempDir, recursive: true); } } // ── build mode + Build scenario (local dev) ────────────────────── [Fact] public void BuildMode_Build_UsesAbsoluteSourcePaths() { var projectDir = CreateFakeProjectDir("MyApp"); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateAppItem("MyApp", projectDir)], manifestPath: manifestPath, mode: "build", isPublishing: false); Assert.True(task.Execute()); var manifest = ReadManifest(manifestPath); Assert.Single(manifest.Apps); var entry = manifest.GetApp("MyApp"); Assert.NotNull(entry); Assert.Equal("dotnet", entry!.Executable); Assert.Equal("run --no-build --no-restore --no-launch-profile", entry.Arguments); Assert.Equal(projectDir, entry.WorkingDirectory); } // ── build mode + Publish scenario ──────────────────────────────── [Fact] public void BuildMode_Publish_UsesRelativeSourcePaths() { var projectDir = CreateFakeProjectDir("MyApp"); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateAppItem("MyApp", projectDir)], manifestPath: manifestPath, mode: "build", isPublishing: true); Assert.True(task.Execute()); var manifest = ReadManifest(manifestPath); var entry = manifest.GetApp("MyApp"); Assert.NotNull(entry); Assert.Equal("dotnet", entry!.Executable); Assert.Equal(Path.Combine("e2e-apps", "MyApp"), entry.WorkingDirectory); } // ── publish mode + Build scenario (errors — no published output) ─ [Fact] public void PublishMode_Build_FailsWhenNoPublishedOutput() { var projectDir = CreateFakeProjectDir("MyApp"); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateAppItem("MyApp", projectDir)], manifestPath: manifestPath, mode: "publish", isPublishing: false); Assert.False(task.Execute()); } // ── publish mode + Publish scenario (exe found) ────────────────── [Fact] public void PublishMode_Publish_WithExe_UsesExeExecutable() { var projectDir = CreateFakeProjectDir("MyApp"); var appsOutputDir = Path.Combine(_tempDir, "e2e-apps"); CreatePublishedApp(appsOutputDir, "MyApp", createExe: true); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateAppItem("MyApp", projectDir)], manifestPath: manifestPath, mode: "publish", isPublishing: true, appsOutputDir: appsOutputDir); Assert.True(task.Execute()); var manifest = ReadManifest(manifestPath); var entry = manifest.GetApp("MyApp"); Assert.NotNull(entry); var expectedExe = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "MyApp.exe" : "MyApp"; Assert.Equal(expectedExe, entry!.Executable); Assert.Equal("", entry.Arguments); Assert.Equal(Path.Combine("e2e-apps", "MyApp"), entry.WorkingDirectory); Assert.Equal("startupHook", entry.HarnessMode); } // ── publish mode + Publish scenario (dll only) ─────────────────── [Fact] public void PublishMode_Publish_WithDll_UsesDotnetExec() { var projectDir = CreateFakeProjectDir("MyApp"); var appsOutputDir = Path.Combine(_tempDir, "e2e-apps"); CreatePublishedApp(appsOutputDir, "MyApp", createExe: false); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateAppItem("MyApp", projectDir)], manifestPath: manifestPath, mode: "publish", isPublishing: true, appsOutputDir: appsOutputDir); Assert.True(task.Execute()); var manifest = ReadManifest(manifestPath); var entry = manifest.GetApp("MyApp"); Assert.NotNull(entry); Assert.Equal("dotnet", entry!.Executable); Assert.Equal("MyApp.dll", entry.Arguments); } // ── all mode + Build scenario (local dev — build entry only, publish fails) ─ [Fact] public void AllMode_Build_FailsWhenNoPublishedOutput() { var projectDir = CreateFakeProjectDir("MyApp"); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateAppItem("MyApp", projectDir)], manifestPath: manifestPath, mode: "all", isPublishing: false); Assert.False(task.Execute()); } // ── all mode + Publish scenario ────────────────────────────────── [Fact] public void AllMode_Publish_CreatesBothEntries() { var projectDir = CreateFakeProjectDir("MyApp"); var appsOutputDir = Path.Combine(_tempDir, "e2e-apps"); // In 'all' mode, published output goes to publish/<name> CreatePublishedApp(Path.Combine(appsOutputDir, "publish"), "MyApp", createExe: true); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateAppItem("MyApp", projectDir)], manifestPath: manifestPath, mode: "all", isPublishing: true, appsOutputDir: appsOutputDir); Assert.True(task.Execute()); var manifest = ReadManifest(manifestPath); Assert.Equal(2, manifest.Apps.Count); // Build entry var buildEntry = manifest.GetApp("MyApp"); Assert.NotNull(buildEntry); Assert.Equal("dotnet", buildEntry!.Executable); Assert.Equal("run --no-launch-profile", buildEntry.Arguments); Assert.Equal(Path.Combine("e2e-apps", "MyApp"), buildEntry.WorkingDirectory); // Publish entry var publishEntry = manifest.GetApp("publish/MyApp"); Assert.NotNull(publishEntry); var expectedExe = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "MyApp.exe" : "MyApp"; Assert.Equal(expectedExe, publishEntry!.Executable); Assert.Equal("", publishEntry.Arguments); Assert.Equal(Path.Combine("e2e-apps", "publish", "MyApp"), publishEntry.WorkingDirectory); } // ── Custom E2EAppsRelativeDir ──────────────────────────────────── [Fact] public void BuildMode_Publish_UsesCustomRelativeDir() { var projectDir = CreateFakeProjectDir("MyApp"); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateAppItem("MyApp", projectDir)], manifestPath: manifestPath, mode: "build", isPublishing: true, appsRelativeDir: "test-apps"); Assert.True(task.Execute()); var manifest = ReadManifest(manifestPath); var entry = manifest.GetApp("MyApp"); Assert.NotNull(entry); Assert.Equal(Path.Combine("test-apps", "MyApp"), entry!.WorkingDirectory); } // ── Multiple apps ──────────────────────────────────────────────── [Fact] public void BuildMode_Build_MultipleApps_AllPresent() { var dir1 = CreateFakeProjectDir("App1"); var dir2 = CreateFakeProjectDir("App2"); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateAppItem("App1", dir1), CreateAppItem("App2", dir2)], manifestPath: manifestPath, mode: "build", isPublishing: false); Assert.True(task.Execute()); var manifest = ReadManifest(manifestPath); Assert.Equal(2, manifest.Apps.Count); Assert.NotNull(manifest.GetApp("App1")); Assert.NotNull(manifest.GetApp("App2")); } // ── PublicUrl metadata flows through ───────────────────────────── [Fact] public void BuildMode_Build_PublicUrlFlowsThrough() { var projectDir = CreateFakeProjectDir("MyApp"); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var item = CreateAppItem("MyApp", projectDir); item.SetMetadata("E2EPublicUrl", "https://localhost:5001"); var task = CreateTask( apps: [item], manifestPath: manifestPath, mode: "build", isPublishing: false); Assert.True(task.Execute()); var manifest = ReadManifest(manifestPath); var entry = manifest.GetApp("MyApp"); Assert.NotNull(entry); Assert.Equal("https://localhost:5001", entry!.PublicUrl); } [Fact] public void PerAppMode_OverridesGlobalMode() { var buildProjectDir = CreateFakeProjectDir("BuildApp"); var publishProjectDir = CreateFakeProjectDir("PublishApp"); var appsOutputDir = Path.Combine(_tempDir, "e2e-apps"); CreatePublishedApp(appsOutputDir, "PublishApp", createExe: true); var publishItem = CreateAppItem("PublishApp", publishProjectDir); publishItem.SetMetadata("E2EAppMode", "publish"); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateAppItem("BuildApp", buildProjectDir), publishItem], manifestPath: manifestPath, mode: "build", isPublishing: true, appsOutputDir: appsOutputDir); Assert.True(task.Execute()); var manifest = ReadManifest(manifestPath); Assert.Equal("dotnet", manifest.GetApp("BuildApp")!.Executable); Assert.NotEqual("dotnet", manifest.GetApp("PublishApp")!.Executable); } [Fact] public void NativeAot_BuildMode_FailsValidation() { var projectDir = CreateFakeProjectDir("MyApp"); var item = CreateNativeAotAppItem("MyApp", projectDir, "win-x64"); var task = CreateTask( apps: [item], manifestPath: Path.Combine(_tempDir, "manifest.json"), mode: "build", isPublishing: false); Assert.False(task.Execute()); } [Fact] public void NativeAot_WithoutRuntimeIdentifier_FailsValidation() { var projectDir = CreateFakeProjectDir("MyApp"); var item = CreateNativeAotAppItem("MyApp", projectDir, runtimeIdentifier: ""); var task = CreateTask( apps: [item], manifestPath: Path.Combine(_tempDir, "manifest.json"), mode: "publish", isPublishing: true); Assert.False(task.Execute()); } [Fact] public void NativeAot_WithOnlyDll_DoesNotFallBackToDotnet() { var projectDir = CreateFakeProjectDir("MyApp"); var appsOutputDir = Path.Combine(_tempDir, "e2e-apps"); CreatePublishedApp(appsOutputDir, "MyApp", createExe: false); var task = CreateTask( apps: [CreateNativeAotAppItem("MyApp", projectDir, "win-x64")], manifestPath: Path.Combine(_tempDir, "manifest.json"), mode: "publish", isPublishing: true, appsOutputDir: appsOutputDir); Assert.False(task.Execute()); } [Fact] public void NativeAot_PerAppPublishMode_OverridesGlobalBuildMode() { var projectDir = CreateFakeProjectDir("MyApp"); var appsOutputDir = Path.Combine(_tempDir, "e2e-apps"); var publishedDir = Path.Combine(appsOutputDir, "MyApp"); Directory.CreateDirectory(publishedDir); File.WriteAllText(Path.Combine(publishedDir, "MyApp.exe"), "fake-native-exe"); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var item = CreateNativeAotAppItem("MyApp", projectDir, "win-x64"); item.SetMetadata("E2EAppMode", "publish"); var task = CreateTask( apps: [item], manifestPath: manifestPath, mode: "build", isPublishing: true, appsOutputDir: appsOutputDir); Assert.True(task.Execute()); var entry = Assert.Single(ReadManifest(manifestPath).Apps).Value; Assert.Equal("MyApp.exe", entry.Executable); Assert.Equal("", entry.Arguments); Assert.Equal("compiled", entry.HarnessMode); } [Fact] public void NativeAot_NonWindowsRuntimeIdentifier_SelectsExtensionlessExecutable() { var projectDir = CreateFakeProjectDir("MyApp"); var appsOutputDir = Path.Combine(_tempDir, "e2e-apps"); var publishedDir = Path.Combine(appsOutputDir, "MyApp"); Directory.CreateDirectory(publishedDir); File.WriteAllText(Path.Combine(publishedDir, "MyApp"), "fake-native-exe"); var manifestPath = Path.Combine(_tempDir, "manifest.json"); var task = CreateTask( apps: [CreateNativeAotAppItem("MyApp", projectDir, "linux-x64")], manifestPath: manifestPath, mode: "publish", isPublishing: true, appsOutputDir: appsOutputDir); Assert.True(task.Execute()); var entry = Assert.Single(ReadManifest(manifestPath).Apps).Value; Assert.Equal("MyApp", entry.Executable); Assert.Equal("compiled", entry.HarnessMode); } // ── Helpers ────────────────────────────────────────────────────── private string CreateFakeProjectDir(string name) { var dir = Path.Combine(_tempDir, "src", name); Directory.CreateDirectory(dir); File.WriteAllText(Path.Combine(dir, name + ".csproj"), "<Project />"); return dir; } private static void CreatePublishedApp(string appsOutputDir, string name, bool createExe) { var dir = Path.Combine(appsOutputDir, name); Directory.CreateDirectory(dir); if (createExe) { var exeSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : ""; File.WriteAllText(Path.Combine(dir, name + exeSuffix), "fake-exe"); } else { File.WriteAllText(Path.Combine(dir, name + ".dll"), "fake-dll"); } } private static TaskItem CreateAppItem(string name, string projectDir) { var item = new TaskItem(Path.Combine(projectDir, name + ".csproj")); item.SetMetadata("E2EApp", "true"); return item; } private static TaskItem CreateNativeAotAppItem(string name, string projectDir, string runtimeIdentifier) { var item = CreateAppItem(name, projectDir); item.SetMetadata("E2ENativeAot", "true"); item.SetMetadata("E2ERuntimeIdentifier", runtimeIdentifier); return item; } #nullable enable private GenerateE2EManifest CreateTask( TaskItem[] apps, string manifestPath, string mode, bool isPublishing, string? appsOutputDir = null, string appsRelativeDir = "e2e-apps") { return new GenerateE2EManifest { AppItems = apps, ManifestPath = manifestPath, E2EAppsOutputDir = appsOutputDir ?? Path.Combine(_tempDir, "e2e-apps"), E2EAppsRelativeDir = appsRelativeDir, E2EAppMode = mode, IsPublishing = isPublishing ? "true" : "false", BuildEngine = new MockBuildEngine(), }; } private static E2EManifest ReadManifest(string path) { var json = File.ReadAllText(path); var manifest = JsonSerializer.Deserialize<E2EManifest>(json); Assert.NotNull(manifest); return manifest!; } private sealed class MockBuildEngine : IBuildEngine { public bool ContinueOnError => false; public int LineNumberOfTaskNode => 0; public int ColumnNumberOfTaskNode => 0; public string ProjectFileOfTaskNode => ""; public void LogErrorEvent(BuildErrorEventArgs e) { } public void LogWarningEvent(BuildWarningEventArgs e) { } public void LogMessageEvent(BuildMessageEventArgs e) { } public void LogCustomEvent(CustomBuildEventArgs e) { } public bool BuildProjectFile(string projectFileName, string[] targetNames, System.Collections.IDictionary globalProperties, System.Collections.IDictionary targetOutputs) => false; } }