-
Notifications
You must be signed in to change notification settings - Fork 0
Refactor FXIOS-12796 [Swift 6 Migration] Fix main actor isolation warnings that are Swift 6 errors in the XCUITests suite - Batch 2 #2
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
base: claude_claude_vs_qodo_base__refactor_fxios-12796_swift_6_migration_fix_main_actor_isolation_warnings_that_are_swift_6_errors_in_the_xcuitests_suite_-_batch_2_pr2
Are you sure you want to change the base?
Changes from all commits
479bf8f
a036560
a7d891e
b5b21d0
ed8f2a5
81d28b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,9 @@ import Shared | |
| let page1 = "http://localhost:\(serverPort)/test-fixture/find-in-page-test.html" | ||
| let page2 = "http://localhost:\(serverPort)/test-fixture/test-example.html" | ||
| let serverPort = ProcessInfo.processInfo.environment["WEBSERVER_PORT"] ?? "\(Int.random(in: 1025..<65000))" | ||
| @MainActor | ||
| let urlBarAddress = XCUIApplication().textFields[AccessibilityIdentifiers.Browser.AddressToolbar.searchTextField] | ||
| @MainActor | ||
| let homepageSearchBar = XCUIApplication().cells[AccessibilityIdentifiers.FirefoxHomepage.SearchBar.itemCell] | ||
|
|
||
| func path(forTestPage page: String) -> String { | ||
|
|
@@ -107,16 +109,16 @@ class BaseTestCase: XCTestCase { | |
| } | ||
| } | ||
|
|
||
| override func setUp() { | ||
| super.setUp() | ||
| override func setUp() async throws { | ||
| try await super.setUp() | ||
| continueAfterFailure = false | ||
| setUpApp() | ||
| setUpScreenGraph() | ||
| } | ||
|
|
||
| override func tearDown() { | ||
| override func tearDown() async throws { | ||
| app.terminate() | ||
| super.tearDown() | ||
| try await super.tearDown() | ||
| } | ||
|
|
||
| var skipPlatform: Bool { | ||
|
|
@@ -503,19 +505,19 @@ class BaseTestCase: XCTestCase { | |
| } | ||
|
|
||
| class IpadOnlyTestCase: BaseTestCase { | ||
| override func setUp() { | ||
| override func setUp() async throws { | ||
| specificForPlatform = .pad | ||
| if iPad() { | ||
| super.setUp() | ||
| await super.setUp() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Bug: Extended reasoning...What the bug isAt line 511 of The specific code pathThe PR changed Why existing code does not prevent itThis is a straightforward oversight during the async/throws migration. The old synchronous ImpactThis will cause a Swift compile error: "Call can throw but is not marked with try". The entire XCUITests suite will fail to compile, blocking all iPad-specific test cases that inherit from Step-by-step proof
FixChange line 511 from |
||
| } | ||
| } | ||
| } | ||
|
|
||
| class IphoneOnlyTestCase: BaseTestCase { | ||
| override func setUp() { | ||
| override func setUp() async throws { | ||
| specificForPlatform = .phone | ||
| if !iPad() { | ||
| super.setUp() | ||
| try await super.setUp() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -291,7 +291,7 @@ class DragAndDropTestIpad: IpadOnlyTestCase { | |
| // This DDBB contains those 4 websites listed in the name | ||
| let historyAndBookmarksDB = "browserYoutubeTwitterMozillaExample-places.db" | ||
|
|
||
| override func setUp() { | ||
| override func setUp() async throws { | ||
|
Comment on lines
291
to
+294
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Bug: DragAndDropTests.tearDown() (line 28) was not converted to async throws like every other tearDown override in this PR. Since BaseTestCase.tearDown() is now async throws, the synchronous super.tearDown() call bypasses BaseTestCase entirely and calls XCTestCase.tearDown() instead, meaning app.terminate() from BaseTestCase.tearDown() may not execute properly for these tests. The inner class DragAndDropTestIpad (line 310) was correctly converted — this is just a missed conversion on the outer class. Extended reasoning...What the bug isThe PR converts setUp and tearDown overrides across the XCUITests suite from synchronous to The specific code pathAt line 28-31 of DragAndDropTests.swift: override func tearDown() {
XCUIDevice.shared.orientation = UIDeviceOrientation.portrait
super.tearDown()
}Meanwhile, BaseTestCase.tearDown() (line 125-128 of BaseTestCase.swift) was converted to: override func tearDown() async throws {
app.terminate()
try await super.tearDown()
}Why existing code doesn't prevent itWhen a class hierarchy has both sync and async versions of tearDown, XCTest's behavior becomes nuanced. The synchronous Step-by-step proof
ImpactThe device orientation will not be properly reset to portrait between DragAndDropTests test cases, potentially causing layout-dependent test failures in subsequent tests. Additionally, this leaves a synchronous tearDown that the PR is specifically trying to migrate away from, defeating the purpose of the Swift 6 migration for this class. Every other test class with a tearDown override was correctly converted. How to fixConvert the method signature to match the pattern used everywhere else in this PR: override func tearDown() async throws {
XCUIDevice.shared.orientation = UIDeviceOrientation.portrait
try await super.tearDown()
}This is the exact same pattern already correctly applied to the inner DragAndDropTestIpad class at lines 310-313 of the same file. |
||
| // Test name looks like: "[Class testFunc]", parse out the function name | ||
| let parts = name.replacingOccurrences(of: "]", with: "").split(separator: " ") | ||
| let key = String(parts[1]) | ||
|
|
@@ -304,12 +304,12 @@ class DragAndDropTestIpad: IpadOnlyTestCase { | |
| LaunchArguments.SkipContextualHints, | ||
| LaunchArguments.DisableAnimations] | ||
| } | ||
| super.setUp() | ||
| try await super.setUp() | ||
| } | ||
|
|
||
| override func tearDown() { | ||
| override func tearDown() async throws { | ||
| XCUIDevice.shared.orientation = UIDeviceOrientation.portrait | ||
| super.tearDown() | ||
| try await super.tearDown() | ||
| } | ||
|
|
||
| // https://mozilla.testrail.io/index.php?/cases/view/2307024 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,10 +43,10 @@ class FeatureFlaggedTestSuite: FeatureFlaggedTestBase { | |
| addLaunchArgument(jsonFileName: jsonFileName, featureName: featureName) | ||
| } | ||
|
|
||
| override func setUp() { | ||
| override func setUp() async throws { | ||
| continueAfterFailure = false | ||
| setUpExperimentVariables() | ||
| setUpApp() | ||
| setUpExperimentVariables() | ||
|
Comment on lines
48
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 Bug: Extended reasoning...What the bug isIn Why this causes a crash
var jsonFileName: String!
var featureName: String!These are initialized to override func setUpApp() {
addLaunchArgument(jsonFileName: jsonFileName, featureName: featureName)
}The func addLaunchArgument(jsonFileName: String, featureName: String)When Step-by-step proof
ImpactEvery test class that extends FixSwap lines 48 and 49 back to the original order: setUpExperimentVariables()
setUpApp() |
||
| setUpLaunchArguments() | ||
| setUpScreenGraph() | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴
forceRestartApp()(line 139) callstearDown()andsetUp()synchronously, but this PR changed both toasync throwsoverrides. In Swift, sync and async methods are separate overloads, so these calls now resolve toXCTestCase's base class no-ops instead ofBaseTestCase's overrides. This makesforceRestartApp()effectively a no-op — it will no longer terminate the app, relaunch it, or rebuild the screen graph. This method is called fromBookmarksTests(line 401) andAuthenticationTest(line 52).Extended reasoning...
What the bug is
The
forceRestartApp()method at line 139-142 ofBaseTestCase.swiftcallstearDown()andsetUp()synchronously:This PR changed
BaseTestCase.setUp()fromoverride func setUp()tooverride func setUp() async throws, and similarlytearDown()tooverride func tearDown() async throws. In Swift, a synchronous method and anasync throwsmethod with the same name are separate overloads — they are not the same method. The synchronous calls inforceRestartApp()will resolve toXCTestCase's base class synchronoussetUp()andtearDown(), which are no-ops.Step-by-step proof
Before this PR,
BaseTestCaseoverrodefunc setUp()(sync). WhenforceRestartApp()calledtearDown()andsetUp(), Swift's method resolution found the sync overrides inBaseTestCase, which executedapp.terminate()(in tearDown) andsetUpApp()/setUpScreenGraph()(in setUp).After this PR,
BaseTestCaseonly overridesfunc setUp() async throwsandfunc tearDown() async throws. The sync versions no longer exist inBaseTestCase.When
forceRestartApp()(a non-async function) callstearDown(), Swift looks for a synchronoustearDown()method. It does NOT find one inBaseTestCase(onlyasync throwsversion exists), so it resolves toXCTestCase.tearDown(), which is a no-op.Similarly, the
setUp()call resolves toXCTestCase.setUp(), another no-op.Result:
forceRestartApp()does nothing — it doesn't terminate the app, doesn't relaunch it, and doesn't rebuild the screen graph.Impact
This method is called in at least two test files:
BookmarksTests.swiftline 401: callsforceRestartApp()to restart the app during a bookmarks link validation testAuthenticationTest.swiftline 52: callsforceRestartApp()to verify that BasicAuth login persists after an app restartIn both cases, the tests will silently pass without actually restarting the app, meaning they are no longer testing what they intend to test. The
AuthenticationTestin particular is supposed to verify persistence across app restarts, which this regression completely undermines.How to fix
The simplest fix is to make
forceRestartApp()async and await the calls:Then update callers to use
await. Alternatively, inline the actual teardown/setup logic (e.g.,app.terminate()+setUpApp()+setUpScreenGraph()) directly inforceRestartApp()without going through the async overrides.