xamarin-macios/tests/generator/BGenTests.cs

1766 строки
72 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NUnit.Framework;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Xamarin;
using Xamarin.Tests;
using Xamarin.Utils;
namespace GeneratorTests {
[TestFixture ()]
[Parallelizable (ParallelScope.All)]
public class BGenTests : BGenBase {
// Removing the following variable might make running the unit tests in VSMac fail.
static Type variable_to_keep_reference_to_system_runtime_compilerservices_unsafe_assembly = typeof (System.Runtime.CompilerServices.Unsafe);
[Test]
#if !NET
[TestCase (Profile.macOSFull)]
[TestCase (Profile.macOSSystem)]
#endif
[TestCase (Profile.macOSMobile)]
public void BMac_Smoke (Profile profile)
{
BuildFile (profile, "bmac_smoke.cs");
}
#if !NET // There's no System.Drawing in the .NET BCL
[Test]
[TestCase (Profile.macOSSystem)]
public void BMac_NonAbsoluteReference_StillBuilds (Profile profile)
{
BuildFile (profile, true, false, new List<string> () { "System.Drawing" }, "bmac_smoke.cs");
}
#endif
#if !NET
[Test]
[TestCase (Profile.macOSSystem)]
public void BMac_AbsoluteSystemReference_StillBuilds (Profile profile)
{
BuildFile (profile, true, false, new List<string> () { "/Library/Frameworks/Mono.framework/Versions/Current/lib/mono/4.5/System.Drawing.dll" }, "bmac_smoke.cs");
}
#endif
[Test]
#if !NET
[TestCase (Profile.macOSFull)]
[TestCase (Profile.macOSSystem)]
#endif
[TestCase (Profile.macOSMobile)]
public void BMac_With_Hyphen_In_Name (Profile profile)
{
BuildFile (profile, "bmac-with-hyphen-in-name.cs");
}
[Test]
#if !NET
[TestCase (Profile.macOSFull)]
[TestCase (Profile.macOSSystem)]
#endif
[TestCase (Profile.macOSMobile)]
public void PropertyRedefinitionMac (Profile profile)
{
BuildFile (profile, "property-redefination-mac.cs");
}
[Test]
#if !NET
[TestCase (Profile.macOSFull)]
[TestCase (Profile.macOSSystem)]
#endif
[TestCase (Profile.macOSMobile)]
public void NSApplicationPublicEnsureMethods (Profile profile)
{
BuildFile (profile, "NSApplicationPublicEnsureMethods.cs");
}
[Test]
#if !NET
[TestCase (Profile.macOSFull)]
[TestCase (Profile.macOSSystem)]
#endif
[TestCase (Profile.macOSMobile)]
public void ProtocolDuplicateAbstract (Profile profile)
{
BuildFile (profile, "protocol-duplicate-abstract.cs");
}
[Test]
public void Bug15283 ()
{
BuildFile (Profile.iOS, "bug15283.cs");
}
[Test]
public void Bug15307 ()
{
BuildFile (Profile.iOS, "bug15307.cs");
}
#if !NET
// error BI1055: bgen: Internal error: failed to convert type 'System.Runtime.Versioning.SupportedOSPlatformAttribute, System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. Please file a bug report (https://github.com/xamarin/xamarin-macios/issues/new) with a test case.
[Test]
public void Bug15799 ()
{
BuildFile (Profile.iOS, "bug15799.cs");
}
#endif
[Test]
public void Bug16036 ()
{
BuildFile (Profile.iOS, "bug16036.cs");
}
[Test]
public void Bug17232 ()
{
BuildFile (Profile.iOS, "bug17232.cs");
}
#if !NET
// error BI1055: bgen: Internal error: failed to convert type 'System.Runtime.Versioning.SupportedOSPlatformAttribute, System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. Please file a bug report (https://github.com/xamarin/xamarin-macios/issues/new) with a test case.
[Test]
public void Bug23041 ()
{
BuildFile (Profile.iOS, "bug23041.cs");
}
#endif
[Test]
public void Bug24078 ()
{
BuildFile (Profile.iOS, "bug24078-ignore-methods-events.cs");
}
[Test]
public void Bug27428 ()
{
BuildFile (Profile.iOS, "bug27428.cs");
}
[Test]
#if !NET
[Ignore ("This only works in .NET")]
#endif
public void Bug27430 ()
{
BuildFile (Profile.iOS, "bug27430.cs");
}
[Test]
public void Bug27986 ()
{
var bgen = BuildFile (Profile.iOS, false, "bug27986.cs");
var allTypes = bgen.ApiAssembly.MainModule.GetTypes ().ToArray ();
var allMembers = ((IEnumerable<ICustomAttributeProvider>) allTypes)
.Union (allTypes.SelectMany ((type) => type.Methods))
.Union (allTypes.SelectMany ((type) => type.Fields))
.Union (allTypes.SelectMany ((type) => type.Properties));
var preserves = allMembers.Count ((v) => v.HasCustomAttributes && v.CustomAttributes.Any ((ca) => ca.AttributeType.Name == "PreserveAttribute"));
#if NET
Assert.AreEqual (36, preserves, "Preserve attribute count"); // If you modified code that generates PreserveAttributes please update the preserve count
#else
Assert.AreEqual (28, preserves, "Preserve attribute count"); // If you modified code that generates PreserveAttributes please update the preserve count
#endif
}
[Test]
public void Bug29493 ()
{
var bgen = BuildFile (Profile.iOS, false, "bug29493.cs");
// Check that there is no call to Class.GetHandle with a "global::"-prefixed string
foreach (var method in bgen.ApiAssembly.MainModule.GetTypes ().SelectMany ((v) => v.Methods)) {
if (!method.HasBody)
continue;
var instructions = method.Body.Instructions;
foreach (var ins in instructions) {
if (ins.OpCode.FlowControl != FlowControl.Call)
continue;
var mr = (MethodReference) ins.Operand;
if (mr.DeclaringType.Namespace != "ObjCRuntime" && mr.DeclaringType.Name != "Class")
continue;
if (mr.Name != "GetHandle" && mr.Name != "GetHandleIntrinsic")
continue;
var str = (string) ins.Previous.Operand;
if (str.StartsWith ("global::", StringComparison.Ordinal))
Assert.Fail ($"Found a call to Class.GetHandle with an invalid ('global::'-prefixed) string in {method.FullName} at offset {ins.Offset}.\n\t{string.Join ("\n\t", instructions)}");
}
}
}
[Test]
#if !NET
[TestCase (Profile.macOSFull)]
[TestCase (Profile.macOSSystem)]
#endif
[TestCase (Profile.macOSMobile)]
public void Bug31788 (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.Defines = BGenTool.GetDefaultDefines (bgen.Profile);
bgen.CreateTemporaryBinding (File.ReadAllText (Path.Combine (Configuration.SourceRoot, "tests", "generator", "bug31788.cs")));
bgen.AssertExecute ("build");
bgen.AssertNoWarnings ();
#if NET
bgen.AssertApiCallsMethod ("Test", "MarshalInProperty", "get_Shared", "xamarin_NativeHandle_objc_msgSend_exception", "MarshalInProperty.Shared getter");
bgen.AssertApiCallsMethod ("Test", "MarshalOnProperty", "get_Shared", "xamarin_NativeHandle_objc_msgSend_exception", "MarshalOnProperty.Shared getter");
#else
bgen.AssertApiCallsMethod ("Test", "MarshalInProperty", "get_Shared", "xamarin_IntPtr_objc_msgSend_exception", "MarshalInProperty.Shared getter");
bgen.AssertApiCallsMethod ("Test", "MarshalOnProperty", "get_Shared", "xamarin_IntPtr_objc_msgSend_exception", "MarshalOnProperty.Shared getter");
#endif
}
[Test]
public void Bug34042 ()
{
BuildFile (Profile.iOS, "bug34042.cs");
}
[Test]
[TestCase (Profile.iOS)]
public void NSCopyingNullability (Profile profile)
{
var bgen = BuildFile (profile, "tests/nscopying-nullability.cs");
bgen.AssertNoWarnings ();
}
[Test]
[TestCase (Profile.iOS)]
public void EditorBrowsable (Profile profile)
{
var bgen = BuildFile (profile, false, true, "tests/editor-browsable.cs");
var types = bgen.ApiAssembly.MainModule.Types;
var hasEditorBrowsableAttribute = new Func<ICustomAttributeProvider, bool> ((ICustomAttributeProvider provider) => {
return provider.CustomAttributes.Any (v => v.AttributeType.Name == "EditorBrowsableAttribute");
});
var strongEnumType = types.Single (v => v.Name == "StrongEnum");
Assert.IsTrue (hasEditorBrowsableAttribute (strongEnumType), "StrongEnumType");
var objcClassType = types.Single (v => v.Name == "ObjCClass");
Assert.IsTrue (hasEditorBrowsableAttribute (objcClassType), "ObjCClass");
}
static string RenderArgument (CustomAttributeArgument arg)
{
var td = arg.Type.Resolve ();
// If it's an enum value, try to find the enum field name and return that.
if (td?.BaseType?.Name == "Enum") {
if (arg.Value is byte b2) {
var fields = td.Fields
.Where (f => f.HasConstant && (byte) f.Constant == b2)
.OrderBy (f => f.Name);
if (fields.Any ())
return td.FullName + "." + fields.First ().Name;
}
}
var obj = arg.Value;
if (obj is null)
return "null";
if (obj is string str)
return "\"" + str + "\"";
if (obj is byte b)
return b.ToString ();
if (obj is int i32)
return i32.ToString ();
// Good enough for now, implement more cases as required.
throw new NotImplementedException (obj.GetType ().FullName);
}
static IEnumerable<CustomAttribute> GetAvailabilityAttributes (ICustomAttributeProvider provider)
{
if (!provider.HasCustomAttributes)
yield break;
foreach (var ca in provider.CustomAttributes) {
switch (ca.AttributeType.Name) {
#if NET
case "SupportedOSPlatformAttribute":
case "UnsupportedOSPlatformAttribute":
case "ObsoletedOSPlatformAttribute":
#else
case "IntroducedAttribute":
case "ObsoletedAttribute":
case "DeprecatedAttribute":
#endif
yield return ca;
break;
}
}
}
static string RenderSupportedOSPlatformAttributes (ICustomAttributeProvider provider)
{
var attributes = GetAvailabilityAttributes (provider).ToArray ();
if (attributes is null || attributes.Length == 0)
return string.Empty;
var lines = new List<string> ();
foreach (var ca in attributes)
lines.Add (RenderSupportedOSPlatformAttribute (ca));
lines.Sort ();
return string.Join ("\n", lines).Replace ("\r", string.Empty);
}
static string RenderSupportedOSPlatformAttribute (CustomAttribute ca)
{
return "[" + ca.AttributeType.Name.Replace ("Attribute", "") + "(" + string.Join (", ", ca.ConstructorArguments.Select (arg => RenderArgument (arg))) + ")]";
}
[Test]
public void Bug35176 ()
{
var bgen = BuildFile (Profile.iOS, "bug35176.cs");
var allTypes = bgen.ApiAssembly.MainModule.GetTypes ().ToArray ();
var allMembers = ((IEnumerable<ICustomAttributeProvider>) allTypes)
.Union (allTypes.SelectMany ((type) => type.Methods))
.Union (allTypes.SelectMany ((type) => type.Fields))
.Union (allTypes.SelectMany ((type) => type.Properties));
#if NET
const string attrib = "SupportedOSPlatformAttribute";
#else
const string attrib = "IntroducedAttribute";
#endif
var allSupportedAttributes = allMembers.SelectMany (v => v.CustomAttributes.Where (ca => ca.AttributeType.Name == attrib).Select (ca => new Tuple<ICustomAttributeProvider, CustomAttribute> (v, ca)));
var renderedSupportedAttributes = allSupportedAttributes.Select (v => v.Item1.ToString () + ": " + RenderSupportedOSPlatformAttribute (v.Item2) + "");
var preserves = allSupportedAttributes.Count ();
var renderedAttributes = "\t" + string.Join ("\n\t", renderedSupportedAttributes.OrderBy (v => v)) + "\n";
#if NET
string expectedAttributes =
@" Bug35176.IFooInterface: [SupportedOSPlatform(""ios14.3"")]
Bug35176.IFooInterface: [SupportedOSPlatform(""maccatalyst15.3"")]
Bug35176.IFooInterface: [SupportedOSPlatform(""macos12.2"")]
UIKit.UIView Bug35176.BarObject::BarView(): [SupportedOSPlatform(""ios14.3"")]
UIKit.UIView Bug35176.BarObject::BarView(): [SupportedOSPlatform(""maccatalyst15.3"")]
UIKit.UIView Bug35176.BarObject::BarView(): [SupportedOSPlatform(""macos12.2"")]
UIKit.UIView Bug35176.BarObject::FooView(): [SupportedOSPlatform(""ios14.3"")]
UIKit.UIView Bug35176.BarObject::FooView(): [SupportedOSPlatform(""maccatalyst15.3"")]
UIKit.UIView Bug35176.BarObject::FooView(): [SupportedOSPlatform(""macos12.2"")]
UIKit.UIView Bug35176.BarObject::get_BarView(): [SupportedOSPlatform(""ios14.4"")]
UIKit.UIView Bug35176.BarObject::get_BarView(): [SupportedOSPlatform(""maccatalyst15.4"")]
UIKit.UIView Bug35176.BarObject::get_BarView(): [SupportedOSPlatform(""macos12.2"")]
UIKit.UIView Bug35176.BarObject::GetBarMember(System.Int32): [SupportedOSPlatform(""ios14.3"")]
UIKit.UIView Bug35176.BarObject::GetBarMember(System.Int32): [SupportedOSPlatform(""maccatalyst15.3"")]
UIKit.UIView Bug35176.BarObject::GetBarMember(System.Int32): [SupportedOSPlatform(""macos12.2"")]
UIKit.UIView Bug35176.FooInterface_Extensions::GetBarView(Bug35176.IFooInterface): [SupportedOSPlatform(""ios14.4"")]
UIKit.UIView Bug35176.FooInterface_Extensions::GetBarView(Bug35176.IFooInterface): [SupportedOSPlatform(""maccatalyst15.4"")]
UIKit.UIView Bug35176.FooInterface_Extensions::GetBarView(Bug35176.IFooInterface): [SupportedOSPlatform(""macos12.2"")]
UIKit.UIView Bug35176.IFooInterface::_GetBarView(Bug35176.IFooInterface): [SupportedOSPlatform(""ios14.4"")]
UIKit.UIView Bug35176.IFooInterface::_GetBarView(Bug35176.IFooInterface): [SupportedOSPlatform(""maccatalyst15.4"")]
UIKit.UIView Bug35176.IFooInterface::_GetBarView(Bug35176.IFooInterface): [SupportedOSPlatform(""macos12.2"")]
UIKit.UIView Bug35176.IFooInterface::get_BarView(): [SupportedOSPlatform(""ios14.4"")]
UIKit.UIView Bug35176.IFooInterface::get_BarView(): [SupportedOSPlatform(""maccatalyst15.4"")]
UIKit.UIView Bug35176.IFooInterface::get_BarView(): [SupportedOSPlatform(""macos12.2"")]
";
#else
string expectedAttributes =
@" Bug35176.IFooInterface: [Introduced(ObjCRuntime.PlatformName.iOS, 14, 3, ObjCRuntime.PlatformArchitecture.None, null)]
Bug35176.IFooInterface: [Introduced(ObjCRuntime.PlatformName.MacCatalyst, 15, 3, ObjCRuntime.PlatformArchitecture.None, null)]
Bug35176.IFooInterface: [Introduced(ObjCRuntime.PlatformName.MacOSX, 12, 2, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::BarView(): [Introduced(ObjCRuntime.PlatformName.iOS, 14, 3, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::BarView(): [Introduced(ObjCRuntime.PlatformName.MacCatalyst, 15, 3, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::BarView(): [Introduced(ObjCRuntime.PlatformName.MacOSX, 12, 2, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::FooView(): [Introduced(ObjCRuntime.PlatformName.iOS, 14, 3, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::FooView(): [Introduced(ObjCRuntime.PlatformName.MacCatalyst, 15, 3, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::FooView(): [Introduced(ObjCRuntime.PlatformName.MacOSX, 12, 2, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::get_BarView(): [Introduced(ObjCRuntime.PlatformName.iOS, 14, 4, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::get_BarView(): [Introduced(ObjCRuntime.PlatformName.MacCatalyst, 15, 4, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::GetBarMember(System.Int32): [Introduced(ObjCRuntime.PlatformName.iOS, 14, 3, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::GetBarMember(System.Int32): [Introduced(ObjCRuntime.PlatformName.MacCatalyst, 15, 3, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.BarObject::GetBarMember(System.Int32): [Introduced(ObjCRuntime.PlatformName.MacOSX, 12, 2, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.FooInterface_Extensions::GetBarView(Bug35176.IFooInterface): [Introduced(ObjCRuntime.PlatformName.iOS, 14, 4, ObjCRuntime.PlatformArchitecture.None, null)]
UIKit.UIView Bug35176.FooInterface_Extensions::GetBarView(Bug35176.IFooInterface): [Introduced(ObjCRuntime.PlatformName.MacCatalyst, 15, 4, ObjCRuntime.PlatformArchitecture.None, null)]
";
#endif
expectedAttributes = expectedAttributes.Replace ("\r", string.Empty);
renderedAttributes = renderedAttributes.Replace ("\r", string.Empty);
if (renderedAttributes != expectedAttributes) {
Console.WriteLine ($"Expected:");
Console.WriteLine (expectedAttributes);
Console.WriteLine ($"Actual:");
Console.WriteLine (renderedAttributes);
}
Assert.AreEqual (expectedAttributes, renderedAttributes, "Introduced attributes");
}
[Test]
[TestCase (Profile.iOS)]
public void INativeObjectsInBlocks (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.Defines = BGenTool.GetDefaultDefines (bgen.Profile);
bgen.AddTestApiDefinition ("tests/inativeobjects-in-blocks.cs");
bgen.AddExtraSourcesRelativeToGeneratorDirectory ("tests/inativeobjects-in-blocks-sources.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
bgen.AssertNoWarnings ();
}
[Test]
public void Bug36457 ()
{
BuildFile (Profile.iOS, "bug36457.cs");
}
[Test]
[TestCase (Profile.iOS)]
public void Bug39614 (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("bug39614.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
bgen.AssertWarning (1103, "'FooType`1' does not live under a namespace; namespaces are a highly recommended .NET best practice");
}
#if !NET
[Ignore ("This only applies to .NET")]
#endif
[TestCase (Profile.iOS)]
public void Bug18035 (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("bug18025.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
bgen.AssertWarning (1103, "'FooType' does not live under a namespace; namespaces are a highly recommended .NET best practice");
}
[Test]
public void Bug40282 ()
{
BuildFile (Profile.iOS, "bug40282.cs");
}
[Test]
public void Bug42742 ()
{
var bgen = BuildFile (Profile.iOS, "bug42742.cs");
var allTypes = bgen.ApiAssembly.MainModule.GetTypes ().ToArray ();
var allMembers = ((IEnumerable<ICustomAttributeProvider>) allTypes)
.Union (allTypes.SelectMany ((type) => type.Methods))
.Union (allTypes.SelectMany ((type) => type.Fields))
.Union (allTypes.SelectMany ((type) => type.Properties));
var preserves = allMembers.Sum ((v) => v.CustomAttributes.Count ((ca) => ca.AttributeType.Name == "AdviceAttribute"));
#if NET
Assert.AreEqual (33, preserves, "Advice attribute count"); // If you modified code that generates AdviceAttributes please update the attribute count
#else
Assert.AreEqual (24, preserves, "Advice attribute count"); // If you modified code that generates AdviceAttributes please update the attribute count
#endif
}
[Test]
public void Bug43579 ()
{
BuildFile (Profile.iOS, "bug43579.cs");
}
[Test]
[TestCase (Profile.iOS)]
public void Bug46292 (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.ProcessEnums = true;
bgen.AddTestApiDefinition ("bug46292.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
var allTypes = bgen.ApiAssembly.MainModule.GetTypes ().ToArray ();
var allMembers = ((IEnumerable<ICustomAttributeProvider>) allTypes)
.Union (allTypes.SelectMany ((type) => type.Methods))
.Union (allTypes.SelectMany ((type) => type.Fields))
.Union (allTypes.SelectMany ((type) => type.Properties));
var attribCount = allMembers.Count ((v) => v.HasCustomAttributes && v.CustomAttributes.Any ((ca) => ca.AttributeType.Name == "ObsoleteAttribute"));
Assert.AreEqual (2, attribCount, "attribute count");
}
[Test]
public void Bug53076 ()
{
var bgen = BuildFile (Profile.iOS, "bug53076.cs");
var allTypes = bgen.ApiAssembly.MainModule.GetTypes ().ToArray ();
var allMethods = bgen.ApiAssembly.MainModule.GetTypes ().SelectMany ((type) => type.Methods);
// Count all *Async methods whose first parameter is 'IMyFooProtocol'.
var methodCount = allMethods.Count ((v) => v.Name.EndsWith ("Async", StringComparison.Ordinal) && v.Parameters.Count > 0 && v.Parameters [0].ParameterType.Name == "IMyFooProtocol");
Assert.AreEqual (10, methodCount, "Async method count");
}
[Test]
public void Bug53076WithModel ()
{
var bgen = BuildFile (Profile.iOS, "bug53076withmodel.cs");
var allTypes = bgen.ApiAssembly.MainModule.GetTypes ().ToArray ();
var allMethods = bgen.ApiAssembly.MainModule.GetTypes ().SelectMany ((type) => type.Methods);
// Count all *Async methods whose first parameter is 'IMyFooProtocol'.
var methodCount = allMethods.Count ((v) => v.Name.EndsWith ("Async", StringComparison.Ordinal) && v.Parameters.Count > 0 && v.Parameters [0].ParameterType.Name == "IMyFooProtocol");
Assert.AreEqual (10, methodCount, "Async method count");
}
[Test]
#if !NET
[Ignore ("This only works in .NET")]
#endif
public void StackOverflow20696157 ()
{
BuildFile (Profile.iOS, "sof20696157.cs");
}
[Test]
[TestCase (Profile.iOS)]
public void TypesInMultipleNamespaces (Profile profile)
{
BuildFile (profile, "tests/types-in-multiple-namespaces.cs");
}
[Test]
public void HyphenInName ()
{
BuildFile (Profile.iOS, "btouch-with-hyphen-in-name.cs");
}
[Test]
public void PropertyRedefinition ()
{
BuildFile (Profile.iOS, "property-redefination-ios.cs");
}
[Test]
public void ArrayFromHandleBug ()
{
BuildFile (Profile.iOS, "arrayfromhandlebug.cs");
}
[Test]
public void StrongDictSupportTemplatedDicts ()
{
BuildFile (Profile.iOS, "strong-dict-support-templated-dicts.cs");
}
[Test]
public void GenericStrongDictionary ()
{
BuildFile (Profile.iOS, "generic-strong-dictionary.cs");
}
[Test]
public void BindAsTests ()
{
BuildFile (Profile.iOS, "bindastests.cs");
}
[Test]
public void Forum54078 ()
{
var bgen = BuildFile (Profile.iOS, "forum54078.cs");
var api = bgen.ApiAssembly;
var type = api.MainModule.GetType ("Test", "CustomController");
foreach (var method in type.Methods)
Asserts.DoesNotThrowExceptions (method, type.FullName);
}
[Test]
public void Desk63279 ()
{
BuildFile (Profile.iOS, "desk63279A.cs", "desk63279B.cs");
}
[Test]
public void Desk79124 ()
{
var bgen = BuildFile (Profile.iOS, "desk79124.cs");
bgen.AssertType ("Desk79124.WYPopoverBackgroundView/WYPopoverBackgroundViewAppearance");
}
[Test]
public void MultipleApiDefinitions1 ()
{
BuildFile (Profile.iOS, "multiple-api-definitions1.cs");
}
[Test]
public void MultipleApiDefinitions2 ()
{
BuildFile (Profile.iOS, "multiple-api-definitions2-a.cs", "multiple-api-definitions2-b.cs");
}
[Test]
[TestCase (Profile.iOS)]
public void INativeObjectArraysInBlocks (Profile profile)
{
BuildFile (profile, "tests/inativeobject-arrays-in-blocks.cs");
}
[Test]
[TestCase (Profile.iOS)]
public void ClassNameCollision (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.Defines = BGenTool.GetDefaultDefines (bgen.Profile);
bgen.Sources.Add (Path.Combine (Configuration.SourceRoot, "tests", "generator", "classNameCollision-enum.cs"));
bgen.ApiDefinitions.Add (Path.Combine (Configuration.SourceRoot, "tests", "generator", "classNameCollision.cs"));
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
bgen.AssertNoWarnings ();
}
[Test]
[TestCase (Profile.iOS)]
public void VirtualWrap (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("virtualwrap.cs");
bgen.CreateTemporaryBinding ();
bgen.ProcessEnums = true;
bgen.AssertExecute ("build");
// verify virtual methods
var attribs = MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot;
bgen.AssertMethod ("WrapTest.MyFooClass", "FromUrl", attribs, null, "Foundation.NSUrl");
bgen.AssertMethod ("WrapTest.MyFooClass", "FromUrl", attribs, null, "System.String");
bgen.AssertMethod ("WrapTest.MyFooClass", "get_FooNSString", attribs | MethodAttributes.SpecialName, "Foundation.NSString");
bgen.AssertMethod ("WrapTest.MyFooClass", "get_FooString", attribs | MethodAttributes.SpecialName, "System.String");
// verify non-virtual methods
attribs = MethodAttributes.Public | MethodAttributes.HideBySig;
bgen.AssertMethod ("WrapTest.MyFooClass", "FromUrlN", attribs, null, "System.String");
bgen.AssertMethod ("WrapTest.MyFooClass", "get_FooNSStringN", attribs | MethodAttributes.SpecialName, "Foundation.NSString");
}
[Test]
[TestCase (Profile.iOS)]
public void NoAsyncInternalWrapper (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("noasyncinternalwrapper.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
var allTypes = bgen.ApiAssembly.MainModule.GetTypes ().ToArray ();
var allMembers = ((IEnumerable<MemberReference>) allTypes)
.Union (allTypes.SelectMany ((type) => type.Methods))
.Union (allTypes.SelectMany ((type) => type.Fields))
.Union (allTypes.SelectMany ((type) => type.Properties));
#if NET
Assert.AreEqual (2, allMembers.Count ((member) => member.Name == "RequiredMethodAsync"), "Expected 2 RequiredMethodAsync members in generated code. If you modified code that generates RequiredMethodAsync (AsyncAttribute) please update the RequiredMethodAsync count.");
#else
Assert.AreEqual (1, allMembers.Count ((member) => member.Name == "RequiredMethodAsync"), "Expected 1 RequiredMethodAsync members in generated code. If you modified code that generates RequiredMethodAsync (AsyncAttribute) please update the RequiredMethodAsync count.");
#endif
var attribs = MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig;
bgen.AssertMethod ("NoAsyncInternalWrapperTests.MyFooDelegate_Extensions", "RequiredMethodAsync", attribs, "System.Threading.Tasks.Task", "NoAsyncInternalWrapperTests.IMyFooDelegate", "System.Int32");
}
[Test]
[TestCase (Profile.iOS)]
public void NoAsyncWarningCS0219 (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("noasyncwarningcs0219.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
bgen.AssertNoWarnings ();
}
[Test]
[TestCase (Profile.iOS)]
public void FieldEnumTests (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.ProcessEnums = true;
bgen.AddTestApiDefinition ("fieldenumtests.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
bgen.AssertNoWarnings ();
}
[Test]
[TestCase (Profile.iOS)]
public void SmartEnumWithFramework (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.ProcessEnums = true;
bgen.AddTestApiDefinition ("smartenumwithframework.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
bgen.AssertApiLoadsField ("SmartEnumWithFramework.FooEnumTestExtensions", "get_First", "ObjCRuntime.Libraries/CoreImage", "Handle", "First getter");
bgen.AssertApiLoadsField ("SmartEnumWithFramework.FooEnumTestExtensions", "get_Second", "ObjCRuntime.Libraries/CoreImage", "Handle", "Second getter");
}
[Test]
public void ForcedType ()
{
var bgen = BuildFile (Profile.iOS, false, "forcedtype.cs");
var allMethods = bgen.ApiAssembly.MainModule.GetTypes ().SelectMany ((type) => type.Methods);
// Count the number of calls to GetINativeObject
var getINativeObjectCalls = allMethods.Sum ((method) => {
if (!method.HasBody)
return 0;
return method.Body.Instructions.Count ((ins) => {
if (ins.OpCode.FlowControl != FlowControl.Call)
return false;
var mr = (MethodReference) ins.Operand;
return mr.Name == "GetINativeObject";
});
});
Assert.AreEqual (12, getINativeObjectCalls, "Preserve attribute count"); // If you modified code that generates PreserveAttributes please update the preserve count
}
[Test]
public void IsDirectBinding ()
{
var bgen = BuildFile (Profile.iOS, "tests/is-direct-binding.cs");
var callsMethod = new Func<MethodDefinition, string, bool> ((method, name) => {
return method.Body.Instructions.Any ((ins) => {
switch (ins.OpCode.Code) {
case Code.Call:
case Code.Calli:
case Code.Callvirt:
var mr = ins.Operand as MethodReference;
return mr.Name == name;
default:
return false;
}
});
});
// The normal constructor should get the IsDirectBinding value, and call both objc_msgSend and objc_msgSendSuper
var cConstructor = bgen.ApiAssembly.MainModule.GetType ("NS", "C").Methods.First ((v) => v.IsConstructor && !v.HasParameters && !v.IsStatic);
Assert.That (callsMethod (cConstructor, "set_IsDirectBinding"), "C: set_IsDirectBinding");
Assert.That (callsMethod (cConstructor, "get_IsDirectBinding"), "C: get_IsDirectBinding");
Assert.That (callsMethod (cConstructor, "IntPtr_objc_msgSend"), "C: objc_msgSend");
Assert.That (callsMethod (cConstructor, "IntPtr_objc_msgSendSuper"), "C: objc_msgSendSuper");
// The constructor for a model should not get the IsDirectBinding value, because it's always 'false'. Neither should it call objc_msgSend, only objc_msgSendSuper
var pConstructor = bgen.ApiAssembly.MainModule.GetType ("NS", "P").Methods.First ((v) => v.IsConstructor && !v.HasParameters && !v.IsStatic);
Assert.That (callsMethod (pConstructor, "set_IsDirectBinding"), "P: set_IsDirectBinding");
Assert.That (!callsMethod (pConstructor, "get_IsDirectBinding"), "P: get_IsDirectBinding");
Assert.That (!callsMethod (pConstructor, "IntPtr_objc_msgSend"), "P: objc_msgSend");
Assert.That (callsMethod (pConstructor, "IntPtr_objc_msgSendSuper"), "P: objc_msgSendSuper");
// The constructor for a sealed class should not get the IsDirectBinding value, because it's always true. Neither should it call objc_msgSendSuper, only objc_msgSend.
var sConstructor = bgen.ApiAssembly.MainModule.GetType ("NS", "S").Methods.First ((v) => v.IsConstructor && !v.HasParameters && !v.IsStatic);
Assert.That (callsMethod (sConstructor, "set_IsDirectBinding"), "S: set_IsDirectBinding");
Assert.That (!callsMethod (sConstructor, "get_IsDirectBinding"), "S: get_IsDirectBinding");
Assert.That (callsMethod (sConstructor, "IntPtr_objc_msgSend"), "S: objc_msgSend");
Assert.That (!callsMethod (sConstructor, "IntPtr_objc_msgSendSuper"), "S: objc_msgSendSuper");
}
[Test]
public void Bug57531 () => BuildFile (Profile.iOS, "bug57531.cs");
[Test]
public void Bug57870 () => BuildFile (Profile.iOS, true, true, "bug57870.cs");
[Test]
public void GHIssue3869 () => BuildFile (Profile.iOS, "ghissue3869.cs");
[Test]
#if NET
[TestCase ("issue3875.cs", "api0__Issue3875_AProtocol")]
#else
[TestCase ("issue3875.cs", "AProtocol")]
#endif
[TestCase ("issue3875B.cs", "BProtocol")]
[TestCase ("issue3875C.cs", "api0__Issue3875_AProtocol")]
public void Issue3875 (string file, string modelName)
{
var bgen = BuildFile (Profile.iOS, file);
var attrib = bgen.ApiAssembly.MainModule.GetType ("Issue3875", "AProtocol").CustomAttributes.Where ((v) => v.AttributeType.Name == "RegisterAttribute").First ();
Assert.AreEqual (modelName, attrib.ConstructorArguments [0].Value, "Custom ObjC name");
}
[Test]
public void GHIssue5444 () => BuildFile (Profile.iOS, "ghissue5444.cs");
[Test]
[TestCase (Profile.iOS)]
public void GH5416_method (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("ghissue5416b.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
bgen.AssertWarning (1118, "[NullAllowed] should not be used on methods, like 'Foundation.NSString Method(Foundation.NSDate, Foundation.NSObject)', but only on properties, parameters and return values.");
}
[Test]
[TestCase (Profile.iOS)]
public void GH5416_setter (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("ghissue5416a.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
bgen.AssertWarning (1118, "[NullAllowed] should not be used on methods, like 'System.Void set_Setter(Foundation.NSString)', but only on properties, parameters and return values.");
}
[Test]
public void GHIssue5692 () => BuildFile (Profile.iOS, "ghissue5692.cs");
[Test]
public void GHIssue7304 () => BuildFile (Profile.macOSMobile, "ghissue7304.cs");
[Test]
#if !NET
[Ignore ("This only works in .NET")]
#endif
public void RefOutParameters ()
{
BuildFile (Profile.macOSMobile, true, "tests/ref-out-parameters.cs");
}
[Test]
public void ReturnRelease ()
{
BuildFile (Profile.iOS, "tests/return-release.cs");
}
[Test]
public void GHIssue6626 () => BuildFile (Profile.iOS, "ghissue6626.cs");
[Test]
public void StrongDictsNativeEnums () => BuildFile (Profile.iOS, "strong-dict-native-enum.cs");
[Test]
public void IgnoreUnavailableProtocol ()
{
var bgen = BuildFile (Profile.iOS, "tests/ignore-unavailable-protocol.cs");
var myClass = bgen.ApiAssembly.MainModule.GetType ("NS", "MyClass");
var myProtocol = bgen.ApiAssembly.MainModule.GetType ("NS", "IMyProtocol");
var myClassInterfaces = myClass.Interfaces.Select (v => v.InterfaceType.Name).ToArray ();
Assert.That (myClassInterfaces, Does.Not.Contain ("IMyProtocol"), "IMyProtocol");
Assert.IsNull (myProtocol, "MyProtocol null");
}
[Test]
public void VSTS970507 ()
{
BuildFile (Profile.iOS, "tests/vsts-970507.cs");
}
[Test]
public void DiamondProtocol ()
{
BuildFile (Profile.iOS, "tests/diamond-protocol.cs");
}
[Test]
public void GHIssue9065_Sealed () => BuildFile (Profile.iOS, nowarnings: true, "ghissue9065.cs");
[Test]
public void GHIssue18645_DuplicatedFiled () => BuildFile (Profile.iOS, nowarnings: true, "ghissue18645.cs");
// looking for [BindingImpl (BindingImplOptions.Optimizable)]
bool IsOptimizable (MethodDefinition method)
{
const int Optimizable = 0x2; // BindingImplOptions flag
if (!method.HasCustomAttributes)
return false;
foreach (var ca in method.CustomAttributes) {
if (ca.AttributeType.Name != "BindingImplAttribute")
continue;
foreach (var a in ca.ConstructorArguments)
return (((int) a.Value & Optimizable) == Optimizable);
}
return false;
}
[Test]
public void DisposeAttributeOptimizable ()
{
var profile = Profile.iOS;
var bgen = BuildFile (profile, "tests/dispose-attribute.cs");
// processing custom attributes (like its properties) will call Resolve so we must be able to find the platform assembly to run this test
var resolver = bgen.ApiAssembly.MainModule.AssemblyResolver as BaseAssemblyResolver;
#if NET
resolver.AddSearchDirectory (Configuration.GetRefDirectory (profile.AsPlatform ()));
#else
resolver.AddSearchDirectory (Path.Combine (Configuration.SdkRootXI, "lib/mono/Xamarin.iOS/"));
#endif
// [Dispose] is, by default, not optimizable
var with_dispose = bgen.ApiAssembly.MainModule.GetType ("NS", "WithDispose").Methods.First ((v) => v.Name == "Dispose");
Assert.NotNull (with_dispose, "WithDispose");
Assert.That (IsOptimizable (with_dispose), Is.False, "WithDispose/Optimizable");
// [Dispose] can opt-in being optimizable
var with_dispose_optin = bgen.ApiAssembly.MainModule.GetType ("NS", "WithDisposeOptInOptimizable").Methods.First ((v) => v.Name == "Dispose");
Assert.NotNull (with_dispose_optin, "WithDisposeOptInOptimizable");
Assert.That (IsOptimizable (with_dispose_optin), Is.True, "WithDisposeOptInOptimizable/Optimizable");
// Without a [Dispose] attribute the generated method is optimizable
var without_dispose = bgen.ApiAssembly.MainModule.GetType ("NS", "WithoutDispose").Methods.First ((v) => v.Name == "Dispose");
Assert.NotNull (without_dispose, "WitoutDispose");
Assert.That (IsOptimizable (without_dispose), Is.True, "WitoutDispose/Optimizable");
}
[Test]
public void SnippetAttributesOptimizable ()
{
var profile = Profile.iOS;
var bgen = BuildFile (profile, "tests/snippet-attributes.cs");
// processing custom attributes (like its properties) will call Resolve so we must be able to find the platform assembly to run this test
var resolver = bgen.ApiAssembly.MainModule.AssemblyResolver as BaseAssemblyResolver;
#if NET
resolver.AddSearchDirectory (Configuration.GetRefDirectory (profile.AsPlatform ()));
#else
resolver.AddSearchDirectory (Path.Combine (Configuration.SdkRootXI, "lib/mono/Xamarin.iOS/"));
#endif
// [SnippetAttribute] subclasses are, by default, not optimizable
var not_opt = bgen.ApiAssembly.MainModule.GetType ("NS", "NotOptimizable");
Assert.NotNull (not_opt, "NotOptimizable");
var pre_not_opt = not_opt.Methods.First ((v) => v.Name == "Pre");
Assert.That (IsOptimizable (pre_not_opt), Is.False, "NotOptimizable/Pre");
var prologue_not_opt = not_opt.Methods.First ((v) => v.Name == "Prologue");
Assert.That (IsOptimizable (prologue_not_opt), Is.False, "NotOptimizable/Prologue");
var post_not_opt = not_opt.Methods.First ((v) => v.Name == "Post");
Assert.That (IsOptimizable (post_not_opt), Is.False, "NotOptimizable/Post");
// [SnippetAttribute] subclasses can opt-in being optimizable
var optin_opt = bgen.ApiAssembly.MainModule.GetType ("NS", "OptInOptimizable");
Assert.NotNull (optin_opt, "OptInOptimizable");
var pre_optin_opt = optin_opt.Methods.First ((v) => v.Name == "Pre");
Assert.That (IsOptimizable (pre_optin_opt), Is.True, "OptInOptimizable/Pre");
var prologue_optin_opt = optin_opt.Methods.First ((v) => v.Name == "Prologue");
Assert.That (IsOptimizable (prologue_optin_opt), Is.True, "OptInOptimizable/Prologue");
var post_optin_opt = optin_opt.Methods.First ((v) => v.Name == "Post");
Assert.That (IsOptimizable (post_optin_opt), Is.True, "OptInOptimizable/Post");
// Without a [SnippetAttribute] subclass attribute the generated method is optimizable
var nothing = bgen.ApiAssembly.MainModule.GetType ("NS", "NoSnippet").Methods.First ((v) => v.Name == "Nothing");
Assert.NotNull (nothing, "NoSnippet");
Assert.That (IsOptimizable (nothing), Is.True, "Nothing/Optimizable");
}
[Test]
[TestCase (Profile.iOS)]
public void NativeEnum (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.ProcessEnums = true;
bgen.Defines = BGenTool.GetDefaultDefines (bgen.Profile);
bgen.Sources = new string [] { Path.Combine (Configuration.SourceRoot, "tests", "generator", "tests", "nativeenum-extensions.cs") }.ToList ();
bgen.ApiDefinitions = new string [] { Path.Combine (Configuration.SourceRoot, "tests", "generator", "tests", "nativeenum.cs") }.ToList ();
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
}
[Test]
public void DelegateWithINativeObjectReturnType ()
{
var bgen = BuildFile (Profile.iOS, "tests/delegate-with-inativeobject-return-type.cs");
bgen.AssertExecute ("build");
// Assert that the return type from the delegate is IntPtr
var type = bgen.ApiAssembly.MainModule.GetType ("ObjCRuntime", "Trampolines").NestedTypes.First (v => v.Name == "DMyHandler");
Assert.NotNull (type, "DMyHandler");
var method = type.Methods.First (v => v.Name == "Invoke");
#if NET
Assert.AreEqual ("ObjCRuntime.NativeHandle", method.ReturnType.FullName, "Return type");
#else
Assert.AreEqual ("System.IntPtr", method.ReturnType.FullName, "Return type");
#endif
}
[Test]
public void ProtocolBindProperty ()
{
var bgen = BuildFile (Profile.iOS, "tests/protocol-bind-property.cs");
bgen.AssertExecute ("build");
// Assert that the return type from the delegate is IntPtr
var type = bgen.ApiAssembly.MainModule.GetType ("NS", "MyProtocol_Extensions");
Assert.NotNull (type, "MyProtocol_Extensions");
var method = type.Methods.First (v => v.Name == "GetOptionalProperty");
var ldstr = method.Body.Instructions.Single (v => v.OpCode == OpCodes.Ldstr);
Assert.AreEqual ("isOptionalProperty", (string) ldstr.Operand, "isOptionalProperty");
method = type.Methods.First (v => v.Name == "SetOptionalProperty");
ldstr = method.Body.Instructions.Single (v => v.OpCode == OpCodes.Ldstr);
Assert.AreEqual ("setOptionalProperty:", (string) ldstr.Operand, "setOptionalProperty");
type = bgen.ApiAssembly.MainModule.GetType ("NS", "MyProtocolWrapper");
Assert.NotNull (type, "MyProtocolWrapper");
method = type.Methods.First (v => v.Name == "get_AbstractProperty");
ldstr = method.Body.Instructions.Single (v => v.OpCode == OpCodes.Ldstr);
Assert.AreEqual ("isAbstractProperty", (string) ldstr.Operand, "isAbstractProperty");
method = type.Methods.First (v => v.Name == "set_AbstractProperty");
ldstr = method.Body.Instructions.Single (v => v.OpCode == OpCodes.Ldstr);
Assert.AreEqual ("setAbstractProperty:", (string) ldstr.Operand, "setAbstractProperty");
}
[Test]
public void AbstractTypeTest ()
{
var bgen = BuildFile (Profile.iOS, "tests/abstract-type.cs");
bgen.AssertExecute ("build");
// Assert that the return type from the delegate is IntPtr
var type = bgen.ApiAssembly.MainModule.GetType ("NS", "MyObject");
Assert.NotNull (type, "MyObject");
#if NET
Assert.IsFalse (type.IsAbstract, "IsAbstract");
#else
Assert.IsTrue (type.IsAbstract, "IsAbstract");
#endif
var method = type.Methods.First (v => v.Name == ".ctor" && !v.HasParameters && !v.IsStatic);
#if NET
Assert.IsTrue (method.IsFamily, "IsProtected ctor");
#else
Assert.IsFalse (method.IsPublic, "IsPublic ctor");
#endif
method = type.Methods.First (v => v.Name == "AbstractMember" && !v.HasParameters && !v.IsStatic);
var throwInstruction = method.Body?.Instructions?.FirstOrDefault (v => v.OpCode == OpCodes.Throw);
Assert.IsTrue (method.IsPublic, "IsPublic ctor");
Assert.IsTrue (method.IsVirtual, "IsVirtual");
#if NET
Assert.IsFalse (method.IsAbstract, "IsAbstract");
Assert.IsNotNull (throwInstruction, "Throw");
#else
Assert.IsTrue (method.IsAbstract, "IsAbstract");
Assert.IsNull (throwInstruction, "Throw");
#endif
}
[Test]
#if !NET
[Ignore ("This only applies to .NET")]
#else
[Ignore ("https://github.com/dotnet/roslyn/issues/61525")]
#endif
public void NativeIntDelegates ()
{
var bgen = BuildFile (Profile.iOS, "tests/nint-delegates.cs");
Func<string, bool> verifyDelegate = (typename) => {
// Assert that the return type from the delegate is IntPtr
var type = bgen.ApiAssembly.MainModule.GetType ("NS", typename);
Assert.NotNull (type, typename);
var method = type.Methods.First (m => m.Name == "Invoke");
Assert.IsNotNull (method.MethodReturnType.CustomAttributes.FirstOrDefault (attr => attr.AttributeType.Name == "NativeIntegerAttribute"), "Return type for delegate " + typename);
foreach (var p in method.Parameters) {
Assert.IsNotNull (p.CustomAttributes.FirstOrDefault (attr => attr.AttributeType.Name == "NativeIntegerAttribute"), $"Parameter {p.Name}'s type for delegate " + typename);
}
return false;
};
verifyDelegate ("D1");
verifyDelegate ("D2");
verifyDelegate ("D3");
verifyDelegate ("NSTableViewColumnRowPredicate");
}
[Test]
#if !NET
[Ignore ("This only applies to .NET")]
#endif
public void CSharp10Syntax ()
{
BuildFile (Profile.iOS, "tests/csharp10syntax.cs");
}
[Test]
[TestCase (Profile.iOS)]
#if !NET
[Ignore ("This only applies to .NET")]
#endif
public void AttributesFromInlinedProtocols (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("tests/attributes-from-inlined-protocols.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
var type = bgen.ApiAssembly.MainModule.GetType ("NS", "TypeA");
var expectedAttributes = new string [] {
@"[BindingImpl(3)]
[Export(""someMethod1:"")]
[SupportedOSPlatform(""ios13.0"")]
[SupportedOSPlatform(""maccatalyst"")]
[UnsupportedOSPlatform(""tvos"")]",
@"[BindingImpl(3)]
[Export(""someMethod2:"")]
[SupportedOSPlatform(""ios13.0"")]
[SupportedOSPlatform(""maccatalyst"")]
[UnsupportedOSPlatform(""tvos"")]",
@"[BindingImpl(3)]
[Export(""someMethod3:"")]
[SupportedOSPlatform(""ios"")]
[SupportedOSPlatform(""maccatalyst"")]
[UnsupportedOSPlatform(""tvos"")]",
@"[BindingImpl(3)]
[Export(""someMethod4:"")]
[SupportedOSPlatform(""ios"")]
[SupportedOSPlatform(""maccatalyst"")]
[UnsupportedOSPlatform(""tvos"")]",
};
int someMethodCount = expectedAttributes.Length;
var someMethod = new MethodDefinition [someMethodCount];
var renderedSomeMethod = new string [someMethodCount];
var failures = new List<string> ();
for (var i = 0; i < someMethodCount; i++) {
someMethod [i] = type.Methods.Single (v => v.Name == "SomeMethod" + (i + 1).ToString ());
renderedSomeMethod [i] = string.Join ("\n", someMethod [i].CustomAttributes.Select (ca => RenderSupportedOSPlatformAttribute (ca)).OrderBy (v => v));
expectedAttributes [i] = expectedAttributes [i].Replace ("\r", string.Empty);
renderedSomeMethod [i] = renderedSomeMethod [i].Replace ("\r", string.Empty);
if (expectedAttributes [i] == renderedSomeMethod [i])
continue;
var msg =
$"{someMethod [i].Name} has different attributes.\n" +
$"Expected attributes:\n" +
expectedAttributes [i] + "\n" +
"Actual attributes:\n" +
renderedSomeMethod [i];
Console.WriteLine ($"❌ {msg}\n");
failures.Add (msg);
}
Assert.That (failures, Is.Empty, "Failures");
}
[Test]
public void NFloatType ()
{
var bgen = BuildFile (Profile.iOS, "tests/nfloat.cs");
var messaging = bgen.ApiAssembly.MainModule.Types.FirstOrDefault (v => v.Name == "Messaging");
Assert.IsNotNull (messaging, "Messaging");
var pinvoke = messaging.Methods.FirstOrDefault (v => v.Name == "xamarin_nfloat_objc_msgSend_exception");
Assert.IsNotNull (pinvoke, "PInvoke");
}
[Test]
[TestCase (Profile.iOS)]
public void NoAvailabilityForAccessors (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("tests/no-availability-for-accessors.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
bgen.AssertMethod ("NS.Whatever", "get_PropA");
bgen.AssertNoMethod ("NS.Whatever", "set_PropA", parameterTypes: "Foundation.NSObject");
bgen.AssertMethod ("NS.Whatever", "set_PropB", parameterTypes: "Foundation.NSObject");
bgen.AssertNoMethod ("NS.Whatever", "get_PropB");
bgen.AssertMethod ("NS.Whatever", "get_IPropA");
bgen.AssertNoMethod ("NS.Whatever", "set_IPropA", parameterTypes: "Foundation.NSObject");
bgen.AssertMethod ("NS.Whatever", "set_IPropB", parameterTypes: "Foundation.NSObject");
bgen.AssertNoMethod ("NS.Whatever", "get_IPropB");
bgen.AssertMethod ("NS.Whatever", "get_IPropAOpt");
bgen.AssertNoMethod ("NS.Whatever", "set_IPropAOpt", parameterTypes: "Foundation.NSObject");
bgen.AssertMethod ("NS.Whatever", "set_IPropBOpt", parameterTypes: "Foundation.NSObject");
bgen.AssertNoMethod ("NS.Whatever", "get_IPropBOpt");
bgen.AssertMethod ("NS.Whatever", ".ctor");
bgen.AssertMethod ("NS.Whatever", ".ctor", parameterTypes: "Foundation.NSObjectFlag");
#if NET
bgen.AssertMethod ("NS.Whatever", ".ctor", parameterTypes: "ObjCRuntime.NativeHandle");
#else
bgen.AssertMethod ("NS.Whatever", ".ctor", parameterTypes: "System.IntPtr");
#endif
bgen.AssertPublicMethodCount ("NS.Whatever", 10); // 6 accessors + 3 constructors + ClassHandle getter
bgen.AssertMethod ("NS.IIProtocol", "get_IPropA");
#if NET
bgen.AssertMethod ("NS.IIProtocol", "get_IPropAOpt");
#endif
bgen.AssertNoMethod ("NS.IIProtocol", "set_IPropA", parameterTypes: "Foundation.NSObject");
bgen.AssertMethod ("NS.IIProtocol", "set_IPropB", parameterTypes: "Foundation.NSObject");
#if NET
bgen.AssertMethod ("NS.IIProtocol", "set_IPropBOpt", parameterTypes: "Foundation.NSObject");
#endif
bgen.AssertNoMethod ("NS.IIProtocol", "get_IPropB");
#if NET
bgen.AssertPublicMethodCount ("NS.IIProtocol", 4);
#else
bgen.AssertPublicMethodCount ("NS.IIProtocol", 2);
#endif
bgen.AssertMethod ("NS.IProtocol_Extensions", "GetIPropAOpt", parameterTypes: "NS.IIProtocol");
bgen.AssertMethod ("NS.IProtocol_Extensions", "SetIPropBOpt", parameterTypes: new string [] { "NS.IIProtocol", "Foundation.NSObject" });
bgen.AssertPublicMethodCount ("NS.IProtocol_Extensions", 2);
}
#if !NET
[Ignore ("This test only applies to .NET")]
#endif
[Test]
public void GeneratedAttributeOnPropertyAccessors ()
{
var bgen = BuildFile (Profile.MacCatalyst, "tests/generated-attribute-on-property-accessors.cs");
var messaging = bgen.ApiAssembly.MainModule.Types.First (v => v.Name == "ISomething");
var property = messaging.Properties.First (v => v.Name == "IsLoadedInProcess");
var getter = messaging.Methods.First (v => v.Name == "get_IsLoadedInProcess");
var expectedPropertyAttributes =
@"[SupportedOSPlatform(""maccatalyst"")]
[SupportedOSPlatform(""macos"")]
[UnsupportedOSPlatform(""ios"")]
[UnsupportedOSPlatform(""tvos"")]";
expectedPropertyAttributes = expectedPropertyAttributes.Replace ("\r", string.Empty);
Assert.AreEqual (expectedPropertyAttributes, RenderSupportedOSPlatformAttributes (property), "Property attributes");
Assert.AreEqual (string.Empty, RenderSupportedOSPlatformAttributes (getter), "Getter Attributes");
}
#if !NET
[Ignore ("This test only applies to .NET")]
#endif
[Test]
public void GeneratedAttributeOnPropertyAccessors2 ()
{
var bgen = BuildFile (Profile.MacCatalyst, "tests/generated-attribute-on-property-accessors2.cs");
var messaging = bgen.ApiAssembly.MainModule.Types.First (v => v.Name == "ISomething");
var property = messaging.Properties.First (v => v.Name == "MicrophoneEnabled");
var getter = messaging.Methods.First (v => v.Name == "get_MicrophoneEnabled");
var setter = messaging.Methods.First (v => v.Name == "set_MicrophoneEnabled");
var expectedPropertyAttributes =
@"[SupportedOSPlatform(""ios"")]
[SupportedOSPlatform(""maccatalyst"")]
[SupportedOSPlatform(""macos13.0"")]
[UnsupportedOSPlatform(""tvos"")]";
var expectedSetterAttributes =
@"[SupportedOSPlatform(""ios"")]
[SupportedOSPlatform(""maccatalyst"")]
[SupportedOSPlatform(""macos13.0"")]
[UnsupportedOSPlatform(""tvos"")]";
expectedPropertyAttributes = expectedPropertyAttributes.Replace ("\r", string.Empty);
expectedSetterAttributes = expectedSetterAttributes.Replace ("\r", string.Empty);
Assert.AreEqual (expectedPropertyAttributes, RenderSupportedOSPlatformAttributes (property), "Property attributes");
Assert.AreEqual (string.Empty, RenderSupportedOSPlatformAttributes (getter), "Getter Attributes");
Assert.AreEqual (expectedSetterAttributes, RenderSupportedOSPlatformAttributes (setter), "Setter Attributes");
}
#if !NET
[Ignore ("This only applies to .NET")]
#endif
[Test]
[TestCase (Profile.iOS)]
public void NewerAvailabilityInInlinedProtocol (Profile profile)
{
var bgen = BuildFile (profile, "tests/newer-availability-in-inlined-protocol.cs");
var expectedMethods = new [] {
new {
Type = "Whatever",
MethodCount = 21,
Methods = new [] {
new { Method = "get_IPropA", Attributes = "[SupportedOSPlatform(\"tvos140.0\")]" },
new { Method = "get_IPropAOpt", Attributes = "[SupportedOSPlatform(\"tvos140.0\")]" },
new { Method = "set_IPropB", Attributes = "[SupportedOSPlatform(\"tvos150.0\")]" },
new { Method = "set_IPropBOpt", Attributes = "[SupportedOSPlatform(\"tvos150.0\")]" },
new { Method = "get_IPropC", Attributes = "[SupportedOSPlatform(\"tvos130.0\")]" },
new { Method = "set_IPropC", Attributes = "[SupportedOSPlatform(\"tvos130.0\")]" },
new { Method = "get_IPropCOpt", Attributes = "[SupportedOSPlatform(\"tvos130.0\")]" },
new { Method = "set_IPropCOpt", Attributes = "[SupportedOSPlatform(\"tvos130.0\")]" },
new { Method = "get_IPropD", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Method = "get_IPropDOpt", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Method = "set_IPropE", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Method = "set_IPropEOpt", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Method = "get_IPropF", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Method = "set_IPropF", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Method = "get_IPropFOpt", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Method = "set_IPropFOpt", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
},
},
new {
Type = "IIProtocol",
MethodCount = 17,
Methods = new [] {
new { Method = ".cctor", Attributes = "" },
new { Method = "get_IPropA", Attributes = "" },
new { Method = "_GetIPropA", Attributes = "" },
new { Method = "set_IPropB", Attributes = "[SupportedOSPlatform(\"tvos150.0\")]" },
new { Method = "_SetIPropB", Attributes = "[SupportedOSPlatform(\"tvos150.0\")]" },
new { Method = "get_IPropC", Attributes = "" },
new { Method = "_GetIPropC", Attributes = "" },
new { Method = "set_IPropC", Attributes = "" },
new { Method = "_SetIPropC", Attributes = "" },
new { Method = "get_IPropAOpt", Attributes = "" },
new { Method = "_GetIPropAOpt", Attributes = "" },
new { Method = "set_IPropBOpt", Attributes = "[SupportedOSPlatform(\"tvos150.0\")]" },
new { Method = "_SetIPropBOpt", Attributes = "[SupportedOSPlatform(\"tvos150.0\")]" },
new { Method = "get_IPropCOpt", Attributes = "" },
new { Method = "get_IPropCOpt", Attributes = "" },
new { Method = "set_IPropCOpt", Attributes = "" },
new { Method = "_SetIPropCOpt", Attributes = "" },
},
},
new {
Type = "IProtocol_Extensions",
MethodCount = 4,
Methods = new [] {
new { Method = "GetIPropAOpt", Attributes = "" },
new { Method = "SetIPropBOpt", Attributes = "[SupportedOSPlatform(\"tvos150.0\")]" },
new { Method = "GetIPropCOpt", Attributes = "" },
new { Method = "SetIPropCOpt", Attributes = "" },
},
},
new {
Type = "IIProtocolLower",
MethodCount = 17,
Methods = new [] {
new { Method = ".cctor", Attributes = "" },
new { Method = "get_IPropD", Attributes = "" },
new { Method = "_GetIPropD", Attributes = "" },
new { Method = "set_IPropE", Attributes = "[SupportedOSPlatform(\"tvos110.0\")]" },
new { Method = "_SetIPropE", Attributes = "[SupportedOSPlatform(\"tvos110.0\")]" },
new { Method = "get_IPropF", Attributes = "" },
new { Method = "_GetIPropF", Attributes = "" },
new { Method = "set_IPropF", Attributes = "" },
new { Method = "_SetIPropF", Attributes = "" },
new { Method = "get_IPropDOpt", Attributes = "" },
new { Method = "_GetIPropDOpt", Attributes = "" },
new { Method = "set_IPropEOpt", Attributes = "[SupportedOSPlatform(\"tvos110.0\")]" },
new { Method = "_SetIPropEOpt", Attributes = "[SupportedOSPlatform(\"tvos110.0\")]" },
new { Method = "get_IPropFOpt", Attributes = "" },
new { Method = "_GetIPropFOpt", Attributes = "" },
new { Method = "set_IPropFOpt", Attributes = "" },
new { Method = "_SetIPropFOpt", Attributes = "" },
},
},
new {
Type = "IProtocolLower_Extensions",
MethodCount = 4,
Methods = new [] {
new { Method = "GetIPropDOpt", Attributes = "" },
new { Method = "SetIPropEOpt", Attributes = "[SupportedOSPlatform(\"tvos110.0\")]" },
new { Method = "GetIPropFOpt", Attributes = "" },
new { Method = "SetIPropFOpt", Attributes = "" },
},
},
};
var expectedProperties = new [] {
new {
Type = "Whatever",
PropertyCount = 13,
Properties = new [] {
new { Property = "IPropA", Attributes = "[SupportedOSPlatform(\"tvos140.0\")]" },
new { Property = "IPropB", Attributes = "[SupportedOSPlatform(\"tvos130.0\")]" },
new { Property = "IPropAOpt", Attributes = "[SupportedOSPlatform(\"tvos140.0\")]" },
new { Property = "IPropBOpt", Attributes = "[SupportedOSPlatform(\"tvos130.0\")]" },
new { Property = "IPropC", Attributes = "[SupportedOSPlatform(\"tvos130.0\")]" },
new { Property = "IPropCOpt", Attributes = "[SupportedOSPlatform(\"tvos130.0\")]" },
new { Property = "IPropD", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Property = "IPropE", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Property = "IPropDOpt", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Property = "IPropEOpt", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Property = "IPropF", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
new { Property = "IPropFOpt", Attributes = "[SupportedOSPlatform(\"tvos120.0\")]" },
},
},
new {
Type = "IIProtocol",
PropertyCount = 6,
Properties = new [] {
new { Property = "IPropA", Attributes = "[SupportedOSPlatform(\"tvos140.0\")]" },
new { Property = "IPropB", Attributes = "" },
new { Property = "IPropC", Attributes = "" },
new { Property = "IPropAOpt", Attributes = "[SupportedOSPlatform(\"tvos140.0\")]" },
new { Property = "IPropBOpt", Attributes = "" },
new { Property = "IPropCOpt", Attributes = "" },
},
},
new {
Type = "IProtocol_Extensions",
PropertyCount = 0,
Properties = new [] {
new { Property = "fake property for c# anonymous type compilation", Attributes = "..." },
},
},
new {
Type = "IIProtocolLower",
PropertyCount = 6,
Properties = new [] {
new { Property = "IPropD", Attributes = "[SupportedOSPlatform(\"tvos100.0\")]" },
new { Property = "IPropE", Attributes = "" },
new { Property = "IPropF", Attributes = "" },
new { Property = "IPropDOpt", Attributes = "[SupportedOSPlatform(\"tvos100.0\")]" },
new { Property = "IPropEOpt", Attributes = "" },
new { Property = "IPropFOpt", Attributes = "" },
},
},
new {
Type = "IProtocolLower_Extensions",
PropertyCount = 0,
Properties = new [] {
new { Property = "fake property for c# anonymous type compilation", Attributes = "..." },
},
},
};
var failures = new List<string> ();
Assert.Multiple (() => {
foreach (var expected in expectedMethods) {
var type = bgen.ApiAssembly.MainModule.Types.FirstOrDefault (v => v.Name == expected.Type);
Assert.IsNotNull (type, $"Type not found: {expected.Type}");
if (type is null)
continue;
Assert.AreEqual (expected.MethodCount, type.Methods.Count, $"Unexpected method count for {expected.Type}.\n\tActual methods:\n\t\t{string.Join ("\n\t\t", type.Methods.Select (v => v.FullName))}");
if (expected.MethodCount == 0)
continue;
foreach (var expectedMember in expected.Methods) {
var member = type.Methods.SingleOrDefault (v => v.Name == expectedMember.Method);
Assert.IsNotNull (member, $"Method not found: {expectedMember.Method} in {type.FullName}");
var renderedAttributes = RenderSupportedOSPlatformAttributes (member);
var expectedAttributes = expectedMember.Attributes.Replace ("\r", string.Empty);
if (renderedAttributes != expectedAttributes) {
var msg =
$"Property: {type.FullName}::{member.Name}\n" +
$"\tExpected attributes:\n" +
$"\t\t{string.Join ("\n\t\t", expectedMember.Attributes.Split ('\n'))}\n" +
$"\tActual attributes:\n" +
$"\t\t{string.Join ("\n\t\t", renderedAttributes.Split ('\n'))}";
failures.Add (msg);
Console.WriteLine ($"❌ {msg}");
}
}
}
foreach (var expected in expectedProperties) {
var type = bgen.ApiAssembly.MainModule.Types.FirstOrDefault (v => v.Name == expected.Type);
Assert.IsNotNull (type, $"Type not found: {expected.Type}");
if (type is null)
continue;
Assert.AreEqual (expected.PropertyCount, type.Properties.Count, $"Unexpected property count for {expected.Type}.\n\tActual properties:\n\t\t{string.Join ("\n\t\t", type.Properties.Select (v => v.Name))}");
if (expected.PropertyCount == 0)
continue;
foreach (var expectedMember in expected.Properties) {
var member = type.Properties.SingleOrDefault (v => v.Name == expectedMember.Property);
Assert.IsNotNull (member, $"Property not found: {expectedMember.Property} in {type.FullName}");
if (member is null)
continue;
var renderedAttributes = RenderSupportedOSPlatformAttributes (member);
var expectedAttributes = expectedMember.Attributes.Replace ("\r", string.Empty);
if (renderedAttributes != expectedAttributes) {
var msg =
$"Property: {type.FullName}::{member.Name}\n" +
$"\tExpected attributes:\n" +
$"\t\t{string.Join ("\n\t\t", expectedMember.Attributes.Split ('\n'))}\n" +
$"\tActual attributes:\n" +
$"\t\t{string.Join ("\n\t\t", renderedAttributes.Split ('\n'))}";
failures.Add (msg);
Console.WriteLine ($"❌ {msg}");
}
}
}
});
Assert.That (failures, Is.Empty, "Failures");
}
[Test]
[TestCase (Profile.iOS)]
public void ErrorDomain (Profile profile)
{
BuildFile (profile, true, true, "tests/errordomain.cs");
}
#if !NET
[Ignore ("This only applies to .NET")]
#endif
[Test]
[TestCase (Profile.iOS)]
public void ObsoletedOSPlatform (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.AddTestApiDefinition ("tests/obsoletedosplatform.cs");
bgen.CreateTemporaryBinding ();
bgen.AssertExecute ("build");
}
[Test]
public void InternalDelegate ()
{
BuildFile (Profile.iOS, "tests/internal-delegate.cs");
}
[Test]
[TestCase (Profile.iOS)]
#if NET
[TestCase (Profile.MacCatalyst)]
#endif
[TestCase (Profile.macOSMobile)]
[TestCase (Profile.tvOS)]
public void XmlDocs (Profile profile)
{
var bgen = BuildFile (profile, false, true, "tests/xmldocs.cs");
Assert.That (bgen.XmlDocumentation, Does.Exist);
var contents = File.ReadAllText (bgen.XmlDocumentation);
#if NET
var expectedContentsPath = Path.Combine (Configuration.SourceRoot, "tests", "generator", $"ExpectedXmlDocs.{profile.AsPlatform ().AsString ()}.xml");
#else
var expectedContentsPath = Path.Combine (Configuration.SourceRoot, "tests", "generator", $"ExpectedXmlDocs.{profile.AsPlatform ().AsString ()}.legacy.xml");
#endif
if (!File.Exists (expectedContentsPath))
File.WriteAllText (expectedContentsPath, string.Empty);
var expectedContents = File.ReadAllText (expectedContentsPath);
// Fix up a few potential whitespace differences we don't care about.
contents = contents.Trim ().Replace ("\r", "");
expectedContents = expectedContents.Trim ().Replace ("\r", "");
if (contents != expectedContents) {
if (!string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("WRITE_KNOWN_FAILURES"))) {
File.WriteAllText (expectedContentsPath, contents);
Assert.AreEqual (expectedContents, contents, $"Xml docs: The known failures have been updated in {expectedContentsPath}, so please commit the results. Re-running the test should now succeed.");
} else {
Assert.AreEqual (expectedContents, contents, $"Xml docs: If this is expected, set the WRITE_KNOWN_FAILURES=1 environment variable, run the test again, and commit the changes to the {expectedContentsPath} file.");
}
}
}
[Test]
#if !NET
[Ignore ("This only applies to .NET")]
#endif
[TestCase (Profile.iOS)]
[TestCase (Profile.MacCatalyst)]
[TestCase (Profile.macOSMobile)]
[TestCase (Profile.tvOS)]
public void PreviewAPIs (Profile profile)
{
var bgen = BuildFile (profile, false, true, "tests/preview.cs");
// Each Experimental attribute in the api definition has its own diagnostic ID (with an incremental number)
// Here we collect all diagnostic IDS for all the Experimental attributes in the compiled assembly,
// and assert that they're all present at least once.
var module = bgen.ApiAssembly.MainModule;
var allExperimentalAttributes = module.GetCustomAttributes ().Where (v => v.AttributeType.Name == "ExperimentalAttribute");
var allExperimentalDiagnosticIds = allExperimentalAttributes.Select (v => (string) v.ConstructorArguments [0].Value).ToHashSet ();
var previewApiCount = 32;
var expectedDiagnosticIds = Enumerable.Range (1, previewApiCount).Select (v => $"BGEN{v:0000}").ToHashSet ();
var unexpectedDiagnosticIds = allExperimentalDiagnosticIds.Except (expectedDiagnosticIds).OrderBy (v => v);
var missingDiagnosticIds = expectedDiagnosticIds.Except (allExperimentalDiagnosticIds).OrderBy (v => v);
Assert.That (unexpectedDiagnosticIds, Is.Empty, "No unexpected diagnostic IDs"); // you probably need to increase the previewApiCount variable above (if you added more definitions to the tests/preview.cs file).
Assert.That (missingDiagnosticIds, Is.Empty, "No missing diagnostic IDs");
}
[Test]
public void DelegateParameterAttributes ()
{
BuildFile (Profile.iOS, "tests/delegate-parameter-attributes.cs");
}
#if NET
[Test]
public void Issue19612 ()
{
var profile = Profile.iOS;
var filename = Path.Combine (Configuration.SourceRoot, "tests", "generator", "issue19612.cs");
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
// Compile the temporary assembly and pass the compiled assembly to the generator instead
// of relying on the generator to compile.
var tmpdir = Cache.CreateTemporaryDirectory ();
var tmpassembly = Path.Combine (tmpdir, "temporaryAssembly.dll");
var cscArguments = new List<string> ();
if (!StringUtils.TryParseArguments (Configuration.DotNetCscCommand, out var cscCommand, out var ex))
throw new InvalidOperationException ($"Unable to parse the .NET csc command '{Configuration.DotNetCscCommand}': {ex.Message}");
cscArguments.AddRange (cscCommand);
var cscExecutable = cscArguments [0];
cscArguments.RemoveAt (0);
cscArguments.Add (filename);
cscArguments.Add ($"/out:{tmpassembly}");
cscArguments.Add ("/target:library");
cscArguments.Add ($"/r:{Path.Combine (Configuration.DotNetBclDir, "System.Runtime.dll")}");
var tf = TargetFramework.Parse (BGenTool.GetTargetFramework (profile));
cscArguments.Add ($"/r:{Configuration.GetBindingAttributePath (tf)}");
cscArguments.Add ($"/r:{Configuration.GetBaseLibrary (tf)}");
BGenTool.AddPreviewNoWarn (cscArguments);
var rv = ExecutionHelper.Execute (cscExecutable, cscArguments);
Assert.AreEqual (0, rv, "CSC exit code");
var bgen = new BGenTool ();
bgen.Profile = profile;
bgen.CompiledApiDefinitionAssembly = tmpassembly;
bgen.Defines = BGenTool.GetDefaultDefines (bgen.Profile);
bgen.CreateTemporaryBinding (filename);
bgen.AssertExecute ("build");
bgen.AssertNoWarnings ();
}
#endif
[Test]
[TestCase (Profile.iOS)]
public void BackingFieldType (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = BuildFile (profile, true, true, "tests/backingfieldtype.cs");
#if NET
const string nintName = "System.IntPtr";
const string nuintName = "System.UIntPtr";
#else
const string nintName = "System.nint";
const string nuintName = "System.nuint";
#endif
var testCases = new [] {
new { BackingFieldType = "NSNumber", NullableType = "Foundation.NSNumber", RenderedBackingFieldType = "Foundation.NSNumber", SimplifiedNullableType = "Foundation.NSNumber" },
new { BackingFieldType = "NSInteger", NullableType = $"System.Nullable`1<{nintName}>", RenderedBackingFieldType = nintName, SimplifiedNullableType = "System.Nullable`1" },
new { BackingFieldType = "NSUInteger", NullableType = $"System.Nullable`1<{nuintName}>", RenderedBackingFieldType = nuintName, SimplifiedNullableType = "System.Nullable`1" },
new { BackingFieldType = "Int32", NullableType = $"System.Nullable`1<System.Int32>", RenderedBackingFieldType = "System.Int32", SimplifiedNullableType = "System.Nullable`1" },
new { BackingFieldType = "Int64", NullableType = $"System.Nullable`1<System.Int64>", RenderedBackingFieldType = "System.Int64", SimplifiedNullableType = "System.Nullable`1" },
new { BackingFieldType = "UInt32", NullableType = $"System.Nullable`1<System.UInt32>", RenderedBackingFieldType = "System.UInt32", SimplifiedNullableType = "System.Nullable`1" },
new { BackingFieldType = "UInt64", NullableType = $"System.Nullable`1<System.UInt64>", RenderedBackingFieldType = "System.UInt64", SimplifiedNullableType = "System.Nullable`1" },
};
foreach (var tc in testCases) {
var getConstant = bgen.ApiAssembly.MainModule.GetType ("BackingField", $"{tc.BackingFieldType}FieldTypeExtensions").Methods.First ((v) => v.Name == "GetConstant");
Assert.AreEqual (tc.NullableType, getConstant.ReturnType.FullName, $"{tc.BackingFieldType}: GetConstant return type");
var getValue = bgen.ApiAssembly.MainModule.GetType ("BackingField", $"{tc.BackingFieldType}FieldTypeExtensions").Methods.First ((v) => v.Name == "GetValue");
Assert.AreEqual (tc.RenderedBackingFieldType, getValue.Parameters [0].ParameterType.FullName, $"{tc.BackingFieldType}: GetValue parameter type");
var toEnumArray = bgen.ApiAssembly.MainModule.GetType ("BackingField", $"{tc.BackingFieldType}FieldTypeExtensions").Methods.First ((v) => v.Name == "ToEnumArray");
Assert.IsTrue (toEnumArray.ReturnType.IsArray, $"{tc.BackingFieldType} ToEnumArray return type IsArray");
Assert.AreEqual ($"{tc.BackingFieldType}FieldType", toEnumArray.ReturnType.GetElementType ().Name, $"{tc.BackingFieldType} ToEnumArray return type");
Assert.IsTrue (toEnumArray.Parameters [0].ParameterType.IsArray, $"{tc.BackingFieldType} ToEnumArray parameter type IsArray");
Assert.AreEqual (tc.RenderedBackingFieldType, toEnumArray.Parameters [0].ParameterType.GetElementType ().FullName, $"{tc.BackingFieldType} ToEnumArray parameter type");
var toConstantArray = bgen.ApiAssembly.MainModule.GetType ("BackingField", $"{tc.BackingFieldType}FieldTypeExtensions").Methods.First ((v) => v.Name == "ToConstantArray");
Assert.IsTrue (toConstantArray.ReturnType.IsArray, $"{tc.BackingFieldType} ToConstantArray return type IsArray");
Assert.AreEqual (tc.SimplifiedNullableType, toConstantArray.ReturnType.GetElementType ().FullName, $"{tc.BackingFieldType} ToConstantArray return type");
Assert.IsTrue (toConstantArray.Parameters [0].ParameterType.IsArray, $"{tc.BackingFieldType} ToConstantArray parameter type IsArray");
Assert.AreEqual ($"{tc.BackingFieldType}FieldType", toConstantArray.Parameters [0].ParameterType.GetElementType ().Name, $"{tc.BackingFieldType} ToConstantArray parameter type");
}
}
[Test]
[TestCase (Profile.iOS)]
public void UnderlyingFieldType (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
BuildFile (profile, true, true, "tests/underlyingfieldtype.cs");
}
[Test]
[TestCase (Profile.iOS)]
public void DelegatesWithNullableReturnType (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = BuildFile (profile, "tests/delegate-nullable-return.cs");
bgen.AssertNoWarnings ();
var delegateCallback = bgen.ApiAssembly.MainModule.GetType ("NS", "MyCallback").Methods.First ((v) => v.Name == "EndInvoke");
Assert.That (delegateCallback.MethodReturnType.CustomAttributes.Any (v => v.AttributeType.Name == "NullableAttribute"), "Nullable return type");
}
[Test]
[TestCase (Profile.iOS)]
public void DelegatesWithPointerTypes (Profile profile)
{
Configuration.IgnoreIfIgnoredPlatform (profile.AsPlatform ());
var bgen = BuildFile (profile, "tests/delegate-types.cs");
bgen.AssertNoWarnings ();
var delegateCallback = bgen.ApiAssembly.MainModule.GetType ("NS", "MyCallback").Methods.First ((v) => v.Name == "EndInvoke");
Assert.IsTrue (delegateCallback.MethodReturnType.ReturnType.IsPointer, "Pointer return type");
foreach (var p in delegateCallback.Parameters.Where (v => v.Name != "result")) {
Assert.IsTrue (p.ParameterType.IsPointer, $"Pointer parameter type: {p.Name}");
}
}
}
}