maui-linux/Xamarin.Forms.Core.UnitTests/BindableObjectUnitTests.cs

1596 строки
44 KiB
C#

using System;
using System.Globalization;
using NUnit.Framework;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Core.UnitTests
{
[TypeConverter(typeof(ToBarConverter))]
internal class Bar
{
}
internal class Baz
{
}
internal class MockBindable
: VisualElement
{
public static readonly BindableProperty TextProperty = BindableProperty.Create<MockBindable, string>(
b => b.Text, "default", BindingMode.TwoWay);
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public string Foo { get; set; }
public int TargetInt { get; set; }
public static readonly BindableProperty BarProperty =
BindableProperty.Create<MockBindable, Bar>(w => w.Bar, default(Bar));
public Bar Bar
{
get { return (Bar)GetValue(BarProperty); }
set { SetValue(BarProperty, value); }
}
public static readonly BindableProperty BazProperty =
BindableProperty.Create<MockBindable, Baz>(w => w.Baz, default(Baz));
[TypeConverter(typeof(ToBazConverter))]
public Baz Baz
{
get { return (Baz)GetValue(BazProperty); }
set { SetValue(BazProperty, value); }
}
public static readonly BindableProperty QuxProperty =
BindableProperty.Create<MockBindable, Baz>(w => w.Qux, default(Baz));
public Baz Qux
{
get { return (Baz)GetValue(QuxProperty); }
set { SetValue(QuxProperty, value); }
}
}
internal class ToBarConverter : TypeConverter
{
public override object ConvertFrom(System.Globalization.CultureInfo culture, object value)
{
return new Bar();
}
}
internal class ToBazConverter : TypeConverter
{
public override object ConvertFrom(System.Globalization.CultureInfo culture, object value)
{
return new Baz();
}
}
[TestFixture]
public class BindableObjectUnitTests : BaseTestFixture
{
[SetUp]
public void Setup()
{
Device.PlatformServices = new MockPlatformServices();
}
[TearDown]
public void TearDown()
{
Device.PlatformServices = null;
}
[Test]
public void BindingContext()
{
var mock = new MockBindable();
Assert.IsNull(mock.BindingContext);
object obj = new object();
mock.BindingContext = obj;
Assert.AreSame(obj, mock.BindingContext);
}
[Test]
public void BindingContextChangedEvent()
{
var mock = new MockBindable();
mock.BindingContextChanged += (sender, args) => Assert.Pass();
mock.BindingContext = new object();
Assert.Fail("The BindingContextChanged event was not fired.");
}
[Test]
public void BindingContextChangedOnce()
{
var count = 0;
var mock = new MockBindable();
mock.BindingContextChanged += (sender, args) => ++count;
mock.BindingContext = new object();
Assert.AreEqual(count, 1);
}
class MockVMEquals
{
public string Key { get; set; }
public string Text { get; set; }
public override bool Equals(object obj)
{
var other = obj as MockVMEquals;
if (other == null)
return false;
return Key == other.Key;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
[Test]
//https://bugzilla.xamarin.com/show_bug.cgi?id=59507
public void BindingContextChangedCompareReferences()
{
var mock = new MockBindable();
mock.BindingContext = new MockVMEquals { Key = "Foo", Text = "Foo" };
mock.BindingContextChanged += (sender, args) => Assert.Pass();
mock.BindingContext = new MockVMEquals { Key = "Foo", Text = "Bar" };
Assert.Fail("The BindingContextChanged event was not fired.");
}
[Test]
public void ParentAndChildBindingContextChanged()
{
int parentCount = 0, childCount = 0;
var didNotChange = " BindingContext did not change.";
var changedMoreThanOnce = " BindingContext changed more than once.";
var changedWhenNoChange = " BindingContext was changed when there was no change in context.";
var parent = new MockBindable();
parent.BindingContextChanged += (sender, args) => { ++parentCount; };
var child = new MockBindable();
child.BindingContextChanged += (sender, args) => { ++childCount; };
child.Parent = parent;
Assert.AreEqual(parentCount, 0, "Parent BindingContext was changed while parenting a child.");
Assert.AreEqual(childCount, 0, "Child" + changedWhenNoChange);
child.BindingContext = new object(); // set manually
Assert.GreaterOrEqual(childCount, 1, "Child" + didNotChange);
Assert.AreEqual(childCount, 1, "Child" + changedMoreThanOnce);
Assert.AreEqual(parentCount, 0, "Parent" + changedWhenNoChange);
parent.BindingContext = new object();
Assert.GreaterOrEqual(parentCount, 1, "Parent" + didNotChange);
Assert.AreEqual(parentCount, 1, "Parent" + changedMoreThanOnce);
Assert.AreEqual(childCount, 1, "Child" + changedWhenNoChange);
child.BindingContext = new object();
Assert.GreaterOrEqual(childCount, 2, "Child" + didNotChange);
Assert.AreEqual(childCount, 2, "Child" + changedMoreThanOnce);
Assert.AreEqual(parentCount, 1, "Parent" + changedWhenNoChange);
}
[Test]
public void ParentSetOnNullChildBindingContext()
{
var parent = new MockBindable();
var child = new MockBindable();
child.BindingContextChanged += (sender, args) => { Assert.Fail("Child BindingContext was changed when there was no change in context."); };
child.Parent = parent; // this should not trigger binding context change on child since there is no change
parent.BindingContext = new object();
parent.BindingContext = new object();
}
[Test]
public void ParentSetOnNonNullChildBindingContext()
{
var count = 0;
var parent = new MockBindable();
parent.BindingContextChanged += (sender, args) => { ++count; };
var child = new MockBindable();
child.BindingContextChanged += (sender, args) => { ++count; };
child.BindingContext = new object(); // set manually
Assert.AreEqual(count, 1);
child.Parent = parent; // this should not trigger binding context change because child binding was set manually
Assert.AreEqual(count, 1);
parent.BindingContext = new object();
Assert.AreEqual(count, 2);
child.BindingContext = new object();
Assert.AreEqual(count, 3);
}
[Test]
[Description("When the BindingContext changes, any bindings should be immediately applied.")]
public void BindingContextChangedBindingsApplied()
{
var mock = new MockBindable();
mock.SetBinding(MockBindable.TextProperty, new Binding("."));
mock.BindingContext = "Test";
Assert.AreEqual("Test", mock.GetValue(MockBindable.TextProperty));
mock.BindingContext = "Testing";
Assert.AreEqual("Testing", mock.GetValue(MockBindable.TextProperty),
"Bindings were not reapplied to the new binding context");
}
[Test]
[Description("When the BindingContext changes, the new context needs to listen for updates.")]
public void BindingContextChangedBindingsListening()
{
var mock = new MockBindable();
mock.SetBinding(MockBindable.TextProperty, new Binding("Text"));
var vm = new MockViewModel();
mock.BindingContext = vm;
mock.BindingContext = (vm = new MockViewModel());
vm.Text = "test";
Assert.AreEqual("test", mock.GetValue(MockBindable.TextProperty),
"The new ViewModel was not being listened to after being set");
}
[Test]
[Description("When an INPC implementer is unset as the BindingContext, its changes shouldn't be listened to any further.")]
public void BindingContextUnsetStopsListening()
{
var mock = new MockBindable();
mock.SetBinding(MockBindable.TextProperty, new Binding("Text"));
var vm = new MockViewModel();
mock.BindingContext = vm;
mock.BindingContext = null;
vm.Text = "test";
Assert.IsNull(mock.GetValue(Entry.TextProperty), "ViewModel was still being listened to after set to null");
}
[Test]
public void PropertyChanging()
{
var mock = new MockBindable();
mock.PropertyChanging += (sender, args) =>
{
Assert.AreEqual(MockBindable.TextProperty.PropertyName, args.PropertyName);
Assert.AreEqual(MockBindable.TextProperty.DefaultValue, mock.GetValue(MockBindable.TextProperty));
Assert.Pass();
};
mock.SetValue(MockBindable.TextProperty, "foo");
Assert.Fail("The PropertyChanging event was not fired.");
}
[Test]
public void PropertyChangingSameValue()
{
const string value = "foo";
var mock = new MockBindable();
mock.SetValue(MockBindable.TextProperty, value);
mock.PropertyChanging += (s, e) => Assert.Fail();
mock.SetValue(MockBindable.TextProperty, value);
Assert.Pass();
}
[Test]
public void PropertyChangingDefaultValue()
{
var prop = BindableProperty.Create<MockBindable, string>(w => w.Foo, "DefaultValue");
var mock = new MockBindable();
mock.PropertyChanging += (s, e) => Assert.Fail();
mock.SetValue(prop, prop.DefaultValue);
Assert.Pass();
}
[Test]
public void PropertyChanged()
{
const string value = "foo";
var mock = new MockBindable();
mock.PropertyChanged += (sender, args) =>
{
Assert.AreEqual(MockBindable.TextProperty.PropertyName, args.PropertyName);
Assert.AreEqual(value, mock.GetValue(MockBindable.TextProperty));
Assert.Pass();
};
mock.SetValue(MockBindable.TextProperty, value);
Assert.Fail("The PropertyChanged event was not fired.");
}
[Test]
public void PropertyChangedSameValue()
{
const string value = "foo";
var mock = new MockBindable();
mock.SetValue(MockBindable.TextProperty, value);
mock.PropertyChanged += (s, e) => Assert.Fail();
mock.SetValue(MockBindable.TextProperty, value);
Assert.Pass();
}
[Test]
public void PropertyChangedDefaultValue()
{
var prop = BindableProperty.Create<MockBindable, string>(w => w.Foo, "DefaultValue");
var mock = new MockBindable();
mock.PropertyChanged += (s, e) => Assert.Fail();
mock.SetValue(prop, prop.DefaultValue);
Assert.Pass();
}
[Test]
public void GetSetValue()
{
const string value = "foo";
var mock = new MockBindable();
mock.SetValue(MockBindable.TextProperty, value);
Assert.AreEqual(value, mock.GetValue(MockBindable.TextProperty));
}
[Test]
public void GetValueDefault()
{
var nulldefault = BindableProperty.Create<MockBindable, string>(w => w.Foo, null);
TestGetValueDefault(nulldefault);
var foodefault = BindableProperty.Create<MockBindable, string>(w => w.Foo, "Foo");
TestGetValueDefault(foodefault);
}
void TestGetValueDefault(BindableProperty property)
{
var mock = new MockBindable();
object value = mock.GetValue(property);
Assert.AreEqual(property.DefaultValue, value);
}
[Test]
public void SetValueInvalid()
{
var mock = new MockBindable();
Assert.Throws<ArgumentNullException>(() => mock.SetValue((BindableProperty)null, "null"));
}
[Test]
public void GetValueInvalid()
{
var mock = new MockBindable();
Assert.Throws<ArgumentNullException>(() => mock.GetValue(null));
}
[Test]
public void ClearValueInvalid()
{
var mock = new MockBindable();
Assert.Throws<ArgumentNullException>(() => mock.ClearValue((BindableProperty)null));
}
[Test]
public void ClearValue()
{
const string value = "foo";
var mock = new MockBindable();
mock.SetValue(MockBindable.TextProperty, value);
Assert.AreEqual(value, mock.GetValue(MockBindable.TextProperty));
mock.ClearValue(MockBindable.TextProperty);
TestGetValueDefault(MockBindable.TextProperty);
}
[Test]
public void ClearValueTriggersINPC()
{
var bindable = new MockBindable();
bool changingfired = false;
bool changedfired = false;
bool changingdelegatefired = false;
bool changeddelegatefired = false;
var property = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable), "foo",
propertyChanged: (b, o, n) => changeddelegatefired = true,
propertyChanging: (b, o, n) => changingdelegatefired = true
);
bindable.PropertyChanged += (sender, e) => { changedfired |= e.PropertyName == "Foo"; };
bindable.PropertyChanging += (sender, e) => { changingfired |= e.PropertyName == "Foo"; };
bindable.SetValue(property, "foobar");
changingfired = changedfired = changeddelegatefired = changingdelegatefired = false;
bindable.ClearValue(property);
Assert.True(changingfired);
Assert.True(changedfired);
Assert.True(changingdelegatefired);
Assert.True(changeddelegatefired);
}
[Test]
public void ClearValueDoesNotTriggersINPCOnSameValues()
{
var bindable = new MockBindable();
bool changingfired = false;
bool changedfired = false;
bool changingdelegatefired = false;
bool changeddelegatefired = false;
var property = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable), "foo",
propertyChanged: (b, o, n) => changeddelegatefired = true,
propertyChanging: (b, o, n) => changingdelegatefired = true
);
bindable.PropertyChanged += (sender, e) => { changedfired |= e.PropertyName == "Foo"; };
bindable.PropertyChanging += (sender, e) => { changingfired |= e.PropertyName == "Foo"; };
bindable.SetValue(property, "foobar");
bindable.SetValue(property, "foo");
changingfired = changedfired = changeddelegatefired = changingdelegatefired = false;
bindable.ClearValue(property);
Assert.False(changingfired);
Assert.False(changedfired);
Assert.False(changingdelegatefired);
Assert.False(changeddelegatefired);
}
[Test]
public void SetBindingInvalid()
{
var mock = new MockBindable();
Assert.Throws<ArgumentNullException>(() => mock.SetBinding(null, new Binding(".")));
Assert.Throws<ArgumentNullException>(() => mock.SetBinding(MockBindable.TextProperty, null));
}
[Test]
public void RemoveUnaddedBinding()
{
var mock = new MockBindable();
Assert.That(() => mock.RemoveBinding(MockBindable.TextProperty), Throws.Nothing);
}
[Test]
public void RemoveBindingInvalid()
{
var mock = new MockBindable();
Assert.Throws<ArgumentNullException>(() => mock.RemoveBinding(null));
}
[Test]
public void RemovedBindingDoesNotUpdate()
{
const string newvalue = "New Value";
var viewmodel = new MockViewModel
{
Text = "Foo"
};
var binding = new Binding("Text");
var bindable = new MockBindable();
bindable.BindingContext = viewmodel;
bindable.SetBinding(MockBindable.TextProperty, binding);
string original = (string)bindable.GetValue(MockBindable.TextProperty);
bindable.RemoveBinding(MockBindable.TextProperty);
viewmodel.Text = newvalue;
Assert.AreEqual(original, bindable.GetValue(MockBindable.TextProperty),
"Property updated from a removed binding");
}
[Test]
public void CoerceValue()
{
var property = BindableProperty.Create<MockBindable, string>(w => w.Foo, null,
coerceValue: (bo, o) => o.ToUpper());
const string value = "value";
var mock = new MockBindable();
mock.SetValue(property, value);
Assert.AreEqual(value.ToUpper(), mock.GetValue(property));
}
[Test]
public void ValidateValue()
{
var property = BindableProperty.Create<MockBindable, string>(w => w.Foo, null,
validateValue: (b, v) => false);
var mock = new MockBindable();
Assert.Throws<ArgumentException>(() => mock.SetValue(property, null));
}
[Test]
public void BindablePropertyChanged()
{
bool changed = false;
string oldv = "bar";
string newv = "foo";
var property = BindableProperty.Create<MockBindable, string>(w => w.Foo, oldv,
propertyChanged: (b, ov, nv) =>
{
Assert.AreSame(oldv, ov);
Assert.AreSame(newv, nv);
changed = true;
});
var mock = new MockBindable();
mock.SetValue(property, newv);
Assert.IsTrue(changed, "PropertyChanged was not called");
}
[Test]
public void RecursiveChange()
{
bool changedA1 = false, changedA2 = false, changedB1 = false, changedB2 = false;
var mock = new MockBindable();
mock.PropertyChanged += (sender, args) =>
{
if (!changedA1)
{
Assert.AreEqual("1", mock.GetValue(MockBindable.TextProperty));
Assert.IsFalse(changedA2);
Assert.IsFalse(changedB1);
Assert.IsFalse(changedB2);
mock.SetValue(MockBindable.TextProperty, "2");
changedA1 = true;
}
else
{
Assert.AreEqual("2", mock.GetValue(MockBindable.TextProperty));
Assert.IsFalse(changedA2);
Assert.IsTrue(changedB1);
Assert.IsFalse(changedB2);
changedA2 = true;
}
};
mock.PropertyChanged += (sender, args) =>
{
if (!changedB1)
{
Assert.AreEqual("1", mock.GetValue(MockBindable.TextProperty));
Assert.IsTrue(changedA1);
Assert.IsFalse(changedA2);
Assert.IsFalse(changedB2);
changedB1 = true;
}
else
{
Assert.AreEqual("2", mock.GetValue(MockBindable.TextProperty));
Assert.IsTrue(changedA1);
Assert.IsTrue(changedA2);
Assert.IsFalse(changedB2);
changedB2 = true;
}
};
mock.SetValue(MockBindable.TextProperty, "1");
Assert.AreEqual("2", mock.GetValue(MockBindable.TextProperty));
Assert.IsTrue(changedA1);
Assert.IsTrue(changedA2);
Assert.IsTrue(changedB1);
Assert.IsTrue(changedB2);
}
[Test]
public void RaiseOnEqual()
{
string foo = "foo";
var mock = new MockBindable();
mock.SetValue(MockBindable.TextProperty, foo);
bool changing = false;
mock.PropertyChanging += (o, e) =>
{
Assert.That(e.PropertyName, Is.EqualTo(MockBindable.TextProperty.PropertyName));
changing = true;
};
bool changed = true;
mock.PropertyChanged += (o, e) =>
{
Assert.That(e.PropertyName, Is.EqualTo(MockBindable.TextProperty.PropertyName));
changed = true;
};
mock.SetValueCore(MockBindable.TextProperty, foo,
SetValueFlags.ClearOneWayBindings | SetValueFlags.ClearDynamicResource | SetValueFlags.RaiseOnEqual);
Assert.That(changing, Is.True, "PropertyChanging event did not fire");
Assert.That(changed, Is.True, "PropertyChanged event did not fire");
}
[Test]
public void BindingContextGetter()
{
var label = new Label();
var view = new StackLayout { Children = { label } };
view.BindingContext = new { item0 = "Foo", item1 = "Bar" };
label.SetBinding(BindableObject.BindingContextProperty, "item0");
label.SetBinding(Label.TextProperty, Binding.SelfPath);
Assert.AreSame(label.BindingContext, label.GetValue(BindableObject.BindingContextProperty));
}
[Test]
public void BoundBindingContextUpdate()
{
var label = new Label();
var view = new StackLayout { Children = { label } };
var vm = new MockViewModel { Text = "FooBar" };
view.BindingContext = vm;
label.SetBinding(BindableObject.BindingContextProperty, "Text");
label.SetBinding(Label.TextProperty, Binding.SelfPath);
Assert.AreEqual("FooBar", label.BindingContext);
vm.Text = "Baz";
Assert.AreEqual("Baz", label.BindingContext);
}
[Test]
public void BoundBindingContextChange()
{
var label = new Label();
var view = new StackLayout { Children = { label } };
view.BindingContext = new MockViewModel { Text = "FooBar" }; ;
label.SetBinding(BindableObject.BindingContextProperty, "Text");
label.SetBinding(Label.TextProperty, Binding.SelfPath);
Assert.AreEqual("FooBar", label.BindingContext);
view.BindingContext = new MockViewModel { Text = "Baz" }; ;
Assert.AreEqual("Baz", label.BindingContext);
}
[Test]
public void TestReadOnly()
{
var bindablePropertyKey = BindableProperty.CreateReadOnly<MockBindable, string>(w => w.Foo, "DefaultValue");
var bindableProperty = bindablePropertyKey.BindableProperty;
var bindable = new MockBindable();
Assert.AreEqual("DefaultValue", bindable.GetValue(bindableProperty));
bindable.SetValue(bindablePropertyKey, "Bar");
Assert.AreEqual("Bar", bindable.GetValue(bindableProperty));
Assert.Throws<InvalidOperationException>(() => bindable.SetValue(bindableProperty, "Baz"));
Assert.AreEqual("Bar", bindable.GetValue(bindableProperty));
Assert.Throws<InvalidOperationException>(() => bindable.ClearValue(bindableProperty));
bindable.ClearValue(bindablePropertyKey);
Assert.AreEqual("DefaultValue", bindable.GetValue(bindableProperty));
}
[Test]
public void TestBindingTwoWayOnReadOnly()
{
var bindablePropertyKey = BindableProperty.CreateReadOnly<MockBindable, string>(w => w.Foo, "DefaultValue", BindingMode.OneWayToSource);
var bindableProperty = bindablePropertyKey.BindableProperty;
var bindable = new MockBindable();
var vm = new MockViewModel();
bindable.SetBinding(bindableProperty, new Binding("Text", BindingMode.TwoWay));
Assert.DoesNotThrow(() => bindable.BindingContext = vm);
Assert.AreEqual("DefaultValue", bindable.GetValue(bindableProperty));
}
[Test]
public void DefaultValueCreator()
{
int invoked = 0;
object defaultValue = new object();
var bindableProperty = BindableProperty.Create("Foo", typeof(object), typeof(MockBindable), defaultValue, defaultValueCreator: o =>
{
invoked++;
return new object();
});
var bindable = new MockBindable();
Assert.AreSame(defaultValue, bindableProperty.DefaultValue);
var newvalue = bindable.GetValue(bindableProperty);
Assert.AreNotSame(defaultValue, newvalue);
Assert.NotNull(newvalue);
Assert.AreEqual(1, invoked);
}
[Test]
public void DefaultValueCreatorIsInvokedOnlyAtFirstTime()
{
int invoked = 0;
var bindableProperty = BindableProperty.Create("Foo", typeof(object), typeof(MockBindable), null, defaultValueCreator: o =>
{
invoked++;
return new object();
});
var bindable = new MockBindable();
var value0 = bindable.GetValue(bindableProperty);
var value1 = bindable.GetValue(bindableProperty);
Assert.NotNull(value0);
Assert.NotNull(value1);
Assert.AreSame(value0, value1);
Assert.AreEqual(1, invoked);
}
[Test]
public void DefaultValueCreatorNotSharedAccrossInstances()
{
int invoked = 0;
var bindableProperty = BindableProperty.Create("Foo", typeof(object), typeof(MockBindable), null, defaultValueCreator: o =>
{
invoked++;
return new object();
});
var bindable0 = new MockBindable();
var bindable1 = new MockBindable();
var value0 = bindable0.GetValue(bindableProperty);
var value1 = bindable1.GetValue(bindableProperty);
Assert.AreNotSame(value0, value1);
Assert.AreEqual(2, invoked);
}
[Test]
public void DefaultValueCreatorInvokedAfterClearValue()
{
int invoked = 0;
var bindableProperty = BindableProperty.Create("Foo", typeof(object), typeof(MockBindable), null, defaultValueCreator: o =>
{
invoked++;
return new object();
});
var bindable = new MockBindable();
Assert.AreEqual(0, invoked);
var value0 = bindable.GetValue(bindableProperty);
Assert.NotNull(value0);
Assert.AreEqual(1, invoked);
bindable.ClearValue(bindableProperty);
var value1 = bindable.GetValue(bindableProperty);
Assert.NotNull(value1);
Assert.AreEqual(2, invoked);
Assert.AreNotSame(value0, value1);
}
[Test]
public void DefaultValueCreatorOnlyInvokedOnGetValue()
{
int invoked = 0;
var bindableProperty = BindableProperty.Create("Foo", typeof(object), typeof(MockBindable), null, defaultValueCreator: o =>
{
invoked++;
return new object();
});
var bindable = new MockBindable();
Assert.AreEqual(0, invoked);
var newvalue = bindable.GetValue(bindableProperty);
Assert.NotNull(newvalue);
Assert.AreEqual(1, invoked);
}
[Test]
public void DefaultValueCreatorDoesNotTriggerINPC()
{
int invoked = 0;
int propertychanged = 0;
int changedfired = 0;
var bindableProperty = BindableProperty.Create("Foo", typeof(object), typeof(MockBindable), null,
propertyChanged: (bindable, oldvalue, newvalue) =>
{
propertychanged++;
},
defaultValueCreator: o =>
{
invoked++;
return new object();
});
var bp = new MockBindable();
bp.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == "Foo")
changedfired++;
};
var value0 = bp.GetValue(bindableProperty);
Assert.NotNull(value0);
Assert.AreEqual(1, invoked);
Assert.AreEqual(0, propertychanged);
Assert.AreEqual(0, changedfired);
}
[Test]
public void IsSetIsFalseWhenPropNotSet()
{
string defaultValue = "default";
var bindableProperty = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable), defaultValue);
var bindable = new MockBindable();
Assert.AreEqual(defaultValue, bindableProperty.DefaultValue);
var isSet = bindable.IsSet(bindableProperty);
Assert.IsFalse(isSet);
}
[Test]
public void IsSetIsTrueWhenPropSet()
{
string defaultValue = "default";
string newValue = "new value";
var bindableProperty = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable), defaultValue);
var bindable = new MockBindable();
Assert.AreEqual(defaultValue, bindableProperty.DefaultValue);
bindable.SetValue(bindableProperty, newValue);
var isSet = bindable.IsSet(bindableProperty);
Assert.IsTrue(isSet);
}
[Test]
public void IsSetIsFalseWhenPropCleared()
{
string defaultValue = "default";
string newValue = "new value";
var bindableProperty = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable), defaultValue);
var bindable = new MockBindable();
bindable.SetValue(bindableProperty, newValue);
bindable.ClearValue(bindableProperty);
Assert.That(bindable.IsSet(bindableProperty), Is.False);
}
[Test]
public void IsSetIsTrueWhenPropSetToDefault()
{
string defaultValue = "default";
var bindableProperty = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable), defaultValue);
var bindable = new MockBindable();
Assert.AreEqual(defaultValue, bindableProperty.DefaultValue);
bindable.SetValue(bindableProperty, defaultValue);
var isSet = bindable.IsSet(bindableProperty);
Assert.IsTrue(isSet);
}
[Test]
public void IsSetIsTrueWhenPropSetByDefaultValueCreator()
{
string defaultValue = "default";
string defaultValueC = "defaultVC";
var bindableProperty = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable), defaultValue, defaultValueCreator: o =>
{
return defaultValueC;
});
var bindable = new MockBindable();
Assert.AreEqual(defaultValue, bindableProperty.DefaultValue);
var created = bindable.GetValue(bindableProperty);
Assert.AreEqual(defaultValueC, created);
var isSet = bindable.IsSet(bindableProperty);
Assert.IsTrue(isSet);
}
[Test]
public void StyleValueIsOverridenByValue()
{
var label = new Label();
label.SetValue(Label.TextProperty, "Foo", true);
Assert.AreEqual("Foo", label.Text);
label.SetValue(Label.TextProperty, "Bar");
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleBindingIsOverridenByValue()
{
var label = new Label();
label.SetBinding(Label.TextProperty, new Binding("foo"), true);
label.BindingContext = new { foo = "Foo" };
Assert.AreEqual("Foo", label.Text);
label.SetValue(Label.TextProperty, "Bar");
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleDynResourceIsOverridenByValue()
{
var label = new Label();
label.SetDynamicResource(Label.TextProperty, "foo", true);
label.Resources = new ResourceDictionary {
{"foo", "Foo"}
};
Assert.AreEqual("Foo", label.Text);
label.SetValue(Label.TextProperty, "Bar");
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleValueIsOverridenByBinding()
{
var label = new Label();
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetValue(Label.TextProperty, "Foo", true);
Assert.AreEqual("Foo", label.Text);
label.SetBinding(Label.TextProperty, "bar");
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleBindingIsOverridenByBinding()
{
var label = new Label();
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetBinding(Label.TextProperty, new Binding("foo"), true);
Assert.AreEqual("Foo", label.Text);
label.SetBinding(Label.TextProperty, "bar");
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleDynResourceIsOverridenByBinding()
{
var label = new Label();
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetDynamicResource(Label.TextProperty, "foo", true);
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
};
Assert.AreEqual("Foo", label.Text);
label.SetBinding(Label.TextProperty, "bar");
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleValueIsOverridenByDynResource()
{
var label = new Label();
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
{"bar", "Bar"}
};
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetValue(Label.TextProperty, "Foo", true);
Assert.AreEqual("Foo", label.Text);
label.SetDynamicResource(Label.TextProperty, "bar");
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleBindingIsOverridenByDynResource()
{
var label = new Label();
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
{"bar", "Bar"}
};
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetBinding(Label.TextProperty, new Binding("foo"), true);
Assert.AreEqual("Foo", label.Text);
label.SetDynamicResource(Label.TextProperty, "bar");
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleDynResourceIsOverridenByDynResource()
{
var label = new Label();
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
{"bar", "Bar"}
};
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetDynamicResource(Label.TextProperty, "foo", true);
Assert.AreEqual("Foo", label.Text);
label.SetDynamicResource(Label.TextProperty, "bar");
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void ValueIsPreservedOnStyleValue()
{
var label = new Label();
label.SetValue(Label.TextProperty, "Foo");
Assert.AreEqual("Foo", label.Text);
label.SetValue(Label.TextProperty, "Bar", true);
Assert.AreEqual("Foo", label.Text);
}
[Test]
public void BindingIsPreservedOnStyleValue()
{
var label = new Label();
label.SetBinding(Label.TextProperty, new Binding("foo"));
label.BindingContext = new { foo = "Foo" };
Assert.AreEqual("Foo", label.Text);
label.SetValue(Label.TextProperty, "Bar", true);
Assert.AreEqual("Foo", label.Text);
}
[Test]
public void DynResourceIsPreservedOnStyleValue()
{
var label = new Label();
label.SetDynamicResource(Label.TextProperty, "foo");
label.Resources = new ResourceDictionary {
{"foo", "Foo"}
};
Assert.AreEqual("Foo", label.Text);
label.SetValue(Label.TextProperty, "Bar", true);
Assert.AreEqual("Foo", label.Text);
}
[Test]
public void ValueIsPreservedOnStyleBinding()
{
var label = new Label();
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetValue(Label.TextProperty, "Foo");
Assert.AreEqual("Foo", label.Text);
label.SetBinding(Label.TextProperty, new Binding("bar"), true);
Assert.AreEqual("Foo", label.Text);
}
[Test]
public void BindingIsPreservedOnStyleBinding()
{
var label = new Label();
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetBinding(Label.TextProperty, new Binding("foo"));
Assert.AreEqual("Foo", label.Text);
label.SetBinding(Label.TextProperty, new Binding("bar"), true);
Assert.AreEqual("Foo", label.Text);
}
[Test]
public void DynResourceIsPreservedOnStyleBinding()
{
var label = new Label();
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetDynamicResource(Label.TextProperty, "foo");
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
};
Assert.AreEqual("Foo", label.Text);
label.SetBinding(Label.TextProperty, new Binding("bar"), true);
Assert.AreEqual("Foo", label.Text);
}
[Test]
public void ValueIsPreservedOnStyleDynResource()
{
var label = new Label();
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
{"bar", "Bar"}
};
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetValue(Label.TextProperty, "Foo");
Assert.AreEqual("Foo", label.Text);
label.SetDynamicResource(Label.TextProperty, "bar", true);
Assert.AreEqual("Foo", label.Text);
}
[Test]
public void BindingIsPreservedOnStyleDynResource()
{
var label = new Label();
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
{"bar", "Bar"}
};
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetBinding(Label.TextProperty, new Binding("foo"));
Assert.AreEqual("Foo", label.Text);
label.SetDynamicResource(Label.TextProperty, "bar", true);
Assert.AreEqual("Foo", label.Text);
}
[Test]
public void DynResourceIsPreservedOnStyleDynResource()
{
var label = new Label();
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
{"bar", "Bar"}
};
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetDynamicResource(Label.TextProperty, "foo");
Assert.AreEqual("Foo", label.Text);
label.SetDynamicResource(Label.TextProperty, "bar", true);
Assert.AreEqual("Foo", label.Text);
}
[Test]
public void StyleValueIsOverridenByStyleValue()
{
var label = new Label();
label.SetValue(Label.TextProperty, "Foo", true);
Assert.AreEqual("Foo", label.Text);
label.SetValue(Label.TextProperty, "Bar", true);
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleBindingIsOverridenByStyleValue()
{
var label = new Label();
label.SetBinding(Label.TextProperty, new Binding("foo"), true);
label.BindingContext = new { foo = "Foo" };
Assert.AreEqual("Foo", label.Text);
label.SetValue(Label.TextProperty, "Bar", true);
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleDynResourceIsOverridenByStyleValue()
{
var label = new Label();
label.SetDynamicResource(Label.TextProperty, "foo", true);
label.Resources = new ResourceDictionary {
{"foo", "Foo"}
};
Assert.AreEqual("Foo", label.Text);
label.SetValue(Label.TextProperty, "Bar", true);
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleValueIsOverridenByStyleBinding()
{
var label = new Label();
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetValue(Label.TextProperty, "Foo", true);
Assert.AreEqual("Foo", label.Text);
label.SetBinding(Label.TextProperty, new Binding("bar"), true);
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleBindingIsOverridenByStyleBinding()
{
var label = new Label();
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetBinding(Label.TextProperty, new Binding("foo"), true);
Assert.AreEqual("Foo", label.Text);
label.SetBinding(Label.TextProperty, new Binding("bar"), true);
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleDynResourceIsOverridenByStyleBinding()
{
var label = new Label();
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetDynamicResource(Label.TextProperty, "foo", true);
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
};
Assert.AreEqual("Foo", label.Text);
label.SetBinding(Label.TextProperty, new Binding("bar"), true);
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleValueIsOverridenByStyleDynResource()
{
var label = new Label();
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
{"bar", "Bar"}
};
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetValue(Label.TextProperty, "Foo", true);
Assert.AreEqual("Foo", label.Text);
label.SetDynamicResource(Label.TextProperty, "bar", true);
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleBindingIsOverridenByStyleDynResource()
{
var label = new Label();
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
{"bar", "Bar"}
};
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetBinding(Label.TextProperty, new Binding("foo"), true);
Assert.AreEqual("Foo", label.Text);
label.SetDynamicResource(Label.TextProperty, "bar", true);
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void StyleDynResourceIsOverridenByStyleDynResource()
{
var label = new Label();
label.Resources = new ResourceDictionary {
{"foo", "Foo"},
{"bar", "Bar"}
};
label.BindingContext = new { foo = "Foo", bar = "Bar" };
label.SetDynamicResource(Label.TextProperty, "foo", true);
Assert.AreEqual("Foo", label.Text);
label.SetDynamicResource(Label.TextProperty, "bar", true);
Assert.AreEqual("Bar", label.Text);
}
[Test]
public void SetValueCoreImplicitelyCastBasicType()
{
var prop = BindableProperty.Create("Foo", typeof(int), typeof(MockBindable), 0);
var bindable = new MockBindable();
Assert.DoesNotThrow(() => bindable.SetValue(prop, (object)(short)42));
Assert.AreEqual(42, bindable.GetValue(prop));
bindable.SetValue(prop, (object)(long)-42);
Assert.AreNotEqual(-42, bindable.GetValue(prop));
}
class CastFromString
{
public string Result { get; private set; }
public static implicit operator CastFromString(string source)
{
var o = new CastFromString();
o.Result = source;
return o;
}
}
[Test]
public void SetValueCoreInvokesOpImplicitOnPropertyType()
{
var prop = BindableProperty.Create("Foo", typeof(CastFromString), typeof(MockBindable), null);
var bindable = new MockBindable();
Assert.Null(bindable.GetValue(prop));
bindable.SetValue(prop, "foo");
Assert.AreEqual("foo", ((CastFromString)bindable.GetValue(prop)).Result);
}
class CastToString
{
string Result { get; set; }
public CastToString(string result)
{
Result = result;
}
public static implicit operator string(CastToString source)
{
return source.Result;
}
public override string ToString()
{
throw new InvalidOperationException();
}
}
[Test]
public void SetValueCoreInvokesOpImplicitOnValue()
{
var prop = BindableProperty.Create("Foo", typeof(string), typeof(MockBindable), null);
var bindable = new MockBindable();
Assert.Null(bindable.GetValue(prop));
bindable.SetValue(prop, new CastToString("foo"));
Assert.AreEqual("foo", bindable.GetValue(prop));
}
[Test]
public void DefaultValueCreatorCalledForChangeDelegates()
{
int changedOld = -1;
int changedNew = -1;
int changingOld = -1;
int changingNew = -1;
var prop = BindableProperty.Create("Foo", typeof(int), typeof(MockBindable), 0, defaultValueCreator: b => 10,
propertyChanged: (b, value, newValue) =>
{
changedOld = (int)value;
changedNew = (int)newValue;
},
propertyChanging: (b, value, newValue) =>
{
changingOld = (int)value;
changingNew = (int)newValue;
});
var bindable = new MockBindable();
var defaultValue = (int)bindable.GetValue(prop);
Assert.AreEqual(10, defaultValue);
bindable.SetValue(prop, 5);
bindable.ClearValue(prop);
Assert.AreEqual(5, changedOld);
Assert.AreEqual(5, changingOld);
Assert.AreEqual(10, changedNew);
Assert.AreEqual(10, changingNew);
}
[Test]
public void GetValuesDefaults()
{
var prop = BindableProperty.Create("Foo", typeof(int), typeof(MockBindable), 0);
var prop1 = BindableProperty.Create("Foo1", typeof(int), typeof(MockBindable), 1);
var prop2 = BindableProperty.Create("Foo2", typeof(int), typeof(MockBindable), 2);
var bindable = new MockBindable();
object[] values = bindable.GetValues(prop, prop1, prop2);
Assert.That(values.Length == 3);
Assert.That(values[0], Is.EqualTo(0));
Assert.That(values[1], Is.EqualTo(1));
Assert.That(values[2], Is.EqualTo(2));
}
[Test]
public void GetValues()
{
var prop = BindableProperty.Create("Foo", typeof(int), typeof(MockBindable), 0);
var prop1 = BindableProperty.Create("Foo1", typeof(int), typeof(MockBindable), 1);
var prop2 = BindableProperty.Create("Foo2", typeof(int), typeof(MockBindable), 2);
var bindable = new MockBindable();
bindable.SetValue(prop, 3);
bindable.SetValue(prop2, 5);
object[] values = bindable.GetValues(prop, prop1, prop2);
Assert.That(values.Length == 3);
Assert.That(values[0], Is.EqualTo(3));
Assert.That(values[1], Is.EqualTo(1));
Assert.That(values[2], Is.EqualTo(5));
}
class BindingContextConverter
: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new MockViewModel { Text = value + "Converted" };
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
[Test]
//https://bugzilla.xamarin.com/show_bug.cgi?id=24485
public void BindingContextBoundThroughConverter()
{
var bindable = new MockBindable();
bindable.BindingContext = "test";
bindable.SetBinding(BindableObject.BindingContextProperty, new Binding(".", converter: new BindingContextConverter()));
bindable.SetBinding(MockBindable.TextProperty, "Text");
Assert.That(() => bindable.Text, Is.EqualTo("testConverted"));
}
public class VMLocator
{
public event EventHandler Invoked;
public int Count;
public object VM
{
get
{
Count++;
var eh = Invoked;
if (eh != null)
eh(this, EventArgs.Empty);
return new object();
}
}
}
[Test]
//https://bugzilla.xamarin.com/show_bug.cgi?id=27299
public void BindingOnBindingContextDoesntReapplyBindingContextBinding()
{
var bindable = new MockBindable();
var locator = new VMLocator();
Assert.AreEqual(0, locator.Count);
locator.Invoked += (sender, e) => Assert.IsTrue(locator.Count <= 1);
bindable.SetBinding(BindableObject.BindingContextProperty, new Binding("VM", source: locator));
Assert.IsTrue(locator.Count == 1);
}
[Test]
//https://github.com/xamarin/Xamarin.Forms/issues/2019
public void EventSubscribingOnBindingContextChanged()
{
var source = new MockBindable();
var bindable = new MockBindable();
var property = BindableProperty.Create("foo", typeof(string), typeof(MockBindable), null);
bindable.SetBinding(property, new Binding("BindingContext", source: source));
Assert.That((string)bindable.GetValue(property), Is.EqualTo(null));
BindableObject.SetInheritedBindingContext(source, "bar"); //inherited BC, only trigger BCChanged
Assert.That((string)bindable.GetValue(property), Is.EqualTo("bar"));
}
[Test]
public void BindingsEditableAfterUnapplied()
{
var bindable = new MockBindable();
var binding = new Binding(".");
bindable.SetBinding(MockBindable.TextProperty, binding);
bindable.BindingContext = "foo";
Assume.That(bindable.Text, Is.EqualTo(bindable.BindingContext));
bindable.RemoveBinding(MockBindable.TextProperty);
Assert.That(() => binding.Path = "foo", Throws.Nothing);
}
[Test]
// https://bugzilla.xamarin.com/show_bug.cgi?id=24054
public void BindingsAppliedUnappliedWithNullContext()
{
var bindable = new MockBindable();
var binding = new Binding(".");
bindable.SetBinding(MockBindable.TextProperty, binding);
bindable.BindingContext = "foo";
Assume.That(bindable.Text, Is.EqualTo(bindable.BindingContext));
bindable.BindingContext = null;
Assume.That(bindable.Text, Is.EqualTo(bindable.BindingContext));
bindable.BindingContext = "bar";
Assume.That(bindable.Text, Is.EqualTo(bindable.BindingContext));
bindable.RemoveBinding(MockBindable.TextProperty);
Assert.That(() => binding.Path = "Foo", Throws.Nothing);
}
class InfoToString
{
public override string ToString() => "converted";
}
[Test]
//https://github.com/xamarin/Xamarin.Forms/issues/6281
public void SetValueToTextInvokesToString()
{
var prop = BindableProperty.Create("foo", typeof(string), typeof(MockBindable), null);
var bindable = new MockBindable();
bindable.SetValue(prop, new InfoToString());
Assert.That(bindable.GetValue(prop), Is.EqualTo("converted"));
}
[Test]
//https://github.com/xamarin/Xamarin.Forms/issues/6281
public void SetBindingToTextInvokesToString()
{
var prop = BindableProperty.Create("foo", typeof(string), typeof(MockBindable), null);
var bindable = new MockBindable() { BindingContext = new { info = new InfoToString() } };
bindable.SetBinding(prop, "info");
Assert.That(bindable.GetValue(prop), Is.EqualTo("converted"));
}
}
}