xamarin-macios/tests/introspection/ApiProtocolTest.cs

499 строки
15 KiB
C#

//
// Test the generated API for common protocol support
//
// Authors:
// Sebastien Pouliot <sebastien@xamarin.com>
//
// Copyright 2013, 2015 Xamarin Inc.
//
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using NUnit.Framework;
#if XAMCORE_2_0
using Foundation;
using ObjCRuntime;
#elif MONOMAC
using MonoMac.Foundation;
using MonoMac.ObjCRuntime;
#else
using MonoTouch.Foundation;
using MonoTouch.ObjCRuntime;
#endif
namespace Introspection {
public abstract class ApiProtocolTest : ApiBaseTest {
static IntPtr conform_to = Selector.GetHandle ("conformsToProtocol:");
public ApiProtocolTest ()
{
ContinueOnFailure = true;
}
static bool ConformTo (IntPtr klass, IntPtr protocol)
{
return bool_objc_msgSend_IntPtr (klass, conform_to, protocol);
}
protected virtual bool Skip (Type type)
{
switch (type.Name) {
// *** NSForwarding: warning: object 0x5cbd078 of class 'JSExport' does not implement methodSignatureForSelector: -- trouble ahead
// *** NSForwarding: warning: object 0x5cbd078 of class 'JSExport' does not implement doesNotRecognizeSelector: -- abort
case "JSExport":
return true;
default:
#if !XAMCORE_2_0
// in Classic our internal delegates _inherits_ the type with the [Protocol] attribute
// in Unified our internal delegates _implements_ the interface that has the [Protocol] attribute
if (type.GetCustomAttribute<ProtocolAttribute> (true) != null)
return true;
#endif
return SkipDueToAttribute (type);
}
}
IntPtr GetClass (Type type)
{
return Class.GetHandle (type);
}
protected virtual bool Skip (Type type, string protocolName)
{
// The following protocols are skipped in classic since they were added in
// later versions
if (IntPtr.Size == 4) {
switch (protocolName) {
case "UIUserActivityRestoring":
return true;
}
}
switch (protocolName) {
case "NSCopying":
switch (type.Name) {
// undocumented conformance (up to 7.0) and conformity varies between iOS versions
case "CAEmitterCell":
case "GKAchievement":
case "GKScore":
case "MPMediaItem":
// new in iOS8 and 10.0
case "NSExtensionContext":
case "NSLayoutAnchor`1":
case "NSLayoutDimension":
case "NSLayoutXAxisAnchor":
case "NSLayoutYAxisAnchor":
// iOS 10 beta 3
case "GKCloudPlayer":
// iOS 10 : test throw because of generic usage
case "NSMeasurement`1":
// Xcode 9 - Conformance not in headers
case "MLDictionaryConstraint":
case "MLImageConstraint":
case "MLMultiArrayConstraint":
case "VSSubscription":
return true; // skip
// xcode 10
case "VSAccountMetadata":
case "VSAccountMetadataRequest":
case "VSAccountProviderResponse":
case "PHEditingExtensionContext":
case "HKCumulativeQuantitySeriesSample":
// Xcode 11 - Conformance not in headers
case "UISceneActivationConditions":
case "UISceneSession":
return true;
// Xcode 11
case "NSFileProviderSearchQuery":
return true;
}
break;
case "NSMutableCopying":
switch (type.Name) {
// iOS 10 : test throw because of generic usage
case "NSMeasurement`1":
return true; // skip
// Xcode 10
case "UNNotificationCategory":
case "UNNotificationSound":
// Xcode 11 - Conformance not in headers
case "UISceneSession":
return true;
}
break;
case "NSCoding":
switch (type.Name) {
// only documented to support NSCopying - not NSCoding (fails on iOS 7.1 but not 8.0)
case "NSUrlSessionTask":
case "NSUrlSessionDataTask":
case "NSUrlSessionUploadTask":
case "NSUrlSessionDownloadTask":
//
case "NSUrlSessionConfiguration":
case "NSMergeConflict":
// new in iOS8 and 10.0
case "NSExtensionContext":
case "NSItemProvider":
// iOS9 / 10.11
case "CNSaveRequest":
case "NSLayoutAnchor`1":
case "NSLayoutDimension":
case "NSLayoutXAxisAnchor":
case "NSLayoutYAxisAnchor":
case "GKCloudPlayer":
case "GKGameSession":
// iOS 10 : test throw because of generic usage
case "NSMeasurement`1":
// iOS 11 / tvOS 11
case "VSSubscription":
// iOS 11.3 / macOS 10.13.4
case "NSEntityMapping":
case "NSMappingModel":
case "NSPropertyMapping":
return true;
// Xcode 10
case "NSManagedObjectID":
case "VSAccountMetadata":
case "VSAccountMetadataRequest":
case "VSAccountProviderResponse":
case "PHEditingExtensionContext":
return true;
// Xcode 11 (on device only)
case "ICHandle":
case "ICNotification":
case "ICNotificationManagerConfiguration":
case "MPSNNNeuronDescriptor":
// Xcode 11
case "NSFileProviderItemVersion":
case "NSFileProviderRequest":
case "NSFileProviderSearchQuery":
return true;
// Xcode 11.4, not documented
case "NSHttpCookie":
return true;
}
break;
case "NSSecureCoding":
switch (type.Name) {
case "NSMergeConflict": // undocumented
// only documented to support NSCopying (and OSX side only does that)
case "NSUrlSessionTask":
case "NSUrlSessionDataTask":
case "NSUrlSessionUploadTask":
case "NSUrlSessionDownloadTask":
case "NSUrlSessionConfiguration":
// new in iOS8 and 10.0
case "NSExtensionContext":
case "NSItemProvider":
case "NSParagraphStyle": //17770106
case "NSMutableParagraphStyle": //17770106
return true; // skip
// iOS9 / 10.11
case "CNSaveRequest":
case "NSPersonNameComponentsFormatter":
case "GKCloudPlayer":
case "GKGameSession":
// iOS 10 : test throw because of generic usage
case "NSMeasurement`1":
return true; // skip
// xcode 9
case "NSConstraintConflict": // Conformance not in headers
case "VSSubscription":
// iOS 11.3 / macOS 10.13.4
case "NSEntityMapping":
case "NSMappingModel":
case "NSPropertyMapping":
return true;
case "MPSImageAllocator": // Header shows NSSecureCoding, but intro gives: MPSImageAllocator conforms to NSSecureCoding but SupportsSecureCoding returned false
return true;
// Xcode 10
case "ARDirectionalLightEstimate":
case "ARFrame":
case "ARLightEstimate":
case "NSManagedObjectID":
// beta 2
case "NSTextAttachment":
case "VSAccountMetadata":
case "VSAccountMetadataRequest":
case "VSAccountProviderResponse":
case "PHEditingExtensionContext":
return true;
// Xcode 11 (on device only)
case "ICHandle":
case "ICNotification":
case "ICNotificationManagerConfiguration":
case "MPSNNNeuronDescriptor":
return true;
// Xcode 11
case "NSFileProviderItemVersion":
case "NSFileProviderRequest":
case "NSFileProviderSearchQuery":
case "INUserContext": // Header shows NSSecureCoding but intro on both device and simulator says nope FB7604793
return true;
// Xcode 11.4, not documented
case "NSHttpCookie":
return true;
}
break;
// conformance added in Xcode 8 (iOS 10 / macOS 10.12)
case "MDLNamed":
switch (type.Name) {
case "MTKMeshBuffer":
case "MDLVoxelArray": // base class changed to MDLObject (was NSObject before)
return true;
}
break;
case "CALayerDelegate":
switch (type.Name) {
case "MTKView":
return true;
}
break;
case "NSProgressReporting":
switch (type.Name) {
case "NSOperationQueue":
if (!TestRuntime.CheckXcodeVersion (11,0))
return true;
break;
default:
if (!TestRuntime.CheckXcodeVersion (9, 0))
return true;
break;
}
break;
case "GKSceneRootNodeType":
// it's an empty protocol, defined by a category and does not reply as expected
switch (type.Name) {
// GameplayKit.framework/Headers/SceneKit+Additions.h
case "SCNScene":
// GameplayKit.framework/Headers/SpriteKit+Additions.h
case "SKScene":
return true;
}
break;
case "UIUserActivityRestoring":
if (!TestRuntime.CheckXcodeVersion (10, 0))
return true;
switch (type.Name) {
// UIKit.framework/Headers/UIDocument.h
case "UIDocument":
// inherits it from UIDocument
case "UIManagedDocument":
return true;
}
break;
case "SCNTechniqueSupport":
if (!TestRuntime.CheckXcodeVersion (11,0))
return false;
switch (type.Name) {
case "SCNLight":
return true;
}
break;
case "VNRequestRevisionProviding":
switch (type.Name) {
case "VNFaceLandmarks":
case "VNFaceLandmarks2D":
case "VNFaceLandmarkRegion":
case "VNFaceLandmarkRegion2D":
if (!TestRuntime.CheckXcodeVersion (11,0))
return true;
break;
}
break;
#if !XAMCORE_4_0
case "MTLCounter":
case "MTLCounterSampleBuffer":
case "MTLCounterSet":
return true; // Incorrectly bound, will be fixed for XAMCORE_4_0.
#endif
}
return false;
}
void CheckProtocol (string protocolName, Action<Type,IntPtr,bool> action)
{
IntPtr protocol = Runtime.GetProtocol (protocolName);
Assert.AreNotEqual (protocol, IntPtr.Zero, protocolName);
int n = 0;
foreach (Type t in Assembly.GetTypes ()) {
if (!NSObjectType.IsAssignableFrom (t))
continue;
if (Skip (t) || Skip (t, protocolName))
continue;
if (LogProgress)
Console.WriteLine ("{0}. {1} conforms to {2}", ++n, t.FullName, protocolName);
IntPtr klass = GetClass (t);
action (t, klass, ConformTo (klass, protocol));
}
}
[Test]
public void Coding ()
{
Errors = 0;
CheckProtocol ("NSCoding", delegate (Type type, IntPtr klass, bool result) {
if (result) {
// `type` conforms to (native) NSCoding so...
if (result) {
// the type should implements INSCoding
if (!typeof (INSCoding).IsAssignableFrom (type)) {
ReportError ("{0} conforms to NSCoding but does not implement INSCoding", type.Name);
}
// FIXME: and implement the .ctor(NSCoder)
}
}
});
Assert.AreEqual (Errors, 0, "{0} types conforms to NSCoding but does not implement INSCoding", Errors);
}
// [Test] -> iOS 6.0+ and Mountain Lion (10.8) +
public virtual void SecureCoding ()
{
Errors = 0;
CheckProtocol ("NSSecureCoding", delegate (Type type, IntPtr klass, bool result) {
if (result) {
// the type should implements INSSecureCoding
if (!typeof (INSSecureCoding).IsAssignableFrom (type)) {
ReportError ("{0} conforms to NSSecureCoding but does not implement INSSecureCoding", type.Name);
}
}
});
Assert.AreEqual (Errors, 0, "{0} types conforms to NSSecureCoding but does not implement INSSecureCoding", Errors);
}
bool SupportsSecureCoding (Type type)
{
Class cls = new Class (type);
if (!bool_objc_msgSend_IntPtr (cls.Handle, Selector.GetHandle ("respondsToSelector:"), Selector.GetHandle ("supportsSecureCoding")))
return false;
return NSSecureCoding.SupportsSecureCoding (type);
}
// [Test] -> iOS 6.0+ and Mountain Lion (10.8) +
public virtual void SupportsSecureCoding ()
{
Errors = 0;
CheckProtocol ("NSSecureCoding", delegate (Type type, IntPtr klass, bool result) {
bool supports = SupportsSecureCoding (type);
if (result) {
// check that +supportsSecureCoding returns YES
if (!supports) {
ReportError ("{0} conforms to NSSecureCoding but SupportsSecureCoding returned false", type.Name);
}
} else if (type.IsPublic && supports) {
// there are internal types, e.g. DataWrapper : NSData, that subclass NSSecureCoding-types without
// [re-]declaring their allegiance - but we can live with those small betrayals
Assert.IsFalse (NSSecureCoding.SupportsSecureCoding (type), "{0} !SupportsSecureCoding", type.Name);
ReportError ("SupportsSecureCoding returns true but {0} does not conforms to NSSecureCoding", type.Name);
}
});
Assert.AreEqual (Errors, 0, "{0} types conforms to NSCoding but does not implement INSSecureCoding", Errors);
}
[Test]
public void Copying ()
{
Errors = 0;
CheckProtocol ("NSCopying", delegate (Type type, IntPtr klass, bool result) {
// `type` conforms to (native) NSCopying so...
if (result) {
// the type should implements INSCopying
if (!typeof (INSCopying).IsAssignableFrom (type)) {
ReportError ("{0} conforms to NSCopying but does not implement INSCopying", type.Name);
}
}
});
Assert.AreEqual (Errors, 0, "{0} types conforms to NSCopying but does not implement INSCopying", Errors);
}
[Test]
public void MutableCopying ()
{
Errors = 0;
CheckProtocol ("NSMutableCopying", delegate (Type type, IntPtr klass, bool result) {
// `type` conforms to (native) NSMutableCopying so...
if (result) {
// the type should implements INSMutableCopying
if (!typeof (INSMutableCopying).IsAssignableFrom (type)) {
ReportError ("{0} conforms to NSMutableCopying but does not implement INSMutableCopying", type.Name);
}
}
});
Assert.AreEqual (Errors, 0, "{0} types conforms to NSMutableCopying but does not implement INSMutableCopying", Errors);
}
[Test]
public void GeneralCase ()
{
Errors = 0;
foreach (Type t in Assembly.GetTypes ()) {
if (!NSObjectType.IsAssignableFrom (t))
continue;
if (Skip (t))
continue;
foreach (var intf in t.GetInterfaces ()) {
if (SkipDueToAttribute (intf))
continue;
string protocolName = intf.Name.Substring (1);
switch (protocolName) {
case "NSCoding":
case "NSSecureCoding":
case "NSCopying":
case "NSMutableCopying":
// we have special test cases for them
continue;
default:
#if !XAMCORE_2_0
// in Classic our internal delegates _inherits_ the type with the [Protocol] attribute
// in Unified our internal delegates _implements_ the interface that has the [Protocol] attribute
var pt = Type.GetType (intf.Namespace + "." + protocolName + ", " + intf.Assembly.FullName);
if (SkipDueToAttribute (pt))
continue;
#endif
if (Skip (t, protocolName))
continue;
break;
}
var a = intf.GetCustomAttribute<ProtocolAttribute> (true);
if (a == null || a.IsInformal)
continue;
IntPtr protocol = Runtime.GetProtocol (protocolName);
if (protocol == IntPtr.Zero)
continue; // not a protocol
if (LogProgress)
Console.WriteLine ("{0} conforms to {1}", t.FullName, protocolName);
var klass = new Class (t);
if (klass.Handle == IntPtr.Zero) {
// This can often by caused by [Protocol] classes with no [Model] but having a [BaseType].
// Either have both a Model and BaseType or neither
AddErrorLine ("[FAIL] Could not load {0}", t.FullName);
} else if (t.IsPublic && !ConformTo (klass.Handle, protocol)) {
// note: some internal types, e.g. like UIAppearance subclasses, return false (and there's not much value in changing this)
ReportError ("Type {0} (native: {1}) does not conform {2}", t.FullName, klass.Name, protocolName);
}
}
}
AssertIfErrors ("{0} types do not really conform to the protocol interfaces", Errors);
}
}
}