diff --git a/.gitignore b/.gitignore index 3998ec85be..c88ebe9bf7 100644 --- a/.gitignore +++ b/.gitignore @@ -246,7 +246,7 @@ src/ReactiveUI.Events*/Events_*.cs # macOS .DS_Store -src/*.Tests/API/*.received.txt +src/*.Tests/**/ApiApprovalTests*.received.txt .idea/ # Fody Weavers (for tests) diff --git a/azure-pipelines-tests.yml b/azure-pipelines-tests.yml index 7872314f8b..f280a6a4ce 100644 --- a/azure-pipelines-tests.yml +++ b/azure-pipelines-tests.yml @@ -12,6 +12,11 @@ pool: vmImage: vs2017-win2016 steps: +- task: DotNetCoreInstaller@0 + displayName: Install Dot Net Core v2.2.1 + inputs: + version: '2.2.103' + - task: BatchScript@1 inputs: filename: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\Tools\\VsDevCmd.bat" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2f77070cdd..c7484b8ebb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -12,6 +12,11 @@ pool: vmImage: vs2017-win2016 steps: +- task: DotNetCoreInstaller@0 + displayName: Install Dot Net Core v2.2.1 + inputs: + version: '2.2.103' + - task: BatchScript@1 inputs: filename: "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\Tools\\VsDevCmd.bat" diff --git a/build.cake b/build.cake index bec06a8852..d7de061ee5 100644 --- a/build.cake +++ b/build.cake @@ -7,7 +7,7 @@ ////////////////////////////////////////////////////////////////////// #addin "nuget:?package=Cake.FileHelpers&version=3.1.0" -#addin "nuget:?package=Cake.Coveralls&version=0.9.0" +#addin "nuget:?package=Cake.Coverlet&version=2.2.1" #addin "nuget:?package=Cake.PinNuGetDependency&loaddependencies=true&version=3.2.3" #addin "nuget:?package=Cake.Powershell&version=0.4.7" #addin "nuget:?package=Cake.Codecov&version=0.5.0" @@ -22,11 +22,11 @@ // TOOLS ////////////////////////////////////////////////////////////////////// -#tool "nuget:?package=OpenCover&version=4.6.519" #tool "nuget:?package=ReportGenerator&version=4.0.4" #tool "nuget:?package=vswhere&version=2.5.2" #tool "nuget:?package=xunit.runner.console&version=2.4.1" #tool "nuget:?package=Codecov&version=1.1.0" +#tool "nuget:?package=OpenCover&version=4.7.906-rc" ////////////////////////////////////////////////////////////////////// // DOTNET TOOLS @@ -45,6 +45,12 @@ if (string.IsNullOrWhiteSpace(target)) target = "Default"; } +var configuration = Argument("configuration", "Release"); +if (string.IsNullOrWhiteSpace(configuration)) +{ + configuration = "Release"; +} + var includePrerelease = Argument("includePrerelease", false); var vsLocationString = Argument("vsLocation", string.Empty); var msBuildPathString = Argument("msBuildPath", string.Empty); @@ -98,10 +104,16 @@ var packageWhitelist = new[] "ReactiveUI.LeakTests" }; -var packageTestWhitelist = new[] +(string projectName, bool performCoverageTesting)[] packageTestWhitelist = new[] { - "ReactiveUI.Tests", - "ReactiveUI.Fody.Tests" + ("ReactiveUI.Tests", true), + ("ReactiveUI.Fody.Tests", true), +}; + +(string name, bool performCoverageTesting)[] testFrameworks = new[] +{ + ("net461", true), + ("netcoreapp2.0", false), }; (string targetName, string destination)[] eventGenerators = new[] @@ -152,9 +164,9 @@ Teardown(context => ////////////////////////////////////////////////////////////////////// // HELPER METHODS ////////////////////////////////////////////////////////////////////// -Action Build = (solution, packageOutputPath, createPackage, forceUseFullDebugType) => +Action Build = (solution, packageOutputPath) => { - Information("Building {0} using {1}, createPackage = {2}, forceUseFullDebugType = {3}", solution, msBuildPath, createPackage, forceUseFullDebugType); + Information("Building {0} using {1}", solution, msBuildPath); var msBuildSettings = new MSBuildSettings() { ToolPath = msBuildPath, @@ -163,26 +175,13 @@ Action Build = (solution, packageOutputPath, createP Restore = true } .WithProperty("TreatWarningsAsErrors", treatWarningsAsErrors.ToString()) - .SetConfiguration("Release") + .SetConfiguration(configuration) + .WithTarget("build;pack") .SetVerbosity(Verbosity.Minimal); - if (forceUseFullDebugType) - { - msBuildSettings = msBuildSettings.WithProperty("DebugType", "full"); - } - - if (createPackage) - { - if (!string.IsNullOrWhiteSpace(packageOutputPath)) - { - msBuildSettings = msBuildSettings.WithProperty("PackageOutputPath", MakeAbsolute(Directory(packageOutputPath)).ToString().Quote()); - } - - msBuildSettings = msBuildSettings.WithTarget("build;pack"); - } - else + if (!string.IsNullOrWhiteSpace(packageOutputPath)) { - msBuildSettings = msBuildSettings.WithTarget("build"); + msBuildSettings = msBuildSettings.WithProperty("PackageOutputPath", MakeAbsolute(Directory(packageOutputPath)).ToString().Quote()); } MSBuild(solution, msBuildSettings); @@ -195,7 +194,7 @@ Action Build = (solution, packageOutputPath, createP Task("BuildEventBuilder") .Does(() => { - Build("./src/EventBuilder.sln", artifactDirectory + "eventbuilder", false, false); + Build("./src/EventBuilder.sln", artifactDirectory + "eventbuilder"); }); Task("GenerateEvents") @@ -243,9 +242,9 @@ Task("BuildReactiveUIPackages") CleanDirectories("./src/**/obj/Release"); CleanDirectories("./src/**/bin/Release"); - foreach(var package in packageWhitelist) + foreach(var packageName in packageWhitelist) { - Build("./src/" + package + "/" + package + ".csproj", packagesArtifactDirectory, true, false); + Build($"./src/{packageName}/{packageName}.csproj", packagesArtifactDirectory); } CopyFiles(GetFiles("./src/**/bin/Release/**/*"), Directory(binariesArtifactDirectory), true); @@ -265,20 +264,21 @@ Task("RunUnitTests") CleanDirectories("./src/**/obj/Release"); CleanDirectories("./src/**/bin/Release"); - foreach (var package in fodyPackages) + foreach (var packageName in fodyPackages) { - Build("./src/" + package + "/" + package + ".csproj", null, true, true); + Build($"./src/{packageName}/{packageName}.csproj", null); } var openCoverSettings = new OpenCoverSettings { ReturnTargetCodeOffset = 0, MergeOutput = true, } - .WithFilter("+[*]*") + .WithFilter("+[ReactiveUI*]*") .WithFilter("-[*.Testing]*") .WithFilter("-[*.Tests*]*") - .WithFilter("-[ReactiveUI.Events]*") - .WithFilter("-[Splat*]*") + .WithFilter("-[ReactiveUI.Events*]*") + .WithFilter("-[ReactiveUI*]ReactiveUI.*Legacy*") + .WithFilter("-[ReactiveUI*]ThisAssembly*") .ExcludeByAttribute("*.ExcludeFromCodeCoverage*") .ExcludeByFile("*/*Designer.cs") .ExcludeByFile("*/*.g.cs") @@ -292,16 +292,37 @@ Task("RunUnitTests") NoAppDomain = true }; - foreach (var projectName in packageTestWhitelist) + foreach (var packageDetails in packageTestWhitelist) { - OpenCover(tool => - { - Build("./src/" + projectName + "/" + projectName + ".csproj", null, true, true); + var packageName = packageDetails.projectName; + var projectName = $"./src/{packageName}/{packageName}.csproj"; + Build(projectName, null); - tool.XUnit2("./src/" + projectName + "/bin/" + "**/*.Tests.dll", xunitSettings); - }, - testCoverageOutputFile, - openCoverSettings); + foreach (var testFramework in testFrameworks) + { + if (testFramework.performCoverageTesting && packageDetails.performCoverageTesting) + { + Information($"Generate OpenCover information for {packageName} {testFramework.name}"); + OpenCover( + tool => tool.XUnit2($"./src/{packageName}/bin/{configuration}/{testFramework.name}/**/*.Tests.dll", xunitSettings), + testCoverageOutputFile, + openCoverSettings); + } + else + { + Information($"Running unit tests only for {packageName} {testFramework.name}"); + var testSettings = new DotNetCoreTestSettings { + NoBuild = true, + Framework = testFramework.name, + Configuration = configuration, + ResultsDirectory = testsArtifactDirectory, + Logger = $"trx;LogFileName=testresults-{packageName}-{testFramework.name}.trx", + TestAdapterPath = GetDirectories("./tools/xunit.runner.console*/**/netcoreapp2.0").FirstOrDefault(), + }; + + DotNetCoreTest(projectName, testSettings); + } + } } ReportGenerator(testCoverageOutputFile, testsArtifactDirectory + "Report/"); diff --git a/src/ApiGeneratorGlobalSuppressions.cs b/src/ApiGeneratorGlobalSuppressions.cs index a12ba4d785..4cc1e892f3 100644 --- a/src/ApiGeneratorGlobalSuppressions.cs +++ b/src/ApiGeneratorGlobalSuppressions.cs @@ -1,7 +1,7 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "NuGet Inclusion", Scope = "member", Target = "~M:PublicApiGenerator.ApiGenerator.AddCtorToTypeDeclaration(System.CodeDom.CodeTypeDeclaration,Mono.Cecil.MethodDefinition,System.Collections.Generic.HashSet{System.String})")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1503:Braces should not be omitted", Justification = "NuGet Inclusion", Scope = "member", Target = "~M:PublicApiGenerator.ApiGenerator.AddFieldToTypeDeclaration(System.CodeDom.CodeTypeDeclaration,Mono.Cecil.FieldDefinition,System.Collections.Generic.HashSet{System.String})")] diff --git a/src/Directory.build.props b/src/Directory.build.props index dc7a47947f..983bcad946 100644 --- a/src/Directory.build.props +++ b/src/Directory.build.props @@ -14,10 +14,7 @@ true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - - - + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb $(MSBuildThisFileDirectory)analyzers.ruleset diff --git a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.approved.txt b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.net461.approved.txt similarity index 100% rename from src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.approved.txt rename to src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.net461.approved.txt diff --git a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.netcoreapp2.0.approved.txt b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.netcoreapp2.0.approved.txt new file mode 100644 index 0000000000..4c792d9add --- /dev/null +++ b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.ReactiveUIFody.netcoreapp2.0.approved.txt @@ -0,0 +1,31 @@ +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.AndroidSupport")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Tests")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Winforms")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Wpf")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.XamForms")] +[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v2.0", FrameworkDisplayName="")] +namespace ReactiveUI.Fody.Helpers +{ + [System.AttributeUsageAttribute(System.AttributeTargets.Method | System.AttributeTargets.Property | System.AttributeTargets.All)] + public class ObservableAsPropertyAttribute : System.Attribute + { + public ObservableAsPropertyAttribute() { } + } + public class static ObservableAsPropertyExtensions + { + public static ReactiveUI.ObservableAsPropertyHelper ToPropertyEx(this System.IObservable @this, TObj source, System.Linq.Expressions.Expression> property, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + where TObj : ReactiveUI.ReactiveObject { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Property | System.AttributeTargets.All)] + public class ReactiveAttribute : System.Attribute + { + public ReactiveAttribute() { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Property | System.AttributeTargets.All)] + public class ReactiveDependencyAttribute : System.Attribute + { + public ReactiveDependencyAttribute(string targetName) { } + public string Target { get; } + public string TargetProperty { get; set; } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.cs b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.cs index f5aae4dbb2..3a3128324b 100644 --- a/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.cs +++ b/src/ReactiveUI.Fody.Tests/API/ApiApprovalTests.cs @@ -14,26 +14,12 @@ namespace ReactiveUI.Fody.Tests.API { [ExcludeFromCodeCoverage] - public class ApiApprovalTests + public class ApiApprovalTests : ApiApprovalBase { [Fact] public void ReactiveUIFody() { - var publicApi = Filter(ApiGenerator.GeneratePublicApi(typeof(ReactiveAttribute).Assembly)); - publicApi.ShouldMatchApproved(); - } - - private static string Filter(string text) - { - return string.Join(Environment.NewLine, text.Split( - new[] - { - Environment.NewLine - }, StringSplitOptions.RemoveEmptyEntries) - .Where(l => !l.StartsWith("[assembly: AssemblyVersion(")) - .Where(l => !l.StartsWith("[assembly: AssemblyFileVersion(")) - .Where(l => !l.StartsWith("[assembly: AssemblyInformationalVersion(")) - .Where(l => !string.IsNullOrWhiteSpace(l))); + CheckApproval(typeof(ReactiveAttribute).Assembly); } } } diff --git a/src/ReactiveUI.Fody.Tests/ApiApprovalBase.cs b/src/ReactiveUI.Fody.Tests/ApiApprovalBase.cs new file mode 100644 index 0000000000..143355e207 --- /dev/null +++ b/src/ReactiveUI.Fody.Tests/ApiApprovalBase.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using PublicApiGenerator; +using Shouldly; +using Splat; +using Xunit; + +namespace ReactiveUI.Fody.Tests +{ + [ExcludeFromCodeCoverage] + public abstract class ApiApprovalBase + { + private static readonly Regex _removeCoverletSectionRegex = new Regex(@"^namespace Coverlet\.Core\.Instrumentation\.Tracker.*?^}", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.Compiled); + + protected static void CheckApproval(Assembly assembly, [CallerMemberName]string memberName = null, [CallerFilePath]string filePath = null) + { + var targetFrameworkName = Assembly.GetExecutingAssembly().GetTargetFrameworkName(); + + var sourceDirectory = Path.GetDirectoryName(filePath); + + var approvedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.approved.txt"); + var receivedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.received.txt"); + + string approvedPublicApi = string.Empty; + + if (File.Exists(approvedFileName)) + { + approvedPublicApi = File.ReadAllText(approvedFileName); + } + + var receivedPublicApi = Filter(ApiGenerator.GeneratePublicApi(assembly)); + + if (!string.Equals(receivedPublicApi, approvedPublicApi, StringComparison.InvariantCulture)) + { + File.WriteAllText(receivedFileName, receivedPublicApi); + ShouldlyConfiguration.DiffTools.GetDiffTool().Open(receivedFileName, approvedFileName, true); + } + + Assert.Equal(approvedPublicApi, receivedPublicApi); + } + + private static string Filter(string text) + { + text = _removeCoverletSectionRegex.Replace(text, string.Empty); + return string.Join(Environment.NewLine, text.Split( + new[] + { + Environment.NewLine + }, StringSplitOptions.RemoveEmptyEntries) + .Where(l => + !l.StartsWith("[assembly: AssemblyVersion(", StringComparison.InvariantCulture) && + !l.StartsWith("[assembly: AssemblyFileVersion(", StringComparison.InvariantCulture) && + !l.StartsWith("[assembly: AssemblyInformationalVersion(", StringComparison.InvariantCulture) && + !string.IsNullOrWhiteSpace(l))); + } + } +} diff --git a/src/ReactiveUI.Fody.Tests/Issues/Issue11Tests.cs b/src/ReactiveUI.Fody.Tests/Issues/Issue11Tests.cs index 4f9b9ceb89..44efc64b40 100644 --- a/src/ReactiveUI.Fody.Tests/Issues/Issue11Tests.cs +++ b/src/ReactiveUI.Fody.Tests/Issues/Issue11Tests.cs @@ -14,22 +14,8 @@ public class Issue11Tests [Fact] public void AllowObservableAsPropertyAttributeOnAccessor() { - var model = new TestModel("foo"); + var model = new Issue11TestModel("foo"); Assert.Equal("foo", model.MyProperty); } - - public class TestModel : ReactiveObject - { - public TestModel(string myProperty) - { - Observable.Return(myProperty).ToPropertyEx(this, x => x.MyProperty); - } - - public extern string MyProperty - { - [ObservableAsProperty] - get; - } - } } } diff --git a/src/ReactiveUI.Fody.Tests/Issues/Mocks/Issue11TestModel.cs b/src/ReactiveUI.Fody.Tests/Issues/Mocks/Issue11TestModel.cs new file mode 100644 index 0000000000..6952b4cf98 --- /dev/null +++ b/src/ReactiveUI.Fody.Tests/Issues/Mocks/Issue11TestModel.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Text; +using System.Threading.Tasks; +using ReactiveUI.Fody.Helpers; + +namespace ReactiveUI.Fody.Tests.Issues +{ + public class Issue11TestModel : ReactiveObject + { + public Issue11TestModel(string myProperty) + { + Observable.Return(myProperty).ToPropertyEx(this, x => x.MyProperty); + } + + public extern string MyProperty + { + [ObservableAsProperty] + get; + } + } +} diff --git a/src/ReactiveUI.Fody.Tests/Mocks/BaseModel.cs b/src/ReactiveUI.Fody.Tests/Mocks/BaseModel.cs new file mode 100644 index 0000000000..c98fdf1e4a --- /dev/null +++ b/src/ReactiveUI.Fody.Tests/Mocks/BaseModel.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Fody.Tests +{ + public class BaseModel : ReactiveObject + { + public virtual int IntProperty { get; set; } = 5; + + public virtual string StringProperty { get; set; } = "Initial Value"; + } +} diff --git a/src/ReactiveUI.Fody.Tests/Mocks/DecoratorModel.cs b/src/ReactiveUI.Fody.Tests/Mocks/DecoratorModel.cs new file mode 100644 index 0000000000..97f6d4b426 --- /dev/null +++ b/src/ReactiveUI.Fody.Tests/Mocks/DecoratorModel.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ReactiveUI.Fody.Helpers; + +namespace ReactiveUI.Fody.Tests +{ + public class DecoratorModel : BaseModel + { + private readonly BaseModel _model; + + // Testing ctor + public DecoratorModel() + { + _model = new BaseModel(); + } + + public DecoratorModel(BaseModel baseModel) + { + _model = baseModel; + } + + [Reactive] + public string SomeCoolNewProperty { get; set; } + + // Works with private fields + [ReactiveDependency(nameof(_model))] + public override string StringProperty { get; set; } + + // Can't be attributed as has additional functionality in the decorated get + public override int IntProperty + { + get => _model.IntProperty * 2; + set + { + _model.IntProperty = value; + this.RaisePropertyChanged(); + } + } + + public void UpdateCoolProperty(string coolNewProperty) + { + SomeCoolNewProperty = coolNewProperty; + } + } +} diff --git a/src/ReactiveUI.Fody.Tests/Mocks/FacadeModel.cs b/src/ReactiveUI.Fody.Tests/Mocks/FacadeModel.cs new file mode 100644 index 0000000000..afb84771ec --- /dev/null +++ b/src/ReactiveUI.Fody.Tests/Mocks/FacadeModel.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ReactiveUI.Fody.Helpers; + +namespace ReactiveUI.Fody.Tests +{ + public class FacadeModel : ReactiveObject + { + private BaseModel _dependency; + + public FacadeModel() + { + _dependency = new BaseModel(); + } + + public FacadeModel(BaseModel dependency) + { + _dependency = dependency; + } + + public BaseModel Dependency + { + get => _dependency; + private set => _dependency = value; + } + + // Property with the same name, will look for a like for like name on the named dependency + [ReactiveDependency(nameof(Dependency))] + public int IntProperty { get; set; } + + // Property named differently to that on the dependency but still pass through value + [ReactiveDependency(nameof(Dependency), TargetProperty = "StringProperty")] + public string AnotherStringProperty { get; set; } + } +} diff --git a/src/ReactiveUI.Fody.Tests/Mocks/ObservableAsTestModel.cs b/src/ReactiveUI.Fody.Tests/Mocks/ObservableAsTestModel.cs new file mode 100644 index 0000000000..42a06877ef --- /dev/null +++ b/src/ReactiveUI.Fody.Tests/Mocks/ObservableAsTestModel.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Text; +using System.Threading.Tasks; +using ReactiveUI.Fody.Helpers; + +namespace ReactiveUI.Fody.Tests +{ + public class ObservableAsTestModel : ReactiveObject + { + public ObservableAsTestModel() + { + Observable.Return("foo").ToPropertyEx(this, x => x.TestProperty); + } + + [ObservableAsProperty] + public string TestProperty { get; private set; } + } +} diff --git a/src/ReactiveUI.Fody.Tests/ObservableAsPropertyTests.cs b/src/ReactiveUI.Fody.Tests/ObservableAsPropertyTests.cs index fa5b64fa41..2358fc370d 100644 --- a/src/ReactiveUI.Fody.Tests/ObservableAsPropertyTests.cs +++ b/src/ReactiveUI.Fody.Tests/ObservableAsPropertyTests.cs @@ -14,19 +14,8 @@ public class ObservableAsPropertyTests [Fact] public void TestPropertyReturnsFoo() { - var model = new TestModel(); + var model = new ObservableAsTestModel(); Assert.Equal("foo", model.TestProperty); } - - public class TestModel : ReactiveObject - { - public TestModel() - { - Observable.Return("foo").ToPropertyEx(this, x => x.TestProperty); - } - - [ObservableAsProperty] - public string TestProperty { get; private set; } - } } } diff --git a/src/ReactiveUI.Fody.Tests/ReactiveDependencyTests.cs b/src/ReactiveUI.Fody.Tests/ReactiveDependencyTests.cs index da76de5578..8f13ebd158 100644 --- a/src/ReactiveUI.Fody.Tests/ReactiveDependencyTests.cs +++ b/src/ReactiveUI.Fody.Tests/ReactiveDependencyTests.cs @@ -129,79 +129,4 @@ public void DecoratorReactiveStringPropertyRaisesPropertyChanged() Assert.Equal(expectedPropertyChanged, resultPropertyChanged); } } - - public class BaseModel : ReactiveObject - { - public virtual int IntProperty { get; set; } = 5; - - public virtual string StringProperty { get; set; } = "Initial Value"; - } - - public class FacadeModel : ReactiveObject - { - private BaseModel _dependency; - - public FacadeModel() - { - _dependency = new BaseModel(); - } - - public FacadeModel(BaseModel dependency) - { - _dependency = dependency; - } - - public BaseModel Dependency - { - get => _dependency; - private set => _dependency = value; - } - - // Property with the same name, will look for a like for like name on the named dependency - [ReactiveDependency(nameof(Dependency))] - public int IntProperty { get; set; } - - // Property named differently to that on the dependency but still pass through value - [ReactiveDependency(nameof(Dependency), TargetProperty = "StringProperty")] - public string AnotherStringProperty { get; set; } - } - - public class DecoratorModel : BaseModel - { - private readonly BaseModel _model; - - // Testing ctor - public DecoratorModel() - { - _model = new BaseModel(); - } - - public DecoratorModel(BaseModel baseModel) - { - _model = baseModel; - } - - [Reactive] - public string SomeCoolNewProperty { get; set; } - - // Works with private fields - [ReactiveDependency(nameof(_model))] - public override string StringProperty { get; set; } - - // Can't be attributed as has additional functionality in the decorated get - public override int IntProperty - { - get => _model.IntProperty * 2; - set - { - _model.IntProperty = value; - this.RaisePropertyChanged(); - } - } - - public void UpdateCoolProperty(string coolNewProperty) - { - SomeCoolNewProperty = coolNewProperty; - } - } } diff --git a/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj b/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj index 29b0812efa..4be1806f2d 100644 --- a/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj +++ b/src/ReactiveUI.Fody.Tests/ReactiveUI.Fody.Tests.csproj @@ -1,6 +1,6 @@  - net461 + net461;netcoreapp2.0 @@ -8,15 +8,6 @@ - - - - - - - - - diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt new file mode 100644 index 0000000000..d9cd2fad94 --- /dev/null +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.net461.approved.txt @@ -0,0 +1,1005 @@ +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.AndroidSupport")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Tests")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Winforms")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Wpf")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.XamForms")] +[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName=".NET Framework 4.6.1")] +namespace ReactiveUI +{ + public class static AutoPersistHelper + { + public static System.IDisposable ActOnEveryObject(this System.Collections.ObjectModel.ObservableCollection @this, System.Action onAdd, System.Action onRemove) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable ActOnEveryObject(this System.Collections.ObjectModel.ReadOnlyObservableCollection @this, System.Action onAdd, System.Action onRemove) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable ActOnEveryObject(this TCollection @this, System.Action onAdd, System.Action onRemove) + where TItem : ReactiveUI.IReactiveObject + where TCollection : System.Collections.Specialized.INotifyCollectionChanged, System.Collections.Generic.IEnumerable<> { } + public static System.IDisposable ActOnEveryObject(this System.IObservable> @this, System.Action onAdd, System.Action onRemove) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersist(this T @this, System.Func> doPersist, System.Nullable interval = null) + where T : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersist(this T @this, System.Func> doPersist, System.IObservable manualSaveSignal, System.Nullable interval = null) + where T : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersistCollection(this System.Collections.ObjectModel.ObservableCollection @this, System.Func> doPersist, System.Nullable interval = null) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersistCollection(this System.Collections.ObjectModel.ObservableCollection @this, System.Func> doPersist, System.IObservable manualSaveSignal, System.Nullable interval = null) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersistCollection(this System.Collections.ObjectModel.ReadOnlyObservableCollection @this, System.Func> doPersist, System.IObservable manualSaveSignal, System.Nullable interval = null) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersistCollection(this TCollection @this, System.Func> doPersist, System.IObservable manualSaveSignal, System.Nullable interval = null) + where TItem : ReactiveUI.IReactiveObject + where TCollection : System.Collections.Specialized.INotifyCollectionChanged, System.Collections.Generic.IEnumerable<> { } + } + public enum BindingDirection + { + OneWay = 0, + TwoWay = 1, + AsyncOneWay = 2, + } + public class CanActivateViewFetcher : ReactiveUI.IActivationForViewFetcher + { + public CanActivateViewFetcher() { } + public System.IObservable GetActivationForView(ReactiveUI.IActivatable view) { } + public int GetAffinityForView(System.Type view) { } + } + public class static ChangeSetMixin + { + public static System.IObservable CountChanged(this System.IObservable changeSet) { } + public static System.IObservable> CountChanged(this System.IObservable> changeSet) { } + public static bool HasCountChanged(this DynamicData.IChangeSet changeSet) { } + } + public class CombinedReactiveCommand : ReactiveUI.ReactiveCommandBase> + { + protected internal CombinedReactiveCommand(System.Collections.Generic.IEnumerable> childCommands, System.IObservable canExecute, System.Reactive.Concurrency.IScheduler outputScheduler) { } + public override System.IObservable CanExecute { get; } + public override System.IObservable IsExecuting { get; } + public override System.IObservable ThrownExceptions { get; } + protected override void Dispose(bool disposing) { } + public override System.IObservable> Execute(TParam parameter = null) { } + public override System.IDisposable Subscribe(System.IObserver> observer) { } + } + public class static CommandBinder + { + public static ReactiveUI.IReactiveBinding BindCommand(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> propertyName, System.Linq.Expressions.Expression> controlName, System.IObservable withParameter, string toEvent = null) + where TView : class, ReactiveUI.IViewFor<> + where TViewModel : class + where TProp : System.Windows.Input.ICommand { } + public static ReactiveUI.IReactiveBinding BindCommand(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> propertyName, System.Linq.Expressions.Expression> controlName, string toEvent = null) + where TView : class, ReactiveUI.IViewFor<> + where TViewModel : class + where TProp : System.Windows.Input.ICommand { } + public static ReactiveUI.IReactiveBinding BindCommand(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> propertyName, System.Linq.Expressions.Expression> controlName, System.Linq.Expressions.Expression> withParameter, string toEvent = null) + where TView : class, ReactiveUI.IViewFor<> + where TViewModel : class + where TProp : System.Windows.Input.ICommand { } + } + public class CommandBinderImplementation : Splat.IEnableLogger + { + public CommandBinderImplementation() { } + public ReactiveUI.IReactiveBinding BindCommand(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> controlProperty, System.Func withParameter, string toEvent = null) + where TView : class, ReactiveUI.IViewFor<> + where TViewModel : class + where TProp : System.Windows.Input.ICommand { } + public ReactiveUI.IReactiveBinding BindCommand(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> controlProperty, System.IObservable withParameter, string toEvent = null) + where TView : class, ReactiveUI.IViewFor<> + where TViewModel : class + where TProp : System.Windows.Input.ICommand { } + } + public class static ComparerChainingExtensions + { + public static System.Collections.Generic.IComparer ThenBy(this System.Collections.Generic.IComparer parent, System.Func selector) { } + public static System.Collections.Generic.IComparer ThenBy(this System.Collections.Generic.IComparer parent, System.Func selector, System.Collections.Generic.IComparer comparer) { } + public static System.Collections.Generic.IComparer ThenByDescending(this System.Collections.Generic.IComparer parent, System.Func selector) { } + public static System.Collections.Generic.IComparer ThenByDescending(this System.Collections.Generic.IComparer parent, System.Func selector, System.Collections.Generic.IComparer comparer) { } + } + public class ComponentModelTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger + { + public ComponentModelTypeConverter() { } + public int GetAffinityForObjects(System.Type fromType, System.Type toType) { } + public bool TryConvert(object from, System.Type toType, object conversionHint, out object result) { } + } + public class CreatesCommandBindingViaCommandParameter : ReactiveUI.ICreatesCommandBinding + { + public CreatesCommandBindingViaCommandParameter() { } + public System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter) { } + public System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter, string eventName) { } + public int GetAffinityForObject(System.Type type, bool hasEventTarget) { } + } + public class CreatesCommandBindingViaEvent : ReactiveUI.ICreatesCommandBinding + { + public CreatesCommandBindingViaEvent() { } + public System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter) { } + public System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter, string eventName) { } + public int GetAffinityForObject(System.Type type, bool hasEventTarget) { } + } + public class static DependencyResolverMixins + { + public static void InitializeReactiveUI(this Splat.IMutableDependencyResolver resolver) { } + public static void RegisterViewsForViewModels(this Splat.IMutableDependencyResolver resolver, System.Reflection.Assembly assembly) { } + } + public class DummySuspensionDriver : ReactiveUI.ISuspensionDriver + { + public DummySuspensionDriver() { } + public System.IObservable InvalidateState() { } + public System.IObservable LoadState() { } + public System.IObservable SaveState(object state) { } + } + public class EqualityTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger + { + public EqualityTypeConverter() { } + public static object DoReferenceCast(object from, System.Type targetType) { } + public int GetAffinityForObjects(System.Type fromType, System.Type toType) { } + public bool TryConvert(object from, System.Type toType, object conversionHint, out object result) { } + } + public class static ExpressionMixins + { + public static object[] GetArgumentsArray(this System.Linq.Expressions.Expression expression) { } + public static System.Collections.Generic.IEnumerable GetExpressionChain(this System.Linq.Expressions.Expression @this) { } + public static System.Reflection.MemberInfo GetMemberInfo(this System.Linq.Expressions.Expression expression) { } + public static System.Linq.Expressions.Expression GetParent(this System.Linq.Expressions.Expression expression) { } + } + public interface IActivatable { } + public interface IActivationForViewFetcher + { + System.IObservable GetActivationForView(ReactiveUI.IActivatable view); + int GetAffinityForView(System.Type view); + } + public interface IBindingTypeConverter : Splat.IEnableLogger + { + int GetAffinityForObjects(System.Type fromType, System.Type toType); + bool TryConvert(object from, System.Type toType, object conversionHint, out object result); + } + public interface ICanActivate + { + System.IObservable Activated { get; } + System.IObservable Deactivated { get; } + } + public interface IComparerBuilder + { + System.Collections.Generic.IComparer OrderBy(System.Func selector); + System.Collections.Generic.IComparer OrderBy(System.Func selector, System.Collections.Generic.IComparer comparer); + System.Collections.Generic.IComparer OrderByDescending(System.Func selector); + System.Collections.Generic.IComparer OrderByDescending(System.Func selector, System.Collections.Generic.IComparer comparer); + } + public interface ICreatesCommandBinding + { + System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter); + System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter, string eventName); + int GetAffinityForObject(System.Type type, bool hasEventTarget); + } + public interface ICreatesObservableForProperty : Splat.IEnableLogger + { + int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False); + System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False); + } + public interface IHandleObservableErrors + { + System.IObservable ThrownExceptions { get; } + } + public interface IMessageBus : Splat.IEnableLogger + { + bool IsRegistered(System.Type type, string contract = null); + System.IObservable Listen(string contract = null); + System.IObservable ListenIncludeLatest(string contract = null); + System.IDisposable RegisterMessageSource(System.IObservable source, string contract = null); + void RegisterScheduler(System.Reactive.Concurrency.IScheduler scheduler, string contract = null); + void SendMessage(T message, string contract = null); + } + public interface INotifyCollectionChanging + { + public event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanging; + } + public interface INotifyPropertyChanging + { + public event ReactiveUI.PropertyChangingEventHandler PropertyChanging; + } + public class INPCObservableForProperty : ReactiveUI.ICreatesObservableForProperty, Splat.IEnableLogger + { + public INPCObservableForProperty() { } + public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged) { } + } + public class Interaction + { + public Interaction(System.Reactive.Concurrency.IScheduler handlerScheduler = null) { } + protected System.Func<, >[] GetHandlers() { } + public virtual System.IObservable Handle(TInput input) { } + public System.IDisposable RegisterHandler(System.Action> handler) { } + public System.IDisposable RegisterHandler(System.Func, System.Threading.Tasks.Task> handler) { } + public System.IDisposable RegisterHandler(System.Func, System.IObservable> handler) { } + } + public sealed class InteractionContext + { + public TInput Input { get; } + public bool IsHandled { get; } + public TOutput GetOutput() { } + public void SetOutput(TOutput output) { } + } + public interface IObservedChange + { + System.Linq.Expressions.Expression Expression { get; } + TSender Sender { get; } + TValue Value { get; } + } + public interface IPropertyBinderImplementation : Splat.IEnableLogger + { + ReactiveUI.IReactiveBinding> Bind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, object conversionHint, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter viewToVMConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor + ; + ReactiveUI.IReactiveBinding> Bind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, System.Func vmToViewConverter, System.Func viewToVmConverter) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor + ; + System.IDisposable BindTo(System.IObservable observedChange, TTarget target, System.Linq.Expressions.Expression> propertyExpression, object conversionHint, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TTarget : class + ; + ReactiveUI.IReactiveBinding OneWayBind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, object conversionHint, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor + ; + ReactiveUI.IReactiveBinding OneWayBind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor + ; + } + public interface IPropertyBindingHook + { + bool ExecuteHook(object source, object target, System.Func[]> getCurrentViewModelProperties, System.Func[]> getCurrentViewProperties, ReactiveUI.BindingDirection direction); + } + public interface IReactiveBinding : System.IDisposable + where out TView : ReactiveUI.IViewFor + where out TViewModel : class + { + System.IObservable Changed { get; } + ReactiveUI.BindingDirection Direction { get; } + TView View { get; } + System.Linq.Expressions.Expression ViewExpression { get; } + TViewModel ViewModel { get; } + System.Linq.Expressions.Expression ViewModelExpression { get; } + } + public interface IReactiveCommand : ReactiveUI.IHandleObservableErrors, System.IDisposable + { + System.IObservable CanExecute { get; } + System.IObservable IsExecuting { get; } + } + public interface IReactiveNotifyPropertyChanged + { + System.IObservable> Changed { get; } + System.IObservable> Changing { get; } + System.IDisposable SuppressChangeNotifications(); + } + public interface IReactiveObject : ReactiveUI.INotifyPropertyChanging, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged + { + void RaisePropertyChanged(System.ComponentModel.PropertyChangedEventArgs args); + void RaisePropertyChanging(ReactiveUI.PropertyChangingEventArgs args); + } + public class static IReactiveObjectExtensions + { + public static TRet RaiseAndSetIfChanged(this TObj @this, ref TRet backingField, TRet newValue, [System.Runtime.CompilerServices.CallerMemberNameAttribute()] string propertyName = null) + where TObj : ReactiveUI.IReactiveObject { } + public static void RaisePropertyChanged(this TSender @this, [System.Runtime.CompilerServices.CallerMemberNameAttribute()] string propertyName = null) + where TSender : ReactiveUI.IReactiveObject { } + public static void RaisePropertyChanging(this TSender @this, [System.Runtime.CompilerServices.CallerMemberNameAttribute()] string propertyName = null) + where TSender : ReactiveUI.IReactiveObject { } + } + public interface IReactivePropertyChangedEventArgs + { + string PropertyName { get; } + TSender Sender { get; } + } + public class IROObservableForProperty : ReactiveUI.ICreatesObservableForProperty, Splat.IEnableLogger + { + public IROObservableForProperty() { } + public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + } + public interface IRoutableViewModel : ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged + { + ReactiveUI.IScreen HostScreen { get; } + string UrlPathSegment { get; } + } + public interface IScreen + { + ReactiveUI.RoutingState Router { get; } + } + public interface ISupportsActivation + { + ReactiveUI.ViewModelActivator Activator { get; } + } + public interface ISuspensionDriver + { + System.IObservable InvalidateState(); + System.IObservable LoadState(); + System.IObservable SaveState(object state); + } + public interface ISuspensionHost : ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged + { + object AppState { get; set; } + System.Func CreateNewAppState { get; set; } + System.IObservable IsLaunchingNew { get; set; } + System.IObservable IsResuming { get; set; } + System.IObservable IsUnpausing { get; set; } + System.IObservable ShouldInvalidateState { get; set; } + System.IObservable ShouldPersistState { get; set; } + } + public interface IViewFor : ReactiveUI.IActivatable + { + object ViewModel { get; set; } + } + public interface IViewFor : ReactiveUI.IActivatable, ReactiveUI.IViewFor + where T : class + { + T ViewModel { get; set; } + } + public interface IViewLocator : Splat.IEnableLogger + { + ReactiveUI.IViewFor ResolveView(T viewModel, string contract = null) + where T : class; + } + public class MessageBus : ReactiveUI.IMessageBus, Splat.IEnableLogger + { + public MessageBus() { } + public static ReactiveUI.IMessageBus Current { get; set; } + public bool IsRegistered(System.Type type, string contract = null) { } + public System.IObservable Listen(string contract = null) { } + public System.IObservable ListenIncludeLatest(string contract = null) { } + public System.IDisposable RegisterMessageSource(System.IObservable source, string contract = null) { } + public void RegisterScheduler(System.Reactive.Concurrency.IScheduler scheduler, string contract = null) { } + public void SendMessage(T message, string contract = null) { } + } + public class static OAPHCreationHelperMixin + { + public static ReactiveUI.ObservableAsPropertyHelper ToProperty(this System.IObservable target, TObj source, System.Linq.Expressions.Expression> property, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + where TObj : class, ReactiveUI.IReactiveObject { } + public static ReactiveUI.ObservableAsPropertyHelper ToProperty(this System.IObservable target, TObj source, System.Linq.Expressions.Expression> property, out ReactiveUI.ObservableAsPropertyHelper<> result, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + where TObj : class, ReactiveUI.IReactiveObject { } + public static ReactiveUI.ObservableAsPropertyHelper ToProperty(this System.IObservable target, TObj source, string property, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + where TObj : class, ReactiveUI.IReactiveObject { } + public static ReactiveUI.ObservableAsPropertyHelper ToProperty(this System.IObservable target, TObj source, string property, out ReactiveUI.ObservableAsPropertyHelper<> result, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + where TObj : class, ReactiveUI.IReactiveObject { } + } + public sealed class ObservableAsPropertyHelper : ReactiveUI.IHandleObservableErrors, Splat.IEnableLogger, System.IDisposable + { + public ObservableAsPropertyHelper(System.IObservable observable, System.Action onChanged, T initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public ObservableAsPropertyHelper(System.IObservable observable, System.Action onChanged, System.Action onChanging = null, T initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public bool IsSubscribed { get; } + public System.IObservable ThrownExceptions { get; } + public T Value { get; } + public static ReactiveUI.ObservableAsPropertyHelper Default(T initialValue = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public void Dispose() { } + } + public class static ObservableLoggingMixin + { + public static System.IObservable Log(this System.IObservable @this, TObj klass, string message = null, System.Func stringifier = null) + where TObj : Splat.IEnableLogger { } + public static System.IObservable LoggedCatch(this System.IObservable @this, TObj klass, System.IObservable next = null, string message = null) + where TObj : Splat.IEnableLogger { } + public static System.IObservable LoggedCatch(this System.IObservable @this, TObj klass, System.Func> next, string message = null) + where TObj : Splat.IEnableLogger + where TException : System.Exception { } + } + public class ObservedChange : ReactiveUI.IObservedChange + { + public ObservedChange(TSender sender, System.Linq.Expressions.Expression expression, TValue value = null) { } + public System.Linq.Expressions.Expression Expression { get; } + public TSender Sender { get; } + public TValue Value { get; } + } + public class static ObservedChangedMixin + { + public static string GetPropertyName(this ReactiveUI.IObservedChange @this) { } + public static TValue GetValue(this ReactiveUI.IObservedChange @this) { } + public static System.IObservable Value(this System.IObservable> @this) { } + } + public class static OrderedComparer + { + public static ReactiveUI.IComparerBuilder For(System.Collections.Generic.IEnumerable enumerable) { } + public static ReactiveUI.IComparerBuilder For() { } + } + public class static OrderedComparer + { + public static System.Collections.Generic.IComparer OrderBy(System.Func selector) { } + public static System.Collections.Generic.IComparer OrderBy(System.Func selector, System.Collections.Generic.IComparer comparer) { } + public static System.Collections.Generic.IComparer OrderByDescending(System.Func selector) { } + public static System.Collections.Generic.IComparer OrderByDescending(System.Func selector, System.Collections.Generic.IComparer comparer) { } + } + public class PlatformRegistrations + { + public PlatformRegistrations() { } + public void Register(System.Action, System.Type> registerFunction) { } + } + public class POCOObservableForProperty : ReactiveUI.ICreatesObservableForProperty, Splat.IEnableLogger + { + public POCOObservableForProperty() { } + public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + } + public class PropertyBinderImplementation : ReactiveUI.IPropertyBinderImplementation, Splat.IEnableLogger + { + public PropertyBinderImplementation() { } + public ReactiveUI.IReactiveBinding> Bind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, object conversionHint, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter viewToVMConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public ReactiveUI.IReactiveBinding> Bind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, System.Func vmToViewConverter, System.Func viewToVmConverter) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public System.IDisposable BindTo(System.IObservable observedChange, TTarget target, System.Linq.Expressions.Expression> propertyExpression, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TTarget : class { } + public ReactiveUI.IReactiveBinding OneWayBind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public ReactiveUI.IReactiveBinding OneWayBind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + } + public class static PropertyBindingMixins + { + public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter viewToVMConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter viewToVMConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func vmToViewConverter, System.Func viewToVmConverter) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, System.Func vmToViewConverter, System.Func viewToVmConverter) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public static System.IDisposable BindTo(this System.IObservable @this, TTarget target, System.Linq.Expressions.Expression> property, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TTarget : class { } + public static ReactiveUI.IReactiveBinding OneWayBind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public static ReactiveUI.IReactiveBinding OneWayBind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + } + public class PropertyChangingEventArgs : System.EventArgs + { + public PropertyChangingEventArgs(string propertyName) { } + public string PropertyName { get; set; } + } + public delegate void PropertyChangingEventHandler(object sender, ReactiveUI.PropertyChangingEventArgs e); + public class static ReactiveCommand + { + public static ReactiveUI.ReactiveCommand Create(System.Action execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand Create(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand Create(System.Action execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand Create(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.CombinedReactiveCommand CreateCombined(System.Collections.Generic.IEnumerable> childCommands, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromObservable(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromObservable(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + } + public class ReactiveCommand : ReactiveUI.ReactiveCommandBase + { + protected internal ReactiveCommand(System.Func> execute, System.IObservable canExecute, System.Reactive.Concurrency.IScheduler outputScheduler) { } + public override System.IObservable CanExecute { get; } + public override System.IObservable IsExecuting { get; } + public override System.IObservable ThrownExceptions { get; } + protected override void Dispose(bool disposing) { } + public override System.IObservable Execute(TParam parameter = null) { } + public override System.IDisposable Subscribe(System.IObserver observer) { } + } + public abstract class ReactiveCommandBase : ReactiveUI.IHandleObservableErrors, ReactiveUI.IReactiveCommand, System.IDisposable, System.IObservable, System.Windows.Input.ICommand + { + protected ReactiveCommandBase() { } + public abstract System.IObservable CanExecute { get; } + public abstract System.IObservable IsExecuting { get; } + public abstract System.IObservable ThrownExceptions { get; } + public event System.EventHandler System.Windows.Input.ICommand.CanExecuteChanged; + public void Dispose() { } + protected abstract void Dispose(bool disposing); + public abstract System.IObservable Execute(TParam parameter = null); + protected virtual bool ICommandCanExecute(object parameter) { } + protected virtual void ICommandExecute(object parameter) { } + protected void OnCanExecuteChanged() { } + public abstract System.IDisposable Subscribe(System.IObserver observer); + } + public class static ReactiveCommandMixins + { + public static System.IDisposable InvokeCommand(this System.IObservable @this, System.Windows.Input.ICommand command) { } + public static System.IDisposable InvokeCommand(this System.IObservable @this, ReactiveUI.ReactiveCommandBase command) { } + public static System.IDisposable InvokeCommand(this System.IObservable @this, TTarget target, System.Linq.Expressions.Expression> commandProperty) + where TTarget : class { } + public static System.IDisposable InvokeCommand(this System.IObservable @this, TTarget target, System.Linq.Expressions.Expression>> commandProperty) + where TTarget : class { } + } + public class static ReactiveNotifyPropertyChangedMixin + { + public static System.IObservable> ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, bool beforeChange = False, bool skipInitial = True) + where TSender : class { } + public static System.IObservable ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, System.Func selector, bool beforeChange = False) + where TSender : class { } + public static System.IObservable> SubscribeToExpressionChain(this TSender source, System.Linq.Expressions.Expression expression, bool beforeChange = False, bool skipInitial = True) { } + } + [System.Runtime.Serialization.DataContractAttribute()] + public class ReactiveObject : ReactiveUI.IHandleObservableErrors, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveNotifyPropertyChanged, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged + { + public ReactiveObject() { } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.IObservable> Changed { get; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.IObservable> Changing { get; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.IObservable ThrownExceptions { get; } + public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; + public event ReactiveUI.PropertyChangingEventHandler PropertyChanging; + public bool AreChangeNotificationsEnabled() { } + public System.IDisposable DelayChangeNotifications() { } + public System.IDisposable SuppressChangeNotifications() { } + } + public class ReactivePropertyChangedEventArgs : System.ComponentModel.PropertyChangedEventArgs, ReactiveUI.IReactivePropertyChangedEventArgs + { + public ReactivePropertyChangedEventArgs(TSender sender, string propertyName) { } + public TSender Sender { get; } + } + public class ReactivePropertyChangingEventArgs : ReactiveUI.PropertyChangingEventArgs, ReactiveUI.IReactivePropertyChangedEventArgs + { + public ReactivePropertyChangingEventArgs(TSender sender, string propertyName) { } + public TSender Sender { get; } + } + public class static Reflection + { + public static string ExpressionToPropertyNames(System.Linq.Expressions.Expression expression) { } + public static System.Type GetEventArgsTypeForEvent(System.Type type, string eventName) { } + public static System.Func GetValueFetcherForProperty(System.Reflection.MemberInfo member) { } + public static System.Func GetValueFetcherOrThrow(System.Reflection.MemberInfo member) { } + public static System.Action GetValueSetterForProperty(System.Reflection.MemberInfo member) { } + public static System.Action GetValueSetterOrThrow(System.Reflection.MemberInfo member) { } + public static bool IsStatic(this System.Reflection.PropertyInfo @this) { } + public static System.Type ReallyFindType(string type, bool throwOnFailure) { } + public static System.Linq.Expressions.Expression Rewrite(System.Linq.Expressions.Expression expression) { } + public static void ThrowIfMethodsNotOverloaded(string callingTypeName, object targetObject, params string[] methodsToCheck) { } + public static bool TryGetAllValuesForPropertyChain(out ReactiveUI.IObservedChange<, > changeValues, object current, System.Collections.Generic.IEnumerable expressionChain) { } + public static bool TryGetValueForPropertyChain(out TValue changeValue, object current, System.Collections.Generic.IEnumerable expressionChain) { } + public static bool TrySetValueToPropertyChain(object target, System.Collections.Generic.IEnumerable expressionChain, TValue value, bool shouldThrow = True) { } + } + public class Registrations + { + public Registrations() { } + public void Register(System.Action, System.Type> registerFunction) { } + } + public class static RoutableViewModelMixin + { + public static System.IDisposable WhenNavigatedTo(this ReactiveUI.IRoutableViewModel @this, System.Func onNavigatedTo) { } + public static System.IObservable WhenNavigatedToObservable(this ReactiveUI.IRoutableViewModel @this) { } + public static System.IObservable WhenNavigatingFromObservable(this ReactiveUI.IRoutableViewModel @this) { } + } + [System.Runtime.Serialization.DataContractAttribute()] + public class RoutingState : ReactiveUI.ReactiveObject + { + public RoutingState() { } + public RoutingState(System.Reactive.Concurrency.IScheduler scheduler) { } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.IObservable CurrentViewModel { get; set; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public ReactiveUI.ReactiveCommand Navigate { get; set; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public ReactiveUI.ReactiveCommand NavigateAndReset { get; set; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public ReactiveUI.ReactiveCommand NavigateBack { get; set; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.IObservable> NavigationChanged { get; set; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.Collections.ObjectModel.ObservableCollection NavigationStack { get; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.Reactive.Concurrency.IScheduler Scheduler { get; set; } + } + public class static RoutingStateMixins + { + public static T FindViewModelInStack(this ReactiveUI.RoutingState @this) + where T : ReactiveUI.IRoutableViewModel { } + public static ReactiveUI.IRoutableViewModel GetCurrentViewModel(this ReactiveUI.RoutingState @this) { } + } + public class static RxApp + { + public const int BigCacheLimit = 256; + public const int SmallCacheLimit = 64; + public static System.IObserver DefaultExceptionHandler { get; set; } + public static System.Reactive.Concurrency.IScheduler MainThreadScheduler { get; set; } + public static bool SupportsRangeNotifications { get; set; } + public static ReactiveUI.ISuspensionHost SuspensionHost { get; set; } + public static System.Reactive.Concurrency.IScheduler TaskpoolScheduler { get; set; } + } + public class ScheduledSubject : System.IDisposable, System.IObservable, System.IObserver, System.Reactive.Subjects.ISubject, System.Reactive.Subjects.ISubject + { + public ScheduledSubject(System.Reactive.Concurrency.IScheduler scheduler, System.IObserver defaultObserver = null, System.Reactive.Subjects.ISubject defaultSubject = null) { } + public void Dispose() { } + protected virtual void Dispose(bool isDisposing) { } + public void OnCompleted() { } + public void OnError(System.Exception error) { } + public void OnNext(T value) { } + public System.IDisposable Subscribe(System.IObserver observer) { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.All)] + public class SingleInstanceViewAttribute : System.Attribute + { + public SingleInstanceViewAttribute() { } + } + public class StringConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger + { + public StringConverter() { } + public int GetAffinityForObjects(System.Type fromType, System.Type toType) { } + public bool TryConvert(object from, System.Type toType, object conversionHint, out object result) { } + } + public class static SuspensionHostExtensions + { + public static T GetAppState(this ReactiveUI.ISuspensionHost @this) { } + public static System.IObservable ObserveAppState(this ReactiveUI.ISuspensionHost @this) + where T : class { } + public static System.IDisposable SetupDefaultSuspendResume(this ReactiveUI.ISuspensionHost @this, ReactiveUI.ISuspensionDriver driver = null) { } + } + public class UnhandledErrorException : System.Exception + { + public UnhandledErrorException() { } + public UnhandledErrorException(string message) { } + public UnhandledErrorException(string message, System.Exception innerException) { } + protected UnhandledErrorException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } + public class UnhandledInteractionException : System.Exception + { + public UnhandledInteractionException(ReactiveUI.Interaction interaction, TInput input) { } + public UnhandledInteractionException() { } + public UnhandledInteractionException(string message) { } + public UnhandledInteractionException(string message, System.Exception innerException) { } + protected UnhandledInteractionException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public TInput Input { get; } + public ReactiveUI.Interaction Interaction { get; } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.All)] + public class ViewContractAttribute : System.Attribute + { + public ViewContractAttribute(string contract) { } + public string Contract { get; } + } + public class static ViewForMixins + { + public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Func> block) { } + public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Action> block) { } + public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Action block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Func> block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Func> block, ReactiveUI.IViewFor view) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action> block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action> block, ReactiveUI.IViewFor view) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action block, ReactiveUI.IViewFor view = null) { } + } + public class static ViewLocator + { + public static ReactiveUI.IViewLocator Current { get; } + } + public class ViewLocatorNotFoundException : System.Exception + { + public ViewLocatorNotFoundException() { } + public ViewLocatorNotFoundException(string message) { } + public ViewLocatorNotFoundException(string message, System.Exception innerException) { } + protected ViewLocatorNotFoundException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } + public sealed class ViewModelActivator : System.IDisposable + { + public ViewModelActivator() { } + public System.IObservable Activated { get; } + public System.IObservable Deactivated { get; } + public System.IDisposable Activate() { } + public void Deactivate(bool ignoreRefCount = False) { } + public void Dispose() { } + } + public class WaitForDispatcherScheduler : System.Reactive.Concurrency.IScheduler + { + public WaitForDispatcherScheduler(System.Func schedulerFactory) { } + public System.DateTimeOffset Now { get; } + public System.IDisposable Schedule(TState state, System.Func action) { } + public System.IDisposable Schedule(TState state, System.TimeSpan dueTime, System.Func action) { } + public System.IDisposable Schedule(TState state, System.DateTimeOffset dueTime, System.Func action) { } + } + public class WeakEventManager + where TEventSource : class + where TEventHandler : class + { + protected WeakEventManager() { } + public static void AddHandler(TEventSource source, TEventHandler handler) { } + public static void DeliverEvent(TEventSource sender, TEventArgs args) { } + public static void RemoveHandler(TEventSource source, TEventHandler handler) { } + protected virtual void StartListening(object source) { } + protected virtual void StopListening(object source) { } + } + public class static WhenAnyMixin + { + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Func, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Func, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Linq.Expressions.Expression> property11, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Linq.Expressions.Expression> property11, System.Linq.Expressions.Expression> property12, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Func, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Func, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Linq.Expressions.Expression property8, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Linq.Expressions.Expression property8, System.Linq.Expressions.Expression property9, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Linq.Expressions.Expression property8, System.Linq.Expressions.Expression property9, System.Linq.Expressions.Expression property10, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Linq.Expressions.Expression property8, System.Linq.Expressions.Expression property9, System.Linq.Expressions.Expression property10, System.Linq.Expressions.Expression property11, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Linq.Expressions.Expression property8, System.Linq.Expressions.Expression property9, System.Linq.Expressions.Expression property10, System.Linq.Expressions.Expression property11, System.Linq.Expressions.Expression property12, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Linq.Expressions.Expression> property11, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Linq.Expressions.Expression> property11, System.Linq.Expressions.Expression> property12, System.Func selector) + where TSender : class { } + } + public class static WhenAnyObservableMixin + { + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10, System.Linq.Expressions.Expression>> obs11) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10, System.Linq.Expressions.Expression>> obs11, System.Linq.Expressions.Expression>> obs12) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10, System.Linq.Expressions.Expression>> obs11, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10, System.Linq.Expressions.Expression>> obs11, System.Linq.Expressions.Expression>> obs12, System.Func selector) + where TSender : class { } + } +} +namespace ReactiveUI.Legacy +{ + public interface IMoveInfo + { + int From { get; } + System.Collections.Generic.IEnumerable MovedItems { get; } + int To { get; } + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveCollection : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, Splat.IEnableLogger, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged + { + void Reset(); + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveDerivedList : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, ReactiveUI.Legacy.IReadOnlyReactiveCollection, ReactiveUI.Legacy.IReadOnlyReactiveList, Splat.IEnableLogger, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.IEnumerable, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged, System.IDisposable { } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveList : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, Splat.IEnableLogger, System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.IEnumerable, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged + { + bool IsEmpty { get; } + void AddRange(System.Collections.Generic.IEnumerable collection); + void InsertRange(int index, System.Collections.Generic.IEnumerable collection); + void RemoveAll(System.Collections.Generic.IEnumerable items); + void RemoveRange(int index, int count); + void Sort(System.Collections.Generic.IComparer comparer = null); + void Sort(System.Comparison comparison); + void Sort(int index, int count, System.Collections.Generic.IComparer comparer); + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveNotifyCollectionChanged + { + System.IObservable BeforeItemsAdded { get; } + System.IObservable> BeforeItemsMoved { get; } + System.IObservable BeforeItemsRemoved { get; } + System.IObservable Changed { get; } + System.IObservable Changing { get; } + System.IObservable CountChanged { get; } + System.IObservable CountChanging { get; } + System.IObservable IsEmptyChanged { get; } + System.IObservable ItemsAdded { get; } + System.IObservable> ItemsMoved { get; } + System.IObservable ItemsRemoved { get; } + System.IObservable ShouldReset { get; } + System.IDisposable SuppressChangeNotifications(); + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveNotifyCollectionItemChanged + { + bool ChangeTrackingEnabled { get; set; } + System.IObservable> ItemChanged { get; } + System.IObservable> ItemChanging { get; } + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReadOnlyReactiveCollection : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, Splat.IEnableLogger, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.IEnumerable, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged { } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReadOnlyReactiveList : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, ReactiveUI.Legacy.IReadOnlyReactiveCollection, Splat.IEnableLogger, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.IEnumerable, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged + { + bool IsEmpty { get; } + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public class static ObservableCollectionMixin + { + public static ReactiveUI.Legacy.IReactiveDerivedList CreateDerivedCollection(this System.Collections.Generic.IEnumerable @this, System.Func selector, System.Action onRemoved, System.Func filter = null, System.Func orderer = null, System.IObservable signalReset = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public static ReactiveUI.Legacy.IReactiveDerivedList CreateDerivedCollection(this System.Collections.Generic.IEnumerable @this, System.Func selector, System.Func filter = null, System.Func orderer = null, System.IObservable signalReset = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public static ReactiveUI.Legacy.IReactiveDerivedList CreateDerivedCollection(this System.Collections.Generic.IEnumerable @this, System.Func selector, System.Action onRemoved, System.Func filter = null, System.Func orderer = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public static ReactiveUI.Legacy.IReactiveDerivedList CreateDerivedCollection(this System.Collections.Generic.IEnumerable @this, System.Func selector, System.Func filter = null, System.Func orderer = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public class static ReactiveCollectionMixins + { + public static ReactiveUI.Legacy.IReactiveDerivedList CreateCollection(this System.IObservable fromObservable, System.Reactive.Concurrency.IScheduler scheduler) { } + public static ReactiveUI.Legacy.IReactiveDerivedList CreateCollection(this System.IObservable fromObservable, System.Nullable withDelay = null, System.Action onError = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + } + [System.Diagnostics.DebuggerDisplayAttribute("Count = {Count}")] + [System.Diagnostics.DebuggerTypeProxyAttribute(typeof(ReactiveUI.Legacy.CollectionDebugView<>))] + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public class ReactiveList : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveList, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, ReactiveUI.Legacy.IReadOnlyReactiveCollection, ReactiveUI.Legacy.IReadOnlyReactiveList, Splat.IEnableLogger, System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged + { + public ReactiveList() { } + public ReactiveList(System.Collections.Generic.IEnumerable initialContents) { } + public ReactiveList(System.Collections.Generic.IEnumerable initialContents = null, double resetChangeThreshold = 0.3, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public System.IObservable BeforeItemsAdded { get; } + public System.IObservable> BeforeItemsMoved { get; } + public System.IObservable BeforeItemsRemoved { get; } + public System.IObservable Changed { get; } + public bool ChangeTrackingEnabled { get; set; } + public System.IObservable Changing { get; } + public int Count { get; } + public System.IObservable CountChanged { get; } + public System.IObservable CountChanging { get; } + public bool IsEmpty { get; } + public System.IObservable IsEmptyChanged { get; } + public virtual bool IsReadOnly { get; } + public virtual T this[int index] { get; set; } + public System.IObservable> ItemChanged { get; } + public System.IObservable> ItemChanging { get; } + public System.IObservable ItemsAdded { get; } + public System.IObservable> ItemsMoved { get; } + public System.IObservable ItemsRemoved { get; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public double ResetChangeThreshold { get; set; } + public System.IObservable ShouldReset { get; } + public event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged; + public event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanging; + public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; + public event ReactiveUI.PropertyChangingEventHandler PropertyChanging; + public virtual void Add(T item) { } + public virtual void AddRange(System.Collections.Generic.IEnumerable collection) { } + public int BinarySearch(T item) { } + public int BinarySearch(T item, System.Collections.Generic.IComparer comparer) { } + public int BinarySearch(int index, int count, T item, System.Collections.Generic.IComparer comparer) { } + public virtual void Clear() { } + protected void ClearItems() { } + public bool Contains(T item) { } + public void CopyTo(T[] array, int arrayIndex) { } + public System.Collections.Generic.IEnumerator GetEnumerator() { } + public int IndexOf(T item) { } + public virtual void Insert(int index, T item) { } + protected void InsertItem(int index, T item) { } + public virtual void InsertRange(int index, System.Collections.Generic.IEnumerable collection) { } + public virtual void Move(int oldIndex, int newIndex) { } + protected void MoveItem(int oldIndex, int newIndex) { } + protected virtual void RaiseCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs args) { } + protected virtual void RaiseCollectionChanging(System.Collections.Specialized.NotifyCollectionChangedEventArgs args) { } + public virtual bool Remove(T item) { } + public virtual void RemoveAll(System.Collections.Generic.IEnumerable items) { } + public virtual void RemoveAt(int index) { } + protected void RemoveItem(int index) { } + public virtual void RemoveRange(int index, int count) { } + public virtual void Reset() { } + protected void SetItem(int index, T item) { } + public virtual void Sort(int index, int count, System.Collections.Generic.IComparer comparer) { } + public virtual void Sort(System.Comparison comparison) { } + public virtual void Sort(System.Collections.Generic.IComparer comparer = null) { } + public System.IDisposable SuppressChangeNotifications() { } + } +} +namespace System.Reactive.Disposables +{ + public class static DisposableMixins { } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt new file mode 100644 index 0000000000..ca4676daa2 --- /dev/null +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.netcoreapp2.0.approved.txt @@ -0,0 +1,999 @@ +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.AndroidSupport")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Tests")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Winforms")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.Wpf")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("ReactiveUI.XamForms")] +[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v2.0", FrameworkDisplayName="")] +namespace ReactiveUI +{ + public class static AutoPersistHelper + { + public static System.IDisposable ActOnEveryObject(this System.Collections.ObjectModel.ObservableCollection @this, System.Action onAdd, System.Action onRemove) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable ActOnEveryObject(this System.Collections.ObjectModel.ReadOnlyObservableCollection @this, System.Action onAdd, System.Action onRemove) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable ActOnEveryObject(this TCollection @this, System.Action onAdd, System.Action onRemove) + where TItem : ReactiveUI.IReactiveObject + where TCollection : System.Collections.Specialized.INotifyCollectionChanged, System.Collections.Generic.IEnumerable<> { } + public static System.IDisposable ActOnEveryObject(this System.IObservable> @this, System.Action onAdd, System.Action onRemove) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersist(this T @this, System.Func> doPersist, System.Nullable interval = null) + where T : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersist(this T @this, System.Func> doPersist, System.IObservable manualSaveSignal, System.Nullable interval = null) + where T : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersistCollection(this System.Collections.ObjectModel.ObservableCollection @this, System.Func> doPersist, System.Nullable interval = null) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersistCollection(this System.Collections.ObjectModel.ObservableCollection @this, System.Func> doPersist, System.IObservable manualSaveSignal, System.Nullable interval = null) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersistCollection(this System.Collections.ObjectModel.ReadOnlyObservableCollection @this, System.Func> doPersist, System.IObservable manualSaveSignal, System.Nullable interval = null) + where TItem : ReactiveUI.IReactiveObject { } + public static System.IDisposable AutoPersistCollection(this TCollection @this, System.Func> doPersist, System.IObservable manualSaveSignal, System.Nullable interval = null) + where TItem : ReactiveUI.IReactiveObject + where TCollection : System.Collections.Specialized.INotifyCollectionChanged, System.Collections.Generic.IEnumerable<> { } + } + public enum BindingDirection + { + OneWay = 0, + TwoWay = 1, + AsyncOneWay = 2, + } + public class CanActivateViewFetcher : ReactiveUI.IActivationForViewFetcher + { + public CanActivateViewFetcher() { } + public System.IObservable GetActivationForView(ReactiveUI.IActivatable view) { } + public int GetAffinityForView(System.Type view) { } + } + public class static ChangeSetMixin + { + public static System.IObservable CountChanged(this System.IObservable changeSet) { } + public static System.IObservable> CountChanged(this System.IObservable> changeSet) { } + public static bool HasCountChanged(this DynamicData.IChangeSet changeSet) { } + } + public class CombinedReactiveCommand : ReactiveUI.ReactiveCommandBase> + { + protected internal CombinedReactiveCommand(System.Collections.Generic.IEnumerable> childCommands, System.IObservable canExecute, System.Reactive.Concurrency.IScheduler outputScheduler) { } + public override System.IObservable CanExecute { get; } + public override System.IObservable IsExecuting { get; } + public override System.IObservable ThrownExceptions { get; } + protected override void Dispose(bool disposing) { } + public override System.IObservable> Execute(TParam parameter = null) { } + public override System.IDisposable Subscribe(System.IObserver> observer) { } + } + public class static CommandBinder + { + public static ReactiveUI.IReactiveBinding BindCommand(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> propertyName, System.Linq.Expressions.Expression> controlName, System.IObservable withParameter, string toEvent = null) + where TView : class, ReactiveUI.IViewFor<> + where TViewModel : class + where TProp : System.Windows.Input.ICommand { } + public static ReactiveUI.IReactiveBinding BindCommand(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> propertyName, System.Linq.Expressions.Expression> controlName, string toEvent = null) + where TView : class, ReactiveUI.IViewFor<> + where TViewModel : class + where TProp : System.Windows.Input.ICommand { } + public static ReactiveUI.IReactiveBinding BindCommand(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> propertyName, System.Linq.Expressions.Expression> controlName, System.Linq.Expressions.Expression> withParameter, string toEvent = null) + where TView : class, ReactiveUI.IViewFor<> + where TViewModel : class + where TProp : System.Windows.Input.ICommand { } + } + public class CommandBinderImplementation : Splat.IEnableLogger + { + public CommandBinderImplementation() { } + public ReactiveUI.IReactiveBinding BindCommand(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> controlProperty, System.Func withParameter, string toEvent = null) + where TView : class, ReactiveUI.IViewFor<> + where TViewModel : class + where TProp : System.Windows.Input.ICommand { } + public ReactiveUI.IReactiveBinding BindCommand(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> controlProperty, System.IObservable withParameter, string toEvent = null) + where TView : class, ReactiveUI.IViewFor<> + where TViewModel : class + where TProp : System.Windows.Input.ICommand { } + } + public class static ComparerChainingExtensions + { + public static System.Collections.Generic.IComparer ThenBy(this System.Collections.Generic.IComparer parent, System.Func selector) { } + public static System.Collections.Generic.IComparer ThenBy(this System.Collections.Generic.IComparer parent, System.Func selector, System.Collections.Generic.IComparer comparer) { } + public static System.Collections.Generic.IComparer ThenByDescending(this System.Collections.Generic.IComparer parent, System.Func selector) { } + public static System.Collections.Generic.IComparer ThenByDescending(this System.Collections.Generic.IComparer parent, System.Func selector, System.Collections.Generic.IComparer comparer) { } + } + public class CreatesCommandBindingViaCommandParameter : ReactiveUI.ICreatesCommandBinding + { + public CreatesCommandBindingViaCommandParameter() { } + public System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter) { } + public System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter, string eventName) { } + public int GetAffinityForObject(System.Type type, bool hasEventTarget) { } + } + public class CreatesCommandBindingViaEvent : ReactiveUI.ICreatesCommandBinding + { + public CreatesCommandBindingViaEvent() { } + public System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter) { } + public System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter, string eventName) { } + public int GetAffinityForObject(System.Type type, bool hasEventTarget) { } + } + public class static DependencyResolverMixins + { + public static void InitializeReactiveUI(this Splat.IMutableDependencyResolver resolver) { } + public static void RegisterViewsForViewModels(this Splat.IMutableDependencyResolver resolver, System.Reflection.Assembly assembly) { } + } + public class DummySuspensionDriver : ReactiveUI.ISuspensionDriver + { + public DummySuspensionDriver() { } + public System.IObservable InvalidateState() { } + public System.IObservable LoadState() { } + public System.IObservable SaveState(object state) { } + } + public class EqualityTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger + { + public EqualityTypeConverter() { } + public static object DoReferenceCast(object from, System.Type targetType) { } + public int GetAffinityForObjects(System.Type fromType, System.Type toType) { } + public bool TryConvert(object from, System.Type toType, object conversionHint, out object result) { } + } + public class static ExpressionMixins + { + public static object[] GetArgumentsArray(this System.Linq.Expressions.Expression expression) { } + public static System.Collections.Generic.IEnumerable GetExpressionChain(this System.Linq.Expressions.Expression @this) { } + public static System.Reflection.MemberInfo GetMemberInfo(this System.Linq.Expressions.Expression expression) { } + public static System.Linq.Expressions.Expression GetParent(this System.Linq.Expressions.Expression expression) { } + } + public interface IActivatable { } + public interface IActivationForViewFetcher + { + System.IObservable GetActivationForView(ReactiveUI.IActivatable view); + int GetAffinityForView(System.Type view); + } + public interface IBindingTypeConverter : Splat.IEnableLogger + { + int GetAffinityForObjects(System.Type fromType, System.Type toType); + bool TryConvert(object from, System.Type toType, object conversionHint, out object result); + } + public interface ICanActivate + { + System.IObservable Activated { get; } + System.IObservable Deactivated { get; } + } + public interface IComparerBuilder + { + System.Collections.Generic.IComparer OrderBy(System.Func selector); + System.Collections.Generic.IComparer OrderBy(System.Func selector, System.Collections.Generic.IComparer comparer); + System.Collections.Generic.IComparer OrderByDescending(System.Func selector); + System.Collections.Generic.IComparer OrderByDescending(System.Func selector, System.Collections.Generic.IComparer comparer); + } + public interface ICreatesCommandBinding + { + System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter); + System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter, string eventName); + int GetAffinityForObject(System.Type type, bool hasEventTarget); + } + public interface ICreatesObservableForProperty : Splat.IEnableLogger + { + int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False); + System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False); + } + public interface IHandleObservableErrors + { + System.IObservable ThrownExceptions { get; } + } + public interface IMessageBus : Splat.IEnableLogger + { + bool IsRegistered(System.Type type, string contract = null); + System.IObservable Listen(string contract = null); + System.IObservable ListenIncludeLatest(string contract = null); + System.IDisposable RegisterMessageSource(System.IObservable source, string contract = null); + void RegisterScheduler(System.Reactive.Concurrency.IScheduler scheduler, string contract = null); + void SendMessage(T message, string contract = null); + } + public interface INotifyCollectionChanging + { + public event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanging; + } + public interface INotifyPropertyChanging + { + public event ReactiveUI.PropertyChangingEventHandler PropertyChanging; + } + public class INPCObservableForProperty : ReactiveUI.ICreatesObservableForProperty, Splat.IEnableLogger + { + public INPCObservableForProperty() { } + public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged) { } + } + public class Interaction + { + public Interaction(System.Reactive.Concurrency.IScheduler handlerScheduler = null) { } + protected System.Func<, >[] GetHandlers() { } + public virtual System.IObservable Handle(TInput input) { } + public System.IDisposable RegisterHandler(System.Action> handler) { } + public System.IDisposable RegisterHandler(System.Func, System.Threading.Tasks.Task> handler) { } + public System.IDisposable RegisterHandler(System.Func, System.IObservable> handler) { } + } + public sealed class InteractionContext + { + public TInput Input { get; } + public bool IsHandled { get; } + public TOutput GetOutput() { } + public void SetOutput(TOutput output) { } + } + public interface IObservedChange + { + System.Linq.Expressions.Expression Expression { get; } + TSender Sender { get; } + TValue Value { get; } + } + public interface IPropertyBinderImplementation : Splat.IEnableLogger + { + ReactiveUI.IReactiveBinding> Bind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, object conversionHint, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter viewToVMConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor + ; + ReactiveUI.IReactiveBinding> Bind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, System.Func vmToViewConverter, System.Func viewToVmConverter) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor + ; + System.IDisposable BindTo(System.IObservable observedChange, TTarget target, System.Linq.Expressions.Expression> propertyExpression, object conversionHint, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TTarget : class + ; + ReactiveUI.IReactiveBinding OneWayBind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, object conversionHint, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor + ; + ReactiveUI.IReactiveBinding OneWayBind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor + ; + } + public interface IPropertyBindingHook + { + bool ExecuteHook(object source, object target, System.Func[]> getCurrentViewModelProperties, System.Func[]> getCurrentViewProperties, ReactiveUI.BindingDirection direction); + } + public interface IReactiveBinding : System.IDisposable + where out TView : ReactiveUI.IViewFor + where out TViewModel : class + { + System.IObservable Changed { get; } + ReactiveUI.BindingDirection Direction { get; } + TView View { get; } + System.Linq.Expressions.Expression ViewExpression { get; } + TViewModel ViewModel { get; } + System.Linq.Expressions.Expression ViewModelExpression { get; } + } + public interface IReactiveCommand : ReactiveUI.IHandleObservableErrors, System.IDisposable + { + System.IObservable CanExecute { get; } + System.IObservable IsExecuting { get; } + } + public interface IReactiveNotifyPropertyChanged + { + System.IObservable> Changed { get; } + System.IObservable> Changing { get; } + System.IDisposable SuppressChangeNotifications(); + } + public interface IReactiveObject : ReactiveUI.INotifyPropertyChanging, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged + { + void RaisePropertyChanged(System.ComponentModel.PropertyChangedEventArgs args); + void RaisePropertyChanging(ReactiveUI.PropertyChangingEventArgs args); + } + public class static IReactiveObjectExtensions + { + public static TRet RaiseAndSetIfChanged(this TObj @this, ref TRet backingField, TRet newValue, [System.Runtime.CompilerServices.CallerMemberNameAttribute()] string propertyName = null) + where TObj : ReactiveUI.IReactiveObject { } + public static void RaisePropertyChanged(this TSender @this, [System.Runtime.CompilerServices.CallerMemberNameAttribute()] string propertyName = null) + where TSender : ReactiveUI.IReactiveObject { } + public static void RaisePropertyChanging(this TSender @this, [System.Runtime.CompilerServices.CallerMemberNameAttribute()] string propertyName = null) + where TSender : ReactiveUI.IReactiveObject { } + } + public interface IReactivePropertyChangedEventArgs + { + string PropertyName { get; } + TSender Sender { get; } + } + public class IROObservableForProperty : ReactiveUI.ICreatesObservableForProperty, Splat.IEnableLogger + { + public IROObservableForProperty() { } + public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + } + public interface IRoutableViewModel : ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged + { + ReactiveUI.IScreen HostScreen { get; } + string UrlPathSegment { get; } + } + public interface IScreen + { + ReactiveUI.RoutingState Router { get; } + } + public interface ISupportsActivation + { + ReactiveUI.ViewModelActivator Activator { get; } + } + public interface ISuspensionDriver + { + System.IObservable InvalidateState(); + System.IObservable LoadState(); + System.IObservable SaveState(object state); + } + public interface ISuspensionHost : ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged + { + object AppState { get; set; } + System.Func CreateNewAppState { get; set; } + System.IObservable IsLaunchingNew { get; set; } + System.IObservable IsResuming { get; set; } + System.IObservable IsUnpausing { get; set; } + System.IObservable ShouldInvalidateState { get; set; } + System.IObservable ShouldPersistState { get; set; } + } + public interface IViewFor : ReactiveUI.IActivatable + { + object ViewModel { get; set; } + } + public interface IViewFor : ReactiveUI.IActivatable, ReactiveUI.IViewFor + where T : class + { + T ViewModel { get; set; } + } + public interface IViewLocator : Splat.IEnableLogger + { + ReactiveUI.IViewFor ResolveView(T viewModel, string contract = null) + where T : class; + } + public class MessageBus : ReactiveUI.IMessageBus, Splat.IEnableLogger + { + public MessageBus() { } + public static ReactiveUI.IMessageBus Current { get; set; } + public bool IsRegistered(System.Type type, string contract = null) { } + public System.IObservable Listen(string contract = null) { } + public System.IObservable ListenIncludeLatest(string contract = null) { } + public System.IDisposable RegisterMessageSource(System.IObservable source, string contract = null) { } + public void RegisterScheduler(System.Reactive.Concurrency.IScheduler scheduler, string contract = null) { } + public void SendMessage(T message, string contract = null) { } + } + public class static OAPHCreationHelperMixin + { + public static ReactiveUI.ObservableAsPropertyHelper ToProperty(this System.IObservable target, TObj source, System.Linq.Expressions.Expression> property, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + where TObj : class, ReactiveUI.IReactiveObject { } + public static ReactiveUI.ObservableAsPropertyHelper ToProperty(this System.IObservable target, TObj source, System.Linq.Expressions.Expression> property, out ReactiveUI.ObservableAsPropertyHelper<> result, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + where TObj : class, ReactiveUI.IReactiveObject { } + public static ReactiveUI.ObservableAsPropertyHelper ToProperty(this System.IObservable target, TObj source, string property, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + where TObj : class, ReactiveUI.IReactiveObject { } + public static ReactiveUI.ObservableAsPropertyHelper ToProperty(this System.IObservable target, TObj source, string property, out ReactiveUI.ObservableAsPropertyHelper<> result, TRet initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) + where TObj : class, ReactiveUI.IReactiveObject { } + } + public sealed class ObservableAsPropertyHelper : ReactiveUI.IHandleObservableErrors, Splat.IEnableLogger, System.IDisposable + { + public ObservableAsPropertyHelper(System.IObservable observable, System.Action onChanged, T initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public ObservableAsPropertyHelper(System.IObservable observable, System.Action onChanged, System.Action onChanging = null, T initialValue = null, bool deferSubscription = False, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public bool IsSubscribed { get; } + public System.IObservable ThrownExceptions { get; } + public T Value { get; } + public static ReactiveUI.ObservableAsPropertyHelper Default(T initialValue = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public void Dispose() { } + } + public class static ObservableLoggingMixin + { + public static System.IObservable Log(this System.IObservable @this, TObj klass, string message = null, System.Func stringifier = null) + where TObj : Splat.IEnableLogger { } + public static System.IObservable LoggedCatch(this System.IObservable @this, TObj klass, System.IObservable next = null, string message = null) + where TObj : Splat.IEnableLogger { } + public static System.IObservable LoggedCatch(this System.IObservable @this, TObj klass, System.Func> next, string message = null) + where TObj : Splat.IEnableLogger + where TException : System.Exception { } + } + public class ObservedChange : ReactiveUI.IObservedChange + { + public ObservedChange(TSender sender, System.Linq.Expressions.Expression expression, TValue value = null) { } + public System.Linq.Expressions.Expression Expression { get; } + public TSender Sender { get; } + public TValue Value { get; } + } + public class static ObservedChangedMixin + { + public static string GetPropertyName(this ReactiveUI.IObservedChange @this) { } + public static TValue GetValue(this ReactiveUI.IObservedChange @this) { } + public static System.IObservable Value(this System.IObservable> @this) { } + } + public class static OrderedComparer + { + public static ReactiveUI.IComparerBuilder For(System.Collections.Generic.IEnumerable enumerable) { } + public static ReactiveUI.IComparerBuilder For() { } + } + public class static OrderedComparer + { + public static System.Collections.Generic.IComparer OrderBy(System.Func selector) { } + public static System.Collections.Generic.IComparer OrderBy(System.Func selector, System.Collections.Generic.IComparer comparer) { } + public static System.Collections.Generic.IComparer OrderByDescending(System.Func selector) { } + public static System.Collections.Generic.IComparer OrderByDescending(System.Func selector, System.Collections.Generic.IComparer comparer) { } + } + public class PlatformRegistrations + { + public PlatformRegistrations() { } + public void Register(System.Action, System.Type> registerFunction) { } + } + public class POCOObservableForProperty : ReactiveUI.ICreatesObservableForProperty, Splat.IEnableLogger + { + public POCOObservableForProperty() { } + public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + } + public class PropertyBinderImplementation : ReactiveUI.IPropertyBinderImplementation, Splat.IEnableLogger + { + public PropertyBinderImplementation() { } + public ReactiveUI.IReactiveBinding> Bind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, object conversionHint, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter viewToVMConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public ReactiveUI.IReactiveBinding> Bind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, System.Func vmToViewConverter, System.Func viewToVmConverter) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public System.IDisposable BindTo(System.IObservable observedChange, TTarget target, System.Linq.Expressions.Expression> propertyExpression, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TTarget : class { } + public ReactiveUI.IReactiveBinding OneWayBind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public ReactiveUI.IReactiveBinding OneWayBind(TViewModel viewModel, TView view, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + } + public class static PropertyBindingMixins + { + public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter viewToVMConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null, ReactiveUI.IBindingTypeConverter viewToVMConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func vmToViewConverter, System.Func viewToVmConverter) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public static ReactiveUI.IReactiveBinding> Bind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.IObservable signalViewUpdate, System.Func vmToViewConverter, System.Func viewToVmConverter) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public static System.IDisposable BindTo(this System.IObservable @this, TTarget target, System.Linq.Expressions.Expression> property, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TTarget : class { } + public static ReactiveUI.IReactiveBinding OneWayBind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, object conversionHint = null, ReactiveUI.IBindingTypeConverter vmToViewConverterOverride = null) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + public static ReactiveUI.IReactiveBinding OneWayBind(this TView view, TViewModel viewModel, System.Linq.Expressions.Expression> vmProperty, System.Linq.Expressions.Expression> viewProperty, System.Func selector) + where TViewModel : class + where TView : class, ReactiveUI.IViewFor { } + } + public class PropertyChangingEventArgs : System.EventArgs + { + public PropertyChangingEventArgs(string propertyName) { } + public string PropertyName { get; set; } + } + public delegate void PropertyChangingEventHandler(object sender, ReactiveUI.PropertyChangingEventArgs e); + public class static ReactiveCommand + { + public static ReactiveUI.ReactiveCommand Create(System.Action execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand Create(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand Create(System.Action execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand Create(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.CombinedReactiveCommand CreateCombined(System.Collections.Generic.IEnumerable> childCommands, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromObservable(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromObservable(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func> execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + public static ReactiveUI.ReactiveCommand CreateFromTask(System.Func execute, System.IObservable canExecute = null, System.Reactive.Concurrency.IScheduler outputScheduler = null) { } + } + public class ReactiveCommand : ReactiveUI.ReactiveCommandBase + { + protected internal ReactiveCommand(System.Func> execute, System.IObservable canExecute, System.Reactive.Concurrency.IScheduler outputScheduler) { } + public override System.IObservable CanExecute { get; } + public override System.IObservable IsExecuting { get; } + public override System.IObservable ThrownExceptions { get; } + protected override void Dispose(bool disposing) { } + public override System.IObservable Execute(TParam parameter = null) { } + public override System.IDisposable Subscribe(System.IObserver observer) { } + } + public abstract class ReactiveCommandBase : ReactiveUI.IHandleObservableErrors, ReactiveUI.IReactiveCommand, System.IDisposable, System.IObservable, System.Windows.Input.ICommand + { + protected ReactiveCommandBase() { } + public abstract System.IObservable CanExecute { get; } + public abstract System.IObservable IsExecuting { get; } + public abstract System.IObservable ThrownExceptions { get; } + public event System.EventHandler System.Windows.Input.ICommand.CanExecuteChanged; + public void Dispose() { } + protected abstract void Dispose(bool disposing); + public abstract System.IObservable Execute(TParam parameter = null); + protected virtual bool ICommandCanExecute(object parameter) { } + protected virtual void ICommandExecute(object parameter) { } + protected void OnCanExecuteChanged() { } + public abstract System.IDisposable Subscribe(System.IObserver observer); + } + public class static ReactiveCommandMixins + { + public static System.IDisposable InvokeCommand(this System.IObservable @this, System.Windows.Input.ICommand command) { } + public static System.IDisposable InvokeCommand(this System.IObservable @this, ReactiveUI.ReactiveCommandBase command) { } + public static System.IDisposable InvokeCommand(this System.IObservable @this, TTarget target, System.Linq.Expressions.Expression> commandProperty) + where TTarget : class { } + public static System.IDisposable InvokeCommand(this System.IObservable @this, TTarget target, System.Linq.Expressions.Expression>> commandProperty) + where TTarget : class { } + } + public class static ReactiveNotifyPropertyChangedMixin + { + public static System.IObservable> ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, bool beforeChange = False, bool skipInitial = True) + where TSender : class { } + public static System.IObservable ObservableForProperty(this TSender @this, System.Linq.Expressions.Expression> property, System.Func selector, bool beforeChange = False) + where TSender : class { } + public static System.IObservable> SubscribeToExpressionChain(this TSender source, System.Linq.Expressions.Expression expression, bool beforeChange = False, bool skipInitial = True) { } + } + [System.Runtime.Serialization.DataContractAttribute()] + public class ReactiveObject : ReactiveUI.IHandleObservableErrors, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveNotifyPropertyChanged, ReactiveUI.IReactiveObject, Splat.IEnableLogger, System.ComponentModel.INotifyPropertyChanged + { + public ReactiveObject() { } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.IObservable> Changed { get; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.IObservable> Changing { get; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.IObservable ThrownExceptions { get; } + public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; + public event ReactiveUI.PropertyChangingEventHandler PropertyChanging; + public bool AreChangeNotificationsEnabled() { } + public System.IDisposable DelayChangeNotifications() { } + public System.IDisposable SuppressChangeNotifications() { } + } + public class ReactivePropertyChangedEventArgs : System.ComponentModel.PropertyChangedEventArgs, ReactiveUI.IReactivePropertyChangedEventArgs + { + public ReactivePropertyChangedEventArgs(TSender sender, string propertyName) { } + public TSender Sender { get; } + } + public class ReactivePropertyChangingEventArgs : ReactiveUI.PropertyChangingEventArgs, ReactiveUI.IReactivePropertyChangedEventArgs + { + public ReactivePropertyChangingEventArgs(TSender sender, string propertyName) { } + public TSender Sender { get; } + } + public class static Reflection + { + public static string ExpressionToPropertyNames(System.Linq.Expressions.Expression expression) { } + public static System.Type GetEventArgsTypeForEvent(System.Type type, string eventName) { } + public static System.Func GetValueFetcherForProperty(System.Reflection.MemberInfo member) { } + public static System.Func GetValueFetcherOrThrow(System.Reflection.MemberInfo member) { } + public static System.Action GetValueSetterForProperty(System.Reflection.MemberInfo member) { } + public static System.Action GetValueSetterOrThrow(System.Reflection.MemberInfo member) { } + public static bool IsStatic(this System.Reflection.PropertyInfo @this) { } + public static System.Type ReallyFindType(string type, bool throwOnFailure) { } + public static System.Linq.Expressions.Expression Rewrite(System.Linq.Expressions.Expression expression) { } + public static void ThrowIfMethodsNotOverloaded(string callingTypeName, object targetObject, params string[] methodsToCheck) { } + public static bool TryGetAllValuesForPropertyChain(out ReactiveUI.IObservedChange<, > changeValues, object current, System.Collections.Generic.IEnumerable expressionChain) { } + public static bool TryGetValueForPropertyChain(out TValue changeValue, object current, System.Collections.Generic.IEnumerable expressionChain) { } + public static bool TrySetValueToPropertyChain(object target, System.Collections.Generic.IEnumerable expressionChain, TValue value, bool shouldThrow = True) { } + } + public class Registrations + { + public Registrations() { } + public void Register(System.Action, System.Type> registerFunction) { } + } + public class static RoutableViewModelMixin + { + public static System.IDisposable WhenNavigatedTo(this ReactiveUI.IRoutableViewModel @this, System.Func onNavigatedTo) { } + public static System.IObservable WhenNavigatedToObservable(this ReactiveUI.IRoutableViewModel @this) { } + public static System.IObservable WhenNavigatingFromObservable(this ReactiveUI.IRoutableViewModel @this) { } + } + [System.Runtime.Serialization.DataContractAttribute()] + public class RoutingState : ReactiveUI.ReactiveObject + { + public RoutingState() { } + public RoutingState(System.Reactive.Concurrency.IScheduler scheduler) { } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.IObservable CurrentViewModel { get; set; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public ReactiveUI.ReactiveCommand Navigate { get; set; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public ReactiveUI.ReactiveCommand NavigateAndReset { get; set; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public ReactiveUI.ReactiveCommand NavigateBack { get; set; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.IObservable> NavigationChanged { get; set; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.Collections.ObjectModel.ObservableCollection NavigationStack { get; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public System.Reactive.Concurrency.IScheduler Scheduler { get; set; } + } + public class static RoutingStateMixins + { + public static T FindViewModelInStack(this ReactiveUI.RoutingState @this) + where T : ReactiveUI.IRoutableViewModel { } + public static ReactiveUI.IRoutableViewModel GetCurrentViewModel(this ReactiveUI.RoutingState @this) { } + } + public class static RxApp + { + public const int BigCacheLimit = 256; + public const int SmallCacheLimit = 64; + public static System.IObserver DefaultExceptionHandler { get; set; } + public static System.Reactive.Concurrency.IScheduler MainThreadScheduler { get; set; } + public static bool SupportsRangeNotifications { get; set; } + public static ReactiveUI.ISuspensionHost SuspensionHost { get; set; } + public static System.Reactive.Concurrency.IScheduler TaskpoolScheduler { get; set; } + } + public class ScheduledSubject : System.IDisposable, System.IObservable, System.IObserver, System.Reactive.Subjects.ISubject, System.Reactive.Subjects.ISubject + { + public ScheduledSubject(System.Reactive.Concurrency.IScheduler scheduler, System.IObserver defaultObserver = null, System.Reactive.Subjects.ISubject defaultSubject = null) { } + public void Dispose() { } + protected virtual void Dispose(bool isDisposing) { } + public void OnCompleted() { } + public void OnError(System.Exception error) { } + public void OnNext(T value) { } + public System.IDisposable Subscribe(System.IObserver observer) { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.All)] + public class SingleInstanceViewAttribute : System.Attribute + { + public SingleInstanceViewAttribute() { } + } + public class StringConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger + { + public StringConverter() { } + public int GetAffinityForObjects(System.Type fromType, System.Type toType) { } + public bool TryConvert(object from, System.Type toType, object conversionHint, out object result) { } + } + public class static SuspensionHostExtensions + { + public static T GetAppState(this ReactiveUI.ISuspensionHost @this) { } + public static System.IObservable ObserveAppState(this ReactiveUI.ISuspensionHost @this) + where T : class { } + public static System.IDisposable SetupDefaultSuspendResume(this ReactiveUI.ISuspensionHost @this, ReactiveUI.ISuspensionDriver driver = null) { } + } + public class UnhandledErrorException : System.Exception + { + public UnhandledErrorException() { } + public UnhandledErrorException(string message) { } + public UnhandledErrorException(string message, System.Exception innerException) { } + protected UnhandledErrorException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } + public class UnhandledInteractionException : System.Exception + { + public UnhandledInteractionException(ReactiveUI.Interaction interaction, TInput input) { } + public UnhandledInteractionException() { } + public UnhandledInteractionException(string message) { } + public UnhandledInteractionException(string message, System.Exception innerException) { } + protected UnhandledInteractionException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + public TInput Input { get; } + public ReactiveUI.Interaction Interaction { get; } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.All)] + public class ViewContractAttribute : System.Attribute + { + public ViewContractAttribute(string contract) { } + public string Contract { get; } + } + public class static ViewForMixins + { + public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Func> block) { } + public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Action> block) { } + public static void WhenActivated(this ReactiveUI.ISupportsActivation @this, System.Action block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Func> block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Func> block, ReactiveUI.IViewFor view) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action> block) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action> block, ReactiveUI.IViewFor view) { } + public static System.IDisposable WhenActivated(this ReactiveUI.IActivatable @this, System.Action block, ReactiveUI.IViewFor view = null) { } + } + public class static ViewLocator + { + public static ReactiveUI.IViewLocator Current { get; } + } + public class ViewLocatorNotFoundException : System.Exception + { + public ViewLocatorNotFoundException() { } + public ViewLocatorNotFoundException(string message) { } + public ViewLocatorNotFoundException(string message, System.Exception innerException) { } + protected ViewLocatorNotFoundException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } + } + public sealed class ViewModelActivator : System.IDisposable + { + public ViewModelActivator() { } + public System.IObservable Activated { get; } + public System.IObservable Deactivated { get; } + public System.IDisposable Activate() { } + public void Deactivate(bool ignoreRefCount = False) { } + public void Dispose() { } + } + public class WaitForDispatcherScheduler : System.Reactive.Concurrency.IScheduler + { + public WaitForDispatcherScheduler(System.Func schedulerFactory) { } + public System.DateTimeOffset Now { get; } + public System.IDisposable Schedule(TState state, System.Func action) { } + public System.IDisposable Schedule(TState state, System.TimeSpan dueTime, System.Func action) { } + public System.IDisposable Schedule(TState state, System.DateTimeOffset dueTime, System.Func action) { } + } + public class WeakEventManager + where TEventSource : class + where TEventHandler : class + { + protected WeakEventManager() { } + public static void AddHandler(TEventSource source, TEventHandler handler) { } + public static void DeliverEvent(TEventSource sender, TEventArgs args) { } + public static void RemoveHandler(TEventSource source, TEventHandler handler) { } + protected virtual void StartListening(object source) { } + protected virtual void StopListening(object source) { } + } + public class static WhenAnyMixin + { + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Func, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Func, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Linq.Expressions.Expression> property11, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAny(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Linq.Expressions.Expression> property11, System.Linq.Expressions.Expression> property12, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Func, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Func, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Linq.Expressions.Expression property8, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Linq.Expressions.Expression property8, System.Linq.Expressions.Expression property9, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Linq.Expressions.Expression property8, System.Linq.Expressions.Expression property9, System.Linq.Expressions.Expression property10, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Linq.Expressions.Expression property8, System.Linq.Expressions.Expression property9, System.Linq.Expressions.Expression property10, System.Linq.Expressions.Expression property11, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyDynamic(this TSender This, System.Linq.Expressions.Expression property1, System.Linq.Expressions.Expression property2, System.Linq.Expressions.Expression property3, System.Linq.Expressions.Expression property4, System.Linq.Expressions.Expression property5, System.Linq.Expressions.Expression property6, System.Linq.Expressions.Expression property7, System.Linq.Expressions.Expression property8, System.Linq.Expressions.Expression property9, System.Linq.Expressions.Expression property10, System.Linq.Expressions.Expression property11, System.Linq.Expressions.Expression property12, System.Func, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, ReactiveUI.IObservedChange, TRet> selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Func selector) + where TSender : class { } + public static System.IObservable> WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Linq.Expressions.Expression> property11, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyValue(this TSender This, System.Linq.Expressions.Expression> property1, System.Linq.Expressions.Expression> property2, System.Linq.Expressions.Expression> property3, System.Linq.Expressions.Expression> property4, System.Linq.Expressions.Expression> property5, System.Linq.Expressions.Expression> property6, System.Linq.Expressions.Expression> property7, System.Linq.Expressions.Expression> property8, System.Linq.Expressions.Expression> property9, System.Linq.Expressions.Expression> property10, System.Linq.Expressions.Expression> property11, System.Linq.Expressions.Expression> property12, System.Func selector) + where TSender : class { } + } + public class static WhenAnyObservableMixin + { + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10, System.Linq.Expressions.Expression>> obs11) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10, System.Linq.Expressions.Expression>> obs11, System.Linq.Expressions.Expression>> obs12) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10, System.Linq.Expressions.Expression>> obs11, System.Func selector) + where TSender : class { } + public static System.IObservable WhenAnyObservable(this TSender This, System.Linq.Expressions.Expression>> obs1, System.Linq.Expressions.Expression>> obs2, System.Linq.Expressions.Expression>> obs3, System.Linq.Expressions.Expression>> obs4, System.Linq.Expressions.Expression>> obs5, System.Linq.Expressions.Expression>> obs6, System.Linq.Expressions.Expression>> obs7, System.Linq.Expressions.Expression>> obs8, System.Linq.Expressions.Expression>> obs9, System.Linq.Expressions.Expression>> obs10, System.Linq.Expressions.Expression>> obs11, System.Linq.Expressions.Expression>> obs12, System.Func selector) + where TSender : class { } + } +} +namespace ReactiveUI.Legacy +{ + public interface IMoveInfo + { + int From { get; } + System.Collections.Generic.IEnumerable MovedItems { get; } + int To { get; } + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveCollection : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, Splat.IEnableLogger, System.Collections.Generic.IEnumerable, System.Collections.IEnumerable, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged + { + void Reset(); + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveDerivedList : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, ReactiveUI.Legacy.IReadOnlyReactiveCollection, ReactiveUI.Legacy.IReadOnlyReactiveList, Splat.IEnableLogger, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.IEnumerable, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged, System.IDisposable { } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveList : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, Splat.IEnableLogger, System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.IEnumerable, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged + { + bool IsEmpty { get; } + void AddRange(System.Collections.Generic.IEnumerable collection); + void InsertRange(int index, System.Collections.Generic.IEnumerable collection); + void RemoveAll(System.Collections.Generic.IEnumerable items); + void RemoveRange(int index, int count); + void Sort(System.Collections.Generic.IComparer comparer = null); + void Sort(System.Comparison comparison); + void Sort(int index, int count, System.Collections.Generic.IComparer comparer); + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveNotifyCollectionChanged + { + System.IObservable BeforeItemsAdded { get; } + System.IObservable> BeforeItemsMoved { get; } + System.IObservable BeforeItemsRemoved { get; } + System.IObservable Changed { get; } + System.IObservable Changing { get; } + System.IObservable CountChanged { get; } + System.IObservable CountChanging { get; } + System.IObservable IsEmptyChanged { get; } + System.IObservable ItemsAdded { get; } + System.IObservable> ItemsMoved { get; } + System.IObservable ItemsRemoved { get; } + System.IObservable ShouldReset { get; } + System.IDisposable SuppressChangeNotifications(); + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveNotifyCollectionItemChanged + { + bool ChangeTrackingEnabled { get; set; } + System.IObservable> ItemChanged { get; } + System.IObservable> ItemChanging { get; } + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReadOnlyReactiveCollection : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, Splat.IEnableLogger, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.IEnumerable, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged { } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReadOnlyReactiveList : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, ReactiveUI.Legacy.IReadOnlyReactiveCollection, Splat.IEnableLogger, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.IEnumerable, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged + { + bool IsEmpty { get; } + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public class static ObservableCollectionMixin + { + public static ReactiveUI.Legacy.IReactiveDerivedList CreateDerivedCollection(this System.Collections.Generic.IEnumerable @this, System.Func selector, System.Action onRemoved, System.Func filter = null, System.Func orderer = null, System.IObservable signalReset = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public static ReactiveUI.Legacy.IReactiveDerivedList CreateDerivedCollection(this System.Collections.Generic.IEnumerable @this, System.Func selector, System.Func filter = null, System.Func orderer = null, System.IObservable signalReset = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public static ReactiveUI.Legacy.IReactiveDerivedList CreateDerivedCollection(this System.Collections.Generic.IEnumerable @this, System.Func selector, System.Action onRemoved, System.Func filter = null, System.Func orderer = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public static ReactiveUI.Legacy.IReactiveDerivedList CreateDerivedCollection(this System.Collections.Generic.IEnumerable @this, System.Func selector, System.Func filter = null, System.Func orderer = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + } + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public class static ReactiveCollectionMixins + { + public static ReactiveUI.Legacy.IReactiveDerivedList CreateCollection(this System.IObservable fromObservable, System.Reactive.Concurrency.IScheduler scheduler) { } + public static ReactiveUI.Legacy.IReactiveDerivedList CreateCollection(this System.IObservable fromObservable, System.Nullable withDelay = null, System.Action onError = null, System.Reactive.Concurrency.IScheduler scheduler = null) { } + } + [System.Diagnostics.DebuggerDisplayAttribute("Count = {Count}")] + [System.Diagnostics.DebuggerTypeProxyAttribute(typeof(ReactiveUI.Legacy.CollectionDebugView<>))] + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public class ReactiveList : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveList, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, ReactiveUI.Legacy.IReadOnlyReactiveCollection, ReactiveUI.Legacy.IReadOnlyReactiveList, Splat.IEnableLogger, System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.INotifyPropertyChanged + { + public ReactiveList() { } + public ReactiveList(System.Collections.Generic.IEnumerable initialContents) { } + public ReactiveList(System.Collections.Generic.IEnumerable initialContents = null, double resetChangeThreshold = 0.3, System.Reactive.Concurrency.IScheduler scheduler = null) { } + public System.IObservable BeforeItemsAdded { get; } + public System.IObservable> BeforeItemsMoved { get; } + public System.IObservable BeforeItemsRemoved { get; } + public System.IObservable Changed { get; } + public bool ChangeTrackingEnabled { get; set; } + public System.IObservable Changing { get; } + public int Count { get; } + public System.IObservable CountChanged { get; } + public System.IObservable CountChanging { get; } + public bool IsEmpty { get; } + public System.IObservable IsEmptyChanged { get; } + public virtual bool IsReadOnly { get; } + public virtual T this[int index] { get; set; } + public System.IObservable> ItemChanged { get; } + public System.IObservable> ItemChanging { get; } + public System.IObservable ItemsAdded { get; } + public System.IObservable> ItemsMoved { get; } + public System.IObservable ItemsRemoved { get; } + [System.Runtime.Serialization.IgnoreDataMemberAttribute()] + public double ResetChangeThreshold { get; set; } + public System.IObservable ShouldReset { get; } + public event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged; + public event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanging; + public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; + public event ReactiveUI.PropertyChangingEventHandler PropertyChanging; + public virtual void Add(T item) { } + public virtual void AddRange(System.Collections.Generic.IEnumerable collection) { } + public int BinarySearch(T item) { } + public int BinarySearch(T item, System.Collections.Generic.IComparer comparer) { } + public int BinarySearch(int index, int count, T item, System.Collections.Generic.IComparer comparer) { } + public virtual void Clear() { } + protected void ClearItems() { } + public bool Contains(T item) { } + public void CopyTo(T[] array, int arrayIndex) { } + public System.Collections.Generic.IEnumerator GetEnumerator() { } + public int IndexOf(T item) { } + public virtual void Insert(int index, T item) { } + protected void InsertItem(int index, T item) { } + public virtual void InsertRange(int index, System.Collections.Generic.IEnumerable collection) { } + public virtual void Move(int oldIndex, int newIndex) { } + protected void MoveItem(int oldIndex, int newIndex) { } + protected virtual void RaiseCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs args) { } + protected virtual void RaiseCollectionChanging(System.Collections.Specialized.NotifyCollectionChangedEventArgs args) { } + public virtual bool Remove(T item) { } + public virtual void RemoveAll(System.Collections.Generic.IEnumerable items) { } + public virtual void RemoveAt(int index) { } + protected void RemoveItem(int index) { } + public virtual void RemoveRange(int index, int count) { } + public virtual void Reset() { } + protected void SetItem(int index, T item) { } + public virtual void Sort(int index, int count, System.Collections.Generic.IComparer comparer) { } + public virtual void Sort(System.Comparison comparison) { } + public virtual void Sort(System.Collections.Generic.IComparer comparer = null) { } + public System.IDisposable SuppressChangeNotifications() { } + } +} +namespace System.Reactive.Disposables +{ + public class static DisposableMixins { } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.Testing.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.Testing.net461.approved.txt similarity index 93% rename from src/ReactiveUI.Tests/API/ApiApprovalTests.Testing.approved.txt rename to src/ReactiveUI.Tests/API/ApiApprovalTests.Testing.net461.approved.txt index 68f536b840..92b48a4a3d 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.Testing.approved.txt +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.Testing.net461.approved.txt @@ -1,4 +1,4 @@ -[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName=".NET Framework 4.6.1")] +[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName=".NET Framework 4.6.1")] namespace ReactiveUI.Testing { public class static TestUtils diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.Testing.netcoreapp2.0.approved.txt b/src/ReactiveUI.Tests/API/ApiApprovalTests.Testing.netcoreapp2.0.approved.txt new file mode 100644 index 0000000000..4a2e257408 --- /dev/null +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.Testing.netcoreapp2.0.approved.txt @@ -0,0 +1,25 @@ +[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v2.0", FrameworkDisplayName="")] +namespace ReactiveUI.Testing +{ + public class static TestUtils + { + public static void AdvanceByMs(this Microsoft.Reactive.Testing.TestScheduler sched, double milliseconds) { } + public static void AdvanceToMs(this Microsoft.Reactive.Testing.TestScheduler sched, double milliseconds) { } + public static long FromTimeSpan(this Microsoft.Reactive.Testing.TestScheduler sched, System.TimeSpan span) { } + public static Microsoft.Reactive.Testing.Recorded> OnCompletedAt(this Microsoft.Reactive.Testing.TestScheduler sched, double milliseconds) { } + public static Microsoft.Reactive.Testing.Recorded> OnErrorAt(this Microsoft.Reactive.Testing.TestScheduler sched, double milliseconds, System.Exception ex) { } + public static Microsoft.Reactive.Testing.Recorded> OnNextAt(this Microsoft.Reactive.Testing.TestScheduler sched, double milliseconds, T value) { } + public static TRet With(this T sched, System.Func block) + where T : System.Reactive.Concurrency.IScheduler { } + public static void With(this T sched, System.Action block) + where T : System.Reactive.Concurrency.IScheduler { } + public static TRet With(this ReactiveUI.IMessageBus messageBus, System.Func block) { } + public static void With(this ReactiveUI.IMessageBus messageBus, System.Action block) { } + public static System.Threading.Tasks.Task WithAsync(this T sched, System.Func> block) + where T : System.Reactive.Concurrency.IScheduler { } + public static System.Threading.Tasks.Task WithAsync(this T sched, System.Func block) + where T : System.Reactive.Concurrency.IScheduler { } + public static System.IDisposable WithMessageBus(this ReactiveUI.IMessageBus messageBus) { } + public static System.IDisposable WithScheduler(System.Reactive.Concurrency.IScheduler sched) { } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.cs b/src/ReactiveUI.Tests/API/ApiApprovalTests.cs index 0909a1854a..ccef8ee02e 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.cs +++ b/src/ReactiveUI.Tests/API/ApiApprovalTests.cs @@ -13,49 +13,18 @@ namespace ReactiveUI.Tests.API { [ExcludeFromCodeCoverage] - public class ApiApprovalTests + public class ApiApprovalTests : ApiApprovalBase { - [Fact] - public void Blend() - { - var publicApi = Filter(ApiGenerator.GeneratePublicApi(typeof(Blend.ObservableTrigger).Assembly)); - publicApi.ShouldMatchApproved(); - } - [Fact] public void Testing() { - var publicApi = Filter(ApiGenerator.GeneratePublicApi(typeof(Testing.TestUtils).Assembly)); - publicApi.ShouldMatchApproved(); + CheckApproval(typeof(Testing.TestUtils).Assembly); } [Fact] public void ReactiveUI() { - var publicApi = Filter(ApiGenerator.GeneratePublicApi(typeof(RxApp).Assembly)); - publicApi.ShouldMatchApproved(); - } - - [Fact] - public void Winforms() - { - var publicApi = Filter(ApiGenerator.GeneratePublicApi(typeof(ReactiveUI.Winforms.WinformsCreatesObservableForProperty).Assembly)); - - publicApi.ShouldMatchApproved(); - } - - private static string Filter(string text) - { - return string.Join(Environment.NewLine, text.Split( - new[] - { - Environment.NewLine - }, StringSplitOptions.RemoveEmptyEntries) - .Where(l => - !l.StartsWith("[assembly: AssemblyVersion(") && - !l.StartsWith("[assembly: AssemblyFileVersion(") && - !l.StartsWith("[assembly: AssemblyInformationalVersion(") && - !string.IsNullOrWhiteSpace(l))); + CheckApproval(typeof(RxApp).Assembly); } } } diff --git a/src/ReactiveUI.Tests/Activation/ActivatingView.cs b/src/ReactiveUI.Tests/Activation/ActivatingView.cs index 6a5b43871a..20040ffbd2 100644 --- a/src/ReactiveUI.Tests/Activation/ActivatingView.cs +++ b/src/ReactiveUI.Tests/Activation/ActivatingView.cs @@ -12,9 +12,6 @@ namespace ReactiveUI.Tests { public sealed class ActivatingView : ReactiveObject, IViewFor, IDisposable { - public Subject Loaded = new Subject(); - public Subject Unloaded = new Subject(); - private ActivatingViewModel _viewModel; public ActivatingView() @@ -26,6 +23,10 @@ public ActivatingView() }); } + public Subject Loaded { get; } = new Subject(); + + public Subject Unloaded { get; } = new Subject(); + public ActivatingViewModel ViewModel { get => _viewModel; diff --git a/src/ReactiveUI.Tests/AutoPersistHelperTest.cs b/src/ReactiveUI.Tests/AutoPersist/AutoPersistCollectionTests.cs similarity index 54% rename from src/ReactiveUI.Tests/AutoPersistHelperTest.cs rename to src/ReactiveUI.Tests/AutoPersist/AutoPersistCollectionTests.cs index c70aa3ddb4..ac42a4bb13 100644 --- a/src/ReactiveUI.Tests/AutoPersistHelperTest.cs +++ b/src/ReactiveUI.Tests/AutoPersist/AutoPersistCollectionTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved. +// Copyright (c) 2019 .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. // See the LICENSE file in the project root for full license information. @@ -14,138 +14,6 @@ namespace ReactiveUI.Tests { - public class AutoPersistHelperTest - { - [Fact] - public void AutoPersistDoesntWorkOnNonDataContractClasses() - { - var fixture = new HostTestFixture(); - - var shouldDie = true; - try - { - fixture.AutoPersist(x => Observables.Unit); - } - catch (Exception) - { - shouldDie = false; - } - - Assert.False(shouldDie); - } - - [Fact] - public void AutoPersistHelperShouldntTriggerOnNonPersistableProperties() - { - new TestScheduler().With(sched => - { - var fixture = new TestFixture(); - var manualSave = new Subject(); - - var timesSaved = 0; - fixture.AutoPersist( - _ => - { - timesSaved++; - return Observables.Unit; - }, - manualSave, - TimeSpan.FromMilliseconds(100)); - - // No changes = no saving - sched.AdvanceByMs(2 * 100); - Assert.Equal(0, timesSaved); - - // Change to not serialized = no saving - fixture.NotSerialized = "Foo"; - sched.AdvanceByMs(2 * 100); - Assert.Equal(0, timesSaved); - }); - } - - [Fact] - public void AutoPersistHelperSavesOnInterval() - { - new TestScheduler().With(sched => - { - var fixture = new TestFixture(); - var manualSave = new Subject(); - - var timesSaved = 0; - fixture.AutoPersist( - _ => - { - timesSaved++; - return Observables.Unit; - }, - manualSave, - TimeSpan.FromMilliseconds(100)); - - // No changes = no saving - sched.AdvanceByMs(2 * 100); - Assert.Equal(0, timesSaved); - - // Change = one save - fixture.IsNotNullString = "Foo"; - sched.AdvanceByMs(2 * 100); - Assert.Equal(1, timesSaved); - - // Two fast changes = one save - fixture.IsNotNullString = "Foo"; - fixture.IsNotNullString = "Bar"; - sched.AdvanceByMs(2 * 100); - Assert.Equal(2, timesSaved); - - // Trigger save twice = one save - manualSave.OnNext(Unit.Default); - manualSave.OnNext(Unit.Default); - sched.AdvanceByMs(2 * 100); - Assert.Equal(3, timesSaved); - }); - } - - [Fact] - public void AutoPersistHelperDisconnects() - { - new TestScheduler().With(sched => - { - var fixture = new TestFixture(); - var manualSave = new Subject(); - - var timesSaved = 0; - var disp = fixture.AutoPersist( - _ => - { - timesSaved++; - return Observables.Unit; - }, - manualSave, - TimeSpan.FromMilliseconds(100)); - - // No changes = no saving - sched.AdvanceByMs(2 * 100); - Assert.Equal(0, timesSaved); - - // Change = one save - fixture.IsNotNullString = "Foo"; - sched.AdvanceByMs(2 * 100); - Assert.Equal(1, timesSaved); - - // Two changes after dispose = no save - disp.Dispose(); - fixture.IsNotNullString = "Foo"; - fixture.IsNotNullString = "Bar"; - sched.AdvanceByMs(2 * 100); - Assert.Equal(1, timesSaved); - - // Trigger save after dispose = no save - manualSave.OnNext(Unit.Default); - sched.AdvanceByMs(2 * 100); - Assert.Equal(1, timesSaved); - }); - } - } - public class AutoPersistCollectionTests { [Fact] diff --git a/src/ReactiveUI.Tests/AutoPersist/AutoPersistHelperTest.cs b/src/ReactiveUI.Tests/AutoPersist/AutoPersistHelperTest.cs new file mode 100644 index 0000000000..fd8db9b384 --- /dev/null +++ b/src/ReactiveUI.Tests/AutoPersist/AutoPersistHelperTest.cs @@ -0,0 +1,148 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Reactive; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using DynamicData.Binding; +using Microsoft.Reactive.Testing; +using ReactiveUI.Testing; +using Xunit; + +namespace ReactiveUI.Tests +{ + public class AutoPersistHelperTest + { + [Fact] + public void AutoPersistDoesntWorkOnNonDataContractClasses() + { + var fixture = new HostTestFixture(); + + var shouldDie = true; + try + { + fixture.AutoPersist(x => Observables.Unit); + } + catch (Exception) + { + shouldDie = false; + } + + Assert.False(shouldDie); + } + + [Fact] + public void AutoPersistHelperShouldntTriggerOnNonPersistableProperties() + { + new TestScheduler().With(sched => + { + var fixture = new TestFixture(); + var manualSave = new Subject(); + + var timesSaved = 0; + fixture.AutoPersist( + _ => + { + timesSaved++; + return Observables.Unit; + }, + manualSave, + TimeSpan.FromMilliseconds(100)); + + // No changes = no saving + sched.AdvanceByMs(2 * 100); + Assert.Equal(0, timesSaved); + + // Change to not serialized = no saving + fixture.NotSerialized = "Foo"; + sched.AdvanceByMs(2 * 100); + Assert.Equal(0, timesSaved); + }); + } + + [Fact] + public void AutoPersistHelperSavesOnInterval() + { + new TestScheduler().With(sched => + { + var fixture = new TestFixture(); + var manualSave = new Subject(); + + var timesSaved = 0; + fixture.AutoPersist( + _ => + { + timesSaved++; + return Observables.Unit; + }, + manualSave, + TimeSpan.FromMilliseconds(100)); + + // No changes = no saving + sched.AdvanceByMs(2 * 100); + Assert.Equal(0, timesSaved); + + // Change = one save + fixture.IsNotNullString = "Foo"; + sched.AdvanceByMs(2 * 100); + Assert.Equal(1, timesSaved); + + // Two fast changes = one save + fixture.IsNotNullString = "Foo"; + fixture.IsNotNullString = "Bar"; + sched.AdvanceByMs(2 * 100); + Assert.Equal(2, timesSaved); + + // Trigger save twice = one save + manualSave.OnNext(Unit.Default); + manualSave.OnNext(Unit.Default); + sched.AdvanceByMs(2 * 100); + Assert.Equal(3, timesSaved); + }); + } + + [Fact] + public void AutoPersistHelperDisconnects() + { + new TestScheduler().With(sched => + { + var fixture = new TestFixture(); + var manualSave = new Subject(); + + var timesSaved = 0; + var disp = fixture.AutoPersist( + _ => + { + timesSaved++; + return Observables.Unit; + }, + manualSave, + TimeSpan.FromMilliseconds(100)); + + // No changes = no saving + sched.AdvanceByMs(2 * 100); + Assert.Equal(0, timesSaved); + + // Change = one save + fixture.IsNotNullString = "Foo"; + sched.AdvanceByMs(2 * 100); + Assert.Equal(1, timesSaved); + + // Two changes after dispose = no save + disp.Dispose(); + fixture.IsNotNullString = "Foo"; + fixture.IsNotNullString = "Bar"; + sched.AdvanceByMs(2 * 100); + Assert.Equal(1, timesSaved); + + // Trigger save after dispose = no save + manualSave.OnNext(Unit.Default); + sched.AdvanceByMs(2 * 100); + Assert.Equal(1, timesSaved); + }); + } + } +} diff --git a/src/ReactiveUI.Tests/CommandBindingTests.cs b/src/ReactiveUI.Tests/CommandBindingTests.cs deleted file mode 100755 index b41f0312b3..0000000000 --- a/src/ReactiveUI.Tests/CommandBindingTests.cs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright (c) 2019 .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. -// See the LICENSE file in the project root for full license information. - -using System; -using System.ComponentModel; -using System.Reactive; -using System.Reactive.Linq; -using System.Reactive.Subjects; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Input; -using Xunit; - -namespace ReactiveUI.Tests -{ - public class FakeViewModel : ReactiveObject - { - public FakeViewModel() - { - Cmd = ReactiveCommand.Create(() => { }); - } - - public ReactiveCommand Cmd { get; protected set; } - } - - public class FakeView : IViewFor - { - public FakeView() - { - TheTextBox = new TextBox(); - ViewModel = new FakeViewModel(); - } - - public TextBox TheTextBox { get; protected set; } - - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (FakeViewModel)value; - } - - public FakeViewModel ViewModel { get; set; } - } - - public class CreatesCommandBindingTests - { - [WpfFact] - public void CommandBinderBindsToButton() - { - var fixture = new CreatesCommandBindingViaCommandParameter(); - var origCmd = ReactiveCommand.Create(() => { }); - var cmd = ReactiveCommand.Create(() => { }); - var input = new Button { Command = origCmd, CommandParameter = 42 }; - - Assert.True(fixture.GetAffinityForObject(input.GetType(), true) <= 0); - Assert.True(fixture.GetAffinityForObject(input.GetType(), false) > 0); - - var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5)); - - Assert.Equal(cmd, input.Command); - Assert.Equal(5, input.CommandParameter); - - disp.Dispose(); - - Assert.Equal(origCmd, input.Command); - Assert.Equal(42, input.CommandParameter); - } - - [Fact] - public void EventBinderBindsToExplicitEvent() - { - var input = new TestFixture(); - var fixture = new CreatesCommandBindingViaEvent(); - var wasCalled = false; - var cmd = ReactiveCommand.Create(x => wasCalled = true); - - Assert.True(fixture.GetAffinityForObject(input.GetType(), true) > 0); - Assert.False(fixture.GetAffinityForObject(input.GetType(), false) > 0); - - var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5), "PropertyChanged"); - input.IsNotNullString = "Foo"; - Assert.True(wasCalled); - - wasCalled = false; - disp.Dispose(); - input.IsNotNullString = "Bar"; - Assert.False(wasCalled); - } - - [WpfFact] - public void EventBinderBindsToExplicitInheritedEvent() - { - var fixture = new FakeView(); - fixture.BindCommand(fixture.ViewModel, x => x.Cmd, x => x.TheTextBox, "MouseDown"); - } - - [WpfFact] - public void EventBinderBindsToImplicitEvent() - { - var input = new Button(); - var fixture = new CreatesCommandBindingViaEvent(); - var cmd = ReactiveCommand.Create(_ => { }); - - Assert.True(fixture.GetAffinityForObject(input.GetType(), false) > 0); - - var invokeCount = 0; - cmd.Subscribe(_ => invokeCount += 1); - - var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5)); - Assert.NotNull(disp); - Assert.Equal(0, invokeCount); - - input.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); - Assert.Equal(1, invokeCount); - - disp.Dispose(); - input.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent)); - Assert.Equal(1, invokeCount); - } - } - - public class CommandBindViewModel : ReactiveObject - { - public ReactiveCommand _Command1; - public ReactiveCommand _Command2; - - private int _value; - - public CommandBindViewModel() - { - Command1 = ReactiveCommand.Create(_ => Unit.Default); - Command2 = ReactiveCommand.Create(() => { }); - } - - public ReactiveCommand Command1 - { - get => _Command1; - set => this.RaiseAndSetIfChanged(ref _Command1, value); - } - - public ReactiveCommand Command2 - { - get => _Command2; - set => this.RaiseAndSetIfChanged(ref _Command2, value); - } - - public FakeNestedViewModel NestedViewModel { get; set; } - - public int Value - { - get => _value; - set => this.RaiseAndSetIfChanged(ref _value, value); - } - } - - public class FakeNestedViewModel : ReactiveObject - { - public FakeNestedViewModel() - { - NestedCommand = ReactiveCommand.Create(() => { }); - } - - public ReactiveCommand NestedCommand { get; protected set; } - } - - public class CustomClickButton : Button - { - public event EventHandler CustomClick; - - public void RaiseCustomClick() => - CustomClick?.Invoke(this, EventArgs.Empty); - } - - public class CommandBindView : IViewFor - { - public CommandBindView() - { - Command1 = new CustomClickButton(); - Command2 = new Image(); - } - - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (CommandBindViewModel)value; - } - - public CommandBindViewModel ViewModel { get; set; } - - public CustomClickButton Command1 { get; protected set; } - - public Image Command2 { get; protected set; } - } - - public class ReactiveObjectCommandBindView : ReactiveObject, IViewFor - { - private CommandBindViewModel _vm; - - public ReactiveObjectCommandBindView() - { - Command1 = new CustomClickButton(); - Command2 = new Image(); - } - - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (CommandBindViewModel)value; - } - - public CommandBindViewModel ViewModel - { - get => _vm; - set => this.RaiseAndSetIfChanged(ref _vm, value); - } - - public CustomClickButton Command1 { get; protected set; } - - public Image Command2 { get; protected set; } - } - - public class CommandBindingImplementationTests - { - [WpfFact] - public void CommandBindByNameWireup() - { - var vm = new CommandBindViewModel(); - var view = new CommandBindView { ViewModel = vm }; - - Assert.Null(view.Command1.Command); - - var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); - Assert.Equal(vm.Command1, view.Command1.Command); - - var newCmd = ReactiveCommand.Create(_ => { }); - vm.Command1 = newCmd; - Assert.Equal(newCmd, view.Command1.Command); - - disp.Dispose(); - Assert.Null(view.Command1.Command); - } - - [WpfFact] - public void CommandBindNestedCommandWireup() - { - var vm = new CommandBindViewModel - { - NestedViewModel = new FakeNestedViewModel() - }; - - var view = new CommandBindView { ViewModel = vm }; - - var disp = view.BindCommand(vm, m => m.NestedViewModel.NestedCommand, x => x.Command1); - - Assert.Equal(vm.NestedViewModel.NestedCommand, view.Command1.Command); - } - - [WpfFact] - public void CommandBindSetsInitialEnabledState_True() - { - var vm = new CommandBindViewModel(); - var view = new CommandBindView { ViewModel = vm }; - - var canExecute1 = new BehaviorSubject(true); - var cmd1 = ReactiveCommand.Create(_ => { }, canExecute1); - vm.Command1 = cmd1; - - var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); - - Assert.True(view.Command1.IsEnabled); - } - - [WpfFact] - public void CommandBindSetsDisablesCommandWhenCanExecuteChanged() - { - var vm = new CommandBindViewModel(); - var view = new CommandBindView { ViewModel = vm }; - - var canExecute1 = new BehaviorSubject(true); - var cmd1 = ReactiveCommand.Create(_ => { }, canExecute1); - vm.Command1 = cmd1; - - var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); - - Assert.True(view.Command1.IsEnabled); - - canExecute1.OnNext(false); - - Assert.False(view.Command1.IsEnabled); - } - - [WpfFact] - public void CommandBindSetsInitialEnabledState_False() - { - var vm = new CommandBindViewModel(); - var view = new CommandBindView { ViewModel = vm }; - - var canExecute1 = new BehaviorSubject(false); - var cmd1 = ReactiveCommand.Create(_ => { }, canExecute1); - vm.Command1 = cmd1; - - var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); - - Assert.False(view.Command1.IsEnabled); - } - - [WpfFact] - public void CommandBindRaisesCanExecuteChangedOnBind() - { - var vm = new CommandBindViewModel(); - var view = new CommandBindView { ViewModel = vm }; - - var canExecute1 = new BehaviorSubject(true); - var cmd1 = ReactiveCommand.Create(_ => { }, canExecute1); - vm.Command1 = cmd1; - - var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); - - Assert.True(view.Command1.IsEnabled); - - // Now change to a disabled cmd - var canExecute2 = new BehaviorSubject(false); - var cmd2 = ReactiveCommand.Create(_ => { }, canExecute2); - vm.Command1 = cmd2; - - Assert.False(view.Command1.IsEnabled); - } - - [WpfFact] - public void CommandBindToExplicitEventWireup() - { - var vm = new CommandBindViewModel(); - var view = new CommandBindView { ViewModel = vm }; - - var invokeCount = 0; - vm.Command2.Subscribe(_ => invokeCount += 1); - - var disp = view.BindCommand(vm, x => x.Command2, x => x.Command2, "MouseUp"); - - view.Command2.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left) { RoutedEvent = UIElement.MouseUpEvent }); - - disp.Dispose(); - - view.Command2.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left) { RoutedEvent = UIElement.MouseUpEvent }); - Assert.Equal(1, invokeCount); - } - - [WpfFact] - public void CommandBindWithParameterExpression() - { - var vm = new CommandBindViewModel(); - var view = new CommandBindView { ViewModel = vm }; - - var received = 0; - var cmd = ReactiveCommand.Create(i => { received = i; }); - vm.Command1 = cmd; - - var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1, x => x.Value, nameof(CustomClickButton.CustomClick)); - - vm.Value = 42; - view.Command1.RaiseCustomClick(); - Assert.Equal(42, received); - - vm.Value = 13; - view.Command1.RaiseCustomClick(); - Assert.Equal(13, received); - } - - [WpfFact] - public void CommandBindWithDelaySetVMParameterExpression() - { - var vm = new CommandBindViewModel(); - var view = new ReactiveObjectCommandBindView(); - - var received = 0; - var cmd = ReactiveCommand.Create(i => { received = i; }); - vm.Command1 = cmd; - - var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1, x => x.Value, nameof(CustomClickButton.CustomClick)); - - view.ViewModel = vm; - - vm.Value = 42; - view.Command1.RaiseCustomClick(); - Assert.Equal(42, received); - - vm.Value = 13; - view.Command1.RaiseCustomClick(); - Assert.Equal(13, received); - } - - [WpfFact] - public void CommandBindWithDelaySetVMParameterNoINPCExpression() - { - var vm = new CommandBindViewModel(); - var view = new CommandBindView(); - - var received = 0; - var cmd = ReactiveCommand.Create(i => { received = i; }); - vm.Command1 = cmd; - - var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1, x => x.Value, nameof(CustomClickButton.CustomClick)); - - view.ViewModel = vm; - - vm.Value = 42; - view.Command1.RaiseCustomClick(); - Assert.Equal(0, received); - - vm.Value = 13; - view.Command1.RaiseCustomClick(); - Assert.Equal(0, received); - } - - [WpfFact] - public void CommandBindWithParameterObservable() - { - var vm = new CommandBindViewModel(); - var view = new CommandBindView { ViewModel = vm }; - - var received = 0; - var cmd = ReactiveCommand.Create(i => { received = i; }); - vm.Command1 = cmd; - - var value = Observable.Return(42); - var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1, value, nameof(CustomClickButton.CustomClick)); - - vm.Value = 42; - view.Command1.RaiseCustomClick(); - - Assert.Equal(42, received); - } - } -} diff --git a/src/ReactiveUI.Tests/Commands/CombinedReactiveCommandTest.cs b/src/ReactiveUI.Tests/Commands/CombinedReactiveCommandTest.cs new file mode 100644 index 0000000000..55d758a8f0 --- /dev/null +++ b/src/ReactiveUI.Tests/Commands/CombinedReactiveCommandTest.cs @@ -0,0 +1,187 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using DynamicData; +using Microsoft.Reactive.Testing; +using ReactiveUI.Testing; +using Xunit; + +namespace ReactiveUI.Tests +{ + public class CombinedReactiveCommandTest + { + [Fact] + public void CanExecuteIsFalseIfAnyChildCannotExecute() + { + var child1 = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + var child2 = ReactiveCommand.Create(() => Observables.Unit, Observables.False, ImmediateScheduler.Instance); + var childCommands = new[] { child1, child2 }; + var fixture = ReactiveCommand.CreateCombined(childCommands, outputScheduler: ImmediateScheduler.Instance); + fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var canExecute).Subscribe(); + + Assert.Equal(1, canExecute.Count); + Assert.False(canExecute[0]); + } + + [Fact] + public void CanExecuteIsFalseIfParentCanExecuteIsFalse() + { + var child1 = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + var child2 = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + var childCommands = new[] { child1, child2 }; + var fixture = ReactiveCommand.CreateCombined(childCommands, Observables.False, ImmediateScheduler.Instance); + fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var canExecute).Subscribe(); + + Assert.Equal(1, canExecute.Count); + Assert.False(canExecute[0]); + } + + [Fact] + public void CanExecuteTicksFailuresInChildCanExecuteThroughThrownExceptions() + { + var canExecuteSubject = new Subject(); + var child1 = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + var child2 = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject, ImmediateScheduler.Instance); + var childCommands = new[] { child1, child2 }; + var fixture = ReactiveCommand.CreateCombined(childCommands, outputScheduler: ImmediateScheduler.Instance); + fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var thrownExceptions).Subscribe(); + + canExecuteSubject.OnError(new InvalidOperationException("oops")); + + Assert.Equal(1, thrownExceptions.Count); + Assert.Equal("oops", thrownExceptions[0].Message); + } + + [Fact] + public void CanExecuteTicksFailuresThroughThrownExceptions() + { + var canExecuteSubject = new Subject(); + var child1 = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + var child2 = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + var childCommands = new[] { child1, child2 }; + var fixture = ReactiveCommand.CreateCombined(childCommands, canExecuteSubject, ImmediateScheduler.Instance); + fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var thrownExceptions).Subscribe(); + + canExecuteSubject.OnError(new InvalidOperationException("oops")); + + Assert.Equal(1, thrownExceptions.Count); + Assert.Equal("oops", thrownExceptions[0].Message); + } + + [Fact] + public void ExceptionsAreDeliveredOnOutputScheduler() + { + new TestScheduler().With( + sched => + { + var child = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException("oops"))); + var childCommands = new[] { child }; + var fixture = ReactiveCommand.CreateCombined(childCommands, outputScheduler: sched); + Exception exception = null; + fixture.ThrownExceptions.Subscribe(ex => exception = ex); + fixture.Execute().Subscribe(_ => { }, _ => { }); + + Assert.Null(exception); + sched.Start(); + Assert.IsType(exception); + }); + } + + [Fact] + public void ExecuteExecutesAllChildCommands() + { + var child1 = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + var child2 = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + var child3 = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + var childCommands = new[] { child1, child2, child3 }; + var fixture = ReactiveCommand.CreateCombined(childCommands, outputScheduler: ImmediateScheduler.Instance); + + fixture.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var isExecuting).Subscribe(); + child1.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var child1IsExecuting).Subscribe(); + child2.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var child2IsExecuting).Subscribe(); + child3.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var child3IsExecuting).Subscribe(); + + fixture.Execute().Subscribe(); + + Assert.Equal(3, isExecuting.Count); + Assert.False(isExecuting[0]); + Assert.True(isExecuting[1]); + Assert.False(isExecuting[2]); + + Assert.Equal(3, child1IsExecuting.Count); + Assert.False(child1IsExecuting[0]); + Assert.True(child1IsExecuting[1]); + Assert.False(child1IsExecuting[2]); + + Assert.Equal(3, child2IsExecuting.Count); + Assert.False(child2IsExecuting[0]); + Assert.True(child2IsExecuting[1]); + Assert.False(child2IsExecuting[2]); + + Assert.Equal(3, child3IsExecuting.Count); + Assert.False(child3IsExecuting[0]); + Assert.True(child3IsExecuting[1]); + Assert.False(child3IsExecuting[2]); + } + + [Fact] + public void ExecuteTicksErrorsInAnyChildCommandThroughThrownExceptions() + { + var child1 = ReactiveCommand.CreateFromObservable(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + var child2 = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException("oops")), outputScheduler: ImmediateScheduler.Instance); + var childCommands = new[] { child1, child2 }; + var fixture = ReactiveCommand.CreateCombined(childCommands, outputScheduler: ImmediateScheduler.Instance); + fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var thrownExceptions).Subscribe(); + + fixture.Execute().Subscribe(_ => { }, _ => { }); + + Assert.Equal(1, thrownExceptions.Count); + Assert.Equal("oops", thrownExceptions[0].Message); + } + + [Fact] + public void ExecuteTicksThroughTheResults() + { + var child1 = ReactiveCommand.CreateFromObservable(() => Observable.Return(1), outputScheduler: ImmediateScheduler.Instance); + var child2 = ReactiveCommand.CreateFromObservable(() => Observable.Return(2), outputScheduler: ImmediateScheduler.Instance); + var childCommands = new[] { child1, child2 }; + var fixture = ReactiveCommand.CreateCombined(childCommands, outputScheduler: ImmediateScheduler.Instance); + + fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var results).Subscribe(); + + fixture.Execute().Subscribe(); + + Assert.Equal(1, results.Count); + Assert.Equal(2, results[0].Count); + Assert.Equal(1, results[0][0]); + Assert.Equal(2, results[0][1]); + } + + [Fact] + public void ResultIsTickedThroughSpecifiedScheduler() + { + new TestScheduler().With( + sched => + { + var child1 = ReactiveCommand.Create(() => Observable.Return(1)); + var child2 = ReactiveCommand.Create(() => Observable.Return(2)); + var childCommands = new[] { child1, child2 }; + var fixture = ReactiveCommand.CreateCombined(childCommands, outputScheduler: sched); + fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var results).Subscribe(); + + fixture.Execute().Subscribe(); + Assert.Empty(results); + + sched.AdvanceByMs(1); + Assert.Equal(1, results.Count); + }); + } + } +} diff --git a/src/ReactiveUI.Tests/Commands/CreatesCommandBindingTests.cs b/src/ReactiveUI.Tests/Commands/CreatesCommandBindingTests.cs new file mode 100644 index 0000000000..9abe08dec3 --- /dev/null +++ b/src/ReactiveUI.Tests/Commands/CreatesCommandBindingTests.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.ComponentModel; +using System.Reactive; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using Xunit; + +namespace ReactiveUI.Tests +{ + public class CreatesCommandBindingTests + { + [Fact] + public void EventBinderBindsToExplicitEvent() + { + var input = new TestFixture(); + var fixture = new CreatesCommandBindingViaEvent(); + var wasCalled = false; + var cmd = ReactiveCommand.Create(x => wasCalled = true); + + Assert.True(fixture.GetAffinityForObject(input.GetType(), true) > 0); + Assert.False(fixture.GetAffinityForObject(input.GetType(), false) > 0); + + var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5), "PropertyChanged"); + input.IsNotNullString = "Foo"; + Assert.True(wasCalled); + + wasCalled = false; + disp.Dispose(); + input.IsNotNullString = "Bar"; + Assert.False(wasCalled); + } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Commands/Mocks/FakeCommand.cs b/src/ReactiveUI.Tests/Commands/Mocks/FakeCommand.cs new file mode 100644 index 0000000000..831ddd7c48 --- /dev/null +++ b/src/ReactiveUI.Tests/Commands/Mocks/FakeCommand.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Windows.Input; + +namespace ReactiveUI.Tests +{ + public class FakeCommand : ICommand + { + public event EventHandler CanExecuteChanged; + + public object CanExecuteParameter { get; private set; } + + public object ExecuteParameter { get; private set; } + + public bool CanExecute(object parameter) + { + CanExecuteParameter = parameter; + return true; + } + + public void Execute(object parameter) + { + ExecuteParameter = parameter; + } + + protected virtual void NotifyCanExecuteChanged(EventArgs e) + { + CanExecuteChanged?.Invoke(this, e); + } + } +} diff --git a/src/ReactiveUI.Tests/Commands/Mocks/ICommandHolder.cs b/src/ReactiveUI.Tests/Commands/Mocks/ICommandHolder.cs new file mode 100644 index 0000000000..b0a82713d9 --- /dev/null +++ b/src/ReactiveUI.Tests/Commands/Mocks/ICommandHolder.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Windows.Input; + +namespace ReactiveUI.Tests +{ + public class ICommandHolder : ReactiveObject + { + private ICommand _theCommand; + + public ICommand TheCommand + { + get => _theCommand; + set => this.RaiseAndSetIfChanged(ref _theCommand, value); + } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Commands/Mocks/ReactiveCommandHolder.cs b/src/ReactiveUI.Tests/Commands/Mocks/ReactiveCommandHolder.cs new file mode 100644 index 0000000000..5dee36dd1a --- /dev/null +++ b/src/ReactiveUI.Tests/Commands/Mocks/ReactiveCommandHolder.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Reactive; + +namespace ReactiveUI.Tests +{ + public class ReactiveCommandHolder : ReactiveObject + { + private ReactiveCommand _theCommand; + + public ReactiveCommand TheCommand + { + get => _theCommand; + set => this.RaiseAndSetIfChanged(ref _theCommand, value); + } + } +} diff --git a/src/ReactiveUI.Tests/ReactiveCommandTest.cs b/src/ReactiveUI.Tests/Commands/ReactiveCommandTest.cs old mode 100755 new mode 100644 similarity index 54% rename from src/ReactiveUI.Tests/ReactiveCommandTest.cs rename to src/ReactiveUI.Tests/Commands/ReactiveCommandTest.cs index 6f01e239e9..420a774958 --- a/src/ReactiveUI.Tests/ReactiveCommandTest.cs +++ b/src/ReactiveUI.Tests/Commands/ReactiveCommandTest.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; +using System.Collections.ObjectModel; using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Linq; @@ -28,9 +28,9 @@ public class ReactiveCommandTest [Fact] public void CanExecuteChangedIsAvailableViaICommand() { - var canExecuteSubject = new Subject(); - ICommand fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject); - var canExecuteChanged = new List(); + Subject canExecuteSubject = new Subject(); + ICommand fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject, ImmediateScheduler.Instance); + List canExecuteChanged = new List(); fixture.CanExecuteChanged += (s, e) => canExecuteChanged.Add(fixture.CanExecute(null)); canExecuteSubject.OnNext(true); @@ -44,8 +44,8 @@ public void CanExecuteChangedIsAvailableViaICommand() [Fact] public void CanExecuteIsAvailableViaICommand() { - var canExecuteSubject = new Subject(); - ICommand fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject); + Subject canExecuteSubject = new Subject(); + ICommand fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject, ImmediateScheduler.Instance); Assert.False(fixture.CanExecute(null)); @@ -59,8 +59,8 @@ public void CanExecuteIsAvailableViaICommand() [Fact] public void CanExecuteIsBehavioral() { - var fixture = ReactiveCommand.Create(() => Observables.Unit); - fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var canExecute).Subscribe(); + ReactiveCommand> fixture = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection canExecute).Subscribe(); Assert.Equal(1, canExecute.Count); Assert.True(canExecute[0]); @@ -70,19 +70,19 @@ public void CanExecuteIsBehavioral() public void CanExecuteIsFalseIfAlreadyExecuting() { new TestScheduler().With( - sched => + scheduler => { - var execute = Observables.Unit.Delay(TimeSpan.FromSeconds(1), sched); - var fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: sched); - fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var canExecute).Subscribe(); + IObservable execute = Observables.Unit.Delay(TimeSpan.FromSeconds(1), scheduler); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: scheduler); + fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection canExecute).Subscribe(); fixture.Execute().Subscribe(); - sched.AdvanceByMs(100); + scheduler.AdvanceByMs(100); Assert.Equal(2, canExecute.Count); Assert.False(canExecute[1]); - sched.AdvanceByMs(901); + scheduler.AdvanceByMs(901); Assert.Equal(3, canExecute.Count); Assert.True(canExecute[2]); @@ -92,9 +92,9 @@ public void CanExecuteIsFalseIfAlreadyExecuting() [Fact] public void CanExecuteIsFalseIfCallerDictatesAsSuch() { - var canExecuteSubject = new Subject(); - var fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject); - fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var canExecute).Subscribe(); + Subject canExecuteSubject = new Subject(); + ReactiveCommand> fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject, ImmediateScheduler.Instance); + fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection canExecute).Subscribe(); canExecuteSubject.OnNext(true); canExecuteSubject.OnNext(false); @@ -108,8 +108,8 @@ public void CanExecuteIsFalseIfCallerDictatesAsSuch() [Fact] public void CanExecuteIsUnsubscribedAfterCommandDisposal() { - var canExecuteSubject = new Subject(); - var fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject); + Subject canExecuteSubject = new Subject(); + ReactiveCommand> fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject, ImmediateScheduler.Instance); Assert.True(canExecuteSubject.HasObservers); @@ -121,9 +121,9 @@ public void CanExecuteIsUnsubscribedAfterCommandDisposal() [Fact] public void CanExecuteOnlyTicksDistinctValues() { - var canExecuteSubject = new Subject(); - var fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject); - fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var canExecute).Subscribe(); + Subject canExecuteSubject = new Subject(); + ReactiveCommand> fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject, ImmediateScheduler.Instance); + fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection canExecute).Subscribe(); canExecuteSubject.OnNext(false); canExecuteSubject.OnNext(false); @@ -140,9 +140,9 @@ public void CanExecuteOnlyTicksDistinctValues() [Fact] public void CanExecuteTicksFailuresThroughThrownExceptions() { - var canExecuteSubject = new Subject(); - var fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject); - fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var thrownExceptions).Subscribe(); + Subject canExecuteSubject = new Subject(); + ReactiveCommand> fixture = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject, ImmediateScheduler.Instance); + fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection thrownExceptions).Subscribe(); canExecuteSubject.OnError(new InvalidOperationException("oops")); @@ -153,8 +153,8 @@ public void CanExecuteTicksFailuresThroughThrownExceptions() [Fact] public void CreateTaskFacilitatesTPLIntegration() { - var fixture = ReactiveCommand.CreateFromTask(() => Task.FromResult(13)); - fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var results).Subscribe(); + ReactiveCommand fixture = ReactiveCommand.CreateFromTask(() => Task.FromResult(13), outputScheduler: ImmediateScheduler.Instance); + fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection results).Subscribe(); fixture.Execute().Subscribe(); @@ -165,8 +165,8 @@ public void CreateTaskFacilitatesTPLIntegration() [Fact] public void CreateTaskFacilitatesTPLIntegrationWithParameter() { - var fixture = ReactiveCommand.CreateFromTask(param => Task.FromResult(param + 1)); - fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var results).Subscribe(); + ReactiveCommand fixture = ReactiveCommand.CreateFromTask(param => Task.FromResult(param + 1), outputScheduler: ImmediateScheduler.Instance); + fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection results).Subscribe(); fixture.Execute(3).Subscribe(); fixture.Execute(41).Subscribe(); @@ -179,7 +179,7 @@ public void CreateTaskFacilitatesTPLIntegrationWithParameter() [Fact] public void CreateThrowsIfExecutionParameterIsNull() { - Assert.Throws(() => ReactiveCommand.Create((Action)null)); + Assert.Throws(() => ReactiveCommand.Create(null)); Assert.Throws(() => ReactiveCommand.Create((Func)null)); Assert.Throws(() => ReactiveCommand.Create((Action)null)); Assert.Throws(() => ReactiveCommand.Create((Func)null)); @@ -193,15 +193,15 @@ public void CreateThrowsIfExecutionParameterIsNull() public void ExceptionsAreDeliveredOnOutputScheduler() { new TestScheduler().With( - sched => + scheduler => { - var fixture = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException()), outputScheduler: sched); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException()), outputScheduler: scheduler); Exception exception = null; fixture.ThrownExceptions.Subscribe(ex => exception = ex); fixture.Execute().Subscribe(_ => { }, _ => { }); Assert.Null(exception); - sched.Start(); + scheduler.Start(); Assert.IsType(exception); }); } @@ -210,21 +210,21 @@ public void ExceptionsAreDeliveredOnOutputScheduler() public void ExecuteCanBeCancelled() { new TestScheduler().With( - sched => + scheduler => { - var execute = Observables.Unit.Delay(TimeSpan.FromSeconds(1), sched); - var fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: sched); - fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var executed).Subscribe(); + IObservable execute = Observables.Unit.Delay(TimeSpan.FromSeconds(1), scheduler); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: scheduler); + fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection executed).Subscribe(); - var sub1 = fixture.Execute().Subscribe(); - var sub2 = fixture.Execute().Subscribe(); - sched.AdvanceByMs(999); + IDisposable sub1 = fixture.Execute().Subscribe(); + IDisposable sub2 = fixture.Execute().Subscribe(); + scheduler.AdvanceByMs(999); Assert.True(fixture.IsExecuting.FirstAsync().Wait()); Assert.Empty(executed); sub1.Dispose(); - sched.AdvanceByMs(2); + scheduler.AdvanceByMs(2); Assert.Equal(1, executed.Count); Assert.False(fixture.IsExecuting.FirstAsync().Wait()); }); @@ -233,8 +233,8 @@ public void ExecuteCanBeCancelled() [Fact] public void ExecuteCanTickThroughMultipleResults() { - var fixture = ReactiveCommand.CreateFromObservable(() => new[] { 1, 2, 3 }.ToObservable()); - fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var results).Subscribe(); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => new[] { 1, 2, 3 }.ToObservable(), outputScheduler: ImmediateScheduler.Instance); + fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection results).Subscribe(); fixture.Execute().Subscribe(); @@ -248,33 +248,33 @@ public void ExecuteCanTickThroughMultipleResults() public void ExecuteFacilitatesAnyNumberOfInFlightExecutions() { new TestScheduler().With( - sched => + scheduler => { - var execute = Observables.Unit.Delay(TimeSpan.FromMilliseconds(500), sched); - var fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: sched); - fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var executed).Subscribe(); + IObservable execute = Observables.Unit.Delay(TimeSpan.FromMilliseconds(500), scheduler); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: scheduler); + fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection executed).Subscribe(); - var sub1 = fixture.Execute().Subscribe(); - var sub2 = fixture.Execute().Subscribe(); - sched.AdvanceByMs(100); + IDisposable sub1 = fixture.Execute().Subscribe(); + IDisposable sub2 = fixture.Execute().Subscribe(); + scheduler.AdvanceByMs(100); - var sub3 = fixture.Execute().Subscribe(); - sched.AdvanceByMs(200); - var sub4 = fixture.Execute().Subscribe(); - sched.AdvanceByMs(100); + IDisposable sub3 = fixture.Execute().Subscribe(); + scheduler.AdvanceByMs(200); + IDisposable sub4 = fixture.Execute().Subscribe(); + scheduler.AdvanceByMs(100); Assert.True(fixture.IsExecuting.FirstAsync().Wait()); Assert.Empty(executed); - sched.AdvanceByMs(101); + scheduler.AdvanceByMs(101); Assert.Equal(2, executed.Count); Assert.True(fixture.IsExecuting.FirstAsync().Wait()); - sched.AdvanceByMs(200); + scheduler.AdvanceByMs(200); Assert.Equal(3, executed.Count); Assert.True(fixture.IsExecuting.FirstAsync().Wait()); - sched.AdvanceByMs(100); + scheduler.AdvanceByMs(100); Assert.Equal(4, executed.Count); Assert.False(fixture.IsExecuting.FirstAsync().Wait()); }); @@ -283,13 +283,14 @@ public void ExecuteFacilitatesAnyNumberOfInFlightExecutions() [Fact] public void ExecuteIsAvailableViaICommand() { - var executed = false; + bool executed = false; ICommand fixture = ReactiveCommand.Create( () => { executed = true; return Observables.Unit; - }); + }, + outputScheduler: ImmediateScheduler.Instance); fixture.Execute(null); Assert.True(executed); @@ -298,13 +299,14 @@ public void ExecuteIsAvailableViaICommand() [Fact] public void ExecutePassesThroughParameter() { - var parameters = new List(); - var fixture = ReactiveCommand.CreateFromObservable( + List parameters = new List(); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable( param => { parameters.Add(param); return Observables.Unit; - }); + }, + outputScheduler: ImmediateScheduler.Instance); fixture.Execute(1).Subscribe(); fixture.Execute(42).Subscribe(); @@ -319,9 +321,9 @@ public void ExecutePassesThroughParameter() [Fact] public void ExecuteReenablesExecutionEvenAfterFailure() { - var fixture = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException("oops"))); - fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var canExecute).Subscribe(); - fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var thrownExceptions).Subscribe(); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException("oops")), outputScheduler: ImmediateScheduler.Instance); + fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection canExecute).Subscribe(); + fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection thrownExceptions).Subscribe(); fixture.Execute().Subscribe(_ => { }, _ => { }); @@ -338,16 +340,16 @@ public void ExecuteReenablesExecutionEvenAfterFailure() public void ExecuteResultIsDeliveredOnSpecifiedScheduler() { new TestScheduler().With( - sched => + scheduler => { - var execute = Observables.Unit; - var fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: sched); - var executed = false; + IObservable execute = Observables.Unit; + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: scheduler); + bool executed = false; fixture.Execute().Subscribe(_ => executed = true); Assert.False(executed); - sched.AdvanceByMs(1); + scheduler.AdvanceByMs(1); Assert.True(executed); }); } @@ -355,7 +357,7 @@ public void ExecuteResultIsDeliveredOnSpecifiedScheduler() [Fact] public void ExecuteTicksAnyException() { - var fixture = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException())); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException()), outputScheduler: ImmediateScheduler.Instance); fixture.ThrownExceptions.Subscribe(); Exception exception = null; fixture.Execute().Subscribe(_ => { }, ex => exception = ex, () => { }); @@ -366,7 +368,7 @@ public void ExecuteTicksAnyException() [Fact] public void ExecuteTicksAnyLambdaException() { - var fixture = ReactiveCommand.CreateFromObservable(() => { throw new InvalidOperationException(); }); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => throw new InvalidOperationException(), outputScheduler: ImmediateScheduler.Instance); fixture.ThrownExceptions.Subscribe(); Exception exception = null; fixture.Execute().Subscribe(_ => { }, ex => exception = ex, () => { }); @@ -377,8 +379,8 @@ public void ExecuteTicksAnyLambdaException() [Fact] public void ExecuteTicksErrorsThroughThrownExceptions() { - var fixture = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException("oops"))); - fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var thrownExceptions).Subscribe(); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException("oops")), outputScheduler: ImmediateScheduler.Instance); + fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection thrownExceptions).Subscribe(); fixture.Execute().Subscribe(_ => { }, _ => { }); @@ -389,8 +391,8 @@ public void ExecuteTicksErrorsThroughThrownExceptions() [Fact] public void ExecuteTicksLambdaErrorsThroughThrownExceptions() { - var fixture = ReactiveCommand.CreateFromObservable(() => { throw new InvalidOperationException("oops"); }); - fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var thrownExceptions).Subscribe(); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => throw new InvalidOperationException("oops"), outputScheduler: ImmediateScheduler.Instance); + fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection thrownExceptions).Subscribe(); fixture.Execute().Subscribe(_ => { }, _ => { }); @@ -402,9 +404,9 @@ public void ExecuteTicksLambdaErrorsThroughThrownExceptions() [Fact] public void ExecuteTicksThroughTheResult() { - var num = 0; - var fixture = ReactiveCommand.CreateFromObservable(() => Observable.Return(num)); - fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var results).Subscribe(); + int num = 0; + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => Observable.Return(num)); + fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection results).Subscribe(); num = 1; fixture.Execute().Subscribe(); @@ -423,7 +425,7 @@ public void ExecuteTicksThroughTheResult() public void ExecuteViaICommandThrowsIfParameterTypeIsIncorrect() { ICommand fixture = ReactiveCommand.Create(_ => { }); - var ex = Assert.Throws(() => fixture.Execute("foo")); + InvalidOperationException ex = Assert.Throws(() => fixture.Execute("foo")); Assert.Equal("Command requires parameters of type System.Int32, but received parameter of type System.String.", ex.Message); fixture = ReactiveCommand.Create(_ => { }); @@ -435,7 +437,7 @@ public void ExecuteViaICommandThrowsIfParameterTypeIsIncorrect() public void ExecuteViaICommandWorksWithNullableTypes() { int? value = null; - ICommand fixture = ReactiveCommand.Create(param => { value = param; }); + ICommand fixture = ReactiveCommand.Create(param => { value = param; }, outputScheduler: ImmediateScheduler.Instance); fixture.Execute(42); Assert.Equal(42, value); @@ -447,11 +449,11 @@ public void ExecuteViaICommandWorksWithNullableTypes() [Fact] public void InvokeCommandAgainstICommandInTargetInvokesTheCommand() { - var executionCount = 0; - var fixture = new ICommandHolder(); - var source = new Subject(); + int executionCount = 0; + ICommandHolder fixture = new ICommandHolder(); + Subject source = new Subject(); source.InvokeCommand(fixture, x => x.TheCommand); - fixture.TheCommand = ReactiveCommand.Create(() => ++executionCount); + fixture.TheCommand = ReactiveCommand.Create(() => ++executionCount, outputScheduler: ImmediateScheduler.Instance); source.OnNext(Unit.Default); Assert.Equal(1, executionCount); @@ -463,10 +465,10 @@ public void InvokeCommandAgainstICommandInTargetInvokesTheCommand() [Fact] public void InvokeCommandAgainstICommandInTargetPassesTheSpecifiedValueToCanExecuteAndExecute() { - var fixture = new ICommandHolder(); - var source = new Subject(); + ICommandHolder fixture = new ICommandHolder(); + Subject source = new Subject(); source.InvokeCommand(fixture, x => x.TheCommand); - var command = new FakeCommand(); + FakeCommand command = new FakeCommand(); fixture.TheCommand = command; source.OnNext(42); @@ -477,12 +479,12 @@ public void InvokeCommandAgainstICommandInTargetPassesTheSpecifiedValueToCanExec [Fact] public void InvokeCommandAgainstICommandInTargetRespectsCanExecute() { - var executed = false; - var canExecute = new BehaviorSubject(false); - var fixture = new ICommandHolder(); - var source = new Subject(); + bool executed = false; + BehaviorSubject canExecute = new BehaviorSubject(false); + ICommandHolder fixture = new ICommandHolder(); + Subject source = new Subject(); source.InvokeCommand(fixture, x => x.TheCommand); - fixture.TheCommand = ReactiveCommand.Create(() => executed = true, canExecute); + fixture.TheCommand = ReactiveCommand.Create(() => executed = true, canExecute, ImmediateScheduler.Instance); source.OnNext(Unit.Default); Assert.False(executed); @@ -495,12 +497,12 @@ public void InvokeCommandAgainstICommandInTargetRespectsCanExecute() [Fact] public void InvokeCommandAgainstICommandInTargetRespectsCanExecuteWindow() { - var executed = false; - var canExecute = new BehaviorSubject(false); - var fixture = new ICommandHolder(); - var source = new Subject(); + bool executed = false; + BehaviorSubject canExecute = new BehaviorSubject(false); + ICommandHolder fixture = new ICommandHolder(); + Subject source = new Subject(); source.InvokeCommand(fixture, x => x.TheCommand); - fixture.TheCommand = ReactiveCommand.Create(() => executed = true, canExecute); + fixture.TheCommand = ReactiveCommand.Create(() => executed = true, canExecute, ImmediateScheduler.Instance); source.OnNext(Unit.Default); Assert.False(executed); @@ -514,17 +516,18 @@ public void InvokeCommandAgainstICommandInTargetRespectsCanExecuteWindow() [Fact] public void InvokeCommandAgainstICommandInTargetSwallowsExceptions() { - var count = 0; - var fixture = new ICommandHolder(); - var command = ReactiveCommand.Create( + int count = 0; + ICommandHolder fixture = new ICommandHolder(); + ReactiveCommand command = ReactiveCommand.Create( () => { ++count; throw new InvalidOperationException(); - }); + }, + outputScheduler: ImmediateScheduler.Instance); command.ThrownExceptions.Subscribe(); fixture.TheCommand = command; - var source = new Subject(); + Subject source = new Subject(); source.InvokeCommand(fixture, x => x.TheCommand); source.OnNext(Unit.Default); @@ -536,9 +539,9 @@ public void InvokeCommandAgainstICommandInTargetSwallowsExceptions() [Fact] public void InvokeCommandAgainstICommandInvokesTheCommand() { - var executionCount = 0; - var fixture = (ICommand)ReactiveCommand.Create(() => ++executionCount); - var source = new Subject(); + int executionCount = 0; + ICommand fixture = ReactiveCommand.Create(() => ++executionCount, outputScheduler: ImmediateScheduler.Instance); + Subject source = new Subject(); source.InvokeCommand(fixture); source.OnNext(Unit.Default); @@ -551,8 +554,8 @@ public void InvokeCommandAgainstICommandInvokesTheCommand() [Fact] public void InvokeCommandAgainstICommandPassesTheSpecifiedValueToCanExecuteAndExecute() { - var fixture = new FakeCommand(); - var source = new Subject(); + FakeCommand fixture = new FakeCommand(); + Subject source = new Subject(); source.InvokeCommand(fixture); source.OnNext(42); @@ -563,10 +566,10 @@ public void InvokeCommandAgainstICommandPassesTheSpecifiedValueToCanExecuteAndEx [Fact] public void InvokeCommandAgainstICommandRespectsCanExecute() { - var executed = false; - var canExecute = new BehaviorSubject(false); - var fixture = (ICommand)ReactiveCommand.Create(() => executed = true, canExecute); - var source = new Subject(); + bool executed = false; + BehaviorSubject canExecute = new BehaviorSubject(false); + ICommand fixture = ReactiveCommand.Create(() => executed = true, canExecute, ImmediateScheduler.Instance); + Subject source = new Subject(); source.InvokeCommand(fixture); source.OnNext(Unit.Default); @@ -580,10 +583,10 @@ public void InvokeCommandAgainstICommandRespectsCanExecute() [Fact] public void InvokeCommandAgainstICommandRespectsCanExecuteWindow() { - var executed = false; - var canExecute = new BehaviorSubject(false); - var fixture = (ICommand)ReactiveCommand.Create(() => executed = true, canExecute); - var source = new Subject(); + bool executed = false; + BehaviorSubject canExecute = new BehaviorSubject(false); + ICommand fixture = ReactiveCommand.Create(() => executed = true, canExecute, ImmediateScheduler.Instance); + Subject source = new Subject(); source.InvokeCommand(fixture); source.OnNext(Unit.Default); @@ -598,15 +601,16 @@ public void InvokeCommandAgainstICommandRespectsCanExecuteWindow() [Fact] public void InvokeCommandAgainstICommandSwallowsExceptions() { - var count = 0; - var fixture = ReactiveCommand.Create( + int count = 0; + ReactiveCommand fixture = ReactiveCommand.Create( () => { ++count; throw new InvalidOperationException(); - }); + }, + outputScheduler: ImmediateScheduler.Instance); fixture.ThrownExceptions.Subscribe(); - var source = new Subject(); + Subject source = new Subject(); source.InvokeCommand((ICommand)fixture); source.OnNext(Unit.Default); @@ -618,11 +622,11 @@ public void InvokeCommandAgainstICommandSwallowsExceptions() [Fact] public void InvokeCommandAgainstReactiveCommandInTargetInvokesTheCommand() { - var executionCount = 0; - var fixture = new ReactiveCommandHolder(); - var source = new Subject(); + int executionCount = 0; + ReactiveCommandHolder fixture = new ReactiveCommandHolder(); + Subject source = new Subject(); source.InvokeCommand(fixture, x => x.TheCommand); - fixture.TheCommand = ReactiveCommand.Create(_ => { ++executionCount; }); + fixture.TheCommand = ReactiveCommand.Create(_ => { ++executionCount; }, outputScheduler: ImmediateScheduler.Instance); source.OnNext(0); Assert.Equal(1, executionCount); @@ -634,11 +638,11 @@ public void InvokeCommandAgainstReactiveCommandInTargetInvokesTheCommand() [Fact] public void InvokeCommandAgainstReactiveCommandInTargetPassesTheSpecifiedValueToExecute() { - var executeReceived = 0; - var fixture = new ReactiveCommandHolder(); - var source = new Subject(); + int executeReceived = 0; + ReactiveCommandHolder fixture = new ReactiveCommandHolder(); + Subject source = new Subject(); source.InvokeCommand(fixture, x => x.TheCommand); - fixture.TheCommand = ReactiveCommand.Create(x => executeReceived = x); + fixture.TheCommand = ReactiveCommand.Create(x => executeReceived = x, outputScheduler: ImmediateScheduler.Instance); source.OnNext(42); Assert.Equal(42, executeReceived); @@ -647,12 +651,12 @@ public void InvokeCommandAgainstReactiveCommandInTargetPassesTheSpecifiedValueTo [Fact] public void InvokeCommandAgainstReactiveCommandInTargetRespectsCanExecute() { - var executed = false; - var canExecute = new BehaviorSubject(false); - var fixture = new ReactiveCommandHolder(); - var source = new Subject(); + bool executed = false; + BehaviorSubject canExecute = new BehaviorSubject(false); + ReactiveCommandHolder fixture = new ReactiveCommandHolder(); + Subject source = new Subject(); source.InvokeCommand(fixture, x => x.TheCommand); - fixture.TheCommand = ReactiveCommand.Create(_ => executed = true, canExecute); + fixture.TheCommand = ReactiveCommand.Create(_ => executed = true, canExecute, ImmediateScheduler.Instance); source.OnNext(0); Assert.False(executed); @@ -665,12 +669,12 @@ public void InvokeCommandAgainstReactiveCommandInTargetRespectsCanExecute() [Fact] public void InvokeCommandAgainstReactiveCommandInTargetRespectsCanExecuteWindow() { - var executed = false; - var canExecute = new BehaviorSubject(false); - var fixture = new ReactiveCommandHolder(); - var source = new Subject(); + bool executed = false; + BehaviorSubject canExecute = new BehaviorSubject(false); + ReactiveCommandHolder fixture = new ReactiveCommandHolder(); + Subject source = new Subject(); source.InvokeCommand(fixture, x => x.TheCommand); - fixture.TheCommand = ReactiveCommand.Create(_ => executed = true, canExecute); + fixture.TheCommand = ReactiveCommand.Create(_ => executed = true, canExecute, ImmediateScheduler.Instance); source.OnNext(0); Assert.False(executed); @@ -684,16 +688,19 @@ public void InvokeCommandAgainstReactiveCommandInTargetRespectsCanExecuteWindow( [Fact] public void InvokeCommandAgainstReactiveCommandInTargetSwallowsExceptions() { - var count = 0; - var fixture = new ReactiveCommandHolder(); - fixture.TheCommand = ReactiveCommand.Create( - _ => - { - ++count; - throw new InvalidOperationException(); - }); + int count = 0; + ReactiveCommandHolder fixture = new ReactiveCommandHolder + { + TheCommand = ReactiveCommand.Create( + _ => + { + ++count; + throw new InvalidOperationException(); + }, + outputScheduler: ImmediateScheduler.Instance) + }; fixture.TheCommand.ThrownExceptions.Subscribe(); - var source = new Subject(); + Subject source = new Subject(); source.InvokeCommand(fixture, x => x.TheCommand); source.OnNext(0); @@ -705,9 +712,9 @@ public void InvokeCommandAgainstReactiveCommandInTargetSwallowsExceptions() [Fact] public void InvokeCommandAgainstReactiveCommandInvokesTheCommand() { - var executionCount = 0; - var fixture = ReactiveCommand.Create(() => ++executionCount); - var source = new Subject(); + int executionCount = 0; + ReactiveCommand fixture = ReactiveCommand.Create(() => ++executionCount, outputScheduler: ImmediateScheduler.Instance); + Subject source = new Subject(); source.InvokeCommand(fixture); source.OnNext(Unit.Default); @@ -720,9 +727,9 @@ public void InvokeCommandAgainstReactiveCommandInvokesTheCommand() [Fact] public void InvokeCommandAgainstReactiveCommandPassesTheSpecifiedValueToExecute() { - var executeReceived = 0; - var fixture = ReactiveCommand.Create(x => executeReceived = x); - var source = new Subject(); + int executeReceived = 0; + ReactiveCommand fixture = ReactiveCommand.Create(x => executeReceived = x, outputScheduler: ImmediateScheduler.Instance); + Subject source = new Subject(); source.InvokeCommand(fixture); source.OnNext(42); @@ -732,10 +739,10 @@ public void InvokeCommandAgainstReactiveCommandPassesTheSpecifiedValueToExecute( [Fact] public void InvokeCommandAgainstReactiveCommandRespectsCanExecute() { - var executed = false; - var canExecute = new BehaviorSubject(false); - var fixture = ReactiveCommand.Create(() => executed = true, canExecute); - var source = new Subject(); + bool executed = false; + BehaviorSubject canExecute = new BehaviorSubject(false); + ReactiveCommand fixture = ReactiveCommand.Create(() => executed = true, canExecute, ImmediateScheduler.Instance); + Subject source = new Subject(); source.InvokeCommand(fixture); source.OnNext(Unit.Default); @@ -749,10 +756,10 @@ public void InvokeCommandAgainstReactiveCommandRespectsCanExecute() [Fact] public void InvokeCommandAgainstReactiveCommandRespectsCanExecuteWindow() { - var executed = false; - var canExecute = new BehaviorSubject(false); - var fixture = ReactiveCommand.Create(() => executed = true, canExecute); - var source = new Subject(); + bool executed = false; + BehaviorSubject canExecute = new BehaviorSubject(false); + ReactiveCommand fixture = ReactiveCommand.Create(() => executed = true, canExecute, outputScheduler: ImmediateScheduler.Instance); + Subject source = new Subject(); source.InvokeCommand(fixture); source.OnNext(Unit.Default); @@ -767,15 +774,16 @@ public void InvokeCommandAgainstReactiveCommandRespectsCanExecuteWindow() [Fact] public void InvokeCommandAgainstReactiveCommandSwallowsExceptions() { - var count = 0; - var fixture = ReactiveCommand.Create( + int count = 0; + ReactiveCommand fixture = ReactiveCommand.Create( () => { ++count; throw new InvalidOperationException(); - }); + }, + outputScheduler: ImmediateScheduler.Instance); fixture.ThrownExceptions.Subscribe(); - var source = new Subject(); + Subject source = new Subject(); source.InvokeCommand(fixture); source.OnNext(Unit.Default); @@ -787,9 +795,9 @@ public void InvokeCommandAgainstReactiveCommandSwallowsExceptions() [Fact] public void InvokeCommandWorksEvenIfTheSourceIsCold() { - var executionCount = 0; - var fixture = ReactiveCommand.Create(() => ++executionCount); - var source = Observable.Return(Unit.Default); + int executionCount = 0; + ReactiveCommand fixture = ReactiveCommand.Create(() => ++executionCount, outputScheduler: ImmediateScheduler.Instance); + IObservable source = Observable.Return(Unit.Default); source.InvokeCommand(fixture); Assert.Equal(1, executionCount); @@ -798,8 +806,8 @@ public void InvokeCommandWorksEvenIfTheSourceIsCold() [Fact] public void IsExecutingIsBehavioral() { - var fixture = ReactiveCommand.Create(() => Observables.Unit); - fixture.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var isExecuting).Subscribe(); + ReactiveCommand> fixture = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: ImmediateScheduler.Instance); + fixture.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection isExecuting).Subscribe(); Assert.Equal(1, isExecuting.Count); Assert.False(isExecuting[0]); @@ -808,8 +816,8 @@ public void IsExecutingIsBehavioral() [Fact] public void IsExecutingRemainsTrueAsLongAsExecutionPipelineHasNotCompleted() { - var execute = new Subject(); - var fixture = ReactiveCommand.CreateFromObservable(() => execute); + Subject execute = new Subject(); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: ImmediateScheduler.Instance); fixture.Execute().Subscribe(); @@ -829,20 +837,20 @@ public void IsExecutingRemainsTrueAsLongAsExecutionPipelineHasNotCompleted() public void IsExecutingTicksAsExecutionsProgress() { new TestScheduler().With( - sched => + scheduler => { - var execute = Observables.Unit.Delay(TimeSpan.FromSeconds(1), sched); - var fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: sched); - fixture.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var isExecuting).Subscribe(); + IObservable execute = Observables.Unit.Delay(TimeSpan.FromSeconds(1), scheduler); + ReactiveCommand fixture = ReactiveCommand.CreateFromObservable(() => execute, outputScheduler: scheduler); + fixture.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection isExecuting).Subscribe(); fixture.Execute().Subscribe(); - sched.AdvanceByMs(100); + scheduler.AdvanceByMs(100); Assert.Equal(2, isExecuting.Count); Assert.False(isExecuting[0]); Assert.True(isExecuting[1]); - sched.AdvanceByMs(901); + scheduler.AdvanceByMs(901); Assert.Equal(3, isExecuting.Count); Assert.False(isExecuting[2]); @@ -853,15 +861,15 @@ public void IsExecutingTicksAsExecutionsProgress() public void ResultIsTickedThroughSpecifiedScheduler() { new TestScheduler().With( - sched => + scheduler => { - var fixture = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: sched); - fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var results).Subscribe(); + ReactiveCommand> fixture = ReactiveCommand.Create(() => Observables.Unit, outputScheduler: scheduler); + fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out ReadOnlyObservableCollection> results).Subscribe(); fixture.Execute().Subscribe(); Assert.Empty(results); - sched.AdvanceByMs(1); + scheduler.AdvanceByMs(1); Assert.Equal(1, results.Count); }); } @@ -869,25 +877,27 @@ public void ResultIsTickedThroughSpecifiedScheduler() [Fact] public void SynchronousCommandExecuteLazily() { - var executionCount = 0; - var fixture1 = ReactiveCommand.Create(() => { ++executionCount; }); - var fixture2 = ReactiveCommand.Create(_ => { ++executionCount; }); - var fixture3 = ReactiveCommand.Create( + int executionCount = 0; + ReactiveCommand fixture1 = ReactiveCommand.Create(() => { ++executionCount; }, outputScheduler: ImmediateScheduler.Instance); + ReactiveCommand fixture2 = ReactiveCommand.Create(_ => { ++executionCount; }, outputScheduler: ImmediateScheduler.Instance); + ReactiveCommand fixture3 = ReactiveCommand.Create( () => { ++executionCount; return 42; - }); - var fixture4 = ReactiveCommand.Create( + }, + outputScheduler: ImmediateScheduler.Instance); + ReactiveCommand fixture4 = ReactiveCommand.Create( _ => { ++executionCount; return 42; - }); - var execute1 = fixture1.Execute(); - var execute2 = fixture2.Execute(); - var execute3 = fixture3.Execute(); - var execute4 = fixture4.Execute(); + }, + outputScheduler: ImmediateScheduler.Instance); + IObservable execute1 = fixture1.Execute(); + IObservable execute2 = fixture2.Execute(); + IObservable execute3 = fixture3.Execute(); + IObservable execute4 = fixture4.Execute(); Assert.Equal(0, executionCount); @@ -907,12 +917,12 @@ public void SynchronousCommandExecuteLazily() [Fact] public void SynchronousCommandsFailCorrectly() { - var fixture1 = ReactiveCommand.Create(() => { throw new InvalidOperationException(); }); - var fixture2 = ReactiveCommand.Create(_ => { throw new InvalidOperationException(); }); - var fixture3 = ReactiveCommand.Create(() => { throw new InvalidOperationException(); }); - var fixture4 = ReactiveCommand.Create(_ => { throw new InvalidOperationException(); }); + ReactiveCommand fixture1 = ReactiveCommand.Create(() => throw new InvalidOperationException(), outputScheduler: ImmediateScheduler.Instance); + ReactiveCommand fixture2 = ReactiveCommand.Create(_ => throw new InvalidOperationException(), outputScheduler: ImmediateScheduler.Instance); + ReactiveCommand fixture3 = ReactiveCommand.Create(() => throw new InvalidOperationException(), outputScheduler: ImmediateScheduler.Instance); + ReactiveCommand fixture4 = ReactiveCommand.Create(_ => throw new InvalidOperationException(), outputScheduler: ImmediateScheduler.Instance); - var failureCount = 0; + int failureCount = 0; Observable.Merge(fixture1.ThrownExceptions, fixture2.ThrownExceptions, fixture3.ThrownExceptions, fixture4.ThrownExceptions).Subscribe(_ => ++failureCount); fixture1.Execute().Subscribe(_ => { }, _ => { }); @@ -927,222 +937,5 @@ public void SynchronousCommandsFailCorrectly() fixture4.Execute().Subscribe(_ => { }, _ => { }); Assert.Equal(4, failureCount); } - - private class FakeCommand : ICommand - { - public event EventHandler CanExecuteChanged; - - public object CanExecuteParameter { get; private set; } - - public object ExecuteParameter { get; private set; } - - public bool CanExecute(object parameter) - { - CanExecuteParameter = parameter; - return true; - } - - public void Execute(object parameter) - { - ExecuteParameter = parameter; - } - - protected virtual void NotifyCanExecuteChanged(EventArgs e) - { - CanExecuteChanged?.Invoke(this, e); - } - } - - private class ICommandHolder : ReactiveObject - { - private ICommand _theCommand; - - public ICommand TheCommand - { - get => _theCommand; - set => this.RaiseAndSetIfChanged(ref _theCommand, value); - } - } - - private class ReactiveCommandHolder : ReactiveObject - { - private ReactiveCommand _theCommand; - - public ReactiveCommand TheCommand - { - get => _theCommand; - set => this.RaiseAndSetIfChanged(ref _theCommand, value); - } - } - } - - public class CombinedReactiveCommandTest - { - [Fact] - public void CanExecuteIsFalseIfAnyChildCannotExecute() - { - var child1 = ReactiveCommand.Create(() => Observables.Unit); - var child2 = ReactiveCommand.Create(() => Observables.Unit, Observables.False); - var childCommands = new[] { child1, child2 }; - var fixture = ReactiveCommand.CreateCombined(childCommands); - fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var canExecute).Subscribe(); - - Assert.Equal(1, canExecute.Count); - Assert.False(canExecute[0]); - } - - [Fact] - public void CanExecuteIsFalseIfParentCanExecuteIsFalse() - { - var child1 = ReactiveCommand.Create(() => Observables.Unit); - var child2 = ReactiveCommand.Create(() => Observables.Unit); - var childCommands = new[] { child1, child2 }; - var fixture = ReactiveCommand.CreateCombined(childCommands, Observables.False); - fixture.CanExecute.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var canExecute).Subscribe(); - - Assert.Equal(1, canExecute.Count); - Assert.False(canExecute[0]); - } - - [Fact] - public void CanExecuteTicksFailuresInChildCanExecuteThroughThrownExceptions() - { - var canExecuteSubject = new Subject(); - var child1 = ReactiveCommand.Create(() => Observables.Unit); - var child2 = ReactiveCommand.Create(() => Observables.Unit, canExecuteSubject); - var childCommands = new[] { child1, child2 }; - var fixture = ReactiveCommand.CreateCombined(childCommands); - fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var thrownExceptions).Subscribe(); - - canExecuteSubject.OnError(new InvalidOperationException("oops")); - - Assert.Equal(1, thrownExceptions.Count); - Assert.Equal("oops", thrownExceptions[0].Message); - } - - [Fact] - public void CanExecuteTicksFailuresThroughThrownExceptions() - { - var canExecuteSubject = new Subject(); - var child1 = ReactiveCommand.Create(() => Observables.Unit); - var child2 = ReactiveCommand.Create(() => Observables.Unit); - var childCommands = new[] { child1, child2 }; - var fixture = ReactiveCommand.CreateCombined(childCommands, canExecuteSubject); - fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var thrownExceptions).Subscribe(); - - canExecuteSubject.OnError(new InvalidOperationException("oops")); - - Assert.Equal(1, thrownExceptions.Count); - Assert.Equal("oops", thrownExceptions[0].Message); - } - - [Fact] - public void ExceptionsAreDeliveredOnOutputScheduler() - { - new TestScheduler().With( - sched => - { - var child = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException("oops"))); - var childCommands = new[] { child }; - var fixture = ReactiveCommand.CreateCombined(childCommands, outputScheduler: sched); - Exception exception = null; - fixture.ThrownExceptions.Subscribe(ex => exception = ex); - fixture.Execute().Subscribe(_ => { }, _ => { }); - - Assert.Null(exception); - sched.Start(); - Assert.IsType(exception); - }); - } - - [Fact] - public void ExecuteExecutesAllChildCommands() - { - var child1 = ReactiveCommand.Create(() => Observables.Unit); - var child2 = ReactiveCommand.Create(() => Observables.Unit); - var child3 = ReactiveCommand.Create(() => Observables.Unit); - var childCommands = new[] { child1, child2, child3 }; - var fixture = ReactiveCommand.CreateCombined(childCommands); - - fixture.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var isExecuting).Subscribe(); - child1.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var child1IsExecuting).Subscribe(); - child2.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var child2IsExecuting).Subscribe(); - child3.IsExecuting.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var child3IsExecuting).Subscribe(); - - fixture.Execute().Subscribe(); - - Assert.Equal(3, isExecuting.Count); - Assert.False(isExecuting[0]); - Assert.True(isExecuting[1]); - Assert.False(isExecuting[2]); - - Assert.Equal(3, child1IsExecuting.Count); - Assert.False(child1IsExecuting[0]); - Assert.True(child1IsExecuting[1]); - Assert.False(child1IsExecuting[2]); - - Assert.Equal(3, child2IsExecuting.Count); - Assert.False(child2IsExecuting[0]); - Assert.True(child2IsExecuting[1]); - Assert.False(child2IsExecuting[2]); - - Assert.Equal(3, child3IsExecuting.Count); - Assert.False(child3IsExecuting[0]); - Assert.True(child3IsExecuting[1]); - Assert.False(child3IsExecuting[2]); - } - - [Fact] - public void ExecuteTicksErrorsInAnyChildCommandThroughThrownExceptions() - { - var child1 = ReactiveCommand.CreateFromObservable(() => Observables.Unit); - var child2 = ReactiveCommand.CreateFromObservable(() => Observable.Throw(new InvalidOperationException("oops"))); - var childCommands = new[] { child1, child2 }; - var fixture = ReactiveCommand.CreateCombined(childCommands); - fixture.ThrownExceptions.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var thrownExceptions).Subscribe(); - - fixture.Execute().Subscribe(_ => { }, _ => { }); - - Assert.Equal(1, thrownExceptions.Count); - Assert.Equal("oops", thrownExceptions[0].Message); - } - - [Fact] - public void ExecuteTicksThroughTheResults() - { - var child1 = ReactiveCommand.CreateFromObservable(() => Observable.Return(1)); - var child2 = ReactiveCommand.CreateFromObservable(() => Observable.Return(2)); - var childCommands = new[] { child1, child2 }; - var fixture = ReactiveCommand.CreateCombined(childCommands); - - fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var results).Subscribe(); - - fixture.Execute().Subscribe(); - - Assert.Equal(1, results.Count); - Assert.Equal(2, results[0].Count); - Assert.Equal(1, results[0][0]); - Assert.Equal(2, results[0][1]); - } - - [Fact] - public void ResultIsTickedThroughSpecifiedScheduler() - { - new TestScheduler().With( - sched => - { - var child1 = ReactiveCommand.Create(() => Observable.Return(1)); - var child2 = ReactiveCommand.Create(() => Observable.Return(2)); - var childCommands = new[] { child1, child2 }; - var fixture = ReactiveCommand.CreateCombined(childCommands, outputScheduler: sched); - fixture.ToObservableChangeSet(ImmediateScheduler.Instance).Bind(out var results).Subscribe(); - - fixture.Execute().Subscribe(); - Assert.Empty(results); - - sched.AdvanceByMs(1); - Assert.Equal(1, results.Count); - }); - } } } diff --git a/src/ReactiveUI.Tests/OrderedComparerTests.cs b/src/ReactiveUI.Tests/Comparers/OrderedComparerTests.cs similarity index 96% rename from src/ReactiveUI.Tests/OrderedComparerTests.cs rename to src/ReactiveUI.Tests/Comparers/OrderedComparerTests.cs index 8732d9ec98..e7608abcb3 100644 --- a/src/ReactiveUI.Tests/OrderedComparerTests.cs +++ b/src/ReactiveUI.Tests/Comparers/OrderedComparerTests.cs @@ -87,11 +87,13 @@ public void WorksWithAnonymousTypes() } [DebuggerDisplay("{Name}")] - public class Employee + private class Employee { - public string Name; - public int Age; - public int Salary; + public string Name { get; set; } + + public int Age { get; set; } + + public int Salary { get; set; } } } } diff --git a/src/ReactiveUI.Tests/DependencyResolverTests.cs b/src/ReactiveUI.Tests/DependencyResolverTests.cs deleted file mode 100644 index ede4b2b71d..0000000000 --- a/src/ReactiveUI.Tests/DependencyResolverTests.cs +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright (c) 2019 .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. -// See the LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using ReactiveUI; -using ReactiveUI.Tests; -using Splat; -using Xunit; - -namespace ReactiveUI.Tests -{ - public class ExampleViewModel : ReactiveObject - { - } - - public class AnotherViewModel : ReactiveObject - { - } - - public class NeverUsedViewModel : ReactiveObject - { - } - - public class SingleInstanceExampleViewModel : ReactiveObject - { - } - - public class ViewModelWithWeirdName : ReactiveObject - { - } - - public class ExampleWindowViewModel : ReactiveObject - { - } - - public class ExampleWindowView : ReactiveWindow - { - } - - public class ExampleView : ReactiveUI.Winforms.ReactiveUserControl - { - } - - public class AnotherView : ReactiveUI.Winforms.ReactiveUserControl - { - } - - [ViewContract("contract")] - public class ContractExampleView : ReactiveUI.Winforms.ReactiveUserControl - { - } - - [SingleInstanceView] - public class NeverUsedView : ReactiveUI.Winforms.ReactiveUserControl - { - public static int Instances; - - public NeverUsedView() - { - Instances++; - } - } - - [SingleInstanceView] - public class SingleInstanceExampleView : ReactiveUI.Winforms.ReactiveUserControl - { - public static int Instances; - - public SingleInstanceExampleView() - { - Instances++; - } - } - - [ViewContract("contract")] - [SingleInstanceView] - public class SingleInstanceWithContractExampleView : ReactiveUI.Winforms.ReactiveUserControl - { - public static int Instances; - - public SingleInstanceWithContractExampleView() - { - Instances++; - } - } - - public class ViewWithoutMatchingName : ReactiveUI.Winforms.ReactiveUserControl - { - } - - public sealed class DependencyResolverTests : IDisposable - { - private readonly IMutableDependencyResolver _resolver; - - public DependencyResolverTests() - { - _resolver = new ModernDependencyResolver(); - _resolver.InitializeSplat(); - _resolver.InitializeReactiveUI(); - _resolver.RegisterViewsForViewModels(GetType().Assembly); - } - - [WpfFact] - public void RegisterViewsForViewModelShouldRegisterAllViews() - { - using (_resolver.WithResolver()) - { - Assert.Single(_resolver.GetServices>()); - Assert.Single(_resolver.GetServices>()); - Assert.Single(_resolver.GetServices>()); - Assert.Single(_resolver.GetServices>()); - } - } - - [WpfFact] - public void RegisterViewsForViewModelShouldIncludeContracts() - { - using (_resolver.WithResolver()) - { - Assert.Single(_resolver.GetServices(typeof(IViewFor), "contract")); - } - } - - [WpfFact] - public void NonContractRegistrationsShouldResolveCorrectly() - { - using (_resolver.WithResolver()) - { - Assert.IsType(_resolver.GetService>()); - } - } - - [WpfFact] - public void ContractRegistrationsShouldResolveCorrectly() - { - using (_resolver.WithResolver()) - { - Assert.IsType(_resolver.GetService(typeof(IViewFor), "contract")); - } - } - - [Fact] - public void SingleInstanceViewsShouldOnlyBeInstantiatedWhenRequested() - { - using (_resolver.WithResolver()) - { - Assert.Equal(0, NeverUsedView.Instances); - } - } - - [WpfFact] - public void SingleInstanceViewsShouldOnlyBeInstantiatedOnce() - { - using (_resolver.WithResolver()) - { - Assert.Equal(0, SingleInstanceExampleView.Instances); - - var instance = _resolver.GetService(typeof(IViewFor)); - Assert.Equal(1, SingleInstanceExampleView.Instances); - - var instance2 = _resolver.GetService(typeof(IViewFor)); - Assert.Equal(1, SingleInstanceExampleView.Instances); - - Assert.Same(instance, instance2); - } - } - - [WpfFact] - public void SingleInstanceViewsWithContractShouldResolveCorrectly() - { - using (_resolver.WithResolver()) - { - Assert.Equal(0, SingleInstanceWithContractExampleView.Instances); - - var instance = _resolver.GetService(typeof(IViewFor), "contract"); - Assert.Equal(1, SingleInstanceWithContractExampleView.Instances); - - var instance2 = _resolver.GetService(typeof(IViewFor), "contract"); - Assert.Equal(1, SingleInstanceWithContractExampleView.Instances); - - Assert.Same(instance, instance2); - } - } - - [Fact] - public void AllDefaultServicesShouldBeRegistered() - { - using (_resolver.WithResolver()) - { - foreach (var shouldRegistered in GetServicesThatShouldBeRegistered()) - { - IEnumerable resolvedServices = _resolver.GetServices(shouldRegistered.Key); - Assert.Equal(shouldRegistered.Value.Count, resolvedServices.Count()); - foreach (Type implementationType in shouldRegistered.Value) - { - var isRegistered = resolvedServices.Any(rs => rs.GetType() == implementationType); - Assert.Equal(true, isRegistered); - } - } - } - } - - public void Dispose() - { - _resolver?.Dispose(); - } - - private static Dictionary> GetServicesThatShouldBeRegistered() - { - Dictionary> serviceTypeToImplementationTypes = new Dictionary>(); - - new Registrations().Register((factory, serviceType) => - { - if (serviceTypeToImplementationTypes.TryGetValue(serviceType, out List implementationTypes) == false) - { - implementationTypes = new List(); - serviceTypeToImplementationTypes.Add(serviceType, implementationTypes); - } - - implementationTypes.Add(factory().GetType()); - }); - - new PlatformRegistrations().Register((factory, serviceType) => - { - if (serviceTypeToImplementationTypes.TryGetValue(serviceType, out List implementationTypes) == false) - { - implementationTypes = new List(); - serviceTypeToImplementationTypes.Add(serviceType, implementationTypes); - } - - implementationTypes.Add(factory().GetType()); - }); - - var typeNames = new[] - { - "ReactiveUI.XamForms.Registrations, ReactiveUI.XamForms", - "ReactiveUI.Winforms.Registrations, ReactiveUI.Winforms", - "ReactiveUI.Wpf.Registrations, ReactiveUI.Wpf" - }; - - typeNames.ForEach(typeName => GetRegistrationsForPlatform(typeName, serviceTypeToImplementationTypes)); - - return serviceTypeToImplementationTypes; - } - - private static void GetRegistrationsForPlatform(string typeName, Dictionary> serviceTypeToImplementationTypes) - { - Type platformRegistrationsType = Type.GetType(typeName); - if (platformRegistrationsType != null) - { - var platformRegistrations = Activator.CreateInstance(platformRegistrationsType); - System.Reflection.MethodInfo register = platformRegistrationsType.GetMethod("Register"); - var registerParameter = new Action, Type>((factory, serviceType) => - { - if (serviceTypeToImplementationTypes.TryGetValue(serviceType, out List implementationTypes) == false) - { - implementationTypes = new List(); - serviceTypeToImplementationTypes.Add(serviceType, implementationTypes); - } - - implementationTypes.Add(factory().GetType()); - }); - - register.Invoke(platformRegistrations, new object[] { registerParameter }); - } - } - } -} diff --git a/src/ReactiveUI.Tests/InteractionsTest.cs b/src/ReactiveUI.Tests/InteractionsTest.cs index 25043f4448..90aa426aaf 100755 --- a/src/ReactiveUI.Tests/InteractionsTest.cs +++ b/src/ReactiveUI.Tests/InteractionsTest.cs @@ -23,7 +23,7 @@ public void RegisterNullHandlerShouldCauseException() var interaction = new Interaction(); Assert.Throws(() => interaction.RegisterHandler((Action>)null)); - Assert.Throws(() => interaction.RegisterHandler((Func, Task>)null)); + Assert.Throws(() => interaction.RegisterHandler(null)); Assert.Throws(() => interaction.RegisterHandler((Func, IObservable>)null)); } diff --git a/src/ReactiveUI.Tests/ViewLocatorTests.cs b/src/ReactiveUI.Tests/Locator/DefaultViewLocatorTests.cs similarity index 82% rename from src/ReactiveUI.Tests/ViewLocatorTests.cs rename to src/ReactiveUI.Tests/Locator/DefaultViewLocatorTests.cs index 5809344593..cc77be6832 100644 --- a/src/ReactiveUI.Tests/ViewLocatorTests.cs +++ b/src/ReactiveUI.Tests/Locator/DefaultViewLocatorTests.cs @@ -9,109 +9,6 @@ namespace ReactiveUI.Tests { - public interface IFooViewModel - { - } - - public interface IBarViewModel - { - } - - public interface IFooView : IViewFor - { - } - - public interface IStrangeInterfaceNotFollowingConvention - { - } - - public interface IRoutableFooViewModel : IRoutableViewModel - { - } - - public class FooViewModel : ReactiveObject, IFooViewModel - { - } - - public class FooViewModelWithWeirdName : ReactiveObject, IFooViewModel - { - } - - public class BarViewModel : ReactiveObject, IBarViewModel - { - } - - public class FooView : IFooView - { - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (IFooViewModel)value; - } - - public IFooViewModel ViewModel { get; set; } - } - - public class BarView : IViewFor - { - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (IBarViewModel)value; - } - - public IBarViewModel ViewModel { get; set; } - } - - public class FooWithWeirdConvention : IFooView - { - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (IFooViewModel)value; - } - - public IFooViewModel ViewModel { get; set; } - } - - public class FooThatThrowsView : IFooView - { - public FooThatThrowsView() - { - throw new InvalidOperationException("This is a test failure."); - } - - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (IFooViewModel)value; - } - - public IFooViewModel ViewModel { get; set; } - } - - public class StrangeClassNotFollowingConvention : IStrangeInterfaceNotFollowingConvention - { - } - - public class RoutableFooViewModel : ReactiveObject, IRoutableFooViewModel - { - public IScreen HostScreen { get; set; } - - public string UrlPathSegment { get; set; } - } - - public class RoutableFooView : IViewFor - { - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (IRoutableFooViewModel)value; - } - - public IRoutableFooViewModel ViewModel { get; set; } - } - public class DefaultViewLocatorTests { [Fact] diff --git a/src/ReactiveUI.Tests/Locator/Mocks/BarView.cs b/src/ReactiveUI.Tests/Locator/Mocks/BarView.cs new file mode 100644 index 0000000000..d548118004 --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/BarView.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class BarView : IViewFor + { + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (IBarViewModel)value; + } + + public IBarViewModel ViewModel { get; set; } + } +} diff --git a/src/ReactiveUI.Tests/Locator/Mocks/BarViewModel.cs b/src/ReactiveUI.Tests/Locator/Mocks/BarViewModel.cs new file mode 100644 index 0000000000..4425b47e5c --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/BarViewModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class BarViewModel : ReactiveObject, IBarViewModel + { + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Locator/Mocks/FooThatThrowsView.cs b/src/ReactiveUI.Tests/Locator/Mocks/FooThatThrowsView.cs new file mode 100644 index 0000000000..81a075f4a0 --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/FooThatThrowsView.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; + +namespace ReactiveUI.Tests +{ + public class FooThatThrowsView : IFooView + { + public FooThatThrowsView() + { + throw new InvalidOperationException("This is a test failure."); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (IFooViewModel)value; + } + + public IFooViewModel ViewModel { get; set; } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Locator/Mocks/FooView.cs b/src/ReactiveUI.Tests/Locator/Mocks/FooView.cs new file mode 100644 index 0000000000..ca7c3b31d8 --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/FooView.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class FooView : IFooView + { + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (IFooViewModel)value; + } + + public IFooViewModel ViewModel { get; set; } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Locator/Mocks/FooViewModel.cs b/src/ReactiveUI.Tests/Locator/Mocks/FooViewModel.cs new file mode 100644 index 0000000000..1c99255c25 --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/FooViewModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class FooViewModel : ReactiveObject, IFooViewModel + { + } +} diff --git a/src/ReactiveUI.Tests/Locator/Mocks/FooViewModelWithWeirdName.cs b/src/ReactiveUI.Tests/Locator/Mocks/FooViewModelWithWeirdName.cs new file mode 100644 index 0000000000..472b594561 --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/FooViewModelWithWeirdName.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class FooViewModelWithWeirdName : ReactiveObject, IFooViewModel + { + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Locator/Mocks/FooWithWeirdConvention.cs b/src/ReactiveUI.Tests/Locator/Mocks/FooWithWeirdConvention.cs new file mode 100644 index 0000000000..669796a01d --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/FooWithWeirdConvention.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class FooWithWeirdConvention : IFooView + { + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (IFooViewModel)value; + } + + public IFooViewModel ViewModel { get; set; } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Locator/Mocks/IBarViewModel.cs b/src/ReactiveUI.Tests/Locator/Mocks/IBarViewModel.cs new file mode 100644 index 0000000000..952dafc592 --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/IBarViewModel.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +namespace ReactiveUI.Tests +{ + [SuppressMessage("Design", "CA1040: Avoid empty interfaces", Justification = "Deliberate empty interface.")] + public interface IBarViewModel + { + } +} diff --git a/src/ReactiveUI.Tests/Locator/Mocks/IFooView.cs b/src/ReactiveUI.Tests/Locator/Mocks/IFooView.cs new file mode 100644 index 0000000000..19a76ca7bd --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/IFooView.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public interface IFooView : IViewFor + { + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Locator/Mocks/IFooViewModel.cs b/src/ReactiveUI.Tests/Locator/Mocks/IFooViewModel.cs new file mode 100644 index 0000000000..ff84c87bbd --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/IFooViewModel.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +namespace ReactiveUI.Tests +{ + [SuppressMessage("Design", "CA1040: Avoid empty interfaces", Justification = "Deliberate empty interface.")] + public interface IFooViewModel + { + } +} diff --git a/src/ReactiveUI.Tests/Locator/Mocks/IRoutableFooViewModel.cs b/src/ReactiveUI.Tests/Locator/Mocks/IRoutableFooViewModel.cs new file mode 100644 index 0000000000..2d480c98bd --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/IRoutableFooViewModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public interface IRoutableFooViewModel : IRoutableViewModel + { + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Locator/Mocks/IStrangeInterfaceNotFollowingConvention.cs b/src/ReactiveUI.Tests/Locator/Mocks/IStrangeInterfaceNotFollowingConvention.cs new file mode 100644 index 0000000000..9fee1565ff --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/IStrangeInterfaceNotFollowingConvention.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +namespace ReactiveUI.Tests +{ + [SuppressMessage("Design", "CA1040: Avoid empty interfaces", Justification = "Deliberate empty interface.")] + public interface IStrangeInterfaceNotFollowingConvention + { + } +} diff --git a/src/ReactiveUI.Tests/Locator/Mocks/RoutableFooView.cs b/src/ReactiveUI.Tests/Locator/Mocks/RoutableFooView.cs new file mode 100644 index 0000000000..a65a85ed70 --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/RoutableFooView.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class RoutableFooView : IViewFor + { + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (IRoutableFooViewModel)value; + } + + public IRoutableFooViewModel ViewModel { get; set; } + } +} diff --git a/src/ReactiveUI.Tests/Locator/Mocks/RoutableFooViewModel.cs b/src/ReactiveUI.Tests/Locator/Mocks/RoutableFooViewModel.cs new file mode 100644 index 0000000000..64e392bd71 --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/RoutableFooViewModel.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class RoutableFooViewModel : ReactiveObject, IRoutableFooViewModel + { + public IScreen HostScreen { get; set; } + + public string UrlPathSegment { get; set; } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Locator/Mocks/StrangeClassNotFollowingConvention.cs b/src/ReactiveUI.Tests/Locator/Mocks/StrangeClassNotFollowingConvention.cs new file mode 100644 index 0000000000..1bc412b00b --- /dev/null +++ b/src/ReactiveUI.Tests/Locator/Mocks/StrangeClassNotFollowingConvention.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class StrangeClassNotFollowingConvention : IStrangeInterfaceNotFollowingConvention + { + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Mocks/AnotherViewModel.cs b/src/ReactiveUI.Tests/Mocks/AnotherViewModel.cs new file mode 100644 index 0000000000..3d8518c457 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/AnotherViewModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class AnotherViewModel : ReactiveObject + { + } +} diff --git a/src/ReactiveUI.Tests/Mocks/CommandBindViewModel.cs b/src/ReactiveUI.Tests/Mocks/CommandBindViewModel.cs new file mode 100644 index 0000000000..c214dc18c2 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/CommandBindViewModel.cs @@ -0,0 +1,48 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests +{ + public class CommandBindViewModel : ReactiveObject + { + private ReactiveCommand _Command1; + private ReactiveCommand _Command2; + + private int _value; + + public CommandBindViewModel() + { + Command1 = ReactiveCommand.Create(_ => Unit.Default); + Command2 = ReactiveCommand.Create(() => { }); + } + + public ReactiveCommand Command1 + { + get => _Command1; + set => this.RaiseAndSetIfChanged(ref _Command1, value); + } + + public ReactiveCommand Command2 + { + get => _Command2; + set => this.RaiseAndSetIfChanged(ref _Command2, value); + } + + public FakeNestedViewModel NestedViewModel { get; set; } + + public int Value + { + get => _value; + set => this.RaiseAndSetIfChanged(ref _value, value); + } + } +} diff --git a/src/ReactiveUI.Tests/Mocks/ExampleViewModel.cs b/src/ReactiveUI.Tests/Mocks/ExampleViewModel.cs new file mode 100644 index 0000000000..a9847f6a9f --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/ExampleViewModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class ExampleViewModel : ReactiveObject + { + } +} diff --git a/src/ReactiveUI.Tests/Mocks/ExampleWindowViewModel.cs b/src/ReactiveUI.Tests/Mocks/ExampleWindowViewModel.cs new file mode 100644 index 0000000000..58d65ec8f0 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/ExampleWindowViewModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class ExampleWindowViewModel : ReactiveObject + { + } +} diff --git a/src/ReactiveUI.Tests/Mocks/FakeCollectionModel.cs b/src/ReactiveUI.Tests/Mocks/FakeCollectionModel.cs new file mode 100644 index 0000000000..171a31edc5 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/FakeCollectionModel.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests +{ + public class FakeCollectionModel : ReactiveObject + { + private bool _isHidden; + + private int _someNumber; + + public bool IsHidden + { + get => _isHidden; + set => this.RaiseAndSetIfChanged(ref _isHidden, value); + } + + public int SomeNumber + { + get => _someNumber; + set => this.RaiseAndSetIfChanged(ref _someNumber, value); + } + } +} diff --git a/src/ReactiveUI.Tests/Mocks/FakeCollectionViewModel.cs b/src/ReactiveUI.Tests/Mocks/FakeCollectionViewModel.cs new file mode 100644 index 0000000000..b6ed712a28 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/FakeCollectionViewModel.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests +{ + public class FakeCollectionViewModel : ReactiveObject + { + private readonly ObservableAsPropertyHelper _numberAsString; + + public FakeCollectionViewModel(FakeCollectionModel model) + { + Model = model; + + this.WhenAny(x => x.Model.SomeNumber, x => x.Value.ToString()).ToProperty(this, x => x.NumberAsString, out _numberAsString); + } + + public FakeCollectionModel Model { get; protected set; } + + public string NumberAsString => _numberAsString.Value; + } +} diff --git a/src/ReactiveUI.Tests/Mocks/FakeNestedViewModel.cs b/src/ReactiveUI.Tests/Mocks/FakeNestedViewModel.cs new file mode 100644 index 0000000000..500ddc7f8d --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/FakeNestedViewModel.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests +{ + public class FakeNestedViewModel : ReactiveObject + { + public FakeNestedViewModel() + { + NestedCommand = ReactiveCommand.Create(() => { }); + } + + public ReactiveCommand NestedCommand { get; protected set; } + } +} diff --git a/src/ReactiveUI.Tests/Mocks/FakeViewModel.cs b/src/ReactiveUI.Tests/Mocks/FakeViewModel.cs new file mode 100644 index 0000000000..e3d1d6c882 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/FakeViewModel.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests +{ + public class FakeViewModel : ReactiveObject + { + public FakeViewModel() + { + Cmd = ReactiveCommand.Create(() => { }); + } + + public ReactiveCommand Cmd { get; protected set; } + } +} diff --git a/src/ReactiveUI.Tests/Mocks/NeverUsedViewModel.cs b/src/ReactiveUI.Tests/Mocks/NeverUsedViewModel.cs new file mode 100644 index 0000000000..0b844b6609 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/NeverUsedViewModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class NeverUsedViewModel : ReactiveObject + { + } +} diff --git a/src/ReactiveUI.Tests/Mocks/PropertyBindModel.cs b/src/ReactiveUI.Tests/Mocks/PropertyBindModel.cs new file mode 100644 index 0000000000..b017961ab9 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/PropertyBindModel.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests +{ + public class PropertyBindModel + { + public int AThing { get; set; } + + public string AnotherThing { get; set; } + } +} diff --git a/src/ReactiveUI.Tests/Mocks/PropertyBindViewModel.cs b/src/ReactiveUI.Tests/Mocks/PropertyBindViewModel.cs new file mode 100644 index 0000000000..7614d6fa91 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/PropertyBindViewModel.cs @@ -0,0 +1,75 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using DynamicData.Binding; + +namespace ReactiveUI.Tests +{ + public class PropertyBindViewModel : ReactiveObject + { + private string _Property1; + private PropertyBindModel _Model; + private int _Property2; + private double _JustADouble; + private decimal _JustADecimal; + private double? _NullableDouble; + private int _JustAInt32; + + public PropertyBindViewModel(PropertyBindModel model = null) + { + Model = model ?? new PropertyBindModel { AThing = 42, AnotherThing = "Baz" }; + SomeCollectionOfStrings = new ObservableCollectionExtended(new[] { "Foo", "Bar" }); + } + + public string Property1 + { + get => _Property1; + set => this.RaiseAndSetIfChanged(ref _Property1, value); + } + + public int Property2 + { + get => _Property2; + set => this.RaiseAndSetIfChanged(ref _Property2, value); + } + + public double JustADouble + { + get => _JustADouble; + set => this.RaiseAndSetIfChanged(ref _JustADouble, value); + } + + public decimal JustADecimal + { + get => _JustADecimal; + set => this.RaiseAndSetIfChanged(ref _JustADecimal, value); + } + + public int JustAInt32 + { + get => _JustAInt32; + set => this.RaiseAndSetIfChanged(ref _JustAInt32, value); + } + + public double? NullableDouble + { + get => _NullableDouble; + set => this.RaiseAndSetIfChanged(ref _NullableDouble, value); + } + + public ObservableCollectionExtended SomeCollectionOfStrings { get; } + + public PropertyBindModel Model + { + get => _Model; + set => this.RaiseAndSetIfChanged(ref _Model, value); + } + } +} diff --git a/src/ReactiveUI.Tests/TestUtilsTest.cs b/src/ReactiveUI.Tests/Mocks/SingleInstanceExampleViewModel.cs similarity index 71% rename from src/ReactiveUI.Tests/TestUtilsTest.cs rename to src/ReactiveUI.Tests/Mocks/SingleInstanceExampleViewModel.cs index 4e32f9565d..985d2eb54e 100644 --- a/src/ReactiveUI.Tests/TestUtilsTest.cs +++ b/src/ReactiveUI.Tests/Mocks/SingleInstanceExampleViewModel.cs @@ -3,14 +3,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Threading.Tasks; -using Microsoft.Reactive.Testing; -using ReactiveUI.Testing; -using Xunit; - namespace ReactiveUI.Tests { - public class TestUtilsTest + public class SingleInstanceExampleViewModel : ReactiveObject { } } diff --git a/src/ReactiveUI.Tests/Mocks/ViewModelWithWeirdName.cs b/src/ReactiveUI.Tests/Mocks/ViewModelWithWeirdName.cs new file mode 100644 index 0000000000..9698cad876 --- /dev/null +++ b/src/ReactiveUI.Tests/Mocks/ViewModelWithWeirdName.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class ViewModelWithWeirdName : ReactiveObject + { + } +} diff --git a/src/ReactiveUI.Tests/ObservableAsPropertyHelper/Mocks/OAPHIndexerTestFixture.cs b/src/ReactiveUI.Tests/ObservableAsPropertyHelper/Mocks/OAPHIndexerTestFixture.cs new file mode 100644 index 0000000000..4fc82636f4 --- /dev/null +++ b/src/ReactiveUI.Tests/ObservableAsPropertyHelper/Mocks/OAPHIndexerTestFixture.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + internal class OAPHIndexerTestFixture : ReactiveObject + { + private string _text; + + public OAPHIndexerTestFixture() + { + var temp = this.WhenAnyValue(f => f.Text) + .ToProperty(this, f => f["Whatever"]) + .Value; + } + + public string Text + { + get => _text; + set => this.RaiseAndSetIfChanged(ref _text, value); + } + + public string this[string propertyName] => string.Empty; + } +} diff --git a/src/ReactiveUI.Tests/ObservableAsPropertyHelperTest.cs b/src/ReactiveUI.Tests/ObservableAsPropertyHelper/ObservableAsPropertyHelperTest.cs old mode 100755 new mode 100644 similarity index 74% rename from src/ReactiveUI.Tests/ObservableAsPropertyHelperTest.cs rename to src/ReactiveUI.Tests/ObservableAsPropertyHelper/ObservableAsPropertyHelperTest.cs index 25991f8df5..2127bbea40 --- a/src/ReactiveUI.Tests/ObservableAsPropertyHelperTest.cs +++ b/src/ReactiveUI.Tests/ObservableAsPropertyHelper/ObservableAsPropertyHelperTest.cs @@ -14,7 +14,6 @@ using DynamicData; using Microsoft.Reactive.Testing; using ReactiveUI.Testing; -using Splat; using Xunit; namespace ReactiveUI.Tests @@ -301,46 +300,6 @@ public void ToProperty_NameOf_ValidValuesProduced(string[] testWords, string[] f } } - [Fact] - public void ToPropertyShouldSubscribeOnlyOnce() - { - using (ProductionMode.Set()) - { - var f = new RaceConditionFixture(); - - // This line is important because it triggers connect to - // be called recursively thus cause the subscription - // to be called twice. Not sure if this is a reactive UI - // or RX bug. - f.PropertyChanged += (e, s) => Debug.WriteLine(f.A); - - // Trigger subscription to the underlying observable. - Assert.Equal(true, f.A); - - Assert.Equal(1, f.Count); - } - } - - [Fact] - public void ToProperty_NameOf_ShouldSubscribeOnlyOnce() - { - using (ProductionMode.Set()) - { - var f = new RaceConditionNameOfFixture(); - - // This line is important because it triggers connect to - // be called recursively thus cause the subscription - // to be called twice. Not sure if this is a reactive UI - // or RX bug. - f.PropertyChanged += (e, s) => Debug.WriteLine(f.A); - - // Trigger subscription to the underlying observable. - Assert.Equal(true, f.A); - - Assert.Equal(1, f.Count); - } - } - [Fact] public void ToProperty_GivenIndexer_NotifiesOnExpectedPropertyName() { @@ -360,85 +319,4 @@ public void ToProperty_GivenIndexer_NotifiesOnExpectedPropertyName() }); } } - - public class RaceConditionFixture : ReactiveObject - { - public ObservableAsPropertyHelper _A; - public int Count; - - public RaceConditionFixture() - { - // We need to generate a value on subscription - // which is different than the default value. - // This triggers the property change firing - // upon subscription in the ObservableAsPropertyHelper - // constructor. - Observables - .True - .Do(_ => Count++) - .ToProperty(this, x => x.A, out _A); - } - - public bool A => _A.Value; - } - - public class RaceConditionNameOfFixture : ReactiveObject - { - public ObservableAsPropertyHelper _A; - public int Count; - - public RaceConditionNameOfFixture() - { - // We need to generate a value on subscription - // which is different than the default value. - // This triggers the property change firing - // upon subscription in the ObservableAsPropertyHelper - // constructor. - Observables - .True - .Do(_ => Count++) - .ToProperty(this, nameof(A), out _A); - } - - public bool A => _A.Value; - } - - internal class ProductionMode : IModeDetector - { - public static IDisposable Set() - { - ModeDetector.OverrideModeDetector(new ProductionMode()); - return Disposable.Create(() => ModeDetector.OverrideModeDetector(new PlatformModeDetector())); - } - - public bool? InUnitTestRunner() - { - return false; - } - - public bool? InDesignMode() - { - return false; - } - } - - internal class OAPHIndexerTestFixture : ReactiveObject - { - private string _text; - - public OAPHIndexerTestFixture() - { - var temp = this.WhenAnyValue(f => f.Text) - .ToProperty(this, f => f["Whatever"]) - .Value; - } - - public string Text - { - get => _text; - set => this.RaiseAndSetIfChanged(ref _text, value); - } - - public string this[string propertyName] => string.Empty; - } } diff --git a/src/ReactiveUI.Tests/ObservedChanged/Mocks/NewGameViewModel.cs b/src/ReactiveUI.Tests/ObservedChanged/Mocks/NewGameViewModel.cs new file mode 100644 index 0000000000..dd27e790fb --- /dev/null +++ b/src/ReactiveUI.Tests/ObservedChanged/Mocks/NewGameViewModel.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using DynamicData.Binding; + +namespace ReactiveUI.Tests +{ + public class NewGameViewModel : ReactiveObject + { + private string _newPlayerName; + + public NewGameViewModel() + { + Players = new ObservableCollectionExtended(); + + var canStart = Players.ToObservableChangeSet().CountChanged().Select(_ => Players.Count >= 3); + StartGame = ReactiveCommand.Create(() => { }, canStart); + RandomizeOrder = ReactiveCommand.Create( + () => + { + using (Players.SuspendNotifications()) + { + var r = new Random(); + var newOrder = Players.OrderBy(x => r.NextDouble()).ToList(); + Players.Clear(); + Players.AddRange(newOrder); + } + }, + canStart); + + RemovePlayer = ReactiveCommand.Create(player => Players.Remove(player)); + var canAddPlayer = this.WhenAnyValue( + x => x.Players.Count, + x => x.NewPlayerName, + (count, newPlayerName) => count < 7 && !string.IsNullOrWhiteSpace(newPlayerName) && !Players.Contains(newPlayerName)); + AddPlayer = ReactiveCommand.Create( + () => + { + Players.Add(NewPlayerName.Trim()); + NewPlayerName = string.Empty; + }, + canAddPlayer); + } + + public ObservableCollectionExtended Players { get; } + + public ReactiveCommand AddPlayer { get; } + + public ReactiveCommand RemovePlayer { get; } + + public ReactiveCommand StartGame { get; } + + public ReactiveCommand RandomizeOrder { get; } + + public string NewPlayerName + { + get => _newPlayerName; + set => this.RaiseAndSetIfChanged(ref _newPlayerName, value); + } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/ObservedChanged/NewGameViewModelTests.cs b/src/ReactiveUI.Tests/ObservedChanged/NewGameViewModelTests.cs new file mode 100644 index 0000000000..e98214154a --- /dev/null +++ b/src/ReactiveUI.Tests/ObservedChanged/NewGameViewModelTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Linq; +using Xunit; + +namespace ReactiveUI.Tests +{ + public class NewGameViewModelTests + { + private readonly NewGameViewModel _viewmodel; + + public NewGameViewModelTests() + { + _viewmodel = new NewGameViewModel(); + } + + [Fact] + public void CanAddUpToSevenPlayers() + { + foreach (var i in Enumerable.Range(1, 7)) + { + _viewmodel.NewPlayerName = "Player" + i; + _viewmodel.AddPlayer.Execute().Subscribe(); + Assert.Equal(i, _viewmodel.Players.Count); + } + } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/ObservedChangedMixinTest.cs b/src/ReactiveUI.Tests/ObservedChanged/ObservedChangedMixinTest.cs old mode 100755 new mode 100644 similarity index 68% rename from src/ReactiveUI.Tests/ObservedChangedMixinTest.cs rename to src/ReactiveUI.Tests/ObservedChanged/ObservedChangedMixinTest.cs index 37085335e7..f730648955 --- a/src/ReactiveUI.Tests/ObservedChangedMixinTest.cs +++ b/src/ReactiveUI.Tests/ObservedChanged/ObservedChangedMixinTest.cs @@ -5,12 +5,8 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; -using System.Reactive; -using System.Reactive.Linq; using System.Reactive.Subjects; -using DynamicData.Binding; using Microsoft.Reactive.Testing; using ReactiveUI.Testing; using Xunit; @@ -169,79 +165,4 @@ public void BindToStackOverFlowTest() }); } } - - public class NewGameViewModelTests - { - private readonly NewGameViewModel _viewmodel; - - public NewGameViewModelTests() - { - _viewmodel = new NewGameViewModel(); - } - - [Fact] - public void CanAddUpToSevenPlayers() - { - foreach (var i in Enumerable.Range(1, 7)) - { - _viewmodel.NewPlayerName = "Player" + i; - _viewmodel.AddPlayer.Execute().Subscribe(); - Assert.Equal(i, _viewmodel.Players.Count); - } - } - } - - public class NewGameViewModel : ReactiveObject - { - private string _newPlayerName; - - public NewGameViewModel() - { - Players = new ObservableCollectionExtended(); - - var canStart = Players.ToObservableChangeSet().CountChanged().Select(_ => Players.Count >= 3); - StartGame = ReactiveCommand.Create(() => { }, canStart); - RandomizeOrder = ReactiveCommand.Create( - () => - { - using (Players.SuspendNotifications()) - { - var r = new Random(); - var newOrder = Players.OrderBy(x => r.NextDouble()).ToList(); - Players.Clear(); - Players.AddRange(newOrder); - } - }, - canStart); - - RemovePlayer = ReactiveCommand.Create(player => Players.Remove(player)); - var canAddPlayer = this.WhenAnyValue( - x => x.Players.Count, - x => x.NewPlayerName, - (count, newPlayerName) => count < 7 && !string.IsNullOrWhiteSpace(newPlayerName) && !Players.Contains(newPlayerName)); - AddPlayer = ReactiveCommand.Create( - () => - { - Players.Add(NewPlayerName.Trim()); - NewPlayerName = string.Empty; - }, - canAddPlayer); - } - - public ObservableCollectionExtended Players { get; } - - public ReactiveCommand AddPlayer { get; } - - public ReactiveCommand RemovePlayer { get; } - - public ReactiveCommand StartGame { get; } - - public ReactiveCommand RandomizeOrder { get; } - - public string NewPlayerName - { - get => _newPlayerName; - set => this.RaiseAndSetIfChanged(ref _newPlayerName, value); - } - } } diff --git a/src/ReactiveUI.Tests/Platforms/common-gui/Mocks/RaceConditionFixture.cs b/src/ReactiveUI.Tests/Platforms/common-gui/Mocks/RaceConditionFixture.cs new file mode 100644 index 0000000000..b80c005e06 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/common-gui/Mocks/RaceConditionFixture.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Reactive.Linq; + +namespace ReactiveUI.Tests +{ + public class RaceConditionFixture : ReactiveObject + { + private ObservableAsPropertyHelper _A; + + public RaceConditionFixture() + { + // We need to generate a value on subscription + // which is different than the default value. + // This triggers the property change firing + // upon subscription in the ObservableAsPropertyHelper + // constructor. + Observables + .True + .Do(_ => Count++) + .ToProperty(this, x => x.A, out _A); + } + + public int Count { get; set; } + + public bool A => _A.Value; + } +} diff --git a/src/ReactiveUI.Tests/Platforms/common-gui/Mocks/RaceConditionNameOfFixture.cs b/src/ReactiveUI.Tests/Platforms/common-gui/Mocks/RaceConditionNameOfFixture.cs new file mode 100644 index 0000000000..013a9c81f5 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/common-gui/Mocks/RaceConditionNameOfFixture.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Reactive.Linq; + +namespace ReactiveUI.Tests +{ + public class RaceConditionNameOfFixture : ReactiveObject + { + private ObservableAsPropertyHelper _A; + + public RaceConditionNameOfFixture() + { + // We need to generate a value on subscription + // which is different than the default value. + // This triggers the property change firing + // upon subscription in the ObservableAsPropertyHelper + // constructor. + Observables + .True + .Do(_ => Count++) + .ToProperty(this, nameof(A), out _A); + } + + public int Count { get; set; } + + public bool A => _A.Value; + } +} diff --git a/src/ReactiveUI.Tests/Platforms/common-gui/ObservableAsPropertyHelperModeTests.cs b/src/ReactiveUI.Tests/Platforms/common-gui/ObservableAsPropertyHelperModeTests.cs new file mode 100644 index 0000000000..32f7db9729 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/common-gui/ObservableAsPropertyHelperModeTests.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Diagnostics; +using Xunit; + +namespace ReactiveUI.Tests +{ + public class ObservableAsPropertyHelperModeTests + { + [Fact] + public void ToPropertyShouldSubscribeOnlyOnce() + { + using (ProductionMode.Set()) + { + var f = new RaceConditionFixture(); + + // This line is important because it triggers connect to + // be called recursively thus cause the subscription + // to be called twice. Not sure if this is a reactive UI + // or RX bug. + f.PropertyChanged += (e, s) => Debug.WriteLine(f.A); + + // Trigger subscription to the underlying observable. + Assert.Equal(true, f.A); + + Assert.Equal(1, f.Count); + } + } + + [Fact] + public void ToProperty_NameOf_ShouldSubscribeOnlyOnce() + { + using (ProductionMode.Set()) + { + var f = new RaceConditionNameOfFixture(); + + // This line is important because it triggers connect to + // be called recursively thus cause the subscription + // to be called twice. Not sure if this is a reactive UI + // or RX bug. + f.PropertyChanged += (e, s) => Debug.WriteLine(f.A); + + // Trigger subscription to the underlying observable. + Assert.Equal(true, f.A); + + Assert.Equal(1, f.Count); + } + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/common-gui/ProductionMode.cs b/src/ReactiveUI.Tests/Platforms/common-gui/ProductionMode.cs new file mode 100644 index 0000000000..24285c580d --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/common-gui/ProductionMode.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Reactive.Disposables; +using Splat; + +namespace ReactiveUI.Tests +{ + internal class ProductionMode : IModeDetector + { + public static IDisposable Set() + { + ModeDetector.OverrideModeDetector(new ProductionMode()); + return Disposable.Create(() => ModeDetector.OverrideModeDetector(new PlatformModeDetector())); + } + + public bool? InUnitTestRunner() + { + return false; + } + + public bool? InDesignMode() + { + return false; + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/uwp/API/UwpApiApprovalTests.cs b/src/ReactiveUI.Tests/Platforms/uwp/API/UwpApiApprovalTests.cs new file mode 100644 index 0000000000..fe6f120f48 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/uwp/API/UwpApiApprovalTests.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2019 .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. +// 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.Text; +using System.Threading.Tasks; +using ReactiveUI; +using Xunit; + +namespace ReactiveUI.Tests.Uwp +{ + [ExcludeFromCodeCoverage] + public class UwpApiApprovalTests : ApiApprovalBase + { + [Fact] + public void Uwp() + { + CheckApproval(typeof(WinRTAppDataDriver).Assembly); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/uwp/App.xaml b/src/ReactiveUI.Tests/Platforms/uwp/App.xaml new file mode 100644 index 0000000000..e5112eac6d --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/uwp/App.xaml @@ -0,0 +1,7 @@ + + diff --git a/src/ReactiveUI.Tests/Platforms/uwp/App.xaml.cs b/src/ReactiveUI.Tests/Platforms/uwp/App.xaml.cs new file mode 100644 index 0000000000..4fc0d335af --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/uwp/App.xaml.cs @@ -0,0 +1,41 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.ApplicationModel; +using Windows.ApplicationModel.Activation; +using Windows.Foundation; +using Windows.Foundation.Collections; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Input; +using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Navigation; + +using Xunit.Runners.UI; + +namespace ReactiveUI.Tests.Uwp +{ + /// + /// Interaction logic for App.xaml. + /// + public partial class App + { + protected override void OnInitializeRunner() + { + AddTestAssembly(GetType().GetTypeInfo().Assembly); + InitializeRunner(); + } + + partial void InitializeRunner(); + } +} diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.Blend.approved.txt b/src/ReactiveUI.Tests/Platforms/windows-xaml/Api/ApiApprovalTests.Blend.net461.approved.txt similarity index 90% rename from src/ReactiveUI.Tests/API/ApiApprovalTests.Blend.approved.txt rename to src/ReactiveUI.Tests/Platforms/windows-xaml/Api/ApiApprovalTests.Blend.net461.approved.txt index c70a0166af..b41cce3e66 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.Blend.approved.txt +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Api/ApiApprovalTests.Blend.net461.approved.txt @@ -1,4 +1,4 @@ -[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName=".NET Framework 4.6.1")] +[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName=".NET Framework 4.6.1")] namespace ReactiveUI.Blend { public class FollowObservableStateBehavior : System.Windows.Interactivity.Behavior diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Api/XamlApiApprovalTests.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Api/XamlApiApprovalTests.cs new file mode 100644 index 0000000000..f38ed085bf --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Api/XamlApiApprovalTests.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2019 .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. +// 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.Text; +using System.Threading.Tasks; +using PublicApiGenerator; +using Shouldly; +using Xunit; + +namespace ReactiveUI.Tests.Xaml +{ + [ExcludeFromCodeCoverage] + public class XamlApiApprovalTests : ApiApprovalBase + { + [Fact] + public void Blend() + { + CheckApproval(typeof(Blend.FollowObservableStateBehavior).Assembly); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/CommandBindingImplementationTests.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/CommandBindingImplementationTests.cs new file mode 100644 index 0000000000..5d736e34e5 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/CommandBindingImplementationTests.cs @@ -0,0 +1,213 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.ComponentModel; +using System.Reactive; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Windows; +using Xunit; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using FactAttribute = Xunit.WpfFactAttribute; +using System.Windows.Controls; +using System.Windows.Input; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class CommandBindingImplementationTests + { + [Fact] + public void CommandBindByNameWireup() + { + var vm = new CommandBindViewModel(); + var view = new CommandBindView { ViewModel = vm }; + + Assert.Null(view.Command1.Command); + + var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1); + Assert.Equal(vm.Command1, view.Command1.Command); + + var newCmd = ReactiveCommand.Create(_ => { }); + vm.Command1 = newCmd; + Assert.Equal(newCmd, view.Command1.Command); + + disp.Dispose(); + Assert.Null(view.Command1.Command); + } + + [Fact] + public void CommandBindNestedCommandWireup() + { + var vm = new CommandBindViewModel + { + NestedViewModel = new FakeNestedViewModel() + }; + + var view = new CommandBindView { ViewModel = vm }; + + view.BindCommand(vm, m => m.NestedViewModel.NestedCommand, x => x.Command1); + + Assert.Equal(vm.NestedViewModel.NestedCommand, view.Command1.Command); + } + + [Fact] + public void CommandBindSetsInitialEnabledState_True() + { + var vm = new CommandBindViewModel(); + var view = new CommandBindView { ViewModel = vm }; + + var canExecute1 = new BehaviorSubject(true); + vm.Command1 = ReactiveCommand.Create(_ => { }, canExecute1); + + view.BindCommand(vm, x => x.Command1, x => x.Command1); + + Assert.True(view.Command1.IsEnabled); + } + + [Fact] + public void CommandBindSetsDisablesCommandWhenCanExecuteChanged() + { + var vm = new CommandBindViewModel(); + var view = new CommandBindView { ViewModel = vm }; + + var canExecute1 = new BehaviorSubject(true); + vm.Command1 = ReactiveCommand.Create(_ => { }, canExecute1); + + view.BindCommand(vm, x => x.Command1, x => x.Command1); + + Assert.True(view.Command1.IsEnabled); + + canExecute1.OnNext(false); + + Assert.False(view.Command1.IsEnabled); + } + + [Fact] + public void CommandBindSetsInitialEnabledState_False() + { + var vm = new CommandBindViewModel(); + var view = new CommandBindView { ViewModel = vm }; + + var canExecute1 = new BehaviorSubject(false); + vm.Command1 = ReactiveCommand.Create(_ => { }, canExecute1); + + view.BindCommand(vm, x => x.Command1, x => x.Command1); + + Assert.False(view.Command1.IsEnabled); + } + + [Fact] + public void CommandBindRaisesCanExecuteChangedOnBind() + { + var vm = new CommandBindViewModel(); + var view = new CommandBindView { ViewModel = vm }; + + var canExecute1 = new BehaviorSubject(true); + vm.Command1 = ReactiveCommand.Create(_ => { }, canExecute1); + + view.BindCommand(vm, x => x.Command1, x => x.Command1); + + Assert.True(view.Command1.IsEnabled); + + // Now change to a disabled cmd + var canExecute2 = new BehaviorSubject(false); + vm.Command1 = ReactiveCommand.Create(_ => { }, canExecute2); + + Assert.False(view.Command1.IsEnabled); + } + + [Fact] + public void CommandBindWithParameterExpression() + { + var vm = new CommandBindViewModel(); + var view = new CommandBindView { ViewModel = vm }; + + var received = 0; + var cmd = ReactiveCommand.Create(i => { received = i; }); + vm.Command1 = cmd; + + var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1, x => x.Value, nameof(CustomClickButton.CustomClick)); + + vm.Value = 42; + view.Command1.RaiseCustomClick(); + Assert.Equal(42, received); + + vm.Value = 13; + view.Command1.RaiseCustomClick(); + Assert.Equal(13, received); + } + + [Fact] + public void CommandBindWithDelaySetVMParameterExpression() + { + var vm = new CommandBindViewModel(); + var view = new ReactiveObjectCommandBindView(); + + var received = 0; + var cmd = ReactiveCommand.Create(i => { received = i; }); + vm.Command1 = cmd; + + var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1, x => x.Value, nameof(CustomClickButton.CustomClick)); + + view.ViewModel = vm; + + vm.Value = 42; + view.Command1.RaiseCustomClick(); + Assert.Equal(42, received); + + vm.Value = 13; + view.Command1.RaiseCustomClick(); + Assert.Equal(13, received); + } + + [Fact] + public void CommandBindWithDelaySetVMParameterNoINPCExpression() + { + var vm = new CommandBindViewModel(); + var view = new CommandBindView(); + + var received = 0; + var cmd = ReactiveCommand.Create(i => { received = i; }); + vm.Command1 = cmd; + + view.BindCommand(vm, x => x.Command1, x => x.Command1, x => x.Value, nameof(CustomClickButton.CustomClick)); + + view.ViewModel = vm; + + vm.Value = 42; + view.Command1.RaiseCustomClick(); + Assert.Equal(0, received); + + vm.Value = 13; + view.Command1.RaiseCustomClick(); + Assert.Equal(0, received); + } + + [Fact] + public void CommandBindWithParameterObservable() + { + var vm = new CommandBindViewModel(); + var view = new CommandBindView { ViewModel = vm }; + + var received = 0; + var cmd = ReactiveCommand.Create(i => { received = i; }); + vm.Command1 = cmd; + + var value = Observable.Return(42); + var disp = view.BindCommand(vm, x => x.Command1, x => x.Command1, value, nameof(CustomClickButton.CustomClick)); + + vm.Value = 42; + view.Command1.RaiseCustomClick(); + + Assert.Equal(42, received); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/xaml/DependencyObjectObservableForPropertyTest.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/DependencyObjectObservableForPropertyTest.cs similarity index 79% rename from src/ReactiveUI.Tests/Platforms/xaml/DependencyObjectObservableForPropertyTest.cs rename to src/ReactiveUI.Tests/Platforms/windows-xaml/DependencyObjectObservableForPropertyTest.cs index b3076a94df..0e84b43b51 100644 --- a/src/ReactiveUI.Tests/Platforms/xaml/DependencyObjectObservableForPropertyTest.cs +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/DependencyObjectObservableForPropertyTest.cs @@ -9,39 +9,23 @@ using System.Linq.Expressions; using System.Reactive.Concurrency; using System.Windows; -using System.Windows.Controls; + using DynamicData; using Xunit; -namespace ReactiveUI.Tests -{ - public class DepObjFixture : FrameworkElement - { - public static readonly DependencyProperty TestStringProperty = - DependencyProperty.Register("TestString", typeof(string), typeof(DepObjFixture), new PropertyMetadata(null)); - - public string TestString - { - get => (string)GetValue(TestStringProperty); - set => SetValue(TestStringProperty, value); - } - } - - public class DerivedDepObjFixture : DepObjFixture - { - public static readonly DependencyProperty AnotherTestStringProperty = - DependencyProperty.Register("AnotherTestString", typeof(string), typeof(DerivedDepObjFixture), new PropertyMetadata(null)); - - public string AnotherTestString - { - get => (string)GetValue(AnotherTestStringProperty); - set => SetValue(AnotherTestStringProperty, value); - } - } +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using FactAttribute = Xunit.WpfFactAttribute; +using System.Windows.Controls; +#endif +namespace ReactiveUI.Tests.Xaml +{ public class DependencyObjectObservableForPropertyTest { - [WpfFact] + [Fact] public void DependencyObjectObservableForPropertySmokeTest() { var fixture = new DepObjFixture(); @@ -64,7 +48,7 @@ public void DependencyObjectObservableForPropertySmokeTest() disp2.Dispose(); } - [WpfFact] + [Fact] public void DerivedDependencyObjectObservableForPropertySmokeTest() { var fixture = new DerivedDepObjFixture(); @@ -87,7 +71,7 @@ public void DerivedDependencyObjectObservableForPropertySmokeTest() disp2.Dispose(); } - [WpfFact] + [Fact] public void WhenAnyWithDependencyObjectTest() { var inputs = new[] { "Foo", "Bar", "Baz" }; @@ -101,7 +85,7 @@ public void WhenAnyWithDependencyObjectTest() Assert.True(inputs.Zip(outputs.Skip(1), (expected, actual) => expected == actual).All(x => x)); } - [WpfFact] + [Fact] public void ListBoxSelectedItemTest() { var input = new ListBox(); diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Legacy/ControlsReactiveCollectionTest.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Legacy/ControlsReactiveCollectionTest.cs new file mode 100644 index 0000000000..f439d8b17a --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Legacy/ControlsReactiveCollectionTest.cs @@ -0,0 +1,97 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Xunit; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Markup; +#else +using FactAttribute = Xunit.WpfFactAttribute; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class ControlsReactiveCollectionTest + { + [Fact] + public void DataboundReactiveListDoesNotThrowForAddRange() + { + var vm = new LegacyPropertyBindViewModel(); + var view = new LegacyPropertyBindView + { + ViewModel = vm + }; + var fixture = new PropertyBinderImplementation(); + fixture.OneWayBind(vm, view, m => m.SomeCollectionOfStrings, v => v.FakeItemsControl.ItemsSource); + + // eliminate the ResetChangeThreshold from the equation + vm.SomeCollectionOfStrings.ResetChangeThreshold = int.MinValue; + + // Within the reset threshold + vm.SomeCollectionOfStrings.AddRange(Create(5)); + vm.SomeCollectionOfStrings.AddRange(Create(20)); + + IEnumerable Create(int numElements) => Enumerable.Range(1, numElements).Select(i => $"item_{i}"); + } + + [Fact] + public void DataboundReactiveListDoesNotThrowForInsertRange() + { + var vm = new LegacyPropertyBindViewModel(); + var view = new LegacyPropertyBindView + { + ViewModel = vm + }; + var fixture = new PropertyBinderImplementation(); + fixture.OneWayBind(vm, view, m => m.SomeCollectionOfStrings, v => v.FakeItemsControl.ItemsSource); + vm.SomeCollectionOfStrings.ResetChangeThreshold = int.MinValue; + + foreach (var item in Create(5)) + { + vm.SomeCollectionOfStrings.Add(item); + } + + // within reset threshold + vm.SomeCollectionOfStrings.InsertRange(2, Create(5)); + + // outside reset threshold + vm.SomeCollectionOfStrings.InsertRange(2, Create(20)); + + IEnumerable Create(int numElements) => Enumerable.Range(1, numElements).Select(i => $"item_{i}"); + } + + [Fact] + public void DataboundReactiveListDoesNotThrowForRemoveRange() + { + var vm = new LegacyPropertyBindViewModel(); + var view = new LegacyPropertyBindView + { + ViewModel = vm + }; + var fixture = new PropertyBinderImplementation(); + fixture.OneWayBind(vm, view, m => m.SomeCollectionOfStrings, v => v.FakeItemsControl.ItemsSource); + vm.SomeCollectionOfStrings.ResetChangeThreshold = int.MinValue; + + foreach (var item in Enumerable.Range(1, 40).Select(i => $"item_{i}")) + { + vm.SomeCollectionOfStrings.Add(item); + } + + // within reset threshold + vm.SomeCollectionOfStrings.RemoveRange(2, 5); + + // outside reset threshold + vm.SomeCollectionOfStrings.RemoveRange(2, 20); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Legacy/Mocks/LegacyPropertyBindView.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Legacy/Mocks/LegacyPropertyBindView.cs new file mode 100644 index 0000000000..dc0b196d8e --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Legacy/Mocks/LegacyPropertyBindView.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using System.Windows.Controls; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class LegacyPropertyBindView : Control, IViewFor + { + public static readonly DependencyProperty ViewModelProperty = + DependencyProperty.Register("ViewModel", typeof(LegacyPropertyBindViewModel), typeof(LegacyPropertyBindView), new PropertyMetadata(null)); + + public LegacyPropertyBindView() + { + SomeTextBox = new TextBox(); + Property2 = new TextBox(); + FakeControl = new PropertyBindFakeControl(); + FakeItemsControl = new ListBox(); + } + + public TextBox SomeTextBox { get; set; } + + public TextBox Property2 { get; set; } + + public PropertyBindFakeControl FakeControl { get; set; } + + public ListBox FakeItemsControl { get; set; } + + public LegacyPropertyBindViewModel ViewModel + { + get => (LegacyPropertyBindViewModel)GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (LegacyPropertyBindViewModel)value; + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Legacy/Mocks/LegacyPropertyBindViewModel.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Legacy/Mocks/LegacyPropertyBindViewModel.cs new file mode 100644 index 0000000000..5d8bafc034 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Legacy/Mocks/LegacyPropertyBindViewModel.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using ReactiveUI.Legacy; + +#pragma warning disable CS0618 // Item has been marked as Obsolete + +namespace ReactiveUI.Tests.Xaml +{ + public class LegacyPropertyBindViewModel : ReactiveObject + { + private decimal _JustADecimal; + + private double _JustADouble; + + private int _JustAInt32; + + private PropertyBindModel _Model; + + private double? _NullableDouble; + + private string _Property1; + + private int _Property2; + + public LegacyPropertyBindViewModel(PropertyBindModel model = null) + { + Model = model ?? new PropertyBindModel + { + AThing = 42, + AnotherThing = "Baz" + }; + SomeCollectionOfStrings = new ReactiveList(new[] { "Foo", "Bar" }); + } + + public decimal JustADecimal + { + get => _JustADecimal; + set => this.RaiseAndSetIfChanged(ref _JustADecimal, value); + } + + public double JustADouble + { + get => _JustADouble; + set => this.RaiseAndSetIfChanged(ref _JustADouble, value); + } + + public int JustAInt32 + { + get => _JustAInt32; + set => this.RaiseAndSetIfChanged(ref _JustAInt32, value); + } + + public PropertyBindModel Model + { + get => _Model; + set => this.RaiseAndSetIfChanged(ref _Model, value); + } + + public double? NullableDouble + { + get => _NullableDouble; + set => this.RaiseAndSetIfChanged(ref _NullableDouble, value); + } + + public string Property1 + { + get => _Property1; + set => this.RaiseAndSetIfChanged(ref _Property1, value); + } + + public int Property2 + { + get => _Property2; + set => this.RaiseAndSetIfChanged(ref _Property2, value); + } + + public ReactiveList SomeCollectionOfStrings { get; } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/CommandBindView.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/CommandBindView.cs new file mode 100644 index 0000000000..cd92bdc9d1 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/CommandBindView.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using System.Windows.Controls; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class CommandBindView : IViewFor + { + public CommandBindView() + { + Command1 = new CustomClickButton(); + Command2 = new Image(); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (CommandBindViewModel)value; + } + + public CommandBindViewModel ViewModel { get; set; } + + public CustomClickButton Command1 { get; protected set; } + + public Image Command2 { get; protected set; } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/CustomClickButton.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/CustomClickButton.cs new file mode 100644 index 0000000000..66f7e60c5c --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/CustomClickButton.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using System.Windows.Controls; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class CustomClickButton : Button + { + public event EventHandler CustomClick; + + public void RaiseCustomClick() => + CustomClick?.Invoke(this, EventArgs.Empty); + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/DepObjFixture.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/DepObjFixture.cs new file mode 100644 index 0000000000..9f73273d18 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/DepObjFixture.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using System.Windows.Controls; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class DepObjFixture : FrameworkElement + { + public static readonly DependencyProperty TestStringProperty = + DependencyProperty.Register("TestString", typeof(string), typeof(DepObjFixture), new PropertyMetadata(null)); + + public string TestString + { + get => (string)GetValue(TestStringProperty); + set => SetValue(TestStringProperty, value); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/DerivedDepObjFixture.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/DerivedDepObjFixture.cs new file mode 100644 index 0000000000..b89265acbc --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/DerivedDepObjFixture.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +#if NETFX_CORE +using Windows.UI.Xaml; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class DerivedDepObjFixture : DepObjFixture + { + public static readonly DependencyProperty AnotherTestStringProperty = + DependencyProperty.Register("AnotherTestString", typeof(string), typeof(DerivedDepObjFixture), new PropertyMetadata(null)); + + public string AnotherTestString + { + get => (string)GetValue(AnotherTestStringProperty); + set => SetValue(AnotherTestStringProperty, value); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/FakeView.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/FakeView.cs new file mode 100644 index 0000000000..dcd966d3ee --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/FakeView.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using System.Windows.Controls; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class FakeView : IViewFor + { + public FakeView() + { + TheTextBox = new TextBox(); + ViewModel = new FakeViewModel(); + } + + public TextBox TheTextBox { get; protected set; } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (FakeViewModel)value; + } + + public FakeViewModel ViewModel { get; set; } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/HostTestView.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/HostTestView.cs new file mode 100644 index 0000000000..3796608f40 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/HostTestView.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using System.Windows; +using System.Windows.Controls; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class HostTestView : Control, IViewFor + { + public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(HostTestFixture), typeof(HostTestView), new PropertyMetadata(null)); + + public HostTestFixture ViewModel + { + get => (HostTestFixture)GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (HostTestFixture)value; + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/xaml/PropertyBindingTestViews.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/PropertyBindFakeControl.cs similarity index 62% rename from src/ReactiveUI.Tests/Platforms/xaml/PropertyBindingTestViews.cs rename to src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/PropertyBindFakeControl.cs index c3e3ee3022..0b6affbae8 100644 --- a/src/ReactiveUI.Tests/Platforms/xaml/PropertyBindingTestViews.cs +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/PropertyBindFakeControl.cs @@ -5,48 +5,20 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; -using System.Reactive; using System.Text; +using System.Threading.Tasks; using System.Windows; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else using System.Windows.Controls; -using ReactiveUI; -using Xunit; +#endif -namespace ReactiveUI.Tests +namespace ReactiveUI.Tests.Xaml { - public class PropertyBindView : Control, IViewFor - { - public static readonly DependencyProperty ViewModelProperty = - DependencyProperty.Register("ViewModel", typeof(PropertyBindViewModel), typeof(PropertyBindView), new PropertyMetadata(null)); - - public TextBox SomeTextBox; - public TextBox Property2; - public PropertyBindFakeControl FakeControl; - public ListBox FakeItemsControl; - - public PropertyBindView() - { - SomeTextBox = new TextBox(); - Property2 = new TextBox(); - FakeControl = new PropertyBindFakeControl(); - FakeItemsControl = new ListBox(); - } - - public PropertyBindViewModel ViewModel - { - get => (PropertyBindViewModel)GetValue(ViewModelProperty); - set => SetValue(ViewModelProperty, value); - } - - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (PropertyBindViewModel)value; - } - } - public class PropertyBindFakeControl : Control { public static readonly DependencyProperty NullHatingStringProperty = diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/PropertyBindView.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/PropertyBindView.cs new file mode 100644 index 0000000000..2cd873564c --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/PropertyBindView.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using System.Windows.Controls; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class PropertyBindView : Control, IViewFor + { + public static readonly DependencyProperty ViewModelProperty = + DependencyProperty.Register("ViewModel", typeof(PropertyBindViewModel), typeof(PropertyBindView), new PropertyMetadata(null)); + + public PropertyBindView() + { + SomeTextBox = new TextBox(); + Property2 = new TextBox(); + FakeControl = new PropertyBindFakeControl(); + FakeItemsControl = new ListBox(); + } + + public TextBox SomeTextBox { get; set; } + + public TextBox Property2 { get; set; } + + public PropertyBindFakeControl FakeControl { get; set; } + + public ListBox FakeItemsControl { get; set; } + + public PropertyBindViewModel ViewModel + { + get => (PropertyBindViewModel)GetValue(ViewModelProperty); + set => SetValue(ViewModelProperty, value); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (PropertyBindViewModel)value; + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/ReactiveObjectCommandBindView.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/ReactiveObjectCommandBindView.cs new file mode 100644 index 0000000000..1f7ebc6455 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Mocks/ReactiveObjectCommandBindView.cs @@ -0,0 +1,48 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +#else +using System.Windows.Controls; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class ReactiveObjectCommandBindView : ReactiveObject, IViewFor + { + private CommandBindViewModel _vm; + + public ReactiveObjectCommandBindView() + { + Command1 = new CustomClickButton(); + Command2 = new Image(); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (CommandBindViewModel)value; + } + + public CommandBindViewModel ViewModel + { + get => _vm; + set => this.RaiseAndSetIfChanged(ref _vm, value); + } + + public CustomClickButton Command1 { get; protected set; } + + public Image Command2 { get; protected set; } + } +} diff --git a/src/ReactiveUI.Tests/PropertyBindingTest.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/PropertyBindingTest.cs similarity index 83% rename from src/ReactiveUI.Tests/PropertyBindingTest.cs rename to src/ReactiveUI.Tests/Platforms/windows-xaml/PropertyBindingTest.cs index 0fc6b2c8b4..6b2874916f 100644 --- a/src/ReactiveUI.Tests/PropertyBindingTest.cs +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/PropertyBindingTest.cs @@ -4,90 +4,22 @@ // See the LICENSE file in the project root for full license information. using System; +using System.Collections; using System.Linq; using System.Reactive; using DynamicData.Binding; using Xunit; -namespace ReactiveUI.Tests -{ - public class PropertyBindModel - { - public int AThing { get; set; } - - public string AnotherThing { get; set; } - } - - public class PropertyBindViewModel : ReactiveObject - { - public string _Property1; - - public PropertyBindModel _Model; - - public int _Property2; - - public double _JustADouble; - - public decimal _JustADecimal; - - public double? _NullableDouble; - - public int _JustAInt32; - - public PropertyBindViewModel(PropertyBindModel model = null) - { - Model = model ?? new PropertyBindModel { AThing = 42, AnotherThing = "Baz" }; - SomeCollectionOfStrings = new ObservableCollectionExtended(new[] { "Foo", "Bar" }); - } - - public string Property1 - { - get => _Property1; - set => this.RaiseAndSetIfChanged(ref _Property1, value); - } - - public int Property2 - { - get => _Property2; - set => this.RaiseAndSetIfChanged(ref _Property2, value); - } - - public double JustADouble - { - get => _JustADouble; - set => this.RaiseAndSetIfChanged(ref _JustADouble, value); - } - - public decimal JustADecimal - { - get => _JustADecimal; - set => this.RaiseAndSetIfChanged(ref _JustADecimal, value); - } - - public int JustAInt32 - { - get => _JustAInt32; - set => this.RaiseAndSetIfChanged(ref _JustAInt32, value); - } - - public double? NullableDouble - { - get => _NullableDouble; - set => this.RaiseAndSetIfChanged(ref _NullableDouble, value); - } - - public ObservableCollectionExtended SomeCollectionOfStrings { get; } - - public PropertyBindModel Model - { - get => _Model; - set => this.RaiseAndSetIfChanged(ref _Model, value); - } - } +#if NETFX_CORE +#else +using FactAttribute = Xunit.WpfFactAttribute; +#endif +namespace ReactiveUI.Tests.Xaml +{ public class PropertyBindingTest { - [WpfFact] + [Fact] [UseInvariantCulture] public void TwoWayBindWithFuncConvertersSmokeTest() { @@ -113,7 +45,7 @@ public void TwoWayBindWithFuncConvertersSmokeTest() Assert.Equal("567.89", view.SomeTextBox.Text); } - [WpfFact] + [Fact] public void TwoWayBindSmokeTest() { var vm = new PropertyBindViewModel(); @@ -138,7 +70,7 @@ public void TwoWayBindSmokeTest() Assert.NotEqual(vm.Property1, view.SomeTextBox.Text); } - [WpfFact] + [Fact] public void TypeConvertedTwoWayBindSmokeTest() { var vm = new PropertyBindViewModel(); @@ -201,7 +133,7 @@ public void TypeConvertedTwoWayBindSmokeTest() Assert.Equal(13, vm.JustAInt32); } - [WpfFact] + [Fact] public void BindingIntoModelObjects() { var vm = new PropertyBindViewModel(); @@ -211,7 +143,7 @@ public void BindingIntoModelObjects() Assert.Equal("Baz", view.SomeTextBox.Text); } - [WpfFact] + [Fact] public void ViewModelNullableToViewNonNullable() { var vm = new PropertyBindViewModel(); @@ -230,7 +162,7 @@ public void ViewModelNullableToViewNonNullable() Assert.Equal(0.0, view.FakeControl.JustADouble); } - [WpfFact] + [Fact] public void ViewModelNonNullableToViewNullable() { var vm = new PropertyBindViewModel(); @@ -249,7 +181,7 @@ public void ViewModelNonNullableToViewNullable() Assert.Equal(0.0, vm.JustADouble); } - [WpfFact] + [Fact] public void ViewModelNullableToViewNullable() { var vm = new PropertyBindViewModel(); @@ -268,7 +200,7 @@ public void ViewModelNullableToViewNullable() Assert.Equal(0.0, vm.NullableDouble); } - [WpfFact] + [Fact] public void ViewModelIndexerToView() { var vm = new PropertyBindViewModel(); @@ -278,7 +210,7 @@ public void ViewModelIndexerToView() Assert.Equal("Foo", view.SomeTextBox.Text); } - [WpfFact] + [Fact] public void ViewModelIndexerToViewChanges() { var vm = new PropertyBindViewModel(); @@ -292,7 +224,7 @@ public void ViewModelIndexerToViewChanges() Assert.Equal("Bar", view.SomeTextBox.Text); } - [WpfFact] + [Fact] public void ViewModelIndexerPropertyToView() { var vm = new PropertyBindViewModel(); @@ -302,7 +234,7 @@ public void ViewModelIndexerPropertyToView() Assert.Equal("3", view.SomeTextBox.Text); } - [WpfFact] + [Fact] public void BindToShouldntInitiallySetToNull() { var vm = new PropertyBindViewModel(); @@ -315,7 +247,7 @@ public void BindToShouldntInitiallySetToNull() Assert.Equal(vm.Model.AnotherThing, view.FakeControl.NullHatingString); } - [WpfFact] + [Fact] public void BindToTypeConversionSmokeTest() { var vm = new PropertyBindViewModel(); @@ -330,7 +262,7 @@ public void BindToTypeConversionSmokeTest() Assert.Equal(vm.JustADouble.ToString(), view.FakeControl.NullHatingString); } - [WpfFact] + [Fact] public void BindToNullShouldThrowHelpfulError() { var view = new PropertyBindView { ViewModel = null }; @@ -340,8 +272,7 @@ public void BindToNullShouldThrowHelpfulError() .BindTo(view.ViewModel, x => x.Property1)); } -#if !MONO - [WpfFact] + [Fact] public void TwoWayBindToSelectedItemOfItemsControl() { var vm = new PropertyBindViewModel(); @@ -360,7 +291,7 @@ public void TwoWayBindToSelectedItemOfItemsControl() Assert.Equal("bbb", view.FakeItemsControl.SelectedItem); } - [WpfFact] + [Fact] public void ItemsControlShouldGetADataTemplate() { var vm = new PropertyBindViewModel(); @@ -372,7 +303,7 @@ public void ItemsControlShouldGetADataTemplate() Assert.NotNull(view.FakeItemsControl.ItemTemplate); } - [WpfFact] + [Fact] public void ItemsControlWithDisplayMemberPathSetShouldNotGetADataTemplate() { var vm = new PropertyBindViewModel(); @@ -385,7 +316,7 @@ public void ItemsControlWithDisplayMemberPathSetShouldNotGetADataTemplate() Assert.Null(view.FakeItemsControl.ItemTemplate); } - [WpfFact] + [Fact] public void ItemsControlShouldGetADataTemplateInBindTo() { var vm = new PropertyBindViewModel(); @@ -401,18 +332,19 @@ public void ItemsControlShouldGetADataTemplateInBindTo() .BindTo(vm, x => x.Property1); } - [WpfFact] + [Fact] public void BindingToItemsControl() { var vm = new PropertyBindViewModel(); var view = new PropertyBindView { ViewModel = vm }; view.OneWayBind(view.ViewModel, x => x.SomeCollectionOfStrings, x => x.FakeItemsControl.ItemsSource); - Assert.True(view.FakeItemsControl.ItemsSource.OfType().Count() > 1); + + var itemsSourceValue = (IList)view.FakeItemsControl.ItemsSource; + Assert.True(itemsSourceValue.OfType().Count() > 1); } -#endif - [WpfFact] + [Fact] public void BindExpectsConverterFuncsToNotBeNull() { var vm = new PropertyBindViewModel(); @@ -425,7 +357,7 @@ public void BindExpectsConverterFuncsToNotBeNull() Assert.Throws(() => fixture.Bind(vm, view, x => x.Property1, x => x.SomeTextBox.Text, (IObservable)null, s => s, nullFunc)); } - [WpfFact] + [Fact] public void BindWithFuncShouldWorkAsExtensionMethodSmokeTest() { var vm = new PropertyBindViewModel(); diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/RxAppDependencyObjectTests.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/RxAppDependencyObjectTests.cs new file mode 100644 index 0000000000..eb23b63c16 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/RxAppDependencyObjectTests.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Splat; +using Xunit; + +namespace ReactiveUI.Tests.Xaml +{ + public class RxAppDependencyObjectTests + { + [Fact] + public void DepPropNotifierShouldBeFound() + { + RxApp.EnsureInitialized(); + + Assert.True(Locator.Current.GetServices() + .Any(x => x is DependencyObjectObservableForProperty)); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/Utilities/DispatcherUtilities.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/Utilities/DispatcherUtilities.cs new file mode 100644 index 0000000000..ca59ffaa93 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/Utilities/DispatcherUtilities.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2019 .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. +// 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.Text; +using System.Threading.Tasks; + +#if !NETFX_CORE +using System.Windows.Threading; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public static class DispatcherUtilities + { + public static void DoEvents() + { +#if !NETFX_CORE + DispatcherFrame frame = new DispatcherFrame(); + Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame); + Dispatcher.PushFrame(frame); +#endif + } + + [SuppressMessage("Design", "CA1801: Parameter never used", Justification = "Used on some platforms.")] + public static object ExitFrame(object f) + { +#if !NETFX_CORE + ((DispatcherFrame)f).Continue = false; +#endif + return null; + } + } +} diff --git a/src/ReactiveUI.Tests/WeakEventManagerTest.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/WeakEventManagerTest.cs old mode 100755 new mode 100644 similarity index 85% rename from src/ReactiveUI.Tests/WeakEventManagerTest.cs rename to src/ReactiveUI.Tests/Platforms/windows-xaml/WeakEventManagerTest.cs index a09c85c42d..1bc8fe2aaa --- a/src/ReactiveUI.Tests/WeakEventManagerTest.cs +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/WeakEventManagerTest.cs @@ -4,12 +4,22 @@ // See the LICENSE file in the project root for full license information. using System; -using System.Reactive; -using System.Windows.Controls; using DynamicData.Binding; using Xunit; -namespace ReactiveUI.Tests +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Automation.Peers; +using Windows.UI.Xaml.Automation.Provider; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Markup; +#else +using System.Windows.Controls; +using FactAttribute = Xunit.WpfFactAttribute; +#endif + +namespace ReactiveUI.Tests.Xaml { public class WeakEventManagerTest { diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/WhenAnyThroughDependencyObjectTests.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/WhenAnyThroughDependencyObjectTests.cs new file mode 100644 index 0000000000..869f781d33 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/WhenAnyThroughDependencyObjectTests.cs @@ -0,0 +1,49 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using Xunit; + +#if NETFX_CORE +#else +using FactAttribute = Xunit.WpfFactAttribute; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class WhenAnyThroughDependencyObjectTests + { + [Fact] + public void WhenAnyThroughAViewShouldntGiveNullValues() + { + var vm = new HostTestFixture + { + Child = new TestFixture + { + IsNotNullString = "Foo", + IsOnlyOneWord = "Baz", + PocoProperty = "Bamf" + }, + }; + + var fixture = new HostTestView(); + + var output = new List(); + + Assert.Equal(0, output.Count); + Assert.Null(fixture.ViewModel); + + fixture.WhenAnyValue(x => x.ViewModel.Child.IsNotNullString).Subscribe(output.Add); + + fixture.ViewModel = vm; + Assert.Equal(1, output.Count); + + fixture.ViewModel.Child.IsNotNullString = "Bar"; + Assert.Equal(2, output.Count); + new[] { "Foo", "Bar" }.AssertAreEqual(output); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/XamlViewCommandTests.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/XamlViewCommandTests.cs new file mode 100644 index 0000000000..c3845ea9a3 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/XamlViewCommandTests.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reactive.Linq; +using System.Threading.Tasks; +using System.Windows; +using Splat; +using Xunit; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Automation.Peers; +using Windows.UI.Xaml.Automation.Provider; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Markup; +#else +using System.Windows.Automation.Peers; +using System.Windows.Automation.Provider; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using FactAttribute = Xunit.WpfFactAttribute; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + public class XamlViewCommandTests + { + [Fact] + public void EventBinderBindsToExplicitInheritedEvent() + { + var fixture = new FakeView(); + fixture.BindCommand(fixture.ViewModel, x => x.Cmd, x => x.TheTextBox, "MouseDown"); + } + + [Fact] + public void EventBinderBindsToImplicitEvent() + { + var input = new Button(); + var fixture = new CreatesCommandBindingViaEvent(); + var cmd = ReactiveCommand.Create(_ => { }); + + Assert.True(fixture.GetAffinityForObject(input.GetType(), false) > 0); + + var invokeCount = 0; + cmd.Subscribe(_ => invokeCount += 1); + + var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5)); + Assert.NotNull(disp); + Assert.Equal(0, invokeCount); + + var automationPeer = new ButtonAutomationPeer(input); + var invoker = (IInvokeProvider)automationPeer.GetPattern(PatternInterface.Invoke); + + invoker.Invoke(); + DispatcherUtilities.DoEvents(); + Assert.Equal(1, invokeCount); + + disp.Dispose(); + invoker.Invoke(); + Assert.Equal(1, invokeCount); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/windows-xaml/XamlViewDependencyResolverTests.cs b/src/ReactiveUI.Tests/Platforms/windows-xaml/XamlViewDependencyResolverTests.cs new file mode 100644 index 0000000000..f7bc9488e3 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/windows-xaml/XamlViewDependencyResolverTests.cs @@ -0,0 +1,65 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Splat; +using Xunit; + +#if NETFX_CORE +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Markup; +#else +using FactAttribute = Xunit.WpfFactAttribute; +#endif + +namespace ReactiveUI.Tests.Xaml +{ + /// + /// Tests associated with UI and the . + /// + public sealed class XamlViewDependencyResolverTests : IDisposable + { + private readonly IMutableDependencyResolver _resolver; + + public XamlViewDependencyResolverTests() + { + _resolver = new ModernDependencyResolver(); + _resolver.InitializeSplat(); + _resolver.InitializeReactiveUI(); + _resolver.RegisterViewsForViewModels(GetType().Assembly); + } + + [Fact] + public void RegisterViewsForViewModelShouldRegisterAllViews() + { + using (_resolver.WithResolver()) + { + Assert.Single(_resolver.GetServices>()); + Assert.Single(_resolver.GetServices>()); + Assert.Single(_resolver.GetServices>()); + Assert.Single(_resolver.GetServices>()); + } + } + + [Fact] + public void RegisterViewsForViewModelShouldIncludeContracts() + { + using (_resolver.WithResolver()) + { + Assert.Single(_resolver.GetServices(typeof(IViewFor), "contract")); + } + } + + public void Dispose() + { + _resolver?.Dispose(); + } + } +} diff --git a/src/ReactiveUI.Tests/API/ApiApprovalTests.Winforms.approved.txt b/src/ReactiveUI.Tests/Platforms/winforms/API/ApiApprovalTests.Winforms.net461.approved.txt similarity index 85% rename from src/ReactiveUI.Tests/API/ApiApprovalTests.Winforms.approved.txt rename to src/ReactiveUI.Tests/Platforms/winforms/API/ApiApprovalTests.Winforms.net461.approved.txt index a787183424..92a4d4b64b 100644 --- a/src/ReactiveUI.Tests/API/ApiApprovalTests.Winforms.approved.txt +++ b/src/ReactiveUI.Tests/Platforms/winforms/API/ApiApprovalTests.Winforms.net461.approved.txt @@ -1,4 +1,4 @@ -[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName=".NET Framework 4.6.1")] +[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName=".NET Framework 4.6.1")] namespace ReactiveUI.Winforms { public class ActivationForViewFetcher : ReactiveUI.IActivationForViewFetcher, Splat.IEnableLogger @@ -14,9 +14,6 @@ namespace ReactiveUI.Winforms public System.IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, System.IObservable commandParameter, string eventName) { } public int GetAffinityForObject(System.Type type, bool hasEventTarget) { } } - [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + - "ps://github.com/rolandpheasant/dynamicdata")] - public interface IReactiveDerivedBindingList : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveDerivedList, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, ReactiveUI.Legacy.IReadOnlyReactiveCollection, ReactiveUI.Legacy.IReadOnlyReactiveList, Splat.IEnableLogger, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.IBindingList, System.ComponentModel.INotifyPropertyChanged, System.IDisposable { } public class PlatformOperations { public PlatformOperations() { } @@ -95,14 +92,17 @@ namespace ReactiveUI.Winforms } namespace ReactiveUI.Winforms.Legacy { + [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + + "ps://github.com/rolandpheasant/dynamicdata")] + public interface IReactiveDerivedBindingList : ReactiveUI.INotifyCollectionChanging, ReactiveUI.INotifyPropertyChanging, ReactiveUI.IReactiveObject, ReactiveUI.Legacy.IReactiveCollection, ReactiveUI.Legacy.IReactiveDerivedList, ReactiveUI.Legacy.IReactiveNotifyCollectionChanged, ReactiveUI.Legacy.IReactiveNotifyCollectionItemChanged, ReactiveUI.Legacy.IReadOnlyReactiveCollection, ReactiveUI.Legacy.IReadOnlyReactiveList, Splat.IEnableLogger, System.Collections.Generic.IEnumerable, System.Collections.Generic.IReadOnlyCollection, System.Collections.Generic.IReadOnlyList, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.Specialized.INotifyCollectionChanged, System.ComponentModel.IBindingList, System.ComponentModel.INotifyPropertyChanged, System.IDisposable { } [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + "ps://github.com/rolandpheasant/dynamicdata")] public class static ObservableCollectionMixin { - public static ReactiveUI.Winforms.IReactiveDerivedBindingList CreateDerivedBindingList(this System.Collections.Generic.IEnumerable collection, System.Func selector, System.Action removed, System.Func filter = null, System.Func orderer = null, System.IObservable signalReset = null) { } - public static ReactiveUI.Winforms.IReactiveDerivedBindingList CreateDerivedBindingList(this System.Collections.Generic.IEnumerable collection, System.Func selector, System.Func filter = null, System.Func orderer = null, System.IObservable signalReset = null) { } - public static ReactiveUI.Winforms.IReactiveDerivedBindingList CreateDerivedBindingList(this System.Collections.Generic.IEnumerable collection, System.Func selector, System.Action removed, System.Func filter = null, System.Func orderer = null) { } - public static ReactiveUI.Winforms.IReactiveDerivedBindingList CreateDerivedBindingList(this System.Collections.Generic.IEnumerable collection, System.Func selector, System.Func filter = null, System.Func orderer = null) { } + public static ReactiveUI.Winforms.Legacy.IReactiveDerivedBindingList CreateDerivedBindingList(this System.Collections.Generic.IEnumerable collection, System.Func selector, System.Action removed, System.Func filter = null, System.Func orderer = null, System.IObservable signalReset = null) { } + public static ReactiveUI.Winforms.Legacy.IReactiveDerivedBindingList CreateDerivedBindingList(this System.Collections.Generic.IEnumerable collection, System.Func selector, System.Func filter = null, System.Func orderer = null, System.IObservable signalReset = null) { } + public static ReactiveUI.Winforms.Legacy.IReactiveDerivedBindingList CreateDerivedBindingList(this System.Collections.Generic.IEnumerable collection, System.Func selector, System.Action removed, System.Func filter = null, System.Func orderer = null) { } + public static ReactiveUI.Winforms.Legacy.IReactiveDerivedBindingList CreateDerivedBindingList(this System.Collections.Generic.IEnumerable collection, System.Func selector, System.Func filter = null, System.Func orderer = null) { } } [System.ObsoleteAttribute("ReactiveList is no longer supported. We suggest replacing it with DynamicData htt" + "ps://github.com/rolandpheasant/dynamicdata")] diff --git a/src/ReactiveUI.Tests/Platforms/winforms/API/WinformsApiApprovalTests.cs b/src/ReactiveUI.Tests/Platforms/winforms/API/WinformsApiApprovalTests.cs new file mode 100644 index 0000000000..b40942d3f8 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/API/WinformsApiApprovalTests.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2019 .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. +// 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.Text; +using System.Threading.Tasks; +using PublicApiGenerator; +using Shouldly; +using Xunit; + +namespace ReactiveUI.Tests +{ + [ExcludeFromCodeCoverage] + public class WinformsApiApprovalTests : ApiApprovalBase + { + [Fact] + public void Winforms() + { + CheckApproval(typeof(ReactiveUI.Winforms.WinformsCreatesObservableForProperty).Assembly); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/ActivationTests.cs b/src/ReactiveUI.Tests/Platforms/winforms/ActivationTests.cs index c68d51f1b2..35ce1b23fc 100644 --- a/src/ReactiveUI.Tests/Platforms/winforms/ActivationTests.cs +++ b/src/ReactiveUI.Tests/Platforms/winforms/ActivationTests.cs @@ -121,13 +121,5 @@ public void SmokeTestUserControl() Assert.Equal(2, userControlDeActivateCount); } } - - private class TestControl : Control, IActivatable - { - } - - private class TestForm : Form, IActivatable - { - } } } diff --git a/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingImplementationTests.cs b/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingImplementationTests.cs new file mode 100644 index 0000000000..4cf718ef7f --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingImplementationTests.cs @@ -0,0 +1,59 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Windows.Forms; +using Xunit; + +namespace ReactiveUI.Tests.Winforms +{ + public class CommandBindingImplementationTests + { + [Fact] + public void CommandBindByNameWireup() + { + var vm = new WinformCommandBindViewModel(); + var view = new WinformCommandBindView { ViewModel = vm }; + var fixture = new CommandBinderImplementation(); + + var invokeCount = 0; + vm.Command1.Subscribe(_ => invokeCount += 1); + + var disp = fixture.BindCommand(vm, view, x => x.Command1, x => x.Command1); + + view.Command1.PerformClick(); + + Assert.Equal(1, invokeCount); + + var newCmd = ReactiveCommand.Create(() => { }); + vm.Command1 = newCmd; + + view.Command1.PerformClick(); + Assert.Equal(1, invokeCount); + + disp.Dispose(); + } + + [Fact] + public void CommandBindToExplicitEventWireup() + { + var vm = new WinformCommandBindViewModel(); + var view = new WinformCommandBindView { ViewModel = vm }; + var fixture = new CommandBinderImplementation(); + + var invokeCount = 0; + vm.Command2.Subscribe(_ => invokeCount += 1); + + var disp = fixture.BindCommand(vm, view, x => x.Command2, x => x.Command2, "MouseUp"); + + view.Command2.RaiseMouseUpEvent(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)); + + disp.Dispose(); + + view.Command2.RaiseMouseUpEvent(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)); + Assert.Equal(1, invokeCount); + } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingTests.cs b/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingTests.cs index 109f20c8be..dafeff797a 100755 --- a/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingTests.cs +++ b/src/ReactiveUI.Tests/Platforms/winforms/CommandBindingTests.cs @@ -4,8 +4,6 @@ // See the LICENSE file in the project root for full license information. using System; -using System.ComponentModel; -using System.Reactive; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Windows.Forms; @@ -134,155 +132,4 @@ public void CommandBinderAffectsEnabledStateForComponents() } } } - - public class CustomClickableComponent : Component - { - public event EventHandler Click; - - public void PerformClick() - { - Click?.Invoke(this, EventArgs.Empty); - } - } - - public class CustomClickableControl : Control - { - public void PerformClick() - { - InvokeOnClick(this, EventArgs.Empty); - } - - public void RaiseMouseClickEvent(MouseEventArgs args) - { - OnMouseClick(args); - } - - public void RaiseMouseUpEvent(MouseEventArgs args) - { - OnMouseUp(args); - } - } - - public class CommandBindingImplementationTests - { - [Fact] - public void CommandBindByNameWireup() - { - var vm = new WinformCommandBindViewModel(); - var view = new WinformCommandBindView { ViewModel = vm }; - var fixture = new CommandBinderImplementation(); - - var invokeCount = 0; - vm.Command1.Subscribe(_ => invokeCount += 1); - - var disp = fixture.BindCommand(vm, view, x => x.Command1, x => x.Command1); - - view.Command1.PerformClick(); - - Assert.Equal(1, invokeCount); - - var newCmd = ReactiveCommand.Create(() => { }); - vm.Command1 = newCmd; - - view.Command1.PerformClick(); - Assert.Equal(1, invokeCount); - - disp.Dispose(); - } - - [Fact] - public void CommandBindToExplicitEventWireup() - { - var vm = new WinformCommandBindViewModel(); - var view = new WinformCommandBindView { ViewModel = vm }; - var fixture = new CommandBinderImplementation(); - - var invokeCount = 0; - vm.Command2.Subscribe(_ => invokeCount += 1); - - var disp = fixture.BindCommand(vm, view, x => x.Command2, x => x.Command2, "MouseUp"); - - view.Command2.RaiseMouseUpEvent(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)); - - disp.Dispose(); - - view.Command2.RaiseMouseUpEvent(new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)); - Assert.Equal(1, invokeCount); - } - } - - public class FakeViewModel : ReactiveObject - { - public FakeViewModel() - { - Cmd = ReactiveCommand.Create(() => { }); - } - - public ReactiveCommand Cmd { get; protected set; } - } - - public class FakeView : IViewFor - { - public FakeView() - { - TheTextBox = new TextBox(); - ViewModel = new FakeViewModel(); - } - - public TextBox TheTextBox { get; protected set; } - - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (FakeViewModel)value; - } - - public FakeViewModel ViewModel { get; set; } - } - - public class WinformCommandBindViewModel : ReactiveObject - { - private ReactiveCommand _command1; - - private ReactiveCommand _command2; - - public WinformCommandBindViewModel() - { - Command1 = ReactiveCommand.Create(() => { }); - Command2 = ReactiveCommand.Create(() => { }); - } - - public ReactiveCommand Command1 - { - get => _command1; - set => this.RaiseAndSetIfChanged(ref _command1, value); - } - - public ReactiveCommand Command2 - { - get => _command2; - set => this.RaiseAndSetIfChanged(ref _command2, value); - } - } - - public class WinformCommandBindView : IViewFor - { - public WinformCommandBindView() - { - Command1 = new Button(); - Command2 = new CustomClickableControl(); - } - - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (WinformCommandBindViewModel)value; - } - - public WinformCommandBindViewModel ViewModel { get; set; } - - public Button Command1 { get; protected set; } - - public CustomClickableControl Command2 { get; protected set; } - } } diff --git a/src/ReactiveUI.Tests/Platforms/winforms/DefaultPropertyBindingTests.cs b/src/ReactiveUI.Tests/Platforms/winforms/DefaultPropertyBindingTests.cs index dfea29c854..c880b8551e 100644 --- a/src/ReactiveUI.Tests/Platforms/winforms/DefaultPropertyBindingTests.cs +++ b/src/ReactiveUI.Tests/Platforms/winforms/DefaultPropertyBindingTests.cs @@ -148,134 +148,4 @@ public void SmokeTestWinformControls() disp.Dispose(); } } - - public class FakeWinformViewModel : ReactiveObject, IRoutableViewModel - { - private bool _someBooleanProperty; - private int _someInteger; - private string _someText; - private double _someDouble; - private string _property1; - private string _property2; - private string _property3; - private string _property4; - - public FakeWinformViewModel(IScreen screen = null) - { - HostScreen = screen; - } - - public string UrlPathSegment => "fake"; - - public IScreen HostScreen { get; } - - public int SomeInteger - { - get => _someInteger; - set => this.RaiseAndSetIfChanged(ref _someInteger, value); - } - - public string SomeText - { - get => _someText; - set => this.RaiseAndSetIfChanged(ref _someText, value); - } - - public double SomeDouble - { - get => _someDouble; - set => this.RaiseAndSetIfChanged(ref _someDouble, value); - } - - public string Property1 - { - get => _property1; - set => this.RaiseAndSetIfChanged(ref _property1, value); - } - - public string Property2 - { - get => _property2; - set => this.RaiseAndSetIfChanged(ref _property2, value); - } - - public string Property3 - { - get => _property3; - set => this.RaiseAndSetIfChanged(ref _property3, value); - } - - public string Property4 - { - get => _property4; - set => this.RaiseAndSetIfChanged(ref _property4, value); - } - - public bool BooleanProperty - { - get => _someBooleanProperty; - set => this.RaiseAndSetIfChanged(ref _someBooleanProperty, value); - } - } - - public class FakeWinformsView : Control, IViewFor - { - public FakeWinformsView() - { - Property1 = new Button(); - Property2 = new Label(); - Property3 = new TextBox(); - Property4 = new RichTextBox(); - BooleanProperty = new CheckBox(); - SomeDouble = new TextBox(); - } - - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (FakeWinformViewModel)value; - } - - public FakeWinformViewModel ViewModel { get; set; } - - public Button Property1 { get; } - - public Label Property2 { get; } - - public TextBox Property3 { get; } - - public RichTextBox Property4 { get; } - - public CheckBox BooleanProperty { get; } - - public TextBox SomeDouble { get; } - } -} - -namespace AThirdPartyNamespace -{ - public class ThirdPartyControl : Control - { - private string _value; - - public event EventHandler ValueChanged; - - public string Value - { - get => _value; - set - { - if (_value != value) - { - _value = value; - OnValueChanged(); - } - } - } - - protected virtual void OnValueChanged() - { - ValueChanged?.Invoke(this, EventArgs.Empty); - } - } } diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/AnotherView.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/AnotherView.cs new file mode 100644 index 0000000000..acdedb7daf --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/AnotherView.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests.Winforms +{ + public class AnotherView : ReactiveUI.Winforms.ReactiveUserControl + { + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ContractExampleView.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ContractExampleView.cs new file mode 100644 index 0000000000..0c9280b7de --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ContractExampleView.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests.Winforms +{ + [ViewContract("contract")] + public class ContractExampleView : ReactiveUI.Winforms.ReactiveUserControl + { + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/CustomClickableComponent.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/CustomClickableComponent.cs new file mode 100644 index 0000000000..93915df400 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/CustomClickableComponent.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.ComponentModel; + +namespace ReactiveUI.Tests.Winforms +{ + public class CustomClickableComponent : Component + { + public event EventHandler Click; + + public void PerformClick() + { + Click?.Invoke(this, EventArgs.Empty); + } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/CustomClickableControl.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/CustomClickableControl.cs new file mode 100644 index 0000000000..6f9f5911b6 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/CustomClickableControl.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Windows.Forms; + +namespace ReactiveUI.Tests.Winforms +{ + public class CustomClickableControl : Control + { + public void PerformClick() + { + InvokeOnClick(this, EventArgs.Empty); + } + + public void RaiseMouseClickEvent(MouseEventArgs args) + { + OnMouseClick(args); + } + + public void RaiseMouseUpEvent(MouseEventArgs args) + { + OnMouseUp(args); + } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ExampleView.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ExampleView.cs new file mode 100644 index 0000000000..44b63e18e2 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ExampleView.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests.Winforms +{ + public class ExampleView : ReactiveUI.Winforms.ReactiveUserControl + { + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeView.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeView.cs new file mode 100644 index 0000000000..5446582f12 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeView.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Windows.Forms; + +namespace ReactiveUI.Tests.Winforms +{ + public class FakeView : IViewFor + { + public FakeView() + { + TheTextBox = new TextBox(); + ViewModel = new FakeViewModel(); + } + + public TextBox TheTextBox { get; protected set; } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (FakeViewModel)value; + } + + public FakeViewModel ViewModel { get; set; } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeViewLocator.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeViewLocator.cs new file mode 100644 index 0000000000..db35765c33 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeViewLocator.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; + +namespace ReactiveUI.Tests.Winforms +{ + internal class FakeViewLocator : IViewLocator + { + public Func LocatorFunc { get; set; } + + public IViewFor ResolveView(T viewModel, string contract = null) + where T : class + { + return LocatorFunc(viewModel.GetType()); + } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeViewModel.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeViewModel.cs new file mode 100644 index 0000000000..2a54960d7f --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeViewModel.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Reactive; + +namespace ReactiveUI.Tests.Winforms +{ + public class FakeViewModel : ReactiveObject + { + public FakeViewModel() + { + Cmd = ReactiveCommand.Create(() => { }); + } + + public ReactiveCommand Cmd { get; protected set; } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeWinformViewModel.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeWinformViewModel.cs new file mode 100644 index 0000000000..f801aff6f0 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeWinformViewModel.cs @@ -0,0 +1,76 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests.Winforms +{ + public class FakeWinformViewModel : ReactiveObject, IRoutableViewModel + { + private bool _someBooleanProperty; + private int _someInteger; + private string _someText; + private double _someDouble; + private string _property1; + private string _property2; + private string _property3; + private string _property4; + + public FakeWinformViewModel(IScreen screen = null) + { + HostScreen = screen; + } + + public string UrlPathSegment => "fake"; + + public IScreen HostScreen { get; } + + public int SomeInteger + { + get => _someInteger; + set => this.RaiseAndSetIfChanged(ref _someInteger, value); + } + + public string SomeText + { + get => _someText; + set => this.RaiseAndSetIfChanged(ref _someText, value); + } + + public double SomeDouble + { + get => _someDouble; + set => this.RaiseAndSetIfChanged(ref _someDouble, value); + } + + public string Property1 + { + get => _property1; + set => this.RaiseAndSetIfChanged(ref _property1, value); + } + + public string Property2 + { + get => _property2; + set => this.RaiseAndSetIfChanged(ref _property2, value); + } + + public string Property3 + { + get => _property3; + set => this.RaiseAndSetIfChanged(ref _property3, value); + } + + public string Property4 + { + get => _property4; + set => this.RaiseAndSetIfChanged(ref _property4, value); + } + + public bool BooleanProperty + { + get => _someBooleanProperty; + set => this.RaiseAndSetIfChanged(ref _someBooleanProperty, value); + } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeWinformsView.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeWinformsView.cs new file mode 100644 index 0000000000..71f8dfa882 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/FakeWinformsView.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Windows.Forms; + +namespace ReactiveUI.Tests.Winforms +{ + public class FakeWinformsView : Control, IViewFor + { + public FakeWinformsView() + { + Property1 = new Button(); + Property2 = new Label(); + Property3 = new TextBox(); + Property4 = new RichTextBox(); + BooleanProperty = new CheckBox(); + SomeDouble = new TextBox(); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (FakeWinformViewModel)value; + } + + public FakeWinformViewModel ViewModel { get; set; } + + public Button Property1 { get; } + + public Label Property2 { get; } + + public TextBox Property3 { get; } + + public RichTextBox Property4 { get; } + + public CheckBox BooleanProperty { get; } + + public TextBox SomeDouble { get; } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/NeverUsedView.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/NeverUsedView.cs new file mode 100644 index 0000000000..40fd1838ed --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/NeverUsedView.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests.Winforms +{ + [SingleInstanceView] + public class NeverUsedView : ReactiveUI.Winforms.ReactiveUserControl + { + public NeverUsedView() + { + Instances++; + } + + public static int Instances { get; private set; } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/SingleInstanceExampleView.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/SingleInstanceExampleView.cs new file mode 100644 index 0000000000..7a96d119d3 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/SingleInstanceExampleView.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests.Winforms +{ + [SingleInstanceView] + public class SingleInstanceExampleView : ReactiveUI.Winforms.ReactiveUserControl + { + public SingleInstanceExampleView() + { + Instances++; + } + + public static int Instances { get; private set; } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/SingleInstanceWithContractExampleView.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/SingleInstanceWithContractExampleView.cs new file mode 100644 index 0000000000..d0444b5b1e --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/SingleInstanceWithContractExampleView.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests.Winforms +{ + [ViewContract("contract")] + [SingleInstanceView] + public class SingleInstanceWithContractExampleView : ReactiveUI.Winforms.ReactiveUserControl + { + public SingleInstanceWithContractExampleView() + { + Instances++; + } + + public static int Instances { get; private set; } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/TestControl.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/TestControl.cs new file mode 100644 index 0000000000..4e71ff2416 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/TestControl.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Windows.Forms; + +namespace ReactiveUI.Tests.Winforms +{ + public class TestControl : Control, IActivatable + { + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/TestForm.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/TestForm.cs new file mode 100644 index 0000000000..ff41655440 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/TestForm.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Windows.Forms; + +namespace ReactiveUI.Tests.Winforms +{ + public class TestForm : Form, IActivatable + { + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ThirdPartyControl.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ThirdPartyControl.cs new file mode 100644 index 0000000000..d5a83a1036 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ThirdPartyControl.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Windows.Forms; + +namespace AThirdPartyNamespace +{ + public class ThirdPartyControl : Control + { + private string _value; + + public event EventHandler ValueChanged; + + public string Value + { + get => _value; + set + { + if (_value != value) + { + _value = value; + OnValueChanged(); + } + } + } + + protected virtual void OnValueChanged() + { + ValueChanged?.Invoke(this, EventArgs.Empty); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ViewWithoutMatchingName.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ViewWithoutMatchingName.cs new file mode 100644 index 0000000000..c8e6869913 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/ViewWithoutMatchingName.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests.Winforms +{ + public class ViewWithoutMatchingName : ReactiveUI.Winforms.ReactiveUserControl + { + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/WinformCommandBindView.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/WinformCommandBindView.cs new file mode 100644 index 0000000000..468e4e16f6 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/WinformCommandBindView.cs @@ -0,0 +1,30 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Windows.Forms; + +namespace ReactiveUI.Tests.Winforms +{ + public class WinformCommandBindView : IViewFor + { + public WinformCommandBindView() + { + Command1 = new Button(); + Command2 = new CustomClickableControl(); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (WinformCommandBindViewModel)value; + } + + public WinformCommandBindViewModel ViewModel { get; set; } + + public Button Command1 { get; protected set; } + + public CustomClickableControl Command2 { get; protected set; } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/Mocks/WinformCommandBindViewModel.cs b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/WinformCommandBindViewModel.cs new file mode 100644 index 0000000000..436ab514e3 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/Mocks/WinformCommandBindViewModel.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Reactive; +using System.Reactive.Concurrency; + +namespace ReactiveUI.Tests.Winforms +{ + public class WinformCommandBindViewModel : ReactiveObject + { + private ReactiveCommand _command1; + + private ReactiveCommand _command2; + + public WinformCommandBindViewModel() + { + Command1 = ReactiveCommand.Create(() => { }, outputScheduler: ImmediateScheduler.Instance); + Command2 = ReactiveCommand.Create(() => { }, outputScheduler: ImmediateScheduler.Instance); + } + + public ReactiveCommand Command1 + { + get => _command1; + set => this.RaiseAndSetIfChanged(ref _command1, value); + } + + public ReactiveCommand Command2 + { + get => _command2; + set => this.RaiseAndSetIfChanged(ref _command2, value); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/RoutedViewHostTests.cs b/src/ReactiveUI.Tests/Platforms/winforms/WinFormsRoutedViewHostTests.cs old mode 100755 new mode 100644 similarity index 100% rename from src/ReactiveUI.Tests/Platforms/winforms/RoutedViewHostTests.cs rename to src/ReactiveUI.Tests/Platforms/winforms/WinFormsRoutedViewHostTests.cs diff --git a/src/ReactiveUI.Tests/Platforms/winforms/WinFormsViewDependencyResolverTests.cs b/src/ReactiveUI.Tests/Platforms/winforms/WinFormsViewDependencyResolverTests.cs new file mode 100644 index 0000000000..a5b3c05185 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/winforms/WinFormsViewDependencyResolverTests.cs @@ -0,0 +1,108 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Splat; +using Xunit; + +using FactAttribute = Xunit.WpfFactAttribute; + +namespace ReactiveUI.Tests.Winforms +{ + public sealed class WinFormsViewDependencyResolverTests : IDisposable + { + private readonly IMutableDependencyResolver _resolver; + + public WinFormsViewDependencyResolverTests() + { + _resolver = new ModernDependencyResolver(); + _resolver.InitializeSplat(); + _resolver.InitializeReactiveUI(); + _resolver.RegisterViewsForViewModels(GetType().Assembly); + } + + [Fact] + public void RegisterViewsForViewModelShouldRegisterAllViews() + { + using (_resolver.WithResolver()) + { + Assert.Single(_resolver.GetServices>()); + Assert.Single(_resolver.GetServices>()); + Assert.Single(_resolver.GetServices>()); + Assert.Single(_resolver.GetServices>()); + } + } + + [Fact] + public void NonContractRegistrationsShouldResolveCorrectly() + { + using (_resolver.WithResolver()) + { + Assert.IsType(_resolver.GetService>()); + } + } + + public void Dispose() + { + _resolver?.Dispose(); + } + + [Fact] + public void ContractRegistrationsShouldResolveCorrectly() + { + using (_resolver.WithResolver()) + { + Assert.IsType(_resolver.GetService(typeof(IViewFor), "contract")); + } + } + + [Fact] + public void SingleInstanceViewsShouldOnlyBeInstantiatedOnce() + { + using (_resolver.WithResolver()) + { + Assert.Equal(0, SingleInstanceExampleView.Instances); + + var instance = _resolver.GetService(typeof(IViewFor)); + Assert.Equal(1, SingleInstanceExampleView.Instances); + + var instance2 = _resolver.GetService(typeof(IViewFor)); + Assert.Equal(1, SingleInstanceExampleView.Instances); + + Assert.Same(instance, instance2); + } + } + + [Fact] + public void SingleInstanceViewsWithContractShouldResolveCorrectly() + { + using (_resolver.WithResolver()) + { + Assert.Equal(0, SingleInstanceWithContractExampleView.Instances); + + var instance = _resolver.GetService(typeof(IViewFor), "contract"); + Assert.Equal(1, SingleInstanceWithContractExampleView.Instances); + + var instance2 = _resolver.GetService(typeof(IViewFor), "contract"); + Assert.Equal(1, SingleInstanceWithContractExampleView.Instances); + + Assert.Same(instance, instance2); + } + } + + [Fact] + public void SingleInstanceViewsShouldOnlyBeInstantiatedWhenRequested() + { + using (_resolver.WithResolver()) + { + Assert.Equal(0, NeverUsedView.Instances); + } + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/winforms/ViewModelViewHostTests.cs b/src/ReactiveUI.Tests/Platforms/winforms/WinFormsViewModelViewHostTests.cs similarity index 91% rename from src/ReactiveUI.Tests/Platforms/winforms/ViewModelViewHostTests.cs rename to src/ReactiveUI.Tests/Platforms/winforms/WinFormsViewModelViewHostTests.cs index 31a96e380c..947fb828de 100644 --- a/src/ReactiveUI.Tests/Platforms/winforms/ViewModelViewHostTests.cs +++ b/src/ReactiveUI.Tests/Platforms/winforms/WinFormsViewModelViewHostTests.cs @@ -3,7 +3,6 @@ // The .NET Foundation 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.Linq; using System.Windows.Forms; using ReactiveUI.Winforms; @@ -92,15 +91,4 @@ public void ShouldNotCacheViewWhenDisabled() Assert.False(ReferenceEquals(cachedView, target.CurrentView)); } } - - internal class FakeViewLocator : IViewLocator - { - public Func LocatorFunc { get; set; } - - public IViewFor ResolveView(T viewModel, string contract = null) - where T : class - { - return LocatorFunc(viewModel.GetType()); - } - } } diff --git a/src/ReactiveUI.Tests/Platforms/wpf/API/ApiApprovalTests.Wpf.net461.approved.txt b/src/ReactiveUI.Tests/Platforms/wpf/API/ApiApprovalTests.Wpf.net461.approved.txt new file mode 100644 index 0000000000..1fd8a824c2 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/wpf/API/ApiApprovalTests.Wpf.net461.approved.txt @@ -0,0 +1,124 @@ +[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.6.1", FrameworkDisplayName=".NET Framework 4.6.1")] +[assembly: System.Windows.Markup.XmlnsDefinitionAttribute("http://reactiveui.net", "ReactiveUI")] +[assembly: System.Windows.ThemeInfoAttribute(System.Windows.ResourceDictionaryLocation.None, System.Windows.ResourceDictionaryLocation.SourceAssembly)] +namespace ReactiveUI +{ + public class ActivationForViewFetcher : ReactiveUI.IActivationForViewFetcher + { + public ActivationForViewFetcher() { } + public System.IObservable GetActivationForView(ReactiveUI.IActivatable view) { } + public int GetAffinityForView(System.Type view) { } + } + public class AutoDataTemplateBindingHook : ReactiveUI.IPropertyBindingHook + { + public AutoDataTemplateBindingHook() { } + public static System.Lazy DefaultItemTemplate { get; } + public bool ExecuteHook(object source, object target, System.Func[]> getCurrentViewModelProperties, System.Func[]> getCurrentViewProperties, ReactiveUI.BindingDirection direction) { } + } + public class AutoSuspendHelper : Splat.IEnableLogger + { + public AutoSuspendHelper(System.Windows.Application app) { } + public System.TimeSpan IdleTimeout { get; set; } + } + [System.FlagsAttribute()] + public enum BooleanToVisibilityHint + { + None = 0, + Inverse = 2, + UseHidden = 4, + } + public class BooleanToVisibilityTypeConverter : ReactiveUI.IBindingTypeConverter, Splat.IEnableLogger + { + public BooleanToVisibilityTypeConverter() { } + public int GetAffinityForObjects(System.Type fromType, System.Type toType) { } + public bool TryConvert(object from, System.Type toType, object conversionHint, out object result) { } + } + public class DependencyObjectObservableForProperty : ReactiveUI.ICreatesObservableForProperty, Splat.IEnableLogger + { + public DependencyObjectObservableForProperty() { } + public int GetAffinityForObject(System.Type type, string propertyName, bool beforeChanged = False) { } + public System.IObservable> GetNotificationForProperty(object sender, System.Linq.Expressions.Expression expression, string propertyName, bool beforeChanged = False) { } + } + public class PlatformOperations + { + public PlatformOperations() { } + public string GetOrientation() { } + } + public abstract class ReactiveUserControl : System.Windows.Controls.UserControl, ReactiveUI.IActivatable, ReactiveUI.IViewFor, ReactiveUI.IViewFor + where TViewModel : class + { + public static readonly System.Windows.DependencyProperty ViewModelProperty; + protected ReactiveUserControl() { } + public TViewModel BindingRoot { get; } + public TViewModel ViewModel { get; set; } + } + public abstract class ReactiveWindow : System.Windows.Window, ReactiveUI.IActivatable, ReactiveUI.IViewFor, ReactiveUI.IViewFor + where TViewModel : class + { + public static readonly System.Windows.DependencyProperty ViewModelProperty; + protected ReactiveWindow() { } + public TViewModel BindingRoot { get; } + public TViewModel ViewModel { get; set; } + } + public class RoutedViewHost : ReactiveUI.TransitioningContentControl, ReactiveUI.IActivatable, Splat.IEnableLogger + { + public static readonly System.Windows.DependencyProperty DefaultContentProperty; + public static readonly System.Windows.DependencyProperty RouterProperty; + public static readonly System.Windows.DependencyProperty ViewContractObservableProperty; + public RoutedViewHost() { } + public object DefaultContent { get; set; } + public ReactiveUI.RoutingState Router { get; set; } + public System.IObservable ViewContractObservable { get; set; } + public ReactiveUI.IViewLocator ViewLocator { get; set; } + } + [System.Windows.TemplatePartAttribute(Name="PART_Container", Type=typeof(System.Windows.FrameworkElement))] + [System.Windows.TemplatePartAttribute(Name="PART_CurrentContentPresentationSite", Type=typeof(System.Windows.Controls.ContentPresenter))] + [System.Windows.TemplatePartAttribute(Name="PART_PreviousContentPresentationSite", Type=typeof(System.Windows.Controls.ContentPresenter))] + [System.Windows.TemplateVisualStateAttribute(GroupName="PresentationStates", Name="Normal")] + public class TransitioningContentControl : System.Windows.Controls.ContentControl + { + public static readonly System.Windows.DependencyProperty TransitionPartProperty; + public static readonly System.Windows.DependencyProperty TransitionProperty; + public TransitioningContentControl() { } + public ReactiveUI.TransitioningContentControl.TransitionType Transition { get; set; } + public ReactiveUI.TransitioningContentControl.TransitionPartType TransitionPart { get; set; } + public event System.Windows.RoutedEventHandler TransitionCompleted; + public event System.Windows.RoutedEventHandler TransitionStarted; + public override void OnApplyTemplate() { } + protected override void OnContentChanged(object oldContent, object newContent) { } + public enum TransitionPartType + { + Out = 0, + In = 1, + OutIn = 2, + } + public enum TransitionType + { + Fade = 0, + FadeDown = 1, + SlideLeft = 2, + } + } + public class ViewModelViewHost : ReactiveUI.TransitioningContentControl, ReactiveUI.IActivatable, ReactiveUI.IViewFor, Splat.IEnableLogger, System.IDisposable + { + public static readonly System.Windows.DependencyProperty DefaultContentProperty; + public static readonly System.Windows.DependencyProperty ViewContractObservableProperty; + public static readonly System.Windows.DependencyProperty ViewModelProperty; + public ViewModelViewHost() { } + public object DefaultContent { get; set; } + public string ViewContract { get; set; } + public System.IObservable ViewContractObservable { get; set; } + public ReactiveUI.IViewLocator ViewLocator { get; set; } + public object ViewModel { get; set; } + public void Dispose() { } + protected virtual void Dispose(bool isDisposing) { } + } +} +namespace ReactiveUI.Wpf +{ + public class Registrations + { + public Registrations() { } + public void Register(System.Action, System.Type> registerFunction) { } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/Platforms/wpf/API/WpfApiApprovalTests.cs b/src/ReactiveUI.Tests/Platforms/wpf/API/WpfApiApprovalTests.cs new file mode 100644 index 0000000000..e9a8a2b5a6 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/wpf/API/WpfApiApprovalTests.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2019 .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. +// 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.Text; +using System.Threading.Tasks; +using Xunit; + +namespace ReactiveUI.Tests.Wpf +{ + [ExcludeFromCodeCoverage] + public class WpfApiApprovalTests : ApiApprovalBase + { + [Fact] + public void Wpf() + { + CheckApproval(typeof(ReactiveWindow<>).Assembly); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/wpf/Mocks/CommandBindingView.cs b/src/ReactiveUI.Tests/Platforms/wpf/Mocks/CommandBindingView.cs new file mode 100644 index 0000000000..94ae129ccf --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/wpf/Mocks/CommandBindingView.cs @@ -0,0 +1,36 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Controls; +using ReactiveUI.Tests.Xaml; + +namespace ReactiveUI.Tests.Wpf +{ + public class CommandBindingView : IViewFor + { + public CommandBindingView() + { + Command1 = new CustomClickButton(); + Command2 = new Image(); + } + + object IViewFor.ViewModel + { + get => ViewModel; + set => ViewModel = (CommandBindingViewModel)value; + } + + public CommandBindingViewModel ViewModel { get; set; } + + public CustomClickButton Command1 { get; protected set; } + + public Image Command2 { get; protected set; } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/wpf/Mocks/CommandBindingViewModel.cs b/src/ReactiveUI.Tests/Platforms/wpf/Mocks/CommandBindingViewModel.cs new file mode 100644 index 0000000000..7e6f08afcb --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/wpf/Mocks/CommandBindingViewModel.cs @@ -0,0 +1,49 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests.Wpf +{ + public class CommandBindingViewModel : ReactiveObject + { + private ReactiveCommand _Command1; + private ReactiveCommand _Command2; + + private int _value; + + public CommandBindingViewModel() + { + Command1 = ReactiveCommand.Create(_ => Unit.Default, outputScheduler: ImmediateScheduler.Instance); + Command2 = ReactiveCommand.Create(() => { }, outputScheduler: ImmediateScheduler.Instance); + } + + public ReactiveCommand Command1 + { + get => _Command1; + set => this.RaiseAndSetIfChanged(ref _Command1, value); + } + + public ReactiveCommand Command2 + { + get => _Command2; + set => this.RaiseAndSetIfChanged(ref _Command2, value); + } + + public FakeNestedViewModel NestedViewModel { get; set; } + + public int Value + { + get => _value; + set => this.RaiseAndSetIfChanged(ref _value, value); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/wpf/Mocks/ExampleWindowView.cs b/src/ReactiveUI.Tests/Platforms/wpf/Mocks/ExampleWindowView.cs new file mode 100644 index 0000000000..bcb62d5bbd --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/wpf/Mocks/ExampleWindowView.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests.Wpf +{ + public class ExampleWindowView : ReactiveWindow + { + } +} diff --git a/src/ReactiveUI.Tests/Platforms/wpf/Mocks/WpfTestUserControl.cs b/src/ReactiveUI.Tests/Platforms/wpf/Mocks/WpfTestUserControl.cs new file mode 100644 index 0000000000..c111f6f344 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/wpf/Mocks/WpfTestUserControl.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using System.Windows.Controls; + +namespace ReactiveUI.Tests.Wpf +{ + public class WpfTestUserControl : UserControl, IActivatable + { + } +} diff --git a/src/ReactiveUI.Tests/Platforms/xaml/ActivationForViewFetcherTest.cs b/src/ReactiveUI.Tests/Platforms/wpf/WpfActivationForViewFetcherTest.cs similarity index 86% rename from src/ReactiveUI.Tests/Platforms/xaml/ActivationForViewFetcherTest.cs rename to src/ReactiveUI.Tests/Platforms/wpf/WpfActivationForViewFetcherTest.cs index cd24384576..b1f58ddb2e 100644 --- a/src/ReactiveUI.Tests/Platforms/xaml/ActivationForViewFetcherTest.cs +++ b/src/ReactiveUI.Tests/Platforms/wpf/WpfActivationForViewFetcherTest.cs @@ -4,26 +4,22 @@ // See the LICENSE file in the project root for full license information. using System; -using System.Collections.Generic; -using System.Linq; using System.Reactive.Concurrency; -using System.Text; -using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; + using DynamicData; -using Microsoft.Reactive.Testing; -using ReactiveUI.Testing; using Xunit; -namespace ReactiveUI.Tests +using FactAttribute = Xunit.WpfFactAttribute; + +namespace ReactiveUI.Tests.Wpf { - public class ActivationForViewFetcherTest + public class WpfActivationForViewFetcherTest { - [WpfFact] + [Fact] public void FrameworkElementIsActivatedAndDeactivated() { - var uc = new TestUserControl(); + var uc = new WpfTestUserControl(); var activation = new ActivationForViewFetcher(); var obs = activation.GetActivationForView(uc); @@ -44,10 +40,10 @@ public void FrameworkElementIsActivatedAndDeactivated() new[] { true, false }.AssertAreEqual(activated); } - [WpfFact] + [Fact] public void IsHitTestVisibleActivatesFrameworkElement() { - var uc = new TestUserControl(); + var uc = new WpfTestUserControl(); uc.IsHitTestVisible = false; var activation = new ActivationForViewFetcher(); @@ -76,10 +72,10 @@ public void IsHitTestVisibleActivatesFrameworkElement() new[] { true, false }.AssertAreEqual(activated); } - [WpfFact] + [Fact] public void IsHitTestVisibleDeactivatesFrameworkElement() { - var uc = new TestUserControl(); + var uc = new WpfTestUserControl(); var activation = new ActivationForViewFetcher(); var obs = activation.GetActivationForView(uc); @@ -97,10 +93,10 @@ public void IsHitTestVisibleDeactivatesFrameworkElement() new[] { true, false }.AssertAreEqual(activated); } - [WpfFact] + [Fact] public void FrameworkElementIsActivatedAndDeactivatedWithHitTest() { - var uc = new TestUserControl(); + var uc = new WpfTestUserControl(); var activation = new ActivationForViewFetcher(); var obs = activation.GetActivationForView(uc); @@ -130,9 +126,5 @@ public void FrameworkElementIsActivatedAndDeactivatedWithHitTest() new[] { true, false, true, false }.AssertAreEqual(activated); } - - public class TestUserControl : UserControl, IActivatable - { - } } } diff --git a/src/ReactiveUI.Tests/Platforms/wpf/WpfCommandBindingImplementationTests.cs b/src/ReactiveUI.Tests/Platforms/wpf/WpfCommandBindingImplementationTests.cs new file mode 100644 index 0000000000..4455eae772 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/wpf/WpfCommandBindingImplementationTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using ReactiveUI.Tests.Xaml; +using Xunit; + +using FactAttribute = Xunit.WpfFactAttribute; + +namespace ReactiveUI.Tests.Wpf +{ + public class WpfCommandBindingImplementationTests + { + [Fact] + public void CommandBindToExplicitEventWireup() + { + var vm = new CommandBindingViewModel(); + var view = new CommandBindingView { ViewModel = vm }; + + var invokeCount = 0; + vm.Command2.Subscribe(_ => invokeCount++); + + var disp = view.BindCommand(vm, x => x.Command2, x => x.Command2, "MouseUp"); + + view.Command2.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left) { RoutedEvent = UIElement.MouseUpEvent }); + + disp.Dispose(); + + view.Command2.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, 0, MouseButton.Left) { RoutedEvent = UIElement.MouseUpEvent }); + Assert.Equal(1, invokeCount); + } + } +} diff --git a/src/ReactiveUI.Tests/Platforms/wpf/WpfViewDependencyResolverTests.cs b/src/ReactiveUI.Tests/Platforms/wpf/WpfViewDependencyResolverTests.cs new file mode 100644 index 0000000000..f1c241abc5 --- /dev/null +++ b/src/ReactiveUI.Tests/Platforms/wpf/WpfViewDependencyResolverTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Splat; +using Xunit; + +using FactAttribute = Xunit.WpfFactAttribute; + +namespace ReactiveUI.Tests.Wpf +{ + public sealed class WpfViewDependencyResolverTests : IDisposable + { + private readonly IMutableDependencyResolver _resolver; + + public WpfViewDependencyResolverTests() + { + _resolver = new ModernDependencyResolver(); + _resolver.InitializeSplat(); + _resolver.InitializeReactiveUI(); + _resolver.RegisterViewsForViewModels(GetType().Assembly); + } + + [Fact] + public void RegisterViewsForViewModelShouldRegisterAllViews() + { + using (_resolver.WithResolver()) + { + Assert.Single(_resolver.GetServices>()); + } + } + + public void Dispose() + { + _resolver?.Dispose(); + } + } +} diff --git a/src/ReactiveUI.Tests/ReactiveCollectionTest.cs b/src/ReactiveUI.Tests/ReactiveCollectionTest.cs deleted file mode 100644 index e9ab8f48d9..0000000000 --- a/src/ReactiveUI.Tests/ReactiveCollectionTest.cs +++ /dev/null @@ -1,2289 +0,0 @@ -// Copyright (c) 2019 .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. -// See the LICENSE file in the project root for full license information. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Reactive; -using System.Reactive.Concurrency; -using System.Reactive.Disposables; -using System.Reactive.Linq; -using System.Reactive.Subjects; -using System.Threading; -using System.Windows; -using System.Windows.Controls; - -using Microsoft.Reactive.Testing; - -using ReactiveUI.Legacy; -using ReactiveUI.Testing; - -using Splat; - -using Xunit; - -#pragma warning disable CS0618 // Item has been marked as Obsolete - -namespace ReactiveUI.Tests.Legacy -{ - public class FakeCollectionModel : ReactiveObject - { - private bool _isHidden; - - private int _someNumber; - - public bool IsHidden - { - get => _isHidden; - set => this.RaiseAndSetIfChanged(ref _isHidden, value); - } - - public int SomeNumber - { - get => _someNumber; - set => this.RaiseAndSetIfChanged(ref _someNumber, value); - } - } - - public class FakeCollectionViewModel : ReactiveObject - { - private readonly ObservableAsPropertyHelper _numberAsString; - - public FakeCollectionViewModel(FakeCollectionModel model) - { - Model = model; - - this.WhenAny(x => x.Model.SomeNumber, x => x.Value.ToString()).ToProperty(this, x => x.NumberAsString, out _numberAsString); - } - - public FakeCollectionModel Model { get; protected set; } - - public string NumberAsString => _numberAsString.Value; - } - - public class ReactiveCollectionTest - { - [Fact] - public void ActOnEveryObjectShouldHandleAddingItems() - { - var testObj = new TestFixture - { - NullableInt = null - }; - var fixture = new ReactiveList(); - - fixture.ActOnEveryObject>(addedObj => { addedObj.NullableInt = 1; }, removedObj => { removedObj.NullableInt = 0; }); - - fixture.Add(testObj); - - Assert.Equal(1, testObj.NullableInt); - } - - [Fact] - public void ActOnEveryObjectShouldHandleAddUnderSuppressedNotifications() - { - var testObj = new TestFixture - { - NullableInt = null - }; - var fixture = new ReactiveList(); - - fixture.ActOnEveryObject>(addedObj => { addedObj.NullableInt = 1; }, removedObj => { removedObj.NullableInt = 0; }); - - using (fixture.SuppressChangeNotifications()) - { - fixture.Add(testObj); - } - - Assert.Equal(1, testObj.NullableInt); - } - - [Fact] - public void ActOnEveryObjectShouldHandleClear() - { - var testObj = new TestFixture - { - NullableInt = null - }; - var fixture = new ReactiveList - { - testObj - }; - - fixture.ActOnEveryObject>(addedObj => { addedObj.NullableInt = 1; }, removedObj => { removedObj.NullableInt = 0; }); - - fixture.Clear(); - - Assert.Equal(0, testObj.NullableInt); - } - - [Fact] - public void ActOnEveryObjectShouldHandleClearUnderSuppressedNotifications() - { - var testObj = new TestFixture - { - NullableInt = null - }; - var fixture = new ReactiveList - { - testObj - }; - - fixture.ActOnEveryObject>(addedObj => { addedObj.NullableInt = 1; }, removedObj => { removedObj.NullableInt = 0; }); - - using (fixture.SuppressChangeNotifications()) - { - fixture.Clear(); - } - - Assert.Equal(0, testObj.NullableInt); - } - - // ActOnEveryObject - [Fact] - public void ActOnEveryObjectShouldHandlePreexistingItems() - { - var testObj = new TestFixture - { - NullableInt = null - }; - var fixture = new ReactiveList - { - testObj - }; - - fixture.ActOnEveryObject>(addedObj => { addedObj.NullableInt = 1; }, removedObj => { removedObj.NullableInt = 0; }); - - Assert.Equal(1, testObj.NullableInt); - } - - [Fact] - public void ActOnEveryObjectShouldHandleRemoveUnderSuppressedNotifications() - { - var testObj = new TestFixture - { - NullableInt = null - }; - var fixture = new ReactiveList - { - testObj - }; - - fixture.ActOnEveryObject>(addedObj => { addedObj.NullableInt = 1; }, removedObj => { removedObj.NullableInt = 0; }); - - using (fixture.SuppressChangeNotifications()) - { - fixture.Remove(testObj); - } - - Assert.Equal(0, testObj.NullableInt); - } - - [Fact] - public void ActOnEveryObjectShouldHandleRemovingItems() - { - var testObj = new TestFixture - { - NullableInt = null - }; - var fixture = new ReactiveList - { - testObj - }; - - fixture.ActOnEveryObject>(addedObj => { addedObj.NullableInt = 1; }, removedObj => { removedObj.NullableInt = 0; }); - - fixture.Remove(testObj); - - Assert.Equal(0, testObj.NullableInt); - } - - [Fact] - public void AddRangeSmokeTest() - { - var fixture = new ReactiveList(); - var output = fixture.CreateDerivedCollection(x => "Prefix" + x); - - fixture.Add("Bamf"); - Assert.Equal(1, fixture.Count); - Assert.Equal(1, output.Count); - Assert.Equal("Bamf", fixture[0]); - Assert.Equal("PrefixBamf", output[0]); - - fixture.AddRange(Enumerable.Repeat("Bar", 4)); - Assert.Equal(5, fixture.Count); - Assert.Equal(5, output.Count); - Assert.Equal("Bamf", fixture[0]); - Assert.Equal("PrefixBamf", output[0]); - - Assert.True(fixture.Skip(1).All(x => x == "Bar")); - Assert.True(output.Skip(1).All(x => x == "PrefixBar")); - - // Trigger the Reset by adding a ton of items - fixture.AddRange(Enumerable.Repeat("Bar", 35)); - Assert.Equal(40, fixture.Count); - Assert.Equal(40, output.Count); - Assert.Equal("Bamf", fixture[0]); - Assert.Equal("PrefixBamf", output[0]); - } - - [Fact] - public void ChangeTrackingItemsShouldBeTrackedEvenWhenSuppressed() - { - var input = new TestFixture(); - var fixture = new ReactiveList - { - ChangeTrackingEnabled = true - }; - - var changes = fixture.ItemChanged.CreateCollection(); - Assert.Equal(0, changes.Count); - - input.IsOnlyOneWord = "foo"; - Assert.Equal(0, changes.Count); - - using (fixture.SuppressChangeNotifications()) - { - fixture.Add(input); - - input.IsOnlyOneWord = "bar"; - Assert.Equal(0, changes.Count); - } - - // Even though we added it during a suppression, we should still - // get notifications now that the suppression is over - input.IsOnlyOneWord = "baz"; - Assert.Equal(1, changes.Count); - - fixture.RemoveAt(0); - input.IsOnlyOneWord = "bamf"; - Assert.Equal(1, changes.Count); - } - - [Fact] - public void ChangeTrackingShouldApplyOnAddRangedItems() - { - var fixture = new ReactiveList - { - new TestFixture() - }; - fixture.ChangeTrackingEnabled = true; - - var reset = fixture.ShouldReset.CreateCollection(); - var itemChanged = fixture.ItemChanged.CreateCollection(); - Assert.Equal(0, reset.Count); - - fixture[0].IsNotNullString = "Foo"; - Assert.Equal(0, reset.Count); - Assert.Equal(1, itemChanged.Count); - - fixture.AddRange( - Enumerable.Range(0, 15).Select( - x => new TestFixture - { - IsOnlyOneWord = x.ToString() - })); - Assert.Equal(1, reset.Count); - Assert.Equal(1, itemChanged.Count); - - fixture[0].IsNotNullString = "Bar"; - Assert.Equal(1, reset.Count); - Assert.Equal(2, itemChanged.Count); - - fixture[5].IsNotNullString = "Baz"; - Assert.Equal(1, reset.Count); - Assert.Equal(3, itemChanged.Count); - } - - [Fact] - public void ChangeTrackingShouldFireNotifications() - { - var fixture = new ReactiveList - { - ChangeTrackingEnabled = true - }; - var before_output = new List>(); - var output = new List>(); - var item1 = new TestFixture - { - IsOnlyOneWord = "Foo" - }; - var item2 = new TestFixture - { - IsOnlyOneWord = "Bar" - }; - - fixture.ItemChanging.Subscribe(x => { before_output.Add(new Tuple(x.Sender, x.PropertyName)); }); - - fixture.ItemChanged.Subscribe(x => { output.Add(new Tuple(x.Sender, x.PropertyName)); }); - - fixture.Add(item1); - fixture.Add(item2); - - item1.IsOnlyOneWord = "Baz"; - Assert.Equal(1, output.Count); - item2.IsNotNullString = "FooBar"; - Assert.Equal(2, output.Count); - - fixture.Remove(item2); - item2.IsNotNullString = "FooBarBaz"; - Assert.Equal(2, output.Count); - - fixture.ChangeTrackingEnabled = false; - item1.IsNotNullString = "Bamf"; - Assert.Equal(2, output.Count); - - new[] { item1, item2 }.AssertAreEqual(output.Select(x => x.Item1)); - new[] { item1, item2 }.AssertAreEqual(before_output.Select(x => x.Item1)); - new[] { "IsOnlyOneWord", "IsNotNullString" }.AssertAreEqual(output.Select(x => x.Item2)); - } - - [Fact] - public void ChangeTrackingShouldStopWhenAnObjectIsReplacedAndChangeNotificationIsSuppressed() - { - var fixture = new ReactiveList - { - ChangeTrackingEnabled = true - }; - - var before_output = new List>(); - var output = new List>(); - var item1 = new TestFixture - { - IsOnlyOneWord = "Foo" - }; - var item2 = new TestFixture - { - IsOnlyOneWord = "Bar" - }; - - fixture.ItemChanging.Subscribe(x => { before_output.Add(new Tuple(x.Sender, x.PropertyName)); }); - - fixture.ItemChanged.Subscribe(x => { output.Add(new Tuple(x.Sender, x.PropertyName)); }); - - fixture.Add(item1); - - item1.IsOnlyOneWord = "Baz"; - Assert.Equal(1, output.Count); - item2.IsNotNullString = "FooBar"; - Assert.Equal(1, output.Count); - - using (IReactiveObjectExtensions.SuppressChangeNotifications(fixture)) - { - fixture[0] = item2; - } - - item1.IsOnlyOneWord = "FooAgain"; - Assert.Equal(1, output.Count); - item2.IsNotNullString = "FooBarBaz"; - Assert.Equal(2, output.Count); - - new[] { item1, item2 }.AssertAreEqual(output.Select(x => x.Item1)); - new[] { item1, item2 }.AssertAreEqual(before_output.Select(x => x.Item1)); - new[] { "IsOnlyOneWord", "IsNotNullString" }.AssertAreEqual(output.Select(x => x.Item2)); - } - - [Fact] - public void ChangeTrackingShouldWorkWhenAddingTheSameThingMoreThanOnce() - { - var fixture = new ReactiveList - { - ChangeTrackingEnabled = true - }; - var output = new List>(); - var item1 = new TestFixture - { - IsOnlyOneWord = "Foo" - }; - - fixture.ItemChanged.Subscribe(x => { output.Add(new Tuple(x.Sender, x.PropertyName)); }); - - fixture.Add(item1); - fixture.Add(item1); - fixture.Add(item1); - - item1.IsOnlyOneWord = "Bar"; - Assert.Equal(1, output.Count); - - fixture.RemoveAt(0); - - item1.IsOnlyOneWord = "Baz"; - Assert.Equal(2, output.Count); - - fixture.RemoveAt(0); - fixture.RemoveAt(0); - - // We've completely removed item1, we shouldn't be seeing any - // notifications from it - item1.IsOnlyOneWord = "Bamf"; - Assert.Equal(2, output.Count); - - fixture.ChangeTrackingEnabled = false; - fixture.Add(item1); - fixture.Add(item1); - fixture.Add(item1); - fixture.ChangeTrackingEnabled = true; - - item1.IsOnlyOneWord = "Bonk"; - Assert.Equal(3, output.Count); - } - - [Fact] - public void CollectionCountChangedFiresWhenClearing() - { - var items = new ReactiveList(new[] { new object() }); - var countChanged = false; - items.CountChanged.Subscribe(_ => { countChanged = true; }); - - items.Clear(); - - Assert.True(countChanged); - } - - [Fact] - public void CollectionCountChangedTest() - { - var fixture = new ReactiveList(); - var before_output = new List(); - var output = new List(); - - fixture.CountChanging.Subscribe(before_output.Add); - fixture.CountChanged.Subscribe(output.Add); - - fixture.Add(10); - fixture.Add(20); - fixture.Add(30); - fixture.RemoveAt(1); - fixture.Clear(); - - var before_results = new[] { 0, 1, 2, 3, 2 }; - Assert.Equal(before_results.Length, before_output.Count); - before_results.AssertAreEqual(before_output); - - var results = new[] { 1, 2, 3, 2, 0 }; - Assert.Equal(results.Length, output.Count); - results.AssertAreEqual(output); - } - - [Fact] - public void CollectionsShouldntShareSubscriptions() - { - var fixture1 = new ReactiveList - { - ChangeTrackingEnabled = true - }; - var fixture2 = new ReactiveList - { - ChangeTrackingEnabled = true - }; - var item1 = new TestFixture - { - IsOnlyOneWord = "Foo" - }; - var output1 = new List>(); - var output2 = new List>(); - - fixture1.ItemChanged.Subscribe(x => { output1.Add(new Tuple(x.Sender, x.PropertyName)); }); - - fixture2.ItemChanged.Subscribe(x => { output2.Add(new Tuple(x.Sender, x.PropertyName)); }); - - fixture1.Add(item1); - fixture1.Add(item1); - fixture2.Add(item1); - fixture2.Add(item1); - - item1.IsOnlyOneWord = "Bar"; - Assert.Equal(1, output1.Count); - Assert.Equal(1, output2.Count); - - fixture2.RemoveAt(0); - - item1.IsOnlyOneWord = "Baz"; - Assert.Equal(2, output1.Count); - Assert.Equal(2, output2.Count); - } - - [Fact] - public void CountPropertyIsNotAmbiguous() - { - IReactiveList reactiveList = new ReactiveList(); - Assert.Equal(0, reactiveList.Count); - IList list = reactiveList; - Assert.Equal(0, list.Count); - - ICollection collection = new ReactiveList(); - var l = (IList)collection; - Assert.Same(collection, l); - } - - [Fact] - public void CreateCollectionWithoutTimer() - { - var input = new[] { "Foo", "Bar", "Baz", "Bamf" }; - var fixture = new TestScheduler().With( - sched => - { - var f = input.ToObservable(sched).CreateCollection(); - - sched.Start(); - return f; - }); - - input.AssertAreEqual(fixture); - } - - [Fact] - public void CreateCollectionWithTimer() - { - var input = new[] { "Foo", "Bar", "Baz", "Bamf" }; - var sched = new TestScheduler(); - - using (TestUtils.WithScheduler(sched)) - { - IReactiveDerivedList fixture = input.ToObservable(sched).CreateCollection(TimeSpan.FromSeconds(0.5)); - sched.AdvanceToMs(1005); - fixture.AssertAreEqual(input.Take(2)); - - sched.AdvanceToMs(1505); - fixture.AssertAreEqual(input.Take(3)); - - sched.AdvanceToMs(10000); - fixture.AssertAreEqual(input); - } - } - - [WpfFact] - public void DataboundReactiveListDoesNotThrowForAddRange() - { - var vm = new PropertyBindViewModel(); - var view = new PropertyBindView - { - ViewModel = vm - }; - var fixture = new PropertyBinderImplementation(); - fixture.OneWayBind(vm, view, m => m.SomeCollectionOfStrings, v => v.FakeItemsControl.ItemsSource); - - // eliminate the ResetChangeThreshold from the equation - vm.SomeCollectionOfStrings.ResetChangeThreshold = int.MinValue; - - // Within the reset threshold - vm.SomeCollectionOfStrings.AddRange(Create(5)); - vm.SomeCollectionOfStrings.AddRange(Create(20)); - - IEnumerable Create(int numElements) => Enumerable.Range(1, numElements).Select(i => $"item_{i}"); - } - - [WpfFact] - public void DataboundReactiveListDoesNotThrowForInsertRange() - { - var vm = new PropertyBindViewModel(); - var view = new PropertyBindView - { - ViewModel = vm - }; - var fixture = new PropertyBinderImplementation(); - fixture.OneWayBind(vm, view, m => m.SomeCollectionOfStrings, v => v.FakeItemsControl.ItemsSource); - vm.SomeCollectionOfStrings.ResetChangeThreshold = int.MinValue; - - foreach (var item in Create(5)) - { - vm.SomeCollectionOfStrings.Add(item); - } - - // within reset threshold - vm.SomeCollectionOfStrings.InsertRange(2, Create(5)); - - // outside reset threshold - vm.SomeCollectionOfStrings.InsertRange(2, Create(20)); - - IEnumerable Create(int numElements) => Enumerable.Range(1, numElements).Select(i => $"item_{i}"); - } - - [WpfFact] - public void DataboundReactiveListDoesNotThrowForRemoveRange() - { - var vm = new PropertyBindViewModel(); - var view = new PropertyBindView - { - ViewModel = vm - }; - var fixture = new PropertyBinderImplementation(); - fixture.OneWayBind(vm, view, m => m.SomeCollectionOfStrings, v => v.FakeItemsControl.ItemsSource); - vm.SomeCollectionOfStrings.ResetChangeThreshold = int.MinValue; - - foreach (var item in Enumerable.Range(1, 40).Select(i => $"item_{i}")) - { - vm.SomeCollectionOfStrings.Add(item); - } - - // within reset threshold - vm.SomeCollectionOfStrings.RemoveRange(2, 5); - - // outside reset threshold - vm.SomeCollectionOfStrings.RemoveRange(2, 20); - } - - [Fact] - public void DerivedCollectionFilterTest() - { - var models = new ReactiveList( - new[] { 0, 1, 2, 3, 4, }.Select( - x => new FakeCollectionModel - { - SomeNumber = x - })) - { - ChangeTrackingEnabled = true - }; - - var viewModels = models.CreateDerivedCollection(x => new FakeCollectionViewModel(x), x => !x.IsHidden); - Assert.Equal(5, viewModels.Count); - - models[0].IsHidden = true; - Assert.Equal(4, viewModels.Count); - - models[4].IsHidden = true; - Assert.Equal(3, viewModels.Count); - - models[0].IsHidden = false; - Assert.Equal(4, viewModels.Count); - } - - [Fact] - public void DerivedCollectionMoveNotificationSmokeTest() - { - var initial = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - var source = new ReactiveList(initial); - - var derived = source.CreateDerivedCollection(x => x); - var nestedDerived = derived.CreateDerivedCollection(x => x); - var derivedSorted = source.CreateDerivedCollection(x => x, orderer: (x, y) => x.CompareTo(y)); - - for (var i = 0; i < initial.Length; i++) - { - for (var j = 0; j < initial.Length; j++) - { - source.Move(i, j); - - Assert.True(derived.SequenceEqual(source)); - Assert.True(nestedDerived.SequenceEqual(source)); - Assert.True(derivedSorted.SequenceEqual(initial)); - } - } - } - - /// - /// This test is a bit contrived and only exists to verify that a particularly gnarly bug doesn't get - /// reintroduced because it's hard to reason about the removal logic in derived collections and it might - /// be tempting to try and reorder the shiftIndices operation in there. - /// - [Fact] - public void DerivedCollectionRemovalRegressionTest() - { - var input = new[] { 'A', 'B', 'C', 'D' }; - var source = new ReactiveList(input); - - // A derived collection that filters away 'A' and 'B' - var derived = source.CreateDerivedCollection(x => x, x => x >= 'C'); - - var changeNotifications = new List(); - derived.Changed.Subscribe(changeNotifications.Add); - - Assert.Equal(0, changeNotifications.Count); - Assert.Equal(2, derived.Count); - Assert.True(derived.SequenceEqual(new[] { 'C', 'D' })); - - // The tricky part here is that 'B' isn't in the derived collection, only 'C' is and this test - // will detect if the dervied collection gets tripped up and removes 'C' instead - source.RemoveAll(new[] { 'B', 'C' }); - - Assert.Equal(1, changeNotifications.Count); - Assert.Equal(1, derived.Count); - Assert.True(derived.SequenceEqual(new[] { 'D' })); - } - - [Fact] - public void DerivedCollectionShouldBeSorted() - { - var input = new[] { "Foo", "Bar", "Baz" }; - var fixture = new ReactiveList(input); - - var output = fixture.CreateDerivedCollection(x => x, orderer: string.CompareOrdinal); - - Assert.Equal(3, output.Count); - Assert.True(new[] { "Bar", "Baz", "Foo" }.Zip(output, (expected, actual) => expected == actual).All(x => x)); - - fixture.Add("Bamf"); - Assert.Equal(4, output.Count); - Assert.True(new[] { "Bamf", "Bar", "Baz", "Foo" }.Zip(output, (expected, actual) => expected == actual).All(x => x)); - - fixture.Add("Eoo"); - Assert.Equal(5, output.Count); - Assert.True(new[] { "Bamf", "Bar", "Baz", "Eoo", "Foo" }.Zip(output, (expected, actual) => expected == actual).All(x => x)); - - fixture.Add("Roo"); - Assert.Equal(6, output.Count); - Assert.True(new[] { "Bamf", "Bar", "Baz", "Eoo", "Foo", "Roo" }.Zip(output, (expected, actual) => expected == actual).All(x => x)); - - fixture.Add("Bar"); - Assert.Equal(7, output.Count); - Assert.True(new[] { "Bamf", "Bar", "Bar", "Baz", "Eoo", "Foo", "Roo" }.Zip(output, (expected, actual) => expected == actual).All(x => x)); - } - - [Fact] - public void DerivedCollectionShouldHandleMovesWhenOnlyContainingOneItem() - { - // This test is here to verify a bug in where newPositionForItem would return an incorrect - // index for lists only containing a single item (the item to find a new position for) - var sanity = new List - { - "a", - "b", - "c", - "d", - "e", - "f" - }; - var source = new System.Collections.ObjectModel.ObservableCollection - { - "a", - "b", - "c", - "d", - "e", - "f" - }; - - var derived = source.CreateDerivedCollection(x => x, x => x == "d"); - - Assert.Equal("d", derived.Single()); - Assert.Equal("d", source[3]); - - source.Move(3, 0); - - Assert.Equal("d", source[0]); - Assert.Equal("d", derived.Single()); - } - - [Fact] - public void DerivedCollectionShouldNotSignalRedundantMoveSignals() - { - var sanity = new List - { - "a", - "b", - "c", - "d", - "e", - "f" - }; - var source = new System.Collections.ObjectModel.ObservableCollection - { - "a", - "b", - "c", - "d", - "e", - "f" - }; - - var derived = source.CreateDerivedCollection(x => x, x => x == "d" || x == "e"); - - var derivedNotification = new List(); - derived.Changed.Subscribe(derivedNotification.Add); - - Assert.Equal("d", source[3]); - source.Move(3, 0); - - Assert.Equal(0, derivedNotification.Count); - } - - [Fact] - public void DerivedCollectionShouldOrderCorrectly() - { - var collection = new ReactiveList(); - var orderedCollection = collection.CreateDerivedCollection(x => x, null, (x, y) => x.CompareTo(y)); - - collection.Add(1); - collection.Add(2); - - Assert.Equal(2, orderedCollection.Count); - Assert.Equal(1, orderedCollection[0]); - Assert.Equal(2, orderedCollection[1]); - } - - [Fact] - public void DerivedCollectionShouldStopFollowingAfterDisposal() - { - var collection = new ReactiveList(); - - var orderedCollection = collection.CreateDerivedCollection(x => x.ToString(), null, (x, y) => x.CompareTo(y)); - - collection.Add(1); - collection.Add(2); - - Assert.Equal(2, orderedCollection.Count); - - orderedCollection.Dispose(); - - collection.Add(3); - Assert.Equal(2, orderedCollection.Count); - } - - [Fact] - public void DerivedCollectionShouldUnderstandDummyMoveSignal() - { - var sanity = new List - { - "a", - "b", - "c", - "d", - "e", - "f" - }; - var source = new System.Collections.ObjectModel.ObservableCollection - { - "a", - "b", - "c", - "d", - "e", - "f" - }; - - var derived = source.CreateDerivedCollection(x => x); - - var sourceNotifications = new List(); - - Observable.FromEventPattern(x => source.CollectionChanged += x, x => source.CollectionChanged -= x).Subscribe(x => sourceNotifications.Add(x.EventArgs)); - - var derivedNotification = new List(); - derived.Changed.Subscribe(derivedNotification.Add); - - source.Move(0, 0); - - Assert.Equal(1, sourceNotifications.Count); - Assert.Equal(NotifyCollectionChangedAction.Move, sourceNotifications.First().Action); - - Assert.Equal(0, derivedNotification.Count); - } - - [Fact] - public void DerivedCollectionShouldUnderstandMoveEvenWhenSorted() - { - var sanity = new List - { - "a", - "b", - "c", - "d", - "e", - "f" - }; - var source = new System.Collections.ObjectModel.ObservableCollection - { - "a", - "b", - "c", - "d", - "e", - "f" - }; - - var derived = source.CreateDerivedCollection(selector: x => x, filter: x => x != "c", orderer: (x, y) => x.CompareTo(y)); - - var sourceNotifications = new List(); - - Observable.FromEventPattern(x => source.CollectionChanged += x, x => source.CollectionChanged -= x).Subscribe(x => sourceNotifications.Add(x.EventArgs)); - - var derivedNotifications = new List(); - derived.Changed.Subscribe(derivedNotifications.Add); - - Assert.Equal(5, derived.Count); - Assert.True(derived.SequenceEqual(new[] { "a", "b" /*, "c" */, "d", "e", "f" })); - - var rnd = new Random(); - - for (var i = 0; i < 50; i++) - { - var from = rnd.Next(0, source.Count); - int to; - - do - { - to = rnd.Next(0, source.Count); - } - while (to == from); - - source.Move(from, to); - - var tmp = sanity[from]; - sanity.RemoveAt(from); - sanity.Insert(to, tmp); - - Assert.True(source.SequenceEqual(sanity)); - Assert.True(derived.SequenceEqual(new[] { "a", "b" /*, "c" */, "d", "e", "f" })); - - Assert.Equal(1, sourceNotifications.Count); - Assert.Equal(NotifyCollectionChangedAction.Move, sourceNotifications.First().Action); - - Assert.Empty(derivedNotifications); - - sourceNotifications.Clear(); - } - } - - [Fact] - public void DerivedCollectionShouldUnderstandMoveSignals() - { - var source = new System.Collections.ObjectModel.ObservableCollection - { - "a", - "b", - "c", - "d", - "e", - "f" - }; - var derived = source.CreateDerivedCollection(x => x); - - var sourceNotifications = new List(); - - Observable.FromEventPattern(x => source.CollectionChanged += x, x => source.CollectionChanged -= x).Subscribe(x => sourceNotifications.Add(x.EventArgs)); - - var derivedNotifications = new List(); - derived.Changed.Subscribe(derivedNotifications.Add); - - Assert.Equal(6, derived.Count); - Assert.True(source.SequenceEqual(derived)); - Assert.Empty(derivedNotifications); - - source.Move(1, 4); - - Assert.True(source.SequenceEqual(new[] { "a", "c", "d", "e", "b", "f" })); - Assert.True(derived.SequenceEqual(source)); - - Assert.Equal(1, sourceNotifications.Count); - Assert.Equal(NotifyCollectionChangedAction.Move, sourceNotifications.First().Action); - - Assert.Equal(1, derivedNotifications.Count); - Assert.Equal(NotifyCollectionChangedAction.Move, derivedNotifications.First().Action); - - sourceNotifications.Clear(); - derivedNotifications.Clear(); - - source.Move(4, 1); - - Assert.True(source.SequenceEqual(new[] { "a", "b", "c", "d", "e", "f" })); - Assert.True(derived.SequenceEqual(source)); - - Assert.Equal(1, sourceNotifications.Count); - Assert.Equal(NotifyCollectionChangedAction.Move, sourceNotifications.First().Action); - - Assert.Equal(1, derivedNotifications.Count); - Assert.Equal(NotifyCollectionChangedAction.Move, derivedNotifications.First().Action); - - source.Move(0, 5); - - Assert.True(source.SequenceEqual(new[] { "b", "c", "d", "e", "f", "a" })); - Assert.True(derived.SequenceEqual(source)); - - source.Move(5, 0); - - Assert.True(source.SequenceEqual(new[] { "a", "b", "c", "d", "e", "f", })); - Assert.True(derived.SequenceEqual(source)); - } - - [Fact] - public void DerivedCollectionShouldUnderstandNestedMoveSignals() - { - var source = new System.Collections.ObjectModel.ObservableCollection - { - "a", - "b", - "c", - "d", - "e", - "f" - }; - var derived = source.CreateDerivedCollection(x => x); - var nested = derived.CreateDerivedCollection(x => x); - - var reverseNested = nested.CreateDerivedCollection(x => x, orderer: OrderedComparer.OrderByDescending(x => x).Compare); - - var sortedNested = reverseNested.CreateDerivedCollection(x => x, orderer: OrderedComparer.OrderBy(x => x).Compare); - - source.Move(1, 4); - - Assert.True(source.SequenceEqual(derived)); - Assert.True(source.SequenceEqual(nested)); - Assert.True(source.OrderByDescending(x => x).SequenceEqual(reverseNested)); - Assert.True(source.OrderBy(x => x).SequenceEqual(sortedNested)); - } - - [Fact] - public void DerivedCollectionSignalledToResetShouldFireExactlyOnce() - { - var input = new List - { - "Foo" - }; - var resetSubject = new Subject(); - var derived = input.CreateDerivedCollection(x => x, signalReset: resetSubject); - - var changeNotifications = new List(); - derived.Changed.Subscribe(changeNotifications.Add); - - Assert.Equal(0, changeNotifications.Count); - Assert.Equal(1, derived.Count); - - input.Add("Bar"); - - // Shouldn't have picked anything up since the input isn't reactive - Assert.Equal(0, changeNotifications.Count); - Assert.Equal(1, derived.Count); - - resetSubject.OnNext(Unit.Default); - - Assert.Equal(1, changeNotifications.Count); - Assert.Equal(2, derived.Count); - } - - [Fact] - public void DerivedCollectionsShouldBeFiltered() - { - var input = new[] { "Foo", "Bar", "Baz", "Bamf" }; - var fixture = new ReactiveList( - input.Select( - x => new TestFixture - { - IsOnlyOneWord = x - })); - var itemsAdded = new List(); - var itemsRemoved = new List(); - - var output = fixture.CreateDerivedCollection(x => x, x => x.IsOnlyOneWord[0] == 'F', (l, r) => l.IsOnlyOneWord.CompareTo(r.IsOnlyOneWord)); - output.ItemsAdded.Subscribe(itemsAdded.Add); - output.ItemsRemoved.Subscribe(itemsRemoved.Add); - - Assert.Equal(1, output.Count); - Assert.Equal(0, itemsAdded.Count); - Assert.Equal(0, itemsRemoved.Count); - - fixture.Add( - new TestFixture - { - IsOnlyOneWord = "Boof" - }); - Assert.Equal(1, output.Count); - Assert.Equal(0, itemsAdded.Count); - Assert.Equal(0, itemsRemoved.Count); - - fixture.Add( - new TestFixture - { - IsOnlyOneWord = "Far" - }); - Assert.Equal(2, output.Count); - Assert.Equal(1, itemsAdded.Count); - Assert.Equal(0, itemsRemoved.Count); - - fixture.RemoveAt(1); // Remove "Bar" - Assert.Equal(2, output.Count); - Assert.Equal(1, itemsAdded.Count); - Assert.Equal(0, itemsRemoved.Count); - - fixture.RemoveAt(0); // Remove "Foo" - Assert.Equal(1, output.Count); - Assert.Equal(1, itemsAdded.Count); - Assert.Equal(1, itemsRemoved.Count); - } - - [Fact] - public void DerivedCollectionsShouldFollowBaseCollection() - { - var input = new[] { "Foo", "Bar", "Baz", "Bamf" }; - var fixture = new ReactiveList( - input.Select( - x => new TestFixture - { - IsOnlyOneWord = x - })); - - var output = fixture.CreateDerivedCollection(x => x.IsOnlyOneWord); - - input.AssertAreEqual(output); - - fixture.Add( - new TestFixture - { - IsOnlyOneWord = "Hello" - }); - Assert.Equal(5, output.Count); - Assert.Equal("Hello", output[4]); - - fixture.RemoveAt(4); - Assert.Equal(4, output.Count); - - fixture[1] = new TestFixture - { - IsOnlyOneWord = "Goodbye" - }; - Assert.Equal(4, output.Count); - Assert.Equal("Goodbye", output[1]); - - fixture.Clear(); - Assert.Equal(0, output.Count); - } - - [Fact] - public void DerviedCollectionShouldHandleItemsRemoved() - { - var input = new[] { "Foo", "Bar", "Baz", "Bamf" }; - var disposed = new List(); - var fixture = new ReactiveList( - input.Select( - x => new TestFixture - { - IsOnlyOneWord = x - })); - - var output = fixture.CreateDerivedCollection(x => Disposable.Create(() => disposed.Add(x)), item => item.Dispose()); - - fixture.Add( - new TestFixture - { - IsOnlyOneWord = "Hello" - }); - Assert.Equal(5, output.Count); - - fixture.RemoveAt(3); - Assert.Equal(4, output.Count); - Assert.Equal(1, disposed.Count); - Assert.Equal("Bamf", disposed[0].IsOnlyOneWord); - - fixture[1] = new TestFixture - { - IsOnlyOneWord = "Goodbye" - }; - Assert.Equal(4, output.Count); - Assert.Equal(2, disposed.Count); - Assert.Equal("Bar", disposed[1].IsOnlyOneWord); - - var count = output.Count; - output.Dispose(); - Assert.Equal(disposed.Count, 2 + count); - } - - [Fact] - public void GetARangeWhenWeAddAListOfItems() - { - var fixture = new ReactiveList - { - 1, - 2, - 3, - 4, - 5 - }; - var changed = fixture.Changed.CreateCollection(); - Assert.Equal(0, changed.Count); - - fixture.AddRange(new[] { 6, 7 }); - Assert.Equal(1, changed.Count); - Assert.Equal(NotifyCollectionChangedAction.Add, changed[0].Action); - } - - [Fact] - public void GetAResetWhenWeAddALotOfItems() - { - var fixture = new ReactiveList - { - 1, - }; - var reset = fixture.ShouldReset.CreateCollection(); - Assert.Equal(0, reset.Count); - - fixture.AddRange(new[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, }); - Assert.Equal(1, reset.Count); - } - - [Fact] - public void GetSingleItemNotificationWhenWeAddAListOfItemsAndRangeIsFalse() - { - try - { - RxApp.SupportsRangeNotifications = false; - - var fixture = new ReactiveList - { - 1, - 2, - 3, - 4, - 5 - }; - var changed = fixture.Changed.CreateCollection(); - Assert.Equal(0, changed.Count); - - fixture.AddRange(new[] { 6, 7 }); - Assert.Equal(2, changed.Count); - Assert.Equal(NotifyCollectionChangedAction.Add, changed[0].Action); - Assert.Equal(NotifyCollectionChangedAction.Add, changed[1].Action); - } - finally - { - RxApp.SupportsRangeNotifications = true; - } - } - - [Fact] - public void IListSmokeTest() - { - var fixture = new ReactiveList() as IList; - Assert.NotNull(fixture); - - var pos = fixture.Add("foo"); - Assert.Equal(0, pos); - Assert.Equal(1, fixture.Count); - Assert.True(fixture.Contains("foo")); - - fixture.Insert(0, "bar"); - Assert.Equal(0, fixture.IndexOf("bar")); - Assert.Equal(1, fixture.IndexOf("foo")); - Assert.Equal("bar", fixture[0] as string); - Assert.Equal("foo", fixture[1] as string); - - var arr = new string[2]; - fixture.CopyTo(arr, 0); - Assert.Equal(2, arr.Length); - Assert.Equal("bar", arr[0]); - Assert.Equal("foo", arr[1]); - - fixture[1] = "baz"; - Assert.Equal(1, fixture.IndexOf("baz")); - Assert.Equal(-1, fixture.IndexOf("foo")); - Assert.Equal("baz", fixture[1] as string); - Assert.False(fixture.Contains("foo")); - Assert.True(fixture.Contains("baz")); - - fixture.Remove("bar"); - Assert.Equal(1, fixture.Count); - Assert.False(fixture.Contains("bar")); - } - - [Fact] - public void IListTSmokeTest() - { - var fixture = new ReactiveList() as IList; - Assert.NotNull(fixture); - - fixture.Add("foo"); - Assert.Equal(1, fixture.Count); - Assert.True(fixture.Contains("foo")); - - fixture.Insert(0, "bar"); - Assert.Equal(0, fixture.IndexOf("bar")); - Assert.Equal(1, fixture.IndexOf("foo")); - Assert.Equal("bar", fixture[0]); - Assert.Equal("foo", fixture[1]); - - var genericEnum = fixture.GetEnumerator(); - Assert.NotNull(genericEnum); - var result = genericEnum.MoveNext(); - Assert.True(result); - Assert.Equal("bar", genericEnum.Current); - result = genericEnum.MoveNext(); - Assert.True(result); - Assert.Equal("foo", genericEnum.Current); - result = genericEnum.MoveNext(); - Assert.False(result); - - var plainEnum = ((IEnumerable)fixture).GetEnumerator(); - Assert.NotNull(plainEnum); - result = plainEnum.MoveNext(); - Assert.True(result); - Assert.Equal("bar", plainEnum.Current as string); - result = plainEnum.MoveNext(); - Assert.True(result); - Assert.Equal("foo", plainEnum.Current as string); - result = plainEnum.MoveNext(); - Assert.False(result); - - var arr = new string[2]; - fixture.CopyTo(arr, 0); - Assert.Equal(2, arr.Length); - Assert.Equal("bar", arr[0]); - Assert.Equal("foo", arr[1]); - - fixture[1] = "baz"; - Assert.Equal(1, fixture.IndexOf("baz")); - Assert.Equal(-1, fixture.IndexOf("foo")); - Assert.Equal("baz", fixture[1]); - Assert.False(fixture.Contains("foo")); - Assert.True(fixture.Contains("baz")); - - fixture.RemoveAt(1); - Assert.False(fixture.Contains("baz")); - - fixture.Remove("bar"); - Assert.Equal(0, fixture.Count); - Assert.False(fixture.Contains("bar")); - } - - [Fact] - public void IndexerIsNotAmbiguous() - { - IReactiveList reactiveList = new ReactiveList - { - 0, - 1 - }; - Assert.Equal(0, reactiveList[0]); - } - - [Fact] - public void InsertRangeSmokeTest() - { - var fixture = new ReactiveList(); - var output = fixture.CreateDerivedCollection(x => "Prefix" + x); - - fixture.Add("Bamf"); - Assert.Equal(1, fixture.Count); - Assert.Equal(1, output.Count); - Assert.Equal("Bamf", fixture[0]); - Assert.Equal("PrefixBamf", output[0]); - - fixture.InsertRange(0, Enumerable.Repeat("Bar", 4)); - Assert.Equal(5, fixture.Count); - Assert.Equal(5, output.Count); - Assert.Equal("Bamf", fixture[4]); - Assert.Equal("PrefixBamf", output[4]); - - Assert.True(fixture.Take(4).All(x => x == "Bar")); - Assert.True(output.Take(4).All(x => x == "PrefixBar")); - - // Trigger the Reset by adding a ton of items - fixture.InsertRange(0, Enumerable.Repeat("Bar", 35)); - Assert.Equal(40, fixture.Count); - Assert.Equal(40, output.Count); - Assert.Equal("Bamf", fixture[39]); - Assert.Equal("PrefixBamf", output[39]); - } - - [Fact] - public void ItemsAddedAndRemovedTest() - { - var fixture = new ReactiveList(); - var before_added = new List(); - var before_removed = new List(); - var added = new List(); - var removed = new List(); - - fixture.BeforeItemsAdded.Subscribe(before_added.Add); - fixture.BeforeItemsRemoved.Subscribe(before_removed.Add); - fixture.ItemsAdded.Subscribe(added.Add); - fixture.ItemsRemoved.Subscribe(removed.Add); - - fixture.Add(10); - fixture.Add(20); - fixture.Add(30); - fixture.RemoveAt(1); - fixture.Clear(); - - var added_results = new[] { 10, 20, 30 }; - Assert.Equal(added_results.Length, added.Count); - added_results.AssertAreEqual(added); - - var removed_results = new[] { 20 }; - Assert.Equal(removed_results.Length, removed.Count); - removed_results.AssertAreEqual(removed); - - Assert.Equal(before_added.Count, added.Count); - added.AssertAreEqual(before_added); - - Assert.Equal(before_removed.Count, removed.Count); - removed.AssertAreEqual(before_removed); - } - - [Fact] - public void MoveShouldBehaveAsObservableCollectionMove() - { - var items = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - - var fixture = new ReactiveList(items); - var reference = new System.Collections.ObjectModel.ObservableCollection(items); - - Assert.True(fixture.SequenceEqual(reference)); - - var fixtureNotifications = new List(); - var referenceNotifications = new List(); - - fixture.Changed.Subscribe(fixtureNotifications.Add); - - Observable.FromEventPattern(x => reference.CollectionChanged += x, x => reference.CollectionChanged -= x).Select(x => x.EventArgs).Subscribe(referenceNotifications.Add); - - for (var i = 0; i < items.Length; i++) - { - for (var j = 0; j < items.Length; j++) - { - reference.Move(i, j); - fixture.Move(i, j); - - Assert.True(fixture.SequenceEqual(reference)); - Assert.Equal(fixtureNotifications.Count, referenceNotifications.Count); - - var lastFixtureNotification = fixtureNotifications.Last(); - var lastReferenceNotification = referenceNotifications.Last(); - - Assert.Equal(NotifyCollectionChangedAction.Move, lastFixtureNotification.Action); - Assert.Equal(NotifyCollectionChangedAction.Move, lastReferenceNotification.Action); - - Assert.Equal(lastFixtureNotification.OldStartingIndex, lastReferenceNotification.OldStartingIndex); - Assert.Equal(lastFixtureNotification.NewStartingIndex, lastReferenceNotification.NewStartingIndex); - - Assert.Equal(lastReferenceNotification.OldItems[0], lastReferenceNotification.OldItems[0]); - Assert.Equal(lastReferenceNotification.NewItems[0], lastReferenceNotification.NewItems[0]); - } - } - } - - [Fact] - public void ReactiveCollectionIsRoundTrippable() - { - var output = new[] { "Foo", "Bar", "Baz", "Bamf" }; - var fixture = new ReactiveList(output); - - var json = JSONHelper.Serialize(fixture); - var results = JSONHelper.Deserialize>(json); - - output.AssertAreEqual(results); - - var should_die = true; - results.ItemsAdded.Subscribe(_ => should_die = false); - results.Add("Foobar"); - Assert.False(should_die); - } - - [Fact] - public void SortShouldActuallySort() - { - var fixture = new ReactiveList(new[] { 5, 1, 3, 2, 4, }); - fixture.Sort(); - - Assert.True(new[] { 1, 2, 3, 4, 5, }.Zip(fixture, (expected, actual) => expected == actual).All(x => x)); - } - - [Fact] - public void TestDelayNotifications() - { - var maxSize = 10; - var data = MakeAsyncCollection(maxSize); - - var list = new ReactiveList(data) - { - ChangeTrackingEnabled = true - }; - - var derivedList = list.CreateDerivedCollection(m => m.Value, m => m.HasData, (a, b) => a.Text.CompareTo(b.Text), Observable.Never(4), RxApp.MainThreadScheduler); - - derivedList.CountChanged.StartWith(derivedList.Count).Subscribe( - count => - { - Debug.WriteLine(count); - Assert.True(count <= maxSize); - }); - - data = MakeAsyncCollection(maxSize); - - Observable.Delay(Observables.Unit, TimeSpan.FromMilliseconds(100)).ObserveOn(RxApp.MainThreadScheduler).Subscribe( - _ => - { - using (list.SuppressChangeNotifications()) - { - list.Clear(); - list.AddRange(data); - } - }); - - var done = new EventWaitHandle(false, EventResetMode.ManualReset); - done.WaitOne(5000); - } - - [Fact] - public void WhenAddingRangeOfNullArgumentNullExceptionIsThrown() - { - var fixture = new ReactiveList(); - - Assert.Throws(() => fixture.AddRange(null)); - } - - [Fact] - public void WhenInsertingRangeOfNullArgumentNullExceptionIsThrown() - { - var fixture = new ReactiveList(); - - Assert.Throws(() => fixture.InsertRange(1, null)); - } - - [Fact] - public void WhenInsertingRangeOutOfRangeExceptionIsThrown() - { - var fixture = new ReactiveList(); - - Assert.Throws( - () => fixture.InsertRange( - 1, - new List - { - 1 - })); - } - - [Fact] - public void WhenRemovingAllOfNullArgumentNullExceptionIsThrown() - { - var fixture = new ReactiveList(); - - Assert.Throws(() => fixture.RemoveAll(null)); - } - - private ReactiveList MakeAsyncCollection(int maxSize) - { - return new ReactiveList(Enumerable.Repeat(Unit.Default, maxSize).Select(_ => new TextModel())); - } - - public static class TheDerivedSortMethods - { - public class TheNewPositionForExistingItemMethod - { - [Fact] - public void TheNewPositionForExistingItemMethodSmokeTest() - { - AssertNewIndex(new[] { 10, 20 }, newValue: 15, currentIndex: 0, expectedNewIndex: 0); - AssertNewIndex(new[] { 10, 20 }, newValue: 25, currentIndex: 0, expectedNewIndex: 1); - - AssertNewIndex(new[] { 10, 20 }, newValue: 15, currentIndex: 1, expectedNewIndex: 1); - AssertNewIndex(new[] { 10, 20 }, newValue: 5, currentIndex: 1, expectedNewIndex: 0); - - AssertNewIndex(new[] { 10, 20, 30 }, newValue: 15, currentIndex: 2, expectedNewIndex: 1); - AssertNewIndex(new[] { 10, 20, 30 }, newValue: 5, currentIndex: 2, expectedNewIndex: 0); - - var items = new[] { 10, 20, 30, 40, 50 }; - - AssertNewIndex(items, newValue: 11, currentIndex: 0, expectedNewIndex: 0); - AssertNewIndex(items, newValue: 10, currentIndex: 0, expectedNewIndex: 0); - AssertNewIndex(items, newValue: 15, currentIndex: 0, expectedNewIndex: 0); - AssertNewIndex(items, newValue: 19, currentIndex: 0, expectedNewIndex: 0); - AssertNewIndex(items, newValue: 21, currentIndex: 0, expectedNewIndex: 1); - AssertNewIndex(items, newValue: 60, currentIndex: 0, expectedNewIndex: 4); - - AssertNewIndex(items, newValue: 50, currentIndex: 3, expectedNewIndex: 3); - - AssertNewIndex(items, newValue: 1, currentIndex: 4, expectedNewIndex: 0); - AssertNewIndex(items, newValue: 51, currentIndex: 4, expectedNewIndex: 4); - AssertNewIndex(items, newValue: 39, currentIndex: 4, expectedNewIndex: 3); - - AssertNewIndex(items, newValue: 10, currentIndex: 1, expectedNewIndex: 1); - } - - private static void AssertNewIndex(IList items, T newValue, int currentIndex, int expectedNewIndex) - { - var newIndex = ReactiveDerivedCollection.NewPositionForExistingItem(items, newValue, currentIndex, Comparer.Default.Compare); - - Assert.Equal(expectedNewIndex, newIndex); - - var test = new List(items); - test.RemoveAt(currentIndex); - test.Insert(newIndex, newValue); - - Assert.True(test.SequenceEqual(test.OrderBy(x => x, Comparer.Default))); - } - } - - public class ThePositionForNewItemMethod - { - [Fact] - public void ThePositionForNewItemMethodSmokeTest() - { - AssertNewIndex(Array.Empty(), newValue: 1, expectedIndex: 0); - - AssertNewIndex(new[] { 10 }, newValue: 9, expectedIndex: 0); - AssertNewIndex(new[] { 10 }, newValue: 10, expectedIndex: 0); - AssertNewIndex(new[] { 10 }, newValue: 11, expectedIndex: 1); - - AssertNewIndex(new[] { 10, 20 }, newValue: 9, expectedIndex: 0); - AssertNewIndex(new[] { 10, 20 }, newValue: 15, expectedIndex: 1); - AssertNewIndex(new[] { 10, 20 }, newValue: 20, expectedIndex: 1); - AssertNewIndex(new[] { 10, 20 }, newValue: 21, expectedIndex: 2); - - var items = new[] { 10, 20, 30, 40, 50, 60, 70, 80, 90 }; - - AssertNewIndex(items, newValue: 0, expectedIndex: 0); - AssertNewIndex(items, newValue: 15, expectedIndex: 1); - AssertNewIndex(items, newValue: 25, expectedIndex: 2); - AssertNewIndex(items, newValue: 35, expectedIndex: 3); - AssertNewIndex(items, newValue: 45, expectedIndex: 4); - AssertNewIndex(items, newValue: 55, expectedIndex: 5); - AssertNewIndex(items, newValue: 65, expectedIndex: 6); - AssertNewIndex(items, newValue: 75, expectedIndex: 7); - AssertNewIndex(items, newValue: 85, expectedIndex: 8); - AssertNewIndex(items, newValue: 95, expectedIndex: 9); - } - - private static void AssertNewIndex(IList items, T newValue, int expectedIndex, Func orderer = null) - { - if (orderer == null) - { - orderer = Comparer.Default.Compare; - } - - var newIndex = ReactiveDerivedCollection.PositionForNewItem(items, newValue, orderer); - - Assert.Equal(expectedIndex, newIndex); - } - } - } - - public class DerivedCollectionLogging - { - [Fact] - public void DerivedCollectionsShouldNotTriggerSupressNotificationWarning() - { - var resolver = new ModernDependencyResolver(); - var logger = new TestLogger(); - - using (resolver.WithResolver()) - { - resolver.RegisterConstant(new FuncLogManager(t => new WrappingFullLogger(new WrappingPrefixLogger(logger, t))), typeof(ILogManager)); - - var incc = new ReactiveList(scheduler: CurrentThreadScheduler.Instance); - var inccDerived = incc.CreateDerivedCollection(x => x); - - Assert.False(logger.Messages.Any(x => x.Item1.Contains("SuppressChangeNotifications"))); - - // Derived collections should only suppress warnings for internal behavior. - inccDerived.ItemsAdded.Subscribe(); - incc.Reset(); - Assert.True(logger.Messages.Any(x => x.Item1.Contains("SuppressChangeNotifications"))); - } - } - - [Fact] - public void DerivedCollectionsShouldWarnWhenSourceIsNotINotifyCollectionChanged() - { - var resolver = new ModernDependencyResolver(); - var logger = new TestLogger(); - - using (resolver.WithResolver()) - { - resolver.RegisterConstant(new FuncLogManager(t => new WrappingFullLogger(new WrappingPrefixLogger(logger, t))), typeof(ILogManager)); - - var incc = new ReactiveList(scheduler: CurrentThreadScheduler.Instance); - Assert.True(incc is INotifyCollectionChanged); - var inccDerived = incc.CreateDerivedCollection(x => x); - - Assert.False(logger.Messages.Any(x => x.Item1.Contains("INotifyCollectionChanged"))); - - // Reset - logger.Messages.Clear(); - - var nonIncc = new List(); - - Assert.False(nonIncc is INotifyCollectionChanged); - var nonInccderived = nonIncc.CreateDerivedCollection(x => x); - - Assert.Equal(1, logger.Messages.Count); - - var m = logger.Messages.Last(); - var message = m.Item1; - var level = m.Item3; - - Assert.Contains("INotifyCollectionChanged", message); - Assert.Equal(LogLevel.Warn, level); - } - } - - // We need a sentinel class to make sure no test has triggered the warnings before - #pragma warning disable CA1812 // Never instantiated classes. - private class NoOneHasEverSeenThisClassBefore - { - } - - private class NoOneHasEverSeenThisClassBeforeEither - { - } - #pragma warning restore CA1812 // Never instantiated class. - } - - public class DerivedPropertyChanges - { - [Fact] - public void DerivedCollectionShouldHandleRemovesOfFilteredItems() - { - var a = new ReactiveVisibilityItem("A", true); - var b = new ReactiveVisibilityItem("B", true); - var c = new ReactiveVisibilityItem("C", true); - var d = new ReactiveVisibilityItem("D", false); - var e = new ReactiveVisibilityItem("E", true); - - var items = new ReactiveList>(new[] { a, b, c, d, e }) - { - ChangeTrackingEnabled = true - }; - - var onlyVisible = items.CreateDerivedCollection(x => x.Value, x => x.IsVisible, OrderedComparer.OrderByDescending(x => x).Compare); - - Assert.True(onlyVisible.SequenceEqual(new[] { "E", "C", "B", "A" }, StringComparer.Ordinal)); - Assert.Equal(4, onlyVisible.Count); - - // Removal of an item from the source collection that's filtered in the derived collection should - // have no effect on the derived. - items.Remove(d); - - Assert.True(onlyVisible.SequenceEqual(new[] { "E", "C", "B", "A" }, StringComparer.Ordinal)); - Assert.Equal(4, onlyVisible.Count); - - c.IsVisible = false; - Assert.Equal(3, onlyVisible.Count); - Assert.True(onlyVisible.SequenceEqual(new[] { "E", "B", "A" }, StringComparer.Ordinal)); - - items.Remove(c); - Assert.Equal(3, onlyVisible.Count); - Assert.True(onlyVisible.SequenceEqual(new[] { "E", "B", "A" }, StringComparer.Ordinal)); - - items.Remove(b); - Assert.Equal(2, onlyVisible.Count); - Assert.True(onlyVisible.SequenceEqual(new[] { "E", "A" }, StringComparer.Ordinal)); - } - - [Fact] - public void DerivedCollectionsShouldReactToPropertyChanges() - { - // This differs from the FilteredDerivedCollectionsShouldReactToPropertyChanges as it tests providing a - // non-identity selector (ie x=>x.Value). - var foo = new ReactiveVisibilityItem("Foo", true); - var bar = new ReactiveVisibilityItem("Bar", true); - var baz = new ReactiveVisibilityItem("Baz", true); - - var items = new ReactiveList>(new[] { foo, bar, baz }) - { - ChangeTrackingEnabled = true - }; - - var onlyVisible = items.CreateDerivedCollection( - x => new string('*', x.Value.Length), // Note, not an identity function. - x => x.IsVisible, - StringComparer.Ordinal.Compare); - - Assert.Equal(3, onlyVisible.Count); - Assert.True(onlyVisible.SequenceEqual(new[] { "***", "***", "***" })); - - foo.IsVisible = false; - - Assert.Equal(2, onlyVisible.Count); - Assert.True(onlyVisible.SequenceEqual(new[] { "***", "***" })); - } - - [Fact] - public void DerivedCollectionsSmokeTest() - { - var adam = new ReactiveEmployee - { - Name = "Adam", - Age = 20, - Salary = 100 - }; - var bob = new ReactiveEmployee - { - Name = "Bob", - Age = 30, - Salary = 150 - }; - var carol = new ReactiveEmployee - { - Name = "Carol", - Age = 40, - Salary = 200 - }; - var dan = new ReactiveEmployee - { - Name = "Dan", - Age = 50, - Salary = 250 - }; - var eve = new ReactiveEmployee - { - Name = "Eve", - Age = 60, - Salary = 300 - }; - - var start = new[] { adam, bob, carol, dan, eve }; - - var employees = new ReactiveList(start) - { - ChangeTrackingEnabled = true - }; - - var employeesByName = DerivedCollectionTestContainer.Create(employees, selector: x => x, orderer: OrderedComparer.OrderBy(x => x.Name)); - - var employeesByAge = DerivedCollectionTestContainer.Create(employees, selector: x => x, orderer: OrderedComparer.OrderBy(x => x.Age)); - - var employeesBySalary = DerivedCollectionTestContainer.Create(employees, selector: x => x, orderer: OrderedComparer.OrderBy(x => x.Salary)); - - // special - - // filtered, ordered, reference - var oldEmployeesByAge = DerivedCollectionTestContainer.Create(employees, selector: x => x, filter: x => x.Age >= 50, orderer: OrderedComparer.OrderBy(x => x.Age)); - - // ordered, not reference - var employeeSalaries = DerivedCollectionTestContainer.Create(employees, selector: x => x.Salary, orderer: Comparer.Default); - - // not filtered (derived filter), not reference, not ordered (derived order) - oldEmployeesByAge.Derived.ChangeTrackingEnabled = true; - var oldEmployeesSalariesByAge = DerivedCollectionTestContainer.Create(oldEmployeesByAge.Derived, selector: x => x.Salary); - - var containers = new List - { - employeesByName, - employeesByAge, - employeesBySalary, - oldEmployeesByAge, - employeeSalaries, - oldEmployeesSalariesByAge - }; - - Action testAll = a => - { - a(); - containers.ForEach(x => x.Test()); - }; - - containers.ForEach(x => x.Test()); - - // if (isIncluded && !shouldBeIncluded) - testAll(() => { dan.Age = 49; }); - - // else if (!isIncluded && shouldBeIncluded) - testAll(() => { dan.Age = eve.Age + 1; }); - - // else if (isIncluded && shouldBeIncluded) - testAll(() => { adam.Salary = 350; }); - testAll(() => { dan.Age = 50; }); - testAll(() => { dan.Age = 51; }); - } - - [Fact] - public void FilteredDerivedCollectionsShouldReactToPropertyChanges() - { - // Naturally this isn't done by magic, it only works if the source implements IReactiveCollection. - var a = new ReactiveVisibilityItem("a", true); - var b = new ReactiveVisibilityItem("b", true); - var c = new ReactiveVisibilityItem("c", true); - - var items = new ReactiveList>(new[] { a, b, c }) - { - ChangeTrackingEnabled = true - }; - - var onlyVisible = items.CreateDerivedCollection(x => x.Value, x => x.IsVisible, StringComparer.Ordinal.Compare); - - var onlyNonVisible = items.CreateDerivedCollection(x => x.Value, x => !x.IsVisible, StringComparer.Ordinal.Compare); - - var onlVisibleStartingWithB = items.CreateDerivedCollection(x => x.Value, x => x.IsVisible && x.Value.StartsWith("b"), StringComparer.Ordinal.Compare); - - Assert.Equal(3, onlyVisible.Count); - Assert.Equal(0, onlyNonVisible.Count); - Assert.Equal(1, onlVisibleStartingWithB.Count); - - a.IsVisible = false; - - Assert.Equal(2, onlyVisible.Count); - Assert.Equal(1, onlyNonVisible.Count); - Assert.Equal(1, onlVisibleStartingWithB.Count); - - b.Value = "D"; - - Assert.Equal(0, onlVisibleStartingWithB.Count); - } - - [Fact] - public void FilteredProjectedDerivedCollectionsShouldReactToPropertyChanges() - { - // This differs from the FilteredDerivedCollectionsShouldReactToPropertyChanges as it tests providing a - // non-identity selector (ie x=>x.Value). - var a = new ReactiveVisibilityItem("a", true); - var b = new ReactiveVisibilityItem("b", true); - var c = new ReactiveVisibilityItem("c", true); - - var items = new ReactiveList>(new[] { a, b, c }) - { - ChangeTrackingEnabled = true - }; - - var onlyVisible = items.CreateDerivedCollection( - x => x.Value.ToUpper(CultureInfo.InvariantCulture), // Note, not an identity function. - x => x.IsVisible, - StringComparer.Ordinal.Compare); - - Assert.Equal(3, onlyVisible.Count); - Assert.True(onlyVisible.SequenceEqual(new[] { "A", "B", "C" })); - - a.IsVisible = false; - - Assert.Equal(2, onlyVisible.Count); - Assert.True(onlyVisible.SequenceEqual(new[] { "B", "C" })); - } - - [Fact] - public void PropertyChangesShouldWorkWithChainedCollections() - { - // This is a highly contrived test and I apologize for it not making much sense. I added it - // specifically track down an bug I was hitting when derived collection notification triggered - // re-entrant notifications. - var a = new ReactiveVisibilityItem("A", true); - var b = new ReactiveVisibilityItem("B", true); - var c = new ReactiveVisibilityItem("C", true); - var d = new ReactiveVisibilityItem("D", true); - var e = new ReactiveVisibilityItem("E", true); - var f = new ReactiveVisibilityItem("F", true); - - var items = new ReactiveList>(new[] { a, b, c, d, e, f }) - { - ChangeTrackingEnabled = true - }; - - var itemsByVisibility = items.CreateDerivedCollection(x => x, orderer: OrderedComparer>.OrderByDescending(x => x.IsVisible).ThenBy(x => x.Value).Compare); - - itemsByVisibility.ChangeTrackingEnabled = true; - - var onlyVisibleReversed = itemsByVisibility.CreateDerivedCollection(x => x, x => x.IsVisible, OrderedComparer>.OrderByDescending(x => x.Value).Compare); - - onlyVisibleReversed.ChangeTrackingEnabled = true; - - var onlyVisibleAndGreaterThanC = onlyVisibleReversed.CreateDerivedCollection(x => x, x => x.Value[0] > 'C', OrderedComparer>.OrderBy(x => x.Value).Compare); - - onlyVisibleAndGreaterThanC.ChangeTrackingEnabled = true; - - Assert.True(items.SequenceEqual(new[] { a, b, c, d, e, f })); - Assert.True(itemsByVisibility.SequenceEqual(new[] { a, b, c, d, e, f })); - Assert.True(onlyVisibleReversed.SequenceEqual(new[] { f, e, d, c, b, a })); - Assert.True(onlyVisibleAndGreaterThanC.SequenceEqual(new[] { d, e, f })); - - // When the value of d changes, update a to Y - d.WhenAnyValue(x => x.Value).Where(x => x == "Y").Subscribe(x => a.Value = "Z"); - - // When the visibility of e changes, update d to Z - e.WhenAnyValue(x => x.IsVisible).Where(x => x == false).Subscribe(x => d.Value = "Y"); - - // As soon as the "last" collection changes, remove b - onlyVisibleAndGreaterThanC.Changed.Subscribe(x => items.Remove(b)); - - e.IsVisible = false; - - Assert.True(items.SequenceEqual(new[] { a, c, d, e, f })); - Assert.True( - itemsByVisibility.SequenceEqual( - new[] - { - c, f, d, // d is now y - a, // a is now z - e // e is now hidden - })); - - Assert.True( - onlyVisibleReversed.SequenceEqual( - new[] - { - a, // a is now z - d, // d is now y - f, c - })); - - Assert.True( - onlyVisibleAndGreaterThanC.SequenceEqual( - new[] - { - f, d, // d is now y - a, // a is now z - })); - } - - public class DerivedCollectionTestContainer - { - public static DerivedCollectionTestContainer Create(IEnumerable source, Func selector, Func filter = null, IComparer orderer = null) - { - var comparison = orderer == null ? (Func)null : orderer.Compare; - var derived = source.CreateDerivedCollection(selector, filter, comparison); - - return new DerivedCollectionTestContainer - { - Source = source, - Selector = selector, - Derived = derived, - Filter = filter, - Orderer = orderer - }; - } - - public virtual void Test() - { - } - } - - public class DerivedCollectionTestContainer : DerivedCollectionTestContainer - { - public IReactiveDerivedList Derived { get; set; } - - public Func Filter { get; set; } - - public IComparer Orderer { get; set; } - - public Func Selector { get; set; } - - public IEnumerable Source { get; set; } - - public override void Test() - { - var filtered = Source; - - if (Filter != null) - { - filtered = filtered.Where(Filter); - } - - var projected = filtered.Select(Selector); - - var ordered = projected; - - if (Orderer != null) - { - ordered = ordered.OrderBy(x => x, Orderer); - } - - var shouldBe = ordered; - var isEqual = Derived.SequenceEqual(shouldBe); - - Assert.True(isEqual); - } - } - - [DebuggerDisplay("{Name} is {Age} years old and makes ${Salary}")] - private class ReactiveEmployee : ReactiveObject - { - private int _age; - - private string _name; - - private int _salary; - - public int Age - { - get => _age; - set => this.RaiseAndSetIfChanged(ref _age, value); - } - - public string Name - { - get => _name; - set => this.RaiseAndSetIfChanged(ref _name, value); - } - - public int Salary - { - get => _salary; - set => this.RaiseAndSetIfChanged(ref _salary, value); - } - } - - private class ReactiveVisibilityItem : ReactiveObject - { - private bool _isVisible; - - private T _value; - - public ReactiveVisibilityItem(T item1, bool isVisible) - { - _value = item1; - _isVisible = isVisible; - } - - public bool IsVisible - { - get => _isVisible; - set => this.RaiseAndSetIfChanged(ref _isVisible, value); - } - - public T Value - { - get => _value; - set => this.RaiseAndSetIfChanged(ref _value, value); - } - } - } - - public class PropertyBindView : Control, IViewFor - { - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(PropertyBindViewModel), typeof(PropertyBindView), new PropertyMetadata(null)); - - public PropertyBindFakeControl FakeControl; - - public ListBox FakeItemsControl; - - public TextBox Property2; - - public TextBox SomeTextBox; - - public PropertyBindView() - { - SomeTextBox = new TextBox(); - Property2 = new TextBox(); - FakeControl = new PropertyBindFakeControl(); - FakeItemsControl = new ListBox(); - } - - public PropertyBindViewModel ViewModel - { - get => (PropertyBindViewModel)GetValue(ViewModelProperty); - set => SetValue(ViewModelProperty, value); - } - - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (PropertyBindViewModel)value; - } - } - - public class PropertyBindViewModel : ReactiveObject - { - public decimal _JustADecimal; - - public double _JustADouble; - - public int _JustAInt32; - - public PropertyBindModel _Model; - - public double? _NullableDouble; - - public string _Property1; - - public int _Property2; - - public PropertyBindViewModel(PropertyBindModel model = null) - { - Model = model ?? new PropertyBindModel - { - AThing = 42, - AnotherThing = "Baz" - }; - SomeCollectionOfStrings = new ReactiveList(new[] { "Foo", "Bar" }); - } - - public decimal JustADecimal - { - get => _JustADecimal; - set => this.RaiseAndSetIfChanged(ref _JustADecimal, value); - } - - public double JustADouble - { - get => _JustADouble; - set => this.RaiseAndSetIfChanged(ref _JustADouble, value); - } - - public int JustAInt32 - { - get => _JustAInt32; - set => this.RaiseAndSetIfChanged(ref _JustAInt32, value); - } - - public PropertyBindModel Model - { - get => _Model; - set => this.RaiseAndSetIfChanged(ref _Model, value); - } - - public double? NullableDouble - { - get => _NullableDouble; - set => this.RaiseAndSetIfChanged(ref _NullableDouble, value); - } - - public string Property1 - { - get => _Property1; - set => this.RaiseAndSetIfChanged(ref _Property1, value); - } - - public int Property2 - { - get => _Property2; - set => this.RaiseAndSetIfChanged(ref _Property2, value); - } - - public ReactiveList SomeCollectionOfStrings { get; } - } - } - - internal class NestedTextModel : ReactiveObject - { - private bool _hasData; - - private string _text; - - public bool HasData - { - get => _hasData; - set => this.RaiseAndSetIfChanged(ref _hasData, value); - } - - public string Text - { - get => _text; - set => this.RaiseAndSetIfChanged(ref _text, value); - } - } - - internal class TextModel : ReactiveObject - { - private NestedTextModel _value; - - public TextModel() - { - this.WhenAnyValue(x => x.Value.HasData).Subscribe(_ => this.RaisePropertyChanged(nameof(HasData))); - } - - public bool HasData => Value.HasData; - - public NestedTextModel Value - { - get - { - if (_value != null) - { - return _value; - } - - var newValue = _value = new NestedTextModel - { - Text = "text", - HasData = true - }; - - this.RaiseAndSetIfChanged(ref _value, newValue); - return _value; - } - } - } -} diff --git a/src/ReactiveUI.Tests/ReactiveObject/Mocks/OaphNameOfTestFixture.cs b/src/ReactiveUI.Tests/ReactiveObject/Mocks/OaphNameOfTestFixture.cs new file mode 100644 index 0000000000..ec24eb50ec --- /dev/null +++ b/src/ReactiveUI.Tests/ReactiveObject/Mocks/OaphNameOfTestFixture.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Reactive.Linq; +using System.Runtime.Serialization; + +namespace ReactiveUI.Tests +{ + public class OaphNameOfTestFixture : TestFixture + { + [IgnoreDataMember] + private readonly ObservableAsPropertyHelper _firstThreeLettersOfOneWord; + + [IgnoreDataMember] + private readonly ObservableAsPropertyHelper _lastThreeLettersOfOneWord; + + public OaphNameOfTestFixture() + { + this.WhenAnyValue(x => x.IsOnlyOneWord).Select(x => x ?? string.Empty).Select(x => x.Length >= 3 ? x.Substring(0, 3) : x).ToProperty(this, nameof(FirstThreeLettersOfOneWord), out _firstThreeLettersOfOneWord); + + _lastThreeLettersOfOneWord = this.WhenAnyValue(x => x.IsOnlyOneWord).Select(x => x ?? string.Empty).Select(x => x.Length >= 3 ? x.Substring(x.Length - 3, 3) : x).ToProperty(this, nameof(LastThreeLettersOfOneWord)); + } + + [IgnoreDataMember] + public string FirstThreeLettersOfOneWord => _firstThreeLettersOfOneWord.Value; + + [IgnoreDataMember] + public string LastThreeLettersOfOneWord => _lastThreeLettersOfOneWord.Value; + } +} diff --git a/src/ReactiveUI.Tests/ReactiveObject/Mocks/OaphTestFixture.cs b/src/ReactiveUI.Tests/ReactiveObject/Mocks/OaphTestFixture.cs new file mode 100644 index 0000000000..e178601154 --- /dev/null +++ b/src/ReactiveUI.Tests/ReactiveObject/Mocks/OaphTestFixture.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Reactive.Linq; +using System.Runtime.Serialization; + +namespace ReactiveUI.Tests +{ + public class OaphTestFixture : TestFixture + { + [IgnoreDataMember] + private readonly ObservableAsPropertyHelper _firstThreeLettersOfOneWord; + + public OaphTestFixture() + { + this.WhenAnyValue(x => x.IsOnlyOneWord).Select(x => x ?? string.Empty).Select(x => x.Length >= 3 ? x.Substring(0, 3) : x).ToProperty(this, x => x.FirstThreeLettersOfOneWord, out _firstThreeLettersOfOneWord); + } + + [IgnoreDataMember] + public string FirstThreeLettersOfOneWord => _firstThreeLettersOfOneWord.Value; + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/ReactiveObject/Mocks/TestFixture.cs b/src/ReactiveUI.Tests/ReactiveObject/Mocks/TestFixture.cs new file mode 100644 index 0000000000..72fe8c15e1 --- /dev/null +++ b/src/ReactiveUI.Tests/ReactiveObject/Mocks/TestFixture.cs @@ -0,0 +1,92 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using DynamicData.Binding; + +namespace ReactiveUI.Tests +{ + [DataContract] + public class TestFixture : ReactiveObject + { + [IgnoreDataMember] + private string _isNotNullString; + + [IgnoreDataMember] + private string _isOnlyOneWord; + + private string _notSerialized; + + [IgnoreDataMember] + private int? _nullableInt; + + [IgnoreDataMember] + private string _pocoProperty; + + [IgnoreDataMember] + private List _stackOverflowTrigger; + + [IgnoreDataMember] + private string _usesExprRaiseSet; + + public TestFixture() + { + TestCollection = new ObservableCollectionExtended(); + } + + [DataMember] + public string IsNotNullString + { + get => _isNotNullString; + set => this.RaiseAndSetIfChanged(ref _isNotNullString, value); + } + + [DataMember] + public string IsOnlyOneWord + { + get => _isOnlyOneWord; + set => this.RaiseAndSetIfChanged(ref _isOnlyOneWord, value); + } + + public string NotSerialized + { + get => _notSerialized; + set => this.RaiseAndSetIfChanged(ref _notSerialized, value); + } + + [DataMember] + public int? NullableInt + { + get => _nullableInt; + set => this.RaiseAndSetIfChanged(ref _nullableInt, value); + } + + [DataMember] + public string PocoProperty + { + get => _pocoProperty; + set => _pocoProperty = value; + } + + [DataMember] + public List StackOverflowTrigger + { + get => _stackOverflowTrigger; + set => this.RaiseAndSetIfChanged(ref _stackOverflowTrigger, value.ToList()); + } + + [DataMember] + public ObservableCollectionExtended TestCollection { get; protected set; } + + [DataMember] + public string UsesExprRaiseSet + { + get => _usesExprRaiseSet; + set => this.RaiseAndSetIfChanged(ref _usesExprRaiseSet, value); + } + } +} diff --git a/src/ReactiveUI.Tests/ReactiveObjectTest.cs b/src/ReactiveUI.Tests/ReactiveObject/ReactiveObjectTests.cs similarity index 65% rename from src/ReactiveUI.Tests/ReactiveObjectTest.cs rename to src/ReactiveUI.Tests/ReactiveObject/ReactiveObjectTests.cs index 4287f91557..2e47410095 100644 --- a/src/ReactiveUI.Tests/ReactiveObjectTest.cs +++ b/src/ReactiveUI.Tests/ReactiveObject/ReactiveObjectTests.cs @@ -7,133 +7,12 @@ using System.Collections.Generic; using System.Linq; using System.Reactive.Concurrency; -using System.Reactive.Linq; -using System.Runtime.Serialization; - using DynamicData; -using DynamicData.Binding; - using Xunit; namespace ReactiveUI.Tests { - [DataContract] - public class TestFixture : ReactiveObject - { - [IgnoreDataMember] - private string _isNotNullString; - - [IgnoreDataMember] - private string _isOnlyOneWord; - - private string _notSerialized; - - [IgnoreDataMember] - private int? _nullableInt; - - [IgnoreDataMember] - private string _pocoProperty; - - [IgnoreDataMember] - private List _stackOverflowTrigger; - - [IgnoreDataMember] - private string _usesExprRaiseSet; - - public TestFixture() - { - TestCollection = new ObservableCollectionExtended(); - } - - [DataMember] - public string IsNotNullString - { - get => _isNotNullString; - set => this.RaiseAndSetIfChanged(ref _isNotNullString, value); - } - - [DataMember] - public string IsOnlyOneWord - { - get => _isOnlyOneWord; - set => this.RaiseAndSetIfChanged(ref _isOnlyOneWord, value); - } - - public string NotSerialized - { - get => _notSerialized; - set => this.RaiseAndSetIfChanged(ref _notSerialized, value); - } - - [DataMember] - public int? NullableInt - { - get => _nullableInt; - set => this.RaiseAndSetIfChanged(ref _nullableInt, value); - } - - [DataMember] - public string PocoProperty - { - get => _pocoProperty; - set => _pocoProperty = value; - } - - [DataMember] - public List StackOverflowTrigger - { - get => _stackOverflowTrigger; - set => this.RaiseAndSetIfChanged(ref _stackOverflowTrigger, value.ToList()); - } - - [DataMember] - public ObservableCollectionExtended TestCollection { get; protected set; } - - [DataMember] - public string UsesExprRaiseSet - { - get => _usesExprRaiseSet; - set => this.RaiseAndSetIfChanged(ref _usesExprRaiseSet, value); - } - } - - public class OaphTestFixture : TestFixture - { - [IgnoreDataMember] - private readonly ObservableAsPropertyHelper _firstThreeLettersOfOneWord; - - public OaphTestFixture() - { - this.WhenAnyValue(x => x.IsOnlyOneWord).Select(x => x ?? string.Empty).Select(x => x.Length >= 3 ? x.Substring(0, 3) : x).ToProperty(this, x => x.FirstThreeLettersOfOneWord, out _firstThreeLettersOfOneWord); - } - - [IgnoreDataMember] - public string FirstThreeLettersOfOneWord => _firstThreeLettersOfOneWord.Value; - } - - public class OaphNameOfTestFixture : TestFixture - { - [IgnoreDataMember] - private readonly ObservableAsPropertyHelper _firstThreeLettersOfOneWord; - - [IgnoreDataMember] - private readonly ObservableAsPropertyHelper _lastThreeLettersOfOneWord; - - public OaphNameOfTestFixture() - { - this.WhenAnyValue(x => x.IsOnlyOneWord).Select(x => x ?? string.Empty).Select(x => x.Length >= 3 ? x.Substring(0, 3) : x).ToProperty(this, nameof(FirstThreeLettersOfOneWord), out _firstThreeLettersOfOneWord); - - _lastThreeLettersOfOneWord = this.WhenAnyValue(x => x.IsOnlyOneWord).Select(x => x ?? string.Empty).Select(x => x.Length >= 3 ? x.Substring(x.Length - 3, 3) : x).ToProperty(this, nameof(LastThreeLettersOfOneWord)); - } - - [IgnoreDataMember] - public string FirstThreeLettersOfOneWord => _firstThreeLettersOfOneWord.Value; - - [IgnoreDataMember] - public string LastThreeLettersOfOneWord => _lastThreeLettersOfOneWord.Value; - } - - public class ReactiveObjectTest + public class ReactiveObjectTests { [Fact] public void ChangingShouldAlwaysArriveBeforeChanged() diff --git a/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj b/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj index a449dbbd69..0de8591fcc 100644 --- a/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj +++ b/src/ReactiveUI.Tests/ReactiveUI.Tests.csproj @@ -1,34 +1,56 @@  - net461 + net461;netcoreapp2.0;uap10.0.16299 + + + + ReactiveUI.Tests_TemporaryKey.pfx - - + + + + + + + + + + + + + + - - - + + + + + + + + + + - - - + + diff --git a/src/ReactiveUI.Tests/Resolvers/DependencyResolverTests.cs b/src/ReactiveUI.Tests/Resolvers/DependencyResolverTests.cs new file mode 100644 index 0000000000..35dc2826b0 --- /dev/null +++ b/src/ReactiveUI.Tests/Resolvers/DependencyResolverTests.cs @@ -0,0 +1,109 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Splat; +using Xunit; + +namespace ReactiveUI.Tests +{ + public sealed class DependencyResolverTests : IDisposable + { + private readonly IMutableDependencyResolver _resolver; + + public DependencyResolverTests() + { + _resolver = new ModernDependencyResolver(); + _resolver.InitializeSplat(); + _resolver.InitializeReactiveUI(); + _resolver.RegisterViewsForViewModels(GetType().Assembly); + } + + [Fact] + public void AllDefaultServicesShouldBeRegistered() + { + using (_resolver.WithResolver()) + { + foreach (var shouldRegistered in GetServicesThatShouldBeRegistered()) + { + IEnumerable resolvedServices = _resolver.GetServices(shouldRegistered.Key); + Assert.Equal(shouldRegistered.Value.Count, resolvedServices.Count()); + foreach (Type implementationType in shouldRegistered.Value) + { + var isRegistered = resolvedServices.Any(rs => rs.GetType() == implementationType); + Assert.Equal(true, isRegistered); + } + } + } + } + + public void Dispose() + { + _resolver?.Dispose(); + } + + private static Dictionary> GetServicesThatShouldBeRegistered() + { + Dictionary> serviceTypeToImplementationTypes = new Dictionary>(); + + new Registrations().Register((factory, serviceType) => + { + if (serviceTypeToImplementationTypes.TryGetValue(serviceType, out List implementationTypes) == false) + { + implementationTypes = new List(); + serviceTypeToImplementationTypes.Add(serviceType, implementationTypes); + } + + implementationTypes.Add(factory().GetType()); + }); + + new PlatformRegistrations().Register((factory, serviceType) => + { + if (serviceTypeToImplementationTypes.TryGetValue(serviceType, out List implementationTypes) == false) + { + implementationTypes = new List(); + serviceTypeToImplementationTypes.Add(serviceType, implementationTypes); + } + + implementationTypes.Add(factory().GetType()); + }); + + var typeNames = new[] + { + "ReactiveUI.XamForms.Registrations, ReactiveUI.XamForms", + "ReactiveUI.Winforms.Registrations, ReactiveUI.Winforms", + "ReactiveUI.Wpf.Registrations, ReactiveUI.Wpf" + }; + + typeNames.ForEach(typeName => GetRegistrationsForPlatform(typeName, serviceTypeToImplementationTypes)); + + return serviceTypeToImplementationTypes; + } + + private static void GetRegistrationsForPlatform(string typeName, Dictionary> serviceTypeToImplementationTypes) + { + Type platformRegistrationsType = Type.GetType(typeName); + if (platformRegistrationsType != null) + { + var platformRegistrations = Activator.CreateInstance(platformRegistrationsType); + System.Reflection.MethodInfo register = platformRegistrationsType.GetMethod("Register"); + var registerParameter = new Action, Type>((factory, serviceType) => + { + if (serviceTypeToImplementationTypes.TryGetValue(serviceType, out List implementationTypes) == false) + { + implementationTypes = new List(); + serviceTypeToImplementationTypes.Add(serviceType, implementationTypes); + } + + implementationTypes.Add(factory().GetType()); + }); + + register.Invoke(platformRegistrations, new object[] { registerParameter }); + } + } + } +} diff --git a/src/ReactiveUI.Tests/INPCObservableForPropertyTests.cs b/src/ReactiveUI.Tests/Resolvers/INPCObservableForPropertyTests.cs similarity index 100% rename from src/ReactiveUI.Tests/INPCObservableForPropertyTests.cs rename to src/ReactiveUI.Tests/Resolvers/INPCObservableForPropertyTests.cs diff --git a/src/ReactiveUI.Tests/PocoObservableForPropertyTests.cs b/src/ReactiveUI.Tests/Resolvers/PocoObservableForPropertyTests.cs similarity index 100% rename from src/ReactiveUI.Tests/PocoObservableForPropertyTests.cs rename to src/ReactiveUI.Tests/Resolvers/PocoObservableForPropertyTests.cs diff --git a/src/ReactiveUI.Tests/Routing/Mocks/TestScreen.cs b/src/ReactiveUI.Tests/Routing/Mocks/TestScreen.cs new file mode 100644 index 0000000000..e221fd9a03 --- /dev/null +++ b/src/ReactiveUI.Tests/Routing/Mocks/TestScreen.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests +{ + public class TestScreen : ReactiveObject, IScreen + { + private RoutingState _router; + + public RoutingState Router + { + get => _router; + set => this.RaiseAndSetIfChanged(ref _router, value); + } + } +} diff --git a/src/ReactiveUI.Tests/Routing/Mocks/TestViewModel.cs b/src/ReactiveUI.Tests/Routing/Mocks/TestViewModel.cs new file mode 100644 index 0000000000..e656f9f720 --- /dev/null +++ b/src/ReactiveUI.Tests/Routing/Mocks/TestViewModel.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests +{ + public class TestViewModel : ReactiveObject, IRoutableViewModel + { + private string _someProp; + + public string SomeProp + { + get => _someProp; + set => this.RaiseAndSetIfChanged(ref _someProp, value); + } + + public string UrlPathSegment => "Test"; + + public IScreen HostScreen => null; + } +} diff --git a/src/ReactiveUI.Tests/RoutableViewModelMixinTests.cs b/src/ReactiveUI.Tests/Routing/RoutableViewModelMixinTests.cs similarity index 95% rename from src/ReactiveUI.Tests/RoutableViewModelMixinTests.cs rename to src/ReactiveUI.Tests/Routing/RoutableViewModelMixinTests.cs index 18b8bd19a8..d1e587b9ed 100644 --- a/src/ReactiveUI.Tests/RoutableViewModelMixinTests.cs +++ b/src/ReactiveUI.Tests/Routing/RoutableViewModelMixinTests.cs @@ -5,33 +5,10 @@ using System; using System.Reactive.Disposables; -using ReactiveUI.Tests.RoutableViewMixinTests; using Xunit; -#pragma warning disable SA1403 // FileMayOnlyContainASingleNamespace - namespace ReactiveUI.Tests { - namespace RoutableViewMixinTests - { - public class TestScreen : IScreen - { - public RoutingState Router { get; } = new RoutingState(); - } - - public class RoutableViewModel : ReactiveObject, IRoutableViewModel - { - public RoutableViewModel(IScreen screen) - { - HostScreen = screen; - } - - public string UrlPathSegment => "Test"; - - public IScreen HostScreen { get; } - } - } - public class RoutableViewModelMixinTests { [Fact] @@ -212,5 +189,22 @@ public void WhenNavigatingFromObservableCompletesWhenNavigationStackIsReset() Assert.Equal(1, count); } + + private class TestScreen : IScreen + { + public RoutingState Router { get; } = new RoutingState(); + } + + private class RoutableViewModel : ReactiveObject, IRoutableViewModel + { + public RoutableViewModel(IScreen screen) + { + HostScreen = screen; + } + + public string UrlPathSegment => "Test"; + + public IScreen HostScreen { get; } + } } } diff --git a/src/ReactiveUI.Tests/RoutingState.cs b/src/ReactiveUI.Tests/Routing/RoutingStateTests.cs old mode 100755 new mode 100644 similarity index 88% rename from src/ReactiveUI.Tests/RoutingState.cs rename to src/ReactiveUI.Tests/Routing/RoutingStateTests.cs index f1a662358a..8c6d7a3f4d --- a/src/ReactiveUI.Tests/RoutingState.cs +++ b/src/ReactiveUI.Tests/Routing/RoutingStateTests.cs @@ -12,34 +12,8 @@ using Microsoft.Reactive.Testing; using Xunit; -namespace ReactiveUI.Routing.Tests +namespace ReactiveUI.Tests { - public class TestViewModel : ReactiveObject, IRoutableViewModel - { - private string _someProp; - - public string SomeProp - { - get => _someProp; - set => this.RaiseAndSetIfChanged(ref _someProp, value); - } - - public string UrlPathSegment => "Test"; - - public IScreen HostScreen => null; - } - - public class TestScreen : ReactiveObject, IScreen - { - private RoutingState _router; - - public RoutingState Router - { - get => _router; - set => this.RaiseAndSetIfChanged(ref _router, value); - } - } - public class RoutingStateTests { [Fact] diff --git a/src/ReactiveUI.Tests/RxAppTest.cs b/src/ReactiveUI.Tests/RxAppTest.cs index 2f034d3505..1b054a062d 100644 --- a/src/ReactiveUI.Tests/RxAppTest.cs +++ b/src/ReactiveUI.Tests/RxAppTest.cs @@ -13,17 +13,6 @@ namespace ReactiveUI.Tests { public class RxAppTest { -#if !MONO - [Fact] - public void DepPropNotifierShouldBeFound() - { - RxApp.EnsureInitialized(); - - Assert.True(Locator.Current.GetServices() - .Any(x => x is DependencyObjectObservableForProperty)); - } -#endif - [Fact(Skip = "Requires initialize to run on seperate thread")] public void SchedulerShouldBeCurrentThreadInTestRunner() { diff --git a/src/ReactiveUI.Tests/Utilities/ApiApprovalBase.cs b/src/ReactiveUI.Tests/Utilities/ApiApprovalBase.cs new file mode 100644 index 0000000000..b70b89db78 --- /dev/null +++ b/src/ReactiveUI.Tests/Utilities/ApiApprovalBase.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using PublicApiGenerator; +using Shouldly; +using Splat; +using Xunit; + +namespace ReactiveUI.Tests +{ + [ExcludeFromCodeCoverage] + public abstract class ApiApprovalBase + { + private static readonly Regex _removeCoverletSectionRegex = new Regex(@"^namespace Coverlet\.Core\.Instrumentation\.Tracker.*?^}", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.Compiled); + + protected static void CheckApproval(Assembly assembly, [CallerMemberName]string memberName = null, [CallerFilePath]string filePath = null) + { + var targetFrameworkName = Assembly.GetExecutingAssembly().GetTargetFrameworkName(); + + var sourceDirectory = Path.GetDirectoryName(filePath); + + var approvedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.approved.txt"); + var receivedFileName = Path.Combine(sourceDirectory, $"ApiApprovalTests.{memberName}.{targetFrameworkName}.received.txt"); + + string approvedPublicApi = string.Empty; + + if (File.Exists(approvedFileName)) + { + approvedPublicApi = File.ReadAllText(approvedFileName); + } + + var receivedPublicApi = Filter(ApiGenerator.GeneratePublicApi(assembly)); + + if (!string.Equals(receivedPublicApi, approvedPublicApi, StringComparison.InvariantCulture)) + { + File.WriteAllText(receivedFileName, receivedPublicApi); + ShouldlyConfiguration.DiffTools.GetDiffTool().Open(receivedFileName, approvedFileName, true); + } + + Assert.Equal(approvedPublicApi, receivedPublicApi); + } + + private static string Filter(string text) + { + text = _removeCoverletSectionRegex.Replace(text, string.Empty); + return string.Join(Environment.NewLine, text.Split( + new[] + { + Environment.NewLine + }, StringSplitOptions.RemoveEmptyEntries) + .Where(l => + !l.StartsWith("[assembly: AssemblyVersion(", StringComparison.InvariantCulture) && + !l.StartsWith("[assembly: AssemblyFileVersion(", StringComparison.InvariantCulture) && + !l.StartsWith("[assembly: AssemblyInformationalVersion(", StringComparison.InvariantCulture) && + !string.IsNullOrWhiteSpace(l))); + } + } +} diff --git a/src/ReactiveUI.Tests/Utilities/CompatMixins.cs b/src/ReactiveUI.Tests/Utilities/CompatMixins.cs new file mode 100644 index 0000000000..68da2ae84e --- /dev/null +++ b/src/ReactiveUI.Tests/Utilities/CompatMixins.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests +{ + internal static class CompatMixins + { + public static void Run(this IEnumerable @this, Action block) + { + foreach (var v in @this) + { + block(v); + } + } + + public static IEnumerable SkipLast(this IEnumerable @this, int count) + { + return @this.Take(@this.Count() - count); + } + } +} diff --git a/src/ReactiveUI.Tests/Utilities/CountingTestScheduler.cs b/src/ReactiveUI.Tests/Utilities/CountingTestScheduler.cs new file mode 100644 index 0000000000..4f359991d3 --- /dev/null +++ b/src/ReactiveUI.Tests/Utilities/CountingTestScheduler.cs @@ -0,0 +1,47 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Concurrency; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveUI.Tests +{ + public class CountingTestScheduler : IScheduler + { + public CountingTestScheduler(IScheduler innerScheduler) + { + InnerScheduler = innerScheduler; + ScheduledItems = new List>(); + } + + public IScheduler InnerScheduler { get; } + + public List> ScheduledItems { get; } + + public DateTimeOffset Now => InnerScheduler.Now; + + public IDisposable Schedule(TState state, DateTimeOffset dueTime, Func action) + { + ScheduledItems.Add(new Tuple(() => action(this, state), null)); + return InnerScheduler.Schedule(state, dueTime, action); + } + + public IDisposable Schedule(TState state, TimeSpan dueTime, Func action) + { + ScheduledItems.Add(new Tuple(() => action(this, state), dueTime)); + return InnerScheduler.Schedule(state, dueTime, action); + } + + public IDisposable Schedule(TState state, Func action) + { + ScheduledItems.Add(new Tuple(() => action(this, state), null)); + return InnerScheduler.Schedule(state, action); + } + } +} diff --git a/src/ReactiveUI.Tests/Utilities/EnumerableTestMixin.cs b/src/ReactiveUI.Tests/Utilities/EnumerableTestMixin.cs new file mode 100644 index 0000000000..26172542ac --- /dev/null +++ b/src/ReactiveUI.Tests/Utilities/EnumerableTestMixin.cs @@ -0,0 +1,63 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace ReactiveUI.Tests +{ + public static class EnumerableTestMixin + { + public static void AssertAreEqual(this IEnumerable lhs, IEnumerable rhs) + { + var left = lhs.ToArray(); + var right = rhs.ToArray(); + + try + { + Assert.Equal(left.Length, right.Length); + for (var i = 0; i < left.Length; i++) + { + Assert.Equal(left[i], right[i]); + } + } + catch + { + Debug.WriteLine("lhs: [{0}]", string.Join(",", lhs.ToArray())); + Debug.WriteLine("rhs: [{0}]", string.Join(",", rhs.ToArray())); + throw; + } + } + + public static IEnumerable DistinctUntilChanged(this IEnumerable @this) + { + var isFirst = true; + var lastValue = default(T); + + foreach (var v in @this) + { + if (isFirst) + { + lastValue = v; + isFirst = false; + yield return v; + continue; + } + + if (!EqualityComparer.Default.Equals(v, lastValue)) + { + yield return v; + } + + lastValue = v; + } + } + } +} diff --git a/src/ReactiveUI.Tests/JsonHelper.cs b/src/ReactiveUI.Tests/Utilities/JsonHelper.cs similarity index 100% rename from src/ReactiveUI.Tests/JsonHelper.cs rename to src/ReactiveUI.Tests/Utilities/JsonHelper.cs diff --git a/src/ReactiveUI.Tests/TestLogger.cs b/src/ReactiveUI.Tests/Utilities/TestLogger.cs similarity index 100% rename from src/ReactiveUI.Tests/TestLogger.cs rename to src/ReactiveUI.Tests/Utilities/TestLogger.cs diff --git a/src/ReactiveUI.Tests/Utilities/UseInvariantCulture.cs b/src/ReactiveUI.Tests/Utilities/UseInvariantCulture.cs new file mode 100644 index 0000000000..dcd7b217c6 --- /dev/null +++ b/src/ReactiveUI.Tests/Utilities/UseInvariantCulture.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit.Sdk; + +namespace ReactiveUI.Tests +{ + // run tests on invariant culture to avoid problems e.g with culture specific decimal separator + public class UseInvariantCulture : BeforeAfterTestAttribute + { + private CultureInfo _storedCulture; + + public override void Before(MethodInfo methodUnderTest) + { + _storedCulture = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; + } + + public override void After(MethodInfo methodUnderTest) + { + Thread.CurrentThread.CurrentCulture = _storedCulture; + } + } +} diff --git a/src/ReactiveUI.Tests/Utility.cs b/src/ReactiveUI.Tests/Utility.cs deleted file mode 100644 index 7a3ec8e690..0000000000 --- a/src/ReactiveUI.Tests/Utility.cs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) 2019 .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. -// See the LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Reactive.Concurrency; -using System.Reflection; -using System.Threading; -using Xunit; -using Xunit.Sdk; - -namespace ReactiveUI.Tests -{ - public static class EnumerableTestMixin - { - public static void AssertAreEqual(this IEnumerable lhs, IEnumerable rhs) - { - var left = lhs.ToArray(); - var right = rhs.ToArray(); - - try - { - Assert.Equal(left.Length, right.Length); - for (var i = 0; i < left.Length; i++) - { - Assert.Equal(left[i], right[i]); - } - } - catch - { - Debug.WriteLine("lhs: [{0}]", string.Join(",", lhs.ToArray())); - Debug.WriteLine("rhs: [{0}]", string.Join(",", rhs.ToArray())); - throw; - } - } - - public static IEnumerable DistinctUntilChanged(this IEnumerable @this) - { - var isFirst = true; - var lastValue = default(T); - - foreach (var v in @this) - { - if (isFirst) - { - lastValue = v; - isFirst = false; - yield return v; - continue; - } - - if (!EqualityComparer.Default.Equals(v, lastValue)) - { - yield return v; - } - - lastValue = v; - } - } - } - - public class CountingTestScheduler : IScheduler - { - public CountingTestScheduler(IScheduler innerScheduler) - { - InnerScheduler = innerScheduler; - ScheduledItems = new List>(); - } - - public IScheduler InnerScheduler { get; private set; } - - public List> ScheduledItems { get; private set; } - - public DateTimeOffset Now => InnerScheduler.Now; - - public IDisposable Schedule(TState state, DateTimeOffset dueTime, Func action) - { - ScheduledItems.Add(new Tuple(() => action(this, state), null)); - return InnerScheduler.Schedule(state, dueTime, action); - } - - public IDisposable Schedule(TState state, TimeSpan dueTime, Func action) - { - ScheduledItems.Add(new Tuple(() => action(this, state), dueTime)); - return InnerScheduler.Schedule(state, dueTime, action); - } - - public IDisposable Schedule(TState state, Func action) - { - ScheduledItems.Add(new Tuple(() => action(this, state), null)); - return InnerScheduler.Schedule(state, action); - } - } - - // run tests on invariant culture to avoid problems e.g with culture specific decimal separator - public class UseInvariantCulture : BeforeAfterTestAttribute - { - private CultureInfo _storedCulture; - - public override void Before(MethodInfo methodUnderTest) - { - _storedCulture = Thread.CurrentThread.CurrentCulture; - Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; - } - - public override void After(MethodInfo methodUnderTest) - { - Thread.CurrentThread.CurrentCulture = _storedCulture; - } - } - - internal static class CompatMixins - { - public static void Run(this IEnumerable @this, Action block) - { - foreach (var v in @this) - { - block(v); - } - } - - public static IEnumerable SkipLast(this IEnumerable @this, int count) - { - return @this.Take(@this.Count() - count); - } - } -} diff --git a/src/ReactiveUI.Tests/WhenAny/Mockups/HostTestFixture.cs b/src/ReactiveUI.Tests/WhenAny/Mockups/HostTestFixture.cs new file mode 100644 index 0000000000..76195770ac --- /dev/null +++ b/src/ReactiveUI.Tests/WhenAny/Mockups/HostTestFixture.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class HostTestFixture : ReactiveObject + { + private TestFixture _Child; + + private NonObservableTestFixture _PocoChild; + + private int _SomeOtherParam; + + public TestFixture Child + { + get => _Child; + set => this.RaiseAndSetIfChanged(ref _Child, value); + } + + public NonObservableTestFixture PocoChild + { + get => _PocoChild; + set => this.RaiseAndSetIfChanged(ref _PocoChild, value); + } + + public int SomeOtherParam + { + get => _SomeOtherParam; + set => this.RaiseAndSetIfChanged(ref _SomeOtherParam, value); + } + } +} diff --git a/src/ReactiveUI.Tests/WhenAny/Mockups/NonObservableTestFixture.cs b/src/ReactiveUI.Tests/WhenAny/Mockups/NonObservableTestFixture.cs new file mode 100644 index 0000000000..4c2143c333 --- /dev/null +++ b/src/ReactiveUI.Tests/WhenAny/Mockups/NonObservableTestFixture.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class NonObservableTestFixture + { + public TestFixture Child { get; set; } + } +} diff --git a/src/ReactiveUI.Tests/WhenAny/Mockups/NonReactiveINPCObject.cs b/src/ReactiveUI.Tests/WhenAny/Mockups/NonReactiveINPCObject.cs new file mode 100644 index 0000000000..1c7bd92e01 --- /dev/null +++ b/src/ReactiveUI.Tests/WhenAny/Mockups/NonReactiveINPCObject.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System.ComponentModel; + +namespace ReactiveUI.Tests +{ + public class NonReactiveINPCObject : INotifyPropertyChanged + { + private TestFixture _inpcProperty; + + public event PropertyChangedEventHandler PropertyChanged; + + public TestFixture InpcProperty + { + get => _inpcProperty; + set + { + if (_inpcProperty == value) + { + return; + } + + _inpcProperty = value; + + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InpcProperty))); + } + } + } +} \ No newline at end of file diff --git a/src/ReactiveUI.Tests/WhenAny/Mockups/ObjChain1.cs b/src/ReactiveUI.Tests/WhenAny/Mockups/ObjChain1.cs new file mode 100644 index 0000000000..affaf79c2a --- /dev/null +++ b/src/ReactiveUI.Tests/WhenAny/Mockups/ObjChain1.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class ObjChain1 : ReactiveObject + { + private ObjChain2 _model = new ObjChain2(); + + public ObjChain2 Model + { + get => _model; + set => this.RaiseAndSetIfChanged(ref _model, value); + } + } +} diff --git a/src/ReactiveUI.Tests/WhenAny/Mockups/ObjChain2.cs b/src/ReactiveUI.Tests/WhenAny/Mockups/ObjChain2.cs new file mode 100644 index 0000000000..8f606e55f4 --- /dev/null +++ b/src/ReactiveUI.Tests/WhenAny/Mockups/ObjChain2.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class ObjChain2 : ReactiveObject + { + private ObjChain3 _model = new ObjChain3(); + + public ObjChain3 Model + { + get => _model; + set => this.RaiseAndSetIfChanged(ref _model, value); + } + } +} diff --git a/src/ReactiveUI.Tests/WhenAny/Mockups/ObjChain3.cs b/src/ReactiveUI.Tests/WhenAny/Mockups/ObjChain3.cs new file mode 100644 index 0000000000..6c8fb5a344 --- /dev/null +++ b/src/ReactiveUI.Tests/WhenAny/Mockups/ObjChain3.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +namespace ReactiveUI.Tests +{ + public class ObjChain3 : ReactiveObject + { + private HostTestFixture _model = new HostTestFixture(); + + public HostTestFixture Model + { + get => _model; + set => this.RaiseAndSetIfChanged(ref _model, value); + } + } +} diff --git a/src/ReactiveUI.Tests/ReactiveNotifyPropertyChangedMixinTest.cs b/src/ReactiveUI.Tests/WhenAny/ReactiveNotifyPropertyChangedMixinTest.cs old mode 100755 new mode 100644 similarity index 70% rename from src/ReactiveUI.Tests/ReactiveNotifyPropertyChangedMixinTest.cs rename to src/ReactiveUI.Tests/WhenAny/ReactiveNotifyPropertyChangedMixinTest.cs index de92596dc5..2da2874b61 --- a/src/ReactiveUI.Tests/ReactiveNotifyPropertyChangedMixinTest.cs +++ b/src/ReactiveUI.Tests/WhenAny/ReactiveNotifyPropertyChangedMixinTest.cs @@ -5,19 +5,12 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Linq.Expressions; using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Threading; -using System.Threading.Tasks; -using System.Windows; - -using DynamicData; -using DynamicData.Binding; - using Microsoft.Reactive.Testing; using ReactiveUI.Legacy; @@ -25,137 +18,8 @@ using Xunit; -#if !MONO -using System.Windows.Controls; -#endif - namespace ReactiveUI.Tests { - public class TestWhenAnyObsViewModel : ReactiveObject - { - private IObservable> _changes; - - private ObservableCollectionExtended _myListOfInts; - - public TestWhenAnyObsViewModel() - { - Command1 = ReactiveCommand.CreateFromObservable(Observable.Return); - Command2 = ReactiveCommand.CreateFromObservable(Observable.Return); - Command3 = ReactiveCommand.CreateFromObservable(Observable.Return); - } - - public IObservable> Changes - { - get => _changes; - set => this.RaiseAndSetIfChanged(ref _changes, value); - } - - public ReactiveCommand Command1 { get; set; } - - public ReactiveCommand Command2 { get; set; } - - public ReactiveCommand Command3 { get; set; } - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Test usage")] - public ObservableCollectionExtended MyListOfInts - { - get => _myListOfInts; - set - { - this.RaiseAndSetIfChanged(ref _myListOfInts, value); - Changes = MyListOfInts?.ToObservableChangeSet(); - } - } - } - - public class HostTestFixture : ReactiveObject - { - public TestFixture _Child; - - public NonObservableTestFixture _PocoChild; - - public int _SomeOtherParam; - - public TestFixture Child - { - get => _Child; - set => this.RaiseAndSetIfChanged(ref _Child, value); - } - - public NonObservableTestFixture PocoChild - { - get => _PocoChild; - set => this.RaiseAndSetIfChanged(ref _PocoChild, value); - } - - public int SomeOtherParam - { - get => _SomeOtherParam; - set => this.RaiseAndSetIfChanged(ref _SomeOtherParam, value); - } - } - - public class NonObservableTestFixture - { - public TestFixture Child { get; set; } - } - - public class NonReactiveINPCObject : INotifyPropertyChanged - { - private TestFixture _inpcProperty; - - public event PropertyChangedEventHandler PropertyChanged; - - public TestFixture InpcProperty - { - get => _inpcProperty; - set - { - if (_inpcProperty == value) - { - return; - } - - _inpcProperty = value; - - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InpcProperty))); - } - } - } - - public class ObjChain1 : ReactiveObject - { - public ObjChain2 _Model = new ObjChain2(); - - public ObjChain2 Model - { - get => _Model; - set => this.RaiseAndSetIfChanged(ref _Model, value); - } - } - - public class ObjChain2 : ReactiveObject - { - public ObjChain3 _Model = new ObjChain3(); - - public ObjChain3 Model - { - get => _Model; - set => this.RaiseAndSetIfChanged(ref _Model, value); - } - } - - public class ObjChain3 : ReactiveObject - { - public HostTestFixture _Model = new HostTestFixture(); - - public HostTestFixture Model - { - get => _Model; - set => this.RaiseAndSetIfChanged(ref _Model, value); - } - } - public class ReactiveNotifyPropertyChangedMixinTest { [Fact] @@ -268,7 +132,7 @@ public void OFPNamedPropertyTest() sched => { var fixture = new TestFixture(); - var changes = fixture.ObservableForProperty(x => x.IsOnlyOneWord).CreateCollection(scheduler: ImmediateScheduler.Instance); + var changes = fixture.ObservableForProperty(x => x.IsOnlyOneWord).CreateCollection(scheduler: ImmediateScheduler.Instance); fixture.IsOnlyOneWord = "Foo"; sched.Start(); @@ -302,7 +166,7 @@ public void OFPNamedPropertyTestBeforeChange() { IsOnlyOneWord = "Pre" }; - var changes = fixture.ObservableForProperty(x => x.IsOnlyOneWord, beforeChange: true).CreateCollection(scheduler: ImmediateScheduler.Instance); + var changes = fixture.ObservableForProperty(x => x.IsOnlyOneWord, beforeChange: true).CreateCollection(scheduler: ImmediateScheduler.Instance); sched.Start(); Assert.Equal(0, changes.Count); @@ -331,7 +195,7 @@ public void OFPNamedPropertyTestNoSkipInitial() { IsOnlyOneWord = "Pre" }; - var changes = fixture.ObservableForProperty(x => x.IsOnlyOneWord, skipInitial: false).CreateCollection(scheduler: ImmediateScheduler.Instance); + var changes = fixture.ObservableForProperty(x => x.IsOnlyOneWord, skipInitial: false).CreateCollection(scheduler: ImmediateScheduler.Instance); sched.Start(); Assert.Equal(1, changes.Count); @@ -353,7 +217,7 @@ public void OFPNamedPropertyTestRepeats() sched => { var fixture = new TestFixture(); - var changes = fixture.ObservableForProperty(x => x.IsOnlyOneWord).CreateCollection(scheduler: ImmediateScheduler.Instance); + var changes = fixture.ObservableForProperty(x => x.IsOnlyOneWord).CreateCollection(scheduler: ImmediateScheduler.Instance); fixture.IsOnlyOneWord = "Foo"; sched.Start(); @@ -629,6 +493,70 @@ public void WhenAnyShouldWorkEvenWithNormalProperties() Assert.Equal(null, output4[0]); } + [Fact] + public void ChangedShouldHaveValidData() + { + var fixture = new TestFixture + { + IsNotNullString = "Foo", + IsOnlyOneWord = "Baz", + PocoProperty = "Bamf" + }; + + object sender = null; + string propertyName = null; + fixture.Changed.ObserveOn(ImmediateScheduler.Instance).Subscribe( + x => + { + sender = x.Sender; + propertyName = x.PropertyName; + }); + + fixture.UsesExprRaiseSet = "abc"; + + Assert.Equal(fixture, sender); + Assert.Equal(nameof(fixture.UsesExprRaiseSet), propertyName); + + sender = null; + propertyName = null; + fixture.PocoProperty = "abc"; + + Assert.Equal(null, sender); + Assert.Equal(null, propertyName); + } + + [Fact] + public void ChangingShouldHaveValidData() + { + var fixture = new TestFixture + { + IsNotNullString = "Foo", + IsOnlyOneWord = "Baz", + PocoProperty = "Bamf" + }; + + object sender = null; + string propertyName = null; + fixture.Changing.ObserveOn(ImmediateScheduler.Instance).Subscribe( + x => + { + sender = x.Sender; + propertyName = x.PropertyName; + }); + + fixture.UsesExprRaiseSet = "abc"; + + Assert.Equal(fixture, sender); + Assert.Equal(nameof(fixture.UsesExprRaiseSet), propertyName); + + sender = null; + propertyName = null; + fixture.PocoProperty = "abc"; + + Assert.Equal(null, sender); + Assert.Equal(null, propertyName); + } + [Fact] public void WhenAnySmokeTest() { @@ -758,168 +686,4 @@ public void WhenAnyValueSmokeTest() }); } } - - public class WhenAnyObservableTests - { - [Fact] - public void NullObservablesDoNotCauseExceptions() - { - var fixture = new TestWhenAnyObsViewModel(); - fixture.Command1 = null; - - // these are the overloads of WhenAnyObservable that perform a Merge - fixture.WhenAnyObservable(x => x.Command1).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); - - // these are the overloads of WhenAnyObservable that perform a CombineLatest - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, (zero, one) => Unit.Default).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two) => Unit.Default).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three) => Unit.Default).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four) => Unit.Default).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five) => Unit.Default).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five, six) => Unit.Default).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five, six, seven) => Unit.Default).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five, six, seven, eight) => Unit.Default).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five, six, seven, eight, nine) => Unit.Default).Subscribe(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five, six, seven, eight, nine, ten) => Unit.Default).Subscribe(); - } - - [Fact] - public async Task WhenAnyObservableSmokeTestCombining() - { - var fixture = new TestWhenAnyObsViewModel(); - - var list = new List(); - fixture.WhenAnyObservable(x => x.Command3, x => x.Command1, (s, i) => s + " : " + i).Subscribe(list.Add); - - Assert.Equal(0, list.Count); - - await fixture.Command1.Execute(1); - await fixture.Command3.Execute("foo"); - Assert.Equal(1, list.Count); - - await fixture.Command1.Execute(2); - Assert.Equal(2, list.Count); - - await fixture.Command3.Execute("bar"); - Assert.Equal(3, list.Count); - - Assert.True( - new[] { "foo : 1", "foo : 2", "bar : 2", }.Zip( - list, - (expected, actual) => new - { - expected, - actual - }).All(x => x.expected == x.actual)); - } - - [Fact] - public async Task WhenAnyObservableSmokeTestMerging() - { - var fixture = new TestWhenAnyObsViewModel(); - - var list = new List(); - fixture.WhenAnyObservable(x => x.Command1, x => x.Command2).Subscribe(list.Add); - - Assert.Equal(0, list.Count); - - await fixture.Command1.Execute(1); - Assert.Equal(1, list.Count); - - await fixture.Command2.Execute(2); - Assert.Equal(2, list.Count); - - await fixture.Command1.Execute(1); - Assert.Equal(3, list.Count); - - Assert.True( - new[] { 1, 2, 1, }.Zip( - list, - (expected, actual) => new - { - expected, - actual - }).All(x => x.expected == x.actual)); - } - - [Fact] - public void WhenAnyObservableWithNullObjectShouldUpdateWhenObjectIsntNullAnymore() - { - var fixture = new TestWhenAnyObsViewModel(); - fixture.WhenAnyObservable(x => x.Changes).Bind(out var output).Subscribe(); - - Assert.Equal(0, output.Count); - - fixture.MyListOfInts = new ObservableCollectionExtended(); - Assert.Equal(0, output.Count); - - fixture.MyListOfInts.Add(1); - Assert.Equal(1, output.Count); - - fixture.MyListOfInts = null; - Assert.Equal(1, output.Count); - } - } - -#if !MONO - public class HostTestView : Control, IViewFor - { - public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(HostTestFixture), typeof(HostTestView), new PropertyMetadata(null)); - - public HostTestFixture ViewModel - { - get => (HostTestFixture)GetValue(ViewModelProperty); - set => SetValue(ViewModelProperty, value); - } - - object IViewFor.ViewModel - { - get => ViewModel; - set => ViewModel = (HostTestFixture)value; - } - } - - public class WhenAnyThroughDependencyObjectTests - { - [WpfFact] - public void WhenAnyThroughAViewShouldntGiveNullValues() - { - var vm = new HostTestFixture - { - Child = new TestFixture - { - IsNotNullString = "Foo", - IsOnlyOneWord = "Baz", - PocoProperty = "Bamf" - }, - }; - - var fixture = new HostTestView(); - - var output = new List(); - - Assert.Equal(0, output.Count); - Assert.Null(fixture.ViewModel); - - fixture.WhenAnyValue(x => x.ViewModel.Child.IsNotNullString).Subscribe(output.Add); - - fixture.ViewModel = vm; - Assert.Equal(1, output.Count); - - fixture.ViewModel.Child.IsNotNullString = "Bar"; - Assert.Equal(2, output.Count); - new[] { "Foo", "Bar" }.AssertAreEqual(output); - } - } -#endif } diff --git a/src/ReactiveUI.Tests/WhenAny/TestWhenAnyObsViewModel.cs b/src/ReactiveUI.Tests/WhenAny/TestWhenAnyObsViewModel.cs new file mode 100644 index 0000000000..c6ac9e965e --- /dev/null +++ b/src/ReactiveUI.Tests/WhenAny/TestWhenAnyObsViewModel.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using DynamicData; +using DynamicData.Binding; + +namespace ReactiveUI.Tests +{ + public class TestWhenAnyObsViewModel : ReactiveObject + { + private IObservable> _changes; + + private ObservableCollectionExtended _myListOfInts; + + public TestWhenAnyObsViewModel() + { + Command1 = ReactiveCommand.CreateFromObservable(Observable.Return, outputScheduler: ImmediateScheduler.Instance); + Command2 = ReactiveCommand.CreateFromObservable(Observable.Return, outputScheduler: ImmediateScheduler.Instance); + Command3 = ReactiveCommand.CreateFromObservable(Observable.Return, outputScheduler: ImmediateScheduler.Instance); + } + + public IObservable> Changes + { + get => _changes; + set => this.RaiseAndSetIfChanged(ref _changes, value); + } + + public ReactiveCommand Command1 { get; set; } + + public ReactiveCommand Command2 { get; set; } + + public ReactiveCommand Command3 { get; set; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "Test usage")] + public ObservableCollectionExtended MyListOfInts + { + get => _myListOfInts; + set + { + this.RaiseAndSetIfChanged(ref _myListOfInts, value); + Changes = MyListOfInts?.ToObservableChangeSet(); + } + } + } +} diff --git a/src/ReactiveUI.Tests/WhenAny/WhenAnyObservableTests.cs b/src/ReactiveUI.Tests/WhenAny/WhenAnyObservableTests.cs new file mode 100644 index 0000000000..9d8b280d92 --- /dev/null +++ b/src/ReactiveUI.Tests/WhenAny/WhenAnyObservableTests.cs @@ -0,0 +1,130 @@ +// Copyright (c) 2019 .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. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Threading.Tasks; +using DynamicData; +using DynamicData.Binding; +using Xunit; + +namespace ReactiveUI.Tests +{ + public class WhenAnyObservableTests + { + [Fact] + public void NullObservablesDoNotCauseExceptions() + { + var fixture = new TestWhenAnyObsViewModel(); + fixture.Command1 = null; + + // these are the overloads of WhenAnyObservable that perform a Merge + fixture.WhenAnyObservable(x => x.Command1).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1).Subscribe(); + + // these are the overloads of WhenAnyObservable that perform a CombineLatest + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, (zero, one) => Unit.Default).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two) => Unit.Default).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three) => Unit.Default).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four) => Unit.Default).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five) => Unit.Default).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five, six) => Unit.Default).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five, six, seven) => Unit.Default).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five, six, seven, eight) => Unit.Default).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five, six, seven, eight, nine) => Unit.Default).Subscribe(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, x => x.Command1, (zero, one, two, three, four, five, six, seven, eight, nine, ten) => Unit.Default).Subscribe(); + } + + [Fact] + public async Task WhenAnyObservableSmokeTestCombining() + { + var fixture = new TestWhenAnyObsViewModel(); + + var list = new List(); + fixture.WhenAnyObservable(x => x.Command3, x => x.Command1, (s, i) => s + " : " + i).ObserveOn(ImmediateScheduler.Instance).Subscribe(list.Add); + + Assert.Equal(0, list.Count); + + await fixture.Command1.Execute(1); + await fixture.Command3.Execute("foo"); + Assert.Equal(1, list.Count); + + await fixture.Command1.Execute(2); + Assert.Equal(2, list.Count); + + await fixture.Command3.Execute("bar"); + Assert.Equal(3, list.Count); + + Assert.True( + new[] { "foo : 1", "foo : 2", "bar : 2", }.Zip( + list, + (expected, actual) => new + { + expected, + actual + }).All(x => x.expected == x.actual)); + } + + [Fact] + public async Task WhenAnyObservableSmokeTestMerging() + { + var fixture = new TestWhenAnyObsViewModel(); + + var list = new List(); + fixture.WhenAnyObservable(x => x.Command1, x => x.Command2).ObserveOn(ImmediateScheduler.Instance).Subscribe(list.Add); + + Assert.Equal(0, list.Count); + + await fixture.Command1.Execute(1); + Assert.Equal(1, list.Count); + + await fixture.Command2.Execute(2); + Assert.Equal(2, list.Count); + + await fixture.Command1.Execute(1); + Assert.Equal(3, list.Count); + + Assert.True( + new[] { 1, 2, 1, }.Zip( + list, + (expected, actual) => new + { + expected, + actual + }).All(x => x.expected == x.actual)); + } + + [Fact] + public void WhenAnyObservableWithNullObjectShouldUpdateWhenObjectIsntNullAnymore() + { + var fixture = new TestWhenAnyObsViewModel(); + fixture.WhenAnyObservable(x => x.Changes).Bind(out var output).ObserveOn(ImmediateScheduler.Instance).Subscribe(); + + Assert.Equal(0, output.Count); + + fixture.MyListOfInts = new ObservableCollectionExtended(); + Assert.Equal(0, output.Count); + + fixture.MyListOfInts.Add(1); + Assert.Equal(1, output.Count); + + fixture.MyListOfInts = null; + Assert.Equal(1, output.Count); + } + } +} diff --git a/src/ReactiveUI.Winforms/IReactiveDerivedBindingList.cs b/src/ReactiveUI.Winforms/IReactiveDerivedBindingList.cs index d0c65356c0..fb01052556 100644 --- a/src/ReactiveUI.Winforms/IReactiveDerivedBindingList.cs +++ b/src/ReactiveUI.Winforms/IReactiveDerivedBindingList.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using ReactiveUI.Legacy; -namespace ReactiveUI.Winforms +namespace ReactiveUI.Winforms.Legacy { /// /// IReactiveDerivedList represents a bindinglist whose contents will "follow" another diff --git a/src/ReactiveUI.sln b/src/ReactiveUI.sln index 207b899105..1ef4b4d2ed 100644 --- a/src/ReactiveUI.sln +++ b/src/ReactiveUI.sln @@ -6,11 +6,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig ..\.vsts-ci.yml = ..\.vsts-ci.yml + analyzers.ruleset = analyzers.ruleset + analyzers.tests.ruleset = analyzers.tests.ruleset Directory.build.props = Directory.build.props Directory.build.targets = Directory.build.targets global.json = global.json - reactiveui.ruleset = reactiveui.ruleset - reactiveui.tests.ruleset = reactiveui.tests.ruleset ..\stylecop.json = ..\stylecop.json EndProjectSection EndProject diff --git a/src/ReactiveUI/CollectionDebugView.cs b/src/ReactiveUI/Legacy/CollectionDebugView.cs similarity index 79% rename from src/ReactiveUI/CollectionDebugView.cs rename to src/ReactiveUI/Legacy/CollectionDebugView.cs index 65a85db040..7d417cf07f 100644 --- a/src/ReactiveUI/CollectionDebugView.cs +++ b/src/ReactiveUI/Legacy/CollectionDebugView.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.Diagnostics; -namespace ReactiveUI +namespace ReactiveUI.Legacy { internal sealed class CollectionDebugView { @@ -15,12 +15,7 @@ internal sealed class CollectionDebugView public CollectionDebugView(ICollection collection) { - if (collection == null) - { - throw new ArgumentNullException(nameof(collection), "collection is null."); - } - - _collection = collection; + _collection = collection ?? throw new ArgumentNullException(nameof(collection), "collection is null."); } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] diff --git a/src/ReactiveUI/Registration/RefcountDisposeWrapper.cs b/src/ReactiveUI/Legacy/RefcountDisposeWrapper.cs similarity index 96% rename from src/ReactiveUI/Registration/RefcountDisposeWrapper.cs rename to src/ReactiveUI/Legacy/RefcountDisposeWrapper.cs index 7f1b1415cc..cadf8b0d02 100644 --- a/src/ReactiveUI/Registration/RefcountDisposeWrapper.cs +++ b/src/ReactiveUI/Legacy/RefcountDisposeWrapper.cs @@ -6,7 +6,7 @@ using System; using System.Threading; -namespace ReactiveUI +namespace ReactiveUI.Legacy { internal sealed class RefcountDisposeWrapper { diff --git a/src/analyzers.tests.ruleset b/src/analyzers.tests.ruleset index c42f159c27..191cfeda3a 100644 --- a/src/analyzers.tests.ruleset +++ b/src/analyzers.tests.ruleset @@ -14,10 +14,7 @@ - - - @@ -66,7 +63,6 @@ - @@ -113,28 +109,32 @@ + + + + - + + - - + - + @@ -143,9 +143,142 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +