2016-05-05 03:14:32 +03:00
|
|
|
//
|
|
|
|
// Base test fixture for introspection tests
|
|
|
|
//
|
|
|
|
// Authors:
|
|
|
|
// Sebastien Pouliot <sebastien@xamarin.com>
|
|
|
|
//
|
|
|
|
// Copyright 2012-2016 Xamarin Inc.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
|
|
|
|
using System;
|
2024-08-23 17:44:32 +03:00
|
|
|
using System.ComponentModel;
|
2016-05-05 03:14:32 +03:00
|
|
|
using System.IO;
|
|
|
|
using System.Reflection;
|
|
|
|
using System.Runtime.InteropServices;
|
2022-10-06 19:15:51 +03:00
|
|
|
using System.Runtime.Versioning;
|
2016-05-05 03:14:32 +03:00
|
|
|
using System.Text;
|
|
|
|
using NUnit.Framework;
|
|
|
|
using Xamarin.Utils;
|
|
|
|
using System.Linq;
|
2016-06-22 02:46:07 +03:00
|
|
|
using Xamarin.Tests;
|
2016-05-05 03:14:32 +03:00
|
|
|
|
|
|
|
using Foundation;
|
|
|
|
using ObjCRuntime;
|
|
|
|
#if MONOTOUCH
|
|
|
|
using UIKit;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace Introspection {
|
|
|
|
|
|
|
|
public abstract class ApiBaseTest {
|
|
|
|
[DllImport ("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
|
|
|
|
protected static extern bool bool_objc_msgSend_IntPtr (IntPtr receiver, IntPtr selector, IntPtr arg1);
|
|
|
|
[DllImport ("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
|
|
|
|
protected static extern IntPtr IntPtr_objc_msgSend (IntPtr receiver, IntPtr selector);
|
|
|
|
|
|
|
|
private LatchedEnvironmentVariable continueOnFailure = new LatchedEnvironmentVariable ("API_TEST_CONTINUE_ON_FAILURE");
|
|
|
|
|
|
|
|
StringBuilder error_output = new StringBuilder ();
|
|
|
|
|
2016-06-16 08:05:39 +03:00
|
|
|
protected ApiBaseTest ()
|
|
|
|
{
|
2016-06-16 18:01:13 +03:00
|
|
|
//LogProgress = true;
|
|
|
|
//ContinueOnFailure = true;
|
2016-06-16 08:05:39 +03:00
|
|
|
}
|
|
|
|
|
2016-05-05 03:14:32 +03:00
|
|
|
protected void AddErrorLine (string line)
|
|
|
|
{
|
|
|
|
error_output.AppendLine (line);
|
2017-09-01 15:58:45 +03:00
|
|
|
if (!line.StartsWith ("[FAIL] ", StringComparison.Ordinal))
|
|
|
|
Console.Error.Write ("[FAIL] ");
|
2016-05-05 03:14:32 +03:00
|
|
|
Console.Error.WriteLine (line);
|
|
|
|
Errors++;
|
|
|
|
}
|
|
|
|
|
2022-11-07 17:20:26 +03:00
|
|
|
protected void AddErrorLine (string format, params object [] parameters)
|
2016-05-05 03:14:32 +03:00
|
|
|
{
|
|
|
|
AddErrorLine (string.Format (format, parameters));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets a value indicating whether this test fixture will continue after failures.
|
|
|
|
/// </summary>
|
|
|
|
/// <value>
|
|
|
|
/// <c>true</c> if continue on failure; otherwise, <c>false</c>.
|
|
|
|
/// </value>
|
2022-11-07 17:20:26 +03:00
|
|
|
public bool ContinueOnFailure {
|
2016-05-05 03:14:32 +03:00
|
|
|
get { return continueOnFailure.Value; }
|
|
|
|
set { continueOnFailure.Value = value; }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private LatchedEnvironmentVariable logProgress = new LatchedEnvironmentVariable ("API_TEST_LOG_PROGRESS");
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets a value indicating whether this test fixture will log it's progress.
|
|
|
|
/// </summary>
|
|
|
|
/// <value>
|
|
|
|
/// <c>true</c> if log progress; otherwise, <c>false</c>.
|
|
|
|
/// </value>
|
|
|
|
public bool LogProgress {
|
|
|
|
get { return logProgress.Value; }
|
|
|
|
set { logProgress.Value = value; }
|
|
|
|
}
|
|
|
|
|
2016-09-08 14:44:43 +03:00
|
|
|
StringBuilder error_data;
|
|
|
|
protected StringBuilder ErrorData {
|
|
|
|
get {
|
|
|
|
return error_data ?? (error_data = new StringBuilder ());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-05 03:14:32 +03:00
|
|
|
protected TextWriter Writer {
|
|
|
|
#if MONOMAC
|
|
|
|
get { return Console.Out; }
|
|
|
|
#elif __WATCHOS__
|
|
|
|
get { return Console.Out; }
|
|
|
|
#else
|
|
|
|
get { return AppDelegate.Runner.Writer; }
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
protected int Errors;
|
|
|
|
protected void ReportError (string s, params object [] parameters)
|
|
|
|
{
|
|
|
|
if (!ContinueOnFailure)
|
|
|
|
Assert.Fail (s, parameters);
|
|
|
|
else {
|
|
|
|
Writer.Write ("[FAIL] ");
|
|
|
|
Writer.WriteLine (s, parameters);
|
2016-09-08 14:44:43 +03:00
|
|
|
ErrorData.AppendFormat (s, parameters).AppendLine ();
|
2016-05-05 03:14:32 +03:00
|
|
|
Errors++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-07 17:20:26 +03:00
|
|
|
protected void AssertIfErrors (string s, params object [] parameters)
|
2016-05-05 03:14:32 +03:00
|
|
|
{
|
|
|
|
if (Errors == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var msg = string.Format (s, parameters);
|
|
|
|
if (error_output.Length > 0) {
|
|
|
|
msg += "\n" + error_output.ToString () + "\n";
|
|
|
|
error_output.Clear ();
|
|
|
|
}
|
|
|
|
Assert.Fail (msg);
|
|
|
|
}
|
2022-11-07 17:20:26 +03:00
|
|
|
|
2016-05-05 03:14:32 +03:00
|
|
|
static protected Type NSObjectType = typeof (NSObject);
|
|
|
|
|
|
|
|
protected virtual bool Skip (Attribute attribute)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-08-23 17:44:32 +03:00
|
|
|
protected bool SkipDueToInvisibleAndUnsupported (MemberInfo member)
|
|
|
|
{
|
|
|
|
if (member is null)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (SkipDueToInvisibleAndUnsupported (member.DeclaringType))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!MemberHasUnsupported (member))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return MemberHasEditorBrowsableNever (member);
|
|
|
|
}
|
|
|
|
|
2016-05-05 03:14:32 +03:00
|
|
|
protected virtual bool SkipDueToAttribute (MemberInfo member)
|
|
|
|
{
|
2023-05-05 18:52:19 +03:00
|
|
|
if (member is null)
|
2016-05-05 03:14:32 +03:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return !member.IsAvailableOnHostPlatform () ||
|
2022-11-07 17:20:26 +03:00
|
|
|
SkipDueToAttribute (member.DeclaringType) ||
|
|
|
|
SkipDueToAttributeInProperty (member);
|
2016-05-05 03:14:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// We need to check Availability info on PropertyInfo attributes too
|
|
|
|
// due to sometimes the Availability info only exist on the property
|
|
|
|
// and not on the property Getter or Setter, this complements the fix for bug:
|
|
|
|
// https://bugzilla.xamarin.com/show_bug.cgi?id=35176
|
|
|
|
protected bool SkipDueToAttributeInProperty (MemberInfo member)
|
|
|
|
{
|
2023-05-05 18:52:19 +03:00
|
|
|
if (member is null)
|
2016-05-05 03:14:32 +03:00
|
|
|
return false;
|
|
|
|
|
|
|
|
var m = member as MethodInfo;
|
|
|
|
|
2023-05-05 18:52:19 +03:00
|
|
|
if (m is null || // Skip anything that is not a method
|
2022-11-07 17:20:26 +03:00
|
|
|
!m.Attributes.HasFlag (MethodAttributes.SpecialName)) // We want properties with SpecialName Attribute
|
2016-05-05 03:14:32 +03:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// FIXME: In the future we could cache this to reduce memory requirements
|
|
|
|
var property = m.DeclaringType
|
2023-08-02 14:46:45 +03:00
|
|
|
.GetProperties (BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
|
|
|
|
.SingleOrDefault (p => p.GetGetMethod (true) == m || p.GetSetMethod (true) == m);
|
2023-05-05 18:52:19 +03:00
|
|
|
return property is not null && SkipDueToAttribute (property);
|
2016-05-05 03:14:32 +03:00
|
|
|
}
|
|
|
|
|
2020-03-02 17:20:29 +03:00
|
|
|
protected bool SkipDueToRejectedTypes (Type type)
|
|
|
|
{
|
|
|
|
switch (type.FullName) {
|
|
|
|
case "UIKit.DeprecatedWebView+_DeprecatedWebViewDelegate":
|
|
|
|
case "UIKit.DeprecatedWebView+DeprecatedWebViewAppearance":
|
|
|
|
return true;
|
|
|
|
case "UIKit.IDeprecatedWebViewDelegate":
|
|
|
|
case "UIKit.DeprecatedWebView":
|
|
|
|
case "UIKit.DeprecatedWebViewDelegate":
|
|
|
|
case "UIKit.DeprecatedWebViewDelegate_Extensions":
|
|
|
|
case "UIKit.DeprecatedWebViewDelegateWrapper":
|
|
|
|
case "UIKit.DeprecatedWebViewNavigationType":
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-06 19:15:51 +03:00
|
|
|
public bool MemberHasObsolete (MemberInfo member)
|
|
|
|
{
|
|
|
|
#if NET
|
2024-09-11 21:24:03 +03:00
|
|
|
return TestRuntime.HasOSPlatformAttributeForCurrentPlatform<ObsoletedOSPlatformAttribute> (member);
|
2022-10-06 19:15:51 +03:00
|
|
|
#else
|
2023-05-05 18:52:19 +03:00
|
|
|
return member.GetCustomAttribute<ObsoleteAttribute> () is not null;
|
2022-10-06 19:15:51 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2024-08-23 17:44:32 +03:00
|
|
|
public bool MemberHasUnsupported (MemberInfo member)
|
|
|
|
{
|
|
|
|
#if NET
|
2024-09-11 21:24:03 +03:00
|
|
|
return TestRuntime.HasOSPlatformAttributeForCurrentPlatform<UnsupportedOSPlatformAttribute> (member);
|
2024-08-23 17:44:32 +03:00
|
|
|
#else
|
|
|
|
return member.GetCustomAttribute<ObsoleteAttribute> () is not null;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool MemberHasEditorBrowsableNever (MemberInfo member)
|
|
|
|
{
|
|
|
|
var attribute = member.GetCustomAttribute<EditorBrowsableAttribute> (false);
|
|
|
|
return attribute?.State == EditorBrowsableState.Never;
|
|
|
|
}
|
|
|
|
|
2016-05-05 03:14:32 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Gets the assembly on which the test fixture will reflect the NSObject-derived types.
|
|
|
|
/// The default implementation returns the assembly where NSObject is defined, e.g.
|
|
|
|
/// monotouch.dll or xammac.dll.
|
|
|
|
/// You need to override this method to return the binding assembly you wish to test.
|
|
|
|
/// </summary>
|
|
|
|
/// <value>
|
|
|
|
/// The assembly on which the fixture will execute it's tests.
|
|
|
|
/// </value>
|
|
|
|
protected virtual Assembly Assembly {
|
|
|
|
get { return NSObjectType.Assembly; }
|
|
|
|
}
|
|
|
|
|
|
|
|
const string libprefix = "/System/Library/Frameworks";
|
|
|
|
static readonly string simprefix = Environment.GetEnvironmentVariable ("IPHONE_SIMULATOR_ROOT");
|
|
|
|
|
|
|
|
protected virtual string FindLibrary (string libname, bool requiresFullPath = false)
|
|
|
|
{
|
|
|
|
string prefix;
|
|
|
|
if (!String.IsNullOrEmpty (simprefix) && libname.StartsWith (libprefix, StringComparison.Ordinal)) {
|
|
|
|
libname = simprefix + libname;
|
|
|
|
prefix = String.Empty;
|
|
|
|
} else {
|
|
|
|
prefix = libprefix; // re-root libname
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (libname) {
|
|
|
|
#if !MONOMAC
|
|
|
|
case "AudioUnit":
|
|
|
|
libname = "AudioToolbox";
|
|
|
|
break;
|
2017-09-05 15:57:58 +03:00
|
|
|
case "IOSurface":
|
|
|
|
if (!TestRuntime.CheckXcodeVersion (9, 0))
|
|
|
|
prefix = Path.Combine (Path.GetDirectoryName (prefix), "PrivateFrameworks");
|
|
|
|
break;
|
2018-07-13 22:14:05 +03:00
|
|
|
case "PdfKit":
|
|
|
|
libname = "PDFKit";
|
|
|
|
break;
|
2016-05-05 03:14:32 +03:00
|
|
|
#endif
|
|
|
|
case "CoreAnimation":
|
|
|
|
// generated code uses QuartzCore correctly - even if the [Field] property is wrong
|
|
|
|
libname = "QuartzCore";
|
|
|
|
break;
|
|
|
|
case "CoreMidi":
|
|
|
|
// generated code uses CoreMIDI correctly
|
|
|
|
libname = "CoreMIDI";
|
|
|
|
break;
|
2021-08-06 23:28:04 +03:00
|
|
|
case "Phase":
|
|
|
|
libname = "PHASE";
|
|
|
|
break;
|
2016-05-05 03:14:32 +03:00
|
|
|
default:
|
|
|
|
if (requiresFullPath && (Path.GetDirectoryName (libname).Length == 0))
|
|
|
|
ReportError ("[FAIL] Library '{0}' is specified without a path", libname);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-11-07 17:20:26 +03:00
|
|
|
return Path.Combine (prefix, libname + ".framework", libname);
|
2016-05-05 03:14:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|