protected void AssertCSharpDocumentMatchesBaseline(RazorCodeDocument codeDocument) { var document = codeDocument.GetCSharpDocument(); // Normalize newlines to match those in the baseline. var actualCode = document.GeneratedCode.Replace("\r", "").Replace("\n", "\r\n"); var baselineFilePath = GetBaselineFilePath(codeDocument, ".codegen.cs"); var baselineDiagnosticsFilePath = GetBaselineFilePath(codeDocument, ".diagnostics.txt"); var baselineMappingsFilePath = GetBaselineFilePath(codeDocument, ".mappings.txt"); var serializedMappings = SourceMappingsSerializer.Serialize(document, codeDocument.Source); if (GenerateBaselines) { var baselineFullPath = Path.Combine(TestProjectRoot, baselineFilePath); Directory.CreateDirectory(Path.GetDirectoryName(baselineFullPath)); WriteBaseline(actualCode, baselineFullPath); var baselineDiagnosticsFullPath = Path.Combine(TestProjectRoot, baselineDiagnosticsFilePath); var lines = document.Diagnostics.Select(RazorDiagnosticSerializer.Serialize).ToArray(); if (lines.Any()) { WriteBaseline(lines, baselineDiagnosticsFullPath); } else if (File.Exists(baselineDiagnosticsFullPath)) { File.Delete(baselineDiagnosticsFullPath); } var baselineMappingsFullPath = Path.Combine(TestProjectRoot, baselineMappingsFilePath); var text = SourceMappingsSerializer.Serialize(document, codeDocument.Source); if (!string.IsNullOrEmpty(text)) { WriteBaseline(text, baselineMappingsFullPath); } else if (File.Exists(baselineMappingsFullPath)) { File.Delete(baselineMappingsFullPath); } return; } var codegenFile = TestFile.Create(baselineFilePath, GetType().Assembly); if (!codegenFile.Exists()) { throw new XunitException($"The resource {baselineFilePath} was not found."); } var baseline = codegenFile.ReadAllText(); Assert.Equal(baseline, actualCode); var baselineDiagnostics = string.Empty; var diagnosticsFile = TestFile.Create(baselineDiagnosticsFilePath, GetType().Assembly); if (diagnosticsFile.Exists()) { baselineDiagnostics = diagnosticsFile.ReadAllText(); } var actualDiagnostics = string.Concat(document.Diagnostics.Select(d => RazorDiagnosticSerializer.Serialize(d) + "\r\n")); Assert.Equal(baselineDiagnostics, actualDiagnostics); var baselineMappings = string.Empty; var mappingsFile = TestFile.Create(baselineMappingsFilePath, GetType().Assembly); if (mappingsFile.Exists()) { baselineMappings = mappingsFile.ReadAllText(); } var actualMappings = SourceMappingsSerializer.Serialize(document, codeDocument.Source); actualMappings = actualMappings.Replace("\r", "").Replace("\n", "\r\n"); Assert.Equal(baselineMappings, actualMappings); }
protected void AssertSourceMappingsMatchBaseline(RazorCodeDocument codeDocument) { if (FileName == null) { var message = $"{nameof(AssertSourceMappingsMatchBaseline)} should only be called from an integration test ({nameof(FileName)} is null)."; throw new InvalidOperationException(message); } var csharpDocument = codeDocument.GetCSharpDocument(); Assert.NotNull(csharpDocument); var baselineFileName = Path.ChangeExtension(FileName, ".mappings.txt"); var serializedMappings = SourceMappingsSerializer.Serialize(csharpDocument, codeDocument.Source); if (GenerateBaselines) { var baselineFullPath = Path.Combine(TestProjectRoot, baselineFileName); File.WriteAllText(baselineFullPath, serializedMappings); return; } var testFile = TestFile.Create(baselineFileName, GetType().GetTypeInfo().Assembly); if (!testFile.Exists()) { throw new XunitException($"The resource {baselineFileName} was not found."); } var baseline = testFile.ReadAllText(); // Normalize newlines to match those in the baseline. var actualBaseline = serializedMappings.Replace("\r", "").Replace("\n", "\r\n"); Assert.Equal(baseline, actualBaseline); var syntaxTree = codeDocument.GetSyntaxTree(); var visitor = new CodeSpanVisitor(); visitor.Visit(syntaxTree.Root); var charBuffer = new char[codeDocument.Source.Length]; codeDocument.Source.CopyTo(0, charBuffer, 0, codeDocument.Source.Length); var sourceContent = new string(charBuffer); var spans = visitor.CodeSpans; for (var i = 0; i < spans.Count; i++) { var span = spans[i]; var sourceSpan = span.GetSourceSpan(codeDocument.Source); var expectedSpan = sourceContent.Substring(sourceSpan.AbsoluteIndex, sourceSpan.Length); // See #2593 if (string.IsNullOrWhiteSpace(expectedSpan)) { // For now we don't verify whitespace inside of a directive. We know that directives cheat // with how they bound whitespace/C#/markup to make completion work. if (span.FirstAncestorOrSelf <RazorDirectiveSyntax>() != null) { continue; } } // See #2594 if (string.Equals("@", expectedSpan)) { // For now we don't verify an escaped transition. In some cases one of the @ tokens in @@foo // will be mapped as C# but will not be present in the output buffer because it's not actually C#. continue; } var found = false; for (var j = 0; j < csharpDocument.SourceMappings.Count; j++) { var mapping = csharpDocument.SourceMappings[j]; if (mapping.OriginalSpan == sourceSpan) { var actualSpan = csharpDocument.GeneratedCode.Substring( mapping.GeneratedSpan.AbsoluteIndex, mapping.GeneratedSpan.Length); if (!string.Equals(expectedSpan, actualSpan, StringComparison.Ordinal)) { throw new XunitException( $"Found the span {sourceSpan} in the output mappings but it contains " + $"'{EscapeWhitespace(actualSpan)}' instead of '{EscapeWhitespace(expectedSpan)}'."); } found = true; break; } } if (!found) { throw new XunitException( $"Could not find the span {sourceSpan} - containing '{EscapeWhitespace(expectedSpan)}' " + $"in the output."); } } }