using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Threading.Tasks; using NUnit.Framework; using Xamarin.Forms.Internals; 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; ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.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; ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.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; ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.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; ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.Android; Device.OnPlatform ( iOS: () => ios = false, Default: () => android = true); Assert.False (ios); Assert.True (android); } [Test] public void TestOnPlatformNoOpWithoutDefault () { bool any = false; ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = "Other"; Device.OnPlatform ( iOS: () => any = true, Android: () => any = true, WinPhone: () => any = true); Assert.False (any); } [Test] public void TestDefaultOniOS () { bool defaultExecuted = false; ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.iOS; Device.OnPlatform ( Android: () => { }, WinPhone: () => { }, Default:() => defaultExecuted = true); Assert.True (defaultExecuted); } [Test] public void TestDefaultOnAndroid () { bool defaultExecuted = false; ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.Android; Device.OnPlatform ( iOS: () => { }, WinPhone: () => { }, Default:() => defaultExecuted = true); Assert.True (defaultExecuted); } [Test] public void TestDefaultOnWinPhone () { bool defaultExecuted = false; ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.WinPhone; Device.OnPlatform ( iOS: () => { }, Android: () => { }, Default:() => defaultExecuted = true); Assert.True (defaultExecuted); } [Test] public void TestDefaultOnOther () { bool defaultExecuted = false; ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = "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 () { ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.WinPhone; Assert.AreEqual (3, Device.OnPlatform (1, 2, 3)); ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.iOS; Assert.AreEqual (1, Device.OnPlatform (1, 2, 3)); ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = Device.Android; Assert.AreEqual (2, Device.OnPlatform (1, 2, 3)); ((MockPlatformServices)Device.PlatformServices).RuntimePlatform = "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(() => 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 (() => 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 (); 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 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); } } }