void Process_Exited(object sender, System.EventArgs e) { if (Process.ExitCode != 0) { ErrorData.Insert(0, string.Format("Exit code: {0}\n", Process.ExitCode)); } ExitedDelegate ExitedHandler = Exited; if (ExitedHandler != null) { EditorMainThread.Run(() => { ExitedHandler(Process.ExitCode, OutputData.ToString().Trim(), ErrorData.ToString().Trim()); }); } if (nextProcess != null) { if (Process.ExitCode == 0 || !nextStopOnError) { nextProcess.Start(); } } }
public void StaticMethods() { Errors = 0; ErrorData.Clear(); int n = 0; IntPtr responds_handle = Selector.GetHandle("respondsToSelector:"); foreach (Type t in Assembly.GetTypes()) { if (t.IsNested || !NSObjectType.IsAssignableFrom(t)) { continue; } if (Skip(t) || SkipDueToAttribute(t)) { continue; } FieldInfo fi = t.GetField("class_ptr", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); if (fi == null) { continue; // e.g. *Delegate } IntPtr class_ptr = (IntPtr)fi.GetValue(null); foreach (var m in t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)) { if (SkipDueToAttribute(m)) { continue; } foreach (object ca in m.GetCustomAttributes(true)) { if (ca is ExportAttribute) { string name = (ca as ExportAttribute).Selector; if (Skip(t, name)) { continue; } bool result = bool_objc_msgSend_IntPtr(class_ptr, responds_handle, Selector.GetHandle(name)); bool response = CheckStaticResponse(result, t, m.DeclaringType, ref name); if (!response) { ReportError(name); } n++; } } } } Assert.AreEqual(0, Errors, "{0} errors found in {1} static selector validated{2}", Errors, n, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }
public void InstanceMethods() { Errors = 0; ErrorData.Clear(); int n = 0; foreach (Type t in Assembly.GetTypes()) { if (t.IsNested || !NSObjectType.IsAssignableFrom(t)) { continue; } if (Skip(t) || SkipDueToAttribute(t)) { continue; } IntPtr class_ptr = GetClassForType(t); if (class_ptr == IntPtr.Zero) { continue; } foreach (var c in t.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) { Process(class_ptr, t, c, ref n); } foreach (var m in t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)) { Process(class_ptr, t, m, ref n); } } Assert.AreEqual(0, Errors, "{0} errors found in {1} instance selector validated{2}", Errors, n, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }
public void Keys() { Errors = 0; ContinueOnFailure = true; var nspace = CIFilterType.Namespace; var types = CIFilterType.Assembly.GetTypes(); foreach (Type t in types) { if (t.Namespace != nspace) { continue; } if (t.IsAbstract || !CIFilterType.IsAssignableFrom(t)) { continue; } // we need to skip the filters that are not supported by the executing version of iOS if (Skip(t)) { continue; } var ctor = t.GetConstructor(Type.EmptyTypes); if ((ctor == null) || ctor.IsAbstract) { continue; } CIFilter f = ctor.Invoke(null) as CIFilter; // first check that every property can be mapped to an input key - except if it starts with "Output" foreach (var p in t.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var pt = p.DeclaringType; if (!CIFilterType.IsAssignableFrom(pt) || (pt == CIFilterType)) { continue; } if (SkipDueToAttribute(p)) { continue; } var getter = p.GetGetMethod(); var ea = getter.GetCustomAttribute <ExportAttribute> (false); // only properties coming (inlined) from protocols have an [Export] attribute if (ea == null) { continue; } var key = ea.Selector; // 'output' is always explicit if (key.StartsWith("output", StringComparison.Ordinal)) { if (Array.IndexOf(f.OutputKeys, key) < 0) { ReportError($"{t.Name}: Property `{p.Name}` mapped to key `{key}` is not part of `OutputKeys`."); //GenerateBinding (f, Console.Out); } } else { // special cases (protocol names are better) switch (t.Name) { case "CIBicubicScaleTransform": switch (key) { case "parameterB": key = "inputB"; break; case "parameterC": key = "inputC"; break; } break; case "CICmykHalftone": switch (key) { case "grayComponentReplacement": key = "inputGCR"; break; case "underColorRemoval": key = "inputUCR"; break; } break; case "CIGlassDistortion": switch (key) { case "textureImage": key = "texture"; break; } break; } // 'input' is implied (generally) and explicit (in a few cases) if (!key.StartsWith("input", StringComparison.Ordinal)) { key = "input" + Char.ToUpperInvariant(key [0]) + key.Substring(1); } if (Array.IndexOf(f.InputKeys, key) < 0) { ReportError($"{t.Name}: Property `{p.Name}` mapped to key `{key}` is not part of `InputKeys`."); //GenerateBinding (f, Console.Out); } } } // second check that every input key is mapped to an property foreach (var key in f.InputKeys) { string cap = Char.ToUpperInvariant(key [0]) + key.Substring(1); // special cases (protocol names are better) switch (t.Name) { case "CICmykHalftone": switch (key) { case "inputGCR": cap = "GrayComponentReplacement"; break; case "inputUCR": cap = "UnderColorRemoval"; break; } break; case "CIAccordionFoldTransition": switch (key) { case "inputNumberOfFolds": cap = "FoldCount"; break; } break; case "CIBicubicScaleTransform": switch (key) { case "inputB": cap = "ParameterB"; break; case "inputC": cap = "ParameterC"; break; } break; } // IgnoreCase because there are acronyms (more than 2 letters) that naming convention force us to change var pi = t.GetProperty(cap, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); if (pi == null) { // 2nd chance: some, but not all, property are prefixed by `Input` if (key.StartsWith("input", StringComparison.Ordinal)) { cap = Char.ToUpperInvariant(key [5]) + key.Substring(6); pi = t.GetProperty(cap, BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); } } if (pi == null) { ReportError($"{t.Name}: Input Key `{key}` is NOT mapped to a `{cap}` property."); //GenerateBinding (f, Console.Out); } else if (pi.GetSetMethod() == null) { ReportError($"{t.Name}: Property `{pi.Name}` MUST have a setter."); } } // third check that every output key is mapped to an property foreach (var key in f.OutputKeys) { // special cases switch (t.Name) { case "CIKeystoneCorrectionCombined": case "CIKeystoneCorrectionHorizontal": case "CIKeystoneCorrectionVertical": switch (key) { case "outputRotationFilter": continue; // lack of documentation about the returned type } break; case "CILanczosScaleTransform": switch (key) { // ref: https://github.com/xamarin/xamarin-macios/issues/7209 case "outputImageNewScaleX:scaleY:": case "outputImageOldScaleX:scaleY:": continue; } break; case "CIDiscBlur": switch (key) { // existed in iOS 10.3 but not in iOS 13 - we're not adding them case "outputImageOriginal": case "outputImageEnhanced": continue; } break; case "CIGaussianBlur": switch (key) { case "outputImageV1": // existed briefly in macOS 10.11, but neither before nor after. continue; } break; case "CIAreaAverage": case "CIAreaHistogram": case "CIAreaMinMax": switch (key) { case "outputImageMPS": case "outputImageMPS:": case "outputImageNonMPS:": // no doc for argument continue; } break; } var cap = Char.ToUpperInvariant(key [0]) + key.Substring(1); // IgnoreCase because there are acronyms (more than 2 letters) that naming convention force us to change var po = t.GetProperty(cap, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy); if (po == null) { ReportError($"{t.Name}: Output Key `{key}` is NOT mapped to a `{cap}` property."); //GenerateBinding (f, Console.Out); } else if (po.GetSetMethod() != null) { ReportError($"{t.Name}: Property `{po.Name}` should NOT have a setter."); } } } Assert.AreEqual(0, Errors, "{0} potential errors found{1}", Errors, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }
public void Protocols() { Errors = 0; var to_confirm_manually = new StringBuilder(); ContinueOnFailure = true; var nspace = CIFilterType.Namespace; var types = CIFilterType.Assembly.GetTypes(); foreach (Type t in types) { if (t.Namespace != nspace) { continue; } // e.g. FooProtocolWrapper if (!t.IsPublic) { continue; } switch (t.Name) { // we are interested in subclasses (real) filters case "CIFilter": continue; // no protocol has been added (yet?) you can confirm with `grep` that it does not report anything in the terminal case "CIAdditionCompositing": case "CIAreaAverage": case "CIAreaHistogram": case "CIAreaMaximum": case "CIAreaMaximumAlpha": case "CIAreaMinimum": case "CIAreaMinimumAlpha": case "CIAreaMinMax": case "CIAreaMinMaxRed": case "CIBlendFilter": case "CIBumpDistortion": case "CIBumpDistortionLinear": case "CICameraCalibrationLensCorrection": case "CICircleSplashDistortion": case "CICircularWrap": case "CIClamp": case "CICodeGenerator": case "CIColorBlendMode": case "CIColorBurnBlendMode": case "CIColorDodgeBlendMode": case "CIColumnAverage": case "CICompositingFilter": case "CIConstantColorGenerator": case "CIConvolution3X3": case "CIConvolution5X5": case "CIConvolution7X7": case "CIConvolution9Horizontal": case "CIConvolution9Vertical": case "CIConvolutionCore": case "CICoreMLModelFilter": case "CICrop": case "CIDarkenBlendMode": case "CIDepthBlurEffect": case "CIDepthDisparityConverter": case "CIDifferenceBlendMode": case "CIDisplacementDistortion": case "CIDistortionFilter": case "CIDivideBlendMode": case "CIDroste": case "CIExclusionBlendMode": case "CIFaceBalance": case "CIGlassDistortion": case "CIGlassLozenge": case "CIGuidedFilter": case "CIHardLightBlendMode": case "CIHistogramDisplayFilter": case "CIHoleDistortion": case "CIHueBlendMode": case "CIImageGenerator": case "CIKeystoneCorrection": case "CIKMeans": case "CILightenBlendMode": case "CILightTunnel": case "CILinearBlur": case "CILinearBurnBlendMode": case "CILinearDodgeBlendMode": case "CILuminosityBlendMode": case "CIMaximumCompositing": case "CIMinimumCompositing": case "CIMorphology": case "CIMorphologyRectangle": case "CIMultiplyBlendMode": case "CIMultiplyCompositing": case "CINinePartStretched": case "CINinePartTiled": case "CIOverlayBlendMode": case "CIPinchDistortion": case "CIPinLightBlendMode": case "CIReductionFilter": case "CIRowAverage": case "CISampleNearest": case "CISaturationBlendMode": case "CIScreenBlendMode": case "CIScreenFilter": case "CISoftLightBlendMode": case "CISourceAtopCompositing": case "CISourceInCompositing": case "CISourceOutCompositing": case "CISourceOverCompositing": case "CIStretchCrop": case "CISubtractBlendMode": case "CITileFilter": case "CITorusLensDistortion": case "CITwirlDistortion": case "CIVortexDistortion": // this list is likely to change with newer Xcode - uncomment if you want to the script to check the list //to_confirm_manually.AppendLine ($"grep {t.Name} `xcode-select -p`/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/CoreImage.framework/Headers/*.h"); // since xtro will report the missing protocols this is a 2nd layer of safety :) continue; } bool assign = typeof(ICIFilterProtocol).IsAssignableFrom(t); bool suffix = t.Name.EndsWith("Protocol", StringComparison.Ordinal); if (t.IsInterface) { if (assign) { // check that `IFooProtocol` has a [Protocol (Name = "Foo")] attribute var ca = t.GetCustomAttribute <ProtocolAttribute> (false); if (ca == null) { ReportError($"Managed {t.Name} should have a '[Protocol (Name=\"{t.Name.Replace ("Protocol", "")}\")]' attribute"); } // check that the managed name ends with Protocol, so we can have the _normal_ name to be a concrete type (like our historic, strongly typed filters) if (!suffix) { ReportError($"Managed {t.Name} should have a 'Protocol' suffix"); } } else if (suffix) { ReportError($"Managed {t.Name} should implement 'ICIFilterProtocol' interface."); } } else if (suffix) { ReportError($"Managed {t.Name} should be an interface since it represent a protocol"); } else if (assign) { // all CIFilter should map to a `ICI*Protocol` interface / protocol bool found = false; foreach (var inft in t.GetInterfaces()) { if (inft.Namespace == nspace && inft.Name.EndsWith("Protocol", StringComparison.Ordinal)) { found = true; break; } } if (!found) { ReportError($"Managed CIFilter '{t.Name}' does not conform to any CoreImage filter protocol."); } } else if (CIFilterType.IsAssignableFrom(t)) { // missing ICIFilterProtocol to_confirm_manually.AppendLine($"grep \"protocol {t.Name} \" `xcode-select -p`/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/CoreImage.framework/Headers/*.h"); ReportError($"Managed CIFilter '{t.Name}' does not conform to 'ICIFilterProtocol' protocol. Confirm with generated `grep` script on console."); } } if (to_confirm_manually.Length > 0) { Console.WriteLine(to_confirm_manually); } Assert.AreEqual(0, Errors, "{0} potential errors found{1}", Errors, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }
public void DefaultCtorAllowed() { Errors = 0; ErrorData.Clear(); int n = 0; foreach (Type t in Assembly.GetTypes()) { if (t.IsAbstract || !NSObjectType.IsAssignableFrom(t)) { continue; } if (Skip(t)) { continue; } var ctor = t.GetConstructor(Type.EmptyTypes); if (SkipDueToAttribute(ctor)) { continue; } if ((ctor == null) || ctor.IsAbstract) { if (LogUntestedTypes) { Console.WriteLine("[WARNING] {0} was skipped because it had no default constructor", t); } continue; } instance_type_name = t.FullName; if (LogProgress) { Console.WriteLine("{0}. {1}", n, instance_type_name); } NSObject obj = null; try { obj = ctor.Invoke(null) as NSObject; CheckHandle(obj); CheckToString(obj); CheckIsDirectBinding(obj); CheckNSObjectProtocol(obj); Dispose(obj, t); } catch (Exception e) { // Objective-C exception thrown if (!ContinueOnFailure) { throw; } TargetInvocationException tie = (e as TargetInvocationException); if (tie != null) { e = tie.InnerException; } ReportError("Default constructor not allowed for {0} : {1}", instance_type_name, e.Message); } n++; } Assert.AreEqual(0, Errors, "{0} potential errors found in {1} default ctor validated{2}", Errors, n, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }
// we just want to confirm the symbol exists so `dlsym` can be disabled protected void Check(Assembly a) { Errors = 0; ErrorData.Clear(); int n = 0; foreach (var t in a.GetTypes()) { foreach (var m in t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)) { if ((m.Attributes & MethodAttributes.PinvokeImpl) == 0) { continue; } var dllimport = m.GetCustomAttribute <DllImportAttribute> (); string name = dllimport.EntryPoint ?? m.Name; switch (name) { // known not to be present in ARM64 case "objc_msgSend_stret": case "objc_msgSendSuper_stret": // the linker normally removes them (IntPtr.Size optimization) continue; } string path = dllimport.Value; switch (path) { case "__Internal": // load from executable path = null; break; #if NET case "QCall": // Globalization hasn't been implemented yet: https://github.com/xamarin/xamarin-macios/issues/8906 if (name.StartsWith("GlobalizationNative_", StringComparison.Ordinal)) { continue; } break; case "libhostpolicy": // There's no libhostpolicy library. // https://github.com/dotnet/runtime/issues/38543 continue; case "libSystem.Native": path += ".dylib"; break; #endif case "libc": // we still have some rogue/not-fully-qualified DllImport path = "/usr/lib/libSystem.dylib"; break; case "System.Native": case "System.Security.Cryptography.Native.Apple": case "System.Net.Security.Native": if (MonoNativeConfig.LinkMode == MonoNativeLinkMode.None) { continue; } #if __IOS__ path = MonoNativeConfig.GetPInvokeLibraryName(MonoNativeFlavor.Compat, MonoNativeConfig.LinkMode); #else path = null; #endif break; } var lib = Dlfcn.dlopen(path, 0); var h = Dlfcn.dlsym(lib, name); if (h == IntPtr.Zero) { ReportError("Could not find the symbol '{0}' in {1}", name, path); } Dlfcn.dlclose(lib); n++; } } Assert.AreEqual(0, Errors, "{0} errors found in {1} symbol lookups{2}", Errors, n, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }
public void NativeSignatures() { int n = 0; Errors = 0; ErrorData.Clear(); foreach (Type t in Assembly.GetTypes()) { var static_type = t.IsSealed && t.IsAbstract; // e.g. [Category] if (t.IsNested || (!static_type && !NSObjectType.IsAssignableFrom(t))) { continue; } if (Skip(t)) { continue; } CurrentType = t; FieldInfo fi = null; if (!static_type) { fi = t.GetField("class_ptr", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static); } IntPtr class_ptr = fi == null ? IntPtr.Zero : (IntPtr)fi.GetValue(null); foreach (MethodBase m in t.GetMethods(Flags)) { CheckMemberSignature(m, t, class_ptr, ref n); } foreach (MethodBase m in t.GetConstructors(Flags)) { CheckMemberSignature(m, t, class_ptr, ref n); } } AssertIfErrors("{0} errors found in {1} signatures validated{2}", Errors, n, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }
public void AsyncCandidates() { int n = 0; Errors = 0; ErrorData.Clear(); foreach (Type t in Assembly.GetTypes()) { // e.g. delegates used for events if (t.IsNested) { continue; } if (!NSObjectType.IsAssignableFrom(t)) { continue; } if (t.GetCustomAttribute <ProtocolAttribute> () != null) { continue; } if (t.GetCustomAttribute <ModelAttribute> () != null) { continue; } // let's not encourage the use of some API if (IsDiscouraged(t)) { continue; } CurrentType = t; var methods = t.GetMethods(Flags); foreach (MethodInfo m in methods) { if (m.DeclaringType != t) { continue; } // skip properties / events if (m.IsSpecialName) { continue; } if (IgnoreAsync(m)) { continue; } // some calls are "natively" async if (m.Name.IndexOf("Async", StringComparison.Ordinal) != -1) { continue; } // let's not encourage the use of some API if (IsDiscouraged(m)) { continue; } // is it a candidate ? var p = m.GetParameters(); if (p.Length == 0) { continue; } var last = p [p.Length - 1]; // trying to limit false positives and the need for large ignore lists to maintain // unlike other introspection tests a failure does not mean a broken API switch (last.Name) { case "completionHandler": case "completion": break; default: continue; } if (!last.ParameterType.IsSubclassOf(typeof(Delegate))) { continue; } // did we provide a async wrapper ? string ma = m.Name + "Async"; if (methods.Where((mi) => mi.Name == ma).FirstOrDefault() != null) { continue; } var name = m.ToString(); var i = name.IndexOf(' '); ErrorData.AppendLine(name.Insert(i + 1, m.DeclaringType.Name + "::")); Errors++; } } AssertIfErrors("{0} errors found in {1} signatures validated{2}", Errors, n, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }
public void ManagedSignature() { int n = 0; Errors = 0; ErrorData.Clear(); foreach (Type t in Assembly.GetTypes()) { if (!NSObjectType.IsAssignableFrom(t)) { continue; } CurrentType = t; foreach (MethodInfo m in t.GetMethods(Flags)) { CheckManagedMemberSignatures(m, t, ref n); } foreach (MethodBase m in t.GetConstructors(Flags)) { CheckManagedMemberSignatures(m, t, ref n); } } AssertIfErrors("{0} errors found in {1} signatures validated{2}", Errors, n, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }
// we just want to confirm the symbol exists so `dlsym` can be disabled protected void Check(Assembly a) { Errors = 0; ErrorData.Clear(); int n = 0; foreach (var t in a.GetTypes()) { foreach (var m in t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)) { if ((m.Attributes & MethodAttributes.PinvokeImpl) == 0) { continue; } var dllimport = m.GetCustomAttribute <DllImportAttribute> (); string name = dllimport.EntryPoint ?? m.Name; switch (name) { // known not to be present in ARM64 case "objc_msgSend_stret": case "objc_msgSendSuper_stret": // the linker normally removes them (IntPtr.Size optimization) continue; } string path = dllimport.Value; switch (path) { case "__Internal": // load from executable path = null; break; case "libc": // we still have some rogue/not-fully-qualified DllImport path = "/usr/lib/libSystem.dylib"; break; } var lib = Dlfcn.dlopen(path, 0); var h = Dlfcn.dlsym(lib, name); if (h == IntPtr.Zero) { ReportError("Could not find the symbol '{0}' in {1}", name, path); } Dlfcn.dlclose(lib); n++; } } Assert.AreEqual(0, Errors, "{0} errors found in {1} symbol lookups{2}", Errors, n, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }
internal static Error Create(ErrorData emessage, params string[] param) { string message = string.Format(emessage.ToString(), param); return(new Error(message, emessage.Code)); }
// we just want to confirm the symbol exists so `dlsym` can be disabled protected void Check(Assembly a) { Errors = 0; ErrorData.Clear(); int n = 0; foreach (var t in a.GetTypes()) { foreach (var m in t.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static)) { if ((m.Attributes & MethodAttributes.PinvokeImpl) == 0) { continue; } var dllimport = m.GetCustomAttribute <DllImportAttribute> (); string name = dllimport.EntryPoint ?? m.Name; switch (name) { // known not to be present in ARM64 case "objc_msgSend_stret": case "objc_msgSendSuper_stret": // the linker normally removes them (IntPtr.Size optimization) continue; } string path = dllimport.Value; switch (path) { case "__Internal": // load from executable path = null; break; #if NET case "libSystem.Globalization.Native": // load from executable (like __Internal above since it's part of the static library) path = null; break; case "libSystem.Native": path += ".dylib"; break; #endif case "libc": // we still have some rogue/not-fully-qualified DllImport path = "/usr/lib/libSystem.dylib"; break; case "System.Native": case "System.Security.Cryptography.Native.Apple": case "System.Net.Security.Native": if (MonoNativeConfig.LinkMode == MonoNativeLinkMode.None) { continue; } #if __IOS__ path = MonoNativeConfig.GetPInvokeLibraryName(MonoNativeFlavor.Compat, MonoNativeConfig.LinkMode); #else path = null; #endif break; } var lib = Dlfcn.dlopen(path, 0); var h = Dlfcn.dlsym(lib, name); if (h == IntPtr.Zero) { ReportError("Could not find the symbol '{0}' in {1} for the P/Invoke {2}.{3} in {4}", name, path, t.FullName, m.Name, a.GetName().Name); } else if (path != null) { // Verify that the P/Invoke points to the right library. Dl_info info = default(Dl_info); var found = dladdr(h, out info); if (found != 0) { // Resolve symlinks in both cases var dllImportPath = ResolveLibrarySymlinks(path); var foundLibrary = ResolveLibrarySymlinks(Marshal.PtrToStringAuto(info.dli_fname)); if (Skip(name, ref dllImportPath, ref foundLibrary)) { // Skipped } else if (foundLibrary != dllImportPath) { ReportError($"Found the symbol '{name}' in the library '{foundLibrary}', but the P/Invoke {t.FullName}.{m.Name} in {a.GetName ().Name} claims it's in '{dllimport.Value}'."); } } else { Console.WriteLine($"Unable to find the library for the symbol '{name}' claimed to be in {path} for the P/Invoke {t.FullName}.{m.Name} in {a.GetName ().Name} (rv: {found})"); } } Dlfcn.dlclose(lib); n++; } } Assert.AreEqual(0, Errors, "{0} errors found in {1} symbol lookups{2}", Errors, n, Errors == 0 ? string.Empty : ":\n" + ErrorData.ToString() + "\n"); }