// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.CodeDom; using System.CodeDom.Compiler; using System.Collections; using System.IO; using System.Linq; using System.Reflection; using System.Web.Compilation; using ASP; using Microsoft.CSharp; using Moq; using Xunit; namespace ASP { public class _Page_Foo_Test_cshtml { } } namespace System.Web.WebPages.Razor.Test { public class RazorBuildProviderTest { private class MockAssemblyBuilder : IAssemblyBuilder { public BuildProvider BuildProvider { get; private set; } public CodeCompileUnit CompileUnit { get; private set; } public string LastTypeFactoryGenerated { get; private set; } public void AddCodeCompileUnit(BuildProvider buildProvider, CodeCompileUnit compileUnit) { BuildProvider = buildProvider; CompileUnit = compileUnit; } public void GenerateTypeFactory(string typeName) { LastTypeFactoryGenerated = typeName; } } [Fact] public void CodeCompilerTypeReturnsTypeFromCodeLanguage() { // Arrange WebPageRazorHost host = new WebPageRazorHost("~/Foo/Baz.cshtml", @"C:\Foo\Baz.cshtml"); RazorBuildProvider provider = CreateBuildProvider("foo @bar baz"); provider.Host = host; // Act CompilerType type = provider.CodeCompilerType; // Assert Assert.Equal(typeof(CSharpCodeProvider), type.CodeDomProviderType); } [Fact] public void CodeCompilerTypeSetsDebugFlagInFullTrust() { // Arrange WebPageRazorHost host = new WebPageRazorHost("~/Foo/Baz.cshtml", @"C:\Foo\Baz.cshtml"); RazorBuildProvider provider = CreateBuildProvider("foo @bar baz"); provider.Host = host; // Act CompilerType type = provider.CodeCompilerType; // Assert Assert.True(type.CompilerParameters.IncludeDebugInformation); } [Fact] public void GetGeneratedTypeUsesNameAndNamespaceFromHostToExtractType() { // Arrange WebPageRazorHost host = new WebPageRazorHost("~/Foo/Test.cshtml", @"C:\Foo\Test.cshtml"); RazorBuildProvider provider = new RazorBuildProvider() { Host = host }; CompilerResults results = new CompilerResults(new TempFileCollection()); results.CompiledAssembly = typeof(_Page_Foo_Test_cshtml).Assembly; // Act Type typ = provider.GetGeneratedType(results); // Assert Assert.Equal(typeof(_Page_Foo_Test_cshtml), typ); } [Fact] public void GenerateCodeCoreAddsGeneratedCodeToAssemblyBuilder() { // Arrange WebPageRazorHost host = new WebPageRazorHost("~/Foo/Baz.cshtml", @"C:\Foo\Baz.cshtml"); RazorBuildProvider provider = new RazorBuildProvider(); CodeCompileUnit ccu = new CodeCompileUnit(); MockAssemblyBuilder asmBuilder = new MockAssemblyBuilder(); provider.Host = host; provider.GeneratedCode = ccu; // Act provider.GenerateCodeCore(asmBuilder); // Assert Assert.Same(provider, asmBuilder.BuildProvider); Assert.Same(ccu, asmBuilder.CompileUnit); Assert.Equal("ASP._Page_Foo_Baz_cshtml", asmBuilder.LastTypeFactoryGenerated); } [Fact] public void CodeGenerationStartedTest() { // Arrange WebPageRazorHost host = new WebPageRazorHost("~/Foo/Baz.cshtml", @"C:\Foo\Baz.cshtml"); RazorBuildProvider provider = CreateBuildProvider("foo @bar baz"); provider.Host = host; // Expected original base dependencies var baseDependencies = new ArrayList(); baseDependencies.Add("/Samples/Foo/Baz.cshtml"); // Expected list of dependencies after GenerateCode is called var dependencies = new ArrayList(); dependencies.Add(baseDependencies[0]); dependencies.Add("/Samples/Foo/Foo.cshtml"); // Set up the event handler provider.CodeGenerationStartedInternal += (sender, e) => { var bp = sender as RazorBuildProvider; bp.AddVirtualPathDependency("/Samples/Foo/Foo.cshtml"); }; // Set up the base dependency MockAssemblyBuilder builder = new MockAssemblyBuilder(); typeof(BuildProvider).GetField("_virtualPath", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(provider, CreateVirtualPath("/Samples/Foo/Baz.cshtml")); // Test that VirtualPathDependencies returns the original dependency before GenerateCode is called Assert.True(baseDependencies.OfType().SequenceEqual(provider.VirtualPathDependencies.OfType())); // Act provider.GenerateCodeCore(builder); // Assert Assert.NotNull(provider.AssemblyBuilderInternal); Assert.Equal(builder, provider.AssemblyBuilderInternal); Assert.True(dependencies.OfType().SequenceEqual(provider.VirtualPathDependencies.OfType())); Assert.Equal("/Samples/Foo/Baz.cshtml", provider.VirtualPath); } [Fact] public void AfterGeneratedCodeEventGetsExecutedAtCorrectTime() { // Arrange WebPageRazorHost host = new WebPageRazorHost("~/Foo/Baz.cshtml", @"C:\Foo\Baz.cshtml"); RazorBuildProvider provider = CreateBuildProvider("foo @bar baz"); provider.Host = host; provider.CodeGenerationCompletedInternal += (sender, e) => { Assert.Equal("~/Foo/Baz.cshtml", e.VirtualPath); e.GeneratedCode.Namespaces.Add(new CodeNamespace("DummyNamespace")); }; // Act CodeCompileUnit generated = provider.GeneratedCode; // Assert Assert.NotNull(generated.Namespaces .OfType() .SingleOrDefault(ns => String.Equals(ns.Name, "DummyNamespace"))); } [Fact] public void GeneratedCodeThrowsHttpParseExceptionForLastParserError() { // Arrange WebPageRazorHost host = new WebPageRazorHost("~/Foo/Baz.cshtml", @"C:\Foo\Baz.cshtml"); RazorBuildProvider provider = CreateBuildProvider("foo @{ if( baz"); provider.Host = host; // Act Assert.Throws(() => { CodeCompileUnit ccu = provider.GeneratedCode; }); } [Fact] public void BuildProviderFiresEventToAlterHostBeforeBuildingPath() { // Arrange WebPageRazorHost expected = new TestHost("~/Foo/Boz.cshtml", @"C:\Foo\Boz.cshtml"); WebPageRazorHost expectedBefore = new WebPageRazorHost("~/Foo/Baz.cshtml", @"C:\Foo\Baz.cshtml"); RazorBuildProvider provider = CreateBuildProvider("foo"); typeof(BuildProvider).GetField("_virtualPath", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(provider, CreateVirtualPath("/Samples/Foo/Baz.cshtml")); Mock.Get(provider).Setup(p => p.GetHostFromConfig()).Returns(expectedBefore); bool called = false; EventHandler handler = (sender, args) => { Assert.Equal("/Samples/Foo/Baz.cshtml", args.VirtualPath); Assert.Same(expectedBefore, args.Host); args.Host = expected; called = true; }; RazorBuildProvider.CompilingPath += handler; try { // Act CodeCompileUnit ccu = provider.GeneratedCode; // Assert Assert.Equal("Test", ccu.Namespaces[0].Name); Assert.Same(expected, provider.Host); Assert.True(called); } finally { RazorBuildProvider.CompilingPath -= handler; } } private static object CreateVirtualPath(string path) { var vPath = typeof(BuildProvider).Assembly.GetType("System.Web.VirtualPath"); var method = vPath.GetMethod("CreateNonRelative", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); return method.Invoke(null, new object[] { path }); } private static RazorBuildProvider CreateBuildProvider(string razorContent) { Mock mockProvider = new Mock() { CallBase = true }; mockProvider.Setup(p => p.InternalOpenReader()) .Returns(() => new StringReader(razorContent)); return mockProvider.Object; } private class TestHost : WebPageRazorHost { public TestHost(string virtualPath, string physicalPath) : base(virtualPath, physicalPath) { } public override void PostProcessGeneratedCode(Web.Razor.Generator.CodeGeneratorContext context) { context.CompileUnit.Namespaces.Insert(0, new CodeNamespace("Test")); } } } }