// // iOS tests for the generated API selectors against typos or non-existing cases // // Authors: // Sebastien Pouliot // // Copyright 2013 Xamarin Inc. All rights reserved. // using System; using System.Reflection; using NUnit.Framework; #if XAMCORE_2_0 using ObjCRuntime; using Foundation; using UIKit; #else using MonoTouch.ObjCRuntime; using MonoTouch.Foundation; using MonoTouch.UIKit; #endif namespace Introspection { [TestFixture] // we want the tests to be available because we use the linker [Preserve (AllMembers = true)] public class iOSApiSignatureTest : ApiSignatureTest { public iOSApiSignatureTest () { ContinueOnFailure = true; //LogProgress = true; } protected override int Size (Type t, bool simd = false) { switch (t.Name) { // rdar 21375616 - Breaking change with EventKit[UI] enum base type // EventKit.EK* enums are anonymous enums in 10.10 and iOS 8, but an NSInteger in 10.11 and iOS 9. case "EKCalendarChooserSelectionStyle": case "EKCalendarChooserDisplayStyle": if (!TestRuntime.CheckXcodeVersion (7, 0)) return 4; break; } return base.Size (t, simd); } protected override bool Skip (Type type, MethodBase method, string selector) { switch (type.Name) { case "UIAlertView": // variable length parameters ... if (selector == "initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:") return true; break; case "UIViewController": switch (selector) { // offically added in 6.0 - but retroactively supported in iOS5 (including documentation) // the code works (see monotouch-test.app) but Apple changed their return value in 6.0 // (they used to return `bool`) case "beginAppearanceTransition:animated:": case "endAppearanceTransition": if (!TestRuntime.CheckXcodeVersion (4, 5)) return true; break; } break; case "NKIssue": // Apple "promoted" this to `NSInteger` in iOS8 but this already existed (as a 32bits value) in iOS 7.x // sadly, and even with a bug report with a few exchanges, this was not fixed before iOS8 GM :-( // 64bits application for iOS 7.x will be uncommon so we prefer to be forward compatible if (selector == "status") return !TestRuntime.CheckXcodeVersion (6, 0); break; case "CMMotionManager": // iOS 8.3 changed CMMotionManager from 4 to 8 bytes on 64bits CPU and we have to follow that breaking // change unless Apple revert that before final. [radar 20295259] if ((IntPtr.Size == 4) || TestRuntime.CheckXcodeVersion (6, 3)) return false; // which means iOS 8.2 (and earlier can't match) switch (selector) { case "startDeviceMotionUpdatesUsingReferenceFrame:": case "startDeviceMotionUpdatesUsingReferenceFrame:toQueue:withHandler:": case "attitudeReferenceFrame": return true; default: return false; } } return base.Skip (type, method, selector); } protected override bool IsValidStruct (Type type, string structName) { switch (structName) { // CIImage 'static MonoTouch.CoreImage.CIImage FromImageBuffer(MonoTouch.CoreVideo.CVPixelBuffer)' selector: imageWithCVPixelBuffer: == @12@0:4^{__CVBuffer=} case "__CVBuffer": return type.Name == "CVPixelBuffer" || type.Name == "CVImageBuffer"; } return base.IsValidStruct (type, structName); } // only handle exception here (to return true) otherwise call base to deal with it // `caller` is provided to make it easier to detect "special" cases protected override bool Check (char encodedType, Type type) { // return an error if null (instead of throwing) so we can continue execution if (type == null) return false; switch (encodedType) { case 'c': // char, used for C# bool #if !XAMCORE_2_0 switch (type.FullName) { // looks like it returns a bool even if documented as a void // UIPrintInteractionController 'instance Void Present(Boolean, MonoTouch.UIKit.UIPrintInteractionCompletionHandler)' selector: presentAnimated:completionHandler: // update: documentation (and header) mistake that Apple corrected (IIRC I filled that issue) case "System.Void": return CurrentType.Name == "UIPrintInteractionController"; } #endif break; // float (32 bits) case 'f': switch (type.FullName) { // documented (web and header file) as NSInteger // UIImageView 'instance Void set_AnimationRepeatCount(Int32)' selector: setAnimationRepeatCount: == v12@0:4f8 case "System.Int32": return CurrentType.FullName == "MonoTouch.UIKit.UIImageView"; } break; case 'i': switch (type.FullName) { case "MonoTouch.EventKitUI.EKCalendarChooserSelectionStyle": case "MonoTouch.EventKitUI.EKCalendarChooserDisplayStyle": case "EventKitUI.EKCalendarChooserSelectionStyle": case "EventKitUI.EKCalendarChooserDisplayStyle": return (IntPtr.Size == 4) || !TestRuntime.CheckXcodeVersion (7, 0); case "System.UInt32": // numberOfTouchesRequired was signed before iOS6, unsigned since then return true; } break; // unsigned 32 bits case 'I': switch (type.FullName) { case "System.Int32": // sign-ness mis-binding, several of them (not critical) // CBATTRequest 'instance Int32 get_Offset()' selector: offset == I8@0:4 return true; } break; // unsigned 32 bits case 'L': switch (type.FullName) { // sign-ness mis-binding (not critical) e.g. // CAMediaTimingFunction 'instance Void GetControlPointAtIndex(Int32, IntPtr)' selector: getControlPointAtIndex:values: == v16@0:4L8[2f]12 case "System.Int32": return true; } break; // unsigned 64 bits case 'Q': switch (type.FullName) { // sign-ness mis-binding (not critical) e.g. // NSIncrementalStoreNode 'instance Int64 get_Version()' selector: version == Q8@0:4 case "System.Int64": return true; } break; } return base.Check (encodedType, type); } protected override void CheckManagedMemberSignatures (MethodBase m, Type t, ref int n) { #if !XAMCORE_4_0 // let's review the tests exceptions if we break things switch (m.Name) { case "get_Source": case "set_Source": // UITableViewSource is our own creation and we did not make an interface out of it if (t.Name == "UITableView") return; // UICollectionViewSource is our own creation and we did not make an interface out of it if (t.Name == "UICollectionView") return; break; case "get_SearchResultsSource": case "set_SearchResultsSource": // UITableViewSource is our own creation and we did not make an interface out of it if (t.Name == "UISearchDisplayController") return; break; case "get_ImagePickerControllerDelegate": case "set_ImagePickerControllerDelegate": case "get_NavigationControllerDelegate": case "set_NavigationControllerDelegate": // fixed in XAMCORE_4_0 - alternative are the Weak* delegates if (t.Name == "UIImagePickerController") return; break; case "get_Model": case "set_Model": // UIPickerViewModel is our own creation and we did not make an interface out of it if (t.Name == "UIPickerView") return; break; } #endif base.CheckManagedMemberSignatures (m, t, ref n); } protected override bool IgnoreAsync (MethodInfo m) { switch (m.Name) { // Called by the OS, i.e. meant to be overridden (not called) by user code. case "DidReceiveNotification": return m.DeclaringType.Name == "WKUserNotificationInterfaceController"; case "AddCompletion": return true; // comes from NSFilePresenter protocol, where we cannot put [Async] today // proposal: https://trello.com/c/dSOh6PXE/680-rfc-async-on-protocol-proposal case "AccommodatePresentedItemDeletion": case "AccommodatePresentedSubitemDeletion": case "SavePresentedItemChanges": return m.DeclaringType.Name == "UIDocument"; } return base.IgnoreAsync (m); } } }