diff --git a/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.targets b/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.targets index 0c1f1b5f4e..319ab4f87c 100644 --- a/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.targets +++ b/src/Microsoft.AspNetCore.Razor.Design/build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.targets @@ -52,7 +52,7 @@ diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs index 98792803b5..9439792abd 100644 --- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs +++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/Assert.cs @@ -1,7 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.IO; +using System.Linq; +using System.Runtime.Serialization; using System.Text; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests @@ -10,7 +13,10 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { public static void BuildPassed(MSBuildResult result) { - NotNull(result); + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + }; if (result.ExitCode != 0) { @@ -18,52 +24,68 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } } - public static void FileExists(MSBuildResult result, string filePath) + public static void FileExists(MSBuildResult result, params string[] paths) { - NotNull(result); - NotNull(filePath); + if (result == null) + { + throw new ArgumentNullException(nameof(result)); + } - if (!File.Exists(Path.Combine(result.Project.DirectoryPath, filePath))) + var filePath = Path.Combine(result.Project.DirectoryPath, Path.Combine(paths)); + if (!File.Exists(filePath)) { throw new FileMissingException(result, filePath); } } - private class BuildFailedException : Xunit.Sdk.XunitException + public static void FileCountEquals(MSBuildResult result, int expected, string directoryPath, string searchPattern) { - public BuildFailedException(MSBuildResult result) + if (result == null) { - Result = result; + throw new ArgumentNullException(nameof(result)); } - public MSBuildResult Result { get; } - - public override string Message + if (directoryPath == null) { - get - { - var message = new StringBuilder(); - message.Append("Build failed: "); - message.Append(Result.FileName); - message.Append(" "); - message.Append(Result.Arguments); - message.AppendLine(); - message.AppendLine(); - message.Append(Result.Output); - return message.ToString(); - } + throw new ArgumentNullException(nameof(directoryPath)); + } + + if (searchPattern == null) + { + throw new ArgumentNullException(nameof(searchPattern)); + } + + directoryPath = Path.Combine(result.Project.DirectoryPath, directoryPath); + + var files = Directory.GetFiles(directoryPath, searchPattern, SearchOption.AllDirectories); + if (files.Length != expected) + { + throw new FileCountException(result, expected, directoryPath, searchPattern, files); } } - private class FileMissingException : Xunit.Sdk.XunitException + public static void FileDoesNotExist(MSBuildResult result, params string[] paths) { - public FileMissingException(MSBuildResult result, string filePath) + if (result == null) { - Result = result; - FilePath = filePath; + throw new ArgumentNullException(nameof(result)); } - public string FilePath { get; } + var filePath = Path.Combine(result.Project.DirectoryPath, Path.Combine(paths)); + if (File.Exists(filePath)) + { + throw new FileFoundException(result, filePath); + } + } + + private abstract class MSBuildXunitException : Xunit.Sdk.XunitException + { + protected MSBuildXunitException(MSBuildResult result) + { + Result = result; + } + + protected abstract string Heading { get; } public MSBuildResult Result { get; } @@ -72,7 +94,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests get { var message = new StringBuilder(); - message.Append($"File: '{FilePath}' was not found."); + message.AppendLine(Heading); message.Append(Result.FileName); message.Append(" "); message.Append(Result.Arguments); @@ -80,10 +102,88 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests message.AppendLine(); message.Append(Result.Output); return message.ToString(); - - ; } } } + + private class BuildFailedException : MSBuildXunitException + { + public BuildFailedException(MSBuildResult result) + : base(result) + { + } + + protected override string Heading => "Build failed:"; + } + + private class FileMissingException : MSBuildXunitException + { + public FileMissingException(MSBuildResult result, string filePath) + : base(result) + { + FilePath = filePath; + } + + public string FilePath { get; } + + protected override string Heading => $"File: '{FilePath}' was not found."; + } + + private class FileCountException : MSBuildXunitException + { + public FileCountException(MSBuildResult result, int expected, string directoryPath, string searchPattern, string[] files) + : base(result) + { + Expected = expected; + DirectoryPath = directoryPath; + SearchPattern = searchPattern; + Files = files; + } + + public string DirectoryPath { get; } + + public int Expected { get; } + + public string[] Files { get; } + + public string SearchPattern { get; } + + protected override string Heading + { + get + { + var heading = new StringBuilder(); + heading.AppendLine($"Expected {Expected} files matching {SearchPattern} in {DirectoryPath}, found {Files.Length}."); + + if (Files.Length > 0) + { + heading.AppendLine("Files:"); + + foreach (var file in Files) + { + heading.Append("\t"); + heading.Append(file); + } + + heading.AppendLine(); + } + + return heading.ToString(); + } + } + } + + private class FileFoundException : MSBuildXunitException + { + public FileFoundException(MSBuildResult result, string filePath) + : base(result) + { + FilePath = filePath; + } + + public string FilePath { get; } + + protected override string Heading => $"File: '{FilePath}' was found, but should not exist."; + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIntegrationTest.cs new file mode 100644 index 0000000000..33a615fc05 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/BuildIntegrationTest.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests +{ + public class BuildIntegrationTest : MSBuildIntegrationTestBase + { + [Fact] + [InitializeTestProject("SimpleMvc")] + public async Task Build_SimpleMvc_CanBuildSuccessfully() + { + var result = await DotnetMSBuild("Build", "/p:RazorCompileOnBuild=true"); + + Assert.BuildPassed(result); + Assert.FileExists(result, OutputPath, "SimpleMvc.dll"); + Assert.FileExists(result, OutputPath, "SimpleMvc.PrecompiledViews.dll"); + } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/CleanProjectIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/CleanProjectIntegrationTest.cs deleted file mode 100644 index 345da5b9fc..0000000000 --- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/CleanProjectIntegrationTest.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests -{ - public class CleanProjectIntegrationTest : MSBuildIntegrationTestBase - { - [Fact] - [InitializeTestProject("SimpleMvc")] - public async Task CleanProject_RunBuild() - { - var result = await DotnetMSBuild("Restore;Build", "/p:RazorCompileOnBuild=true;Configuration=Debug"); - - Assert.BuildPassed(result); - Assert.FileExists(result, Path.Combine("bin", "Debug", "netcoreapp2.0", "SimpleMvc.dll")); - Assert.FileExists(result, Path.Combine("bin", "Debug", "netcoreapp2.0", "SimpleMvc.PrecompiledViews.dll")); - } - } -} diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/InitializeTestProjectAttribute.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/InitializeTestProjectAttribute.cs index 7074c440c8..18b93aa99c 100644 --- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/InitializeTestProjectAttribute.cs +++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/InitializeTestProjectAttribute.cs @@ -36,7 +36,5 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests MSBuildIntegrationTestBase.Project.Dispose(); MSBuildIntegrationTestBase.Project = null; } - - } } diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/MSBuildIntegrationTestBase.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/MSBuildIntegrationTestBase.cs index 452ed625ad..6ce4634d9d 100644 --- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/MSBuildIntegrationTestBase.cs +++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/MSBuildIntegrationTestBase.cs @@ -2,55 +2,49 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Diagnostics; -#if NET461 -using System.Runtime.Remoting; -using System.Runtime.Remoting.Messaging; -#else +using System.IO; using System.Threading; -#endif using System.Threading.Tasks; namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests { public abstract class MSBuildIntegrationTestBase { -#if NET461 -#elif NETCOREAPP2_0 || NETCOREAPP2_1 private static readonly AsyncLocal _project = new AsyncLocal(); -#else -#error TFM not supported -#endif protected MSBuildIntegrationTestBase() { } +#if DEBUG + protected string Configuration => "Debug"; +#elif RELEASE + protected string Configuration => "Release"; +#else +#error Configuration not supported +#endif + + protected string IntermediateOutputPath => Path.Combine("obj", Configuration, TargetFramework); + + protected string OutputPath => Path.Combine("bin", Configuration, TargetFramework); + // Used by the test framework to set the project that we're working with internal static ProjectDirectory Project { -#if NET461 - get - { - var handle = (ObjectHandle)CallContext.LogicalGetData("MSBuildIntegrationTestBase_Project"); - return (ProjectDirectory)handle.Unwrap(); - } - set - { - CallContext.LogicalSetData("MSBuildIntegrationTestBase_Project", new ObjectHandle(value)); - } -#elif NETCOREAPP2_0 || NETCOREAPP2_1 get { return _project.Value; } set { _project.Value = value; } -#else -#error TFM not supported -#endif } - internal Task DotnetMSBuild(string target, string args = null, bool debug = false) + protected string RazorIntermediateOutputPath => Path.Combine(IntermediateOutputPath, "Razor"); + + protected string TargetFramework { get; set; } = "netcoreapp2.0"; + + internal Task DotnetMSBuild(string target, string args = null, bool suppressRestore = false, bool suppressTimeout = false) { - var timeout = debug ? (TimeSpan?)Timeout.InfiniteTimeSpan : null; - return MSBuildProcessManager.RunProcessAsync(Project, $"/t:{target} {args}", timeout); + var timeout = suppressTimeout ? (TimeSpan?)Timeout.InfiniteTimeSpan : null; + var restoreArgument = suppressRestore ? "" : "/restore"; + + return MSBuildProcessManager.RunProcessAsync(Project, $"{restoreArgument} /t:{target} /p:Configuration={Configuration} {args}", timeout); } } } diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/ProjectDirectory.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/ProjectDirectory.cs index 1b2dfbb2a9..2c31984594 100644 --- a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/ProjectDirectory.cs +++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/ProjectDirectory.cs @@ -35,9 +35,11 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests throw new InvalidOperationException($"Could not find project at '{projectRoot}'"); } + var binariesRoot = Path.GetDirectoryName(typeof(ProjectDirectory).Assembly.Location); + CopyDirectory(new DirectoryInfo(projectRoot), new DirectoryInfo(destinationPath)); - CreateDirectoryProps(projectRoot, destinationPath); + CreateDirectoryProps(projectRoot, binariesRoot, destinationPath); CreateDirectoryTargets(destinationPath); return new ProjectDirectory(destinationPath); @@ -69,7 +71,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests } } - void CreateDirectoryProps(string originalProjectRoot, string projectRoot) + void CreateDirectoryProps(string originalProjectRoot, string binariesRoot, string projectRoot) { #if DEBUG var configuration = "Debug"; @@ -82,6 +84,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests {originalProjectRoot} + {binariesRoot} <_RazorMSBuildRoot>$(OriginalProjectRoot)\..\..\..\src\Microsoft.AspNetCore.Razor.Design\bin\{configuration}\netstandard2.0\ diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/RazorGenerateIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/RazorGenerateIntegrationTest.cs new file mode 100644 index 0000000000..0b40241929 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Design.Test/IntegrationTests/RazorGenerateIntegrationTest.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests +{ + public class RazorGenerateIntegrationTest : MSBuildIntegrationTestBase + { + [Fact] + [InitializeTestProject("SimpleMvc")] + public async Task RazorGenerate_Success_GeneratesFilesOnDisk() + { + var result = await DotnetMSBuild("RazorGenerate"); + + Assert.BuildPassed(result); + + // RazorGenerate should compile the assembly, but not the views. + Assert.FileExists(result, IntermediateOutputPath, "SimpleMvc.dll"); + Assert.FileDoesNotExist(result, IntermediateOutputPath, "SimpleMvc.PrecompiledViews.dll"); + + Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "_ViewImports.cs"); + Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "_ViewStart.cs"); + Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Home", "About.cs"); + Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Home", "Contact.cs"); + Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Home", "Index.cs"); + Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Shared", "_Layout.cs"); + Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Shared", "_ValidationScriptsPartial.cs"); + Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Shared", "Error.cs"); + Assert.FileCountEquals(result, 8, RazorIntermediateOutputPath, "*.cs"); + } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Design.Test/Microsoft.AspNetCore.Razor.Design.Test.csproj b/test/Microsoft.AspNetCore.Razor.Design.Test/Microsoft.AspNetCore.Razor.Design.Test.csproj index 951ccec788..5493ad8adc 100644 --- a/test/Microsoft.AspNetCore.Razor.Design.Test/Microsoft.AspNetCore.Razor.Design.Test.csproj +++ b/test/Microsoft.AspNetCore.Razor.Design.Test/Microsoft.AspNetCore.Razor.Design.Test.csproj @@ -9,6 +9,9 @@ --> netcoreapp2.0 true + + + true @@ -23,6 +26,14 @@ + + + + + + + + diff --git a/test/testapps/SimpleMvc/SimpleMvc.csproj b/test/testapps/SimpleMvc/SimpleMvc.csproj index 3e5e49458c..d41ce133e5 100644 --- a/test/testapps/SimpleMvc/SimpleMvc.csproj +++ b/test/testapps/SimpleMvc/SimpleMvc.csproj @@ -3,10 +3,16 @@ netcoreapp2.0 - + + + + + + +