diff options
author | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 13:02:25 -0700 |
---|---|---|
committer | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 16:13:41 -0700 |
commit | 17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch) | |
tree | b5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Core.UnitTests | |
download | xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2 xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip |
Initial import
Diffstat (limited to 'Xamarin.Forms.Core.UnitTests')
117 files changed, 26408 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core.UnitTests/AbsoluteLayoutTests.cs b/Xamarin.Forms.Core.UnitTests/AbsoluteLayoutTests.cs new file mode 100644 index 00000000..3faec22e --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/AbsoluteLayoutTests.cs @@ -0,0 +1,265 @@ +using System; +using NUnit.Framework; +using NUnit.Framework.Constraints; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class AbsoluteLayoutTests : BaseTestFixture + { + + [Test] + public void Constructor () + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + Assert.That (abs.Children, Is.Empty); + + var sizeReq = abs.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + Assert.AreEqual (Size.Zero, sizeReq.Request); + Assert.AreEqual (Size.Zero, sizeReq.Minimum); + } + + [Test] + public void AbsolutePositionAndSize () + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View {IsPlatformEnabled = true}; + + abs.Children.Add (child, new Rectangle (10, 20, 30, 40)); + + abs.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (10, 20, 30, 40), child.Bounds); + } + + [Test] + public void AbsolutePositionRelativeSize () + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View {IsPlatformEnabled = true}; + + + abs.Children.Add (child, new Rectangle (10, 20, 0.4, 0.5), AbsoluteLayoutFlags.SizeProportional); + + abs.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.That (child.X, Is.EqualTo (10)); + Assert.That (child.Y, Is.EqualTo (20)); + Assert.That (child.Width, Is.EqualTo (40).Within (0.0001)); + Assert.That (child.Height, Is.EqualTo (50).Within (0.0001)); + } + + [TestCase (30, 40, 0.2, 0.3)] + [TestCase (35, 45, 0.5, 0.5)] + [TestCase (35, 45, 0, 0)] + [TestCase (35, 45, 1, 1)] + public void RelativePositionAbsoluteSize (double width, double height, double relX, double relY) + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View {IsPlatformEnabled = true}; + + abs.Children.Add (child, new Rectangle (relX, relY, width, height), AbsoluteLayoutFlags.PositionProportional); + + abs.Layout (new Rectangle (0, 0, 100, 100)); + + double expectedX = Math.Round ((100 - width) * relX); + double expectedY = Math.Round ((100 - height) * relY); + Assert.That (child.X, Is.EqualTo (expectedX).Within (0.0001)); + Assert.That (child.Y, Is.EqualTo (expectedY).Within (0.0001)); + Assert.That (child.Width, Is.EqualTo (width)); + Assert.That (child.Height, Is.EqualTo (height)); + } + + [Test] + public void RelativePositionRelativeSize ([Values (0.0, 0.2, 0.5, 1.0)] double relX, [Values (0.0, 0.2, 0.5, 1.0)] double relY, [Values (0.0, 0.2, 0.5, 1.0)] double relHeight, [Values (0.0, 0.2, 0.5, 1.0)] double relWidth) + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View { + IsPlatformEnabled = true + }; + abs.Children.Add (child, new Rectangle(relX, relY, relWidth, relHeight), AbsoluteLayoutFlags.All); + abs.Layout (new Rectangle(0, 0, 100, 100)); + + double expectedWidth = Math.Round (100 * relWidth); + double expectedHeight = Math.Round (100 * relHeight); + double expectedX = Math.Round ((100 - expectedWidth) * relX); + double expectedY = Math.Round ((100 - expectedHeight) * relY); + Assert.That (child.X, Is.EqualTo (expectedX).Within (0.0001)); + Assert.That (child.Y, Is.EqualTo (expectedY).Within (0.0001)); + Assert.That (child.Width, Is.EqualTo (expectedWidth).Within (0.0001)); + Assert.That (child.Height, Is.EqualTo (expectedHeight).Within (0.0001)); + } + + [Test] + public void SizeRequestWithNormalChild () + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View (); + + // ChildSizeReq == 100x20 + abs.Children.Add (child, new Rectangle (10, 20, 30, 40)); + + var sizeReq = abs.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + + Assert.AreEqual (new Size (40, 60), sizeReq.Request); + Assert.AreEqual (new Size (40, 60), sizeReq.Minimum); + } + + [Test] + public void SizeRequestWithRelativePositionChild () + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View (); + + // ChildSizeReq == 100x20 + abs.Children.Add (child, new Rectangle(0.5, 0.5, 30, 40), AbsoluteLayoutFlags.PositionProportional); + + var sizeReq = abs.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + + Assert.AreEqual (new Size (30, 40), sizeReq.Request); + Assert.AreEqual (new Size (30, 40), sizeReq.Minimum); + } + + [Test] + public void SizeRequestWithRelativeChild () + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + // ChildSizeReq == 100x20 + abs.Children.Add (child, new Rectangle(0.5, 0.5, 0.5, 0.5), AbsoluteLayoutFlags.All); + + var sizeReq = abs.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + + Assert.AreEqual (new Size (200, 40), sizeReq.Request); + Assert.AreEqual (new Size (0, 0), sizeReq.Minimum); + } + + [Test] + public void SizeRequestWithRelativeSizeChild () + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + // ChildSizeReq == 100x20 + abs.Children.Add (child, new Rectangle(10, 20, 0.5, 0.5), AbsoluteLayoutFlags.SizeProportional); + + var sizeReq = abs.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + + Assert.AreEqual (new Size (210, 60), sizeReq.Request); + Assert.AreEqual (new Size (10, 20), sizeReq.Minimum); + } + + [Test] + public void MeasureInvalidatedFiresWhenFlagsChanged () + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + abs.Children.Add (child, new Rectangle (1, 1, 100, 100)); + + bool fired = false; + abs.MeasureInvalidated += (sender, args) => fired = true; + + AbsoluteLayout.SetLayoutFlags (child, AbsoluteLayoutFlags.PositionProportional); + + Assert.True (fired); + } + + [Test] + public void MeasureInvalidatedFiresWhenBoundsChanged () + { + var abs = new AbsoluteLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + abs.Children.Add (child, new Rectangle (1, 1, 100, 100)); + + bool fired = false; + abs.MeasureInvalidated += (sender, args) => fired = true; + + AbsoluteLayout.SetLayoutBounds (child, new Rectangle (2, 2, 200, 200)); + + Assert.True (fired); + } + + [Test] + public void TestBoundsTypeConverter () + { + var converter = new BoundsTypeConverter (); + + Assert.IsTrue (converter.CanConvertFrom (typeof(string))); + Assert.AreEqual (new Rectangle (3, 4, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize), converter.ConvertFromInvariantString ("3, 4")); + Assert.AreEqual (new Rectangle (3, 4, 20, 30), converter.ConvertFromInvariantString ("3, 4, 20, 30")); + Assert.AreEqual (new Rectangle (3, 4, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize), converter.ConvertFromInvariantString ("3, 4, AutoSize, AutoSize")); + Assert.AreEqual (new Rectangle (3, 4, AbsoluteLayout.AutoSize, 30), converter.ConvertFromInvariantString ("3, 4, AutoSize, 30")); + Assert.AreEqual (new Rectangle (3, 4, 20, AbsoluteLayout.AutoSize), converter.ConvertFromInvariantString ("3, 4, 20, AutoSize")); + } + + [Test] + public void TurkeyTestBoundsTypeConverter () + { + var converter = new BoundsTypeConverter (); + + var autoSize = "AutoSize"; + + Assert.AreEqual (new Rectangle (3.3, 4.4, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize), converter.ConvertFromInvariantString ("3.3, 4.4, " + autoSize + ", AutoSize")); + Assert.AreEqual (new Rectangle (3.3, 4.4, 5.5, 6.6), converter.ConvertFromInvariantString ("3.3, 4.4, 5.5, 6.6")); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/AnimatableKeyTests.cs b/Xamarin.Forms.Core.UnitTests/AnimatableKeyTests.cs new file mode 100644 index 00000000..385ab1fc --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/AnimatableKeyTests.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class AnimatableKeyTests + { + class FakeAnimatable : IAnimatable + { + public void BatchBegin () + { + + } + + public void BatchCommit () + { + + } + } + + [Test] + public void KeysWithDifferentHandlesAreNotEqual () + { + var animatable = new FakeAnimatable(); + + var key1 = new AnimatableKey(animatable, "handle1"); + var key2 = new AnimatableKey(animatable, "handle2"); + + Assert.AreNotEqual (key1, key2); + } + + [Test] + public void KeysWithDifferentAnimatablesAreNotEqual () + { + var animatable1 = new FakeAnimatable(); + var animatable2 = new FakeAnimatable(); + + var key1 = new AnimatableKey(animatable1, "handle"); + var key2 = new AnimatableKey(animatable2, "handle"); + + Assert.AreNotEqual (key1, key2); + } + + [Test] + public void KeysWithSameAnimatableAndHandleAreEqual () + { + var animatable = new FakeAnimatable(); + + var key1 = new AnimatableKey(animatable, "handle"); + var key2 = new AnimatableKey(animatable, "handle"); + + Assert.AreEqual (key1, key2); + } + + [Test] + public void ThrowsWhenKeysWithSameAnimatableAdded () + { + var animatable = new FakeAnimatable(); + + var key1 = new AnimatableKey(animatable, "handle"); + var key2 = new AnimatableKey(animatable, "handle"); + + var dict = new Dictionary<AnimatableKey, object> { { key1, new object () } }; + + Assert.Throws<ArgumentException> (() => { + dict.Add (key2, new object ()); + }); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/BaseTestFixture.cs b/Xamarin.Forms.Core.UnitTests/BaseTestFixture.cs new file mode 100644 index 00000000..d3a57ea3 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BaseTestFixture.cs @@ -0,0 +1,32 @@ +using System; +using System.Globalization; +using System.Threading; + +using NUnit.Framework; +using NUnit.Framework.Constraints; + +namespace Xamarin.Forms.Core.UnitTests +{ + public class BaseTestFixture + { + [SetUp] + public virtual void Setup () + { +#if !WINDOWS_PHONE + var culture = Environment.GetEnvironmentVariable ("UNIT_TEST_CULTURE"); + + if (!string.IsNullOrEmpty (culture)) { + var thead = Thread.CurrentThread; + thead.CurrentCulture = new CultureInfo (culture); + } +#endif + Console.WriteLine ("Current culture: {0}", Thread.CurrentThread.CurrentCulture.Name); + } + + [TearDown] + public virtual void TearDown () + { + + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/BehaviorTest.cs b/Xamarin.Forms.Core.UnitTests/BehaviorTest.cs new file mode 100644 index 00000000..78e0cfb0 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BehaviorTest.cs @@ -0,0 +1,110 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + internal class MockBehavior<T> : Behavior<T> where T:BindableObject + { + public bool attached; + public bool detached; + + protected override void OnAttachedTo (BindableObject bindable) + { + base.OnAttachedTo (bindable); + attached = true; + AssociatedObject = bindable; + } + + protected override void OnDetachingFrom (BindableObject bindable) + { + detached = true; + base.OnDetachingFrom (bindable); + AssociatedObject = null; + } + + public BindableObject AssociatedObject {get;set;} + } + + [TestFixture] + public class BehaviorTest : BaseTestFixture + { + [Test] + public void AttachAndDetach () + { + var behavior = new MockBehavior<MockBindable> (); + var bindable = new MockBindable (); + + Assert.False (behavior.attached); + Assert.False (behavior.detached); + Assert.Null (behavior.AssociatedObject); + + ((IAttachedObject)behavior).AttachTo (bindable); + + Assert.True (behavior.attached); + Assert.False (behavior.detached); + Assert.AreSame (bindable, behavior.AssociatedObject); + + ((IAttachedObject)behavior).DetachFrom (bindable); + + Assert.True (behavior.attached); + Assert.True (behavior.detached); + Assert.Null (behavior.AssociatedObject); + } + + [Test] + public void AttachToTypeCompatibleWithTargetType () + { + var behavior = new MockBehavior<MockBindable> (); + var bindable = new View (); + + Assert.Throws<InvalidOperationException> (() => ((IAttachedObject)behavior).AttachTo (bindable)); + } + + [Test] + public void BehaviorsInCollectionAreAttachedWhenCollectionIsAttached () + { + var behavior = new MockBehavior<MockBindable> (); + var collection = new AttachedCollection<Behavior> (); + var bindable = new MockBindable (); + collection.Add (behavior); + Assert.Null (behavior.AssociatedObject); + + ((IAttachedObject)collection).AttachTo (bindable); + Assert.AreSame (bindable, behavior.AssociatedObject); + + ((IAttachedObject)collection).DetachFrom (bindable); + Assert.Null (behavior.AssociatedObject); + } + + [Test] + public void BehaviorsAddedToAttachedCollectionAreAttached () + { + var behavior = new MockBehavior<MockBindable> (); + var collection = new AttachedCollection<Behavior> (); + var bindable = new MockBindable (); + ((IAttachedObject)collection).AttachTo (bindable); + Assert.Null (behavior.AssociatedObject); + + collection.Add (behavior); + Assert.AreSame (bindable, behavior.AssociatedObject); + + collection.Remove (behavior); + Assert.Null (behavior.AssociatedObject); + } + + [Test] + public void TestBehaviorsAttachedDP () + { + var behavior = new MockBehavior<MockBindable> (); + var bindable = new MockBindable (); + var collection = bindable.Behaviors; + Assert.Null (behavior.AssociatedObject); + + collection.Add (behavior); + Assert.AreSame (bindable, behavior.AssociatedObject); + + collection.Remove (behavior); + Assert.Null (behavior.AssociatedObject); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/BindableObjectExtensionTests.cs b/Xamarin.Forms.Core.UnitTests/BindableObjectExtensionTests.cs new file mode 100644 index 00000000..06a24c7f --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BindableObjectExtensionTests.cs @@ -0,0 +1,72 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class BindableObjectExtensionTests : BaseTestFixture + { + [Test] + public void SetBindingNull() + { + Assert.That (() => BindableObjectExtensions.SetBinding (null, MockBindable.TextProperty, "Name"), + Throws.InstanceOf<ArgumentNullException>()); + Assert.That (() => BindableObjectExtensions.SetBinding (new MockBindable(), null, "Name"), + Throws.InstanceOf<ArgumentNullException>()); + Assert.That (() => BindableObjectExtensions.SetBinding (new MockBindable(), MockBindable.TextProperty, null), + Throws.InstanceOf<ArgumentNullException>()); + + Assert.That (() => BindableObjectExtensions.SetBinding<MockViewModel> (null, MockBindable.TextProperty, vm => vm.Text), + Throws.InstanceOf<ArgumentNullException>()); + Assert.That (() => BindableObjectExtensions.SetBinding<MockViewModel> (new MockBindable(), null, vm => vm.Text), + Throws.InstanceOf<ArgumentNullException>()); + Assert.That (() => BindableObjectExtensions.SetBinding<MockViewModel> (new MockBindable(), MockBindable.TextProperty, null), + Throws.InstanceOf<ArgumentNullException>()); + } + + [Test] + public void Issue2643() + { + Label labelTempoDiStampa = new Label(); + labelTempoDiStampa.BindingContext = new { Name = "1", Company = "Xamarin" }; + labelTempoDiStampa.SetBinding (Label.TextProperty, "Name", stringFormat: "Hi: {0}"); + + Assert.That (labelTempoDiStampa.Text, Is.EqualTo ("Hi: 1")); + } + + class Bz27229ViewModel + { + public object Member { get; private set; } + public Bz27229ViewModel () + { + Member = new Generic<Label> (new Label {Text="foo"}); + } + } + + class Generic<TResult> + { + public Generic (TResult result) + { + Result = result; + } + + public TResult Result { get; set; } + } + + [Test] + public void Bz27229 () + { + var totalCheckTime = new TextCell { Text = "Total Check Time" }; + totalCheckTime.BindingContext = new Bz27229ViewModel (); + totalCheckTime.SetBinding(TextCell.DetailProperty,"Member.Result.Text"); + Assert.AreEqual ("foo", totalCheckTime.Detail); + + totalCheckTime = new TextCell { Text = "Total Check Time" }; + totalCheckTime.BindingContext = new Bz27229ViewModel (); + totalCheckTime.SetBinding<Bz27229ViewModel>(TextCell.DetailProperty, vm => + ((Generic<Label>)vm.Member).Result.Text); + + Assert.AreEqual ("foo", totalCheckTime.Detail); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/BindableObjectUnitTests.cs b/Xamarin.Forms.Core.UnitTests/BindableObjectUnitTests.cs new file mode 100644 index 00000000..7c4bac7a --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BindableObjectUnitTests.cs @@ -0,0 +1,1326 @@ +using System; +using System.Globalization; +using NUnit.Framework; + +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] + [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, + BindableObject.SetValueFlags.ClearOneWayBindings | BindableObject.SetValueFlags.ClearDynamicResource | BindableObject.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 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] + 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); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/BindablePropertyUnitTests.cs b/Xamarin.Forms.Core.UnitTests/BindablePropertyUnitTests.cs new file mode 100644 index 00000000..1d006a6d --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BindablePropertyUnitTests.cs @@ -0,0 +1,135 @@ +using System; +using System.Linq; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class BindablePropertyUnitTests : BaseTestFixture + { + [Test] + public void Create() + { + const BindingMode mode = BindingMode.OneWayToSource; + const string dvalue = "default"; + BindableProperty.CoerceValueDelegate<string> coerce = (bindable, value) => value; + BindableProperty.ValidateValueDelegate<string> validate = (b, v) => true; + BindableProperty.BindingPropertyChangedDelegate<string> changed = (b, ov, nv) => { }; + BindableProperty.BindingPropertyChangingDelegate<string> changing = (b, ov, nv) => { }; + + var prop = BindableProperty.Create<Button, string> (b => b.Text, dvalue, mode, validate, changed, changing, coerce); + Assert.AreEqual ("Text", prop.PropertyName); + Assert.AreEqual (typeof (Button), prop.DeclaringType); + Assert.AreEqual (typeof (string), prop.ReturnType); + Assert.AreEqual (dvalue, prop.DefaultValue); + Assert.AreEqual (mode, prop.DefaultBindingMode); + } + + [Test] + public void CreateWithDefaultMode () + { + const BindingMode mode = BindingMode.Default; + var prop = BindableProperty.Create<Button, string> (b => b.Text, null, defaultBindingMode: mode); + Assert.AreEqual (BindingMode.OneWay, prop.DefaultBindingMode); + } + + [Test] + public void CreateCasted() + { + var prop = BindableProperty.Create<Cell, bool> (c => c.IsEnabled, true); + + Assert.AreEqual ("IsEnabled", prop.PropertyName); + Assert.AreEqual (typeof (Cell), prop.DeclaringType); + Assert.AreEqual (typeof (bool), prop.ReturnType); + } + + [Test] + public void CreateNonGeneric() + { + const BindingMode mode = BindingMode.OneWayToSource; + const string dvalue = "default"; + BindableProperty.CoerceValueDelegate coerce = (bindable, value) => value; + BindableProperty.ValidateValueDelegate validate = (b, v) => true; + BindableProperty.BindingPropertyChangedDelegate changed = (b, ov, nv) => { }; + BindableProperty.BindingPropertyChangingDelegate changing = (b, ov, nv) => { }; + + var prop = BindableProperty.Create ("Text", typeof(string), typeof(Button), dvalue, mode, validate, changed, changing, coerce); + Assert.AreEqual ("Text", prop.PropertyName); + Assert.AreEqual (typeof (Button), prop.DeclaringType); + Assert.AreEqual (typeof (string), prop.ReturnType); + Assert.AreEqual (dvalue, prop.DefaultValue); + Assert.AreEqual (mode, prop.DefaultBindingMode); + } + + class GenericView<T> : View + { + public string Text + { + get; + set; + } + } + + [Test] + public void CreateForGeneric() + { + const BindingMode mode = BindingMode.OneWayToSource; + const string dvalue = "default"; + BindableProperty.CoerceValueDelegate coerce = (bindable, value) => value; + BindableProperty.ValidateValueDelegate validate = (b, v) => true; + BindableProperty.BindingPropertyChangedDelegate changed = (b, ov, nv) => { }; + BindableProperty.BindingPropertyChangingDelegate changing = (b, ov, nv) => { }; + + var prop = BindableProperty.Create ("Text", typeof(string), typeof(GenericView<>), dvalue, mode, validate, changed, changing, coerce); + Assert.AreEqual ("Text", prop.PropertyName); + Assert.AreEqual (typeof (GenericView<>), prop.DeclaringType); + Assert.AreEqual (typeof (string), prop.ReturnType); + Assert.AreEqual (dvalue, prop.DefaultValue); + Assert.AreEqual (mode, prop.DefaultBindingMode); + } + + [Test] + public void ChangingBeforeChanged () + { + bool changingfired = false; + bool changedfired = false; + BindableProperty.BindingPropertyChangedDelegate<string> changed = (b, ov, nv) => { + Assert.True (changingfired); + changedfired = true; + }; + BindableProperty.BindingPropertyChangingDelegate<string> changing = (b, ov, nv) => { + Assert.False (changedfired); + changingfired = true; + }; + + var prop = BindableProperty.Create<Button, string> (b => b.Text, "Foo", + propertyChanging: changing, + propertyChanged: changed); + + Assert.False (changingfired); + Assert.False (changedfired); + + (new View ()).SetValue (prop, "Bar"); + + Assert.True (changingfired); + Assert.True (changedfired); + } + + [Test] + public void NullableProperty () + { + var prop = BindableProperty.Create ("foo", typeof(DateTime?), typeof(MockBindable), null); + Assert.AreEqual (typeof(DateTime?), prop.ReturnType); + + var bindable = new MockBindable (); + Assert.AreEqual (null, bindable.GetValue (prop)); + + var now = DateTime.Now; + bindable.SetValue (prop, now); + Assert.AreEqual (now, bindable.GetValue (prop)); + + bindable.SetValue (prop, null); + Assert.AreEqual (null, bindable.GetValue (prop)); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/BindingBaseUnitTests.cs b/Xamarin.Forms.Core.UnitTests/BindingBaseUnitTests.cs new file mode 100644 index 00000000..dd0d2244 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BindingBaseUnitTests.cs @@ -0,0 +1,227 @@ +using System; +using System.Linq; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + public abstract class BindingBaseUnitTests : BaseTestFixture + { + protected abstract BindingBase CreateBinding (BindingMode mode, string stringFormat = null); + + [Test] + public void CloneMode() + { + var binding = CreateBinding (BindingMode.Default); + var clone = binding.Clone(); + + Assert.AreEqual (binding.Mode, clone.Mode); + } + + [Test] + public void StringFormat() + { + var property = BindableProperty.Create<MockBindable, string> (w => w.Foo, null); + + var binding = CreateBinding (BindingMode.Default, "Foo {0}"); + + var vm = new MockViewModel { Text = "Bar" }; + var bo = new MockBindable { BindingContext = vm }; + bo.SetBinding (property, binding); + + Assert.That (bo.GetValue (property), Is.EqualTo ("Foo Bar")); + } + + [Test] + public void StringFormatOnUpdate() + { + var property = BindableProperty.Create<MockBindable, string> (w => w.Foo, null); + + var binding = CreateBinding (BindingMode.Default, "Foo {0}"); + + var vm = new MockViewModel { Text = "Bar" }; + var bo = new MockBindable { BindingContext = vm }; + bo.SetBinding (property, binding); + + vm.Text = "Baz"; + + Assert.That (bo.GetValue (property), Is.EqualTo ("Foo Baz")); + } + + [Test] + [Description ("StringFormat should not be applied to OneWayToSource bindings")] + public void StringFormatOneWayToSource() + { + var property = BindableProperty.Create<MockBindable, string> (w => w.Foo, null); + + var binding = CreateBinding (BindingMode.OneWayToSource, "Foo {0}"); + + var vm = new MockViewModel { Text = "Bar" }; + var bo = new MockBindable { BindingContext = vm }; + bo.SetBinding (property, binding); + + bo.SetValue (property, "Bar"); + + Assert.That (vm.Text, Is.EqualTo ("Bar")); + } + + [Test] + [Description ("StringFormat should only be applied from from source in TwoWay bindings")] + public void StringFormatTwoWay() + { + var property = BindableProperty.Create<MockBindable, string> (w => w.Foo, null); + + var binding = CreateBinding (BindingMode.TwoWay, "Foo {0}"); + + var vm = new MockViewModel { Text = "Bar" }; + var bo = new MockBindable { BindingContext = vm }; + bo.SetBinding (property, binding); + + bo.SetValue (property, "Baz"); + + Assert.That (vm.Text, Is.EqualTo ("Baz")); + Assert.That (bo.GetValue (property), Is.EqualTo ("Foo Baz")); + } + + [Test] + [Description ("You should get an exception when trying to change a binding after it's been applied")] + public void ChangeAfterApply() + { + var property = BindableProperty.Create<MockBindable, string> (w => w.Foo, null); + + var binding = CreateBinding (BindingMode.OneWay); + + var vm = new MockViewModel { Text = "Bar" }; + var bo = new MockBindable { BindingContext = vm }; + bo.SetBinding (property, binding); + + Assert.That (() => binding.Mode = BindingMode.OneWayToSource, Throws.InvalidOperationException); + Assert.That (() => binding.StringFormat = "{0}", Throws.InvalidOperationException); + } + } + + [TestFixture] + public class BindingBaseTests : BaseTestFixture + { + [Test] + public void EnableCollectionSynchronizationInvalid() + { + Assert.That (() => BindingBase.EnableCollectionSynchronization (null, new object(), + (collection, context, method, access) => { }), Throws.InstanceOf<ArgumentNullException>()); + Assert.That (() => BindingBase.EnableCollectionSynchronization (new string[0], new object(), + null), Throws.InstanceOf<ArgumentNullException>()); + Assert.That (() => BindingBase.EnableCollectionSynchronization (new string[0], null, + (collection, context, method, access) => { }), Throws.Nothing); + } + + [Test] + public void EnableCollectionSynchronization() + { + string[] stuff = new[] {"foo", "bar"}; + object context = new object(); + CollectionSynchronizationCallback callback = (collection, o, method, access) => { }; + + BindingBase.EnableCollectionSynchronization (stuff, context, callback); + + CollectionSynchronizationContext syncContext; + Assert.IsTrue (BindingBase.TryGetSynchronizedCollection (stuff, out syncContext)); + Assert.That (syncContext, Is.Not.Null); + Assert.AreSame (syncContext.Callback, callback); + Assert.That (syncContext.ContextReference, Is.Not.Null); + Assert.That (syncContext.ContextReference.Target, Is.SameAs (context)); + } + + [Test] + public void DisableCollectionSynchronization() + { + string[] stuff = new[] {"foo", "bar"}; + object context = new object(); + CollectionSynchronizationCallback callback = (collection, o, method, access) => { }; + + BindingBase.EnableCollectionSynchronization (stuff, context, callback); + + BindingBase.DisableCollectionSynchronization (stuff); + + CollectionSynchronizationContext syncContext; + Assert.IsFalse (BindingBase.TryGetSynchronizedCollection (stuff, out syncContext)); + Assert.IsNull (syncContext); + } + + [Test] + public void CollectionAndContextAreHeldWeakly() + { + WeakReference weakCollection = null, weakContext = null; + + int i = 0; + Action create = null; + create = () => { + if (i++ < 1024) { + create(); + return; + } + + string[] collection = new[] {"foo", "bar"}; + weakCollection = new WeakReference (collection); + + object context = new object(); + weakContext = new WeakReference (context); + + BindingBase.EnableCollectionSynchronization (collection, context, (enumerable, o, method, access) => { }); + }; + + create(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + Assert.IsFalse (weakCollection.IsAlive); + Assert.IsFalse (weakContext.IsAlive); + } + + [Test] + public void CollectionAndContextAreHeldWeaklyClosingOverCollection() + { + WeakReference weakCollection = null, weakContext = null; + + int i = 0; + Action create = null; + create = () => { + if (i++ < 1024) { + create(); + return; + } + + string[] collection = new[] {"foo", "bar"}; + weakCollection = new WeakReference (collection); + + object context = new object(); + weakContext = new WeakReference (context); + + BindingBase.EnableCollectionSynchronization (collection, context, (enumerable, o, method, access) => { + collection[0] = "baz"; + }); + }; + + create(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + Assert.IsFalse (weakCollection.IsAlive); + Assert.IsFalse (weakContext.IsAlive); + } + + [Test] + public void DisableCollectionSynchronizationInvalid() + { + Assert.That (() => BindingBase.DisableCollectionSynchronization (null), Throws.InstanceOf<ArgumentNullException>()); + } + + [Test] + public void TryGetSynchronizedCollectionInvalid() + { + CollectionSynchronizationContext context; + Assert.That (() => BindingBase.TryGetSynchronizedCollection (null, out context), + Throws.InstanceOf<ArgumentNullException>()); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/BindingExpressionTests.cs b/Xamarin.Forms.Core.UnitTests/BindingExpressionTests.cs new file mode 100644 index 00000000..b3f0198d --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BindingExpressionTests.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class BindingExpressionTests : BaseTestFixture + { + [Test] + public void Ctor() + { + string path = "Foo.Bar"; + var binding = new Binding (path); + + var be = new BindingExpression (binding, path); + + Assert.AreSame (binding, be.Binding); + Assert.AreEqual (path, be.Path); + } + + [Test] + public void CtorInvalid() + { + string path = "Foo.Bar"; + var binding = new Binding (path); + + Assert.Throws<ArgumentNullException> (() => new BindingExpression (binding, null), + "Allowed the path to eb null"); + + Assert.Throws<ArgumentNullException> (() => new BindingExpression (null, path), + "Allowed the binding to be null"); + } + + [Test] + public void ApplyNull() + { + const string path = "Foo.Bar"; + var binding = new Binding (path); + var be = new BindingExpression (binding, path); + Assert.DoesNotThrow (() => be.Apply (null, new MockBindable(), TextCell.TextProperty)); + } + + // We only throw on invalid path features, if they give an invalid property + // name, it won't have compiled in the first place or they misstyped. + [TestCase ("Foo.")] + [TestCase ("Foo[]")] + [TestCase ("Foo.Bar[]")] + [TestCase ("Foo[1")] + public void InvalidPaths (string path) + { + var fex = Assert.Throws<FormatException> (() => { + var binding = new Binding (path); + new BindingExpression (binding, path); + }); + + Assert.IsFalse (String.IsNullOrWhiteSpace (fex.Message), + "FormatException did not contain an explanation"); + } + + [Test] + public void ValidPaths ( + [Values ( + ".", "[1]", "[1 ]", ".[1]", ". [1]", + "Foo", "Foo.Bar", "Foo. Bar", "Foo.Bar[1]", + "Foo.Bar [1]")] + string path, + [Values (true, false)] bool spaceBefore, + [Values (true, false)] bool spaceAfter) + { + if (spaceBefore) + path = " " + path; + if (spaceAfter) + path = path + " "; + + var binding = new Binding (path); + Assert.DoesNotThrow (() => new BindingExpression (binding, path)); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/BindingTests.cs b/Xamarin.Forms.Core.UnitTests/BindingTests.cs new file mode 100644 index 00000000..d586a6dd --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BindingTests.cs @@ -0,0 +1,75 @@ +using NUnit.Framework; +using System.Collections.Generic; +using System; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + internal class BindingSystemTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + class BindableViewCell : ViewCell + { + public static readonly BindableProperty NameProperty = + BindableProperty.Create<BindableViewCell, string> (w => w.Name, ""); + + public Label NameLabel { get; set; } + + public string Name + { + get { return (string) GetValue (NameProperty); } + set { SetValue (NameProperty, value); } + } + + public BindableViewCell () + { + NameLabel = new Label {BindingContext = this}; + NameLabel.SetBinding (Label.TextProperty, new Binding ("Name")); + View = NameLabel; + } + } + + [Test] + public void RecursiveSettingInSystem () + { + var tempObjects = new[] { + new {Name = "Test1"}, + new {Name = "Test2"} + }; + + var template = new DataTemplate (typeof (BindableViewCell)) { + Bindings = { {BindableViewCell.NameProperty, new Binding ("Name")} } + }; + + var cell1 = (Cell)template.CreateContent (); + cell1.BindingContext = tempObjects[0]; + cell1.Parent = new ListView (); + + var cell2 = (Cell)template.CreateContent (); + cell2.BindingContext = tempObjects[1]; + cell2.Parent = new ListView (); + + var viewCell1 = (BindableViewCell) cell1; + var viewCell2 = (BindableViewCell) cell2; + + Assert.AreEqual ("Test1", viewCell1.Name); + Assert.AreEqual ("Test2", viewCell2.Name); + + Assert.AreEqual ("Test1", viewCell1.NameLabel.Text); + Assert.AreEqual ("Test2", viewCell2.NameLabel.Text); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/BindingTypeConverterTests.cs b/Xamarin.Forms.Core.UnitTests/BindingTypeConverterTests.cs new file mode 100644 index 00000000..e6d97d24 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BindingTypeConverterTests.cs @@ -0,0 +1,27 @@ +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class BindingTypeConverterTests : BaseTestFixture + { + [Test] + public void CanConvertFrom() + { + var c = new BindingTypeConverter(); + Assert.That (c.CanConvertFrom (typeof (string)), Is.True); + Assert.That (c.CanConvertFrom (typeof (int)), Is.False); + } + + [Test] + public void Convert() + { + var c = new BindingTypeConverter(); + var binding = c.ConvertFromInvariantString ("Path"); + + Assert.That (binding, Is.InstanceOf<Binding>()); + Assert.That (((Binding) binding).Path, Is.EqualTo ("Path")); + Assert.That (((Binding) binding).Mode, Is.EqualTo (BindingMode.Default)); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/BindingUnitTests.cs b/Xamarin.Forms.Core.UnitTests/BindingUnitTests.cs new file mode 100644 index 00000000..c520e929 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BindingUnitTests.cs @@ -0,0 +1,2649 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using NUnit.Framework; +using CategoryAttribute=NUnit.Framework.CategoryAttribute; +using DescriptionAttribute=NUnit.Framework.DescriptionAttribute; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class BindingUnitTests + : BindingBaseUnitTests + { + class Logger + : LogListener + { + public IReadOnlyList<string> Messages + { + get { return messages; } + } + + public override void Warning (string category, string message) + { + messages.Add ("[" + category + "] " + message); + } + + readonly List<string> messages = new List<string>(); + } + + Logger log; + + [SetUp] + public override void Setup() + { + base.Setup (); + log = new Logger(); + + Device.PlatformServices = new MockPlatformServices (); + Log.Listeners.Add (log); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + Log.Listeners.Remove (log); + } + + protected override BindingBase CreateBinding (BindingMode mode, string stringFormat = null) + { + return new Binding ("Text", mode, stringFormat: stringFormat); + } + + [Test] + public void Ctor() + { + const string path = "Foo"; + + var binding = new Binding (path, BindingMode.OneWayToSource); + Assert.AreEqual (path, binding.Path); + Assert.AreEqual (BindingMode.OneWayToSource, binding.Mode); + } + + [Test] + public void InvalidCtor() + { + Assert.Throws<ArgumentNullException> (() => new Binding (null), + "Allowed null Path"); + + Assert.Throws<ArgumentException> (() => new Binding (String.Empty), + "Allowed empty path"); + + Assert.Throws<ArgumentException> (() => new Binding (" "), + "Allowed whitespace path"); + + Assert.Throws<ArgumentException> (() => new Binding ("Path", (BindingMode)Int32.MaxValue), + "Allowed invalid value for BindingMode"); + } + + [Test] + [Description ("You should get an exception when trying to change a binding after it's been applied")] + public void ChangeBindingAfterApply() + { + var property = BindableProperty.Create<MockBindable, string> (w => w.Foo, null); + + var binding = new Binding { Path = "Text" }; + + var vm = new MockViewModel { Text = "Bar" }; + var bo = new MockBindable { BindingContext = vm }; + bo.SetBinding (property, binding); + + Assert.That (() => binding.Path = "path", Throws.InvalidOperationException); + Assert.That (() => binding.Converter = null, Throws.InvalidOperationException); + Assert.That (() => binding.ConverterParameter = new object(), Throws.InvalidOperationException); + } + + [Test] + public void NullPathIsSelf() + { + var property = BindableProperty.Create<MockBindable, string> (w => w.Foo, null); + + var binding = new Binding(); + + var bo = new MockBindable { BindingContext = "Foo" }; + bo.SetBinding (property, binding); + + Assert.That (bo.GetValue (property), Is.EqualTo ("Foo")); + } + + class DoubleViewModel + : MockViewModel + { + public double Value + { + get; + set; + } + } + + [Test] + public void StringFormatNonStringType() + { + var property = BindableProperty.Create<MockBindable, string> (w => w.Foo, null); + + var binding = new Binding ("Value", stringFormat: "{0:P2}"); + + var vm = new DoubleViewModel { Value = 0.95 }; + var bo = new MockBindable { BindingContext = vm }; + bo.SetBinding (property, binding); + + if (System.Threading.Thread.CurrentThread.CurrentCulture.Name == "tr-TR") + Assert.That (bo.GetValue (property), Is.EqualTo ("%95,00")); + else + Assert.That (bo.GetValue (property), Is.EqualTo ("95.00 %")); + } + + [Test] + public void ReuseBindingInstance() + { + var vm = new MockViewModel(); + + var bindable = new MockBindable(); + bindable.BindingContext = vm; + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, null); + var binding = new Binding ("Text"); + bindable.SetBinding (property, binding); + + var bindable2 = new MockBindable(); + bindable2.BindingContext = new MockViewModel(); + Assert.Throws<InvalidOperationException> (() => bindable2.SetBinding (property, binding), + "Binding allowed reapplication with a different context"); + } + + class ComplexPropertyNamesViewModel + : MockViewModel + { + public string Foo_Bar + { + get; + set; + } + + public string @if + { + get; + set; + } + + /*public string P̀ः०‿ + { + get; + set; + }*/ + + public string _UnderscoreStart + { + get; + set; + } + } + + [Category ("[Binding] Complex paths")] + [TestCase ("Foo_Bar")] + [TestCase ("if")] + //TODO FIXME [TestCase ("P̀ः०‿")] + [TestCase ("_UnderscoreStart")] + public void ComplexPropertyNames (string propertyName) + { + var vm = new ComplexPropertyNamesViewModel(); + vm.GetType().GetProperty (propertyName).SetValue (vm, "Value"); + + var binding = new Binding (propertyName); + + var bindable = new MockBindable { BindingContext = vm }; + bindable.SetBinding (MockBindable.TextProperty, binding); + + Assert.That (bindable.Text, Is.EqualTo ("Value")); + } + + [Test, Category ("[Binding] Simple paths")] + public void ValueSetOnOneWayWithSimplePathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new MockViewModel { + Text = value + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, null, propertyDefault); + + var binding = new Binding ("Text", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel.Text, + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Simple paths")] + public void ValueSetOnOneWayToSourceWithSimplePathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new MockViewModel(); + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWayToSource; + if (isDefault) { + propertyDefault = BindingMode.OneWayToSource; + bindingMode = BindingMode.Default; + } + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, + defaultValue: value, defaultBindingMode: propertyDefault); + + var binding = new Binding ("Text", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, bindable.GetValue (property), + "Target property changed"); + Assert.AreEqual (value, viewmodel.Text, + "BindingContext property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Simple paths")] + public void ValueSetOnTwoWayWithSimplePathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new MockViewModel { + Text = value + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding ("Text", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel.Text, + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test] + [Category ("[Binding] Complex paths")] + public void ValueSetOnOneWayWithComplexPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Text = value + } + } + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, null, propertyDefault); + + var binding = new Binding ("Model.Model.Text", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel.Model.Model.Text, + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Complex paths")] + public void ValueSetOnOneWayToSourceWithComplexPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + } + } + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWayToSource; + if (isDefault) { + propertyDefault = BindingMode.OneWayToSource; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, + defaultValue: value, defaultBindingMode: propertyDefault); + + var binding = new Binding ("Model.Model.Text", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, bindable.GetValue (property), + "Target property changed"); + Assert.AreEqual (value, viewmodel.Model.Model.Text, + "BindingContext property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Complex paths")] + public void ValueSetOnTwoWayWithComplexPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Text = value + } + } + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding ("Model.Model.Text", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel.Model.Model.Text, + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + class Outer { + public Outer (Inner inner) + { + PropertyWithPublicSetter = inner; + PropertyWithPrivateSetter = inner; + } + + public Inner PropertyWithPublicSetter { get; set; } + public Inner PropertyWithPrivateSetter { get; private set; } + } + + class Inner { + public Inner (string property) + { + GetSetProperty = property; + } + + public string GetSetProperty { get; set; } + } + + [Test, Category ("[Binding] Complex paths")] + public void BindingWithNoPublicSetterOnParent ( + [Values (true, false)] bool setContextFirst, + [Values (BindingMode.OneWay, BindingMode.TwoWay)] BindingMode bindingmode, + [Values (true, false)] bool usePrivateSetter) + { + var value = "FooBar"; + var property = BindableProperty.Create<MockBindable, string> (w => w.Text, "default value", BindingMode.Default); + var binding = new Binding (usePrivateSetter? "PropertyWithPrivateSetter.GetSetProperty": "PropertyWithPublicSetter.GetSetProperty", bindingmode); + var viewmodel = new Outer (new Inner (value)); + var bindable = new MockBindable (); + + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel.PropertyWithPublicSetter.GetSetProperty); + Assert.AreEqual (value, bindable.GetValue (property)); + + if (bindingmode == BindingMode.TwoWay) { + var updatedValue = "Qux"; + bindable.SetValue (property, updatedValue); + Assert.AreEqual (updatedValue, bindable.GetValue (property)); + Assert.AreEqual (updatedValue, viewmodel.PropertyWithPublicSetter.GetSetProperty); + } + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Indexed paths")] + public void ValueSetOnOneWayWithIndexedPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel() + } + }; + viewmodel.Model.Model[1] = value; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, null, propertyDefault); + + var binding = new Binding ("Model.Model[1]", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel.Model.Model[1], + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Indexed paths")] + public void ValueSetOnOneWayWithSelfIndexedPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new ComplexMockViewModel(); + viewmodel[1] = value; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, null, propertyDefault); + + var binding = new Binding (".[1]", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel[1], + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Indexed paths")] + public void ValueSetOnOneWayWithIndexedPathArrayBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Bar"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Array = new [] { "Foo", "Bar" } + } + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, null, propertyDefault); + + var binding = new Binding ("Model.Array[1]", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel.Model.Array[1], + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Indexed paths")] + public void ValueSetOnOneWayWithIndexedSelfPathArrayBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "bar"; + string[] context = new[] { "foo", value }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, null, propertyDefault); + + var binding = new Binding (".[1]", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = context; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = context; + } + + Assert.AreEqual (value, context[1], + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Indexed paths")] + public void ValueSetOnOneWayToSourceWithIndexedPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel() + } + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWayToSource; + if (isDefault) { + propertyDefault = BindingMode.OneWayToSource; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, defaultValue: value, defaultBindingMode: propertyDefault); + + var binding = new Binding ("Model.Model[1]", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, bindable.GetValue (property), + "Target property changed"); + Assert.AreEqual (value, viewmodel.Model.Model[1], + "BindingContext property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Indexed paths")] + public void ValueSetOnTwoWayWithIndexedPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel() + } + }; + viewmodel.Model.Model[1] = value; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding ("Model.Model[1]", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel.Model.Model[1], + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Indexed paths")] + public void ValueSetOnTwoWayWithIndexedArrayPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Array = new string[2] + } + }; + viewmodel.Model.Array[1] = value; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding ("Model.Array[1]", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel.Model.Array[1], + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Indexed paths")] + public void ValueSetOnTwoWayWithIndexedArraySelfPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + const string value = "Foo"; + string[] viewmodel = new [] { "bar", value }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding (".[1]", bindingMode); + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = viewmodel; + } + + Assert.AreEqual (value, viewmodel[1], + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Self paths")] + public void ValueSetOnOneWayWithSelfPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, null, propertyDefault); + + var binding = new Binding (".", bindingMode); + + const string value = "value"; + + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = value; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = value; + } + + Assert.AreEqual (value, bindable.BindingContext, + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Self paths")] + public void ValueNotSetOnOneWayToSourceWithSelfPathBinding ( + [Values (true, false)] bool isDefault) + { + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWayToSource; + if (isDefault) { + propertyDefault = BindingMode.OneWayToSource; + bindingMode = BindingMode.Default; + } + + const string value = "Foo"; + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, defaultValue: value, defaultBindingMode: propertyDefault); + + var binding = new Binding (".", bindingMode); + + var bindable = new MockBindable(); + Assert.IsNull (bindable.BindingContext); + + bindable.SetBinding (property, binding); + + Assert.AreEqual (value, bindable.GetValue (property), + "Target property changed"); + Assert.IsNull (bindable.BindingContext, + "BindingContext changed with self-path binding"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Self paths")] + public void ValueSetOnTwoWayWithSelfPathBinding ( + [Values (true, false)] bool setContextFirst, + [Values (true, false)] bool isDefault) + { + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding (".", bindingMode); + + const string value = "Foo"; + var bindable = new MockBindable(); + if (setContextFirst) { + bindable.BindingContext = value; + bindable.SetBinding (property, binding); + } else { + bindable.SetBinding (property, binding); + bindable.BindingContext = value; + } + + Assert.AreEqual (value, bindable.BindingContext, + "BindingContext property changed"); + Assert.AreEqual (value, bindable.GetValue (property), + "Target property did not change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Simple paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithSimplePathOnOneWayBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new MockViewModel { + Text = "Foo" + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, new Binding ("Text", bindingMode)); + + viewmodel.Text = newvalue; + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Bindable did not update on binding context property change"); + Assert.AreEqual (newvalue, viewmodel.Text, + "Source property changed when it shouldn't"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Simple paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithSimplePathOnOneWayToSourceBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new MockViewModel { + Text = "Foo" + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWayToSource; + if (isDefault) { + propertyDefault = BindingMode.OneWayToSource; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, new Binding ("Text", bindingMode)); + + string original = (string)bindable.GetValue (property); + const string value = "value"; + viewmodel.Text = value; + Assert.AreEqual (original, bindable.GetValue (property), + "Target updated from Source on OneWayToSource"); + + bindable.SetValue (property, newvalue); + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Bindable did not update on binding context property change"); + Assert.AreEqual (newvalue, viewmodel.Text, + "Source property changed when it shouldn't"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Simple paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithSimplePathOnTwoWayBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new MockViewModel { + Text = "Foo" + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, new Binding ("Text", bindingMode)); + + viewmodel.Text = newvalue; + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Target property did not update change"); + Assert.AreEqual (newvalue, viewmodel.Text, + "Source property changed from what it was set to"); + + const string newvalue2 = "New Value in the other direction"; + + bindable.SetValue (property, newvalue2); + Assert.AreEqual (newvalue2, viewmodel.Text, + "Source property did not update with Target's change"); + Assert.AreEqual (newvalue2, bindable.GetValue (property), + "Target property changed from what it was set to"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Complex paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithComplexPathOnOneWayBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Text = "Foo" + } + } + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, new Binding ("Model.Model.Text", bindingMode)); + + viewmodel.Model.Model.Text = newvalue; + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Bindable did not update on binding context property change"); + Assert.AreEqual (newvalue, viewmodel.Model.Model.Text, + "Source property changed when it shouldn't"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Complex paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithComplexPathOnOneWayToSourceBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Text = "Foo" + } + } + };; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWayToSource; + if (isDefault) { + propertyDefault = BindingMode.OneWayToSource; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, new Binding ("Model.Model.Text", bindingMode)); + + string original = (string)bindable.GetValue (property); + const string value = "value"; + viewmodel.Model.Model.Text = value; + Assert.AreEqual (original, bindable.GetValue (property), + "Target updated from Source on OneWayToSource"); + + bindable.SetValue (property, newvalue); + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Bindable did not update on binding context property change"); + Assert.AreEqual (newvalue, viewmodel.Model.Model.Text, + "Source property changed when it shouldn't"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Complex paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithComplexPathOnTwoWayBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Text = "Foo" + } + } + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, new Binding ("Model.Model.Text", bindingMode)); + + viewmodel.Model.Model.Text = newvalue; + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Target property did not update change"); + Assert.AreEqual (newvalue, viewmodel.Model.Model.Text, + "Source property changed from what it was set to"); + + const string newvalue2 = "New Value in the other direction"; + + bindable.SetValue (property, newvalue2); + Assert.AreEqual (newvalue2, viewmodel.Model.Model.Text, + "Source property did not update with Target's change"); + Assert.AreEqual (newvalue2, bindable.GetValue (property), + "Target property changed from what it was set to"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Indexed paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithIndexedPathOnOneWayBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel() + } + }; + viewmodel.Model.Model[1] = "Foo"; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, new Binding ("Model.Model[1]", bindingMode)); + + viewmodel.Model.Model[1] = newvalue; + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Bindable did not update on binding context property change"); + Assert.AreEqual (newvalue, viewmodel.Model.Model[1], + "Source property changed when it shouldn't"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Indexed paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithIndexedPathOnOneWayToSourceBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel() + } + }; + viewmodel.Model.Model[1] = "Foo"; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWayToSource; + if (isDefault) { + propertyDefault = BindingMode.OneWayToSource; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, new Binding ("Model.Model[1]", bindingMode)); + + string original = (string)bindable.GetValue (property); + const string value = "value"; + viewmodel.Model.Model[1] = value; + Assert.AreEqual (original, bindable.GetValue (property), + "Target updated from Source on OneWayToSource"); + + bindable.SetValue (property, newvalue); + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Bindable did not update on binding context property change"); + Assert.AreEqual (newvalue, viewmodel.Model.Model[1], + "Source property changed when it shouldn't"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Indexed paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithIndexedPathOnTwoWayBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel() + } + }; + viewmodel.Model.Model[1] = "Foo"; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, new Binding ("Model.Model[1]", bindingMode)); + + viewmodel.Model.Model[1] = newvalue; + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Target property did not update change"); + Assert.AreEqual (newvalue, viewmodel.Model.Model[1], + "Source property changed from what it was set to"); + + const string newvalue2 = "New Value in the other direction"; + + bindable.SetValue (property, newvalue2); + Assert.AreEqual (newvalue2, viewmodel.Model.Model[1], + "Source property did not update with Target's change"); + Assert.AreEqual (newvalue2, bindable.GetValue (property), + "Target property changed from what it was set to"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Indexed paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithIndexedArrayPathOnTwoWayBinding (bool isDefault) + { + var viewmodel = new ComplexMockViewModel { + Array = new string[2] + }; + viewmodel.Array[1] = "Foo"; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, new Binding ("Array[1]", bindingMode)); + + const string newvalue2 = "New Value in the other direction"; + + bindable.SetValue (property, newvalue2); + Assert.AreEqual (newvalue2, viewmodel.Array[1], + "Source property did not update with Target's change"); + Assert.AreEqual (newvalue2, bindable.GetValue (property), + "Target property changed from what it was set to"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Self paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithSelfPathOnOneWayBinding (bool isDefault) + { + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + const string value = "foo"; + + var bindable = new MockBindable(); + bindable.BindingContext = value; + bindable.SetBinding (property, new Binding (".", bindingMode)); + + const string newvalue = "value"; + bindable.SetValue (property, newvalue); + Assert.AreEqual (value, bindable.BindingContext, + "Source was updated from Target on OneWay binding"); + + bindable.BindingContext = newvalue; + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Bindable did not update on binding context property change"); + Assert.AreEqual (newvalue, bindable.BindingContext, + "Source property changed when it shouldn't"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Self paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueDoesNotUpdateWithSelfPathOnOneWayToSourceBinding (bool isDefault) + { + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWayToSource; + if (isDefault) { + propertyDefault = BindingMode.OneWayToSource; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding (".", bindingMode); + + var bindable = new MockBindable(); + bindable.SetBinding (property, binding); + + const string newvalue = "new value"; + + string original = (string)bindable.GetValue (property); + bindable.BindingContext = newvalue; + Assert.AreEqual (original, bindable.GetValue (property), + "Target updated from Source on OneWayToSource with self path"); + + const string newvalue2 = "new value 2"; + bindable.SetValue (property, newvalue2); + Assert.AreEqual (newvalue2, bindable.GetValue (property), + "Target property changed on OneWayToSource with self path"); + Assert.AreEqual (newvalue, bindable.BindingContext, + "Source property changed on OneWayToSource with self path"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Category ("[Binding] Self paths")] + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithSelfPathOnTwoWayBinding (bool isDefault) + { + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding (".", bindingMode); + + var bindable = new MockBindable(); + bindable.BindingContext = "value"; + bindable.SetBinding (property, binding); + + const string newvalue = "New Value"; + bindable.BindingContext = newvalue; + Assert.AreEqual (newvalue, bindable.GetValue (property), + "Target property did not update change"); + Assert.AreEqual (newvalue, bindable.BindingContext, + "Source property changed from what it was set to"); + + const string newvalue2 = "New Value in the other direction"; + + bindable.SetValue (property, newvalue2); + Assert.AreEqual (newvalue, bindable.BindingContext, + "Self-path Source changed with Target's change"); + Assert.AreEqual (newvalue2, bindable.GetValue (property), + "Target property changed from what it was set to"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithOldContextDoesNotUpdateWithOneWayBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new MockViewModel { + Text = "Foo" + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWay; + if (isDefault) { + propertyDefault = BindingMode.OneWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding ("Text", bindingMode); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + + bindable.BindingContext = new MockViewModel(); + Assert.AreEqual (null, bindable.GetValue (property)); + + viewmodel.Text = newvalue; + Assert.AreEqual (null, bindable.GetValue (property), + "Target updated from old Source property change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithOldContextDoesNotUpdateWithTwoWayBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new MockViewModel { + Text = "Foo" + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.TwoWay; + if (isDefault) { + propertyDefault = BindingMode.TwoWay; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding ("Text", bindingMode); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + + bindable.BindingContext = new MockViewModel(); + Assert.AreEqual (null, bindable.GetValue (property)); + + viewmodel.Text = newvalue; + Assert.AreEqual (null, bindable.GetValue (property), + "Target updated from old Source property change"); + + string original = viewmodel.Text; + + bindable.SetValue (property, newvalue); + Assert.AreEqual (original, viewmodel.Text, + "Source updated from old Target property change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [TestCase (true)] + [TestCase (false)] + public void ValueUpdatedWithOldContextDoesNotUpdateWithOneWayToSourceBinding (bool isDefault) + { + const string newvalue = "New Value"; + var viewmodel = new MockViewModel { + Text = "Foo" + }; + + BindingMode propertyDefault = BindingMode.OneWay; + BindingMode bindingMode = BindingMode.OneWayToSource; + if (isDefault) { + propertyDefault = BindingMode.OneWayToSource; + bindingMode = BindingMode.Default; + } + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", propertyDefault); + + var binding = new Binding ("Text", bindingMode); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + + bindable.BindingContext = new MockViewModel(); + Assert.AreEqual (property.DefaultValue, bindable.GetValue (property)); + + viewmodel.Text = newvalue; + Assert.AreEqual (property.DefaultValue, bindable.GetValue (property), + "Target updated from old Source property change"); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test] + public void BindingStaysOnUpdateValueFromBinding() + { + const string newvalue = "New Value"; + var viewmodel = new MockViewModel { + Text = "Foo" + }; + + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, null); + + var binding = new Binding ("Text"); + + var bindable = new MockBindable(); + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + + viewmodel.Text = newvalue; + Assert.AreEqual (newvalue, bindable.GetValue (property)); + + const string newValue2 = "new value 2"; + viewmodel.Text = newValue2; + Assert.AreEqual (newValue2, bindable.GetValue (property)); + + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test] + public void OneWayToSourceContextSetToNull() + { + var binding = new Binding ("Text", BindingMode.OneWayToSource); + + MockBindable bindable = new MockBindable { + BindingContext = new MockViewModel() + }; + bindable.SetBinding (MockBindable.TextProperty, binding); + + Assert.That (() => bindable.BindingContext = null, Throws.Nothing); + } + + [Category ("[Binding] Simple paths")] + [TestCase (BindingMode.OneWay)] + [TestCase (BindingMode.OneWayToSource)] + [TestCase (BindingMode.TwoWay)] + public void SourceAndTargetAreWeakWeakSimplePath (BindingMode mode) + { + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value", BindingMode.OneWay); + + var binding = new Binding ("Text", mode); + + WeakReference weakViewModel = null, weakBindable = null; + + int i = 0; + Action create = null; + create = () => { + if (i++ < 1024) { + create(); + return; + } + + MockBindable bindable = new MockBindable(); + weakBindable = new WeakReference (bindable); + + MockViewModel viewmodel = new MockViewModel(); + weakViewModel = new WeakReference (viewmodel); + + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + + Assume.That (() => bindable.BindingContext = null, Throws.Nothing); + }; + + create(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + if (mode == BindingMode.TwoWay || mode == BindingMode.OneWay) + Assert.IsFalse (weakViewModel.IsAlive, "ViewModel wasn't collected"); + + if (mode == BindingMode.TwoWay || mode == BindingMode.OneWayToSource) + Assert.IsFalse (weakBindable.IsAlive, "Bindable wasn't collected"); + } + + internal class ComplexMockViewModel + : MockViewModel + { + public ComplexMockViewModel Model + { + get { return model; } + set + { + if (model == value) + return; + + model = value; + OnPropertyChanged ("Model"); + } + } + + internal int count; + public int QueryCount + { + get { return count++; } + } + + [IndexerName ("Indexer")] + public string this [int v] + { + get { return values[v]; } + set + { + if (values[v] == value) + return; + + values[v] = value; + OnPropertyChanged ("Indexer[" + v + "]"); + } + } + + public string[] Array + { + get; + set; + } + + public object DoStuff() + { + return null; + } + + public object DoStuff (object argument) + { + return null; + } + + string[] values = new string[5]; + ComplexMockViewModel model; + } + + [Category ("[Binding] Complex paths")] + [TestCase (BindingMode.OneWay)] + [TestCase (BindingMode.OneWayToSource)] + [TestCase (BindingMode.TwoWay)] + public void SourceAndTargetAreWeakComplexPath (BindingMode mode) + { + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "default value"); + + var binding = new Binding ("Model.Model[1]"); + + WeakReference weakViewModel = null, weakBindable = null; + + HackAroundMonoSucking (0, property, binding, out weakViewModel, out weakBindable); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + if (mode == BindingMode.TwoWay || mode == BindingMode.OneWay) + Assert.IsFalse (weakViewModel.IsAlive, "ViewModel wasn't collected"); + + if (mode == BindingMode.TwoWay || mode == BindingMode.OneWayToSource) + Assert.IsFalse (weakBindable.IsAlive, "Bindable wasn't collected"); + } + + // Mono doesn't handle the GC properly until the stack frame where the object is created is popped. + // This means calling another method and not just using lambda as works in real .NET + void HackAroundMonoSucking (int i, BindableProperty property, Binding binding, out WeakReference weakViewModel, out WeakReference weakBindable) + { + if (i++ < 1024) { + HackAroundMonoSucking (i, property, binding, out weakViewModel, out weakBindable); + return; + } + + MockBindable bindable = new MockBindable(); + + weakBindable = new WeakReference (bindable); + + ComplexMockViewModel viewmodel = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Model = new ComplexMockViewModel() + } + }; + + weakViewModel = new WeakReference (viewmodel); + + bindable.BindingContext = viewmodel; + bindable.SetBinding (property, binding); + + bindable.BindingContext = null; + } + + class TestConverter<TSource,TTarget> + : IValueConverter + { + public object Convert (object value, Type targetType, object parameter, CultureInfo culture) + { + Assert.AreEqual (typeof (TTarget), targetType); + return System.Convert.ChangeType (value, targetType, CultureInfo.CurrentUICulture); + } + + public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture) + { + Assert.AreEqual (typeof (TSource), targetType); + return System.Convert.ChangeType (value, targetType, CultureInfo.CurrentUICulture); + } + } + + [Test] + public void ValueConverter() + { + var converter = new TestConverter<string, int>(); + + var vm = new MockViewModel { Text = "1" }; + var property = BindableProperty.Create<MockBindable, int> (w=>w.TargetInt, 0); + + var bindable = new MockBindable(); + bindable.SetBinding (property, new Binding ("Text", converter: converter)); + bindable.BindingContext = vm; + + Assert.AreEqual (1, bindable.GetValue (property)); + + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test] + public void ValueConverterBack() + { + var converter = new TestConverter<string, int>(); + + var vm = new MockViewModel(); + var property = BindableProperty.Create<MockBindable, int> (w=>w.TargetInt, 1, BindingMode.OneWayToSource); + var bindable = new MockBindable(); + bindable.SetBinding (property, new Binding ("Text", converter: converter)); + bindable.BindingContext = vm; + + Assert.AreEqual ("1", vm.Text); + + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + + class TestConverterParameter : IValueConverter + { + public object Convert (object value, Type targetType, object parameter, CultureInfo culture) + { + return parameter; + } + + public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture) + { + return parameter; + } + } + + [Test] + public void ValueConverterParameter () + { + var converter = new TestConverterParameter (); + + var vm = new MockViewModel (); + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "Bar", BindingMode.OneWayToSource); + var bindable = new MockBindable(); + bindable.SetBinding (property, new Binding ("Text", converter: converter, converterParameter: "Foo")); + bindable.BindingContext = vm; + + Assert.AreEqual ("Foo", vm.Text); + + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + class TestConverterCulture : IValueConverter + { + public object Convert (object value, Type targetType, object parameter, CultureInfo culture) + { + return culture.ToString (); + } + + public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture) + { + return culture.ToString (); + } + } + + #if !WINDOWS_PHONE + [Test] + [SetUICulture ("pt-PT")] + public void ValueConverterCulture () + { + var converter = new TestConverterCulture (); + var vm = new MockViewModel (); + var property = BindableProperty.Create<MockBindable, string> (w=>w.Text, "Bar", BindingMode.OneWayToSource); + var bindable = new MockBindable(); + bindable.SetBinding (property, new Binding ("Text", converter: converter)); + bindable.BindingContext = vm; + + Assert.AreEqual ("pt-PT", vm.Text); + } + #endif + + [Test] + public void SelfBindingConverter() + { + var converter = new TestConverter<int, string> (); + + var property = BindableProperty.Create<MockBindable, string> (w => w.Text, "0"); + var bindable = new MockBindable (); + bindable.BindingContext = 1; + bindable.SetBinding (property, new Binding (Binding.SelfPath, converter:converter)); + Assert.AreEqual ("1", bindable.GetValue (property)); + + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + internal class MultiplePropertyViewModel + : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + int done; + public int Done + { + get { return done; } + set + { + done = value; + OnPropertyChanged(); + OnPropertyChanged ("Progress"); + } + } + + int total = 100; + public int Total + { + get { return total; } + set + { + if (total == value) + return; + + total = value; + OnPropertyChanged(); + OnPropertyChanged ("Progress"); + } + } + + public float Progress + { + get { return (float)done / total; } + } + + protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + handler (this, new PropertyChangedEventArgs (propertyName)); + } + } + + internal class MultiplePropertyBindable + : BindableObject + { + public static readonly BindableProperty ValueProperty = + BindableProperty.Create<MultiplePropertyBindable, float> (b => b.Value, 0f); + + public float Value + { + get { return (float)GetValue (ValueProperty); } + set { SetValue (ValueProperty, value); } + } + + public static readonly BindableProperty DoneProperty = + BindableProperty.Create<MultiplePropertyBindable, int> (b => b.Done, 0); + + public int Done + { + get { return (int)GetValue (DoneProperty); } + set { SetValue (DoneProperty, value); } + } + } + + [Test] + public void MultiplePropertyUpdates() + { + var mpvm = new MultiplePropertyViewModel(); + + var bindable = new MultiplePropertyBindable(); + bindable.SetBinding (MultiplePropertyBindable.ValueProperty, new Binding ("Progress", BindingMode.OneWay)); + bindable.SetBinding (MultiplePropertyBindable.DoneProperty, new Binding ("Done", BindingMode.OneWayToSource)); + bindable.BindingContext = mpvm; + + bindable.Done = 5; + + Assert.AreEqual (5, mpvm.Done); + Assert.AreEqual (0.05f, mpvm.Progress); + Assert.AreEqual (5, bindable.Done); + Assert.AreEqual (0.05f, bindable.Value); + + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Complex paths")] + [Description ("When part of a complex path can not be evaluated during an update, bindables should return to their default value.")] + public void NullInPathUsesDefaultValue() + { + var vm = new ComplexMockViewModel { + Model = new ComplexMockViewModel() + }; + + var property = BindableProperty.Create<MockBindable, string> (w => w.Text, "foo bar"); + + var bindable = new MockBindable(); + bindable.SetBinding (property, new Binding ("Model.Text", BindingMode.OneWay)); + bindable.BindingContext = vm; + + vm.Model = null; + + Assert.AreEqual (property.DefaultValue, bindable.GetValue (property)); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test, Category ("[Binding] Complex paths")] + [Description ("When part of a complex path can not be evaluated during an update, bindables should return to their default value.")] + public void NullContextUsesDefaultValue() + { + var vm = new ComplexMockViewModel { + Model = new ComplexMockViewModel { + Text = "vm value" + } + }; + + var property = BindableProperty.Create<MockBindable, string> (w => w.Text, "foo bar"); + + var bindable = new MockBindable(); + bindable.SetBinding (property, new Binding ("Model.Text", BindingMode.OneWay)); + bindable.BindingContext = vm; + + Assume.That (bindable.GetValue (property), Is.EqualTo (vm.Model.Text)); + + bindable.BindingContext = null; + + Assert.AreEqual (property.DefaultValue, bindable.GetValue (property)); + Assert.That (log.Messages.Count, Is.EqualTo (0), + "An error was logged: " + log.Messages.FirstOrDefault()); + } + + [Test] + [Description ("OneWay bindings should not double apply on source updates.")] + public void OneWayBindingsDontDoubleApplyOnSourceUpdates() + { + var vm = new ComplexMockViewModel(); + + var bindable = new MockBindable(); + bindable.SetBinding (MultiplePropertyBindable.DoneProperty, new Binding ("QueryCount", BindingMode.OneWay)); + bindable.BindingContext = vm; + + Assert.AreEqual (1, vm.count); + + bindable.BindingContext = null; + + Assert.AreEqual (1, vm.count, "Source property was queried on an unset"); + + bindable.BindingContext = vm; + + Assert.AreEqual (2, vm.count, "Source property was queried multiple times on a reapply"); + } + + [Test] + [Description ("When there are multiple bindings, an update in one should not cause the other to udpate.")] + public void BindingsShouldNotTriggerOtherBindings() + { + var vm = new ComplexMockViewModel(); + + var bindable = new MockBindable(); + bindable.SetBinding (MultiplePropertyBindable.DoneProperty, new Binding ("QueryCount", BindingMode.OneWay)); + bindable.SetBinding (MockBindable.TextProperty, new Binding ("Text", BindingMode.OneWay)); + bindable.BindingContext = vm; + + Assert.AreEqual (1, vm.count); + + vm.Text = "update"; + + Assert.AreEqual (1, vm.count, "Source property was queried due to a different binding update."); + } + + internal class DerivedViewModel + : MockViewModel + { + public override string Text + { + get { return base.Text + "2"; } + set { base.Text = value; } + } + } + + [Test] + [Description ("The most derived version of a property should always be called.")] + public void MostDerviedPropertyOnContextSwitchOfSimilarType() + { + var vm = new MockViewModel { Text = "text" }; + + var bindable = new MockBindable(); + bindable.BindingContext = vm; + bindable.SetBinding (MockBindable.TextProperty, new Binding ("Text")); + + Assert.AreEqual (vm.Text, bindable.GetValue (MockBindable.TextProperty)); + + bindable.BindingContext = vm = new DerivedViewModel { Text = "text" }; + + Assert.AreEqual (vm.Text, bindable.GetValue (MockBindable.TextProperty)); + } + + internal class EmptyViewModel + { + } + + internal class DifferentViewModel + : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + string text = "foo"; + + public string Text + { + get { return text; } + } + + public string Text2 + { + set { text = value; } + } + + public string PrivateSetter + { + get; + private set; + } + } + + [Test] + [Description ("Paths should not distinguish types, a context change to a completely different type should work.")] + public void DifferentContextTypeAccessedCorrectlyWithSamePath() + { + var vm = new MockViewModel { Text = "text" }; + + var bindable = new MockBindable(); + bindable.BindingContext = vm; + bindable.SetBinding (MockBindable.TextProperty, new Binding ("Text")); + + Assert.AreEqual (vm.Text, bindable.GetValue (MockBindable.TextProperty)); + + var dvm = new DifferentViewModel(); + bindable.BindingContext = dvm; + + Assert.AreEqual (dvm.Text, bindable.GetValue (MockBindable.TextProperty)); + } + + [Test] + public void PropertyChangeBindingsOccurThroughMainThread() + { + var vm = new MockViewModel { Text = "text" }; + + var bindable = new MockBindable(); + bindable.BindingContext = vm; + bindable.SetBinding (MockBindable.TextProperty, new Binding ("Text")); + + bool mainThread = false; + Device.PlatformServices = new MockPlatformServices (invokeOnMainThread: a => mainThread = true); + + vm.Text = "updated"; + + Assert.IsTrue (mainThread, "Binding did not occur on main thread"); + Assert.AreNotEqual (vm.Text, bindable.GetValue (MockBindable.TextProperty), "Binding was applied anyway through other means"); + } + + [Test] + public void Clone() + { + object param = new object(); + var binding = new Binding (".", converter: new TestConverter<string, int>(), converterParameter: param, stringFormat: "{0}"); + var clone = (Binding)binding.Clone(); + + Assert.AreSame (binding.Converter, clone.Converter); + Assert.AreSame (binding.ConverterParameter, clone.ConverterParameter); + Assert.AreEqual (binding.Mode, clone.Mode); + Assert.AreEqual (binding.Path, clone.Path); + Assert.AreEqual (binding.StringFormat, clone.StringFormat); + } + + [Test] + public void PropertyMissingOneWay() + { + var bindable = new MockBindable { BindingContext = new MockViewModel() }; + bindable.Text = "foo"; + + Assert.That (() => bindable.SetBinding (MockBindable.TextProperty, new Binding ("Monkeys", BindingMode.OneWay)), Throws.Nothing); + Assert.That (log.Messages.Count, Is.EqualTo (1), "An error was not logged"); + Assert.That (bindable.Text, Is.EqualTo (MockBindable.TextProperty.DefaultValue)); + } + + [Test] + public void PropertyMissingOneWayToSource() + { + var bindable = new MockBindable { BindingContext = new MockViewModel() }; + bindable.Text = "foo"; + + Assert.That (() => bindable.SetBinding (MockBindable.TextProperty, new Binding ("Monkeys", BindingMode.OneWayToSource)), Throws.Nothing); + Assert.That (log.Messages.Count, Is.EqualTo (1), "An error was not logged"); + Assert.That (bindable.Text, Is.EqualTo (bindable.Text)); + } + + [Test] + public void PropertyMissingTwoWay() + { + var bindable = new MockBindable { BindingContext = new MockViewModel() }; + bindable.Text = "foo"; + + Assert.That (() => bindable.SetBinding (MockBindable.TextProperty, new Binding ("Monkeys")), Throws.Nothing); + // The first error is for the initial binding, the second is for reflecting the update back to the default value + Assert.That (log.Messages.Count, Is.EqualTo (2), "An error was not logged"); + Assert.That (bindable.Text, Is.EqualTo (MockBindable.TextProperty.DefaultValue)); + } + + [Test] + public void GetterMissingTwoWay() + { + var bindable = new MockBindable { BindingContext = new DifferentViewModel() }; + bindable.Text = "foo"; + + Assert.That (() => bindable.SetBinding (MockBindable.TextProperty, new Binding ("Text2")), Throws.Nothing); + Assert.That (bindable.Text, Is.EqualTo (MockBindable.TextProperty.DefaultValue)); + Assert.That (log.Messages.Count, Is.EqualTo (1), "An error was not logged"); + Assert.That (log.Messages[0], Is.StringContaining (String.Format (BindingExpression.PropertyNotFoundErrorMessage, + "Text2", + "Xamarin.Forms.Core.UnitTests.BindingUnitTests+DifferentViewModel", + "Xamarin.Forms.Core.UnitTests.MockBindable", + "Text"))); + + Assert.That (((DifferentViewModel) bindable.BindingContext).Text, Is.EqualTo (MockBindable.TextProperty.DefaultValue)); + } + + [Test] + public void BindingAppliesAfterGetterPreviouslyMissing() + { + var bindable = new MockBindable { BindingContext = new EmptyViewModel() }; + bindable.SetBinding (MockBindable.TextProperty, new Binding ("Text")); + + bindable.BindingContext = new MockViewModel { Text = "Foo" }; + Assert.That (bindable.Text, Is.EqualTo ("Foo")); + + Assert.That (log.Messages.Count, Is.Not.GreaterThan (1), "Too many errors were logged"); + Assert.That (log.Messages[0], Is.StringContaining (String.Format (BindingExpression.PropertyNotFoundErrorMessage, + "Text", + "Xamarin.Forms.Core.UnitTests.BindingUnitTests+EmptyViewModel", + "Xamarin.Forms.Core.UnitTests.MockBindable", + "Text"))); + } + + [Test] + public void SetterMissingTwoWay() + { + var bindable = new MockBindable { BindingContext = new DifferentViewModel() }; + Assert.That (() => bindable.SetBinding (MockBindable.TextProperty, new Binding ("Text")), Throws.Nothing); + + Assert.That (log.Messages.Count, Is.EqualTo (1), "An error was not logged"); + Assert.That (log.Messages[0], Is.StringContaining (String.Format (BindingExpression.PropertyNotFoundErrorMessage, + "Text", + "Xamarin.Forms.Core.UnitTests.BindingUnitTests+DifferentViewModel", + "Xamarin.Forms.Core.UnitTests.MockBindable", + "Text"))); + + Assert.That (() => bindable.SetValueCore (MockBindable.TextProperty, "foo"), Throws.Nothing); + } + + [Test] + public void PrivateSetterTwoWay() + { + var bindable = new MockBindable { BindingContext = new DifferentViewModel() }; + Assert.That (() => bindable.SetBinding (MockBindable.TextProperty, new Binding ("PrivateSetter")), Throws.Nothing); + + Assert.That (log.Messages.Count, Is.EqualTo (1), "An error was not logged"); + Assert.That (log.Messages[0], Is.StringContaining (String.Format (BindingExpression.PropertyNotFoundErrorMessage, + "PrivateSetter", + "Xamarin.Forms.Core.UnitTests.BindingUnitTests+DifferentViewModel", + "Xamarin.Forms.Core.UnitTests.MockBindable", + "Text"))); + + Assert.That (() => bindable.SetValueCore (MockBindable.TextProperty, "foo"), Throws.Nothing); + + Assert.That (log.Messages.Count, Is.EqualTo (2), "An error was not logged"); + Assert.That (log.Messages[1], Is.StringContaining (String.Format (BindingExpression.PropertyNotFoundErrorMessage, + "PrivateSetter", + "Xamarin.Forms.Core.UnitTests.BindingUnitTests+DifferentViewModel", + "Xamarin.Forms.Core.UnitTests.MockBindable", + "Text"))); + } + + [Test] + public void PropertyNotFound() + { + var bindable = new MockBindable { BindingContext = new MockViewModel() }; + Assert.That (() => bindable.SetBinding (MockBindable.TextProperty, new Binding ("MissingProperty")), Throws.Nothing); + + Assert.That (log.Messages.Count, Is.EqualTo (1), "An error was not logged"); + Assert.That (log.Messages[0], Is.StringContaining (String.Format (BindingExpression.PropertyNotFoundErrorMessage, + "MissingProperty", + "Xamarin.Forms.Core.UnitTests.MockViewModel", + "Xamarin.Forms.Core.UnitTests.MockBindable", + "Text"))); + } + + [Test] + [Description ("When binding with a multi-part path and part is null, no error should be thrown or logged")] + public void ChainedPartNull() + { + var bindable = new MockBindable { BindingContext = new ComplexMockViewModel() }; + Assert.That (() => bindable.SetBinding (MockBindable.TextProperty, new Binding ("Model.Text")), Throws.Nothing); + Assert.That (log.Messages.Count, Is.EqualTo (0), "An error was logged"); + } + + [Test] + public void PropertyNotFoundChained() + { + var bindable = new MockBindable { + BindingContext = new ComplexMockViewModel { + Model = new ComplexMockViewModel() + } + + }; + Assert.That (() => bindable.SetBinding (MockBindable.TextProperty, new Binding ("Model.MissingProperty")), Throws.Nothing); + + Assert.That (log.Messages.Count, Is.EqualTo (1), "An error was not logged"); + Assert.That (log.Messages[0], Is.StringContaining (String.Format (BindingExpression.PropertyNotFoundErrorMessage, + "MissingProperty", + "Xamarin.Forms.Core.UnitTests.BindingUnitTests+ComplexMockViewModel", + "Xamarin.Forms.Core.UnitTests.MockBindable", + "Text"))); + + Assert.That (bindable.Text, Is.EqualTo (MockBindable.TextProperty.DefaultValue)); + } + + [Test] + public void CreateBindingNull() + { + Assert.That (() => Binding.Create<MockViewModel> (null), Throws.InstanceOf<ArgumentNullException>()); + } + + [Test] + public void CreateBindingSimple() + { + Binding binding = Binding.Create<MockViewModel> (mvm => mvm.Text); + Assert.IsNotNull (binding); + Assert.AreEqual ("Text", binding.Path); + } + + [Test] + public void CreateBindingComplex() + { + Binding binding = Binding.Create<ComplexMockViewModel> (vm => vm.Model.Model.Text); + Assert.IsNotNull (binding); + Assert.AreEqual ("Model.Model.Text", binding.Path); + } + + [Test] + public void CreateBindingIndexed() + { + Binding binding = Binding.Create<ComplexMockViewModel> (vm => vm.Model.Model[5]); + Assert.IsNotNull (binding); + Assert.AreEqual ("Model.Model[5]", binding.Path); + } + + [Test] + public void CreateBindingIndexedNonConstant() + { + int x = 5; + Assert.That ( + () => Binding.Create<ComplexMockViewModel> (vm => vm.Model.Model[x]), + Throws.ArgumentException); + } + + internal class ReferenceTypeIndexerViewModel + : MockViewModel + { + public string this [string value] + { + get { return value; } + } + } + + [Test] + public void CreateBindingNullToIndexer() + { + Assert.That ( + () => Binding.Create<ReferenceTypeIndexerViewModel> (vm => vm[null]), + Throws.Nothing); + } + + [Test] + public void CreateBindingWithMethod() + { + Assert.That ( + () => Binding.Create<ComplexMockViewModel> (vm => vm.DoStuff()), + Throws.ArgumentException); + } + + [Test] + [Description ("Indexers are seen as methods, we don't want to get them confused with real methods.")] + public void CreateBindingWithMethodArgument() + { + Assert.That ( + () => Binding.Create<ComplexMockViewModel> (vm => vm.DoStuff (null)), + Throws.ArgumentException); + } + + object Method (MockViewModel vm) + { + return vm.Text; + } + + [Test] + public void CreateBindingMethod() + { + Func<MockViewModel, object> func = vm => vm.Text; + Assert.That (() => Binding.Create<MockViewModel> (vm => func (vm)), + Throws.ArgumentException); + + Assert.That (() => Binding.Create<MockViewModel> (vm => Method (vm)), + Throws.ArgumentException); + } + + [Test] + public void CreateBindingPrivateIndexer() + { + Assert.That (() => Binding.Create<InternalIndexerViewModel> (vm => vm[5]), + Throws.ArgumentException); + } + + class InternalIndexerViewModel + { + internal int this [int x] + { + get { return x; } + } + } + + [Test] + public void CreateBindingInvalidExpression() + { + Assert.That (() => Binding.Create<MockViewModel> (vm => vm.Text + vm.Text), + Throws.ArgumentException); + + Assert.That (() => Binding.Create<MockViewModel> (vm => 5), + Throws.ArgumentException); + + Assert.That (() => Binding.Create<MockViewModel> (vm => null), + Throws.ArgumentException); + } + + [Test] + public void SetBindingContextBeforeContextBindingAndInnerBindings () + { + 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.AreEqual ("Foo", label.Text); + } + + [Test] + public void SetBindingContextAndInnerBindingBeforeContextBinding () + { + var label = new Label (); + var view = new StackLayout { Children = {label} }; + + view.BindingContext = new {item0 = "Foo", item1 = "Bar"}; + label.SetBinding (Label.TextProperty, Binding.SelfPath); + label.SetBinding (BindableObject.BindingContextProperty, "item0"); + + Assert.AreEqual ("Foo", label.Text); + } + + [Test] + public void SetBindingContextAfterContextBindingAndInnerBindings () + { + var label = new Label (); + var view = new StackLayout { Children = {label} }; + + label.SetBinding (BindableObject.BindingContextProperty, "item0"); + label.SetBinding (Label.TextProperty, Binding.SelfPath); + view.BindingContext = new {item0 = "Foo", item1 = "Bar"}; + + Assert.AreEqual ("Foo", label.Text); + } + + [Test] + public void SetBindingContextAfterInnerBindingsAndContextBinding () + { + var label = new Label (); + var view = new StackLayout { Children = {label} }; + + label.SetBinding (Label.TextProperty, Binding.SelfPath); + label.SetBinding (BindableObject.BindingContextProperty, "item0"); + view.BindingContext = new {item0 = "Foo", item1 = "Bar"}; + + Assert.AreEqual ("Foo", label.Text); + } + + [Test] + public void Convert () + { + var slider = new Slider (); + var vm = new MockViewModel { Text = "0.5" }; + slider.BindingContext = vm; + slider.SetBinding (Slider.ValueProperty, "Text", BindingMode.TwoWay); + + Assert.That (slider.Value, Is.EqualTo (0.5)); + + slider.Value = 0.9; + + Assert.That (vm.Text, Is.EqualTo ("0.9")); + } + + #if !WINDOWS_PHONE + [Test] + [SetCulture ("pt-PT")] + [SetUICulture ("pt-PT")] + public void ConvertIsCultureInvariant () + { + var slider = new Slider (); + var vm = new MockViewModel { Text = "0.5" }; + slider.BindingContext = vm; + slider.SetBinding (Slider.ValueProperty, "Text", BindingMode.TwoWay); + + Assert.That (slider.Value, Is.EqualTo (0.5)); + + slider.Value = 0.9; + + Assert.That (vm.Text, Is.EqualTo ("0.9")); + } + #endif + + [Test] + public void FailToConvert () + { + var slider = new Slider (); + slider.BindingContext = new ComplexMockViewModel { Model = new ComplexMockViewModel() }; + + Assert.That (() => { + slider.SetBinding (Slider.ValueProperty, "Model"); + }, Throws.Nothing); + + Assert.That (slider.Value, Is.EqualTo (Slider.ValueProperty.DefaultValue)); + } + + class NullViewModel : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public string Foo + { + get; + set; + } + + public string Bar + { + get; + set; + } + + public void SignalAllPropertiesChanged (bool useNull) + { + var changed = PropertyChanged; + if (changed != null) + changed (this, new PropertyChangedEventArgs ((useNull) ? null : String.Empty)); + } + } + + class MockBindable2 : MockBindable + { + public static readonly BindableProperty Text2Property = BindableProperty.Create<MockBindable2, string> ( + b => b.Text2, "default", BindingMode.TwoWay); + + public string Text2 + { + get { return (string)GetValue (Text2Property); } + set { SetValue (Text2Property, value); } + } + } + + [TestCase (true)] + [TestCase (false)] + public void NullPropertyUpdatesAllBindings (bool useStringEmpty) + { + var vm = new NullViewModel(); + var bindable = new MockBindable2(); + bindable.BindingContext = vm; + bindable.SetBinding (MockBindable.TextProperty, "Foo"); + bindable.SetBinding (MockBindable2.Text2Property, "Bar"); + + vm.Foo = "Foo"; + vm.Bar = "Bar"; + Assert.That (() => vm.SignalAllPropertiesChanged (useNull: !useStringEmpty), Throws.Nothing); + + Assert.That (bindable.Text, Is.EqualTo ("Foo")); + Assert.That (bindable.Text2, Is.EqualTo ("Bar")); + } + + [TestCase] + public void BindingSourceOverContext () + { + var label = new Label (); + label.BindingContext = "bindingcontext"; + label.SetBinding (Label.TextProperty, Binding.SelfPath); + Assert.AreEqual ("bindingcontext", label.Text); + + label.SetBinding (Label.TextProperty, new Binding (Binding.SelfPath, source: "bindingsource")); + Assert.AreEqual ("bindingsource", label.Text); + } + + class TestViewModel : INotifyPropertyChanged + { + event PropertyChangedEventHandler PropertyChanged; + + event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged + { + add { PropertyChanged += value; } + remove { PropertyChanged -= value; } + } + + public string Foo { get; set; } + + public int InvocationListSize () + { + if (PropertyChanged == null) + return 0; + return PropertyChanged.GetInvocationList ().Length; + } + + public virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (propertyName)); + } + } + + [Test] + public void BindingUnsubscribesForDeadTarget () + { + TestViewModel viewmodel = new TestViewModel(); + + int i = 0; + Action create = null; + create = () => { + if (i++ < 1024) { + create(); + return; + } + + var button = new Button (); + button.SetBinding (Button.TextProperty, "Foo"); + button.BindingContext = viewmodel; + }; + + create(); + + Assume.That (viewmodel.InvocationListSize (), Is.EqualTo (1)); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect (); + + viewmodel.OnPropertyChanged ("Foo"); + + Assert.AreEqual (0, viewmodel.InvocationListSize ()); + } + + [Test] + public void BindingCreatesSingleSubscription () + { + TestViewModel viewmodel = new TestViewModel(); + + var button = new Button (); + button.SetBinding (Button.TextProperty, "Foo"); + button.BindingContext = viewmodel; + + Assert.That (viewmodel.InvocationListSize (), Is.EqualTo (1)); + } + + public class IndexedViewModel : INotifyPropertyChanged + { + Dictionary<string, object> dict = new Dictionary<string, object> (); + + public object this [string index] + { + get { return dict[index]; } + set + { + dict[index] = value; + OnPropertyChanged ("Item[" + index + "]"); + + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (propertyName)); + } + } + + [Test] + public void IndexedViewModelPropertyChanged () + { + var label = new Label (); + var viewModel = new IndexedViewModel (); + //viewModel["Foo"] = "Bar"; + + label.BindingContext = new { + Data = viewModel + }; + label.SetBinding (Label.TextProperty, "Data[Foo]"); + + + Assert.AreEqual (null, label.Text); + + viewModel["Foo"] = "Baz"; + + Assert.AreEqual ("Baz", label.Text); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/BoxViewUnitTests.cs b/Xamarin.Forms.Core.UnitTests/BoxViewUnitTests.cs new file mode 100644 index 00000000..6d26af45 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/BoxViewUnitTests.cs @@ -0,0 +1,42 @@ +using System; +using NUnit.Framework; + +using System.Collections.Generic; +using System.Linq; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class BoxViewUnitTests : BaseTestFixture + { + [Test] + public void TestConstructor () + { + var box = new BoxView { + Color = new Color (0.2, 0.3, 0.4), + WidthRequest=20, + HeightRequest=30, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + Assert.AreEqual (new Color (0.2, 0.3, 0.4), box.Color); + var request = box.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request; + Assert.AreEqual (20, request.Width); + Assert.AreEqual (30, request.Height); + } + + [Test] + public void DefaultSize () + { + var box = new BoxView { + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + var request = box.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request; + Assert.AreEqual (40, request.Width); + Assert.AreEqual (40, request.Height); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ButtonUnitTest.cs b/Xamarin.Forms.Core.UnitTests/ButtonUnitTest.cs new file mode 100644 index 00000000..29ad2d0e --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ButtonUnitTest.cs @@ -0,0 +1,181 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ButtonUnitTest + : CommandSourceTests<Button> + { + [SetUp] + public override void Setup () + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown () + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void MeasureInvalidatedOnTextChange () + { + var button = new Button (); + + bool fired = false; + button.MeasureInvalidated += (sender, args) => fired = true; + + button.Text = "foo"; + Assert.True (fired); + } + + [Test] + public void TestTappedEvent () + { + var view = new Button (); + + bool activated = false; + view.Clicked += (sender, e) => activated = true; + + ((IButtonController) view).SendClicked (); + + Assert.True (activated); + } + + protected override Button CreateSource() + { + return new Button(); + } + + protected override void Activate (Button source) + { + ((IButtonController) source).SendClicked(); + } + + protected override BindableProperty IsEnabledProperty + { + get { return Button.IsEnabledProperty; } + } + + protected override BindableProperty CommandProperty + { + get { return Button.CommandProperty; } + } + + protected override BindableProperty CommandParameterProperty + { + get { return Button.CommandParameterProperty; } + } + + + [Test] + public void TestBindingContextPropagation () + { + var context = new object (); + var button = new Button (); + button.BindingContext = context; + var source = new FileImageSource (); + button.Image = source; + Assert.AreSame (context, source.BindingContext); + + button = new Button (); + source = new FileImageSource (); + button.Image = source; + button.BindingContext = context; + Assert.AreSame (context, source.BindingContext); + } + + [Test] + public void TestImageSourcePropertiesChangedTriggerResize () + { + var source = new FileImageSource (); + var button = new Button { Image = source }; + bool fired = false; + button.MeasureInvalidated += (sender, e) => fired = true; + Assert.Null (source.File); + source.File = "foo.png"; + Assert.NotNull (source.File); + Assert.True (fired); + } + + [Test] + public void AssignToFontStructUpdatesFontFamily ( + [Values (NamedSize.Default, NamedSize.Large, NamedSize.Medium, NamedSize.Small, NamedSize.Micro)] NamedSize size, + [Values (FontAttributes.None, FontAttributes.Bold, FontAttributes.Italic, FontAttributes.Bold | FontAttributes.Italic)] FontAttributes attributes) + { + var button = new Button {Platform = new UnitPlatform ()}; + double startSize = button.FontSize; + var startAttributes = button.FontAttributes; + + bool firedSizeChanged = false; + bool firedAttributesChanged = false; + button.PropertyChanged += (sender, args) => { + if (args.PropertyName == Label.FontSizeProperty.PropertyName) + firedSizeChanged = true; + if (args.PropertyName == Label.FontAttributesProperty.PropertyName) + firedAttributesChanged = true; + }; + + button.Font = Font.OfSize ("Testing123", size).WithAttributes (attributes); + + Assert.AreEqual (Device.GetNamedSize (size, typeof (Label), true), button.FontSize); + Assert.AreEqual (attributes, button.FontAttributes); + Assert.AreEqual (startSize != button.FontSize, firedSizeChanged); + Assert.AreEqual (startAttributes != button.FontAttributes, firedAttributesChanged); + } + + [Test] + public void AssignToFontFamilyUpdatesFont () + { + var button = new Button {Platform = new UnitPlatform ()}; + + button.FontFamily = "CrazyFont"; + Assert.AreEqual (button.Font, Font.OfSize ("CrazyFont", button.FontSize)); + } + + [Test] + public void AssignToFontSizeUpdatesFont () + { + var button = new Button {Platform = new UnitPlatform ()}; + + button.FontSize = 1000; + Assert.AreEqual (button.Font, Font.SystemFontOfSize (1000)); + } + + [Test] + public void AssignToFontAttributesUpdatesFont () + { + var button = new Button {Platform = new UnitPlatform ()}; + + button.FontAttributes = FontAttributes.Italic | FontAttributes.Bold; + Assert.AreEqual (button.Font, Font.SystemFontOfSize (button.FontSize, FontAttributes.Bold | FontAttributes.Italic)); + } + + [Test] + public void CommandCanExecuteUpdatesEnabled () + { + var button = new Button (); + + bool result = false; + + var bindingContext = new { + Command = new Command (() => { }, () => result) + }; + + button.SetBinding (Button.CommandProperty, "Command"); + button.BindingContext = bindingContext; + + Assert.False (button.IsEnabled); + + result = true; + + bindingContext.Command.ChangeCanExecute (); + + Assert.True (button.IsEnabled); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/CarouselPageTests.cs b/Xamarin.Forms.Core.UnitTests/CarouselPageTests.cs new file mode 100644 index 00000000..553d67ce --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/CarouselPageTests.cs @@ -0,0 +1,29 @@ +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + public class CarouselPageTests : MultiPageTests<ContentPage> + { + protected override MultiPage<ContentPage> CreateMultiPage() + { + return new CarouselPage(); + } + + protected override ContentPage CreateContainedPage() + { + return new ContentPage { Content = new View() }; + } + + protected override int GetIndex (ContentPage page) + { + return CarouselPage.GetIndex (page); + } + + [Test] + public void TestConstructor() + { + var page = new CarouselPage(); + Assert.That (page.Children, Is.Empty); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/CellTests.cs b/Xamarin.Forms.Core.UnitTests/CellTests.cs new file mode 100644 index 00000000..0006b4cf --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/CellTests.cs @@ -0,0 +1,190 @@ +using NUnit.Framework; +using System; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class CellTests : BaseTestFixture + { + internal class TestCell : Cell + { + public bool OnAppearingSent { get; set; } + public bool OnDisappearingSent { get; set; } + + protected override void OnAppearing () + { + base.OnAppearing (); + OnAppearingSent = true; + } + + protected override void OnDisappearing () + { + base.OnDisappearing (); + OnDisappearingSent = true; + } + } + + [Test] + public void Selected () + { + var cell = new TestCell (); + + bool tapped = false; + cell.Tapped += (sender, args) => tapped = true; + + cell.OnTapped(); + Assert.IsTrue (tapped); + } + + [Test] + public void AppearingEvent () + { + var cell = new TestCell (); + + bool emitted = false; + cell.Appearing += (sender, args) => emitted = true; + + cell.SendAppearing (); + Assert.True (emitted); + Assert.True (cell.OnAppearingSent); + Assert.False (cell.OnDisappearingSent); + } + + [Test] + public void DisappearingEvent () + { + var cell = new TestCell (); + + bool emitted = false; + cell.Disappearing += (sender, args) => emitted = true; + + cell.SendDisappearing (); + Assert.True (emitted); + Assert.False (cell.OnAppearingSent); + Assert.True (cell.OnDisappearingSent); + } + + [Test] + public void TestBindingContextPropagationOnImageCell () + { + var context = new object (); + var cell = new ImageCell (); + cell.BindingContext = context; + var source = new FileImageSource (); + cell.ImageSource = source; + Assert.AreSame (context, source.BindingContext); + + cell = new ImageCell (); + source = new FileImageSource (); + cell.ImageSource = source; + cell.BindingContext = context; + Assert.AreSame (context, source.BindingContext); + } + + [Test] + public void HasContextActions() + { + bool changed = false; + + var cell = new TextCell(); + cell.PropertyChanged += (sender, args) => { + if (args.PropertyName == "HasContextActions") + changed = true; + }; + + Assert.That (cell.HasContextActions, Is.False); + Assert.That (changed, Is.False); + + var collection = cell.ContextActions; + + Assert.That (cell.HasContextActions, Is.False); + Assert.That (changed, Is.False); + + collection.Add (new MenuItem()); + + Assert.That (cell.HasContextActions, Is.True); + Assert.That (changed, Is.True); + } + + [Test] + public void MenuItemsGetBindingContext() + { + var cell = new TextCell { + ContextActions = { + new MenuItem () + } + }; + + object bc = new object (); + + cell.BindingContext = bc; + Assert.That (cell.ContextActions [0].BindingContext, Is.SameAs (bc)); + + cell = new TextCell { BindingContext = new object () }; + cell.ContextActions.Add (new MenuItem ()); + + Assert.That (cell.ContextActions [0].BindingContext, Is.SameAs (cell.BindingContext)); + } + + [Test] + public void RenderHeightINPCFromParent() + { + var lv = new ListView(); + var cell = new TextCell(); + cell.Parent = lv; + + int changing = 0, changed = 0; + cell.PropertyChanging += (sender, args) => { + if (args.PropertyName == "RenderHeight") + changing++; + }; + + cell.PropertyChanged += (sender, args) => { + if (args.PropertyName == "RenderHeight") + changed++; + }; + + lv.RowHeight = 5; + + Assume.That (cell.RenderHeight, Is.EqualTo (5)); + + Assert.That (changing, Is.EqualTo (1)); + Assert.That (changed, Is.EqualTo (1)); + } + + [Test] + public async void ForceUpdateSizeCallsAreRateLimited() + { + var lv = new ListView { HasUnevenRows = true }; + var cell = new ViewCell { Parent = lv }; + + int numberOfCalls = 0; + cell.ForceUpdateSizeRequested += (object sender, System.EventArgs e) => { numberOfCalls++; }; + + cell.ForceUpdateSize (); + cell.ForceUpdateSize (); + cell.ForceUpdateSize (); + cell.ForceUpdateSize (); + + await System.Threading.Tasks.Task.Delay (TimeSpan.FromMilliseconds (150)); + + Assert.AreEqual (1, numberOfCalls); + } + + [Test] + public async void ForceUpdateSizeWillNotBeCalledIfParentIsNotAListViewWithUnevenRows () + { + var lv = new ListView { HasUnevenRows = false }; + var cell = new ViewCell { Parent = lv }; + + int numberOfCalls = 0; + cell.ForceUpdateSizeRequested += (object sender, System.EventArgs e) => { numberOfCalls++; }; + + cell.ForceUpdateSize (); + + await System.Threading.Tasks.Task.Delay (TimeSpan.FromMilliseconds (16)); + + Assert.AreEqual (0, numberOfCalls); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ColorUnitTests.cs b/Xamarin.Forms.Core.UnitTests/ColorUnitTests.cs new file mode 100644 index 00000000..b29a9680 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ColorUnitTests.cs @@ -0,0 +1,268 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ColorUnitTests : BaseTestFixture + { + [Test] + public void TestHSLPostSetEquality () + { + var color = new Color (1, 0.5, 0.2); + var color2 = color; + + color2 = color.WithLuminosity (.2); + Assert.False (color == color2); + } + + [Test] + public void TestHSLPostSetInequality () + { + var color = new Color (1, 0.5, 0.2); + var color2 = color; + + color2 = color.WithLuminosity (.2); + + Assert.True (color != color2); + } + + [Test] + public void TestHSLSetToDefaultValue () + { + var color = new Color (0.2, 0.5, 0.8); + + // saturation is initialized to 0, make sure we still update + color = color.WithSaturation (0); + + Assert.AreEqual (color.R, color.G); + Assert.AreEqual (color.R, color.B); + } + + [Test] + public void TestHSLModifiers () + { + var color = Color.Default; + Assert.Throws<InvalidOperationException> (()=> color.WithHue (.1)); + Assert.Throws<InvalidOperationException> (()=> color.WithLuminosity (.1)); + Assert.Throws<InvalidOperationException> (()=> color.WithSaturation (.1)); + + color = Color.FromHsla (.8, .6, .2); + Assert.AreEqual (Color.FromHsla (.1, .6, .2), color.WithHue (.1)); + Assert.AreEqual (Color.FromHsla (.8, .1, .2), color.WithSaturation (.1)); + Assert.AreEqual (Color.FromHsla (.8, .6, .1), color.WithLuminosity (.1)); + } + + [Test] + public void TestMultiplyAlpha () + { + var color = new Color (1, 1, 1, 1); + color = color.MultiplyAlpha (0.25); + Assert.AreEqual (.25, color.A); + + color = Color.Default; + Assert.Throws<InvalidOperationException>(()=>color = color.MultiplyAlpha (0.25)); + + color = Color.FromHsla (1, 1, 1, 1); + color = color.MultiplyAlpha (0.25); + Assert.AreEqual (.25, color.A); + } + + [Test] + public void TestClamping () + { + var color = new Color (2, 2, 2, 2); + + Assert.AreEqual (1, color.R); + Assert.AreEqual (1, color.G); + Assert.AreEqual (1, color.B); + Assert.AreEqual (1, color.A); + + color = new Color (-1, -1, -1, -1); + + Assert.AreEqual (0, color.R); + Assert.AreEqual (0, color.G); + Assert.AreEqual (0, color.B); + Assert.AreEqual (0, color.A); + } + + [Test] + public void TestRGBToHSL () + { + var color = new Color (.5, .1, .1); + + Assert.That (color.Hue, Is.EqualTo (1).Within (0.001)); + Assert.That (color.Saturation, Is.EqualTo (0.662).Within (0.01)); + Assert.That (color.Luminosity, Is.EqualTo (0.302).Within (0.01)); + } + + [Test] + public void TestHSLToRGB () + { + var color = Color.FromHsla (0, .662, .302); + + Assert.That (color.R, Is.EqualTo (0.5).Within (0.01)); + Assert.That (color.G, Is.EqualTo (0.1).Within (0.01)); + Assert.That (color.B, Is.EqualTo (0.1).Within (0.01)); + } + + [Test] + public void TestColorFromValue () + { + var color = new Color (0.2); + + Assert.AreEqual (new Color (0.2, 0.2, 0.2, 1), color); + } + + [Test] + public void TestAddLuminosity () + { + var color = new Color (0.2); + var brighter = color.AddLuminosity (0.2); + Assert.That (brighter.Luminosity, Is.EqualTo (color.Luminosity + 0.2).Within (0.001)); + + color = Color.Default; + Assert.Throws<InvalidOperationException> (() => color.AddLuminosity (0.2)); + } + + [Test] + public void TestZeroLuminosity () + { + var color = new Color (0.1, 0.2, 0.3); + color = color.AddLuminosity (-1); + + Assert.AreEqual (0, color.Luminosity); + Assert.AreEqual (0, color.R); + Assert.AreEqual (0, color.G); + Assert.AreEqual (0, color.B); + } + + [Test] + public void TestHashCode () + { + var color1 = new Color (0.1); + var color2 = new Color (0.1); + + Assert.True (color1.GetHashCode () == color2.GetHashCode ()); + color2 = Color.FromHsla (color2.Hue, color2.Saturation, .5); + + Assert.False (color1.GetHashCode () == color2.GetHashCode ()); + } + + [Test] + public void TestHashCodeNamedColors () + { + Color red = Color.Red; //R=1, G=0, B=0, A=1 + int hashRed = red.GetHashCode(); + + Color blue = Color.Blue; //R=0, G=0, B=1, A=1 + int hashBlue = blue.GetHashCode(); + + Assert.False (hashRed == hashBlue); + } + + [Test] + public void TestHashCodeAll () + { + Dictionary<int,Color> colorsAndHashes = new Dictionary<int,Color> (); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Transparent.GetHashCode (), Color.Transparent)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Aqua.GetHashCode (), Color.Aqua)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Black.GetHashCode (), Color.Black)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Blue.GetHashCode (), Color.Blue)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Fuchsia.GetHashCode (), Color.Fuchsia)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Gray.GetHashCode (), Color.Gray)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Green.GetHashCode (), Color.Green)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Lime.GetHashCode (), Color.Lime)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Maroon.GetHashCode (), Color.Maroon)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Navy.GetHashCode (), Color.Navy)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Olive.GetHashCode (), Color.Olive)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Purple.GetHashCode (), Color.Purple)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Pink.GetHashCode (), Color.Pink)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Red.GetHashCode (), Color.Red)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Silver.GetHashCode (), Color.Silver)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Teal.GetHashCode (), Color.Teal)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.White.GetHashCode (), Color.White)); + Assert.DoesNotThrow (() => colorsAndHashes.Add (Color.Yellow.GetHashCode (), Color.Yellow)); + } + + [Test] + public void TestSetHue () + { + var color = new Color (0.2, 0.5, 0.7); + color = Color.FromHsla (.2, color.Saturation, color.Luminosity); + + Assert.That (color.R, Is.EqualTo (0.6).Within (0.001)); + Assert.That (color.G, Is.EqualTo (0.7).Within (0.001)); + Assert.That (color.B, Is.EqualTo (0.2).Within (0.001)); + } + + [Test] + public void ZeroLuminToRGB () + { + var color = new Color (0); + Assert.AreEqual (0, color.Luminosity); + Assert.AreEqual (0, color.Hue); + Assert.AreEqual (0, color.Saturation); + } + + [Test] + public void TestToString () + { + var color = new Color (1, 1, 1, 0.5); + Assert.AreEqual ("[Color: A=0.5, R=1, G=1, B=1, Hue=0, Saturation=0, Luminosity=1]", color.ToString ()); + } + + [Test] + public void TestFromHex () + { + var color = Color.FromRgb(138, 43, 226); + Assert.AreEqual(color, Color.FromHex("8a2be2")); + + Assert.AreEqual (Color.FromRgba (138, 43, 226, 128), Color.FromHex ("#808a2be2")); + Assert.AreEqual (Color.FromHex ("#aabbcc"), Color.FromHex ("#abc")); + Assert.AreEqual (Color.FromHex ("#aabbccdd"), Color.FromHex ("#abcd")); + } + + [Test] + public void FromRGBDouble () + { + var color = Color.FromRgb (0.2, 0.3, 0.4); + + Assert.AreEqual (new Color (0.2, 0.3, 0.4), color); + } + + [Test] + public void FromRGBADouble () + { + var color = Color.FromRgba (0.2, 0.3, 0.4, 0.5); + + Assert.AreEqual (new Color (0.2, 0.3, 0.4, 0.5), color); + } + + [Test] + public void TestColorTypeConverter () + { + var converter = new ColorTypeConverter (); + Assert.True (converter.CanConvertFrom (typeof(string))); + Assert.AreEqual (Color.Blue, converter.ConvertFromInvariantString ("Color.Blue")); + Assert.AreEqual (Color.Blue, converter.ConvertFromInvariantString ("Blue")); + Assert.AreEqual (Color.Blue, converter.ConvertFromInvariantString ("#0000ff")); + Assert.AreEqual (Color.Default, converter.ConvertFromInvariantString ("Color.Default")); + Assert.AreEqual (Color.Accent, converter.ConvertFromInvariantString ("Accent")); + var hotpink = Color.FromHex ("#FF69B4"); + Color.Accent = hotpink; + Assert.AreEqual (Color.Accent, converter.ConvertFromInvariantString ("Accent")); + Assert.Throws<InvalidOperationException> (() => converter.ConvertFromInvariantString ("")); + } + + [Test] + public void TestDefault () + { + Assert.AreEqual (Color.Default, default(Color)); + Assert.AreEqual (Color.Default, new Color ()); + } + + + } +} diff --git a/Xamarin.Forms.Core.UnitTests/CommandSourceTests.cs b/Xamarin.Forms.Core.UnitTests/CommandSourceTests.cs new file mode 100644 index 00000000..f544a297 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/CommandSourceTests.cs @@ -0,0 +1,184 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + public abstract class CommandSourceTests<T> : BaseTestFixture + where T : BindableObject + { + [Test] + public void TestCommand () + { + var source = CreateSource(); + + bool executed = false; + source.SetValue (CommandProperty, new Command (o => { + executed = true; + Assert.AreEqual (source, o); + })); + + source.SetValue (CommandParameterProperty, source); + + Activate (source); + + Assert.True (executed); + } + + [Test] + public void CommandCanExecuteModifiesEnabled ([Values(true, false)] bool initial) + { + bool canExecute = initial; + Command command; + var source = CreateSource(); + source.SetValue (CommandProperty, command = new Command (() => { }, () => canExecute)); + + Assert.AreEqual (canExecute, source.GetValue (IsEnabledProperty)); + + canExecute = !initial; + command.ChangeCanExecute (); + + Assert.AreEqual (canExecute, source.GetValue (IsEnabledProperty)); + } + + [Test] + public void ReenabledAfterCommandRemoved() + { + var source = CreateSource(); + source.SetValue (CommandProperty, new Command (() => { }, () => false)); + + Assert.That (source.GetValue (IsEnabledProperty), Is.False); + + source.SetValue (CommandProperty, null); + + Assert.That (source.GetValue (IsEnabledProperty), Is.True); + } + + [Test] + public void CommandUnhooksOnNull () + { + bool canExecute = false; + Command command; + var source = CreateSource(); + + bool raised = false; + source.SetValue (CommandProperty, command = new Command (() => { }, () => { + raised = true; + return canExecute; + })); + + raised = false; + source.SetValue (CommandProperty, null); + + canExecute = true; + command.ChangeCanExecute (); + + Assert.False (raised); + } + + [Test] + public void CommandCanExecuteInvokedOnCommandSet () + { + bool fired = false; + Func<bool> canExecute = () => { + fired = true; + return true; + }; + + Assert.IsFalse (fired); + var source = CreateSource(); + source.SetValue (CommandProperty, new Command (() => { }, canExecute)); + + Assert.True (fired); + } + + [Test] + public void CommandCanExecuteInvokedOnCommandParameterSet () + { + bool fired; + Func<bool> canExecute = () => { + fired = true; + return true; + }; + + var source = CreateSource(); + source.SetValue (CommandProperty, new Command (() => { }, canExecute)); + + fired = false; + Assert.IsFalse (fired); + source.SetValue (CommandParameterProperty, new object ()); + Assert.True (fired); + } + + [Test] + public void CommandCanExecuteInvokedOnChange() + { + bool fired; + Func<bool> canExecute = () => { + fired = true; + return true; + }; + + var cmd = new Command (() => { }, canExecute); + var source = CreateSource(); + source.SetValue (CommandProperty, cmd); + + fired = false; + + cmd.ChangeCanExecute(); + + Assert.That (fired, Is.True, "CanExecute was not called when the event was raised"); + } + + class BoolViewModel + : MockViewModel + { + bool toggle; + + public bool Toggle + { + get { return toggle; } + set + { + if (toggle == value) + return; + + toggle = value; + OnPropertyChanged(); + } + } + } + + [Test] + public void EnabledUpdatesDoNotRemoveBindings() + { + var vm = new BoolViewModel { Toggle = true }; + var source = CreateSource(); + source.BindingContext = vm; + source.SetBinding (IsEnabledProperty, "Toggle"); + + Assert.That (source.GetValue (IsEnabledProperty), Is.True); + + source.SetValue (CommandProperty, new Command (() => { })); + + Assert.That (source.GetIsBound (IsEnabledProperty), Is.True); + } + + protected abstract T CreateSource(); + protected abstract void Activate (T source); + + protected abstract BindableProperty IsEnabledProperty + { + get; + } + + protected abstract BindableProperty CommandProperty + { + get; + } + + protected abstract BindableProperty CommandParameterProperty + { + get; + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/CommandTests.cs b/Xamarin.Forms.Core.UnitTests/CommandTests.cs new file mode 100644 index 00000000..1182fe40 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/CommandTests.cs @@ -0,0 +1,155 @@ +using System; +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class CommandTests : BaseTestFixture + { + [Test] + public void Constructor () + { + var cmd = new Command (() => { }); + Assert.True (cmd.CanExecute (null)); + } + + [Test] + public void ThrowsWithNullConstructor () + { + Assert.Throws<ArgumentNullException> (() => new Command ((Action)null)); + } + + [Test] + public void ThrowsWithNullParameterizedConstructor () + { + Assert.Throws<ArgumentNullException> (() => new Command ((Action<object>)null)); + } + + [Test] + public void ThrowsWithNullCanExecute () + { + Assert.Throws<ArgumentNullException> (() => new Command (() => { }, null)); + } + + [Test] + public void ThrowsWithNullParameterizedCanExecute () + { + Assert.Throws<ArgumentNullException> (() => new Command (o => { }, null)); + } + + [Test] + public void ThrowsWithNullExecuteValidCanExecute () + { + Assert.Throws<ArgumentNullException> (() => new Command (null, () => true)); + } + + [Test] + public void Execute () + { + bool executed = false; + var cmd = new Command (() => executed = true); + + cmd.Execute (null); + Assert.True (executed); + } + + [Test] + public void ExecuteParameterized () + { + object executed = null; + var cmd = new Command (o => executed = o); + + var expected = new object (); + cmd.Execute (expected); + + Assert.AreEqual (expected, executed); + } + + [Test] + public void ExecuteWithCanExecute () + { + bool executed = false; + var cmd = new Command (() => executed = true, () => true); + + cmd.Execute (null); + Assert.True (executed); + } + + [Test] + public void CanExecute ([Values (true, false)] bool expected) + { + bool canExecuteRan = false; + var cmd = new Command (() => { }, () => { + canExecuteRan = true; + return expected; + }); + + Assert.AreEqual(expected, cmd.CanExecute (null)); + Assert.True (canExecuteRan); + } + + [Test] + public void ChangeCanExecute () + { + bool signaled = false; + var cmd = new Command (() => { }); + + cmd.CanExecuteChanged += (sender, args) => signaled = true; + + cmd.ChangeCanExecute (); + Assert.True (signaled); + } + + [Test] + public void GenericThrowsWithNullExecute () + { + Assert.Throws<ArgumentNullException> (() => new Command<string> (null)); + } + + [Test] + public void GenericThrowsWithNullExecuteAndCanExecuteValid () + { + Assert.Throws<ArgumentNullException> (() => new Command<string> (null, s => true)); + } + + [Test] + public void GenericThrowsWithValidExecuteAndCanExecuteNull () + { + Assert.Throws<ArgumentNullException> (() => new Command<string> (s => { }, null)); + } + + [Test] + public void GenericExecute () + { + string result = null; + var cmd = new Command<string> (s => result = s); + + cmd.Execute ("Foo"); + Assert.AreEqual ("Foo", result); + } + + [Test] + public void GenericExecuteWithCanExecute () + { + string result = null; + var cmd = new Command<string> (s => result = s, s => true); + + cmd.Execute ("Foo"); + Assert.AreEqual ("Foo", result); + } + + [Test] + public void GenericCanExecute ([Values (true, false)] bool expected) + { + string result = null; + var cmd = new Command<string> (s => { }, s => { + result = s; + return expected; + }); + + Assert.AreEqual (expected, cmd.CanExecute ("Foo")); + Assert.AreEqual ("Foo", result); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ContentFormUnitTests.cs b/Xamarin.Forms.Core.UnitTests/ContentFormUnitTests.cs new file mode 100644 index 00000000..5b6f3e60 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ContentFormUnitTests.cs @@ -0,0 +1,68 @@ +using System; +using System.Linq; +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ContentPageUnitTests : BaseTestFixture + { + [Test] + public void PropagateBindingContextBefore() + { + var stack = new StackLayout(); + + var content = new ContentPage(); + content.Content = stack; + + object context = new object(); + content.BindingContext = context; + + Assert.AreSame (context, stack.BindingContext); + } + + [Test] + public void PropagateBindingContextAfter() + { + var stack = new StackLayout(); + + var content = new ContentPage(); + + object context = new object(); + content.BindingContext = context; + + content.Content = stack; + + Assert.AreSame (context, stack.BindingContext); + } + + [Test] + public void PropagateToolbarItemBindingContextPreAdd () + { + var page = new ContentPage (); + object context = "hello"; + + var toolbarItem = new ToolbarItem (); + page.ToolbarItems.Add (toolbarItem); + + page.BindingContext = context; + + Assert.AreEqual (context, toolbarItem.BindingContext); + } + + [Test] + public void PropagateToolbarItemBindingContextPostAdd () + { + var page = new ContentPage (); + object context = "hello"; + + var toolbarItem = new ToolbarItem (); + page.BindingContext = context; + + page.ToolbarItems.Add (toolbarItem); + + Assert.AreEqual (context, toolbarItem.BindingContext); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ContentViewUnitTest.cs b/Xamarin.Forms.Core.UnitTests/ContentViewUnitTest.cs new file mode 100644 index 00000000..9d7a12e0 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ContentViewUnitTest.cs @@ -0,0 +1,393 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ContentViewUnitTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void TestConstructor () + { + var contentView = new ContentView (); + + Assert.Null (contentView.Content); + Assert.AreEqual (Color.Default, contentView.BackgroundColor); + Assert.AreEqual (new Thickness (0), contentView.Padding); + } + + [Test] + public void TestSetChild () + { + var contentView = new ContentView (); + + var child1 = new Label (); + + bool added = false; + + contentView.ChildAdded += (sender, e) => added = true; + + contentView.Content = child1; + + Assert.True (added); + Assert.AreEqual (child1, contentView.Content); + + added = false; + contentView.Content = child1; + + Assert.False (added); + } + + [Test] + public void TestReplaceChild () + { + var contentView = new ContentView (); + + var child1 = new Label (); + var child2 = new Label (); + + contentView.Content = child1; + + bool removed = false; + bool added = false; + + contentView.ChildRemoved += (sender, e) => removed = true; + contentView.ChildAdded += (sender, e) => added = true; + + contentView.Content = child2; + + Assert.True (removed); + Assert.True (added); + Assert.AreEqual (child2, contentView.Content); + } + + [Test] + public void TestFrameLayout () + { + View child; + + var contentView = new ContentView { + Padding = new Thickness (10), + Content = child = new View { + WidthRequest = 100, + HeightRequest = 200, + IsPlatformEnabled = true + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + Assert.AreEqual (new Size (120, 220), contentView.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request); + + contentView.Layout (new Rectangle (0, 0, 300, 300)); + + Assert.AreEqual (new Rectangle (10, 10, 280, 280), child.Bounds); + } + + [Test] + public void WidthRequest () + { + View child; + + var contentView = new ContentView { + Padding = new Thickness (10), + Content = child = new View { + WidthRequest = 100, + HeightRequest = 200, + IsPlatformEnabled = true + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + WidthRequest = 20 + }; + + Assert.AreEqual (new Size (40, 220), contentView.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request); + } + + [Test] + public void HeightRequest () + { + View child; + + var contentView = new ContentView { + Padding = new Thickness (10), + Content = child = new View { + WidthRequest = 100, + HeightRequest = 200, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + HeightRequest = 20 + }; + + Assert.AreEqual (new Size (120, 40), contentView.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request); + } + + [Test] + public void LayoutVerticallyCenter() + { + View child; + + var contentView = new ContentView { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.Center + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + contentView.Layout (new Rectangle(0,0, 200, 200)); + + Assert.AreEqual (new Rectangle (0, 50, 200, 100), child.Bounds); + } + + [Test] + public void LayoutVerticallyBegin() + { + View child; + + var contentView = new ContentView { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.Start + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + contentView.Layout (new Rectangle(0,0, 200, 200)); + + Assert.AreEqual (new Rectangle (0, 0, 200, 100), child.Bounds); + } + + [Test] + public void LayoutVerticallyEnd() + { + View child; + + var contentView = new ContentView { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.End + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + contentView.Layout (new Rectangle(0,0, 200, 200)); + + Assert.AreEqual (new Rectangle (0, 100, 200, 100), child.Bounds); + } + + [Test] + public void LayoutHorizontallyCenter() + { + View child; + + var contentView = new ContentView { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + HorizontalOptions = LayoutOptions.Center + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + contentView.Layout (new Rectangle(0,0, 200, 200)); + + Assert.AreEqual (new Rectangle (50, 0, 100, 200), child.Bounds); + } + + [Test] + public void LayoutHorizontallyBegin() + { + View child; + + var contentView = new ContentView { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + HorizontalOptions = LayoutOptions.Start + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + contentView.Layout (new Rectangle(0,0, 200, 200)); + + Assert.AreEqual (new Rectangle (0, 0, 100, 200), child.Bounds); + } + + [Test] + public void LayoutHorizontallyEnd() + { + View child; + + var contentView = new ContentView { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + HorizontalOptions = LayoutOptions.End + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + contentView.Layout (new Rectangle(0,0, 200, 200)); + + Assert.AreEqual (new Rectangle (100, 0, 100, 200), child.Bounds); + } + + [Test] + public void NullTemplateDirectlyHosts () + { + // order of setting properties carefully picked to emulate running on real backend + var platform = new UnitPlatform (); + + var contentView = new ContentView (); + var child = new View (); + + contentView.Content = child; + contentView.Platform = platform; + + Assert.AreEqual (child, contentView.LogicalChildren[0]); + } + + class SimpleTemplate : StackLayout + { + public SimpleTemplate () + { + Children.Add (new Label ()); + Children.Add (new ContentPresenter ()); + } + } + + + [Test] + public void TemplateInflates () + { + var platform = new UnitPlatform (); + + var contentView = new ContentView (); + + contentView.ControlTemplate = new ControlTemplate (typeof (SimpleTemplate)); + contentView.Platform = platform; + + Assert.That (contentView.LogicalChildren[0], Is.TypeOf<SimpleTemplate> ()); + } + + [Test] + public void PacksContent () + { + var platform = new UnitPlatform (); + + var contentView = new ContentView (); + var child = new View (); + + contentView.ControlTemplate = new ControlTemplate (typeof (SimpleTemplate)); + contentView.Content = child; + contentView.Platform = platform; + + Assume.That (contentView.LogicalChildren[0], Is.TypeOf<SimpleTemplate> ()); + Assert.That (contentView.Descendants (), Contains.Item (child)); + } + + [Test] + public void DoesNotInheritBindingContextToTemplate () + { + var platform = new UnitPlatform (); + + var contentView = new ContentView (); + var child = new View (); + + contentView.ControlTemplate = new ControlTemplate (typeof (SimpleTemplate)); + contentView.Content = child; + contentView.Platform = platform; + + var bc = "Test"; + contentView.BindingContext = bc; + + Assert.AreNotEqual (bc, contentView.LogicalChildren[0].BindingContext); + Assert.IsNull (contentView.LogicalChildren[0].BindingContext); + } + + [Test] + public void ContentDoesGetBindingContext () + { + var platform = new UnitPlatform (); + + var contentView = new ContentView (); + var child = new View (); + + contentView.ControlTemplate = new ControlTemplate (typeof (SimpleTemplate)); + contentView.Content = child; + contentView.Platform = platform; + + var bc = "Test"; + contentView.BindingContext = bc; + + Assert.AreEqual (bc, child.BindingContext); + } + + [Test] + public void ContentParentIsNotInsideTempalte () + { + var platform = new UnitPlatform (); + + var contentView = new ContentView (); + var child = new View (); + + contentView.ControlTemplate = new ControlTemplate (typeof (SimpleTemplate)); + contentView.Content = child; + contentView.Platform = platform; + + Assert.AreEqual (contentView, child.Parent); + } + + [Test] + public void NonTemplatedContentInheritsBindingContext () + { + var platform = new UnitPlatform (); + + var contentView = new ContentView (); + var child = new View (); + + contentView.Content = child; + contentView.Platform = platform; + contentView.BindingContext = "Foo"; + + Assert.AreEqual ("Foo", child.BindingContext); + } + } + +} diff --git a/Xamarin.Forms.Core.UnitTests/ContraintTypeConverterTests.cs b/Xamarin.Forms.Core.UnitTests/ContraintTypeConverterTests.cs new file mode 100644 index 00000000..e34f9a4a --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ContraintTypeConverterTests.cs @@ -0,0 +1,16 @@ +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ContraintTypeConverterTests : BaseTestFixture + { + [Test] + public void ConvertFrom () + { + var converter = new ConstraintTypeConverter (); + Assert.AreEqual (Constraint.Constant (1.0).Compute (null), ((Constraint)converter.ConvertFromInvariantString ("1.0")).Compute (null)); + Assert.AreEqual (Constraint.Constant (1.3).Compute (null), ((Constraint)converter.ConvertFromInvariantString ("1.3")).Compute (null)); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/ControlTemplateTests.cs b/Xamarin.Forms.Core.UnitTests/ControlTemplateTests.cs new file mode 100644 index 00000000..ffced2d2 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ControlTemplateTests.cs @@ -0,0 +1,181 @@ +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using NUnit.Framework; +using Xamarin.Forms; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ControlTemplateTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + public class ContentControl : StackLayout + { + public ContentControl () + { + var label = new Label (); + label.SetBinding (Label.TextProperty, new TemplateBinding ("Name")); + + Children.Add (label); + Children.Add (new ContentPresenter ()); + } + } + + public class PresenterWrapper : ContentView + { + public PresenterWrapper () + { + Content = new ContentPresenter (); + } + } + + public class TestView : ContentView + { + public static readonly BindableProperty NameProperty = + BindableProperty.Create (nameof (Name), typeof (string), typeof (TestView), default(string)); + + public string Name + { + get { return (string)GetValue (NameProperty); } + set { SetValue (NameProperty, value); } + } + + public TestView () + { + ControlTemplate = new ControlTemplate(typeof (ContentControl)); + } + } + + [Test] + public void ResettingControlTemplateNullsPresenterContent () + { + var testView = new TestView { + Platform = new UnitPlatform (), + ControlTemplate = new ControlTemplate (typeof (PresenterWrapper)) + }; + + var label = new Label (); + testView.Content = label; + var originalPresenter = (ContentPresenter)testView.LogicalChildren[0].LogicalChildren[0]; + + Assert.AreEqual (label, originalPresenter.Content); + + testView.ControlTemplate = new ControlTemplate (typeof (PresenterWrapper)); + + Assert.IsNull (originalPresenter.Content); + } + + [Test] + public void NestedTemplateBindings () + { + var testView = new TestView (); + var label = (Label)testView.LogicalChildren[0].LogicalChildren[0]; + + testView.Platform = new UnitPlatform (); + Assert.IsNull (label.Text); + + testView.Name = "Bar"; + Assert.AreEqual ("Bar", label.Text); + } + + [Test] + public void ParentControlTemplateDoesNotClearChildTemplate () + { + var parentView = new TestView (); + var childView = new TestView (); + parentView.Platform = new UnitPlatform (); + + parentView.Content = childView; + childView.Content = new Button (); + var childPresenter = (ContentPresenter)childView.LogicalChildren[0].LogicalChildren[1]; + + parentView.ControlTemplate = new ControlTemplate (typeof (ContentControl)); + Assert.IsNotNull (childPresenter.Content); + } + + [Test] + public void NullConstructor () + { + Assert.Throws<ArgumentNullException> (() => new ControlTemplate (null)); + } + + class TestPage : ContentPage + { + public static readonly BindableProperty NameProperty = + BindableProperty.Create (nameof (Name), typeof (string), typeof (TestPage), null); + + public string Name + { + get { return (string)GetValue (NameProperty); } + set { SetValue (NameProperty, value); } + } + } + + class TestContent : ContentView + { + public TestContent () + { + Content = new Entry (); + Content.SetBinding (Entry.TextProperty, new TemplateBinding ("Name", BindingMode.TwoWay)); + } + } + + class ViewModel : INotifyPropertyChanged + { + string name; + + public string Name + { + get { return name; } + set + { + if (name == value) + return; + name = value; + OnPropertyChanged (); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (propertyName)); + } + } + + [Test] + public void DoubleTwoWayBindingWorks () + { + var page = new TestPage (); + page.Platform = new UnitPlatform (); + var viewModel = new ViewModel { + Name = "Jason" + }; + page.BindingContext = viewModel; + + page.ControlTemplate = new ControlTemplate (typeof (TestContent)); + page.SetBinding (TestPage.NameProperty, "Name"); + + var entry = ((ContentView)page.LogicalChildren[0]).Content as Entry; + ((IElementController)entry).SetValueFromRenderer (Entry.TextProperty, "Bar"); + viewModel.Name = "Raz"; + + Assert.AreEqual ("Raz", entry.Text); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/DataTemplateSelectorTests.cs b/Xamarin.Forms.Core.UnitTests/DataTemplateSelectorTests.cs new file mode 100644 index 00000000..85779956 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/DataTemplateSelectorTests.cs @@ -0,0 +1,79 @@ +using System; +using NUnit.Framework; +using Xamarin.Forms; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class DataTemplateSelectorTests : BaseTestFixture + { + class TemplateOne : DataTemplate + { + public TemplateOne () : base (typeof (ViewCell)) + { + + } + } + + class TemplateTwo : DataTemplate + { + public TemplateTwo () : base (typeof (EntryCell)) + { + + } + } + + class TestDTS : DataTemplateSelector + { + public TestDTS () + { + templateOne = new TemplateOne (); + templateTwo = new TemplateTwo (); + } + + protected override DataTemplate OnSelectTemplate (object item, BindableObject container) + { + if (item is double) + return templateOne; + if (item is byte) + return new TestDTS (); + return templateTwo; + } + + readonly DataTemplate templateOne; + readonly DataTemplate templateTwo; + } + + [Test] + public void Constructor () + { + var dts = new TestDTS (); + } + + [Test] + public void ReturnsCorrectType () + { + var dts = new TestDTS (); + Assert.IsInstanceOf<TemplateOne> (dts.SelectTemplate (1d, null)); + Assert.IsInstanceOf<TemplateTwo> (dts.SelectTemplate ("test", null)); + } + + [Test] + public void ListViewSupport () + { + var listView = new ListView(ListViewCachingStrategy.RecycleElement); + listView.ItemsSource = new object[] { 0d, "test" }; + + listView.ItemTemplate = new TestDTS (); + Assert.IsInstanceOf<ViewCell> (listView.TemplatedItems[0]); + Assert.IsInstanceOf<EntryCell> (listView.TemplatedItems[1]); + } + + [Test] + public void NestingThrowsException () + { + var dts = new TestDTS (); + Assert.Throws<NotSupportedException> (() => dts.SelectTemplate ((byte)0, null)); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/DataTemplateTests.cs b/Xamarin.Forms.Core.UnitTests/DataTemplateTests.cs new file mode 100644 index 00000000..24bf6a06 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/DataTemplateTests.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class DataTemplateTests : BaseTestFixture + { + [Test] + public void CtorInvalid() + { + Assert.Throws<ArgumentNullException> (() => new DataTemplate ((Func<object>)null), + "Allowed null creator delegate"); + + Assert.Throws<ArgumentNullException> (() => new DataTemplate ((Type)null), + "Allowed null type"); + } + + [Test] + public void CreateContent() + { + var template = new DataTemplate (() => new MockBindable()); + object obj = template.CreateContent(); + + Assert.IsNotNull (obj); + Assert.That (obj, Is.InstanceOf<MockBindable>()); + } + + [Test] + public void CreateContentType() + { + var template = new DataTemplate (typeof (MockBindable)); + object obj = template.CreateContent(); + + Assert.IsNotNull (obj); + Assert.That (obj, Is.InstanceOf<MockBindable>()); + } + + [Test] + public void CreateContentValues() + { + var template = new DataTemplate (typeof (MockBindable)) { + Values = { { MockBindable.TextProperty, "value" } } + }; + + MockBindable bindable = (MockBindable)template.CreateContent(); + Assert.That (bindable.GetValue (MockBindable.TextProperty), Is.EqualTo ("value")); + } + + [Test] + public void CreateContentBindings() + { + var template = new DataTemplate (() => new MockBindable()) { + Bindings = { { MockBindable.TextProperty, new Binding (".") } } + }; + + MockBindable bindable = (MockBindable)template.CreateContent(); + bindable.BindingContext = "text"; + Assert.That (bindable.GetValue (MockBindable.TextProperty), Is.EqualTo ("text")); + } + + [Test] + public void SetBindingInvalid() + { + var template = new DataTemplate (typeof (MockBindable)); + Assert.That (() => template.SetBinding (null, new Binding (".")), Throws.InstanceOf<ArgumentNullException>()); + Assert.That (() => template.SetBinding (MockBindable.TextProperty, null), Throws.InstanceOf<ArgumentNullException>()); + } + + [Test] + public void SetBindingOverridesValue() + { + var template = new DataTemplate (typeof (MockBindable)); + template.SetValue (MockBindable.TextProperty, "value"); + template.SetBinding (MockBindable.TextProperty, new Binding (".")); + + MockBindable bindable = (MockBindable) template.CreateContent(); + Assume.That (bindable.GetValue (MockBindable.TextProperty), Is.EqualTo (bindable.BindingContext)); + + bindable.BindingContext = "binding"; + Assert.That (bindable.GetValue (MockBindable.TextProperty), Is.EqualTo ("binding")); + } + + [Test] + public void SetValueOverridesBinding() + { + var template = new DataTemplate (typeof (MockBindable)); + template.SetBinding (MockBindable.TextProperty, new Binding (".")); + template.SetValue (MockBindable.TextProperty, "value"); + + MockBindable bindable = (MockBindable) template.CreateContent(); + Assert.That (bindable.GetValue (MockBindable.TextProperty), Is.EqualTo ("value")); + bindable.BindingContext = "binding"; + Assert.That (bindable.GetValue (MockBindable.TextProperty), Is.EqualTo ("value")); + } + + [Test] + public void SetValueInvalid() + { + var template = new DataTemplate (typeof (MockBindable)); + Assert.That (() => template.SetValue (null, "string"), Throws.InstanceOf<ArgumentNullException>()); + } + + [Test] + public void SetValueAndBinding () + { + var template = new DataTemplate (typeof (TextCell)) { + Bindings = { + {TextCell.TextProperty, new Binding ("Text")} + }, + Values = { + {TextCell.TextProperty, "Text"} + } + }; + Assert.That (() => template.CreateContent (), Throws.InstanceOf<InvalidOperationException> ()); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/DataTriggerTests.cs b/Xamarin.Forms.Core.UnitTests/DataTriggerTests.cs new file mode 100644 index 00000000..585bebcf --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/DataTriggerTests.cs @@ -0,0 +1,146 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class DataTriggerTests : BaseTestFixture + { + class MockElement : VisualElement + { + } + + [Test] + public void SettersAppliedOnAttachIfConditionIsTrue () + { + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var datatrigger = new DataTrigger (typeof(VisualElement)) { + Binding = new Binding ("foo"), + Value = "foobar", + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + + element.SetValue (setterbp, "default"); + element.BindingContext = new {foo = "foobar"}; + Assert.AreEqual ("default", element.GetValue (setterbp)); + element.Triggers.Add (datatrigger); + Assert.AreEqual ("qux", element.GetValue (setterbp)); + } + + [Test] + public void SettersUnappliedOnDetach () + { + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var datatrigger = new DataTrigger (typeof(VisualElement)) { + Binding = new Binding ("foo"), + Value = "foobar", + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + + element.SetValue (setterbp, "default"); + element.Triggers.Add (datatrigger); + + Assert.AreEqual ("default", element.GetValue (setterbp)); + element.BindingContext = new {foo = "foobar"}; + Assert.AreEqual ("qux", element.GetValue (setterbp)); + element.Triggers.Remove (datatrigger); + Assert.AreEqual ("default", element.GetValue (setterbp)); + } + + [Test] + public void SettersAppliedOnConditionChanged () + { + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var trigger = new DataTrigger (typeof (VisualElement)){ + Binding = new Binding ("foo"), + Value = "foobar", + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + + element.SetValue (setterbp, "default"); + element.Triggers.Add (trigger); + + Assert.AreEqual ("default", element.GetValue (setterbp)); + element.BindingContext = new {foo = "foobar"}; + Assert.AreEqual ("qux", element.GetValue (setterbp)); + element.BindingContext = new {foo = ""}; + Assert.AreEqual ("default", element.GetValue (setterbp)); + } + + [Test] + public void TriggersAppliedOnMultipleElements () + { + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var trigger = new DataTrigger (typeof(VisualElement)) { + Binding = new Binding ("foo"), + Value = "foobar", + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + var element0 = new MockElement { Triggers = { trigger } }; + var element1 = new MockElement { Triggers = { trigger } }; + + element0.BindingContext = element1.BindingContext = new {foo = "foobar"}; + Assert.AreEqual ("qux", element0.GetValue (setterbp)); + Assert.AreEqual ("qux", element1.GetValue (setterbp)); + } + + [Test] + //https://bugzilla.xamarin.com/show_bug.cgi?id=30074 + public void AllTriggersUnappliedBeforeApplying () + { + var boxview = new BoxView { + Triggers = { + new DataTrigger (typeof(BoxView)) { + Binding = new Binding ("."), + Value = "Complete", + Setters = { + new Setter { Property = BoxView.ColorProperty, Value = Color.Green }, + new Setter { Property = VisualElement.OpacityProperty, Value = .5 }, + } + }, + new DataTrigger (typeof(BoxView)) { + Binding = new Binding ("."), + Value = "MissingInfo", + Setters = { + new Setter { Property = BoxView.ColorProperty, Value = Color.Yellow }, + } + }, + new DataTrigger (typeof(BoxView)) { + Binding = new Binding ("."), + Value = "Error", + Setters = { + new Setter { Property = BoxView.ColorProperty, Value = Color.Red }, + } + }, + } + }; + + boxview.BindingContext = "Complete"; + Assert.AreEqual (Color.Green, boxview.Color); + Assert.AreEqual (.5, boxview.Opacity); + + boxview.BindingContext = "MissingInfo"; + Assert.AreEqual (Color.Yellow, boxview.Color); + Assert.AreEqual (1, boxview.Opacity); + + boxview.BindingContext = "Error"; + Assert.AreEqual (Color.Red, boxview.Color); + Assert.AreEqual (1, boxview.Opacity); + + boxview.BindingContext = "Complete"; + Assert.AreEqual (Color.Green, boxview.Color); + Assert.AreEqual (.5, boxview.Opacity); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/DatePickerUnitTest.cs b/Xamarin.Forms.Core.UnitTests/DatePickerUnitTest.cs new file mode 100644 index 00000000..97bbf410 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/DatePickerUnitTest.cs @@ -0,0 +1,178 @@ +using System; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class DatePickerUnitTest : BaseTestFixture + { + [Test] + public void TestMinimumDateException () + { + DatePicker picker = new DatePicker (); + + picker.MinimumDate = new DateTime (1950, 1, 1); + + Assert.AreEqual (new DateTime (1950, 1, 1), picker.MinimumDate); + + Assert.That (() => picker.MinimumDate = new DateTime (2200, 1, 1), Throws.ArgumentException); + } + + [Test] + public void TestMaximumDateException () + { + DatePicker picker = new DatePicker (); + + picker.MaximumDate = new DateTime (2050, 1, 1); + + Assert.AreEqual (new DateTime (2050, 1, 1), picker.MaximumDate); + + Assert.That (() => picker.MaximumDate = new DateTime (1800, 1, 1), Throws.ArgumentException); + } + + [Test] + public void TestMaximumDateClamping () + { + DatePicker picker = new DatePicker (); + + picker.Date = new DateTime (2050, 1, 1); + + Assert.AreEqual (new DateTime (2050, 1, 1), picker.Date); + + bool dateChanged = false; + bool maximumDateChanged = false; + picker.PropertyChanged += (sender, e) => { + switch (e.PropertyName) { + case "MaximumDate": + maximumDateChanged = true; + break; + case "Date": + dateChanged = true; + Assert.IsFalse (maximumDateChanged); + break; + } + }; + + var newDate = new DateTime (2000, 1, 1); + picker.MaximumDate = newDate; + + Assert.IsTrue (maximumDateChanged); + Assert.IsTrue (dateChanged); + + Assert.AreEqual (newDate, picker.MaximumDate); + Assert.AreEqual (newDate, picker.Date); + Assert.AreEqual (picker.MaximumDate, picker.Date); + } + + [Test] + public void TestMinimumDateClamping () + { + DatePicker picker = new DatePicker (); + + picker.Date = new DateTime (1950, 1, 1); + + Assert.AreEqual (new DateTime (1950, 1, 1), picker.Date); + + bool dateChanged = false; + bool minimumDateChanged = false; + picker.PropertyChanged += (sender, e) => { + switch (e.PropertyName) { + case "MinimumDate": + minimumDateChanged = true; + break; + case "Date": + dateChanged = true; + Assert.IsFalse (minimumDateChanged); + break; + } + }; + + var newDate = new DateTime (2000, 1, 1); + picker.MinimumDate = newDate; + + Assert.IsTrue (minimumDateChanged); + Assert.IsTrue (dateChanged); + + Assert.AreEqual (newDate, picker.MinimumDate); + Assert.AreEqual (newDate, picker.Date); + Assert.AreEqual (picker.MinimumDate, picker.Date); + } + + [Test] + public void TestDateClamping () + { + DatePicker picker = new DatePicker (); + + picker.Date = new DateTime (1500, 1, 1); + + Assert.AreEqual (picker.MinimumDate, picker.Date); + + picker.Date = new DateTime (2500, 1, 1); + + Assert.AreEqual (picker.MaximumDate, picker.Date); + } + + [Test] + public void TestDateSelected () + { + var picker = new DatePicker (); + + bool selected = false; + picker.DateSelected += (sender, arg) => selected = true; + + // we can be fairly sure it wont ever be 2008 again + picker.Date = new DateTime (2008, 5, 5); + + Assert.True (selected); + } + + static object[] DateTimes = { + new object[] { new DateTime (2006, 12, 20), new DateTime (2011, 11, 30) }, + new object[] { new DateTime (1900, 1, 1), new DateTime (1999, 01, 15) }, // Minimum Date + new object[] { new DateTime (2006, 12, 20), new DateTime (2100, 12, 31) } // Maximum Date + }; + + [Test, TestCaseSource("DateTimes")] + public void DatePickerSelectedEventArgs (DateTime initialDate, DateTime finalDate) + { + var datePicker = new DatePicker (); + datePicker.Date = initialDate; + + DatePicker pickerFromSender = null; + DateTime oldDate = new DateTime (); + DateTime newDate = new DateTime (); + + datePicker.DateSelected += (s, e) => { + pickerFromSender = (DatePicker)s; + oldDate = e.OldDate; + newDate = e.NewDate; + }; + + datePicker.Date = finalDate; + + Assert.AreEqual (datePicker, pickerFromSender); + Assert.AreEqual (initialDate, oldDate); + Assert.AreEqual (finalDate, newDate); + } + + [Test] + //https://bugzilla.xamarin.com/show_bug.cgi?id=32144 + public void SetNullValueDoesNotThrow () + { + var datePicker = new DatePicker (); + Assert.DoesNotThrow (() => datePicker.SetValue (DatePicker.DateProperty, null)); + Assert.AreEqual (DatePicker.DateProperty.DefaultValue, datePicker.Date); + } + + [Test] + public void SetNullableDateTime () + { + var datePicker = new DatePicker (); + var dateTime = new DateTime (2015, 7, 21); + DateTime? nullableDateTime = dateTime; + datePicker.SetValue (DatePicker.DateProperty, nullableDateTime); + Assert.AreEqual (dateTime, datePicker.Date); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/DependencyServiceTests.cs b/Xamarin.Forms.Core.UnitTests/DependencyServiceTests.cs new file mode 100644 index 00000000..30e38b62 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/DependencyServiceTests.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; +using Xamarin.Forms; +using Xamarin.Forms.Core.UnitTests; + +[assembly: Dependency(typeof (DependencyTestImpl))] + +namespace Xamarin.Forms.Core.UnitTests +{ + public interface IDependencyTest + { + bool Works { get; } + } + + public interface IDependencyTestRegister + { + bool Works { get; } + } + + public interface IUnsatisfied + { + bool Broken { get; } + } + + public class DependencyTestImpl : IDependencyTest + { + public bool Works { get { return true; } } + } + + public class DependencyTestRegisterImpl : IDependencyTestRegister + { + public bool Works { get { return true; } } + } + + public class DependencyTestRegisterImpl2 : IDependencyTestRegister + { + public bool Works { get { return false; } } + } + + public class DependencyServiceTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void GetGlobalInstance () + { + var global = DependencyService.Get<IDependencyTest> (); + + Assert.NotNull (global); + + var secondFetch = DependencyService.Get<IDependencyTest> (); + + Assert.True (ReferenceEquals (global, secondFetch)); + } + + [Test] + public void NewInstanceIsNotGlobalInstance () + { + var global = DependencyService.Get<IDependencyTest> (); + + Assert.NotNull (global); + + var secondFetch = DependencyService.Get<IDependencyTest> (DependencyFetchTarget.NewInstance); + + Assert.False (ReferenceEquals (global, secondFetch)); + } + + [Test] + public void NewInstanceIsAlwaysNew () + { + var firstFetch = DependencyService.Get<IDependencyTest> (DependencyFetchTarget.NewInstance); + + Assert.NotNull (firstFetch); + + var secondFetch = DependencyService.Get<IDependencyTest> (DependencyFetchTarget.NewInstance); + + Assert.False (ReferenceEquals (firstFetch, secondFetch)); + } + + [Test] + public void UnsatisfiedReturnsNull () + { + Assert.Null (DependencyService.Get<IUnsatisfied> ()); + } + + [Test] + public void RegisterTypeImplementation () + { + DependencyService.Register<DependencyTestRegisterImpl> (); + var global = DependencyService.Get<DependencyTestRegisterImpl> (); + Assert.NotNull (global); + } + + + [Test] + public void RegisterInterfaceAndImplementations () + { + DependencyService.Register<IDependencyTestRegister, DependencyTestRegisterImpl2> (); + var global = DependencyService.Get<IDependencyTestRegister> (); + Assert.IsInstanceOf<DependencyTestRegisterImpl2> (global); + } + + [Test] + public void RegisterInterfaceAndOverrideImplementations () + { + DependencyService.Register<IDependencyTestRegister, DependencyTestRegisterImpl> (); + DependencyService.Register<IDependencyTestRegister, DependencyTestRegisterImpl2> (); + var global = DependencyService.Get<IDependencyTestRegister> (); + Assert.IsInstanceOf<DependencyTestRegisterImpl2> (global); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/DistanceTests.cs b/Xamarin.Forms.Core.UnitTests/DistanceTests.cs new file mode 100644 index 00000000..829bf957 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/DistanceTests.cs @@ -0,0 +1,197 @@ +using System; +using NUnit.Framework; +using Xamarin.Forms.Maps; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class DistanceTests : BaseTestFixture + { + [Test] + public void Constructor () + { + var distance = new Distance (25); + Assert.AreEqual (25, distance.Meters); + } + + [Test] + public void ConstructFromKilometers() + { + const double EPSILON = 0.001; + + Distance distance = Distance.FromKilometers(2); + + Assert.True(Math.Abs(distance.Kilometers - 2) < EPSILON); + Assert.True(Math.Abs(distance.Meters - 2000) < EPSILON); + Assert.True(Math.Abs(distance.Miles - 1.24274) < EPSILON); + } + + [Test] + public void ConstructFromMeters() + { + const double EPSILON = 0.001; + + Distance distance = Distance.FromMeters(10560); + + Assert.True(Math.Abs(distance.Meters - 10560) < EPSILON); + Assert.True(Math.Abs(distance.Miles - 6.5616798) < EPSILON); + Assert.True(Math.Abs(distance.Kilometers - 10.56) < EPSILON); + } + + [Test] + public void ConstructFromMiles() + { + const double EPSILON = 0.001; + + // Reached the limit of double precision using the number + // of miles of the earth's circumference + const double EPSILON_FOR_LARGE_MILES_TO_METERS = 16; + + // Reached the limit of double precision + const double EPSILON_FOR_LARGE_MILES_TO_KM = 0.1; + + Distance distance = Distance.FromMiles(3963.1676); + + Assert.True(Math.Abs(distance.Miles - 3963.1676) < EPSILON); + Assert.True(Math.Abs(distance.Meters - 6378099.99805) < EPSILON_FOR_LARGE_MILES_TO_METERS); + Assert.True(Math.Abs(distance.Kilometers - 6378.09999805) < EPSILON_FOR_LARGE_MILES_TO_KM); + } + + [Test] + public void EqualityOp([Range(5, 9)] double x, [Range(5, 9)] double y) + { + bool result = Distance.FromMeters(x) == Distance.FromMeters(y); + + if (x == y) + Assert.True(result); + else + Assert.False(result); + } + + [Test] + public void Equals([Range(3, 7)] double x, [Range(3, 7)] double y) + { + bool result = Distance.FromMiles(x).Equals(Distance.FromMiles(y)); + if (x == y) + Assert.True(result); + else + Assert.False(result); + } + + [Test] + public void EqualsNull() + { + Assert.False(Distance.FromMeters(5).Equals(null)); + } + + [Test] + public void GettingAndSettingKilometers() + { + const double EPSILON = 0.001; + + Distance distance = Distance.FromKilometers(1891); + Assert.True(Math.Abs(distance.Kilometers - 1891) < EPSILON); + } + + [Test] + public void GettingAndSettingMeters() + { + const double EPSILON = 0.001; + + Distance distance = Distance.FromMeters(123434); + Assert.True(Math.Abs(distance.Meters - 123434) < EPSILON); + } + + [Test] + public void GettingAndSettingMiles() + { + const double EPSILON = 0.001; + + Distance distance = Distance.FromMiles(515); + Assert.True(Math.Abs(distance.Miles - 515) < EPSILON); + } + + [Test] + public void HashCode([Range(4, 5)] double x, [Range(4, 5)] double y) + { + Distance distance1 = Distance.FromMiles(x); + Distance distance2 = Distance.FromMiles(y); + + bool result = distance1.GetHashCode() == distance2.GetHashCode(); + + if (x == y) + Assert.True(result); + else + Assert.False(result); + } + + [Test] + public void InequalityOp([Range(5, 9)] double x, [Range(5, 9)] double y) + { + bool result = Distance.FromMeters(x) != Distance.FromMeters(y); + + if (x != y) + Assert.True(result); + else + Assert.False(result); + } + + [Test] + public void ObjectInitializerKilometers() + { + const double EPSILON = 0.001; + + Distance distance = Distance.FromKilometers(10); + Assert.True(Math.Abs(distance.Meters - 10000) < EPSILON); + } + + [Test] + public void ObjectInitializerMeters() + { + const double EPSILON = 0.001; + + Distance distance = Distance.FromMeters(1057); + Assert.True(Math.Abs(distance.Kilometers - 1.057) < EPSILON); + } + + [Test] + public void ObjectInitializerMiles() + { + const double EPSILON = 0.001; + + Distance distance = Distance.FromMiles(100); + Assert.True(Math.Abs(distance.Meters - 160934.4) < EPSILON); + } + + [Test] + public void ClampFromMeters () + { + var distance = Distance.FromMeters (-1); + + Assert.AreEqual (0, distance.Meters); + } + + [Test] + public void ClampFromMiles () + { + var distance = Distance.FromMiles (-1); + + Assert.AreEqual (0, distance.Meters); + } + + [Test] + public void ClampFromKilometers () + { + var distance = Distance.FromKilometers (-1); + + Assert.AreEqual (0, distance.Meters); + } + + [Test] + public void Equals () + { + Assert.True (Distance.FromMiles (2).Equals ((object) Distance.FromMiles (2))); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/DynamicBindingContextTests.cs b/Xamarin.Forms.Core.UnitTests/DynamicBindingContextTests.cs new file mode 100644 index 00000000..53618909 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/DynamicBindingContextTests.cs @@ -0,0 +1,314 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class DynamicBindingContextTests + { + [Test] + public void BindingTwoWayToDynamicModel () + { + var view = new MockBindable (); + var model = new DynamicModel + { + Properties = + { + { "Title", "Foo" }, + } + }; + + view.SetBinding (MockBindable.TextProperty, "Title"); + view.BindingContext = model; + + Assert.AreEqual ("Foo", view.Text); + + view.Text = "Bar"; + + Assert.AreEqual ("Bar", model.Properties["Title"]); + } + + // This whole class and inner types is just a very simple + // dictionary-based dynamic model that proves that the + // approach works. It implements just the bare minimum of + // the base types to get our bindings to work properly and + // pass the tests. + class DynamicModel : IReflectableType + { + public DynamicModel () + { + Properties = new Dictionary<string, object> (); + } + + public TypeInfo GetTypeInfo () + { + return new DynamicTypeInfo (Properties); + } + + public IDictionary<string, object> Properties { get; private set; } + + class DynamicTypeInfo : TypeDelegator + { + IDictionary<string, object> properties; + + public DynamicTypeInfo (IDictionary<string, object> properties) + : base (typeof(object)) + { + this.properties = properties; + } + + public override PropertyInfo GetDeclaredProperty (string name) + { + if (!properties.ContainsKey (name)) + return null; + + return new DynamicPropertyInfo (properties, name); + } + + internal class DynamicPropertyInfo : PropertyInfo + { + IDictionary<string, object> properties; + string name; + + public DynamicPropertyInfo (IDictionary<string, object> properties, string name) + { + this.properties = properties; + this.name = name; + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanWrite + { + get { return true; } + } + + public override MethodInfo GetGetMethod (bool nonPublic) + { + return new DynamicPropertyGetterInfo (this, properties); + } + + public override MethodInfo GetSetMethod (bool nonPublic) + { + return new DynamicPropertySetterInfo (this, properties); + } + + public override Type PropertyType + { + get { return properties[name].GetType (); } + } + + public override string Name + { + get { return name; } + } + + public override PropertyAttributes Attributes + { + get { return PropertyAttributes.None; } + } + + public override MethodInfo[] GetAccessors (bool nonPublic) + { + return new[] { GetGetMethod (nonPublic), GetSetMethod (nonPublic) }; + } + + public override ParameterInfo[] GetIndexParameters () + { + return new ParameterInfo[0]; + } + + public override object GetValue (object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException (); + } + + public override void SetValue (object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException (); + } + + public override Type DeclaringType + { + get { throw new NotImplementedException (); } + } + + public override object[] GetCustomAttributes (Type attributeType, bool inherit) + { + throw new NotImplementedException (); + } + + public override object[] GetCustomAttributes (bool inherit) + { + throw new NotImplementedException (); + } + + public override bool IsDefined (Type attributeType, bool inherit) + { + throw new NotImplementedException (); + } + + public override Type ReflectedType + { + get { throw new NotImplementedException (); } + } + } + + internal class DynamicPropertyGetterInfo : DynamicPropertyMethodInfo + { + public DynamicPropertyGetterInfo (PropertyInfo property, IDictionary<string, object> properties) + : base (property, properties) + { + } + + public override object Invoke (object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + return Properties[Property.Name]; + } + + public override ParameterInfo[] GetParameters () + { + return new[] { new DynamicParameterInfo (Property, Property.PropertyType, "value") }; + } + + public override Type ReturnType + { + get { return Property.PropertyType; } + } + } + + internal class DynamicPropertySetterInfo : DynamicPropertyMethodInfo + { + public DynamicPropertySetterInfo (PropertyInfo property, IDictionary<string, object> properties) + : base (property, properties) + { + } + + public override object Invoke (object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture) + { + Properties[Property.Name] = parameters[0]; + return null; + } + + public override ParameterInfo[] GetParameters () + { + return new[] { + new DynamicParameterInfo (Property, typeof(DynamicTypeInfo), "this"), + new DynamicParameterInfo (Property, Property.PropertyType, "value") + }; + } + + public override Type ReturnType + { + get { return typeof(void); } + } + } + + internal abstract class DynamicPropertyMethodInfo : MethodInfo + { + public DynamicPropertyMethodInfo (PropertyInfo property, IDictionary<string, object> properties) + { + Property = property; + Properties = properties; + } + + protected PropertyInfo Property { get; private set; } + + protected IDictionary<string, object> Properties { get; private set; } + + public override MethodInfo GetBaseDefinition () + { + return null; + } + + public override ICustomAttributeProvider ReturnTypeCustomAttributes + { + get { return null; } + } + + public override MethodAttributes Attributes + { + get { return MethodAttributes.Public; } + } + + public override MethodImplAttributes GetMethodImplementationFlags () + { + return MethodImplAttributes.IL; + } + + public override ParameterInfo[] GetParameters () + { + return new ParameterInfo[0]; + } + + public override RuntimeMethodHandle MethodHandle + { + get { throw new NotImplementedException (); } + } + + public override Type DeclaringType + { + get { return typeof(DynamicModel); } + } + + public override object[] GetCustomAttributes (Type attributeType, bool inherit) + { + return new object[0]; + } + + public override object[] GetCustomAttributes (bool inherit) + { + return new object[0]; + } + + public override bool IsDefined (Type attributeType, bool inherit) + { + return false; + } + + public override string Name + { + get { return Property.Name; } + } + + public override Type ReflectedType + { + get { return typeof(DynamicModel); } + } + } + + internal class DynamicParameterInfo : ParameterInfo + { + MemberInfo member; + Type type; + + public DynamicParameterInfo (MemberInfo member, Type type, string name) + { + this.member = member; + this.type = type; + } + + public override MemberInfo Member + { + get { return member; } + } + + public override Type ParameterType + { + get { return type; } + } + } + } + + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/DynamicResourceTests.cs b/Xamarin.Forms.Core.UnitTests/DynamicResourceTests.cs new file mode 100644 index 00000000..faea9963 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/DynamicResourceTests.cs @@ -0,0 +1,153 @@ +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class DynamicResourceTests : BaseTestFixture + { + [SetUp] + public override void Setup () + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [Test] + public void TestDynamicResource () + { + var label = new Label (); + label.SetDynamicResource (Label.TextProperty, "foo"); + var layout = new StackLayout { + Children = { + label + } + }; + + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + + layout.Resources = new ResourceDictionary { + { "foo", "FOO" } + }; + Assert.AreEqual ("FOO", label.Text); + } + + [Test] + public void SetResourceTriggerSetValue () + { + var label = new Label (); + label.SetDynamicResource (Label.TextProperty, "foo"); + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + label.Resources = new ResourceDictionary { + {"foo", "FOO"} + }; + Assert.AreEqual ("FOO", label.Text); + } + + [Test] + public void SetResourceOnParentTriggerSetValue () + { + var label = new Label (); + var layout = new StackLayout { Children = {label}}; + label.SetDynamicResource (Label.TextProperty, "foo"); + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + layout.Resources = new ResourceDictionary { + {"foo", "FOO"} + }; + Assert.AreEqual ("FOO", label.Text); + } + + [Test] + public void SettingResourceTriggersValueChanged () + { + var label = new Label (); + label.SetDynamicResource (Label.TextProperty, "foo"); + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + label.Resources = new ResourceDictionary (); + label.Resources.Add ("foo", "FOO"); + Assert.AreEqual ("FOO", label.Text); + } + + [Test] + public void AddingAResourceDictionaryTriggersValueChangedForExistingValues () + { + var label = new Label (); + label.SetDynamicResource (Label.TextProperty, "foo"); + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + var rd = new ResourceDictionary { {"foo","FOO"}}; + label.Resources = rd; + Assert.AreEqual ("FOO", label.Text); + } + + [Test] + public void ValueChangedTriggeredOnSubscribeIfKeyAlreadyExists () + { + var label = new Label (); + label.Resources = new ResourceDictionary { {"foo","FOO"}}; + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + label.SetDynamicResource (Label.TextProperty, "foo"); + Assert.AreEqual ("FOO", label.Text); + } + + [Test] + public void RemoveDynamicResourceStopsUpdating () + { + var label = new Label (); + label.Resources = new ResourceDictionary { {"foo","FOO"}}; + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + label.SetDynamicResource (Label.TextProperty, "foo"); + Assert.AreEqual ("FOO", label.Text); + label.RemoveDynamicResource (Label.TextProperty); + label.Resources ["foo"] = "BAR"; + Assert.AreEqual ("FOO", label.Text); + } + + [Test] + public void ReparentResubscribe () + { + var layout0 = new ContentView { Resources = new ResourceDictionary {{"foo","FOO"}}}; + var layout1 = new ContentView { Resources = new ResourceDictionary {{"foo","BAR"}}}; + + var label = new Label (); + label.SetDynamicResource (Label.TextProperty, "foo"); + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + + layout0.Content = label; + Assert.AreEqual ("FOO", label.Text); + + layout0.Content = null; + layout1.Content = label; + Assert.AreEqual ("BAR", label.Text); + } + + [Test] + public void ClearedResourcesDoesNotClearValues () + { + var layout0 = new ContentView { Resources = new ResourceDictionary {{"foo","FOO"}}}; + var label = new Label (); + label.SetDynamicResource (Label.TextProperty, "foo"); + layout0.Content = label; + + Assert.AreEqual ("FOO", label.Text); + + layout0.Resources.Clear (); + Assert.AreEqual ("FOO", label.Text); + } + + [Test] + //Issue 2608 + public void ResourcesCanBeChanged () + { + var label = new Label (); + label.BindingContext = new MockViewModel (); + label.SetBinding (Label.TextProperty, "Text", BindingMode.TwoWay); + label.SetDynamicResource (Label.TextProperty, "foo"); + label.Resources = new ResourceDictionary { {"foo","FOO"}}; + + Assert.AreEqual ("FOO", label.Text); + + label.Resources ["foo"] = "BAR"; + + Assert.AreEqual ("BAR", label.Text); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/EasingTests.cs b/Xamarin.Forms.Core.UnitTests/EasingTests.cs new file mode 100644 index 00000000..be491ed8 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/EasingTests.cs @@ -0,0 +1,32 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class EasingTests : BaseTestFixture + { + [Test] + public void Linear ([Range (0, 10)] double input) + { + Assert.AreEqual (input, Easing.Linear.Ease (input)); + } + + [Test] + public void AllRunFromZeroToOne ([Values (0.0, 1.0)] double val) + { + const double epsilon = 0.001; + Assert.True (Math.Abs (val - Easing.Linear.Ease (val)) < epsilon); + Assert.True (Math.Abs (val - Easing.BounceIn.Ease (val)) < epsilon); + Assert.True (Math.Abs (val - Easing.BounceOut.Ease (val)) < epsilon); + Assert.True (Math.Abs (val - Easing.CubicIn.Ease (val)) < epsilon); + Assert.True (Math.Abs (val - Easing.CubicInOut.Ease (val)) < epsilon); + Assert.True (Math.Abs (val - Easing.CubicOut.Ease (val)) < epsilon); + Assert.True (Math.Abs (val - Easing.SinIn.Ease (val)) < epsilon); + Assert.True (Math.Abs (val - Easing.SinInOut.Ease (val)) < epsilon); + Assert.True (Math.Abs (val - Easing.SinOut.Ease (val)) < epsilon); + Assert.True (Math.Abs (val - Easing.SpringIn.Ease (val)) < epsilon); + Assert.True (Math.Abs (val - Easing.SpringOut.Ease (val)) < epsilon); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/EditorTests.cs b/Xamarin.Forms.Core.UnitTests/EditorTests.cs new file mode 100644 index 00000000..c16f1d2a --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/EditorTests.cs @@ -0,0 +1,34 @@ +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class EditorTests : BaseTestFixture + { + [TestCase ("Hi", "My text has changed")] + [TestCase (null, "My text has changed")] + [TestCase ("Hi", null)] + public void EditorTextChangedEventArgs (string initialText, string finalText) + { + var editor = new Editor { + Text = initialText + }; + + Editor editorFromSender = null; + string oldText = null; + string newText = null; + + editor.TextChanged += (s, e) => { + editorFromSender = (Editor)s; + oldText = e.OldTextValue; + newText = e.NewTextValue; + }; + + editor.Text = finalText; + + Assert.AreEqual (editor, editorFromSender); + Assert.AreEqual (initialText, oldText); + Assert.AreEqual (finalText, newText); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/EffectTests.cs b/Xamarin.Forms.Core.UnitTests/EffectTests.cs new file mode 100644 index 00000000..66399bc8 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/EffectTests.cs @@ -0,0 +1,121 @@ +using System; +using NUnit.Framework; + +using System.Collections.Generic; +using System.Linq; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class EffectTests : BaseTestFixture + { + [Test] + public void ResolveSetsId () + { + string id = "Unknown"; + var effect = Effect.Resolve (id); + Assert.AreEqual (id, effect.ResolveId); + } + + [Test] + public void UnknownIdReturnsNullEffect () + { + var effect = Effect.Resolve ("Foo"); + Assert.IsInstanceOf<NullEffect> (effect); + } + + [Test] + public void SendAttachedSetsFlag () + { + var effect = Effect.Resolve ("Foo"); + effect.SendAttached (); + Assert.True (effect.IsAttached); + } + + [Test] + public void SendDetachedUnsetsFlag () + { + var effect = Effect.Resolve ("Foo"); + effect.SendAttached (); + effect.SendDetached (); + Assert.False (effect.IsAttached); + } + + [Test] + public void EffectLifecyclePreProvider () + { + var effect = new CustomEffect (); + var element = new Label (); + + element.Effects.Add (effect); + ((IVisualElementController)element).EffectControlProvider = new EffectControlProvider (); + + Assert.True (effect.IsAttached); + Assert.True (effect.OnAttachedCalled); + Assert.True (effect.Registered); + Assert.False (effect.OnDetachedCalled); + + element.Effects.Remove (effect); + Assert.True (effect.OnDetachedCalled); + } + + [Test] + public void EffectLifecyclePostProvider () + { + var effect = new CustomEffect (); + var element = new Label (); + + ((IVisualElementController)element).EffectControlProvider = new EffectControlProvider (); + element.Effects.Add (effect); + + Assert.True (effect.IsAttached); + Assert.True (effect.OnAttachedCalled); + Assert.True (effect.Registered); + Assert.False (effect.OnDetachedCalled); + + element.Effects.Remove (effect); + Assert.True (effect.OnDetachedCalled); + } + + [Test] + public void EffectsClearDetachesEffect () + { + var effect = new CustomEffect (); + var element = new Label (); + + ((IVisualElementController)element).EffectControlProvider = new EffectControlProvider (); + element.Effects.Add (effect); + + element.Effects.Clear (); + + Assert.True (effect.OnDetachedCalled); + } + + class EffectControlProvider : IEffectControlProvider + { + public void RegisterEffect (Effect effect) + { + var e = effect as CustomEffect; + if (e != null) + e.Registered = true; + } + } + + class CustomEffect : Effect + { + public bool OnAttachedCalled; + public bool OnDetachedCalled; + public bool Registered; + + protected override void OnAttached () + { + OnAttachedCalled = true; + } + + protected override void OnDetached () + { + OnDetachedCalled = true; + } + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ElementTests.cs b/Xamarin.Forms.Core.UnitTests/ElementTests.cs new file mode 100644 index 00000000..6bffe338 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ElementTests.cs @@ -0,0 +1,176 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + public class TestElement + : Element + { + public TestElement () + { + internalChildren.CollectionChanged += OnChildrenChanged; + } + + void OnChildrenChanged (object sender, NotifyCollectionChangedEventArgs e) + { + if (e.NewItems != null) { + foreach (Element element in e.NewItems) + OnChildAdded (element); + } + + if (e.OldItems != null) { + foreach (Element element in e.OldItems) + OnChildRemoved (element); + } + } + + public IList<Element> Children + { + get { return internalChildren; } + } + + internal override ReadOnlyCollection<Element> LogicalChildren + { + get { return new ReadOnlyCollection<Element> (internalChildren); } + } + + readonly ObservableCollection<Element> internalChildren = new ObservableCollection<Element> (); + } + + [TestFixture] + public class ElementTests + : BaseTestFixture + { + [Test] + public void DescendantAddedLevel1 () + { + var root = new TestElement(); + + var child = new TestElement(); + + bool added = false; + root.DescendantAdded += (sender, args) => { + Assert.That (args.Element, Is.SameAs (child)); + added = true; + }; + + root.Children.Add (child); + } + + [Test] + public void DescendantAddedLevel2 () + { + var root = new TestElement(); + + var child = new TestElement(); + root.Children.Add (child); + + var child2 = new TestElement(); + + bool added = false; + root.DescendantAdded += (sender, args) => { + Assert.That (args.Element, Is.SameAs (child2)); + added = true; + }; + + child.Children.Add (child2); + } + + [Test] + public void DescendantAddedExistingChildren () + { + var root = new TestElement(); + + var tier2 = new TestElement(); + + var child = new TestElement { + Children = { + tier2 + } + }; + + bool tier1added = false; + bool tier2added = false; + root.DescendantAdded += (sender, args) => { + if (!tier1added) + tier1added = ReferenceEquals (child, args.Element); + if (!tier2added) + tier2added = ReferenceEquals (tier2, args.Element); + }; + + root.Children.Add (child); + + Assert.That (tier1added, Is.True); + Assert.That (tier2added, Is.True); + } + + [Test] + public void DescendantRemovedLevel1 () + { + var root = new TestElement(); + + var child = new TestElement(); + root.Children.Add (child); + + bool removed = false; + root.DescendantRemoved += (sender, args) => { + Assert.That (args.Element, Is.SameAs (child)); + removed = true; + }; + + root.Children.Remove (child); + } + + [Test] + public void DescendantRemovedLevel2 () + { + var root = new TestElement(); + + var child = new TestElement(); + root.Children.Add (child); + + var child2 = new TestElement(); + child.Children.Add (child2); + + bool removed = false; + root.DescendantRemoved += (sender, args) => { + Assert.That (args.Element, Is.SameAs (child2)); + removed = true; + }; + + child.Children.Remove (child2); + } + + [Test] + public void DescendantRemovedWithChildren () + { + var root = new TestElement(); + + var tier2 = new TestElement(); + + var child = new TestElement { + Children = { + tier2 + } + }; + + root.Children.Add (child); + + bool tier1removed = false; + bool tier2removed = false; + root.DescendantRemoved += (sender, args) => { + if (!tier1removed) + tier1removed = ReferenceEquals (child, args.Element); + if (!tier2removed) + tier2removed = ReferenceEquals (tier2, args.Element); + }; + + root.Children.Remove (child); + + Assert.That (tier1removed, Is.True); + Assert.That (tier2removed, Is.True); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/EntryCellTests.cs b/Xamarin.Forms.Core.UnitTests/EntryCellTests.cs new file mode 100644 index 00000000..34b7b663 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/EntryCellTests.cs @@ -0,0 +1,82 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class EntryCellTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [Test] + public void ChangingHorizontalTextAlignmentFiresXAlignChanged () + { + var entryCell = new EntryCell { HorizontalTextAlignment = TextAlignment.Center }; + + var xAlignFired = false; + var horizontalTextAlignmentFired = false; + + entryCell.PropertyChanged += (sender, args) => { + if (args.PropertyName == "XAlign") { + xAlignFired = true; + } else if (args.PropertyName == EntryCell.HorizontalTextAlignmentProperty.PropertyName) { + horizontalTextAlignmentFired = true; + } + }; + + entryCell.HorizontalTextAlignment = TextAlignment.End; + + Assert.True(xAlignFired); + Assert.True(horizontalTextAlignmentFired); + } + + [Test] + public void EntryCellXAlignBindingMatchesHorizontalTextAlignmentBinding () + { + var vm = new ViewModel (); + vm.Alignment = TextAlignment.Center; + + var entryCellXAlign = new EntryCell () { BindingContext = vm }; + entryCellXAlign.SetBinding (EntryCell.XAlignProperty, new Binding ("Alignment")); + + var entryCellHorizontalTextAlignment = new EntryCell () { BindingContext = vm }; + entryCellHorizontalTextAlignment.SetBinding (EntryCell.HorizontalTextAlignmentProperty, new Binding ("Alignment")); + + Assert.AreEqual (TextAlignment.Center, entryCellXAlign.XAlign); + Assert.AreEqual (TextAlignment.Center, entryCellHorizontalTextAlignment.HorizontalTextAlignment); + + vm.Alignment = TextAlignment.End; + + Assert.AreEqual (TextAlignment.End, entryCellXAlign.XAlign); + Assert.AreEqual (TextAlignment.End, entryCellHorizontalTextAlignment.HorizontalTextAlignment); + } + + sealed class ViewModel : INotifyPropertyChanged + { + TextAlignment alignment; + + public TextAlignment Alignment + { + get { return alignment; } + set + { + alignment = value; + OnPropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + void OnPropertyChanged ([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (propertyName)); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/EntryUnitTests.cs b/Xamarin.Forms.Core.UnitTests/EntryUnitTests.cs new file mode 100644 index 00000000..83f71b9a --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/EntryUnitTests.cs @@ -0,0 +1,55 @@ +using System.Diagnostics; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class EntryUnitTests : BaseTestFixture + { + [Test] + public void ValueChangedFromSetValue() + { + var entry = new Entry(); + + const string value = "Foo"; + + bool signaled = false; + entry.TextChanged += (sender, args) => { + signaled = true; + Assert.AreEqual (value, args.NewTextValue); + }; + + entry.SetValue (Entry.TextProperty, value); + + Assert.IsTrue (signaled, "ValueChanged did not fire"); + } + + [TestCase (null, "foo")] + [TestCase ("foo", "bar")] + [TestCase ("foo", null)] + public void ValueChangedArgs (string initial, string final) + { + var entry = new Entry { + Text = initial + }; + + string oldValue = null; + string newValue = null; + + Entry entryFromSender = null; + + entry.TextChanged += (s, e) => { + entryFromSender = (Entry)s; + oldValue = e.OldTextValue; + newValue = e.NewTextValue; + }; + + entry.Text = final; + + Assert.AreEqual (entry, entryFromSender); + Assert.AreEqual (initial, oldValue); + Assert.AreEqual (final, newValue); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/EventTriggerTest.cs b/Xamarin.Forms.Core.UnitTests/EventTriggerTest.cs new file mode 100644 index 00000000..89034a03 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/EventTriggerTest.cs @@ -0,0 +1,73 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + internal class MockTriggerAction : TriggerAction<BindableObject> + { + public bool Invoked { get; set;} + + protected override void Invoke (BindableObject sender) + { + Invoked = true; + } + } + + internal class MockBindableWithEvent : VisualElement + { + public void FireEvent () + { + if (MockEvent != null) + MockEvent (this, EventArgs.Empty); + } + + public void FireEvent2 () + { + if (MockEvent2 != null) + MockEvent2 (this, EventArgs.Empty); + } + + public event EventHandler MockEvent; + public event EventHandler MockEvent2; + } + + [TestFixture] + public class EventTriggerTest : BaseTestFixture + { + [Test] + public void TestTriggerActionInvoked () + { + var bindable = new MockBindableWithEvent (); + var triggeraction = new MockTriggerAction (); + var eventtrigger = new EventTrigger () { Event = "MockEvent", Actions = { triggeraction } }; + var collection = bindable.Triggers; + collection.Add (eventtrigger); + + Assert.False (triggeraction.Invoked); + bindable.FireEvent (); + Assert.True (triggeraction.Invoked); + } + + [Test] + public void TestChangeEventOnEventTrigger () + { + var bindable = new MockBindableWithEvent (); + var triggeraction = new MockTriggerAction (); + var eventtrigger = new EventTrigger { Event = "MockEvent", Actions = { triggeraction } }; + var collection = bindable.Triggers; + collection.Add (eventtrigger); + + triggeraction.Invoked = false; + Assert.False (triggeraction.Invoked); + bindable.FireEvent (); + Assert.True (triggeraction.Invoked); + + triggeraction.Invoked = false; + Assert.False (triggeraction.Invoked); + bindable.FireEvent2 (); + Assert.False (triggeraction.Invoked); + + Assert.Throws<InvalidOperationException>(()=> eventtrigger.Event = "MockEvent2"); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/FluentTests.cs b/Xamarin.Forms.Core.UnitTests/FluentTests.cs new file mode 100644 index 00000000..b8780adb --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/FluentTests.cs @@ -0,0 +1,8 @@ +using System.Linq; +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + +} diff --git a/Xamarin.Forms.Core.UnitTests/FontUnitTests.cs b/Xamarin.Forms.Core.UnitTests/FontUnitTests.cs new file mode 100644 index 00000000..dcf972f9 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/FontUnitTests.cs @@ -0,0 +1,129 @@ +using System.Globalization; + +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class FontUnitTests : BaseTestFixture + { + [Test] + public void TestFontForSize () + { + var font = Font.OfSize ("Foo", 12); + Assert.AreEqual ("Foo", font.FontFamily); + Assert.AreEqual (12, font.FontSize); + Assert.AreEqual ((NamedSize)0, font.NamedSize); + } + + [Test] + public void TestFontForSizeDouble () + { + var font = Font.OfSize ("Foo", 12.7); + Assert.AreEqual ("Foo", font.FontFamily); + Assert.AreEqual (12.7, font.FontSize); + Assert.AreEqual ((NamedSize)0, font.NamedSize); + } + + [Test] + public void TestFontForNamedSize () + { + var font = Font.OfSize ("Foo", NamedSize.Large); + Assert.AreEqual ("Foo", font.FontFamily); + Assert.AreEqual (0, font.FontSize); + Assert.AreEqual (NamedSize.Large, font.NamedSize); + } + + [Test] + public void TestSystemFontOfSize () + { + var font = Font.SystemFontOfSize (12); + Assert.AreEqual (null, font.FontFamily); + Assert.AreEqual (12, font.FontSize); + Assert.AreEqual ((NamedSize)0, font.NamedSize); + + font = Font.SystemFontOfSize (NamedSize.Medium); + Assert.AreEqual (null, font.FontFamily); + Assert.AreEqual (0, font.FontSize); + Assert.AreEqual (NamedSize.Medium, font.NamedSize); + } + + [Test] + public void CultureTestSystemFontOfSizeDouble () + { + var font = Font.SystemFontOfSize (12.7); + Assert.AreEqual (null, font.FontFamily); + Assert.AreEqual (12.7, font.FontSize); + Assert.AreEqual ((NamedSize)0, font.NamedSize); + + font = Font.SystemFontOfSize (NamedSize.Medium); + Assert.AreEqual (null, font.FontFamily); + Assert.AreEqual (0, font.FontSize); + Assert.AreEqual (NamedSize.Medium, font.NamedSize); + } + + [Test] + public void TestEquality () + { + var font1 = Font.SystemFontOfSize (12); + var font2 = Font.SystemFontOfSize (12); + + Assert.True (font1 == font2); + Assert.False (font1 != font2); + + font2 = Font.SystemFontOfSize (13); + + Assert.False (font1 == font2); + Assert.True (font1 != font2); + } + + [Test] + public void TestHashCode () + { + var font1 = Font.SystemFontOfSize (12); + var font2 = Font.SystemFontOfSize (12); + + Assert.True (font1.GetHashCode () == font2.GetHashCode ()); + + font2 = Font.SystemFontOfSize (13); + + Assert.False (font1.GetHashCode () == font2.GetHashCode ()); + } + + [Test] + public void TestEquals () + { + var font = Font.SystemFontOfSize (12); + + Assert.False (font.Equals (null)); + Assert.True (font.Equals (font)); + Assert.False (font.Equals ("Font")); + Assert.True (font.Equals (Font.SystemFontOfSize (12))); + } + + [Test] + public void TestFontConverter () + { + var converter = new FontTypeConverter (); + Assert.True (converter.CanConvertFrom (typeof(string))); + Assert.AreEqual (Font.SystemFontOfSize (NamedSize.Medium), converter.ConvertFromInvariantString ("Medium")); + Assert.AreEqual (Font.SystemFontOfSize (42), converter.ConvertFromInvariantString ("42")); + Assert.AreEqual (Font.OfSize ("Foo", NamedSize.Micro), converter.ConvertFromInvariantString ("Foo, Micro")); + Assert.AreEqual (Font.OfSize ("Foo", 42), converter.ConvertFromInvariantString ("Foo, 42")); + Assert.AreEqual (Font.OfSize ("Foo", 12.7), converter.ConvertFromInvariantString ("Foo, 12.7")); + Assert.AreEqual (Font.SystemFontOfSize (NamedSize.Large, FontAttributes.Bold), converter.ConvertFromInvariantString ("Bold, Large")); + Assert.AreEqual (Font.SystemFontOfSize (42, FontAttributes.Bold), converter.ConvertFromInvariantString ("Bold, 42")); + Assert.AreEqual (Font.OfSize ("Foo", NamedSize.Medium), converter.ConvertFromInvariantString ("Foo")); + Assert.AreEqual (Font.OfSize ("Foo", NamedSize.Large).WithAttributes (FontAttributes.Bold), converter.ConvertFromInvariantString ("Foo, Bold, Large")); + Assert.AreEqual (Font.OfSize ("Foo", NamedSize.Large).WithAttributes (FontAttributes.Italic), converter.ConvertFromInvariantString ("Foo, Italic, Large")); + Assert.AreEqual (Font.OfSize ("Foo", NamedSize.Large).WithAttributes (FontAttributes.Bold | FontAttributes.Italic), converter.ConvertFromInvariantString ("Foo, Bold, Italic, Large")); + Assert.AreEqual (Font.OfSize ("Foo", 12).WithAttributes (FontAttributes.Bold), converter.ConvertFromInvariantString ("Foo, Bold, 12")); + Assert.AreEqual (Font.OfSize ("Foo", 12.7).WithAttributes (FontAttributes.Bold), converter.ConvertFromInvariantString ("Foo, Bold, 12.7")); + Assert.AreEqual (Font.OfSize ("Foo", 12).WithAttributes (FontAttributes.Italic), converter.ConvertFromInvariantString ("Foo, Italic, 12")); + Assert.AreEqual (Font.OfSize ("Foo", 12.7).WithAttributes (FontAttributes.Italic), converter.ConvertFromInvariantString ("Foo, Italic, 12.7")); + Assert.AreEqual (Font.OfSize ("Foo", 12).WithAttributes (FontAttributes.Bold | FontAttributes.Italic), converter.ConvertFromInvariantString ("Foo, Bold, Italic, 12")); + Assert.AreEqual (Font.OfSize ("Foo", 12.7).WithAttributes (FontAttributes.Bold | FontAttributes.Italic), converter.ConvertFromInvariantString ("Foo, Bold, Italic, 12.7")); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/FormattedStringTests.cs b/Xamarin.Forms.Core.UnitTests/FormattedStringTests.cs new file mode 100644 index 00000000..7c247bda --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/FormattedStringTests.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.ObjectModel; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class FormattedStringTests : BaseTestFixture + { + [SetUp] + public override void Setup () + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown () + { + base.Setup (); + Device.PlatformServices = null; + } + + [Test] + public void NullSpansNotAllowed() + { + var fs = new FormattedString(); + Assert.That (() => fs.Spans.Add (null), Throws.InstanceOf<ArgumentNullException>()); + + fs = new FormattedString(); + fs.Spans.Add (new Span()); + + Assert.That (() => { + fs.Spans[0] = null; + }, Throws.InstanceOf<ArgumentNullException>()); + } + + [Test] + public void SpanChangeTriggersSpansPropertyChange() + { + var span = new Span(); + var fs = new FormattedString(); + fs.Spans.Add (span); + + bool spansChanged = false; + fs.PropertyChanged += (s, e) => { + if (e.PropertyName == "Spans") + spansChanged = true; + }; + + span.Text = "New text"; + + Assert.That (spansChanged, Is.True); + } + + [Test] + public void SpanChangesUnsubscribes() + { + var span = new Span(); + var fs = new FormattedString(); + fs.Spans.Add (span); + fs.Spans.Remove (span); + + bool spansChanged = false; + fs.PropertyChanged += (s, e) => { + if (e.PropertyName == "Spans") + spansChanged = true; + }; + + span.Text = "New text"; + + Assert.That (spansChanged, Is.False); + } + + [Test] + public void AddingSpanTriggersSpansPropertyChange() + { + var span = new Span(); + var fs = new FormattedString(); + + bool spansChanged = false; + fs.PropertyChanged += (s, e) => { + if (e.PropertyName == "Spans") + spansChanged = true; + }; + + fs.Spans.Add (span); + + Assert.That (spansChanged, Is.True); + } + + [Test] + public void ImplicitStringConversion() + { + string original = "fubar"; + FormattedString fs = original; + Assert.That (fs, Is.Not.Null); + Assert.That (fs.Spans.Count, Is.EqualTo (1)); + Assert.That (fs.Spans[0], Is.Not.Null); + Assert.That (fs.Spans[0].Text, Is.EqualTo (original)); + } + + [Test] + public void ImplicitStringConversionNull() + { + string original = null; + FormattedString fs = original; + Assert.That (fs, Is.Not.Null); + Assert.That (fs.Spans.Count, Is.EqualTo (1)); + Assert.That (fs.Spans[0], Is.Not.Null); + Assert.That (fs.Spans[0].Text, Is.EqualTo (original)); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/FrameUnitTests.cs b/Xamarin.Forms.Core.UnitTests/FrameUnitTests.cs new file mode 100644 index 00000000..e7bf5974 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/FrameUnitTests.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class FrameUnitTests : BaseTestFixture + { + [Test] + public void TestConstructor () + { + Frame frame = new Frame (); + + Assert.Null (frame.Content); + Assert.AreEqual (new Thickness (20, 20, 20, 20), frame.Padding); + } + + [Test] + public void TestPackWithoutChild () + { + Frame frame = new Frame (); + + var parent = new NaiveLayout (); + + bool thrown = false; + try { + parent.Children.Add (frame); + } catch { + thrown = true; + } + + Assert.False (thrown); + } + + [Test] + public void TestPackWithChild () + { + Frame frame = new Frame { + Content = new View () + }; + + var parent = new NaiveLayout (); + + bool thrown = false; + try { + parent.Children.Add (frame); + } catch { + thrown = true; + } + + Assert.False (thrown); + } + + [Test] + public void TestSetChild () + { + Frame frame = new Frame (); + + var child1 = new Label (); + + bool added = false; + + frame.ChildAdded += (sender, e) => added = true; + + frame.Content = child1; + + Assert.True (added); + Assert.AreEqual (child1, frame.Content); + + added = false; + frame.Content = child1; + + Assert.False (added); + } + + [Test] + public void TestReplaceChild () + { + Frame frame = new Frame (); + + var child1 = new Label (); + var child2 = new Label (); + + frame.Content = child1; + + bool removed = false; + bool added = false; + + frame.ChildRemoved += (sender, e) => removed = true; + frame.ChildAdded += (sender, e) => added = true; + + frame.Content = child2; + + Assert.True (removed); + Assert.True (added); + Assert.AreEqual (child2, frame.Content); + } + + [Test] + public void TestFrameLayout () + { + View child; + + var frame = new Frame { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 200, + IsPlatformEnabled = true + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + Assert.AreEqual (new Size (140, 240), frame.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request); + + frame.Layout (new Rectangle (0, 0, 300, 300)); + + Assert.AreEqual (new Rectangle (20, 20, 260, 260), child.Bounds); + } + + [Test] + public void TestDoesNotThrowOnSetNullChild () + { + Assert.DoesNotThrow (() => new Frame {Content = null}); + } + + [Test] + public void WidthRequest () + { + var frame = new Frame { + Content = new View { + WidthRequest = 100, + HeightRequest = 200, + IsPlatformEnabled = true + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + WidthRequest = 20 + }; + + Assert.AreEqual (new Size (60, 240), frame.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request); + } + + [Test] + public void HeightRequest () + { + var frame = new Frame { + Content = new View { + WidthRequest = 100, + HeightRequest = 200, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + HeightRequest = 20 + }; + + Assert.AreEqual (new Size (140, 60), frame.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request); + } + + [Test] + public void LayoutVerticallyCenter () + { + View child; + + var frame = new Frame { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.Center + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + frame.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (20, 50, 160, 100), child.Bounds); + } + + [Test] + public void LayoutVerticallyBegin() + { + View child; + + var frame = new Frame { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.Start + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + frame.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (20, 20, 160, 100), child.Bounds); + } + + [Test] + public void LayoutVerticallyEnd() + { + View child; + + var frame = new Frame { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.End + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + frame.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (20, 80, 160, 100), child.Bounds); + } + + [Test] + public void LayoutHorizontallyCenter() + { + View child; + + var frame = new Frame { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + HorizontalOptions = LayoutOptions.Center + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + frame.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (50, 20, 100, 160), child.Bounds); + } + + [Test] + public void LayoutHorizontallyBegin() + { + View child; + + var frame = new Frame { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + HorizontalOptions = LayoutOptions.Start + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + frame.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (20, 20, 100, 160), child.Bounds); + } + + [Test] + public void LayoutHorizontallyEnd() + { + View child; + + var frame = new Frame { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 100, + IsPlatformEnabled = true, + HorizontalOptions = LayoutOptions.End + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + frame.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (80, 20, 100, 160), child.Bounds); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/GeocoderUnitTests.cs b/Xamarin.Forms.Core.UnitTests/GeocoderUnitTests.cs new file mode 100644 index 00000000..414c49de --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/GeocoderUnitTests.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using NUnit.Framework; +using Xamarin.Forms.Maps; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class GeocoderUnitTests : BaseTestFixture + { + [Test] + public async void AddressesForPosition () + { + Geocoder.GetAddressesForPositionFuncAsync = GetAddressesForPositionFuncAsync; + var geocoder = new Geocoder (); + var result = await geocoder.GetAddressesForPositionAsync (new Position (1, 2)); + Assert.AreEqual (new String[] { "abc", "def" }, result); + } + + async Task<IEnumerable<string>> GetAddressesForPositionFuncAsync (Position position) + { + Assert.AreEqual (new Position (1, 2), position); + return new string[] {"abc", "def"}; + } + + [Test] + public async void PositionsForAddress () { + Geocoder.GetPositionsForAddressAsyncFunc = GetPositionsForAddressAsyncFunc ; + var geocoder = new Geocoder (); + var result = await geocoder.GetPositionsForAddressAsync ("quux"); + Assert.AreEqual (new Position [] { new Position (1, 2), new Position (3, 4) }, result); + } + + async Task<IEnumerable<Position>> GetPositionsForAddressAsyncFunc (string address) + { + Assert.AreEqual ("quux", address); + return new Position[] {new Position (1, 2), new Position (3, 4)}; + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/GridLengthTypeConverterTests.cs b/Xamarin.Forms.Core.UnitTests/GridLengthTypeConverterTests.cs new file mode 100644 index 00000000..4c1f21f7 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/GridLengthTypeConverterTests.cs @@ -0,0 +1,53 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class GridLengthTypeConverterTests : BaseTestFixture + { + [Test] + public void TestAbsolute () + { + var converter = new GridLengthTypeConverter (); + + Assert.AreEqual (new GridLength (42), converter.ConvertFromInvariantString ("42")); + Assert.AreEqual (new GridLength (42.2), converter.ConvertFromInvariantString ("42.2")); + + Assert.Throws<FormatException> (() => converter.ConvertFromInvariantString ("foo")); + } + + [Test] + public void TestAuto () + { + var converter = new GridLengthTypeConverter (); + + Assert.AreEqual (GridLength.Auto, converter.ConvertFromInvariantString ("auto")); + Assert.AreEqual (GridLength.Auto, converter.ConvertFromInvariantString (" AuTo ")); + } + + [Test] + public void TestStar () + { + var converter = new GridLengthTypeConverter (); + + Assert.AreEqual (new GridLength (1, GridUnitType.Star), converter.ConvertFromInvariantString ("*")); + Assert.AreEqual (new GridLength (42, GridUnitType.Star), converter.ConvertFromInvariantString ("42*")); + + } + + [Test] + public void TestValue () + { + var converter = new GridLengthTypeConverter (); + Assert.AreEqual (new GridLength(3.3), converter.ConvertFromInvariantString("3.3")); + } + + [Test] + public void TestValueStar () + { + var converter = new GridLengthTypeConverter (); + Assert.AreEqual (new GridLength(32.3, GridUnitType.Star), converter.ConvertFromInvariantString("32.3*")); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/GridTests.cs b/Xamarin.Forms.Core.UnitTests/GridTests.cs new file mode 100644 index 00000000..203d3b64 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/GridTests.cs @@ -0,0 +1,1569 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class GridTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void ThrowsOnNullAdd () + { + var layout = new Grid (); + + Assert.Throws<ArgumentNullException> (() => layout.Children.Add (null)); + } + + [Test] + public void ThrowsOnNullRemove () + { + var layout = new Grid (); + + Assert.Throws<ArgumentNullException> (() => layout.Children.Remove (null)); + } + + [Test] + public void TestBasicVerticalLayout () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.Children.AddVertical (new View[] { + label1, + label2, + label3 + }); + + layout.Layout (new Rectangle (0, 0, 912, 912)); + + Assert.AreEqual (912, layout.Width); + Assert.AreEqual (912, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 912, 300), label1.Bounds); + Assert.AreEqual (new Rectangle (0, 306, 912, 300), label2.Bounds); + Assert.AreEqual (new Rectangle (0, 612, 912, 300), label3.Bounds); + } + + [Test] + public void TestBasicHorizontalLayout () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.Children.AddHorizontal (new View[] { + label1, + label2, + label3 + }); + + layout.Layout (new Rectangle (0, 0, 912, 912)); + + Assert.AreEqual (912, layout.Width); + Assert.AreEqual (912, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 300, 912), label1.Bounds); + Assert.AreEqual (new Rectangle (306, 0, 300, 912), label2.Bounds); + Assert.AreEqual (new Rectangle (612, 0, 300, 912), label3.Bounds); + } + + [Test] + public void TestVerticalExpandStart () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition { Height = new GridLength (1, GridUnitType.Star) }, + new RowDefinition { Height = GridLength.Auto}, + }; + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 0, 1); + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 1000, 1000 - 20 - layout.RowSpacing), label1.Bounds); + Assert.AreEqual (new Rectangle (0, 1000 - 20, 1000, 20), label2.Bounds); + } + + [Test] + public void TestHorizontalExpandStart () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label { Platform = platform, IsPlatformEnabled = true }; + var label2 = new Label { Platform = platform, IsPlatformEnabled = true }; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) }, + new ColumnDefinition { Width = GridLength.Auto }, + }; + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 1, 0); + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 1000 - 106, 1000), label1.Bounds); + Assert.AreEqual (new Rectangle (1000 - 100, 0, 100, 1000), label2.Bounds); + } + + [Test] + public void TestVerticalExpandEnd () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition { Height = GridLength.Auto}, + new RowDefinition { Height = new GridLength (1, GridUnitType.Star) }, + }; + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 0, 1); + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 1000, 20), label1.Bounds); + Assert.AreEqual (new Rectangle (0, 26, 1000, 1000 - 26), label2.Bounds); + } + + [Test] + public void TestHorizontalExpandEnd () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label { Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition { Width = GridLength.Auto }, + new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) }, + }; + + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 1, 0); + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 100, 1000), label1.Bounds); + Assert.AreEqual (new Rectangle (106, 0, 1000 - 106, 1000), label2.Bounds); + } + + [Test] + public void TestVerticalExpandMiddle () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition { Height = GridLength.Auto}, + new RowDefinition { Height = new GridLength (1, GridUnitType.Star) }, + new RowDefinition { Height = GridLength.Auto} + }; + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 0, 1); + layout.Children.Add (label3, 0, 2); + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 1000, 20), label1.Bounds); + Assert.AreEqual (new Rectangle (0, 26, 1000, 1000 - 52), label2.Bounds); + Assert.AreEqual (new Rectangle (0, 980, 1000, 20), label3.Bounds); + } + + [Test] + public void TestHorizontalExpandMiddle () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition { Width = GridLength.Auto }, + new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) }, + new ColumnDefinition { Width = GridLength.Auto }, + }; + + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 1, 0); + layout.Children.Add (label3, 2, 0); + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 100, 1000), label1.Bounds); + Assert.AreEqual (new Rectangle (106, 0, 1000 - 212, 1000), label2.Bounds); + Assert.AreEqual (new Rectangle (900, 0, 100, 1000), label3.Bounds); + } + + [Test] + public void TestTableNoExpand () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label4 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 1, 0); + layout.Children.Add (label3, 0, 1); + layout.Children.Add (label4, 1, 1); + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition { Width = GridLength.Auto }, + new ColumnDefinition { Width = GridLength.Auto }, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition { Height = GridLength.Auto}, + new RowDefinition { Height = GridLength.Auto} + }; + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 100, 20), label1.Bounds); + Assert.AreEqual (new Rectangle (106, 0, 100, 20), label2.Bounds); + Assert.AreEqual (new Rectangle (0, 26, 100, 20), label3.Bounds); + Assert.AreEqual (new Rectangle (106, 26, 100, 20), label4.Bounds); + } + + [Test] + public void TestTableExpand () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label4 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition { Width = GridLength.Auto }, + new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) }, + }; + + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 1, 0); + layout.Children.Add (label3, 0, 1); + layout.Children.Add (label4, 1, 1); + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 100, 497), label1.Bounds); + Assert.AreEqual (new Rectangle (106, 0, 894, 497), label2.Bounds); + Assert.AreEqual (new Rectangle (0, 503, 100, 497), label3.Bounds); + Assert.AreEqual (new Rectangle (106, 503, 894, 497), label4.Bounds); + } + + [Test] + public void TestTableSpan () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.Children.Add (label1, 0, 2, 0, 1); + layout.Children.Add (label2, 0, 1, 1, 2); + layout.Children.Add (label3, 1, 2, 1, 2); + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition { Width = GridLength.Auto }, + new ColumnDefinition { Width = GridLength.Auto }, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition { Height = GridLength.Auto}, + new RowDefinition { Height = GridLength.Auto} + }; + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 206, 20), label1.Bounds); + Assert.AreEqual (new Rectangle (0, 26, 100, 20), label2.Bounds); + Assert.AreEqual (new Rectangle (106, 26, 100, 20), label3.Bounds); + } + + [Test] + public void TestTableExpandedSpan () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) }, + new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) }, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition { Height = GridLength.Auto}, + new RowDefinition { Height = GridLength.Auto} + }; + + layout.Children.Add (label1, 0, 2, 0, 1); + layout.Children.Add (label2, 0, 1, 1, 2); + layout.Children.Add (label3, 1, 2, 1, 2); + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 1000, 20), label1.Bounds); + Assert.AreEqual (new Rectangle (0, 26, 497, 20), label2.Bounds); + Assert.AreEqual (new Rectangle (503, 26, 497, 20), label3.Bounds); + } + + [Test] + public void TestInvalidSet () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + + bool thrown = false; + + try { + layout.Children.Add (label1, 2, 1, 0, 1); + } catch (ArgumentOutOfRangeException) { + thrown = true; + } + + Assert.True (thrown); + } + + [Test] + public void TestCentering () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true, HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center }; + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition () {Width = new GridLength (1, GridUnitType.Star)}, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition () {Height = new GridLength (1,GridUnitType.Star)}, + }; + + layout.Children.Add (label1); + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (new Rectangle (450, 490, 100, 20), label1.Bounds); + } + + [Test] + public void TestStart () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label { Platform = platform, IsPlatformEnabled = true, HorizontalOptions = LayoutOptions.Start, VerticalOptions = LayoutOptions.StartAndExpand }; + + layout.Children.AddVertical (label1); + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition () {Width = new GridLength (1, GridUnitType.Star)}, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition () {Height = new GridLength (1,GridUnitType.Star)}, + }; + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (new Rectangle (0, 0, 100, 20), label1.Bounds); + } + + [Test] + public void TestEnd () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label { Platform = platform, IsPlatformEnabled = true, HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.EndAndExpand }; + + layout.Children.AddVertical (label1); + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition () {Width = new GridLength (1, GridUnitType.Star)}, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition () {Height = new GridLength (1,GridUnitType.Star)}, + }; + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (new Rectangle (900, 980, 100, 20), label1.Bounds); + } + + [Test] + public void TestDefaultRowSpacing () + { + var layout = new Grid (); + + bool preferredSizeChanged = false; + layout.MeasureInvalidated += (sender, args) => { + preferredSizeChanged = true; + }; + + layout.RowSpacing = layout.RowSpacing; + + Assert.False (preferredSizeChanged); + + layout.RowSpacing = 10; + + Assert.True (preferredSizeChanged); + } + + [Test] + public void TestDefaultColumnSpacing () + { + var layout = new Grid (); + + bool preferredSizeChanged = false; + layout.MeasureInvalidated += (sender, args) => { + preferredSizeChanged = true; + }; + + layout.ColumnSpacing = layout.ColumnSpacing; + + Assert.False (preferredSizeChanged); + + layout.ColumnSpacing = 10; + + Assert.True (preferredSizeChanged); + } + + [Test] + public void TestAddCell () + { + var layout = new Grid (); + bool preferredSizeChanged = false; + layout.MeasureInvalidated += (sender, args) => preferredSizeChanged = true; + + Assert.False (preferredSizeChanged); + + layout.Children.Add (new Label (), 0, 0); + + Assert.True (preferredSizeChanged); + } + + [Test] + public void TestMoveCell () + { + var layout = new Grid (); + var label = new Label (); + layout.Children.Add (label, 0, 0); + + bool preferredSizeChanged = false; + layout.MeasureInvalidated += (sender, args) => { + preferredSizeChanged = true; + }; + + Assert.False (preferredSizeChanged); + Grid.SetRow (label, 2); + Assert.True (preferredSizeChanged); + + preferredSizeChanged = false; + Assert.False (preferredSizeChanged); + Grid.SetColumn (label, 2); + Assert.True (preferredSizeChanged); + + preferredSizeChanged = false; + Assert.False (preferredSizeChanged); + Grid.SetRowSpan (label, 2); + Assert.True (preferredSizeChanged); + + preferredSizeChanged = false; + Assert.False (preferredSizeChanged); + Grid.SetColumnSpan (label, 2); + Assert.True (preferredSizeChanged); + } + + [Test] + public void TestInvalidBottomAdd () + { + var layout = new Grid (); + + Assert.Throws<ArgumentOutOfRangeException> (() => layout.Children.Add (new View (), 0, 1, 1, 0)); + } + + [Test] + public void TestZeroSizeConstraints () + { + var layout = new Grid {Platform = new UnitPlatform ()}; + + Assert.AreEqual (new Size (0, 0), layout.GetSizeRequest (0, 0).Request); + Assert.AreEqual (new Size (0, 0), layout.GetSizeRequest (0, 10).Request); + Assert.AreEqual (new Size (0, 0), layout.GetSizeRequest (10, 0).Request); + } + + [Test] + public void TestSizeRequest () + { + var platform = new UnitPlatform (); + var layout = new Grid {Platform = platform, IsPlatformEnabled = true}; + layout.Children.AddVertical (new[] { + new View {Platform = platform, IsPlatformEnabled = true}, + new View {Platform = platform, IsPlatformEnabled = true}, + new View {Platform = platform, IsPlatformEnabled = true} + }); + + var result = layout.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request; + Assert.AreEqual (new Size (100, 72), result); + } + + [Test] + public void TestLimitedSizeRequest () + { + var platform = new UnitPlatform (); + var layout = new Grid {Platform = platform, IsPlatformEnabled = true}; + layout.Children.AddVertical (new[] { + new View {Platform = platform, IsPlatformEnabled = true}, + new View {Platform = platform, IsPlatformEnabled = true}, + new View {Platform = platform, IsPlatformEnabled = true} + }); + + var result = layout.GetSizeRequest (10, 10).Request; + Assert.AreEqual (new Size (100, 72), result); + } + + [Test] + public void TestLimitedWidthSizeRequest () + { + var platform = new UnitPlatform (); + var layout = new Grid {Platform = platform, IsPlatformEnabled = true}; + layout.Children.AddVertical (new[] { + new View {Platform = platform, IsPlatformEnabled = true}, + new View {Platform = platform, IsPlatformEnabled = true}, + new View {Platform = platform, IsPlatformEnabled = true} + }); + + var result = layout.GetSizeRequest (10, double.PositiveInfinity).Request; + Assert.AreEqual (new Size (100, 72), result); + } + + [Test] + public void TestLimitedHeightSizeRequest () + { + + var platform = new UnitPlatform (); + var layout = new Grid {Platform = platform, IsPlatformEnabled = true}; + layout.Children.AddVertical (new[] { + new View {Platform = platform, IsPlatformEnabled = true}, + new View {Platform = platform, IsPlatformEnabled = true}, + new View {Platform = platform, IsPlatformEnabled = true} + }); + + var result = layout.GetSizeRequest (double.PositiveInfinity, 10).Request; + Assert.AreEqual (new Size (100, 72), result); + } + + [Test] + public void IgnoresInvisibleChildren () + { + var platform = new UnitPlatform (); + var layout = new Grid {Platform = platform}; + + var label1 = new Label { Platform = platform, IsVisible = false, IsPlatformEnabled = true, VerticalOptions = LayoutOptions.FillAndExpand }; + var label2 = new Label { Platform = platform, IsPlatformEnabled = true}; + + layout.Children.AddVertical (label1); + layout.Children.AddVertical (label2); + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition { Width = GridLength.Auto }, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition { Height = GridLength.Auto}, + new RowDefinition { Height = GridLength.Auto}, + }; + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, -1, -1), label1.Bounds); + Assert.AreEqual (new Rectangle (0, 6, 100, 20), label2.Bounds); + } + + [Test] + public void TestSizeRequestWithPadding () + { + var platform = new UnitPlatform (); + var layout = new Grid {Platform = platform, IsPlatformEnabled = true, Padding = new Thickness(20, 10, 15, 5)}; + layout.Children.AddVertical (new[] { + new View {Platform = platform, IsPlatformEnabled = true}, + new View {Platform = platform, IsPlatformEnabled = true}, + new View {Platform = platform, IsPlatformEnabled = true} + }); + + var result = layout.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request; + Assert.AreEqual (new Size (135, 87), result); + } + + [Test] + public void InvalidCallsToStaticMethods () + { + Assert.Throws<ArgumentException> (() => Grid.SetRow (new Label (), -1)); + Assert.Throws<ArgumentException> (() => Grid.SetColumn (new Label (), -1)); + Assert.Throws<ArgumentException> (() => Grid.SetRowSpan (new Label (), 0)); + Assert.Throws<ArgumentException> (() => Grid.SetColumnSpan (new Label (), 0)); + } + + [Test] + public void TestAddedBP () + { + var platform = new UnitPlatform (); + + var labela0 = new Label {Platform = platform, IsPlatformEnabled = true}; + var labela1 = new Label { Platform = platform, IsPlatformEnabled = true }; + Grid.SetColumn (labela1, 1); + var labelb1 = new Label {Platform = platform, IsPlatformEnabled = true}; + Grid.SetRow (labelb1, 1); + Grid.SetColumn (labelb1, 1); + var labelc = new Label {Platform = platform, IsPlatformEnabled = true}; + Grid.SetRow (labelc, 2); + Grid.SetColumnSpan (labelc, 2); + + var layout = new Grid { + Platform = platform, + Children = { + labela0, + labela1, + labelb1, + labelc + } + }; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition { Width = GridLength.Auto }, + new ColumnDefinition { Width = GridLength.Auto }, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition { Height = GridLength.Auto}, + new RowDefinition { Height = GridLength.Auto}, + new RowDefinition { Height = GridLength.Auto}, + }; + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 100, 20), labela0.Bounds); + Assert.AreEqual (new Rectangle (106, 0, 100, 20), labela1.Bounds); + Assert.AreEqual (new Rectangle (106, 26, 100, 20), labelb1.Bounds); + Assert.AreEqual (new Rectangle (0, 52, 206, 20), labelc.Bounds); + } + + [Test] + public void Remove () + { + var platform = new UnitPlatform(); + + var labela0 = new Label { Platform = platform, IsPlatformEnabled = true }; + var labela1 = new Label { Platform = platform, IsPlatformEnabled = true }; + Grid.SetColumn(labela1, 1); + var labelb1 = new Label { Platform = platform, IsPlatformEnabled = true }; + Grid.SetRow(labelb1, 1); + Grid.SetColumn(labelb1, 1); + var labelc = new Label { Platform = platform, IsPlatformEnabled = true }; + Grid.SetRow(labelc, 2); + Grid.SetColumnSpan(labelc, 2); + + var layout = new Grid { + Platform = platform, + Children = { + labela0, + labela1, + labelb1, + labelc + } + }; + + layout.Children.Remove (labela0); + Assert.False (layout.LogicalChildren.Contains (labela0)); + } + + [Test] + public void TestAbsoluteLayout () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition {Width = new GridLength (150)}, + new ColumnDefinition {Width = new GridLength (150)}, + new ColumnDefinition {Width = new GridLength (150)}, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition {Height = new GridLength (30)}, + new RowDefinition {Height = new GridLength (30)}, + new RowDefinition {Height = new GridLength (30)}, + }; + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 1, 1); + layout.Children.Add (label3, 2, 2); + + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 150, 30), label1.Bounds); + Assert.AreEqual (new Rectangle (156, 36, 150, 30), label2.Bounds); + Assert.AreEqual (new Rectangle (312, 72, 150, 30), label3.Bounds); + } + + [Test] + public void TestAbsoluteLayoutWithSpans () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition {Width = new GridLength (150)}, + new ColumnDefinition {Width = new GridLength (150)}, + new ColumnDefinition {Width = new GridLength (150)}, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition {Height = new GridLength (30)}, + new RowDefinition {Height = new GridLength (30)}, + new RowDefinition {Height = new GridLength (30)}, + }; + layout.Children.Add (label1, 0, 2, 0, 1); + layout.Children.Add (label2, 2, 3, 0, 2); + layout.Children.Add (label3, 1, 2); + + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 306, 30), label1.Bounds); + Assert.AreEqual (new Rectangle (312, 0, 150, 66), label2.Bounds); + Assert.AreEqual (new Rectangle (156, 72, 150, 30), label3.Bounds); + } + + [Test] + public void TestStarLayout () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition {Width = new GridLength (1, GridUnitType.Star)}, + new ColumnDefinition {Width = new GridLength (1, GridUnitType.Star)}, + new ColumnDefinition {Width = new GridLength (1, GridUnitType.Star)}, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition {Height = new GridLength (1, GridUnitType.Star)}, + new RowDefinition {Height = new GridLength (1, GridUnitType.Star)}, + new RowDefinition {Height = new GridLength (1, GridUnitType.Star)}, + }; + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 1, 1); + layout.Children.Add (label3, 2, 2); + + var request = layout.GetSizeRequest (1002, 462); + Assert.AreEqual (312, request.Request.Width); + Assert.AreEqual (72, request.Request.Height); + + layout.Layout (new Rectangle (0, 0, 1002, 462)); + Assert.AreEqual (1002, layout.Width); + Assert.AreEqual (462, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 330, 150), label1.Bounds); + Assert.AreEqual (new Rectangle (336, 156, 330, 150), label2.Bounds); + Assert.AreEqual (new Rectangle (672, 312, 330, 150), label3.Bounds); + } + + [Test] + public void TestStarLayoutWithSpans () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition {Width = new GridLength (1, GridUnitType.Star)}, + new ColumnDefinition {Width = new GridLength (1, GridUnitType.Star)}, + new ColumnDefinition {Width = new GridLength (1, GridUnitType.Star)}, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition {Height = new GridLength (1, GridUnitType.Star)}, + new RowDefinition {Height = new GridLength (1, GridUnitType.Star)}, + new RowDefinition {Height = new GridLength (1, GridUnitType.Star)}, + }; + layout.Children.Add (label1, 0, 2, 0, 1); + layout.Children.Add (label2, 2, 3, 0, 2); + layout.Children.Add (label3, 1, 2); + + layout.Layout (new Rectangle (0, 0, 1002, 462)); + + Assert.AreEqual (1002, layout.Width); + Assert.AreEqual (462, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 666, 150), label1.Bounds); + Assert.AreEqual (new Rectangle (672, 0, 330, 306), label2.Bounds); + Assert.AreEqual (new Rectangle (336, 312, 330, 150), label3.Bounds); + } + + [Test] + public void TestAutoLayout () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition {Width = GridLength.Auto}, + new ColumnDefinition {Width = GridLength.Auto}, + new ColumnDefinition {Width = GridLength.Auto}, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition {Height = GridLength.Auto}, + new RowDefinition {Height = GridLength.Auto}, + new RowDefinition {Height = GridLength.Auto}, + }; + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 1, 1); + layout.Children.Add (label3, 2, 2); + + + layout.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (1000, layout.Width); + Assert.AreEqual (1000, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 100, 20), label1.Bounds); + Assert.AreEqual (new Rectangle (106, 26, 100, 20), label2.Bounds); + Assert.AreEqual (new Rectangle (212, 52, 100, 20), label3.Bounds); + } + + [Test] + public void TestAutoLayoutWithSpans () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label { Platform = platform, IsPlatformEnabled = true, WidthRequest = 150, Text = "label1" }; + var label2 = new Label { Platform = platform, IsPlatformEnabled = true, HeightRequest = 50, Text = "label2" }; + var label3 = new Label { Platform = platform, IsPlatformEnabled = true, Text = "label3" }; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition {Width = GridLength.Auto}, + new ColumnDefinition {Width = GridLength.Auto}, + new ColumnDefinition {Width = GridLength.Auto}, + }; + layout.RowDefinitions = new RowDefinitionCollection { + new RowDefinition {Height = GridLength.Auto}, + new RowDefinition {Height = GridLength.Auto}, + new RowDefinition {Height = GridLength.Auto}, + }; + layout.Children.Add (label1, 0, 2, 0, 1); + layout.Children.Add (label2, 2, 3, 0, 2); + layout.Children.Add (label3, 1, 2); + + layout.Layout (new Rectangle (0, 0, 1002, 462)); + + Assert.AreEqual (1002, layout.Width); + Assert.AreEqual (462, layout.Height); + + Assert.AreEqual (new Rectangle (0, 0, 150, 20), label1.Bounds); + Assert.AreEqual (new Rectangle (156, 0, 100, 50), label2.Bounds); + Assert.AreEqual (new Rectangle (50, 56, 100, 20), label3.Bounds); + } + + [Test] + public void AutoLayoutWithComplexSpans () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label4 = new Label {Platform = platform, IsPlatformEnabled = true, WidthRequest = 206}; + var label5 = new Label {Platform = platform, IsPlatformEnabled = true, WidthRequest = 312}; + var label6 = new Label {Platform = platform, IsPlatformEnabled = true, WidthRequest = 312}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition {Width = GridLength.Auto}, + new ColumnDefinition {Width = GridLength.Auto}, + new ColumnDefinition {Width = GridLength.Auto}, + new ColumnDefinition {Width = GridLength.Auto}, + new ColumnDefinition {Width = GridLength.Auto}, + }; + + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 1, 0); + layout.Children.Add (label3, 4, 0); + layout.Children.Add (label4, 2, 4, 0, 1); + layout.Children.Add (label5, 0, 3, 0, 1); + layout.Children.Add (label6, 2, 6, 0, 1); + + layout.Layout (new Rectangle (0, 0, 1000, 500)); + + Assert.AreEqual (100, layout.ColumnDefinitions [0].ActualWidth); + Assert.AreEqual (100, layout.ColumnDefinitions [1].ActualWidth); + Assert.AreEqual (100, layout.ColumnDefinitions [2].ActualWidth); + Assert.AreEqual (100, layout.ColumnDefinitions [3].ActualWidth); + Assert.AreEqual (100, layout.ColumnDefinitions [4].ActualWidth); + } + + [Test] + public void AutoLayoutExpandColumns () + { + var platform = new UnitPlatform (); + var layout = new Grid (); + layout.Platform = platform; + + var label1 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label2 = new Label {Platform = platform, IsPlatformEnabled = true}; + var label3 = new Label {Platform = platform, IsPlatformEnabled = true, WidthRequest = 300}; + + layout.ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition { Width = GridLength.Auto }, + new ColumnDefinition { Width = GridLength.Auto }, + }; + + layout.Children.Add (label1, 0, 0); + layout.Children.Add (label2, 1, 0); + layout.Children.Add (label3, 0, 2, 0, 1); + + layout.Layout (new Rectangle (0, 0, 1000, 500)); + + Assert.AreEqual (100, layout.ColumnDefinitions [0].ActualWidth); + Assert.AreEqual (194, layout.ColumnDefinitions [1].ActualWidth); + } + + [Test] + public void GridHasDefaultDefinitions () + { + var grid = new Grid (); + Assert.NotNull (grid.ColumnDefinitions); + Assert.NotNull (grid.RowDefinitions); + } + + [Test] + public void DefaultDefinitionsArentSharedAccrossInstances () + { + var grid0 = new Grid (); + var coldefs = grid0.ColumnDefinitions; + var rowdefs = grid0.RowDefinitions; + + var grid1 = new Grid (); + Assert.AreNotSame (grid0, grid1); + Assert.AreNotSame (coldefs, grid1.ColumnDefinitions); + Assert.AreNotSame (rowdefs, grid1.RowDefinitions); + } + + [Test] + public void ChildrenLayoutRespectAlignment () + { + var platform = new UnitPlatform (); + var grid = new Grid { + ColumnDefinitions = { new ColumnDefinition { Width = new GridLength (300) } }, + RowDefinitions = { new RowDefinition { Height = new GridLength (100) } }, + Platform = platform, + }; + var label = new Label { + Platform = platform, + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.Center, + HorizontalOptions = LayoutOptions.End, + }; + + grid.Children.Add (label); + grid.Layout (new Rectangle (0, 0, 500, 500)); + + Assert.AreEqual (new Rectangle (200, 40, 100, 20), label.Bounds); + } + + [Test] + public void BothChildrenPropertiesUseTheSameBackendStore () + { + var view = new View (); + var grid = new Grid (); + Assert.AreEqual (0, grid.Children.Count); + (grid as Layout<View>).Children.Add (view); + Assert.AreEqual (1, grid.Children.Count); + Assert.AreEqual (1, (grid as Layout<View>).Children.Count); + Assert.AreSame (view, (grid as Layout<View>).Children.First ()); + Assert.AreSame (view, grid.Children.First ()); + } + + [Test] + //Issue 1384 + public void ImageInAutoCellIsProperlyConstrained () + { + var platform = new UnitPlatform (); + + var content = new Image { + Aspect= Aspect.AspectFit, + Platform = platform, + IsPlatformEnabled = true + }; + var grid = new Grid { + Platform = platform, + IsPlatformEnabled = true, + BackgroundColor = Color.Red, + VerticalOptions=LayoutOptions.Start, + Children = { + content + }, + RowDefinitions = { new RowDefinition { Height = GridLength.Auto} }, + ColumnDefinitions = { new ColumnDefinition { Width = GridLength.Auto } } + }; + var view = new ContentView { + Platform = platform, + IsPlatformEnabled = true, + Content = grid, + }; + view.Layout (new Rectangle (0, 0, 100, 100)); + Assert.AreEqual (100, grid.Width); + Assert.AreEqual (20, grid.Height); + + view.Layout (new Rectangle (0, 0, 50, 50)); + Assert.AreEqual (50, grid.Width); + Assert.AreEqual (10, grid.Height); + } + + [Test] + //Issue 1384 + public void ImageInStarCellIsProperlyConstrained () + { + var platform = new UnitPlatform (); + + var content = new Image { + Aspect= Aspect.AspectFit, + Platform = platform, + MinimumHeightRequest = 10, + MinimumWidthRequest = 50, + IsPlatformEnabled = true + }; + var grid = new Grid { + Platform = platform, + IsPlatformEnabled = true, + BackgroundColor = Color.Red, + VerticalOptions=LayoutOptions.Start, + Children = { + content + } + }; + var view = new ContentView { + Platform = platform, + IsPlatformEnabled = true, + Content = grid, + }; + view.Layout (new Rectangle (0, 0, 100, 100)); + Assert.AreEqual (100, grid.Width); + Assert.AreEqual (20, grid.Height); + + view.Layout (new Rectangle (0, 0, 50, 50)); + Assert.AreEqual (50, grid.Width); + Assert.AreEqual (10, grid.Height); + } + + [Test] + public void SizeRequestForStar () + { + var platform = new UnitPlatform (); + + var grid = new Grid{ + RowDefinitions = new RowDefinitionCollection { + new RowDefinition {Height = new GridLength (1, GridUnitType.Star)}, + new RowDefinition {Height = GridLength.Auto}, + }, + ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition {Width = new GridLength (1, GridUnitType.Star)}, + new ColumnDefinition {Width = GridLength.Auto}, + } + }; + grid.Children.Add (new Label {BackgroundColor = Color.Lime, Text="Foo", Platform = platform, IsPlatformEnabled = true}); + grid.Children.Add (new Label {Text = "Bar", Platform = platform, IsPlatformEnabled = true},0,1); + grid.Children.Add (new Label {Text="Baz",XAlign = TextAlignment.End, Platform = platform, IsPlatformEnabled = true},1,0); + grid.Children.Add (new Label {Text="Qux", XAlign = TextAlignment.End, Platform = platform, IsPlatformEnabled = true},1,1); + + var request = grid.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + Assert.AreEqual (206, request.Request.Width); + Assert.AreEqual (46, request.Request.Height); + + Assert.AreEqual (106, request.Minimum.Width); + Assert.AreEqual (26, request.Minimum.Height); + // + } + + [Test] + //Issue 1497 + public void StarRowsShouldOccupyTheSpace () + { + var platform = new UnitPlatform (); + var label = new Label { + Platform = platform, + IsPlatformEnabled = true, + }; + var Button = new Button { + HorizontalOptions = LayoutOptions.FillAndExpand, + VerticalOptions = LayoutOptions.EndAndExpand, + Platform = platform, + IsPlatformEnabled = true, + }; + var grid = new Grid { + RowDefinitions = new RowDefinitionCollection { + new RowDefinition { Height = GridLength.Auto }, + new RowDefinition { Height = new GridLength (1, GridUnitType.Star) }, + }, + ColumnDefinitions = new ColumnDefinitionCollection { + new ColumnDefinition {Width = new GridLength(1, GridUnitType.Star)}, + }, + Platform = platform, + IsPlatformEnabled = true, + }; + + grid.Children.Add (label); + grid.Children.Add (Button, 0, 1); + + grid.Layout (new Rectangle (0, 0, 300, 300)); + Assert.AreEqual (new Rectangle (0, 280, 300, 20), Button.Bounds); + } + + [Test] + public void StarColumnsWithSpansDoNotExpandAutos () + { + var grid = new Grid { + RowDefinitions = { + new RowDefinition {Height = GridLength.Auto}, + new RowDefinition {Height = GridLength.Auto}, + }, + ColumnDefinitions = { + new ColumnDefinition {Width = new GridLength (1, GridUnitType.Auto)}, + new ColumnDefinition {Width = new GridLength (1, GridUnitType.Auto)}, + new ColumnDefinition {Width = new GridLength (1, GridUnitType.Star)} + }, + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var spanBox = new BoxView {WidthRequest = 70, HeightRequest = 20, IsPlatformEnabled = true}; + var box1 = new BoxView {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}; + var box2 = new BoxView {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}; + var box3 = new BoxView {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}; + + grid.Children.Add (spanBox, 0, 3, 0, 1); + grid.Children.Add (box1, 0, 1); + grid.Children.Add (box2, 1, 1); + grid.Children.Add (box3, 2, 1); + + grid.Layout (new Rectangle(0, 0, 300, 46)); + + Assert.AreEqual (new Rectangle (0, 0, 300, 20), spanBox.Bounds); + Assert.AreEqual (new Rectangle (0, 26, 20, 20), box1.Bounds); + Assert.AreEqual (new Rectangle (26, 26, 20, 20), box2.Bounds); + Assert.AreEqual (new Rectangle (52, 26, 248, 20), box3.Bounds); + } + + static SizeRequest GetResizableSize (VisualElement view, double widthconstraint, double heightconstraint) + { + if (!(view is Editor)) + return new SizeRequest(new Size (100, 20)); + if (widthconstraint < 100) + return new SizeRequest(new Size (widthconstraint, 2000/widthconstraint)); + if (heightconstraint < 20) + return new SizeRequest(new Size (2000/heightconstraint, heightconstraint)); + return new SizeRequest(new Size (100, 20)); + } + + [Test] + //Issue 1893 + public void EditorSpanningOnMultipleAutoRows () + { + var grid0 = new Grid { + ColumnDefinitions = { + new ColumnDefinition { Width = GridLength.Auto }, + new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) }, + }, + RowDefinitions = { + new RowDefinition { Height = GridLength.Auto }, + new RowDefinition { Height = GridLength.Auto }, + }, + Platform = new UnitPlatform (GetResizableSize), + IsPlatformEnabled = true, + }; + + var label0 = new Label { IsPlatformEnabled = true }; + var editor0 = new Editor { IsPlatformEnabled = true }; + grid0.Children.Add (label0, 0, 0); + grid0.Children.Add (editor0, 1, 2, 0, 2); + + grid0.Layout (new Rectangle (0, 0, 156, 200)); + Assert.AreEqual (new Rectangle (106, 0, 50, 40), editor0.Bounds); + + var grid1 = new Grid { + ColumnDefinitions = { + new ColumnDefinition { Width = GridLength.Auto }, + new ColumnDefinition { Width = new GridLength (1, GridUnitType.Star) }, + }, + RowDefinitions = { + new RowDefinition { Height = GridLength.Auto }, + }, + Platform = new UnitPlatform (GetResizableSize), + IsPlatformEnabled = true, + }; + + var label1 = new Label { IsPlatformEnabled = true }; + var editor1 = new Editor { IsPlatformEnabled = true }; + grid1.Children.Add (label1, 0, 0); + grid1.Children.Add (editor1, 1, 0); + + grid1.Layout (new Rectangle (0, 0, 156, 200)); + Assert.AreEqual (new Rectangle (106, 0, 50, 40), editor1.Bounds); + } + + [Test] + public void WidthBoundRequestRespected () + { + var grid = new Grid { + ColumnDefinitions = { + new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }, + new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) } + }, + RowDefinitions = { + new RowDefinition { Height = GridLength.Auto }, + new RowDefinition { Height = GridLength.Auto }, + }, + Platform = new UnitPlatform (GetResizableSize), + IsPlatformEnabled = true, + RowSpacing = 0, + ColumnSpacing = 0, + }; + + var topLabel = new Editor {IsPlatformEnabled = true}; + var leftLabel = new Label {IsPlatformEnabled = true, WidthRequest = 10}; + var rightLabel = new Label {IsPlatformEnabled = true, WidthRequest = 10}; + + grid.Children.Add (topLabel, 0, 2, 0, 1); + grid.Children.Add (leftLabel, 0, 1); + grid.Children.Add (rightLabel, 1, 1); + + var unboundRequest = grid.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + var widthBoundRequest = grid.GetSizeRequest (50, double.PositiveInfinity); + + Assert.AreEqual (new SizeRequest (new Size (20, 120), new Size (0, 120)), unboundRequest); + Assert.AreEqual (new SizeRequest (new Size (50, 60), new Size (0, 60)), widthBoundRequest); + } + + [Test] + //https://bugzilla.xamarin.com/show_bug.cgi?id=31608 + public void ColAndRowDefinitionsAreActuallyBindable () + { + var rowdef = new RowDefinition (); + rowdef.SetBinding (RowDefinition.HeightProperty, "Height"); + var grid = new Grid { + RowDefinitions = new RowDefinitionCollection { rowdef }, + }; + Assert.AreEqual (RowDefinition.HeightProperty.DefaultValue, rowdef.Height); + grid.BindingContext = new {Height = 32}; + Assert.AreEqual (new GridLength(32), rowdef.Height); + } + + [Test] + //https://bugzilla.xamarin.com/show_bug.cgi?id=31967 + public void ChangingRowHeightViaBindingTriggersRedraw () + { + var rowdef = new RowDefinition (); + rowdef.SetBinding (RowDefinition.HeightProperty, "Height"); + var grid = new Grid { +// RowDefinitions = new RowDefinitionCollection { +// new RowDefinition { Height = GridLength.Auto }, +// rowdef +// }, + RowSpacing = 0, + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + }; + grid.RowDefinitions.Add (new RowDefinition { Height = GridLength.Auto }); + grid.RowDefinitions.Add (rowdef); + + var label0 = new Label { IsPlatformEnabled = true }; + Grid.SetRow (label0, 0); + var label1 = new Label { IsPlatformEnabled = true }; + Grid.SetRow (label1, 1); + + grid.BindingContext = new {Height = 0}; + grid.Children.Add (label0); + grid.Children.Add (label1); + + Assert.AreEqual (new SizeRequest (new Size (100, 20), new Size (0, 20)), grid.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity)); + grid.BindingContext = new {Height = 42}; + Assert.AreEqual (new SizeRequest (new Size (100, 62), new Size (0, 62)), grid.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity)); + } + + [Test] + public void InvalidationBlockedForAbsoluteCell () + { + var grid = new Grid () { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + RowDefinitions = { + new RowDefinition { Height = new GridLength (100, GridUnitType.Absolute) } + }, + ColumnDefinitions = { + new ColumnDefinition { Width = new GridLength (200, GridUnitType.Absolute) } + } + }; + + var label = new Label { IsPlatformEnabled = true }; + grid.Children.Add (label); + + bool invalidated = false; + grid.MeasureInvalidated += (sender, args) => { + invalidated = true; + }; + + label.Text = "Testing"; + + Assert.False (invalidated); + } + + // because the constraint is internal, we need this + public enum HackLayoutConstraint + { + None = LayoutConstraint.None, + VerticallyFixed = LayoutConstraint.VerticallyFixed, + HorizontallyFixed = LayoutConstraint.HorizontallyFixed, + Fixed = LayoutConstraint.Fixed + } + + [TestCase (HackLayoutConstraint.None, GridUnitType.Absolute, GridUnitType.Absolute, ExpectedResult = true)] + [TestCase (HackLayoutConstraint.None, GridUnitType.Star, GridUnitType.Absolute, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.None, GridUnitType.Absolute, GridUnitType.Star, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.None, GridUnitType.Auto, GridUnitType.Absolute, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.None, GridUnitType.Absolute, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.None, GridUnitType.Star, GridUnitType.Star, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.None, GridUnitType.Auto, GridUnitType.Star, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.None, GridUnitType.Star, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.None, GridUnitType.Auto, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.VerticallyFixed, GridUnitType.Absolute, GridUnitType.Absolute, ExpectedResult = true)] + [TestCase (HackLayoutConstraint.VerticallyFixed, GridUnitType.Star, GridUnitType.Absolute, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.VerticallyFixed, GridUnitType.Absolute, GridUnitType.Star, ExpectedResult = true)] + [TestCase (HackLayoutConstraint.VerticallyFixed, GridUnitType.Auto, GridUnitType.Absolute, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.VerticallyFixed, GridUnitType.Absolute, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.VerticallyFixed, GridUnitType.Star, GridUnitType.Star, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.VerticallyFixed, GridUnitType.Auto, GridUnitType.Star, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.VerticallyFixed, GridUnitType.Star, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.VerticallyFixed, GridUnitType.Auto, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.HorizontallyFixed, GridUnitType.Absolute, GridUnitType.Absolute, ExpectedResult = true)] + [TestCase (HackLayoutConstraint.HorizontallyFixed, GridUnitType.Star, GridUnitType.Absolute, ExpectedResult = true)] + [TestCase (HackLayoutConstraint.HorizontallyFixed, GridUnitType.Absolute, GridUnitType.Star, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.HorizontallyFixed, GridUnitType.Auto, GridUnitType.Absolute, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.HorizontallyFixed, GridUnitType.Absolute, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.HorizontallyFixed, GridUnitType.Star, GridUnitType.Star, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.HorizontallyFixed, GridUnitType.Auto, GridUnitType.Star, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.HorizontallyFixed, GridUnitType.Star, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.HorizontallyFixed, GridUnitType.Auto, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.Fixed, GridUnitType.Absolute, GridUnitType.Absolute, ExpectedResult = true)] + [TestCase (HackLayoutConstraint.Fixed, GridUnitType.Star, GridUnitType.Absolute, ExpectedResult = true)] + [TestCase (HackLayoutConstraint.Fixed, GridUnitType.Absolute, GridUnitType.Star, ExpectedResult = true)] + [TestCase (HackLayoutConstraint.Fixed, GridUnitType.Auto, GridUnitType.Absolute, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.Fixed, GridUnitType.Absolute, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.Fixed, GridUnitType.Star, GridUnitType.Star, ExpectedResult = true)] + [TestCase (HackLayoutConstraint.Fixed, GridUnitType.Auto, GridUnitType.Star, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.Fixed, GridUnitType.Star, GridUnitType.Auto, ExpectedResult = false)] + [TestCase (HackLayoutConstraint.Fixed, GridUnitType.Auto, GridUnitType.Auto, ExpectedResult = false)] + public bool InvalidationPropogationTests (HackLayoutConstraint gridConstraint, GridUnitType horizontalType, GridUnitType verticalType) + { + var grid = new Grid { + ComputedConstraint = (LayoutConstraint) gridConstraint, + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + RowDefinitions = { + new RowDefinition { Height = new GridLength (1, verticalType) } + }, + ColumnDefinitions = { + new ColumnDefinition { Width = new GridLength (1, horizontalType) } + } + }; + + var label = new Label { IsPlatformEnabled = true }; + grid.Children.Add (label); + + bool invalidated = false; + grid.MeasureInvalidated += (sender, args) => { + invalidated = true; + }; + + label.Text = "Testing"; + + return !invalidated; + } + } + + [TestFixture] + public class GridMeasureTests : BaseTestFixture + { + static List<Action> delayActions = new List<Action> (); + + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (invokeOnMainThread: a => { delayActions.Add (a); }); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void NestedInvalidateMeasureDoesNotCrash () + { + var grid = new Grid { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new Label { + IsPlatformEnabled = true + }; + grid.Children.Add (child); + + var child2 = new Label { + IsPlatformEnabled = true + }; + grid.Children.Add (child2); + + bool fire = true; + child.SizeChanged += (sender, args) => { + if (fire) + child.InvalidateMeasure (InvalidationTrigger.Undefined); + fire = false; + }; + + grid.Layout (new Rectangle (0, 0, 100, 100)); + + foreach (var delayAction in delayActions) { + delayAction (); + } + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/GroupViewUnitTests.cs b/Xamarin.Forms.Core.UnitTests/GroupViewUnitTests.cs new file mode 100644 index 00000000..61e38cf1 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/GroupViewUnitTests.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections; +using System.Linq; +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + internal class NaiveLayout : Layout<View> + { + protected override void LayoutChildren (double x, double y, double width, double height) + { + foreach (var child in LogicalChildren.Cast<View>()) { + var result = new Rectangle (x, y, 0, 0); + var request = child.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + result.Width = request.Request.Width; + result.Height = request.Request.Height; + + child.Layout (result); + } + } + } + + [TestFixture] + public class LayoutUnitTests : BaseTestFixture + { + [Test] + public void TestRaiseChild () + { + var view = new NaiveLayout (); + + var child1 = new View (); + var child2 = new View (); + var child3 = new View (); + + view.Children.Add (child1); + view.Children.Add (child2); + view.Children.Add (child3); + + bool reordered = false; + view.ChildrenReordered += (sender, args) => reordered = true; + + view.RaiseChild (child1); + + Assert.AreEqual (child1, view.LogicalChildren [2]); + Assert.True (reordered); + + view.RaiseChild (child2); + Assert.AreEqual (child2, view.LogicalChildren [2]); + } + + [Test] + public void TestRaiseUnownedChild () + { + var view = new NaiveLayout (); + + var child1 = new View (); + var child2 = new View (); + var child3 = new View (); + + view.Children.Add (child1); + view.Children.Add (child3); + + bool reordered = false; + view.ChildrenReordered += (sender, args) => reordered = true; + + view.RaiseChild (child2); + + Assert.False (reordered); + } + + [Test] + public void TestLowerChild () + { + var view = new NaiveLayout (); + + var child1 = new View (); + var child2 = new View (); + var child3 = new View (); + + view.Children.Add (child1); + view.Children.Add (child2); + view.Children.Add (child3); + + bool reordered = false; + view.ChildrenReordered += (sender, args) => reordered = true; + + view.LowerChild (child3); + + Assert.AreEqual (child3, view.LogicalChildren [0]); + Assert.True (reordered); + + view.LowerChild (child2); + Assert.AreEqual (child2, view.LogicalChildren [0]); + } + + [Test] + public void TestLowerUnownedChild () + { + var view = new NaiveLayout (); + + var child1 = new View (); + var child2 = new View (); + var child3 = new View (); + + view.Children.Add (child1); + view.Children.Add (child3); + + bool reordered = false; + view.ChildrenReordered += (sender, args) => reordered = true; + + view.LowerChild (child2); + + Assert.False (reordered); + } + + [Test] + public void TestAdd () + { + var view = new NaiveLayout (); + var child1 = new View (); + + bool added = false; + view.ChildAdded += (sender, args) => added = true; + + view.Children.Add (child1); + + Assert.True (added); + Assert.AreEqual (child1, view.LogicalChildren [0]); + } + + [Test] + public void TestDoubleAdd () + { + var view = new NaiveLayout (); + var child1 = new View (); + view.Children.Add (child1); + + bool added = false; + view.ChildAdded += (sender, args) => added = true; + + view.Children.Add (child1); + + Assert.False (added); + Assert.AreEqual (child1, view.LogicalChildren [0]); + } + + [Test] + public void TestRemove () + { + var view = new NaiveLayout (); + var child1 = new View (); + + view.Children.Add (child1); + + bool removed = false; + view.ChildRemoved += (sender, args) => removed = true; + + view.Children.Remove (child1); + + Assert.True (removed); + Assert.False (view.LogicalChildren.Any ()); + } + + [Test] + public void TestGenericEnumerator () + { + var view = new NaiveLayout (); + + var children = new[] { + new View (), + new View (), + new View () + }; + + foreach (var child in children) + view.Children.Add (child); + + int i = 0; + foreach (var child in view.LogicalChildren) { + Assert.AreEqual (children[i], child); + i++; + } + } + + [Test] + public void TestEnumerator () + { + var view = new NaiveLayout (); + + var children = new [] { + new View (), + new View (), + new View () + }; + + foreach (var child in children) + view.Children.Add (child); + + int i = 0; + var enumerator = (view.LogicalChildren as IEnumerable).GetEnumerator (); + while (enumerator.MoveNext ()) { + Assert.AreEqual (children [i], enumerator.Current as View); + i++; + } + } + + [Test] + public void TestInitializerSyntax () + { + View view1, view2; + var group = new NaiveLayout { + Children = { + (view1 = new View ()), + (view2 = new View ()) + } + }; + + Assert.AreEqual (2, group.LogicalChildren.Count); + Assert.IsTrue (group.LogicalChildren.Contains (view1)); + Assert.IsTrue (group.LogicalChildren.Contains (view2)); + Assert.AreEqual (view1, group.LogicalChildren[0]); + } + + [Test] + public void TestChildren () + { + View view1, view2; + var group = new NaiveLayout { + Children = { + (view1 = new View ()), + (view2 = new View ()) + } + }; + + Assert.AreEqual (2, group.Children.Count); + Assert.IsTrue (group.Children.Contains (view1)); + Assert.IsTrue (group.Children.Contains (view2)); + Assert.AreEqual (view1, group.Children[0]); + } + + [Test] + public void TestDefaultLayout () + { + View view; + var group = new NaiveLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Children = { + (view = new View { + WidthRequest = 50, + HeightRequest = 20, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }) + } + }; + + group.Layout (new Rectangle (0, 0, 400, 400)); + + Assert.AreEqual (new Rectangle (0, 0, 50, 20), view.Bounds); + } + + [Test] + public void ThrowsInvalidOperationOnSelfAdd () + { + var group = new NaiveLayout (); + Assert.Throws<InvalidOperationException> (() => group.Children.Add (group)); + } + + [Test] + public void ReorderChildrenDoesNotRaiseChildAddedOrRemoved () + { + var child1 = new BoxView (); + var child2 = new BoxView (); + var layout = new NaiveLayout { + Children = {child1, child2} + }; + + var added = false; + var removed = false; + + layout.ChildAdded += (sender, args) => added = true; + layout.ChildRemoved += (sender, args) => removed = true; + + layout.RaiseChild (child1); + + Assert.False (added); + Assert.False (removed); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ImageSourceTests.cs b/Xamarin.Forms.Core.UnitTests/ImageSourceTests.cs new file mode 100644 index 00000000..95aa5131 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ImageSourceTests.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; +using System.IO; +using System.Threading.Tasks; +using System.Threading; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ImageSourceTests : BaseTestFixture + { + [SetUp] + public override void Setup () + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [Test] + public void TestConstructors () + { + var filesource = new FileImageSource { File = "File.png" }; + Assert.AreEqual ("File.png", filesource.File); + + Func<CancellationToken, Task<Stream>> stream = token => new Task<Stream> (() => new FileStream ("Foo", System.IO.FileMode.Open), token); + var streamsource = new StreamImageSource { Stream = stream }; + Assert.AreEqual (stream, streamsource.Stream); + } + + [Test] + public void TestHelpers () + { + var imagesource = ImageSource.FromFile ("File.png"); + Assert.That (imagesource, Is.TypeOf<FileImageSource> ()); + Assert.AreEqual ("File.png", ((FileImageSource)imagesource).File); + + Func<Stream> stream = () => new System.IO.FileStream ("Foo", System.IO.FileMode.Open); + var streamsource = ImageSource.FromStream (stream); + Assert.That (streamsource, Is.TypeOf<StreamImageSource> ()); + + var urisource = ImageSource.FromUri (new Uri ("http://xamarin.com/img.png")); + Assert.That (urisource, Is.TypeOf<UriImageSource> ()); + Assert.AreEqual ("http://xamarin.com/img.png", ((UriImageSource)(urisource)).Uri.AbsoluteUri); + } + + [Test] + public void TestImplicitFileConversion () + { + var image = new Image { Source = "File.png" }; + Assert.IsTrue (image.Source != null); + Assert.That (image.Source, Is.InstanceOf<FileImageSource> ()); + Assert.AreEqual ("File.png", ((FileImageSource)(image.Source)).File); + } + + [Test] + public void TestImplicitUriConversion () + { + var image = new Image { Source = new Uri ("http://xamarin.com/img.png") }; + Assert.IsTrue (image.Source != null); + Assert.That (image.Source, Is.InstanceOf<UriImageSource> ()); + Assert.AreEqual ("http://xamarin.com/img.png", ((UriImageSource)(image.Source)).Uri.AbsoluteUri); + } + + [Test] + public void TestImplicitStringUriConversion () + { + var image = new Image { Source = "http://xamarin.com/img.png" }; + Assert.IsTrue (image.Source != null); + Assert.That (image.Source, Is.InstanceOf<UriImageSource> ()); + Assert.AreEqual ("http://xamarin.com/img.png", ((UriImageSource)(image.Source)).Uri.AbsoluteUri); + } + + [Test] + public void TestSetStringValue () + { + var image = new Image (); + image.SetValue (Image.SourceProperty, "foo.png"); + Assert.IsNotNull (image.Source); + Assert.That (image.Source, Is.InstanceOf<FileImageSource> ()); + Assert.AreEqual ("foo.png", ((FileImageSource)(image.Source)).File); + } + + [Test] + public void TextBindToStringValue () + { + var image = new Image (); + image.SetBinding (Image.SourceProperty, "."); + Assert.IsNull (image.Source); + image.BindingContext = "foo.png"; + Assert.IsNotNull (image.Source); + Assert.That (image.Source, Is.InstanceOf<FileImageSource> ()); + Assert.AreEqual ("foo.png", ((FileImageSource)(image.Source)).File); + } + + [Test] + public void TextBindToStringUriValue () + { + var image = new Image (); + image.SetBinding (Image.SourceProperty, "."); + Assert.IsNull (image.Source); + image.BindingContext = "http://xamarin.com/img.png"; + Assert.IsNotNull (image.Source); + Assert.That (image.Source, Is.InstanceOf<UriImageSource> ()); + Assert.AreEqual ("http://xamarin.com/img.png", ((UriImageSource)(image.Source)).Uri.AbsoluteUri); + } + + [Test] + public void TextBindToUriValue () + { + var image = new Image (); + image.SetBinding (Image.SourceProperty, "."); + Assert.IsNull (image.Source); + image.BindingContext = new Uri("http://xamarin.com/img.png"); + Assert.IsNotNull (image.Source); + Assert.That (image.Source, Is.InstanceOf<UriImageSource> ()); + Assert.AreEqual ("http://xamarin.com/img.png", ((UriImageSource)(image.Source)).Uri.AbsoluteUri); + } + + class MockImageSource : ImageSource + { + } + + [Test] + public void TestBindingContextPropagation () + { + var context = new object (); + var image = new Image (); + image.BindingContext = context; + var source = new MockImageSource (); + image.Source = source; + Assert.AreSame (context, source.BindingContext); + + image = new Image (); + source = new MockImageSource (); + image.Source = source; + image.BindingContext = context; + Assert.AreSame (context, source.BindingContext); + } + + [Test] + public void ImplicitCastOnAbsolutePathsShouldCreateAFileImageSource () + { + var path = "/private/var/mobile/Containers/Data/Application/B1E5AB19-F815-4B4A-AB97-BD4571D53743/Documents/temp/IMG_20140603_150614_preview.jpg"; + var image = new Image { Source = path }; + Assert.That (image.Source, Is.TypeOf<FileImageSource> ()); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ImageTests.cs b/Xamarin.Forms.Core.UnitTests/ImageTests.cs new file mode 100644 index 00000000..1e2546db --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ImageTests.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; +using System.IO; +using System.Threading.Tasks; +using System.Threading; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ImageTests : BaseTestFixture + { + [SetUp] + public override void Setup () + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (getStreamAsync: GetStreamAsync); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void TestSizing () + { + var image = new Image {Source = ImageSource.FromFile ("File.png"), Platform = new UnitPlatform (), IsPlatformEnabled = true}; + + var result = image.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + + Assert.AreEqual (100, result.Request.Width); + Assert.AreEqual (20, result.Request.Height); + } + + [Test] + public void TestAspectSizingWithConstrainedHeight () + { + var image = new Image {Source = ImageSource.FromFile ("File.png"), Platform = new UnitPlatform (), IsPlatformEnabled = true}; + + var result = image.GetSizeRequest (double.PositiveInfinity, 10); + + Assert.AreEqual (50, result.Request.Width); + Assert.AreEqual (10, result.Request.Height); + } + + [Test] + public void TestAspectSizingWithConstrainedWidth () + { + var image = new Image {Source = ImageSource.FromFile ("File.png"), Platform = new UnitPlatform (), IsPlatformEnabled = true}; + + var result = image.GetSizeRequest (25, double.PositiveInfinity); + + Assert.AreEqual (25, result.Request.Width); + Assert.AreEqual (5, result.Request.Height); + } + + [Test] + public void TestAspectFillSizingWithConstrainedHeight () + { + var image = new Image {Source = ImageSource.FromFile ("File.png"), Platform = new UnitPlatform (), IsPlatformEnabled = true}; + + image.Aspect = Aspect.AspectFill; + var result = image.GetSizeRequest (double.PositiveInfinity, 10); + + Assert.AreEqual (50, result.Request.Width); + Assert.AreEqual (10, result.Request.Height); + } + + [Test] + public void TestAspectFillSizingWithConstrainedWidth () + { + var image = new Image {Source = ImageSource.FromFile ("File.png"), Platform = new UnitPlatform (), IsPlatformEnabled = true}; + + image.Aspect = Aspect.AspectFill; + var result = image.GetSizeRequest (25, double.PositiveInfinity); + + Assert.AreEqual (25, result.Request.Width); + Assert.AreEqual (5, result.Request.Height); + } + + [Test] + public void TestFillSizingWithConstrainedHeight () + { + var image = new Image {Source = ImageSource.FromFile ("File.png"), Platform = new UnitPlatform (), IsPlatformEnabled = true}; + + image.Aspect = Aspect.AspectFill; + var result = image.GetSizeRequest (double.PositiveInfinity, 10); + + Assert.AreEqual (50, result.Request.Width); + Assert.AreEqual (10, result.Request.Height); + } + + [Test] + public void TestFillSizingWithConstrainedWidth () + { + var image = new Image {Source = ImageSource.FromFile ("File.png"), Platform = new UnitPlatform (), IsPlatformEnabled = true}; + + image.Aspect = Aspect.AspectFill; + var result = image.GetSizeRequest (25, double.PositiveInfinity); + + Assert.AreEqual (25, result.Request.Width); + Assert.AreEqual (5, result.Request.Height); + } + + [Test] + public void TestSizeChanged () + { + var image = new Image { Source = "File0.png" }; + Assert.AreEqual ("File0.png", ((FileImageSource)image.Source).File); + + var preferredSizeChanged = false; + image.MeasureInvalidated += (sender, args) => preferredSizeChanged = true; + + image.Source = "File1.png"; + Assert.AreEqual ("File1.png", ((FileImageSource)image.Source).File); + Assert.True (preferredSizeChanged); + } + + [Test] + public void TestSource () + { + var image = new Image (); + + Assert.IsNull (image.Source); + + bool signaled = false; + image.PropertyChanged += (sender, e) => { + if (e.PropertyName == "Source") + signaled = true; + }; + + var source = ImageSource.FromFile ("File.png"); + image.Source = source; + + Assert.AreEqual (source, image.Source); + Assert.True (signaled); + } + + [Test] + public void TestSourceDoubleSet () + { + var image = new Image {Source = ImageSource.FromFile ("File.png")}; + + bool signaled = false; + image.PropertyChanged += (sender, e) => { + if (e.PropertyName == "Source") + signaled = true; + }; + + image.Source = image.Source; + + Assert.False (signaled); + } + + [Test] + public void TestFileImageSourceChanged () + { + var source = (FileImageSource)ImageSource.FromFile ("File.png"); + + bool signaled = false; + source.SourceChanged += (sender, e) => { + signaled = true; + }; + + source.File = "Other.png"; + Assert.AreEqual ("Other.png", source.File); + + Assert.True (signaled); + } + + [Test] + public void TestFileImageSourcePropertiesChangedTriggerResize () + { + var source = new FileImageSource (); + var image = new Image { Source = source }; + bool fired = false; + image.MeasureInvalidated += (sender, e) => fired = true; + Assert.Null (source.File); + source.File = "foo.png"; + Assert.NotNull (source.File); + Assert.True (fired); + } + + [Test] + public void TestStreamImageSourcePropertiesChangedTriggerResize () + { + var source = new StreamImageSource (); + var image = new Image { Source = source }; + bool fired = false; + image.MeasureInvalidated += (sender, e) => fired = true; + Assert.Null (source.Stream); + source.Stream = token => Task.FromResult<Stream> (null); + Assert.NotNull (source.Stream); + Assert.True (fired); + } + + [Test] + public void TestImageSourceToNullCancelsLoading () + { + var image = new Image (); + var mockImageRenderer = new MockImageRenderer (image); + var loader = new UriImageSource { Uri = new Uri ("http://www.public-domain-image.com/free-images/miscellaneous/big-high-border-fence.jpg") }; + image.Source = loader; + Assert.IsTrue (image.IsLoading); + image.Source = null; + Assert.IsFalse (image.IsLoading); + Assert.IsTrue (cancelled); + } + + static bool cancelled; + + static async Task<Stream> GetStreamAsync (Uri uri, CancellationToken cancellationToken) + { + try { + await Task.Delay (5000, cancellationToken); + } catch (TaskCanceledException ex) { + cancelled = true; + throw ex; + } + + if (cancellationToken.IsCancellationRequested) { + cancelled = true; + throw new TaskCanceledException (); + } + + var stream = typeof(ImageTests).Assembly.GetManifestResourceStream (uri.LocalPath.Substring (1)); + return stream; + } + + class MockImageRenderer + { + public MockImageRenderer (Image element) + { + Element = element; + Element.PropertyChanged += ( sender, e) => { + if (e.PropertyName == nameof (Image.Source)) + Load (); + }; + } + + public Image Element { get; set; } + + public async void Load () + { + if (initialLoad && Element.Source != null) { + initialLoad = false; + ((IElementController)Element).SetValueFromRenderer (Image.IsLoadingPropertyKey, true); + await (Element.Source as UriImageSource).GetStreamAsync (); + ((IElementController)Element).SetValueFromRenderer (Image.IsLoadingPropertyKey, false); + } + } + + bool initialLoad = true; + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/Images/crimson.jpg b/Xamarin.Forms.Core.UnitTests/Images/crimson.jpg Binary files differnew file mode 100644 index 00000000..3db7bb21 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/Images/crimson.jpg diff --git a/Xamarin.Forms.Core.UnitTests/KeyboardTests.cs b/Xamarin.Forms.Core.UnitTests/KeyboardTests.cs new file mode 100644 index 00000000..773f0903 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/KeyboardTests.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + internal class KeyboardTests : BaseTestFixture + { + [Test] + public void KeyboardTypesAreCorrect () + { + Assert.True (Keyboard.Chat is ChatKeyboard); + Assert.True (Keyboard.Email is EmailKeyboard); + Assert.True (Keyboard.Numeric is NumericKeyboard); + Assert.True (Keyboard.Telephone is TelephoneKeyboard); + Assert.True (Keyboard.Text is TextKeyboard); + Assert.True (Keyboard.Url is UrlKeyboard); + } + } + + [TestFixture] + internal class KeyboardTypeConverterTests : BaseTestFixture + { + [Test] + public void ConversionConvert () + { + + var converter = new KeyboardTypeConverter (); + Assert.True (converter.CanConvertFrom (typeof(string))); + foreach (var kvp in new Dictionary<string, Keyboard> { + {"Keyboard.Default", Keyboard.Default}, + {"Keyboard.Email", Keyboard.Email}, + {"Keyboard.Text", Keyboard.Text}, + {"Keyboard.Url", Keyboard.Url}, + {"Keyboard.Telephone", Keyboard.Telephone}, + {"Keyboard.Chat", Keyboard.Chat}, + }) + Assert.AreSame (kvp.Value, converter.ConvertFromInvariantString (kvp.Key)); + } + + [Test] + public void ConversionFail () + { + var converter = new KeyboardTypeConverter (); + Assert.Throws<InvalidOperationException> (() => converter.ConvertFromInvariantString ("Foo")); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/LabelTests.cs b/Xamarin.Forms.Core.UnitTests/LabelTests.cs new file mode 100644 index 00000000..f56f792d --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/LabelTests.cs @@ -0,0 +1,296 @@ +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using NUnit.Framework; +using NUnit.Framework.Constraints; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class LabelTests : BaseTestFixture + { + [SetUp] + public override void Setup () + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown () + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void TextAndAttributedTextMutuallyExclusive () + { + var label = new Label (); + Assert.IsNull (label.Text); + Assert.IsNull (label.FormattedText); + + label.Text = "Foo"; + Assert.AreEqual ("Foo", label.Text); + Assert.IsNull (label.FormattedText); + + var fs = new FormattedString (); + label.FormattedText = fs; + Assert.IsNull (label.Text); + Assert.AreSame (fs, label.FormattedText); + + label.Text = "Foo"; + Assert.AreEqual ("Foo", label.Text); + Assert.IsNull (label.FormattedText); + } + + [Test] + public void AssignToFontStructUpdatesFontFamily ( + [Values (NamedSize.Default, NamedSize.Large, NamedSize.Medium, NamedSize.Small, NamedSize.Micro)] NamedSize size, + [Values (FontAttributes.None, FontAttributes.Bold, FontAttributes.Italic, FontAttributes.Bold | FontAttributes.Italic)] FontAttributes attributes) + { + var label = new Label {Platform = new UnitPlatform ()}; + double startSize = label.FontSize; + var startAttributes = label.FontAttributes; + + bool firedSizeChanged = false; + bool firedAttributesChanged = false; + label.PropertyChanged += (sender, args) => { + if (args.PropertyName == Label.FontSizeProperty.PropertyName) + firedSizeChanged = true; + if (args.PropertyName == Label.FontAttributesProperty.PropertyName) + firedAttributesChanged = true; + }; + + label.Font = Font.OfSize ("Testing123", size).WithAttributes (attributes); + + Assert.AreEqual (Device.GetNamedSize (size, typeof (Label), true), label.FontSize); + Assert.AreEqual (attributes, label.FontAttributes); + Assert.AreEqual (startSize != label.FontSize, firedSizeChanged); + Assert.AreEqual (startAttributes != label.FontAttributes, firedAttributesChanged); + } + + [Test] + public void AssignToFontFamilyUpdatesFont () + { + var label = new Label {Platform = new UnitPlatform ()}; + + label.FontFamily = "CrazyFont"; + Assert.AreEqual (label.Font, Font.OfSize ("CrazyFont", label.FontSize)); + } + + [Test] + public void AssignToFontSizeUpdatesFont () + { + var label = new Label {Platform = new UnitPlatform ()}; + + label.FontSize = 1000; + Assert.AreEqual (label.Font, Font.SystemFontOfSize (1000)); + } + + [Test] + public void AssignedToFontSizeUpdatesFontDouble () + { + var label = new Label {Platform = new UnitPlatform ()}; + + label.FontSize = 10.7; + Assert.AreEqual (label.Font, Font.SystemFontOfSize (10.7)); + } + + [Test] + public void AssignedToFontSizeDouble () + { + var label = new Label {Platform = new UnitPlatform ()}; + + label.FontSize = 10.7; + Assert.AreEqual (label.FontSize, 10.7); + } + + + [Test] + public void AssignToFontAttributesUpdatesFont () + { + var label = new Label {Platform = new UnitPlatform ()}; + + label.FontAttributes = FontAttributes.Italic | FontAttributes.Bold; + Assert.AreEqual (label.Font, Font.SystemFontOfSize (label.FontSize, FontAttributes.Bold | FontAttributes.Italic)); + } + + [Test] + public void LabelResizesWhenFontChanges () + { + var label = new Label {Platform = new UnitPlatform ((ve, w, h) => { + var l = (Label) ve; + return new SizeRequest(new Size(l.Font.FontSize, l.Font.FontSize)); + }), IsPlatformEnabled = true}; + + Assert.AreEqual (label.Font.FontSize, label.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request.Width); + + bool fired = false; + + label.MeasureInvalidated += (sender, args) => { + Assert.AreEqual (25, label.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request.Width); + fired = true; + }; + + + label.FontSize = 25; + + Assert.True (fired); + } + + [Test] + public void FontSizeConverterTests () + { + var converter = new FontSizeConverter (); + Assert.AreEqual (12, converter.ConvertFromInvariantString ("12")); + Assert.AreEqual (10.7, converter.ConvertFromInvariantString ("10.7")); + } + + [Test] + public void FontSizeCanBeSetFromStyle () + { + var label = new Label (); + + Assert.AreEqual (10.0, label.FontSize); + + label.SetValue (Label.FontSizeProperty, 1.0, true); + Assert.AreEqual (1.0, label.FontSize); + } + + [Test] + public void ManuallySetFontSizeNotOverridenByStyle () + { + var label = new Label (); + + Assert.AreEqual (10.0, label.FontSize); + + label.SetValue (Label.FontSizeProperty, 2.0, false); + Assert.AreEqual (2.0, label.FontSize); + + label.SetValue (Label.FontSizeProperty, 1.0, true); + Assert.AreEqual (2.0, label.FontSize); + } + + [Test] + public void ChangingHorizontalTextAlignmentFiresXAlignChanged () + { + var label = new Label () { HorizontalTextAlignment = TextAlignment.Center }; + + var xAlignFired = false; + var horizontalTextAlignmentFired = false; + + label.PropertyChanged += (sender, args) => { + if (args.PropertyName == "XAlign") { + xAlignFired = true; + } else if (args.PropertyName == Label.HorizontalTextAlignmentProperty.PropertyName) { + horizontalTextAlignmentFired = true; + } + }; + + label.HorizontalTextAlignment = TextAlignment.End; + + Assert.True(xAlignFired); + Assert.True(horizontalTextAlignmentFired); + } + + [Test] + public void ChangingVerticalTextAlignmentFiresYAlignChanged () + { + var label = new Label () { VerticalTextAlignment = TextAlignment.Center }; + + var yAlignFired = false; + var verticalTextAlignmentFired = false; + + label.PropertyChanged += (sender, args) => { + if (args.PropertyName == "YAlign") { + yAlignFired = true; + } else if (args.PropertyName == Label.VerticalTextAlignmentProperty.PropertyName) { + verticalTextAlignmentFired = true; + } + }; + + label.VerticalTextAlignment = TextAlignment.End; + + Assert.True (yAlignFired); + Assert.True (verticalTextAlignmentFired); + } + + [Test] + public void EntryCellXAlignBindingMatchesHorizontalTextAlignmentBinding () + { + var vm = new ViewModel (); + vm.HorizontalAlignment = TextAlignment.Center; + + var labelXAlign = new Label () { BindingContext = vm }; + labelXAlign.SetBinding (Label.XAlignProperty, new Binding ("HorizontalAlignment")); + + var labelHorizontalTextAlignment = new Label () { BindingContext = vm }; + labelHorizontalTextAlignment.SetBinding (Label.HorizontalTextAlignmentProperty, new Binding ("HorizontalAlignment")); + + Assert.AreEqual (TextAlignment.Center, labelXAlign.XAlign); + Assert.AreEqual (TextAlignment.Center, labelHorizontalTextAlignment.HorizontalTextAlignment); + + vm.HorizontalAlignment = TextAlignment.End; + + Assert.AreEqual (TextAlignment.End, labelXAlign.XAlign); + Assert.AreEqual (TextAlignment.End, labelHorizontalTextAlignment.HorizontalTextAlignment); + } + + [Test] + public void EntryCellYAlignBindingMatchesVerticalTextAlignmentBinding () + { + var vm = new ViewModel (); + vm.VerticalAlignment = TextAlignment.Center; + + var labelYAlign = new Label () { BindingContext = vm }; + labelYAlign.SetBinding (Label.YAlignProperty, new Binding ("VerticalAlignment")); + + var labelVerticalTextAlignment = new Label () { BindingContext = vm }; + labelVerticalTextAlignment.SetBinding (Label.VerticalTextAlignmentProperty, new Binding ("VerticalAlignment")); + + Assert.AreEqual (TextAlignment.Center, labelYAlign.YAlign); + Assert.AreEqual (TextAlignment.Center, labelVerticalTextAlignment.VerticalTextAlignment); + + vm.VerticalAlignment = TextAlignment.End; + + Assert.AreEqual (TextAlignment.End, labelYAlign.YAlign); + Assert.AreEqual (TextAlignment.End, labelVerticalTextAlignment.VerticalTextAlignment); + } + + sealed class ViewModel : INotifyPropertyChanged + { + TextAlignment horizontalAlignment; + TextAlignment verticalAlignment; + + public TextAlignment HorizontalAlignment + { + get { return horizontalAlignment; } + set + { + horizontalAlignment = value; + OnPropertyChanged(); + } + } + + public TextAlignment VerticalAlignment + { + get { return verticalAlignment; } + set + { + verticalAlignment = value; + OnPropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + + void OnPropertyChanged ([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (propertyName)); + } + } + } +} + diff --git a/Xamarin.Forms.Core.UnitTests/LayoutOptionsUnitTests.cs b/Xamarin.Forms.Core.UnitTests/LayoutOptionsUnitTests.cs new file mode 100644 index 00000000..476cf73c --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/LayoutOptionsUnitTests.cs @@ -0,0 +1,22 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class LayoutOptionsUnitTests : BaseTestFixture + { + [Test] + public void TestTypeConverter () + { + var converter = new LayoutOptionsConverter (); + Assert.True (converter.CanConvertFrom (typeof(string))); + Assert.AreEqual (LayoutOptions.Center, converter.ConvertFromInvariantString ("LayoutOptions.Center")); + Assert.AreEqual (LayoutOptions.Center, converter.ConvertFromInvariantString ("Center")); + Assert.AreNotEqual (LayoutOptions.CenterAndExpand, converter.ConvertFromInvariantString ("Center")); + Assert.Throws<InvalidOperationException> (() => converter.ConvertFromInvariantString ("foo")); + Assert.Throws<InvalidOperationException> (() => converter.ConvertFromInvariantString ("foo.bar")); + Assert.Throws<InvalidOperationException> (() => converter.ConvertFromInvariantString ("foo.bar.baz")); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/ListProxyTests.cs b/Xamarin.Forms.Core.UnitTests/ListProxyTests.cs new file mode 100644 index 00000000..6fa85d2c --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ListProxyTests.cs @@ -0,0 +1,426 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ListProxyTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void ListCount() + { + var list = new List<string> { "foo", "bar" }; + var proxy = new ListProxy (list); + + Assert.AreEqual (list.Count, proxy.Count); + list.Add ("baz"); + Assert.AreEqual (list.Count, proxy.Count); + } + + [Test] + public void CollectionCount() + { + var list = new Collection<string> { "foo", "bar" }; + var proxy = new ListProxy (list); + + Assert.AreEqual (list.Count, proxy.Count); + list.Add ("baz"); + Assert.AreEqual (list.Count, proxy.Count); + } + + [Test] + [Description ("Count should ensure that the window is created if neccessary")] + public void EnumerableInitialCount() + { + var enumerable = Enumerable.Range (0, 100); + var proxy = new ListProxy (enumerable, 10); + + Assert.AreEqual (10, proxy.Count); + } + + [Test] + public void EnumerableCount() + { + var enumerable = Enumerable.Range (0, 100); + var proxy = new ListProxy (enumerable, 10); + + int changed = 0; + proxy.CountChanged += (o, e) => changed++; + + var enumerator = proxy.GetEnumerator(); + enumerator.MoveNext(); + + Assert.AreEqual (10, proxy.Count); + Assert.AreEqual (1, changed); + + enumerator.MoveNext(); + + Assert.AreEqual (10, proxy.Count); + Assert.AreEqual (1, changed); + + while (enumerator.MoveNext()) { + } + + enumerator.Dispose(); + + Assert.AreEqual (100, proxy.Count); + Assert.AreEqual (19, changed); + + using (enumerator = proxy.GetEnumerator()) { + + Assert.AreEqual (100, proxy.Count); + + while (enumerator.MoveNext()) + Assert.AreEqual (100, proxy.Count); + + Assert.AreEqual (100, proxy.Count); + } + + Assert.AreEqual (19, changed); + } + + [Test] + public void InsideWindowSize() + { + var numbers = Enumerable.Range (0, 100); + var proxy = new ListProxy (numbers, 10); + + int i = (int)proxy[5]; + Assert.That (i, Is.EqualTo (5)); + } + + [Test] + public void IndexOutsideWindowSize() + { + var numbers = Enumerable.Range (0, 100); + var proxy = new ListProxy (numbers, 10); + + int i = (int)proxy[50]; + Assert.That (i, Is.EqualTo (50)); + } + + [Test] + public void IndexInsideToOutsideWindowSize() + { + var numbers = Enumerable.Range (0, 100); + var proxy = new ListProxy (numbers, 10); + + int i = (int)proxy[5]; + Assert.That (i, Is.EqualTo (5)); + + i = (int)proxy[50]; + Assert.That (i, Is.EqualTo (50)); + } + + [Test] + public void IndexOutsideToPreWindowSize() + { + var numbers = Enumerable.Range (0, 100); + var proxy = new ListProxy (numbers, 10); + + int i = (int)proxy[50]; + Assert.That (i, Is.EqualTo (50)); + + i = (int)proxy[5]; + Assert.That (i, Is.EqualTo (5)); + } + + [Test] + public void EnumerableIndexOutOfRange() + { + var numbers = Enumerable.Range (0, 100); + var proxy = new ListProxy (numbers); + + Assert.That (() => proxy[100], Throws.InstanceOf<ArgumentOutOfRangeException>()); + } + + class IntCollection + : ICollection + { + readonly List<int> ints; + + public IntCollection (IEnumerable<int> ints) + { + this.ints = ints.ToList(); + } + + public IEnumerator GetEnumerator() + { + return ints.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void CopyTo (Array array, int index) + { + throw new NotImplementedException(); + } + + public int Count { get { return ints.Count; }} + + public object SyncRoot + { + get { throw new NotImplementedException(); } + } + + public bool IsSynchronized + { + get { throw new NotImplementedException(); } + } + + public bool IsReadOnly { get { return true; } } + } + + [Test] + public void CollectionIndexOutOfRange() + { + var numbers = new IntCollection (Enumerable.Range (0, 100)); + var proxy = new ListProxy (numbers); + + Assert.That (() => proxy[100], Throws.InstanceOf<ArgumentOutOfRangeException>()); + } + + [Test] + public void ListIndexOutOfRange() + { + var numbers = Enumerable.Range (0, 100).ToList(); + var proxy = new ListProxy (numbers); + + Assert.That (() => proxy[100], Throws.InstanceOf<ArgumentOutOfRangeException>()); + } + + [Test] + public void CollectionChangedWhileEnumerating() + { + var c = new ObservableCollection<string> { "foo", "bar" }; + var p = new ListProxy (c); + + IEnumerator<object> e = p.GetEnumerator(); + Assert.IsTrue (e.MoveNext(), "Initial MoveNext() failed, test can't continue"); + + c.Add ("baz"); + + Assert.That (() => e.MoveNext(), Throws.InvalidOperationException, + "MoveNext did not throw an exception when the underlying collection had changed"); + } + + [Test] + public void SynchronizedCollectionAccess() + { + var collection = new ObservableCollection<string> { "foo" }; + var context = new object(); + + var list = new ListProxy (collection); + + bool executed = false; + BindingBase.EnableCollectionSynchronization (collection, context, (enumerable, o, method, access) => { + executed = true; + Assert.AreSame (collection, enumerable); + Assert.AreSame (context, o); + Assert.IsNotNull (method); + Assert.IsFalse (access); + + lock (enumerable) + method(); + }); + + object value = list[0]; + + Assert.IsTrue (executed, "Callback was not executed"); + } + + [Test] + public void SynchronizedCollectionAdd() + { + bool invoked = false; + Device.PlatformServices = new MockPlatformServices (invokeOnMainThread: action => { + invoked = true; + action(); + }); + + var collection = new ObservableCollection<string> { "foo" }; + var context = new object(); + + var list = new ListProxy (collection); + + Assert.IsFalse (invoked, "An invoke shouldn't be executed just setting up ListProxy"); + + bool executed = false; + BindingBase.EnableCollectionSynchronization (collection, context, (enumerable, o, method, access) => { + executed = true; + Assert.AreSame (collection, enumerable); + Assert.AreSame (context, o); + Assert.IsNotNull (method); + Assert.IsFalse (access); + + lock (enumerable) + method(); + }); + + var mre = new ManualResetEvent (false); + + Task.Factory.StartNew (() => { + lock (collection) + collection.Add ("foo"); + + mre.Set(); + }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default); + + mre.WaitOne (5000); + + Assert.IsTrue (executed, "Callback was not executed"); + Assert.IsTrue (invoked, "Callback was not executed on the UI thread"); + } + + [Test] + public void ClearEnumerable() + { + var proxy = new ListProxy (Enumerable.Range (0, 100)); + var enumerator = proxy.GetEnumerator(); + enumerator.MoveNext(); + enumerator.MoveNext(); + + proxy.Clear(); + + Assert.AreEqual (100, proxy.Count); + Assert.That (() => enumerator.MoveNext(), Throws.InvalidOperationException); + } + + [Test] + public void ClearCollection() + { + var proxy = new ListProxy (new IntCollection (Enumerable.Range (0, 100))); + var enumerator = proxy.GetEnumerator(); + enumerator.MoveNext(); + enumerator.MoveNext(); + + proxy.Clear(); + + Assert.AreEqual (100, proxy.Count); + Assert.That (() => enumerator.MoveNext(), Throws.InvalidOperationException); + } + + [Test] + public void ClearList() + { + var proxy = new ListProxy (Enumerable.Range (0, 100).ToList()); + var enumerator = proxy.GetEnumerator(); + enumerator.MoveNext(); + enumerator.MoveNext(); + + proxy.Clear(); + + Assert.AreEqual (100, proxy.Count); + Assert.That (() => enumerator.MoveNext(), Throws.InvalidOperationException); + } + + [Test] + public void IndexOfValueTypeNonList() + { + var proxy = new ListProxy (Enumerable.Range (0, 100)); + Assert.AreEqual (1, proxy.IndexOf (1)); + } + + [Test] + public void EnumeratorForEnumerable() + { + var proxy = new ListProxy (Enumerable.Range (0, 2)); + + var enumerator = proxy.GetEnumerator(); + Assert.That (enumerator.Current, Is.Null); + Assert.That (enumerator.MoveNext(), Is.True); + Assert.That (enumerator.Current, Is.EqualTo (0)); + Assert.That (enumerator.MoveNext(), Is.True); + Assert.That (enumerator.Current, Is.EqualTo (1)); + Assert.That (enumerator.MoveNext(), Is.False); + } + + [Test] + public void ProxyIsWeaklyHeldByINotifyCollectionChanged() + { + ObservableCollection<string> collection = new ObservableCollection<string>(); + + WeakReference weakProxy = null; + + int i = 0; + Action create = null; + create = () => { + if (i++ < 1024) { + create(); + return; + } + + var proxy = new ListProxy (collection); + weakProxy = new WeakReference (proxy); + }; + + create(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + Assert.That (weakProxy.IsAlive, Is.False); + } + + [Test] + public void IEnumerableAddDoesNotReport0() + { + var custom = new CustomINCC(); + custom.Add ("test"); + custom.Add ("test2"); + + var proxy = new ListProxy (custom); + Assert.That (proxy.Count, Is.EqualTo (2)); + + custom.Add ("testing"); + Assert.That (proxy.Count, Is.EqualTo (3)); + } + + class CustomINCC : IEnumerable<string>, INotifyCollectionChanged + { + public event NotifyCollectionChangedEventHandler CollectionChanged; + List<string> Items = new List<string> (); + + public void Add (string s) + { + Items.Add(s); + if (CollectionChanged != null) + CollectionChanged (this, new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, s)); + } + + public IEnumerator<string> GetEnumerator () + { + return Items.GetEnumerator (); + } + IEnumerator IEnumerable.GetEnumerator () + { + return Items.GetEnumerator (); + } + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ListViewTests.cs b/Xamarin.Forms.Core.UnitTests/ListViewTests.cs new file mode 100644 index 00000000..d5672f77 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ListViewTests.cs @@ -0,0 +1,1490 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Input; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ListViewTests : BaseTestFixture + { + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + Device.Info = null; + } + + [SetUp] + public override void Setup () + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + Device.Info = new TestDeviceInfo (); + } + + [Test] + public void TestConstructor () + { + var listView = new ListView (); + + Assert.Null (listView.ItemsSource); + Assert.Null (listView.ItemTemplate); + Assert.AreEqual (LayoutOptions.FillAndExpand, listView.HorizontalOptions); + Assert.AreEqual (LayoutOptions.FillAndExpand, listView.VerticalOptions); + } + + internal class ListItem + { + public string Name { get; set; } + public string Description { get; set; } + } + + [Test] + public void TestTemplating () + { + var cellTemplate = new DataTemplate (typeof (TextCell)); + cellTemplate.SetBinding (TextCell.TextProperty, new Binding ("Name")); + cellTemplate.SetBinding (TextCell.DetailProperty, new Binding ("Description")); + + var listView = new ListView { + ItemsSource = new[] { + new ListItem {Name = "Foo", Description = "Bar"}, + new ListItem {Name = "Baz", Description = "Raz"} + }, + ItemTemplate = cellTemplate + }; + + var cell = (Cell)listView.ItemTemplate.CreateContent (); + + var textCell = (TextCell)cell; + cell.BindingContext = listView.ItemsSource.OfType<ListItem> ().First (); + + Assert.AreEqual ("Foo", textCell.Text); + Assert.AreEqual ("Bar", textCell.Detail); + } + + [Test] + public void TemplateNullObject() + { + var listView = new ListView { + ItemsSource = new object[] { + null + } + }; + + Cell cell = listView.TemplatedItems[0]; + + Assert.That (cell, Is.Not.Null); + Assert.That (cell, Is.InstanceOf<TextCell>()); + Assert.That (((TextCell) cell).Text, Is.Null); + } + + [Test] + [Description ("Setting GroupDisplayBinding or GroupHeaderTemplate when the other is set should set the other one to null.")] + public void SettingGroupHeaderTemplateSetsDisplayBindingToNull() + { + var listView = new ListView { + GroupDisplayBinding = new Binding ("Path") + }; + + listView.GroupHeaderTemplate = new DataTemplate (typeof (TextCell)); + + Assert.That (listView.GroupDisplayBinding, Is.Null); + } + + [Test] + [Description ("Setting GroupDisplayBinding or GroupHeaderTemplate when the other is set should set the other one to null.")] + public void SettingGroupDisplayBindingSetsHeaderTemplateToNull() + { + var listView = new ListView { + GroupHeaderTemplate = new DataTemplate (typeof (TextCell)) + }; + + listView.GroupDisplayBinding = new Binding ("Path"); + + Assert.That (listView.GroupHeaderTemplate, Is.Null); + } + + [Test] + [Description ("You should be able to set ItemsSource without having set the other properties first without issue")] + public void SettingItemsSourceWithoutBindingsOrItemsSource() + { + var listView = new ListView { + IsGroupingEnabled = true + }; + + Assert.That (() => listView.ItemsSource = new[] { new[] { new object() } }, Throws.Nothing); + } + + [Test] + public void DefaultGroupHeaderTemplates() + { + var items = new[] { new[] { new object() } }; + + var listView = new ListView { + IsGroupingEnabled = true, + ItemsSource = items + }; + + var til = (TemplatedItemsList<ItemsView<Cell>, Cell>)((IList)listView.TemplatedItems)[0]; + Cell cell = til.HeaderContent; + + Assert.That (cell, Is.Not.Null); + Assert.That (cell, Is.InstanceOf<TextCell>()); + Assert.That (((TextCell) cell).Text, Is.EqualTo (items[0].ToString())); + } + + [Test] + [Description ("Tapping a different item (row) that is equal to the current item selection should still raise ItemSelected")] + public void NotifyRowTappedDifferentIndex() + { + string item = "item"; + + var listView = new ListView { + ItemsSource = new[] { + item, + item + } + }; + + listView.NotifyRowTapped (0); + + bool raised = false; + listView.ItemSelected += (sender, arg) => raised = true; + + listView.NotifyRowTapped (1); + Assert.That (raised, Is.True, "ItemSelected was not raised"); + } + + [Test] + public void DoesNotCrashWhenAddingToSource () + { + var items = new ObservableCollection<string> { + "Foo", + "Bar", + "Baz" + }; + + var listView = new ListView { + ItemsSource = items, + ItemTemplate = new DataTemplate(typeof(TextCell)) + }; + + Assert.DoesNotThrow (() => items.Add ("Blah")); + } + + [Test] + public void DoesNotThrowWhenMovingInSource () + { + var items = new ObservableCollection<string> { + "Foo", + "Bar", + "Baz" + }; + + var listView = new ListView { + ItemsSource = items, + ItemTemplate = new DataTemplate (typeof (TextCell)) + }; + + Assert.DoesNotThrow (() => items.Move (0, 1)); + } + + [Test] + [Description ("A cell being tapped from the UI should raise both tapped events, but not change ItemSelected")] + public void NotifyTappedSameItem() + { + int cellTapped = 0; + int itemTapped = 0; + int itemSelected = 0; + + var listView = new ListView { + ItemsSource = new[] { "item" }, + ItemTemplate = new DataTemplate (() => { + var cell = new TextCell(); + cell.Tapped += (s, e) => { + cellTapped++; + }; + return cell; + }) + }; + + listView.ItemTapped += (sender, arg) => itemTapped++; + listView.ItemSelected += (sender, arg) => itemSelected++; + + listView.NotifyRowTapped (0); + + Assert.That (cellTapped, Is.EqualTo (1), "Cell.Tapped was not raised"); + Assert.That (itemTapped, Is.EqualTo (1), "ListView.ItemTapped was not raised"); + Assert.That (itemSelected, Is.EqualTo (1), "ListView.ItemSelected was not raised"); + + listView.NotifyRowTapped (0); + + Assert.That (cellTapped, Is.EqualTo (2), "Cell.Tapped was not raised a second time"); + Assert.That (itemTapped, Is.EqualTo (2), "ListView.ItemTapped was not raised a second time"); + Assert.That (itemSelected, Is.EqualTo (1), "ListView.ItemSelected was raised a second time"); + } + + [Test] + public void ScrollTo() + { + var listView = new ListView { + IsPlatformEnabled = true, + Platform = new UnitPlatform() + }; + + object item = new object(); + + bool requested = false; + listView.ScrollToRequested += (sender, args) => { + requested = true; + + Assert.That (args.Item, Is.SameAs (item)); + Assert.That (args.Group, Is.Null); + Assert.That (args.Position, Is.EqualTo (ScrollToPosition.Center)); + Assert.That (args.ShouldAnimate, Is.EqualTo (true)); + }; + + listView.ScrollTo (item, ScrollToPosition.Center, animated: true); + Assert.That (requested, Is.True); + } + + [Test] + public void ScrollToDelayed() + { + var listView = new ListView(); + + object item = new object(); + + bool requested = false; + listView.ScrollToRequested += (sender, args) => { + requested = true; + + Assert.That (args.Item, Is.SameAs (item)); + Assert.That (args.Group, Is.Null); + Assert.That (args.Position, Is.EqualTo (ScrollToPosition.Center)); + Assert.That (args.ShouldAnimate, Is.EqualTo (true)); + }; + + listView.ScrollTo (item, ScrollToPosition.Center, animated: true); + Assert.That (requested, Is.False); + + listView.IsPlatformEnabled = true; + listView.Platform = new UnitPlatform(); + + Assert.That (requested, Is.True); + } + + [Test] + public void ScrollToGroup() + { + // Fake a renderer so we pass along messages right away + var listView = new ListView { + IsPlatformEnabled = true, + Platform = new UnitPlatform(), + IsGroupingEnabled = true + }; + + object item = new object(); + object group = new object(); + + bool requested = false; + listView.ScrollToRequested += (sender, args) => { + requested = true; + + Assert.That (args.Item, Is.SameAs (item)); + Assert.That (args.Group, Is.SameAs (group)); + Assert.That (args.Position, Is.EqualTo (ScrollToPosition.Center)); + Assert.That (args.ShouldAnimate, Is.EqualTo (true)); + }; + + listView.ScrollTo (item, group, ScrollToPosition.Center, animated: true); + Assert.That (requested, Is.True); + } + + [Test] + public void ScrollToInvalid() + { + var listView = new ListView { + IsPlatformEnabled = true, + Platform = new UnitPlatform() + }; + + Assert.That (() => listView.ScrollTo (new object(), (ScrollToPosition) 500, true), Throws.ArgumentException); + Assert.That (() => listView.ScrollTo (new object(), new object(), ScrollToPosition.Start, true), Throws.InvalidOperationException); + + listView.IsGroupingEnabled = true; + Assert.That (() => listView.ScrollTo (new object(), new object(), (ScrollToPosition) 500, true), Throws.ArgumentException); + } + + [Test] + public void GetSizeRequest () + { + var listView = new ListView { + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + HasUnevenRows = false, + RowHeight = 50, + ItemsSource = Enumerable.Range (0, 20).ToList () + }; + + + var sizeRequest = listView.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + Assert.AreEqual (40, sizeRequest.Minimum.Width); + Assert.AreEqual (40, sizeRequest.Minimum.Height); + Assert.AreEqual (50, sizeRequest.Request.Width); + Assert.AreEqual (50 * 20, sizeRequest.Request.Height); + } + + [Test] + public void GetSizeRequestUneven () + { + var listView = new ListView { + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + HasUnevenRows = true, + RowHeight = 50, + ItemsSource = Enumerable.Range (0, 20).ToList () + }; + + + var sizeRequest = listView.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + Assert.AreEqual (40, sizeRequest.Minimum.Width); + Assert.AreEqual (40, sizeRequest.Minimum.Height); + Assert.AreEqual (50, sizeRequest.Request.Width); + Assert.AreEqual (100, sizeRequest.Request.Height); + } + + public class ListItemValue : IComparable<ListItemValue> + { + public string Name { get; private set; } + + public ListItemValue (string name) + { + Name = name; + } + + int IComparable<ListItemValue>.CompareTo (ListItemValue value) + { + return Name.CompareTo (value.Name); + } + + public string Label + { + get { return Name[0].ToString (); } + } + } + + public class ListItemCollection : ObservableCollection<ListItemValue> + { + public string Title { get; private set; } + + public ListItemCollection (string title) + { + Title = title; + } + + public static List<ListItemValue> GetSortedData () + { + var items = ListItems; + items.Sort (); + return items; + } + + // Data used to populate our list. + static readonly List<ListItemValue> ListItems = new List<ListItemValue> () { + new ListItemValue ("Babbage"), + new ListItemValue ("Boole"), + new ListItemValue ("Berners-Lee"), + new ListItemValue ("Atanasoff"), + new ListItemValue ("Allen"), + new ListItemValue ("Cormack"), + new ListItemValue ("Cray"), + new ListItemValue ("Dijkstra"), + new ListItemValue ("Dix"), + new ListItemValue ("Dewey"), + new ListItemValue ("Erdos"), + }; + } + + public class TestCell : TextCell + { + public static int NumberOfCells = 0; + + public TestCell () + { + Interlocked.Increment (ref NumberOfCells); + } + + ~TestCell () + { + Interlocked.Decrement (ref NumberOfCells); + } + } + + ObservableCollection<ListItemCollection> SetupList () + { + var allListItemGroups = new ObservableCollection<ListItemCollection> (); + + foreach (var item in ListItemCollection.GetSortedData ()) { + // Attempt to find any existing groups where theg group title matches the first char of our ListItem's name. + var listItemGroup = allListItemGroups.FirstOrDefault (g => g.Title == item.Label); + + // If the list group does not exist, we create it. + if (listItemGroup == null) { + listItemGroup = new ListItemCollection (item.Label) { item }; + allListItemGroups.Add (listItemGroup); + } else { + // If the group does exist, we simply add the demo to the existing group. + listItemGroup.Add (item); + } + } + return allListItemGroups; + } + + [Test] + public void UncollectableHeaderReferences () + { + var list = new ListView { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + ItemTemplate = new DataTemplate (typeof (TextCell)) { + Bindings = { + {TextCell.TextProperty, new Binding ("Name")} + } + }, + GroupHeaderTemplate = new DataTemplate (typeof (TestCell)) { + Bindings = { + {TextCell.TextProperty, new Binding ("Title")} + } + }, + IsGroupingEnabled = true, + ItemsSource = SetupList (), + }; + + Assert.AreEqual (5, TestCell.NumberOfCells); + + var newList1 = SetupList (); + var newList2 = SetupList (); + + for (var i = 0; i < 400; i++) { + list.ItemsSource = i % 2 > 0 ? newList1 : newList2; + + // grab a header just so we can be sure its reailized + var header = list.TemplatedItems.GetGroup (0).HeaderContent; + } + + GC.Collect (); + GC.WaitForPendingFinalizers (); + + // use less or equal because mono will keep the last header var alive no matter what + Assert.True (TestCell.NumberOfCells <= 6); + + var keepAlive = list.ToString (); + } + + [Test] + public void CollectionChangedMultipleFires () + { + var source = new ObservableCollection<string> { + "Foo", + "Bar" + }; + + var list = new ListView { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + ItemsSource = source, + ItemTemplate = new DataTemplate (typeof (TextCell)) + }; + + int fireCount = 0; + list.TemplatedItems.CollectionChanged += (sender, args) => { + fireCount++; + }; + + source.Add ("Baz"); + + Assert.AreEqual (1, fireCount); + } + + [Test] + public void GroupedCollectionChangedMultipleFires () + { + var source = new ObservableCollection<ObservableCollection <string>> { + new ObservableCollection<string> {"Foo"}, + new ObservableCollection<string> {"Bar"} + }; + + var list = new ListView { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + IsGroupingEnabled = true, + ItemsSource = source, + ItemTemplate = new DataTemplate (typeof (TextCell)) { + Bindings = { + {TextCell.TextProperty, new Binding (".") } + } + } + }; + + int fireCount = 0; + list.TemplatedItems.GroupedCollectionChanged += (sender, args) => { + fireCount++; + }; + + source[0].Add ("Baz"); + + Assert.AreEqual (1, fireCount); + } + + [Test] + public void HeaderAsView() + { + var label = new Label { Text = "header" }; + var lv = new ListView { + Header = label + }; + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.SameAs (label)); + } + + [Test] + public void HeaderTemplated() + { + var lv = new ListView { + Header = "header", + HeaderTemplate = new DataTemplate(typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding (".") } + } + } + }; + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.Not.Null); + Assert.That (controller.HeaderElement, Is.InstanceOf<Label>()); + Assert.That (((Label) controller.HeaderElement).Text, Is.EqualTo (lv.Header)); + } + + [Test] + public void HeaderTemplateThrowsIfCell () + { + var lv = new ListView (); + + Assert.Throws<ArgumentException> (() => lv.HeaderTemplate = new DataTemplate (typeof (TextCell))); + } + + [Test] + public void FooterTemplateThrowsIfCell () + { + var lv = new ListView (); + + Assert.Throws<ArgumentException> (() => lv.FooterTemplate = new DataTemplate (typeof (TextCell))); + } + + [Test] + public void HeaderObjectTemplatedChanged() + { + var lv = new ListView { + Header = "header", + HeaderTemplate = new DataTemplate (typeof (Label)) { + Bindings = { + { Label.TextProperty, new Binding (".") } + } + } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changed = true; + }; + + lv.Header = "newheader"; + + Assert.That (changing, Is.False); + Assert.That (changed, Is.False); + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.Not.Null); + Assert.That (controller.HeaderElement, Is.InstanceOf<Label>()); + Assert.That (((Label) controller.HeaderElement).Text, Is.EqualTo (lv.Header)); + } + + [Test] + public void HeaderViewChanged() + { + var lv = new ListView { + Header = new Label { Text = "header" } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changed = true; + }; + + Label label = new Label { Text = "header" }; + lv.Header = label; + + Assert.That (changing, Is.True); + Assert.That (changed, Is.True); + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.SameAs (label)); + } + + + [Test] + public void HeaderTemplateChanged() + { + var lv = new ListView { + Header = "header", + HeaderTemplate = new DataTemplate(typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding (".") } + } + } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changed = true; + }; + + lv.HeaderTemplate = new DataTemplate (typeof (Entry)) { + Bindings = { + { Entry.TextProperty, new Binding (".") } + } + }; + + Assert.That (changing, Is.True); + Assert.That (changed, Is.True); + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.Not.Null); + Assert.That (controller.HeaderElement, Is.InstanceOf<Entry>()); + Assert.That (((Entry) controller.HeaderElement).Text, Is.EqualTo (lv.Header)); + } + + [Test] + public void HeaderTemplateChangedNoObject() + { + var lv = new ListView { + HeaderTemplate = new DataTemplate(typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding (".") } + } + } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changed = true; + }; + + lv.HeaderTemplate = new DataTemplate (typeof (Entry)) { + Bindings = { + { Entry.TextProperty, new Binding (".") } + } + }; + + Assert.That (changing, Is.False); + Assert.That (changed, Is.False); + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.Null); + } + + [Test] + public void HeaderNoTemplate() + { + var lv = new ListView { + Header = "foo" + }; + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.Not.Null); + Assert.That (controller.HeaderElement, Is.InstanceOf<Label>()); + Assert.That (((Label) controller.HeaderElement).Text, Is.EqualTo (lv.Header)); + } + + [Test] + public void HeaderChangedNoTemplate() + { + var lv = new ListView { + Header = "foo" + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changed = true; + }; + + lv.Header = "bar"; + + Assert.That (changing, Is.True); + Assert.That (changed, Is.True); + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.Not.Null); + Assert.That (controller.HeaderElement, Is.InstanceOf<Label>()); + Assert.That (((Label) controller.HeaderElement).Text, Is.EqualTo (lv.Header)); + } + + [Test] + public void HeaderViewButTemplated() + { + var lv = new ListView { + Header = new Entry { Text = "foo" }, + HeaderTemplate = new DataTemplate (typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding ("Text") } + } + } + }; + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.Not.Null); + Assert.That (controller.HeaderElement, Is.InstanceOf<Label>()); + Assert.That (((Label) controller.HeaderElement).Text, Is.EqualTo (((Entry)lv.Header).Text)); + } + + [Test] + public void HeaderTemplatedChangedToView() + { + var lv = new ListView { + Header = new Entry { Text = "foo" }, + HeaderTemplate = new DataTemplate (typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding ("Text") } + } + } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changed = true; + }; + + lv.HeaderTemplate = null; + + Assert.That (changing, Is.True); + Assert.That (changed, Is.True); + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.Not.Null); + Assert.That (controller.HeaderElement, Is.InstanceOf<Entry>()); + Assert.That (((Entry) controller.HeaderElement).Text, Is.EqualTo (((Entry)lv.Header).Text)); + } + + [Test] + public void HeaderTemplatedSetToNull() + { + var lv = new ListView { + Header = "header", + HeaderTemplate = new DataTemplate(typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding (".") } + } + } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "HeaderElement") + changed = true; + }; + + lv.Header = null; + + Assert.That (changing, Is.True); + Assert.That (changed, Is.True); + + IListViewController controller = lv; + Assert.That (controller.HeaderElement, Is.Null); + } + + [Test] + public void FooterAsView() + { + var label = new Label { Text = "footer" }; + var lv = new ListView { + Footer = label + }; + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.SameAs (label)); + } + + [Test] + public void FooterTemplated() + { + var lv = new ListView { + Footer = "footer", + FooterTemplate = new DataTemplate(typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding (".") } + } + } + }; + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.Not.Null); + Assert.That (controller.FooterElement, Is.InstanceOf<Label>()); + Assert.That (((Label) controller.FooterElement).Text, Is.EqualTo (lv.Footer)); + } + + [Test] + public void FooterObjectTemplatedChanged() + { + var lv = new ListView { + Footer = "footer", + FooterTemplate = new DataTemplate (typeof (Label)) { + Bindings = { + { Label.TextProperty, new Binding (".") } + } + } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "FooterElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "FooterElement") + changed = true; + }; + + lv.Footer = "newfooter"; + + Assert.That (changing, Is.False); + Assert.That (changed, Is.False); + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.Not.Null); + Assert.That (controller.FooterElement, Is.InstanceOf<Label>()); + Assert.That (((Label) controller.FooterElement).Text, Is.EqualTo (lv.Footer)); + } + + [Test] + public void FooterViewChanged() + { + var lv = new ListView { + Footer = new Label { Text = "footer" } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "FooterElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "FooterElement") + changed = true; + }; + + Label label = new Label { Text = "footer" }; + lv.Footer = label; + + Assert.That (changing, Is.True); + Assert.That (changed, Is.True); + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.SameAs (label)); + } + + + [Test] + public void FooterTemplateChanged() + { + var lv = new ListView { + Footer = "footer", + FooterTemplate = new DataTemplate(typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding (".") } + } + } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "FooterElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "FooterElement") + changed = true; + }; + + lv.FooterTemplate = new DataTemplate (typeof (Entry)) { + Bindings = { + { Entry.TextProperty, new Binding (".") } + } + }; + + Assert.That (changing, Is.True); + Assert.That (changed, Is.True); + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.Not.Null); + Assert.That (controller.FooterElement, Is.InstanceOf<Entry>()); + Assert.That (((Entry) controller.FooterElement).Text, Is.EqualTo (lv.Footer)); + } + + [Test] + public void FooterTemplateChangedNoObject() + { + var lv = new ListView { + FooterTemplate = new DataTemplate(typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding (".") } + } + } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "FooterElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "FooterElement") + changed = true; + }; + + lv.FooterTemplate = new DataTemplate (typeof (Entry)) { + Bindings = { + { Entry.TextProperty, new Binding (".") } + } + }; + + Assert.That (changing, Is.False); + Assert.That (changed, Is.False); + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.Null); + } + + [Test] + public void FooterNoTemplate() + { + var lv = new ListView { + Footer = "foo" + }; + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.Not.Null); + Assert.That (controller.FooterElement, Is.InstanceOf<Label>()); + Assert.That (((Label) controller.FooterElement).Text, Is.EqualTo (lv.Footer)); + } + + [Test] + public void FooterChangedNoTemplate() + { + var lv = new ListView { + Footer = "foo" + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "FooterElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "FooterElement") + changed = true; + }; + + lv.Footer = "bar"; + + Assert.That (changing, Is.True); + Assert.That (changed, Is.True); + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.Not.Null); + Assert.That (controller.FooterElement, Is.InstanceOf<Label>()); + Assert.That (((Label) controller.FooterElement).Text, Is.EqualTo (lv.Footer)); + } + + [Test] + public void FooterViewButTemplated() + { + var lv = new ListView { + Footer = new Entry { Text = "foo" }, + FooterTemplate = new DataTemplate (typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding ("Text") } + } + } + }; + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.Not.Null); + Assert.That (controller.FooterElement, Is.InstanceOf<Label>()); + Assert.That (((Label) controller.FooterElement).Text, Is.EqualTo (((Entry)lv.Footer).Text)); + } + + [Test] + public void FooterTemplatedChangedToView() + { + var lv = new ListView { + Footer = new Entry { Text = "foo" }, + FooterTemplate = new DataTemplate (typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding ("Text") } + } + } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "FooterElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "FooterElement") + changed = true; + }; + + lv.FooterTemplate = null; + + Assert.That (changing, Is.True); + Assert.That (changed, Is.True); + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.Not.Null); + Assert.That (controller.FooterElement, Is.InstanceOf<Entry>()); + Assert.That (((Entry) controller.FooterElement).Text, Is.EqualTo (((Entry)lv.Footer).Text)); + } + + [Test] + public void FooterTemplatedSetToNull() + { + var lv = new ListView { + Footer = "footer", + FooterTemplate = new DataTemplate(typeof(Label)) { + Bindings = { + { Label.TextProperty, new Binding (".") } + } + } + }; + + bool changed = false, changing = false; + lv.PropertyChanging += (sender, args) => { + if (args.PropertyName == "FooterElement") + changing = true; + }; + lv.PropertyChanged += (sender, args) => { + if (args.PropertyName == "FooterElement") + changed = true; + }; + + lv.Footer = null; + + Assert.That (changing, Is.True); + Assert.That (changed, Is.True); + + IListViewController controller = lv; + Assert.That (controller.FooterElement, Is.Null); + } + + [Test] + public void BeginRefresh() + { + var lv = new ListView(); + + bool refreshing = false; + lv.Refreshing += (sender, args) => { + refreshing = true; + }; + + lv.BeginRefresh(); + + Assert.That (refreshing, Is.True); + Assert.That (lv.IsRefreshing, Is.True); + } + + [Test] + public void SendRefreshing() + { + var lv = new ListView(); + + bool refreshing = false; + lv.Refreshing += (sender, args) => { + refreshing = true; + }; + + IListViewController controller = lv; + controller.SendRefreshing(); + + Assert.That (refreshing, Is.True); + Assert.That (lv.IsRefreshing, Is.True); + } + + [Test] + public void RefreshCommand() + { + var lv = new ListView(); + + bool commandExecuted = false; + + Command refresh = new Command (() => commandExecuted = true); + + lv.RefreshCommand = refresh; + + IListViewController controller = lv; + controller.SendRefreshing(); + + Assert.That (commandExecuted, Is.True); + } + + [TestCase (true)] + [TestCase (false)] + public void RefreshCommandCanExecute (bool initial) + { + var lv = new ListView { IsPullToRefreshEnabled = initial }; + + bool commandExecuted = false; + + Command refresh = new Command (() => commandExecuted = true, + () => !initial); + + lv.RefreshCommand = refresh; + + Assert.That ((lv as IListViewController).RefreshAllowed, Is.EqualTo (!initial)); + } + + [TestCase (true)] + [TestCase (false)] + public void RefreshCommandCanExecuteChanges (bool initial) + { + var lv = new ListView { IsPullToRefreshEnabled = initial }; + + bool commandExecuted = false; + + Command refresh = new Command (() => commandExecuted = true, + () => initial); + + lv.RefreshCommand = refresh; + + Assert.That ((lv as IListViewController).RefreshAllowed, Is.EqualTo (initial)); + + initial = !initial; + refresh.ChangeCanExecute(); + + Assert.That ((lv as IListViewController).RefreshAllowed, Is.EqualTo (initial)); + } + + [Test] + public void BeginRefreshDoesNothingWhenCannotExecute() + { + var lv = new ListView(); + + bool commandExecuted = false, eventFired = false; + + lv.Refreshing += (sender, args) => eventFired = true; + + Command refresh = new Command (() => commandExecuted = true, + () => false); + + lv.RefreshCommand = refresh; + lv.BeginRefresh(); + + Assert.That (lv.IsRefreshing, Is.False); + Assert.That (eventFired, Is.False); + Assert.That (commandExecuted, Is.False); + } + + [Test] + public void SendRefreshingDoesNothingWhenCannotExecute() + { + var lv = new ListView(); + + bool commandExecuted = false, eventFired = false; + + lv.Refreshing += (sender, args) => eventFired = true; + + Command refresh = new Command (() => commandExecuted = true, + () => false); + + lv.RefreshCommand = refresh; + + ((IListViewController)lv).SendRefreshing(); + + Assert.That (lv.IsRefreshing, Is.False); + Assert.That (eventFired, Is.False); + Assert.That (commandExecuted, Is.False); + } + + [Test] + public void SettingIsRefreshingDoesntFireEvent() + { + var lv = new ListView(); + + bool refreshing = false; + lv.Refreshing += (sender, args) => { + refreshing = true; + }; + + lv.IsRefreshing = true; + + Assert.That (refreshing, Is.False); + } + + [Test] + public void EndRefresh() + { + var lv = new ListView { IsRefreshing = true }; + + Assert.That (lv.IsRefreshing, Is.True); + + lv.EndRefresh(); + + Assert.That (lv.IsRefreshing, Is.False); + } + + [Test] + public void CanRefreshAfterCantExecuteCommand() + { + var lv = new ListView(); + + bool commandExecuted = false, eventFired = false; + + lv.Refreshing += (sender, args) => eventFired = true; + + Command refresh = new Command (() => commandExecuted = true, + () => false); + + lv.RefreshCommand = refresh; + lv.RefreshCommand = null; + + ((IListViewController)lv).SendRefreshing(); + + Assert.That (lv.IsRefreshing, Is.True); + Assert.That (eventFired, Is.True); + Assert.That (commandExecuted, Is.False); + } + + [Test] + public void StopsListeningToCommandAfterCleared() + { + var lv = new ListView(); + + bool commandExecuted = false, canExecuteRequested = false; + + Command refresh = new Command (() => commandExecuted = true, + () => canExecuteRequested = true); + + lv.RefreshCommand = refresh; + canExecuteRequested = false; + + lv.RefreshCommand = null; + + Assert.That (() => refresh.ChangeCanExecute(), Throws.Nothing); + Assert.That (canExecuteRequested, Is.False); + + lv.BeginRefresh(); + + Assert.That (commandExecuted, Is.False); + } + + [Test] + [Description ("We should be able to set selected item when using ReadOnlyList")] + public void SetItemSelectedOnReadOnlyList() + { + var source = new ReadOnlySource (); + var listView = new ListView { + ItemsSource = source + }; + + bool raised = false; + listView.ItemSelected += (sender, arg) => raised = true; + + listView.SelectedItem = source [0]; + Assert.That (raised, Is.True, "ItemSelected was raised on ReadOnlySource"); + } + + internal class ReadOnlySource : IReadOnlyList<ListItem> + { + List<ListItem> items; + public ReadOnlySource () + { + items = new List<ListItem> (); + + for (int i = 0; i < 100; i++) { + items.Add (new ListItem { Name="person " + i } ); + } + + } + #region IEnumerable implementation + public IEnumerator<ListItem> GetEnumerator () + { + return items.GetEnumerator (); + } + #endregion + #region IEnumerable implementation + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () + { + return items.GetEnumerator (); + } + #endregion + #region IReadOnlyList implementation + public ListItem this [int index] { + get { + return items [index]; + } + } + #endregion + #region IReadOnlyCollection implementation + public int Count { + get { + return items.Count; + } + } + #endregion + + } + + [Test] + public void ChildElementsParentIsNulledWhenReset() + { + var list = new ListView(); + list.ItemsSource = new[] { "Hi", "Bye" }; + + var cell = list.TemplatedItems[0]; + Assume.That (cell.Parent, Is.SameAs (list)); + + list.ItemsSource = null; + Assert.That (cell.Parent, Is.Null); + } + + [Test] + public void ChildElementsParentIsNulledWhenRemoved() + { + var collection = new ObservableCollection<string> { + "Hi", "Bye" + }; + + var list = new ListView(); + list.ItemsSource = collection; + + var cell = list.TemplatedItems[0]; + Assume.That (cell.Parent, Is.SameAs (list)); + + collection.Remove (collection[0]); + Assert.That (cell.Parent, Is.Null); + } + + [Test] + public void ChildElementsParentIsNulledWhenCleared() + { + var collection = new ObservableCollection<string> { + "Hi", "Bye" + }; + + var list = new ListView(); + list.ItemsSource = collection; + + var cell = list.TemplatedItems[0]; + Assume.That (cell.Parent, Is.SameAs (list)); + + collection.Clear(); + Assert.That (cell.Parent, Is.Null); + } + + [TestCase (TargetPlatform.Android, ListViewCachingStrategy.RecycleElement)] + [TestCase (TargetPlatform.iOS, ListViewCachingStrategy.RecycleElement)] + [TestCase (TargetPlatform.Windows, ListViewCachingStrategy.RetainElement)] + [TestCase (TargetPlatform.Other, ListViewCachingStrategy.RetainElement)] + [TestCase (TargetPlatform.WinPhone, ListViewCachingStrategy.RetainElement)] + public void EnforcesCachingStrategy (TargetPlatform platform, ListViewCachingStrategy expected) + { + var oldOS = Device.OS; + // we need to do this because otherwise we cant set the caching strategy + Device.OS = platform; + var listView = new ListView (ListViewCachingStrategy.RecycleElement); + + Assert.AreEqual (expected, listView.CachingStrategy); + Device.OS = oldOS; + } + + [Test] + public void DefaultCacheStrategy () + { + var listView = new ListView (); + + Assert.AreEqual (ListViewCachingStrategy.RetainElement, listView.CachingStrategy); + } + + [Test] + public void DoesNotRetainInRecycleMode () + { + var items = new ObservableCollection<string> { + "Foo", + "Bar" + }; + + var oldOS = Device.OS; + // we need to do this because otherwise we cant set the caching strategy + Device.OS = TargetPlatform.Android; + + var bindable = new ListView (ListViewCachingStrategy.RecycleElement); + bindable.ItemTemplate = new DataTemplate (typeof (TextCell)) { + Bindings = { + { TextCell.TextProperty, new Binding (".") } + } + }; + + bindable.ItemsSource = items; + var item1 = bindable.TemplatedItems[0]; + var item2 = bindable.TemplatedItems[0]; + + Assert.False(ReferenceEquals (item1, item2)); + + Device.OS = oldOS; + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/MapSpanTests.cs b/Xamarin.Forms.Core.UnitTests/MapSpanTests.cs new file mode 100644 index 00000000..68645116 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MapSpanTests.cs @@ -0,0 +1,44 @@ +using NUnit.Framework; +using Xamarin.Forms.Maps; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class MapSpanTests : BaseTestFixture + { + [Test] + public void Constructor () + { + var span = new MapSpan (new Position(0, 0), 1, 1); + + Assert.AreEqual (new Position (0, 0), span.Center); + Assert.AreEqual (1, span.LatitudeDegrees); + Assert.AreEqual (1, span.LongitudeDegrees); + Assert.IsTrue (span.Radius.Kilometers > 54 && span.Radius.Kilometers < 56); + } + + [Test] + public void Equals () + { + Assert.True (new MapSpan (new Position (1, 2), 3, 4) == new MapSpan (new Position (1, 2), 3, 4)); + Assert.True (new MapSpan (new Position (1, 2), 3, 4) != new MapSpan (new Position (2, 3), 4, 5)); + Assert.True (new MapSpan (new Position (1, 2), 3, 4).Equals (new MapSpan (new Position (1, 2), 3, 4))); + Assert.False (new MapSpan (new Position (1, 2), 3, 4).Equals ("MapSpan")); + Assert.False (new MapSpan (new Position (1, 2), 3, 4).Equals (null)); + } + + [Test] + public void HashCode () + { + Assert.AreEqual (new MapSpan (new Position (1, 2), 3, 4).GetHashCode (), new MapSpan (new Position (1, 2), 3, 4).GetHashCode ()); + } + + [Test] + public void RangeClamping () + { + var span = new MapSpan (new Position (0, 0), -1, -2); + Assert.IsTrue (span.LatitudeDegrees > 0); + Assert.IsTrue (span.LongitudeDegrees > 0); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/MapTests.cs b/Xamarin.Forms.Core.UnitTests/MapTests.cs new file mode 100644 index 00000000..7c798b33 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MapTests.cs @@ -0,0 +1,147 @@ +using System; +using NUnit.Framework; +using Xamarin.Forms.Maps; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class MapTests : BaseTestFixture + { + [Test] + public void AddPin () + { + var map = new Map (); + + var home = new Pin { + Label = "Home", + Position = new Position (88, 2), + Type = PinType.Place, + Address = "123 My Place" + }; + + map.Pins.Add (home); + + Assert.AreEqual (map.Pins.Count, 1); + Assert.AreEqual (map.Pins[0].Label, "Home"); + var mall = new Pin { + Label = "Mall", + Position = new Position (-12, -67), + Type = PinType.Place, + Address = "123 Fun" + }; + + map.Pins.Add (mall); + + Assert.AreEqual (map.Pins.Count, 2); + Assert.AreEqual (map.Pins[1].Position.Latitude, -12); + } + + [Test] + public void AddPinWithoutName () + { + var map = new Map (); + var noNamePin = new Pin { + Position = new Position (50, 50), + Type = PinType.Generic, + Address = "123 Fun" + }; + + var exception = Assert.Throws<ArgumentException> (() => map.Pins.Add (noNamePin)); + Assert.That (exception.Message, Is.EqualTo ("Pin must have a Label to be added to a map")); + } + + [Test] + public void AddPinWithoutAddress () + { + var map = new Map (); + var noAddressPin = new Pin { + Position = new Position (37.9, -20.87), + Label = "I have no address", + Type = PinType.SearchResult + }; + + map.Pins.Add (noAddressPin); + Assert.AreEqual (map.Pins.Count, 1); + Assert.AreEqual (map.Pins[0].Label, "I have no address"); + Assert.AreEqual (map.Pins[0].Address, null); + } + + [Test] + public void Constructor () + { + var center = new Position (15.5, 176); + var span = new MapSpan (center, 1, 2); + var map = new Map (span); + + Assert.AreEqual (1, map.LastMoveToRegion.LatitudeDegrees); + Assert.AreEqual (2, map.LastMoveToRegion.LongitudeDegrees); + var position = new Position (15.5, 176); + Assert.AreEqual (position, map.LastMoveToRegion.Center); + } + + [Test] + public void RemovePin () + { + var map = new Map (); + var genericPlace = new Pin { + Label = "Generic", + Position = new Position (-12, -67), + Type = PinType.Generic, + Address = "XXX" + }; + + var mall = new Pin { + Label = "Mall", + Position = new Position (-29, -87), + Type = PinType.Place, + Address = "123 Fun" + }; + + map.Pins.Add (genericPlace); + Assert.AreEqual (map.Pins.Count, 1); + + map.Pins.Add (mall); + Assert.AreEqual (map.Pins.Count, 2); + + map.Pins.Remove (genericPlace); + Assert.AreEqual (map.Pins.Count, 1); + + Assert.True (map.Pins.Contains (mall)); + Assert.False (map.Pins.Contains (genericPlace)); + } + + [Test] + public void VisibleRegion () + { + var map = new Map (new MapSpan (new Position (), 0, 0)); + map.MoveToRegion (new MapSpan (new Position (1, 2), 3, 4)); + Assert.AreEqual (null, map.VisibleRegion); + + bool signaled = false; + MessagingCenter.Subscribe<Map, MapSpan> (this, "MapMoveToRegion", (s, a) => { + signaled = true; + map.VisibleRegion = a; + }, map); + + map.MoveToRegion (new MapSpan (new Position (1, 2), 3, 4)); + Assert.AreEqual (new MapSpan (new Position (1, 2), 3, 4), map.LastMoveToRegion); + Assert.True (signaled); + } + + [Test] + public void VisibleRegionDoubleSet () + { + var map = new Map (); + + bool signaled = false; + map.PropertyChanged += (sender, args) => { + if (args.PropertyName == "VisibleRegion") + signaled = true; + }; + + map.VisibleRegion = map.VisibleRegion; + + Assert.False (signaled); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/MarginTests.cs b/Xamarin.Forms.Core.UnitTests/MarginTests.cs new file mode 100644 index 00000000..fd5debfa --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MarginTests.cs @@ -0,0 +1,134 @@ +using NUnit.Framework; +using Xamarin.Forms; + +namespace Xamarin.Forms.Core.UnitTests +{ + public class MarginTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void GetSizeRequestIncludesMargins () + { + var platform = new UnitPlatform ((b, d, e) => new SizeRequest(new Size(100,50))); + + var parent = new ContentView { + Platform = platform, + IsPlatformEnabled = true, + }; + var child = new Button { + Text = "Test", + Platform = platform, + IsPlatformEnabled = true, + }; + + + child.Margin = new Thickness (10, 20, 30, 40); + parent.Content = child; + + var result = parent.Measure (double.PositiveInfinity, double.PositiveInfinity, MeasureFlags.IncludeMargins); + Assert.AreEqual (new Size (140, 110), result.Request); + } + + [Test] + public void MarginsAffectPositionInContentView () + { + var platform = new UnitPlatform ((b, d, e) => new SizeRequest(new Size(100,50))); + + var parent = new ContentView { + Platform = platform, + IsPlatformEnabled = true, + }; + var child = new Button { + Text = "Test", + Platform = platform, + IsPlatformEnabled = true, + }; + + + child.Margin = new Thickness (10, 20, 30, 40); + parent.Content = child; + + parent.Layout (new Rectangle (0, 0, 140, 110)); + Assert.AreEqual (new Rectangle (10, 20, 100, 50), child.Bounds); + } + + [Test] + public void ChangingMarginCausesRelayout () + { + var platform = new UnitPlatform ((b, d, e) => new SizeRequest(new Size(100,50))); + + var parent = new ContentView { + Platform = platform, + IsPlatformEnabled = true, + }; + var child = new Button { + Text = "Test", + VerticalOptions = LayoutOptions.Start, + HorizontalOptions = LayoutOptions.Start, + Platform = platform, + IsPlatformEnabled = true, + }; + + + child.Margin = new Thickness (10, 20, 30, 40); + parent.Content = child; + + parent.Layout (new Rectangle (0, 0, 1000, 1000)); + Assert.AreEqual (new Rectangle (10, 20, 100, 50), child.Bounds); + } + + [Test] + public void IntegrationTest () + { + var platform = new UnitPlatform ((b, d, e) => new SizeRequest(new Size(100,50))); + + var parent = new StackLayout { + Platform = platform, + Spacing = 0, + IsPlatformEnabled = true, + }; + + var child1 = new Button { + Text = "Test", + VerticalOptions = LayoutOptions.Start, + HorizontalOptions = LayoutOptions.Start, + Platform = platform, + IsPlatformEnabled = true, + }; + + var child2 = new Button { + Text = "Test", + Platform = platform, + IsPlatformEnabled = true, + }; + + child2.Margin = new Thickness (5, 10, 15, 20); + + parent.Children.Add (child1); + parent.Children.Add (child2); + + parent.Layout (new Rectangle (0, 0, 1000, 1000)); + + Assert.AreEqual (new Rectangle (0, 0, 100, 50), child1.Bounds); + Assert.AreEqual (new Rectangle (5, 60, 980, 50), child2.Bounds); + + child1.Margin = new Thickness (10, 20, 30, 40); + + Assert.AreEqual (new Rectangle (10, 20, 100, 50), child1.Bounds); + Assert.AreEqual (new Rectangle (5, 120, 980, 50), child2.Bounds); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/MasterDetailFormUnitTests.cs b/Xamarin.Forms.Core.UnitTests/MasterDetailFormUnitTests.cs new file mode 100644 index 00000000..3c1e27ad --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MasterDetailFormUnitTests.cs @@ -0,0 +1,415 @@ +using System; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + internal class TestDeviceInfo : DeviceInfo + { + public TestDeviceInfo () + { + CurrentOrientation = DeviceOrientation.Portrait; + } + public override Size PixelScreenSize + { + get { return new Size (100, 200); } + } + + public override Size ScaledScreenSize + { + get { return new Size (50, 100); } + } + + public override double ScalingFactor + { + get { return 2; } + } + } + + [TestFixture] + public class MasterDetailPageUnitTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + var mockDeviceInfo = new TestDeviceInfo (); + Device.Info = mockDeviceInfo; + } + + [Test] + public void TestConstructor () + { + + MasterDetailPage page = new MasterDetailPage (); + + Assert.Null (page.Master); + Assert.Null (page.Detail); + } + + [Test] + public void TestMasterSetter () + { + MasterDetailPage page = new MasterDetailPage (); + var child = new ContentPage {Content = new Label (), Title = "Foo"}; + page.Master = child; + + Assert.AreEqual (child, page.Master); + } + + [Test] + public void TestMasterSetNull () + { + MasterDetailPage page = new MasterDetailPage (); + var child = new ContentPage {Content = new Label (), Title = "Foo"}; + page.Master = child; + + Assert.Throws<ArgumentNullException> (() => { page.Master = null; }); + } + + [Test] + public void TestMasterChanged () + { + MasterDetailPage page = new MasterDetailPage (); + var child = new ContentPage {Content = new Label (), Title = "Foo"}; + + bool changed = false; + page.PropertyChanged += (sender, e) => { + if (e.PropertyName == "Master") + changed = true; + }; + + page.Master = child; + + Assert.True (changed); + } + + [Test] + public void TestDetailSetter () + { + MasterDetailPage page = new MasterDetailPage (); + var child = new ContentPage {Content = new Label ()}; + page.Detail = child; + + Assert.AreEqual (child, page.Detail); + } + + [Test] + public void TestDetailSetNull () + { + MasterDetailPage page = new MasterDetailPage (); + var child = new ContentPage {Content = new Label ()}; + page.Detail = child; + + Assert.Throws<ArgumentNullException> ( () => { page.Detail = null; }); + } + + [Test] + public void TestDetailChanged () + { + MasterDetailPage page = new MasterDetailPage (); + var child = new ContentPage {Content = new Label ()}; + + bool changed = false; + page.PropertyChanged += (sender, e) => { + if (e.PropertyName == "Detail") + changed = true; + }; + + page.Detail = child; + + Assert.True (changed); + } + + [Test] + public void ThrowsWhenMasterSetWithoutValidTitle ([Values (null, "")] string title) + { + var page = new MasterDetailPage (); + Assert.Throws<InvalidOperationException> (() => page.Master = new ContentPage {Title = title}); + } + + [Test] + public void TestThrowsWhenPackedWithoutSetting () + { + MasterDetailPage page = new MasterDetailPage (); + Assert.Throws<InvalidOperationException> (() => new TabbedPage {Children = {page}}); + } + + [Test] + public void TestDoesNotThrowWhenPackedWithSetting () + { + MasterDetailPage page = new MasterDetailPage { + Master = new ContentPage {Content = new View (), Title = "Foo"}, + Detail = new ContentPage {Content = new View ()} + }; + Assert.DoesNotThrow (() => new TabbedPage {Children = {page}}); + } + + [Test] + public void TestMasterVisible () + { + var page = new MasterDetailPage (); + + Assert.AreEqual (false, page.IsPresented); + + bool signaled = false; + page.PropertyChanged += (sender, args) => { + if (args.PropertyName == MasterDetailPage.IsPresentedProperty.PropertyName) + signaled = true; + }; + + page.IsPresented = true; + + Assert.AreEqual (true, page.IsPresented); + Assert.True (signaled); + } + + [Test] + public void TestMasterVisibleDoubleSet () + { + var page = new MasterDetailPage (); + + bool signaled = false; + page.PropertyChanged += (sender, args) => { + if (args.PropertyName == MasterDetailPage.IsPresentedProperty.PropertyName) + signaled = true; + }; + + page.IsPresented = page.IsPresented; + + Assert.False (signaled); + } + + [Test] + public void TestSetMasterBounds () + { + var page = new MasterDetailPage { + Master = new ContentPage {Content = new View (), Title = "Foo"}, + Detail = new ContentPage {Content = new View ()} + }; + + page.MasterBounds = new Rectangle (0, 0, 100, 100); + Assert.AreEqual (new Rectangle (0, 0, 100, 100), page.Master.Bounds); + Assert.AreEqual (new Rectangle (0, 0, 100, 100), page.MasterBounds); + } + + [Test] + public void TestSetDetailBounds () + { + var page = new MasterDetailPage { + Master = new ContentPage {Content = new View (), Title = "Foo"}, + Detail = new ContentPage {Content = new View ()} + }; + + page.DetailBounds = new Rectangle (0, 0, 100, 100); + Assert.AreEqual (new Rectangle (0, 0, 100, 100), page.Detail.Bounds); + Assert.AreEqual (new Rectangle (0, 0, 100, 100), page.DetailBounds); + } + + [Test] + public void TestLayoutChildren () + { + var page = new MasterDetailPage { + Master = new ContentPage { Content = new View (), IsPlatformEnabled = true, Title = "Foo" }, + Detail = new ContentPage { Content = new View (), IsPlatformEnabled = true }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + page.MasterBounds = new Rectangle (0, 0, 100, 200); + page.DetailBounds = new Rectangle (0, 0, 100, 100); + + page.Master.Layout (new Rectangle(0, 0, 1, 1)); + page.Detail.Layout (new Rectangle(0, 0, 1, 1)); + + page.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (0, 0, 100, 200), page.Master.Bounds); + Assert.AreEqual (new Rectangle (0, 0, 100, 100), page.Detail.Bounds); + } + + [Test] + public void ThorwsInLayoutChildrenWithNullDetail () + { + var page = new MasterDetailPage { + Master = new ContentPage { Content = new View (), IsPlatformEnabled = true, Title = "Foo" }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + Assert.Throws<InvalidOperationException> (() => page.Layout (new Rectangle (0, 0, 200, 200))); + } + + [Test] + public void ThorwsInLayoutChildrenWithNullMaster () + { + var page = new MasterDetailPage { + Detail = new ContentPage { Content = new View (), IsPlatformEnabled = true }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + Assert.Throws<InvalidOperationException> (() => page.Layout (new Rectangle(0, 0, 200, 200))); + } + + [Test] + public void ThorwsInSetDetailBoundsWithNullDetail () + { + var page = new MasterDetailPage { + Master = new ContentPage {Content = new View (), Title = "Foo"}, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + Assert.Throws<InvalidOperationException> (() => page.DetailBounds = new Rectangle(0, 0, 200, 200)); + } + + [Test] + public void ThrowsInSetMasterBoundsWithNullMaster () + { + var page = new MasterDetailPage { + Detail = new ContentPage {Content = new View ()}, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + Assert.Throws<InvalidOperationException> (() => page.MasterBounds = new Rectangle(0, 0, 200, 200)); + } + + [Test] + public void ThrowsInSetIsPresentOnSplitModeOnTablet () + { + Device.Idiom = TargetIdiom.Tablet; + var page = new MasterDetailPage { + Master = new ContentPage { Content = new View (), IsPlatformEnabled = true, Title = "Foo" }, + Detail = new ContentPage { Content = new View (), IsPlatformEnabled = true }, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + MasterBehavior = MasterBehavior.Split + }; + + Assert.Throws<InvalidOperationException> (() => page.IsPresented = false); + } + + [Test] + public void ThorwsInSetIsPresentOnSplitPortraitModeOnTablet () + { + Device.Idiom = TargetIdiom.Tablet; + Device.Info.CurrentOrientation = DeviceOrientation.Portrait; + + var page = new MasterDetailPage { + Master = new ContentPage { Content = new View (), IsPlatformEnabled = true, Title = "Foo" }, + Detail = new ContentPage { Content = new View (), IsPlatformEnabled = true }, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + MasterBehavior = MasterBehavior.SplitOnPortrait + }; + + Assert.Throws<InvalidOperationException> (() => page.IsPresented = false); + } + + [Test] + public void TestSetIsPresentedOnPopoverMode () + { + Device.Info.CurrentOrientation = DeviceOrientation.Landscape; + + var page = new MasterDetailPage { + Master = new ContentPage { Content = new View (), IsPlatformEnabled = true, Title = "Foo" }, + Detail = new ContentPage { Content = new View (), IsPlatformEnabled = true }, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + MasterBehavior = MasterBehavior.Popover + }; + page.IsPresented = true; + + Assert.AreEqual (true, page.IsPresented); + } + + [Test] + public void SendsBackEventToPresentedMasterFirst () + { + var detail = new BackButtonPage () {Handle = true}; + var master = new BackButtonPage () {Title = "Master"}; + var mdp = new MasterDetailPage () { + Detail = detail, + Master = master, + IsPresented = true, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + mdp.BackButtonPressed += (sender, args) => { + args.Handled = mdp.IsPresented; + mdp.IsPresented = false; + }; + + var detailEmitted = false; + var masterEmitted = false; + + detail.BackPressed += (sender, args) => detailEmitted = true; + master.BackPressed += (sender, args) => masterEmitted = true; + + var result = mdp.SendBackButtonPressed (); + + Assert.True (masterEmitted); + Assert.False (detailEmitted); + Assert.True (result); + } + + [Test] + public void EmitsCorrectlyWhenPresentedOnBackPressed () + { + var detail = new BackButtonPage (); + var master = new BackButtonPage { Title = "Master" }; + var mdp = new MasterDetailPage { + Detail = detail, + Master = master, + IsPresented = true, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + mdp.BackButtonPressed += (sender, args) => { + args.Handled = mdp.IsPresented; + mdp.IsPresented = false; + }; + + var detailEmitted = false; + var masterEmitted = false; + + detail.BackPressed += (sender, args) => detailEmitted = true; + master.BackPressed += (sender, args) => masterEmitted = true; + + var result = mdp.SendBackButtonPressed (); + + Assert.True (masterEmitted); + Assert.False (detailEmitted); + Assert.True (result); + } + + [Test] + public void ThrowsExceptionWhenAddingAlreadyParentedDetail () + { + var detail = new ContentPage {}; + + // give detail a parent + var nav = new NavigationPage (detail); + + var mdp = new MasterDetailPage (); + Assert.Throws<InvalidOperationException> (() => mdp.Detail = detail); + } + + [Test] + public void ThrowsExceptionWhenAddingAlreadyParentedMaster () + { + var master = new ContentPage { Title = "Foo" }; + + // give master a parent + var nav = new NavigationPage (master); + + var mdp = new MasterDetailPage (); + Assert.Throws<InvalidOperationException> (() => mdp.Master = master); + } + } + +} diff --git a/Xamarin.Forms.Core.UnitTests/MenuItemTests.cs b/Xamarin.Forms.Core.UnitTests/MenuItemTests.cs new file mode 100644 index 00000000..d6163058 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MenuItemTests.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + public class MenuItemTests + : MenuItemTests<MenuItem> + { + } + + [TestFixture] + public abstract class MenuItemTests<T> + : CommandSourceTests<T> + where T : MenuItem, new() + { + [Test] + public void Activated() + { + var item = new MenuItem(); + + bool activated = false; + item.Clicked += (sender, args) => activated = true; + + item.Activate(); + + Assert.That (activated, Is.True); + } + + [Test] + public void Command() + { + bool executed = false; + var param = new object(); + + var c = new Command (o => { + Assert.That (o, Is.SameAs (param)); + executed = true; + }); + + var item = new MenuItem { Command = c, CommandParameter = param }; + item.Activate(); + + Assert.That (executed, Is.True); + } + + protected override T CreateSource() + { + return new T(); + } + + protected override void Activate (T source) + { + source.Activate(); + } + + protected override BindableProperty IsEnabledProperty + { + get { return MenuItem.IsEnabledProperty; } + } + + protected override BindableProperty CommandProperty + { + get { return MenuItem.CommandProperty; } + } + + protected override BindableProperty CommandParameterProperty + { + get { return MenuItem.CommandParameterProperty; } + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/MessagingCenterTests.cs b/Xamarin.Forms.Core.UnitTests/MessagingCenterTests.cs new file mode 100644 index 00000000..46dfdcdb --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MessagingCenterTests.cs @@ -0,0 +1,191 @@ +using System; +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class MessagingCenterTests : BaseTestFixture + { + [Test] + public void SingleSubscriber () + { + string sentMessage = null; + MessagingCenter.Subscribe<MessagingCenterTests, string> (this, "SimpleTest", (sender, args) => sentMessage = args); + + MessagingCenter.Send (this, "SimpleTest", "My Message"); + + Assert.That (sentMessage, Is.EqualTo ("My Message")); + + MessagingCenter.Unsubscribe<MessagingCenterTests, string> (this, "SimpleTest"); + } + + [Test] + public void Filter () + { + string sentMessage = null; + MessagingCenter.Subscribe<MessagingCenterTests, string> (this, "SimpleTest", (sender, args) => sentMessage = args, this); + + MessagingCenter.Send (new MessagingCenterTests (), "SimpleTest", "My Message"); + + Assert.That (sentMessage, Is.Null); + + MessagingCenter.Send (this, "SimpleTest", "My Message"); + + Assert.That (sentMessage, Is.EqualTo ("My Message")); + + MessagingCenter.Unsubscribe<MessagingCenterTests, string> (this, "SimpleTest"); + } + + [Test] + public void MultiSubscriber () + { + var sub1 = new object (); + var sub2 = new object (); + string sentMessage1 = null; + string sentMessage2 = null; + MessagingCenter.Subscribe<MessagingCenterTests, string> (sub1, "SimpleTest", (sender, args) => sentMessage1 = args); + MessagingCenter.Subscribe<MessagingCenterTests, string> (sub2, "SimpleTest", (sender, args) => sentMessage2 = args); + + MessagingCenter.Send (this, "SimpleTest", "My Message"); + + Assert.That (sentMessage1, Is.EqualTo ("My Message")); + Assert.That (sentMessage2, Is.EqualTo ("My Message")); + + MessagingCenter.Unsubscribe<MessagingCenterTests, string> (sub1, "SimpleTest"); + MessagingCenter.Unsubscribe<MessagingCenterTests, string> (sub2, "SimpleTest"); + } + + [Test] + public void Unsubscribe () + { + string sentMessage = null; + MessagingCenter.Subscribe<MessagingCenterTests, string> (this, "SimpleTest", (sender, args) => sentMessage = args); + MessagingCenter.Unsubscribe<MessagingCenterTests, string> (this, "SimpleTest"); + + MessagingCenter.Send (this, "SimpleTest", "My Message"); + + Assert.That (sentMessage, Is.EqualTo (null)); + } + + [Test] + public void SendWithoutSubscribers () + { + Assert.DoesNotThrow (() => MessagingCenter.Send (this, "SimpleTest", "My Message")); + } + + [Test] + public void NoArgSingleSubscriber () + { + bool sentMessage = false; + MessagingCenter.Subscribe<MessagingCenterTests> (this, "SimpleTest", sender => sentMessage = true); + + MessagingCenter.Send (this, "SimpleTest"); + + Assert.That (sentMessage, Is.True); + + MessagingCenter.Unsubscribe<MessagingCenterTests> (this, "SimpleTest"); + } + + [Test] + public void NoArgFilter () + { + bool sentMessage = false; + MessagingCenter.Subscribe (this, "SimpleTest", (sender) => sentMessage = true, this); + + MessagingCenter.Send (new MessagingCenterTests (), "SimpleTest"); + + Assert.That (sentMessage, Is.False); + + MessagingCenter.Send (this, "SimpleTest"); + + Assert.That (sentMessage, Is.True); + + MessagingCenter.Unsubscribe<MessagingCenterTests> (this, "SimpleTest"); + } + + [Test] + public void NoArgMultiSubscriber () + { + var sub1 = new object (); + var sub2 = new object (); + bool sentMessage1 = false; + bool sentMessage2 = false; + MessagingCenter.Subscribe<MessagingCenterTests> (sub1, "SimpleTest", (sender) => sentMessage1 = true); + MessagingCenter.Subscribe<MessagingCenterTests> (sub2, "SimpleTest", (sender) => sentMessage2 = true); + + MessagingCenter.Send (this, "SimpleTest"); + + Assert.That (sentMessage1, Is.True); + Assert.That (sentMessage2, Is.True); + + MessagingCenter.Unsubscribe<MessagingCenterTests> (sub1, "SimpleTest"); + MessagingCenter.Unsubscribe<MessagingCenterTests> (sub2, "SimpleTest"); + } + + [Test] + public void NoArgUnsubscribe () + { + bool sentMessage = false; + MessagingCenter.Subscribe<MessagingCenterTests> (this, "SimpleTest", (sender) => sentMessage = true); + MessagingCenter.Unsubscribe<MessagingCenterTests> (this, "SimpleTest"); + + MessagingCenter.Send (this, "SimpleTest", "My Message"); + + Assert.That (sentMessage, Is.False); + } + + [Test] + public void NoArgSendWithoutSubscribers () + { + Assert.DoesNotThrow (() => MessagingCenter.Send (this, "SimpleTest")); + } + + [Test] + public void ThrowOnNullArgs () + { + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests, string> (null, "Foo", (sender, args) => { })); + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests, string> (this, null, (sender, args) => { })); + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests, string> (this, "Foo", null)); + + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests> (null, "Foo", (sender) => { })); + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests> (this, null, (sender) => { })); + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Subscribe<MessagingCenterTests> (this, "Foo", null)); + + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Send<MessagingCenterTests, string> (null, "Foo", "Bar")); + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Send<MessagingCenterTests, string> (this, null, "Bar")); + + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Send<MessagingCenterTests> (null, "Foo")); + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Send<MessagingCenterTests> (this, null)); + + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Unsubscribe<MessagingCenterTests> (null, "Foo")); + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Unsubscribe<MessagingCenterTests> (this, null)); + + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Unsubscribe<MessagingCenterTests, string> (null, "Foo")); + Assert.Throws<ArgumentNullException> (() => MessagingCenter.Unsubscribe<MessagingCenterTests, string> (this, null)); + } + + [Test] + public void UnsubscribeInCallback () + { + int messageCount = 0; + + var subscriber1 = new object (); + var subscriber2 = new object (); + + MessagingCenter.Subscribe<MessagingCenterTests> (subscriber1, "SimpleTest", (sender) => { + messageCount++; + MessagingCenter.Unsubscribe<MessagingCenterTests> (subscriber2, "SimpleTest"); + }); + + MessagingCenter.Subscribe<MessagingCenterTests> (subscriber2, "SimpleTest", (sender) => { + messageCount++; + MessagingCenter.Unsubscribe<MessagingCenterTests> (subscriber1, "SimpleTest"); + }); + + MessagingCenter.Send (this, "SimpleTest"); + + Assert.AreEqual (1, messageCount); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/MockPlatformServices.cs b/Xamarin.Forms.Core.UnitTests/MockPlatformServices.cs new file mode 100644 index 00000000..7d2317b3 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MockPlatformServices.cs @@ -0,0 +1,270 @@ +using System; +using System.Threading.Tasks; +using System.IO; +using System.Threading; +using System.Reflection; +using System.IO.IsolatedStorage; +using System.Collections.Generic; +using Xamarin.Forms; +using Xamarin.Forms.Core.UnitTests; +using System.Security.Cryptography; +using System.Text; +#if WINDOWS_PHONE +using Xamarin.Forms.Platform.WinPhone; +#endif + +[assembly:Dependency (typeof(MockDeserializer))] +[assembly:Dependency (typeof(MockResourcesProvider))] + +namespace Xamarin.Forms.Core.UnitTests +{ + internal class MockPlatformServices : IPlatformServices + { + Action<Action> invokeOnMainThread; + Action<Uri> openUriAction; + Func<Uri, CancellationToken, Task<Stream>> getStreamAsync; + public MockPlatformServices (Action<Action> invokeOnMainThread = null, Action<Uri> openUriAction = null, Func<Uri, CancellationToken, Task<Stream>> getStreamAsync = null) + { + this.invokeOnMainThread = invokeOnMainThread; + this.openUriAction = openUriAction; + this.getStreamAsync = getStreamAsync; + } + + static MD5CryptoServiceProvider checksum = new MD5CryptoServiceProvider (); + + public string GetMD5Hash (string input) + { + var bytes = checksum.ComputeHash (Encoding.UTF8.GetBytes (input)); + var ret = new char [32]; + for (int i = 0; i < 16; i++){ + ret [i*2] = (char)hex (bytes [i] >> 4); + ret [i*2+1] = (char)hex (bytes [i] & 0xf); + } + return new string (ret); + } + static int hex (int v) + { + if (v < 10) + return '0' + v; + return 'a' + v-10; + } + + public double GetNamedSize (NamedSize size, Type targetElement, bool useOldSizes) + { + switch (size) { + case NamedSize.Default: + return 10; + case NamedSize.Micro: + return 4; + case NamedSize.Small: + return 8; + case NamedSize.Medium: + return 12; + case NamedSize.Large: + return 16; + default: + throw new ArgumentOutOfRangeException ("size"); + } + } + + public void OpenUriAction (Uri uri) + { + if (openUriAction != null) + openUriAction (uri); + else + throw new NotImplementedException (); + } + + public bool IsInvokeRequired + { + get { return false; } + } + + public void BeginInvokeOnMainThread (Action action) + { + if (invokeOnMainThread == null) + action (); + else + invokeOnMainThread (action); + } + + public void StartTimer (TimeSpan interval, Func<bool> callback) + { + Timer timer = null; + TimerCallback onTimeout = o => BeginInvokeOnMainThread (() => { + if (callback ()) + return; + + timer.Dispose (); + }); + timer = new Timer (onTimeout, null, interval, interval); + } + + public Task<Stream> GetStreamAsync (Uri uri, CancellationToken cancellationToken) + { + if (getStreamAsync == null) + throw new NotImplementedException (); + return getStreamAsync (uri, cancellationToken); + } + + public Assembly[] GetAssemblies () + { + return AppDomain.CurrentDomain.GetAssemblies (); + } + + public ITimer CreateTimer (Action<object> callback) + { + return new MockTimer (new Timer (o => callback(o))); + } + + public ITimer CreateTimer (Action<object> callback, object state, int dueTime, int period) + { + return new MockTimer (new Timer (o => callback(o), state, dueTime, period)); + } + + public ITimer CreateTimer (Action<object> callback, object state, long dueTime, long period) + { + return new MockTimer (new Timer (o => callback(o), state, dueTime, period)); + } + + public ITimer CreateTimer (Action<object> callback, object state, TimeSpan dueTime, TimeSpan period) + { + return new MockTimer (new Timer (o => callback(o), state, dueTime, period)); + } + + public ITimer CreateTimer (Action<object> callback, object state, uint dueTime, uint period) + { + return new MockTimer (new Timer (o => callback(o), state, dueTime, period)); + } + + public class MockTimer : ITimer + { + readonly Timer timer; + public MockTimer (Timer timer) + { + this.timer = timer; + } + + public void Change (int dueTime, int period) + { + timer.Change (dueTime, period); + } + public void Change (long dueTime, long period) + { + timer.Change (dueTime, period); + } + public void Change (TimeSpan dueTime, TimeSpan period) + { + timer.Change (dueTime, period); + } + public void Change (uint dueTime, uint period) + { + timer.Change (dueTime, period); + } + } + + public IIsolatedStorageFile GetUserStoreForApplication () + { +#if WINDOWS_PHONE + return new MockIsolatedStorageFile (IsolatedStorageFile.GetUserStoreForApplication ()); +#else + return new MockIsolatedStorageFile (IsolatedStorageFile.GetUserStoreForAssembly ()); +#endif + } + + public class MockIsolatedStorageFile : IIsolatedStorageFile + { + readonly IsolatedStorageFile isolatedStorageFile; + public MockIsolatedStorageFile (IsolatedStorageFile isolatedStorageFile) + { + this.isolatedStorageFile = isolatedStorageFile; + } + + public Task<bool> GetDirectoryExistsAsync (string path) + { + return Task.FromResult (isolatedStorageFile.DirectoryExists (path)); + } + + public Task CreateDirectoryAsync (string path) + { + isolatedStorageFile.CreateDirectory (path); + return Task.FromResult (true); + } + + public Task<Stream> OpenFileAsync (string path, FileMode mode, FileAccess access) + { + Stream stream = isolatedStorageFile.OpenFile (path, (System.IO.FileMode)mode, (System.IO.FileAccess)access); + return Task.FromResult (stream); + } + + public Task<Stream> OpenFileAsync (string path, FileMode mode, FileAccess access, FileShare share) + { + Stream stream = isolatedStorageFile.OpenFile (path, (System.IO.FileMode)mode, (System.IO.FileAccess)access, (System.IO.FileShare)share); + return Task.FromResult (stream); + } + + public Task<bool> GetFileExistsAsync (string path) + { + return Task.FromResult (isolatedStorageFile.FileExists (path)); + } + + public Task<DateTimeOffset> GetLastWriteTimeAsync (string path) + { + return Task.FromResult (isolatedStorageFile.GetLastWriteTime (path)); + } + } + } + + internal class MockDeserializer : IDeserializer + { + public Task<IDictionary<string, object>> DeserializePropertiesAsync () + { + return Task.FromResult<IDictionary<string, object>> (new Dictionary<string,object> ()); + } + + public Task SerializePropertiesAsync (IDictionary<string, object> properties) + { + return Task.FromResult (false); + } + } + + internal class MockResourcesProvider : ISystemResourcesProvider + { + public IResourceDictionary GetSystemResources () + { + var dictionary = new ResourceDictionary (); + Style style; + style = new Style (typeof(Label)); + dictionary [Device.Styles.BodyStyleKey] = style; + + style = new Style (typeof(Label)); + style.Setters.Add (Label.FontSizeProperty, 50); + dictionary [Device.Styles.TitleStyleKey] = style; + + style = new Style (typeof(Label)); + style.Setters.Add (Label.FontSizeProperty, 40); + dictionary [Device.Styles.SubtitleStyleKey] = style; + + style = new Style (typeof(Label)); + style.Setters.Add (Label.FontSizeProperty, 30); + dictionary [Device.Styles.CaptionStyleKey] = style; + + style = new Style (typeof(Label)); + style.Setters.Add (Label.FontSizeProperty, 20); + dictionary [Device.Styles.ListItemTextStyleKey] = style; + + style = new Style (typeof(Label)); + style.Setters.Add (Label.FontSizeProperty, 10); + dictionary [Device.Styles.ListItemDetailTextStyleKey] = style; + + return dictionary; + } + } + + public class MockApplication : Application + { + public MockApplication () + { + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/MockViewModel.cs b/Xamarin.Forms.Core.UnitTests/MockViewModel.cs new file mode 100644 index 00000000..da2f6f96 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MockViewModel.cs @@ -0,0 +1,30 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Xamarin.Forms.Core.UnitTests +{ + internal class MockViewModel + : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + string text; + public virtual string Text { + get { return text; } + set { + if (text == value) + return; + + text = value; + OnPropertyChanged ("Text"); + } + } + + protected void OnPropertyChanged ([CallerMemberName] string propertyName = null) + { + PropertyChangedEventHandler handler = PropertyChanged; + if (handler != null) + handler (this, new PropertyChangedEventArgs (propertyName)); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/MotionTests.cs b/Xamarin.Forms.Core.UnitTests/MotionTests.cs new file mode 100644 index 00000000..acb764d7 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MotionTests.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + internal class BlockingTicker : Ticker + { + bool enabled; + + protected override void EnableTimer () + { + enabled = true; + + while (enabled) { + SendSignals (16); + } + } + + protected override void DisableTimer () + { + enabled = false; + } + } + + [TestFixture] + public class MotionTests : BaseTestFixture + { + [TestFixtureSetUp] + public void Init () + { + Device.PlatformServices = new MockPlatformServices (); + Ticker.Default = new BlockingTicker (); + } + + [TestFixtureTearDown] + public void End () + { + Device.PlatformServices = null; + Ticker.Default = null; + } + + [Test] + public void TestLinearTween () + { + var tweener = new Tweener (250); + + double value = 0; + int updates = 0; + tweener.ValueUpdated += (sender, args) => { + Assert.That (tweener.Value, Is.GreaterThanOrEqualTo (value)); + value = tweener.Value; + updates++; + }; + tweener.Start (); + + Assert.That (updates, Is.GreaterThanOrEqualTo (10)); + } + + [Test] + public void ThrowsWithNullCallback () + { + Assert.Throws<ArgumentNullException> (() => new View().Animate ("Test", (Action<double>) null)); + } + + [Test] + public void ThrowsWithNullTransform () + { + Assert.Throws<ArgumentNullException> (() => new View().Animate<float> ("Test", null, f => { })); + } + + [Test] + public void ThrowsWithNullSelf () + { + Assert.Throws<ArgumentNullException> (() => AnimationExtensions.Animate (null, "Foo", d => (float)d, f => { })); + } + + [Test] + public void Kinetic () + { + var view = new View (); + var resultList = new List<Tuple<double, double>> (); + view.AnimateKinetic ( + name: "Kinetics", + callback: (distance, velocity) => { + resultList.Add (new Tuple<double, double> (distance, velocity)); + return true; + }, + velocity: 100, + drag: 1); + + Assert.That (resultList, Is.Not.Empty); + int checkVelo = 100; + int dragStep = 16; + + foreach (var item in resultList) { + checkVelo -= dragStep; + Assert.AreEqual (checkVelo, item.Item2); + Assert.AreEqual (checkVelo * dragStep, item.Item1); + } + } + + [Test] + public void KineticFinished () + { + var view = new View (); + bool finished = false; + view.AnimateKinetic ( + name: "Kinetics", + callback: (distance, velocity) => true, + velocity: 100, + drag: 1, + finished: () => finished = true); + + Assert.True (finished); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/MultiPageTests.cs b/Xamarin.Forms.Core.UnitTests/MultiPageTests.cs new file mode 100644 index 00000000..3d9faad5 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MultiPageTests.cs @@ -0,0 +1,805 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + public abstract class MultiPageTests<T> : BaseTestFixture + where T : Page + { + protected abstract MultiPage<T> CreateMultiPage(); + protected abstract T CreateContainedPage(); + protected abstract int GetIndex (T page); + + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void TestSetChildren () + { + var container = CreateMultiPage(); + var page = (Page) container; + + int childCount = 0; + page.ChildAdded += (sender, args) => childCount++; + + int pagesAdded = 0; + container.PagesChanged += (sender, args) => { + if (args.Action == NotifyCollectionChangedAction.Add) + pagesAdded++; + }; + + container.Children.Add (CreateContainedPage()); + container.Children.Add (CreateContainedPage()); + + Assert.AreEqual (2, childCount); + Assert.AreEqual (2, page.LogicalChildren.Count); + Assert.AreEqual (2, pagesAdded); + } + + [Test] + public void TestOverwriteChildren() + { + var page = CreateMultiPage(); + page.Children.Add (CreateContainedPage()); + page.Children.Add (CreateContainedPage()); + + int childCount = 0; + int removeCount = 0; + page.ChildAdded += (sender, args) => childCount++; + page.ChildRemoved += (sender, args) => removeCount++; + + foreach (var child in page.Children.ToArray()) + page.Children.Remove ((T)child); + + page.Children.Add (CreateContainedPage()); + page.Children.Add (CreateContainedPage()); + + Assert.AreEqual (2, removeCount); + Assert.AreEqual (2, childCount); + Assert.AreEqual (2, page.LogicalChildren.Count); + } + + [Test] + public void CurrentPageSetAfterAdd() + { + var page = CreateMultiPage(); + Assert.That (page.CurrentPage, Is.Null); + + var child = CreateContainedPage(); + + bool property = false; + page.PropertyChanged += (o, e) => { + if (e.PropertyName == "CurrentPage") + property = true; + }; + + page.Children.Add (child); + + Assert.That (page.CurrentPage, Is.SameAs (child)); + Assert.That (property, Is.True, "CurrentPage property change did not fire"); + } + + [Test] + public void CurrentPageChangedAfterRemove() + { + var page = CreateMultiPage(); + var child = CreateContainedPage(); + var child2 = CreateContainedPage(); + page.Children.Add (child); + page.Children.Add (child2); + + bool property = false; + page.PropertyChanged += (o, e) => { + if (e.PropertyName == "CurrentPage") + property = true; + }; + + page.Children.Remove (child); + + Assert.That (page.CurrentPage, Is.SameAs (child2), "MultiPage.CurrentPage is not set to a new page after current was removed"); + Assert.That (property, Is.True, "CurrentPage property change did not fire"); + } + + [Test] + public void CurrentPageNullAfterRemove() + { + var page = CreateMultiPage(); + var child = CreateContainedPage(); + page.Children.Add (child); + + bool property = false; + page.PropertyChanged += (o, e) => { + if (e.PropertyName == "CurrentPage") + property = true; + }; + + page.Children.Remove (child); + + Assert.That (page.CurrentPage, Is.Null, "MultiPage.CurrentPage is still set after that page was removed"); + Assert.That (property, Is.True, "CurrentPage property change did not fire"); + } + + [Test] + public void TemplatedPage() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + page.ItemsSource = new[] { "Foo", "Bar" }; + + Action<Page, string> assertPage = (p, s) => { + Assert.That (p, Is.InstanceOf<ContentPage>()); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (2)); + assertPage ((Page)pages[0], "Foo"); + assertPage ((Page)pages[1], "Bar"); + } + + [Test] + public void SelectedItemSetAfterAdd() + { + var page = CreateMultiPage(); + Assert.That (page.CurrentPage, Is.Null); + + var items = new ObservableCollection<string>(); + + page.ItemsSource = items; + + bool selected = false; + bool current = false; + page.PropertyChanged += (o, e) => { + if (e.PropertyName == "CurrentPage") + current = true; + else if (e.PropertyName == "SelectedItem") + selected = true; + }; + + items.Add ("foo"); + + Assert.That (page.SelectedItem, Is.SameAs (items.First())); + Assert.That (page.CurrentPage.BindingContext, Is.SameAs (page.SelectedItem)); + Assert.That (current, Is.True, "CurrentPage property change did not fire"); + Assert.That (selected, Is.True, "SelectedItem property change did not fire"); + } + + [Test] + public void SelectedItemNullAfterRemove() + { + var page = CreateMultiPage(); + Assert.That (page.CurrentPage, Is.Null); + + var items = new ObservableCollection<string> { "foo" }; + page.ItemsSource = items; + + bool selected = false; + bool current = false; + page.PropertyChanged += (o, e) => { + if (e.PropertyName == "CurrentPage") + current = true; + else if (e.PropertyName == "SelectedItem") + selected = true; + }; + + items.Remove ("foo"); + + Assert.That (page.SelectedItem, Is.Null, "MultiPage.SelectedItem is still set after that page was removed"); + Assert.That (page.CurrentPage, Is.Null, "MultiPage.CurrentPage is still set after that page was removed"); + Assert.That (current, Is.True, "CurrentPage property change did not fire"); + Assert.That (selected, Is.True, "SelectedItem property change did not fire"); + } + + [Test] + [Description ("When ItemsSource is set with items, the first item should automatically be selected")] + public void SelectedItemSetAfterItemsSourceSet() + { + var page = CreateMultiPage(); + + bool selected = false; + bool current = false; + page.PropertyChanged += (o, e) => { + if (e.PropertyName == "CurrentPage") + current = true; + else if (e.PropertyName == "SelectedItem") + selected = true; + }; + + page.ItemsSource = new[] { "foo" }; + + Assert.That (page.SelectedItem, Is.SameAs (((string[]) page.ItemsSource)[0])); + Assert.That (page.CurrentPage.BindingContext, Is.SameAs (page.SelectedItem)); + Assert.That (current, Is.True, "CurrentPage property change did not fire"); + Assert.That (selected, Is.True, "SelectedItem property change did not fire"); + } + + [Test] + public void SelectedItemNoLongerPresent() + { + var page = CreateMultiPage(); + + string[] items = new[] { "foo", "bar" }; + page.ItemsSource = items; + page.SelectedItem = items[1]; + + items = new[] { "fad", "baz" }; + page.ItemsSource = items; + + Assert.That (page.SelectedItem, Is.SameAs (items[0])); + } + + [Test] + public void SelectedItemAfterMove() + { + var page = CreateMultiPage(); + + var items = new ObservableCollection<string> { "foo", "bar" }; + page.ItemsSource = items; + + Assert.That (page.SelectedItem, Is.SameAs (items[0])); + Assert.That (page.CurrentPage, Is.Not.Null); + Assert.That (page.CurrentPage.BindingContext, Is.SameAs (items[0])); + + page.SelectedItem = items[1]; + Assert.That (page.CurrentPage.BindingContext, Is.SameAs (items[1])); + + items.Move (1, 0); + + Assert.That (page.SelectedItem, Is.SameAs (items[0])); + Assert.That (page.CurrentPage, Is.Not.Null); + Assert.That (page.CurrentPage.BindingContext, Is.SameAs (items[0])); + } + + [Test] + public void UntemplatedItemsSourcePage() + { + var page = CreateMultiPage(); + + page.ItemsSource = new[] { "Foo", "Bar" }; + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (2)); + Assert.That (((Page)pages[0]).Title, Is.EqualTo ("Foo")); + Assert.That (((Page)pages[1]).Title, Is.EqualTo ("Bar")); + } + + [Test] + public void TemplatePagesAdded() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + var items = new ObservableCollection<string> { "Foo", "Bar" }; + page.ItemsSource = items; + + Action<IList<Element>, int, string> assertPage = (ps, index, s) => { + Page p = (Page)ps[index]; + Assert.That (p, Is.InstanceOf<ContentPage>()); + Assert.That (GetIndex ((T) p), Is.EqualTo (index)); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + items.Add ("Baz"); + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (3), "Children should have 3 pages"); + assertPage (pages, 0, "Foo"); + assertPage (pages, 1, "Bar"); + assertPage (pages, 2, "Baz"); + } + + [Test] + public void TemplatePagesRangeAdded() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + var items = new ObservableList<string> { "Foo", "Bar" }; + page.ItemsSource = items; + + Action<IList<Element>, int, string> assertPage = (ps, index, s) => { + Page p = (Page)ps[index]; + Assert.That (p, Is.InstanceOf<ContentPage>()); + Assert.That (GetIndex ((T) p), Is.EqualTo (index)); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + int addedCount = 0; + page.PagesChanged += (sender, e) => { + if (e.Action != NotifyCollectionChangedAction.Add) + return; + + addedCount++; + Assert.That (e.NewItems.Count, Is.EqualTo (2)); + }; + + items.AddRange (new[] { "Baz", "Bam" }); + + Assert.That (addedCount, Is.EqualTo (1)); + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (4)); + assertPage (pages, 0, "Foo"); + assertPage (pages, 1, "Bar"); + assertPage (pages, 2, "Baz"); + assertPage (pages, 3, "Bam"); + } + + [Test] + public void TemplatePagesInserted() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + var items = new ObservableCollection<string> { "Foo", "Bar" }; + page.ItemsSource = items; + + Action<IList<Element>, int, string> assertPage = (ps, index, s) => { + Page p = (Page)ps[index]; + Assert.That (p, Is.InstanceOf<ContentPage>()); + Assert.That (GetIndex ((T) p), Is.EqualTo (index)); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + items.Insert (1, "Baz"); + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (3)); + assertPage (pages, 0, "Foo"); + assertPage (pages, 1, "Baz"); + assertPage (pages, 2, "Bar"); + } + + [Test] + public void TemplatePagesRangeInserted() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + var items = new ObservableList<string> { "Foo", "Bar" }; + page.ItemsSource = items; + + Action<IList<Element>, int, string> assertPage = (ps, index, s) => { + Page p = (Page)ps[index]; + Assert.That (p, Is.InstanceOf<ContentPage>()); + Assert.That (GetIndex ((T) p), Is.EqualTo (index)); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + items.InsertRange (1, new[] { "Baz", "Bam" }); + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (4)); + assertPage (pages, 0, "Foo"); + assertPage (pages, 1, "Baz"); + assertPage (pages, 2, "Bam"); + assertPage (pages, 3, "Bar"); + } + + [Test] + public void TemplatePagesRemoved() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + var items = new ObservableCollection<string> { "Foo", "Bar" }; + page.ItemsSource = items; + + Action<IList<Element>, int, string> assertPage = (ps, index, s) => { + Page p = (Page)ps[index]; + Assert.That (p, Is.InstanceOf<ContentPage>()); + Assert.That (GetIndex ((T) p), Is.EqualTo (index)); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + items.Remove ("Foo"); + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (1)); + assertPage (pages, 0, "Bar"); + } + + [Test] + public void TemplatePagesRangeRemoved() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + var items = new ObservableList<string> { "Foo", "Bar", "Baz", "Bam", "Who" }; + page.ItemsSource = items; + + Action<IList<Element>, int, string> assertPage = (ps, index, s) => { + Page p = (Page)ps[index]; + Assert.That (p, Is.InstanceOf<ContentPage>()); + Assert.That (GetIndex ((T) p), Is.EqualTo (index)); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + items.RemoveAt (1, 2); + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (3)); + assertPage (pages, 0, "Foo"); + assertPage (pages, 1, "Bam"); + assertPage (pages, 2, "Who"); + } + + [Test] + public void TemplatePagesReordered() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + var items = new ObservableCollection<string> { "Foo", "Bar" }; + page.ItemsSource = items; + + Action<IList<Element>, int, string> assertPage = (ps, index, s) => { + Page p = (Page)ps[index]; + Assert.That (p, Is.InstanceOf<ContentPage>()); + Assert.That (GetIndex ((T) p), Is.EqualTo (index)); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + items.Move (0, 1); + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (2)); + assertPage (pages, 0, "Bar"); + assertPage (pages, 1, "Foo"); + } + + [Test] + public void TemplatePagesRangeReorderedForward() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + var items = new ObservableList<string> { "Foo", "Bar", "Baz", "Bam", "Who", "Where" }; + page.ItemsSource = items; + + Action<IList<Element>, int, string> assertPage = (ps, index, s) => { + Page p = (Page)ps[index]; + Assert.That (p, Is.InstanceOf<ContentPage>()); + Assert.That (GetIndex ((T) p), Is.EqualTo (index)); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + items.Move (1, 4, 2); + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (6)); + assertPage (pages, 0, "Foo"); + assertPage (pages, 1, "Bam"); + assertPage (pages, 2, "Who"); + assertPage (pages, 3, "Bar"); + assertPage (pages, 4, "Baz"); + assertPage (pages, 5, "Where"); + } + + [Test] + public void TemplatePagesRangeReorderedBackward() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + var items = new ObservableList<string> { "Foo", "Bar", "Baz", "Bam", "Who", "Where", "When" }; + page.ItemsSource = items; + + Action<IList<Element>, int, string> assertPage = (ps, index, s) => { + Page p = (Page)ps[index]; + Assert.That (p, Is.InstanceOf<ContentPage>()); + Assert.That (GetIndex ((T) p), Is.EqualTo (index)); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + items.Move (4, 1, 2); + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (7)); + assertPage (pages, 0, "Foo"); + assertPage (pages, 1, "Who"); + assertPage (pages, 2, "Where"); + assertPage (pages, 3, "Bar"); + assertPage (pages, 4, "Baz"); + assertPage (pages, 5, "Bam"); + assertPage (pages, 6, "When"); + } + + [Test] + public void TemplatePagesReplaced() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + var items = new ObservableCollection<string> { "Foo", "Bar" }; + page.ItemsSource = items; + + Action<IList<Element>, int, string> assertPage = (ps, index, s) => { + Page p = (Page)ps[index]; + Assert.That (p, Is.InstanceOf<ContentPage>()); + Assert.That (GetIndex ((T) p), Is.EqualTo (index)); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + items[0] = "Baz"; + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (2)); + assertPage (pages, 0, "Baz"); + assertPage (pages, 1, "Bar"); + } + + [Test] + public void TemplatedPagesSourceReplaced() + { + var page = CreateMultiPage(); + + page.ItemTemplate = new DataTemplate (() => { + var p = new ContentPage(); + p.Content = new Label(); + p.Content.SetBinding (Label.TextProperty, new Binding (".")); + return p; + }); + + page.ItemsSource = new ObservableCollection<string> { "Foo", "Bar" }; + + Action<Page, string> assertPage = (p, s) => { + Assert.That (p, Is.InstanceOf<ContentPage>()); + + var cp = (ContentPage) p; + Assert.That (cp.Content, Is.InstanceOf<Label>()); + Assert.That (((Label)cp.Content).Text, Is.EqualTo (s)); + }; + + page.ItemsSource = new ObservableCollection<string> { "Baz", "Bar" }; + + var pages = page.Children.ToArray(); + Assert.That (pages.Length, Is.EqualTo (2)); + assertPage ((Page)pages[0], "Baz"); + assertPage ((Page)pages[1], "Bar"); + } + + [Test] + [Description ("If you have a templated set of items, setting CurrentPage (usually from renderers) should update SelectedItem properly")] + public void SettingCurrentPageWithTemplatesUpdatesSelectedItem() + { + var page = CreateMultiPage(); + + var items = new[] { "Foo", "Bar" }; + page.ItemsSource = items; + + // If these aren't correct, the rest of the test is invalid + Assert.That (page.CurrentPage, Is.SameAs (page.Children[0])); + Assert.That (page.SelectedItem, Is.SameAs (items[0])); + + page.CurrentPage = (T)page.Children[1]; + + Assert.That (page.SelectedItem, Is.SameAs (items[1])); + } + + [Test] + public void PagesChangedOnItemsSourceChange() + { + var page = CreateMultiPage(); + + page.ItemsSource = new[] { "Baz", "Bam" }; + + int fail = 0; + int reset = 0; + page.PagesChanged += (sender, args) => { + if (args.Action == NotifyCollectionChangedAction.Reset) + reset++; + else + fail++; + }; + + page.ItemsSource = new[] { "Foo", "Bar" }; + + Assert.That (reset, Is.EqualTo (1), "PagesChanged wasn't raised or was raised too many times for Reset"); + Assert.That (fail, Is.EqualTo (0), "PagesChanged was raised with an unexpected action"); + } + + [Test] + public void PagesChangedOnTemplateChange() + { + var page = CreateMultiPage(); + + page.ItemsSource = new[] { "Foo", "Bar" }; + + int fail = 0; + int reset = 0; + page.PagesChanged += (sender, args) => { + if (args.Action == NotifyCollectionChangedAction.Reset) + reset++; + else + fail++; + }; + + page.ItemTemplate = new DataTemplate (() => new ContentPage { + Content = new Label { Text = "Content" } + }); + + Assert.That (reset, Is.EqualTo (1), "PagesChanged wasn't raised or was raised too many times for Reset"); + Assert.That (fail, Is.EqualTo (0), "PagesChanged was raised with an unexpected action"); + } + + [Test] + public void SelectedItemSetBeforeTemplate() + { + var page = CreateMultiPage(); + + string[] items = new[] { "foo", "bar" }; + page.ItemsSource = items; + page.SelectedItem = items[1]; + + var template = new DataTemplate (typeof (ContentPage)); + template.SetBinding (ContentPage.TitleProperty, "."); + page.ItemTemplate = template; + + Assert.That (page.SelectedItem, Is.SameAs (items[1])); + } + + [Test] + public void CurrentPageUpdatedWithTemplate() + { + var page = CreateMultiPage(); + string[] items = new[] { "foo", "bar" }; + page.ItemsSource = items; + + var untemplated = page.CurrentPage; + + bool raised = false; + page.PropertyChanged += (sender, e) => { + if (e.PropertyName == "CurrentPage") + raised = true; + }; + + var template = new DataTemplate(() => { + var p = new ContentPage { Content = new Label() }; + p.Content.SetBinding (Label.TextProperty, "."); + return p; + }); + + page.ItemTemplate = template; + + Assert.That (raised, Is.True, "CurrentPage did not change with the template"); + Assert.That (page.CurrentPage, Is.Not.SameAs (untemplated)); + } + + [Test] + public void CurrentPageChanged() + { + var page = CreateMultiPage(); + page.Children.Add (CreateContainedPage()); + page.Children.Add (CreateContainedPage()); + + bool raised = false; + page.CurrentPageChanged += (sender, e) => { + raised = true; + }; + + page.CurrentPage = page.Children[0]; + + Assert.That (raised, Is.False); + + page.CurrentPage = page.Children[1]; + + Assert.That (raised, Is.True); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/MultiTriggerTests.cs b/Xamarin.Forms.Core.UnitTests/MultiTriggerTests.cs new file mode 100644 index 00000000..df253e76 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/MultiTriggerTests.cs @@ -0,0 +1,134 @@ +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class MultiTriggerTests : BaseTestFixture + { + class MockElement : VisualElement + { + } + + [Test] + public void SettersAppliedOnAttachIfConditionIsTrue () + { + var conditionbp = BindableProperty.Create ("foo", typeof(string), typeof(BindableObject), null); + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var multiTrigger = new MultiTrigger (typeof(VisualElement)) { + Conditions = { + new PropertyCondition { Property = conditionbp, Value = "foobar" }, + new BindingCondition { Binding = new Binding ("baz"), Value = "foobaz" }, + }, + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + + element.SetValue (setterbp, "default"); + element.SetValue (conditionbp, "foobar"); + element.BindingContext = new {baz = "foobaz"}; + Assert.AreEqual ("default", element.GetValue (setterbp)); + element.Triggers.Add (multiTrigger); + Assert.AreEqual ("qux", element.GetValue (setterbp)); + } + + [Test] + public void SettersNotAppliedOnAttachIfOneConditionIsFalse () + { + var conditionbp = BindableProperty.Create ("foo", typeof(string), typeof(BindableObject), null); + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var multiTrigger = new MultiTrigger (typeof(VisualElement)) { + Conditions = { + new PropertyCondition { Property = conditionbp, Value = "foobar" }, + new BindingCondition { Binding = new Binding ("baz"), Value = "foobaz" }, + }, + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + + element.SetValue (setterbp, "default"); + element.SetValue (conditionbp, "foobar"); + element.BindingContext = new {baz = "foobazXX"}; + Assert.AreEqual ("default", element.GetValue (setterbp)); + element.Triggers.Add (multiTrigger); + Assert.AreEqual ("default", element.GetValue (setterbp)); + } + + [Test] + public void SettersUnappliedOnDetach () + { + var conditionbp = BindableProperty.Create ("foo", typeof(string), typeof(BindableObject), null); + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var multiTrigger = new MultiTrigger (typeof(VisualElement)) { + Conditions = { + new PropertyCondition { Property = conditionbp, Value = "foobar" }, + new BindingCondition { Binding = new Binding ("baz"), Value = "foobaz" }, + }, + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + + element.SetValue (setterbp, "default"); + element.BindingContext = new {baz = "" }; + element.Triggers.Add (multiTrigger); + Assert.AreEqual ("default", element.GetValue (setterbp)); //both conditions false + + element.SetValue (conditionbp, "foobar"); + Assert.AreEqual ("default", element.GetValue (setterbp)); //one condition false + + element.BindingContext = new {baz = "foobaz"}; + Assert.AreEqual ("qux", element.GetValue (setterbp)); //both condition true + element.Triggers.Remove (multiTrigger); + Assert.AreEqual ("default", element.GetValue (setterbp)); + } + + [Test] + public void SettersAppliedAndUnappliedOnConditionsChange () + { + var conditionbp = BindableProperty.Create ("foo", typeof(string), typeof(BindableObject), null); + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var multiTrigger = new MultiTrigger (typeof(VisualElement)) { + Conditions = { + new PropertyCondition { Property = conditionbp, Value = "foobar" }, + new BindingCondition { Binding = new Binding ("baz"), Value = "foobaz" }, + }, + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + + element.SetValue (setterbp, "default"); + element.BindingContext = new {baz = "" }; + element.Triggers.Add (multiTrigger); + Assert.AreEqual ("default", element.GetValue (setterbp)); //both conditions false + + element.SetValue (conditionbp, "foobar"); + Assert.AreEqual ("default", element.GetValue (setterbp)); //one condition false + + element.BindingContext = new {baz = "foobaz"}; + Assert.AreEqual ("qux", element.GetValue (setterbp)); //both condition true + + element.BindingContext = new {baz = ""}; + Assert.AreEqual ("default", element.GetValue (setterbp)); //one condition false + + element.BindingContext = new {baz = "foobaz"}; + Assert.AreEqual ("qux", element.GetValue (setterbp)); //both condition true + + element.SetValue (conditionbp, ""); + Assert.AreEqual ("default", element.GetValue (setterbp)); //one condition false + + element.SetValue (conditionbp, "foobar"); + Assert.AreEqual ("qux", element.GetValue (setterbp)); //both condition true + + element.SetValue (conditionbp, ""); + element.BindingContext = new {baz = "foobaz"}; + Assert.AreEqual ("default", element.GetValue (setterbp)); //both conditions false + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/NavigationMenuUnitTests.cs b/Xamarin.Forms.Core.UnitTests/NavigationMenuUnitTests.cs new file mode 100644 index 00000000..b91f0229 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/NavigationMenuUnitTests.cs @@ -0,0 +1,191 @@ +using System; +using System.Threading.Tasks; +using NUnit.Framework; + +using System.Collections.Generic; +using System.Linq; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class NavigationMenuUnitTests : BaseTestFixture + { + [Test] + public void TestTargets () + { + var menu = new NavigationMenu (); + + Assert.That (menu.Targets, Is.Empty); + + bool signaled = false; + menu.PropertyChanged += (sender, args) => { + if (args.PropertyName == "Targets") + signaled = true; + }; + + var newArray = new[] { + new ContentPage { Content = new View (), Icon = "img1.jpg" }, + new ContentPage { Content = new View (), Icon = "img2.jpg" } + }; + menu.Targets = newArray; + + Assert.AreEqual (newArray, menu.Targets); + Assert.True (signaled); + } + + [Test] + public void TestTargetsDoubleSet () + { + var menu = new NavigationMenu (); + + bool signaled = false; + menu.PropertyChanged += (sender, args) => { + if (args.PropertyName == "Targets") + signaled = true; + }; + + menu.Targets = menu.Targets; + + Assert.False (signaled); + } + + [Test] + public void TestAdd () + { + var menu = new NavigationMenu (); + + bool signaled = false; + menu.PropertyChanged += (sender, args) => { + switch (args.PropertyName) { + case "Targets": + signaled = true; + break; + } + }; + + var child = new ContentPage { + Content = new View (), + Icon = "img.jpg" + }; + menu.Add (child); + Assert.True (menu.Targets.Contains (child)); + Assert.True (signaled); + } + + [Test] + public void IconNotSet () + { + var menu = new NavigationMenu (); + var childWithoutIcon = new ContentPage { Title = "I have no image" }; + + var ex = Assert.Throws<Exception> (() => menu.Add (childWithoutIcon)); + Assert.That (ex.Message, Is.EqualTo ("Icon must be set for each page before adding them to a Navigation Menu")); + } + + [Test] + public void TestDoubleAdd () + { + var menu = new NavigationMenu (); + + var child = new ContentPage { + Icon = "img.img", + Content = new View () + }; + + menu.Add (child); + + bool signaled = false; + menu.PropertyChanged += (sender, args) => { + switch (args.PropertyName) { + case "Targets": + signaled = true; + break; + } + }; + + menu.Add (child); + + Assert.True (menu.Targets.Contains (child)); + Assert.False (signaled); + } + + [Test] + public void TestRemove () + { + var menu = new NavigationMenu (); + + var child = new ContentPage { + Icon = "img.img", + Content = new View () + }; + menu.Add (child); + + bool signaled = false; + menu.PropertyChanged += (sender, args) => { + switch (args.PropertyName) { + case "Targets": + signaled = true; + break; + } + }; + + menu.Remove (child); + + Assert.False (menu.Targets.Contains (child)); + Assert.True (signaled); + } + + [Test] + public void TestDoubleRemove () + { + var menu = new NavigationMenu (); + + var child = new ContentPage { + Icon = "jpg.jpg", + Content = new View () + }; + menu.Add (child); + menu.Remove (child); + + bool signaled = false; + menu.PropertyChanged += (sender, args) => { + switch (args.PropertyName) { + case "Targets": + signaled = true; + break; + } + }; + + menu.Remove (child); + + Assert.False (menu.Targets.Contains (child)); + Assert.False (signaled); + } + + [Test] + public async Task TestSendTargetSelected () + { + var menu = new NavigationMenu (); + var navForm = new NavigationPage (); + + await navForm.PushAsync (new ContentPage { + Title = "Menu", + Content = menu + }); + + bool pushed = false; + navForm.Pushed += (sender, arg) => pushed = true; + + var child = new ContentPage { + Icon = "img.jpg", + Content = new View () + }; + menu.Add (child); + + menu.SendTargetSelected (child); + + Assert.True (pushed); + Assert.AreEqual (child, navForm.CurrentPage); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/NavigationModelTests.cs b/Xamarin.Forms.Core.UnitTests/NavigationModelTests.cs new file mode 100644 index 00000000..ec3aaf5f --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/NavigationModelTests.cs @@ -0,0 +1,304 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class NavigationModelTests : BaseTestFixture + { + [Test] + public void CurrentNullWhenEmpty () + { + var navModel = new NavigationModel (); + Assert.Null (navModel.CurrentPage); + } + + [Test] + public void CurrentGivesLastViewWithoutModal () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + + navModel.Push (page1, null); + navModel.Push (page2, page1); + + Assert.AreEqual (page2, navModel.CurrentPage); + } + + [Test] + public void CurrentGivesLastViewWithModal() + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + + var modal1 = new ContentPage (); + var modal2 = new ContentPage (); + + navModel.Push (page1, null); + navModel.Push (page2, page1); + + navModel.PushModal (modal1); + navModel.Push (modal2, modal1); + + Assert.AreEqual (modal2, navModel.CurrentPage); + } + + [Test] + public void Roots () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + + var modal1 = new ContentPage (); + var modal2 = new ContentPage (); + + navModel.Push (page1, null); + navModel.Push (page2, page1); + + navModel.PushModal (modal1); + navModel.Push (modal2, modal1); + + Assert.True (navModel.Roots.SequenceEqual (new[] {page1, modal1})); + } + + [Test] + public void PushFirstItem () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + navModel.Push (page1, null); + + Assert.AreEqual (page1, navModel.CurrentPage); + Assert.AreEqual (page1, navModel.Roots.First ()); + } + + [Test] + public void ThrowsWhenPushingWithoutAncestor () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + + navModel.Push (page1, null); + Assert.Throws<InvalidNavigationException> (() => navModel.Push (page2, null)); + } + + [Test] + public void PushFromNonRootAncestor () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + var page3 = new ContentPage (); + + page2.Parent = page1; + page3.Parent = page2; + + navModel.Push (page1, null); + navModel.Push (page2, page1); + navModel.Push (page3, page2); + + Assert.AreEqual (page3, navModel.CurrentPage); + } + + [Test] + public void ThrowsWhenPushFromInvalidAncestor () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + + Assert.Throws<InvalidNavigationException> (() => navModel.Push (page2, page1)); + } + + [Test] + public void Pop () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + + navModel.Push (page1, null); + navModel.Push (page2, page1); + + navModel.Pop (page1); + + Assert.AreEqual (page1, navModel.CurrentPage); + } + + [Test] + public void ThrowsPoppingRootItem () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + + navModel.Push (page1, null); + + Assert.Throws<InvalidNavigationException> (() => navModel.Pop (page1)); + } + + [Test] + public void ThrowsPoppingRootOfModal () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + + var modal1 = new ContentPage (); + + navModel.Push (page1, null); + navModel.Push (page2, page1); + + navModel.PushModal (modal1); + Assert.Throws<InvalidNavigationException> (() => navModel.Pop (modal1)); + } + + [Test] + public void ThrowsPoppingWithInvalidAncestor () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + + navModel.Push (page1, null); + + Assert.Throws<InvalidNavigationException> (() => navModel.Pop (new ContentPage ())); + } + + [Test] + public void PopToRoot () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + var page3 = new ContentPage (); + + page2.Parent = page1; + page3.Parent = page2; + + navModel.Push (page1, null); + navModel.Push (page2, page1); + navModel.Push (page3, page2); + + navModel.PopToRoot (page2); + + Assert.AreEqual (page1, navModel.CurrentPage); + } + + [Test] + public void ThrowsWhenPopToRootOnRoot () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + + navModel.Push (page1, null); + Assert.Throws<InvalidNavigationException> (() => navModel.PopToRoot (page1)); + } + + [Test] + public void ThrowsWhenPopToRootWithInvalidAncestor() + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + + navModel.Push (page1, null); + navModel.Push (page2, page1); + + Assert.Throws<InvalidNavigationException> (() => navModel.PopToRoot (new ContentPage ())); + } + + [Test] + public void PopModal () + { + var navModel = new NavigationModel (); + + var child1 = new ContentPage (); + var modal1 = new ContentPage (); + + navModel.Push (child1, null); + navModel.PushModal (modal1); + + navModel.PopModal (); + + Assert.AreEqual (child1, navModel.CurrentPage); + Assert.AreEqual (1, navModel.Roots.Count ()); + } + + [Test] + public void ReturnsCorrectModal () + { + var navModel = new NavigationModel (); + + var child1 = new ContentPage (); + var modal1 = new ContentPage (); + var modal2 = new ContentPage (); + + navModel.Push (child1, null); + navModel.PushModal (modal1); + navModel.PushModal (modal2); + + Assert.AreEqual (modal2, navModel.PopModal ()); + } + + [Test] + public void PopTopPageWithoutModals () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var page2 = new ContentPage (); + + navModel.Push (page1, null); + navModel.Push (page2, page1); + + Assert.AreEqual (page2, navModel.PopTopPage ()); + } + + [Test] + public void PopTopPageWithSinglePage () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + + navModel.Push (page1, null); + + Assert.Null (navModel.PopTopPage ()); + } + + [Test] + public void PopTopPageWithModal () + { + var navModel = new NavigationModel (); + + var page1 = new ContentPage (); + var modal1 = new ContentPage (); + + navModel.Push (page1, null); + navModel.PushModal (modal1); + + Assert.AreEqual (modal1, navModel.PopTopPage ()); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/NavigationProxyTests.cs b/Xamarin.Forms.Core.UnitTests/NavigationProxyTests.cs new file mode 100644 index 00000000..459f4cf4 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/NavigationProxyTests.cs @@ -0,0 +1,188 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using System.Threading.Tasks; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class NavigationProxyTests : BaseTestFixture + { + class NavigationTest : INavigation + { + public Page LastPushed { get; set; } + public Page LastPushedModal { get; set; } + + public bool Popped { get; set; } + public bool PoppedModal { get; set; } + + public Task PushAsync (Page root) + { + return PushAsync (root, true); + } + + public Task<Page> PopAsync () + { + return PopAsync (true); + } + + public Task PopToRootAsync () + { + return PopToRootAsync (true); + } + + public Task PushModalAsync (Page root) + { + return PushModalAsync (root, true); + } + + public Task<Page> PopModalAsync () + { + return PopModalAsync (true); + } + + public Task PushAsync (Page root, bool animated) + { + LastPushed = root; + return Task.FromResult (root); + } + + public Task<Page> PopAsync (bool animated) + { + Popped = true; + return Task.FromResult (LastPushed); + } + + public Task PopToRootAsync (bool animated) + { + return Task.FromResult<Page> (null); + } + + public Task PushModalAsync (Page root, bool animated) + { + LastPushedModal = root; + return Task.FromResult<object> (null); + } + + public Task<Page> PopModalAsync (bool animated) + { + PoppedModal = true; + return Task.FromResult<Page> (null); + } + + public void RemovePage (Page page) + { + } + + public void InsertPageBefore (Page page, Page before) + { + } + + public System.Collections.Generic.IReadOnlyList<Page> NavigationStack + { + get { return new List<Page> (); } + } + + public System.Collections.Generic.IReadOnlyList<Page> ModalStack + { + get { return new List<Page> (); } + } + } + + [Test] + public void Constructor () + { + var proxy = new NavigationProxy (); + + Assert.Null (proxy.Inner); + } + + [Test] + public async Task PushesIntoNextInner () + { + var page = new ContentPage (); + var navProxy = new NavigationProxy (); + + await navProxy.PushAsync (page); + + var navTest = new NavigationTest (); + navProxy.Inner = navTest; + + Assert.AreEqual (page, navTest.LastPushed); + } + + [Test] + public async Task PushesModalIntoNextInner () + { + var page = new ContentPage (); + var navProxy = new NavigationProxy (); + + await navProxy.PushModalAsync (page); + + var navTest = new NavigationTest (); + navProxy.Inner = navTest; + + Assert.AreEqual (page, navTest.LastPushedModal); + } + + [Test] + public async Task TestPushWithInner () + { + var proxy = new NavigationProxy (); + var inner = new NavigationTest (); + + proxy.Inner = inner; + + var child = new ContentPage {Content = new View ()}; + await proxy.PushAsync (child); + + Assert.AreEqual (child, inner.LastPushed); + } + + [Test] + public async Task TestPushModalWithInner () + { + var proxy = new NavigationProxy (); + var inner = new NavigationTest (); + + proxy.Inner = inner; + + var child = new ContentPage {Content = new View ()}; + await proxy.PushModalAsync (child); + + Assert.AreEqual (child, inner.LastPushedModal); + } + + [Test] + public async Task TestPopWithInner () + { + var proxy = new NavigationProxy (); + var inner = new NavigationTest (); + + proxy.Inner = inner; + + var child = new ContentPage {Content = new View ()}; + await proxy.PushAsync (child); + + var result = await proxy.PopAsync (); + Assert.AreEqual (child, result); + Assert.True (inner.Popped, "Pop was never called on the inner proxy item"); + } + + [Test] + public async Task TestPopModalWithInner () + { + var proxy = new NavigationProxy (); + var inner = new NavigationTest (); + + proxy.Inner = inner; + + var child = new ContentPage {Content = new View ()}; + await proxy.PushModalAsync (child); + + await proxy.PopModalAsync (); + Assert.True (inner.PoppedModal, "Pop was never called on the inner proxy item"); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/NavigationUnitTest.cs b/Xamarin.Forms.Core.UnitTests/NavigationUnitTest.cs new file mode 100644 index 00000000..1d3d4bd2 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/NavigationUnitTest.cs @@ -0,0 +1,393 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class NavigationUnitTest : BaseTestFixture + { + [Test] + public async Task TestNavigationImplPush () + { + NavigationPage nav = new NavigationPage (); + + Assert.IsNull (nav.CurrentPage); + + Label child = new Label {Text = "Label"}; + Page childRoot = new ContentPage {Content = child}; + + await nav.Navigation.PushAsync (childRoot); + + Assert.AreSame (childRoot, nav.CurrentPage); + } + + [Test] + public async Task TestNavigationImplPop () + { + NavigationPage nav = new NavigationPage (); + + Label child = new Label (); + Page childRoot = new ContentPage {Content = child}; + + Label child2 = new Label (); + Page childRoot2 = new ContentPage {Content = child2}; + + await nav.Navigation.PushAsync (childRoot); + await nav.Navigation.PushAsync (childRoot2); + + bool fired = false; + nav.Popped += (sender, e) => fired = true; + var popped = await nav.Navigation.PopAsync (); + + Assert.True (fired); + Assert.AreSame (childRoot, nav.CurrentPage); + Assert.AreEqual (childRoot2, popped); + + await nav.PopAsync (); + var last = await nav.Navigation.PopAsync (); + + Assert.IsNull (last); + } + + [Test] + public async Task TestPushRoot () + { + NavigationPage nav = new NavigationPage (); + + Assert.IsNull (nav.CurrentPage); + + Label child = new Label {Text = "Label"}; + Page childRoot = new ContentPage {Content = child}; + + await nav.PushAsync (childRoot); + + Assert.AreSame (childRoot, nav.CurrentPage); + } + + [Test] + public async Task TestPushEvent () + { + NavigationPage nav = new NavigationPage (); + + Label child = new Label (); + Page childRoot = new ContentPage {Content = child}; + + bool fired = false; + nav.Pushed += (sender, e) => fired = true; + + await nav.PushAsync (childRoot); + + Assert.True (fired); + } + + [Test] + public async Task TestDoublePush () + { + NavigationPage nav = new NavigationPage (); + + Label child = new Label (); + Page childRoot = new ContentPage {Content = child}; + + await nav.PushAsync (childRoot); + + bool fired = false; + nav.Pushed += (sender, e) => fired = true; + + await nav.PushAsync (childRoot); + + Assert.False (fired); + Assert.AreEqual (childRoot, nav.CurrentPage); + } + + [Test] + public async Task TestPop () + { + NavigationPage nav = new NavigationPage (); + + Label child = new Label (); + Page childRoot = new ContentPage {Content = child}; + + Label child2 = new Label (); + Page childRoot2 = new ContentPage {Content = child2}; + + await nav.PushAsync (childRoot); + await nav.PushAsync (childRoot2); + + bool fired = false; + nav.Popped += (sender, e) => fired = true; + var popped = await nav.PopAsync (); + + Assert.True (fired); + Assert.AreSame (childRoot, nav.CurrentPage); + Assert.AreEqual (childRoot2, popped); + + await nav.PopAsync (); + var last = await nav.PopAsync (); + + Assert.IsNull (last); + } + + [Test] + public void TestTint () + { + var nav = new NavigationPage (); + + Assert.AreEqual (Color.Default, nav.Tint); + + bool signaled = false; + nav.PropertyChanged += (sender, args) => { + if (args.PropertyName == "Tint") + signaled = true; + }; + + nav.Tint = new Color (1, 0, 0); + + Assert.AreEqual (new Color (1, 0, 0), nav.Tint); + Assert.True (signaled); + } + + [Test] + public void TestTintDoubleSet () + { + var nav = new NavigationPage (); + + bool signaled = false; + nav.PropertyChanged += (sender, args) => { + if (args.PropertyName == "Tint") + signaled = true; + }; + + nav.Tint = nav.Tint; + + Assert.False (signaled); + } + + [Test] + public async Task TestPopToRoot () + { + var nav = new NavigationPage (); + + bool signaled = false; + nav.PoppedToRoot += (sender, args) => signaled = true; + + var root = new ContentPage {Content = new View ()}; + var child1 = new ContentPage {Content = new View ()}; + var child2 = new ContentPage {Content = new View ()}; + + await nav.PushAsync (root); + await nav.PushAsync (child1); + await nav.PushAsync (child2); + + nav.PopToRootAsync (); + + Assert.True (signaled); + Assert.AreEqual (root, nav.CurrentPage); + } + + [Test] + public async Task TestStackCopy () + { + var nav = new NavigationPage (); + + bool signaled = false; + nav.PoppedToRoot += (sender, args) => signaled = true; + + var root = new ContentPage {Content = new View ()}; + var child1 = new ContentPage {Content = new View ()}; + var child2 = new ContentPage {Content = new View ()}; + + await nav.PushAsync (root); + await nav.PushAsync (child1); + await nav.PushAsync (child2); + + var copy = nav.StackCopy; + + Assert.AreEqual (child2, copy.Pop ()); + Assert.AreEqual (child1, copy.Pop ()); + Assert.AreEqual (root, copy.Pop ()); + } + + [Test] + public void ConstructWithRoot () + { + var root = new ContentPage (); + var nav = new NavigationPage (root); + + Assert.AreEqual (root, nav.LogicalChildren[0]); + Assert.AreEqual (1, nav.StackDepth); + } + + [Test] + public async Task NavigationChangedEventArgs () + { + var rootPage = new ContentPage { Title = "Root" }; + var navPage = new NavigationPage (rootPage); + + var rootArg = new Page (); + + navPage.Pushed += (s, e) => { + rootArg = e.Page; + }; + + var pushPage = new ContentPage { + Title = "Page 2" + }; + + await navPage.PushAsync (pushPage); + + Assert.AreEqual (rootArg, pushPage); + + var secondPushPage = new ContentPage { + Title = "Page 3" + }; + + await navPage.PushAsync (secondPushPage); + + Assert.AreEqual (rootArg, secondPushPage); + } + + [Test] + public async Task CurrentPageChanged() + { + var root = new ContentPage { Title = "Root" }; + var navPage = new NavigationPage (root); + + bool changing = false; + navPage.PropertyChanging += (object sender, PropertyChangingEventArgs e) => { + if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName) { + Assert.That (navPage.CurrentPage, Is.SameAs (root)); + changing = true; + } + }; + + var next = new ContentPage { Title = "Next" }; + + bool changed = false; + navPage.PropertyChanged += (sender, e) => { + if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName) { + Assert.That (navPage.CurrentPage, Is.SameAs (next)); + changed = true; + } + }; + + await navPage.PushAsync (next); + + Assert.That (changing, Is.True, "PropertyChanging was not raised for 'CurrentPage'"); + Assert.That (changed, Is.True, "PropertyChanged was not raised for 'CurrentPage'"); + } + + [Test] + public async void HandlesPopToRoot () + { + var root = new ContentPage { Title = "Root" }; + var navPage = new NavigationPage (root); + + await navPage.PushAsync (new ContentPage ()); + await navPage.PushAsync (new ContentPage ()); + + bool popped = false; + navPage.PoppedToRoot += (sender, args) => { + popped = true; + }; + + await navPage.Navigation.PopToRootAsync (); + + Assert.True (popped); + } + + [Test] + public void SendsBackButtonEventToCurrentPage () + { + var current = new BackButtonPage (); + var navPage = new NavigationPage (current); + + var emitted = false; + current.BackPressed += (sender, args) => emitted = true; + + navPage.SendBackButtonPressed (); + + Assert.True (emitted); + } + + [Test] + public void DoesNotSendBackEventToNonCurrentPage () + { + var current = new BackButtonPage (); + var navPage = new NavigationPage (current); + navPage.PushAsync (new ContentPage ()); + + var emitted = false; + current.BackPressed += (sender, args) => emitted = true; + + navPage.SendBackButtonPressed (); + + Assert.False (emitted); + } + + [Test] + public async void NavigatesBackWhenBackButtonPressed () + { + var root = new ContentPage { Title = "Root" }; + var navPage = new NavigationPage (root); + + await navPage.PushAsync (new ContentPage ()); + + var result = navPage.SendBackButtonPressed (); + + Assert.AreEqual (root, navPage.CurrentPage); + Assert.True (result); + } + + [Test] + public async void DoesNotNavigatesBackWhenBackButtonPressedIfHandled () + { + var root = new BackButtonPage { Title = "Root" }; + var second = new BackButtonPage () {Handle = true}; + var navPage = new NavigationPage (root); + + await navPage.PushAsync (second); + + navPage.SendBackButtonPressed (); + + Assert.AreEqual (second, navPage.CurrentPage); + } + + [Test] + public void DoesNotHandleBackButtonWhenNoNavStack () + { + var root = new ContentPage { Title = "Root" }; + var navPage = new NavigationPage (root); + + var result = navPage.SendBackButtonPressed (); + Assert.False (result); + } + + [Test (Description = "CurrentPage should not be set to null when you attempt to pop the last page")] + [Property ("Bugzilla", 28335)] + public async Task CurrentPageNotNullPoppingRoot() + { + var root = new ContentPage { Title = "Root" }; + var navPage = new NavigationPage (root); + var popped = await navPage.PopAsync (); + Assert.That (popped, Is.Null); + Assert.That (navPage.CurrentPage, Is.SameAs (root)); + } + } + + internal class BackButtonPage : ContentPage + { + public event EventHandler BackPressed; + + public bool Handle = false; + + protected override bool OnBackButtonPressed () + { + if (BackPressed != null) + BackPressed (this, EventArgs.Empty); + return Handle; + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/NotifiedPropertiesTests.cs b/Xamarin.Forms.Core.UnitTests/NotifiedPropertiesTests.cs new file mode 100644 index 00000000..344606bf --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/NotifiedPropertiesTests.cs @@ -0,0 +1,214 @@ +using System; +using NUnit.Framework; +using System.ComponentModel; +using Xamarin.Forms.Maps; +using System.Windows.Input; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class NotifiedPropertiesTests : BaseTestFixture + { + public abstract class PropertyTestCase + { + public string Name { get; set;} + public Func<INotifyPropertyChanged,object> PropertyGetter { get; set; } + public Action<INotifyPropertyChanged, object> PropertySetter { get; set; } + public object ExpectedDefaultValue { get; set; } + public object TestValue { get; set; } + public abstract INotifyPropertyChanged CreateView (); + public virtual string DebugName { + get { return Name; } + } + } + + public class PropertyTestCase<TView, TProperty>:PropertyTestCase where TView : INotifyPropertyChanged + { + Func<TView> init; + Func<TProperty> expectedValueCreator; + + public PropertyTestCase (string name, Func<TView,TProperty> propertyGetter, Action<TView, TProperty> propertySetter, Func<TProperty> expectedDefaultValue, TProperty testValue, Func<TView> init = null) + { + Name = name; + PropertyGetter = v => propertyGetter((TView)v); + PropertySetter = (v,o)=> propertySetter ((TView)v, (TProperty)o); + expectedValueCreator = expectedDefaultValue; + TestValue = testValue; + this.init = init; + } + + public override INotifyPropertyChanged CreateView () + { + ExpectedDefaultValue = expectedValueCreator (); + if (init != null) + return init (); + if (typeof(TView) == typeof(View)) + return new View (); + return (TView)Activator.CreateInstance (typeof(TView), new object[]{ }); + } + + public override string DebugName { + get { + return typeof(TView).Name + "." + Name; + } + } + } + + static PropertyTestCase[] Properties = { + new PropertyTestCase<View, ResourceDictionary> ("Resources", v => v.Resources, (v, o) => v.Resources = o, () => null, new ResourceDictionary ()), + new PropertyTestCase<View, bool> ("InputTransparent", v => v.InputTransparent, (v, o) => v.InputTransparent = o, () => false, true), + new PropertyTestCase<View, double> ("Scale", v => v.Scale, (v, o) => v.Scale = o, () => 1d, 2d), + new PropertyTestCase<View, double> ("Rotation", v => v.Rotation, (v, o) => v.Rotation = o, () => 0d, 90d), + new PropertyTestCase<View, double> ("RotationX", v => v.RotationX, (v, o) => v.RotationX = o, () => 0d, 90d), + new PropertyTestCase<View, double> ("RotationY", v => v.RotationY, (v, o) => v.RotationY = o, () => 0d, 90d), + new PropertyTestCase<View, double> ("AnchorX", v => v.AnchorX, (v, o) => v.AnchorX = o, () => 0.5d, 0d), + new PropertyTestCase<View, double> ("AnchorY", v => v.AnchorY, (v, o) => v.AnchorY = o, () => 0.5d, 0d), + new PropertyTestCase<View, double> ("TranslationX", v => v.TranslationX, (v, o) => v.TranslationX = o, () => 0d, 20d), + new PropertyTestCase<View, double> ("TranslationY", v => v.TranslationY, (v, o) => v.TranslationY = o, () => 0d, 20d), + new PropertyTestCase<View, double> ("Opacity", v => v.Opacity, (v, o) => v.Opacity = o, () => 1d, 0.5d), + new PropertyTestCase<View, bool> ("IsEnabled", v => v.IsEnabled, (v, o) => v.IsEnabled = o, () => true, false), + new PropertyTestCase<View, bool> ("IsVisible", v => v.IsVisible, (v, o) => v.IsVisible = o, () => true, false), + new PropertyTestCase<View, string> ("ClassId", v => v.ClassId, (v, o) => v.ClassId = o, () => null, "Foo"), + new PropertyTestCase<ActivityIndicator, bool> ("IsRunning", v => v.IsRunning, (v, o) => v.IsRunning = o, () => false, true), + new PropertyTestCase<ActivityIndicator, Color> ("Color", v => v.Color, (v, o) => v.Color = o, () => Color.Default, new Color (0, 1, 0)), + new PropertyTestCase<Button, string> ("Text", v => v.Text, (v, o) => v.Text = o, () => null, "Foo"), + new PropertyTestCase<Button, Color> ("TextColor", v => v.TextColor, (v, o) => v.TextColor = o, () => Color.Default, new Color (0, 1, 0)), + new PropertyTestCase<Button, Font> ("Font", v => v.Font, (v, o) => v.Font = o, () => default (Font), Font.SystemFontOfSize (20)), + new PropertyTestCase<Button, double> ("BorderWidth", v => v.BorderWidth, (v, o) => v.BorderWidth = o, () => 0d, 16d), + new PropertyTestCase<Button, int> ("BorderRadius", v => v.BorderRadius, (v, o) => v.BorderRadius = o, () => 5, 12), + new PropertyTestCase<Button, Color> ("BorderColor", v => v.BorderColor, (v, o) => v.BorderColor = o, () => Color.Default, new Color (0, 1, 0)), + new PropertyTestCase<Button, string> ("FontFamily", v => v.FontFamily, (v, o) => v.FontFamily = o, () => null, "TestingFace"), + new PropertyTestCase<Button, double> ("FontSize", v => v.FontSize, (v, o) => v.FontSize = o, () => Device.GetNamedSize (NamedSize.Default, typeof (Button), true), 123.0), + new PropertyTestCase<Button, FontAttributes> ("FontAttributes", v => v.FontAttributes, (v, o) => v.FontAttributes = o, () => FontAttributes.None, FontAttributes.Italic), + new PropertyTestCase<CellTests.TestCell, double> ("Height", v => v.Height, (v, o) => v.Height = o, () => -1, 10), + new PropertyTestCase<DatePicker, DateTime> ("MinimumDate", v => v.MinimumDate, (v, o) => v.MinimumDate = o, () => new DateTime (1900, 1, 1), new DateTime (2014, 02, 05)), + new PropertyTestCase<DatePicker, DateTime> ("MaximumDate", v => v.MaximumDate, (v, o) => v.MaximumDate = o, () => new DateTime (2100, 12, 31), new DateTime (2014, 02, 05)), + new PropertyTestCase<DatePicker, DateTime> ("Date", v => v.Date, (v, o) => v.Date = o, () => DateTime.Now.Date, new DateTime (2008, 5, 5)), + new PropertyTestCase<DatePicker, string> ("Format", v => v.Format, (v, o) => v.Format = o, () => "d", "D"), + new PropertyTestCase<Editor, string> ("Text", v => v.Text, (v, o) => v.Text = o, () => null, "Foo"), + new PropertyTestCase<Entry, string> ("Text", v => v.Text, (v, o) => v.Text = o, () => null, "Foo"), + new PropertyTestCase<Entry, string> ("Placeholder", v => v.Placeholder, (v, o) => v.Placeholder = o, () => null, "Foo"), + new PropertyTestCase<Entry, bool> ("IsPassword", v => v.IsPassword, (v, o) => v.IsPassword = o, () => false, true), + new PropertyTestCase<Entry, Color> ("TextColor", v => v.TextColor, (v, o) => v.TextColor = o, () => Color.Default, new Color (0, 1, 0)), + new PropertyTestCase<Frame, Color> ("BackgroundColor", v => v.BackgroundColor, (v, o) => v.BackgroundColor = o, () => Color.Default, new Color (0, 1, 0)), + new PropertyTestCase<Frame, Color> ("OutlineColor", v => v.OutlineColor, (v, o) => v.OutlineColor = o, () => Color.Default, new Color (0, 1, 0)), + new PropertyTestCase<Frame, bool> ("HasShadow", v => v.HasShadow, (v, o) => v.HasShadow = o, () => true, false), + new PropertyTestCase<Grid, double> ("RowSpacing", v => v.RowSpacing, (v, o) => v.RowSpacing = o, () => 6, 12), + new PropertyTestCase<Grid, double> ("ColumnSpacing", v => v.ColumnSpacing, (v, o) => v.ColumnSpacing = o, () => 6, 12), + new PropertyTestCase<NaiveLayout, Thickness> ("Padding", v => v.Padding, (v, o) => v.Padding = o, () => default(Thickness), new Thickness (20, 20, 10, 10)), + new PropertyTestCase<Image, ImageSource> ("Source", v => v.Source, (v, o) => v.Source = o, () => null, ImageSource.FromFile("Foo")), + new PropertyTestCase<Image, Aspect> ("Aspect", v => v.Aspect, (v, o) => v.Aspect = o, () => Aspect.AspectFit, Aspect.AspectFill), + new PropertyTestCase<Image, bool> ("IsOpaque", v => v.IsOpaque, (v, o) => v.IsOpaque = o, () => false, true), + new PropertyTestCase<Label, string> ("Text", v => v.Text, (v, o) => v.Text = o, () => null, "Foo"), + new PropertyTestCase<Label, TextAlignment> ("XAlign", v => v.XAlign, (v, o) => v.XAlign = o, () => TextAlignment.Start, TextAlignment.End), + new PropertyTestCase<Label, TextAlignment> ("YAlign", v => v.YAlign, (v, o) => v.YAlign = o, () => TextAlignment.Start, TextAlignment.End), + new PropertyTestCase<Label, Color> ("TextColor", v => v.TextColor, (v, o) => v.TextColor = o, () => Color.Default, new Color (0, 1, 0)), + new PropertyTestCase<Label, LineBreakMode> ("LineBreakMode", v => v.LineBreakMode, (v, o) => v.LineBreakMode = o, () => LineBreakMode.WordWrap, LineBreakMode.TailTruncation), + new PropertyTestCase<Label, Font> ("Font", v => v.Font, (v, o) => v.Font = o, () => default (Font), Font.SystemFontOfSize (12)), + new PropertyTestCase<Label, string> ("FontFamily", v => v.FontFamily, (v, o) => v.FontFamily = o, () => null, "TestingFace"), + new PropertyTestCase<Label, double> ("FontSize", v => v.FontSize, (v, o) => v.FontSize = o, () => Device.GetNamedSize (NamedSize.Default, typeof (Label), true), 123.0), + new PropertyTestCase<Label, FontAttributes> ("FontAttributes", v => v.FontAttributes, (v, o) => v.FontAttributes = o, () => FontAttributes.None, FontAttributes.Italic), + new PropertyTestCase<Label, FormattedString> ("FormattedText", v => v.FormattedText, (v, o) => v.FormattedText = o, () => default (FormattedString), new FormattedString()), + new PropertyTestCase<Map, MapType> ("MapType", v => v.MapType, (v, o) => v.MapType = o, () => MapType.Street, MapType.Satellite), + new PropertyTestCase<Map, bool> ("IsShowingUser", v => v.IsShowingUser, (v, o) => v.IsShowingUser = o, () => false, true), + new PropertyTestCase<Map, bool> ("HasScrollEnabled", v => v.HasScrollEnabled, (v, o) => v.HasScrollEnabled = o, () => true, false), + new PropertyTestCase<Map, bool> ("HasZoomEnabled", v => v.HasZoomEnabled, (v, o) => v.HasZoomEnabled = o, () => true, false), + new PropertyTestCase<OpenGLView, bool> ("HasRenderLoop", v => v.HasRenderLoop, (v, o) => v.HasRenderLoop = o, () => false, true), + new PropertyTestCase<Page, string> ("BackgroundImage", v => v.BackgroundImage, (v, o) => v.BackgroundImage = o, () => null, "Foo"), + new PropertyTestCase<Page, Color> ("BackgroundColor", v => v.BackgroundColor, (v, o) => v.BackgroundColor = o, () => default(Color), new Color (0, 1, 0)), + new PropertyTestCase<Page, string> ("Title", v => v.Title, (v, o) => v.Title = o, () => null, "Foo"), + new PropertyTestCase<Page, bool> ("IsBusy", v => v.IsBusy, (v, o) => v.IsBusy = o, () => false, true), + new PropertyTestCase<Page, bool> ("IgnoresContainerArea", v => v.IgnoresContainerArea, (v, o) => v.IgnoresContainerArea = o, () => false, true), + new PropertyTestCase<Page, Thickness> ("Padding", v => v.Padding, (v, o) => v.Padding = o, () => default(Thickness), new Thickness (12)), + new PropertyTestCase<Picker, string> ("Title", v=>v.Title, (v, o) =>v.Title = o, () => null, "FooBar"), + new PropertyTestCase<Picker, int> ("SelectedIndex", v=>v.SelectedIndex, (v, o) =>v.SelectedIndex = o, () => -1, 2, ()=>new Picker{Items= {"Foo", "Bar", "Baz", "Qux"}}), + new PropertyTestCase<ProgressBar, double> ("Progress", v => v.Progress, (v, o) => v.Progress = o, () => 0, .5), + new PropertyTestCase<SearchBar, string> ("Placeholder", v => v.Placeholder, (v, o) => v.Placeholder = o, () => null, "Foo"), + new PropertyTestCase<SearchBar, string> ("Text", v => v.Text, (v, o) => v.Text = o, () => null, "Foo"), + new PropertyTestCase<Slider, double> ("Minimum", v => v.Minimum, (v, o) => v.Minimum = o, () => 0, .5), + new PropertyTestCase<Slider, double> ("Maximum", v => v.Maximum, (v, o) => v.Maximum = o, () => 1, .5), + new PropertyTestCase<Slider, double> ("Value", v => v.Value, (v, o) => v.Value = o, () => 0, .5), + new PropertyTestCase<StackLayout, StackOrientation> ("Orientation", v => v.Orientation, (v, o) => v.Orientation = o, () => StackOrientation.Vertical, StackOrientation.Horizontal), + new PropertyTestCase<StackLayout, double> ("Spacing", v => v.Spacing, (v, o) => v.Spacing = o, () => 6, 12), + new PropertyTestCase<Stepper, double> ("Minimum", v => v.Minimum, (v, o) => v.Minimum = o, () => 0, 50), + new PropertyTestCase<Stepper, double> ("Maximum", v => v.Maximum, (v, o) => v.Maximum = o, () => 100, 50), + new PropertyTestCase<Stepper, double> ("Value", v => v.Value, (v, o) => v.Value = o, () => 0, 50), + new PropertyTestCase<Stepper, double> ("Increment", v => v.Increment, (v, o) => v.Increment = o, () => 1, 2), + new PropertyTestCase<TableRoot, string> ("Title", v => v.Title, (v, o) => v.Title = o, () => null, "Foo"), + new PropertyTestCase<TableView, int> ("RowHeight", v => v.RowHeight, (v, o) => v.RowHeight = o, () => -1, 20), + new PropertyTestCase<TableView, bool> ("HasUnevenRows", v => v.HasUnevenRows, (v, o) => v.HasUnevenRows = o, () => false, true), + new PropertyTestCase<TableView, TableIntent> ("Intent", v => v.Intent, (v, o) => v.Intent = o, () => TableIntent.Data, TableIntent.Menu), + new PropertyTestCase<TextCell, string> ("Text", v => v.Text, (v, o) => v.Text = o, () => null, "Foo"), + new PropertyTestCase<TextCell, string> ("Detail", v => v.Detail, (v, o) => v.Detail = o, () => null, "Foo"), + new PropertyTestCase<TextCell, Color> ("TextColor", v => v.TextColor, (v, o) => v.TextColor = o, () => Color.Default, new Color (0, 1, 0)), + new PropertyTestCase<TextCell, Color> ("DetailColor", v => v.DetailColor, (v, o) => v.DetailColor = o, () => Color.Default, new Color (0, 1, 0)), + new PropertyTestCase<TimePicker, TimeSpan> ("Time", v => v.Time, (v, o) => v.Time = o, () => default(TimeSpan), new TimeSpan (8, 0, 0)), + new PropertyTestCase<TimePicker, string> ("Format", v => v.Format, (v, o) => v.Format = o, () => "t", "T"), + new PropertyTestCase<ViewCell, View> ("View", v => v.View, (v, o) => v.View = o, () => null, new View ()), + new PropertyTestCase<WebView, WebViewSource> ("Source", v => v.Source, (v, o) => v.Source = o, () => null, new UrlWebViewSource { Url = "Foo" }), + new PropertyTestCase<TapGestureRecognizer, int> ("NumberOfTapsRequired", t => t.NumberOfTapsRequired, (t, o) => t.NumberOfTapsRequired = o, () => 1, 3), + new PropertyTestCase<TapGestureRecognizer, object> ("CommandParameter", t => t.CommandParameter, (t, o) => t.CommandParameter = o, () => null, "Test"), + new PropertyTestCase<TapGestureRecognizer, ICommand> ("Command", t => t.Command, (t, o) => t.Command = o, () => null, new Command(()=>{})), + new PropertyTestCase<MasterDetailPage, bool> ("IsGestureEnabled", md => md.IsGestureEnabled, (md, v) => md.IsGestureEnabled = v, () => true, false) + }; + + [SetUp] + public override void Setup () + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown () + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test, TestCaseSource ("Properties")] + public void DefaultValues (PropertyTestCase property) + { + var view = property.CreateView (); + Assert.AreEqual (property.ExpectedDefaultValue, property.PropertyGetter (view), property.DebugName); + } + + [Test, TestCaseSource ("Properties")] + public void Set (PropertyTestCase property) + { + var view = property.CreateView (); + + bool changed = false; + view.PropertyChanged += (sender, args) => { + if (args.PropertyName == property.Name) + changed = true; + }; + + var testvalue = property.TestValue; + property.PropertySetter (view, testvalue); + + Assert.True (changed, property.DebugName); + Assert.AreEqual (testvalue, property.PropertyGetter (view), property.DebugName); + } + + [Test, TestCaseSource ("Properties")] + public void DoubleSet (PropertyTestCase property) + { + var view = property.CreateView (); + + var testvalue = property.TestValue; + property.PropertySetter (view, testvalue); + + bool changed = false; + view.PropertyChanged += (sender, args) => { + if (args.PropertyName == property.Name) + changed = true; + }; + + property.PropertySetter (view, testvalue); + + Assert.False (changed, property.DebugName); + Assert.AreEqual (testvalue, property.PropertyGetter (view), property.DebugName); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/NotifyCollectionChangedEventArgsExtensionsTests.cs b/Xamarin.Forms.Core.UnitTests/NotifyCollectionChangedEventArgsExtensionsTests.cs new file mode 100644 index 00000000..e9acde45 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/NotifyCollectionChangedEventArgsExtensionsTests.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class NotifyCollectionChangedEventArgsExtensionsTests : BaseTestFixture + { + [Test] + public void Add() + { + List<string> applied = new List<string> { "foo", "bar", "baz" }; + + Action reset = () => Assert.Fail ("Reset should not be called"); + Action<object, int, bool> insert = (o, i, create) => { + Assert.That (create, Is.True); + applied.Insert (i, (string) o); + }; + + Action<object, int> removeAt = (o, i) => applied.RemoveAt (i); + + var items = new ObservableCollection<string> (applied); + items.CollectionChanged += (s, e) => e.Apply (insert, removeAt, reset); + + items.Add ("monkey"); + + CollectionAssert.AreEqual (items, applied); + } + + [Test] + public void Insert() + { + List<string> applied = new List<string> { "foo", "bar", "baz" }; + + Action reset = () => Assert.Fail ("Reset should not be called"); + Action<object, int, bool> insert = (o, i, create) => { + Assert.That (create, Is.True); + applied.Insert (i, (string) o); + }; + Action<object, int> removeAt = (o, i) => applied.RemoveAt (i); + + var items = new ObservableCollection<string> (applied); + items.CollectionChanged += (s, e) => e.Apply (insert, removeAt, reset); + + items.Insert (1, "monkey"); + + CollectionAssert.AreEqual (items, applied); + } + + [Test] + public void Move() + { + List<string> applied = new List<string> { "foo", "bar", "baz" }; + + Action reset = () => Assert.Fail ("Reset should not be called"); + Action<object, int, bool> insert = (o, i, create) => { + Assert.That (create, Is.False); + applied.Insert (i, (string) o); + }; + + Action<object, int> removeAt = (o, i) => applied.RemoveAt (i); + + var items = new ObservableCollection<string> (applied); + items.CollectionChanged += (s, e) => e.Apply (insert, removeAt, reset); + + items.Move (0, 2); + + CollectionAssert.AreEqual (items, applied); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ObservableWrapperTests.cs b/Xamarin.Forms.Core.UnitTests/ObservableWrapperTests.cs new file mode 100644 index 00000000..2dc8a58d --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ObservableWrapperTests.cs @@ -0,0 +1,402 @@ +using System; +using System.Collections.ObjectModel; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ObservableWrapperTests : BaseTestFixture + { + [Test] + public void Constructor () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (observableCollection); + + Assert.IsEmpty (wrapper); + + Assert.Throws<ArgumentNullException> (() => new ObservableWrapper<View, View> (null)); + } + + [Test] + public void IgnoresInternallyAdded () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (observableCollection); + + var child = new View (); + + observableCollection.Add (child); + + Assert.IsEmpty (wrapper); + } + + [Test] + public void TracksExternallyAdded () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (observableCollection); + + var child = new Button (); + + wrapper.Add (child); + + Assert.AreEqual (child, wrapper[0]); + Assert.AreEqual (child, observableCollection[0]); + } + + [Test] + public void AddWithInternalItemsAlreadyAdded () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (observableCollection); + + var view = new View (); + observableCollection.Add (view); + + var btn = new Button (); + + wrapper.Add (btn); + + Assert.AreEqual (btn, wrapper[0]); + Assert.AreEqual (1, wrapper.Count); + + Assert.Contains (btn, observableCollection); + Assert.Contains (view, observableCollection); + Assert.AreEqual (2, observableCollection.Count); + } + + [Test] + public void IgnoresInternallyAddedSameType () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, View> (observableCollection); + + var child = new View (); + + observableCollection.Add (child); + + Assert.IsEmpty (wrapper); + } + + [Test] + public void TracksExternallyAddedSameType () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, View> (observableCollection); + + var child = new Button (); + + wrapper.Add (child); + + Assert.AreEqual (child, wrapper[0]); + Assert.AreEqual (child, observableCollection[0]); + } + + [Test] + public void AddWithInternalItemsAlreadyAddedSameType () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, View> (observableCollection); + + var view = new View (); + observableCollection.Add (view); + + var btn = new Button (); + + wrapper.Add (btn); + + Assert.AreEqual (btn, wrapper[0]); + Assert.AreEqual (1, wrapper.Count); + + Assert.Contains (btn, observableCollection); + Assert.Contains (view, observableCollection); + Assert.AreEqual (2, observableCollection.Count); + } + + [Test] + public void CannotRemoveInternalItem () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, View> (observableCollection); + + var child = new View (); + + observableCollection.Add (child); + + Assert.IsEmpty (wrapper); + + Assert.False (wrapper.Remove (child)); + + Assert.Contains (child, observableCollection); + } + + [Test] + public void ReadOnly () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (observableCollection); + + Assert.False (wrapper.IsReadOnly); + + wrapper.Add (new Button ()); + + wrapper.IsReadOnly = true; + + Assert.True (wrapper.IsReadOnly); + + Assert.Throws<NotSupportedException> (() => wrapper.Remove (wrapper[0])); + Assert.Throws<NotSupportedException> (() => wrapper.Add (new Button ())); + Assert.Throws<NotSupportedException> (() => wrapper.RemoveAt (0)); + Assert.Throws<NotSupportedException> (() => wrapper.Insert (0, new Button ())); + Assert.Throws<NotSupportedException> (wrapper.Clear); + } + + [Test] + public void Indexer () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (observableCollection); + + wrapper.Add (new Button ()); + + var newButton = new Button (); + + wrapper[0] = newButton; + + Assert.AreEqual (newButton, wrapper[0]); + } + + [Test] + public void IndexerSameType () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, View> (observableCollection); + + wrapper.Add (new Button ()); + + var newButton = new Button (); + + wrapper[0] = newButton; + + Assert.AreEqual (newButton, wrapper[0]); + } + + [Test] + public void CopyTo () + { + var observableCollection = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, View> (observableCollection); + + var child1 = new Button (); + var child2 = new Button (); + var child3 = new Button (); + var child4 = new Button (); + var child5 = new Button (); + + observableCollection.Add (new Stepper ()); + wrapper.Add (child1); + observableCollection.Add (new Button ()); + wrapper.Add (child2); + wrapper.Add (child3); + wrapper.Add (child4); + wrapper.Add (child5); + observableCollection.Add (new Button ()); + + var target = new View[30]; + wrapper.CopyTo (target, 2); + + Assert.AreEqual (target[2], child1); + Assert.AreEqual (target[3], child2); + Assert.AreEqual (target[4], child3); + Assert.AreEqual (target[5], child4); + Assert.AreEqual (target[6], child5); + } + + [Test] + public void INCCSimpleAdd () + { + var oc = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, View> (oc); + + var child = new Button (); + + Button addedResult = null; + int addIndex = -1; + wrapper.CollectionChanged += (sender, args) => { + addedResult = args.NewItems[0] as Button; + addIndex = args.NewStartingIndex; + }; + + wrapper.Add (child); + + Assert.AreEqual (0, addIndex); + Assert.AreEqual (child, addedResult); + } + + [Test] + public void INCCSimpleAddToInner () + { + var oc = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, View> (oc); + + var child = new Button (); + + Button addedResult = null; + int addIndex = -1; + wrapper.CollectionChanged += (sender, args) => { + addedResult = args.NewItems[0] as Button; + addIndex = args.NewStartingIndex; + }; + + oc.Add (child); + + Assert.AreEqual (-1, addIndex); + Assert.AreEqual (null, addedResult); + } + + [Test] + public void INCCComplexAdd () + { + var oc = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (oc); + + oc.Add (new Stepper ()); + + var child = new Button (); + + Button addedResult = null; + int addIndex = -1; + wrapper.CollectionChanged += (sender, args) => { + addedResult = args.NewItems[0] as Button; + addIndex = args.NewStartingIndex; + }; + + wrapper.Add (child); + + Assert.AreEqual (0, addIndex); + Assert.AreEqual (child, addedResult); + } + + [Test] + public void INCCSimpleRemove () + { + var oc = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (oc); + + var child = new Button (); + wrapper.Add (child); + + Button removedResult = null; + int removeIndex = -1; + wrapper.CollectionChanged += (sender, args) => { + removedResult = args.OldItems[0] as Button; + removeIndex = args.OldStartingIndex; + }; + + wrapper.Remove (child); + + Assert.AreEqual (0, removeIndex); + Assert.AreEqual (child, removedResult); + } + + [Test] + public void INCCSimpleRemoveFromInner () + { + var oc = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (oc); + + var child = new Button (); + oc.Add (child); + + Button addedResult = null; + int addIndex = -1; + wrapper.CollectionChanged += (sender, args) => { + addedResult = args.OldItems[0] as Button; + addIndex = args.OldStartingIndex; + }; + + oc.Remove (child); + + Assert.AreEqual (-1, addIndex); + Assert.AreEqual (null, addedResult); + } + + [Test] + public void INCCComplexRemove () + { + var oc = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (oc); + + oc.Add (new Stepper ()); + + var child = new Button (); + wrapper.Add (child); + + Button removedResult = null; + int removeIndex = -1; + wrapper.CollectionChanged += (sender, args) => { + removedResult = args.OldItems[0] as Button; + removeIndex = args.OldStartingIndex; + }; + + wrapper.Remove (child); + + Assert.AreEqual (child, removedResult); + Assert.AreEqual (0, removeIndex); + } + + [Test] + public void INCCComplexRemoveLast () + { + var oc = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (oc); + + oc.Add (new Stepper ()); + + wrapper.Add (new Button ()); + wrapper.Add (new Button ()); + var child = new Button (); + wrapper.Add (child); + + Button removedResult = null; + int removeIndex = -1; + wrapper.CollectionChanged += (sender, args) => { + removedResult = args.OldItems[0] as Button; + removeIndex = args.OldStartingIndex; + }; + + wrapper.Remove (child); + + Assert.AreEqual (child, removedResult); + Assert.AreEqual (2, removeIndex); + } + + [Test] + public void INCCReplace () + { + var oc = new ObservableCollection<View> (); + var wrapper = new ObservableWrapper<View, Button> (oc); + + var child1 = new Button (); + var child2 = new Button (); + + wrapper.Add (child1); + + int index = -1; + Button oldItem = null; + Button newItem = null; + wrapper.CollectionChanged += (sender, args) => { + index = args.NewStartingIndex; + oldItem = args.OldItems[0] as Button; + newItem = args.NewItems[0] as Button; + }; + + wrapper[0] = child2; + + Assert.AreEqual (0, index); + Assert.AreEqual (child1, oldItem); + Assert.AreEqual (child2, newItem); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/OpenGLViewUnitTests.cs b/Xamarin.Forms.Core.UnitTests/OpenGLViewUnitTests.cs new file mode 100644 index 00000000..e095914c --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/OpenGLViewUnitTests.cs @@ -0,0 +1,20 @@ +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class OpenGLViewUnitTests : BaseTestFixture + { + [Test] + public void Display () + { + var view = new OpenGLView (); + bool displayed = false; + + ((IOpenGlViewController)view).DisplayRequested += (s, o) => displayed = true; + + view.Display (); + Assert.True (displayed); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/PageTests.cs b/Xamarin.Forms.Core.UnitTests/PageTests.cs new file mode 100644 index 00000000..f44d004f --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/PageTests.cs @@ -0,0 +1,498 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class PageTests : BaseTestFixture + { + [TearDown] + public override void TearDown() + { + base.TearDown (); + MessagingCenter.ClearSubscribers(); + } + + [Test] + public void TestConstructor () + { + var child = new Label (); + Page root = new ContentPage {Content = child}; + + Assert.AreEqual (root.LogicalChildren.Count, 1); + Assert.AreSame (root.LogicalChildren.First (), child); + } + + [Test] + public void TestChildFillBehavior () + { + var child = new Label (); + Page root = new ContentPage {Content = child}; + root.IsPlatformEnabled = child.IsPlatformEnabled = true; + + root.Layout (new Rectangle (0, 0, 200, 500)); + + Assert.AreEqual (child.Width, 200); + Assert.AreEqual (child.Height, 500); + } + + [Test] + public void TestSizedChildBehavior () + { + var plat = new UnitPlatform (); + var child = new Label {Platform = plat, IsPlatformEnabled = true, WidthRequest = 100, HorizontalOptions = LayoutOptions.Center}; + var root = new ContentPage {Platform = plat, IsPlatformEnabled = true, Content = child}; + + root.Layout (new Rectangle (0, 0, 200, 500)); + + Assert.AreEqual (50, child.X); + Assert.AreEqual (100, child.Width); + Assert.AreEqual (500, child.Height); + + child = new Label () { + Platform = plat, IsPlatformEnabled = true, + HeightRequest = 100, + VerticalOptions = LayoutOptions.Center + }; + + root = new ContentPage { + Platform = plat, IsPlatformEnabled = true, + Content = child + }; + + root.Layout (new Rectangle (0, 0, 200, 500)); + + Assert.AreEqual (0, child.X); + Assert.AreEqual (200, child.Y); + Assert.AreEqual (200, child.Width); + Assert.AreEqual (100, child.Height); + + child = new Label (); + child.IsPlatformEnabled = true; + child.HeightRequest = 100; + + root = new ContentPage { + Content = child, + Platform = plat, IsPlatformEnabled = true + }; + + root.Layout (new Rectangle (0, 0, 200, 500)); + + Assert.AreEqual (0, child.X); + Assert.AreEqual (0, child.Y); + Assert.AreEqual (200, child.Width); + Assert.AreEqual (500, child.Height); + } + + [Test] + public void NativeSizedChildBehavior () + { + var plat = new UnitPlatform (); + var child = new Label {Platform = plat, IsPlatformEnabled = true, HorizontalOptions = LayoutOptions.Center}; + var root = new ContentPage {Platform = plat, IsPlatformEnabled = true, Content = child}; + + root.Layout (new Rectangle (0, 0, 200, 500)); + + Assert.AreEqual (50, child.X); + Assert.AreEqual (100, child.Width); + Assert.AreEqual (500, child.Height); + + child = new Label () { + Platform = plat, IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.Center + }; + + root = new ContentPage { + Platform = plat, IsPlatformEnabled = true, + Content = child + }; + + root.Layout (new Rectangle (0, 0, 200, 500)); + + Assert.AreEqual (0, child.X); + Assert.AreEqual (240, child.Y); + Assert.AreEqual (200, child.Width); + Assert.AreEqual (20, child.Height); + } + + [Test] + public void TestContentPageSetContent () + { + View child; + var page = new ContentPage {Content = child = new View ()}; + + Assert.AreEqual (child, page.Content); + + bool fired = false; + page.PropertyChanged += (sender, args) => { + if (args.PropertyName == "Content") + fired = true; + }; + + page.Content = child; + Assert.False (fired); + + page.Content = new View (); + Assert.True (fired); + + page.Content = null; + Assert.Null (page.Content); + } + + [Test] + public void TestLayoutChildrenFill () + { + View child; + var page = new ContentPage { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 200, + IsPlatformEnabled = true + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + page.Layout (new Rectangle (0, 0, 800, 800)); + + Assert.AreEqual (new Rectangle (0, 0, 800, 800), child.Bounds); + + page.Layout (new Rectangle (0, 0, 50, 50)); + + Assert.AreEqual (new Rectangle (0, 0, 50, 50), child.Bounds); + } + + [Test] + public void TestLayoutChildrenStart () + { + View child; + var page = new ContentPage { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 200, + HorizontalOptions = LayoutOptions.Start, + VerticalOptions = LayoutOptions.Start, + IsPlatformEnabled = true + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + page.Layout (new Rectangle (0, 0, 800, 800)); + + Assert.AreEqual (new Rectangle (0, 0, 100, 200), child.Bounds); + + page.Layout (new Rectangle (0, 0, 50, 50)); + + Assert.AreEqual (new Rectangle (0, 0, 50, 50), child.Bounds); + } + + [Test] + public void TestLayoutChildrenEnd () + { + View child; + var page = new ContentPage { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 200, + HorizontalOptions = LayoutOptions.End, + VerticalOptions = LayoutOptions.End, + IsPlatformEnabled = true + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + page.Layout (new Rectangle (0, 0, 800, 800)); + + Assert.AreEqual (new Rectangle (700, 600, 100, 200), child.Bounds); + + page.Layout (new Rectangle (0, 0, 50, 50)); + + Assert.AreEqual (new Rectangle (0, 0, 50, 50), child.Bounds); + } + + [Test] + public void TestLayoutChildrenCenter () + { + View child; + var page = new ContentPage { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 200, + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + IsPlatformEnabled = true + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + page.Layout (new Rectangle (0, 0, 800, 800)); + + Assert.AreEqual (new Rectangle (350, 300, 100, 200), child.Bounds); + + page.Layout (new Rectangle (0, 0, 50, 50)); + + Assert.AreEqual (new Rectangle (0, 0, 50, 50), child.Bounds); + } + + [Test] + public void TestLayoutWithContainerArea () + { + View child; + var page = new ContentPage { + Content = child = new View { + WidthRequest = 100, + HeightRequest = 200, + IsPlatformEnabled = true + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + page.Layout (new Rectangle (0, 0, 800, 800)); + + Assert.AreEqual (new Rectangle (0, 0, 800, 800), child.Bounds); + + page.ContainerArea = new Rectangle (10, 10, 30, 30); + + Assert.AreEqual (new Rectangle (10, 10, 30, 30), child.Bounds); + + page.Layout (new Rectangle (0, 0, 50, 50)); + + Assert.AreEqual (new Rectangle (10, 10, 30, 30), child.Bounds); + } + + [Test] + public void TestThrowOnInvalidAlignment () + { + bool thrown = false; + + try { + new ContentPage { + Content = new View { + WidthRequest = 100, + HeightRequest = 200, + HorizontalOptions = new LayoutOptions((LayoutAlignment) int.MaxValue, false), + VerticalOptions = LayoutOptions.Center, + IsPlatformEnabled = true + }, + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + } catch (ArgumentOutOfRangeException) { + thrown = true; + } + + Assert.True (thrown); + } + + [Test] + public void BusyNotSentWhenNotVisible () + { + var sent = false; + MessagingCenter.Subscribe<Page, bool> (this, Page.BusySetSignalName, (p, b) => sent = true); + + new ContentPage { IsBusy = true }; + + Assert.That (sent, Is.False, "Busy message sent while not visible"); + } + + [Test] + public void BusySentWhenBusyPageAppears() + { + var sent = false; + MessagingCenter.Subscribe<Page, bool> (this, Page.BusySetSignalName, (p, b) => { + Assert.That (b, Is.True); + sent = true; + }); + + var page = new ContentPage { IsBusy = true }; + + Assert.That (sent, Is.False, "Busy message sent while not visible"); + + page.SendAppearing(); + + Assert.That (sent, Is.True, "Busy message not sent when visible"); + } + + [Test] + public void BusySentWhenBusyPageDisappears() + { + var page = new ContentPage { IsBusy = true }; + page.SendAppearing(); + + var sent = false; + MessagingCenter.Subscribe<Page, bool> (this, Page.BusySetSignalName, (p, b) => { + Assert.That (b, Is.False); + sent = true; + }); + + page.SendDisappearing(); + + Assert.That (sent, Is.True, "Busy message not sent when visible"); + } + + [Test] + public void BusySentWhenVisiblePageSetToBusy() + { + var sent = false; + MessagingCenter.Subscribe<Page, bool> (this, Page.BusySetSignalName, (p, b) => sent = true); + + var page = new ContentPage(); + page.SendAppearing(); + + Assert.That (sent, Is.False, "Busy message sent appearing while not busy"); + + page.IsBusy = true; + + Assert.That (sent, Is.True, "Busy message not sent when visible"); + } + + [Test] + public void DisplayAlert () + { + var page = new ContentPage (); + + AlertArguments args = null; + MessagingCenter.Subscribe (this, Page.AlertSignalName, (Page sender, AlertArguments e) => args = e); + + var task = page.DisplayAlert ("Title", "Message", "Accept", "Cancel"); + + Assert.AreEqual ("Title", args.Title); + Assert.AreEqual ("Message", args.Message); + Assert.AreEqual ("Accept", args.Accept); + Assert.AreEqual ("Cancel", args.Cancel); + + bool completed = false; + var continueTask = task.ContinueWith (t => completed = true); + + args.SetResult (true); + continueTask.Wait (); + Assert.True (completed); + } + + [Test] + public void DisplayActionSheet () + { + var page = new ContentPage (); + + ActionSheetArguments args = null; + MessagingCenter.Subscribe (this, Page.ActionSheetSignalName, (Page sender, ActionSheetArguments e) => args = e); + + var task = page.DisplayActionSheet ("Title", "Cancel", "Destruction", "Other 1", "Other 2"); + + Assert.AreEqual ("Title", args.Title); + Assert.AreEqual ("Destruction", args.Destruction); + Assert.AreEqual ("Cancel", args.Cancel); + Assert.AreEqual ("Other 1", args.Buttons.First()); + Assert.AreEqual ("Other 2", args.Buttons.Skip (1).First()); + + bool completed = false; + var continueTask = task.ContinueWith (t => completed = true); + + args.SetResult ("Cancel"); + continueTask.Wait (); + Assert.True (completed); + } + + [Test] + public void SendAppearing () + { + var page = new ContentPage (); + + bool sent = false; + page.Appearing += (sender, args) => sent = true; + + page.SendAppearing (); + + Assert.True (sent); + } + + [Test] + public void SendDisappearing () + { + var page = new ContentPage (); + + page.SendAppearing (); + + bool sent = false; + page.Disappearing += (sender, args) => sent = true; + + page.SendDisappearing (); + + Assert.True (sent); + } + + [Test] + public void SendAppearingDoesntGetCalledMultipleTimes () + { + var page = new ContentPage (); + + int countAppearing = 0; + page.Appearing += (sender, args) => countAppearing++; + + page.SendAppearing (); + page.SendAppearing (); + + Assert.That (countAppearing, Is.EqualTo(1)); + } + + [Test] + public void IsVisibleWorks () + { + var page = new ContentPage (); + page.IsVisible = false; + Assert.False (page.IsVisible); + } + + [Test] + public void SendAppearingToChildrenAfter () + { + var page = new ContentPage (); + + var navPage = new NavigationPage (page); + + bool sentNav = false; + bool sent = false; + page.Appearing += (sender, args) => { + if (sentNav) + sent = true; + }; + navPage.Appearing += (sender, e) => sentNav = true; + + navPage.SendAppearing (); + + Assert.True (sentNav); + Assert.True (sent); + + } + + [Test] + public void SendDisappearingToChildrenPageFirst () + { + var page = new ContentPage (); + + var navPage = new NavigationPage (page); + navPage.SendAppearing (); + + bool sentNav = false; + bool sent = false; + page.Disappearing += (sender, args) => { + sent = true; + }; + navPage.Disappearing += (sender, e) => { + if (sent) + sentNav = true; + }; + navPage.SendDisappearing (); + + Assert.True (sentNav); + Assert.True (sent); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/PanGestureRecognizerUnitTests.cs b/Xamarin.Forms.Core.UnitTests/PanGestureRecognizerUnitTests.cs new file mode 100644 index 00000000..c0207d69 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/PanGestureRecognizerUnitTests.cs @@ -0,0 +1,99 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + + public class PanGestureRecognizerUnitTests : BaseTestFixture + { + [Test] + public void PanRaisesStartedEventTest () + { + var view = new View (); + var pan = new PanGestureRecognizer (); + + GestureStatus target = GestureStatus.Canceled; + pan.PanUpdated += (object sender, PanUpdatedEventArgs e) => { + target = e.StatusType; + }; + + ((IPanGestureController)pan).SendPanStarted (view, 0); + Assert.AreEqual (GestureStatus.Started, target); + } + + [Test] + public void PanRaisesRunningEventTest () + { + var view = new View (); + var pan = new PanGestureRecognizer (); + + GestureStatus target = GestureStatus.Canceled; + pan.PanUpdated += (object sender, PanUpdatedEventArgs e) => { + target = e.StatusType; + }; + + ((IPanGestureController)pan).SendPan (view, gestureId: 0, totalX: 5, totalY: 10); + Assert.AreEqual (GestureStatus.Running, target); + } + + [Test] + public void PanRunningEventContainsTotalXTest () + { + var view = new View (); + var pan = new PanGestureRecognizer (); + + double target = 0; + pan.PanUpdated += (object sender, PanUpdatedEventArgs e) => { + target = e.TotalX; + }; + + ((IPanGestureController)pan).SendPan (view, gestureId: 0, totalX: 5, totalY: 10); + Assert.AreEqual (5, target); + } + + [Test] + public void PanRunningEventContainsTotalYTest () + { + var view = new View (); + var pan = new PanGestureRecognizer (); + + double target = 0; + pan.PanUpdated += (object sender, PanUpdatedEventArgs e) => { + target = e.TotalY; + }; + + ((IPanGestureController)pan).SendPan (view, gestureId: 0, totalX: 5, totalY: 10); + Assert.AreEqual (10, target); + } + + [Test] + public void PanRaisesCompletedEventTest () + { + var view = new View (); + var pan = new PanGestureRecognizer (); + + GestureStatus target = GestureStatus.Canceled; + pan.PanUpdated += (object sender, PanUpdatedEventArgs e) => { + target = e.StatusType; + }; + + ((IPanGestureController)pan).SendPanCompleted (view, 0); + Assert.AreEqual (GestureStatus.Completed, target); + } + + [Test] + public void PanRaisesCanceledEventTest () + { + var view = new View (); + var pan = new PanGestureRecognizer (); + + GestureStatus target = GestureStatus.Started; + pan.PanUpdated += (object sender, PanUpdatedEventArgs e) => { + target = e.StatusType; + }; + + ((IPanGestureController)pan).SendPanCanceled (view, 0); + Assert.AreEqual (GestureStatus.Canceled, target); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/PickerTests.cs b/Xamarin.Forms.Core.UnitTests/PickerTests.cs new file mode 100644 index 00000000..859f9025 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/PickerTests.cs @@ -0,0 +1,59 @@ +using System; + +using NUnit.Framework; +using System.Collections.Generic; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class PickerTests : BaseTestFixture + { + [Test] + public void TestSetSelectedIndexOnNullRows() + { + var picker = new Picker (); + + Assert.IsEmpty (picker.Items); + Assert.AreEqual (-1, picker.SelectedIndex); + + picker.SelectedIndex = 2; + + Assert.AreEqual (-1, picker.SelectedIndex); + } + + [Test] + public void TestSelectedIndexInRange () + { + var picker = new Picker { Items = { "John", "Paul", "George", "Ringo" } }; + + picker.SelectedIndex = 2; + Assert.AreEqual (2, picker.SelectedIndex); + + picker.SelectedIndex = 42; + Assert.AreEqual (3, picker.SelectedIndex); + + picker.SelectedIndex = -1; + Assert.AreEqual (-1, picker.SelectedIndex); + + picker.SelectedIndex = -42; + Assert.AreEqual (-1, picker.SelectedIndex); + } + + [Test] + public void TestSelectedIndexChangedOnCollectionShrink() + { + var picker = new Picker { Items = { "John", "Paul", "George", "Ringo" }, SelectedIndex = 3 }; + + Assert.AreEqual (3, picker.SelectedIndex); + + picker.Items.RemoveAt (3); + picker.Items.RemoveAt (2); + + + Assert.AreEqual (1, picker.SelectedIndex); + + picker.Items.Clear (); + Assert.AreEqual (-1, picker.SelectedIndex); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/PinTests.cs b/Xamarin.Forms.Core.UnitTests/PinTests.cs new file mode 100644 index 00000000..6ee7af64 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/PinTests.cs @@ -0,0 +1,105 @@ +using NUnit.Framework; +using Xamarin.Forms.Maps; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class PinTests : BaseTestFixture + { + [Test] + public void Constructor () + { + Pin pin = new Pin { + Type = PinType.SavedPin, + Position = new Position (-92, 178), + Label = "My Desktop", + Address = "123 Hello World Street" + }; + + Assert.AreEqual (pin.Type, PinType.SavedPin); + Assert.AreEqual (pin.Position.Latitude, -90); + Assert.AreEqual (pin.Label, "My Desktop"); + Assert.AreEqual (pin.Address, "123 Hello World Street"); + } + + [Test] + public void Equals () + { + Pin pin1 = new Pin (); + Pin pin2 = new Pin (); + Pin pin3 = new Pin { + Type = PinType.Place, + Position = new Position (12, -24), + Label = "Test", + Address = "123 Test street" + }; + + Pin pin4 = new Pin { + Type = PinType.Place, + Position = new Position (12, -24), + Label = "Test", + Address = "123 Test street" + }; + + Assert.True (pin1.Equals (pin2)); + Assert.True (pin3.Equals (pin4)); + Assert.False (pin1.Equals (pin3)); + } + + [Test] + public void EqualsOp () { + var pin1 = new Pin { + Type = PinType.Place, + Position = new Position (12, -24), + Label = "Test", + Address = "123 Test street" + }; + + var pin2 = new Pin { + Type = PinType.Place, + Position = new Position (12, -24), + Label = "Test", + Address = "123 Test street" + }; + + Assert.True (pin1 == pin2); + } + + [Test] + public void InEqualsOp () { + var pin1 = new Pin { + Type = PinType.Place, + Position = new Position (11.9, -24), + Label = "Test", + Address = "123 Test street" + }; + + var pin2 = new Pin { + Type = PinType.Place, + Position = new Position (12, -24), + Label = "Test", + Address = "123 Test street" + }; + + Assert.True (pin1 != pin2); + } + + [Test] + public void Label () + { + var pin = new Pin { + Label = "OriginalLabel" + }; + + bool signaled = false; + pin.PropertyChanged += (sender, args) => { + if (args.PropertyName == "Label") + signaled = true; + }; + + pin.Label = "Should Signal"; + + Assert.True (signaled); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/PinchGestureRecognizerTests.cs b/Xamarin.Forms.Core.UnitTests/PinchGestureRecognizerTests.cs new file mode 100644 index 00000000..3af55035 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/PinchGestureRecognizerTests.cs @@ -0,0 +1,75 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + public class PinchGestureRecognizerTests : BaseTestFixture + { + [Test] + public void Constructor () + { + var pinch = new PinchGestureRecognizer (); + + } + + [Test] + public void PinchStartedTest () + { + var view = new View (); + var pinch = new PinchGestureRecognizer (); + + GestureStatus result = GestureStatus.Canceled; + var point = new Point (10, 10); + var resultPoint = Point.Zero; + pinch.PinchUpdated += (object sender, PinchGestureUpdatedEventArgs e) => { + result = e.Status; + resultPoint = e.ScaleOrigin; + }; + + ((IPinchGestureController)pinch).SendPinchStarted (view,point); + Assert.AreEqual (GestureStatus.Started, result); + Assert.AreEqual (point, resultPoint); + } + + [Test] + public void PinchCompletedTest () + { + var view = new View (); + var pinch = new PinchGestureRecognizer (); + + GestureStatus result = GestureStatus.Canceled; + pinch.PinchUpdated += (object sender, PinchGestureUpdatedEventArgs e) => { + result = e.Status; + }; + + ((IPinchGestureController)pinch).SendPinchEnded (view); + Assert.AreEqual (GestureStatus.Completed, result); + } + + [Test] + public void PinchUpdatedTest () + { + var view = new View (); + var pinch = new PinchGestureRecognizer (); + var point = new Point (10, 10); + var resultPoint = Point.Zero; + double result = -1; + pinch.PinchUpdated += (object sender, PinchGestureUpdatedEventArgs e) => { + result = e.Scale; + resultPoint = e.ScaleOrigin; + }; + + ((IPinchGestureController)pinch).SendPinch (view, 2, point); + Assert.AreEqual (2, result); + } + + [Test] + public void OnlyOnePinchGesturePerViewTest () + { + var view = new View (); + view.GestureRecognizers.Add(new PinchGestureRecognizer ()); + Assert.Throws<InvalidOperationException> (() => view.GestureRecognizers.Add (new PinchGestureRecognizer ())); + } + } +} + diff --git a/Xamarin.Forms.Core.UnitTests/PointTests.cs b/Xamarin.Forms.Core.UnitTests/PointTests.cs new file mode 100644 index 00000000..40a31b81 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/PointTests.cs @@ -0,0 +1,121 @@ +using System; +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class PointTests : BaseTestFixture + { + [Test] + public void TestPointEquality () + { + Assert.True (new Point (0, 1) != new Point (1, 0)); + Assert.True (new Point (5, 5) == new Point (5, 5)); + } + + [Test] + public void TestPointDistance () + { + Assert.That (new Point (2, 2).Distance (new Point (5, 6)), Is.EqualTo (5).Within (0.001)); + } + + [Test] + public void TestPointMath () + { + var point = new Point (2, 3) + new Size (3, 2); + Assert.AreEqual (new Point (5, 5), point); + + point = new Point (3, 4) - new Size (2, 3); + Assert.AreEqual (new Point (1, 1), point); + } + + [Test] + public void TestPointFromSize () + { + var point = new Point (new Size (10, 20)); + + Assert.AreEqual (10, point.X); + Assert.AreEqual (20, point.Y); + } + + [Test] + public void TestPointOffset () + { + var point = new Point (2, 2); + + point = point.Offset (10, 20); + + Assert.AreEqual (new Point (12, 22), point); + } + + [Test] + public void TestPointRound () + { + var point = new Point (2.4, 2.7); + point = point.Round (); + + Assert.AreEqual (Math.Round (2.4), point.X); + Assert.AreEqual (Math.Round (2.7), point.Y); + } + + [Test] + public void TestPointEmpty () + { + var point = new Point (); + + Assert.True (point.IsEmpty); + } + + [Test] + public void TestPointHashCode ([Range (3, 6)] double x1, [Range (3, 6)] double y1, [Range (3, 6)] double x2, + [Range (3, 6)] double y2) + { + bool result = new Point (x1, y1).GetHashCode () == new Point (x2, y2).GetHashCode (); + if (x1 == x2 && y1 == y2) + Assert.True (result); + else + Assert.False (result); + } + + [Test] + public void TestSizeFromPoint () + { + var point = new Point (2, 4); + var size = (Size) point; + + Assert.AreEqual (new Size (2, 4), size); + } + + [Test] + public void TestPointEquals () + { + var point = new Point (2, 4); + + Assert.True (point.Equals (new Point (2, 4))); + Assert.False (point.Equals (new Point (3, 4))); + Assert.False (point.Equals ("Point")); + } + + [Test] + [TestCase (0, 0, ExpectedResult = "{X=0 Y=0}")] + [TestCase (5, 2, ExpectedResult = "{X=5 Y=2}")] + public string TestPointToString (double x, double y) + { + return new Point (x, y).ToString (); + } + + [Test] + public void TestPointTypeConverter () + { + var converter = new PointTypeConverter (); + Assert.True (converter.CanConvertFrom (typeof(string))); + Assert.AreEqual (new Point (1, 2), converter.ConvertFromInvariantString ("1,2")); + Assert.AreEqual (new Point (1, 2), converter.ConvertFromInvariantString ("1, 2")); + Assert.AreEqual (new Point (1, 2), converter.ConvertFromInvariantString (" 1 , 2 ")); + Assert.AreEqual (new Point (1.1, 2), converter.ConvertFromInvariantString ("1.1,2")); + Assert.Throws<InvalidOperationException> (()=>converter.ConvertFromInvariantString ("")); + } + + } +} diff --git a/Xamarin.Forms.Core.UnitTests/PositionTests.cs b/Xamarin.Forms.Core.UnitTests/PositionTests.cs new file mode 100644 index 00000000..aec845ed --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/PositionTests.cs @@ -0,0 +1,89 @@ +using System; +using NUnit.Framework; +using Xamarin.Forms.Maps; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class PositionTests : BaseTestFixture + { + [Test] + public void Construction () + { + Position position = new Position(); + Assert.AreEqual (position.Latitude, 0); + Assert.AreEqual (position.Longitude, 0); + } + + [Test] + public void CopyConstructor () + { + var position = new Position (12, 13); + var copy = position; + + Assert.AreEqual (12, copy.Latitude); + Assert.AreEqual (13, copy.Longitude); + } + + [Test] + public void EqualityOp () + { + var position1 = new Position (-28, 180); + var position2 = new Position (-28, 180); + Assert.True (position1 == position2); + } + + [Test] + public void InEqualityOp() + { + var position1 = new Position (-8, 180); + var position2 = new Position (-28, 180); + Assert.True(position1 != position2); + } + + + [Test] + public void Equals () + { + var position1 = new Position (78, 167); + var position2 = new Position (78, 167); + Assert.True (position1.Equals (position2)); + Assert.False (position2.Equals (null)); + Assert.True (position2.Equals (position2)); + Assert.False (position2.Equals ("position2")); + } + + [Test] + public void LatitudeClamping () { + var position = new Position (-90.1, 0); + Assert.AreEqual (position.Latitude, -90); + + position = new Position (165, 0); + Assert.AreEqual (position.Latitude, 90); + + position = new Position (15.0, 0); + Assert.AreEqual (position.Latitude, 15.0); + } + + [Test] + public void LongitudeClamping () { + var position = new Position (0, -180.001); + Assert.AreEqual (position.Longitude, -180.0); + + position = new Position (0, 1000); + Assert.AreEqual (position.Longitude, 180); + + position = new Position (0, 0); + Assert.AreEqual (position.Longitude, 0); + } + + [Test] + public void Hashcode () + { + var position = new Position (20, 25); + var position2 = new Position (25, 20); + Assert.AreNotEqual (position.GetHashCode (), position2.GetHashCode ()); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ProgressBarTests.cs b/Xamarin.Forms.Core.UnitTests/ProgressBarTests.cs new file mode 100644 index 00000000..2af7eac0 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ProgressBarTests.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ProgressBarTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + Ticker.Default = new BlockingTicker (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + Ticker.Default = null; + } + + [Test] + public void TestClamp () + { + ProgressBar bar = new ProgressBar (); + + bar.Progress = 2; + Assert.AreEqual (1, bar.Progress); + + bar.Progress = -1; + Assert.AreEqual (0, bar.Progress); + } + + [Test] + public void TestProgressTo () + { + var bar = new ProgressBar (); + + bar.ProgressTo (0.8, 250, Easing.Linear); + + Assert.That (bar.Progress, Is.EqualTo (0.8).Within (0.001)); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/Properties/AssemblyInfo.cs b/Xamarin.Forms.Core.UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..51c143e2 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("Xamarin.Forms.Core.UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Xamarin Inc.")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Xamarin Inc.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/Xamarin.Forms.Core.UnitTests/RectangleUnitTests.cs b/Xamarin.Forms.Core.UnitTests/RectangleUnitTests.cs new file mode 100644 index 00000000..722020bb --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/RectangleUnitTests.cs @@ -0,0 +1,208 @@ +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class RectangleUnitTests : BaseTestFixture + { + [Test] + public void TestRectangleConstruction () + { + var rect = new Rectangle (); + Assert.AreEqual (0, rect.X); + Assert.AreEqual (0, rect.Y); + Assert.AreEqual (0, rect.Width); + Assert.AreEqual (0, rect.Height); + + rect = new Rectangle (2, 3, 4, 5); + Assert.AreEqual (2, rect.X); + Assert.AreEqual (3, rect.Y); + Assert.AreEqual (4, rect.Width); + Assert.AreEqual (5, rect.Height); + + rect = new Rectangle (new Point (2, 3), new Size (4, 5)); + Assert.AreEqual (2, rect.X); + Assert.AreEqual (3, rect.Y); + Assert.AreEqual (4, rect.Width); + Assert.AreEqual (5, rect.Height); + } + + [Test] + public void TestRectangleFromLTRB () + { + var rect = Rectangle.FromLTRB (10, 10, 30, 40); + + Assert.AreEqual (new Rectangle (10, 10, 20, 30), rect); + } + + [Test] + public void TestRectangleCalculatedPoints () + { + var rect = new Rectangle (2, 3, 4, 5); + Assert.AreEqual (2, rect.Left); + Assert.AreEqual (3, rect.Top); + Assert.AreEqual (6, rect.Right); + Assert.AreEqual (8, rect.Bottom); + + Assert.AreEqual (new Size (4, 5), rect.Size); + Assert.AreEqual (new Point (2, 3), rect.Location); + + Assert.AreEqual (new Point (4, 5.5), rect.Center); + + rect.Left = 1; + Assert.AreEqual (1, rect.X); + + rect.Right = 3; + Assert.AreEqual (2, rect.Width); + + rect.Top = 1; + Assert.AreEqual (1, rect.Y); + + rect.Bottom = 2; + Assert.AreEqual (1, rect.Height); + } + + [Test] + public void TestRectangleContains () + { + var rect = new Rectangle (0, 0, 10, 10); + Assert.True (rect.Contains (5, 5)); + Assert.True (rect.Contains (new Point (5, 5))); + Assert.True (rect.Contains (new Rectangle (1, 1, 3, 3))); + + Assert.True (rect.Contains (0, 0)); + Assert.False (rect.Contains (10, 10)); + } + + [Test] + public void TestRectangleInflate () + { + var rect = new Rectangle (0, 0, 10, 10); + rect = rect.Inflate (5, 5); + + Assert.AreEqual (new Rectangle (-5, -5, 20, 20), rect); + + rect = rect.Inflate (new Size (-5, -5)); + + Assert.AreEqual (new Rectangle (0, 0, 10, 10), rect); + } + + [Test] + public void TestRectangleOffset () + { + var rect = new Rectangle (0, 0, 10, 10); + rect = rect.Offset (10, 10); + + Assert.AreEqual (new Rectangle (10, 10, 10, 10), rect); + + rect = rect.Offset (new Point (-10, -10)); + + Assert.AreEqual (new Rectangle (0, 0, 10, 10), rect); + } + + [Test] + public void TestRectangleRound () + { + var rect = new Rectangle (0.2, 0.3, 0.6, 0.7); + + Assert.AreEqual (new Rectangle (0, 0, 1, 1), rect.Round ()); + } + + [Test] + public void TestRectangleIntersect () + { + var rect1 = new Rectangle (0, 0, 10, 10); + + var rect2 = new Rectangle (2, 2, 6, 6); + + var intersection = rect1.Intersect (rect2); + + Assert.AreEqual (rect2, intersection); + + rect2 = new Rectangle(2, 2, 12, 12); + intersection = rect1.Intersect (rect2); + + Assert.AreEqual (new Rectangle (2, 2, 8, 8), intersection); + + rect2 = new Rectangle (20, 20, 2, 2); + intersection = rect1.Intersect (rect2); + + Assert.AreEqual (Rectangle.Zero, intersection); + } + + [Test] + [TestCase(0, 0, ExpectedResult = true)] + [TestCase(0, 5, ExpectedResult = true)] + [TestCase(5, 0, ExpectedResult = true)] + [TestCase(2, 3, ExpectedResult = false)] + public bool TestIsEmpty (int w, int h) + { + return new Rectangle (0, 0, w, h).IsEmpty; + } + + [Test] + [TestCase(0, 0, 8, 8, 0, 0, 5, 5, ExpectedResult = true)] + [TestCase(0, 0, 5, 5, 5, 5, 5, 5, ExpectedResult = false)] + [TestCase(0, 0, 2, 2, 3, 0, 5, 5, ExpectedResult = false)] + public bool TestIntersectsWith (double x1, double y1, double w1, double h1, double x2, double y2, double w2, double h2) + { + return new Rectangle (x1, y1, w1, h1).IntersectsWith (new Rectangle (x2, y2, w2, h2)); + } + + [Test] + public void TestSetSize () + { + var rect = new Rectangle (); + rect.Size = new Size (10, 20); + + Assert.AreEqual (new Rectangle (0, 0, 10, 20), rect); + } + + [Test] + public void TestSetLocation () + { + var rect = new Rectangle (); + rect.Location = new Point (10, 20); + + Assert.AreEqual (new Rectangle (10, 20, 0, 0), rect); + } + + [Test] + public void TestUnion () + { + Assert.AreEqual (new Rectangle (0, 3, 13, 10), new Rectangle (3, 3, 10, 10).Union (new Rectangle(0, 5, 2, 2))); + } + + [Test] + [TestCase(0, 0, 2, 2, ExpectedResult = "{X=0 Y=0 Width=2 Height=2}")] + [TestCase(1, 0, 3, 2, ExpectedResult = "{X=1 Y=0 Width=3 Height=2}")] + public string TestRectangleToString (double x, double y, double w, double h) + { + return new Rectangle (x, y, w, h).ToString (); + } + + [Test] + public void TestRectangleEquals () + { + Assert.True (new Rectangle (0, 0, 10, 10).Equals (new Rectangle(0, 0, 10, 10))); + Assert.False (new Rectangle (0, 0, 10, 10).Equals ("Rectangle")); + Assert.False (new Rectangle (0, 0, 10, 10).Equals (null)); + + Assert.True (new Rectangle (0, 0, 10, 10) == new Rectangle (0, 0, 10, 10)); + Assert.True (new Rectangle (0, 0, 10, 10) != new Rectangle (0, 0, 10, 5)); + } + + [Test] + public void TestRectangleGetHashCode ([Range(3, 4)] double x1, [Range(3, 4)] double y1, [Range(3, 4)] double w1, [Range(3, 4)] double h1, + [Range(3, 4)] double x2, [Range(3, 4)] double y2, [Range(3, 4)] double w2, [Range(3, 4)] double h2) + { + bool result = new Rectangle (x1, y1, w1, h1).GetHashCode () == new Rectangle (x2, y2, w2, h2).GetHashCode (); + + if (x1 == x2 && y1 == y2 && w1 == w2 && h1 == h2) + Assert.True (result); + else + Assert.False (result); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/RegistrarUnitTests.cs b/Xamarin.Forms.Core.UnitTests/RegistrarUnitTests.cs new file mode 100644 index 00000000..9758eaf0 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/RegistrarUnitTests.cs @@ -0,0 +1,149 @@ +using System; +using System.Reflection; +using NUnit.Framework; +using Xamarin.Forms; +using Xamarin.Forms.Core.UnitTests; + +[assembly: TestHandler (typeof (Button), typeof (ButtonTarget))] +[assembly: TestHandler (typeof (Slider), typeof (SliderTarget))] + +namespace Xamarin.Forms.Core.UnitTests +{ + internal class TestHandlerAttribute : HandlerAttribute + { + public TestHandlerAttribute (Type handler, Type target) : base (handler, target) + { + + } + } + + internal class ButtonTarget : IRegisterable {} + + internal class SliderTarget : IRegisterable {} + + [TestFixture] + public class RegistrarTests : BaseTestFixture + { + [SetUp] + public override void Setup () + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + Registrar.RegisterAll (new [] { + typeof (TestHandlerAttribute) + }); + + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void GetButtonHandler () + { + var buttonTarget = Registrar.Registered.GetHandler<ButtonTarget> (typeof (Button)); + Assert.IsNotNull (buttonTarget); + Assert.That (buttonTarget, Is.InstanceOf<ButtonTarget>()); + } + + [Test] + public void GetSliderHandler() + { + var sliderTarget = Registrar.Registered.GetHandler<SliderTarget> (typeof (Slider)); + Assert.IsNotNull (sliderTarget); + Assert.That (sliderTarget, Is.InstanceOf<SliderTarget> ()); + } + } + + [TestFixture] + public class SimpleRegistrarUnitTests + { + class MockRenderer {} + class ButtonMockRenderer : MockRenderer {} + class ShinyButtonMockRenderer : MockRenderer {} + class CrashMockRenderer : MockRenderer + { + public CrashMockRenderer () + { + throw new NotImplementedException(); + } + } + + [Test] + public void TestConstructor () + { + var registrar = new Registrar<MockRenderer> (); + + var renderer = registrar.GetHandler (typeof (Button)); + + Assert.Null (renderer); + } + + [Test] + public void TestGetRendererForKnownClass () + { + var registrar = new Registrar<MockRenderer> (); + + registrar.Register (typeof(View), typeof(MockRenderer)); + + var renderer = registrar.GetHandler (typeof (View)); + + Assert.That (renderer, Is.InstanceOf<MockRenderer>()); + } + + [Test] + public void TestGetRendererForUnknownSubclass () + { + var registrar = new Registrar<MockRenderer> (); + + registrar.Register (typeof (View), typeof (MockRenderer)); + + var renderer = registrar.GetHandler (typeof (Button)); + + Assert.That (renderer, Is.InstanceOf<MockRenderer>()); + } + + [Test] + public void TestGetRendererWithRegisteredSubclass () + { + var registrar = new Registrar<MockRenderer> (); + + registrar.Register (typeof (View), typeof (MockRenderer)); + registrar.Register (typeof (Button), typeof (ButtonMockRenderer)); + + var buttonRenderer = registrar.GetHandler (typeof (Button)); + var viewRenderer = registrar.GetHandler (typeof (View)); + + Assert.That (buttonRenderer, Is.InstanceOf<ButtonMockRenderer>()); + Assert.That (viewRenderer, Is.Not.InstanceOf<ButtonMockRenderer>()); + Assert.That (viewRenderer, Is.InstanceOf<MockRenderer>()); + } + + [Test] + public void TestReplaceRenderer () + { + var registrar = new Registrar<MockRenderer> (); + + registrar.Register (typeof (View), typeof (MockRenderer)); + registrar.Register (typeof (Button), typeof (ButtonMockRenderer)); + registrar.Register (typeof (Button), typeof (ShinyButtonMockRenderer)); + + var buttonRenderer = registrar.GetHandler (typeof (Button)); + + Assert.That (buttonRenderer, Is.InstanceOf<ShinyButtonMockRenderer>()); + } + + [Test] + public void GetHandlerType() + { + var registrar = new Registrar<MockRenderer>(); + registrar.Register (typeof (View), typeof (MockRenderer)); + + Assert.AreEqual (typeof (MockRenderer), registrar.GetHandlerType (typeof (View))); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/RelativeLayoutTests.cs b/Xamarin.Forms.Core.UnitTests/RelativeLayoutTests.cs new file mode 100644 index 00000000..96b3dfc8 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/RelativeLayoutTests.cs @@ -0,0 +1,516 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class RelativeLayoutTests : BaseTestFixture + { + class UnitExpressionSearch : ExpressionVisitor, IExpressionSearch + { + List<object> results; + Type targeType; + public List<T> FindObjects<T>(Expression expression) where T : class + { + results = new List<object> (); + targeType = typeof (T); + Visit (expression); + return results.Select (o => o as T).ToList (); + } + + protected override Expression VisitMember(MemberExpression node) + { + if (node.Expression is ConstantExpression && node.Member is FieldInfo) { + var container = ((ConstantExpression)node.Expression).Value; + var value = ((FieldInfo)node.Member).GetValue (container); + + if (targeType.IsInstanceOfType (value)) { + results.Add (value); + } + } + return base.VisitMember (node); + } + } + + [SetUp] + public override void Setup() + { + base.Setup (); + ExpressionSearch.Default = new UnitExpressionSearch (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + ExpressionSearch.Default = new UnitExpressionSearch (); + } + + [Test] + public void SimpleLayout () + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child, + Constraint.Constant (30), + Constraint.Constant (20), + Constraint.RelativeToParent (parent => parent.Height / 2), + Constraint.RelativeToParent (parent => parent.Height / 4)); + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (30, 20, 50, 25), child.Bounds); + } + + [Test] + public void SimpleExpressionLayout () + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child, + () => 30, + () => 20, + () => relativeLayout.Height / 2, + () => relativeLayout.Height / 4); + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (30, 20, 50, 25), child.Bounds); + } + + [Test] + public void SimpleBoundsSizing () + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child, () => new Rectangle (30, 20, relativeLayout.Height / 2, relativeLayout.Height / 4)); + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (30, 20, 50, 25), child.Bounds); + } + + [Test] + public void UnconstrainedSize() + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View { + IsPlatformEnabled = true, + WidthRequest = 25, + HeightRequest = 50 + }; + + relativeLayout.Children.Add (child, Constraint.Constant (30), Constraint.Constant (20)); + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (30, 20, 25, 50), child.Bounds); + } + + [Test] + public void ViewRelativeLayout () + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child1 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child1, + Constraint.Constant (30), + Constraint.Constant (20), + Constraint.RelativeToParent (parent => parent.Height / 5), + Constraint.RelativeToParent (parent => parent.Height / 10)); + + var child2 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child2, + Constraint.RelativeToView (child1, (layout, view) => view.Bounds.Right + 10), + Constraint.RelativeToView (child1, (layout, view) => view.Y), + Constraint.RelativeToView (child1, (layout, view) => view.Width), + Constraint.RelativeToView (child1, (layout, view) => view.Height)); + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (30, 20, 20, 10), child1.Bounds); + Assert.AreEqual (new Rectangle (60, 20, 20, 10), child2.Bounds); + } + + [Test] + public void ViewRelativeLayoutWithExpressions() + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child1 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child1, + () => 30, + () => 20, + () => relativeLayout.Height / 5, + () => relativeLayout.Height / 10); + + var child2 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child2, + () => child1.Bounds.Right + 10, + () => child1.Y, + () => child1.Width, + () => child1.Height); + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (30, 20, 20, 10), child1.Bounds); + Assert.AreEqual (new Rectangle (60, 20, 20, 10), child2.Bounds); + } + + [Test] + public void ViewRelativeToMultipleViews () + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child1 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child1, + Constraint.Constant (30), + Constraint.Constant (20), + Constraint.RelativeToParent (parent => parent.Height / 5), + Constraint.RelativeToParent (parent => parent.Height / 10)); + + var child2 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child2, + Constraint.Constant (30), + Constraint.Constant (50), + Constraint.RelativeToParent (parent => parent.Height / 4), + Constraint.RelativeToParent (parent => parent.Height / 5)); + + var child3 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child3, + Constraint.RelativeToView (child1, (layout, view) => view.Bounds.Right + 10), + Constraint.RelativeToView (child2, (layout, view) => view.Y), + Constraint.RelativeToView (child1, (layout, view) => view.Width), + Constraint.RelativeToView (child2, (layout, view) => view.Height * 2)); + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (30, 20, 20, 10), child1.Bounds); + Assert.AreEqual (new Rectangle (30, 50, 25, 20), child2.Bounds); + Assert.AreEqual (new Rectangle (60, 50, 20, 40), child3.Bounds); + } + + [Test] + public void ExpressionRelativeToMultipleViews() + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child1 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child1, + Constraint.Constant (30), + Constraint.Constant (20), + Constraint.RelativeToParent (parent => parent.Height / 5), + Constraint.RelativeToParent (parent => parent.Height / 10)); + + var child2 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child2, + Constraint.Constant (30), + Constraint.Constant (50), + Constraint.RelativeToParent (parent => parent.Height / 4), + Constraint.RelativeToParent (parent => parent.Height / 5)); + + var child3 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child3, + () => child1.Bounds.Right + 10, + () => child1.Y, + () => child1.Width + child2.Width, + () => child1.Height * 2 + child2.Height); + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (30, 20, 20, 10), child1.Bounds); + Assert.AreEqual (new Rectangle (30, 50, 25, 20), child2.Bounds); + Assert.AreEqual (new Rectangle (60, 20, 45, 40), child3.Bounds); + } + + [Test] + public void ThreePassLayout () + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child1 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child1, + Constraint.Constant (30), + Constraint.Constant (20), + Constraint.RelativeToParent (parent => parent.Height / 5), + Constraint.RelativeToParent (parent => parent.Height / 10)); + + var child2 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child2, + Constraint.Constant (30), + Constraint.Constant (50), + Constraint.RelativeToParent (parent => parent.Height / 4), + Constraint.RelativeToParent (parent => parent.Height / 5)); + + var child3 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child3, + Constraint.RelativeToView (child1, (layout, view) => view.Bounds.Right + 10), + Constraint.RelativeToView (child2, (layout, view) => view.Y), + Constraint.RelativeToView (child1, (layout, view) => view.Width), + Constraint.RelativeToView (child2, (layout, view) => view.Height * 2)); + + var child4 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child4, + Constraint.RelativeToView (child1, (layout, view) => view.Bounds.Right + 10), + Constraint.RelativeToView (child2, (layout, view) => view.Y), + Constraint.RelativeToView (child1, (layout, view) => view.Width), + Constraint.RelativeToView (child3, (layout, view) => view.Height * 2)); + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (30, 20, 20, 10), child1.Bounds); + Assert.AreEqual (new Rectangle (30, 50, 25, 20), child2.Bounds); + Assert.AreEqual (new Rectangle (60, 50, 20, 40), child3.Bounds); + Assert.AreEqual (new Rectangle (60, 50, 20, 80), child4.Bounds); + } + + [Test] + public void ThreePassLayoutWithExpressions() + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child1 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child1, + x: () => 30, + y: () => 20, + width: () => relativeLayout.Height / 5, + height: () => relativeLayout.Height / 10); + + var child2 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child2, + x: () => 30, + y: () => 50, + width: () => relativeLayout.Height / 4, + height: () => relativeLayout.Height / 5); + + var child3 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child3, + x: () => child1.Bounds.Right + 10, + y: () => child2.Y, + width: () => child1.Width, + height: () => child2.Height * 2); + + var child4 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child4, + x: () => child1.Bounds.Right + 10, + y: () => child2.Y, + width: () => child1.Width, + height: () => child3.Height * 2); + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (30, 20, 20, 10), child1.Bounds); + Assert.AreEqual (new Rectangle (30, 50, 25, 20), child2.Bounds); + Assert.AreEqual (new Rectangle (60, 50, 20, 40), child3.Bounds); + Assert.AreEqual (new Rectangle (60, 50, 20, 80), child4.Bounds); + } + + [Test] + public void ThrowsWithUnsolvableConstraints () + { + var relativeLayout = new RelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child1 = new View { + IsPlatformEnabled = true + }; + + var child2 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child1, + () => 30, + () => 20, + () => child2.Height / 5, + () => relativeLayout.Height / 10); + + relativeLayout.Children.Add (child2, + () => child1.Bounds.Right + 10, + () => child1.Y, + () => child1.Width, + () => child1.Height); + + Assert.Throws<UnsolvableConstraintsException> (() => relativeLayout.Layout (new Rectangle (0, 0, 100, 100))); + } + + [Test] + public void ChildAddedBeforeLayoutChildrenAfterInitialLayout () + { + var relativeLayout = new MockRelativeLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + var child = new View { + IsPlatformEnabled = true + }; + + var child1 = new View { + IsPlatformEnabled = true + }; + + relativeLayout.Children.Add (child, + Constraint.Constant (30), + Constraint.Constant (20), + Constraint.RelativeToParent (parent => parent.Height / 2), + Constraint.RelativeToParent (parent => parent.Height / 4)); + + + relativeLayout.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.IsTrue (relativeLayout.childAdded); + Assert.IsTrue (relativeLayout.added); + Assert.IsTrue (relativeLayout.layoutChildren); + + relativeLayout.layoutChildren = relativeLayout.added = relativeLayout.childAdded = false; + + Assert.IsFalse (relativeLayout.childAdded); + Assert.IsFalse (relativeLayout.added); + Assert.IsFalse (relativeLayout.layoutChildren); + + relativeLayout.Children.Add (child1, + Constraint.Constant (30), + Constraint.Constant (20), + Constraint.RelativeToParent (parent => parent.Height / 2), + Constraint.RelativeToParent (parent => parent.Height / 4)); + + Assert.IsTrue (relativeLayout.childAdded); + Assert.IsTrue (relativeLayout.added); + Assert.IsTrue (relativeLayout.layoutChildren); + + } + } + + internal class MockRelativeLayout : RelativeLayout + { + internal bool layoutChildren; + internal bool childAdded; + internal bool added; + + protected override void LayoutChildren (double x, double y, double width, double height) + { + if(added) + layoutChildren = true; + + base.LayoutChildren (x, y, width, height); + } + + protected override void OnAdded (View view) + { + if(childAdded) + added = true; + base.OnAdded (view); + } + + protected override void OnChildAdded (Element child) + { + childAdded = true; + base.OnChildAdded (child); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ResourceDictionaryTests.cs b/Xamarin.Forms.Core.UnitTests/ResourceDictionaryTests.cs new file mode 100644 index 00000000..d4229221 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ResourceDictionaryTests.cs @@ -0,0 +1,256 @@ +using System; +using System.Linq; +using NUnit.Framework; +using System.Collections.Generic; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ResourceDictionaryTests : BaseTestFixture + { + [Test] + public void Add () + { + var rd = new ResourceDictionary (); + rd.Add ("foo", "bar"); + Assert.AreEqual ("bar", rd ["foo"]); + } + + [Test] + public void AddKVP () + { + var rd = new ResourceDictionary (); + ((ICollection<KeyValuePair<string,object>>)rd).Add (new KeyValuePair<string,object> ("foo", "bar")); + Assert.AreEqual ("bar", rd ["foo"]); + } + + [Test] + public void ResourceDictionaryTriggersValueChangedOnAdd () + { + var rd = new ResourceDictionary (); + ((IResourceDictionary)rd).ValuesChanged += (sender, e) => { + Assert.AreEqual (1, e.Values.Count()); + var kvp = e.Values.First(); + Assert.AreEqual ("foo", kvp.Key); + Assert.AreEqual ("FOO", kvp.Value); + Assert.Pass (); + }; + rd.Add ("foo", "FOO"); + Assert.Fail (); + } + + [Test] + public void ResourceDictionaryTriggersValueChangedOnChange () + { + var rd = new ResourceDictionary (); + rd.Add ("foo", "FOO"); + ((IResourceDictionary)rd).ValuesChanged += (sender, e) => { + Assert.AreEqual (1, e.Values.Count()); + var kvp = e.Values.First(); + Assert.AreEqual ("foo", kvp.Key); + Assert.AreEqual ("BAR", kvp.Value); + Assert.Pass (); + }; + rd ["foo"] = "BAR"; + Assert.Fail (); + } + + [Test] + public void ResourceDictionaryCtor () + { + var rd = new ResourceDictionary (); + Assert.AreEqual (0, rd.Count()); + } + + [Test] + public void ElementMergesParentRDWithCurrent () + { + var elt = new VisualElement { + Resources = new ResourceDictionary { + { "bar","BAR" }, + } + }; + + var parent = new VisualElement { + Resources = new ResourceDictionary { + { "foo", "FOO" }, + } + }; + + elt.Parent = parent; + + object value; + Assert.True (elt.TryGetResource ("foo", out value)); + Assert.AreEqual ("FOO", value); + Assert.True (elt.TryGetResource ("bar", out value)); + Assert.AreEqual ("BAR", value); + } + + [Test] + public void CurrentOverridesParentValues () + { + var elt = new VisualElement { + Resources = new ResourceDictionary { + { "bar","BAZ" }, + } + }; + + var parent = new VisualElement { + Resources = new ResourceDictionary { + { "foo", "FOO" }, + { "bar","BAR" }, + } + }; + + elt.Parent = parent; + + object value; + Assert.True (elt.TryGetResource ("foo", out value)); + Assert.AreEqual ("FOO", value); + Assert.True (elt.TryGetResource ("bar", out value)); + Assert.AreEqual ("BAZ", value); + } + + [Test] + public void AddingToParentTriggersValuesChanged () + { + var elt = new VisualElement { + Resources = new ResourceDictionary { + { "bar","BAR" }, + } + }; + + var parent = new VisualElement { + Resources = new ResourceDictionary { + { "foo", "FOO" }, + } + }; + + elt.Parent = parent; + + ((IElement)elt).AddResourcesChangedListener ((sender, e) => { + Assert.AreEqual (1, e.Values.Count ()); + var kvp = e.Values.First (); + Assert.AreEqual ("baz", kvp.Key); + Assert.AreEqual ("BAZ", kvp.Value); + Assert.Pass (); + }); + + parent.Resources ["baz"] = "BAZ"; + Assert.Fail (); + } + + [Test] + public void ResourcesChangedNotRaisedIfKeyExistsInCurrent () + { + var elt = new VisualElement { + Resources = new ResourceDictionary { + { "bar","BAR" }, + } + }; + + var parent = new VisualElement { + Resources = new ResourceDictionary { + { "foo", "FOO" }, + } + }; + + elt.Parent = parent; + + ((IElement)elt).AddResourcesChangedListener ((sender, e) => Assert.Fail ()); + parent.Resources ["bar"] = "BAZ"; + Assert.Pass (); + } + + [Test] + public void SettingParentTriggersValuesChanged () + { + var elt = new VisualElement { + Resources = new ResourceDictionary { + { "bar","BAR" }, + } + }; + + var parent = new VisualElement { + Resources = new ResourceDictionary { + {"foo", "FOO"}, + {"baz", "BAZ"}, + {"bar", "NEWBAR"} + } + }; + + ((IElement)elt).AddResourcesChangedListener ((sender, e) => { + Assert.AreEqual (2, e.Values.Count ()); + Assert.AreEqual ("FOO", e.Values.First (kvp => kvp.Key == "foo").Value); + Assert.AreEqual ("BAZ", e.Values.First (kvp => kvp.Key == "baz").Value); + Assert.Pass (); + }); + elt.Parent = parent; + Assert.Fail (); + } + + [Test] + public void SettingResourcesTriggersResourcesChanged () + { + var elt = new VisualElement (); + + var parent = new VisualElement { + Resources = new ResourceDictionary { + {"bar", "BAR"} + } + }; + + elt.Parent = parent; + + ((IElement)elt).AddResourcesChangedListener ((sender, e) => { + Assert.AreEqual (3, e.Values.Count ()); + Assert.Pass (); + }); + elt.Resources = new ResourceDictionary { + {"foo", "FOO"}, + {"baz", "BAZ"}, + {"bar", "NEWBAR"} + }; + Assert.Fail(); + } + + [Test] + public void DontThrowOnReparenting () + { + var elt = new View { Resources = new ResourceDictionary () }; + var parent = new StackLayout (); + + parent.Children.Add (elt); + Assert.DoesNotThrow (() => parent.Children.Remove (elt)); + } + + [Test] + public void MultiLevelMerge () + { + var elt = new VisualElement { + Resources = new ResourceDictionary { + { "bar","BAR" }, + } + }; + + var parent = new VisualElement { + Resources = new ResourceDictionary { + { "foo", "FOO" }, + }, + Parent = new VisualElement { + Resources = new ResourceDictionary { + {"baz", "BAZ"} + } + } + }; + + ((IElement)elt).AddResourcesChangedListener ((sender, e) => { + Assert.AreEqual (2, e.Values.Count ()); + Assert.Pass (); + }); + + elt.Parent = parent; + Assert.Fail (); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/ScrollViewUnitTests.cs b/Xamarin.Forms.Core.UnitTests/ScrollViewUnitTests.cs new file mode 100644 index 00000000..066d4e5d --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ScrollViewUnitTests.cs @@ -0,0 +1,403 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ScrollViewUnitTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void TestConstructor () + { + ScrollView scrollView = new ScrollView (); + + Assert.Null (scrollView.Content); + + View view = new View (); + scrollView = new ScrollView {Content = view}; + + Assert.AreEqual (view, scrollView.Content); + } + + [Test] + [TestCase (ScrollOrientation.Horizontal)] + [TestCase (ScrollOrientation.Both)] + public void GetsCorrectSizeRequestWithWrappingContent (ScrollOrientation orientation) + { + var scrollView = new ScrollView { + IsPlatformEnabled = true, + Orientation = orientation, + Platform = new UnitPlatform (null, true) + }; + + var hLayout = new StackLayout { + IsPlatformEnabled = true, + Orientation = StackOrientation.Horizontal, + Children = { + new Label {Text = "THIS IS A REALLY LONG STRING", IsPlatformEnabled = true}, + new Label {Text = "THIS IS A REALLY LONG STRING", IsPlatformEnabled = true}, + new Label {Text = "THIS IS A REALLY LONG STRING", IsPlatformEnabled = true}, + new Label {Text = "THIS IS A REALLY LONG STRING", IsPlatformEnabled = true}, + new Label {Text = "THIS IS A REALLY LONG STRING", IsPlatformEnabled = true}, + } + }; + + scrollView.Content = hLayout; + + var r = scrollView.GetSizeRequest (100, 100); + + Assert.AreEqual (10, r.Request.Height); + } + + [Test] + public void TestContentSizeChangedVertical () + { + View view = new View {IsPlatformEnabled = true, WidthRequest = 100, HeightRequest = 100}; + + ScrollView scroll = new ScrollView {Content = view}; + scroll.Platform = new UnitPlatform (); + scroll.Layout (new Rectangle (0, 0, 50, 50)); + + Assert.AreEqual (new Size (50, 100), scroll.ContentSize); + + bool changed = false; + scroll.PropertyChanged += (sender, e) => { + switch (e.PropertyName) { + case "ContentSize": + changed = true; + break; + } + }; + + view.HeightRequest = 200; + + Assert.True (changed); + Assert.AreEqual (new Size (50, 200), scroll.ContentSize); + } + + [Test] + public void TestContentSizeChangedVerticalBidirectional () + { + View view = new View { IsPlatformEnabled = true, WidthRequest = 100, HeightRequest = 100 }; + + ScrollView scroll = new ScrollView { Content = view, Orientation = ScrollOrientation.Both }; + scroll.Platform = new UnitPlatform (); + scroll.Layout (new Rectangle (0, 0, 50, 50)); + + Assert.AreEqual (new Size (100, 100), scroll.ContentSize); + + bool changed = false; + scroll.PropertyChanged += (sender, e) => { + switch (e.PropertyName) { + case "ContentSize": + changed = true; + break; + } + }; + + view.HeightRequest = 200; + + Assert.True (changed); + Assert.AreEqual (new Size (100, 200), scroll.ContentSize); + } + + [Test] + public void TestContentSizeChangedHorizontal () + { + View view = new View {IsPlatformEnabled = true, WidthRequest = 100, HeightRequest = 100}; + + var scroll = new ScrollView { + Orientation = ScrollOrientation.Horizontal, + Content = view + }; + scroll.Platform = new UnitPlatform (); + scroll.Layout (new Rectangle (0, 0, 50, 50)); + + Assert.AreEqual (new Size (100, 50), scroll.ContentSize); + + bool changed = false; + scroll.PropertyChanged += (sender, e) => { + switch (e.PropertyName) { + case "ContentSize": + changed = true; + break; + } + }; + + view.WidthRequest = 200; + + Assert.True (changed); + Assert.AreEqual (new Size (200, 50), scroll.ContentSize); + } + + [Test] + public void TestContentSizeChangedHorizontalBidirectional () + { + View view = new View { IsPlatformEnabled = true, WidthRequest = 100, HeightRequest = 100 }; + + var scroll = new ScrollView { + Orientation = ScrollOrientation.Both, + Content = view + }; + scroll.Platform = new UnitPlatform (); + scroll.Layout (new Rectangle (0, 0, 50, 50)); + + Assert.AreEqual (new Size (100, 100), scroll.ContentSize); + + bool changed = false; + scroll.PropertyChanged += (sender, e) => { + switch (e.PropertyName) { + case "ContentSize": + changed = true; + break; + } + }; + + view.WidthRequest = 200; + + Assert.True (changed); + Assert.AreEqual (new Size (200, 100), scroll.ContentSize); + } + + [Test] + public void TestContentSizeClamping () + { + View view = new View {IsPlatformEnabled = true, WidthRequest = 100, HeightRequest = 100}; + + var scroll = new ScrollView { + Orientation = ScrollOrientation.Horizontal, + Content = view, + Platform = new UnitPlatform () + }; + scroll.Layout (new Rectangle (0, 0, 50, 50)); + + bool changed = false; + scroll.PropertyChanged += (sender, e) => { + switch (e.PropertyName) { + case "ContentSize": + changed = true; + break; + } + }; + + view.HeightRequest = 200; + + Assert.False (changed); + Assert.AreEqual (new Size (100, 50), scroll.ContentSize); + } + + [Test] + public void TestChildChanged () + { + ScrollView scrollView = new ScrollView (); + + bool changed = false; + scrollView.PropertyChanged += (sender, e) => { + switch (e.PropertyName) { + case "Content": + changed = true; + break; + } + }; + View view = new View (); + scrollView.Content = view; + + Assert.True (changed); + } + + [Test] + public void TestChildDoubleSet () + { + var scrollView = new ScrollView (); + + bool changed = false; + scrollView.PropertyChanged += (sender, args) => { + if (args.PropertyName == "Content") + changed = true; + }; + + var child = new View (); + scrollView.Content = child; + + Assert.True (changed); + Assert.AreEqual (child, scrollView.Content); + + changed = false; + + scrollView.Content = child; + + Assert.False (changed); + + scrollView.Content = null; + + Assert.True (changed); + Assert.Null (scrollView.Content); + } + + [Test] + public void TestOrientation () + { + var scrollView = new ScrollView (); + + Assert.AreEqual (ScrollOrientation.Vertical, scrollView.Orientation); + + bool signaled = false; + scrollView.PropertyChanged += (sender, args) => { + if (args.PropertyName == "Orientation") + signaled = true; + }; + + scrollView.Orientation = ScrollOrientation.Horizontal; + + Assert.AreEqual (ScrollOrientation.Horizontal, scrollView.Orientation); + Assert.True (signaled); + + scrollView.Orientation = ScrollOrientation.Both; + Assert.AreEqual (ScrollOrientation.Both, scrollView.Orientation); + Assert.True (signaled); + } + + [Test] + public void TestOrientationDoubleSet () + { + var scrollView = new ScrollView (); + + bool signaled = false; + scrollView.PropertyChanged += (sender, args) => { + if (args.PropertyName == "Orientation") + signaled = true; + }; + + scrollView.Orientation = scrollView.Orientation; + + Assert.False (signaled); + } + + + [Test] + public void TestScrollTo() + { + var scrollView = new ScrollView (); + scrollView.Platform = new UnitPlatform (); + + var item = new View { }; + scrollView.Content = new StackLayout { Children = { item } }; + + bool requested = false; + ((IScrollViewController) scrollView).ScrollToRequested += (sender, args) => { + requested = true; + Assert.AreEqual(args.ScrollY,100); + Assert.AreEqual(args.ScrollX,0); + Assert.Null (args.Item); + Assert.That (args.ShouldAnimate, Is.EqualTo (true)); + }; + + scrollView.ScrollToAsync (0,100, true); + Assert.That (requested, Is.True); + } + + [Test] + public void TestScrollToNotAnimated() + { + var scrollView = new ScrollView (); + scrollView.Platform = new UnitPlatform (); + + var item = new View { }; + scrollView.Content = new StackLayout { Children = { item } }; + + bool requested = false; + ((IScrollViewController) scrollView).ScrollToRequested += (sender, args) => { + requested = true; + Assert.AreEqual(args.ScrollY,100); + Assert.AreEqual(args.ScrollX,0); + Assert.Null (args.Item); + Assert.That (args.ShouldAnimate, Is.EqualTo (false)); + }; + + scrollView.ScrollToAsync (0,100, false); + Assert.That (requested, Is.True); + } + + [Test] + public void TestScrollToElement () + { + var scrollView = new ScrollView (); + scrollView.Platform = new UnitPlatform (); + + var item = new Label { Text = "Test" }; + scrollView.Content = new StackLayout { Children = { item } }; + + bool requested = false; + ((IScrollViewController) scrollView).ScrollToRequested += (sender, args) => { + requested = true; + + Assert.That (args.Element, Is.SameAs (item)); + Assert.That (args.Position, Is.EqualTo (ScrollToPosition.Center)); + Assert.That (args.ShouldAnimate, Is.EqualTo (true)); + }; + + scrollView.ScrollToAsync (item, ScrollToPosition.Center, true); + Assert.That (requested, Is.True); + } + + [Test] + public void TestScrollToElementNotAnimated () + { + var scrollView = new ScrollView (); + scrollView.Platform = new UnitPlatform (); + + var item = new Label { Text = "Test" }; + scrollView.Content = new StackLayout { Children = { item } }; + + bool requested = false; + ((IScrollViewController) scrollView).ScrollToRequested += (sender, args) => { + requested = true; + + Assert.That (args.Element, Is.SameAs (item)); + Assert.That (args.Position, Is.EqualTo (ScrollToPosition.Center)); + Assert.That (args.ShouldAnimate, Is.EqualTo (false)); + }; + + scrollView.ScrollToAsync (item, ScrollToPosition.Center, false); + Assert.That (requested, Is.True); + } + + [Test] + public void TestScrollToInvalid () + { + var scrollView = new ScrollView (); + scrollView.Platform = new UnitPlatform (); + + Assert.That (() => scrollView.ScrollToAsync (new VisualElement(), ScrollToPosition.Center, true), Throws.ArgumentException); + Assert.That (() => scrollView.ScrollToAsync (null, (ScrollToPosition) 500, true), Throws.ArgumentException); + } + + [Test] + public void SetScrollPosition () + { + var scroll = new ScrollView(); + IScrollViewController controller = scroll; + controller.SetScrolledPosition (100, 100); + + Assert.That (scroll.ScrollX, Is.EqualTo (100)); + Assert.That (scroll.ScrollY, Is.EqualTo (100)); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/SearchBarUnitTests.cs b/Xamarin.Forms.Core.UnitTests/SearchBarUnitTests.cs new file mode 100644 index 00000000..a09aa429 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/SearchBarUnitTests.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class SearchBarUnitTests : BaseTestFixture + { + [Test] + public void TestConstructor () + { + SearchBar searchBar = new SearchBar (); + + Assert.Null (searchBar.Placeholder); + Assert.Null (searchBar.Text); + } + + [Test] + public void TestContentsChanged () + { + SearchBar searchBar = new SearchBar (); + + bool thrown = false; + + searchBar.TextChanged += (sender, e) => thrown = true; + + searchBar.Text = "Foo"; + + Assert.True (thrown); + } + + [Test] + public void TestSearchButtonPressed () + { + SearchBar searchBar = new SearchBar (); + + bool thrown = false; + searchBar.SearchButtonPressed += (sender, e) => thrown = true; + + searchBar.OnSearchButtonPressed (); + + Assert.True (thrown); + } + + [Test] + public void TestSearchCommandParameter () + { + var searchBar = new SearchBar (); + + object param = "Testing"; + object result = null; + searchBar.SearchCommand = new Command (p => { result = p; }); + searchBar.SearchCommandParameter = param; + + searchBar.OnSearchButtonPressed (); + + Assert.AreEqual (param, result); + } + + [TestCase (null, "Text Changed")] + [TestCase ("Initial Text", null)] + [TestCase ("Initial Text", "Text Changed")] + public void SearchBarTextChangedEventArgs (string initialText, string finalText) + { + var searchBar = new SearchBar { + Text = initialText + }; + + SearchBar searchBarFromSender = null; + string oldText = null; + string newText = null; + + searchBar.TextChanged += (s, e) => { + searchBarFromSender = (SearchBar)s; + oldText = e.OldTextValue; + newText = e.NewTextValue; + }; + + searchBar.Text = finalText; + + Assert.AreEqual (searchBar, searchBarFromSender); + Assert.AreEqual (initialText, oldText); + Assert.AreEqual (finalText, newText); + } + + [Test] + public void CommandCanExecuteUpdatesEnabled () + { + var searchBar = new SearchBar (); + + bool result = false; + + var bindingContext = new { + Command = new Command (() => { }, () => result) + }; + + searchBar.SetBinding (SearchBar.SearchCommandProperty, "Command"); + searchBar.BindingContext = bindingContext; + + Assert.False (searchBar.IsEnabled); + + result = true; + + bindingContext.Command.ChangeCanExecute (); + + Assert.True (searchBar.IsEnabled); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/SizeTests.cs b/Xamarin.Forms.Core.UnitTests/SizeTests.cs new file mode 100644 index 00000000..d845d615 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/SizeTests.cs @@ -0,0 +1,93 @@ +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class SizeTests : BaseTestFixture + { + [Test] + public void TestSizeIsZero () + { + var size = new Size (); + + Assert.True (size.IsZero); + + size = new Size (10, 10); + + Assert.False (size.IsZero); + } + + [Test] + public void TestSizeAdd () + { + var size1 = new Size (10, 10); + var size2 = new Size (20, 20); + + var result = size1 + size2; + + Assert.AreEqual (new Size (30, 30), result); + } + + [Test] + public void TestSizeSubtract () + { + var size1 = new Size (10, 10); + var size2 = new Size (2, 2); + + var result = size1 - size2; + + Assert.AreEqual (new Size (8, 8), result); + } + + [Test] + public void TestPointFromSize ([Range(0, 2)] double x, [Range(0, 2)] double y) + { + var size = new Size (x, y); + var point = (Point) size; + + Assert.AreEqual (x, point.X); + Assert.AreEqual (y, point.Y); + } + + [Test] + public void HashCode ([Range(3, 5)] double w1, [Range(3, 5)] double h1, [Range(3, 5)] double w2, [Range(3, 5)] double h2) + { + bool result = new Size (w1, h1).GetHashCode () == new Size (w2, h2).GetHashCode (); + + if (w1 == w2 && h1 == h2) + Assert.True (result); + else + Assert.False (result); + } + + [Test] + public void Equality () + { + Assert.False (new Size ().Equals (null)); + Assert.False (new Size ().Equals ("Size")); + Assert.True (new Size (2, 3).Equals (new Size (2, 3))); + + Assert.True (new Size (2, 3) == new Size (2, 3)); + Assert.True (new Size (2, 3) != new Size (3, 2)); + } + + [Test] + [TestCase(0, 0, ExpectedResult = "{Width=0 Height=0}")] + [TestCase(1, 5, ExpectedResult = "{Width=1 Height=5}")] + public string TestToString (double w, double h) + { + return new Size (w, h).ToString (); + } + + [Test] + public void MultiplyByScalar ([Range (12, 15)] int w, [Range (12, 15)] int h, [Values (0.0, 2.0, 7.0, 0.25)] double scalar) + { + var size = new Size (w, h); + var result = size * scalar; + + Assert.AreEqual (w * scalar, result.Width); + Assert.AreEqual (h * scalar, result.Height); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/SliderUnitTests.cs b/Xamarin.Forms.Core.UnitTests/SliderUnitTests.cs new file mode 100644 index 00000000..22ad75ba --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/SliderUnitTests.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class SliderUnitTests : BaseTestFixture + { + [Test] + public void TestConstructor () + { + var slider = new Slider (20, 200, 50); + + Assert.AreEqual (20, slider.Minimum); + Assert.AreEqual (200, slider.Maximum); + Assert.AreEqual (50, slider.Value); + } + + [Test] + public void TestInvalidConstructor () + { + Assert.Throws<ArgumentOutOfRangeException> (() => new Slider (10, 5, 10)); + } + + [Test] + public void TestConstructorClamping () + { + Slider slider = new Slider (50, 100, 0); + + Assert.AreEqual (50, slider.Value); + } + + [Test] + public void TestMinValueClamp () + { + Slider slider = new Slider (0, 100, 0); + + slider.Minimum = 10; + + Assert.AreEqual (10, slider.Value); + Assert.AreEqual (10, slider.Minimum); + } + + [Test] + public void TestMaxValueClamp () + { + Slider slider = new Slider (0, 100, 100); + + slider.Maximum = 10; + + Assert.AreEqual (10, slider.Value); + Assert.AreEqual (10, slider.Maximum); + } + + [Test] + public void TestInvalidMaxValue () + { + var slider = new Slider (); + Assert.Throws<ArgumentException> (() => slider.Maximum = slider.Minimum - 1); + } + + [Test] + public void TestInvalidMinValue () + { + var slider = new Slider (); + Assert.Throws<ArgumentException> (() => slider.Minimum = slider.Maximum + 1); + } + + [Test] + public void TestValueChanged () + { + var slider = new Slider (); + var changed = false; + + slider.ValueChanged += (sender, arg) => changed = true; + + slider.Value += 1; + + Assert.True (changed); + } + + [TestCase (0.0, 1.0)] + [TestCase (1.0, 0.5)] + public void SliderValueChangedEventArgs (double initialValue, double finalValue) + { + var slider = new Slider { + Minimum = 0.0, + Maximum = 1.0, + Value = initialValue + }; + + Slider sliderFromSender = null; + double oldValue = 0.0; + double newValue = 0.0; + + slider.ValueChanged += (s, e) => { + sliderFromSender = (Slider)s; + oldValue = e.OldValue; + newValue = e.NewValue; + }; + + slider.Value = finalValue; + + Assert.AreEqual (slider, sliderFromSender); + Assert.AreEqual (initialValue, oldValue); + Assert.AreEqual (finalValue, newValue); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/StackLayoutUnitTests.cs b/Xamarin.Forms.Core.UnitTests/StackLayoutUnitTests.cs new file mode 100644 index 00000000..fb4a8aac --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/StackLayoutUnitTests.cs @@ -0,0 +1,619 @@ +using System.Collections; +using System.Linq; +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class StackLayoutUnitTests : BaseTestFixture + { + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void EmptyLayoutDoesntCrash () + { + var stackLayout = new StackLayout (); + stackLayout.Layout (new Rectangle (0, 0, 200, 200)); + } + + [Test] + public void TestLastChildNotVisible () + { + View child1, child2; + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Children = { + (child1 = new View {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}), + (child2 = new View {WidthRequest = 30, HeightRequest = 30, IsPlatformEnabled = true, IsVisible = false}) + } + }; + + var r = stack.GetSizeRequest (100, 100); + + Assert.AreEqual (new SizeRequest (new Size(20, 20)), r); + } + + [Test] + public void TestLayoutVertical () + { + View child1, child2; + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Children = { + (child1 = new View {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}), + (child2 = new View {WidthRequest = 30, HeightRequest = 30, IsPlatformEnabled = true}) + } + }; + + stack.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (0, 0, 100, 20), child1.Bounds); + Assert.AreEqual (new Rectangle (0, 26, 100, 30), child2.Bounds); + } + + [Test] + public void ReinflatesViewsCorrectly () + { + var child1 = new BoxView { + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.FillAndExpand, + HeightRequest = 400, + MinimumHeightRequest = 10 + }; + + var child2 = new BoxView { + IsPlatformEnabled = true, + HeightRequest = 40, + MinimumHeightRequest = 40 + }; + + var stack = new StackLayout { + Spacing = 0, + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Children = { child1, child2 } + }; + + stack.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (0, 0, 100, 60), child1.Bounds); + Assert.AreEqual (new Rectangle (0, 60, 100, 40), child2.Bounds); + + stack.GetSizeRequest (100, 100); + stack.Layout (new Rectangle (0, 0, 100, 500)); + + Assert.AreEqual (new Rectangle (0, 0, 100, 460), child1.Bounds); + Assert.AreEqual (new Rectangle (0, 460, 100, 40), child2.Bounds); + } + + [Test] + public void TestLayoutHorizontal () + { + View child1, child2; + var stack = new StackLayout { + Orientation = StackOrientation.Horizontal, + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Children = { + (child1 = new View {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}), + (child2 = new View {WidthRequest = 30, HeightRequest = 30, IsPlatformEnabled = true}) + } + }; + + stack.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (0, 0, 20, 100), child1.Bounds); + Assert.AreEqual (new Rectangle (26, 0, 30, 100), child2.Bounds); + } + + [Test] + public void TestExpandVertical () + { + View child1, child2, child3; + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Orientation = StackOrientation.Vertical, + Children = { + (child1 = new View {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}), + (child2 = new View { + WidthRequest = 20, + HeightRequest = 20, + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.FillAndExpand + }), + (child3 = new View {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}) + } + }; + + stack.Padding = new Thickness (10, 5); + stack.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (10, 5, 80, 20), child1.Bounds); + Assert.AreEqual (new Rectangle (10, 31, 80, 100 - 2 * 31), child2.Bounds); + Assert.AreEqual (new Rectangle (10, 75, 80, 20), child3.Bounds); + } + + [Test] + public void TestExpandHorizontal() { + View child1, child2, child3; + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Orientation = StackOrientation.Horizontal, + Children = { + (child1 = new View {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}), + (child2 = new View { + WidthRequest = 20, + HeightRequest = 20, + IsPlatformEnabled = true, + HorizontalOptions = LayoutOptions.FillAndExpand + }), + (child3 = new View {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}) + } + }; + + stack.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.AreEqual (new Rectangle (0, 0, 20, 100), child1.Bounds); + Assert.AreEqual (new Rectangle (26, 0, 100 - 2 * 26, 100), child2.Bounds); + Assert.AreEqual (new Rectangle (80, 0, 20, 100), child3.Bounds); + } + + [Test] + public void TestSizeRequestVertical () + { + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Children = { + new View {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}, + new View {WidthRequest = 30, HeightRequest = 30, IsPlatformEnabled = true} + } + }; + + var size = stack.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request; + Assert.AreEqual (new Size (30, 56), size); + } + + [Test] + public void TestSizeRequestHorizontal () + { + var stack = new StackLayout { + Orientation = StackOrientation.Horizontal, + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Children = { + new View {WidthRequest = 20, HeightRequest = 20, IsPlatformEnabled = true}, + new View {WidthRequest = 30, HeightRequest = 30, IsPlatformEnabled = true} + } + }; + + var size = stack.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request; + Assert.AreEqual (new Size (56, 30), size); + } + + [Test] + public void HorizontalRequestInVerticalLayout () + { + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Children = { + new View {WidthRequest = 20, HeightRequest = 30, IsPlatformEnabled = true, HorizontalOptions = LayoutOptions.Start}, + new View { + WidthRequest = 20, + HeightRequest = 30, + IsPlatformEnabled = true, + HorizontalOptions = LayoutOptions.Center + }, + new View {WidthRequest = 20, HeightRequest = 30, IsPlatformEnabled = true, HorizontalOptions = LayoutOptions.End} + } + }; + + stack.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (0, 0, 20, 30), stack.Children[0].Bounds); + Assert.AreEqual (new Rectangle (90, 36, 20, 30), stack.Children[1].Bounds); + Assert.AreEqual (new Rectangle (180, 72, 20, 30), stack.Children[2].Bounds); + } + + [Test] + public void VerticalRequestInHorizontalLayout () + { + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Orientation = StackOrientation.Horizontal, + Children = { + new View {WidthRequest = 20, HeightRequest = 30, IsPlatformEnabled = true, VerticalOptions = LayoutOptions.Start}, + new View {WidthRequest = 20, HeightRequest = 30, IsPlatformEnabled = true, VerticalOptions = LayoutOptions.Center}, + new View {WidthRequest = 20, HeightRequest = 30, IsPlatformEnabled = true, VerticalOptions = LayoutOptions.End} + } + }; + + stack.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (0, 0, 20, 30), stack.Children.Cast<View>().ToArray()[0].Bounds); + Assert.AreEqual (new Rectangle (26, 85, 20, 30), stack.Children.Cast<View>().ToArray()[1].Bounds); + Assert.AreEqual (new Rectangle (52, 170, 20, 30), stack.Children.Cast<View>().ToArray()[2].Bounds); + } + + [Test] + public void RespectMinimumHeightRequest () + { + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Spacing = 10, + Children = { + new View {WidthRequest = 100, HeightRequest = 100, IsPlatformEnabled = true}, + new View {WidthRequest = 100, HeightRequest = 100, IsPlatformEnabled = true}, + new View {WidthRequest = 100, HeightRequest = 100, MinimumHeightRequest = 10, IsPlatformEnabled = true} + } + }; + + stack.Layout (new Rectangle (0, 0, 100, 250)); + + Assert.That (stack.Children.ToArray()[0].Bounds, Is.EqualTo (new Rectangle (0, 0, 100, 100))); + Assert.That (stack.Children.ToArray()[1].Bounds, Is.EqualTo (new Rectangle (0, 110, 100, 100))); + Assert.That (stack.Children.ToArray()[2].Bounds, Is.EqualTo (new Rectangle (0, 220, 100, 30))); + } + + [Test] + public void RespectMinimumWidthRequest () + { + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Orientation = StackOrientation.Horizontal, + Spacing = 10, + Children = { + new View {WidthRequest = 100, HeightRequest = 100, IsPlatformEnabled = true}, + new View {WidthRequest = 100, HeightRequest = 100, IsPlatformEnabled = true}, + new View {WidthRequest = 100, HeightRequest = 100, MinimumWidthRequest = 10, IsPlatformEnabled = true} + } + }; + + stack.Layout (new Rectangle (0, 0, 250, 100)); + + Assert.That (stack.Children.ToArray()[0].Bounds, Is.EqualTo (new Rectangle (0, 0, 100, 100))); + Assert.That (stack.Children.ToArray()[1].Bounds, Is.EqualTo (new Rectangle (110, 0, 100, 100))); + Assert.That (stack.Children.ToArray()[2].Bounds, Is.EqualTo (new Rectangle (220, 0, 30, 100))); + } + + [Test] + public void GetMinimumSizeVertical () + { + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Spacing = 10, + Children = { + new View {WidthRequest = 100, HeightRequest = 100, IsPlatformEnabled = true}, + new View {WidthRequest = 100, HeightRequest = 100, IsPlatformEnabled = true}, + new View {WidthRequest = 100, HeightRequest = 100, MinimumHeightRequest = 10, IsPlatformEnabled = true} + } + }; + + var result = stack.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + Assert.That (result.Minimum, Is.EqualTo (new Size (100, 230))); + } + + [Test] + public void GetMinimumSizeHorizontal () + { + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Spacing = 10, + Orientation = StackOrientation.Horizontal, + Children = { + new View {WidthRequest = 100, HeightRequest = 100, IsPlatformEnabled = true}, + new View {WidthRequest = 100, HeightRequest = 100, IsPlatformEnabled = true}, + new View {WidthRequest = 100, HeightRequest = 100, MinimumWidthRequest = 10, IsPlatformEnabled = true} + } + }; + + var result = stack.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + Assert.That (result.Minimum, Is.EqualTo (new Size (230, 100))); + } + + [Test] + public void TestVisibility () + { + View child1, child2; + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Children = { + (child1 = new View {IsPlatformEnabled = true}), + (child2 = new View {IsPlatformEnabled = true}) + } + }; + + stack.Layout (new Rectangle (0, 0, 100, 100)); + + var size = stack.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request; + Assert.AreEqual (new Rectangle (0, 0, 100, 20), child1.Bounds); + Assert.AreEqual (new Rectangle (0, 26, 100, 20), child2.Bounds); + Assert.AreEqual (new Size (100, 46), size); + + child1.IsVisible = false; + Assert.False (child1.IsVisible); + Assert.AreEqual (new Rectangle (0, 0, 100, 20), child2.Bounds); + size = stack.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request; + Assert.AreEqual (new Size (100, 20), size); + + } + + [Test] + public void OffOrientationMinimumSize () + { + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Spacing = 0 + }; + + stack.Children.Add (new View { + IsPlatformEnabled = true, + WidthRequest = 100, + HeightRequest = 100, + MinimumWidthRequest = 10, + MinimumHeightRequest = 10 + }); + + var result = stack.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + Assert.AreEqual (new Size (10, 10), result.Minimum); + } + + [Test] + public void NestedMinimumSizeOverflow () + { + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Spacing = 0 + }; + + var hbox = new StackLayout { + IsPlatformEnabled = true, + Spacing = 0, + Orientation = StackOrientation.Horizontal + }; + + View child1, child2; + hbox.Children.Add (child1 = new View { + IsPlatformEnabled = true, + WidthRequest = 100, + HeightRequest = 100, + MinimumWidthRequest = 10, + MinimumHeightRequest = 10 + }); + + hbox.Children.Add (child2 = new View { + IsPlatformEnabled = true, + WidthRequest = 100, + HeightRequest = 100, + MinimumWidthRequest = 10, + MinimumHeightRequest = 10 + }); + + stack.Children.Add (hbox); + + stack.Layout (new Rectangle (0, 0, 70, 70)); + Assert.AreEqual (new Rectangle (0, 0, 70, 70), stack.Bounds); + Assert.AreEqual (new Rectangle (0, 0, 35, 70), child1.Bounds); + Assert.AreEqual (new Rectangle (35, 0, 35, 70), child2.Bounds); + } + + [TestCase (StackOrientation.Vertical, LayoutAlignment.Start, false, 0, 0, 200, 100, 0, 100, 200, 10)] + [TestCase (StackOrientation.Vertical, LayoutAlignment.Start, true, 0, 0, 200, 100, 0, 190, 200, 10)] + [TestCase (StackOrientation.Vertical, LayoutAlignment.Center, false, 0, 0, 200, 100, 0, 100, 200, 10)] + [TestCase (StackOrientation.Vertical, LayoutAlignment.Center, true, 0, 45, 200, 100, 0, 190, 200, 10)] + [TestCase (StackOrientation.Vertical, LayoutAlignment.End, false, 0, 0, 200, 100, 0, 100, 200, 10)] + [TestCase (StackOrientation.Vertical, LayoutAlignment.End, true, 0, 90, 200, 100, 0, 190, 200, 10)] + [TestCase (StackOrientation.Horizontal, LayoutAlignment.Start, false, 0, 0, 100, 200, 100, 0, 10, 200)] + [TestCase (StackOrientation.Horizontal, LayoutAlignment.Start, true, 0, 0, 100, 200, 190, 0, 10, 200)] + [TestCase (StackOrientation.Horizontal, LayoutAlignment.Center, false, 0, 0, 100, 200, 100, 0, 10, 200)] + [TestCase (StackOrientation.Horizontal, LayoutAlignment.Center, true, 45, 0, 100, 200, 190, 0, 10, 200)] + [TestCase (StackOrientation.Horizontal, LayoutAlignment.End, false, 0, 0, 100, 200, 100, 0, 10, 200)] + [TestCase (StackOrientation.Horizontal, LayoutAlignment.End, true, 90, 0, 100, 200, 190, 0, 10, 200)] + public void LayoutExpansion(StackOrientation orientation, LayoutAlignment align, bool expand, double x1, double y1, double w1, double h1, double x2, double y2, double w2, double h2) + { + var options = new LayoutOptions (align, expand); + var stack = new StackLayout { + Platform = new UnitPlatform (), + IsPlatformEnabled = true, + Spacing = 0, + Orientation = orientation + }; + + View child1, child2; + stack.Children.Add (child1 = new View { + IsPlatformEnabled = true, + WidthRequest = 100, + HeightRequest = 100 + }); + + if (orientation == StackOrientation.Vertical) + child1.VerticalOptions = options; + else + child1.HorizontalOptions = options; + + stack.Children.Add (child2 = new View { + IsPlatformEnabled = true, + WidthRequest = 10, + HeightRequest = 10 + }); + + stack.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (x1, y1, w1, h1), child1.Bounds); + Assert.AreEqual (new Rectangle (x2, y2, w2, h2), child2.Bounds); + } + + [Test] + public void RelayoutOnRemove () + { + var child1 = new BoxView { + IsPlatformEnabled = true, + }; + + var child2 = new BoxView { + IsPlatformEnabled = true, + }; + + var stack = new StackLayout { + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + Children = {child1, child2} + }; + + stack.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (new Rectangle (0, 0, 200, 40), child1.Bounds); + Assert.AreEqual (new Rectangle (0, 46, 200, 40), child2.Bounds); + + stack.Children.RemoveAt (0); + + Assert.AreEqual (new Rectangle (0, 0, 200, 40), child2.Bounds); + } + + [Test] + public void FixedVerticalStackFixesExpander () + { + var child1 = new BoxView { + IsPlatformEnabled = true, + }; + + var child2 = new BoxView { + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.FillAndExpand + }; + + var stack = new StackLayout { + ComputedConstraint = LayoutConstraint.Fixed, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + Children = {child1, child2} + }; + + stack.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (LayoutConstraint.Fixed, child2.Constraint); + } + + [Test] + public void HFixedVerticalStackFixesExpander () + { + var child1 = new BoxView { + IsPlatformEnabled = true, + }; + + var child2 = new BoxView { + IsPlatformEnabled = true, + VerticalOptions = LayoutOptions.FillAndExpand + }; + + var stack = new StackLayout { + ComputedConstraint = LayoutConstraint.HorizontallyFixed, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + Children = {child1, child2} + }; + + stack.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (LayoutConstraint.HorizontallyFixed, child2.Constraint); + } + + [Test] + public void FixedHorizontalStackFixesExpander () + { + var child1 = new BoxView { + IsPlatformEnabled = true, + }; + + var child2 = new BoxView { + IsPlatformEnabled = true, + HorizontalOptions = LayoutOptions.FillAndExpand + }; + + var stack = new StackLayout { + ComputedConstraint = LayoutConstraint.Fixed, + Orientation = StackOrientation.Horizontal, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + Children = {child1, child2} + }; + + stack.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (LayoutConstraint.Fixed, child2.Constraint); + } + + [Test] + public void HFixedHorizontalStackFixesExpander () + { + var child1 = new BoxView { + IsPlatformEnabled = true, + }; + + var child2 = new BoxView { + IsPlatformEnabled = true, + HorizontalOptions = LayoutOptions.FillAndExpand + }; + + var stack = new StackLayout { + ComputedConstraint = LayoutConstraint.VerticallyFixed, + Orientation = StackOrientation.Horizontal, + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + Children = {child1, child2} + }; + + stack.Layout (new Rectangle (0, 0, 200, 200)); + + Assert.AreEqual (LayoutConstraint.VerticallyFixed, child2.Constraint); + } + + [Ignore ("This test intended to test bz38416 however I just for the life of me cant figure it out in simplified form. I am failure.")] + [Test] + public void TheWTFTest () + { + var child1 = new BoxView { + IsPlatformEnabled = true, + WidthRequest = 20, + HeightRequest = 20, + MinimumWidthRequest = 10, + MinimumHeightRequest = 10, + VerticalOptions = LayoutOptions.FillAndExpand + }; + + var stack = new StackLayout { + IsPlatformEnabled = true, + Platform = new UnitPlatform (), + Children = {child1} + }; + + stack.Layout (new Rectangle (0, 0, 100, 100)); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/StepperUnitTests.cs b/Xamarin.Forms.Core.UnitTests/StepperUnitTests.cs new file mode 100644 index 00000000..d011c7ab --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/StepperUnitTests.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class StepperUnitTests : BaseTestFixture + { + [Test] + public void TestConstructor () + { + var stepper = new Stepper (120, 200, 150, 2); + + Assert.AreEqual (120, stepper.Minimum); + Assert.AreEqual (200, stepper.Maximum); + Assert.AreEqual (150, stepper.Value); + Assert.AreEqual (2, stepper.Increment); + } + + [Test] + public void TestInvalidConstructor () + { + Assert.Throws<ArgumentOutOfRangeException>(()=>new Stepper (100, 0, 50, 1)); + } + + [Test] + public void TestInvalidMaxValue () + { + Stepper stepper = new Stepper (); + Assert.Throws<ArgumentException> (() => stepper.Maximum = stepper.Minimum - 1); + } + + [Test] + public void TestInvalidMinValue () + { + Stepper stepper = new Stepper (); + Assert.Throws<ArgumentException> (() => stepper.Minimum = stepper.Maximum + 1); + } + + [Test] + public void TestValidMaxValue () + { + Stepper stepper = new Stepper (); + + stepper.Maximum = 2000; + + Assert.AreEqual (2000, stepper.Maximum); + } + + [Test] + public void TestValidMinValue () + { + Stepper stepper = new Stepper (); + + stepper.Maximum = 2000; + stepper.Minimum = 200; + + Assert.AreEqual (200, stepper.Minimum); + } + + [Test] + public void TestConstructorClampValue () + { + Stepper stepper = new Stepper (0, 100, 2000, 1); + + Assert.AreEqual (100, stepper.Value); + + stepper = new Stepper (0, 100, -200, 1); + + Assert.AreEqual (0, stepper.Value); + } + + [Test] + public void TestMinClampValue () + { + Stepper stepper = new Stepper (); + + bool minThrown = false; + bool valThrown = false; + + stepper.PropertyChanged += (sender, e) => { + switch (e.PropertyName) { + case "Minimum": + minThrown = true; + break; + case "Value": + Assert.False (minThrown); + valThrown = true; + break; + } + }; + + stepper.Minimum = 10; + + Assert.AreEqual (10, stepper.Minimum); + Assert.AreEqual (10, stepper.Value); + Assert.True (minThrown); + Assert.True (valThrown); + } + + [Test] + public void TestMaxClampValue () + { + Stepper stepper = new Stepper (); + + stepper.Value = 50; + + bool maxThrown = false; + bool valThrown = false; + + stepper.PropertyChanged += (sender, e) => { + switch (e.PropertyName) { + case "Maximum": + maxThrown = true; + break; + case "Value": + Assert.False (maxThrown); + valThrown = true; + break; + } + }; + + stepper.Maximum = 25; + + Assert.AreEqual (25, stepper.Maximum); + Assert.AreEqual (25, stepper.Value); + Assert.True (maxThrown); + Assert.True (valThrown); + } + + [Test] + public void TestValueChangedEvent () + { + var stepper = new Stepper (); + + bool fired = false; + stepper.ValueChanged += (sender, arg) => fired = true; + + stepper.Value = 50; + + Assert.True (fired); + } + + [TestCase (100.0, 0.5)] + [TestCase (10.0, 25.0)] + [TestCase (0, 39.5)] + public void StepperValueChangedEventArgs (double initialValue, double finalValue) + { + var stepper = new Stepper { + Maximum = 100, + Minimum = 0, + Increment = 0.5, + Value = initialValue + }; + + Stepper stepperFromSender = null; + var oldValue = 0.0; + var newValue = 0.0; + + stepper.ValueChanged += (s, e) => { + stepperFromSender = (Stepper)s; + oldValue = e.OldValue; + newValue = e.NewValue; + }; + + stepper.Value = finalValue; + + Assert.AreEqual (stepper, stepperFromSender); + Assert.AreEqual (initialValue, oldValue); + Assert.AreEqual (finalValue, newValue); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/StyleTests.cs b/Xamarin.Forms.Core.UnitTests/StyleTests.cs new file mode 100644 index 00000000..be1c7990 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/StyleTests.cs @@ -0,0 +1,670 @@ +using NUnit.Framework; +using System; +using System.Threading.Tasks; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class StyleTests : BaseTestFixture + { + [Test] + public void ApplyUnapplyStyle () + { + var style = new Style (typeof(VisualElement)) { + Setters = { + new Setter { Property = Label.TextProperty, Value = "foo" }, + new Setter { Property = VisualElement.BackgroundColorProperty, Value = Color.Pink }, + } + }; + + var label = new Label { + Style = style + }; + Assert.AreEqual ("foo", label.Text); + Assert.AreEqual (Color.Pink, label.BackgroundColor); + + label.Style = null; + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + Assert.AreEqual (VisualElement.BackgroundColorProperty.DefaultValue, label.BackgroundColor); + } + + [Test] + public void BindingAndDynamicResourcesInStyle () + { + var style = new Style (typeof(VisualElement)) { + Setters = { + new Setter { Property = VisualElement.IsEnabledProperty, Value = false } + } + }; + style.Setters.AddBinding (Label.TextProperty, new Binding ("foo")); + style.Setters.AddDynamicResource (VisualElement.BackgroundColorProperty, "qux"); + + var label = new Label { + Style = style + }; + + label.BindingContext = new {foo = "FOO"}; + Assert.AreEqual ("FOO", label.Text); + + label.Resources = new ResourceDictionary { + {"qux", Color.Pink} + }; + Assert.AreEqual (Color.Pink, label.BackgroundColor); + + label.Style = null; + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + Assert.AreEqual (VisualElement.BackgroundColorProperty.DefaultValue, label.BackgroundColor); + } + + [Test] + public void StyleCanBeAppliedMultipleTimes () + { + var style = new Style (typeof(VisualElement)) { + Setters = { + new Setter{ Property = VisualElement.IsEnabledProperty, Value = false } + } + }; + style.Setters.AddBinding (Label.TextProperty, new Binding ("foo")); + style.Setters.AddDynamicResource (VisualElement.BackgroundColorProperty, "qux"); + + var label0 = new Label { + Style = style + }; + var label1 = new Label { + Style = style + }; + + label0.BindingContext = label1.BindingContext = new {foo = "FOO"}; + label0.Resources = label1.Resources = new ResourceDictionary { + {"qux", Color.Pink} + }; + + Assert.AreEqual ("FOO", label0.Text); + Assert.AreEqual ("FOO", label1.Text); + + Assert.AreEqual (Color.Pink, label0.BackgroundColor); + Assert.AreEqual (Color.Pink, label1.BackgroundColor); + + label0.Style = label1.Style = null; + + Assert.AreEqual (Label.TextProperty.DefaultValue, label0.Text); + Assert.AreEqual (Label.TextProperty.DefaultValue, label1.Text); + Assert.AreEqual (VisualElement.BackgroundColorProperty.DefaultValue, label0.BackgroundColor); + Assert.AreEqual (VisualElement.BackgroundColorProperty.DefaultValue, label1.BackgroundColor); + } + + [Test] + public void BaseStyleIsAppliedUnapplied () + { + var baseStyle = new Style (typeof(VisualElement)) { + Setters = { + new Setter { Property = Label.TextProperty, Value = "baseStyle" }, + } + }; + var style = new Style(typeof(Label)) { + BasedOn = baseStyle, + }; + + var label = new Label { + Style = style + }; + Assert.AreEqual ("baseStyle", label.Text); + + label.Style = null; + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + } + + [Test] + public void StyleOverrideBaseStyle () + { + var baseStyle = new Style(typeof(VisualElement)) { + Setters = { + new Setter{Property= Label.TextProperty,Value= "baseStyle" }, + } + }; + var style = new Style(typeof(VisualElement)) { + BasedOn = baseStyle, + Setters = { + new Setter{Property= Label.TextProperty,Value= "style" }, + } + }; + + var label = new Label { + Style = style + }; + Assert.AreEqual ("style", label.Text); + + label.Style = null; + Assert.AreEqual (Label.TextProperty.DefaultValue, label.Text); + } + + [Test] + public void AddImplicitStyleToResourceDictionary () + { + var rd = new ResourceDictionary { + new Style (typeof(Label)) { Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Pink }, + } + }, + { "foo", "FOO" }, + {"labelStyle", new Style (typeof(Label)) { Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Purple } + } + } + } + }; + + Assert.AreEqual (3, rd.Count); + Assert.Contains ("Xamarin.Forms.Label", (System.Collections.ICollection)rd.Keys); + } + + [Test] + public void ImplicitStylesAreAppliedOnSettingRD () + { + var rd = new ResourceDictionary { + new Style (typeof(Label)) { Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Pink }, + } + }, + { "foo", "FOO" }, + {"labelStyle", new Style (typeof(Label)) { Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Purple } + } + } + } + }; + + var label = new Label (); + var layout = new StackLayout { Children = { label } }; + + Assert.AreEqual (label.TextColor, Label.TextColorProperty.DefaultValue); + layout.Resources = rd; + Assert.AreEqual (label.TextColor, Color.Pink); + } + + [Test] + public void ImplicitStylesAreAppliedOnSettingParrent () + { + var rd = new ResourceDictionary { + new Style (typeof(Label)) { Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Pink }, + } + }, + { "foo", "FOO" }, + {"labelStyle", new Style (typeof(Label)) { Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Purple } + } + } + } + }; + + var label = new Label (); + var layout = new StackLayout (); + layout.Resources = rd; + + Assert.AreEqual (label.TextColor, Label.TextColorProperty.DefaultValue); + layout.Children.Add (label); + Assert.AreEqual (label.TextColor, Color.Pink); + } + + [Test] + public void ImplicitStylesOverridenByStyle () + { + var rd = new ResourceDictionary { + new Style (typeof(Label)) { Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Pink }, + } + }, + { "foo", "FOO" }, + {"labelStyle", new Style (typeof(Label)) { Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Purple } + } + } + } + }; + + var label = new Label (); + label.SetDynamicResource (VisualElement.StyleProperty, "labelStyle"); + var layout = new StackLayout { Children = { label }, Resources = rd }; + + Assert.AreEqual (label.TextColor, Color.Purple); + } + + [Test] + public void UnsettingStyleReApplyImplicit () + { + var rd = new ResourceDictionary { + new Style (typeof(Label)) { Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Pink }, + } + }, + { "foo", "FOO" }, + {"labelStyle", new Style (typeof(Label)) { Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Purple } + } + } + } + }; + + var label = new Label (); + label.SetDynamicResource (VisualElement.StyleProperty, "labelStyle"); + var layout = new StackLayout { Children = { label }, Resources = rd }; + + Assert.AreEqual (label.TextColor, Color.Purple); + label.Style = null; + Assert.AreEqual (label.TextColor, Color.Pink); + } + + [Test] + public void DynamicStyle () + { + var baseStyle0 = new Style(typeof(Label)) { + Setters = { + new Setter {Property = Label.TextProperty, Value = "foo"}, + new Setter {Property = Label.TextColorProperty, Value = Color.Pink} + } + }; + var baseStyle1 = new Style(typeof(Label)) { + Setters = { + new Setter {Property = Label.TextProperty, Value = "bar"}, + new Setter {Property = Label.TextColorProperty, Value = Color.Purple} + } + }; + var style = new Style (typeof(Label)) { + BaseResourceKey = "basestyle", + Setters = { + new Setter { Property = Label.BackgroundColorProperty, Value = Color.Red }, + new Setter { Property = Label.TextColorProperty, Value = Color.Red }, + } + }; + + var label0 = new Label { + Style = style + }; + + Assert.AreEqual (Color.Red, label0.BackgroundColor); + Assert.AreEqual (Color.Red, label0.TextColor); + Assert.AreEqual (Label.TextProperty.DefaultValue, label0.Text); + + var layout0 = new StackLayout { + Resources = new ResourceDictionary { + {"basestyle", baseStyle0} + }, + Children = { + label0 + } + }; + + Assert.AreEqual (Color.Red, label0.BackgroundColor); + Assert.AreEqual (Color.Red, label0.TextColor); + Assert.AreEqual ("foo", label0.Text); + + var label1 = new Label { + Style = style + }; + + Assert.AreEqual (Color.Red, label1.BackgroundColor); + Assert.AreEqual (Color.Red, label1.TextColor); + Assert.AreEqual (Label.TextProperty.DefaultValue, label1.Text); + + var layout1 = new StackLayout { + Children = { + label1 + } + }; + layout1.Resources = new ResourceDictionary { + {"basestyle", baseStyle1} + }; + + Assert.AreEqual (Color.Red, label1.BackgroundColor); + Assert.AreEqual (Color.Red, label1.TextColor); + Assert.AreEqual ("bar", label1.Text); + } + + [Test] + public void TestTriggersAndBehaviors () + { + var behavior = new MockBehavior<Entry> (); + var style = new Style (typeof(Entry)) { + Setters = { + new Setter {Property = Entry.TextProperty, Value = "foo"}, + }, + Triggers = { + new Trigger (typeof (VisualElement)) {Property = Entry.IsPasswordProperty, Value=true, Setters = { + new Setter {Property = VisualElement.ScaleProperty, Value = 2d}, + }} + }, + Behaviors = { + behavior, + } + }; + + var entry = new Entry { Style = style }; + Assert.AreEqual ("foo", entry.Text); + Assert.AreEqual (1d, entry.Scale); + + entry.IsPassword = true; + Assert.AreEqual (2d, entry.Scale); + + Assert.True (behavior.attached); + + entry.Style = null; + + Assert.AreEqual (Entry.TextProperty.DefaultValue, entry.Text); + Assert.True (entry.IsPassword); + Assert.AreEqual (1d, entry.Scale); + Assert.True (behavior.detached); + } + + [Test] + //Issue #2124 + public void SetValueOverridesStyle () + { + var style = new Style (typeof(Label)) { + Setters = { + new Setter {Property = Label.TextColorProperty, Value=Color.Black}, + } + }; + + var label = new Label { TextColor = Color.White, Style = style }; + Assert.AreEqual (Color.White, label.TextColor); + } + + [Test] + //https://bugzilla.xamarin.com/show_bug.cgi?id=28556 + public void TriggersAppliedAfterSetters () + { + var style = new Style (typeof(Entry)) { + Setters = { + new Setter { Property = Entry.TextColorProperty, Value = Color.Yellow } + }, + Triggers = { + new Trigger (typeof(Entry)) { + Property = VisualElement.IsEnabledProperty, + Value = false, + Setters = { + new Setter { Property = Entry.TextColorProperty, Value = Color.Red } + }, + } + }, + }; + + var entry = new Entry { IsEnabled = false, Style = style }; + Assert.AreEqual (Color.Red, entry.TextColor); + entry.IsEnabled = true; + Assert.AreEqual (Color.Yellow, entry.TextColor); + } + + [Test] + //https://bugzilla.xamarin.com/show_bug.cgi?id=31207 + public async void StyleDontHoldStrongReferences () + { + var style = new Style (typeof(Label)); + var label = new Label (); + var tracker = new WeakReference (label); + label.Style = style; + label = null; + + await Task.Delay (10); + GC.Collect (); + + Assert.False (tracker.IsAlive); + Assert.NotNull (style); + } + + class MyLabel : Label + { + } + + class MyButton : Button + { + } + + [Test] + public void ImplicitStylesNotAppliedToDerivedTypesByDefault () + { + var style = new Style (typeof (Label)) { + Setters = { + new Setter { Property = Label.TextProperty, Value = "Foo" } + } + }; + var view = new ContentView { + Resources = new ResourceDictionary { style }, + Content = new MyLabel (), + }; + + Assert.AreEqual (Label.TextProperty.DefaultValue, ((MyLabel)view.Content).Text); + } + + [Test] + public void ImplicitStylesAreAppliedToDerivedIfSpecified () + { + var style = new Style (typeof (Label)) { + Setters = { + new Setter { Property = Label.TextProperty, Value = "Foo" } + }, + ApplyToDerivedTypes = true + }; + var view = new ContentView { + Resources = new ResourceDictionary { style }, + Content = new MyLabel (), + }; + + Assert.AreEqual ("Foo", ((MyLabel)view.Content).Text); + } + + [Test] + public void ClassStylesAreApplied () + { + var classstyle = new Style (typeof (Label)) { + Setters = { + new Setter { Property = Label.TextProperty, Value = "Foo" } + }, + Class = "fooClass", + }; + var style = new Style (typeof (Label)) { + Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Red } + }, + }; + var view = new ContentView { + Resources = new ResourceDictionary { classstyle }, + Content = new Label { + StyleClass = "fooClass", + Style = style + } + }; + Assert.AreEqual ("Foo", ((Label)view.Content).Text); + Assert.AreEqual (Color.Red, ((Label)view.Content).TextColor); + } + + [Test] + public void ImplicitStylesNotAppliedByDefaultIfAStyleExists () + { + var implicitstyle = new Style (typeof (Label)) { + Setters = { + new Setter { Property = Label.TextProperty, Value = "Foo" } + }, + }; + var style = new Style (typeof (Label)) { + Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Red } + }, + }; + var view = new ContentView { + Resources = new ResourceDictionary { implicitstyle }, + Content = new Label { + Style = style + } + }; + Assert.AreEqual (Label.TextProperty.DefaultValue, ((Label)view.Content).Text); + Assert.AreEqual (Color.Red, ((Label)view.Content).TextColor); + } + + [Test] + public void ImplicitStylesAppliedIfStyleCanCascade () + { + var implicitstyle = new Style (typeof (Label)) { + Setters = { + new Setter { Property = Label.TextProperty, Value = "Foo" } + }, + }; + var style = new Style (typeof (Label)) { + Setters = { + new Setter { Property = Label.TextColorProperty, Value = Color.Red }, + }, + CanCascade = true + }; + var view = new ContentView { + Resources = new ResourceDictionary { implicitstyle }, + Content = new Label { + Style = style + } + }; + Assert.AreEqual ("Foo", ((Label)view.Content).Text); + Assert.AreEqual (Color.Red, ((Label)view.Content).TextColor); + } + + [Test] + public void MultipleStylesCanShareTheSameClassName () + { + var buttonStyle = new Style (typeof(Button)) { + Setters = { + new Setter { Property = Button.TextColorProperty, Value = Color.Pink }, + }, + Class = "pink", + ApplyToDerivedTypes = true, + }; + var labelStyle = new Style (typeof(Label)) { + Setters = { + new Setter { Property = Button.BackgroundColorProperty, Value = Color.Pink }, + }, + Class = "pink", + ApplyToDerivedTypes = false, + }; + + + var button = new Button { + StyleClass = "pink", + }; + var myButton = new MyButton { + StyleClass = "pink", + }; + + var label = new Label { + StyleClass = "pink" + }; + var myLabel = new MyLabel { + StyleClass = "pink" + }; + + + new StackLayout { + Resources = new ResourceDictionary {buttonStyle, labelStyle}, + Children = { + button, + label, + myLabel, + myButton, + } + }; + + Assert.AreEqual (Color.Pink, button.TextColor); + Assert.AreEqual (Color.Default, button.BackgroundColor); + + Assert.AreEqual (Color.Pink, myButton.TextColor); + Assert.AreEqual (Color.Default, myButton.BackgroundColor); + + Assert.AreEqual (Color.Pink, label.BackgroundColor); + Assert.AreEqual (Color.Default, label.TextColor); + + Assert.AreEqual (Color.Default, myLabel.BackgroundColor); + Assert.AreEqual (Color.Default, myLabel.TextColor); + } + + [Test] + public void StyleClassAreCorrecltyMerged () + { + var buttonStyle = new Style (typeof(Button)) { + Setters = { + new Setter { Property = Button.TextColorProperty, Value = Color.Pink }, + }, + Class = "pink", + ApplyToDerivedTypes = true, + }; + var labelStyle = new Style (typeof(Label)) { + Setters = { + new Setter { Property = Button.BackgroundColorProperty, Value = Color.Pink }, + }, + Class = "pink", + ApplyToDerivedTypes = false, + }; + + var button = new Button { + StyleClass = "pink", + }; + var label = new Label { + StyleClass = "pink" + }; + + var cv = new ContentView { + Resources = new ResourceDictionary {buttonStyle}, + Content = new StackLayout { + Resources = new ResourceDictionary {labelStyle}, + Children = { + button, + label, + } + } + }; + + Assert.AreEqual (Color.Pink, button.TextColor); + Assert.AreEqual (Color.Default, button.BackgroundColor); + + Assert.AreEqual (Color.Pink, label.BackgroundColor); + Assert.AreEqual (Color.Default, label.TextColor); + } + + [Test] + public void StyleClassAreCorrecltyMergedForAlreadyParentedPArents () + { + var buttonStyle = new Style (typeof (Button)) { + Setters = { + new Setter { Property = Button.TextColorProperty, Value = Color.Pink }, + }, + Class = "pink", + ApplyToDerivedTypes = true, + }; + var labelStyle = new Style (typeof (Label)) { + Setters = { + new Setter { Property = Button.BackgroundColorProperty, Value = Color.Pink }, + }, + Class = "pink", + ApplyToDerivedTypes = false, + }; + + var button = new Button { + StyleClass = "pink", + }; + var label = new Label { + StyleClass = "pink" + }; + + var cv = new ContentView { + Resources = new ResourceDictionary { buttonStyle }, + Content = new StackLayout { + Resources = new ResourceDictionary { labelStyle }, + } + }; + + (cv.Content as StackLayout).Children.Add (button); + (cv.Content as StackLayout).Children.Add (label); + + Assert.AreEqual (Color.Pink, button.TextColor); + Assert.AreEqual (Color.Default, button.BackgroundColor); + + Assert.AreEqual (Color.Pink, label.BackgroundColor); + Assert.AreEqual (Color.Default, label.TextColor); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/SwitchCellTests.cs b/Xamarin.Forms.Core.UnitTests/SwitchCellTests.cs new file mode 100644 index 00000000..16fee76b --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/SwitchCellTests.cs @@ -0,0 +1,62 @@ +using System; +using NUnit.Framework; +using NUnit.Framework.Constraints; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class SwitchCellTemplateTests : BaseTestFixture + { + [Test] + public void Create() + { + var template = new DataTemplate (typeof(SwitchCell)); + var content = template.CreateContent(); + + Assert.That (content, Is.InstanceOf<SwitchCell>()); + } + + [Test] + public void Text() + { + var template = new DataTemplate (typeof (SwitchCell)); + template.SetValue (SwitchCell.TextProperty, "text"); + + SwitchCell cell = (SwitchCell)template.CreateContent(); + Assert.That (cell.Text, Is.EqualTo ("text")); + } + + [Test] + public void On() + { + var template = new DataTemplate (typeof (SwitchCell)); + template.SetValue (SwitchCell.OnProperty, true); + + SwitchCell cell = (SwitchCell)template.CreateContent(); + Assert.That (cell.On, Is.EqualTo (true)); + } + + [TestCase (false, true)] + [TestCase (true, false)] + public void SwitchCellSwitchChangedArgs (bool initialValue, bool finalValue) + { + var template = new DataTemplate (typeof (SwitchCell)); + SwitchCell cell = (SwitchCell)template.CreateContent (); + + SwitchCell switchCellFromSender = null; + bool newSwitchValue = false; + + cell.On = initialValue; + + cell.OnChanged += (s, e) => { + switchCellFromSender = (SwitchCell)s; + newSwitchValue = e.Value; + }; + + cell.On = finalValue; + + Assert.AreEqual (cell, switchCellFromSender); + Assert.AreEqual (finalValue, newSwitchValue); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/SwitchUnitTests.cs b/Xamarin.Forms.Core.UnitTests/SwitchUnitTests.cs new file mode 100644 index 00000000..9748c3e9 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/SwitchUnitTests.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class SwitchUnitTests : BaseTestFixture + { + [Test] + public void TestConstructor () + { + Switch sw = new Switch (); + + Assert.IsFalse (sw.IsToggled); + } + + [Test] + public void TestOnEvent () + { + Switch sw = new Switch (); + + bool fired = false; + sw.Toggled += (sender, e) => fired = true; + + sw.IsToggled = true; + + Assert.IsTrue (fired); + } + + [Test] + public void TestOnEventNotDoubleFired () + { + var sw = new Switch (); + + bool fired = false; + sw.IsToggled = true; + + sw.Toggled += (sender, args) => fired = true; + sw.IsToggled = true; + + Assert.IsFalse (fired); + } + } + +} diff --git a/Xamarin.Forms.Core.UnitTests/TabbedFormUnitTests.cs b/Xamarin.Forms.Core.UnitTests/TabbedFormUnitTests.cs new file mode 100644 index 00000000..608ad393 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TabbedFormUnitTests.cs @@ -0,0 +1,32 @@ +using System.Linq; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TabbedPageTests : MultiPageTests<Page> + { + protected override MultiPage<Page> CreateMultiPage() + { + return new TabbedPage(); + } + + protected override Page CreateContainedPage() + { + return new ContentPage { Content = new View() }; + } + + protected override int GetIndex (Page page) + { + return TabbedPage.GetIndex (page); + } + + [Test] + public void TestConstructor () + { + TabbedPage page = new TabbedPage (); + + Assert.That (page.Children, Is.Empty); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/TableModelTests.cs b/Xamarin.Forms.Core.UnitTests/TableModelTests.cs new file mode 100644 index 00000000..9173d387 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TableModelTests.cs @@ -0,0 +1,87 @@ +using System; +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TableModelTests : BaseTestFixture + { + class TestModel : TableModel + { + public override int GetRowCount (int section) + { + return 10; + } + + public override int GetSectionCount () + { + return 1; + } + + public override object GetItem (int section, int row) + { + return "Foo"; + } + + public string ProtectedSectionTitle () + { + return GetSectionTitle (0); + } + } + + [Test] + public void DefaultSectionTitle () + { + Assert.IsNull (new TestModel ().ProtectedSectionTitle ()); + } + + [Test] + public void DefualtSectionIndexTitles () + { + Assert.IsNull (new TestModel ().GetSectionIndexTitles ()); + } + + [Test] + public void DefaultHeaderCell () + { + Assert.IsNull (new TestModel ().GetHeaderCell (0)); + } + + [Test] + public void DefaultCellFromObject () + { + var model = new TestModel (); + var cell = model.GetCell (0, 5); + + Assert.That (cell, Is.TypeOf<TextCell> ()); + + var textCell = (TextCell) cell; + Assert.AreEqual ("Foo", textCell.Text); + } + + [Test] + public void RowLongPressed () + { + var model = new TestModel (); + + var longPressedItem = ""; + model.ItemLongPressed += (sender, arg) => { + longPressedItem = (string)arg.Data; + }; + + model.RowLongPressed (0, 5); + } + + [Test] + public void RowSelectedForObject () + { + var model = new TestModel (); + string result = null; + model.ItemSelected += (sender, arg) => result = (string)arg.Data; + + model.RowSelected ("Foobar"); + Assert.AreEqual ("Foobar", result); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/TableRootUnitTests.cs b/Xamarin.Forms.Core.UnitTests/TableRootUnitTests.cs new file mode 100644 index 00000000..0c9d42fe --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TableRootUnitTests.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TableRootUnitTests : BaseTestFixture + { + [Test] + public void Ctor() + { + const string title = "FooBar"; + var model = new TableRoot (title); + Assert.AreEqual (title, model.Title); + } + + [Test] + public void CtorInvalid() + { + Assert.Throws<ArgumentNullException> (() => new TableRoot (null)); + } + + [Test] + public void TestGetSections () + { + var model = new TableRoot ("Name") { + new TableSection ("Section 1") { + new TextCell { Text = "Item 1.1", Detail = "Hint 1"}, + new TextCell { Text = "Item 1.2", Detail = "Hint 2"}, + new TextCell { Text = "Item 1.3", Detail = "Hint 3"} + }, + new TableSection ("Section 2") { + new TextCell { Text = "Item 2.1", Detail = "Hint 1"}, + new TextCell { Text = "Item 2.2", Detail = "Hint 2"}, + new TextCell { Text = "Item 2.3", Detail = "Hint 3"} + } + }; + + Assert.AreEqual (2, model.Count); + } + + [Test] + public void TestCollectionChanged () + { + var model = new TableRoot (); + + bool changed = false; + model.CollectionChanged += (sender, e) => changed = true; + + model.Add (new TableSection ("Foo")); + + Assert.True (changed); + + changed = false; + + model [0].Add (new TextCell { Text = "Foobar" }); + + // Our tree is not supposed to track up like this + Assert.False (changed); + } + + [Test] + public void TestTree () + { + var model = new TableRoot ("Name") { + new TableSection ("Section 1") { + new TextCell { Text = "Item 1.1", Detail = "Hint 1"}, + new TextCell { Text = "Item 1.2", Detail = "Hint 2"}, + new TextCell { Text = "Item 1.3", Detail = "Hint 3"} + }, + new TableSection { + new TextCell { Text = "Item 2.1", Detail = "Hint 1"}, + new TextCell { Text = "Item 2.2", Detail = "Hint 2"}, + new TextCell { Text = "Item 2.3", Detail = "Hint 3"} + } + }; + + Assert.AreEqual ("Item 1.1", (model[0][0] as TextCell).Text); + Assert.AreEqual ("Item 1.2", (model[0][1] as TextCell).Text); + Assert.AreEqual ("Item 1.3", (model[0][2] as TextCell).Text); + Assert.AreEqual ("Item 2.1", (model[1][0] as TextCell).Text); + Assert.AreEqual ("Item 2.2", (model[1][1] as TextCell).Text); + Assert.AreEqual ("Item 2.3", (model[1][2] as TextCell).Text); + } + + //[Test] + //public void TestAddFromEnumerable () + //{; + // TableSection first, second; + // var model = new TableRoot (); + // model.Add (new[] { + // first = new TableSection (), + // second = new TableSection () + // }); + + // Assert.AreEqual (first, model[0]); + // Assert.AreEqual (second, model[1]); + //} + } +} diff --git a/Xamarin.Forms.Core.UnitTests/TableSectionTests.cs b/Xamarin.Forms.Core.UnitTests/TableSectionTests.cs new file mode 100644 index 00000000..bcf405b5 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TableSectionTests.cs @@ -0,0 +1,225 @@ +using System.Collections.Generic; +using NUnit.Framework; + +using NContains = NUnit.Framework.Contains; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TableSectionTests : BaseTestFixture + { + [SetUp] + public void Setup() + { + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public void TearDown() + { + Device.PlatformServices = null; + } + + [Test] + public void Constructor () + { + var section = new TableSection ("Title"); + Assert.AreEqual ("Title", section.Title); + Assert.That (section, Is.Empty); + } + + [Test] + public void IsReadOnly () + { + var section = new TableSection () as ICollection<Cell>; + Assert.False (section.IsReadOnly); + } + + [Test] + public void Add () + { + var section = new TableSection (); + TextCell first, second; + section.Add (first = new TextCell { Text = "Text" }); + section.Add (second = new TextCell { Text = "Text" }); + + Assert.That (section, NContains.Item (first)); + Assert.That (section, NContains.Item (second)); + } + + [Test] + public void Remove () + { + var section = new TableSection (); + TextCell first; + section.Add (first = new TextCell { Text = "Text" }); + section.Add (new TextCell { Text = "Text" }); + + var result = section.Remove (first); + Assert.True (result); + Assert.That (section, Has.No.Contains (first)); + } + + [Test] + public void Clear () + { + var section = new TableSection {new TextCell { Text = "Text" }, new TextCell { Text = "Text" }}; + section.Clear (); + Assert.That (section, Is.Empty); + } + + [Test] + public void Contains () + { + var section = new TableSection (); + TextCell first, second; + section.Add (first = new TextCell { Text = "Text" }); + section.Add (second = new TextCell { Text = "Text" }); + + Assert.True (section.Contains (first)); + Assert.True (section.Contains (second)); + } + + [Test] + public void IndexOf () + { + var section = new TableSection (); + TextCell first, second; + section.Add (first = new TextCell { Text = "Text" }); + section.Add (second = new TextCell { Text = "Text" }); + + Assert.AreEqual (0, section.IndexOf (first)); + Assert.AreEqual (1, section.IndexOf (second)); + } + + [Test] + public void Insert () + { + var section = new TableSection (); + section.Add (new TextCell { Text = "Text" }); + section.Add (new TextCell { Text = "Text" }); + + var third = new TextCell { Text = "Text" }; + section.Insert (1, third); + Assert.AreEqual (third, section[1]); + } + + [Test] + public void RemoveAt () + { + var section = new TableSection (); + TextCell first, second; + section.Add (first = new TextCell { Text = "Text" }); + section.Add (second = new TextCell { Text = "Text" }); + + section.RemoveAt (0); + Assert.That (section, Has.No.Contains (first)); + } + + [Test] + public void Overwrite () + { + var section = new TableSection (); + TextCell second; + section.Add (new TextCell { Text = "Text" }); + section.Add (second = new TextCell { Text = "Text" }); + + var third = new TextCell { Text = "Text" }; + section[1] = third; + + Assert.AreEqual (third, section[1]); + Assert.That (section, Has.No.Contains (second)); + } + + [Test] + public void CopyTo () + { + var section = new TableSection (); + TextCell first, second; + section.Add (first = new TextCell { Text = "Text" }); + section.Add (second = new TextCell { Text = "Text" }); + + Cell[] cells = new Cell[2]; + section.CopyTo (cells, 0); + + Assert.AreEqual (first, cells[0]); + Assert.AreEqual (second, cells[1]); + } + + [Test] + public void ChainsBindingContextOnSet () + { + var section = new TableSection (); + TextCell first, second; + section.Add (first = new TextCell { Text = "Text" }); + section.Add (second = new TextCell { Text = "Text" }); + + var bindingContext = "bindingContext"; + + section.BindingContext = bindingContext; + + Assert.AreEqual (bindingContext, first.BindingContext); + Assert.AreEqual (bindingContext, second.BindingContext); + } + + [Test] + public void ChainsBindingContextWithExistingContext () + { + var section = new TableSection (); + TextCell first, second; + section.Add (first = new TextCell { Text = "Text" }); + section.Add (second = new TextCell { Text = "Text" }); + + var bindingContext = "bindingContext"; + section.BindingContext = bindingContext; + + bindingContext = "newContext"; + section.BindingContext = bindingContext; + + Assert.AreEqual (bindingContext, first.BindingContext); + Assert.AreEqual (bindingContext, second.BindingContext); + } + + [Test] + public void ChainsBindingContextToNewlyAdded () + { + var section = new TableSection (); + var bindingContext = "bindingContext"; + section.BindingContext = bindingContext; + + TextCell first, second; + section.Add (first = new TextCell { Text = "Text" }); + section.Add (second = new TextCell { Text = "Text" }); + + Assert.AreEqual (bindingContext, first.BindingContext); + Assert.AreEqual (bindingContext, second.BindingContext); + } + + [Test] + public void TestBindingTitleSectionChange () + { + var vm = new MockViewModel { Text = "FooBar" }; + var section = new TableSection (); + + section.BindingContext = vm; + section.SetBinding (TableSectionBase.TitleProperty, "Text"); + + Assert.AreEqual ("FooBar", section.Title); + + vm.Text = "Baz"; + + Assert.AreEqual ("Baz", section.Title); + } + + [Test] + public void TestBindingTitle () + { + var section = new TableSection (); + var mock = new MockViewModel (); + section.BindingContext = mock; + section.SetBinding (TableSection.TitleProperty, new Binding ("Text")); + + Assert.AreEqual (mock.Text, section.Title); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/TableViewUnitTests.cs b/Xamarin.Forms.Core.UnitTests/TableViewUnitTests.cs new file mode 100644 index 00000000..7e5ca496 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TableViewUnitTests.cs @@ -0,0 +1,84 @@ +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TableViewUnitTests : BaseTestFixture + { + [Test] + public void TestConstructor () + { + var table = new TableView (); + + Assert.False (table.Root.Any ()); + Assert.AreEqual (LayoutOptions.FillAndExpand, table.HorizontalOptions); + Assert.AreEqual (LayoutOptions.FillAndExpand, table.VerticalOptions); + } + + [Test] + public void TestModelChanged () + { + var table = new TableView (); + + bool changed = false; + + table.ModelChanged += (sender, e) => changed = true; + + table.Root = new TableRoot ("NewRoot"); + + Assert.True (changed); + } + + [Test] + public void BindingsContextChainsToModel () + { + const string context = "Context"; + var table = new TableView { BindingContext = context, Root = new TableRoot() }; + + Assert.AreEqual (context, table.Root.BindingContext); + + // reverse assignment order + table = new TableView { Root = new TableRoot(), BindingContext = context}; + Assert.AreEqual (context, table.Root.BindingContext); + } + + [Test] + public void ParentsViewCells () + { + ViewCell viewCell = new ViewCell { View = new Label () }; + var table = new TableView { + Platform = new UnitPlatform (), + Root = new TableRoot { + new TableSection { + viewCell + } + } + }; + + Assert.AreEqual (table, viewCell.Parent); + Assert.AreEqual (viewCell, viewCell.View.Parent); + Assert.AreEqual (table.Platform, viewCell.View.Platform); + } + + [Test] + public void ParentsAddedViewCells () + { + var viewCell = new ViewCell { View = new Label () }; + var section = new TableSection (); + var table = new TableView { + Platform = new UnitPlatform (), + Root = new TableRoot { + section + } + }; + + section.Add (viewCell); + + Assert.AreEqual (table, viewCell.Parent); + Assert.AreEqual (viewCell, viewCell.View.Parent); + Assert.AreEqual (table.Platform, viewCell.View.Platform); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/TapGestureRecognizerTests.cs b/Xamarin.Forms.Core.UnitTests/TapGestureRecognizerTests.cs new file mode 100644 index 00000000..5b639b2c --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TapGestureRecognizerTests.cs @@ -0,0 +1,33 @@ +using System; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TapGestureRecognizerTests : BaseTestFixture + { + [Test] + public void Constructor () + { + var tap = new TapGestureRecognizer (); + + Assert.AreEqual (null, tap.Command); + Assert.AreEqual (null, tap.CommandParameter); + Assert.AreEqual (1, tap.NumberOfTapsRequired); + } + + [Test] + public void CallbackPassesParameter () + { + var view = new View (); + var tap = new TapGestureRecognizer (); + tap.CommandParameter = "Hello"; + + object result = null; + tap.Command = new Command (o => result = o); + + tap.SendTapped (view); + Assert.AreEqual (result, tap.CommandParameter); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/TemplatedItemsListTests.cs b/Xamarin.Forms.Core.UnitTests/TemplatedItemsListTests.cs new file mode 100644 index 00000000..d723b660 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TemplatedItemsListTests.cs @@ -0,0 +1,1073 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TemplatedItemsListTests : BaseTestFixture + { + class MockItemsView + : ItemsView<BindableObject> + { + public MockItemsView() + { + TemplatedItems.IsGroupingEnabledProperty = IsGroupingEnabledProperty; + } + + public event Action<BindableObject> Hooked; + public event Action<BindableObject> Unhooked; + + public static readonly BindableProperty IsGroupingEnabledProperty = + BindableProperty.Create<MockItemsView, bool> (lv => lv.IsGroupingEnabled, false); + + public bool IsGroupingEnabled + { + get { return (bool)GetValue (IsGroupingEnabledProperty); } + set { SetValue (IsGroupingEnabledProperty, value); } + } + + protected override BindableObject CreateDefault (object item) + { + return new TextCell { Text = item.ToString () }; + } + + public new TemplatedItemsList<ItemsView<BindableObject>, BindableObject> TemplatedItems + { + get { return base.TemplatedItems; } + } + + BindingBase groupShortNameBinding; + public BindingBase GroupShortNameBinding + { + get { return groupShortNameBinding; } + set { + if (groupShortNameBinding == value) + return; + OnPropertyChanging (); + groupShortNameBinding = value; + TemplatedItems.GroupShortNameBinding = value; + OnPropertyChanged (); + } + } + + BindingBase groupDisplayBinding; + public BindingBase GroupDisplayBinding + { + get { return groupDisplayBinding; } + set { + if (groupDisplayBinding == value) + return; + + OnPropertyChanging (); + groupDisplayBinding = value; + TemplatedItems.GroupDisplayBinding = value; + OnPropertyChanged (); + } + } + + protected override void SetupContent (BindableObject content, int index) + { + base.SetupContent (content, index); + + if (Hooked != null) + Hooked (content); + } + + protected override void UnhookContent (BindableObject content) + { + base.UnhookContent (content); + + if (Unhooked != null) + Unhooked (content); + } + } + + [SetUp] + public override void Setup() + { + base.Setup (); + bindable = new MockItemsView(); + + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + } + + MockItemsView bindable; + + [Test] + public void ListProxyNotNullWithNullItemsSource() + { + Assert.IsNotNull (bindable.TemplatedItems.ListProxy); + } + + [Test] + public void ResetOnItemsSourceChanged() + { + bool raised = false; + NotifyCollectionChangedAction action = default(NotifyCollectionChangedAction); + bindable.TemplatedItems.CollectionChanged += (sender, args) => { + raised = true; + action = args.Action; + }; + + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, Enumerable.Empty<string>()); + + Assert.IsTrue (raised, "CollectionChanged was not raised"); + Assert.AreEqual (NotifyCollectionChangedAction.Reset, action); + } + /* + [Test] + public void ResetOnInfiniteScrollingChanged() + { + bool raised = false; + NotifyCollectionChangedAction action = default(NotifyCollectionChangedAction); + bindable.TemplatedItems.CollectionChanged += (sender, args) => { + raised = true; + action = args.Action; + }; + + bindable.SetValue (ItemsView.InfiniteScrollingProperty, true); + + Assert.IsTrue (raised, "CollectionChanged was not raised"); + Assert.AreEqual (NotifyCollectionChangedAction.Reset, action); + }*/ + + [Test] + public void ResetOnTemplateChanged() + { + // Template changes won't trigger a reset if there's no items. + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, new[] { "Foo" }); + + bool raised = false; + NotifyCollectionChangedAction action = default(NotifyCollectionChangedAction); + bindable.TemplatedItems.CollectionChanged += (sender, args) => { + raised = true; + action = args.Action; + }; + + bindable.SetValue (ItemsView<BindableObject>.ItemTemplateProperty, new DataTemplate()); + + Assert.IsTrue (raised, "CollectionChanged was not raised"); + Assert.AreEqual (NotifyCollectionChangedAction.Reset, action); + } + + [Test] + public void PassThroughChanges() + { + var collection = new ObservableCollection<string>(); + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + bool raised = false; + NotifyCollectionChangedEventArgs args = null; + bindable.TemplatedItems.CollectionChanged += (sender, eventArgs) => { + raised = true; + args = eventArgs; + }; + + string str = "foo bar"; + collection.Add (str); + + Assert.IsTrue (raised, "CollectionChanged was not raised"); + Assert.AreEqual (NotifyCollectionChangedAction.Add, args.Action); + Assert.AreEqual (0, args.NewStartingIndex); + Assert.IsNotNull (args.NewItems); + Assert.AreEqual (1, args.NewItems.Count); + Assert.AreSame (str, ((Cell)args.NewItems[0]).BindingContext); + } + + [Test] + public void ListProxyUpdatesWithItemsSource() + { + var collection = new List<string> { "foo bar" }; + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + Assert.IsNotNull (bindable.TemplatedItems.ListProxy); + Assert.AreEqual (collection.Count, bindable.TemplatedItems.ListProxy.Count); + } + + [Test] + public void GetOrCreateContent ([Values (0, 1, 2)] int index) + { + var collection = new List<string> { "foo", "bar", "baz" }; + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + DataTemplate template = new DataTemplate (typeof (TextCell)); + template.SetBinding (TextCell.TextProperty, new Binding (".")); + bindable.SetValue (ItemsView<BindableObject>.ItemTemplateProperty, template); + + BindableObject content = bindable.TemplatedItems.GetOrCreateContent (index, collection[index]); + Assert.IsNotNull (content); + + TextCell textCell = content as TextCell; + Assert.IsNotNull (textCell, "Content was did not match the template type, expected {0} but got {1}", typeof(TextCell), content.GetType()); + + Assert.AreSame (collection[index], textCell.BindingContext); + Assert.AreSame (collection[index], textCell.Text); + + BindableObject content2 = bindable.TemplatedItems.GetOrCreateContent (index, collection[index]); + Assert.AreSame (content, content2); + } + + [Test] + public void GetOrCreateContentDefault() + { + Assert.IsNull (bindable.GetValue (ItemsView<BindableObject>.ItemTemplateProperty)); + + var collection = new List<string> { "foo", "bar", "baz" }; + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + const int index = 0; + BindableObject content = bindable.TemplatedItems.GetOrCreateContent (index, collection[index]); + Assert.IsNotNull (content); + + TextCell textCell = content as TextCell; + Assert.IsNotNull (textCell, "Content was did not match the template type, expected {0} but got {1}", typeof(TextCell), content.GetType()); + + Assert.AreSame (collection[index], textCell.BindingContext); + Assert.AreSame (collection[index], textCell.Text); + + BindableObject content2 = bindable.TemplatedItems.GetOrCreateContent (index, collection[index]); + Assert.AreSame (content, content2); + } + + [Test] + public void GetOrCreateContentAfterTemplateChange() + { + var collection = new List<string> { "foo", "bar", "baz" }; + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + const int index = 0; + + DataTemplate template = new DataTemplate (typeof (TextCell)); + template.SetBinding (TextCell.TextProperty, new Binding (".")); + bindable.SetValue (ItemsView<BindableObject>.ItemTemplateProperty, template); + + BindableObject content = bindable.TemplatedItems.GetOrCreateContent (index, collection[index]); + Assert.That (content, Is.InstanceOf<TextCell>()); + + template = new DataTemplate (typeof (SwitchCell)); + template.SetBinding (SwitchCell.TextProperty, new Binding (".")); + + bindable.SetValue (ItemsView<BindableObject>.ItemTemplateProperty, template); + + BindableObject content2 = bindable.TemplatedItems.GetOrCreateContent (index, collection[index]); + Assert.IsNotNull (content2); + Assert.That (content2, Is.InstanceOf<SwitchCell>()); + + var switchCell = (SwitchCell) content2; + Assert.AreSame (collection[index], switchCell.BindingContext); + } + + [Test] + public void GetOrCreateContentAfterItemsSourceChanged() + { + var collection = new List<string> { "foo", "bar", "baz" }; + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + const int index = 0; + + DataTemplate template = new DataTemplate (typeof (TextCell)); + template.SetBinding (TextCell.TextProperty, new Binding (".")); + bindable.SetValue (ItemsView<BindableObject>.ItemTemplateProperty, template); + + BindableObject content = bindable.TemplatedItems.GetOrCreateContent (index, collection[index]); + Assert.IsNotNull (content); + + collection = new List<string> { "we", "wee", "weee" }; + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + var content2 = (TextCell)bindable.TemplatedItems.GetOrCreateContent (index, collection[index]); + + Assert.AreNotSame (content, content2); + Assert.AreSame (collection[index], content2.BindingContext); + Assert.AreSame (collection[index], content2.Text); + } + + /*[Test] + public void GetOrCreateContentAfterInfiniteScrollingChanged() + { + var collection = new List<string> { "foo", "bar", "baz" }; + bindable.SetValue (ItemsView.ItemsSourceProperty, collection); + + const int index = 0; + + DataTemplate template = new DataTemplate (typeof (TextCell)); + template.SetBinding (TextCell.TextProperty, new Binding (".")); + bindable.SetValue (ItemsView.ItemTemplateProperty, template); + + BindableObject content = bindable.TemplatedItems.GetOrCreateContent (index, collection[index], o => new TextCell (o.ToString())); + Assert.IsNotNull (content); + + bindable.SetValue (ItemsView.InfiniteScrollingProperty, true); + + var content2 = (TextCell)bindable.TemplatedItems.GetOrCreateContent (index, collection[index], o => new TextCell (o.ToString())); + + Assert.AreNotSame (content, content2); + Assert.AreSame (collection[index], content2.BindingContext); + Assert.AreSame (collection[index], content2.Text); + }*/ + + [Test] + [Description ("Make sure we're not duplicate cell instances for an equal item if it's a different index")] + public void GetOrCreateContentEqualItemDifferentItemDifferentIndex() + { + Assert.IsNull (bindable.GetValue (ItemsView<BindableObject>.ItemTemplateProperty)); + + var collection = new List<string> { "foo", "foo" }; + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + BindableObject content = bindable.TemplatedItems.GetOrCreateContent (0, collection[0]); + Assert.IsNotNull (content); + + TextCell textCell = content as TextCell; + Assert.IsNotNull (textCell, "Content was did not match the template type, expected {0} but got {1}", typeof(TextCell), content.GetType()); + + Assert.AreSame (collection[0], textCell.BindingContext); + Assert.AreSame (collection[0], textCell.Text); + + BindableObject content2 = bindable.TemplatedItems.GetOrCreateContent (1, collection[1]); + Assert.AreNotSame (content, content2); + + Assert.AreSame (collection[1], textCell.BindingContext); + Assert.AreSame (collection[1], textCell.Text); + } + + [Test] + [Description ("Make sure we're not duplicate cell instances for the same item if it's a different index")] + public void GetOrCreateContentEqualItemSameItemDifferentIndex() + { + Assert.IsNull (bindable.GetValue (ItemsView<BindableObject>.ItemTemplateProperty)); + + string item = "foo"; + + var collection = new List<string> { item, item }; + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + BindableObject content = bindable.TemplatedItems.GetOrCreateContent (0, item); + Assert.IsNotNull (content); + + TextCell textCell = content as TextCell; + Assert.IsNotNull (textCell, "Content was did not match the template type, expected {0} but got {1}", typeof(TextCell), content.GetType()); + + Assert.AreSame (item, textCell.BindingContext); + Assert.AreSame (item, textCell.Text); + + BindableObject content2 = bindable.TemplatedItems.GetOrCreateContent (1, item); + Assert.AreNotSame (content, content2); + + Assert.AreSame (item, textCell.BindingContext); + Assert.AreSame (item, textCell.Text); + } + + [Test] + public void ItemsSourceInsertPreRealzied() + { + var collection = new ObservableCollection<string> { "foo", "bar" }; + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + collection.Insert (1, "baz"); + + Assert.That (bindable.TemplatedItems.Count, Is.EqualTo (3)); + Assert.That (bindable.TemplatedItems.GetOrCreateContent (0, collection[0]).BindingContext, Is.SameAs (collection[0])); + Assert.That (bindable.TemplatedItems.GetOrCreateContent (1, collection[1]).BindingContext, Is.SameAs (collection[1])); + Assert.That (bindable.TemplatedItems.GetOrCreateContent (2, collection[2]).BindingContext, Is.SameAs (collection[2])); + } + + [Test] + public void ItemsSourceInsertPostRealized() + { + var collection = new ObservableCollection<string> { "foo", "bar" }; + bindable.SetValue (ItemsView<BindableObject>.ItemsSourceProperty, collection); + + // Force the handler to realize/create the content + bindable.TemplatedItems.GetOrCreateContent (0, collection[0]); + bindable.TemplatedItems.GetOrCreateContent (1, collection[1]); + + collection.Insert (1, "baz"); + + Assert.That (bindable.TemplatedItems.Count, Is.EqualTo (3)); + Assert.That (bindable.TemplatedItems.GetOrCreateContent (0, collection[0]).BindingContext, Is.SameAs (collection[0])); + Assert.That (bindable.TemplatedItems.GetOrCreateContent (1, collection[1]).BindingContext, Is.SameAs (collection[1])); + Assert.That (bindable.TemplatedItems.GetOrCreateContent (2, collection[2]).BindingContext, Is.SameAs (collection[2])); + } + + [TestCase (0, 0, 0)] + [TestCase (3, 1, 0)] + public void GetGroupIndexFromGlobalGroupIndex (int globalIndex, int expectedIndex, int expectedLeftOver) + { + var collection = new[] { + new[] { "foo", "fad" }, + new[] { "bar", "baz" } + }; + bindable.ItemsSource = collection; + bindable.IsGroupingEnabled = true; + + int leftOver; + int index = bindable.TemplatedItems.GetGroupIndexFromGlobal (globalIndex, out leftOver); + Assert.That (index, Is.EqualTo (expectedIndex)); + Assert.That (leftOver, Is.EqualTo (expectedLeftOver)); + } + + [TestCase (1, 0, 1)] + [TestCase (2, 0, 2)] + [TestCase (4, 1, 1)] + [TestCase (5, 1, 2)] + public void GetGroupIndexFromGlobalItemIndex (int globalIndex, int expectedIndex, int expectedLeftOver) + { + var collection = new[] { + new[] { "foo", "fad" }, + new[] { "bar", "baz" } + }; + bindable.ItemsSource = collection; + bindable.IsGroupingEnabled = true; + + int leftOver; + int index = bindable.TemplatedItems.GetGroupIndexFromGlobal (globalIndex, out leftOver); + Assert.That (index, Is.EqualTo (expectedIndex)); + Assert.That (leftOver, Is.EqualTo (expectedLeftOver)); + } + + [Test] + public void GetGroupAndIndexOfItem ([Values (0, 1, 2)] int group, [Values (0, 1, 2)] int index) + { + var collection = new[] { + new[] { "foo", "fad" }, + new[] { "bar", "baz" } + }; + bindable.ItemsSource = collection; + bindable.IsGroupingEnabled = true; + + object item = null; + if (group < collection.Length) { + if (index < collection[group].Length) + item = collection[group][index]; + } + + if (item == null) { + item = "not in here"; + group = index = -1; + } + + var location = bindable.TemplatedItems.GetGroupAndIndexOfItem (item); + Assert.That (location.Item1, Is.EqualTo (group), "Group index was incorrect"); + Assert.That (location.Item2, Is.EqualTo (index), "Item index was incorrect"); + } + + [Test] + public void GetGroupAndIndexOfItemNotGrouped () + { + var items = Enumerable.Range (0, 10).ToList (); + bindable.ItemsSource = items; + + var location = bindable.TemplatedItems.GetGroupAndIndexOfItem (null, items[2]); + + Assert.That (location.Item1, Is.EqualTo (0)); + Assert.That (location.Item2, Is.EqualTo (2)); + } + + [Test] + public void ItemsSourcePropertyChangedWithBindable() + { + bool raised = false; + bindable.TemplatedItems.PropertyChanged += (s, e) => { + if (e.PropertyName == "ItemsSource") + raised = true; + }; + + bindable.ItemsSource = new object[0]; + + Assert.That (raised, Is.True, "INPC not raised for ItemsSource"); + } + + [Test] + public void IndexCorrectAfterMovingGroups() + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.IsGroupingEnabled = true; + + items.Move (0, 1); + + var til = bindable.TemplatedItems.GetGroup (1); + int index = GetIndex (til.HeaderContent); + + Assert.That (index, Is.EqualTo (1)); + } + + [Test] + public void ShortNameSetBeforeGrouping() + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupDisplayBinding = new Binding (".[0]"); + bindable.GroupShortNameBinding = new Binding (".[0]"); + bindable.IsGroupingEnabled = true; + + Assert.That (bindable.TemplatedItems.ShortNames[0], Is.EqualTo ("foo")); + } + + [Test] + public void ItemAddedWithShortNameSetButUngrouped() + { + var items = new ObservableCollection<string> { "foo", "bar" }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + + Assert.That (() => items.Add ("baz"), Throws.Nothing); + } + + [Test] + public void ItemAddedWithShortNameSetButGroupingDisabled() + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + bindable.IsGroupingEnabled = false; + + Assert.That (() => items.Add (new ObservableCollection<string>()), Throws.Nothing); + } + + int GetIndex (BindableObject item) + { + return TemplatedItemsList<ItemsView<BindableObject>, BindableObject>.GetIndex (item); + } + + [Test] + public void GetIndex() + { + var items = new List<string> { "foo", "bar", "baz" }; + + bindable.ItemsSource = items; + + BindableObject item = bindable.TemplatedItems.GetOrCreateContent (1, items[1]); + int index = GetIndex (item); + Assert.That (index, Is.EqualTo (1)); + } + + [Test] + public void GetIndexAfterInsert() + { + var items = new ObservableCollection<string> { "foo", "bar", "baz" }; + + bindable.ItemsSource = items; + + BindableObject originalItem = bindable.TemplatedItems.GetOrCreateContent (1, items[1]); + + items.Insert (1, "fad"); + + BindableObject item = bindable.TemplatedItems.GetOrCreateContent (1, items[1]); + + int index = GetIndex (item); + Assert.That (index, Is.EqualTo (1)); + + index = GetIndex (originalItem); + Assert.That (index, Is.EqualTo (2)); + } + + [Test] + public void GetIndexAfterMove() + { + var items = new ObservableCollection<string> { "foo", "bar", "baz" }; + + bindable.ItemsSource = items; + + BindableObject item0 = bindable.TemplatedItems.GetOrCreateContent (0, items[0]); + BindableObject item1 = bindable.TemplatedItems.GetOrCreateContent (1, items[1]); + BindableObject item2 = bindable.TemplatedItems.GetOrCreateContent (2, items[2]); + + items.Move (0, 2); // foo, bar, baz becomes bar (1), baz (2), foo (0) + + Assert.That (GetIndex (item0), Is.EqualTo (2)); + Assert.That (GetIndex (item1), Is.EqualTo (0)); + Assert.That (GetIndex (item2), Is.EqualTo (1)); + } + + [Test] + public void GetIndexAfterRemove() + { + var items = new ObservableCollection<string> { "foo", "bar", "baz" }; + + bindable.ItemsSource = items; + + BindableObject item1 = bindable.TemplatedItems.GetOrCreateContent (1, items[1]); + BindableObject item2 = bindable.TemplatedItems.GetOrCreateContent (2, items[2]); + + items.RemoveAt (0); + + Assert.That (GetIndex (item1), Is.EqualTo (0)); + Assert.That (GetIndex (item2), Is.EqualTo (1)); + } + + [Test] + public void GetGroup() + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + var til = bindable.TemplatedItems.GetGroup (1); + var item = til[1]; + + var group = TemplatedItemsList<ItemsView<BindableObject>, BindableObject>.GetGroup (item); + + Assert.That (group, Is.SameAs (til)); + } + + [Test] + public void GetIndexGrouped() + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + var til = bindable.TemplatedItems.GetGroup (1); + var item = til[1]; + + int index = GetIndex (item); + Assert.That (index, Is.EqualTo (1)); + } + + [Test] + public void GetGroupAndIndexOfItem() + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + var result = bindable.TemplatedItems.GetGroupAndIndexOfItem (items[1], items[1][1]); + + Assert.That (result.Item1, Is.EqualTo (1)); + Assert.That (result.Item2, Is.EqualTo (1)); + } + + [Test] + public void GetGroupAndIndexOfItemNoGroup() + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + var result = bindable.TemplatedItems.GetGroupAndIndexOfItem (null, items[1][1]); + + Assert.That (result.Item1, Is.EqualTo (1)); + Assert.That (result.Item2, Is.EqualTo (1)); + } + + [Test] + public void GetGroupAndIndexOfItemNotFound() + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + var group = new ObservableCollection<string> { "bam" }; + + var result = bindable.TemplatedItems.GetGroupAndIndexOfItem (group, group[0]); + + Assert.That (result.Item1, Is.EqualTo (-1)); + Assert.That (result.Item2, Is.EqualTo (-1)); + } + + [Test] + public void GetGroupAndIndexOfItemItemNotFound() + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + var result = bindable.TemplatedItems.GetGroupAndIndexOfItem (items[1], "bam"); + + Assert.That (result.Item1, Is.EqualTo (1)); + Assert.That (result.Item2, Is.EqualTo (-1)); + } + + [Test] + [Description ("Issue #2464: ANE thrown when moving items in a ListView")] + public void MovingPastRealizedWindowAndBackDoesntThrow() + { + var items = new ObservableCollection<string> (Enumerable.Range (0, 100).Select (i => i.ToString())); + + bindable.ItemsSource = items; + + bindable.TemplatedItems.GetOrCreateContent (0, items[0]); + bindable.TemplatedItems.GetOrCreateContent (1, items[1]); + + items.Move (0, 3); + Assert.That (() => items.Move (3, 2), Throws.Nothing); + + Assert.That (GetIndex (bindable.TemplatedItems[0]), Is.EqualTo (0)); + Assert.That (GetIndex (bindable.TemplatedItems[1]), Is.EqualTo (1)); + Assert.That (GetIndex (bindable.TemplatedItems[2]), Is.EqualTo (2)); + Assert.That (GetIndex (bindable.TemplatedItems[3]), Is.EqualTo (3)); + } + + [Test] + public void GetGlobalIndexOfItem () + { + var items = new ObservableCollection<string> (Enumerable.Range (0, 100).Select (i => i.ToString())); + bindable.ItemsSource = items; + + int global = bindable.TemplatedItems.GetGlobalIndexOfItem ("50"); + Assert.That (global, Is.EqualTo (50)); + } + + [Test] + public void GetGlobalIndexOfItemNotFound () + { + var items = new ObservableCollection<string> (Enumerable.Range (0, 100).Select (i => i.ToString())); + bindable.ItemsSource = items; + + int global = bindable.TemplatedItems.GetGlobalIndexOfItem ("101"); + Assert.That (global, Is.EqualTo (-1)); + } + + [Test] + public void GetGlobalIndexOfItemGrouped () + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + int global = bindable.TemplatedItems.GetGlobalIndexOfItem ("baz"); + Assert.That (global, Is.EqualTo (5)); + } + + [Test] + public void GetGlobalIndexOfItemGroupedNotFound () + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + int global = bindable.TemplatedItems.GetGlobalIndexOfItem ("101"); + Assert.That (global, Is.EqualTo (-1)); + } + + [Test] + public void GetGlobalIndexOfGroupItemGrouped () + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "baz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + int global = bindable.TemplatedItems.GetGlobalIndexOfItem (items[1], "baz"); + Assert.That (global, Is.EqualTo (5)); + } + + [Test] + public void GetGlobalIndexOfGroupItemGroupedNotFound () + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "foo", "faz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + int global = bindable.TemplatedItems.GetGlobalIndexOfItem (items[1], "bar"); + Assert.That (global, Is.EqualTo (-1)); + } + + [Test] + public void GetGlobalIndexOfGroupItemGroupedGroupNotFound () + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "foo", "faz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + int global = bindable.TemplatedItems.GetGlobalIndexOfItem (new object(), "foo"); + Assert.That (global, Is.EqualTo (-1)); + } + + [Test] + public void SetupContentOnCreation() + { + var items = new ObservableCollection<string> { + "Foo", + "Bar" + }; + + bindable.ItemTemplate = new DataTemplate (typeof (TextCell)) { + Bindings = { + { TextCell.TextProperty, new Binding (".") } + } + }; + + int count = 0; + bindable.Hooked += obj => { + count++; + Assert.That (obj, Is.InstanceOf (typeof (TextCell))); + Assert.That (items, Contains.Item (obj.BindingContext)); + }; + + bindable.ItemsSource = items; + + bindable.TemplatedItems.GetOrCreateContent (0, items[0]); + bindable.TemplatedItems.GetOrCreateContent (1, items[1]); + + Assert.That (count, Is.EqualTo (2)); + } + + [Test] + public void UnhookGroupOnRemoval() + { + var inner = new ObservableCollection<string> { + "Foo", + }; + + var items = new ObservableCollection<ObservableCollection<string>> { + inner + }; + + bindable.IsGroupingEnabled = true; + bindable.GroupDisplayBinding = new Binding (".[0]"); + + bindable.ItemTemplate = new DataTemplate (typeof (TextCell)) { + Bindings = { + { TextCell.TextProperty, new Binding (".") } + } + }; + + int count = 0; + bindable.Unhooked += obj => { + if (count == 0) { + Assert.That (obj.BindingContext, Is.InstanceOf (typeof (TemplatedItemsList<ItemsView<BindableObject>, BindableObject>))); + Assert.That (((TemplatedItemsList<ItemsView<BindableObject>, BindableObject>) obj.BindingContext).ListProxy.ProxiedEnumerable, Is.SameAs (inner)); + } else { + Assert.That (obj.BindingContext, Is.SameAs (inner[0])); + } + + count++; + }; + + bindable.ItemsSource = items; + + var til = bindable.TemplatedItems.GetGroup (0); + til.GetOrCreateContent (0, inner[0]); + + Assume.That (til, Is.Not.Null); + Assume.That (til.HeaderContent, Is.Not.Null); + Assume.That (count, Is.EqualTo (0)); + + items.RemoveAt (0); + + Assert.That (count, Is.EqualTo (2)); + } + + [Test] + public void HookAndUnhookGroupOnReplace() + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { + "Foo", + } + }; + + bindable.IsGroupingEnabled = true; + bindable.GroupDisplayBinding = new Binding (".[0]"); + bindable.ItemTemplate = new DataTemplate (typeof (TextCell)) { + Bindings = { + { TextCell.TextProperty, new Binding (".") } + } + }; + + bindable.ItemsSource = items; + + var til = bindable.TemplatedItems.GetGroup (0); + til.GetOrCreateContent (0, items[0][0]); + + Assume.That (til, Is.Not.Null); + Assume.That (til.HeaderContent, Is.Not.Null); + + int hcount = 0; + bindable.Hooked += obj => { + Assert.That (obj.BindingContext, Is.InstanceOf (typeof (TemplatedItemsList<ItemsView<BindableObject>, BindableObject>))); + hcount++; + }; + + int ucount = 0; + bindable.Unhooked += obj => { + Assert.That (obj.BindingContext, + Is.InstanceOf (typeof (TemplatedItemsList<ItemsView<BindableObject>, BindableObject>)) + .Or.EqualTo ("Foo")); + + ucount++; + }; + + items[0] = new ObservableCollection<string> { "Baz" }; + + Assert.That (hcount, Is.EqualTo (1)); + Assert.That (ucount, Is.EqualTo (2)); + } + + [Test] + public void UnhookContentOnRemoval() + { + var items = new ObservableCollection<string> { + "Foo", + "Bar" + }; + + bindable.ItemTemplate = new DataTemplate (typeof (TextCell)) { + Bindings = { + { TextCell.TextProperty, new Binding (".") } + } + }; + + int count = 0; + bindable.Unhooked += obj => { + count++; + Assert.That (obj, Is.InstanceOf (typeof (TextCell))); + Assert.That (obj.BindingContext, Is.EqualTo ("Foo")); + }; + + bindable.ItemsSource = items; + + bindable.TemplatedItems.GetOrCreateContent (0, items[0]); + bindable.TemplatedItems.GetOrCreateContent (1, items[1]); + + Assume.That (count, Is.EqualTo (0)); + + items.RemoveAt (0); + + Assert.That (count, Is.EqualTo (1)); + } + + [Test] + public void HookAndUnhookContentOnReplace() + { + var items = new ObservableCollection<string> { + "Foo", + "Bar" + }; + + bindable.ItemTemplate = new DataTemplate (typeof (TextCell)) { + Bindings = { + { TextCell.TextProperty, new Binding (".") } + } + }; + + bindable.ItemsSource = items; + + bindable.TemplatedItems.GetOrCreateContent (0, items[0]); + bindable.TemplatedItems.GetOrCreateContent (1, items[1]); + + int hcount = 0; + bindable.Hooked += obj => { + hcount++; + Assert.That (obj, Is.InstanceOf (typeof (TextCell))); + Assert.That (obj.BindingContext, Is.EqualTo ("Baz")); + }; + + int ucount = 0; + bindable.Unhooked += obj => { + ucount++; + Assert.That (obj, Is.InstanceOf (typeof (TextCell))); + Assert.That (obj.BindingContext, Is.EqualTo ("Foo")); + }; + + items[0] = "Baz"; + + Assert.That (hcount, Is.EqualTo (1)); + Assert.That (ucount, Is.EqualTo (1)); + } + + [Test (Description = "If the cell exists and has an index, we still need to check if it's in the group asked for")] + public void IndexOfFailsForCellInAnotherGroup () + { + var items = new ObservableCollection<ObservableCollection<string>> { + new ObservableCollection<string> { "foo", "faz" }, + new ObservableCollection<string> { "bar", "baz" } + }; + + bindable.ItemsSource = items; + bindable.GroupShortNameBinding = new Binding ("."); + bindable.IsGroupingEnabled = true; + + var group1 = bindable.TemplatedItems.GetGroup (0); + var cell = group1[1]; + + Assume.That (group1.IndexOf (cell), Is.EqualTo (1)); + + var group2 = bindable.TemplatedItems.GetGroup (1); + + Assert.That (group2.IndexOf (cell), Is.EqualTo (-1)); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/TextCellTests.cs b/Xamarin.Forms.Core.UnitTests/TextCellTests.cs new file mode 100644 index 00000000..43965a35 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TextCellTests.cs @@ -0,0 +1,129 @@ +using System; +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TextCellTests : BaseTestFixture + { + [Test] + public void TestTapped () + { + var cell = new TextCell (); + bool tapped = false; + cell.Tapped += (sender, args) => tapped = true; + + cell.OnTapped(); + Assert.True (tapped); + } + + [Test] + public void TestCommand() + { + bool executed = false; + + var cmd = new Command (() => executed = true); + var cell = new TextCell(); + cell.Command = cmd; + cell.OnTapped(); + + Assert.IsTrue (executed, "Command was not executed"); + } + + [Test] + public void TestCommandParameter() + { + bool executed = false; + + object obj = new object(); + var cmd = new Command (p => { + Assert.AreSame (obj, p); + executed = true; + }); + + var cell = new TextCell { + Command = cmd, + CommandParameter = obj + }; + + cell.OnTapped(); + + Assert.IsTrue (executed, "Command was not executed"); + } + + [Test] + public void TestCommandCanExecute() + { + bool tested = false; + + var cmd = new Command (() => { }, + canExecute: () => { + tested = true; + return true; + }); + + new TextCell { Command = cmd }; + Assert.IsTrue (tested, "Command.CanExecute was not called"); + } + + [Test] + public void TestCommandCanExecuteDisables() + { + var cmd = new Command (() => { }, () => false); + var cell = new TextCell { Command = cmd }; + Assert.IsFalse (cell.IsEnabled, "Cell was not disabled"); + } + + [Test] + public void TestCommandCanExecuteChanged() + { + bool first = true; + var cmd = new Command (() => { }, () => { + if (first) { + first = false; + return false; + } else { + return true; + } + }); + + var cell = new TextCell { Command = cmd }; + Assert.IsFalse (cell.IsEnabled, "Cell was not disabled"); + + cmd.ChangeCanExecute(); + + Assert.IsTrue (cell.IsEnabled, "Cell was not reenabled"); + } + + [Test] + public void Create () + { + var template = new DataTemplate (typeof (TextCell)); + var content = template.CreateContent (); + + Assert.IsNotNull (content); + Assert.That (content, Is.InstanceOf<TextCell> ()); + } + + [Test] + public void Detail () + { + var template = new DataTemplate (typeof (TextCell)); + template.SetValue (TextCell.DetailProperty, "detail"); + + TextCell cell = (TextCell)template.CreateContent (); + Assert.That (cell.Detail, Is.EqualTo ("detail")); + } + + [Test] + public void Text () + { + var template = new DataTemplate (typeof (TextCell)); + template.SetValue (TextCell.TextProperty, "text"); + + TextCell cell = (TextCell)template.CreateContent (); + Assert.That (cell.Text, Is.EqualTo ("text")); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ThicknessTests.cs b/Xamarin.Forms.Core.UnitTests/ThicknessTests.cs new file mode 100644 index 00000000..2ba08269 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ThicknessTests.cs @@ -0,0 +1,132 @@ +using NUnit.Framework; +using System; +using System.Globalization; +using System.Security.Cryptography.X509Certificates; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ThicknessTests : BaseTestFixture + { + [Test] + public void Constructor () + { + var thickness = new Thickness (); + + Assert.AreEqual (0, thickness.Left); + Assert.AreEqual (0, thickness.Top); + Assert.AreEqual (0, thickness.Right); + Assert.AreEqual (0, thickness.Bottom); + Assert.AreEqual (0, thickness.HorizontalThickness); + Assert.AreEqual (0, thickness.VerticalThickness); + } + + [Test] + public void UniformParameterizedConstructor() + { + var thickness = new Thickness(3); + + Assert.AreEqual(3, thickness.Left); + Assert.AreEqual(3, thickness.Top); + Assert.AreEqual(3, thickness.Right); + Assert.AreEqual(3, thickness.Bottom); + Assert.AreEqual(6, thickness.HorizontalThickness); + Assert.AreEqual(6, thickness.VerticalThickness); + } + + [Test] + public void HorizontalVerticalParameterizedConstructor() + { + var thickness = new Thickness(4, 5); + + Assert.AreEqual(4, thickness.Left); + Assert.AreEqual(5, thickness.Top); + Assert.AreEqual(4, thickness.Right); + Assert.AreEqual(5, thickness.Bottom); + Assert.AreEqual(8, thickness.HorizontalThickness); + Assert.AreEqual(10, thickness.VerticalThickness); + } + + [Test] + public void ParameterizedConstructor () + { + var thickness = new Thickness (1, 2, 3, 4); + + Assert.AreEqual (1, thickness.Left); + Assert.AreEqual (2, thickness.Top); + Assert.AreEqual (3, thickness.Right); + Assert.AreEqual (4, thickness.Bottom); + Assert.AreEqual (4, thickness.HorizontalThickness); + Assert.AreEqual (6, thickness.VerticalThickness); + } + + [Test] + public void ParameterizedConstuctorDoubles () + { + var thickness = new Thickness (1.2, 3.3, 4.2, 10.66); + Assert.AreEqual (1.2, thickness.Left); + Assert.AreEqual (3.3, thickness.Top); + Assert.AreEqual (4.2, thickness.Right); + Assert.AreEqual (10.66, thickness.Bottom); + Assert.AreEqual (5.4, thickness.HorizontalThickness); + Assert.AreEqual (13.96, thickness.VerticalThickness); + } + + [Test] + public void Equality () + { + Assert.False (new Thickness ().Equals (null)); + Assert.False (new Thickness ().Equals ("Thickness")); + Assert.False (new Thickness ().Equals (new Thickness (1, 2, 3, 4))); + Assert.True (new Thickness ().Equals (new Thickness ())); + + Assert.True (new Thickness () == new Thickness ()); + Assert.True (new Thickness (4, 3, 2, 1) != new Thickness (1, 2, 3, 4)); + } + + [Test] + public void HashCode ([Range(3, 4)] double l1, [Range(3, 4)] double t1, [Range(3, 4)] double r1, [Range(3, 4)] double b1, + [Range(3, 4)] double l2, [Range(3, 4)] double t2, [Range(3, 4)] double r2, [Range(3, 4)] double b2) + { + bool result = new Thickness (l1, t1, r1, b1).GetHashCode () == new Thickness (l2, t2, r2, b2).GetHashCode (); + if (l1 == l2 && t1 == t2 && r1 == r2 && b1 == b2) + Assert.True (result); + else + Assert.False (result); + } + + [Test] + public void ImplicitConversionFromSize () + { + Thickness thickness = new Thickness(); + Assert.DoesNotThrow (() => thickness = new Size (42, 84)); + Assert.AreEqual (new Thickness (42, 84), thickness); + + Assert.DoesNotThrow (() => thickness = 42); + Assert.AreEqual (new Thickness (42), thickness); + } + + [Test] + public void TestThicknessTypeConverter () + { + var converter = new ThicknessTypeConverter (); + Assert.True (converter.CanConvertFrom (typeof(string))); + Assert.AreEqual (new Thickness (1), converter.ConvertFromInvariantString ("1")); + Assert.AreEqual (new Thickness (1, 2), converter.ConvertFromInvariantString ("1, 2")); + Assert.AreEqual (new Thickness (1, 2, 3, 4), converter.ConvertFromInvariantString ("1, 2, 3, 4")); + Assert.AreEqual (new Thickness (1.1, 2), converter.ConvertFromInvariantString ("1.1,2")); + Assert.Throws<InvalidOperationException> (() => converter.ConvertFromInvariantString ("")); + } + + [Test] + public void ThicknessTypeConverterDoubles () + { + var converter = new ThicknessTypeConverter (); + Assert.AreEqual (new Thickness (1.3), converter.ConvertFromInvariantString ("1.3")); + Assert.AreEqual (new Thickness (1.4, 2.8), converter.ConvertFromInvariantString ("1.4, 2.8")); + Assert.AreEqual (new Thickness (1.6, 2.1, 3.8, 4.2), converter.ConvertFromInvariantString (" 1.6 , 2.1, 3.8, 4.2")); + Assert.AreEqual (new Thickness (1.1, 2), converter.ConvertFromInvariantString ("1.1,2")); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/TimePickerUnitTest.cs b/Xamarin.Forms.Core.UnitTests/TimePickerUnitTest.cs new file mode 100644 index 00000000..343d3ed1 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TimePickerUnitTest.cs @@ -0,0 +1,43 @@ +using System; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TimePickerUnitTest : BaseTestFixture + { + [Test] + public void TestConstructor () + { + TimePicker picker = new TimePicker (); + + Assert.AreEqual (new TimeSpan (), picker.Time); + } + + [Test] + public void TestTimeOutOfRange () + { + TimePicker picker = new TimePicker (); + + Assert.That (() => picker.Time = new TimeSpan (1000, 0, 0), Throws.ArgumentException); + Assert.AreEqual (picker.Time, new TimeSpan ()); + + picker.Time = new TimeSpan (8, 30, 0); + + Assert.AreEqual (new TimeSpan (8, 30, 0), picker.Time); + + Assert.That (() => picker.Time = new TimeSpan (-1, 0, 0), Throws.ArgumentException); + Assert.AreEqual (new TimeSpan (8, 30, 0), picker.Time); + } + + [Test] + [Description ("Issue #745")] + public void ZeroTimeIsValid() + { + var picker = new TimePicker (); + + Assert.That (() => picker.Time = new TimeSpan (0, 0, 0), Throws.Nothing); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ToolbarItemTests.cs b/Xamarin.Forms.Core.UnitTests/ToolbarItemTests.cs new file mode 100644 index 00000000..d5fefb45 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ToolbarItemTests.cs @@ -0,0 +1,11 @@ +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ToolbarItemTests + : MenuItemTests + { + + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/ToolbarTrackerTests.cs b/Xamarin.Forms.Core.UnitTests/ToolbarTrackerTests.cs new file mode 100644 index 00000000..32155be3 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ToolbarTrackerTests.cs @@ -0,0 +1,194 @@ +using System.Linq; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + internal class ToolbarTrackerTests : BaseTestFixture + { + [Test] + public void Constructor () + { + var tracker = new ToolbarTracker (); + Assert.Null (tracker.Target); + Assert.False (tracker.ToolbarItems.Any ()); + } + + [Test] + public void SimpleTrackEmpty () + { + var tracker = new ToolbarTracker (); + + var page = new ContentPage (); + tracker.Target = page; + + Assert.False (tracker.ToolbarItems.Any ()); + } + + [Test] + public void SimpleTrackWithItems () + { + var tracker = new ToolbarTracker (); + + ToolbarItem item1, item2; + var page = new ContentPage { + ToolbarItems = { + new ToolbarItem ("Foo", "Foo.png", () => {}), + new ToolbarItem ("Bar", "Bar.png", () => {}) + } + }; + tracker.Target = page; + + Assert.True (tracker.ToolbarItems.Contains (page.ToolbarItems[0])); + Assert.True (tracker.ToolbarItems.Contains (page.ToolbarItems[1])); + } + + [Test] + public void TrackPreConstructedTabbedPage () + { + var tracker = new ToolbarTracker (); + + var toolbarItem1 = new ToolbarItem ("Foo", "Foo.png", () => { }); + var toolbarItem2 = new ToolbarItem ("Foo", "Foo.png", () => { }); + var toolbarItem3 = new ToolbarItem ("Foo", "Foo.png", () => { }); + + var subPage1 = new ContentPage { + ToolbarItems = {toolbarItem1} + }; + + var subPage2 = new ContentPage { + ToolbarItems = {toolbarItem2, toolbarItem3} + }; + + var tabbedpage = new TabbedPage { + Children = { + subPage1, + subPage2 + } + }; + + tabbedpage.CurrentPage = subPage1; + + tracker.Target = tabbedpage; + + Assert.True (tracker.ToolbarItems.Count () == 1); + Assert.True (tracker.ToolbarItems.First () == subPage1.ToolbarItems[0]); + + bool changed = false; + tracker.CollectionChanged += (sender, args) => changed = true; + + tabbedpage.CurrentPage = subPage2; + + Assert.True (tracker.ToolbarItems.Count () == 2); + Assert.True (tracker.ToolbarItems.First () == subPage2.ToolbarItems[0]); + Assert.True (tracker.ToolbarItems.Last () == subPage2.ToolbarItems[1]); + } + + [Test] + public void AdditionalTargets () + { + var tracker = new ToolbarTracker (); + + var toolbarItem1 = new ToolbarItem ("Foo", "Foo.png", () => { }); + var toolbarItem2 = new ToolbarItem ("Foo", "Foo.png", () => { }); + + var page = new ContentPage { + ToolbarItems = { + toolbarItem1 + } + }; + + var additionalPage = new ContentPage { + ToolbarItems = {toolbarItem2} + }; + + tracker.Target = page; + tracker.AdditionalTargets = new[] {additionalPage}; + + Assert.True (tracker.ToolbarItems.Contains (toolbarItem1)); + Assert.True (tracker.ToolbarItems.Contains (toolbarItem2)); + } + + [Test] + public async Task PushAfterTrackingStarted () + { + var tracker = new ToolbarTracker (); + + var toolbarItem1 = new ToolbarItem ("Foo", "Foo.png", () => { }); + var toolbarItem2 = new ToolbarItem ("Foo", "Foo.png", () => { }); + + var page = new NavigationPage { + ToolbarItems = { + toolbarItem1 + } + }; + + var firstPage = new ContentPage { + ToolbarItems = { toolbarItem2 } + }; + + tracker.Target = page; + + Assert.True (tracker.ToolbarItems.Contains (toolbarItem1)); + Assert.False (tracker.ToolbarItems.Contains (toolbarItem2)); + + await page.Navigation.PushAsync (firstPage); + + Assert.True (tracker.ToolbarItems.Contains (toolbarItem1)); + Assert.True (tracker.ToolbarItems.Contains (toolbarItem2)); + } + + [Test] + public async Task PopAfterTrackingStarted () + { + var tracker = new ToolbarTracker (); + + var toolbarItem1 = new ToolbarItem ("Foo", "Foo.png", () => { }); + var toolbarItem2 = new ToolbarItem ("Foo", "Foo.png", () => { }); + + var page = new NavigationPage (new ContentPage ()) { + ToolbarItems = { + toolbarItem1 + } + }; + + var firstPage = new ContentPage { + ToolbarItems = { toolbarItem2 } + }; + + tracker.Target = page; + + await page.Navigation.PushAsync (firstPage); + + Assert.True (tracker.ToolbarItems.Contains (toolbarItem1)); + Assert.True (tracker.ToolbarItems.Contains (toolbarItem2)); + + await page.Navigation.PopAsync (); + + Assert.True (tracker.ToolbarItems.Contains (toolbarItem1)); + Assert.False (tracker.ToolbarItems.Contains (toolbarItem2)); + } + + [Test] + public void UnsetTarget () + { + var tracker = new ToolbarTracker (); + + ToolbarItem item1, item2; + var page = new ContentPage { + ToolbarItems = { + new ToolbarItem ("Foo", "Foo.png", () => {}), + new ToolbarItem ("Bar", "Bar.png", () => {}) + } + }; + tracker.Target = page; + + Assert.True (tracker.ToolbarItems.Count () == 2); + + tracker.Target = null; + + Assert.False (tracker.ToolbarItems.Any ()); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ToolbarUnitTests.cs b/Xamarin.Forms.Core.UnitTests/ToolbarUnitTests.cs new file mode 100644 index 00000000..efe07ebf --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ToolbarUnitTests.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ToolbarUnitTests : BaseTestFixture + { + [Test] + public void TestConstructor () + { + Toolbar toolbar = new Toolbar (); + + Assert.That (toolbar.Items, Is.Empty); + } + + [Test] + public void TestAdd () + { + Toolbar toolbar = new Toolbar (); + + ToolbarItem item = new ToolbarItem ("Foo", "Bar.jpg", () => {}); + toolbar.Add (item); + + Assert.AreEqual (item, toolbar.Items [0]); + } + + [Test] + public void TestRemove () + { + Toolbar toolbar = new Toolbar (); + + ToolbarItem item = new ToolbarItem ("Foo", "Bar.jpg", () => {}); + ToolbarItem item2 = new ToolbarItem ("Foo", "Bar.jpg", () => {}); + toolbar.Add (item); + toolbar.Add (item2); + + toolbar.Remove (item); + + Assert.AreEqual (item2, toolbar.Items [0]); + } + + [Test] + public void TestItemAdded () + { + Toolbar toolbar = new Toolbar (); + + ToolbarItem item = new ToolbarItem ("Foo", "Bar.jpg", () => {}); + + bool added = false; + toolbar.ItemAdded += (sender, e) => added = true; + + toolbar.Add (item); + + Assert.True (added); + + added = false; + toolbar.Add (item); + + Assert.False (added); + } + + [Test] + public void TestItemRemoved () + { + Toolbar toolbar = new Toolbar (); + + ToolbarItem item = new ToolbarItem ("Foo", "Bar.jpg", () => {}); + toolbar.Add (item); + + bool removed = false; + toolbar.ItemRemoved += (sender, e) => removed = true; + + toolbar.Remove (item); + + Assert.True (removed); + } + + [Test] + public void TestClear () + { + var toolbar = new Toolbar (); + + var item = new ToolbarItem ("Foo", "Bar.jpg", () => {}); + var item2 = new ToolbarItem ("Foo", "Bar.jpg", () => {}); + + toolbar.Add (item); + toolbar.Add (item2); + + toolbar.Clear (); + + Assert.That (toolbar.Items, Is.Empty); + } + + [Test] + public void ToolBarItemAddedEventArgs () + { + var toolbar = new Toolbar (); + + var item = new ToolbarItem ("Foo", "Bar.jpg", () => { }); + var item2 = new ToolbarItem ("Foo", "Bar.jpg", () => { }); + + ToolbarItem itemArg = null; + + toolbar.ItemAdded += (s, e) => { + itemArg = e.ToolbarItem; + }; + + toolbar.Add (item); + Assert.AreEqual (item, itemArg); + + toolbar.Add (item2); + Assert.AreEqual (item2, itemArg); + } + + [Test] + public void ToolBarItemRemovedEventArgs () + { + var toolbar = new Toolbar (); + + var item = new ToolbarItem ("Foo", "Bar.jpg", () => { }); + var item2 = new ToolbarItem ("Foo", "Bar.jpg", () => { }); + + toolbar.Add (item); + toolbar.Add (item2); + + ToolbarItem itemArg = null; + + toolbar.ItemRemoved += (s, e) => { + itemArg = e.ToolbarItem; + }; + + toolbar.Remove (item); + Assert.AreEqual (item, itemArg); + + toolbar.Remove (item2); + Assert.AreEqual (item2, itemArg); + } + } + +} diff --git a/Xamarin.Forms.Core.UnitTests/TriggerTests.cs b/Xamarin.Forms.Core.UnitTests/TriggerTests.cs new file mode 100644 index 00000000..1fe16669 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TriggerTests.cs @@ -0,0 +1,116 @@ +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TriggerTests : BaseTestFixture + { + class MockElement : VisualElement + { + } + + [Test] + public void SettersAppliedOnConditionChanged () + { + var conditionbp = BindableProperty.Create ("foo", typeof(string), typeof(BindableObject), null); + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var trigger = new Trigger (typeof(VisualElement)) { + Property = conditionbp, + Value = "foobar", + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + + element.SetValue (setterbp, "default"); + element.Triggers.Add (trigger); + + Assert.AreEqual ("default", element.GetValue (setterbp)); + element.SetValue (conditionbp, "foobar"); + Assert.AreEqual ("qux", element.GetValue (setterbp)); + element.SetValue (conditionbp, ""); + Assert.AreEqual ("default", element.GetValue (setterbp)); + } + + [Test] + public void SettersAppliedOnAttachIfConditionIsTrue () + { + var conditionbp = BindableProperty.Create ("foo", typeof(string), typeof(BindableObject), null); + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var trigger = new Trigger (typeof(VisualElement)) { + Property = conditionbp, + Value = "foobar", + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + + element.SetValue (setterbp, "default"); + element.SetValue (conditionbp, "foobar"); + Assert.AreEqual ("default", element.GetValue (setterbp)); + element.Triggers.Add (trigger); + Assert.AreEqual ("qux", element.GetValue (setterbp)); + } + + [Test] + public void SettersUnappliedOnDetach () + { + var conditionbp = BindableProperty.Create ("foo", typeof(string), typeof(BindableObject), null); + var setterbp = BindableProperty.Create ("bar", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var trigger = new Trigger (typeof(VisualElement)) { + Property = conditionbp, + Value = "foobar", + Setters = { + new Setter { Property = setterbp, Value = "qux" }, + } + }; + + element.SetValue (setterbp, "default"); + element.Triggers.Add (trigger); + + Assert.AreEqual ("default", element.GetValue (setterbp)); + element.SetValue (conditionbp, "foobar"); + Assert.AreEqual ("qux", element.GetValue (setterbp)); + element.Triggers.Remove (trigger); + Assert.AreEqual ("default", element.GetValue (setterbp)); + } + + [Test] + public void EnterAndExitActionsTriggered () + { + var conditionbp = BindableProperty.Create ("foo", typeof(string), typeof(BindableObject), null); + var element = new MockElement (); + var enteraction = new MockTriggerAction (); + var exitaction = new MockTriggerAction (); + var trigger = new Trigger (typeof(VisualElement)) { + Property = conditionbp, + Value = "foobar", + EnterActions = { + enteraction + }, + ExitActions = { + exitaction + } + }; + + Assert.False (enteraction.Invoked); + Assert.False (exitaction.Invoked); + element.Triggers.Add (trigger); + Assert.False (enteraction.Invoked); + Assert.False (exitaction.Invoked); + element.SetValue (conditionbp, "foobar"); + Assert.True (enteraction.Invoked); + Assert.False (exitaction.Invoked); + + enteraction.Invoked = exitaction.Invoked = false; + Assert.False (enteraction.Invoked); + Assert.False (exitaction.Invoked); + element.SetValue (conditionbp, ""); + Assert.False (enteraction.Invoked); + Assert.True (exitaction.Invoked); + } + } +}
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/TypeUnitTests.cs b/Xamarin.Forms.Core.UnitTests/TypeUnitTests.cs new file mode 100644 index 00000000..70c40a55 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/TypeUnitTests.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class TypeUnitTests : BaseTestFixture + { + [Test] + public void TestVec2 () + { + var vec2 = new Vec2 (); + + Assert.AreEqual (0, vec2.X); + Assert.AreEqual (0, vec2.Y); + + vec2 = new Vec2 (2, 3); + + Assert.AreEqual (2, vec2.X); + Assert.AreEqual (3, vec2.Y); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/UnitPlatform.cs b/Xamarin.Forms.Core.UnitTests/UnitPlatform.cs new file mode 100644 index 00000000..8dbf3e04 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/UnitPlatform.cs @@ -0,0 +1,40 @@ +using System; + +namespace Xamarin.Forms.Core.UnitTests +{ + public class UnitPlatform : IPlatform + { + Page page; + Func<VisualElement, double, double, SizeRequest> getNativeSizeFunc; + readonly bool useRealisticLabelMeasure; + + public UnitPlatform (Func<VisualElement, double, double, SizeRequest> getNativeSizeFunc = null, bool useRealisticLabelMeasure = false) + { + this.getNativeSizeFunc = getNativeSizeFunc; + this.useRealisticLabelMeasure = useRealisticLabelMeasure; + } + + public SizeRequest GetNativeSize (VisualElement view, double widthConstraint, double heightConstraint) + { + if (getNativeSizeFunc != null) + return getNativeSizeFunc (view, widthConstraint, heightConstraint); + // EVERYTHING IS 100 x 20 + + var label = view as Label; + if (label != null && useRealisticLabelMeasure) { + var letterSize = new Size (5, 10); + var w = label.Text.Length * letterSize.Width; + var h = letterSize.Height; + if (!double.IsPositiveInfinity (widthConstraint) && w > widthConstraint) { + h = ((int) w / (int) widthConstraint) * letterSize.Height; + w = widthConstraint - (widthConstraint % letterSize.Width); + + } + return new SizeRequest (new Size (w, h), new Size (Math.Min (10, w), h)); + } + + return new SizeRequest(new Size (100, 20)); + } + } + +} diff --git a/Xamarin.Forms.Core.UnitTests/UriImageSourceTests.cs b/Xamarin.Forms.Core.UnitTests/UriImageSourceTests.cs new file mode 100644 index 00000000..39bce931 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/UriImageSourceTests.cs @@ -0,0 +1,145 @@ +using System; +using NUnit.Framework; +using System.Threading; +using System.IO; +using System.Threading.Tasks; +using System.Reflection; +using System.IO.IsolatedStorage; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class UriImageSourceTests : BaseTestFixture + { + IsolatedStorageFile NativeStore { get; set; } + + [SetUp] + public override void Setup() + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (getStreamAsync: GetStreamAsync); + NativeStore = IsolatedStorageFile.GetUserStoreForAssembly (); + networkcalls = 0; + } + + [TearDown] + public override void TearDown() + { + base.TearDown (); + Device.PlatformServices = null; + string cacheName = "ImageLoaderCache"; + if (NativeStore.DirectoryExists (cacheName)) { + foreach (var f in NativeStore.GetFileNames (cacheName + "/*")) + NativeStore.DeleteFile (Path.Combine (cacheName, f)); + } + NativeStore.Dispose (); + NativeStore = null; + } + + static Random rnd = new Random (); + static int networkcalls = 0; + static async Task<Stream> GetStreamAsync (Uri uri, CancellationToken cancellationToken) + { + await Task.Delay (rnd.Next (30, 2000)); + if (cancellationToken.IsCancellationRequested) + throw new TaskCanceledException (); + networkcalls++; + return typeof(UriImageSourceTests).Assembly.GetManifestResourceStream (uri.LocalPath.Substring (1)); + } + + [Test] + [Ignore] + public void LoadImageFromStream () + { + var loader = new UriImageSource { + Uri = new Uri ("http://foo.com/Images/crimson.jpg"), + }; + Stream s0 = loader.GetStreamAsync ().Result; + + Assert.AreEqual (79109, s0.Length); + } + + [Test] + [Ignore] + public void SecondCallLoadFromCache () + { + var loader = new UriImageSource { + Uri = new Uri ("http://foo.com/Images/crimson.jpg"), + }; + Assert.AreEqual (0, networkcalls); + + using (var s0 = loader.GetStreamAsync ().Result) { + Assert.AreEqual (79109, s0.Length); + Assert.AreEqual (1, networkcalls); + } + + using (var s1 = loader.GetStreamAsync ().Result) { + Assert.AreEqual (79109, s1.Length); + Assert.AreEqual (1, networkcalls); + } + } + + [Test] + [Ignore] + public void DoNotKeepFailedRetrieveInCache () + { + var loader = new UriImageSource { + Uri = new Uri ("http://foo.com/missing.png"), + }; + Assert.AreEqual (0, networkcalls); + + var s0 = loader.GetStreamAsync ().Result; + Assert.IsNull (s0); + Assert.AreEqual (1, networkcalls); + + var s1 = loader.GetStreamAsync ().Result; + Assert.IsNull (s1); + Assert.AreEqual (2, networkcalls); + } + + [Test] + [Ignore] + public void ConcurrentCallsOnSameUriAreQueued () + { + var loader = new UriImageSource { + Uri = new Uri ("http://foo.com/Images/crimson.jpg"), + }; + Assert.AreEqual (0, networkcalls); + + var t0 = loader.GetStreamAsync (); + var t1 = loader.GetStreamAsync (); + + //var s0 = t0.Result; + using (var s1 = t1.Result) { + Assert.AreEqual (1, networkcalls); + Assert.AreEqual (79109, s1.Length); + } + } + + [Test] + public void NullUriDoesNotCrash () + { + var loader = new UriImageSource (); + Assert.DoesNotThrow (() => { + loader.Uri = null; + }); + } + + [Test] + public void UrlHashKeyAreTheSame () + { + var urlHash1 = Device.PlatformServices.GetMD5Hash ("http://www.optipess.com/wp-content/uploads/2010/08/02_Bad-Comics6-10.png?a=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbasdasdasdasdasasdasdasdasdasd"); + var urlHash2 = Device.PlatformServices.GetMD5Hash ("http://www.optipess.com/wp-content/uploads/2010/08/02_Bad-Comics6-10.png?a=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbasdasdasdasdasasdasdasdasdasd"); + Assert.IsTrue (urlHash1 == urlHash2); + } + + [Test] + public void UrlHashKeyAreNotTheSame () + { + var urlHash1 = Device.PlatformServices.GetMD5Hash ("http://www.optipess.com/wp-content/uploads/2010/08/02_Bad-Comics6-10.png?a=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbasdasdasdasdasasdasdasdasdasd"); + var urlHash2 = Device.PlatformServices.GetMD5Hash ("http://www.optipess.com/wp-content/uploads/2010/08/02_Bad-Comics6-10.png?a=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbasdasdasdasdasasdasda"); + Assert.IsTrue (urlHash1 != urlHash2); + } + + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ViewCellTests.cs b/Xamarin.Forms.Core.UnitTests/ViewCellTests.cs new file mode 100644 index 00000000..2a8647d4 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ViewCellTests.cs @@ -0,0 +1,65 @@ +using System; +using NUnit.Framework; + + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ViewCellTests : BaseTestFixture + { + [Test] + public void SetParentBeforeView () + { + var parent = new View { Platform = new UnitPlatform () }; + var child = new View (); + var viewCell = new ViewCell (); + + Assert.Null (viewCell.View); + Assert.DoesNotThrow (() => viewCell.Parent = parent); + + viewCell.View = child; + Assert.AreSame (parent, viewCell.Parent); + Assert.AreSame (viewCell, child.Parent); + Assert.AreSame (parent.Platform, child.Platform); + } + + [Test] + //issue 550 + public void SetBindingContextBeforeParent () + { + var parent = new View { + Platform = new UnitPlatform (), + BindingContext = new object (), + }; + + var itemcontext = new object (); + var cell = new ViewCell { View = new Label ()}; + cell.BindingContext = itemcontext; + cell.Parent = parent; + + Assert.AreSame (itemcontext, cell.View.BindingContext); + } + + [Test] + public void SetBindingContextBeforeView () + { + var context = new object (); + var view = new View (); + var cell = new ViewCell (); + cell.BindingContext = context; + cell.View = view; + Assert.AreSame (context, view.BindingContext); + } + + [Test] + public void SetViewBeforeBindingContext () + { + var context = new object (); + var view = new View (); + var cell = new ViewCell (); + cell.View = view; + cell.BindingContext = context; + Assert.AreSame (context, view.BindingContext); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/ViewUnitTests.cs b/Xamarin.Forms.Core.UnitTests/ViewUnitTests.cs new file mode 100644 index 00000000..97700ca4 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/ViewUnitTests.cs @@ -0,0 +1,868 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Threading.Tasks; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class ViewUnitTests : BaseTestFixture + { + [SetUp] + public override void Setup () + { + base.Setup (); + Device.PlatformServices = new MockPlatformServices (); + } + + [TearDown] + public override void TearDown () + { + base.TearDown (); + Device.PlatformServices = null; + } + + [Test] + public void TestLayout () + { + View view = new View (); + view.Layout (new Rectangle (50, 25, 100, 200)); + + Assert.AreEqual (view.X, 50); + Assert.AreEqual (view.Y, 25); + Assert.AreEqual (view.Width, 100); + Assert.AreEqual (view.Height, 200); + } + + [Test] + public void TestPreferredSize () + { + View view = new View { + IsPlatformEnabled = true, + Platform = new UnitPlatform () + }; + + bool fired = false; + view.MeasureInvalidated += (sender, e) => fired = true; + + view.WidthRequest = 200; + view.HeightRequest = 300; + + Assert.True (fired); + + var result = view.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity).Request; + Assert.AreEqual (new Size (200, 300), result); + } + + [Test] + public void TestSizeChangedEvent () + { + View view = new View (); + + bool fired = false; + view.SizeChanged += (sender, e) => fired = true; + + view.Layout (new Rectangle (0, 0, 100, 100)); + + Assert.True (fired); + } + + [Test] + public void TestOpacityClamping () + { + var view = new View (); + + view.Opacity = -1; + Assert.AreEqual (0, view.Opacity); + + view.Opacity = 2; + Assert.AreEqual (1, view.Opacity); + } + + [Test] + public void TestMeasureInvalidatedFiredOnVisibilityChanged () + { + var view = new View {IsVisible = false}; + bool signaled = false; + view.MeasureInvalidated += (sender, e) => { + signaled = true; + }; + view.IsVisible = true; + Assert.True (signaled); + } + + [Test] + public void TestOnPlatformiOS () + { + var view = new View (); + + bool ios = false; + bool android = false; + bool winphone = false; + + Device.OS = TargetPlatform.iOS; + + Device.OnPlatform ( + iOS: () => ios = true, + Android: () => android = true, + WinPhone: () => winphone = true); + + Assert.True (ios); + Assert.False (android); + Assert.False (winphone); + } + + [Test] + public void TestOnPlatformAndroid () + { + var view = new View (); + + bool ios = false; + bool android = false; + bool winphone = false; + + Device.OS = TargetPlatform.Android; + + Device.OnPlatform ( + iOS: () => ios = true, + Android: () => android = true, + WinPhone: () => winphone = true); + + Assert.False (ios); + Assert.True (android); + Assert.False (winphone); + } + + [Test] + public void TestOnPlatformWinPhone () + { + var view = new View (); + + bool ios = false; + bool android = false; + bool winphone = false; + + Device.OS = TargetPlatform.WinPhone; + + Device.OnPlatform ( + iOS: () => ios = true, + Android: () => android = true, + WinPhone: () => winphone = true); + + Assert.False (ios); + Assert.False (android); + Assert.True (winphone); + } + + [Test] + public void TestOnPlatformDefault () + { + var view = new View (); + + bool ios = false; + bool android = false; + + Device.OS = TargetPlatform.Android; + + Device.OnPlatform ( + iOS: () => ios = false, + Default: () => android = true); + + Assert.False (ios); + Assert.True (android); + } + + [Test] + public void TestOnPlatformNoOpWithoutDefault () + { + bool any = false; + Device.OS = TargetPlatform.Other; + + Device.OnPlatform ( + iOS: () => any = true, + Android: () => any = true, + WinPhone: () => any = true); + + Assert.False (any); + } + + [Test] + public void TestDefaultOniOS () + { + bool defaultExecuted = false; + + Device.OS = TargetPlatform.iOS; + + Device.OnPlatform ( + Android: () => { }, + WinPhone: () => { }, + Default:() => defaultExecuted = true); + + Assert.True (defaultExecuted); + } + + [Test] + public void TestDefaultOnAndroid () + { + bool defaultExecuted = false; + + Device.OS = TargetPlatform.Android; + + Device.OnPlatform ( + iOS: () => { }, + WinPhone: () => { }, + Default:() => defaultExecuted = true); + + Assert.True (defaultExecuted); + } + + [Test] + public void TestDefaultOnWinPhone () + { + bool defaultExecuted = false; + + Device.OS = TargetPlatform.WinPhone; + + Device.OnPlatform ( + iOS: () => { }, + Android: () => { }, + Default:() => defaultExecuted = true); + + Assert.True (defaultExecuted); + } + + [Test] + public void TestDefaultOnOther () + { + bool defaultExecuted = false; + + Device.OS = TargetPlatform.Other; + + Device.OnPlatform ( + iOS: () => { }, + Android: () => { }, + WinPhone: () => { }, + Default:() => defaultExecuted = true); + + Assert.True (defaultExecuted); + } + + [Test] + public void TestNativeStateConsistent () + { + var view = new View { IsPlatformEnabled = true }; + + Assert.True (view.IsNativeStateConsistent); + + view.IsNativeStateConsistent = false; + + Assert.False (view.IsNativeStateConsistent); + + bool sizeChanged = false; + view.MeasureInvalidated += (sender, args) => { + sizeChanged = true; + }; + + view.IsNativeStateConsistent = true; + + Assert.True (sizeChanged); + + sizeChanged = false; + view.IsNativeStateConsistent = true; + + Assert.False (sizeChanged); + } + + [Test] + public void TestFadeTo () + { + var view = new View {IsPlatformEnabled = true, Platform = new UnitPlatform ()}; + Ticker.Default = new BlockingTicker (); + + view.FadeTo (0.1); + + Assert.True (Math.Abs (0.1 - view.Opacity) < 0.001); + } + + [Test] + public void TestTranslateTo () + { + var view = new View {IsPlatformEnabled = true, Platform = new UnitPlatform ()}; + Ticker.Default = new BlockingTicker (); + + view.TranslateTo (100, 50); + + Assert.AreEqual (100, view.TranslationX); + Assert.AreEqual (50, view.TranslationY); + } + + [Test] + public void ScaleTo () + { + var view = new View {IsPlatformEnabled = true, Platform = new UnitPlatform ()}; + Ticker.Default = new BlockingTicker (); + + view.ScaleTo (2); + + Assert.AreEqual (2, view.Scale); + } + + [Test] + public void TestNativeSizeChanged () + { + var view = new View (); + + bool sizeChanged = false; + view.MeasureInvalidated += (sender, args) => sizeChanged = true; + + ((IVisualElementController)view).NativeSizeChanged (); + + Assert.True (sizeChanged); + } + + [Test] + public void TestRotateTo () + { + var view = new View {IsPlatformEnabled = true, Platform = new UnitPlatform ()}; + Ticker.Default = new BlockingTicker (); + + view.RotateTo (25); + + Assert.That (view.Rotation, Is.EqualTo (25).Within (0.001)); + } + + [Test] + public void TestRotateYTo () + { + var view = new View {IsPlatformEnabled = true, Platform = new UnitPlatform ()}; + Ticker.Default = new BlockingTicker (); + + view.RotateYTo (25); + + Assert.That (view.RotationY, Is.EqualTo (25).Within (0.001)); + } + + [Test] + public void TestRotateXTo () + { + var view = new View {IsPlatformEnabled = true, Platform = new UnitPlatform ()}; + Ticker.Default = new BlockingTicker (); + + view.RotateXTo (25); + + Assert.That (view.RotationX, Is.EqualTo (25).Within (0.001)); + } + + [Test] + public void TestRelRotateTo () + { + var view = new View {Rotation = 30, IsPlatformEnabled = true, Platform = new UnitPlatform ()}; + Ticker.Default = new BlockingTicker (); + + view.RelRotateTo (20); + + Assert.That (view.Rotation, Is.EqualTo (50).Within (0.001)); + } + + [Test] + public void TestRelScaleTo () + { + var view = new View {Scale = 1, IsPlatformEnabled = true, Platform = new UnitPlatform ()}; + Ticker.Default = new BlockingTicker (); + + view.RelScaleTo (1); + + Assert.That (view.Scale, Is.EqualTo (2).Within (0.001)); + } + + class ParentSignalView : View + { + public bool ParentSet { get; set; } + + protected override void OnParentSet () + { + ParentSet = true; + base.OnParentSet (); + } + } + + [Test] + public void TestDoubleSetParent () + { + var view = new ParentSignalView (); + var parent = new NaiveLayout {Children = {view}}; + + view.ParentSet = false; + view.Parent = parent; + + Assert.False (view.ParentSet, "OnParentSet should not be called in the event the parent is already properly set"); + } + + [Test] + public void TestAncestorAdded () + { + var child = new NaiveLayout (); + var view = new NaiveLayout {Children = {child}}; + + bool added = false; + view.DescendantAdded += (sender, arg) => added = true; + + child.Children.Add (new View ()); + + Assert.True (added, "AncestorAdded must fire when adding a child to an ancestor of a view."); + } + + [Test] + public void TestAncestorRemoved () + { + var ancestor = new View (); + var child = new NaiveLayout {Children = {ancestor}}; + var view = new NaiveLayout {Children = {child}}; + + bool removed = false; + view.DescendantRemoved += (sender, arg) => removed = true; + + child.Children.Remove (ancestor); + Assert.True (removed, "AncestorRemoved must fire when removing a child from an ancestor of a view."); + } + + [Test] + public void TestOnPlatformGeneric () + { + Device.OS = TargetPlatform.WinPhone; + Assert.AreEqual (3, Device.OnPlatform (1, 2, 3)); + + Device.OS = TargetPlatform.iOS; + Assert.AreEqual (1, Device.OnPlatform (1, 2, 3)); + + Device.OS = TargetPlatform.Android; + Assert.AreEqual (2, Device.OnPlatform (1, 2, 3)); + + Device.OS = TargetPlatform.Other; + Assert.AreEqual (1, Device.OnPlatform (1, 2, 3)); + } + + [Test] + public void TestBatching () + { + var view = new View (); + + bool committed = false; + view.BatchCommitted += (sender, arg) => committed = true; + + view.BatchBegin (); + + Assert.True (view.Batched); + + view.BatchBegin (); + + Assert.True (view.Batched); + + view.BatchCommit (); + + Assert.True (view.Batched); + Assert.False (committed); + + view.BatchCommit (); + + Assert.False (view.Batched); + Assert.True (committed); + } + + [Test] + public void IsPlatformEnabled () + { + var view = new View (); + + Assert.False (view.IsPlatformEnabled); + + view.IsPlatformEnabled = true; + + Assert.True (view.IsPlatformEnabled); + + view.IsPlatformEnabled = false; + + Assert.False (view.IsPlatformEnabled); + } + + [Test] + public void TestBindingContextChaining () + { + View child; + var group = new NaiveLayout { + Children = { (child = new View ()) } + }; + + var context = new object (); + group.BindingContext = context; + + Assert.AreEqual (context, child.BindingContext); + } + + + + [Test] + public void FocusWithoutSubscriber () + { + var view = new View (); + + Assert.False (view.Focus ()); + } + + [Test] + public void FocusWithSubscriber ([Values(true, false)] bool result) + { + var view = new View (); + view.FocusChangeRequested += (sender, arg) => arg.Result = result; + Assert.True (view.Focus () == result); + } + + [Test] + public void DoNotSignalWhenAlreadyFocused () + { + var view = new View (); + view.SetValueCore (VisualElement.IsFocusedPropertyKey, true); + bool signaled = false; + view.FocusChangeRequested += (sender, args) => signaled = true; + + Assert.True (view.Focus (), "View.Focus returned false"); + Assert.False (signaled, "FocusRequested was raised"); + } + + [Test] + public void UnFocus () + { + var view = new View (); + view.SetValueCore (VisualElement.IsFocusedPropertyKey, true); + + var requested = false; + view.FocusChangeRequested += (sender, args) => { + requested = !args.Focus; + }; + + view.Unfocus (); + + Assert.True (requested); + } + + [Test] + public void UnFocusDoesNotFireWhenNotFocused () + { + var view = new View (); + view.SetValueCore (VisualElement.IsFocusedPropertyKey, false); + + var requested = false; + view.FocusChangeRequested += (sender, args) => { + requested = args.Focus; + }; + + view.Unfocus (); + + Assert.False (requested); + } + + [Test] + public void PlatformSet () + { + var view = new View (); + bool set = false; + view.PlatformSet += (sender, args) => set = true; + + view.Platform = new UnitPlatform (); + + Assert.True (set); + } + + [Test] + public void TestFocusedEvent () + { + var view = new View (); + + bool fired = false; + view.Focused += (sender, args) => fired = true; + view.SetValueCore (VisualElement.IsFocusedPropertyKey, true); + + + Assert.True (fired); + } + + [Test] + public void TestUnFocusedEvent () + { + var view = new View (); + view.SetValueCore (VisualElement.IsFocusedPropertyKey, true); + + bool fired = false; + view.Unfocused += (sender, args) => fired = true; + view.SetValueCore (VisualElement.IsFocusedPropertyKey, false); + + Assert.True (fired); + } + + [Test] + public void TestBeginInvokeOnMainThread () + { + Device.PlatformServices = new MockPlatformServices (invokeOnMainThread: action => action ()); + + bool invoked = false; + Device.BeginInvokeOnMainThread (() => invoked = true); + + Assert.True (invoked); + } + + [Test] + public void InvokeOnMainThreadThrowsWhenNull () + { + Device.PlatformServices = null; + Assert.Throws<InvalidOperationException>(() => Device.BeginInvokeOnMainThread (() => { })); + } + + [Test] + public void TestOpenUriAction () + { + var uri = new Uri ("http://www.xamarin.com/"); + var invoked = false; + Device.PlatformServices = new MockPlatformServices (openUriAction: u => { + Assert.AreSame (uri, u); + invoked = true; + }); + + Device.OpenUri (uri); + Assert.True (invoked); + } + + [Test] + public void OpenUriThrowsWhenNull () + { + Device.PlatformServices = null; + var uri = new Uri ("http://www.xamarin.com/"); + Assert.Throws<InvalidOperationException> (() => Device.OpenUri (uri)); + } + + [Test] + public void MinimumWidthRequest () + { + var view = new View (); + + bool signaled = false; + view.MeasureInvalidated += (sender, args) => signaled = true; + + view.MinimumWidthRequest = 10; + Assert.True (signaled); + Assert.AreEqual (10, view.MinimumWidthRequest); + + signaled = false; + view.MinimumWidthRequest = 10; + Assert.False (signaled); + } + + [Test] + public void MinimumHeightRequest () + { + var view = new View (); + + bool signaled = false; + view.MeasureInvalidated += (sender, args) => signaled = true; + + view.MinimumHeightRequest = 10; + Assert.True (signaled); + Assert.AreEqual (10, view.MinimumHeightRequest); + + signaled = false; + view.MinimumHeightRequest = 10; + Assert.False (signaled); + } + + [Test] + public void MinimumWidthRequestInSizeRequest () + { + var view = new View { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + view.HeightRequest = 20; + view.WidthRequest = 200; + view.MinimumWidthRequest = 100; + + var result = view.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + Assert.AreEqual (new Size (200, 20), result.Request); + Assert.AreEqual (new Size (100, 20), result.Minimum); + } + + [Test] + public void MinimumHeightRequestInSizeRequest () + { + var view = new View { + Platform = new UnitPlatform (), + IsPlatformEnabled = true + }; + + view.HeightRequest = 200; + view.WidthRequest = 20; + view.MinimumHeightRequest = 100; + + var result = view.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + Assert.AreEqual (new Size (20, 200), result.Request); + Assert.AreEqual (new Size (20, 100), result.Minimum); + } + + [Test] + public void StartTimerSimple () + { + Device.PlatformServices = new MockPlatformServices (); + var task = new TaskCompletionSource<bool> (); + + Task.Factory.StartNew (() => Device.StartTimer (TimeSpan.FromMilliseconds (200), () => { + task.SetResult (false); + return false; + })); + + task.Task.Wait (); + Assert.False (task.Task.Result); + Device.PlatformServices = null; + } + + [Test] + public void StartTimerMultiple () + { + Device.PlatformServices = new MockPlatformServices (); + var task = new TaskCompletionSource<int> (); + + int steps = 0; + Task.Factory.StartNew (() => Device.StartTimer (TimeSpan.FromMilliseconds (200), () => { + steps++; + if (steps < 2) + return true; + task.SetResult (steps); + return false; + })); + + task.Task.Wait (); + Assert.AreEqual (2, task.Task.Result); + Device.PlatformServices = null; + } + + [Test] + public void BindingsApplyAfterViewAddedToParentWithContextSet() + { + var parent = new NaiveLayout(); + parent.BindingContext = new MockViewModel { Text = "test" }; + + var child = new Entry(); + child.SetBinding (Entry.TextProperty, new Binding ("Text")); + + parent.Children.Add (child); + + Assert.That (child.BindingContext, Is.SameAs (parent.BindingContext)); + Assert.That (child.Text, Is.EqualTo ("test")); + } + + [Test] + public void IdIsUnique () + { + var view1 = new View (); + var view2 = new View (); + + Assert.True (view1.Id != view2.Id); + } + + [Test] + public void MockBounds () + { + var view = new View (); + view.Layout (new Rectangle (10, 20, 30, 40)); + + bool changed = false; + view.PropertyChanged += (sender, args) => { + if (args.PropertyName == View.XProperty.PropertyName || + args.PropertyName == View.YProperty.PropertyName || + args.PropertyName == View.WidthProperty.PropertyName || + args.PropertyName == View.HeightProperty.PropertyName) + changed = true; + }; + + view.SizeChanged += (sender, args) => changed = true; + + view.MockBounds (new Rectangle (5, 10, 15, 20)); + + Assert.AreEqual (new Rectangle (5, 10, 15, 20), view.Bounds); + Assert.False (changed); + + view.UnmockBounds (); + + Assert.AreEqual (new Rectangle (10, 20, 30, 40), view.Bounds); + Assert.False (changed); + } + + [Test] + public void AddGestureRecognizer () + { + var view = new View (); + var gestureRecognizer = new TapGestureRecognizer (); + + view.GestureRecognizers.Add (gestureRecognizer); + + Assert.True (view.GestureRecognizers.Contains (gestureRecognizer)); + } + + [Test] + public void AddGestureRecognizerSetsParent () + { + var view = new View (); + var gestureRecognizer = new TapGestureRecognizer (); + + view.GestureRecognizers.Add (gestureRecognizer); + + Assert.AreEqual (view, gestureRecognizer.Parent); + } + + [Test] + public void RemoveGestureRecognizerUnsetsParent () + { + var view = new View (); + var gestureRecognizer = new TapGestureRecognizer (); + + view.GestureRecognizers.Add (gestureRecognizer); + view.GestureRecognizers.Remove (gestureRecognizer); + + Assert.Null (gestureRecognizer.Parent); + } + + [Test] + public void WidthRequestEffectsGetSizeRequest () + { + var view = new View (); + view.IsPlatformEnabled = true; + view.Platform = new UnitPlatform ((ve, widthConstraint, heightConstraint) => { + if (widthConstraint < 30) + return new SizeRequest (new Size (40, 50)); + return new SizeRequest(new Size(20, 100)); + }); + + view.WidthRequest = 20; + var request = view.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + + Assert.AreEqual (new Size (20, 50), request.Request); + } + + [Test] + public void HeightRequestEffectsGetSizeRequest () + { + var view = new View (); + view.IsPlatformEnabled = true; + view.Platform = new UnitPlatform ((ve, widthConstraint, heightConstraint) => { + if (heightConstraint < 30) + return new SizeRequest (new Size (40, 50)); + return new SizeRequest(new Size(20, 100)); + }); + + view.HeightRequest = 20; + var request = view.GetSizeRequest (double.PositiveInfinity, double.PositiveInfinity); + + Assert.AreEqual (new Size (40, 20), request.Request); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/WebViewUnitTests.cs b/Xamarin.Forms.Core.UnitTests/WebViewUnitTests.cs new file mode 100644 index 00000000..d441f4b8 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/WebViewUnitTests.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using NUnit.Framework; + +namespace Xamarin.Forms.Core.UnitTests +{ + [TestFixture] + public class WebViewUnitTests : BaseTestFixture + { + [Test] + public void TestSourceImplicitConversion () + { + var web = new WebView (); + Assert.Null (web.Source); + web.Source = "http://www.google.com"; + Assert.NotNull (web.Source); + Assert.True (web.Source is UrlWebViewSource); + Assert.AreEqual ("http://www.google.com", ((UrlWebViewSource)web.Source).Url); + } + + [Test] + public void TestSourceChangedPropagation () + { + var source = new UrlWebViewSource {Url ="http://www.google.com"}; + var web = new WebView { Source = source }; + bool signaled = false; + web.PropertyChanged += (sender, args) => { + if (args.PropertyName == WebView.SourceProperty.PropertyName) + signaled = true; + }; + Assert.False (signaled); + source.Url = "http://www.xamarin.com"; + Assert.True (signaled); + } + + [Test] + public void TestSourceDisconnected () + { + var source = new UrlWebViewSource {Url="http://www.google.com"}; + var web = new WebView { Source = source }; + web.Source = new UrlWebViewSource {Url="Foo"}; + bool signaled = false; + web.PropertyChanged += (sender, args) => { + if (args.PropertyName == WebView.SourceProperty.PropertyName) + signaled = true; + }; + Assert.False (signaled); + source.Url = "http://www.xamarin.com"; + Assert.False (signaled); + } + + class ViewModel + { + public string HTML { get; set; } = "<html><body><p>This is a WebView!</p></body></html>"; + + public string URL { get; set; } = "http://xamarin.com"; + + } + + [Test] + public void TestBindingContextPropagatesToSource () + { + var htmlWebView = new WebView { + }; + var urlWebView = new WebView { + }; + + var htmlSource = new HtmlWebViewSource (); + htmlSource.SetBinding (HtmlWebViewSource.HtmlProperty, "HTML"); + htmlWebView.Source = htmlSource; + + var urlSource = new UrlWebViewSource (); + urlSource.SetBinding (UrlWebViewSource.UrlProperty, "URL"); + urlWebView.Source = urlSource; + + var viewModel = new ViewModel (); + + var container = new StackLayout { + BindingContext = viewModel, + Padding = new Size (20, 20), + Children = { + htmlWebView, + urlWebView + } + }; + + Assert.AreEqual ("<html><body><p>This is a WebView!</p></body></html>", htmlSource.Html); + Assert.AreEqual ("http://xamarin.com", urlSource.Url); + } + } +} diff --git a/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj b/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj new file mode 100644 index 00000000..a7324ca1 --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/Xamarin.Forms.Core.UnitTests.csproj @@ -0,0 +1,206 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{00259593-A283-47A5-ACB7-9C3819B16364}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Xamarin.Forms.Core.UnitTests</RootNamespace> + <AssemblyName>Xamarin.Forms.Core.UnitTests</AssemblyName> + <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> + <RestorePackages>true</RestorePackages> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>bin\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>bin\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Turkey|AnyCPU'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\Turkey\</OutputPath> + <DefineConstants>TRACE;DEBUG;TURKEY</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>AnyCPU</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <ItemGroup> + <Reference Include="nunit.framework"> + <HintPath>..\packages\NUnit.2.6.2\lib\nunit.framework.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> + </ItemGroup> + <ItemGroup> + <Compile Include="AnimatableKeyTests.cs" /> + <Compile Include="BaseTestFixture.cs" /> + <Compile Include="AbsoluteLayoutTests.cs" /> + <Compile Include="BindableObjectExtensionTests.cs" /> + <Compile Include="BindableObjectUnitTests.cs" /> + <Compile Include="BindablePropertyUnitTests.cs" /> + <Compile Include="BindingBaseUnitTests.cs" /> + <Compile Include="BindingExpressionTests.cs" /> + <Compile Include="BindingTests.cs" /> + <Compile Include="BindingTypeConverterTests.cs" /> + <Compile Include="BindingUnitTests.cs" /> + <Compile Include="ButtonUnitTest.cs" /> + <Compile Include="CarouselPageTests.cs" /> + <Compile Include="CellTests.cs" /> + <Compile Include="ColorUnitTests.cs" /> + <Compile Include="CommandSourceTests.cs" /> + <Compile Include="CommandTests.cs" /> + <Compile Include="ContentFormUnitTests.cs" /> + <Compile Include="ContraintTypeConverterTests.cs" /> + <Compile Include="ControlTemplateTests.cs" /> + <Compile Include="DataTemplateSelectorTests.cs" /> + <Compile Include="DatePickerUnitTest.cs" /> + <Compile Include="DependencyServiceTests.cs" /> + <Compile Include="DistanceTests.cs" /> + <Compile Include="DynamicBindingContextTests.cs" /> + <Compile Include="EasingTests.cs" /> + <Compile Include="EffectTests.cs" /> + <Compile Include="ElementTests.cs" /> + <Compile Include="EntryCellTests.cs" /> + <Compile Include="EntryUnitTests.cs" /> + <Compile Include="FluentTests.cs" /> + <Compile Include="FontUnitTests.cs" /> + <Compile Include="FormattedStringTests.cs" /> + <Compile Include="FrameUnitTests.cs" /> + <Compile Include="GeocoderUnitTests.cs" /> + <Compile Include="GroupViewUnitTests.cs" /> + <Compile Include="ImageTests.cs" /> + <Compile Include="KeyboardTests.cs" /> + <Compile Include="LabelTests.cs" /> + <Compile Include="ListProxyTests.cs" /> + <Compile Include="ListViewTests.cs" /> + <Compile Include="MapSpanTests.cs" /> + <Compile Include="MapTests.cs" /> + <Compile Include="MarginTests.cs" /> + <Compile Include="MasterDetailFormUnitTests.cs" /> + <Compile Include="MenuItemTests.cs" /> + <Compile Include="MessagingCenterTests.cs" /> + <Compile Include="MockViewModel.cs" /> + <Compile Include="MotionTests.cs" /> + <Compile Include="MultiPageTests.cs" /> + <Compile Include="NavigationMenuUnitTests.cs" /> + <Compile Include="NavigationModelTests.cs" /> + <Compile Include="NavigationProxyTests.cs" /> + <Compile Include="NavigationUnitTest.cs" /> + <Compile Include="NotifyCollectionChangedEventArgsExtensionsTests.cs" /> + <Compile Include="ObservableWrapperTests.cs" /> + <Compile Include="OpenGLViewUnitTests.cs" /> + <Compile Include="PageTests.cs" /> + <Compile Include="PanGestureRecognizerUnitTests.cs" /> + <Compile Include="PinTests.cs" /> + <Compile Include="PointTests.cs" /> + <Compile Include="PositionTests.cs" /> + <Compile Include="ProgressBarTests.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="RectangleUnitTests.cs" /> + <Compile Include="RegistrarUnitTests.cs" /> + <Compile Include="RelativeLayoutTests.cs" /> + <Compile Include="ScrollViewUnitTests.cs" /> + <Compile Include="SearchBarUnitTests.cs" /> + <Compile Include="SizeTests.cs" /> + <Compile Include="SliderUnitTests.cs" /> + <Compile Include="StackLayoutUnitTests.cs" /> + <Compile Include="StepperUnitTests.cs" /> + <Compile Include="SwitchCellTests.cs" /> + <Compile Include="SwitchUnitTests.cs" /> + <Compile Include="TabbedFormUnitTests.cs" /> + <Compile Include="TableModelTests.cs" /> + <Compile Include="TableRootUnitTests.cs" /> + <Compile Include="TableSectionTests.cs" /> + <Compile Include="TableViewUnitTests.cs" /> + <Compile Include="TapGestureRecognizerTests.cs" /> + <Compile Include="TemplatedItemsListTests.cs" /> + <Compile Include="TextCellTests.cs" /> + <Compile Include="ThicknessTests.cs" /> + <Compile Include="TimePickerUnitTest.cs" /> + <Compile Include="ToolbarItemTests.cs" /> + <Compile Include="ToolbarTrackerTests.cs" /> + <Compile Include="ToolbarUnitTests.cs" /> + <Compile Include="TypeUnitTests.cs" /> + <Compile Include="ViewCellTests.cs" /> + <Compile Include="ViewUnitTests.cs" /> + <Compile Include="WebViewUnitTests.cs" /> + <Compile Include="ImageSourceTests.cs" /> + <Compile Include="DataTemplateTests.cs" /> + <Compile Include="LayoutOptionsUnitTests.cs" /> + <Compile Include="ContentViewUnitTest.cs" /> + <Compile Include="MockPlatformServices.cs" /> + <Compile Include="GridTests.cs" /> + <Compile Include="UnitPlatform.cs" /> + <Compile Include="GridLengthTypeConverterTests.cs" /> + <Compile Include="BoxViewUnitTests.cs" /> + <Compile Include="NotifiedPropertiesTests.cs" /> + <Compile Include="PickerTests.cs" /> + <Compile Include="EditorTests.cs" /> + <Compile Include="UriImageSourceTests.cs" /> + <Compile Include="ResourceDictionaryTests.cs" /> + <Compile Include="BehaviorTest.cs" /> + <Compile Include="EventTriggerTest.cs" /> + <Compile Include="DynamicResourceTests.cs" /> + <Compile Include="StyleTests.cs" /> + <Compile Include="DataTriggerTests.cs" /> + <Compile Include="MultiTriggerTests.cs" /> + <Compile Include="TriggerTests.cs" /> + <Compile Include="PinchGestureRecognizerTests.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\Xamarin.Forms.Core\Xamarin.Forms.Core.csproj"> + <Project>{57B8B73D-C3B5-4C42-869E-7B2F17D354AC}</Project> + <Name>Xamarin.Forms.Core</Name> + </ProjectReference> + <ProjectReference Include="..\Xamarin.Forms.Maps\Xamarin.Forms.Maps.csproj"> + <Project>{7D13BAC2-C6A4-416A-B07E-C169B199E52B}</Project> + <Name>Xamarin.Forms.Maps</Name> + </ProjectReference> + <ProjectReference Include="..\Xamarin.Forms.Platform\Xamarin.Forms.Platform.csproj"> + <Project>{67f9d3a8-f71e-4428-913f-c37ae82cdb24}</Project> + <Name>Xamarin.Forms.Platform</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> + <ItemGroup /> + <ItemGroup> + <EmbeddedResource Include="Images\crimson.jpg"> + <LogicalName>Images/crimson.jpg</LogicalName> + </EmbeddedResource> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/Xamarin.Forms.Core.UnitTests/packages.config b/Xamarin.Forms.Core.UnitTests/packages.config new file mode 100644 index 00000000..d27552db --- /dev/null +++ b/Xamarin.Forms.Core.UnitTests/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="NUnit" version="2.6.2" targetFramework="net45" /> +</packages> |