Avoid errors when generating baselines

- ignore missing expected output resources in this mode
- update `BaselineWriter` to avoid `IOException`s (file access issues)
 - serialize file operations
 - one file handle per file
- repeatedly `rm TestFiles/CodeGenerator/Output/*; `dnx . test`; no errors

also
- add generation of design-time line mappings
- fix ChunkGenerator -> CodeGenerator typo in path names
- update files only if they have changed
 - new `TestFiles.Exists()` method to support this check
- do not check results in `CSharpCodeBuilderTests` if `GENERATE_BASELINES` set

nits:
- update BOM in CodeBlockWithTextElement.cs to avoid future `git diff`s
- use more `var` and improve variable names in `TestFile`
- wrap long lines
This commit is contained in:
Doug Bunting 2015-05-30 22:14:34 -07:00
parent 1e5ad1154d
commit d545f47fe4
5 changed files with 155 additions and 40 deletions

View File

@ -2,6 +2,9 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if !DNXCORE50
#if GENERATE_BASELINES
using System;
#endif
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.Test;
using Microsoft.AspNet.Razor.Test.Generator;
@ -29,18 +32,37 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
codeGeneratorContext.ChunkTreeBuilder.AddUsingChunk("FakeNamespace1", syntaxTreeNode.Object);
codeGeneratorContext.ChunkTreeBuilder.AddUsingChunk("FakeNamespace2.SubNamespace", syntaxTreeNode.Object);
var codeGenerator = new CodeGenTestCodeGenerator(codeGeneratorContext);
var testFile = TestFile.Create("TestFiles/CodeGenerator/Output/CSharpCodeGenerator.cs");
string expectedOutput;
#if GENERATE_BASELINES
if (testFile.Exists())
{
expectedOutput = testFile.ReadAllText();
}
else
{
expectedOutput = null;
}
#else
expectedOutput = testFile.ReadAllText();
#endif
// Act
var result = codeGenerator.Generate();
BaselineWriter.WriteBaseline(
@"test\Microsoft.AspNet.Razor.Test\TestFiles\CodeGenerator\Output\CSharpCodeGenerator.cs",
result.Code);
var expectedOutput = TestFile.Create("TestFiles/CodeGenerator/Output/CSharpCodeGenerator.cs").ReadAllText();
// Assert
#if GENERATE_BASELINES
// Update baseline files if files do not already match.
if (!string.Equals(expectedOutput, result.Code, StringComparison.Ordinal))
{
BaselineWriter.WriteBaseline(
@"test\Microsoft.AspNet.Razor.Test\TestFiles\CodeGenerator\Output\CSharpCodeGenerator.cs",
result.Code);
}
#else
Assert.Equal(expectedOutput, result.Code);
#endif
}
}
}

View File

@ -127,9 +127,22 @@ namespace Microsoft.AspNet.Razor.Test.Generator
}
var sourceLocation = string.Format("TestFiles/CodeGenerator/Source/{0}.{1}", name, FileExtension);
var expectedOutput = TestFile
.Create(string.Format("TestFiles/CodeGenerator/Output/{0}.{1}", baselineName, BaselineExtension))
.ReadAllText();
var testFile = TestFile
.Create(string.Format("TestFiles/CodeGenerator/Output/{0}.{1}", baselineName, BaselineExtension));
string expectedOutput;
#if GENERATE_BASELINES
if (testFile.Exists())
{
expectedOutput = testFile.ReadAllText();
}
else
{
expectedOutput = null;
}
#else
expectedOutput = testFile.ReadAllText();
#endif
// Set up the host and engine
var host = CreateHost();
@ -179,17 +192,20 @@ namespace Microsoft.AspNet.Razor.Test.Generator
rootNamespace: TestRootNamespaceName,
sourceFileName: sourceFileName);
}
// Only called if GENERATE_BASELINES is set, otherwise compiled out.
BaselineWriter.WriteBaseline(
string.Format(
@"test\Microsoft.AspNet.Razor.Test\TestFiles\ChunkGenerator\Output\{0}.{1}",
baselineName,
BaselineExtension),
results.GeneratedCode);
#if !GENERATE_BASELINES
var textOutput = results.GeneratedCode;
#if GENERATE_BASELINES
var outputFile = string.Format(
@"test\Microsoft.AspNet.Razor.Test\TestFiles\CodeGenerator\Output\{0}.{1}",
baselineName,
BaselineExtension);
// Update baseline files if files do not already match.
if (!string.Equals(expectedOutput, textOutput, StringComparison.Ordinal))
{
BaselineWriter.WriteBaseline(outputFile, textOutput);
}
#else
if (onResults != null)
{
onResults(results);
@ -216,17 +232,70 @@ namespace Microsoft.AspNet.Razor.Test.Generator
if (expectedDesignTimePragmas != null)
{
Assert.True(results.DesignTimeLineMappings != null); // Guard
Assert.NotNull(results.DesignTimeLineMappings); // Guard
#if GENERATE_BASELINES
if (expectedDesignTimePragmas == null ||
!Enumerable.SequenceEqual(expectedDesignTimePragmas, results.DesignTimeLineMappings))
{
var lineMappingFile = Path.ChangeExtension(outputFile, "lineMappings.cs");
var lineMappingCode = GetDesignTimeLineMappingsCode(results.DesignTimeLineMappings);
BaselineWriter.WriteBaseline(lineMappingFile, lineMappingCode);
}
#else
for (var i = 0; i < expectedDesignTimePragmas.Count && i < results.DesignTimeLineMappings.Count; i++)
{
Assert.Equal(expectedDesignTimePragmas[i], results.DesignTimeLineMappings[i]);
}
Assert.Equal(expectedDesignTimePragmas.Count, results.DesignTimeLineMappings.Count);
#endif
}
}
}
private static string GetDesignTimeLineMappingsCode(IList<LineMapping> designTimeLineMappings)
{
var lineMappings = new StringBuilder();
lineMappings.AppendLine($"// !!! Do not check in. Instead paste content into test method. !!!");
lineMappings.AppendLine();
var indent = " ";
lineMappings.AppendLine($"{ indent }var expectedLineMappings = new[]");
lineMappings.AppendLine($"{ indent }{{");
foreach (var lineMapping in designTimeLineMappings)
{
var innerIndent = indent + " ";
var documentLocation = lineMapping.DocumentLocation;
var generatedLocation = lineMapping.GeneratedLocation;
lineMappings.AppendLine($"{ innerIndent }BuildLineMapping(");
innerIndent += " ";
lineMappings.AppendLine($"{ innerIndent }documentAbsoluteIndex: { documentLocation.AbsoluteIndex },");
lineMappings.AppendLine($"{ innerIndent }documentLineIndex: { documentLocation.LineIndex },");
if (documentLocation.CharacterIndex != generatedLocation.CharacterIndex)
{
lineMappings.AppendLine($"{ innerIndent }documentCharacterOffsetIndex: { documentLocation.CharacterIndex },");
}
lineMappings.AppendLine($"{ innerIndent }generatedAbsoluteIndex: { generatedLocation.AbsoluteIndex },");
lineMappings.AppendLine($"{ innerIndent }generatedLineIndex: { generatedLocation.LineIndex },");
if (documentLocation.CharacterIndex != generatedLocation.CharacterIndex)
{
lineMappings.AppendLine($"{ innerIndent }generatedCharacterOffsetIndex: { generatedLocation.CharacterIndex },");
}
else
{
lineMappings.AppendLine($"{ innerIndent }characterOffsetIndex: { generatedLocation.CharacterIndex },");
}
lineMappings.AppendLine($"{ innerIndent }contentLength: { generatedLocation.ContentLength }),");
}
lineMappings.AppendLine($"{ indent }}};");
return lineMappings.ToString();
}
private void VerifyNoBrokenEndOfLines(string text)
{
for (int i = 0; i < text.Length; i++)

View File

@ -1,4 +1,4 @@
#pragma checksum "CodeBlockWithTextElement.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "13e48ff59aab8106ceb68dd4a10b0bdf10c322fc"
#pragma checksum "CodeBlockWithTextElement.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "13e48ff59aab8106ceb68dd4a10b0bdf10c322fc"
namespace TestOutput
{
using System;

View File

@ -8,19 +8,26 @@ namespace Microsoft.AspNet.Razor.Test.Utils
{
public static class BaselineWriter
{
private static object baselineLock = new object();
[Conditional("GENERATE_BASELINES")]
public static void WriteBaseline(string baselineFile, string output)
{
var root = RecursiveFind("Razor.sln", Path.GetFullPath("."));
var baselinePath = Path.Combine(root, baselineFile);
// Update baseline
// IMPORTANT! Replace this path with the local path on your machine to the baseline files!
if (File.Exists(baselinePath))
// Serialize writes to minimize contention for file handles and directory access.
lock (baselineLock)
{
File.Delete(baselinePath);
// Update baseline
using (var stream = File.Open(baselinePath, FileMode.Create, FileAccess.Write))
{
using (var writer = new StreamWriter(stream))
{
writer.Write(output);
}
}
}
File.WriteAllText(baselinePath, output.ToString());
}
private static string RecursiveFind(string path, string start)

View File

@ -1,6 +1,7 @@
// 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.Reflection;
using Xunit;
@ -9,16 +10,15 @@ namespace Microsoft.AspNet.Razor.Test
{
public class TestFile
{
public const string ResourceNameFormat = "{0}.TestFiles.{1}";
public TestFile(string resourceName, Assembly assembly)
{
Assembly = assembly;
ResourceName = Assembly.GetName().Name + "." + resourceName.Replace('/', '.');
}
public Assembly Assembly { get; }
public string ResourceName { get; }
public Assembly Assembly { get; set; }
public TestFile(string resName, Assembly asm)
{
Assembly = asm;
ResourceName = Assembly.GetName().Name + "." + resName.Replace('/', '.');
}
public static TestFile Create(string localResourceName)
{
@ -27,27 +27,44 @@ namespace Microsoft.AspNet.Razor.Test
public Stream OpenRead()
{
var strm = Assembly.GetManifestResourceStream(ResourceName);
if (strm == null)
var stream = Assembly.GetManifestResourceStream(ResourceName);
if (stream == null)
{
Assert.True(false, string.Format("Manifest resource: {0} not found", ResourceName));
}
return strm;
return stream;
}
public bool Exists()
{
var resourceNames = Assembly.GetManifestResourceNames();
foreach (var resourceName in resourceNames)
{
// Resource names are case-sensitive.
if (string.Equals(ResourceName, resourceName, StringComparison.Ordinal))
{
return true;
}
}
return false;
}
public byte[] ReadAllBytes()
{
using (Stream stream = OpenRead())
using (var stream = OpenRead())
{
byte[] buffer = new byte[stream.Length];
var buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
return buffer;
}
}
public string ReadAllText()
{
using (StreamReader reader = new StreamReader(OpenRead()))
using (var reader = new StreamReader(OpenRead()))
{
// The .Replace() calls normalize line endings, in case you get \n instead of \r\n
// since all the unit tests rely on the assumption that the files will have \r\n endings.
@ -66,9 +83,9 @@ namespace Microsoft.AspNet.Razor.Test
Directory.CreateDirectory(directory);
}
using (Stream outStream = File.Create(filePath))
using (var outStream = File.Create(filePath))
{
using (Stream inStream = OpenRead())
using (var inStream = OpenRead())
{
inStream.CopyTo(outStream);
}