/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs
1 209 строк
32 KB
Jason Malinowski
Switch code that was using Assert.True/False as a replacement for .Fail
21 апр 2026, 23:28
21 апр 2026, 23:28
3bf59ba
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.Editor.CSharp.SplitStringLiteral; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Text.Operations; using Roslyn.Test.Utilities; using Xunit; using IndentStyle = Microsoft.CodeAnalysis.Formatting.FormattingOptions2.IndentStyle; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SplitStringLiteral; [UseExportProvider] [Trait(Traits.Feature, Traits.Features.SplitStringLiteral)] public sealed class SplitStringLiteralCommandHandlerTests { /// <summary> /// verifyUndo is needed because of https://github.com/dotnet/roslyn/issues/28033 Most tests will continue to /// verifyUndo, but select tests will skip it due to this known test infrastructure issue. This bug does not /// represent a product failure. /// </summary> private static void TestWorker( string inputMarkup, string? expectedOutputMarkup, Action callback, bool verifyUndo = true, IndentStyle indentStyle = IndentStyle.Smart, bool useTabs = false, string? endOfLine = null) { var workspaceXml = $""" <Workspace> <Project Language="C#"> <Document Normalize="{endOfLine is null}">{(endOfLine is null ? inputMarkup : inputMarkup.ReplaceLineEndings(endOfLine))}</Document> </Project> </Workspace> """; using var workspace = EditorTestWorkspace.Create(workspaceXml); if (useTabs && expectedOutputMarkup != null) { Assert.Contains("\t", expectedOutputMarkup); } var editorOptionsFactory = workspace.GetService<IEditorOptionsFactoryService>(); var document = workspace.Documents.Single(); var view = document.GetTextView(); var textBuffer = view.TextBuffer; var options = editorOptionsFactory.GetOptions(textBuffer); options.SetOptionValue(DefaultOptions.ConvertTabsToSpacesOptionId, !useTabs); options.SetOptionValue(DefaultOptions.TabSizeOptionId, 4); options.SetOptionValue(DefaultOptions.IndentStyleId, indentStyle.ToEditorIndentStyle()); if (endOfLine != null) options.SetOptionValue(DefaultOptions.NewLineCharacterOptionId, endOfLine); // Remove once https://github.com/dotnet/roslyn/issues/62204 is fixed: workspace.GlobalOptions.SetGlobalOption(IndentationOptionsStorage.SmartIndent, document.Project.Language, indentStyle); var originalSnapshot = textBuffer.CurrentSnapshot; var originalSelections = document.SelectedSpans; // primary caret will be the last one: view.SetMultiSelection(originalSelections.Select(selection => selection.ToSnapshotSpan(originalSnapshot))); // only validate when there is no selected text since the splitter is disabled in that case: if (originalSelections.All(selection => selection.IsEmpty)) { Assert.Equal(originalSelections.Last().Start, view.Caret.Position.BufferPosition.Position); } var undoHistoryRegistry = workspace.GetService<ITextUndoHistoryRegistry>(); var commandHandler = workspace.ExportProvider.GetCommandHandler<SplitStringLiteralCommandHandler>(nameof(SplitStringLiteralCommandHandler)); if (!commandHandler.ExecuteCommand(new ReturnKeyCommandArgs(view, textBuffer), TestCommandExecutionContext.Create())) { callback(); } if (expectedOutputMarkup != null) { MarkupTestFile.GetSpans(expectedOutputMarkup, out var expectedOutput, out var expectedSpans); Assert.Equal(expectedOutput, textBuffer.CurrentSnapshot.AsText().ToString()); Assert.Equal(expectedSpans.Last().Start, view.Caret.Position.BufferPosition.Position); if (verifyUndo) { // Ensure that after undo we go back to where we were to begin with. var history = undoHistoryRegistry.GetHistory(document.GetTextBuffer()); history.Undo(count: originalSelections.Count); var currentSnapshot = document.GetTextBuffer().CurrentSnapshot; Assert.Equal(originalSnapshot.GetText(), currentSnapshot.GetText()); Assert.Equal(originalSelections.Last().Start, view.Caret.Position.BufferPosition.Position); } } } /// <summary> /// verifyUndo is needed because of https://github.com/dotnet/roslyn/issues/28033 Most tests will continue to /// verifyUndo, but select tests will skip it due to this known test infrastructure issue. This bug does not /// represent a product failure. /// </summary> private static void TestHandled( [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string inputMarkup, [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expectedOutputMarkup, bool verifyUndo = true, IndentStyle indentStyle = IndentStyle.Smart, bool useTabs = false, string? endOfLine = null) { TestWorker( inputMarkup, expectedOutputMarkup, callback: () => { Assert.Fail("Should not reach here."); }, verifyUndo, indentStyle, useTabs, endOfLine); } private static void TestNotHandled( [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string inputMarkup) { var notHandled = false; TestWorker( inputMarkup, null, callback: () => { notHandled = true; }); Assert.True(notHandled); } [WpfFact] public void TestMissingBeforeString() => TestNotHandled( """ class C { void M() { var v = [||]""; } } """); [WpfFact] public void TestMissingBeforeUtf8String() => TestNotHandled( """ class C { void M() { var v = [||]""u8; } } """); [WpfFact] public void TestMissingBeforeInterpolatedString() => TestNotHandled( """ class C { void M() { var v = [||]$""; } } """); [WpfFact] public void TestMissingAfterString_1() => TestNotHandled( """ class C { void M() { var v = ""[||]; } } """); [WpfFact] public void TestMissingAfterString_2() => TestNotHandled( """ class C { void M() { var v = "" [||]; } } """); [WpfFact] public void TestMissingAfterString_3() => TestNotHandled( """ class C { void M() { var v = ""[||] } } """); [WpfFact] public void TestMissingAfterString_4() => TestNotHandled( """ class C { void M() { var v = "" [||] } } """); [WpfFact] public void TestMissingAfterInterpolatedString_1() => TestNotHandled( """ class C { void M() { var v = $""[||]; } } """); [WpfFact] public void TestMissingAfterInterpolatedString_2() => TestNotHandled( """ class C { void M() { var v = $"" [||]; } } """); [WpfFact] public void TestMissingAfterInterpolatedString_3() => TestNotHandled( """ class C { void M() { var v = $""[||] } } """); [WpfFact] public void TestMissingAfterInterpolatedString_4() => TestNotHandled( """ class C { void M() { var v = $"" [||] } } """); [WpfFact] public void TestMissingAfterUtf8String_1() => TestNotHandled( """ class C { void M() { var v = ""[||]u8; } } """); [WpfFact] public void TestMissingAfterUtf8String_2() => TestNotHandled( """ class C { void M() { var v = ""u8[||]; } } """); [WpfFact] public void TestMissingAfterUtf8String_3() => TestNotHandled( """ class C { void M() { var v = ""u8[||] } } """); [WpfFact] public void TestMissingAfterUtf8String_4() => TestNotHandled( """ class C { void M() { var v = $""u8 [||] } } """); [WpfFact] public void TestMissingAfterUtf8String_5() => TestNotHandled( """ class C { void M() { var v = ""u[||]8; } } """); [WpfFact] public void TestMissingInVerbatimString() => TestNotHandled( """ class C { void M() { var v = @"a[||]b"; } } """); [WpfFact] public void TestMissingInUtf8VerbatimString() => TestNotHandled( """ class C { void M() { var v = @"a[||]b"u8; } } """); [WpfFact] public void TestMissingInInterpolatedVerbatimString() => TestNotHandled( """ class C { void M() { var v = $@"a[||]b"; } } """); [WpfFact] public void TestInEmptyString() { // Do not verifyUndo because of https://github.com/dotnet/roslyn/issues/28033 // When that issue is fixed, we can reenable verifyUndo TestHandled( """ class C { void M() { var v = "[||]"; } } """, """ class C { void M() { var v = "" + "[||]"; } } """, verifyUndo: false); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/41322")] public void TestInEmptyString_LF() { // Do not verifyUndo because of https://github.com/dotnet/roslyn/issues/28033 // When that issue is fixed, we can reenable verifyUndo TestHandled( "class C\n{\n void M()\n {\n var v = \"[||]\";\n }\n}", "class C\n{\n void M()\n {\n var v = \"\" +\n \"[||]\";\n }\n}", verifyUndo: false, endOfLine: "\n"); } [WpfFact] public void TestInEmptyString_BlockIndent() { // Do not verifyUndo because of https://github.com/dotnet/roslyn/issues/28033 // When that issue is fixed, we can reenable verifyUndo TestHandled( """ class C { void M() { var v = "[||]"; } } """, """ class C { void M() { var v = "" + "[||]"; } } """, verifyUndo: false, IndentStyle.Block); } [WpfFact] public void TestInEmptyString_NoneIndent() { // Do not verifyUndo because of https://github.com/dotnet/roslyn/issues/28033 // When that issue is fixed, we can reenable verifyUndo TestHandled( """ class C { void M() { var v = "[||]"; } } """, """ class C { void M() { var v = "" + "[||]"; } } """, verifyUndo: false, IndentStyle.None); } [WpfFact] public void TestInEmptyInterpolatedString() => TestHandled( """ class C { void M() { var v = $"[||]"; } } """, """ class C { void M() { var v = $"" + $"[||]"; } } """); [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/41322")] public void TestInEmptyInterpolatedString_LF() => TestHandled( "class C\n{\n void M()\n {\n var v = $\"[||]\";\n }\n}", "class C\n{\n void M()\n {\n var v = $\"\" +\n $\"[||]\";\n }\n}", endOfLine: "\n"); [WpfFact] public void TestInEmptyInterpolatedString_BlockIndent() => TestHandled( """ class C { void M() { var v = $"[||]"; } } """, """ class C { void M() { var v = $"" + $"[||]"; } } """, indentStyle: IndentStyle.Block); [WpfFact] public void TestInEmptyInterpolatedString_NoneIndent() => TestHandled( """ class C { void M() { var v = $"[||]"; } } """, """ class C { void M() { var v = $"" + $"[||]"; } } """, indentStyle: IndentStyle.None); [WpfFact] public void TestSimpleString1() => TestHandled( """ class C { void M() { var v = "now is [||]the time"; } } """, """ class C { void M() { var v = "now is " + "[||]the time"; } } """); [WpfFact] public void TestUtf8String_1() => TestHandled( """ class C { void M() { var v = "now is [||]the time"u8; } } """, """ class C { void M() { var v = "now is "u8 + "[||]the time"u8; } } """); [WpfFact] public void TestUtf8String_2() => TestHandled( """ class C { void M() { var v = "now is [||]the time"U8; } } """, """ class C { void M() { var v = "now is "U8 + "[||]the time"U8; } } """); [WpfFact] public void TestInterpolatedString1() => TestHandled( """ class C { void M() { var v = $"now is [||]the { 1 + 2 } time for { 3 + 4 } all good men"; } } """, """ class C { void M() { var v = $"now is " + $"[||]the { 1 + 2 } time for { 3 + 4 } all good men"; } } """); [WpfFact] public void TestInterpolatedString2() => TestHandled( """ class C { void M() { var v = $"now is the [||]{ 1 + 2 } time for { 3 + 4 } all good men"; } } """, """ class C { void M() { var v = $"now is the " + $"[||]{ 1 + 2 } time for { 3 + 4 } all good men"; } } """); [WpfFact] public void TestInterpolatedString3() => TestHandled( """ class C { void M() { var v = $"now is the { 1 + 2 }[||] time for { 3 + 4 } all good men"; } } """, """ class C { void M() { var v = $"now is the { 1 + 2 }" + $"[||] time for { 3 + 4 } all good men"; } } """); [WpfFact] public void TestMissingInInterpolation1() => TestNotHandled( """ class C { void M() { var v = $"now is the {[||] 1 + 2 } time for { 3 + 4 } all good men"; } } """); [WpfFact] public void TestMissingInInterpolation2() => TestNotHandled( """ class C { void M() { var v = $"now is the { 1 + 2 [||]} time for { 3 + 4 } all good men"; } } """); [WpfFact] public void TestSelection() => TestNotHandled( """ class C { void M() { var v = "now is [|the|] time"; } } """); [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/20258")] public void TestBeforeEndQuote1() { // Do not verifyUndo because of https://github.com/dotnet/roslyn/issues/28033 // When that issue is fixed, we can reenable verifyUndo TestHandled( """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}[||]" + $"{args[1]}" + $"{args[2]}"; var str2 = "string1" + "string2" + "string3"; } } """, """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"[||]" + $"{args[1]}" + $"{args[2]}"; var str2 = "string1" + "string2" + "string3"; } } """, verifyUndo: false); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/20258")] public void TestBeforeEndQuote2() { // Do not verifyUndo because of https://github.com/dotnet/roslyn/issues/28033 // When that issue is fixed, we can reenable verifyUndo TestHandled( """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"{args[1]}[||]" + $"{args[2]}"; var str2 = "string1" + "string2" + "string3"; } } """, """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"{args[1]}" + $"[||]" + $"{args[2]}"; var str2 = "string1" + "string2" + "string3"; } } """, verifyUndo: false); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/20258")] public void TestBeforeEndQuote3() { // Do not verifyUndo because of https://github.com/dotnet/roslyn/issues/28033 // When that issue is fixed, we can reenable verifyUndo TestHandled( """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"{args[1]}" + $"{args[2]}[||]"; var str2 = "string1" + "string2" + "string3"; } } """, """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"{args[1]}" + $"{args[2]}" + $"[||]"; var str2 = "string1" + "string2" + "string3"; } } """, verifyUndo: false); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/20258")] public void TestBeforeEndQuote4() { // Do not verifyUndo because of https://github.com/dotnet/roslyn/issues/28033 // When that issue is fixed, we can reenable verifyUndo TestHandled( """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"{args[1]}" + $"{args[2]}"; var str2 = "string1[||]" + "string2" + "string3"; } } """, """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"{args[1]}" + $"{args[2]}"; var str2 = "string1" + "[||]" + "string2" + "string3"; } } """, verifyUndo: false); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/20258")] public void TestBeforeEndQuote5() { // Do not verifyUndo because of https://github.com/dotnet/roslyn/issues/28033 // When that issue is fixed, we can reenable verifyUndo TestHandled( """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"{args[1]}" + $"{args[2]}"; var str2 = "string1" + "string2[||]" + "string3"; } } """, """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"{args[1]}" + $"{args[2]}"; var str2 = "string1" + "string2" + "[||]" + "string3"; } } """, verifyUndo: false); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/20258")] public void TestBeforeEndQuote6() { // Do not verifyUndo because of https://github.com/dotnet/roslyn/issues/28033 // When that issue is fixed, we can reenable verifyUndo TestHandled( """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"{args[1]}" + $"{args[2]}"; var str2 = "string1" + "string2" + "string3[||]"; } } """, """ class Program { static void Main(string[] args) { var str = $"somestring { args[0]}" + $"{args[1]}" + $"{args[2]}"; var str2 = "string1" + "string2" + "string3" + "[||]"; } } """, verifyUndo: false); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/39040")] public void TestMultiCaretSingleLine() => TestHandled( """ class C { void M() { var v = "now is [||]the ti[||]me"; } } """, """ class C { void M() { var v = "now is " + "[||]the ti" + "[||]me"; } } """); [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/39040")] public void TestMultiCaretMultiLines() => TestHandled( """ class C { string s = "hello w[||]orld"; void M() { var v = "now is [||]the ti[||]me"; } } """, """ class C { string s = "hello w" + "[||]orld"; void M() { var v = "now is " + "[||]the ti" + "[||]me"; } } """); [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/39040")] public void TestMultiCaretInterpolatedString() => TestHandled( """ class C { string s = "hello w[||]orld"; void M() { var location = "world"; var s = $"H[||]ello {location}!"; } } """, """ class C { string s = "hello w" + "[||]orld"; void M() { var location = "world"; var s = $"H" + $"[||]ello {location}!"; } } """); [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/64812")] public void TestMultiCaretPositionsAreAllSet() { var inputMarkup = """ class C { void M() { var v = "now is [||]the ti[||]me"; } } """; var expectedMarkup = """ class C { void M() { var v = "now is " + "[||]the ti" + "[||]me"; } } """; TestHandled(inputMarkup, expectedMarkup); } [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/40277")] public void TestInStringWithKeepTabsEnabled1() => TestHandled( """ class C { void M() { var s = "Hello [||]world"; } } """, """ class C { void M() { var s = "Hello " + "[||]world"; } } """, useTabs: true); [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/40277")] public void TestInStringWithKeepTabsEnabled2() => TestHandled( """ class C { void M() { var s = "Hello " + "there [||]world"; } } """, """ class C { void M() { var s = "Hello " + "there " + "[||]world"; } } """, useTabs: true); [WpfFact] public void TestMissingInRawStringLiteral() => TestNotHandled( """" class C { void M() { var v = """Hello[||]there world """; } } """"); [WpfFact] public void TestMissingInRawStringLiteralInterpolation() => TestNotHandled( """" class C { void M() { var v = $"""Hello[||]there world """; } } """"); [WpfFact] public void TestMissingInRawStringLiteralInterpolation_MultiBrace() => TestNotHandled( """" class C { void M() { var v = ${|#0:|}$"""Hello[||]there world """; } } """"); [WpfFact] public void TestMissingInRawUtf8StringLiteral() => TestNotHandled( """" class C { void M() { var v = """Hello[||]there world """u8; } } """"); }