diff --git a/.editorconfig b/.editorconfig index f1d3d943..392b891b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -526,4 +526,116 @@ end_of_line = lf [*.{cmd, bat}] end_of_line = crlf -vsspell_dictionary_languages = en-US \ No newline at end of file +vsspell_dictionary_languages = en-US +[*.{cs,vb}] +############################################# +# NUnit Analyzers — enable all as errors +############################################# + +# Structure Rules (NUnit1001 - ) +dotnet_diagnostic.NUnit1001.severity = error # TestCase args must match parameter types +dotnet_diagnostic.NUnit1002.severity = error # TestCaseSource should use nameof +dotnet_diagnostic.NUnit1003.severity = error # TestCase provided too few arguments +dotnet_diagnostic.NUnit1004.severity = error # TestCase provided too many arguments +dotnet_diagnostic.NUnit1005.severity = error # ExpectedResult type must match return type +dotnet_diagnostic.NUnit1006.severity = error # ExpectedResult must not be used on void methods +dotnet_diagnostic.NUnit1007.severity = error # Non-void method but no ExpectedResult provided +dotnet_diagnostic.NUnit1008.severity = error # ParallelScope.Self at assembly level has no effect +dotnet_diagnostic.NUnit1009.severity = error # ParallelScope.Children on non-parameterized test +dotnet_diagnostic.NUnit1010.severity = error # ParallelScope.Fixtures on a test method +dotnet_diagnostic.NUnit1011.severity = error # TestCaseSource member does not exist +dotnet_diagnostic.NUnit1012.severity = error # async test method must have non-void return type +dotnet_diagnostic.NUnit1013.severity = error # async method must use non-generic Task when no result +dotnet_diagnostic.NUnit1014.severity = error # async method must use Task when result expected +dotnet_diagnostic.NUnit1015.severity = error # Source type does not implement I(Async)Enumerable +dotnet_diagnostic.NUnit1016.severity = error # Source type lacks default constructor +dotnet_diagnostic.NUnit1017.severity = error # Specified source is not static +dotnet_diagnostic.NUnit1018.severity = error # TestCaseSource param count mismatch (target method) +dotnet_diagnostic.NUnit1019.severity = error # Source does not return I(Async)Enumerable +dotnet_diagnostic.NUnit1020.severity = error # Parameters provided to field/property source +dotnet_diagnostic.NUnit1021.severity = error # ValueSource should use nameof +dotnet_diagnostic.NUnit1022.severity = error # Specified ValueSource is not static +dotnet_diagnostic.NUnit1023.severity = error # ValueSource cannot supply required parameters +dotnet_diagnostic.NUnit1024.severity = error # ValueSource does not return I(Async)Enumerable +dotnet_diagnostic.NUnit1025.severity = error # ValueSource member does not exist +dotnet_diagnostic.NUnit1026.severity = error # Test or setup/teardown method is not public +dotnet_diagnostic.NUnit1027.severity = error # Test method has parameters but no arguments supplied +dotnet_diagnostic.NUnit1028.severity = error # Non-test method is public +dotnet_diagnostic.NUnit1029.severity = error # TestCaseSource param count mismatch (Test method) +dotnet_diagnostic.NUnit1030.severity = error # TestCaseSource parameter type mismatch (Test method) +dotnet_diagnostic.NUnit1031.severity = error # ValuesAttribute args must match parameter types +dotnet_diagnostic.NUnit1032.severity = error # IDisposable field/property should be disposed in TearDown +dotnet_diagnostic.NUnit1033.severity = error # TestContext.Write methods will be obsolete +dotnet_diagnostic.NUnit1034.severity = error # Base TestFixtures should be abstract +dotnet_diagnostic.NUnit1035.severity = error # Range 'step' parameter cannot be zero +dotnet_diagnostic.NUnit1036.severity = error # Range: from < to when step is positive +dotnet_diagnostic.NUnit1037.severity = error # Range: from > to when step is negative +dotnet_diagnostic.NUnit1038.severity = error # Attribute values' types must match parameter type + +# Assertion Rules (NUnit2001 - ) +dotnet_diagnostic.NUnit2001.severity = error # Prefer Assert.That(..., Is.False) over ClassicAssert.False +dotnet_diagnostic.NUnit2002.severity = error # Prefer Assert.That(..., Is.False) over ClassicAssert.IsFalse +dotnet_diagnostic.NUnit2003.severity = error # Prefer Assert.That(..., Is.True) over ClassicAssert.IsTrue +dotnet_diagnostic.NUnit2004.severity = error # Prefer Assert.That(..., Is.True) over ClassicAssert.True +dotnet_diagnostic.NUnit2005.severity = error # Prefer Is.EqualTo over AreEqual +dotnet_diagnostic.NUnit2006.severity = error # Prefer Is.Not.EqualTo over AreNotEqual +dotnet_diagnostic.NUnit2007.severity = error # Actual value should not be a constant +dotnet_diagnostic.NUnit2008.severity = error # Incorrect IgnoreCase usage +dotnet_diagnostic.NUnit2009.severity = error # Same value used for actual and expected +dotnet_diagnostic.NUnit2010.severity = error # Use EqualConstraint for better messages +dotnet_diagnostic.NUnit2011.severity = error # Use ContainsConstraint for better messages +dotnet_diagnostic.NUnit2012.severity = error # Use StartsWithConstraint for better messages +dotnet_diagnostic.NUnit2013.severity = error # Use EndsWithConstraint for better messages +dotnet_diagnostic.NUnit2014.severity = error # Use SomeItemsConstraint for better messages +dotnet_diagnostic.NUnit2015.severity = error # Prefer Is.SameAs over AreSame +dotnet_diagnostic.NUnit2016.severity = error # Prefer Is.Null over ClassicAssert.Null +dotnet_diagnostic.NUnit2017.severity = error # Prefer Is.Null over ClassicAssert.IsNull +dotnet_diagnostic.NUnit2018.severity = error # Prefer Is.Not.Null over ClassicAssert.NotNull +dotnet_diagnostic.NUnit2019.severity = error # Prefer Is.Not.Null over ClassicAssert.IsNotNull +dotnet_diagnostic.NUnit2020.severity = error # Incompatible types for SameAs constraint +dotnet_diagnostic.NUnit2021.severity = error # Incompatible types for EqualTo constraint +dotnet_diagnostic.NUnit2022.severity = error # Missing property required for constraint +dotnet_diagnostic.NUnit2023.severity = error # Invalid NullConstraint usage +dotnet_diagnostic.NUnit2024.severity = error # Wrong actual type with String constraint +dotnet_diagnostic.NUnit2025.severity = error # Wrong actual type with ContainsConstraint +dotnet_diagnostic.NUnit2026.severity = error # Wrong actual type with SomeItems+EqualConstraint +dotnet_diagnostic.NUnit2027.severity = error # Prefer Is.GreaterThan over ClassicAssert.Greater +dotnet_diagnostic.NUnit2028.severity = error # Prefer Is.GreaterThanOrEqualTo over GreaterOrEqual +dotnet_diagnostic.NUnit2029.severity = error # Prefer Is.LessThan over ClassicAssert.Less +dotnet_diagnostic.NUnit2030.severity = error # Prefer Is.LessThanOrEqualTo over LessOrEqual +dotnet_diagnostic.NUnit2031.severity = error # Prefer Is.Not.SameAs over AreNotSame +dotnet_diagnostic.NUnit2032.severity = error # Prefer Is.Zero over ClassicAssert.Zero +dotnet_diagnostic.NUnit2033.severity = error # Prefer Is.Not.Zero over ClassicAssert.NotZero +dotnet_diagnostic.NUnit2034.severity = error # Prefer Is.NaN over ClassicAssert.IsNaN +dotnet_diagnostic.NUnit2035.severity = error # Prefer Is.Empty over ClassicAssert.IsEmpty +dotnet_diagnostic.NUnit2036.severity = error # Prefer Is.Not.Empty over ClassicAssert.IsNotEmpty +dotnet_diagnostic.NUnit2037.severity = error # Prefer Does.Contain over ClassicAssert.Contains +dotnet_diagnostic.NUnit2038.severity = error # Prefer Is.InstanceOf over ClassicAssert.IsInstanceOf +dotnet_diagnostic.NUnit2039.severity = error # Prefer Is.Not.InstanceOf over ClassicAssert.IsNotInstanceOf +dotnet_diagnostic.NUnit2040.severity = error # Non-reference types for SameAs constraint +dotnet_diagnostic.NUnit2041.severity = error # Incompatible types for comparison constraint +dotnet_diagnostic.NUnit2042.severity = error # Comparison constraint on object +dotnet_diagnostic.NUnit2043.severity = error # Use ComparisonConstraint for better messages +dotnet_diagnostic.NUnit2044.severity = error # Non-delegate actual parameter +dotnet_diagnostic.NUnit2045.severity = error # Use Assert.EnterMultipleScope or Assert.Multiple +dotnet_diagnostic.NUnit2046.severity = error # Use CollectionConstraint for better messages +dotnet_diagnostic.NUnit2047.severity = error # Incompatible types for Within constraint +dotnet_diagnostic.NUnit2048.severity = error # Prefer Assert.That over StringAssert +dotnet_diagnostic.NUnit2049.severity = error # Prefer Assert.That over CollectionAssert +dotnet_diagnostic.NUnit2050.severity = error # NUnit 4 no longer supports string.Format spec +dotnet_diagnostic.NUnit2051.severity = error # Prefer Is.Positive over ClassicAssert.Positive +dotnet_diagnostic.NUnit2052.severity = error # Prefer Is.Negative over ClassicAssert.Negative +dotnet_diagnostic.NUnit2053.severity = error # Prefer Is.AssignableFrom over ClassicAssert.IsAssignableFrom +dotnet_diagnostic.NUnit2054.severity = error # Prefer Is.Not.AssignableFrom over ClassicAssert.IsNotAssignableFrom +dotnet_diagnostic.NUnit2055.severity = error # Prefer Is.InstanceOf over 'is T' expression +dotnet_diagnostic.NUnit2056.severity = error # Prefer Assert.EnterMultipleScope statement over Multiple + +# Suppressor Rules (NUnit3001 - ) +dotnet_diagnostic.NUnit3001.severity = error # Expression checked in NotNull/IsNotNull/Assert.That +dotnet_diagnostic.NUnit3002.severity = error # Field/Property initialized in SetUp/OneTimeSetUp +dotnet_diagnostic.NUnit3003.severity = error # TestFixture instantiated via reflection +dotnet_diagnostic.NUnit3004.severity = error # Field should be disposed in TearDown/OneTimeTearDown + +# Style Rules (NUnit4001 - ) +dotnet_diagnostic.NUnit4001.severity = error # Simplify the Values attribute +dotnet_diagnostic.NUnit4002.severity = error # Use Specific constraint diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 9ef9b377..0f5392bd 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -6,9 +6,17 @@ on: pull_request: branches: [ main ] +# Needed so the reusable workflow can optionally delete the temp per-OS artifacts it creates. +permissions: + contents: read + actions: write + jobs: build: uses: reactiveui/actions-common/.github/workflows/workflow-common-setup-and-build.yml@main with: configuration: Release productNamespacePrefix: "ReactiveUI.Validation" + installWorkloads: true + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 49d444c8..4d51c4f3 100644 --- a/.gitignore +++ b/.gitignore @@ -363,3 +363,5 @@ src/*.Tests/API/*.received.txt # Fody Weavers (for tests) src/Tools/ +.dotnet/ +dotnet-install.sh diff --git a/NUNIT_MIGRATION.md b/NUNIT_MIGRATION.md new file mode 100644 index 00000000..2b47fde6 --- /dev/null +++ b/NUNIT_MIGRATION.md @@ -0,0 +1,123 @@ +# Migration to NUnit 4.4.0 - Summary + +## Overview +Successfully migrated ReactiveUI.Validation test suite from xUnit + FluentAssertions to NUnit 4.4.0. + +## Changes Made + +### 1. Package Updates (ReactiveUI.Validation.Tests.csproj) +**Removed:** +- `xunit` (2.9.3) +- `xunit.runner.visualstudio` (3.1.4) +- `xunit.runner.console` (2.9.3) +- `Xunit.StaFact` (1.2.69) +- `FluentAssertions` (8.6.0) +- `Verify.Xunit` (30.10.0) + +**Added:** +- `NUnit` (4.4.0) +- `NUnit3TestAdapter` (5.*) +- `Verify.NUnit` (30.*) + +**Updated:** +- `DiffEngine` (16.2.3 → 16.*) +- `Microsoft.NET.Test.Sdk` (already at 17.14.1) + +### 2. Parallelization Configuration + +**Created `AssemblyInfo.Parallel.cs`:** +```csharp +[assembly: Parallelizable(ParallelScope.Fixtures)] +[assembly: LevelOfParallelism(4)] +``` +- Tests run sequentially within each fixture +- Parallel execution across different fixtures +- 4 parallel workers + +**Created `tests.runsettings`:** +```xml + + + 4 + + +``` + +### 3. Test File Migrations + +All test files migrated from xUnit to NUnit: + +1. **ApiApprovalTests.cs** - Changed from `[Fact]` to `[Test]`, updated to use `Verify.NUnit` +2. **ApiExtensions.cs** - Updated imports to use `VerifyNUnit` +3. **MemoryLeakTests.cs** - Converted assertions, replaced `ITestOutputHelper` with `TestContext.WriteLine` +4. **NotifyDataErrorInfoTests.cs** - Converted all xUnit assertions to NUnit `Assert.That()` style +5. **ObservableValidationTests.cs** - Added `[SetUp]` method, converted field initialization +6. **PropertyValidationTests.cs** - Converted assertions, added `Assert.Multiple()` for grouped assertions +7. **ValidationBindingTests.cs** - Converted all assertions to NUnit constraints +8. **ValidationContextTests.cs** - Converted assertions with extensive use of `Assert.Multiple()` +9. **ValidationTextTests.cs** - Converted assertions with `Assert.Multiple()` + +### 4. Assertion Conversions + +**From xUnit/FluentAssertions to NUnit:** + +| Old Syntax | New Syntax | +|------------|-----------| +| `Assert.True(x)` | `Assert.That(x, Is.True)` | +| `Assert.False(x)` | `Assert.That(x, Is.False)` | +| `Assert.Equal(a, b)` | `Assert.That(b, Is.EqualTo(a))` | +| `Assert.Same(a, b)` | `Assert.That(b, Is.SameAs(a))` | +| `Assert.Null(x)` | `Assert.That(x, Is.Null)` | +| `Assert.Empty(x)` | `Assert.That(x, Is.Empty)` | +| `Assert.Single(x)` | `Assert.That(x, Has.Count.EqualTo(1))` | +| `x.Should().Be(y)` | `Assert.That(x, Is.EqualTo(y))` | +| `x.Should().BeTrue()` | `Assert.That(x, Is.True)` | +| `x.Should().BeFalse()` | `Assert.That(x, Is.False)` | + +**Key Improvements:** +- Used `Assert.Multiple()` to group related assertions +- Used proper NUnit constraints (e.g., `Is.GreaterThan`, `Is.Empty`, `Has.Count`) +- Converted comparison operations to use `.Count()` where needed for `IEnumerable` + +### 5. Files Removed +- `xunit.runner.json` - No longer needed with NUnit + +## Test Results + +### All Tests Passing ✅ +``` +Passed! - Failed: 0, Passed: 68, Skipped: 0, Total: 68 +``` + +**Tested on:** +- ✅ net8.0 (Duration: 368 ms) +- ✅ net9.0 (Duration: 589 ms) + +## Key Takeaways + +1. **Parallelization Strategy**: Tests within each fixture run sequentially (to handle ReactiveUI's static state), but different fixtures can run in parallel. + +2. **Assert.Multiple()**: Extensively used for grouping related assertions, improving test readability and failure reporting. + +3. **Constraint Model**: Full adoption of NUnit's constraint model (`Assert.That()`) provides more readable and maintainable test code. + +4. **No Breaking Changes**: All existing test logic preserved; only the testing framework and assertion syntax changed. + +5. **Performance**: Similar test execution times compared to xUnit baseline. + +## Running Tests + +**Standard run:** +```bash +dotnet test --settings tests.runsettings +``` + +**Force full serialization (if needed):** +```bash +dotnet test -- NUnit.NumberOfTestWorkers=1 +``` + +**Target specific framework:** +```bash +dotnet test -f net8.0 --settings tests.runsettings +``` diff --git a/samples/Directory.build.props b/samples/Directory.build.props index a2dc388c..46d8078c 100644 --- a/samples/Directory.build.props +++ b/samples/Directory.build.props @@ -6,7 +6,7 @@ - + diff --git a/samples/LoginApp.Wpf/LoginApp.Wpf.csproj b/samples/LoginApp.Wpf/LoginApp.Wpf.csproj index c9607ccb..fecf55ca 100644 --- a/samples/LoginApp.Wpf/LoginApp.Wpf.csproj +++ b/samples/LoginApp.Wpf/LoginApp.Wpf.csproj @@ -10,7 +10,7 @@ - + diff --git a/samples/LoginApp/LoginApp.csproj b/samples/LoginApp/LoginApp.csproj index e7de0059..96f5fc3d 100644 --- a/samples/LoginApp/LoginApp.csproj +++ b/samples/LoginApp/LoginApp.csproj @@ -10,11 +10,11 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/Directory.build.props b/src/Directory.Build.props similarity index 62% rename from src/Directory.build.props rename to src/Directory.Build.props index 878774d7..7f2c2749 100644 --- a/src/Directory.build.props +++ b/src/Directory.Build.props @@ -1,11 +1,15 @@ - + - Copyright (c) .NET Foundation and Contributors + true + $(WarningsAsErrors);nullable + preview + enable + Copyright © 2019-$(CompanyName) $([System.DateTime]::Now.ToString(`yyyy`)) ReactiveUI and Contributors ReactiveUI.Validations ($(TargetFramework)) MIT https://github.com/reactiveui/ReactiveUI.Validation/ https://github.com/reactiveui/ReactiveUI.Validation/blob/master/media/logo.png?raw=true - .NET Foundation and Contributors + ReactiveUI and Contributors Validations library for ReactiveUI. xanaisbettsx;ghuntley reactiveui;validation library;reactive programming;netcore;netstandard;netframework @@ -24,12 +28,28 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb CS8600;CS8602;CS8603;CS8604;CS8605;CS8606;CS8607;CS8608;CS8609;CS8610;CS8611;CS8612;CS8613;CS8614;CS8615;CS8616;CS8617;CS8618;CS8619;CS8620;CS8621;CS8622;CS8623;CS8624;CS8625 - preview True latest - $(NoWarn);IDE1006;IDE0130 + $(NoWarn);IDE1006;IDE0130 + + + + net8.0;net9.0;net10.0;netstandard2.0 + net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0;net10.0-windows10.0.19041.0 + net9.0-android;net10.0-android + net9.0-ios;net9.0-tvos;net9.0-macos;net9.0-maccatalyst;net10.0-ios;net10.0-tvos;net10.0-macos;net10.0-maccatalyst + net462;net472 + net8.0;net9.0;net10.0 + + $(MauiAndroidTargets);$(MauiAppleTargets) + $(MauiAndroidTargets);$(MauiAppleTargets) + $(MauiAndroidTargets) + + $(NetCorePlatforms);$(NetWindowsPlatforms);$(CreatableMauiTargets);$(NetFrameworkPlatforms) + + @@ -40,9 +60,9 @@ - + - + diff --git a/src/Directory.build.targets b/src/Directory.Build.targets similarity index 100% rename from src/Directory.build.targets rename to src/Directory.Build.targets diff --git a/src/ReactiveUI.Validation.AndroidX/Extensions/ViewForExtensions.cs b/src/ReactiveUI.Validation.AndroidX/Extensions/ViewForExtensions.cs index 259496b4..0982fbce 100644 --- a/src/ReactiveUI.Validation.AndroidX/Extensions/ViewForExtensions.cs +++ b/src/ReactiveUI.Validation.AndroidX/Extensions/ViewForExtensions.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation.AndroidX/ReactiveUI.Validation.AndroidX.csproj b/src/ReactiveUI.Validation.AndroidX/ReactiveUI.Validation.AndroidX.csproj index c19d6ed3..3415120b 100644 --- a/src/ReactiveUI.Validation.AndroidX/ReactiveUI.Validation.AndroidX.csproj +++ b/src/ReactiveUI.Validation.AndroidX/ReactiveUI.Validation.AndroidX.csproj @@ -1,7 +1,7 @@  - net8.0-android;net9.0-android + $(MauiAndroidTargets) Provides ReactiveUI.Validation extensions for the AndroidX Library ReactiveUI.Validation.AndroidX $(NoWarn);CS1591 @@ -9,7 +9,8 @@ - + + diff --git a/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet10_0.verified.txt b/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet10_0.verified.txt new file mode 100644 index 00000000..73f9f14a --- /dev/null +++ b/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet10_0.verified.txt @@ -0,0 +1,335 @@ +[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v10.0", FrameworkDisplayName=".NET 10.0")] +namespace ReactiveUI.Validation.Abstractions +{ + public interface IValidatableViewModel + { + ReactiveUI.Validation.Contexts.IValidationContext ValidationContext { get; } + } +} +namespace ReactiveUI.Validation.Collections +{ + public interface IValidationText : System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.IEnumerable + { + string ToSingleLine(string? separator = ","); + } + public static class ValidationText + { + public static readonly ReactiveUI.Validation.Collections.IValidationText Empty; + public static readonly ReactiveUI.Validation.Collections.IValidationText None; + public static ReactiveUI.Validation.Collections.IValidationText Create(System.Collections.Generic.IEnumerable? validationTexts) { } + public static ReactiveUI.Validation.Collections.IValidationText Create(System.Collections.Generic.IEnumerable? validationTexts) { } + public static ReactiveUI.Validation.Collections.IValidationText Create(string? validationText) { } + public static ReactiveUI.Validation.Collections.IValidationText Create(params string?[]? validationTexts) { } + } +} +namespace ReactiveUI.Validation.Comparators +{ + public class ValidationStateComparer : System.Collections.Generic.EqualityComparer + { + public ValidationStateComparer() { } + public override bool Equals(ReactiveUI.Validation.States.IValidationState? x, ReactiveUI.Validation.States.IValidationState? y) { } + public override int GetHashCode(ReactiveUI.Validation.States.IValidationState obj) { } + } +} +namespace ReactiveUI.Validation.Components.Abstractions +{ + public interface IPropertyValidationComponent : ReactiveUI.Validation.Components.Abstractions.IValidatesProperties, ReactiveUI.Validation.Components.Abstractions.IValidationComponent { } + public interface IValidatesProperties + { + System.Collections.Generic.IEnumerable Properties { get; } + int PropertyCount { get; } + bool ContainsPropertyName(string propertyName, bool exclusively = false); + } + public interface IValidationComponent + { + bool IsValid { get; } + ReactiveUI.Validation.Collections.IValidationText? Text { get; } + System.IObservable ValidationStatusChange { get; } + } +} +namespace ReactiveUI.Validation.Components +{ + public abstract class BasePropertyValidation : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Components.Abstractions.IPropertyValidationComponent, ReactiveUI.Validation.Components.Abstractions.IValidatesProperties, ReactiveUI.Validation.Components.Abstractions.IValidationComponent, System.IDisposable + { + protected BasePropertyValidation() { } + public bool IsValid { get; } + public System.Collections.Generic.IEnumerable Properties { get; } + public int PropertyCount { get; } + public ReactiveUI.Validation.Collections.IValidationText? Text { get; } + public System.IObservable ValidationStatusChange { get; } + protected void AddProperty(System.Linq.Expressions.Expression> property) { } + public bool ContainsPropertyName(string propertyName, bool exclusively = false) { } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + protected abstract System.IObservable GetValidationChangeObservable(); + } + public sealed class BasePropertyValidation : ReactiveUI.Validation.Components.BasePropertyValidation + { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public BasePropertyValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isValidFunc, System.Func message) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public BasePropertyValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isValidFunc, System.Func messageFunc) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public BasePropertyValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isValidFunc, string message) { } + protected override void Dispose(bool disposing) { } + protected override System.IObservable GetValidationChangeObservable() { } + } + public abstract class ObservableValidationBase : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Components.Abstractions.IPropertyValidationComponent, ReactiveUI.Validation.Components.Abstractions.IValidatesProperties, ReactiveUI.Validation.Components.Abstractions.IValidationComponent, System.IDisposable + { + protected ObservableValidationBase(System.IObservable observable) { } + protected ObservableValidationBase(TViewModel viewModel, System.IObservable observable, System.Func isValidFunc, System.Func messageFunc) { } + public bool IsValid { get; } + public System.Collections.Generic.IEnumerable Properties { get; } + public int PropertyCount { get; } + public ReactiveUI.Validation.Collections.IValidationText? Text { get; } + public System.IObservable ValidationStatusChange { get; } + protected void AddProperty(System.Linq.Expressions.Expression> property) { } + public bool ContainsPropertyName(string propertyName, bool exclusively = false) { } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + } + public sealed class ObservableValidation : ReactiveUI.Validation.Components.ObservableValidationBase + { + public ObservableValidation(System.IObservable observable) { } + public ObservableValidation(TViewModel viewModel, System.IObservable observable, System.Func isValidFunc, System.Func messageFunc) { } + public ObservableValidation(TViewModel viewModel, System.IObservable observable, System.Func isValidFunc, string message) { } + public ObservableValidation(TViewModel viewModel, System.IObservable observable, System.Func isValidFunc, System.Func messageFunc) { } + public ObservableValidation(TViewModel viewModel, System.IObservable observable, System.Func isValidFunc, System.Func messageFunc) { } + public ObservableValidation(TViewModel viewModel, System.IObservable observable, System.Func isValidFunc, string message) { } + } + public sealed class ObservableValidation : ReactiveUI.Validation.Components.ObservableValidationBase + { + public ObservableValidation(System.Linq.Expressions.Expression> viewModelProperty, System.IObservable observable) { } + public ObservableValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable observable, System.Func isValidFunc, System.Func messageFunc) { } + public ObservableValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable observable, System.Func isValidFunc, string message) { } + public ObservableValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable observable, System.Func isValidFunc, System.Func messageFunc) { } + public ObservableValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable observable, System.Func isValidFunc, System.Func messageFunc) { } + public ObservableValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable observable, System.Func isValidFunc, string message) { } + } +} +namespace ReactiveUI.Validation.Contexts +{ + public interface IValidationContext : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Components.Abstractions.IValidationComponent, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.INotifyPropertyChanging, System.IDisposable, System.Reactive.Disposables.ICancelable + { + System.IObservable Valid { get; } + DynamicData.IObservableList Validations { get; } + void Add(ReactiveUI.Validation.Components.Abstractions.IValidationComponent validation); + bool GetIsValid(); + void Remove(ReactiveUI.Validation.Components.Abstractions.IValidationComponent validation); + void RemoveMany(System.Collections.Generic.IEnumerable validations); + } + public class ValidationContext : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Components.Abstractions.IValidationComponent, ReactiveUI.Validation.Contexts.IValidationContext, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.INotifyPropertyChanging, System.IDisposable, System.Reactive.Disposables.ICancelable + { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public ValidationContext(System.Reactive.Concurrency.IScheduler? scheduler = null) { } + public bool IsDisposed { get; } + public bool IsValid { get; } + public ReactiveUI.Validation.Collections.IValidationText Text { get; } + public System.IObservable Valid { get; } + public System.IObservable ValidationStatusChange { get; } + public DynamicData.IObservableList Validations { get; } + public void Add(ReactiveUI.Validation.Components.Abstractions.IValidationComponent validation) { } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + public bool GetIsValid() { } + public void Remove(ReactiveUI.Validation.Components.Abstractions.IValidationComponent validation) { } + public void RemoveMany(System.Collections.Generic.IEnumerable validations) { } + } +} +namespace ReactiveUI.Validation.Extensions +{ + public static class ValidatableViewModelExtensions + { + public static void ClearValidationRules(this TViewModel viewModel) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + public static void ClearValidationRules(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + public static System.IObservable IsValid(this TViewModel viewModel) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable, string message) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel + where TValue : ReactiveUI.Validation.States.IValidationState { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable validationObservable) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable, System.Func isValidFunc, System.Func messageFunc) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable viewModelObservable, string message) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isPropertyValid, System.Func message) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isPropertyValid, string message) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable validationObservable) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel + where TValue : ReactiveUI.Validation.States.IValidationState { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable viewModelObservable, System.Func isValidFunc, System.Func messageFunc) + where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + } + public static class ValidatesPropertiesExtensions + { + public static bool ContainsProperty(this ReactiveUI.Validation.Components.Abstractions.IValidatesProperties validatesProperties, System.Linq.Expressions.Expression> propertyExpression, bool exclusively = false) { } + } + public static class ValidationContextExtensions + { + public static System.IObservable> ObserveFor(this ReactiveUI.Validation.Contexts.IValidationContext context, System.Linq.Expressions.Expression> viewModelProperty, bool strict = true) { } + } + public static class ViewForExtensions + { + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static System.IDisposable BindValidation(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) + where TView : ReactiveUI.IViewFor + where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static System.IDisposable BindValidation(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> viewModelHelperProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) + where TView : ReactiveUI.IViewFor + where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static System.IDisposable BindValidation(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) + where TView : ReactiveUI.IViewFor + where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + } +} +namespace ReactiveUI.Validation.Formatters.Abstractions +{ + public interface IValidationTextFormatter + { + TOut Format(ReactiveUI.Validation.Collections.IValidationText validationText); + } +} +namespace ReactiveUI.Validation.Formatters +{ + public class SingleLineFormatter : ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter + { + public SingleLineFormatter(string? separator = null) { } + public static ReactiveUI.Validation.Formatters.SingleLineFormatter Default { get; } + public string Format(ReactiveUI.Validation.Collections.IValidationText? validationText) { } + } +} +namespace ReactiveUI.Validation.Helpers +{ + public abstract class ReactiveValidationObject : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel, System.ComponentModel.INotifyDataErrorInfo, System.IDisposable + { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + protected ReactiveValidationObject(System.Reactive.Concurrency.IScheduler? scheduler = null, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) { } + public bool HasErrors { get; } + public ReactiveUI.Validation.Contexts.IValidationContext ValidationContext { get; } + public event System.EventHandler? ErrorsChanged; + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + public virtual System.Collections.IEnumerable GetErrors(string? propertyName) { } + protected void RaiseErrorsChanged(string propertyName = "") { } + } + public class ValidationHelper : ReactiveUI.ReactiveObject, System.IDisposable + { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public ValidationHelper(ReactiveUI.Validation.Components.Abstractions.IValidationComponent validation, System.IDisposable? cleanup = null) { } + public bool IsValid { get; } + public ReactiveUI.Validation.Collections.IValidationText Message { get; } + public System.IObservable ValidationChanged { get; } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + } +} +namespace ReactiveUI.Validation.States +{ + public interface IValidationState + { + bool IsValid { get; } + ReactiveUI.Validation.Collections.IValidationText Text { get; } + } + public class ValidationState : ReactiveUI.Validation.States.IValidationState + { + public static readonly ReactiveUI.Validation.States.IValidationState Valid; + public ValidationState(bool isValid, ReactiveUI.Validation.Collections.IValidationText text) { } + public ValidationState(bool isValid, string text) { } + public bool IsValid { get; } + public ReactiveUI.Validation.Collections.IValidationText Text { get; } + } +} +namespace ReactiveUI.Validation.ValidationBindings.Abstractions +{ + public interface IValidationBinding : System.IDisposable { } +} +namespace ReactiveUI.Validation.ValidationBindings +{ + public sealed class ValidationBinding : ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding, System.IDisposable + { + public void Dispose() { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForProperty(TView view, System.Linq.Expressions.Expression> viewModelProperty, System.Action, System.Collections.Generic.IList> action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter formatter, bool strict = true) + where TView : ReactiveUI.IViewFor + where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForProperty(TView view, System.Linq.Expressions.Expression> viewModelProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null, bool strict = true) + where TView : ReactiveUI.IViewFor + where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForValidationHelperProperty(TView view, System.Linq.Expressions.Expression> viewModelHelperProperty, System.Action action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter formatter) + where TView : ReactiveUI.IViewFor + where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForValidationHelperProperty(TView view, System.Linq.Expressions.Expression> viewModelHelperProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) + where TView : ReactiveUI.IViewFor + where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForViewModel(TView view, System.Action action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter formatter) + where TView : ReactiveUI.IViewFor + where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] + public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForViewModel(TView view, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) + where TView : ReactiveUI.IViewFor + where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet8_0.verified.txt b/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet8_0.verified.txt index ad7c7130..3a844a7d 100644 --- a/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet8_0.verified.txt +++ b/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet8_0.verified.txt @@ -65,8 +65,11 @@ namespace ReactiveUI.Validation.Components } public sealed class BasePropertyValidation : ReactiveUI.Validation.Components.BasePropertyValidation { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public BasePropertyValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isValidFunc, System.Func message) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public BasePropertyValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isValidFunc, System.Func messageFunc) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public BasePropertyValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isValidFunc, string message) { } protected override void Dispose(bool disposing) { } protected override System.IObservable GetValidationChangeObservable() { } @@ -117,6 +120,7 @@ namespace ReactiveUI.Validation.Contexts } public class ValidationContext : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Components.Abstractions.IValidationComponent, ReactiveUI.Validation.Contexts.IValidationContext, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.INotifyPropertyChanging, System.IDisposable, System.Reactive.Disposables.ICancelable { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public ValidationContext(System.Reactive.Concurrency.IScheduler? scheduler = null) { } public bool IsDisposed { get; } public bool IsValid { get; } @@ -142,26 +146,56 @@ namespace ReactiveUI.Validation.Extensions where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } public static System.IObservable IsValid(this TViewModel viewModel) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable, string message) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel where TValue : ReactiveUI.Validation.States.IValidationState { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable validationObservable) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable, System.Func isValidFunc, System.Func messageFunc) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable viewModelObservable, string message) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isPropertyValid, System.Func message) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isPropertyValid, string message) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable validationObservable) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel where TValue : ReactiveUI.Validation.States.IValidationState { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable viewModelObservable, System.Func isValidFunc, System.Func messageFunc) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } } @@ -175,12 +209,21 @@ namespace ReactiveUI.Validation.Extensions } public static class ViewForExtensions { + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static System.IDisposable BindValidation(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static System.IDisposable BindValidation(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> viewModelHelperProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static System.IDisposable BindValidation(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } @@ -206,6 +249,7 @@ namespace ReactiveUI.Validation.Helpers { public abstract class ReactiveValidationObject : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel, System.ComponentModel.INotifyDataErrorInfo, System.IDisposable { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] protected ReactiveValidationObject(System.Reactive.Concurrency.IScheduler? scheduler = null, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) { } public bool HasErrors { get; } public ReactiveUI.Validation.Contexts.IValidationContext ValidationContext { get; } @@ -217,6 +261,7 @@ namespace ReactiveUI.Validation.Helpers } public class ValidationHelper : ReactiveUI.ReactiveObject, System.IDisposable { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public ValidationHelper(ReactiveUI.Validation.Components.Abstractions.IValidationComponent validation, System.IDisposable? cleanup = null) { } public bool IsValid { get; } public ReactiveUI.Validation.Collections.IValidationText Message { get; } @@ -250,21 +295,39 @@ namespace ReactiveUI.Validation.ValidationBindings public sealed class ValidationBinding : ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding, System.IDisposable { public void Dispose() { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForProperty(TView view, System.Linq.Expressions.Expression> viewModelProperty, System.Action, System.Collections.Generic.IList> action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter formatter, bool strict = true) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForProperty(TView view, System.Linq.Expressions.Expression> viewModelProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null, bool strict = true) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForValidationHelperProperty(TView view, System.Linq.Expressions.Expression> viewModelHelperProperty, System.Action action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter formatter) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForValidationHelperProperty(TView view, System.Linq.Expressions.Expression> viewModelHelperProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForViewModel(TView view, System.Action action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter formatter) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForViewModel(TView view, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } diff --git a/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet9_0.verified.txt b/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet9_0.verified.txt index 1bd00054..f200898f 100644 --- a/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet9_0.verified.txt +++ b/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.ValidationProject.DotNet9_0.verified.txt @@ -65,8 +65,11 @@ namespace ReactiveUI.Validation.Components } public sealed class BasePropertyValidation : ReactiveUI.Validation.Components.BasePropertyValidation { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public BasePropertyValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isValidFunc, System.Func message) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public BasePropertyValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isValidFunc, System.Func messageFunc) { } + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public BasePropertyValidation(TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isValidFunc, string message) { } protected override void Dispose(bool disposing) { } protected override System.IObservable GetValidationChangeObservable() { } @@ -117,6 +120,7 @@ namespace ReactiveUI.Validation.Contexts } public class ValidationContext : ReactiveUI.ReactiveObject, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Components.Abstractions.IValidationComponent, ReactiveUI.Validation.Contexts.IValidationContext, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged, System.ComponentModel.INotifyPropertyChanging, System.IDisposable, System.Reactive.Disposables.ICancelable { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public ValidationContext(System.Reactive.Concurrency.IScheduler? scheduler = null) { } public bool IsDisposed { get; } public bool IsValid { get; } @@ -142,26 +146,56 @@ namespace ReactiveUI.Validation.Extensions where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } public static System.IObservable IsValid(this TViewModel viewModel) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable, string message) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel where TValue : ReactiveUI.Validation.States.IValidationState { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable validationObservable) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.IObservable validationObservable, System.Func isValidFunc, System.Func messageFunc) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable viewModelObservable, string message) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isPropertyValid, System.Func message) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Func isPropertyValid, string message) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable validationObservable) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel where TValue : ReactiveUI.Validation.States.IValidationState { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.Helpers.ValidationHelper ValidationRule(this TViewModel viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.IObservable viewModelObservable, System.Func isValidFunc, System.Func messageFunc) where TViewModel : ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } } @@ -175,12 +209,21 @@ namespace ReactiveUI.Validation.Extensions } public static class ViewForExtensions { + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static System.IDisposable BindValidation(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static System.IDisposable BindValidation(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> viewModelHelperProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static System.IDisposable BindValidation(this TView view, TViewModel? viewModel, System.Linq.Expressions.Expression> viewModelProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } @@ -206,6 +249,7 @@ namespace ReactiveUI.Validation.Helpers { public abstract class ReactiveValidationObject : ReactiveUI.ReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel, System.ComponentModel.INotifyDataErrorInfo, System.IDisposable { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] protected ReactiveValidationObject(System.Reactive.Concurrency.IScheduler? scheduler = null, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) { } public bool HasErrors { get; } public ReactiveUI.Validation.Contexts.IValidationContext ValidationContext { get; } @@ -217,6 +261,7 @@ namespace ReactiveUI.Validation.Helpers } public class ValidationHelper : ReactiveUI.ReactiveObject, System.IDisposable { + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public ValidationHelper(ReactiveUI.Validation.Components.Abstractions.IValidationComponent validation, System.IDisposable? cleanup = null) { } public bool IsValid { get; } public ReactiveUI.Validation.Collections.IValidationText Message { get; } @@ -250,21 +295,39 @@ namespace ReactiveUI.Validation.ValidationBindings public sealed class ValidationBinding : ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding, System.IDisposable { public void Dispose() { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForProperty(TView view, System.Linq.Expressions.Expression> viewModelProperty, System.Action, System.Collections.Generic.IList> action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter formatter, bool strict = true) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForProperty(TView view, System.Linq.Expressions.Expression> viewModelProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null, bool strict = true) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForValidationHelperProperty(TView view, System.Linq.Expressions.Expression> viewModelHelperProperty, System.Action action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter formatter) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForValidationHelperProperty(TView view, System.Linq.Expressions.Expression> viewModelHelperProperty, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForViewModel(TView view, System.Action action, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter formatter) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT s" + + "cenarios.")] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] public static ReactiveUI.Validation.ValidationBindings.Abstractions.IValidationBinding ForViewModel(TView view, System.Linq.Expressions.Expression> viewProperty, ReactiveUI.Validation.Formatters.Abstractions.IValidationTextFormatter? formatter = null) where TView : ReactiveUI.IViewFor where TViewModel : class, ReactiveUI.IReactiveObject, ReactiveUI.Validation.Abstractions.IValidatableViewModel { } diff --git a/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.cs b/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.cs index 109246a5..f2ea716a 100644 --- a/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.cs +++ b/src/ReactiveUI.Validation.Tests/API/ApiApprovalTests.cs @@ -1,14 +1,14 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; +using NUnit.Framework; using ReactiveUI.Validation.APITests; using ReactiveUI.Validation.ValidationBindings; -using VerifyXunit; -using Xunit; +using VerifyNUnit; namespace ReactiveUI.Validation.Tests.API; @@ -16,12 +16,13 @@ namespace ReactiveUI.Validation.Tests.API; /// Tests to make sure that the API matches the approved ones. /// [ExcludeFromCodeCoverage] +[TestFixture] public class ApiApprovalTests { /// /// Tests to make sure the splat project is approved. /// /// A representing the asynchronous unit test. - [Fact] - public Task ValidationProject() => typeof(ValidationBinding).Assembly.CheckApproval(["ReactiveUI.Validation"]); + [Test] + public Task ValidationProject() => typeof(ValidationBinding).Assembly.CheckApproval([" ReactiveUI.Validation"]); } diff --git a/src/ReactiveUI.Validation.Tests/API/ApiExtensions.cs b/src/ReactiveUI.Validation.Tests/API/ApiExtensions.cs index df6385b1..fd211b86 100644 --- a/src/ReactiveUI.Validation.Tests/API/ApiExtensions.cs +++ b/src/ReactiveUI.Validation.Tests/API/ApiExtensions.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; using System.Threading.Tasks; using PublicApiGenerator; -using VerifyXunit; +using VerifyNUnit; namespace ReactiveUI.Validation.APITests; diff --git a/src/ReactiveUI.Validation.Tests/AssemblyInfo.Parallel.cs b/src/ReactiveUI.Validation.Tests/AssemblyInfo.Parallel.cs new file mode 100644 index 00000000..2095daac --- /dev/null +++ b/src/ReactiveUI.Validation.Tests/AssemblyInfo.Parallel.cs @@ -0,0 +1,9 @@ +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using NUnit.Framework; + +[assembly: Parallelizable(ParallelScope.Fixtures)] +[assembly: LevelOfParallelism(4)] diff --git a/src/ReactiveUI.Validation.Tests/MemoryLeakTests.cs b/src/ReactiveUI.Validation.Tests/MemoryLeakTests.cs index 664e9f20..49d5c288 100644 --- a/src/ReactiveUI.Validation.Tests/MemoryLeakTests.cs +++ b/src/ReactiveUI.Validation.Tests/MemoryLeakTests.cs @@ -1,39 +1,36 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; -using FluentAssertions; using JetBrains.dotMemoryUnit; +using NUnit.Framework; using ReactiveUI.Validation.Tests.Models; -using Xunit; -using Xunit.Abstractions; namespace ReactiveUI.Validation.Tests; /// /// MemoryLeakTests. /// +[TestFixture] public class MemoryLeakTests { /// - /// Initializes a new instance of the class. + /// Sets up the test fixtures. /// - /// The test output helper. - public MemoryLeakTests(ITestOutputHelper testOutputHelper) + [SetUp] + public void SetUp() { - ArgumentNullException.ThrowIfNull(testOutputHelper); - - DotMemoryUnitTestOutput.SetOutputMethod(testOutputHelper.WriteLine); + DotMemoryUnitTestOutput.SetOutputMethod(TestContext.WriteLine); } /// Tests whether the created object can be garbage collected. - [Fact] + [Test] [DotMemoryUnit(FailIfRunWithoutSupport = false)] public void Instance_Released_IsGarbageCollected() { - WeakReference reference = null; + WeakReference? reference = null; new Action( () => { @@ -45,12 +42,16 @@ public void Instance_Released_IsGarbageCollected() // memTest should have gone out of scope about now, so the garbage collector can clean it up dotMemory.Check( - memory => memory.GetObjects( - where => where.Type.Is()).ObjectsCount.Should().Be(0, "it is out of scope")); + memory => Assert.That( + memory.GetObjects(where => where.Type.Is()).ObjectsCount, + Is.Zero, + "it is out of scope")); GC.Collect(); GC.WaitForPendingFinalizers(); - reference.IsAlive.Should().BeFalse("it is garbage collected"); + Assert.That(reference, Is.Not.Null); + + Assert.That(reference.IsAlive, Is.False, "it is garbage collected"); } } diff --git a/src/ReactiveUI.Validation.Tests/Models/ISampleViewModel.cs b/src/ReactiveUI.Validation.Tests/Models/ISampleViewModel.cs index 7acee777..7a1a9576 100644 --- a/src/ReactiveUI.Validation.Tests/Models/ISampleViewModel.cs +++ b/src/ReactiveUI.Validation.Tests/Models/ISampleViewModel.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using ReactiveUI.Validation.Abstractions; diff --git a/src/ReactiveUI.Validation.Tests/Models/IndeiTestView.cs b/src/ReactiveUI.Validation.Tests/Models/IndeiTestView.cs index 5be8519d..ab4569f1 100644 --- a/src/ReactiveUI.Validation.Tests/Models/IndeiTestView.cs +++ b/src/ReactiveUI.Validation.Tests/Models/IndeiTestView.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. namespace ReactiveUI.Validation.Tests.Models; @@ -17,22 +17,22 @@ public class IndeiTestView : IViewFor public IndeiTestView(IndeiTestViewModel viewModel) => ViewModel = viewModel; /// - object IViewFor.ViewModel + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = value as IndeiTestViewModel; } /// - public IndeiTestViewModel ViewModel { get; set; } + public IndeiTestViewModel? ViewModel { get; set; } /// /// Gets or sets the Name Label which emulates a Text property (eg. Entry in Xamarin.Forms). /// - public string NameLabel { get; set; } + public string NameLabel { get; set; } = null!; /// /// Gets or sets the NameError Label which emulates a Text property (eg. Entry in Xamarin.Forms). /// - public string NameErrorLabel { get; set; } -} \ No newline at end of file + public string NameErrorLabel { get; set; } = null!; +} diff --git a/src/ReactiveUI.Validation.Tests/Models/IndeiTestViewModel.cs b/src/ReactiveUI.Validation.Tests/Models/IndeiTestViewModel.cs index 84c2b57c..4ef1cc03 100644 --- a/src/ReactiveUI.Validation.Tests/Models/IndeiTestViewModel.cs +++ b/src/ReactiveUI.Validation.Tests/Models/IndeiTestViewModel.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Reactive.Concurrency; @@ -14,8 +14,8 @@ namespace ReactiveUI.Validation.Tests.Models; /// public class IndeiTestViewModel : ReactiveValidationObject { - private string _name; - private string _otherName; + private string? _name; + private string? _otherName; /// /// Initializes a new instance of the class. @@ -37,7 +37,7 @@ public IndeiTestViewModel(IValidationTextFormatter formatter) /// /// Gets or sets get the Name. /// - public string Name + public string? Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); @@ -46,9 +46,9 @@ public string Name /// /// Gets or sets get the Name. /// - public string OtherName + public string? OtherName { get => _otherName; set => this.RaiseAndSetIfChanged(ref _otherName, value); } -} \ No newline at end of file +} diff --git a/src/ReactiveUI.Validation.Tests/Models/SampleView.cs b/src/ReactiveUI.Validation.Tests/Models/SampleView.cs index 2c18713a..a9d66329 100644 --- a/src/ReactiveUI.Validation.Tests/Models/SampleView.cs +++ b/src/ReactiveUI.Validation.Tests/Models/SampleView.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. namespace ReactiveUI.Validation.Tests.Models; @@ -22,22 +22,19 @@ public class SampleView : IViewFor /// /// Gets or sets the view model of this particular view. /// - public ISampleViewModel ViewModel { get; set; } - + public ISampleViewModel? ViewModel { get; set; } /// /// Gets or sets the name view property. /// - public string NameLabel { get; set; } - + public string NameLabel { get; set; } = null!; /// /// Gets or sets the name error text. /// - public string NameErrorLabel { get; set; } - + public string NameErrorLabel { get; set; } = null!; /// - object IViewFor.ViewModel + object? IViewFor.ViewModel { get => ViewModel; - set => ViewModel = (ISampleViewModel)value; + set => ViewModel = (ISampleViewModel?)value; } -} \ No newline at end of file +} diff --git a/src/ReactiveUI.Validation.Tests/Models/SampleViewModel.cs b/src/ReactiveUI.Validation.Tests/Models/SampleViewModel.cs index 8354ccf6..c7e81503 100644 --- a/src/ReactiveUI.Validation.Tests/Models/SampleViewModel.cs +++ b/src/ReactiveUI.Validation.Tests/Models/SampleViewModel.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Reactive.Concurrency; @@ -13,7 +13,7 @@ namespace ReactiveUI.Validation.Tests.Models; /// public class SampleViewModel : ReactiveValidationObject, ISampleViewModel { - private string _name; + private string _name = null!; /// /// Initializes a new instance of the class. diff --git a/src/ReactiveUI.Validation.Tests/Models/SourceDestinationView.cs b/src/ReactiveUI.Validation.Tests/Models/SourceDestinationView.cs index 1a336986..df16d32b 100644 --- a/src/ReactiveUI.Validation.Tests/Models/SourceDestinationView.cs +++ b/src/ReactiveUI.Validation.Tests/Models/SourceDestinationView.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. namespace ReactiveUI.Validation.Tests.Models; @@ -17,22 +17,22 @@ public class SourceDestinationView : IViewFor public SourceDestinationView(SourceDestinationViewModel viewModel) => ViewModel = viewModel; /// - object IViewFor.ViewModel + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = value as SourceDestinationViewModel; } /// - public SourceDestinationViewModel ViewModel { get; set; } + public SourceDestinationViewModel? ViewModel { get; set; } /// /// Gets or sets the SourceError Label which emulates a Text property (eg. Entry in Xamarin.Forms). /// - public string SourceError { get; set; } + public string SourceError { get; set; } = null!; /// /// Gets or sets the DestinationError Label which emulates a Text property (eg. Entry in Xamarin.Forms). /// - public string DestinationError { get; set; } -} \ No newline at end of file + public string DestinationError { get; set; } = null!; +} diff --git a/src/ReactiveUI.Validation.Tests/Models/SourceDestinationViewModel.cs b/src/ReactiveUI.Validation.Tests/Models/SourceDestinationViewModel.cs index 3189ed0d..ab33fdf5 100644 --- a/src/ReactiveUI.Validation.Tests/Models/SourceDestinationViewModel.cs +++ b/src/ReactiveUI.Validation.Tests/Models/SourceDestinationViewModel.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Reactive.Concurrency; diff --git a/src/ReactiveUI.Validation.Tests/Models/TestClassMemory.cs b/src/ReactiveUI.Validation.Tests/Models/TestClassMemory.cs index 58423e62..91261eb0 100644 --- a/src/ReactiveUI.Validation.Tests/Models/TestClassMemory.cs +++ b/src/ReactiveUI.Validation.Tests/Models/TestClassMemory.cs @@ -1,10 +1,11 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; using System.Reactive.Disposables; +using System.Reactive.Disposables.Fluent; using ReactiveUI.Validation.Extensions; using ReactiveUI.Validation.Helpers; @@ -42,8 +43,7 @@ public TestClassMemory() /// /// The name. /// - public string Name { get; set; } - + public string Name { get; set; } = null!; /// /// Disposes the specified disposing. /// diff --git a/src/ReactiveUI.Validation.Tests/Models/TestControl.cs b/src/ReactiveUI.Validation.Tests/Models/TestControl.cs index 1d678ce8..cb3f3336 100644 --- a/src/ReactiveUI.Validation.Tests/Models/TestControl.cs +++ b/src/ReactiveUI.Validation.Tests/Models/TestControl.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. namespace ReactiveUI.Validation.Tests.Models; @@ -13,5 +13,5 @@ public class TestControl /// /// Gets or sets the text property of the container. /// - public string Text { get; set; } + public string Text { get; set; } = null!; } \ No newline at end of file diff --git a/src/ReactiveUI.Validation.Tests/Models/TestView.cs b/src/ReactiveUI.Validation.Tests/Models/TestView.cs index 8b7cd208..80147fc0 100644 --- a/src/ReactiveUI.Validation.Tests/Models/TestView.cs +++ b/src/ReactiveUI.Validation.Tests/Models/TestView.cs @@ -1,16 +1,18 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. namespace ReactiveUI.Validation.Tests.Models; /// -/// Mocked View. +/// Represents a mocked view used by the tests under ReactiveUI.Validation.Tests.Models. +/// Implements for and exposes +/// simple properties that emulate UI elements used in validation scenarios. /// public class TestView : ReactiveObject, IViewFor { - private TestViewModel _viewModel; + private TestViewModel? _viewModel; /// /// Initializes a new instance of the class. @@ -20,52 +22,54 @@ public TestView() } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with the specified view model. /// - /// ViewModel instance of type . + /// The instance to associate with this view. public TestView(TestViewModel viewModel) => ViewModel = viewModel; /// - object IViewFor.ViewModel + object? IViewFor.ViewModel { get => ViewModel; set => ViewModel = value as TestViewModel; } - /// - public TestViewModel ViewModel + /// + /// Gets or sets the view model associated with this view. + /// + public TestViewModel? ViewModel { get => _viewModel; set => this.RaiseAndSetIfChanged(ref _viewModel, value); } /// - /// Gets or sets the Name Label which emulates a Text property (eg. Entry in Xamarin.Forms). + /// Gets or sets the Name label text which emulates a Text property (e.g., Entry in Xamarin.Forms). /// - public string NameLabel { get; set; } + public string NameLabel { get; set; } = null!; /// - /// Gets or sets the Name2 Label which emulates a Text property (eg. Entry in Xamarin.Forms). + /// Gets or sets the Name2 label text which emulates a Text property (e.g., Entry in Xamarin.Forms). /// - public string Name2Label { get; set; } + public string Name2Label { get; set; } = null!; /// - /// Gets or sets the NameError Label which emulates a Text property (eg. Entry in Xamarin.Forms). + /// Gets or sets the Name error label text which emulates a Text property (e.g., Entry in Xamarin.Forms). /// - public string NameErrorLabel { get; set; } + public string NameErrorLabel { get; set; } = null!; /// - /// Gets or sets the Name2 Error Label which emulates a Text property (eg. Entry in Xamarin.Forms). + /// Gets or sets the Name2 error label text which emulates a Text property (e.g., Entry in Xamarin.Forms). /// - public string Name2ErrorLabel { get; set; } + public string Name2ErrorLabel { get; set; } = null!; /// - /// Gets or sets a value indicating whether the form is valid. + /// Gets or sets a value indicating whether the Name field is valid. /// public bool IsNameValid { get; set; } /// - /// Gets the error label which is represented by a container. + /// Gets the container control for the Name validation error message. /// public TestControl NameErrorContainer { get; } = new(); -} \ No newline at end of file +} diff --git a/src/ReactiveUI.Validation.Tests/Models/TestViewModel.cs b/src/ReactiveUI.Validation.Tests/Models/TestViewModel.cs index cf6c9e46..c1f842a1 100644 --- a/src/ReactiveUI.Validation.Tests/Models/TestViewModel.cs +++ b/src/ReactiveUI.Validation.Tests/Models/TestViewModel.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; @@ -17,14 +17,14 @@ namespace ReactiveUI.Validation.Tests.Models; /// public class TestViewModel : ReactiveObject, IValidatableViewModel { - private ValidationHelper _nameRule; - private string _name; - private string _name2; + private ValidationHelper? _nameRule; + private string? _name; + private string? _name2; /// /// Gets or sets get the Name. /// - public string Name + public string? Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); @@ -33,7 +33,7 @@ public string Name /// /// Gets or sets get the Name2. /// - public string Name2 + public string? Name2 { get => _name2; set => this.RaiseAndSetIfChanged(ref _name2, value); @@ -42,7 +42,7 @@ public string Name2 /// /// Gets or sets the rule of Name property. /// - public ValidationHelper NameRule + public ValidationHelper? NameRule { get => _nameRule; set => this.RaiseAndSetIfChanged(ref _nameRule, value); diff --git a/src/ReactiveUI.Validation.Tests/NotifyDataErrorInfoTests.cs b/src/ReactiveUI.Validation.Tests/NotifyDataErrorInfoTests.cs index 14ceef14..6f09c8fb 100644 --- a/src/ReactiveUI.Validation.Tests/NotifyDataErrorInfoTests.cs +++ b/src/ReactiveUI.Validation.Tests/NotifyDataErrorInfoTests.cs @@ -1,24 +1,27 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Generic; using System.ComponentModel; using System.Linq; + +using NUnit.Framework; + using ReactiveUI.Validation.Collections; using ReactiveUI.Validation.Components; using ReactiveUI.Validation.Extensions; using ReactiveUI.Validation.Formatters.Abstractions; using ReactiveUI.Validation.Helpers; using ReactiveUI.Validation.Tests.Models; -using Xunit; namespace ReactiveUI.Validation.Tests; /// /// Tests for INotifyDataErrorInfo support. /// +[TestFixture] public class NotifyDataErrorInfoTests { private const string NameShouldNotBeEmptyMessage = "Name shouldn't be empty."; @@ -26,7 +29,7 @@ public class NotifyDataErrorInfoTests /// /// Verifies that the ErrorsChanged event fires on ViewModel initialization. /// - [Fact] + [Test] public void ShouldMarkPropertiesAsInvalidOnInit() { var viewModel = new IndeiTestViewModel(); @@ -43,20 +46,26 @@ public void ShouldMarkPropertiesAsInvalidOnInit() view.BindValidation(view.ViewModel, vm => vm.Name, v => v.NameErrorLabel); // Verify validation context behavior. - Assert.False(viewModel.ValidationContext.IsValid); - Assert.Single(viewModel.ValidationContext.Validations.Items); - Assert.Equal(NameShouldNotBeEmptyMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.ValidationContext.IsValid, Is.False); + Assert.That(viewModel.ValidationContext.Validations.Items, Has.Count.EqualTo(1)); + Assert.That(view.NameErrorLabel, Is.EqualTo(NameShouldNotBeEmptyMessage)); + } // Verify INotifyDataErrorInfo behavior. - Assert.True(viewModel.HasErrors); - Assert.Equal(NameShouldNotBeEmptyMessage, viewModel.GetErrors("Name").Cast().First()); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.HasErrors, Is.True); + Assert.That(viewModel.GetErrors("Name").Cast().First(), Is.EqualTo(NameShouldNotBeEmptyMessage)); + } } /// /// Verifies that the view model listens to the INotifyPropertyChanged event /// and sends INotifyDataErrorInfo notifications. /// - [Fact] + [Test] public void ShouldSynchronizeNotifyDataErrorInfoWithValidationContext() { var viewModel = new IndeiTestViewModel(); @@ -73,41 +82,50 @@ public void ShouldSynchronizeNotifyDataErrorInfoWithValidationContext() view.BindValidation(view.ViewModel, vm => vm.Name, v => v.NameErrorLabel); // Verify the initial state. - Assert.True(viewModel.HasErrors); - Assert.False(viewModel.ValidationContext.IsValid); - Assert.Single(viewModel.ValidationContext.Validations.Items); - Assert.Equal(NameShouldNotBeEmptyMessage, viewModel.GetErrors("Name").Cast().First()); - Assert.Equal(NameShouldNotBeEmptyMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.HasErrors, Is.True); + Assert.That(viewModel.ValidationContext.IsValid, Is.False); + Assert.That(viewModel.ValidationContext.Validations.Items, Has.Count.EqualTo(1)); + Assert.That(viewModel.GetErrors("Name").Cast().First(), Is.EqualTo(NameShouldNotBeEmptyMessage)); + Assert.That(view.NameErrorLabel, Is.EqualTo(NameShouldNotBeEmptyMessage)); + } // Send INotifyPropertyChanged. viewModel.Name = "JoJo"; // Verify the changed state. - Assert.False(viewModel.HasErrors); - Assert.True(viewModel.ValidationContext.IsValid); - Assert.Empty(viewModel.GetErrors("Name").Cast()); - Assert.Empty(view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.HasErrors, Is.False); + Assert.That(viewModel.ValidationContext.IsValid, Is.True); + Assert.That(viewModel.GetErrors("Name").Cast(), Is.Empty); + Assert.That(view.NameErrorLabel, Is.Empty); + } // Send INotifyPropertyChanged. viewModel.Name = string.Empty; // Verify the changed state. - Assert.True(viewModel.HasErrors); - Assert.False(viewModel.ValidationContext.IsValid); - Assert.Single(viewModel.ValidationContext.Validations.Items); - Assert.Equal(NameShouldNotBeEmptyMessage, viewModel.GetErrors("Name").Cast().First()); - Assert.Equal(NameShouldNotBeEmptyMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.HasErrors, Is.True); + Assert.That(viewModel.ValidationContext.IsValid, Is.False); + Assert.That(viewModel.ValidationContext.Validations.Items, Has.Count.EqualTo(1)); + Assert.That(viewModel.GetErrors("Name").Cast().First(), Is.EqualTo(NameShouldNotBeEmptyMessage)); + Assert.That(view.NameErrorLabel, Is.EqualTo(NameShouldNotBeEmptyMessage)); + } } /// /// The ErrorsChanged event should fire when properties change. /// - [Fact] + [Test] public void ShouldFireErrorsChangedEventWhenValidationStateChanges() { var viewModel = new IndeiTestViewModel(); - DataErrorsChangedEventArgs arguments = null; + DataErrorsChangedEventArgs? arguments = null; viewModel.ErrorsChanged += (_, args) => arguments = args; using var firstValidation = new BasePropertyValidation( @@ -118,23 +136,29 @@ public void ShouldFireErrorsChangedEventWhenValidationStateChanges() viewModel.ValidationContext.Add(firstValidation); - Assert.True(viewModel.HasErrors); - Assert.False(viewModel.ValidationContext.IsValid); - Assert.Single(viewModel.ValidationContext.Validations.Items); - Assert.Single(viewModel.GetErrors("Name").Cast()); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.HasErrors, Is.True); + Assert.That(viewModel.ValidationContext.IsValid, Is.False); + Assert.That(viewModel.ValidationContext.Validations.Items, Has.Count.EqualTo(1)); + Assert.That(viewModel.GetErrors("Name").Cast().Count(), Is.EqualTo(1)); + } viewModel.Name = "JoJo"; - Assert.False(viewModel.HasErrors); - Assert.Empty(viewModel.GetErrors("Name").Cast()); - Assert.NotNull(arguments); - Assert.Equal("Name", arguments.PropertyName); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.HasErrors, Is.False); + Assert.That(viewModel.GetErrors("Name").Cast(), Is.Empty); + Assert.That(arguments, Is.Not.Null); + Assert.That(arguments!.PropertyName, Is.EqualTo("Name")); + } } /// /// Using ModelObservableValidation with NotifyDataErrorInfo should return errors when associated property changes. /// - [Fact] + [Test] public void ShouldDeliverErrorsWhenModelObservableValidationTriggers() { var viewModel = new IndeiTestViewModel(); @@ -148,27 +172,33 @@ public void ShouldDeliverErrorsWhenModelObservableValidationTriggers() (name, other) => name == other), namesShouldMatchMessage); - Assert.False(viewModel.HasErrors); - Assert.True(viewModel.ValidationContext.IsValid); - Assert.Single(viewModel.ValidationContext.Validations.Items); - Assert.Empty(viewModel.GetErrors(nameof(viewModel.Name)).Cast()); - Assert.Empty(viewModel.GetErrors(nameof(viewModel.OtherName)).Cast()); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.HasErrors, Is.False); + Assert.That(viewModel.ValidationContext.IsValid, Is.True); + Assert.That(viewModel.ValidationContext.Validations.Items, Has.Count.EqualTo(1)); + Assert.That(viewModel.GetErrors(nameof(viewModel.Name)).Cast(), Is.Empty); + Assert.That(viewModel.GetErrors(nameof(viewModel.OtherName)).Cast(), Is.Empty); + } viewModel.Name = "JoJo"; viewModel.OtherName = "NoNo"; - Assert.True(viewModel.HasErrors); - Assert.Empty(viewModel.GetErrors(nameof(viewModel.Name)).Cast()); - Assert.Single(viewModel.GetErrors(nameof(viewModel.OtherName)).Cast()); - Assert.Single(viewModel.ValidationContext.Text); - Assert.Equal(namesShouldMatchMessage, viewModel.ValidationContext.Text.Single()); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.HasErrors, Is.True); + Assert.That(viewModel.GetErrors(nameof(viewModel.Name)).Cast(), Is.Empty); + Assert.That(viewModel.GetErrors(nameof(viewModel.OtherName)).Cast().Count(), Is.EqualTo(1)); + Assert.That(viewModel.ValidationContext.Text, Has.Count.EqualTo(1)); + Assert.That(viewModel.ValidationContext.Text!.Single(), Is.EqualTo(namesShouldMatchMessage)); + } } /// /// Verifies that validation rules of the same property do not duplicate. /// Earlier they sometimes could, due to the .Connect() method misuse. /// - [Fact] + [Test] public void ValidationRulesOfTheSamePropertyShouldNotDuplicate() { var viewModel = new IndeiTestViewModel(); @@ -182,15 +212,18 @@ public void ValidationRulesOfTheSamePropertyShouldNotDuplicate() m => !string.IsNullOrWhiteSpace(m), "Name shouldn't be white space."); - Assert.False(viewModel.ValidationContext.IsValid); - Assert.Equal(2, viewModel.ValidationContext.Validations.Count); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.ValidationContext.IsValid, Is.False); + Assert.That(viewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + } } /// /// Verifies that the events are published /// according to the changes of the validated properties. /// - [Fact] + [Test] public void ShouldSendPropertyChangeNotificationsForCorrectProperties() { var viewModel = new IndeiTestViewModel(); @@ -204,26 +237,35 @@ public void ShouldSendPropertyChangeNotificationsForCorrectProperties() m => m is not null, "Other name shouldn't be null."); - Assert.Single(viewModel.GetErrors(nameof(viewModel.Name))); - Assert.Single(viewModel.GetErrors(nameof(viewModel.OtherName))); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.GetErrors(nameof(viewModel.Name)).Cast().Count(), Is.EqualTo(1)); + Assert.That(viewModel.GetErrors(nameof(viewModel.OtherName)).Cast().Count(), Is.EqualTo(1)); + } var arguments = new List(); viewModel.ErrorsChanged += (_, args) => arguments.Add(args); viewModel.Name = "Josuke"; viewModel.OtherName = "Jotaro"; - Assert.Equal(2, arguments.Count); - Assert.Equal(nameof(viewModel.Name), arguments[0].PropertyName); - Assert.Equal(nameof(viewModel.OtherName), arguments[1].PropertyName); - Assert.False(viewModel.HasErrors); + using (Assert.EnterMultipleScope()) + { + Assert.That(arguments, Has.Count.EqualTo(2)); + Assert.That(arguments[0].PropertyName, Is.EqualTo(nameof(viewModel.Name))); + Assert.That(arguments[1].PropertyName, Is.EqualTo(nameof(viewModel.OtherName))); + Assert.That(viewModel.HasErrors, Is.False); + } viewModel.Name = null; viewModel.OtherName = null; - Assert.Equal(4, arguments.Count); - Assert.Equal(nameof(viewModel.Name), arguments[2].PropertyName); - Assert.Equal(nameof(viewModel.OtherName), arguments[3].PropertyName); - Assert.True(viewModel.HasErrors); + using (Assert.EnterMultipleScope()) + { + Assert.That(arguments, Has.Count.EqualTo(4)); + Assert.That(arguments[2].PropertyName, Is.EqualTo(nameof(viewModel.Name))); + Assert.That(arguments[3].PropertyName, Is.EqualTo(nameof(viewModel.OtherName))); + Assert.That(viewModel.HasErrors, Is.True); + } } /// @@ -231,11 +273,12 @@ public void ShouldSendPropertyChangeNotificationsForCorrectProperties() /// is disposed. Also, here we ensure that /// the property change subscriptions are unsubscribed. /// - [Fact] + [Test] public void ShouldDetachAndDisposeTheComponentWhenValidationHelperDisposes() { var view = new IndeiTestView(new IndeiTestViewModel { Name = string.Empty }); var arguments = new List(); + Assert.That(view.ViewModel, Is.Not.Null); view.ViewModel.ErrorsChanged += (_, args) => arguments.Add(args); var helper = view @@ -245,48 +288,61 @@ public void ShouldDetachAndDisposeTheComponentWhenValidationHelperDisposes() name => !string.IsNullOrWhiteSpace(name), "Name shouldn't be empty."); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.True(view.ViewModel.HasErrors); - Assert.Equal(1, arguments.Count); - Assert.Equal(nameof(view.ViewModel.Name), arguments[0].PropertyName); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.HasErrors, Is.True); + Assert.That(arguments, Has.Count.EqualTo(1)); + Assert.That(arguments[0].PropertyName, Is.EqualTo(nameof(view.ViewModel.Name))); + } helper.Dispose(); - Assert.Equal(0, view.ViewModel.ValidationContext.Validations.Count); - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.False(view.ViewModel.HasErrors); - Assert.Equal(2, arguments.Count); - Assert.Equal(nameof(view.ViewModel.Name), arguments[1].PropertyName); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.Zero); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.ViewModel.HasErrors, Is.False); + Assert.That(arguments, Has.Count.EqualTo(2)); + Assert.That(arguments[1].PropertyName, Is.EqualTo(nameof(view.ViewModel.Name))); + } } /// /// Verifies that we support custom formatters in our implementation. /// - [Fact] + [Test] public void ShouldInvokeCustomFormatters() { var formatter = new PrefixFormatter("Validation error:"); var view = new IndeiTestView(new IndeiTestViewModel(formatter) { Name = string.Empty }); var arguments = new List(); + Assert.That(view.ViewModel, Is.Not.Null); view.ViewModel.ErrorsChanged += (_, args) => arguments.Add(args); view.ViewModel.ValidationRule( viewModel => viewModel.Name, name => !string.IsNullOrWhiteSpace(name), "Name shouldn't be empty."); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.True(view.ViewModel.HasErrors); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.HasErrors, Is.True); + } var errors = view.ViewModel .GetErrors("Name") .Cast() .ToArray(); - Assert.Single(errors); - Assert.Equal("Validation error: Name shouldn't be empty.", errors[0]); + using (Assert.EnterMultipleScope()) + { + Assert.That(errors, Has.Length.EqualTo(1)); + Assert.That(errors[0], Is.EqualTo("Validation error: Name shouldn't be empty.")); + } } private class PrefixFormatter : IValidationTextFormatter diff --git a/src/ReactiveUI.Validation.Tests/ObservableValidationTests.cs b/src/ReactiveUI.Validation.Tests/ObservableValidationTests.cs index d7d8c15d..3ea7f9ce 100644 --- a/src/ReactiveUI.Validation.Tests/ObservableValidationTests.cs +++ b/src/ReactiveUI.Validation.Tests/ObservableValidationTests.cs @@ -1,16 +1,16 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using System.Reactive.Subjects; +using NUnit.Framework; using ReactiveUI.Validation.Components; using ReactiveUI.Validation.Extensions; using ReactiveUI.Validation.States; using ReactiveUI.Validation.Tests.Models; -using Xunit; namespace ReactiveUI.Validation.Tests; @@ -18,19 +18,39 @@ namespace ReactiveUI.Validation.Tests; /// Tests for the generic and for /// as well. /// +[TestFixture] public class ObservableValidationTests { - private readonly ISubject _validState = new ReplaySubject(1); - private readonly TestViewModel _validModel = new() + private ReplaySubject _validState = default!; + private TestViewModel _validModel = default!; + + /// + /// Sets up the test fixtures. + /// + [SetUp] + public void SetUp() + { + _validState = new ReplaySubject(1); + _validModel = new TestViewModel + { + Name = "name", + Name2 = "name2" + }; + } + + /// + /// Tears down the test fixtures. + /// + [TearDown] + public void TearDown() { - Name = "name", - Name2 = "name2" - }; + _validState?.Dispose(); + } /// /// Verifies if the initial state is True. /// - [Fact] + [Test] public void InitialValidStateIsCorrectTest() { _validState.OnNext(true); @@ -41,31 +61,31 @@ public void InitialValidStateIsCorrectTest() valid => valid, "broken"); - Assert.True(validation.IsValid); + Assert.That(validation.IsValid, Is.True); } /// /// Verifies if the initial state is True. /// - [Fact] + [Test] public void InitialValidStateOfPropertyValidationIsCorrectTest() { _validState.OnNext(true); using var propertyValidation = new ObservableValidation( _validModel, - state => state.Name, + state => state.Name!, _validState, valid => valid, "broken"); - Assert.True(propertyValidation.IsValid); + Assert.That(propertyValidation.IsValid, Is.True); } /// /// Verifies if the observable returns invalid. /// - [Fact] + [Test] public void ObservableToInvalidTest() { using var validation = new ObservableValidation( @@ -78,19 +98,22 @@ public void ObservableToInvalidTest() _validState.OnNext(true); _validState.OnNext(false); - Assert.False(validation.IsValid); - Assert.Equal("broken", validation.Text?.ToSingleLine()); + using (Assert.EnterMultipleScope()) + { + Assert.That(validation.IsValid, Is.False); + Assert.That(validation.Text?.ToSingleLine(), Is.EqualTo("broken")); + } } /// /// Verifies if the observable returns invalid. /// - [Fact] + [Test] public void ObservableToInvalidOfPropertyValidationTest() { using var propertyValidation = new ObservableValidation( _validModel, - state => state.Name, + state => state.Name!, _validState, valid => valid, "broken"); @@ -99,15 +122,18 @@ public void ObservableToInvalidOfPropertyValidationTest() _validState.OnNext(true); _validState.OnNext(false); - Assert.False(propertyValidation.IsValid); - Assert.Equal("broken", propertyValidation.Text?.ToSingleLine()); + using (Assert.EnterMultipleScope()) + { + Assert.That(propertyValidation.IsValid, Is.False); + Assert.That(propertyValidation.Text?.ToSingleLine(), Is.EqualTo("broken")); + } } /// /// Verifies that a call to Dispose disconnects the underlying observable /// of a . /// - [Fact] + [Test] public void DisposeShouldStopTrackingTheObservable() { var validation = new ObservableValidation( @@ -118,11 +144,11 @@ public void DisposeShouldStopTrackingTheObservable() _validState.OnNext(true); - Assert.True(validation.IsValid); + Assert.That(validation.IsValid, Is.True); _validState.OnNext(false); - Assert.False(validation.IsValid); + Assert.That(validation.IsValid, Is.False); validation.Dispose(); @@ -130,30 +156,30 @@ public void DisposeShouldStopTrackingTheObservable() _validState.OnNext(false); _validState.OnNext(true); - Assert.False(validation.IsValid); + Assert.That(validation.IsValid, Is.False); } /// /// Verifies that a call to Dispose disconnects the underlying observable /// of a . /// - [Fact] + [Test] public void DisposeShouldStopTrackingThePropertyValidationObservable() { var validation = new ObservableValidation( _validModel, - state => state.Name, + state => state.Name!, _validState, validity => validity, "broken"); _validState.OnNext(true); - Assert.True(validation.IsValid); + Assert.That(validation.IsValid, Is.True); _validState.OnNext(false); - Assert.False(validation.IsValid); + Assert.That(validation.IsValid, Is.False); validation.Dispose(); @@ -161,35 +187,39 @@ public void DisposeShouldStopTrackingThePropertyValidationObservable() _validState.OnNext(false); _validState.OnNext(true); - Assert.False(validation.IsValid); + Assert.That(validation.IsValid, Is.False); } /// /// Verifies that we support resolving properties by expressions. /// - [Fact] + [Test] public void ShouldResolveTypedProperties() { var viewModel = new TestViewModel { Name = string.Empty }; using var component = - new ObservableValidation( + new ObservableValidation( viewModel, - model => model.Name, + model => model.Name!, viewModel.WhenAnyValue(x => x.Name), state => !string.IsNullOrWhiteSpace(state), "Name shouldn't be empty."); - Assert.True(component.ContainsProperty(model => model.Name)); - Assert.True(component.ContainsProperty(model => model.Name, true)); - Assert.False(component.ContainsProperty(model => model.Name2)); - Assert.False(component.ContainsProperty(model => model.Name2, true)); + using (Assert.EnterMultipleScope()) + { + Assert.That(component.ContainsProperty(model => model.Name), Is.True); + Assert.That(component.ContainsProperty(model => model.Name, true), Is.True); + Assert.That(component.ContainsProperty(model => model.Name2), Is.False); + Assert.That(component.ContainsProperty(model => model.Name2, true), Is.False); + } + Assert.Throws(() => component.ContainsProperty(null!)); } /// /// Verifies that we support the simplest possible observable-based validation component. /// - [Fact] + [Test] public void ShouldSupportMinimalObservableValidation() { using var stream = new Subject(); @@ -198,21 +228,25 @@ public void ShouldSupportMinimalObservableValidation() component.ValidationStatusChange.Subscribe(arguments.Add); stream.OnNext(ValidationState.Valid); - Assert.True(component.IsValid); - Assert.Empty(component.Text!.ToSingleLine()); - Assert.Single(arguments); - - Assert.True(arguments[0].IsValid); - Assert.Empty(arguments[0].Text.ToSingleLine()); + using (Assert.EnterMultipleScope()) + { + Assert.That(component.IsValid, Is.True); + Assert.That(component.Text!.ToSingleLine(), Is.Empty); + Assert.That(arguments, Has.Count.EqualTo(1)); + Assert.That(arguments[0].IsValid, Is.True); + Assert.That(arguments[0].Text.ToSingleLine(), Is.Empty); + } const string errorMessage = "Errors exist."; stream.OnNext(new ValidationState(false, errorMessage)); - Assert.False(component.IsValid); - Assert.Equal(errorMessage, component.Text.ToSingleLine()); - Assert.Equal(2, arguments.Count); - - Assert.False(arguments[1].IsValid); - Assert.Equal(errorMessage, arguments[1].Text.ToSingleLine()); + using (Assert.EnterMultipleScope()) + { + Assert.That(component.IsValid, Is.False); + Assert.That(component.Text.ToSingleLine(), Is.EqualTo(errorMessage)); + Assert.That(arguments, Has.Count.EqualTo(2)); + Assert.That(arguments[1].IsValid, Is.False); + Assert.That(arguments[1].Text.ToSingleLine(), Is.EqualTo(errorMessage)); + } } -} \ No newline at end of file +} diff --git a/src/ReactiveUI.Validation.Tests/PropertyValidationTests.cs b/src/ReactiveUI.Validation.Tests/PropertyValidationTests.cs index d0b409ed..a8570bef 100644 --- a/src/ReactiveUI.Validation.Tests/PropertyValidationTests.cs +++ b/src/ReactiveUI.Validation.Tests/PropertyValidationTests.cs @@ -1,27 +1,28 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; using System.Collections.Generic; +using NUnit.Framework; using ReactiveUI.Validation.Comparators; using ReactiveUI.Validation.Components; using ReactiveUI.Validation.States; using ReactiveUI.Validation.Tests.Models; -using Xunit; namespace ReactiveUI.Validation.Tests; /// /// Tests for . /// +[TestFixture] public class PropertyValidationTests { /// /// Verifies if the default state is true. /// - [Fact] + [Test] public void ValidModelDefaultStateTest() { var model = CreateDefaultValidModel(); @@ -32,14 +33,17 @@ public void ValidModelDefaultStateTest() n => !string.IsNullOrEmpty(n), "broken"); - Assert.True(validation.IsValid); - Assert.True(string.IsNullOrEmpty(validation.Text?.ToSingleLine())); + using (Assert.EnterMultipleScope()) + { + Assert.That(validation.IsValid, Is.True); + Assert.That(string.IsNullOrEmpty(validation.Text?.ToSingleLine()), Is.True); + } } /// /// Verifies if the state transition is valid when the IsValid property changes. /// - [Fact] + [Test] public void StateTransitionsWhenValidityChangesTest() { const string testValue = "test"; @@ -58,20 +62,26 @@ public void StateTransitionsWhenValidityChangesTest() .ValidationStatusChange .Subscribe(v => lastVal = v.IsValid); - Assert.False(validation.IsValid); - Assert.False(lastVal); - Assert.True(lastVal.HasValue); + using (Assert.EnterMultipleScope()) + { + Assert.That(validation.IsValid, Is.False); + Assert.That(lastVal, Is.False); + Assert.That(lastVal.HasValue, Is.True); + } model.Name = testValue + "-" + testValue; - Assert.True(validation.IsValid); - Assert.True(lastVal); + using (Assert.EnterMultipleScope()) + { + Assert.That(validation.IsValid, Is.True); + Assert.That(lastVal, Is.True); + } } /// /// Verifies if the validation message is the expected. /// - [Fact] + [Test] public void PropertyContentsProvidedToMessageTest() { const string testValue = "bongo"; @@ -86,13 +96,13 @@ public void PropertyContentsProvidedToMessageTest() model.Name = testValue; - Assert.Equal("The value 'bongo' is incorrect", validation.Text?.ToSingleLine()); + Assert.That(validation.Text?.ToSingleLine(), Is.EqualTo("The value 'bongo' is incorrect")); } /// /// Verifies that validation message updates are correctly propagated. /// - [Fact] + [Test] public void MessageUpdatedWhenPropertyChanged() { const string testRoot = "bon"; @@ -112,21 +122,31 @@ public void MessageUpdatedWhenPropertyChanged() validation.ValidationStatusChange.Subscribe(v => changes.Add(v)); - Assert.Equal("The value 'bongo' is incorrect", validation.Text?.ToSingleLine()); - Assert.Single(changes); - Assert.Equal(new ValidationState(false, "The value 'bongo' is incorrect"), changes[0], new ValidationStateComparer()); + var expectedState1 = new ValidationState(false, "The value 'bongo' is incorrect"); + var comparer = new ValidationStateComparer(); + + using (Assert.EnterMultipleScope()) + { + Assert.That(validation.Text?.ToSingleLine(), Is.EqualTo("The value 'bongo' is incorrect")); + Assert.That(changes, Has.Count.EqualTo(1)); + Assert.That(comparer.Equals(changes[0], expectedState1), Is.True, "Validation states should be equal"); + } model.Name = testRoot; - Assert.Equal("The value 'bon' is incorrect", validation.Text?.ToSingleLine()); - Assert.Equal(2, changes.Count); - Assert.Equal(new ValidationState(false, "The value 'bon' is incorrect"), changes[1], new ValidationStateComparer()); + var expectedState2 = new ValidationState(false, "The value 'bon' is incorrect"); + using (Assert.EnterMultipleScope()) + { + Assert.That(validation.Text?.ToSingleLine(), Is.EqualTo("The value 'bon' is incorrect")); + Assert.That(changes, Has.Count.EqualTo(2)); + Assert.That(comparer.Equals(changes[1], expectedState2), Is.True, "Validation states should be equal"); + } } /// /// Verifies that validation message changes if one validation is valid but the other one is not. /// - [Fact] + [Test] public void DualStateMessageTest() { const string testRoot = "bon"; @@ -140,11 +160,11 @@ public void DualStateMessageTest() n => n is not null && n.Length > testRoot.Length, (p, v) => v ? "cool" : $"The value '{p}' is incorrect"); - Assert.Equal("cool", validation.Text?.ToSingleLine()); + Assert.That(validation.Text?.ToSingleLine(), Is.EqualTo("cool")); model.Name = testRoot; - Assert.Equal("The value 'bon' is incorrect", validation.Text?.ToSingleLine()); + Assert.That(validation.Text?.ToSingleLine(), Is.EqualTo("The value 'bon' is incorrect")); } private static TestViewModel CreateDefaultValidModel() => new() { Name = "name" }; diff --git a/src/ReactiveUI.Validation.Tests/ReactiveUI.Validation.Tests.csproj b/src/ReactiveUI.Validation.Tests/ReactiveUI.Validation.Tests.csproj index b3f2fa81..b3240d3f 100644 --- a/src/ReactiveUI.Validation.Tests/ReactiveUI.Validation.Tests.csproj +++ b/src/ReactiveUI.Validation.Tests/ReactiveUI.Validation.Tests.csproj @@ -1,8 +1,8 @@  - net8.0;net9.0 - $(NoWarn);1591;CA1707;SA1633 + $(TestTargetFrameworks) + enable @@ -10,26 +10,19 @@ - - - - - + + + + + - - + all runtime; build; native; contentfiles; analyzers - - - - - Always - - + diff --git a/src/ReactiveUI.Validation.Tests/ValidationBindingTests.cs b/src/ReactiveUI.Validation.Tests/ValidationBindingTests.cs index 22b113ea..fbd9211d 100644 --- a/src/ReactiveUI.Validation.Tests/ValidationBindingTests.cs +++ b/src/ReactiveUI.Validation.Tests/ValidationBindingTests.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; @@ -8,6 +8,9 @@ using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Subjects; + +using NUnit.Framework; + using ReactiveUI.Validation.Collections; using ReactiveUI.Validation.Components; using ReactiveUI.Validation.Contexts; @@ -18,86 +21,95 @@ using ReactiveUI.Validation.States; using ReactiveUI.Validation.Tests.Models; using ReactiveUI.Validation.ValidationBindings; -using Xunit; namespace ReactiveUI.Validation.Tests; /// /// Contains tests for validation binding extensions. /// +[TestFixture] public class ValidationBindingTests { /// /// Verifies that two validations properties are correctly applied in a View property. /// - [Fact] + [Test] public void ShouldSupportBindingTwoValidationsForOneProperty() { const int minimumLength = 5; var minimumLengthErrorMessage = $"Minimum length is {minimumLength}"; var view = new TestView(new TestViewModel { Name = "some" }); - view.ViewModel.ValidationRule( - vm => vm.Name, + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( + vm => vm!.Name, s => !string.IsNullOrEmpty(s), "Name is required."); - view.ViewModel.ValidationRule( - vm => vm.Name, - s => s.Length > minimumLength, + view.ViewModel!.ValidationRule( + vm => vm!.Name, + s => s!.Length > minimumLength, _ => minimumLengthErrorMessage); view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); view.BindValidation(view.ViewModel, vm => vm.Name, v => v.NameErrorLabel); - view.ViewModel.Name = "som"; + view.ViewModel!.Name = "som"; - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(2, view.ViewModel.ValidationContext.Validations.Count); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + } // Checks if second validation error message is shown - Assert.Equal(minimumLengthErrorMessage, view.NameErrorLabel); + Assert.That(view.NameErrorLabel, Is.EqualTo(minimumLengthErrorMessage)); } /// /// Verifies that two validations properties are correctly applied /// in a View property given by a complex expression. /// - [Fact] + [Test] public void ShouldSupportBindingTwoValidationsForOnePropertyToChainedViewProperties() { const int minimumLength = 5; var minimumLengthErrorMessage = $"Minimum length is {minimumLength}"; var view = new TestView(new TestViewModel { Name = "some" }); - view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( vm => vm.Name, s => !string.IsNullOrEmpty(s), "Name is required."); - view.ViewModel.ValidationRule( + view.ViewModel!.ValidationRule( vm => vm.Name, - s => s.Length > minimumLength, + s => s?.Length > minimumLength, minimumLengthErrorMessage); view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); view.BindValidation(view.ViewModel, vm => vm.Name, v => v.NameErrorContainer.Text); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(2, view.ViewModel.ValidationContext.Validations.Count); - Assert.Equal(minimumLengthErrorMessage, view.NameErrorContainer.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel!.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel!.ValidationContext.Validations, Has.Count.EqualTo(2)); + Assert.That(view.NameErrorContainer.Text, Is.EqualTo(minimumLengthErrorMessage)); + } } /// /// Verifies that validations registered with different lambda names are retrieved successfully. /// - [Fact] + [Test] public void RegisterValidationsWithDifferentLambdaNameWorksTest() { const string validName = "valid"; var view = new TestView(new TestViewModel { Name = validName }); - view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( vm => vm.Name, s => !string.IsNullOrEmpty(s), s => $"Name {s} isn't valid"); @@ -105,24 +117,28 @@ public void RegisterValidationsWithDifferentLambdaNameWorksTest() view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); view.BindValidation(view.ViewModel, vm => vm.Name, v => v.NameErrorLabel); - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.Single(view.ViewModel.ValidationContext.Validations.Items); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.ViewModel.ValidationContext.Validations.Items, Has.Count.EqualTo(1)); + } } /// /// Verifies that validation error messages get concatenated using white space. /// - [Fact] + [Test] public void ValidationMessagesDefaultConcatenationTest() { var view = new TestView(new TestViewModel { Name = string.Empty }); - view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( viewModelProperty => viewModelProperty.Name, s => !string.IsNullOrEmpty(s), "Name should not be empty."); - view.ViewModel.ValidationRule( + view.ViewModel!.ValidationRule( viewModelProperty => viewModelProperty.Name2, s => !string.IsNullOrEmpty(s), "Name2 should not be empty."); @@ -131,17 +147,20 @@ public void ValidationMessagesDefaultConcatenationTest() view.Bind(view.ViewModel, vm => vm.Name2, v => v.Name2Label); view.BindValidation(view.ViewModel, v => v.NameErrorLabel); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(2, view.ViewModel.ValidationContext.Validations.Count); - Assert.NotEmpty(view.NameErrorLabel); - Assert.Equal("Name should not be empty. Name2 should not be empty.", view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + Assert.That(view.NameErrorLabel, Is.Not.Empty); + Assert.That(view.NameErrorLabel, Is.EqualTo("Name should not be empty. Name2 should not be empty.")); + } } /// /// Property validations backed by ModelObservableValidationBase should /// be bound to view as well as base property validations are. /// - [Fact] + [Test] public void ComplexValidationRulesShouldBeBoundToView() { const string errorMessage = "Both inputs should be the same"; @@ -151,22 +170,26 @@ public void ComplexValidationRulesShouldBeBoundToView() Name2 = "Jotaro Kujo" }); - view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( m => m.Name, view.ViewModel.WhenAnyValue(x => x.Name, x => x.Name2, (name, name2) => name == name2), errorMessage); view.BindValidation(view.ViewModel, x => x.Name, x => x.NameErrorLabel); - Assert.NotEmpty(view.NameErrorLabel); - Assert.Equal(errorMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.NameErrorLabel, Is.Not.Empty); + Assert.That(view.NameErrorLabel, Is.EqualTo(errorMessage)); + } } /// /// Using 2 validation rules ending with the same property name should not /// result in both properties having all the errors of both properties. /// - [Fact] + [Test] public void ErrorsWithTheSameLastPropertyShouldNotShareErrors() { var model = new SourceDestinationViewModel(); @@ -185,20 +208,24 @@ public void ErrorsWithTheSameLastPropertyShouldNotShareErrors() view.BindValidation(view.ViewModel, x => x.Source.Name, x => x.SourceError); view.BindValidation(view.ViewModel, x => x.Destination.Name, x => x.DestinationError); - Assert.NotNull(view.SourceError); - Assert.Equal("Source text", view.SourceError); - Assert.Equal("Destination text", view.DestinationError); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.SourceError, Is.Not.Null); + Assert.That(view.SourceError, Is.EqualTo("Source text")); + Assert.That(view.DestinationError, Is.EqualTo("Destination text")); + } } /// /// Verifies that we still support binding to properties. /// - [Fact] + [Test] public void ShouldSupportBindingValidationHelperProperties() { const string nameErrorMessage = "Name should not be empty."; var view = new TestView(new TestViewModel { Name = string.Empty }); + Assert.That(view.ViewModel, Is.Not.Null); view.ViewModel.NameRule = view .ViewModel .ValidationRule( @@ -207,27 +234,36 @@ public void ShouldSupportBindingValidationHelperProperties() nameErrorMessage); view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); - view.BindValidation(view.ViewModel, vm => vm.NameRule, v => v.NameErrorLabel); + view.BindValidation(view.ViewModel, vm => vm!.NameRule, v => v.NameErrorLabel); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Single(view.ViewModel.ValidationContext.Validations.Items); - Assert.Equal(nameErrorMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.ValidationContext.Validations.Items, Has.Count.EqualTo(1)); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameErrorMessage)); + } view.ViewModel.Name = "Jonathan"; - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.Empty(view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.NameErrorLabel, Is.Empty); + } view.ViewModel.Name = string.Empty; - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(nameErrorMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameErrorMessage)); + } } /// /// Verifies that bindings support model observable validations. /// - [Fact] + [Test] public void ShouldSupportBindingModelObservableValidationHelperProperties() { const string namesShouldMatchMessage = "Names should match."; @@ -237,6 +273,7 @@ public void ShouldSupportBindingModelObservableValidationHelperProperties() Name2 = "Bongo" }); + Assert.That(view.ViewModel, Is.Not.Null); view.ViewModel.NameRule = view .ViewModel .ValidationRule( @@ -246,30 +283,37 @@ public void ShouldSupportBindingModelObservableValidationHelperProperties() view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); view.Bind(view.ViewModel, vm => vm.Name2, v => v.Name2Label); - view.BindValidation(view.ViewModel, vm => vm.NameRule, v => v.NameErrorLabel); + view.BindValidation(view.ViewModel, vm => vm!.NameRule, v => v.NameErrorLabel); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Single(view.ViewModel.ValidationContext.Validations.Items); - Assert.Equal(namesShouldMatchMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.ValidationContext.Validations.Items, Has.Count.EqualTo(1)); + Assert.That(view.NameErrorLabel, Is.EqualTo(namesShouldMatchMessage)); + } view.ViewModel.Name = "Bongo"; view.ViewModel.Name2 = "Bongo"; - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.Single(view.ViewModel.ValidationContext.Validations.Items); - Assert.Empty(view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.ViewModel.ValidationContext.Validations.Items, Has.Count.EqualTo(1)); + Assert.That(view.NameErrorLabel, Is.Empty); + } } /// /// Verifies that the IsValid and Message properties of a /// produce change notifications. /// - [Fact] + [Test] public void ShouldUpdateBindableValidationHelperIsValidProperty() { const string nameErrorMessage = "Name should not be empty."; var view = new TestView(new TestViewModel { Name = string.Empty }); + Assert.That(view.ViewModel, Is.Not.Null); view.ViewModel.NameRule = view .ViewModel .ValidationRule( @@ -277,28 +321,35 @@ public void ShouldUpdateBindableValidationHelperIsValidProperty() s => !string.IsNullOrEmpty(s), nameErrorMessage); - view.OneWayBind(view.ViewModel, vm => vm.NameRule.IsValid, v => v.IsNameValid); - view.OneWayBind(view.ViewModel, vm => vm.NameRule.Message, v => v.NameErrorLabel, s => s.ToSingleLine()); + view.OneWayBind(view.ViewModel, vm => vm.NameRule!.IsValid, v => v.IsNameValid); + view.OneWayBind(view.ViewModel, vm => vm.NameRule!.Message, v => v.NameErrorLabel, s => s.ToSingleLine()); - Assert.False(view.IsNameValid); - Assert.Equal(nameErrorMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.IsNameValid, Is.False); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameErrorMessage)); + } view.ViewModel.Name = "Bingo"; - Assert.True(view.IsNameValid); - Assert.Empty(view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.IsNameValid, Is.True); + Assert.That(view.NameErrorLabel, Is.Empty); + } } /// /// Ensures that we allow to use custom formatters in bindings. /// - [Fact] + [Test] public void ShouldAllowUsingCustomFormatters() { const string validationConstant = "View model is invalid."; var view = new TestView(new TestViewModel { Name = string.Empty }); - view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( vm => vm.Name, s => !string.IsNullOrEmpty(s), "Name should not be empty."); @@ -306,21 +357,27 @@ public void ShouldAllowUsingCustomFormatters() view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); view.BindValidation(view.ViewModel, v => v.NameErrorLabel, new ConstFormatter(validationConstant)); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.NotEmpty(view.NameErrorLabel); - Assert.Equal(validationConstant, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.NameErrorLabel, Is.Not.Empty); + Assert.That(view.NameErrorLabel, Is.EqualTo(validationConstant)); + } } /// /// Verifies that we support binding to a separate /// wrapped in the bindable class. /// - [Fact] + [Test] public void ShouldSupportBindingToValidationContextWrappedInValidationHelper() { const string nameValidationError = "Name should not be empty."; var view = new TestView(new TestViewModel { Name = string.Empty }); + + Assert.That(view.ViewModel, Is.Not.Null); + using var outerContext = new ValidationContext(ImmediateScheduler.Instance); using var validation = new BasePropertyValidation( view.ViewModel, @@ -332,22 +389,28 @@ public void ShouldSupportBindingToValidationContextWrappedInValidationHelper() view.ViewModel.NameRule = new ValidationHelper(outerContext); view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); - view.BindValidation(view.ViewModel, vm => vm.NameRule, v => v.NameErrorLabel); + view.BindValidation(view.ViewModel, vm => vm!.NameRule, v => v.NameErrorLabel); - Assert.False(view.ViewModel.NameRule.IsValid); - Assert.NotEmpty(view.NameErrorLabel); - Assert.Equal(nameValidationError, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.NameRule.IsValid, Is.False); + Assert.That(view.NameErrorLabel, Is.Not.Empty); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameValidationError)); + } view.ViewModel.Name = "Jotaro"; - Assert.True(view.ViewModel.NameRule.IsValid); - Assert.Empty(view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.NameRule.IsValid, Is.True); + Assert.That(view.NameErrorLabel, Is.Empty); + } } /// /// Verifies that we support various validation rule overloads. /// - [Fact] + [Test] public void ShouldSupportObservableValidationRuleOverloads() { var view = new TestView(new TestViewModel @@ -363,23 +426,24 @@ public void ShouldSupportObservableValidationRuleOverloads() state => state.Name2, (name, name2) => new { Name = name, Name2 = name2 }); - view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( state => state.Name, namesAreEqual, state => state.Name == state.Name2, state => $"{state.Name} != {state.Name2}."); - view.ViewModel.ValidationRule( + view.ViewModel!.ValidationRule( state => state.Name2, namesAreEqual, state => state.Name == state.Name2, state => $"{state.Name2} != {state.Name}."); - view.ViewModel.ValidationRule( + view.ViewModel!.ValidationRule( namesAreEqual.Select(names => names.Name == names.Name2), "Names should be equal."); - view.ViewModel.ValidationRule( + view.ViewModel!.ValidationRule( namesAreEqual, state => state.Name == state.Name2, state => $"{state.Name} should equal {state.Name2}."); @@ -388,44 +452,54 @@ public void ShouldSupportObservableValidationRuleOverloads() view.Bind(view.ViewModel, x => x.Name2, x => x.Name2Label); view.BindValidation(view.ViewModel, x => x.NameErrorLabel); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(4, view.ViewModel.ValidationContext.Validations.Count); - Assert.NotEmpty(view.NameErrorLabel); - Assert.Equal("Foo != Bar. Bar != Foo. Names should be equal. Foo should equal Bar.", view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(4)); + Assert.That(view.NameErrorLabel, Is.Not.Empty); + Assert.That(view.NameErrorLabel, Is.EqualTo("Foo != Bar. Bar != Foo. Names should be equal. Foo should equal Bar.")); + } view.ViewModel.Name = "Foo"; view.ViewModel.Name2 = "Foo"; - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(4, view.ViewModel.ValidationContext.Validations.Count); - Assert.Empty(view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(4)); + Assert.That(view.NameErrorLabel, Is.Empty); + } } /// /// Verifies that we support binding validations to actions. This feature is required for platform-specific /// extension methods implementation, e.g. the for the Android Platform. /// - [Fact] + [Test] public void ShouldSupportActionBindingRequiredForPlatformSpecificImplementations() { const string nameErrorMessage = "Name should not be empty."; var view = new TestView(new TestViewModel { Name = string.Empty }); - view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( vm => vm.Name, s => !string.IsNullOrEmpty(s), nameErrorMessage); - ValidationBinding.ForProperty( + ValidationBinding.ForProperty( view, - viewModel => viewModel.Name, - (_, errorText) => view.NameErrorLabel = errorText.FirstOrDefault(msg => !string.IsNullOrEmpty(msg)), + viewModel => viewModel!.Name, + (_, errorText) => view.NameErrorLabel = errorText.FirstOrDefault(msg => !string.IsNullOrEmpty(msg)) ?? string.Empty, SingleLineFormatter.Default); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.NotEmpty(view.NameErrorLabel); - Assert.Equal(nameErrorMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.NameErrorLabel, Is.Not.Empty); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameErrorMessage)); + } } /// @@ -433,11 +507,12 @@ public void ShouldSupportActionBindingRequiredForPlatformSpecificImplementations /// is required for platform-specific extension methods implementation, e.g. the /// for the Android Platform. /// - [Fact] + [Test] public void ShouldSupportValidationHelperActionBindingRequiredForPlatformSpecificImplementations() { const string nameErrorMessage = "Name should not be empty."; var view = new TestView(new TestViewModel { Name = string.Empty }); + Assert.That(view.ViewModel, Is.Not.Null); view.ViewModel.NameRule = view .ViewModel .ValidationRule( @@ -447,14 +522,17 @@ public void ShouldSupportValidationHelperActionBindingRequiredForPlatformSpecifi ValidationBinding.ForValidationHelperProperty( view, - viewModel => viewModel.NameRule, + viewModel => viewModel!.NameRule, (_, errorText) => view.NameErrorLabel = errorText, SingleLineFormatter.Default); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.NotEmpty(view.NameErrorLabel); - Assert.Equal(nameErrorMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.NameErrorLabel, Is.Not.Empty); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameErrorMessage)); + } } /// @@ -462,13 +540,14 @@ public void ShouldSupportValidationHelperActionBindingRequiredForPlatformSpecifi /// is required for platform-specific extension methods implementation, e.g. the /// for the Android Platform. /// - [Fact] + [Test] public void ShouldSupportViewModelActionBindingRequiredForPlatformSpecificImplementations() { const string nameErrorMessage = "Name should not be empty."; var view = new TestView(new TestViewModel { Name = string.Empty }); - view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( vm => vm.Name, s => !string.IsNullOrEmpty(s), nameErrorMessage); @@ -478,23 +557,27 @@ public void ShouldSupportViewModelActionBindingRequiredForPlatformSpecificImplem errorText => view.NameErrorLabel = errorText, SingleLineFormatter.Default); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.NotEmpty(view.NameErrorLabel); - Assert.Equal(nameErrorMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.NameErrorLabel, Is.Not.Empty); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameErrorMessage)); + } } /// /// Verifies that we support creating validation rules from interfaces, and also support /// creating bindings to with interface supplied as a type argument. /// - [Fact] + [Test] public void ShouldSupportBindingToInterfaces() { const string nameErrorMessage = "Name shouldn't be empty."; var view = new SampleView(new SampleViewModel()); - view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( viewModel => viewModel.Name, name => !string.IsNullOrWhiteSpace(name), nameErrorMessage); @@ -502,15 +585,21 @@ public void ShouldSupportBindingToInterfaces() view.Bind(view.ViewModel, x => x.Name, x => x.NameLabel); view.BindValidation(view.ViewModel, x => x.Name, x => x.NameErrorLabel); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.NotEmpty(view.NameErrorLabel); - Assert.Equal(nameErrorMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.NameErrorLabel, Is.Not.Empty); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameErrorMessage)); + } view.ViewModel.Name = "Saitama"; - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.Empty(view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.NameErrorLabel, Is.Empty); + } } /// @@ -518,18 +607,19 @@ public void ShouldSupportBindingToInterfaces() /// is disposed. Also, here we ensure that /// the property change subscriptions are unsubscribed. /// - [Fact] + [Test] public void ShouldDetachAndDisposeTheComponentWhenValidationHelperDisposes() { const string nameErrorMessage = "Name shouldn't be empty."; const string name2ErrorMessage = "Name shouldn't be empty."; var view = new TestView(new TestViewModel { Name = string.Empty }); - var nameRule = view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + var nameRule = view.ViewModel!.ValidationRule( viewModel => viewModel.Name, name => !string.IsNullOrWhiteSpace(name), nameErrorMessage); - var name2Rule = view.ViewModel.ValidationRule( + var name2Rule = view.ViewModel!.ValidationRule( viewModel => viewModel.Name2, name => !string.IsNullOrWhiteSpace(name), name2ErrorMessage); @@ -538,34 +628,48 @@ public void ShouldDetachAndDisposeTheComponentWhenValidationHelperDisposes() view.BindValidation(view.ViewModel, x => x.Name, x => x.NameErrorLabel); view.BindValidation(view.ViewModel, x => x.Name2, x => x.Name2ErrorLabel); - Assert.Equal(2, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(nameErrorMessage, view.NameErrorLabel); - Assert.Equal(name2ErrorMessage, view.Name2ErrorLabel); + Assert.That(view.ViewModel, Is.Not.Null); + + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameErrorMessage)); + Assert.That(view.Name2ErrorLabel, Is.EqualTo(name2ErrorMessage)); + } nameRule.Dispose(); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Empty(view.NameErrorLabel); - Assert.Equal(name2ErrorMessage, view.Name2ErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorLabel, Is.Empty); + Assert.That(view.Name2ErrorLabel, Is.EqualTo(name2ErrorMessage)); + } name2Rule.Dispose(); - Assert.Equal(0, view.ViewModel.ValidationContext.Validations.Count); - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.Empty(view.NameErrorLabel); - Assert.Empty(view.Name2ErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.Zero); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.NameErrorLabel, Is.Empty); + Assert.That(view.Name2ErrorLabel, Is.Empty); + } view.ViewModel.ValidationRule( viewModel => viewModel.Name, name => !string.IsNullOrWhiteSpace(name), nameErrorMessage); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(nameErrorMessage, view.NameErrorLabel); - Assert.Empty(view.Name2ErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameErrorMessage)); + Assert.That(view.Name2ErrorLabel, Is.Empty); + } } /// @@ -573,16 +677,17 @@ public void ShouldDetachAndDisposeTheComponentWhenValidationHelperDisposes() /// e.g. when one disposes of a , the view model /// validity should recalculate. /// - [Fact] + [Test] public void ShouldUpdateViewModelValidityWhenValidationHelpersDetach() { var view = new TestView(new TestViewModel { Name = string.Empty }); - var nameRule = view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + var nameRule = view.ViewModel!.ValidationRule( viewModel => viewModel.Name, name => !string.IsNullOrWhiteSpace(name), "Name is empty."); - var name2Rule = view.ViewModel.ValidationRule( + var name2Rule = view.ViewModel!.ValidationRule( viewModel => viewModel.Name2, name => !string.IsNullOrWhiteSpace(name), "Name2 is empty."); @@ -590,42 +695,56 @@ public void ShouldUpdateViewModelValidityWhenValidationHelpersDetach() view.Bind(view.ViewModel, x => x.Name, x => x.NameLabel); view.BindValidation(view.ViewModel, x => x.NameErrorContainer.Text); - Assert.Equal(2, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal("Name is empty. Name2 is empty.", view.NameErrorContainer.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorContainer.Text, Is.EqualTo("Name is empty. Name2 is empty.")); + } nameRule.Dispose(); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal("Name2 is empty.", view.NameErrorContainer.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorContainer.Text, Is.EqualTo("Name2 is empty.")); + } name2Rule.Dispose(); - Assert.Equal(0, view.ViewModel.ValidationContext.Validations.Count); - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.Empty(view.NameErrorContainer.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.Zero); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.NameErrorContainer.Text, Is.Empty); + } view.ViewModel.ValidationRule( viewModel => viewModel.Name, name => !string.IsNullOrWhiteSpace(name), "Name is empty."); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal("Name is empty.", view.NameErrorContainer.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorContainer.Text, Is.EqualTo("Name is empty.")); + } } /// /// Verifies that we update the binding to property when that /// property sends notifications. /// - [Fact] + [Test] public void ShouldUpdateValidationHelperBindingOnPropertyChange() { var view = new TestView(new TestViewModel { Name = string.Empty }); const string nameErrorMessage = "Name shouldn't be empty."; + + Assert.That(view.ViewModel, Is.Not.Null); view.ViewModel.NameRule = view.ViewModel .ValidationRule( viewModel => viewModel.Name, @@ -633,18 +752,24 @@ public void ShouldUpdateValidationHelperBindingOnPropertyChange() nameErrorMessage); view.Bind(view.ViewModel, x => x.Name, x => x.NameLabel); - view.BindValidation(view.ViewModel, x => x.NameRule, x => x.NameErrorLabel); + view.BindValidation(view.ViewModel, x => x!.NameRule, x => x.NameErrorLabel); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(nameErrorMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorLabel, Is.EqualTo(nameErrorMessage)); + } view.ViewModel.NameRule.Dispose(); view.ViewModel.NameRule = null; - Assert.Equal(0, view.ViewModel.ValidationContext.Validations.Count); - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.Empty(view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.Zero); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.NameErrorLabel, Is.Empty); + } const string secretMessage = "This is the secret message."; view.ViewModel.NameRule = view.ViewModel @@ -653,15 +778,18 @@ public void ShouldUpdateValidationHelperBindingOnPropertyChange() name => !string.IsNullOrWhiteSpace(name), secretMessage); - Assert.Equal(1, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Equal(secretMessage, view.NameErrorLabel); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorLabel, Is.EqualTo(secretMessage)); + } } /// /// Verifies that the methods work. /// - [Fact] + [Test] public void ShouldBindValidationRuleEmittingValidationStates() { const StringComparison comparison = StringComparison.InvariantCulture; @@ -678,37 +806,44 @@ public void ShouldBindValidationRuleEmittingValidationStates() !string.IsNullOrWhiteSpace(name), nameErrorMessage)); - view.ViewModel.ValidationRule( + Assert.That(view.ViewModel, Is.Not.Null); + view.ViewModel!.ValidationRule( viewModel => viewModel.Name, nameValidationState); var viewModelBlockedValidationState = isViewModelBlocked.Select(blocked => (IValidationState)new CustomValidationState(!blocked, viewModelIsBlockedMessage)); - view.ViewModel.ValidationRule(viewModelBlockedValidationState); + view.ViewModel!.ValidationRule(viewModelBlockedValidationState); view.Bind(view.ViewModel, x => x.Name, x => x.NameLabel); view.BindValidation(view.ViewModel, x => x.Name, x => x.NameErrorLabel); view.BindValidation(view.ViewModel, x => x.NameErrorContainer.Text); - Assert.Equal(2, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Contains(nameErrorMessage, view.NameErrorLabel, comparison); - Assert.Contains(viewModelIsBlockedMessage, view.NameErrorContainer.Text, comparison); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorLabel.Contains(nameErrorMessage, comparison), Is.True); + Assert.That(view.NameErrorContainer.Text.Contains(viewModelIsBlockedMessage, comparison), Is.True); + } view.ViewModel.Name = "Qwerty"; isViewModelBlocked.OnNext(false); - Assert.Equal(2, view.ViewModel.ValidationContext.Validations.Count); - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.DoesNotContain(nameErrorMessage, view.NameErrorLabel, comparison); - Assert.DoesNotContain(viewModelIsBlockedMessage, view.NameErrorContainer.Text, comparison); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.NameErrorLabel.Contains(nameErrorMessage, comparison), Is.False); + Assert.That(view.NameErrorContainer.Text.Contains(viewModelIsBlockedMessage, comparison), Is.False); + } } /// /// Verifies that the methods work. /// - [Fact] + [Test] public void ShouldBindValidationRuleEmittingValidationStatesGeneric() { const StringComparison comparison = StringComparison.InvariantCulture; @@ -719,15 +854,17 @@ public void ShouldBindValidationRuleEmittingValidationStatesGeneric() isViewModelBlocked.OnNext(true); // Use the observable directly in the rules, which use the generic version of the ex - view.ViewModel.ValidationRule( - viewModel => viewModel.Name, + Assert.That(view.ViewModel, Is.Not.Null); + + view.ViewModel!.ValidationRule( + viewModel => viewModel!.Name, view.ViewModel.WhenAnyValue( vm => vm.Name, name => new CustomValidationState( !string.IsNullOrWhiteSpace(name), nameErrorMessage))); - view.ViewModel.ValidationRule( + view.ViewModel!.ValidationRule( isViewModelBlocked.Select(blocked => new CustomValidationState(!blocked, viewModelIsBlockedMessage))); @@ -735,24 +872,30 @@ public void ShouldBindValidationRuleEmittingValidationStatesGeneric() view.BindValidation(view.ViewModel, x => x.Name, x => x.NameErrorLabel); view.BindValidation(view.ViewModel, x => x.NameErrorContainer.Text); - Assert.Equal(2, view.ViewModel.ValidationContext.Validations.Count); - Assert.False(view.ViewModel.ValidationContext.IsValid); - Assert.Contains(nameErrorMessage, view.NameErrorLabel, comparison); - Assert.Contains(viewModelIsBlockedMessage, view.NameErrorContainer.Text, comparison); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.False); + Assert.That(view.NameErrorLabel.Contains(nameErrorMessage, comparison), Is.True); + Assert.That(view.NameErrorContainer.Text.Contains(viewModelIsBlockedMessage, comparison), Is.True); + } view.ViewModel.Name = "Qwerty"; isViewModelBlocked.OnNext(false); - Assert.Equal(2, view.ViewModel.ValidationContext.Validations.Count); - Assert.True(view.ViewModel.ValidationContext.IsValid); - Assert.DoesNotContain(nameErrorMessage, view.NameErrorLabel, comparison); - Assert.DoesNotContain(viewModelIsBlockedMessage, view.NameErrorContainer.Text, comparison); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.ViewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + Assert.That(view.ViewModel.ValidationContext.IsValid, Is.True); + Assert.That(view.NameErrorLabel.Contains(nameErrorMessage, comparison), Is.False); + Assert.That(view.NameErrorContainer.Text.Contains(viewModelIsBlockedMessage, comparison), Is.False); + } } /// /// Verifies that we support nullable view model properties. /// - [Fact] + [Test] public void ShouldSupportDelayedViewModelInitialization() { var view = new TestView @@ -765,18 +908,24 @@ public void ShouldSupportDelayedViewModelInitialization() view.BindValidation(view.ViewModel, x => x.Name, x => x.NameErrorLabel); view.BindValidation(view.ViewModel, x => x.NameErrorContainer.Text); - Assert.Empty(view.NameErrorLabel); - Assert.Empty(view.NameErrorContainer.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.NameErrorLabel, Is.Empty); + Assert.That(view.NameErrorContainer.Text, Is.Empty); + } const string errorMessage = "Name shouldn't be empty."; var viewModel = new TestViewModel(); viewModel.ValidationRule(x => x.Name, x => !string.IsNullOrWhiteSpace(x), errorMessage); view.ViewModel = viewModel; - Assert.NotEmpty(view.NameErrorLabel); - Assert.NotEmpty(view.NameErrorContainer.Text); - Assert.Equal(errorMessage, view.NameErrorLabel); - Assert.Equal(errorMessage, view.NameErrorContainer.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(view.NameErrorLabel, Is.Not.Empty); + Assert.That(view.NameErrorContainer.Text, Is.Not.Empty); + Assert.That(view.NameErrorLabel, Is.EqualTo(errorMessage)); + Assert.That(view.NameErrorContainer.Text, Is.EqualTo(errorMessage)); + } } private class CustomValidationState(bool isValid, string message) : IValidationState diff --git a/src/ReactiveUI.Validation.Tests/ValidationContextTests.cs b/src/ReactiveUI.Validation.Tests/ValidationContextTests.cs index 7dd5bbd6..234e247a 100644 --- a/src/ReactiveUI.Validation.Tests/ValidationContextTests.cs +++ b/src/ReactiveUI.Validation.Tests/ValidationContextTests.cs @@ -1,39 +1,43 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; using System.Reactive.Concurrency; +using NUnit.Framework; using ReactiveUI.Validation.Components; using ReactiveUI.Validation.Contexts; using ReactiveUI.Validation.Extensions; using ReactiveUI.Validation.Tests.Models; -using Xunit; namespace ReactiveUI.Validation.Tests; /// /// Tests for . /// +[TestFixture] public class ValidationContextTests { /// /// Verifies that a without validations is valid. /// - [Fact] + [Test] public void EmptyValidationContextIsValid() { using var vc = new ValidationContext(ImmediateScheduler.Instance); - Assert.True(vc.IsValid); - Assert.Equal(0, vc.Text.Count); + using (Assert.EnterMultipleScope()) + { + Assert.That(vc.IsValid, Is.True); + Assert.That(vc.Text, Has.Count.Zero); + } } /// /// Verifies that validations can be added in the . /// - [Fact] + [Test] public void CanAddValidationComponentsTest() { using var vc = new ValidationContext(ImmediateScheduler.Instance); @@ -50,20 +54,22 @@ public void CanAddValidationComponentsTest() vc.Add(v1); - Assert.True(vc.IsValid); + Assert.That(vc.IsValid, Is.True); vm.Name = invalidName; - Assert.False(v1.IsValid); - Assert.False(vc.IsValid); - - Assert.Equal(1, vc.Text.Count); + using (Assert.EnterMultipleScope()) + { + Assert.That(v1.IsValid, Is.False); + Assert.That(vc.IsValid, Is.False); + Assert.That(vc.Text, Has.Count.EqualTo(1)); + } } /// /// Verifies that two validations properties are correctly applied in the . /// - [Fact] + [Test] public void TwoValidationComponentsCorrectlyResultInContextTest() { const string validName = "valid"; @@ -88,32 +94,44 @@ public void TwoValidationComponentsCorrectlyResultInContextTest() vc.Add(firstValidation); vc.Add(secondValidation); - Assert.True(vc.IsValid); - Assert.Equal(0, vc.Text.Count); + using (Assert.EnterMultipleScope()) + { + Assert.That(vc.IsValid, Is.True); + Assert.That(vc.Text, Has.Count.Zero); + } vm.Name = invalidName; - Assert.False(vc.IsValid); - Assert.Equal(1, vc.Text.Count); - Assert.Equal("Name " + invalidName + " isn't valid", vc.Text[0]); + using (Assert.EnterMultipleScope()) + { + Assert.That(vc.IsValid, Is.False); + Assert.That(vc.Text, Has.Count.EqualTo(1)); + Assert.That(vc.Text[0], Is.EqualTo("Name " + invalidName + " isn't valid")); + } vm.Name2 = invalidName; - Assert.False(vc.IsValid); - Assert.Equal(2, vc.Text.Count); - Assert.Equal("Name " + invalidName + " isn't valid", vc.Text[0]); - Assert.Equal("Name 2 " + invalidName + " isn't valid", vc.Text[1]); + using (Assert.EnterMultipleScope()) + { + Assert.That(vc.IsValid, Is.False); + Assert.That(vc.Text, Has.Count.EqualTo(2)); + Assert.That(vc.Text[0], Is.EqualTo("Name " + invalidName + " isn't valid")); + Assert.That(vc.Text[1], Is.EqualTo("Name 2 " + invalidName + " isn't valid")); + } vm.Name = validName; vm.Name2 = validName; - Assert.True(vc.IsValid); - Assert.Equal(0, vc.Text.Count); + using (Assert.EnterMultipleScope()) + { + Assert.That(vc.IsValid, Is.True); + Assert.That(vc.Text, Has.Count.Zero); + } } /// /// Verifies that this.IsValid() extension method observes a /// and emits new values. /// - [Fact] + [Test] public void IsValidShouldNotifyOfValidityChange() { var viewModel = new TestViewModel { Name = string.Empty }; @@ -126,13 +144,13 @@ public void IsValidShouldNotifyOfValidityChange() var latestValidity = false; var d = viewModel.IsValid().Subscribe(isValid => latestValidity = isValid); - Assert.False(latestValidity); + Assert.That(latestValidity, Is.False); viewModel.Name = "Jonathan"; - Assert.True(latestValidity); + Assert.That(latestValidity, Is.True); viewModel.Name = string.Empty; - Assert.False(latestValidity); + Assert.That(latestValidity, Is.False); d.Dispose(); } @@ -140,7 +158,7 @@ public void IsValidShouldNotifyOfValidityChange() /// Ensures that the ClearValidationRules extension method works. /// Also verifies that the ClearValidationRules extension method is idempotent. /// - [Fact] + [Test] public void ShouldClearAttachedValidationRules() { var viewModel = new TestViewModel { Name = string.Empty }; @@ -159,29 +177,38 @@ public void ShouldClearAttachedValidationRules() viewModel.ValidationContext.Add(nameValidation); viewModel.ValidationContext.Add(name2Validation); - Assert.Equal(2, viewModel.ValidationContext.Validations.Count); - Assert.False(viewModel.ValidationContext.IsValid); - Assert.NotEmpty(viewModel.ValidationContext.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + Assert.That(viewModel.ValidationContext.IsValid, Is.False); + Assert.That(viewModel.ValidationContext.Text, Is.Not.Empty); + } viewModel.ClearValidationRules(); - Assert.Equal(0, viewModel.ValidationContext.Validations.Count); - Assert.True(viewModel.ValidationContext.IsValid); - Assert.Empty(viewModel.ValidationContext.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.ValidationContext.Validations, Has.Count.Zero); + Assert.That(viewModel.ValidationContext.IsValid, Is.True); + Assert.That(viewModel.ValidationContext.Text, Is.Empty); + } // Verify that the method is idempotent. viewModel.ClearValidationRules(); - Assert.Equal(0, viewModel.ValidationContext.Validations.Count); - Assert.True(viewModel.ValidationContext.IsValid); - Assert.Empty(viewModel.ValidationContext.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.ValidationContext.Validations, Has.Count.Zero); + Assert.That(viewModel.ValidationContext.IsValid, Is.True); + Assert.That(viewModel.ValidationContext.Text, Is.Empty); + } } /// /// Ensures that the ClearValidationRules extension method accepting an expression works. /// Also verifies that the ClearValidationRules extension method is idempotent. /// - [Fact] + [Test] public void ShouldClearAttachedValidationRulesForTheGivenProperty() { var viewModel = new TestViewModel { Name = string.Empty }; @@ -201,24 +228,34 @@ public void ShouldClearAttachedValidationRulesForTheGivenProperty() viewModel.ValidationContext.Add(nameValidation); viewModel.ValidationContext.Add(name2Validation); - Assert.Equal(2, viewModel.ValidationContext.Validations.Count); - Assert.False(viewModel.ValidationContext.IsValid); - Assert.NotEmpty(viewModel.ValidationContext.Text); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.ValidationContext.Validations, Has.Count.EqualTo(2)); + Assert.That(viewModel.ValidationContext.IsValid, Is.False); + Assert.That(viewModel.ValidationContext.Text, Is.Not.Empty); + } + Assert.Throws(() => viewModel.ClearValidationRules(null!)); viewModel.ClearValidationRules(x => x.Name); - Assert.Equal(1, viewModel.ValidationContext.Validations.Count); - Assert.False(viewModel.ValidationContext.IsValid); - Assert.NotEmpty(viewModel.ValidationContext.Text); - Assert.Equal(name2ErrorMessage, viewModel.ValidationContext.Text.ToSingleLine()); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(viewModel.ValidationContext.IsValid, Is.False); + Assert.That(viewModel.ValidationContext.Text, Is.Not.Empty); + Assert.That(viewModel.ValidationContext.Text.ToSingleLine(), Is.EqualTo(name2ErrorMessage)); + } // Verify that the method is idempotent. viewModel.ClearValidationRules(x => x.Name); - Assert.Equal(1, viewModel.ValidationContext.Validations.Count); - Assert.False(viewModel.ValidationContext.IsValid); - Assert.NotEmpty(viewModel.ValidationContext.Text); - Assert.Equal(name2ErrorMessage, viewModel.ValidationContext.Text.ToSingleLine()); + using (Assert.EnterMultipleScope()) + { + Assert.That(viewModel.ValidationContext.Validations, Has.Count.EqualTo(1)); + Assert.That(viewModel.ValidationContext.IsValid, Is.False); + Assert.That(viewModel.ValidationContext.Text, Is.Not.Empty); + Assert.That(viewModel.ValidationContext.Text.ToSingleLine(), Is.EqualTo(name2ErrorMessage)); + } } } diff --git a/src/ReactiveUI.Validation.Tests/ValidationTextTests.cs b/src/ReactiveUI.Validation.Tests/ValidationTextTests.cs index ac649c50..faa45a5c 100644 --- a/src/ReactiveUI.Validation.Tests/ValidationTextTests.cs +++ b/src/ReactiveUI.Validation.Tests/ValidationTextTests.cs @@ -1,225 +1,227 @@ -// Copyright (c) 2021 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using ReactiveUI.Validation.Collections; using ReactiveUI.Validation.Contexts; -using Xunit; -using Xunit.Abstractions; namespace ReactiveUI.Validation.Tests; /// /// Tests for . /// +[TestFixture] public class ValidationTextTests { /// /// Verifies that is genuinely empty. /// - [Fact] + [Test] public void NoneValidationTextIsEmpty() { var vt = ValidationText.None; - Assert.Equal(0, vt.Count); - - // Calling Count() checks the enumeration returns no results, unlike the Count property. - Assert.Equal(0, vt.Count); - Assert.Equal(string.Empty, vt.ToSingleLine()); + using (Assert.EnterMultipleScope()) + { + Assert.That(vt, Has.Count.Zero); + Assert.That(vt, Has.Count.Zero); + Assert.That(vt.ToSingleLine(), Is.EqualTo(string.Empty)); + } } /// /// Verifies that has a single empty item. /// - [Fact] + [Test] public void EmptyValidationTextIsSingleEmpty() { var vt = ValidationText.Empty; - Assert.Equal(1, vt.Count); - - // Calling Count() checks the enumeration returns no results, unlike the Count property. - Assert.Equal(1, vt.Count); - Assert.Same(string.Empty, vt.Single()); - Assert.Equal(string.Empty, vt.ToSingleLine()); + using (Assert.EnterMultipleScope()) + { + Assert.That(vt, Has.Count.EqualTo(1)); + Assert.That(vt, Has.Count.EqualTo(1)); + Assert.That(vt.Single(), Is.SameAs(string.Empty)); + Assert.That(vt.ToSingleLine(), Is.EqualTo(string.Empty)); + } } /// /// Verifies that calling without parameters returns . /// - [Fact] + [Test] public void ParameterlessCreateReturnsNone() { var vt = ValidationText.Create(); - Assert.Same(ValidationText.None, vt); + Assert.That(vt, Is.SameAs(ValidationText.None)); } /// /// Verifies that calling with an empty enumerable . /// - [Fact] + [Test] public void CreateEmptyStringEnumerableReturnsNone() { var vt = ValidationText.Create((IEnumerable)[]); - Assert.Same(ValidationText.None, vt); + Assert.That(vt, Is.SameAs(ValidationText.None)); } /// /// Verifies that calling with an empty enumerable . /// - [Fact] + [Test] public void CreateEmptyValidationTextEnumerableReturnsNone() { var vt = ValidationText.Create(Array.Empty()); - Assert.Same(ValidationText.None, vt); + Assert.That(vt, Is.SameAs(ValidationText.None)); } /// /// Verifies that calling with returns . /// - [Fact] + [Test] public void CreateNullReturnsNone() { - var vt = ValidationText.Create((string)null); + var vt = ValidationText.Create((string)null!); - Assert.Same(ValidationText.None, vt); + Assert.That(vt, Is.SameAs(ValidationText.None)); } /// /// Verifies that calling with enumerable returns . /// - [Fact] + [Test] public void CreateNullStringEnumerableReturnsNone() { - var vt = ValidationText.Create((IEnumerable)null); + var vt = ValidationText.Create((IEnumerable)null!); - Assert.Same(ValidationText.None, vt); + Assert.That(vt, Is.SameAs(ValidationText.None)); } /// /// Verifies that calling with returns . /// - [Fact] + [Test] public void CreateNullValidationTextEnumerableReturnsNone() { - var vt = ValidationText.Create((IEnumerable)null); + var vt = ValidationText.Create((IEnumerable)null!); - Assert.Same(ValidationText.None, vt); + Assert.That(vt, Is.SameAs(ValidationText.None)); } /// /// Verifies that calling with an enumerable containing returns . /// - [Fact] + [Test] public void CreateNullItemStringEnumerableReturnsNone() { - var vt = ValidationText.Create((IEnumerable)[null]); + var vt = ValidationText.Create((IEnumerable)[null!]); - Assert.Same(ValidationText.None, vt); + Assert.That(vt, Is.SameAs(ValidationText.None)); } /// /// Verifies that calling with an enumerable containing returns . /// - [Fact] + [Test] public void CreateNoneItemValidationTextEnumerableReturnsNone() { var vt = ValidationText.Create(new[] { ValidationText.None }); - Assert.Same(ValidationText.None, vt); + Assert.That(vt, Is.SameAs(ValidationText.None)); } /// /// Verifies that calling with an enumerable containing returns . /// - [Fact] + [Test] public void CreateNoneItemStringEnumerableReturnsNone() { var vt = ValidationText.Create(ValidationText.None); - Assert.Same(ValidationText.None, vt); + Assert.That(vt, Is.SameAs(ValidationText.None)); } /// /// Verifies that calling with returns . /// - [Fact] + [Test] public void CreateStringEmptyReturnsEmpty() { var vt = ValidationText.Create(string.Empty); - Assert.Same(ValidationText.Empty, vt); + Assert.That(vt, Is.SameAs(ValidationText.Empty)); } /// /// Verifies that calling with an enumerable containing returns . /// - [Fact] + [Test] public void CreateSingleStringEmptyReturnsEmpty() { var vt = ValidationText.Create((IEnumerable)[string.Empty]); - Assert.Same(ValidationText.Empty, vt); + Assert.That(vt, Is.SameAs(ValidationText.Empty)); } /// /// Verifies that calling with an enumerable containing returns . /// - [Fact] + [Test] public void CreateValidationTextEmptyReturnsEmpty() { var vt = ValidationText.Create(new[] { ValidationText.Empty }); - Assert.Same(ValidationText.Empty, vt); + Assert.That(vt, Is.SameAs(ValidationText.Empty)); } /// /// Verifies that calling with an enumerable containing two returns . /// - [Fact] + [Test] public void CombineValidationTextNoneReturnsNone() { var vt = ValidationText.Create(new[] { ValidationText.None, ValidationText.None }); - Assert.Same(ValidationText.None, vt); + Assert.That(vt, Is.SameAs(ValidationText.None)); } /// /// Verifies that calling with an enumerable containing and returns . /// - [Fact] + [Test] public void CombineValidationTextEmptyAndNoneReturnsEmpty() { var vt = ValidationText.Create(new[] { ValidationText.None, ValidationText.Empty }); - Assert.Same(ValidationText.Empty, vt); + Assert.That(vt, Is.SameAs(ValidationText.Empty)); } /// /// Verifies that calling with an enumerable containing two /// returns a single with two empty strings. /// - [Fact] + [Test] public void CombineValidationTextEmptyReturnsTwoEmpty() { var vt = ValidationText.Create(new[] { ValidationText.Empty, ValidationText.Empty }); - Assert.NotSame(ValidationText.Empty, vt); - Assert.Equal(2, vt.Count); - - // Calling Count() checks the enumeration returns no results, unlike the Count property. - Assert.Equal(2, vt.Count); - Assert.Equal(string.Empty, vt[0]); - Assert.Equal(string.Empty, vt[1]); - - Assert.Equal("|", vt.ToSingleLine("|")); + using (Assert.EnterMultipleScope()) + { + Assert.That(vt, Is.Not.SameAs(ValidationText.Empty)); + Assert.That(vt, Has.Count.EqualTo(2)); + Assert.That(vt, Has.Count.EqualTo(2)); + Assert.That(vt[0], Is.EqualTo(string.Empty)); + Assert.That(vt[1], Is.EqualTo(string.Empty)); + Assert.That(vt.ToSingleLine("|"), Is.EqualTo("|")); + } } } diff --git a/src/ReactiveUI.Validation.Tests/xunit.runner.json b/src/ReactiveUI.Validation.Tests/xunit.runner.json deleted file mode 100644 index 42db7ef9..00000000 --- a/src/ReactiveUI.Validation.Tests/xunit.runner.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "shadowCopy": false -} diff --git a/src/ReactiveUI.Validation.sln b/src/ReactiveUI.Validation.sln index c5f76378..7120bd2c 100644 --- a/src/ReactiveUI.Validation.sln +++ b/src/ReactiveUI.Validation.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.9.34728.123 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11109.219 d18.0-oob MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReactiveUI.Validation", "ReactiveUI.Validation\ReactiveUI.Validation.csproj", "{B62AABD0-22A4-470D-B6EB-F6B3EAE668DE}" EndProject @@ -13,7 +13,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\.gitignore = ..\.gitignore Directory.build.props = Directory.build.props Directory.build.targets = Directory.build.targets - global.json = global.json ..\README.md = ..\README.md stylecop.json = stylecop.json ..\version.json = ..\version.json diff --git a/src/ReactiveUI.Validation/Abstractions/IValidatableViewModel.cs b/src/ReactiveUI.Validation/Abstractions/IValidatableViewModel.cs index a40d5e06..7b08a9d3 100755 --- a/src/ReactiveUI.Validation/Abstractions/IValidatableViewModel.cs +++ b/src/ReactiveUI.Validation/Abstractions/IValidatableViewModel.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using ReactiveUI.Validation.Contexts; diff --git a/src/ReactiveUI.Validation/Collections/ArrayValidationText.cs b/src/ReactiveUI.Validation/Collections/ArrayValidationText.cs index ae8db21c..7e48bdc0 100644 --- a/src/ReactiveUI.Validation/Collections/ArrayValidationText.cs +++ b/src/ReactiveUI.Validation/Collections/ArrayValidationText.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections; diff --git a/src/ReactiveUI.Validation/Collections/IValidationText.cs b/src/ReactiveUI.Validation/Collections/IValidationText.cs index d6641112..74728558 100644 --- a/src/ReactiveUI.Validation/Collections/IValidationText.cs +++ b/src/ReactiveUI.Validation/Collections/IValidationText.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Generic; diff --git a/src/ReactiveUI.Validation/Collections/ReadOnlyDisposableCollection{T}.cs b/src/ReactiveUI.Validation/Collections/ReadOnlyDisposableCollection{T}.cs index 4530dcec..bfa5c14b 100644 --- a/src/ReactiveUI.Validation/Collections/ReadOnlyDisposableCollection{T}.cs +++ b/src/ReactiveUI.Validation/Collections/ReadOnlyDisposableCollection{T}.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Collections/SingleValidationText.cs b/src/ReactiveUI.Validation/Collections/SingleValidationText.cs index a3d2ca3d..89970512 100644 --- a/src/ReactiveUI.Validation/Collections/SingleValidationText.cs +++ b/src/ReactiveUI.Validation/Collections/SingleValidationText.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Collections/ValidationText.cs b/src/ReactiveUI.Validation/Collections/ValidationText.cs index 9c288bde..d8ea6ede 100755 --- a/src/ReactiveUI.Validation/Collections/ValidationText.cs +++ b/src/ReactiveUI.Validation/Collections/ValidationText.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Comparators/ValidationStateComparer.cs b/src/ReactiveUI.Validation/Comparators/ValidationStateComparer.cs index 4bf6b36c..fd7a4b1f 100755 --- a/src/ReactiveUI.Validation/Comparators/ValidationStateComparer.cs +++ b/src/ReactiveUI.Validation/Comparators/ValidationStateComparer.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Components/Abstractions/IPropertyValidationComponent.cs b/src/ReactiveUI.Validation/Components/Abstractions/IPropertyValidationComponent.cs index e57ef325..3328761c 100644 --- a/src/ReactiveUI.Validation/Components/Abstractions/IPropertyValidationComponent.cs +++ b/src/ReactiveUI.Validation/Components/Abstractions/IPropertyValidationComponent.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. namespace ReactiveUI.Validation.Components.Abstractions; diff --git a/src/ReactiveUI.Validation/Components/Abstractions/IValidatesProperties.cs b/src/ReactiveUI.Validation/Components/Abstractions/IValidatesProperties.cs index 286d7e10..f4f728d1 100644 --- a/src/ReactiveUI.Validation/Components/Abstractions/IValidatesProperties.cs +++ b/src/ReactiveUI.Validation/Components/Abstractions/IValidatesProperties.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System.Collections.Generic; diff --git a/src/ReactiveUI.Validation/Components/Abstractions/IValidationComponent.cs b/src/ReactiveUI.Validation/Components/Abstractions/IValidationComponent.cs index 9747dd65..7b723b8f 100755 --- a/src/ReactiveUI.Validation/Components/Abstractions/IValidationComponent.cs +++ b/src/ReactiveUI.Validation/Components/Abstractions/IValidationComponent.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Components/BasePropertyValidation.cs b/src/ReactiveUI.Validation/Components/BasePropertyValidation.cs index 5f8d990c..7132d3ad 100755 --- a/src/ReactiveUI.Validation/Components/BasePropertyValidation.cs +++ b/src/ReactiveUI.Validation/Components/BasePropertyValidation.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; @@ -9,6 +9,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reactive.Disposables; +using System.Reactive.Disposables.Fluent; using System.Reactive.Linq; using System.Reactive.Subjects; @@ -180,6 +181,9 @@ public sealed class BasePropertyValidation : Bas /// ViewModel property. /// Func to define if the viewModelProperty is valid or not. /// Validation error message. +#if NET6_0_OR_GREATER + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public BasePropertyValidation( TViewModel viewModel, Expression> viewModelProperty, @@ -196,6 +200,9 @@ public BasePropertyValidation( /// ViewModel property. /// Func to define if the viewModelProperty is valid or not. /// Func to define the validation error message based on the viewModelProperty value. +#if NET6_0_OR_GREATER + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public BasePropertyValidation( TViewModel viewModel, Expression> viewModelProperty, @@ -213,6 +220,9 @@ public BasePropertyValidation( /// ViewModel property. /// Func to define if the viewModelProperty is valid or not. /// Func to define the validation error message based on the viewModelProperty and isValidFunc values. +#if NET6_0_OR_GREATER + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public BasePropertyValidation( TViewModel viewModel, Expression> viewModelProperty, @@ -231,6 +241,10 @@ public BasePropertyValidation( /// ViewModel property. /// Func to define if the viewModelProperty is valid or not. /// Func to define the validation error message based on the viewModelProperty and isValidFunc values. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif private BasePropertyValidation( TViewModel viewModel, Expression> viewModelProperty, diff --git a/src/ReactiveUI.Validation/Components/ObservableValidationBase{TViewModel,TValue}.cs b/src/ReactiveUI.Validation/Components/ObservableValidationBase{TViewModel,TValue}.cs index 6c07713c..51a073e1 100644 --- a/src/ReactiveUI.Validation/Components/ObservableValidationBase{TViewModel,TValue}.cs +++ b/src/ReactiveUI.Validation/Components/ObservableValidationBase{TViewModel,TValue}.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; @@ -8,6 +8,7 @@ using System.Linq; using System.Linq.Expressions; using System.Reactive.Disposables; +using System.Reactive.Disposables.Fluent; using System.Reactive.Linq; using System.Reactive.Subjects; using ReactiveUI.Validation.Collections; diff --git a/src/ReactiveUI.Validation/Components/ObservableValidation{TViewModel,TValue,TProp}.cs b/src/ReactiveUI.Validation/Components/ObservableValidation{TViewModel,TValue,TProp}.cs index ed6c01b7..38b9c9c4 100644 --- a/src/ReactiveUI.Validation/Components/ObservableValidation{TViewModel,TValue,TProp}.cs +++ b/src/ReactiveUI.Validation/Components/ObservableValidation{TViewModel,TValue,TProp}.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Components/ObservableValidation{TViewModel,TValue}.cs b/src/ReactiveUI.Validation/Components/ObservableValidation{TViewModel,TValue}.cs index a5f70555..aa280def 100644 --- a/src/ReactiveUI.Validation/Components/ObservableValidation{TViewModel,TValue}.cs +++ b/src/ReactiveUI.Validation/Components/ObservableValidation{TViewModel,TValue}.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Contexts/IValidationContext.cs b/src/ReactiveUI.Validation/Contexts/IValidationContext.cs index 740d5c6d..ab8cff0f 100644 --- a/src/ReactiveUI.Validation/Contexts/IValidationContext.cs +++ b/src/ReactiveUI.Validation/Contexts/IValidationContext.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Contexts/ValidationContext.cs b/src/ReactiveUI.Validation/Contexts/ValidationContext.cs index effeefd8..61942943 100755 --- a/src/ReactiveUI.Validation/Contexts/ValidationContext.cs +++ b/src/ReactiveUI.Validation/Contexts/ValidationContext.cs @@ -1,17 +1,21 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; using System.Buffers; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reactive.Concurrency; using System.Reactive.Disposables; +using System.Reactive.Disposables.Fluent; using System.Reactive.Linq; using System.Reactive.Subjects; + using DynamicData; + using ReactiveUI.Validation.Collections; using ReactiveUI.Validation.Components.Abstractions; using ReactiveUI.Validation.States; @@ -46,6 +50,9 @@ public class ValidationContext : ReactiveObject, IValidationContext /// Initializes a new instance of the class. /// /// Optional scheduler to use for the properties. Uses the current thread scheduler by default. +#if NET6_0_OR_GREATER + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public ValidationContext(IScheduler? scheduler = null) { scheduler ??= CurrentThreadScheduler.Instance; diff --git a/src/ReactiveUI.Validation/Extensions/ArrayPoolExtensions.cs b/src/ReactiveUI.Validation/Extensions/ArrayPoolExtensions.cs index 4f3e2b9b..9e472e28 100644 --- a/src/ReactiveUI.Validation/Extensions/ArrayPoolExtensions.cs +++ b/src/ReactiveUI.Validation/Extensions/ArrayPoolExtensions.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Extensions/ExpressionExtensions.cs b/src/ReactiveUI.Validation/Extensions/ExpressionExtensions.cs index e39cdfa0..71483d4d 100644 --- a/src/ReactiveUI.Validation/Extensions/ExpressionExtensions.cs +++ b/src/ReactiveUI.Validation/Extensions/ExpressionExtensions.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Extensions/ValidatableViewModelExtensions.cs b/src/ReactiveUI.Validation/Extensions/ValidatableViewModelExtensions.cs index a6005d75..c9ee2df5 100644 --- a/src/ReactiveUI.Validation/Extensions/ValidatableViewModelExtensions.cs +++ b/src/ReactiveUI.Validation/Extensions/ValidatableViewModelExtensions.cs @@ -1,13 +1,15 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reactive.Disposables; using System.Reactive.Linq; + using ReactiveUI.Validation.Abstractions; using ReactiveUI.Validation.Components; using ReactiveUI.Validation.Components.Abstractions; @@ -32,6 +34,10 @@ public static class ValidatableViewModelExtensions /// Func to define if the viewModelProperty is valid or not. /// Validation error message. /// Returns a object. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static ValidationHelper ValidationRule( this TViewModel viewModel, Expression> viewModelProperty, @@ -76,6 +82,10 @@ public static ValidationHelper ValidationRule( /// Func to define if the viewModelProperty is valid or not. /// Func to define the validation error message based on the viewModelProperty value. /// Returns a object. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static ValidationHelper ValidationRule( this TViewModel viewModel, Expression> viewModelProperty, @@ -120,6 +130,10 @@ public static ValidationHelper ValidationRule( /// It should be noted that the observable should provide an initial value, otherwise that can result /// in an inconsistent performance. /// +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static ValidationHelper ValidationRule( this TViewModel viewModel, IObservable validationObservable, @@ -164,6 +178,10 @@ public static ValidationHelper ValidationRule( /// It should be noted that the observable should provide an initial value, otherwise that can result /// in an inconsistent performance. /// +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static ValidationHelper ValidationRule( this TViewModel viewModel, IObservable validationObservable, @@ -207,6 +225,10 @@ public static ValidationHelper ValidationRule( /// It should be noted that the observable should provide an initial value, otherwise that can result /// in an inconsistent performance. /// +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static ValidationHelper ValidationRule( this TViewModel viewModel, IObservable validationObservable) @@ -239,6 +261,10 @@ public static ValidationHelper ValidationRule( /// It should be noted that the observable should provide an initial value, otherwise that can result /// in an inconsistent performance. /// +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static ValidationHelper ValidationRule( this TViewModel viewModel, IObservable validationObservable) @@ -275,6 +301,10 @@ public static ValidationHelper ValidationRule( /// It should be noted that the observable should provide an initial value, otherwise that can result /// in an inconsistent performance. /// +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static ValidationHelper ValidationRule( this TViewModel viewModel, Expression> viewModelProperty, @@ -328,6 +358,10 @@ public static ValidationHelper ValidationRule( /// It should be noted that the observable should provide an initial value, otherwise that can result /// in an inconsistent performance. /// +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static ValidationHelper ValidationRule( this TViewModel viewModel, Expression> viewModelProperty, @@ -379,6 +413,10 @@ public static ValidationHelper ValidationRule +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static ValidationHelper ValidationRule( this TViewModel viewModel, Expression> viewModelProperty, @@ -419,6 +457,10 @@ public static ValidationHelper ValidationRule( /// It should be noted that the observable should provide an initial value, otherwise that can result /// in an inconsistent performance. /// +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static ValidationHelper ValidationRule( this TViewModel viewModel, Expression> viewModelProperty, @@ -525,6 +567,10 @@ public static IObservable IsValid(this TViewModel viewModel) /// The disposable validation component to register into the context. /// The disposable validation component type. /// The bindable validation helper holding the disposable. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif private static ValidationHelper RegisterValidation( this IValidatableViewModel viewModel, TValidationComponent validation) diff --git a/src/ReactiveUI.Validation/Extensions/ValidatesPropertiesExtensions.cs b/src/ReactiveUI.Validation/Extensions/ValidatesPropertiesExtensions.cs index c3f5bc81..f797e7bf 100644 --- a/src/ReactiveUI.Validation/Extensions/ValidatesPropertiesExtensions.cs +++ b/src/ReactiveUI.Validation/Extensions/ValidatesPropertiesExtensions.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Extensions/ValidationContextExtensions.cs b/src/ReactiveUI.Validation/Extensions/ValidationContextExtensions.cs index ce768b64..c0a7f2c5 100755 --- a/src/ReactiveUI.Validation/Extensions/ValidationContextExtensions.cs +++ b/src/ReactiveUI.Validation/Extensions/ValidationContextExtensions.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/Extensions/ViewForExtensions.cs b/src/ReactiveUI.Validation/Extensions/ViewForExtensions.cs index de16b085..6e8440c8 100755 --- a/src/ReactiveUI.Validation/Extensions/ViewForExtensions.cs +++ b/src/ReactiveUI.Validation/Extensions/ViewForExtensions.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; @@ -37,6 +37,10 @@ public static class ViewForExtensions /// IValidationTextFormatter<string> into Splat.Locator. /// /// Returns a object. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static IDisposable BindValidation( this TView view, TViewModel? viewModel, @@ -74,6 +78,10 @@ public static IDisposable BindValidation /// Returns a object. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static IDisposable BindValidation( this TView view, TViewModel? viewModel, @@ -106,6 +114,10 @@ public static IDisposable BindValidation( /// IValidationTextFormatter<string> into Splat.Locator. /// /// Returns a object. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static IDisposable BindValidation( this TView view, TViewModel? viewModel, diff --git a/src/ReactiveUI.Validation/Formatters/Abstractions/IValidationTextFormatter.cs b/src/ReactiveUI.Validation/Formatters/Abstractions/IValidationTextFormatter.cs index 2b6c4cf0..06dd45df 100755 --- a/src/ReactiveUI.Validation/Formatters/Abstractions/IValidationTextFormatter.cs +++ b/src/ReactiveUI.Validation/Formatters/Abstractions/IValidationTextFormatter.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using ReactiveUI.Validation.Collections; diff --git a/src/ReactiveUI.Validation/Formatters/SingleLineFormatter.cs b/src/ReactiveUI.Validation/Formatters/SingleLineFormatter.cs index 7521ae5c..443e5c96 100755 --- a/src/ReactiveUI.Validation/Formatters/SingleLineFormatter.cs +++ b/src/ReactiveUI.Validation/Formatters/SingleLineFormatter.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using ReactiveUI.Validation.Collections; diff --git a/src/ReactiveUI.Validation/Helpers/ReactiveValidationObject.cs b/src/ReactiveUI.Validation/Helpers/ReactiveValidationObject.cs index 06450361..e6331d7a 100644 --- a/src/ReactiveUI.Validation/Helpers/ReactiveValidationObject.cs +++ b/src/ReactiveUI.Validation/Helpers/ReactiveValidationObject.cs @@ -1,23 +1,28 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reactive.Concurrency; using System.Reactive.Disposables; +using System.Reactive.Disposables.Fluent; using System.Reactive.Linq; + using DynamicData; + using ReactiveUI.Validation.Abstractions; using ReactiveUI.Validation.Collections; using ReactiveUI.Validation.Components.Abstractions; using ReactiveUI.Validation.Contexts; using ReactiveUI.Validation.Formatters; using ReactiveUI.Validation.Formatters.Abstractions; + using Splat; namespace ReactiveUI.Validation.Helpers; @@ -43,6 +48,9 @@ public abstract class ReactiveValidationObject : ReactiveObject, IValidatableVie /// default value, implement and register an instance of /// IValidationTextFormatter<string> into Splat.Locator. /// +#if NET6_0_OR_GREATER + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif protected ReactiveValidationObject( IScheduler? scheduler = null, IValidationTextFormatter? formatter = null) diff --git a/src/ReactiveUI.Validation/Helpers/ValidationHelper.cs b/src/ReactiveUI.Validation/Helpers/ValidationHelper.cs index be6055d7..9b4ffbd1 100755 --- a/src/ReactiveUI.Validation/Helpers/ValidationHelper.cs +++ b/src/ReactiveUI.Validation/Helpers/ValidationHelper.cs @@ -1,10 +1,12 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; +using System.Diagnostics.CodeAnalysis; using System.Reactive.Linq; + using ReactiveUI.Validation.Collections; using ReactiveUI.Validation.Components.Abstractions; using ReactiveUI.Validation.States; @@ -28,6 +30,9 @@ public class ValidationHelper : ReactiveObject, IDisposable /// /// Validation property. /// The disposable to dispose when the helper is disposed. +#if NET6_0_OR_GREATER + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public ValidationHelper(IValidationComponent validation, IDisposable? cleanup = null) { _validation = validation ?? throw new ArgumentNullException(nameof(validation)); diff --git a/src/ReactiveUI.Validation/ReactiveUI.Validation.csproj b/src/ReactiveUI.Validation/ReactiveUI.Validation.csproj index a75c1396..bdb359bc 100644 --- a/src/ReactiveUI.Validation/ReactiveUI.Validation.csproj +++ b/src/ReactiveUI.Validation/ReactiveUI.Validation.csproj @@ -1,17 +1,15 @@  - netstandard2.0;net8.0;net8.0-android;net8.0-ios;net8.0-tvos;net8.0-macos;net8.0-maccatalyst;net9.0;net9.0-android;net9.0-ios;net9.0-tvos;net9.0-macos;net9.0-maccatalyst - $(TargetFrameworks);net462;net472;net8.0-windows10.0.17763.0;net9.0-windows10.0.17763.0 - $(NoWarn);CS1591 + $(LibraryTargetFrameworks) enable - + - + diff --git a/src/ReactiveUI.Validation/States/IValidationState.cs b/src/ReactiveUI.Validation/States/IValidationState.cs index 998ea331..5f3780b6 100644 --- a/src/ReactiveUI.Validation/States/IValidationState.cs +++ b/src/ReactiveUI.Validation/States/IValidationState.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/States/ValidationState.cs b/src/ReactiveUI.Validation/States/ValidationState.cs index 446f7d43..dd339be1 100755 --- a/src/ReactiveUI.Validation/States/ValidationState.cs +++ b/src/ReactiveUI.Validation/States/ValidationState.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/ValidationBindings/Abstractions/IValidationBinding.cs b/src/ReactiveUI.Validation/ValidationBindings/Abstractions/IValidationBinding.cs index 08035e1d..555370e2 100755 --- a/src/ReactiveUI.Validation/ValidationBindings/Abstractions/IValidationBinding.cs +++ b/src/ReactiveUI.Validation/ValidationBindings/Abstractions/IValidationBinding.cs @@ -1,6 +1,6 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; diff --git a/src/ReactiveUI.Validation/ValidationBindings/ValidationBinding.cs b/src/ReactiveUI.Validation/ValidationBindings/ValidationBinding.cs index 6e408f4c..ea244dc4 100755 --- a/src/ReactiveUI.Validation/ValidationBindings/ValidationBinding.cs +++ b/src/ReactiveUI.Validation/ValidationBindings/ValidationBinding.cs @@ -1,14 +1,16 @@ -// Copyright (c) 2025 .NET Foundation and Contributors. All rights reserved. -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. +// Copyright (c) 2025 ReactiveUI and Contributors. All rights reserved. +// Licensed to the ReactiveUI and Contributors under one or more agreements. +// The ReactiveUI and Contributors licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reactive; using System.Reactive.Linq; + using ReactiveUI.Validation.Abstractions; using ReactiveUI.Validation.Extensions; using ReactiveUI.Validation.Formatters; @@ -16,6 +18,7 @@ using ReactiveUI.Validation.Helpers; using ReactiveUI.Validation.States; using ReactiveUI.Validation.ValidationBindings.Abstractions; + using Splat; namespace ReactiveUI.Validation.ValidationBindings; @@ -44,6 +47,10 @@ public sealed class ValidationBinding : IValidationBinding /// /// Indicates if the ViewModel property to find is unique. /// Returns a validation component. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static IValidationBinding ForProperty( TView view, Expression> viewModelProperty, @@ -99,6 +106,10 @@ public static IValidationBinding ForPropertyValidation formatter. /// Indicates if the ViewModel property to find is unique. /// Returns a validation component. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static IValidationBinding ForProperty( TView view, Expression> viewModelProperty, @@ -155,6 +166,10 @@ public static IValidationBinding ForProperty /// Returns a validation component. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static IValidationBinding ForValidationHelperProperty( TView view, Expression> viewModelHelperProperty, @@ -210,6 +225,10 @@ public static IValidationBinding ForValidationHelperPropertyAction to be executed. /// Validation formatter. /// Returns a validation component. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static IValidationBinding ForValidationHelperProperty( TView view, Expression> viewModelHelperProperty, @@ -266,6 +285,10 @@ public static IValidationBinding ForValidationHelperPropertyAction to be executed. /// Validation formatter. /// Returns a validation component. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static IValidationBinding ForViewModel( TView view, Action action, @@ -312,6 +335,10 @@ public static IValidationBinding ForViewModel( /// IValidationTextFormatter<string> into Splat.Locator. /// /// Returns a validation component. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif public static IValidationBinding ForViewModel( TView view, Expression> viewProperty, @@ -356,6 +383,10 @@ public void Dispose() => /// Target instance. /// View property. /// Returns a validation component. +#if NET6_0_OR_GREATER + [RequiresDynamicCode("WhenAnyValue uses expression trees which require dynamic code generation in AOT scenarios.")] + [RequiresUnreferencedCode("WhenAnyValue may reference members that could be trimmed in AOT scenarios.")] +#endif private static IObservable BindToView( IObservable valueChange, TTarget target, diff --git a/src/stylecop.json b/src/stylecop.json index 4d017362..c21d7662 100644 --- a/src/stylecop.json +++ b/src/stylecop.json @@ -12,8 +12,8 @@ "documentInterfaces": true, "documentPrivateFields": false, "documentationCulture": "en-US", - "companyName": ".NET Foundation and Contributors", - "copyrightText": "Copyright (c) 2025 {companyName}. All rights reserved.\nLicensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the {licenseName} license.\nSee the {licenseFile} file in the project root for full license information.", + "companyName": "ReactiveUI and Contributors", + "copyrightText": "Copyright (c) 2025 {companyName}. All rights reserved.\nLicensed to the {companyName} under one or more agreements.\nThe {companyName} licenses this file to you under the {licenseName} license.\nSee the {licenseFile} file in the project root for full license information.", "variables": { "licenseName": "MIT", "licenseFile": "LICENSE" diff --git a/tests.runsettings b/tests.runsettings new file mode 100644 index 00000000..e5d4ab7d --- /dev/null +++ b/tests.runsettings @@ -0,0 +1,6 @@ + + + + 4 + + diff --git a/version.json b/version.json index 7532ada5..85a8426b 100644 --- a/version.json +++ b/version.json @@ -1,5 +1,5 @@ { - "version": "5.0", + "version": "6.0", "publicReleaseRefSpec": [ "^refs/heads/main$", "^refs/heads/latest$",