Skip to content

Commit 758c1f2

Browse files
committed
Add a failing test (and more!)
Starting the basis of a test framework that takes the formatting log files and repros issues. Not sure if this is worth it, or if its better to just use them to isolate the issue and then create a regular test for actual found bug.
1 parent 0d4d79b commit 758c1f2

File tree

5 files changed

+290
-6
lines changed

5 files changed

+290
-6
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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+
4+
using System.Linq;
5+
using System.Runtime.CompilerServices;
6+
using System.Runtime.Serialization;
7+
using System.Text.Json;
8+
using System.Threading.Tasks;
9+
using Microsoft.AspNetCore.Razor;
10+
using Microsoft.AspNetCore.Razor.Language;
11+
using Microsoft.AspNetCore.Razor.Test.Common;
12+
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features;
13+
using Microsoft.CodeAnalysis.Razor.Formatting;
14+
using Microsoft.CodeAnalysis.Razor.Protocol;
15+
using Microsoft.VisualStudio.Razor.LanguageClient.Cohost;
16+
using Microsoft.VisualStudio.Razor.LanguageClient.Cohost.Formatting;
17+
using Xunit;
18+
using Xunit.Abstractions;
19+
20+
namespace Microsoft.VisualStudio.LanguageServices.Razor.Test.Cohost.Formatting;
21+
22+
/// <summary>
23+
/// Not tests of the formatting log, but tests that use formatting logs sent in
24+
/// by users reporting issues.
25+
/// </summary>
26+
[Collection(HtmlFormattingCollection.Name)]
27+
public class FormattingLogTest(FormattingTestContext context, HtmlFormattingFixture fixture, ITestOutputHelper testOutput)
28+
: FormattingTestBase(context, fixture.Service, testOutput), IClassFixture<FormattingTestContext>
29+
{
30+
[Fact]
31+
[WorkItem("https://github.com/dotnet/vscode-csharp/issues/7264")]
32+
public async Task UnexpectedFalseInIndentBlockOperation()
33+
{
34+
var contents = GetResource("InitialDocument.txt");
35+
var document = CreateProjectAndRazorDocument(contents);
36+
37+
var optionsFile = GetResource("Options.json");
38+
var options = (TempRazorFormattingOptions)JsonSerializer.Deserialize(optionsFile, typeof(TempRazorFormattingOptions), JsonHelpers.JsonSerializerOptions).AssumeNotNull();
39+
40+
var formattingService = (RazorFormattingService)OOPExportProvider.GetExportedValue<IRazorFormattingService>();
41+
formattingService.GetTestAccessor().SetFormattingLoggerFactory(new TestFormattingLoggerFactory(TestOutputHelper));
42+
43+
var htmlChangesFile = GetResource("HtmlChanges.json");
44+
var htmlChanges = JsonSerializer.Deserialize<RazorTextChange[]>(htmlChangesFile, JsonHelpers.JsonSerializerOptions);
45+
var sourceText = await document.GetTextAsync();
46+
var htmlEdits = htmlChanges.Select(c => sourceText.GetTextEdit(c.ToTextChange())).ToArray();
47+
48+
await GetFormattingEditsAsync(document, htmlEdits, span: default, options.CodeBlockBraceOnNextLine, options.InsertSpaces, options.TabSize, options.ToRazorFormattingOptions().CSharpSyntaxFormattingOptions);
49+
}
50+
51+
private string GetResource(string name, [CallerMemberName] string? testName = null)
52+
{
53+
var baselineFileName = $@"TestFiles\FormattingLog\{testName}\{name}";
54+
55+
var testFile = TestFile.Create(baselineFileName, GetType().Assembly);
56+
Assert.True(testFile.Exists());
57+
58+
return testFile.ReadAllText();
59+
}
60+
61+
// HACK: Temporary types for deserializing because RazorCSharpSyntaxFormattingOptions doesn't have a parameterless constructor.
62+
internal class TempRazorFormattingOptions()
63+
{
64+
[DataMember(Order = 0)]
65+
public bool InsertSpaces { get; init; } = true;
66+
[DataMember(Order = 1)]
67+
public int TabSize { get; init; } = 4;
68+
[DataMember(Order = 2)]
69+
public bool CodeBlockBraceOnNextLine { get; init; } = false;
70+
[DataMember(Order = 3)]
71+
public TempRazorCSharpSyntaxFormattingOptions? CSharpSyntaxFormattingOptions { get; init; }
72+
73+
public RazorFormattingOptions ToRazorFormattingOptions()
74+
=> new()
75+
{
76+
InsertSpaces = InsertSpaces,
77+
TabSize = TabSize,
78+
CodeBlockBraceOnNextLine = CodeBlockBraceOnNextLine,
79+
CSharpSyntaxFormattingOptions = CSharpSyntaxFormattingOptions is not null
80+
? new RazorCSharpSyntaxFormattingOptions(
81+
CSharpSyntaxFormattingOptions.Spacing,
82+
CSharpSyntaxFormattingOptions.SpacingAroundBinaryOperator,
83+
CSharpSyntaxFormattingOptions.NewLines,
84+
CSharpSyntaxFormattingOptions.LabelPositioning,
85+
CSharpSyntaxFormattingOptions.Indentation,
86+
CSharpSyntaxFormattingOptions.WrappingKeepStatementsOnSingleLine,
87+
CSharpSyntaxFormattingOptions.WrappingPreserveSingleLine,
88+
CSharpSyntaxFormattingOptions.NamespaceDeclarations,
89+
CSharpSyntaxFormattingOptions.PreferTopLevelStatements,
90+
CSharpSyntaxFormattingOptions.CollectionExpressionWrappingLength)
91+
: RazorCSharpSyntaxFormattingOptions.Default
92+
};
93+
}
94+
95+
[DataContract]
96+
internal sealed record class TempRazorCSharpSyntaxFormattingOptions(
97+
[property: DataMember] RazorSpacePlacement Spacing,
98+
[property: DataMember] RazorBinaryOperatorSpacingOptions SpacingAroundBinaryOperator,
99+
[property: DataMember] RazorNewLinePlacement NewLines,
100+
[property: DataMember] RazorLabelPositionOptions LabelPositioning,
101+
[property: DataMember] RazorIndentationPlacement Indentation,
102+
[property: DataMember] bool WrappingKeepStatementsOnSingleLine,
103+
[property: DataMember] bool WrappingPreserveSingleLine,
104+
[property: DataMember] RazorNamespaceDeclarationPreference NamespaceDeclarations,
105+
[property: DataMember] bool PreferTopLevelStatements,
106+
[property: DataMember] int CollectionExpressionWrappingLength)
107+
{
108+
public TempRazorCSharpSyntaxFormattingOptions()
109+
: this(
110+
default,
111+
default,
112+
default,
113+
default,
114+
default,
115+
default,
116+
default,
117+
default,
118+
true,
119+
default)
120+
{
121+
}
122+
}
123+
}

src/Razor/test/Microsoft.VisualStudio.LanguageServices.Razor.Test/Cohost/Formatting/FormattingTestBase.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,11 @@ private protected async Task RunFormattingTestAsync(
7676
var uri = new Uri(document.CreateUri(), $"{document.FilePath}{FeatureOptions.HtmlVirtualDocumentSuffix}");
7777
var htmlEdits = await _htmlFormattingService.GetDocumentFormattingEditsAsync(LoggerFactory, uri, generatedHtml, insertSpaces, tabSize);
7878

79-
var requestInvoker = new TestHtmlRequestInvoker([(Methods.TextDocumentFormattingName, htmlEdits)]);
80-
81-
var clientSettingsManager = new ClientSettingsManager(changeTriggers: []);
82-
clientSettingsManager.Update(clientSettingsManager.GetClientSettings().AdvancedSettings with { CodeBlockBraceOnNextLine = codeBlockBraceOnNextLine });
83-
8479
var span = input.TryGetNamedSpans(string.Empty, out var spans)
8580
? spans.First()
8681
: default;
87-
var edits = await GetFormattingEditsAsync(span, insertSpaces, tabSize, document, requestInvoker, clientSettingsManager, csharpSyntaxFormattingOptions);
82+
83+
var edits = await GetFormattingEditsAsync(document, htmlEdits, span, codeBlockBraceOnNextLine, insertSpaces, tabSize, csharpSyntaxFormattingOptions);
8884

8985
if (edits is null)
9086
{
@@ -99,6 +95,17 @@ private protected async Task RunFormattingTestAsync(
9995
AssertEx.EqualOrDiff(expected, finalText.ToString());
10096
}
10197

98+
private protected async Task<TextEdit[]?> GetFormattingEditsAsync(TextDocument document, TextEdit[]? htmlEdits, TextSpan span, bool codeBlockBraceOnNextLine, bool insertSpaces, int tabSize, RazorCSharpSyntaxFormattingOptions csharpSyntaxFormattingOptions)
99+
{
100+
var requestInvoker = new TestHtmlRequestInvoker([(Methods.TextDocumentFormattingName, htmlEdits)]);
101+
102+
var clientSettingsManager = new ClientSettingsManager(changeTriggers: []);
103+
clientSettingsManager.Update(clientSettingsManager.GetClientSettings().AdvancedSettings with { CodeBlockBraceOnNextLine = codeBlockBraceOnNextLine });
104+
105+
var edits = await GetFormattingEditsAsync(span, insertSpaces, tabSize, document, requestInvoker, clientSettingsManager, csharpSyntaxFormattingOptions);
106+
return edits;
107+
}
108+
102109
private protected async Task RunOnTypeFormattingTestAsync(
103110
TestCode input,
104111
string expected,

0 commit comments

Comments
 (0)