Swift-testing and @available?

i’m kind of in a pickle right now, because i have some test @Suites in the swift-hash package that use @available-gated APIs that are higher than the deployment minimum of the package itself.

specifically, the deployment minima i would like to preserve is

platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6)]

but the tests need

@available(macOS 13.3, iOS 16.4, macCatalyst 16.4, tvOS 16.4, visionOS 1.0, watchOS 9.4, *)

it seems the @available attribute is incompatible with @Suite, because when i place it there, i get

Attribute 'Suite' cannot be applied to this structure 
because it has been marked '@available(macOS 13.3, 
iOS 16.4, macCatalyst 16.4, tvOS 16.4, visionOS 1.0, 
watchOS 9.4, *)' (from macro 'Suite') SourceKit

i tried placing them on the individual @Test functions, but Swift Testing does not seem to preserve the attributes correctly, leading to compiler errors on macOS:

   |10 | @available(*, deprecated, message: "This type is an implementation detail of the testing library. Do not use it directly.")
   |11 | enum $s8MD5Tests7HashingV6string4TestfMp_81__🟠$test_container__function__staticfuncstring__test__expected_MD5_input_String__fMu_: Testing.__TestContainer {
   |12 |   static var __tests: [Testing.Test] {
   |   |              `- note: add @available attribute to enclosing static property
   |13 |     get async {
   |14 |       return [

normally, i would just raise the platform minima in the Package.swift, but this would break a few people (cc: @stackotter ) far downstream of the package who are using swift-png due to the virality of platforms and SwiftPM’s handling of that field.

but without upgrading the dependency and tagging a new swift-png release, we experience a dependency hell situation even further afield of the original package knot. so there are no good options i can think of here.

what is the recommended way to handle @available guards while using Swift Testing?

1 Like

Stupid workaround for now: wrap the inside of your entire test function with this

if #available(...) {
  // actual test here
} else {
  print("Skipping test due to old OS")
}

that’s what we did originally because the tests were all in one big function, but Swift Testing uses a one-function-per-test code organization model, which means the if #available {} else {} boilerplate needs to be added to every test function.

not impossible, but a lot of tedious reformatting nonetheless.

1 Like

@available is not supported on test suite types (with or without @Suite) because in that position it is not possible to know during macro expansion the semantic availability of a test function contained within the suite. For example:

@available(...)
struct S1 {}

extension S {
  // no way to see the @available attribute on S1
  @Suite struct S2 {
    @Test func f() {}
  }
}

@available should work without issue on individual test functions. There may be bugs in how we parse the attribute—a concrete example would be helpful.

1 Like

this commit of swift-hash exhibits the issue, my guess is it has something to do with the arguments passed to the Test(arguments:) macro