diff --git a/Tests/Perspex.Base.UnitTests/GlobalSuppressions.cs b/Tests/Perspex.Base.UnitTests/GlobalSuppressions.cs index c4e71ed365..5b748a54d0 100644 --- a/Tests/Perspex.Base.UnitTests/GlobalSuppressions.cs +++ b/Tests/Perspex.Base.UnitTests/GlobalSuppressions.cs @@ -3,6 +3,10 @@ // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage( + "StyleCop.CSharp.MaintainabilityRules", + "SA1401:Fields must be private", + Justification = "PerspexProperty fields should not be private.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage( "StyleCop.CSharp.DocumentationRules", "SA1600:Elements must be documented", diff --git a/Tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj b/Tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj index e0ed27530f..d1fa0f4dd0 100644 --- a/Tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj +++ b/Tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj @@ -79,9 +79,15 @@ + + + + + + + - diff --git a/Tests/Perspex.Base.UnitTests/PerspexObjectTests.cs b/Tests/Perspex.Base.UnitTests/PerspexObjectTests.cs deleted file mode 100644 index 88f14bd565..0000000000 --- a/Tests/Perspex.Base.UnitTests/PerspexObjectTests.cs +++ /dev/null @@ -1,698 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright 2013 MIT Licence. See licence.md for more information. -// -// ----------------------------------------------------------------------- - -namespace Perspex.Base.UnitTests -{ - using System; - using System.Linq; - using System.Reactive.Linq; - using System.Reactive.Subjects; - using Xunit; - - public class PerspexObjectTests - { - public PerspexObjectTests() - { - // Ensure properties are registered. - PerspexProperty p; - p = Class1.FooProperty; - p = Class2.BarProperty; - } - - [Fact] - public void GetProperties_Returns_Registered_Properties() - { - string[] names = PerspexObject.GetProperties(typeof(Class1)).Select(x => x.Name).ToArray(); - - Assert.Equal(new[] { "Foo", "Baz", "Qux" }, names); - } - - [Fact] - public void GetProperties_Returns_Registered_Properties_For_Base_Types() - { - string[] names = PerspexObject.GetProperties(typeof(Class2)).Select(x => x.Name).ToArray(); - - Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux" }, names); - } - - [Fact] - public void GetValue_Returns_Default_Value() - { - Class1 target = new Class1(); - - Assert.Equal("foodefault", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void GetValue_Returns_Overridden_Default_Value() - { - Class2 target = new Class2(); - - Assert.Equal("foooverride", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void GetValue_Returns_Set_Value() - { - Class1 target = new Class1(); - - target.SetValue(Class1.FooProperty, "newvalue"); - - Assert.Equal("newvalue", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void GetValue_Returns_Inherited_Value() - { - Class1 parent = new Class1(); - Class2 child = new Class2 { Parent = parent }; - - parent.SetValue(Class1.BazProperty, "changed"); - - Assert.Equal("changed", child.GetValue(Class1.BazProperty)); - } - - [Fact] - public void ClearValue_Clears_Value() - { - Class1 target = new Class1(); - - target.SetValue(Class1.FooProperty, "newvalue"); - target.ClearValue(Class1.FooProperty); - - Assert.Equal("foodefault", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void SetValue_Sets_Value() - { - Class1 target = new Class1(); - - target.SetValue(Class1.FooProperty, "newvalue"); - - Assert.Equal("newvalue", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void SetValue_Raises_PropertyChanged() - { - Class1 target = new Class1(); - bool raised = false; - - target.PropertyChanged += (s, e) => - { - raised = s == target && - e.Property == Class1.FooProperty && - (string)e.OldValue == "foodefault" && - (string)e.NewValue == "newvalue"; - }; - - target.SetValue(Class1.FooProperty, "newvalue"); - - Assert.True(raised); - } - - [Fact] - public void SetValue_Doesnt_Raise_PropertyChanged_If_Value_Not_Changed() - { - Class1 target = new Class1(); - bool raised = false; - - target.SetValue(Class1.FooProperty, "bar"); - - target.PropertyChanged += (s, e) => - { - raised = true; - }; - - target.SetValue(Class1.FooProperty, "bar"); - - Assert.False(raised); - } - - [Fact] - public void SetValue_Doesnt_Raise_PropertyChanged_If_Value_Not_Changed_From_Default() - { - Class1 target = new Class1(); - bool raised = false; - - target.PropertyChanged += (s, e) => - { - raised = true; - }; - - target.SetValue(Class1.FooProperty, "foodefault"); - - Assert.False(raised); - } - - [Fact] - public void SetValue_Throws_Exception_For_Unregistered_Property() - { - Class1 target = new Class1(); - - Assert.Throws(() => - { - target.SetValue(Class2.BarProperty, "invalid"); - }); - } - - [Fact] - public void SetValue_Throws_Exception_For_Invalid_Value_Type() - { - Class1 target = new Class1(); - - Assert.Throws(() => - { - target.SetValue(Class1.FooProperty, 123); - }); - } - - [Fact] - public void SetValue_Of_Integer_On_Double_Property_Works() - { - Class2 target = new Class2(); - - target.SetValue((PerspexProperty)Class2.FlobProperty, 4); - - var value = target.GetValue(Class2.FlobProperty); - Assert.IsType(value); - Assert.Equal(4, value); - } - - [Fact] - public void SetValue_Respects_Implicit_Conversions() - { - Class2 target = new Class2(); - - target.SetValue((PerspexProperty)Class2.FlobProperty, new ImplictDouble(4)); - - var value = target.GetValue(Class2.FlobProperty); - Assert.IsType(value); - Assert.Equal(4, value); - } - - [Fact] - public void SetValue_Can_Convert_To_Nullable() - { - Class2 target = new Class2(); - - target.SetValue((PerspexProperty)Class2.FredProperty, 4.0); - - var value = target.GetValue(Class2.FredProperty); - Assert.IsType(value); - Assert.Equal(4, value); - } - - [Fact] - public void SetValue_Causes_Coercion() - { - Class1 target = new Class1(); - - target.SetValue(Class1.QuxProperty, 5); - Assert.Equal(5, target.GetValue(Class1.QuxProperty)); - target.SetValue(Class1.QuxProperty, -5); - Assert.Equal(0, target.GetValue(Class1.QuxProperty)); - target.SetValue(Class1.QuxProperty, 15); - Assert.Equal(10, target.GetValue(Class1.QuxProperty)); - } - - [Fact] - public void SetValue_Respects_Priority() - { - Class1 target = new Class1(); - - target.SetValue(Class1.FooProperty, "one", BindingPriority.TemplatedParent); - Assert.Equal("one", target.GetValue(Class1.FooProperty)); - target.SetValue(Class1.FooProperty, "two", BindingPriority.Style); - Assert.Equal("one", target.GetValue(Class1.FooProperty)); - target.SetValue(Class1.FooProperty, "three", BindingPriority.StyleTrigger); - Assert.Equal("three", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void CoerceValue_Causes_Recoercion() - { - Class1 target = new Class1(); - - target.SetValue(Class1.QuxProperty, 7); - Assert.Equal(7, target.GetValue(Class1.QuxProperty)); - target.MaxQux = 5; - target.CoerceValue(Class1.QuxProperty); - } - - [Fact] - public void GetObservable_Returns_Initial_Value() - { - Class1 target = new Class1(); - int raised = 0; - - target.GetObservable(Class1.FooProperty).Subscribe(x => - { - if (x == "foodefault") - { - ++raised; - } - }); - - Assert.Equal(1, raised); - } - - [Fact] - public void GetObservable_Returns_Property_Change() - { - Class1 target = new Class1(); - bool raised = false; - - target.GetObservable(Class1.FooProperty).Subscribe(x => raised = x == "newvalue"); - raised = false; - target.SetValue(Class1.FooProperty, "newvalue"); - - Assert.True(raised); - } - - [Fact] - public void GetObservable_Returns_Property_Change_Only_For_Correct_Property() - { - Class2 target = new Class2(); - bool raised = false; - - target.GetObservable(Class1.FooProperty).Subscribe(x => raised = true); - raised = false; - target.SetValue(Class2.BarProperty, "newvalue"); - - Assert.False(raised); - } - - [Fact] - public void GetObservable_Dispose_Stops_Property_Changes() - { - Class1 target = new Class1(); - bool raised = false; - - target.GetObservable(Class1.FooProperty) - .Subscribe(x => raised = true) - .Dispose(); - raised = false; - target.SetValue(Class1.FooProperty, "newvalue"); - - Assert.False(raised); - } - - [Fact] - public void Setting_InheritanceParent_Raises_PropertyChanged_When_Value_Changed_In_Parent() - { - bool raised = false; - - Class1 parent = new Class1(); - parent.SetValue(Class1.BazProperty, "changed"); - - Class2 child = new Class2(); - child.PropertyChanged += (s, e) => - raised = s == child && - e.Property == Class1.BazProperty && - (string)e.OldValue == "bazdefault" && - (string)e.NewValue == "changed"; - - child.Parent = parent; - - Assert.True(raised); - } - - [Fact] - public void Setting_InheritanceParent_Doesnt_Raise_PropertyChanged_When_Local_Value_Set() - { - bool raised = false; - - Class1 parent = new Class1(); - parent.SetValue(Class1.BazProperty, "changed"); - - Class2 child = new Class2(); - child.SetValue(Class1.BazProperty, "localvalue"); - child.PropertyChanged += (s, e) => raised = true; - - child.Parent = parent; - - Assert.False(raised); - } - - [Fact] - public void Setting_Value_In_InheritanceParent_Raises_PropertyChanged() - { - bool raised = false; - - Class1 parent = new Class1(); - - Class2 child = new Class2(); - child.PropertyChanged += (s, e) => - raised = s == child && - e.Property == Class1.BazProperty && - (string)e.OldValue == "bazdefault" && - (string)e.NewValue == "changed"; - child.Parent = parent; - - parent.SetValue(Class1.BazProperty, "changed"); - - Assert.True(raised); - } - - [Fact] - public void Bind_Sets_Current_Value() - { - Class1 target = new Class1(); - Class1 source = new Class1(); - - source.SetValue(Class1.FooProperty, "initial"); - target.Bind(Class1.FooProperty, source.GetObservable(Class1.FooProperty)); - - Assert.Equal("initial", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void Bind_NonGeneric_Sets_Current_Value() - { - Class1 target = new Class1(); - Class1 source = new Class1(); - - source.SetValue(Class1.FooProperty, "initial"); - target.Bind((PerspexProperty)Class1.FooProperty, source.GetObservable(Class1.FooProperty)); - - Assert.Equal("initial", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void Bind_Throws_Exception_For_Unregistered_Property() - { - Class1 target = new Class1(); - - Assert.Throws(() => - { - target.Bind(Class2.BarProperty, Observable.Return("foo")); - }); - } - - [Fact] - public void Bind_Sets_Subsequent_Value() - { - Class1 target = new Class1(); - Class1 source = new Class1(); - - source.SetValue(Class1.FooProperty, "initial"); - target.Bind(Class1.FooProperty, source.GetObservable(Class1.FooProperty)); - source.SetValue(Class1.FooProperty, "subsequent"); - - Assert.Equal("subsequent", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void Bind_Throws_Exception_For_Invalid_Value_Type() - { - Class1 target = new Class1(); - - Assert.Throws(() => - { - target.Bind((PerspexProperty)Class1.FooProperty, Observable.Return((object)123)); - }); - } - - [Fact] - public void Two_Way_Binding_Works() - { - Class1 obj1 = new Class1(); - Class1 obj2 = new Class1(); - - obj1.SetValue(Class1.FooProperty, "initial1"); - obj2.SetValue(Class1.FooProperty, "initial2"); - - obj1.Bind(Class1.FooProperty, obj2.GetObservable(Class1.FooProperty)); - obj2.Bind(Class1.FooProperty, obj1.GetObservable(Class1.FooProperty)); - - Assert.Equal("initial2", obj1.GetValue(Class1.FooProperty)); - Assert.Equal("initial2", obj2.GetValue(Class1.FooProperty)); - - obj1.SetValue(Class1.FooProperty, "first"); - - Assert.Equal("first", obj1.GetValue(Class1.FooProperty)); - Assert.Equal("first", obj2.GetValue(Class1.FooProperty)); - - obj2.SetValue(Class1.FooProperty, "second"); - - Assert.Equal("second", obj1.GetValue(Class1.FooProperty)); - Assert.Equal("second", obj2.GetValue(Class1.FooProperty)); - - obj1.SetValue(Class1.FooProperty, "third"); - - Assert.Equal("third", obj1.GetValue(Class1.FooProperty)); - Assert.Equal("third", obj2.GetValue(Class1.FooProperty)); - } - - [Fact] - public void Two_Way_Binding_With_Priority_Works() - { - Class1 obj1 = new Class1(); - Class1 obj2 = new Class1(); - - obj1.SetValue(Class1.FooProperty, "initial1", BindingPriority.Style); - obj2.SetValue(Class1.FooProperty, "initial2", BindingPriority.Style); - - obj1.Bind(Class1.FooProperty, obj2.GetObservable(Class1.FooProperty), BindingPriority.Style); - obj2.Bind(Class1.FooProperty, obj1.GetObservable(Class1.FooProperty), BindingPriority.Style); - - Assert.Equal("initial2", obj1.GetValue(Class1.FooProperty)); - Assert.Equal("initial2", obj2.GetValue(Class1.FooProperty)); - - obj1.SetValue(Class1.FooProperty, "first", BindingPriority.Style); - - Assert.Equal("first", obj1.GetValue(Class1.FooProperty)); - Assert.Equal("first", obj2.GetValue(Class1.FooProperty)); - - obj2.SetValue(Class1.FooProperty, "second", BindingPriority.Style); - - Assert.Equal("second", obj1.GetValue(Class1.FooProperty)); - Assert.Equal("second", obj2.GetValue(Class1.FooProperty)); - - obj1.SetValue(Class1.FooProperty, "third", BindingPriority.Style); - - Assert.Equal("third", obj1.GetValue(Class1.FooProperty)); - Assert.Equal("third", obj2.GetValue(Class1.FooProperty)); - } - - [Fact] - public void BindTwoWay_Gets_Initial_Value_From_Source() - { - Class1 source = new Class1(); - Class1 target = new Class1(); - - source.SetValue(Class1.FooProperty, "initial"); - target.BindTwoWay(Class1.FooProperty, source, Class1.FooProperty); - - Assert.Equal("initial", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void BindTwoWay_Updates_Values() - { - Class1 source = new Class1(); - Class1 target = new Class1(); - - source.SetValue(Class1.FooProperty, "first"); - target.BindTwoWay(Class1.FooProperty, source, Class1.FooProperty); - - Assert.Equal("first", target.GetValue(Class1.FooProperty)); - source.SetValue(Class1.FooProperty, "second"); - Assert.Equal("second", target.GetValue(Class1.FooProperty)); - target.SetValue(Class1.FooProperty, "third"); - Assert.Equal("third", source.GetValue(Class1.FooProperty)); - } - - [Fact] - public void Setting_UnsetValue_Reverts_To_Default_Value() - { - Class1 target = new Class1(); - - target.SetValue(Class1.FooProperty, "newvalue"); - target.SetValue(Class1.FooProperty, PerspexProperty.UnsetValue); - - Assert.Equal("foodefault", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void Local_Binding_Overwrites_Local_Value() - { - var target = new Class1(); - var binding = new Subject(); - - target.Bind(Class1.FooProperty, binding); - - binding.OnNext("first"); - Assert.Equal("first", target.GetValue(Class1.FooProperty)); - - target.SetValue(Class1.FooProperty, "second"); - Assert.Equal("second", target.GetValue(Class1.FooProperty)); - - binding.OnNext("third"); - Assert.Equal("third", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void StyleBinding_Overrides_Default_Value() - { - Class1 target = new Class1(); - - target.Bind(Class1.FooProperty, this.Single("stylevalue"), BindingPriority.Style); - - Assert.Equal("stylevalue", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void this_Operator_Returns_Value_Property() - { - Class1 target = new Class1(); - - target.SetValue(Class1.FooProperty, "newvalue"); - - Assert.Equal("newvalue", target[Class1.FooProperty]); - } - - [Fact] - public void this_Operator_Sets_Value_Property() - { - Class1 target = new Class1(); - - target[Class1.FooProperty] = "newvalue"; - - Assert.Equal("newvalue", target.GetValue(Class1.FooProperty)); - } - - [Fact] - public void this_Operator_Doesnt_Accept_Observable() - { - Class1 target = new Class1(); - - Assert.Throws(() => - { - target[Class1.FooProperty] = Observable.Return("newvalue"); - }); - } - - [Fact] - public void this_Operator_Binds_One_Way() - { - Class1 target1 = new Class1(); - Class1 target2 = new Class1(); - Binding binding = Class1.FooProperty.Bind().WithMode(BindingMode.OneWay); - - target1.SetValue(Class1.FooProperty, "first"); - target2[binding] = target1[!Class1.FooProperty]; - target1.SetValue(Class1.FooProperty, "second"); - - Assert.Equal("second", target2.GetValue(Class1.FooProperty)); - } - - [Fact] - public void this_Operator_Binds_Two_Way() - { - Class1 target1 = new Class1(); - Class1 target2 = new Class1(); - Binding binding = Class1.FooProperty.Bind().WithMode(BindingMode.TwoWay); - - target1.SetValue(Class1.FooProperty, "first"); - target2[binding] = target1[!Class1.FooProperty]; - Assert.Equal("first", target2.GetValue(Class1.FooProperty)); - target1.SetValue(Class1.FooProperty, "second"); - Assert.Equal("second", target2.GetValue(Class1.FooProperty)); - target2.SetValue(Class1.FooProperty, "third"); - Assert.Equal("third", target1.GetValue(Class1.FooProperty)); - } - - [Fact] - public void this_Operator_Binds_One_Time() - { - Class1 target1 = new Class1(); - Class1 target2 = new Class1(); - Binding binding = Class1.FooProperty.Bind().WithMode(BindingMode.OneTime); - - target1.SetValue(Class1.FooProperty, "first"); - target2[binding] = target1[!Class1.FooProperty]; - target1.SetValue(Class1.FooProperty, "second"); - - Assert.Equal("first", target2.GetValue(Class1.FooProperty)); - } - - /// - /// Returns an observable that returns a single value but does not complete. - /// - /// The type of the observable. - /// The value. - /// The observable. - private IObservable Single(T value) - { - return Observable.Never().StartWith(value); - } - - private class Class1 : PerspexObject - { - public static readonly PerspexProperty FooProperty = - PerspexProperty.Register("Foo", "foodefault"); - - public static readonly PerspexProperty BazProperty = - PerspexProperty.Register("Baz", "bazdefault", true); - - public static readonly PerspexProperty QuxProperty = - PerspexProperty.Register("Qux", coerce: Coerce); - - public Class1() - { - this.MaxQux = 10; - } - - public int MaxQux { get; set; } - - private static int Coerce(PerspexObject instance, int value) - { - return Math.Min(Math.Max(value, 0), ((Class1)instance).MaxQux); - } - } - - private class Class2 : Class1 - { - public static readonly PerspexProperty BarProperty = - PerspexProperty.Register("Bar", "bardefault"); - - public static readonly PerspexProperty FlobProperty = - PerspexProperty.Register("Flob"); - - public static readonly PerspexProperty FredProperty = - PerspexProperty.Register("Fred"); - - static Class2() - { - FooProperty.OverrideDefaultValue(typeof(Class2), "foooverride"); - } - - public Class1 Parent - { - get { return (Class1)this.InheritanceParent; } - set { this.InheritanceParent = value; } - } - } - - private class ImplictDouble - { - public ImplictDouble(double value) - { - this.Value = value; - } - - public double Value { get; } - - public static implicit operator double(ImplictDouble v) - { - return v.Value; - } - } - } -} diff --git a/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs new file mode 100644 index 0000000000..eea8f421b3 --- /dev/null +++ b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Binding.cs @@ -0,0 +1,294 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Base.UnitTests +{ + using System; + using System.Reactive.Linq; + using System.Reactive.Subjects; + using Xunit; + + public class PerspexObjectTests_Binding + { + [Fact] + public void Bind_Sets_Current_Value() + { + Class1 target = new Class1(); + Class1 source = new Class1(); + + source.SetValue(Class1.FooProperty, "initial"); + target.Bind(Class1.FooProperty, source.GetObservable(Class1.FooProperty)); + + Assert.Equal("initial", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void Bind_NonGeneric_Sets_Current_Value() + { + Class1 target = new Class1(); + Class1 source = new Class1(); + + source.SetValue(Class1.FooProperty, "initial"); + target.Bind((PerspexProperty)Class1.FooProperty, source.GetObservable(Class1.FooProperty)); + + Assert.Equal("initial", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void Bind_Throws_Exception_For_Unregistered_Property() + { + Class1 target = new Class1(); + + Assert.Throws(() => + { + target.Bind(Class2.BarProperty, Observable.Return("foo")); + }); + } + + [Fact] + public void Bind_Sets_Subsequent_Value() + { + Class1 target = new Class1(); + Class1 source = new Class1(); + + source.SetValue(Class1.FooProperty, "initial"); + target.Bind(Class1.FooProperty, source.GetObservable(Class1.FooProperty)); + source.SetValue(Class1.FooProperty, "subsequent"); + + Assert.Equal("subsequent", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void Bind_Throws_Exception_For_Invalid_Value_Type() + { + Class1 target = new Class1(); + + Assert.Throws(() => + { + target.Bind((PerspexProperty)Class1.FooProperty, Observable.Return((object)123)); + }); + } + + [Fact] + public void Two_Way_Binding_Works() + { + Class1 obj1 = new Class1(); + Class1 obj2 = new Class1(); + + obj1.SetValue(Class1.FooProperty, "initial1"); + obj2.SetValue(Class1.FooProperty, "initial2"); + + obj1.Bind(Class1.FooProperty, obj2.GetObservable(Class1.FooProperty)); + obj2.Bind(Class1.FooProperty, obj1.GetObservable(Class1.FooProperty)); + + Assert.Equal("initial2", obj1.GetValue(Class1.FooProperty)); + Assert.Equal("initial2", obj2.GetValue(Class1.FooProperty)); + + obj1.SetValue(Class1.FooProperty, "first"); + + Assert.Equal("first", obj1.GetValue(Class1.FooProperty)); + Assert.Equal("first", obj2.GetValue(Class1.FooProperty)); + + obj2.SetValue(Class1.FooProperty, "second"); + + Assert.Equal("second", obj1.GetValue(Class1.FooProperty)); + Assert.Equal("second", obj2.GetValue(Class1.FooProperty)); + + obj1.SetValue(Class1.FooProperty, "third"); + + Assert.Equal("third", obj1.GetValue(Class1.FooProperty)); + Assert.Equal("third", obj2.GetValue(Class1.FooProperty)); + } + + [Fact] + public void Two_Way_Binding_With_Priority_Works() + { + Class1 obj1 = new Class1(); + Class1 obj2 = new Class1(); + + obj1.SetValue(Class1.FooProperty, "initial1", BindingPriority.Style); + obj2.SetValue(Class1.FooProperty, "initial2", BindingPriority.Style); + + obj1.Bind(Class1.FooProperty, obj2.GetObservable(Class1.FooProperty), BindingPriority.Style); + obj2.Bind(Class1.FooProperty, obj1.GetObservable(Class1.FooProperty), BindingPriority.Style); + + Assert.Equal("initial2", obj1.GetValue(Class1.FooProperty)); + Assert.Equal("initial2", obj2.GetValue(Class1.FooProperty)); + + obj1.SetValue(Class1.FooProperty, "first", BindingPriority.Style); + + Assert.Equal("first", obj1.GetValue(Class1.FooProperty)); + Assert.Equal("first", obj2.GetValue(Class1.FooProperty)); + + obj2.SetValue(Class1.FooProperty, "second", BindingPriority.Style); + + Assert.Equal("second", obj1.GetValue(Class1.FooProperty)); + Assert.Equal("second", obj2.GetValue(Class1.FooProperty)); + + obj1.SetValue(Class1.FooProperty, "third", BindingPriority.Style); + + Assert.Equal("third", obj1.GetValue(Class1.FooProperty)); + Assert.Equal("third", obj2.GetValue(Class1.FooProperty)); + } + + [Fact] + public void BindTwoWay_Gets_Initial_Value_From_Source() + { + Class1 source = new Class1(); + Class1 target = new Class1(); + + source.SetValue(Class1.FooProperty, "initial"); + target.BindTwoWay(Class1.FooProperty, source, Class1.FooProperty); + + Assert.Equal("initial", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void BindTwoWay_Updates_Values() + { + Class1 source = new Class1(); + Class1 target = new Class1(); + + source.SetValue(Class1.FooProperty, "first"); + target.BindTwoWay(Class1.FooProperty, source, Class1.FooProperty); + + Assert.Equal("first", target.GetValue(Class1.FooProperty)); + source.SetValue(Class1.FooProperty, "second"); + Assert.Equal("second", target.GetValue(Class1.FooProperty)); + target.SetValue(Class1.FooProperty, "third"); + Assert.Equal("third", source.GetValue(Class1.FooProperty)); + } + + [Fact] + public void Local_Binding_Overwrites_Local_Value() + { + var target = new Class1(); + var binding = new Subject(); + + target.Bind(Class1.FooProperty, binding); + + binding.OnNext("first"); + Assert.Equal("first", target.GetValue(Class1.FooProperty)); + + target.SetValue(Class1.FooProperty, "second"); + Assert.Equal("second", target.GetValue(Class1.FooProperty)); + + binding.OnNext("third"); + Assert.Equal("third", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void StyleBinding_Overrides_Default_Value() + { + Class1 target = new Class1(); + + target.Bind(Class1.FooProperty, this.Single("stylevalue"), BindingPriority.Style); + + Assert.Equal("stylevalue", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void this_Operator_Returns_Value_Property() + { + Class1 target = new Class1(); + + target.SetValue(Class1.FooProperty, "newvalue"); + + Assert.Equal("newvalue", target[Class1.FooProperty]); + } + + [Fact] + public void this_Operator_Sets_Value_Property() + { + Class1 target = new Class1(); + + target[Class1.FooProperty] = "newvalue"; + + Assert.Equal("newvalue", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void this_Operator_Doesnt_Accept_Observable() + { + Class1 target = new Class1(); + + Assert.Throws(() => + { + target[Class1.FooProperty] = Observable.Return("newvalue"); + }); + } + + [Fact] + public void this_Operator_Binds_One_Way() + { + Class1 target1 = new Class1(); + Class1 target2 = new Class1(); + Binding binding = Class1.FooProperty.Bind().WithMode(BindingMode.OneWay); + + target1.SetValue(Class1.FooProperty, "first"); + target2[binding] = target1[!Class1.FooProperty]; + target1.SetValue(Class1.FooProperty, "second"); + + Assert.Equal("second", target2.GetValue(Class1.FooProperty)); + } + + [Fact] + public void this_Operator_Binds_Two_Way() + { + Class1 target1 = new Class1(); + Class1 target2 = new Class1(); + Binding binding = Class1.FooProperty.Bind().WithMode(BindingMode.TwoWay); + + target1.SetValue(Class1.FooProperty, "first"); + target2[binding] = target1[!Class1.FooProperty]; + Assert.Equal("first", target2.GetValue(Class1.FooProperty)); + target1.SetValue(Class1.FooProperty, "second"); + Assert.Equal("second", target2.GetValue(Class1.FooProperty)); + target2.SetValue(Class1.FooProperty, "third"); + Assert.Equal("third", target1.GetValue(Class1.FooProperty)); + } + + [Fact] + public void this_Operator_Binds_One_Time() + { + Class1 target1 = new Class1(); + Class1 target2 = new Class1(); + Binding binding = Class1.FooProperty.Bind().WithMode(BindingMode.OneTime); + + target1.SetValue(Class1.FooProperty, "first"); + target2[binding] = target1[!Class1.FooProperty]; + target1.SetValue(Class1.FooProperty, "second"); + + Assert.Equal("first", target2.GetValue(Class1.FooProperty)); + } + + /// + /// Returns an observable that returns a single value but does not complete. + /// + /// The type of the observable. + /// The value. + /// The observable. + private IObservable Single(T value) + { + return Observable.Never().StartWith(value); + } + + private class Class1 : PerspexObject + { + public static readonly PerspexProperty FooProperty = + PerspexProperty.Register("Foo", "foodefault"); + + public static readonly PerspexProperty BazProperty = + PerspexProperty.Register("Baz", "bazdefault", true); + } + + private class Class2 : Class1 + { + public static readonly PerspexProperty BarProperty = + PerspexProperty.Register("Bar", "bardefault"); + } + } +} diff --git a/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Coercion.cs b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Coercion.cs new file mode 100644 index 0000000000..bc1784720d --- /dev/null +++ b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Coercion.cs @@ -0,0 +1,59 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Base.UnitTests +{ + using System; + using System.Linq; + using System.Reactive.Linq; + using System.Reactive.Subjects; + using Xunit; + + public class PerspexObjectTests_Coercion + { + [Fact] + public void CoerceValue_Causes_Recoercion() + { + Class1 target = new Class1(); + + target.SetValue(Class1.QuxProperty, 7); + Assert.Equal(7, target.GetValue(Class1.QuxProperty)); + target.MaxQux = 5; + target.CoerceValue(Class1.QuxProperty); + } + + [Fact] + public void SetValue_Causes_Coercion() + { + Class1 target = new Class1(); + + target.SetValue(Class1.QuxProperty, 5); + Assert.Equal(5, target.GetValue(Class1.QuxProperty)); + target.SetValue(Class1.QuxProperty, -5); + Assert.Equal(0, target.GetValue(Class1.QuxProperty)); + target.SetValue(Class1.QuxProperty, 15); + Assert.Equal(10, target.GetValue(Class1.QuxProperty)); + } + + private class Class1 : PerspexObject + { + public static readonly PerspexProperty QuxProperty = + PerspexProperty.Register("Qux", coerce: Coerce); + + public Class1() + { + this.MaxQux = 10; + } + + public int MaxQux { get; set; } + + private static int Coerce(PerspexObject instance, int value) + { + return Math.Min(Math.Max(value, 0), ((Class1)instance).MaxQux); + } + } + } +} diff --git a/Tests/Perspex.Base.UnitTests/PerspexObjectTests_GetObservable.cs b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_GetObservable.cs new file mode 100644 index 0000000000..2fce9f4d36 --- /dev/null +++ b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_GetObservable.cs @@ -0,0 +1,85 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Base.UnitTests +{ + using System; + using System.Reactive.Linq; + using Xunit; + + public class PerspexObjectTests_GetObservable + { + [Fact] + public void GetObservable_Returns_Initial_Value() + { + Class1 target = new Class1(); + int raised = 0; + + target.GetObservable(Class1.FooProperty).Subscribe(x => + { + if (x == "foodefault") + { + ++raised; + } + }); + + Assert.Equal(1, raised); + } + + [Fact] + public void GetObservable_Returns_Property_Change() + { + Class1 target = new Class1(); + bool raised = false; + + target.GetObservable(Class1.FooProperty).Subscribe(x => raised = x == "newvalue"); + raised = false; + target.SetValue(Class1.FooProperty, "newvalue"); + + Assert.True(raised); + } + + [Fact] + public void GetObservable_Returns_Property_Change_Only_For_Correct_Property() + { + Class2 target = new Class2(); + bool raised = false; + + target.GetObservable(Class1.FooProperty).Subscribe(x => raised = true); + raised = false; + target.SetValue(Class2.BarProperty, "newvalue"); + + Assert.False(raised); + } + + [Fact] + public void GetObservable_Dispose_Stops_Property_Changes() + { + Class1 target = new Class1(); + bool raised = false; + + target.GetObservable(Class1.FooProperty) + .Subscribe(x => raised = true) + .Dispose(); + raised = false; + target.SetValue(Class1.FooProperty, "newvalue"); + + Assert.False(raised); + } + + private class Class1 : PerspexObject + { + public static readonly PerspexProperty FooProperty = + PerspexProperty.Register("Foo", "foodefault"); + } + + private class Class2 : Class1 + { + public static readonly PerspexProperty BarProperty = + PerspexProperty.Register("Bar", "bardefault"); + } + } +} diff --git a/Tests/Perspex.Base.UnitTests/PerspexObjectTests_GetValue.cs b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_GetValue.cs new file mode 100644 index 0000000000..288234e673 --- /dev/null +++ b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_GetValue.cs @@ -0,0 +1,73 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Base.UnitTests +{ + using Xunit; + + public class PerspexObjectTests_GetValue + { + [Fact] + public void GetValue_Returns_Default_Value() + { + Class1 target = new Class1(); + + Assert.Equal("foodefault", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void GetValue_Returns_Overridden_Default_Value() + { + Class2 target = new Class2(); + + Assert.Equal("foooverride", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void GetValue_Returns_Set_Value() + { + Class1 target = new Class1(); + + target.SetValue(Class1.FooProperty, "newvalue"); + + Assert.Equal("newvalue", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void GetValue_Returns_Inherited_Value() + { + Class1 parent = new Class1(); + Class2 child = new Class2 { Parent = parent }; + + parent.SetValue(Class1.BazProperty, "changed"); + + Assert.Equal("changed", child.GetValue(Class1.BazProperty)); + } + + private class Class1 : PerspexObject + { + public static readonly PerspexProperty FooProperty = + PerspexProperty.Register("Foo", "foodefault"); + + public static readonly PerspexProperty BazProperty = + PerspexProperty.Register("Baz", "bazdefault", true); + } + + private class Class2 : Class1 + { + static Class2() + { + FooProperty.OverrideDefaultValue(typeof(Class2), "foooverride"); + } + + public Class1 Parent + { + get { return (Class1)this.InheritanceParent; } + set { this.InheritanceParent = value; } + } + } + } +} diff --git a/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Inheritance.cs b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Inheritance.cs new file mode 100644 index 0000000000..e8dfe41d30 --- /dev/null +++ b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Inheritance.cs @@ -0,0 +1,104 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Base.UnitTests +{ + using Xunit; + + public class PerspexObjectTests_Inheritance + { + [Fact] + public void GetValue_Returns_Inherited_Value() + { + Class1 parent = new Class1(); + Class2 child = new Class2 { Parent = parent }; + + parent.SetValue(Class1.BazProperty, "changed"); + + Assert.Equal("changed", child.GetValue(Class1.BazProperty)); + } + + [Fact] + public void Setting_InheritanceParent_Raises_PropertyChanged_When_Value_Changed_In_Parent() + { + bool raised = false; + + Class1 parent = new Class1(); + parent.SetValue(Class1.BazProperty, "changed"); + + Class2 child = new Class2(); + child.PropertyChanged += (s, e) => + raised = s == child && + e.Property == Class1.BazProperty && + (string)e.OldValue == "bazdefault" && + (string)e.NewValue == "changed"; + + child.Parent = parent; + + Assert.True(raised); + } + + [Fact] + public void Setting_InheritanceParent_Doesnt_Raise_PropertyChanged_When_Local_Value_Set() + { + bool raised = false; + + Class1 parent = new Class1(); + parent.SetValue(Class1.BazProperty, "changed"); + + Class2 child = new Class2(); + child.SetValue(Class1.BazProperty, "localvalue"); + child.PropertyChanged += (s, e) => raised = true; + + child.Parent = parent; + + Assert.False(raised); + } + + [Fact] + public void Setting_Value_In_InheritanceParent_Raises_PropertyChanged() + { + bool raised = false; + + Class1 parent = new Class1(); + + Class2 child = new Class2(); + child.PropertyChanged += (s, e) => + raised = s == child && + e.Property == Class1.BazProperty && + (string)e.OldValue == "bazdefault" && + (string)e.NewValue == "changed"; + child.Parent = parent; + + parent.SetValue(Class1.BazProperty, "changed"); + + Assert.True(raised); + } + + private class Class1 : PerspexObject + { + public static readonly PerspexProperty FooProperty = + PerspexProperty.Register("Foo", "foodefault"); + + public static readonly PerspexProperty BazProperty = + PerspexProperty.Register("Baz", "bazdefault", true); + } + + private class Class2 : Class1 + { + static Class2() + { + FooProperty.OverrideDefaultValue(typeof(Class2), "foooverride"); + } + + public Class1 Parent + { + get { return (Class1)this.InheritanceParent; } + set { this.InheritanceParent = value; } + } + } + } +} diff --git a/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs new file mode 100644 index 0000000000..dcbc1ceedf --- /dev/null +++ b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_Metadata.cs @@ -0,0 +1,65 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Base.UnitTests +{ + using System; + using System.Linq; + using System.Reactive.Linq; + using System.Reactive.Subjects; + using Xunit; + + public class PerspexObjectTests_Metadata + { + public PerspexObjectTests_Metadata() + { + // Ensure properties are registered. + PerspexProperty p; + p = Class1.FooProperty; + p = Class2.BarProperty; + } + + [Fact] + public void GetProperties_Returns_Registered_Properties() + { + string[] names = PerspexObject.GetProperties(typeof(Class1)).Select(x => x.Name).ToArray(); + + Assert.Equal(new[] { "Foo", "Baz", "Qux" }, names); + } + + [Fact] + public void GetProperties_Returns_Registered_Properties_For_Base_Types() + { + string[] names = PerspexObject.GetProperties(typeof(Class2)).Select(x => x.Name).ToArray(); + + Assert.Equal(new[] { "Bar", "Flob", "Fred", "Foo", "Baz", "Qux" }, names); + } + + private class Class1 : PerspexObject + { + public static readonly PerspexProperty FooProperty = + PerspexProperty.Register("Foo"); + + public static readonly PerspexProperty BazProperty = + PerspexProperty.Register("Baz"); + + public static readonly PerspexProperty QuxProperty = + PerspexProperty.Register("Qux"); + } + + private class Class2 : Class1 + { + public static readonly PerspexProperty BarProperty = + PerspexProperty.Register("Bar"); + + public static readonly PerspexProperty FlobProperty = + PerspexProperty.Register("Flob"); + + public static readonly PerspexProperty FredProperty = + PerspexProperty.Register("Fred"); + } + } +} diff --git a/Tests/Perspex.Base.UnitTests/PerspexObjectTests_SetValue.cs b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_SetValue.cs new file mode 100644 index 0000000000..12f4cbb899 --- /dev/null +++ b/Tests/Perspex.Base.UnitTests/PerspexObjectTests_SetValue.cs @@ -0,0 +1,212 @@ +// ----------------------------------------------------------------------- +// +// Copyright 2015 MIT Licence. See licence.md for more information. +// +// ----------------------------------------------------------------------- + +namespace Perspex.Base.UnitTests +{ + using System; + using System.Linq; + using System.Reactive.Linq; + using System.Reactive.Subjects; + using Xunit; + + public class PerspexObjectTests_SetValue + { + [Fact] + public void ClearValue_Clears_Value() + { + Class1 target = new Class1(); + + target.SetValue(Class1.FooProperty, "newvalue"); + target.ClearValue(Class1.FooProperty); + + Assert.Equal("foodefault", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void SetValue_Sets_Value() + { + Class1 target = new Class1(); + + target.SetValue(Class1.FooProperty, "newvalue"); + + Assert.Equal("newvalue", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void SetValue_Raises_PropertyChanged() + { + Class1 target = new Class1(); + bool raised = false; + + target.PropertyChanged += (s, e) => + { + raised = s == target && + e.Property == Class1.FooProperty && + (string)e.OldValue == "foodefault" && + (string)e.NewValue == "newvalue"; + }; + + target.SetValue(Class1.FooProperty, "newvalue"); + + Assert.True(raised); + } + + [Fact] + public void SetValue_Doesnt_Raise_PropertyChanged_If_Value_Not_Changed() + { + Class1 target = new Class1(); + bool raised = false; + + target.SetValue(Class1.FooProperty, "bar"); + + target.PropertyChanged += (s, e) => + { + raised = true; + }; + + target.SetValue(Class1.FooProperty, "bar"); + + Assert.False(raised); + } + + [Fact] + public void SetValue_Doesnt_Raise_PropertyChanged_If_Value_Not_Changed_From_Default() + { + Class1 target = new Class1(); + bool raised = false; + + target.PropertyChanged += (s, e) => + { + raised = true; + }; + + target.SetValue(Class1.FooProperty, "foodefault"); + + Assert.False(raised); + } + + [Fact] + public void SetValue_Throws_Exception_For_Unregistered_Property() + { + Class1 target = new Class1(); + + Assert.Throws(() => + { + target.SetValue(Class2.BarProperty, "invalid"); + }); + } + + [Fact] + public void SetValue_Throws_Exception_For_Invalid_Value_Type() + { + Class1 target = new Class1(); + + Assert.Throws(() => + { + target.SetValue(Class1.FooProperty, 123); + }); + } + + [Fact] + public void SetValue_Of_Integer_On_Double_Property_Works() + { + Class2 target = new Class2(); + + target.SetValue((PerspexProperty)Class2.FlobProperty, 4); + + var value = target.GetValue(Class2.FlobProperty); + Assert.IsType(value); + Assert.Equal(4, value); + } + + [Fact] + public void SetValue_Respects_Implicit_Conversions() + { + Class2 target = new Class2(); + + target.SetValue((PerspexProperty)Class2.FlobProperty, new ImplictDouble(4)); + + var value = target.GetValue(Class2.FlobProperty); + Assert.IsType(value); + Assert.Equal(4, value); + } + + [Fact] + public void SetValue_Can_Convert_To_Nullable() + { + Class2 target = new Class2(); + + target.SetValue((PerspexProperty)Class2.FredProperty, 4.0); + + var value = target.GetValue(Class2.FredProperty); + Assert.IsType(value); + Assert.Equal(4, value); + } + + [Fact] + public void SetValue_Respects_Priority() + { + Class1 target = new Class1(); + + target.SetValue(Class1.FooProperty, "one", BindingPriority.TemplatedParent); + Assert.Equal("one", target.GetValue(Class1.FooProperty)); + target.SetValue(Class1.FooProperty, "two", BindingPriority.Style); + Assert.Equal("one", target.GetValue(Class1.FooProperty)); + target.SetValue(Class1.FooProperty, "three", BindingPriority.StyleTrigger); + Assert.Equal("three", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void Setting_UnsetValue_Reverts_To_Default_Value() + { + Class1 target = new Class1(); + + target.SetValue(Class1.FooProperty, "newvalue"); + target.SetValue(Class1.FooProperty, PerspexProperty.UnsetValue); + + Assert.Equal("foodefault", target.GetValue(Class1.FooProperty)); + } + + private class Class1 : PerspexObject + { + public static readonly PerspexProperty FooProperty = + PerspexProperty.Register("Foo", "foodefault"); + } + + private class Class2 : Class1 + { + public static readonly PerspexProperty BarProperty = + PerspexProperty.Register("Bar", "bardefault"); + + public static readonly PerspexProperty FlobProperty = + PerspexProperty.Register("Flob"); + + public static readonly PerspexProperty FredProperty = + PerspexProperty.Register("Fred"); + + public Class1 Parent + { + get { return (Class1)this.InheritanceParent; } + set { this.InheritanceParent = value; } + } + } + + private class ImplictDouble + { + public ImplictDouble(double value) + { + this.Value = value; + } + + public double Value { get; } + + public static implicit operator double (ImplictDouble v) + { + return v.Value; + } + } + } +}