A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

442 lines
14 KiB

using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Avalonia.Analyzers.GeneratedProperties;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Testing;
using Microsoft.CodeAnalysis.Testing;
using Xunit;
namespace Avalonia.Analyzers.Tests.GeneratedProperties;
public class ConvertToGeneratedPropertyAnalyzerTests
{
private static Task Verify([StringSyntax("csharp")] string source, LanguageVersion? languageVersion = null)
{
var test = new ConvertToGeneratedPropertyAnalyzerTest
{
TestCode = """
using Avalonia;
using Avalonia.Data;
""" + source
};
if (languageVersion is { } version)
{
test.LanguageVersion = version;
}
return test.RunAsync(TestContext.Current.CancellationToken);
}
[Fact]
public Task Styled_Minimal_Reports_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double> {|AVP2102:ScaleProperty|} =
AvaloniaProperty.Register<MyControl, double>(nameof(Scale));
public double Scale
{
get => GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
}
""");
[Fact]
public Task Styled_Constant_Default_Reports_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double> {|AVP2102:ScaleProperty|} =
AvaloniaProperty.Register<MyControl, double>(nameof(Scale), 1.0d);
public double Scale
{
get => GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
}
""");
[Fact]
public Task Styled_All_Constant_Arguments_Report_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<string?> {|AVP2102:HeaderProperty|} =
AvaloniaProperty.Register<MyControl, string?>(
nameof(Header), "header", inherits: true,
defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true);
public string? Header
{
get => GetValue(HeaderProperty);
set => SetValue(HeaderProperty, value);
}
}
""");
[Fact]
public Task Styled_Block_Bodies_Report_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double> {|AVP2102:ScaleProperty|} =
AvaloniaProperty.Register<MyControl, double>("Scale");
public double Scale
{
get { return (double)this.GetValue(ScaleProperty); }
set { this.SetValue(ScaleProperty, value); }
}
}
""");
[Fact]
public Task Styled_Restricted_Setter_Reports_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<int> {|AVP2102:CountProperty|} =
AvaloniaProperty.Register<MyControl, int>(nameof(Count));
public int Count
{
get => GetValue(CountProperty);
private set => SetValue(CountProperty, value);
}
}
""");
[Fact]
public Task Direct_ReadWrite_Reports_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly DirectProperty<MyControl, string> {|AVP2102:TextProperty|} =
AvaloniaProperty.RegisterDirect<MyControl, string>(
nameof(Text), o => o.Text, (o, v) => o.Text = v);
private string _text = "";
public string Text
{
get => _text;
set => SetAndRaise(TextProperty, ref _text, value);
}
}
""");
[Fact]
public Task Direct_ReadOnly_Reports_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly DirectProperty<MyControl, string> {|AVP2102:TextProperty|} =
AvaloniaProperty.RegisterDirect<MyControl, string>(nameof(Text), static o => o.Text);
private string _text = "";
public string Text
{
get => _text;
private set => SetAndRaise(TextProperty, ref _text, value);
}
}
""");
[Fact]
public Task Attached_Reports_AVP2102() => Verify(
"""
public class MyPanel : AvaloniaObject
{
public static readonly AttachedProperty<int> {|AVP2102:RowProperty|} =
AvaloniaProperty.RegisterAttached<MyPanel, Visual, int>("Row");
public static int GetRow(Visual element) => element.GetValue(RowProperty);
public static void SetRow(Visual element, int value) => element.SetValue(RowProperty, value);
}
""");
[Fact]
public Task Attached_On_Static_Class_Reports_AVP2102() => Verify(
"""
public static class Helper
{
public static readonly AttachedProperty<int> {|AVP2102:RowProperty|} =
AvaloniaProperty.RegisterAttached<Visual, int>("Row", typeof(Helper));
public static int GetRow(Visual element) => element.GetValue(RowProperty);
public static void SetRow(Visual element, int value) => element.SetValue(RowProperty, value);
}
""");
[Fact]
public Task Styled_AddOwner_Reports_AVP2102() => Verify(
"""
public class RangeBase : AvaloniaObject
{
// Own registration: also a candidate.
public static readonly StyledProperty<double> {|AVP2102:ValueProperty|} =
AvaloniaProperty.Register<RangeBase, double>("Value");
}
public class MyControl : RangeBase
{
public static readonly StyledProperty<double> {|AVP2102:ValueProperty|} =
RangeBase.ValueProperty.AddOwner<MyControl>();
public double Value
{
get => GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
}
""");
[Fact]
public Task Direct_AddOwner_Reports_AVP2102() => Verify(
"""
public class TextBase : AvaloniaObject
{
// Own registration: also a candidate.
public static readonly DirectProperty<TextBase, string> {|AVP2102:TextProperty|} =
AvaloniaProperty.RegisterDirect<TextBase, string>("Text", o => "");
}
public class MyControl : TextBase
{
public static readonly DirectProperty<MyControl, string> {|AVP2102:TextProperty|} =
TextBase.TextProperty.AddOwner<MyControl>(o => o.Text, (o, v) => o.Text = v);
private string _text = "";
public string Text
{
get => _text;
set => SetAndRaise(TextProperty, ref _text, value);
}
}
""");
[Fact]
public Task Validate_Callback_Reports_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double> {|AVP2102:ScaleProperty|} =
AvaloniaProperty.Register<MyControl, double>(nameof(Scale), validate: v => v >= 0);
public double Scale
{
get => GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
}
""");
[Fact]
public Task Coerce_Method_Group_Reports_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double> {|AVP2102:ScaleProperty|} =
AvaloniaProperty.Register<MyControl, double>(nameof(Scale), coerce: CoerceScale);
private static double CoerceScale(AvaloniaObject sender, double value) => value;
public double Scale
{
get => GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
}
""");
[Fact]
public Task NonConstant_Default_Reports_AVP2102() => Verify(
"""
using Avalonia.Media;
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<Color> {|AVP2102:BarColorProperty|} =
AvaloniaProperty.Register<MyControl, Color>(nameof(BarColor), Colors.White);
public Color BarColor
{
get => GetValue(BarColorProperty);
set => SetValue(BarColorProperty, value);
}
}
""");
[Fact]
public Task Side_Effecting_Accessor_Reports_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double> {|AVP2102:ScaleProperty|} =
AvaloniaProperty.Register<MyControl, double>(nameof(Scale));
private int _setCount;
public double Scale
{
get => GetValue(ScaleProperty);
set
{
_setCount++;
SetValue(ScaleProperty, value);
}
}
}
""");
[Fact]
public Task Owner_Mismatch_Reports_Nothing() => Verify(
"""
public class OtherControl : AvaloniaObject
{
}
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double> ScaleProperty =
AvaloniaProperty.Register<OtherControl, double>("Scale");
public double Scale
{
get => GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
}
""");
[Fact]
public Task Field_Name_Without_Property_Suffix_Reports_Nothing() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double> ScaleProp =
AvaloniaProperty.Register<MyControl, double>("Scale");
public double Scale
{
get => GetValue(ScaleProp);
set => SetValue(ScaleProp, value);
}
}
""");
[Fact]
public Task Generic_Containing_Type_Reports_Nothing() => Verify(
"""
public class MyControl<T> : AvaloniaObject
{
public static readonly StyledProperty<double> ScaleProperty =
AvaloniaProperty.Register<MyControl<T>, double>(nameof(Scale));
public double Scale
{
get => GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
}
""");
[Fact]
public Task Multiple_Declarators_Report_Nothing() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double>
ScaleProperty = AvaloniaProperty.Register<MyControl, double>(nameof(Scale)),
ZoomProperty = AvaloniaProperty.Register<MyControl, double>(nameof(Zoom));
public double Scale
{
get => GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
public double Zoom
{
get => GetValue(ZoomProperty);
set => SetValue(ZoomProperty, value);
}
}
""");
[Fact]
public Task Field_Alias_Reports_Nothing() => Verify(
"""
using Avalonia.Layout;
public class MyControl : AvaloniaObject
{
// Plain alias of an existing property, not a Register/AddOwner call.
public static readonly StyledProperty<double> WidthProperty = Layoutable.WidthProperty;
}
""");
[Fact]
public Task CSharp12_Reports_Nothing() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double> ScaleProperty =
AvaloniaProperty.Register<MyControl, double>(nameof(Scale));
public double Scale
{
get => GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
}
""",
LanguageVersion.CSharp12);
[Fact]
public Task CSharp13_Reports_AVP2102() => Verify(
"""
public class MyControl : AvaloniaObject
{
public static readonly StyledProperty<double> {|AVP2102:ScaleProperty|} =
AvaloniaProperty.Register<MyControl, double>(nameof(Scale));
public double Scale
{
get => GetValue(ScaleProperty);
set => SetValue(ScaleProperty, value);
}
}
""",
LanguageVersion.CSharp13);
public class ConvertToGeneratedPropertyAnalyzerTest : CSharpAnalyzerTest<ConvertToGeneratedPropertyAnalyzer, DefaultVerifier>
{
public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.CSharp14;
public ConvertToGeneratedPropertyAnalyzerTest()
{
ReferenceAssemblies = new ReferenceAssemblies(TestReferences.DefaultTargetFramework);
foreach (var reference in TestReferences.All.Value)
{
TestState.AdditionalReferences.Add(reference);
}
// The manual declarations under test are valid code, but keep parity with the
// other generated-property tests and only assert our own diagnostics.
CompilerDiagnostics = CompilerDiagnostics.None;
}
protected override ParseOptions CreateParseOptions()
=> ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion);
}
}