2016-05-05 03:14:32 +03:00
/ /
// PlatformInfo.cs: info about the host platform
// and AvailabilityBaseAttribute extensions for tests
/ /
// Author:
// Aaron Bockover <abock@xamarin.com>
/ /
// Copyright 2015 Xamarin Inc. All rights reserved.
using System ;
using System.Linq ;
using System.Reflection ;
using System.Collections.Generic ;
2021-04-10 18:09:14 +03:00
using System.Runtime.Versioning ;
2016-05-05 03:14:32 +03:00
using ObjCRuntime ;
using Foundation ;
2018-01-16 21:15:59 +03:00
#if ! ( MONOMAC | | __MACOS__ )
2016-05-05 03:14:32 +03:00
using UIKit ;
#endif
2021-11-22 22:54:07 +03:00
using Xamarin.Utils ;
2016-06-22 02:46:07 +03:00
namespace Xamarin.Tests
2016-05-05 03:14:32 +03:00
{
public sealed class PlatformInfo
{
static PlatformInfo GetHostPlatformInfo ( )
{
string name ;
string version ;
2021-04-07 05:59:51 +03:00
#if __MACCATALYST__
name = "MacCatalyst" ;
[tests] Use the correct OS version for Mac Catalyst. (#14143)
This fixes the CheckManagedFilters tests on Mac Catalyst on older macOS versions:
[FAIL] CheckManagedFilters : Managed filters not found for CIAreaMinMax, CIAreaMinMaxRed, CIAttributedTextImageGenerator, CIBarcodeGenerator, CIBicubicScaleTransform, CIBlendWithBlueMask, CIBlendWithRedMask, CIBokehBlur, CICameraCalibrationLensCorrection, CIColorCubesMixedWithMask, CIColorCurves, CICoreMLModelFilter, CIDepthBlurEffect, CIDepthToDisparity, CIDisparityToDepth, CIDither, CIDocumentEnhancer, CIEdgePreserveUpsampleFilter, CIGaborGradients, CIGuidedFilter, CIKeystoneCorrectionCombined, CIKeystoneCorrectionHorizontal, CIKeystoneCorrectionVertical, CIKMeans, CILabDeltaE, CIMeshGenerator, CIMix, CIMorphologyGradient, CIMorphologyMaximum, CIMorphologyMinimum, CIMorphologyRectangleMaximum, CIMorphologyRectangleMinimum, CIPaletteCentroid, CIPalettize, CIPerspectiveRotate, CIRoundedRectangleGenerator, CISaliencyMapFilter, CISampleNearest, CITextImageGenerator
Expected: 0
But was: 39
2022-02-16 22:11:57 +03:00
version = iOSSupportVersion ;
2021-04-07 05:59:51 +03:00
#elif __TVOS__ | | __IOS__
2016-05-05 03:14:32 +03:00
name = UIDevice . CurrentDevice . SystemName ;
version = UIDevice . CurrentDevice . SystemVersion ;
#elif __WATCHOS__
name = WatchKit . WKInterfaceDevice . CurrentDevice . SystemName ;
version = WatchKit . WKInterfaceDevice . CurrentDevice . SystemVersion ;
2018-01-16 21:15:59 +03:00
#elif MONOMAC | | __MACOS__
2016-05-05 03:14:32 +03:00
using ( var plist = NSDictionary . FromFile ( "/System/Library/CoreServices/SystemVersion.plist" ) ) {
name = ( NSString ) plist [ "ProductName" ] ;
version = ( NSString ) plist [ "ProductVersion" ] ;
}
#else
#error Unknown platform
#endif
name = name ? . Replace ( " " , String . Empty ) ? . ToLowerInvariant ( ) ;
2021-04-07 05:59:51 +03:00
if ( name = = null )
throw new FormatException ( "Product name is `null`" ) ;
2016-05-05 03:14:32 +03:00
var platformInfo = new PlatformInfo ( ) ;
2021-11-22 22:54:07 +03:00
#if NET
if ( name . StartsWith ( "maccatalyst" , StringComparison . Ordinal ) )
platformInfo . Name = ApplePlatform . MacCatalyst ;
else if ( name . StartsWith ( "mac" , StringComparison . Ordinal ) )
platformInfo . Name = ApplePlatform . MacOSX ;
else if ( name . StartsWith ( "ios" , StringComparison . Ordinal ) | | name . StartsWith ( "iphoneos" , StringComparison . Ordinal ) | | name . StartsWith ( "ipados" , StringComparison . Ordinal ) )
platformInfo . Name = ApplePlatform . iOS ;
else if ( name . StartsWith ( "tvos" , StringComparison . Ordinal ) )
platformInfo . Name = ApplePlatform . TVOS ;
else if ( name . StartsWith ( "watchos" , StringComparison . Ordinal ) )
platformInfo . Name = ApplePlatform . WatchOS ;
else
throw new FormatException ( $"Unknown product name: {name}" ) ;
#else
2021-04-07 05:59:51 +03:00
if ( name . StartsWith ( "maccatalyst" , StringComparison . Ordinal ) )
platformInfo . Name = PlatformName . MacCatalyst ;
else if ( name . StartsWith ( "mac" , StringComparison . Ordinal ) )
2016-05-05 03:14:32 +03:00
platformInfo . Name = PlatformName . MacOSX ;
2021-04-07 05:59:51 +03:00
else if ( name . StartsWith ( "ios" , StringComparison . Ordinal ) | | name . StartsWith ( "iphoneos" , StringComparison . Ordinal ) )
2016-05-05 03:14:32 +03:00
platformInfo . Name = PlatformName . iOS ;
2021-04-07 05:59:51 +03:00
else if ( name . StartsWith ( "tvos" , StringComparison . Ordinal ) )
2016-05-05 03:14:32 +03:00
platformInfo . Name = PlatformName . TvOS ;
2021-04-07 05:59:51 +03:00
else if ( name . StartsWith ( "watchos" , StringComparison . Ordinal ) )
2016-05-05 03:14:32 +03:00
platformInfo . Name = PlatformName . WatchOS ;
else
throw new FormatException ( $"Unknown product name: {name}" ) ;
2021-11-22 22:54:07 +03:00
#endif
2016-05-05 03:14:32 +03:00
platformInfo . Version = Version . Parse ( version ) ;
2021-11-22 22:54:07 +03:00
#if ! NET
2016-05-05 03:14:32 +03:00
if ( IntPtr . Size = = 4 )
platformInfo . Architecture = PlatformArchitecture . Arch32 ;
else if ( IntPtr . Size = = 8 )
platformInfo . Architecture = PlatformArchitecture . Arch64 ;
2021-11-22 22:54:07 +03:00
#endif
2016-05-05 03:14:32 +03:00
return platformInfo ;
}
[tests] Use the correct OS version for Mac Catalyst. (#14143)
This fixes the CheckManagedFilters tests on Mac Catalyst on older macOS versions:
[FAIL] CheckManagedFilters : Managed filters not found for CIAreaMinMax, CIAreaMinMaxRed, CIAttributedTextImageGenerator, CIBarcodeGenerator, CIBicubicScaleTransform, CIBlendWithBlueMask, CIBlendWithRedMask, CIBokehBlur, CICameraCalibrationLensCorrection, CIColorCubesMixedWithMask, CIColorCurves, CICoreMLModelFilter, CIDepthBlurEffect, CIDepthToDisparity, CIDisparityToDepth, CIDither, CIDocumentEnhancer, CIEdgePreserveUpsampleFilter, CIGaborGradients, CIGuidedFilter, CIKeystoneCorrectionCombined, CIKeystoneCorrectionHorizontal, CIKeystoneCorrectionVertical, CIKMeans, CILabDeltaE, CIMeshGenerator, CIMix, CIMorphologyGradient, CIMorphologyMaximum, CIMorphologyMinimum, CIMorphologyRectangleMaximum, CIMorphologyRectangleMinimum, CIPaletteCentroid, CIPalettize, CIPerspectiveRotate, CIRoundedRectangleGenerator, CISaliencyMapFilter, CISampleNearest, CITextImageGenerator
Expected: 0
But was: 39
2022-02-16 22:11:57 +03:00
#if __MACCATALYST__
static string? _iOSSupportVersion ;
internal static string iOSSupportVersion {
get {
if ( _iOSSupportVersion is null ) {
// This is how Apple does it: https://github.com/llvm/llvm-project/blob/62ec4ac90738a5f2d209ed28c822223e58aaaeb7/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm#L100-L105
using var dict = NSMutableDictionary . FromFile ( "/System/Library/CoreServices/SystemVersion.plist" ) ;
using var str = ( NSString ) "iOSSupportVersion" ;
using var obj = dict . ObjectForKey ( str ) ;
_iOSSupportVersion = obj . ToString ( ) ;
}
return _iOSSupportVersion ;
}
}
#endif
2016-05-05 03:14:32 +03:00
public static readonly PlatformInfo Host = GetHostPlatformInfo ( ) ;
2021-11-22 22:54:07 +03:00
#if NET
public ApplePlatform Name { get ; private set ; }
#else
2016-05-05 03:14:32 +03:00
public PlatformName Name { get ; private set ; }
public PlatformArchitecture Architecture { get ; private set ; }
2021-11-22 22:54:07 +03:00
#endif
2016-05-05 03:14:32 +03:00
public Version Version { get ; private set ; }
2021-11-22 22:54:07 +03:00
#if NET
public bool IsMac = > Name = = ApplePlatform . MacOSX ;
public bool IsIos = > Name = = ApplePlatform . iOS ;
#else
2016-05-05 03:14:32 +03:00
public bool IsMac = > Name = = PlatformName . MacOSX ;
public bool IsIos = > Name = = PlatformName . iOS ;
public bool IsArch32 = > Architecture . HasFlag ( PlatformArchitecture . Arch32 ) ;
public bool IsArch64 = > Architecture . HasFlag ( PlatformArchitecture . Arch64 ) ;
2021-11-22 22:54:07 +03:00
#endif
2016-05-05 03:14:32 +03:00
PlatformInfo ( )
{
}
}
public static class AvailabilityExtensions
{
public static bool IsAvailableOnHostPlatform ( this ICustomAttributeProvider attributeProvider )
{
return attributeProvider . IsAvailable ( PlatformInfo . Host ) ;
}
public static bool IsAvailable ( this ICustomAttributeProvider attributeProvider , PlatformInfo targetPlatform )
{
2021-11-22 22:54:07 +03:00
var customAttributes = attributeProvider . GetCustomAttributes ( true ) ;
2021-04-10 18:09:14 +03:00
#if NET
2021-11-22 22:54:07 +03:00
customAttributes = customAttributes . ToArray ( ) ; // don't iterate twice
if ( customAttributes . Any ( v = > v is ObsoleteAttribute ) )
return false ;
#endif
return customAttributes
#if NET
. OfType < OSPlatformAttribute > ( )
2021-04-10 18:09:14 +03:00
#else
2016-05-05 03:14:32 +03:00
. OfType < AvailabilityBaseAttribute > ( )
2021-04-10 18:09:14 +03:00
#endif
2021-11-22 22:54:07 +03:00
. IsAvailable ( targetPlatform ) ;
2016-05-05 03:14:32 +03:00
}
2021-11-22 22:54:07 +03:00
#if NET
public static bool IsAvailableOnHostPlatform ( this IEnumerable < OSPlatformAttribute > attributes )
#else
2016-05-05 03:14:32 +03:00
public static bool IsAvailableOnHostPlatform ( this IEnumerable < AvailabilityBaseAttribute > attributes )
2021-11-22 22:54:07 +03:00
#endif
2016-05-05 03:14:32 +03:00
{
return attributes . IsAvailable ( PlatformInfo . Host ) ;
}
2021-11-22 22:54:07 +03:00
#if NET
[tests] Fix checking availability for .NET. (#13470)
Change the logic to detect if an API is available to:
* First check if there are any applicable UnavailableOSPlatform attributes,
and only if an applicable attribute is found, then state that the API is
unavailable (we can't ascertain that an API is available from an
UnavailableOSPlatform attribute, only that it's unavailable).
* Once we know there are no applicable UnavailableOSPlatform attributes, we go
on to check for applicable SupportedOSPlatform attributes, and if one is
found, then we can say whether the API is available or not.
* If neither attributes were found, and we're building for Mac Catalyst, then
repeat the two above checks for iOS instead.
* If still nothing, then assume the API is available (while incorrect, it's
how our attributes are currently implemented).
This fixes introspection showing numerous test failures on older OS versions,
because we were detecting availability wrong - we were assuming that if
there's an UnavailableOSPlatform attribute whose version didn't match the OS
version, that the API was available (test case that proves this logic is
incorrect: OS version = 1.0, API introduced in 2.0, API unavailable in 3.0
- we'd detect that OS version 1.0 < unavailable in 3.0, and say "yay, we're
not unavailable, so we must be available!").
2021-12-01 19:29:20 +03:00
static IEnumerable < ( OSPlatformAttribute Attribute , ApplePlatform Platform , Version Version ) > ParseAttributes ( IEnumerable < OSPlatformAttribute > attributes )
2021-11-22 22:54:07 +03:00
{
foreach ( var attr in attributes ) {
if ( ! attr . TryParse ( out ApplePlatform ? platform , out var version ) )
continue ;
[tests] Fix checking availability for .NET. (#13470)
Change the logic to detect if an API is available to:
* First check if there are any applicable UnavailableOSPlatform attributes,
and only if an applicable attribute is found, then state that the API is
unavailable (we can't ascertain that an API is available from an
UnavailableOSPlatform attribute, only that it's unavailable).
* Once we know there are no applicable UnavailableOSPlatform attributes, we go
on to check for applicable SupportedOSPlatform attributes, and if one is
found, then we can say whether the API is available or not.
* If neither attributes were found, and we're building for Mac Catalyst, then
repeat the two above checks for iOS instead.
* If still nothing, then assume the API is available (while incorrect, it's
how our attributes are currently implemented).
This fixes introspection showing numerous test failures on older OS versions,
because we were detecting availability wrong - we were assuming that if
there's an UnavailableOSPlatform attribute whose version didn't match the OS
version, that the API was available (test case that proves this logic is
incorrect: OS version = 1.0, API introduced in 2.0, API unavailable in 3.0
- we'd detect that OS version 1.0 < unavailable in 3.0, and say "yay, we're
not unavailable, so we must be available!").
2021-12-01 19:29:20 +03:00
yield return new ( attr , platform . Value , version ) ;
}
}
2021-11-22 22:54:07 +03:00
[tests] Fix checking availability for .NET. (#13470)
Change the logic to detect if an API is available to:
* First check if there are any applicable UnavailableOSPlatform attributes,
and only if an applicable attribute is found, then state that the API is
unavailable (we can't ascertain that an API is available from an
UnavailableOSPlatform attribute, only that it's unavailable).
* Once we know there are no applicable UnavailableOSPlatform attributes, we go
on to check for applicable SupportedOSPlatform attributes, and if one is
found, then we can say whether the API is available or not.
* If neither attributes were found, and we're building for Mac Catalyst, then
repeat the two above checks for iOS instead.
* If still nothing, then assume the API is available (while incorrect, it's
how our attributes are currently implemented).
This fixes introspection showing numerous test failures on older OS versions,
because we were detecting availability wrong - we were assuming that if
there's an UnavailableOSPlatform attribute whose version didn't match the OS
version, that the API was available (test case that proves this logic is
incorrect: OS version = 1.0, API introduced in 2.0, API unavailable in 3.0
- we'd detect that OS version 1.0 < unavailable in 3.0, and say "yay, we're
not unavailable, so we must be available!").
2021-12-01 19:29:20 +03:00
public static bool IsAvailable ( this IEnumerable < OSPlatformAttribute > attributes , PlatformInfo targetPlatform )
{
var parsedAttributes = ParseAttributes ( attributes ) ;
2021-11-22 22:54:07 +03:00
[tests] Fix checking availability for .NET. (#13470)
Change the logic to detect if an API is available to:
* First check if there are any applicable UnavailableOSPlatform attributes,
and only if an applicable attribute is found, then state that the API is
unavailable (we can't ascertain that an API is available from an
UnavailableOSPlatform attribute, only that it's unavailable).
* Once we know there are no applicable UnavailableOSPlatform attributes, we go
on to check for applicable SupportedOSPlatform attributes, and if one is
found, then we can say whether the API is available or not.
* If neither attributes were found, and we're building for Mac Catalyst, then
repeat the two above checks for iOS instead.
* If still nothing, then assume the API is available (while incorrect, it's
how our attributes are currently implemented).
This fixes introspection showing numerous test failures on older OS versions,
because we were detecting availability wrong - we were assuming that if
there's an UnavailableOSPlatform attribute whose version didn't match the OS
version, that the API was available (test case that proves this logic is
incorrect: OS version = 1.0, API introduced in 2.0, API unavailable in 3.0
- we'd detect that OS version 1.0 < unavailable in 3.0, and say "yay, we're
not unavailable, so we must be available!").
2021-12-01 19:29:20 +03:00
var available = IsAvailable ( parsedAttributes , targetPlatform , targetPlatform . Name ) ;
if ( available . HasValue )
return available . Value ;
if ( targetPlatform . Name = = ApplePlatform . MacCatalyst ) {
available = IsAvailable ( parsedAttributes , targetPlatform , ApplePlatform . iOS ) ;
if ( available . HasValue )
return available . Value ;
2021-11-22 22:54:07 +03:00
}
// Our current attribute logic assumes that no attribute means that an API is available on all versions of that platform.
// This is not correct: the correct logic is that an API is available on a platform if there are no availability attributes
// for any other platforms. However, enforcing this here would make a few of our tests fail, so we must keep the incorrect
// logic until we've got all the right attributes implemented.
return true ;
// Correct logic:
// Only available if there aren't any attributes for other platforms
// return attributes.Count () == 0;
}
[tests] Fix checking availability for .NET. (#13470)
Change the logic to detect if an API is available to:
* First check if there are any applicable UnavailableOSPlatform attributes,
and only if an applicable attribute is found, then state that the API is
unavailable (we can't ascertain that an API is available from an
UnavailableOSPlatform attribute, only that it's unavailable).
* Once we know there are no applicable UnavailableOSPlatform attributes, we go
on to check for applicable SupportedOSPlatform attributes, and if one is
found, then we can say whether the API is available or not.
* If neither attributes were found, and we're building for Mac Catalyst, then
repeat the two above checks for iOS instead.
* If still nothing, then assume the API is available (while incorrect, it's
how our attributes are currently implemented).
This fixes introspection showing numerous test failures on older OS versions,
because we were detecting availability wrong - we were assuming that if
there's an UnavailableOSPlatform attribute whose version didn't match the OS
version, that the API was available (test case that proves this logic is
incorrect: OS version = 1.0, API introduced in 2.0, API unavailable in 3.0
- we'd detect that OS version 1.0 < unavailable in 3.0, and say "yay, we're
not unavailable, so we must be available!").
2021-12-01 19:29:20 +03:00
public static bool? IsAvailable ( IEnumerable < ( OSPlatformAttribute Attribute , ApplePlatform Platform , Version Version ) > attributes , PlatformInfo targetPlatform , ApplePlatform attributePlatform )
{
// First we check for any unsupported attributes, and only once we know that there aren't any unsupported
// attributes, we check for supported attributes. Otherwise we might determine that an API is available
// if we check the supported attribute first for OS=3.0 here:
// [SupportedOSPlatform ("1.0")
// [UnsupportedOSPlatform ("2.0")
// if we run into the SupportedOSPlatform attribute first.
foreach ( var ( attr , platform , version ) in attributes ) {
if ( platform ! = attributePlatform )
continue ;
if ( attr is UnsupportedOSPlatformAttribute ) {
var isUnsupported = version is not null & & targetPlatform . Version > = version ;
// At this point we can't ascertain that the API is available, only that it's unavailable,
// so only return in that case. We need to check the SupportedOSPlatform attributes
// to see if the API is available.
if ( isUnsupported )
return false ;
}
}
foreach ( var ( attr , platform , version ) in attributes ) {
if ( platform ! = attributePlatform )
continue ;
if ( attr is SupportedOSPlatformAttribute )
return version is null | | targetPlatform . Version > = version ;
}
return null ;
}
2021-11-22 22:54:07 +03:00
#else
2016-05-05 03:14:32 +03:00
public static bool IsAvailable ( this IEnumerable < AvailabilityBaseAttribute > attributes , PlatformInfo targetPlatform )
{
// always "available" from a binding perspective if
// there are no explicit annotations saying otherwise
var available = true ;
foreach ( var attr in attributes ) {
if ( attr . Platform ! = targetPlatform . Name )
continue ;
switch ( attr . AvailabilityKind ) {
case AvailabilityKind . Introduced :
if ( attr . Version ! = null )
available & = targetPlatform . Version > = attr . Version ;
if ( attr . Architecture ! = PlatformArchitecture . None & &
attr . Architecture ! = PlatformArchitecture . All )
available & = attr . Architecture . HasFlag ( targetPlatform . Architecture ) ;
break ;
case AvailabilityKind . Deprecated :
case AvailabilityKind . Obsoleted :
if ( attr . Version ! = null )
available & = targetPlatform . Version < attr . Version ;
// FIXME: handle architecture-level _un_availability?
// we didn't do this with the old AvailabilityAttribute...
break ;
case AvailabilityKind . Unavailable :
available = false ;
break ;
}
if ( ! available )
return false ;
}
return available ;
}
2021-11-22 22:54:07 +03:00
#endif
2016-05-05 03:14:32 +03:00
}
}