summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core.UnitTests
diff options
context:
space:
mode:
authorStephane Delcroix <stephane@delcroix.org>2016-09-08 20:39:05 +0200
committerJason Smith <jason.smith@xamarin.com>2016-09-08 11:39:05 -0700
commit85426c5d9495eb1d55b3128bf97e50c68a73b53f (patch)
tree2f81e5868ce61eb90d15c6c51a354603b8395627 /Xamarin.Forms.Core.UnitTests
parent11326e1c182b3ff5c3d82c6ef7d09c193bc19891 (diff)
downloadxamarin-forms-85426c5d9495eb1d55b3128bf97e50c68a73b53f.tar.gz
xamarin-forms-85426c5d9495eb1d55b3128bf97e50c68a73b53f.tar.bz2
xamarin-forms-85426c5d9495eb1d55b3128bf97e50c68a73b53f.zip
Native Bindings (#278)
* [C, I, A, W] Support Native Bindings * fix tabs
Diffstat (limited to 'Xamarin.Forms.Core.UnitTests')
-rw-r--r--Xamarin.Forms.Core.UnitTests/NativeBindingTests.cs487
-rw-r--r--Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj1
2 files changed, 488 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core.UnitTests/NativeBindingTests.cs b/Xamarin.Forms.Core.UnitTests/NativeBindingTests.cs
new file mode 100644
index 00000000..e7c7f9a8
--- /dev/null
+++ b/Xamarin.Forms.Core.UnitTests/NativeBindingTests.cs
@@ -0,0 +1,487 @@
+using System;
+using System.Collections.Generic;
+using NUnit.Framework;
+using System.ComponentModel;
+using System.Globalization;
+
+namespace Xamarin.Forms.Core.UnitTests
+{
+ public class MockNativeView
+ {
+ public IList<MockNativeView> SubViews { get; set; }
+ public string Foo { get; set; }
+ public int Bar { get; set; }
+ public string Baz { get; set; }
+
+ public void FireBazChanged()
+ {
+ BazChanged?.Invoke(this, new TappedEventArgs(null));
+ }
+
+ public event EventHandler<TappedEventArgs> BazChanged;
+
+ public event EventHandler SelectedColorChanged;
+
+ MockNativeColor _selectedColor;
+ public MockNativeColor SelectedColor
+ {
+ get { return _selectedColor; }
+ set
+ {
+ if (_selectedColor == value)
+ return;
+ _selectedColor = value;
+ SelectedColorChanged?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ }
+
+ class MockNativeViewWrapper : View
+ {
+ public MockNativeView NativeView { get; }
+
+ public MockNativeViewWrapper(MockNativeView nativeView)
+ {
+ NativeView = nativeView;
+ nativeView.TransferbindablePropertiesToWrapper(this);
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ NativeView.SetBindingContext(BindingContext, nv => nv.SubViews);
+ base.OnBindingContextChanged();
+ }
+
+ }
+
+ public class MockNativeColor
+ {
+
+ public MockNativeColor(Color color)
+ {
+ FormsColor = color;
+ }
+
+ public Color FormsColor
+ {
+ get;
+ set;
+ }
+ }
+
+ public static class MockNativeViewExtensions
+ {
+ public static View ToView(this MockNativeView nativeView)
+ {
+ return new MockNativeViewWrapper(nativeView);
+ }
+
+ public static void SetBinding(this MockNativeView target, string targetProperty, BindingBase binding, string updateSourceEventName = null)
+ {
+ NativeBindingHelpers.SetBinding(target, targetProperty, binding, updateSourceEventName);
+ }
+
+ internal static void SetBinding(this MockNativeView target, string targetProperty, BindingBase binding, INotifyPropertyChanged propertyChanged)
+ {
+ NativeBindingHelpers.SetBinding(target, targetProperty, binding, propertyChanged);
+ }
+
+ public static void SetBinding(this MockNativeView target, BindableProperty targetProperty, BindingBase binding)
+ {
+ NativeBindingHelpers.SetBinding(target, targetProperty, binding);
+ }
+
+ public static void SetValue(this MockNativeView target, BindableProperty targetProperty, object value)
+ {
+ NativeBindingHelpers.SetValue(target, targetProperty, value);
+ }
+
+ public static void SetBindingContext(this MockNativeView target, object bindingContext, Func<MockNativeView, IEnumerable<MockNativeView>> getChild = null)
+ {
+ NativeBindingHelpers.SetBindingContext(target, bindingContext, getChild);
+ }
+
+ internal static void TransferbindablePropertiesToWrapper(this MockNativeView target, MockNativeViewWrapper wrapper)
+ {
+ NativeBindingHelpers.TransferBindablePropertiesToWrapper(target, wrapper);
+ }
+ }
+
+ class MockCustomColorConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is Color)
+ return new MockNativeColor((Color)value);
+ return value;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is MockNativeColor)
+ return ((MockNativeColor)value).FormsColor;
+ return value;
+ }
+ }
+
+ class MockINPC : INotifyPropertyChanged
+ {
+ public void FireINPC(object sender, string propertyName)
+ {
+ PropertyChanged?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+ }
+
+ class MockVMForNativeBinding : INotifyPropertyChanged
+ {
+ string fFoo;
+ public string FFoo {
+ get { return fFoo; }
+ set {
+ fFoo = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("FFoo"));
+ }
+ }
+
+ int bBar;
+ public int BBar {
+ get { return bBar; }
+ set {
+ bBar = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("BBar"));
+ }
+ }
+
+ Color cColor;
+ public Color CColor
+ {
+ get { return cColor; }
+ set
+ {
+ if (cColor == value)
+ return;
+ cColor = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CColor"));
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+ }
+
+ [TestFixture]
+ public class NativeBindingTests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ Device.PlatformServices = new MockPlatformServices();
+
+ //this should collect the ConditionalWeakTable
+ GC.Collect();
+ }
+
+ [Test]
+ public void SetOneWayBinding()
+ {
+ var nativeView = new MockNativeView();
+ Assert.AreEqual(null, nativeView.Foo);
+ Assert.AreEqual(0, nativeView.Bar);
+
+ nativeView.SetBinding("Foo", new Binding("FFoo", mode:BindingMode.OneWay));
+ nativeView.SetBinding("Bar", new Binding("BBar", mode:BindingMode.OneWay));
+ Assert.AreEqual(null, nativeView.Foo);
+ Assert.AreEqual(0, nativeView.Bar);
+
+ nativeView.SetBindingContext(new { FFoo = "Foo", BBar = 42 });
+ Assert.AreEqual("Foo", nativeView.Foo);
+ Assert.AreEqual(42, nativeView.Bar);
+ }
+
+ [Test]
+ public void AttachedPropertiesAreTransferredFromTheBackpack()
+ {
+ var nativeView = new MockNativeView();
+ nativeView.SetValue(Grid.ColumnProperty, 3);
+ nativeView.SetBinding(Grid.RowProperty, new Binding("foo"));
+
+ var view = nativeView.ToView();
+ view.BindingContext = new { foo = 42 };
+ Assert.AreEqual(3, view.GetValue(Grid.ColumnProperty));
+ Assert.AreEqual(42, view.GetValue(Grid.RowProperty));
+ }
+
+ [Test]
+ public void Set2WayBindings()
+ {
+ var nativeView = new MockNativeView();
+ Assert.AreEqual(null, nativeView.Foo);
+ Assert.AreEqual(0, nativeView.Bar);
+
+ var vm = new MockVMForNativeBinding();
+ nativeView.SetBindingContext(vm);
+ var inpc = new MockINPC();
+ nativeView.SetBinding("Foo", new Binding("FFoo", mode:BindingMode.TwoWay), inpc);
+ nativeView.SetBinding("Bar", new Binding("BBar", mode:BindingMode.TwoWay), inpc);
+ Assert.AreEqual(null, nativeView.Foo);
+ Assert.AreEqual(0, nativeView.Bar);
+ Assert.AreEqual(null, vm.FFoo);
+ Assert.AreEqual(0, vm.BBar);
+
+ nativeView.Foo = "oof";
+ inpc.FireINPC(nativeView, "Foo");
+ nativeView.Bar = -42;
+ inpc.FireINPC(nativeView, "Bar");
+ Assert.AreEqual("oof", nativeView.Foo);
+ Assert.AreEqual(-42, nativeView.Bar);
+ Assert.AreEqual("oof", vm.FFoo);
+ Assert.AreEqual(-42, vm.BBar);
+
+ vm.FFoo = "foo";
+ vm.BBar = 42;
+ Assert.AreEqual("foo", nativeView.Foo);
+ Assert.AreEqual(42, nativeView.Bar);
+ Assert.AreEqual("foo", vm.FFoo);
+ Assert.AreEqual(42, vm.BBar);
+ }
+
+ [Test]
+ public void Set2WayBindingsWithUpdateSourceEvent()
+ {
+ var nativeView = new MockNativeView();
+ Assert.AreEqual(null, nativeView.Baz);
+
+ var vm = new MockVMForNativeBinding();
+ nativeView.SetBindingContext(vm);
+
+ nativeView.SetBinding("Baz", new Binding("FFoo", mode: BindingMode.TwoWay), "BazChanged");
+ Assert.AreEqual(null, nativeView.Baz);
+ Assert.AreEqual(null, vm.FFoo);
+
+ nativeView.Baz = "oof";
+ nativeView.FireBazChanged();
+ Assert.AreEqual("oof", nativeView.Baz);
+ Assert.AreEqual("oof", vm.FFoo);
+
+ vm.FFoo = "foo";
+ Assert.AreEqual("foo", nativeView.Baz);
+ Assert.AreEqual("foo", vm.FFoo);
+ }
+
+ [Test]
+ public void Set2WayBindingsWithUpdateSourceEventInBindingObject()
+ {
+ var nativeView = new MockNativeView();
+ Assert.AreEqual(null, nativeView.Baz);
+
+ var vm = new MockVMForNativeBinding();
+ nativeView.SetBindingContext(vm);
+
+ nativeView.SetBinding("Baz", new Binding("FFoo", mode: BindingMode.TwoWay) { UpdateSourceEventName = "BazChanged"});
+ Assert.AreEqual(null, nativeView.Baz);
+ Assert.AreEqual(null, vm.FFoo);
+
+ nativeView.Baz = "oof";
+ nativeView.FireBazChanged();
+ Assert.AreEqual("oof", nativeView.Baz);
+ Assert.AreEqual("oof", vm.FFoo);
+
+ vm.FFoo = "foo";
+ Assert.AreEqual("foo", nativeView.Baz);
+ Assert.AreEqual("foo", vm.FFoo);
+ }
+
+ [Test]
+ public void NativeViewsAreCollected()
+ {
+ WeakReference wr = null;
+
+ int i = 0;
+ Action create = null;
+ create = () => {
+ if (i++ < 1024) {
+ create();
+ return;
+ }
+
+ var nativeView = new MockNativeView();
+ nativeView.SetBinding("fooBar", new Binding("Foo", BindingMode.TwoWay));
+ nativeView.SetBinding("Baz", new Binding("Qux", BindingMode.TwoWay), "BazChanged");
+
+ wr = new WeakReference(nativeView);
+ nativeView = null;
+
+ };
+
+ create();
+
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect();
+
+ Assert.False(wr.IsAlive);
+ }
+
+ [Test]
+ public void ProxiesAreCollected()
+ {
+ WeakReference wr = null;
+
+ int i = 0;
+ Action create = null;
+ create = () => {
+ if (i++ < 1024) {
+ create();
+ return;
+ }
+
+ var nativeView = new MockNativeView();
+ nativeView.SetBinding("fooBar", new Binding("Foo", BindingMode.TwoWay));
+ nativeView.SetBinding("Baz", new Binding("Qux", BindingMode.TwoWay), "BazChanged");
+
+ NativeBindingHelpers.BindableObjectProxy<MockNativeView> proxy;
+ if (!NativeBindingHelpers.BindableObjectProxy<MockNativeView>.BindableObjectProxies.TryGetValue(nativeView, out proxy))
+ Assert.Fail();
+
+ wr = new WeakReference(proxy);
+ nativeView = null;
+ };
+
+ create();
+
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect();
+
+ Assert.False(wr.IsAlive);
+ }
+
+ [Test]
+ public void SetBindingContextToSubviews()
+ {
+ var nativeView = new MockNativeView { SubViews = new List<MockNativeView> ()};
+ var nativeViewChild = new MockNativeView();
+
+ nativeViewChild.SetBinding("Foo", new Binding("FFoo", mode: BindingMode.OneWay));
+ nativeViewChild.SetBinding("Bar", new Binding("BBar", mode: BindingMode.OneWay));
+
+ nativeView.SubViews.Add(nativeViewChild);
+
+ var vm = new MockVMForNativeBinding();
+ nativeView.SetBindingContext(vm, v => v.SubViews);
+
+ Assert.AreEqual(null, nativeViewChild.Foo);
+ Assert.AreEqual(0, nativeViewChild.Bar);
+
+ nativeView.SetBindingContext(new { FFoo = "Foo", BBar = 42 }, v => v.SubViews);
+ Assert.AreEqual("Foo", nativeViewChild.Foo);
+ Assert.AreEqual(42, nativeViewChild.Bar);
+ }
+
+ [Test]
+ public void ThrowsNeedsConverter()
+ {
+ var nativeView = new MockNativeView();
+ Assert.AreEqual(null, nativeView.Foo);
+ Assert.AreEqual(0, nativeView.Bar);
+ var vm = new MockVMForNativeBinding();
+ nativeView.SetBinding("SelectedColor", new Binding("CColor"));
+ Assert.Throws<ArgumentException>(() => nativeView.SetBindingContext(vm));
+ }
+
+ [Test]
+ public void TestConverterDoesNotThrow()
+ {
+ var nativeView = new MockNativeView();
+ Assert.AreEqual(null, nativeView.Foo);
+ Assert.AreEqual(0, nativeView.Bar);
+ var vm = new MockVMForNativeBinding();
+ var converter = new MockCustomColorConverter();
+ nativeView.SetBinding("SelectedColor", new Binding("CColor", converter: converter));
+ Assert.DoesNotThrow(() => nativeView.SetBindingContext(vm));
+ }
+
+ [Test]
+ public void TestConverterWorks()
+ {
+ var nativeView = new MockNativeView();
+ Assert.AreEqual(null, nativeView.Foo);
+ Assert.AreEqual(0, nativeView.Bar);
+ var vm = new MockVMForNativeBinding();
+ vm.CColor = Color.Red;
+ var converter = new MockCustomColorConverter();
+ nativeView.SetBinding("SelectedColor", new Binding("CColor", converter: converter));
+ nativeView.SetBindingContext(vm);
+ Assert.AreEqual(vm.CColor, nativeView.SelectedColor.FormsColor);
+ }
+
+ [Test]
+ public void TestConverter2WayWorks()
+ {
+ var nativeView = new MockNativeView();
+ Assert.AreEqual(null, nativeView.Foo);
+ Assert.AreEqual(0, nativeView.Bar);
+ var inpc = new MockINPC();
+ var vm = new MockVMForNativeBinding();
+ vm.CColor = Color.Red;
+ var converter = new MockCustomColorConverter();
+ nativeView.SetBinding("SelectedColor", new Binding("CColor", BindingMode.TwoWay, converter), inpc);
+ nativeView.SetBindingContext(vm);
+ Assert.AreEqual(vm.CColor, nativeView.SelectedColor.FormsColor);
+
+ var newFormsColor = Color.Blue;
+ var newColor = new MockNativeColor(newFormsColor);
+ nativeView.SelectedColor = newColor;
+ inpc.FireINPC(nativeView, nameof(nativeView.SelectedColor));
+
+ Assert.AreEqual(newFormsColor, vm.CColor);
+
+ }
+
+ [Test]
+ public void Binding2WayWithConvertersDoNotLoop()
+ {
+ var nativeView = new MockNativeView();
+ int count = 0;
+
+ nativeView.SelectedColorChanged += (o, e) => {
+ if (++count > 5)
+ Assert.Fail("Probable loop detected");
+ };
+
+ var vm = new MockVMForNativeBinding { CColor = Color.Red };
+
+ nativeView.SetBinding("SelectedColor", new Binding("CColor", BindingMode.TwoWay, new MockCustomColorConverter()), "SelectedColorChanged");
+ nativeView.SetBindingContext(vm);
+
+ Assert.AreEqual(count, 1);
+ }
+
+ [Test]
+ public void ThrowsOnMissingProperty()
+ {
+ var nativeView = new MockNativeView();
+ nativeView.SetBinding("Qux", new Binding("Foo"));
+ Assert.Throws<InvalidOperationException>(()=>nativeView.SetBindingContext(new { Foo = 42 }));
+ }
+
+ [Test]
+ public void ThrowsOnMissingEvent()
+ {
+ var nativeView = new MockNativeView();
+ Assert.Throws<ArgumentException>(()=>nativeView.SetBinding("Foo", new Binding("Foo", BindingMode.TwoWay), "missingEvent"));
+ }
+
+ [Test]
+ public void OneWayToSourceAppliedOnSetBC()
+ {
+ var nativeView = new MockNativeView { Foo = "foobar" };
+ nativeView.SetBinding("Foo", new Binding("FFoo", BindingMode.OneWayToSource));
+ var vm = new MockVMForNativeBinding { FFoo = "qux" };
+ nativeView.SetBindingContext(vm);
+ Assert.AreEqual("foobar", vm.FFoo);
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj b/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj
index 74544605..2c7490d1 100644
--- a/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj
+++ b/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj
@@ -178,6 +178,7 @@
<Compile Include="TriggerTests.cs" />
<Compile Include="PinchGestureRecognizerTests.cs" />
<Compile Include="AppLinkEntryTests.cs" />
+ <Compile Include="NativeBindingTests.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj">