mirror of
https://github.com/CommunityToolkit/dotnet.git
synced 2026-10-19 05:31:42 +08:00
2133 lines
119 KiB
C#
2133 lines
119 KiB
C#
// 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.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|
|
|
namespace CommunityToolkit.Mvvm.SourceGenerators.UnitTests;
|
|
|
|
[TestClass]
|
|
public class Test_SourceGeneratorsCodegen
|
|
{
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNonNullableReferenceType_EmitsMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private string name;
|
|
}
|
|
""";
|
|
|
|
#if NET6_0_OR_GREATER
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <inheritdoc cref="name"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public string Name
|
|
{
|
|
get => name;
|
|
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("name")]
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<string>.Default.Equals(name, value))
|
|
{
|
|
OnNameChanging(value);
|
|
OnNameChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Name);
|
|
name = value;
|
|
OnNameChanged(value);
|
|
OnNameChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanging(string value);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanging(string? oldValue, string newValue);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanged(string value);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanged(string? oldValue, string newValue);
|
|
}
|
|
}
|
|
""";
|
|
#else
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <inheritdoc cref="name"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public string Name
|
|
{
|
|
get => name;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<string>.Default.Equals(name, value))
|
|
{
|
|
OnNameChanging(value);
|
|
OnNameChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Name);
|
|
name = value;
|
|
OnNameChanged(value);
|
|
OnNameChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanging(string value);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanging(string? oldValue, string newValue);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanged(string value);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanged(string? oldValue, string newValue);
|
|
}
|
|
}
|
|
""";
|
|
#endif
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNonNullableValueType_DoesNotEmitMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using System;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private Guid id;
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <inheritdoc cref="id"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public global::System.Guid Id
|
|
{
|
|
get => id;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<global::System.Guid>.Default.Equals(id, value))
|
|
{
|
|
OnIdChanging(value);
|
|
OnIdChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Id);
|
|
id = value;
|
|
OnIdChanged(value);
|
|
OnIdChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Id"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Id"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnIdChanging(global::System.Guid value);
|
|
/// <summary>Executes the logic for when <see cref="Id"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Id"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnIdChanging(global::System.Guid oldValue, global::System.Guid newValue);
|
|
/// <summary>Executes the logic for when <see cref="Id"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Id"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnIdChanged(global::System.Guid value);
|
|
/// <summary>Executes the logic for when <see cref="Id"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Id"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnIdChanged(global::System.Guid oldValue, global::System.Guid newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNonNullableUnconstrainedGenericType_EmitsMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using System;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel<T> : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private T content;
|
|
}
|
|
""";
|
|
|
|
#if NET6_0_OR_GREATER
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="content"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public T Content
|
|
{
|
|
get => content;
|
|
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("content")]
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(content, value))
|
|
{
|
|
OnContentChanging(value);
|
|
OnContentChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
|
|
content = value;
|
|
OnContentChanged(value);
|
|
OnContentChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? oldValue, T newValue);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? oldValue, T newValue);
|
|
}
|
|
}
|
|
""";
|
|
#else
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="content"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public T Content
|
|
{
|
|
get => content;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(content, value))
|
|
{
|
|
OnContentChanging(value);
|
|
OnContentChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
|
|
content = value;
|
|
OnContentChanged(value);
|
|
OnContentChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? oldValue, T newValue);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? oldValue, T newValue);
|
|
}
|
|
}
|
|
""";
|
|
#endif
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
|
|
|
|
// Also test with C# 8 to double check that using a nullable unconstrained type parameter doesn't cause issues, but just a (suppressed) warning
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, LanguageVersion.CSharp8, ("MyApp.MyViewModel`1.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNonNullableConstrainedNotNullGenericType_EmitsMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using System;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel<T> : ObservableObject
|
|
where T : notnull
|
|
{
|
|
[ObservableProperty]
|
|
private T content;
|
|
}
|
|
""";
|
|
|
|
#if NET6_0_OR_GREATER
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="content"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public T Content
|
|
{
|
|
get => content;
|
|
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("content")]
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(content, value))
|
|
{
|
|
OnContentChanging(value);
|
|
OnContentChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
|
|
content = value;
|
|
OnContentChanged(value);
|
|
OnContentChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? oldValue, T newValue);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? oldValue, T newValue);
|
|
}
|
|
}
|
|
""";
|
|
#else
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="content"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public T Content
|
|
{
|
|
get => content;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(content, value))
|
|
{
|
|
OnContentChanging(value);
|
|
OnContentChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
|
|
content = value;
|
|
OnContentChanged(value);
|
|
OnContentChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? oldValue, T newValue);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? oldValue, T newValue);
|
|
}
|
|
}
|
|
""";
|
|
#endif
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNonNullableConstrainedReferenceTypeGenericType_EmitsMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using System;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel<T> : ObservableObject
|
|
where T : class
|
|
{
|
|
[ObservableProperty]
|
|
private T content;
|
|
}
|
|
""";
|
|
|
|
#if NET6_0_OR_GREATER
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="content"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public T Content
|
|
{
|
|
get => content;
|
|
[global::System.Diagnostics.CodeAnalysis.MemberNotNull("content")]
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(content, value))
|
|
{
|
|
OnContentChanging(value);
|
|
OnContentChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
|
|
content = value;
|
|
OnContentChanged(value);
|
|
OnContentChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? oldValue, T newValue);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? oldValue, T newValue);
|
|
}
|
|
}
|
|
""";
|
|
#else
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="content"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public T Content
|
|
{
|
|
get => content;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(content, value))
|
|
{
|
|
OnContentChanging(value);
|
|
OnContentChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
|
|
content = value;
|
|
OnContentChanged(value);
|
|
OnContentChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? oldValue, T newValue);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? oldValue, T newValue);
|
|
}
|
|
}
|
|
""";
|
|
#endif
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNonNullableConstrainedValueTypeGenericType_DoesNotEmitMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using System;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel<T> : ObservableObject
|
|
where T : struct
|
|
{
|
|
[ObservableProperty]
|
|
private T content;
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="content"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public T Content
|
|
{
|
|
get => content;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<T>.Default.Equals(content, value))
|
|
{
|
|
OnContentChanging(value);
|
|
OnContentChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
|
|
content = value;
|
|
OnContentChanged(value);
|
|
OnContentChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T oldValue, T newValue);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T oldValue, T newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNullableReferenceType_DoesNotEmitMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private string? name;
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <inheritdoc cref="name"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public string? Name
|
|
{
|
|
get => name;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(name, value))
|
|
{
|
|
OnNameChanging(value);
|
|
OnNameChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Name);
|
|
name = value;
|
|
OnNameChanged(value);
|
|
OnNameChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanging(string? value);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanging(string? oldValue, string? newValue);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanged(string? value);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanged(string? oldValue, string? newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNullableValueType_DoesNotEmitMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using System;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private Guid? id;
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <inheritdoc cref="id"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public global::System.Guid? Id
|
|
{
|
|
get => id;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<global::System.Guid?>.Default.Equals(id, value))
|
|
{
|
|
OnIdChanging(value);
|
|
OnIdChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Id);
|
|
id = value;
|
|
OnIdChanged(value);
|
|
OnIdChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Id"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Id"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnIdChanging(global::System.Guid? value);
|
|
/// <summary>Executes the logic for when <see cref="Id"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Id"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnIdChanging(global::System.Guid? oldValue, global::System.Guid? newValue);
|
|
/// <summary>Executes the logic for when <see cref="Id"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Id"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnIdChanged(global::System.Guid? value);
|
|
/// <summary>Executes the logic for when <see cref="Id"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Id"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnIdChanged(global::System.Guid? oldValue, global::System.Guid? newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNullableUnconstrainedGenericType_DoesNotEmitMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using System;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel<T> : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private T? content;
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="content"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public T? Content
|
|
{
|
|
get => content;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<T?>.Default.Equals(content, value))
|
|
{
|
|
OnContentChanging(value);
|
|
OnContentChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
|
|
content = value;
|
|
OnContentChanged(value);
|
|
OnContentChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? oldValue, T? newValue);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? oldValue, T? newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNullableConstrainedReferenceTypeGenericType_DoesNotEmitMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using System;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel<T> : ObservableObject
|
|
where T : class
|
|
{
|
|
[ObservableProperty]
|
|
private T? content;
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="content"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public T? Content
|
|
{
|
|
get => content;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<T?>.Default.Equals(content, value))
|
|
{
|
|
OnContentChanging(value);
|
|
OnContentChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
|
|
content = value;
|
|
OnContentChanged(value);
|
|
OnContentChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? oldValue, T? newValue);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? oldValue, T? newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithNullableConstrainedValueTypeGenericType_DoesNotEmitMemberNotNullAttribute()
|
|
{
|
|
string source = """
|
|
using System;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel<T> : ObservableObject
|
|
where T : struct
|
|
{
|
|
[ObservableProperty]
|
|
private T? content;
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="content"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public T? Content
|
|
{
|
|
get => content;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<T?>.Default.Equals(content, value))
|
|
{
|
|
OnContentChanging(value);
|
|
OnContentChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Content);
|
|
content = value;
|
|
OnContentChanged(value);
|
|
OnContentChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Content);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanging(T? oldValue, T? newValue);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? value);
|
|
/// <summary>Executes the logic for when <see cref="Content"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Content"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnContentChanged(T? oldValue, T? newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel`1.g.cs", result));
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/601
|
|
[TestMethod]
|
|
public void ObservablePropertyWithForwardedAttributesWithNumberLiterals_PreservesType()
|
|
{
|
|
string source = """
|
|
using System.ComponentModel;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel : ObservableObject
|
|
{
|
|
const double MyDouble = 3.14;
|
|
const float MyFloat = 3.14f;
|
|
|
|
[ObservableProperty]
|
|
[property: DefaultValue(0.0)]
|
|
[property: DefaultValue(1.24)]
|
|
[property: DefaultValue(0.0f)]
|
|
[property: DefaultValue(0.0f)]
|
|
[property: DefaultValue(MyDouble)]
|
|
[property: DefaultValue(MyFloat)]
|
|
private object? a;
|
|
}
|
|
|
|
public class DefaultValueAttribute : Attribute
|
|
{
|
|
public DefaultValueAttribute(object value)
|
|
{
|
|
}
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <inheritdoc cref="a"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
[global::MyApp.DefaultValueAttribute(0D)]
|
|
[global::MyApp.DefaultValueAttribute(1.24D)]
|
|
[global::MyApp.DefaultValueAttribute(0F)]
|
|
[global::MyApp.DefaultValueAttribute(0F)]
|
|
[global::MyApp.DefaultValueAttribute(3.14D)]
|
|
[global::MyApp.DefaultValueAttribute(3.14F)]
|
|
public object? A
|
|
{
|
|
get => a;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<object?>.Default.Equals(a, value))
|
|
{
|
|
OnAChanging(value);
|
|
OnAChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.A);
|
|
a = value;
|
|
OnAChanged(value);
|
|
OnAChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.A);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="A"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanging(object? value);
|
|
/// <summary>Executes the logic for when <see cref="A"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanging(object? oldValue, object? newValue);
|
|
/// <summary>Executes the logic for when <see cref="A"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanged(object? value);
|
|
/// <summary>Executes the logic for when <see cref="A"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanged(object? oldValue, object? newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void RelayCommandMethodWithForwardedAttributesWithNumberLiterals_PreservesType()
|
|
{
|
|
string source = """
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel
|
|
{
|
|
const double MyDouble = 3.14;
|
|
const float MyFloat = 3.14f;
|
|
|
|
[RelayCommand]
|
|
[field: DefaultValue(0.0)]
|
|
[field: DefaultValue(1.24)]
|
|
[field: DefaultValue(0.0f)]
|
|
[field: DefaultValue(0.0f)]
|
|
[field: DefaultValue(MyDouble)]
|
|
[field: DefaultValue(MyFloat)]
|
|
[property: DefaultValue(0.0)]
|
|
[property: DefaultValue(1.24)]
|
|
[property: DefaultValue(0.0f)]
|
|
[property: DefaultValue(0.0f)]
|
|
[property: DefaultValue(MyDouble)]
|
|
[property: DefaultValue(MyFloat)]
|
|
private void Test()
|
|
{
|
|
}
|
|
}
|
|
|
|
public class DefaultValueAttribute : Attribute
|
|
{
|
|
public DefaultValueAttribute(object value)
|
|
{
|
|
}
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <summary>The backing field for <see cref="TestCommand"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::MyApp.DefaultValueAttribute(0D)]
|
|
[global::MyApp.DefaultValueAttribute(1.24D)]
|
|
[global::MyApp.DefaultValueAttribute(0F)]
|
|
[global::MyApp.DefaultValueAttribute(0F)]
|
|
[global::MyApp.DefaultValueAttribute(3.14D)]
|
|
[global::MyApp.DefaultValueAttribute(3.14F)]
|
|
private global::CommunityToolkit.Mvvm.Input.RelayCommand? testCommand;
|
|
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="Test"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
[global::MyApp.DefaultValueAttribute(0D)]
|
|
[global::MyApp.DefaultValueAttribute(1.24D)]
|
|
[global::MyApp.DefaultValueAttribute(0F)]
|
|
[global::MyApp.DefaultValueAttribute(0F)]
|
|
[global::MyApp.DefaultValueAttribute(3.14D)]
|
|
[global::MyApp.DefaultValueAttribute(3.14F)]
|
|
public global::CommunityToolkit.Mvvm.Input.IRelayCommand TestCommand => testCommand ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test));
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new RelayCommandGenerator() }, ("MyApp.MyViewModel.Test.g.cs", result));
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/681
|
|
[TestMethod]
|
|
public void ObservablePropertyWithForwardedAttributesWithEnumWithNegativeValue_WorksCorrectly()
|
|
{
|
|
string source = """
|
|
using System.ComponentModel;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
[property: DefaultValue(NegativeEnum1.Problem)]
|
|
[property: DefaultValue(NegativeEnum2.Problem)]
|
|
[property: DefaultValue(NegativeEnum3.Problem)]
|
|
[property: DefaultValue(NegativeEnum4.Problem)]
|
|
private object? a;
|
|
}
|
|
|
|
public class DefaultValueAttribute : Attribute
|
|
{
|
|
public DefaultValueAttribute(object value)
|
|
{
|
|
}
|
|
}
|
|
|
|
public enum NegativeEnum1
|
|
{
|
|
Problem = -1073741824,
|
|
OK = 0
|
|
}
|
|
|
|
public enum NegativeEnum2 : long
|
|
{
|
|
Problem = long.MinValue,
|
|
OK = 0
|
|
}
|
|
|
|
public enum NegativeEnum3 : short
|
|
{
|
|
Problem = -1234,
|
|
OK = 0
|
|
}
|
|
|
|
public enum NegativeEnum4 : sbyte
|
|
{
|
|
Problem = -1,
|
|
OK = 0
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <inheritdoc cref="a"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)]
|
|
public object? A
|
|
{
|
|
get => a;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<object?>.Default.Equals(a, value))
|
|
{
|
|
OnAChanging(value);
|
|
OnAChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.A);
|
|
a = value;
|
|
OnAChanged(value);
|
|
OnAChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.A);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="A"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanging(object? value);
|
|
/// <summary>Executes the logic for when <see cref="A"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanging(object? oldValue, object? newValue);
|
|
/// <summary>Executes the logic for when <see cref="A"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanged(object? value);
|
|
/// <summary>Executes the logic for when <see cref="A"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanged(object? oldValue, object? newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithForwardedAttributesWithEnumWithCombinedValues_WorksCorrectly()
|
|
{
|
|
string source = """
|
|
using System.ComponentModel;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
[property: DefaultValue(NegativeEnum1.A)]
|
|
[property: DefaultValue(NegativeEnum1.B)]
|
|
[property: DefaultValue((NegativeEnum1)42)]
|
|
[property: DefaultValue((NegativeEnum1)OtherClass.MyNumber)]
|
|
[property: DefaultValue(OtherClass.MyEnumValue)]
|
|
[property: DefaultValue(NegativeEnum2.A)]
|
|
[property: DefaultValue(NegativeEnum2.B)]
|
|
[property: DefaultValue((NegativeEnum2)42)]
|
|
[property: DefaultValue((NegativeEnum2)OtherClass.MyNumber)]
|
|
[property: DefaultValue((NegativeEnum2)(int)OtherClass.MyEnumValue)]
|
|
[property: DefaultValue(NegativeEnum3.A)]
|
|
[property: DefaultValue(NegativeEnum3.B)]
|
|
[property: DefaultValue((NegativeEnum3)42)]
|
|
[property: DefaultValue((NegativeEnum3)OtherClass.MyNumber)]
|
|
[property: DefaultValue((NegativeEnum3)(int)OtherClass.MyEnumValue)]
|
|
[property: DefaultValue(NegativeEnum4.A)]
|
|
[property: DefaultValue(NegativeEnum4.B)]
|
|
[property: DefaultValue((NegativeEnum4)42)]
|
|
[property: DefaultValue((NegativeEnum4)unchecked((sbyte)OtherClass.MyNumber))]
|
|
[property: DefaultValue((NegativeEnum4)(int)OtherClass.MyEnumValue)]
|
|
private object? a;
|
|
}
|
|
|
|
public static class OtherClass
|
|
{
|
|
public const int MyNumber = 456;
|
|
public const NegativeEnum1 MyEnumValue = NegativeEnum1.C;
|
|
}
|
|
|
|
public class DefaultValueAttribute : Attribute
|
|
{
|
|
public DefaultValueAttribute(object value)
|
|
{
|
|
}
|
|
}
|
|
|
|
public enum NegativeEnum1
|
|
{
|
|
A = 0,
|
|
B = -1073741824,
|
|
C = 123
|
|
}
|
|
|
|
public enum NegativeEnum2 : long
|
|
{
|
|
A = 0,
|
|
B = long.MinValue
|
|
}
|
|
|
|
public enum NegativeEnum3 : short
|
|
{
|
|
A = 1,
|
|
B = -1234
|
|
}
|
|
|
|
public enum NegativeEnum4 : sbyte
|
|
{
|
|
A = 1,
|
|
B = -1
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <inheritdoc cref="a"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)0)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)42)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)456)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)123)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)0)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)42)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)456)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)123)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)1)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)42)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)456)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)123)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)1)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)42)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-56)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)123)]
|
|
public object? A
|
|
{
|
|
get => a;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<object?>.Default.Equals(a, value))
|
|
{
|
|
OnAChanging(value);
|
|
OnAChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.A);
|
|
a = value;
|
|
OnAChanged(value);
|
|
OnAChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.A);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="A"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanging(object? value);
|
|
/// <summary>Executes the logic for when <see cref="A"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanging(object? oldValue, object? newValue);
|
|
/// <summary>Executes the logic for when <see cref="A"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanged(object? value);
|
|
/// <summary>Executes the logic for when <see cref="A"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanged(object? oldValue, object? newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result));
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/632
|
|
[TestMethod]
|
|
public void RelayCommandMethodWithPartialDeclarations_TriggersCorrectly()
|
|
{
|
|
string source = """
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel
|
|
{
|
|
[RelayCommand]
|
|
private partial void Test1()
|
|
{
|
|
}
|
|
|
|
private partial void Test1();
|
|
|
|
private partial void Test2()
|
|
{
|
|
}
|
|
|
|
[RelayCommand]
|
|
private partial void Test2();
|
|
}
|
|
""";
|
|
|
|
string result1 = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <summary>The backing field for <see cref="Test1Command"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
private global::CommunityToolkit.Mvvm.Input.RelayCommand? test1Command;
|
|
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="Test1"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public global::CommunityToolkit.Mvvm.Input.IRelayCommand Test1Command => test1Command ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test1));
|
|
}
|
|
}
|
|
""";
|
|
|
|
string result2 = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <summary>The backing field for <see cref="Test2Command"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
private global::CommunityToolkit.Mvvm.Input.RelayCommand? test2Command;
|
|
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="Test2"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public global::CommunityToolkit.Mvvm.Input.IRelayCommand Test2Command => test2Command ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test2));
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new RelayCommandGenerator() }, ("MyApp.MyViewModel.Test1.g.cs", result1), ("MyApp.MyViewModel.Test2.g.cs", result2));
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/632
|
|
[TestMethod]
|
|
public void RelayCommandMethodWithForwardedAttributesOverPartialDeclarations_MergesAttributes()
|
|
{
|
|
string source = """
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel
|
|
{
|
|
[RelayCommand]
|
|
[field: Value(0)]
|
|
[property: Value(1)]
|
|
private partial void Test1()
|
|
{
|
|
}
|
|
|
|
[field: Value(2)]
|
|
[property: Value(3)]
|
|
private partial void Test1();
|
|
|
|
[field: Value(0)]
|
|
[property: Value(1)]
|
|
private partial void Test2()
|
|
{
|
|
}
|
|
|
|
[RelayCommand]
|
|
[field: Value(2)]
|
|
[property: Value(3)]
|
|
private partial void Test2();
|
|
}
|
|
|
|
public class ValueAttribute : Attribute
|
|
{
|
|
public ValueAttribute(object value)
|
|
{
|
|
}
|
|
}
|
|
""";
|
|
|
|
string result1 = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <summary>The backing field for <see cref="Test1Command"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::MyApp.ValueAttribute(2)]
|
|
[global::MyApp.ValueAttribute(0)]
|
|
private global::CommunityToolkit.Mvvm.Input.RelayCommand? test1Command;
|
|
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="Test1"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
[global::MyApp.ValueAttribute(3)]
|
|
[global::MyApp.ValueAttribute(1)]
|
|
public global::CommunityToolkit.Mvvm.Input.IRelayCommand Test1Command => test1Command ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test1));
|
|
}
|
|
}
|
|
""";
|
|
|
|
string result2 = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <summary>The backing field for <see cref="Test2Command"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::MyApp.ValueAttribute(2)]
|
|
[global::MyApp.ValueAttribute(0)]
|
|
private global::CommunityToolkit.Mvvm.Input.RelayCommand? test2Command;
|
|
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="Test2"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
[global::MyApp.ValueAttribute(3)]
|
|
[global::MyApp.ValueAttribute(1)]
|
|
public global::CommunityToolkit.Mvvm.Input.IRelayCommand Test2Command => test2Command ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test2));
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new RelayCommandGenerator() }, ("MyApp.MyViewModel.Test1.g.cs", result1), ("MyApp.MyViewModel.Test2.g.cs", result2));
|
|
}
|
|
|
|
// See https://github.com/CommunityToolkit/dotnet/issues/681
|
|
[TestMethod]
|
|
public void RelayCommandMethodWithForwardedAttributesWithEnumValues_WorksCorrectly()
|
|
{
|
|
string source = """
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel
|
|
{
|
|
[RelayCommand]
|
|
[field: DefaultValue(NegativeEnum1.Problem)]
|
|
[field: DefaultValue(NegativeEnum2.Problem)]
|
|
[field: DefaultValue(NegativeEnum3.Problem)]
|
|
[field: DefaultValue(NegativeEnum4.Problem)]
|
|
[property: DefaultValue(NegativeEnum1.Problem)]
|
|
[property: DefaultValue(NegativeEnum2.Problem)]
|
|
[property: DefaultValue(NegativeEnum3.Problem)]
|
|
[property: DefaultValue(NegativeEnum4.Problem)]
|
|
private void Test()
|
|
{
|
|
}
|
|
}
|
|
|
|
public class DefaultValueAttribute : Attribute
|
|
{
|
|
public DefaultValueAttribute(object value)
|
|
{
|
|
}
|
|
}
|
|
|
|
public enum NegativeEnum1
|
|
{
|
|
Problem = -1073741824,
|
|
OK = 0
|
|
}
|
|
|
|
public enum NegativeEnum2 : long
|
|
{
|
|
Problem = long.MinValue,
|
|
OK = 0
|
|
}
|
|
|
|
public enum NegativeEnum3 : short
|
|
{
|
|
Problem = -1234,
|
|
OK = 0
|
|
}
|
|
|
|
public enum NegativeEnum4 : sbyte
|
|
{
|
|
Problem = -1,
|
|
OK = 0
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <summary>The backing field for <see cref="TestCommand"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)]
|
|
private global::CommunityToolkit.Mvvm.Input.RelayCommand? testCommand;
|
|
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="Test"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum1)-1073741824)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum2)-9223372036854775808)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum3)-1234)]
|
|
[global::MyApp.DefaultValueAttribute((global::MyApp.NegativeEnum4)-1)]
|
|
public global::CommunityToolkit.Mvvm.Input.IRelayCommand TestCommand => testCommand ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test));
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new RelayCommandGenerator() }, ("MyApp.MyViewModel.Test.g.cs", result));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservablePropertyWithinGenericAndNestedTypes()
|
|
{
|
|
string source = """
|
|
using System.ComponentModel;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
#nullable enable
|
|
|
|
namespace MyApp;
|
|
|
|
partial class Foo
|
|
{
|
|
partial class MyViewModel<T> : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private string? a;
|
|
}
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class Foo
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel<T>
|
|
{
|
|
/// <inheritdoc cref="a"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public string? A
|
|
{
|
|
get => a;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(a, value))
|
|
{
|
|
OnAChanging(value);
|
|
OnAChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.A);
|
|
a = value;
|
|
OnAChanged(value);
|
|
OnAChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.A);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="A"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanging(string? value);
|
|
/// <summary>Executes the logic for when <see cref="A"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanging(string? oldValue, string? newValue);
|
|
/// <summary>Executes the logic for when <see cref="A"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanged(string? value);
|
|
/// <summary>Executes the logic for when <see cref="A"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="A"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnAChanged(string? oldValue, string? newValue);
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
#if ROSLYN_4_3_1_OR_GREATER
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.Foo+MyViewModel`1.g.cs", result));
|
|
#else
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.Foo.MyViewModel_1.g.cs", result));
|
|
#endif
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ObservableProperty_ValidateGeneratedCachedArguments()
|
|
{
|
|
string source = """
|
|
using System.ComponentModel;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
|
|
namespace MyApp;
|
|
|
|
partial class MyViewModel : ObservableObject
|
|
{
|
|
[ObservableProperty]
|
|
private string? name;
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace MyApp
|
|
{
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <inheritdoc cref="name"/>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public string? Name
|
|
{
|
|
get => name;
|
|
set
|
|
{
|
|
if (!global::System.Collections.Generic.EqualityComparer<string?>.Default.Equals(name, value))
|
|
{
|
|
OnNameChanging(value);
|
|
OnNameChanging(default, value);
|
|
OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Name);
|
|
name = value;
|
|
OnNameChanged(value);
|
|
OnNameChanged(default, value);
|
|
OnPropertyChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
|
|
/// <param name="value">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanging(string? value);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
|
|
/// <param name="oldValue">The previous property value that is being replaced.</param>
|
|
/// <param name="newValue">The new property value being set.</param>
|
|
/// <remarks>This method is invoked right before the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanging(string? oldValue, string? newValue);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
|
|
/// <param name="value">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanged(string? value);
|
|
/// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
|
|
/// <param name="oldValue">The previous property value that was replaced.</param>
|
|
/// <param name="newValue">The new property value that was set.</param>
|
|
/// <remarks>This method is invoked right after the value of <see cref="Name"/> is changed.</remarks>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
partial void OnNameChanged(string? oldValue, string? newValue);
|
|
}
|
|
}
|
|
""";
|
|
|
|
string changingArgs = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace CommunityToolkit.Mvvm.ComponentModel.__Internals
|
|
{
|
|
/// <summary>
|
|
/// A helper type providing cached, reusable <see cref="global::System.ComponentModel.PropertyChangingEventArgs"/> instances
|
|
/// for all properties generated with <see cref="global::CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute"/>.
|
|
/// </summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.DebuggerNonUserCode]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
|
[global::System.Obsolete("This type is not intended to be used directly by user code")]
|
|
internal static class __KnownINotifyPropertyChangingArgs
|
|
{
|
|
/// <summary>The cached <see cref="global::System.ComponentModel.PropertyChangingEventArgs"/> instance for all "Name" generated properties.</summary>
|
|
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
|
[global::System.Obsolete("This field is not intended to be referenced directly by user code")]
|
|
public static readonly global::System.ComponentModel.PropertyChangingEventArgs Name = new global::System.ComponentModel.PropertyChangingEventArgs("Name");
|
|
}
|
|
}
|
|
""";
|
|
|
|
string changedArgs = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
namespace CommunityToolkit.Mvvm.ComponentModel.__Internals
|
|
{
|
|
/// <summary>
|
|
/// A helper type providing cached, reusable <see cref="global::System.ComponentModel.PropertyChangedEventArgs"/> instances
|
|
/// for all properties generated with <see cref="global::CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute"/>.
|
|
/// </summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.DebuggerNonUserCode]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
|
[global::System.Obsolete("This type is not intended to be used directly by user code")]
|
|
internal static class __KnownINotifyPropertyChangedArgs
|
|
{
|
|
/// <summary>The cached <see cref="global::System.ComponentModel.PropertyChangedEventArgs"/> instance for all "Name" generated properties.</summary>
|
|
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
|
[global::System.Obsolete("This field is not intended to be referenced directly by user code")]
|
|
public static readonly global::System.ComponentModel.PropertyChangedEventArgs Name = new global::System.ComponentModel.PropertyChangedEventArgs("Name");
|
|
}
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new ObservablePropertyGenerator() }, ("MyApp.MyViewModel.g.cs", result), ("__KnownINotifyPropertyChangingArgs.g.cs", changingArgs), ("__KnownINotifyPropertyChangedArgs.g.cs", changedArgs));
|
|
}
|
|
|
|
[TestMethod]
|
|
public void RelayCommandMethodInTopLevelTypeWithNoNamespace_PreservesInheritdoc()
|
|
{
|
|
string source = """
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
|
partial class MyViewModel
|
|
{
|
|
[RelayCommand]
|
|
private void Test()
|
|
{
|
|
}
|
|
}
|
|
""";
|
|
|
|
string result = """
|
|
// <auto-generated/>
|
|
#pragma warning disable
|
|
#nullable enable
|
|
/// <inheritdoc/>
|
|
partial class MyViewModel
|
|
{
|
|
/// <summary>The backing field for <see cref="TestCommand"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
private global::CommunityToolkit.Mvvm.Input.RelayCommand? testCommand;
|
|
/// <summary>Gets an <see cref="global::CommunityToolkit.Mvvm.Input.IRelayCommand"/> instance wrapping <see cref="Test"/>.</summary>
|
|
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator", <ASSEMBLY_VERSION>)]
|
|
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
|
|
public global::CommunityToolkit.Mvvm.Input.IRelayCommand TestCommand => testCommand ??= new global::CommunityToolkit.Mvvm.Input.RelayCommand(new global::System.Action(Test));
|
|
}
|
|
""";
|
|
|
|
VerifyGenerateSources(source, new[] { new RelayCommandGenerator() }, ("MyViewModel.Test.g.cs", result));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates the requested sources
|
|
/// </summary>
|
|
/// <param name="source">The input source to process.</param>
|
|
/// <param name="generators">The generators to apply to the input syntax tree.</param>
|
|
/// <param name="results">The source files to compare.</param>
|
|
private static void VerifyGenerateSources(string source, IIncrementalGenerator[] generators, params (string Filename, string Text)[] results)
|
|
{
|
|
VerifyGenerateSources(source, generators, LanguageVersion.CSharp10, results);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates the requested sources
|
|
/// </summary>
|
|
/// <param name="source">The input source to process.</param>
|
|
/// <param name="generators">The generators to apply to the input syntax tree.</param>
|
|
/// <param name="languageVersion">The language version to use.</param>
|
|
/// <param name="results">The source files to compare.</param>
|
|
private static void VerifyGenerateSources(string source, IIncrementalGenerator[] generators, LanguageVersion languageVersion, params (string Filename, string Text)[] results)
|
|
{
|
|
// Ensure CommunityToolkit.Mvvm and System.ComponentModel.DataAnnotations are loaded
|
|
Type observableObjectType = typeof(ObservableObject);
|
|
Type validationAttributeType = typeof(ValidationAttribute);
|
|
|
|
// Get all assembly references for the loaded assemblies (easy way to pull in all necessary dependencies)
|
|
IEnumerable<MetadataReference> references =
|
|
from assembly in AppDomain.CurrentDomain.GetAssemblies()
|
|
where !assembly.IsDynamic
|
|
let reference = MetadataReference.CreateFromFile(assembly.Location)
|
|
select reference;
|
|
|
|
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, CSharpParseOptions.Default.WithLanguageVersion(languageVersion));
|
|
|
|
// Create a syntax tree with the input source
|
|
CSharpCompilation compilation = CSharpCompilation.Create(
|
|
"original",
|
|
new SyntaxTree[] { syntaxTree },
|
|
references,
|
|
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
|
|
|
|
GeneratorDriver driver = CSharpGeneratorDriver.Create(generators).WithUpdatedParseOptions((CSharpParseOptions)syntaxTree.Options);
|
|
|
|
// Run all source generators on the input source code
|
|
_ = driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation outputCompilation, out ImmutableArray<Diagnostic> diagnostics);
|
|
|
|
// Ensure that no diagnostics were generated
|
|
CollectionAssert.AreEquivalent(Array.Empty<Diagnostic>(), diagnostics);
|
|
|
|
foreach ((string filename, string text) in results)
|
|
{
|
|
string filePath = filename;
|
|
|
|
// Update the assembly version using the version from the assembly of the input generators.
|
|
// This allows the tests to not need updates whenever the version of the MVVM Toolkit changes.
|
|
string expectedText = text.Replace("<ASSEMBLY_VERSION>", $"\"{generators[0].GetType().Assembly.GetName().Version}\"");
|
|
|
|
#if !ROSLYN_4_3_1_OR_GREATER
|
|
// Adjust the filenames for the legacy Roslyn 4.0
|
|
filePath = filePath.Replace('`', '_');
|
|
#endif
|
|
|
|
SyntaxTree generatedTree = outputCompilation.SyntaxTrees.Single(tree => Path.GetFileName(tree.FilePath) == filePath);
|
|
|
|
Assert.AreEqual(expectedText, generatedTree.ToString());
|
|
}
|
|
|
|
GC.KeepAlive(observableObjectType);
|
|
GC.KeepAlive(validationAttributeType);
|
|
}
|
|
}
|