MobileNativeFoundation / bluepill

Bluepill is a reliable iOS testing tool that runs UI tests using multiple simulators on a single machine
BSD 2-Clause "Simplified" License
3.19k stars 232 forks source link

Add logic test support #567

Open lthrockmorton opened 9 months ago

lthrockmorton commented 9 months ago

Adds logic test support to Bluepill, so that unit tests can be run without an app host. Resolves https://github.com/MobileNativeFoundation/bluepill/issues/531.

Leverages CoreSimulator's private spawn method to run the xctest executable on individual logic test bundles. Doing so requires adjustments to various inputs (e.g. the xctest executable expects the list of tests in a different format) as well as different error handling (based on the nuances of how errors/crashes surface from the spawn method.

A number of critical pieces of functionality were added to support this:

  1. Logic tests run using a different command than traditional unit or scenario tests (xcrun simctl spawn -s xctest).

    • Reverse engineering of Apple's CoreSimulator framework was required to use the private spawnAsyncWithPath: method to spawn a logic test execution.
    • Implementation details can be found in BPSimulator's executeLogicTestsWithParser method.
  2. The xctest API does not support a test skip-list, and instead only provides an opt-in list. Bluepill has long had a hacky, broken solution for identifying all tests in a test bundle.

    • The fix is to inject a dylib directly into the xctest execution to pipe out test data. Full context on the problem and solution are detailed in this document
    • The injected library is BPTestInspector, a new project in the BP workspace. It is injected during BPSimulator's collectTestSuiteInfoWithCompletion method.
    • Making this dylib available and linked to both the Bluepill and xctest executions requires some additional env setup, both in objc code (for xctest) and Bazel (for Bluepill)
  3. When running Xcode in Rosetta mode (as is the case for many consumers), there is a weird interaction with spawned executions -- they will try to run universal binaries in arm64 rather than x86.

    • As a result, we have to adapt the xctest executable to the desired architecture before running it.
    • Impl details are in BPUtils's lipoExecutableAtPath:

Also fixes a bug where retrying tests after a crash was being dictated by the wrong config variable (onlyRetryFailed rather than retryAppCrashTests).

Tests were added to validate:

Additionally, a new logic test batch was created in a consuming app (Voyager iOS), and tests passed. Notably, the new BPTestInspector code correctly omitted hundreds of other legacy false positive test method names, and only the desired tests were treated as tests.