Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Linux] Teach SwiftPM how to handle actor-isolated test functions #5525

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

grynspan
Copy link
Contributor

This change teaches SwiftPM how to handle actor-isolated test cases in a package on Linux.

Motivation:

Given the following XCTestCase subclass:

@MainActor
class Tests1: XCTestCase {
  func testX() { }
}

Or this one:

class Tests1: XCTestCase {
  @MainActor
  func testX() { }
}

SwiftPM currently does not handle them properly, leading to a build error when compiling the test suite of the form:

error: converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'
  testCase(Tests1.__allTests__Tests1),
error: cannot convert value of type '[Any]' to expected argument type '[(String, (XCTestCase) -> () -> Void)]'
  testCase(ObserverTests.__allTests__Tests1),

Modifications:

This change emits a helper function into the synthesized test binary that takes either a synchronous or asynchronous member function of a test case class and returns a function of the appropriate form. It then uses that function when synthesizing the __allTests__ array instead of trying to check if a function isAsync.

Since a function of the form @MainActor func testX() { } lives in the twilight zone between sync and async, but can be trivially converted to an async function (with a synthesized thunk), this allows such functions to be added to the array in question.

Result:

This change should allow test suites with actor-isolated tests to build and be tested successfully.

…f test function signature (e.g. actor-isolated functions.)
@grynspan grynspan added the linux label May 18, 2022
@grynspan grynspan requested a review from compnerd May 18, 2022 19:30
@grynspan grynspan self-assigned this May 18, 2022
@grynspan
Copy link
Contributor Author

@swift-ci please smoke test

class MainActorIsolatedTestCaseTests: XCTestCase {
@MainActor
func test_actorIsolatedToMain() { }
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a clever way to test, but probably best to create a fixture instead: see FunctionalTests/TestDiscoveryTests for examples on how to set such up

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, will refactor.

@grynspan
Copy link
Contributor Author

Build failure does not appear related to my changes (timeout?)

@tomerd
Copy link
Contributor

tomerd commented May 20, 2022

looks like its failing on the change:

/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:6:57: error: heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
    static let __allTests__ActorIsolatedTestCaseTests = [
                                                        ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:107:69: error: heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
    static let __allTests__IndividuallyActorIsolatedTestCaseTests = [
                                                                    ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:108:39: error: call to global actor 'TestActor'-isolated instance method 'test_actorIsolatedByClass()' in a synchronous nonisolated context
        ("test_actorIsolatedByClass", test_actorIsolatedByClass),
                                      ^
/home/build-user/swiftpm/Tests/BasicsTests/ActorIsolatedTestCaseTests.swift:31:10: note: calls to instance method 'test_actorIsolatedByClass()' from outside of its actor context are implicitly asynchronous
    func test_actorIsolatedByClass() { }
         ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:116:38: error: call to main actor-isolated instance method 'test_actorIsolatedToMain()' in a synchronous nonisolated context
        ("test_actorIsolatedToMain", test_actorIsolatedToMain),
                                     ^
/home/build-user/swiftpm/Tests/BasicsTests/ActorIsolatedTestCaseTests.swift:39:10: note: calls to instance method 'test_actorIsolatedToMain()' from outside of its actor context are implicitly asynchronous
    func test_actorIsolatedToMain() { }
         ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:230:45: error: cannot convert value of type '[Any]' to expected argument type '[(String, (XCTestCase) -> () -> Void)]'
        testCase(ActorIsolatedTestCaseTests.__allTests__ActorIsolatedTestCaseTests),
                                            ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:230:45: note: arguments to generic parameter 'Element' ('Any' and '(String, (XCTestCase) -> () -> Void)') are expected to be equal
        testCase(ActorIsolatedTestCaseTests.__allTests__ActorIsolatedTestCaseTests),
                                            ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:240:57: error: cannot convert value of type '[Any]' to expected argument type '[(String, (XCTestCase) -> () -> Void)]'
        testCase(IndividuallyActorIsolatedTestCaseTests.__allTests__IndividuallyActorIsolatedTestCaseTests),
                                                        ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:240:57: note: arguments to generic parameter 'Element' ('Any' and '(String, (XCTestCase) -> () -> Void)') are expected to be equal
        testCase(IndividuallyActorIsolatedTestCaseTests.__allTests__IndividuallyActorIsolatedTestCaseTests),
                                                        ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:241:49: error: converting function value of type '@MainActor () -> ()' to '() -> Void' loses global actor 'MainActor'
        testCase(MainActorIsolatedTestCaseTests.__allTests__MainActorIsolatedTestCaseTests),
                                                ^
[235/253] Compiling SwiftPMPackageTests PackageCollectionsSigningTests.swift
[236/253] Compiling SwiftPMPackageTests PackageCollectionsModelTests.swift
[237/253] Compiling SwiftPMPackageTests PackageDescriptionTests.swift
[238/253] Compiling SwiftPMPackageTests PackageCollectionsTests.swift
[239/253] Compiling SwiftPMPackageTests PackageGraphPerformanceTests.swift
[240/253] Compiling SwiftPMPackageTests PackageGraphTests.swift
[241/253] Compiling SwiftPMPackageTests PackageLoadingTests.swift
[242/253] Compiling SwiftPMPackageTests PackageLoadingPerformanceTests.swift
error: emit-module command failed with exit code 1 (use -v to see invocation)[243/253] Emitting module SwiftPMPackageTests
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:6:57: error: heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
    static let __allTests__ActorIsolatedTestCaseTests = [
                                                        ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:107:69: error: heterogeneous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional
    static let __allTests__IndividuallyActorIsolatedTestCaseTests = [
                                                                    ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:108:39: error: call to global actor 'TestActor'-isolated instance method 'test_actorIsolatedByClass()' in a synchronous nonisolated context
        ("test_actorIsolatedByClass", test_actorIsolatedByClass),
                                      ^
/home/build-user/swiftpm/Tests/BasicsTests/ActorIsolatedTestCaseTests.swift:31:10: note: calls to instance method 'test_actorIsolatedByClass()' from outside of its actor context are implicitly asynchronous
    func test_actorIsolatedByClass() { }
         ^
/home/build-user/swiftpm/.build/x86_64-unknown-linux-gnu/debug/SwiftPMPackageTests.derived/BasicsTests.swift:116:38: error: call to main actor-isolated instance method 'test_actorIsolatedToMain()' in a synchronous nonisolated context
        ("test_actorIsolatedToMain", test_actorIsolatedToMain),
                                     ^
/home/build-user/swiftpm/Tests/BasicsTests/ActorIsolatedTestCaseTests.swift:39:10: note: calls to instance method 'test_actorIsolatedToMain()' from outside of its actor context are implicitly asynchronous
    func test_actorIsolatedToMain() { }
         ^

@grynspan
Copy link
Contributor Author

Blergh, fair. I think that's using the deprecated path? I can make the same change on that path but I'm not sure when one path or the other is used. Any thoughts?

@tomerd
Copy link
Contributor

tomerd commented May 21, 2022

i guess if you remove these tests and instead make them into a fixture, then that would use the just-built SwiftPM to run those and should help resolve it?

@tomerd
Copy link
Contributor

tomerd commented Jun 6, 2022

@grynspan do you need any more information on how to create such fixture?

@grynspan
Copy link
Contributor Author

grynspan commented Jun 6, 2022

I might—I have had to change focus and haven't been looking at this PR but will come back to it when I can.

@grynspan
Copy link
Contributor Author

I need to circle back to this at some point!

@grynspan grynspan added the swift test Changes impacting `swift test` tool label Feb 13, 2024
jshier pushed a commit to Alamofire/Alamofire that referenced this pull request Jan 7, 2025
### Goals ⚽

This PR restores the Linux and Android build actions, which were
disabled in #3920. It also
updates the Android build action to use
[swift-android-action@v2](https://github.com/marketplace/actions/swift-android-action),
which enables it to run on Linux rather than requiring macOS.

### Implementation Details 🚧
Linux and Android toolchains cannot build test methods annotated with
`@MainActor`, as discussed at
swiftlang/swift-package-manager#5525. Rather
than disabling building the tests, we instead trim out the `@MainActor`
declarations with a simple `sed` command prior to performing the build
on those platforms.

### Testing Details 🔍
I didn't add or remove any tests. The builds at passing in my fork for
[Android](https://github.com/marcprux/Alamofire/actions/runs/12614003719/job/35152459756)
and
[Linux](https://github.com/marcprux/Alamofire/actions/runs/12614003719/job/35152460366).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
linux swift test Changes impacting `swift test` tool
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants