diff --git a/MvcPrecompilation.sln b/MvcPrecompilation.sln index 0ee22293f9..d41b701568 100644 --- a/MvcPrecompilation.sln +++ b/MvcPrecompilation.sln @@ -19,6 +19,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0398AFFF-5 EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Mvc.Razor.Precompilation.FunctionalTests", "test\Microsoft.AspNetCore.Mvc.Razor.Precompilation.FunctionalTests\Microsoft.AspNetCore.Mvc.Razor.Precompilation.FunctionalTests.xproj", "{46C9A4B2-8B1C-451B-B670-C194901D66AC}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test", "test\Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test\Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test.xproj", "{B58E3380-D451-4E54-B522-CD21404FCD58}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test", "test\Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test\Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test.xproj", "{E0D75B4E-839F-4F80-9B1F-B33F616BCC5F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -37,6 +41,14 @@ Global {46C9A4B2-8B1C-451B-B670-C194901D66AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {46C9A4B2-8B1C-451B-B670-C194901D66AC}.Release|Any CPU.ActiveCfg = Release|Any CPU {46C9A4B2-8B1C-451B-B670-C194901D66AC}.Release|Any CPU.Build.0 = Release|Any CPU + {B58E3380-D451-4E54-B522-CD21404FCD58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B58E3380-D451-4E54-B522-CD21404FCD58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B58E3380-D451-4E54-B522-CD21404FCD58}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B58E3380-D451-4E54-B522-CD21404FCD58}.Release|Any CPU.Build.0 = Release|Any CPU + {E0D75B4E-839F-4F80-9B1F-B33F616BCC5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0D75B4E-839F-4F80-9B1F-B33F616BCC5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0D75B4E-839F-4F80-9B1F-B33F616BCC5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0D75B4E-839F-4F80-9B1F-B33F616BCC5F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -45,5 +57,7 @@ Global {4339FC9B-AEC6-442A-B413-A41555ED76C7} = {02F7AA35-91AF-491E-9F0E-03CFAF86C720} {F8BF7D95-0633-407F-BB0B-02563F13C068} = {02F7AA35-91AF-491E-9F0E-03CFAF86C720} {46C9A4B2-8B1C-451B-B670-C194901D66AC} = {0398AFFF-505E-4283-89DA-BBD9D28B53DB} + {B58E3380-D451-4E54-B522-CD21404FCD58} = {0398AFFF-505E-4283-89DA-BBD9D28B53DB} + {E0D75B4E-839F-4F80-9B1F-B33F616BCC5F} = {0398AFFF-505E-4283-89DA-BBD9D28B53DB} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design/Internal/PrecompilationApplication.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design/Internal/PrecompilationApplication.cs index 72e4861055..d4a27d4311 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design/Internal/PrecompilationApplication.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design/Internal/PrecompilationApplication.cs @@ -37,10 +37,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal } catch (Exception ex) { - Console.Error.WriteLine(ex.Message); -#if DEBUG - Console.Error.WriteLine(ex); -#endif + Error.WriteLine(ex.Message); + Error.WriteLine(ex.StackTrace); return 1; } } @@ -48,14 +46,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal private string GetInformationalVersion() { var assembly = _callingType.GetTypeInfo().Assembly; - var attributes = assembly.GetCustomAttributes( - typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute[]; - - var versionAttribute = attributes.Length == 0 ? - assembly.GetName().Version.ToString() : - attributes[0].InformationalVersion; - - return versionAttribute; + var attribute = assembly.GetCustomAttribute(); + return attribute.InformationalVersion; } } } diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design/Internal/PrecompileRunCommand.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design/Internal/PrecompileRunCommand.cs index f223a0f6d4..2f776d041c 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design/Internal/PrecompileRunCommand.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design/Internal/PrecompileRunCommand.cs @@ -24,13 +24,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal { public class PrecompileRunCommand { - public static readonly string ApplicationNameTemplate = "--applicationName"; + public static readonly string ApplicationNameTemplate = "--application-name"; public static readonly string OutputPathTemplate = "--output-path"; private static readonly ParallelOptions ParalellOptions = new ParallelOptions { MaxDegreeOfParallelism = 4 }; + private CommandLineApplication Application { get; set; } + private CommandOption OutputPathOption { get; set; } private CommandOption ApplicationNameOption { get; set; } @@ -45,6 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal public void Configure(CommandLineApplication app) { + Application = app; Options.Configure(app); StrongNameOptions.Configure(app); @@ -63,7 +66,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal private int Execute() { - ParseArguments(); + if (!ParseArguments()) + { + return 1; + } MvcServiceProvider = new MvcServiceProvider( ProjectPath, @@ -71,7 +77,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal Options.ContentRootOption.Value(), Options.ConfigureCompilationType.Value()); - Console.WriteLine("Running Razor view precompilation."); + Application.Out.WriteLine("Running Razor view precompilation."); var stopWatch = Stopwatch.StartNew(); var results = GenerateCode(); @@ -83,7 +89,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal success = false; foreach (var error in result.GeneratorResults.ParserErrors) { - Console.Error.WriteLine($"{error.Location.FilePath} ({error.Location.LineIndex}): {error.Message}"); + Application.Error.WriteLine($"{error.Location.FilePath} ({error.Location.LineIndex}): {error.Message}"); } } } @@ -104,15 +110,15 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal { foreach (var diagnostic in emitResult.Diagnostics) { - Console.Error.WriteLine(CSharpDiagnosticFormatter.Instance.Format(diagnostic)); + Application.Error.WriteLine(CSharpDiagnosticFormatter.Instance.Format(diagnostic)); } return 1; } stopWatch.Stop(); - Console.WriteLine($"Precompiled views emitted to {assemblyPath}."); - Console.WriteLine($"Successfully compiled {results.Length} Razor views in {stopWatch.ElapsedMilliseconds}ms."); + Application.Out.WriteLine($"Precompiled views emitted to {assemblyPath}."); + Application.Out.WriteLine($"Successfully compiled {results.Length} Razor views in {stopWatch.ElapsedMilliseconds}ms."); return 0; } @@ -198,23 +204,34 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal return codeGenerator.Compilation; } - private void ParseArguments() + private bool ParseArguments() { ProjectPath = Options.ProjectArgument.Value; if (string.IsNullOrEmpty(ProjectPath)) { - throw new ArgumentException("Project path not specified."); + Application.Error.WriteLine("Project path not specified."); + return false; } if (!OutputPathOption.HasValue()) { - throw new ArgumentException($"Option {OutputPathTemplate} does not specify a value."); + Application.Error.WriteLine($"Option {OutputPathTemplate} does not specify a value."); + return false; } if (!ApplicationNameOption.HasValue()) { - throw new ArgumentException($"Option {ApplicationNameTemplate} does not specify a value."); + Application.Error.WriteLine($"Option {ApplicationNameTemplate} does not specify a value."); + return false; } + + if (!Options.ContentRootOption.HasValue()) + { + Application.Error.WriteLine($"Option {CommonOptions.ContentRootTemplate} does not specify a value."); + return false; + } + + return true; } private ViewCompilationInfo[] GenerateCode() diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools/Internal/PrecompileDispatchCommand.cs b/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools/Internal/PrecompileDispatchCommand.cs index c8bb5185cf..4db727a086 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools/Internal/PrecompileDispatchCommand.cs +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools/Internal/PrecompileDispatchCommand.cs @@ -17,6 +17,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Internal { public class PrecompileDispatchCommand { + private CommandLineApplication Application { get; set; } + private CommonOptions Options { get; } = new CommonOptions(); private CommandOption FrameworkOption { get; set; } @@ -25,10 +27,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Internal private CommandOption OutputPathOption { get; set; } - private NuGetFramework TargetFramework { get; set; } - private CommandOption BuildBasePathOption { get; set; } + private NuGetFramework TargetFramework { get; set; } + private CommandOption DumpFilesOption { get; set; } private string ProjectPath { get; set; } @@ -39,6 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Internal public void Configure(CommandLineApplication app) { + Application = app; Options.Configure(app); FrameworkOption = app.Option( "-f|--framework", @@ -52,7 +55,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Internal OutputPathOption = app.Option( "-o|--output-path", - "Published path of the application.", + "Output path.", CommandOptionType.SingleValue); app.OnExecute(() => Execute()); @@ -60,7 +63,10 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Internal private int Execute() { - ParseArguments(); + if (!ParseArguments()) + { + return 1; + } var runtimeContext = GetRuntimeContext(); @@ -125,41 +131,45 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Internal toolName: toolName); var commandExitCode = dispatchCommand - .ForwardStdErr(Console.Error) - .ForwardStdOut(Console.Out) + .ForwardStdErr(Application.Error) + .ForwardStdOut(Application.Out) .Execute() .ExitCode; return commandExitCode; } - private void ParseArguments() + private bool ParseArguments() { - ProjectPath = GetProjectPath(); + ProjectPath = GetProjectPath(Options.ProjectArgument.Value); Configuration = ConfigurationOption.Value() ?? DotNet.Cli.Utils.Constants.DefaultConfiguration; if (!FrameworkOption.HasValue()) { - throw new Exception($"Option {FrameworkOption.Template} does not have a value."); + Application.Error.WriteLine($"Option {FrameworkOption.Template} does not have a value."); + return false; } TargetFramework = NuGetFramework.Parse(FrameworkOption.Value()); if (!OutputPathOption.HasValue()) { - throw new Exception($"Option {OutputPathOption.Template} does not have a value."); + Application.Error.WriteLine($"Option {OutputPathOption.Template} does not have a value."); + return false; } OutputPath = OutputPathOption.Value(); + + return true; } - private string GetProjectPath() + public static string GetProjectPath(string projectArgument) { string projectPath; - if (!string.IsNullOrEmpty(Options.ProjectArgument.Value)) + if (!string.IsNullOrEmpty(projectArgument)) { - projectPath = Path.GetFullPath(Options.ProjectArgument.Value); - if (string.Equals(Path.GetFileName(ProjectPath), "project.json", StringComparison.OrdinalIgnoreCase)) + projectPath = Path.GetFullPath(projectArgument); + if (string.Equals(Path.GetFileName(projectPath), "project.json", StringComparison.OrdinalIgnoreCase)) { - projectPath = Path.GetDirectoryName(ProjectPath); + projectPath = Path.GetDirectoryName(projectPath); } if (!Directory.Exists(projectPath)) diff --git a/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools/project.json b/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools/project.json index e19f3cf655..681230ea57 100644 --- a/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools/project.json +++ b/src/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools/project.json @@ -25,7 +25,9 @@ "xmlDoc": true }, "dependencies": { - "Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design": { "target": "project" }, + "Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design": { + "target": "project" + }, "Microsoft.DotNet.Cli.Utils": "1.0.0-*", "Microsoft.Extensions.CommandLineUtils": "1.1.0-*", "Microsoft.Extensions.DotnetToolDispatcher.Sources": { diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test.xproj b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test.xproj new file mode 100644 index 0000000000..8edf50470d --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test.xproj @@ -0,0 +1,20 @@ + + + + 14.0.25420 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + e0d75b4e-839f-4f80-9b1f-b33f616bcc5f + .\obj + .\bin\ + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test/PrecompileRunCommandTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test/PrecompileRunCommandTest.cs new file mode 100644 index 0000000000..222d4c937a --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test/PrecompileRunCommandTest.cs @@ -0,0 +1,193 @@ +// 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.Reflection; +using Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools +{ + public class PrecompileRunCommandTest + { + [Fact] + public void RunPrintsHelp_WhenHelpOptionIsSpecified() + { + // Arrange + var expected = +$@"Microsoft Razor Precompilation Utility {GetToolVersion()} + +Usage: razor-precompile [arguments] [options] + +Arguments: + project The path to the project (project folder or project.json) with precompilation. + +Options: + -?|-h|--help Show help information + --configure-compilation-type Type with Configure method + --content-root The application's content root. + --embed-view-sources Embed view sources as resources in the generated assembly. + --key-file Strong name key path + --delay-sign Determines if the precompiled view assembly is to be delay signed. + --public-sign Determines if the precompiled view assembly is to be public signed. + --output-path Path to the emit the precompiled assembly to. + --application-name Name of the application to produce precompiled assembly for."; + + var args = new[] + { + "--help" + }; + + // Act + var result = Execute(args); + + // Assert + Assert.Equal(0, result.ExitCode); + Assert.Equal(expected, result.Out.Trim(), ignoreLineEndingDifferences: true); + Assert.Empty(result.Error); + } + + [Fact] + public void Run_PrintsHelpWhenInvalidOptionsAreSpecified() + { + // Arrange + var expectedOut = @"Specify --help for a list of available options and commands."; + var expectedError = @"Unrecognized option '--bad-option'"; + var args = new[] + { + "--bad-option" + }; + + // Act + var result = Execute(args); + + // Assert + Assert.Equal(1, result.ExitCode); + Assert.Equal(expectedOut, result.Out.Trim()); + Assert.Equal( + expectedError, + result.Error.Split(new[] { Environment.NewLine }, StringSplitOptions.None).First()); + } + + [Fact] + public void Run_PrintsErrorWhenArgumentIsMissing() + { + // Arrange + var expectedError = @"Project path not specified."; + var args = new string[0]; + + // Act + var result = Execute(args); + + // Assert + Assert.Equal(1, result.ExitCode); + Assert.Empty(result.Out); + Assert.Equal(expectedError, result.Error.Trim()); + } + + [Fact] + public void Run_PrintsErrorWhenOutputPathOptionIsMissing() + { + // Arrange + var expectedError = @"Option --output-path does not specify a value."; + var args = new[] + { + Directory.GetCurrentDirectory(), + }; + + // Act + var result = Execute(args); + + // Assert + Assert.Equal(1, result.ExitCode); + Assert.Empty(result.Out); + Assert.Equal(expectedError, result.Error.Trim()); + } + + [Fact] + public void Run_PrintsErrorWhenApplicationNameOptionIsMissing() + { + // Arrange + var expectedError = @"Option --application-name does not specify a value."; + var args = new[] + { + Directory.GetCurrentDirectory(), + "--output-path", + Directory.GetCurrentDirectory(), + }; + + // Act + var result = Execute(args); + + // Assert + Assert.Equal(1, result.ExitCode); + Assert.Empty(result.Out); + Assert.Equal(expectedError, result.Error.Trim()); + } + + [Fact] + public void Run_PrintsErrorWhenContentRootOptionIsMissing() + { + // Arrange + var expectedError = @"Option --content-root does not specify a value."; + var args = new[] + { + Directory.GetCurrentDirectory(), + "--output-path", + Directory.GetCurrentDirectory(), + "--application-name", + "TestApplicationName", + }; + + // Act + var result = Execute(args); + + // Assert + Assert.Equal(1, result.ExitCode); + Assert.Empty(result.Out); + Assert.Equal(expectedError, result.Error.Trim()); + } + + private static string GetToolVersion() + { + return typeof(Program) + .GetTypeInfo() + .Assembly + .GetCustomAttribute() + .InformationalVersion; + } + + private class ExecuteResult + { + public string Out { get; set; } + + public string Error { get; set; } + + public int ExitCode { get; set; } + } + + private ExecuteResult Execute(string[] args) + { + using (var outputWriter = new StringWriter()) + using (var errorWriter = new StringWriter()) + { + var app = new PrecompilationApplication(typeof(Program)) + { + Out = outputWriter, + Error = errorWriter, + }; + new PrecompileRunCommand().Configure(app); + var exitCode = app.Execute(args); + + return new ExecuteResult + { + ExitCode = exitCode, + Out = outputWriter.ToString(), + Error = errorWriter.ToString(), + }; + } + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test/project.json b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test/project.json new file mode 100644 index 0000000000..b40a97d94f --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Test/project.json @@ -0,0 +1,20 @@ +{ + "dependencies": { + "dotnet-test-xunit": "2.2.0-*", + "Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools": "1.0.0-*", + "xunit": "2.2.0-*" + }, + + "frameworks": { + "netcoreapp1.0": { + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0" + } + }, + "net451": {} + } + }, + "testRunner": "xunit" +} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.FunctionalTests/PublishWithEmbedViewSourcesTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.FunctionalTests/PublishWithEmbedViewSourcesTest.cs index 609be6da99..a408edaefa 100644 --- a/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.FunctionalTests/PublishWithEmbedViewSourcesTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.FunctionalTests/PublishWithEmbedViewSourcesTest.cs @@ -64,9 +64,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.FunctionalTests Fixture.Logger); // Assert - 2 - Assert.Equal( - expectedViews, - response2.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)); + var actual = response2.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries) + .OrderBy(p => p, StringComparer.OrdinalIgnoreCase); + Assert.Equal(expectedViews, actual); } } diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test.xproj b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test.xproj new file mode 100644 index 0000000000..c5140ed5fd --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test.xproj @@ -0,0 +1,20 @@ + + + + 14.0.25420 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + b58e3380-d451-4e54-b522-cd21404fcd58 + .\obj + .\bin\ + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test/PrecompileDispatchCommandTest.cs b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test/PrecompileDispatchCommandTest.cs new file mode 100644 index 0000000000..cdaeca1c0c --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test/PrecompileDispatchCommandTest.cs @@ -0,0 +1,196 @@ +// 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.Reflection; +using Microsoft.AspNetCore.Mvc.Razor.Precompilation.Design.Internal; +using Microsoft.AspNetCore.Mvc.Razor.Precompilation.Internal; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools +{ + public class PrecompileDispatchCommandTest + { + [Fact] + public void RunPrintsHelp_WhenHelpOptionIsSpecified() + { + // Arrange + var expected = +$@"Microsoft Razor Precompilation Utility {GetToolVersion()} + +Usage: razor-precompile [arguments] [options] + +Arguments: + project The path to the project (project folder or project.json) with precompilation. + +Options: + -?|-h|--help Show help information + --configure-compilation-type Type with Configure method + --content-root The application's content root. + --embed-view-sources Embed view sources as resources in the generated assembly. + -f|--framework Target Framework + -c|--configuration Configuration + -o|--output-path Output path."; + var args = new[] + { + "--help" + }; + + // Act + var result = Execute(args); + + // Assert + Assert.Equal(0, result.ExitCode); + Assert.Equal(expected, result.Out.Trim(), ignoreLineEndingDifferences: true); + Assert.Empty(result.Error); + } + + [Fact] + public void RunPrintsHelp_WhenInvalidOptionsAreSpecified() + { + // Arrange + var expectedOut = @"Specify --help for a list of available options and commands."; + var expectedError = @"Unrecognized option '--bad-option'"; + var args = new[] + { + "--bad-option" + }; + + // Act + var result = Execute(args); + + // Assert + Assert.Equal(1, result.ExitCode); + Assert.Equal(expectedOut, result.Out.Trim()); + Assert.Equal( + expectedError, + result.Error.Split(new[] { Environment.NewLine }, StringSplitOptions.None).First()); + } + + [Fact] + public void RunPrintsError_IfFrameworkIfNotSpecified() + { + // Arrange + var expected = "Option -f|--framework does not have a value."; + var args = new string[0]; + + // Act + var result = Execute(args); + + // Assert + Assert.Equal(1, result.ExitCode); + Assert.Empty(result.Out); + Assert.Equal(expected, result.Error.Trim()); + } + + [Fact] + public void RunPrintsError_IfOutputPathIfNotSpecified() + { + // Arrange + var expected = "Option -o|--output-path does not have a value."; + var args = new[] + { + "-f", + "framework" + }; + + // Act + var result = Execute(args); + + // Assert + Assert.Equal(1, result.ExitCode); + Assert.Empty(result.Out); + Assert.Equal(expected, result.Error.Trim()); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void GetProjectPath_ReturnsCurrentDirectoryIfArgumentIsNullOrEmpty(string projectPath) + { + // Act + var actual = PrecompileDispatchCommand.GetProjectPath(projectPath); + + // Assert + Assert.Equal(Directory.GetCurrentDirectory(), actual); + } + + public static TheoryData GetProjectPath_ReturnsArgumentIfNotNullOrEmptyData + { + get + { + return new TheoryData + { + { "", Directory.GetCurrentDirectory() }, + { "project.json", Directory.GetCurrentDirectory() }, + { Path.GetTempPath(), Path.GetTempPath() }, + }; + } + } + + [Theory] + [MemberData(nameof(GetProjectPath_ReturnsArgumentIfNotNullOrEmptyData))] + public void GetProjectPath_ReturnsArgumentIfNotNullOrEmpty(string projectPath, string expected) + { + // Act + var actual = PrecompileDispatchCommand.GetProjectPath(projectPath); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void GetProjectPath_ThrowsIfDirectoryDoesNotExist() + { + // Arrange + var nonExistent = Path.GetRandomFileName(); + + // Act & Assert + var ex = Assert.Throws( + () => PrecompileDispatchCommand.GetProjectPath(nonExistent)); + Assert.Equal($"Could not find directory {Path.GetFullPath(nonExistent)}.", ex.Message); + } + + private static string GetToolVersion() + { + return typeof(Program) + .GetTypeInfo() + .Assembly + .GetCustomAttribute() + .InformationalVersion; + } + + private class ExecuteResult + { + public string Out { get; set; } + + public string Error { get; set; } + + public int ExitCode { get; set; } + } + + private ExecuteResult Execute(string[] args) + { + using (var outputWriter = new StringWriter()) + using (var errorWriter = new StringWriter()) + { + var app = new PrecompilationApplication(typeof(Program)) + { + Out = outputWriter, + Error = errorWriter, + }; + new PrecompileDispatchCommand().Configure(app); + var exitCode = app.Execute(args); + + return new ExecuteResult + { + ExitCode = exitCode, + Out = outputWriter.ToString(), + Error = errorWriter.ToString(), + }; + } + } + } +} diff --git a/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test/project.json b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test/project.json new file mode 100644 index 0000000000..b40a97d94f --- /dev/null +++ b/test/Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools.Test/project.json @@ -0,0 +1,20 @@ +{ + "dependencies": { + "dotnet-test-xunit": "2.2.0-*", + "Microsoft.AspNetCore.Mvc.Razor.Precompilation.Tools": "1.0.0-*", + "xunit": "2.2.0-*" + }, + + "frameworks": { + "netcoreapp1.0": { + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0" + } + }, + "net451": {} + } + }, + "testRunner": "xunit" +}