BuildIncrementalismTest.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. using Moq;
  4. namespace Microsoft.NET.Sdk.Razor.Tests
  5. {
  6. public class BuildIncrementalismTest : AspNetSdkTest
  7. {
  8. public BuildIncrementalismTest(ITestOutputHelper log) : base(log) { }
  9. [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/28780")]
  10. public void Build_ErrorInGeneratedCode_ReportsMSBuildError_OnIncrementalBuild()
  11. {
  12. var testAsset = "RazorSimpleMvc";
  13. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  14. // Introducing a Razor semantic error
  15. var indexPage = Path.Combine(projectDirectory.Path, "Views", "Home", "Index.cshtml");
  16. File.WriteAllText(indexPage, "@{ // Unterminated code block");
  17. // Regular build
  18. VerifyError(projectDirectory);
  19. // Incremental build
  20. VerifyError(projectDirectory);
  21. void VerifyError(TestAsset privateDirectory)
  22. {
  23. var build = new BuildCommand(projectDirectory);
  24. var result = build.Execute();
  25. result.Should().Fail().And.HaveStdOutContaining("RZ1006");
  26. var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString();
  27. // Compilation failed without creating the views assembly
  28. new FileInfo(Path.Combine(intermediateOutputPath, "SimpleMvc.dll")).Should().Exist();
  29. new FileInfo(Path.Combine(intermediateOutputPath, "SimpleMvc.Views.dll")).Should().NotExist();
  30. // File with error does not get written to disk.
  31. new FileInfo(Path.Combine(intermediateOutputPath, "Razor", "Views", "Home", "Index.cshtml.g.cs")).Should().NotExist();
  32. }
  33. }
  34. [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/28780")]
  35. public void BuildComponents_DoesNotRegenerateComponentDefinition_WhenDefinitionIsUnchanged()
  36. {
  37. var testAsset = "RazorMvcWithComponents";
  38. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  39. // Act - 1
  40. var build = new BuildCommand(projectDirectory);
  41. var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString();
  42. var outputPath = build.GetOutputDirectory(DefaultTfm, "Debug").ToString();
  43. var updatedContent = "Some content";
  44. var tagHelperOutputCache = Path.Combine(intermediateOutputPath, "MvcWithComponents.TagHelpers.output.cache");
  45. var generatedFile = Path.Combine(intermediateOutputPath, "Razor", "Views", "Shared", "NavMenu.razor.g.cs");
  46. var generatedDefinitionFile = Path.Combine(intermediateOutputPath, "RazorDeclaration", "Views", "Shared", "NavMenu.razor.g.cs");
  47. // Assert - 1
  48. var result = build.Execute();
  49. result.Should().Pass();
  50. var outputFile = Path.Combine(outputPath, "MvcWithComponents.dll");
  51. new FileInfo(outputFile).Should().Exist();
  52. var outputAssemblyThumbprint = FileThumbPrint.Create(outputFile);
  53. new FileInfo(generatedDefinitionFile).Should().Exist();
  54. var generatedDefinitionThumbprint = FileThumbPrint.Create(generatedDefinitionFile);
  55. new FileInfo(generatedFile).Should().Exist();
  56. var generatedFileThumbprint = FileThumbPrint.Create(generatedFile);
  57. new FileInfo(tagHelperOutputCache).Should().Exist();
  58. new FileInfo(tagHelperOutputCache).Should().Contain(@"""Name"":""MvcWithComponents.Views.Shared.NavMenu""");
  59. var definitionThumbprint = FileThumbPrint.Create(tagHelperOutputCache);
  60. // Act - 2
  61. var page = Path.Combine(projectDirectory.Path, "Views", "Shared", "NavMenu.razor");
  62. File.WriteAllText(page, updatedContent, Encoding.UTF8);
  63. File.SetLastWriteTimeUtc(page, File.GetLastWriteTimeUtc(page).AddSeconds(1));
  64. build = new BuildCommand(projectDirectory);
  65. result = build.Execute();
  66. // Assert - 2
  67. new FileInfo(generatedDefinitionFile).Should().Exist();
  68. // Definition file remains unchanged.
  69. Assert.Equal(generatedDefinitionThumbprint, FileThumbPrint.Create(generatedDefinitionFile));
  70. new FileInfo(generatedFile).Should().Exist();
  71. // Generated file should change and include the new content.
  72. Assert.NotEqual(generatedFileThumbprint, FileThumbPrint.Create(generatedFile));
  73. new FileInfo(generatedFile).Should().Contain(updatedContent);
  74. // TagHelper cache should remain unchanged.
  75. Assert.Equal(definitionThumbprint, FileThumbPrint.Create(tagHelperOutputCache));
  76. }
  77. [Fact(Skip = "https://github.com/dotnet/aspnetcore/issues/28780")]
  78. public void Build_TouchesUpToDateMarkerFile()
  79. {
  80. var testAsset = "RazorClassLibrary";
  81. var projectDirectory = CreateAspNetSdkTestAsset(testAsset);
  82. // Remove the components so that they don't interfere with these tests
  83. Directory.Delete(Path.Combine(projectDirectory.Path, "Components"), recursive: true);
  84. var build = new BuildCommand(projectDirectory);
  85. build.Execute()
  86. .Should()
  87. .Pass();
  88. string intermediateOutputPath = Path.Combine(build.GetBaseIntermediateDirectory().FullName, "Debug", DefaultTfm);
  89. var classLibraryDll = Path.Combine(intermediateOutputPath, "ClassLibrary.dll");
  90. var classLibraryViewsDll = Path.Combine(intermediateOutputPath, "ClassLibrary.Views.dll");
  91. var markerFile = Path.Combine(intermediateOutputPath, "ClassLibrary.csproj.CopyComplete"); ;
  92. new FileInfo(classLibraryDll).Should().Exist();
  93. new FileInfo(classLibraryViewsDll).Should().Exist();
  94. new FileInfo(markerFile).Should().Exist();
  95. // Gather thumbprints before incremental build.
  96. var classLibraryThumbPrint = FileThumbPrint.Create(classLibraryDll);
  97. var classLibraryViewsThumbPrint = FileThumbPrint.Create(classLibraryViewsDll);
  98. var markerFileThumbPrint = FileThumbPrint.Create(markerFile);
  99. build = new BuildCommand(projectDirectory);
  100. build.Execute()
  101. .Should()
  102. .Pass();
  103. // Verify thumbprint file is unchanged between true incremental builds
  104. Assert.Equal(classLibraryThumbPrint, FileThumbPrint.Create(classLibraryDll));
  105. Assert.Equal(classLibraryViewsThumbPrint, FileThumbPrint.Create(classLibraryViewsDll));
  106. // In practice, this should remain unchanged. However, since our tests reference
  107. // binaries from other projects, this file gets updated by Microsoft.Common.targets
  108. Assert.NotEqual(markerFileThumbPrint, FileThumbPrint.Create(markerFile));
  109. // Change a cshtml file and verify ClassLibrary.Views.dll and marker file are updated
  110. File.AppendAllText(Path.Combine(projectDirectory.Path, "Views", "_ViewImports.cshtml"), Environment.NewLine);
  111. build = new BuildCommand(projectDirectory);
  112. build.Execute()
  113. .Should()
  114. .Pass();
  115. Assert.Equal(classLibraryThumbPrint, FileThumbPrint.Create(classLibraryDll));
  116. Assert.NotEqual(classLibraryViewsThumbPrint, FileThumbPrint.Create(classLibraryViewsDll));
  117. Assert.NotEqual(markerFileThumbPrint, FileThumbPrint.Create(markerFile));
  118. }
  119. private IDisposable LockDirectory(string directory)
  120. {
  121. var disposables = new List<IDisposable>();
  122. foreach (var file in Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories))
  123. {
  124. disposables.Add(File.Open(file, FileMode.Open, FileAccess.Read, FileShare.None));
  125. }
  126. var disposable = new Mock<IDisposable>();
  127. disposable.Setup(d => d.Dispose())
  128. .Callback(() => disposables.ForEach(d => d.Dispose()));
  129. return disposable.Object;
  130. }
  131. }
  132. }