|
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.AddDebuggerDisplay;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AddDebuggerDisplay;
using VerifyCS = CSharpCodeRefactoringVerifier<CSharpAddDebuggerDisplayCodeRefactoringProvider>;
[UseExportProvider]
[Trait(Traits.Feature, Traits.Features.CodeActionsAddDebuggerDisplay)]
public sealed class AddDebuggerDisplayTests
{
[Fact]
public async Task OfferedOnEmptyClass()
{
await VerifyCS.VerifyRefactoringAsync("""
[||]class C
{
}
""", """
using System.Diagnostics;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
class C
{
private string GetDebuggerDisplay()
{
return ToString();
}
}
""");
}
[Fact]
public async Task SupportsConstantInterpolatedStrings()
{
var code = """
[||]class C
{
}
""";
var fixedCode = """
using System.Diagnostics;
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
class C
{
private string GetDebuggerDisplay()
{
return ToString();
}
}
""";
await new VerifyCS.Test()
{
LanguageVersion = LanguageVersion.CSharp12,
TestCode = code,
FixedCode = fixedCode,
}.RunAsync();
}
[Fact]
public async Task OfferedOnEmptyRecord()
{
var code = """
[||]record C;
""";
var fixedCode = """
using System.Diagnostics;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
record C
{
private string GetDebuggerDisplay()
{
return ToString();
}
}
""";
await new VerifyCS.Test()
{
LanguageVersion = LanguageVersion.CSharp9,
ReferenceAssemblies = ReferenceAssemblies.Net.Net50,
TestCode = code,
FixedCode = fixedCode,
}.RunAsync();
}
[Fact]
public async Task OfferedOnEmptyStruct()
{
await VerifyCS.VerifyRefactoringAsync("""
[||]struct Foo
{
}
""", """
using System.Diagnostics;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
struct Foo
{
private string GetDebuggerDisplay()
{
return ToString();
}
}
""");
}
[Fact]
public async Task NotOfferedOnStaticClass()
{
var code = """
[||]static class Foo
{
}
""";
await VerifyCS.VerifyRefactoringAsync(code, code);
}
[Fact]
public async Task NotOfferedOnInterfaceWithToString()
{
var code = """
[||]interface IFoo
{
string ToString();
}
""";
await VerifyCS.VerifyRefactoringAsync(code, code);
}
[Fact]
public async Task NotOfferedOnEnum()
{
var code = """
[||]enum Foo
{
}
""";
await VerifyCS.VerifyRefactoringAsync(code, code);
}
[Fact]
public async Task NotOfferedOnDelegate()
{
var code = """
[||]delegate void Foo();
""";
await VerifyCS.VerifyRefactoringAsync(code, code);
}
[Fact]
public async Task NotOfferedOnUnrelatedClassMembers()
{
var code = """
class C
{
[||]public int Foo { get; }
}
""";
await VerifyCS.VerifyRefactoringAsync(code, code);
}
[Fact]
public async Task OfferedOnToString()
{
await VerifyCS.VerifyRefactoringAsync("""
class C
{
public override string [||]ToString() => "Foo";
}
""", """
using System.Diagnostics;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
class C
{
public override string ToString() => "Foo";
private string GetDebuggerDisplay()
{
return ToString();
}
}
""");
}
[Fact]
public async Task OfferedOnShadowingToString()
{
await VerifyCS.VerifyRefactoringAsync("""
class A
{
public new string [||]ToString() => "Foo";
}
""", """
using System.Diagnostics;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
class A
{
public new string ToString() => "Foo";
private string GetDebuggerDisplay()
{
return ToString();
}
}
""");
}
[Fact]
public async Task NotOfferedOnWrongOverloadOfToString()
{
var code = """
class A
{
public virtual string ToString(int bar = 0) => "Foo";
}
class B : A
{
public override string [||]ToString(int bar = 0) => "Bar";
}
""";
await VerifyCS.VerifyRefactoringAsync(code, code);
}
[Fact]
public async Task OfferedOnExistingDebuggerDisplayMethod()
{
await VerifyCS.VerifyRefactoringAsync("""
class C
{
private string [||]GetDebuggerDisplay() => "Foo";
}
""", """
using System.Diagnostics;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
class C
{
private string GetDebuggerDisplay() => "Foo";
}
""");
}
[Fact]
public async Task NotOfferedOnWrongOverloadOfDebuggerDisplayMethod()
{
var code = """
class A
{
private string [||]GetDebuggerDisplay(int bar = 0) => "Foo";
}
""";
await VerifyCS.VerifyRefactoringAsync(code, code);
}
[Fact]
public async Task NamespaceImportIsNotDuplicated()
{
await VerifyCS.VerifyRefactoringAsync("""
using System.Diagnostics;
[||]class C
{
}
""", """
using System.Diagnostics;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
class C
{
private string GetDebuggerDisplay()
{
return ToString();
}
}
""");
}
[Fact]
public async Task NamespaceImportIsSorted()
{
await VerifyCS.VerifyRefactoringAsync("""
using System.Xml;
[||]class C
{
}
""", """
using System.Diagnostics;
using System.Xml;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
class C
{
private string GetDebuggerDisplay()
{
return ToString();
}
}
""");
}
[Fact]
public async Task NotOfferedWhenAlreadySpecified()
{
var code = """
[System.Diagnostics.DebuggerDisplay("Foo")]
[||]class C
{
}
""";
await VerifyCS.VerifyRefactoringAsync(code, code);
}
[Fact]
public async Task NotOfferedWhenAlreadySpecifiedWithSuffix()
{
var code = """
[System.Diagnostics.DebuggerDisplayAttribute("Foo")]
[||]class C
{
}
""";
await VerifyCS.VerifyRefactoringAsync(code, code);
}
[Fact]
public async Task OfferedWhenAttributeWithTheSameNameIsSpecified()
{
await VerifyCS.VerifyRefactoringAsync("""
[{|CS0246:BrokenCode|}.DebuggerDisplay("Foo")]
[||]class C
{
}
""", """
using System.Diagnostics;
[{|CS0246:BrokenCode|}.DebuggerDisplay("Foo")]
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
[||]class C
{
private string GetDebuggerDisplay()
{
return ToString();
}
}
""");
}
[Fact]
public async Task OfferedWhenAttributeWithTheSameNameIsSpecifiedWithSuffix()
{
await VerifyCS.VerifyRefactoringAsync("""
[{|CS0246:BrokenCode|}.DebuggerDisplayAttribute("Foo")]
[||]class C
{
}
""", """
using System.Diagnostics;
[{|CS0246:BrokenCode|}.DebuggerDisplayAttribute("Foo")]
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
[||]class C
{
private string GetDebuggerDisplay()
{
return ToString();
}
}
""");
}
[Fact]
public async Task AliasedTypeIsRecognized()
{
var code = """
using DD = System.Diagnostics.DebuggerDisplayAttribute;
[DD("Foo")]
[||]class C
{
}
""";
await VerifyCS.VerifyRefactoringAsync(code, code);
}
[Fact]
public async Task OfferedWhenBaseClassHasDebuggerDisplay()
{
await VerifyCS.VerifyRefactoringAsync("""
using System.Diagnostics;
[DebuggerDisplay("Foo")]
class A
{
}
[||]class B : A
{
}
""", """
using System.Diagnostics;
[DebuggerDisplay("Foo")]
class A
{
}
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
class B : A
{
private string GetDebuggerDisplay()
{
return ToString();
}
}
""");
}
[Fact]
public async Task ExistingDebuggerDisplayMethodIsUsedEvenWhenPublicStaticNonString()
{
await VerifyCS.VerifyRefactoringAsync("""
[||]class C
{
public static object GetDebuggerDisplay() => "Foo";
}
""", """
using System.Diagnostics;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
class C
{
public static object GetDebuggerDisplay() => "Foo";
}
""");
}
[Fact]
public async Task ExistingDebuggerDisplayMethodWithParameterIsNotUsed()
{
await VerifyCS.VerifyRefactoringAsync("""
[||]class C
{
private string GetDebuggerDisplay(int foo = 0) => foo.ToString();
}
""", """
using System.Diagnostics;
[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
class C
{
private string GetDebuggerDisplay(int foo = 0) => foo.ToString();
private string GetDebuggerDisplay()
{
return ToString();
}
}
""");
}
}
|