public async Task Publish_UpdatesServiceWorkerVersionHash_WhenSourcesChange() { // Arrange using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args : "/bl:initial.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js"); Assert.BuildPassed(result); var publishOutputDirectory = project.PublishOutputDirectory; var serviceWorkerFile = Assert.FileExists(result, publishOutputDirectory, "wwwroot", "serviceworkers", "my-service-worker.js"); var version = File.ReadAllLines(serviceWorkerFile).Last(); var match = Regex.Match(version, "\\/\\* Manifest version: (.{8}) \\*\\/"); Assert.True(match.Success); Assert.Equal(2, match.Groups.Count); Assert.NotNull(match.Groups[1].Value); var capture = match.Groups[1].Value; // Act var cssFile = Path.Combine(project.DirectoryPath, "LinkToWebRoot", "css", "app.css"); File.WriteAllText(cssFile, ".updated { }"); // Assert result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", args : "/bl:updated.binlog /p:ServiceWorkerAssetsManifest=service-worker-assets.js"); Assert.BuildPassed(result); var updatedVersion = File.ReadAllLines(serviceWorkerFile).Last(); var updatedMatch = Regex.Match(updatedVersion, "\\/\\* Manifest version: (.{8}) \\*\\/"); Assert.True(updatedMatch.Success); Assert.Equal(2, updatedMatch.Groups.Count); Assert.NotNull(updatedMatch.Groups[1].Value); var updatedCapture = updatedMatch.Groups[1].Value; Assert.NotEqual(capture, updatedCapture); }
public async Task Publish_InRelease_Works() { // Arrange using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary", "LinkBaseToWebRoot" }); project.Configuration = "Release"; var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", ""); Assert.BuildPassed(result); var publishDirectory = project.PublishOutputDirectory; var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. // Verify referenced static web assets Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js"); Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "styles.css"); // Verify static assets are in the publish directory Assert.FileExists(result, blazorPublishDirectory, "index.html"); // Verify link item assets are in the publish directory Assert.FileExists(result, blazorPublishDirectory, "js", "LinkedScript.js"); var cssFile = Assert.FileExists(result, blazorPublishDirectory, "css", "app.css"); Assert.FileContains(result, cssFile, ".publish"); Assert.FileDoesNotExist(result, "dist", "Fake-License.txt"); // Verify web.config Assert.FileExists(result, publishDirectory, "web.config"); Assert.FileCountEquals(result, 1, publishDirectory, "*", SearchOption.TopDirectoryOnly); }
public async Task Publish_HostedApp_WithNoBuild_Works() { // Arrange using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build"); Assert.BuildPassed(result); result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "/p:NoBuild=true"); var publishDirectory = project.PublishOutputDirectory; // Make sure the main project exists Assert.FileExists(result, publishDirectory, "blazorhosted.dll"); var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. // Verify static assets are in the publish directory Assert.FileExists(result, blazorPublishDirectory, "index.html"); // Verify static web assets from referenced projects are copied. Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js"); Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css"); // Verify web.config Assert.FileExists(result, publishDirectory, "web.config"); VerifyBootManifestHashes(result, blazorPublishDirectory); VerifyServiceWorkerFiles(result, blazorPublishDirectory, serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"), serviceWorkerContent: "// This is the production service worker", assetsManifestPath: "custom-service-worker-assets.js"); }
public async Task Publish_UpdatesFilesWhenSourcesChange() { // Arrange using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary" }); var result = await MSBuildProcessManager.DotnetMSBuild(project, target : "publish"); Assert.BuildPassed(result); // Act var mainAppDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll"); var mainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); var mainAppCompressedDll = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazorwasm.dll.br"); var mainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); var blazorBootJson = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); var blazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); var blazorBootJsonCompressed = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework", "blazor.boot.json.br"); var blazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); var programFile = Path.Combine(project.DirectoryPath, "..", "blazorwasm", "Program.cs"); var programFileContents = File.ReadAllText(programFile); File.WriteAllText(programFile, programFileContents.Replace("args", "arguments")); // Assert result = await MSBuildProcessManager.DotnetMSBuild(project, target : "publish"); Assert.BuildPassed(result); var newMainAppDllThumbPrint = FileThumbPrint.Create(mainAppDll); var newMainAppCompressedDllThumbPrint = FileThumbPrint.Create(mainAppCompressedDll); var newBlazorBootJsonThumbPrint = FileThumbPrint.Create(blazorBootJson); var newBlazorBootJsonCompressedThumbPrint = FileThumbPrint.Create(blazorBootJsonCompressed); Assert.NotEqual(mainAppDllThumbPrint, newMainAppDllThumbPrint); Assert.NotEqual(mainAppCompressedDllThumbPrint, newMainAppCompressedDllThumbPrint); Assert.NotEqual(blazorBootJsonThumbPrint, newBlazorBootJsonThumbPrint); Assert.NotEqual(blazorBootJsonCompressedThumbPrint, newBlazorBootJsonCompressedThumbPrint); }
public async Task BuildMinimal_Works() { // Arrange // Minimal has no project references, service worker etc. This is pretty close to the project template. using var project = ProjectDirectory.Create("blazorwasm-minimal"); var result = await MSBuildProcessManager.DotnetMSBuild(project); Assert.BuildPassed(result); var buildOutputDirectory = project.BuildOutputDirectory; Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "dotnet.wasm.gz"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", DotNetJsFileName); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazorwasm-minimal.dll"); var staticWebAssets = Assert.FileExists(result, buildOutputDirectory, "blazorwasm-minimal.StaticWebAssets.xml"); Assert.FileContains(result, staticWebAssets, Path.Combine(project.TargetFramework, "wwwroot")); }
public async Task Publish_LazyLoadExplicitAssembly_Debug_Works() { // Arrange using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); project.Configuration = "Debug"; project.AddProjectFileContent( @" <ItemGroup> <BlazorWebAssemblyLazyLoad Include='RazorClassLibrary.dll' /> </ItemGroup> "); var result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish"); var publishDirectory = project.PublishOutputDirectory; // Verify that a blazor.boot.json file has been created Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "blazor.boot.json"); // And that the assembly is in the output Assert.FileExists(result, publishDirectory, "wwwroot", "_framework", "RazorClassLibrary.dll"); var bootJson = ReadBootJsonData(result, Path.Combine(publishDirectory, "wwwroot", "_framework", "blazor.boot.json")); // And that it has been labelled as a dynamic assembly in the boot.json var lazyAssemblies = bootJson.resources.lazyAssembly; var assemblies = bootJson.resources.assembly; Assert.NotNull(lazyAssemblies); Assert.Contains("RazorClassLibrary.dll", lazyAssemblies.Keys); Assert.DoesNotContain("RazorClassLibrary.dll", assemblies.Keys); // App assembly should not be lazy loaded Assert.DoesNotContain("blazorwasm.dll", lazyAssemblies.Keys); Assert.Contains("blazorwasm.dll", assemblies.Keys); }
public async Task Publish_CompressesAllFrameworkFiles() { // Arrange using var project = ProjectDirectory.Create("blazorwasm", additionalProjects: new[] { "razorclasslibrary" }); var result = await MSBuildProcessManager.DotnetMSBuild(project, target : "publish"); Assert.BuildPassed(result); var extensions = new[] { ".dll", ".js", ".pdb", ".wasm", ".map", ".json", ".dat" }; // Act var frameworkFilesPath = Path.Combine(project.DirectoryPath, project.PublishOutputDirectory, "wwwroot", "_framework"); foreach (var file in Directory.EnumerateFiles(frameworkFilesPath, "*", new EnumerationOptions { RecurseSubdirectories = true, })) { var extension = Path.GetExtension(file); if (extension != ".br" && extension != ".gz") { Assert.FileExists(result, file + ".br"); } } }
public static Task <MSBuildResult> RunProcessAsync( ProjectDirectory project, string arguments, TimeSpan?timeout = null, MSBuildProcessKind msBuildProcessKind = MSBuildProcessKind.Dotnet) { timeout = timeout ?? TimeSpan.FromSeconds(60); var processStartInfo = new ProcessStartInfo() { WorkingDirectory = project.DirectoryPath, UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, }; if (msBuildProcessKind == MSBuildProcessKind.Desktop) { if (string.IsNullOrEmpty(BuildVariables.MSBuildPath)) { throw new ArgumentException("Unable to locate MSBuild.exe to run desktop tests."); } processStartInfo.FileName = BuildVariables.MSBuildPath; processStartInfo.Arguments = arguments; } else { processStartInfo.FileName = "dotnet"; processStartInfo.Arguments = $"msbuild {arguments}"; } var process = new Process() { StartInfo = processStartInfo, EnableRaisingEvents = true, }; var completionSource = new TaskCompletionSource <MSBuildResult>(); var output = new StringBuilder(); process.Exited += Process_Exited; process.ErrorDataReceived += Process_ErrorDataReceived; process.OutputDataReceived += Process_OutputDataReceived; process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); var timeoutTask = Task.Delay(timeout.Value).ContinueWith((t) => { // Don't timeout during debug sessions while (Debugger.IsAttached) { Thread.Sleep(TimeSpan.FromSeconds(1)); } if (process.HasExited) { // This will happen on success, the 'real' task has already completed so this value will // never be visible. return((MSBuildResult)null); } // This is a timeout. process.Kill(); throw new TimeoutException($"command '${process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out after {timeout}."); }); return(Task.WhenAny <MSBuildResult>(completionSource.Task, timeoutTask).Unwrap()); void Process_Exited(object sender, EventArgs e) { var result = new MSBuildResult(project, process.StartInfo.FileName, process.StartInfo.Arguments, process.ExitCode, output.ToString()); completionSource.SetResult(result); } void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e) { output.AppendLine(e.Data); } void Process_OutputDataReceived(object sender, DataReceivedEventArgs e) { output.AppendLine(e.Data); } }
public static async Task <MSBuildResult> RunProcessAsync( ProjectDirectory project, string arguments, TimeSpan?timeout = null, MSBuildProcessKind msBuildProcessKind = MSBuildProcessKind.Dotnet) { var processStartInfo = new ProcessStartInfo() { WorkingDirectory = project.DirectoryPath, UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, }; if (msBuildProcessKind == MSBuildProcessKind.Desktop) { if (string.IsNullOrEmpty(BuildVariables.MSBuildPath)) { throw new ArgumentException("Unable to locate MSBuild.exe to run desktop tests. " + "MSBuild.exe is located using state created as part of running build[cmd|sh] at the root of the repository. Run build /t:Prepare to set this up if this hasn't been done."); } processStartInfo.FileName = BuildVariables.MSBuildPath; processStartInfo.Arguments = arguments; } else { processStartInfo.FileName = DotNetMuxer.MuxerPathOrDefault(); processStartInfo.Arguments = $"msbuild {arguments}"; // Suppresses the 'Welcome to .NET Core!' output that times out tests and causes locked file issues. // When using dotnet we're not guarunteed to run in an environment where the dotnet.exe has had its first run experience already invoked. processStartInfo.EnvironmentVariables["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "true"; } ProcessResult processResult; try { processResult = await RunProcessCoreAsync(processStartInfo, timeout); } catch (TimeoutException ex) { // Copy the binlog to the artifacts directory if executing MSBuild throws. // This would help diagnosing failures on the CI. var binaryLogFile = Path.Combine(project.ProjectFilePath, "msbuild.binlog"); var artifactsLogDir = Assembly.GetExecutingAssembly() .GetCustomAttributes <AssemblyMetadataAttribute>() .FirstOrDefault(ama => ama.Key == "ArtifactsLogDir")?.Value; if (!string.IsNullOrEmpty(artifactsLogDir) && File.Exists(binaryLogFile)) { var targetPath = Path.Combine(artifactsLogDir, Path.GetFileNameWithoutExtension(project.ProjectFilePath) + "." + Path.GetRandomFileName() + ".binlog"); File.Copy(binaryLogFile, targetPath); throw new TimeoutException(ex.Message + $"{Environment.NewLine}Captured binlog at {targetPath}"); } throw; } return(new MSBuildResult(project, processResult.FileName, processResult.Arguments, processResult.ExitCode, processResult.Output)); }
public static Task <MSBuildResult> RunProcessAsync( ProjectDirectory project, string arguments, TimeSpan?timeout = null, MSBuildProcessKind msBuildProcessKind = MSBuildProcessKind.Dotnet) { timeout = timeout ?? TimeSpan.FromSeconds(60); var processStartInfo = new ProcessStartInfo() { WorkingDirectory = project.DirectoryPath, UseShellExecute = false, RedirectStandardError = true, RedirectStandardOutput = true, }; if (msBuildProcessKind == MSBuildProcessKind.Desktop) { if (string.IsNullOrEmpty(BuildVariables.MSBuildPath)) { throw new ArgumentException("Unable to locate MSBuild.exe to run desktop tests. " + "MSBuild.exe is located using state created as part of running build[cmd|sh] at the root of the repository. Run build /t:Prepare to set this up if this hasn't been done."); } processStartInfo.FileName = BuildVariables.MSBuildPath; processStartInfo.Arguments = arguments; } else { processStartInfo.FileName = "dotnet"; processStartInfo.Arguments = $"msbuild {arguments}"; } var process = new Process() { StartInfo = processStartInfo, EnableRaisingEvents = true, }; var output = new StringBuilder(); var outputLock = new object(); process.ErrorDataReceived += Process_ErrorDataReceived; process.OutputDataReceived += Process_OutputDataReceived; process.Start(); process.BeginErrorReadLine(); process.BeginOutputReadLine(); var timeoutTask = Task.Delay(timeout.Value).ContinueWith((t) => { // Don't timeout during debug sessions while (Debugger.IsAttached) { Thread.Sleep(TimeSpan.FromSeconds(1)); } if (process.HasExited) { // This will happen on success, the 'real' task has already completed so this value will // never be visible. return((MSBuildResult)null); } // This is a timeout. process.Kill(); throw new TimeoutException($"command '${process.StartInfo.FileName} {process.StartInfo.Arguments}' timed out after {timeout}."); }); var waitTask = Task.Run(() => { // We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously // this code used Process.Exited, which could result in us missing some output due to the ordering of // events. // // See the remarks here: https://msdn.microsoft.com/en-us/library/ty0d8k56(v=vs.110).aspx if (!process.WaitForExit(Int32.MaxValue)) { // unreachable - the timeoutTask will kill the process before this happens. throw new TimeoutException(); } process.WaitForExit(); string outputString; lock (outputLock) { outputString = output.ToString(); } var result = new MSBuildResult(project, process.StartInfo.FileName, process.StartInfo.Arguments, process.ExitCode, outputString); return(result); }); return(Task.WhenAny <MSBuildResult>(waitTask, timeoutTask).Unwrap()); void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e) { lock (outputLock) { output.AppendLine(e.Data); } } void Process_OutputDataReceived(object sender, DataReceivedEventArgs e) { lock (outputLock) { output.AppendLine(e.Data); } } }
private static void AddFileToProject(ProjectDirectory project, string filename, string content) { var path = Path.Combine(project.DirectoryPath, filename); File.WriteAllText(path, content); }
// Regression test for https://github.com/dotnet/aspnetcore/issues/18752 public async Task Publish_HostedApp_WithoutTrimming_Works() { // Arrange using var project = ProjectDirectory.Create("blazorhosted", additionalProjects: new[] { "blazorwasm", "razorclasslibrary", }); AddWasmProjectContent(project, @" <PropertyGroup> <PublishTrimmed>false</PublishTrimmed> </PropertyGroup>"); // VS builds projects individually and then a publish with BuildDependencies=false, but building the main project is a close enough approximation for this test. var result = await MSBuildProcessManager.DotnetMSBuild(project, "Build"); Assert.BuildPassed(result); // Publish result = await MSBuildProcessManager.DotnetMSBuild(project, "Publish", "/p:BuildDependencies=false"); var publishDirectory = project.PublishOutputDirectory; // Make sure the main project exists Assert.FileExists(result, publishDirectory, "blazorhosted.dll"); // Verification for https://github.com/dotnet/aspnetcore/issues/19926. Verify binaries for projects // referenced by the Hosted project appear in the publish directory Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll"); Assert.FileExists(result, publishDirectory, "blazorwasm.dll"); var blazorPublishDirectory = Path.Combine(publishDirectory, "wwwroot"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm"); Assert.FileExists(result, blazorPublishDirectory, "_framework", DotNetJsFileName); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output. // Verify project references appear as static web assets Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll"); // Also verify project references to the server project appear in the publish output Assert.FileExists(result, publishDirectory, "RazorClassLibrary.dll"); // Verify static assets are in the publish directory Assert.FileExists(result, blazorPublishDirectory, "index.html"); // Verify static web assets from referenced projects are copied. Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js"); Assert.FileExists(result, publishDirectory, "wwwroot", "_content", "RazorClassLibrary", "styles.css"); // Verify web.config Assert.FileExists(result, publishDirectory, "web.config"); VerifyBootManifestHashes(result, blazorPublishDirectory); // Verify compression works Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.br"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.br"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.br"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.br"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "dotnet.wasm.gz"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazorwasm.dll.gz"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "RazorClassLibrary.dll.gz"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll.gz"); VerifyServiceWorkerFiles(result, blazorPublishDirectory, serviceWorkerPath: Path.Combine("serviceworkers", "my-service-worker.js"), serviceWorkerContent: "// This is the production service worker", assetsManifestPath: "custom-service-worker-assets.js"); }