xamarin-macios/tests/introspection/ApiBaseTest.cs

295 строки
8.5 KiB
C#
Исходник Обычный вид История

//
// 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;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using NUnit.Framework;
using Xamarin.Utils;
using System.Linq;
using Xamarin.Tests;
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 ();
protected ApiBaseTest ()
{
//LogProgress = true;
//ContinueOnFailure = true;
}
protected void AddErrorLine (string line)
{
error_output.AppendLine (line);
if (!line.StartsWith ("[FAIL] ", StringComparison.Ordinal))
Console.Error.Write ("[FAIL] ");
Console.Error.WriteLine (line);
Errors++;
}
protected void AddErrorLine (string format, params object [] parameters)
{
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>
public bool ContinueOnFailure {
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; }
}
StringBuilder error_data;
protected StringBuilder ErrorData {
get {
return error_data ?? (error_data = new StringBuilder ());
}
}
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);
ErrorData.AppendFormat (s, parameters).AppendLine ();
Errors++;
}
}
protected void AssertIfErrors (string s, params object [] parameters)
{
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);
}
static protected Type NSObjectType = typeof (NSObject);
protected virtual bool Skip (Attribute attribute)
{
return false;
}
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);
}
protected virtual bool SkipDueToAttribute (MemberInfo member)
{
if (member is null)
return false;
return !member.IsAvailableOnHostPlatform () ||
SkipDueToAttribute (member.DeclaringType) ||
SkipDueToAttributeInProperty (member);
}
// 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)
{
if (member is null)
return false;
var m = member as MethodInfo;
if (m is null || // Skip anything that is not a method
!m.Attributes.HasFlag (MethodAttributes.SpecialName)) // We want properties with SpecialName Attribute
return false;
// FIXME: In the future we could cache this to reduce memory requirements
var property = m.DeclaringType
.GetProperties (BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
.SingleOrDefault (p => p.GetGetMethod (true) == m || p.GetSetMethod (true) == m);
return property is not null && SkipDueToAttribute (property);
}
[mtouch] Add `force-rejected-types-removal` optimization (#8009) This optimization can be enabled when it's not possible to use the managed linker (e.g. **Don't link**) or when the managed linker cannot remove references to deprecated types that would cause an application to be rejected by Apple. References to the existing types will be renamed, e.g. `UIWebView` to `DeprecatedWebView`, in every assemblies. The type definition is also renamed (for validity) and all custom attributes on the types and their members will be removed. Code inside the members will be replaced with a `throw new NotSupportedException ();`. The msbuild test app `MyReleaseBuild` has been updated to test that the optimization is working as expected (device builds are slow so reusing this test has little impact in test time). Basically the test ensure that `UIWebView` is used and cannot be removed by the compiler (optimization) or the managed linker (since it's referenced). Since the optimization is enabled then we can `grep` then final `.app` directory to ensure there's no mention of `UIWebView` inside any of the files that would be submitted. The application can be run, by itself, and will turn green if OK, red if `DeprecatedWebView` can't be found (skeleton replacement for `UIWebView`) or orange if a `NotSupportedException` is thrown. Finally introspection tests have been updated to skip over the deprecated (and renamed) types. It should not be an issue right now, since this optimization is not enabled by default, but it made testing easier.
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;
}
}
public bool MemberHasObsolete (MemberInfo member)
{
#if NET
return TestRuntime.HasOSPlatformAttributeForCurrentPlatform<ObsoletedOSPlatformAttribute> (member);
#else
return member.GetCustomAttribute<ObsoleteAttribute> () is not null;
#endif
}
public bool MemberHasUnsupported (MemberInfo member)
{
#if NET
return TestRuntime.HasOSPlatformAttributeForCurrentPlatform<UnsupportedOSPlatformAttribute> (member);
#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;
}
/// <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;
case "IOSurface":
if (!TestRuntime.CheckXcodeVersion (9, 0))
prefix = Path.Combine (Path.GetDirectoryName (prefix), "PrivateFrameworks");
break;
case "PdfKit":
libname = "PDFKit";
break;
#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;
case "Phase":
libname = "PHASE";
break;
default:
if (requiresFullPath && (Path.GetDirectoryName (libname).Length == 0))
ReportError ("[FAIL] Library '{0}' is specified without a path", libname);
break;
}
return Path.Combine (prefix, libname + ".framework", libname);
}
}
}