parent
3f948ad3c5
commit
14fc427068
|
|
@ -91,10 +91,34 @@
|
|||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target Name="_RazorCreateSourceHashFiles" DependsOnTargets="_RazorResolveSourceFiles">
|
||||
<PropertyGroup>
|
||||
<_RazorSourcesHashFile>$(IntermediateOutputPath)$(MSBuildProjectName).RazorSourceHash.cache</_RazorSourcesHashFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<Hash ItemsToHash="@(RazorCompile)">
|
||||
<Output TaskParameter="HashResult" PropertyName="_RazorSourcesHash" />
|
||||
</Hash>
|
||||
|
||||
<MakeDir
|
||||
Directories="$(IntermediateOutputPath)"
|
||||
Condition="!Exists('$(IntermediateOutputPath)')" />
|
||||
|
||||
<WriteLinesToFile
|
||||
Lines="$(_RazorSourcesHash)"
|
||||
File="$(_RazorSourcesHashFile)"
|
||||
Overwrite="True"
|
||||
WriteOnlyWhenDifferent="True" />
|
||||
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(_RazorSourcesHashFile)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<Target
|
||||
Name="RazorCoreGenerate"
|
||||
DependsOnTargets="_RazorResolveSourceFiles;_RazorResolveTagHelpers"
|
||||
Inputs="$(MSBuildAllProjects);@(RazorCompile);$(_RazorTagHelperOutputCache)"
|
||||
DependsOnTargets="_RazorResolveSourceFiles;_RazorCreateSourceHashFiles;_RazorResolveTagHelpers"
|
||||
Inputs="$(MSBuildAllProjects);$(_RazorSourcesHashFile);@(RazorCompile);$(_RazorTagHelperOutputCache)"
|
||||
Outputs="@(_RazorGenerated)">
|
||||
|
||||
<RemoveDir
|
||||
|
|
@ -235,4 +259,4 @@
|
|||
DestinationFolder="$(OutDir)"
|
||||
SkipUnchangedFiles="true"/>
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
if (result == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(result));
|
||||
};
|
||||
}
|
||||
|
||||
if (result.ExitCode != 0)
|
||||
{
|
||||
|
|
@ -259,4 +259,4 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
protected override string Heading => $"File: '{FilePath}' was found, but should not exist.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
// 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.Security.Cryptography;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||
{
|
||||
public class FileThumbPrint : IEquatable<FileThumbPrint>
|
||||
{
|
||||
private FileThumbPrint(DateTime lastWriteTimeUtc, string hash)
|
||||
{
|
||||
LastWriteTimeUtc = lastWriteTimeUtc;
|
||||
Hash = hash;
|
||||
}
|
||||
|
||||
public DateTime LastWriteTimeUtc { get; }
|
||||
|
||||
public string Hash { get; }
|
||||
|
||||
public static FileThumbPrint Create(string path)
|
||||
{
|
||||
byte[] hashBytes;
|
||||
using (var sha1 = SHA1.Create())
|
||||
using (var fileStream = File.OpenRead(path))
|
||||
{
|
||||
hashBytes = sha1.ComputeHash(fileStream);
|
||||
}
|
||||
|
||||
var hash = Convert.ToBase64String(hashBytes);
|
||||
var lastWriteTimeUtc = File.GetLastWriteTimeUtc(path);
|
||||
return new FileThumbPrint(lastWriteTimeUtc, hash);
|
||||
}
|
||||
|
||||
public bool Equals(FileThumbPrint other)
|
||||
{
|
||||
return LastWriteTimeUtc == other.LastWriteTimeUtc &&
|
||||
string.Equals(Hash, other.Hash, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => LastWriteTimeUtc.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,12 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||
{
|
||||
|
|
@ -68,5 +70,37 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
|
||||
File.WriteAllText(filePath, content, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Locks all files, discovered at the time of method invocation, under the
|
||||
/// specified <paramref name="directory" /> from reads or writes.
|
||||
/// </summary>
|
||||
public IDisposable LockDirectory(string directory)
|
||||
{
|
||||
directory = Path.Combine(Project.DirectoryPath, directory);
|
||||
var disposables = new List<IDisposable>();
|
||||
foreach (var file in Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
disposables.Add(LockFile(file));
|
||||
}
|
||||
|
||||
var disposable = new Mock<IDisposable>();
|
||||
disposable.Setup(d => d.Dispose())
|
||||
.Callback(() => disposables.ForEach(d => d.Dispose()));
|
||||
|
||||
return disposable.Object;
|
||||
}
|
||||
|
||||
public IDisposable LockFile(string path)
|
||||
{
|
||||
path = Path.Combine(Project.DirectoryPath, path);
|
||||
return File.Open(path, FileMode.Open, FileAccess.Read, FileShare.None);
|
||||
}
|
||||
|
||||
public FileThumbPrint GetThumbPrint(string path)
|
||||
{
|
||||
path = Path.Combine(Project.DirectoryPath, path);
|
||||
return FileThumbPrint.Create(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,18 +2,27 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Moq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||
{
|
||||
internal class ProjectDirectory : IDisposable
|
||||
{
|
||||
#if PRESERVE_WORKING_DIRECTORY
|
||||
public bool PreserveWorkingDirectory { get; set; } = true;
|
||||
#else
|
||||
public bool PreserveWorkingDirectory { get; set; }
|
||||
#endif
|
||||
|
||||
public static ProjectDirectory Create(string projectName)
|
||||
{
|
||||
var destinationPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
var destinationPath = Path.Combine(Path.GetTempPath(), "Razor", Path.GetRandomFileName());
|
||||
Directory.CreateDirectory(destinationPath);
|
||||
|
||||
try
|
||||
|
|
@ -80,6 +89,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
#else
|
||||
#error Unknown Configuration
|
||||
#endif
|
||||
|
||||
var text = $@"
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
|
|
@ -113,7 +123,14 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
|
||||
public void Dispose()
|
||||
{
|
||||
CleanupDirectory(DirectoryPath);
|
||||
if (PreserveWorkingDirectory)
|
||||
{
|
||||
Console.WriteLine($"Skipping deletion of working directory {DirectoryPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
CleanupDirectory(DirectoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanupDirectory(string filePath)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -8,11 +11,13 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
{
|
||||
public class RazorGenerateIntegrationTest : MSBuildIntegrationTestBase
|
||||
{
|
||||
private const string RazorGenerateTarget = "RazorGenerate";
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task RazorGenerate_Success_GeneratesFilesOnDisk()
|
||||
{
|
||||
var result = await DotnetMSBuild("RazorGenerate");
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
|
||||
|
|
@ -52,5 +57,126 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
// The file should still be generated even if we had a Razor syntax error.
|
||||
Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Home", "Index.cs");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task RazorGenerate_BuildsIncrementally()
|
||||
{
|
||||
// Act - 1
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
var generatedFile = Path.Combine(Project.DirectoryPath, RazorIntermediateOutputPath, "Views", "Home", "About.cs");
|
||||
|
||||
// Assert - 1
|
||||
Assert.BuildPassed(result);
|
||||
Assert.FileExists(result, generatedFile);
|
||||
var thumbPrint = GetThumbPrint(generatedFile);
|
||||
|
||||
// Act - 2
|
||||
using (var razorGenDirectoryLock = LockDirectory(RazorIntermediateOutputPath))
|
||||
{
|
||||
result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
}
|
||||
|
||||
// Assert - 2
|
||||
Assert.BuildPassed(result);
|
||||
Assert.FileExists(result, generatedFile);
|
||||
var currentThumbPrint = GetThumbPrint(generatedFile);
|
||||
Assert.Equal(thumbPrint, currentThumbPrint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task RazorGenerate_Rebuilds_IfSourcesAreUpdated()
|
||||
{
|
||||
// Act - 1
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
var file = Path.Combine(Project.DirectoryPath, "Views", "Home", "Contact.cshtml");
|
||||
var generatedFile = Path.Combine(RazorIntermediateOutputPath, "Views", "Home", "Contact.cs");
|
||||
|
||||
// Assert - 1
|
||||
Assert.BuildPassed(result);
|
||||
var fileThumbPrint = GetThumbPrint(generatedFile);
|
||||
|
||||
// Act - 2
|
||||
// Update the source content and build. We should expect the outputs to be regenerated.
|
||||
// Timestamps on xplat are precise only to a second. Add a delay so we can ensure that MSBuild recognizes the
|
||||
// file change. See https://github.com/dotnet/corefx/issues/26024
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
ReplaceContent("Uodated content", file);
|
||||
result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
|
||||
// Assert - 2
|
||||
Assert.BuildPassed(result);
|
||||
var newThumbPrint = GetThumbPrint(generatedFile);
|
||||
Assert.NotEqual(fileThumbPrint, newThumbPrint);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task RazorGenerate_Rebuilds_IfOutputFilesAreMissing()
|
||||
{
|
||||
// Act - 1
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
var file = Path.Combine(Project.DirectoryPath, RazorIntermediateOutputPath, "Views", "Home", "About.cs");
|
||||
|
||||
// Assert - 1
|
||||
Assert.BuildPassed(result);
|
||||
Assert.FileExists(result, file);
|
||||
|
||||
// Act - 2
|
||||
File.Delete(file);
|
||||
result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
|
||||
// Assert - 2
|
||||
Assert.BuildPassed(result);
|
||||
Assert.FileExists(result, file);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task RazorGenerate_Rebuilds_IfInputFilesAreRenamed()
|
||||
{
|
||||
// Act - 1
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
var file = Path.Combine(Project.DirectoryPath, "Views", "Home", "Index.cshtml");
|
||||
var renamed = Path.Combine(Project.DirectoryPath, "Views", "Home", "NewIndex.cshtml");
|
||||
var generated = Path.Combine(RazorIntermediateOutputPath, "Views", "Home", "Index.cs");
|
||||
|
||||
// Assert - 1
|
||||
Assert.BuildPassed(result);
|
||||
Assert.FileExists(result, file);
|
||||
Assert.FileExists(result, generated);
|
||||
|
||||
// Act - 2
|
||||
File.Move(file, renamed);
|
||||
result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
|
||||
// Assert - 2
|
||||
Assert.BuildPassed(result);
|
||||
Assert.FileExists(result, RazorIntermediateOutputPath, "Views", "Home", "NewIndex.cs");
|
||||
Assert.FileDoesNotExist(result, generated);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task RazorGenerate_Rebuilds_IfInputFilesAreDeleted()
|
||||
{
|
||||
// Act - 1
|
||||
var result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
var file = Path.Combine(Project.DirectoryPath, "Views", "Home", "Index.cshtml");
|
||||
var generatedFile = Path.Combine(RazorIntermediateOutputPath, "Views", "Home", "Index.cs");
|
||||
|
||||
// Assert - 1
|
||||
Assert.BuildPassed(result);
|
||||
Assert.FileExists(result, generatedFile);
|
||||
|
||||
// Act - 2
|
||||
File.Delete(file);
|
||||
result = await DotnetMSBuild(RazorGenerateTarget);
|
||||
|
||||
// Assert - 2
|
||||
Assert.BuildPassed(result);
|
||||
Assert.FileDoesNotExist(result, generatedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
-->
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
|
||||
<DefineConstants Condition="'$(PreserveWorkingDirectory)'=='true'">$(DefineConstants);PRESERVE_WORKING_DIRECTORY</DefineConstants>
|
||||
<!-- Copy references locally so that we can use them in the test. -->
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(StandardTestTfms);netstandard2.0</TargetFrameworks>
|
||||
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue