Skip to content

fix(macos): unblock native unit testing — UIImage, gzip linkage, sanitizer hang - #3095

Open
Ty Rauber (tyrauber) wants to merge 3 commits into
microsoft:mainfrom
tyrauber:fix-macos-unit-test-infra
Open

fix(macos): unblock native unit testing — UIImage, gzip linkage, sanitizer hang#3095
Ty Rauber (tyrauber) wants to merge 3 commits into
microsoft:mainfrom
tyrauber:fix-macos-unit-test-infra

Conversation

@tyrauber

Copy link
Copy Markdown

Summary:

Right now nothing can run a macOS native unit test in this repo. xcodebuild test -scheme RNTester-macOS fails before a single test executes, for three separate, pre-existing reasons unrelated to any specific feature:

  1. RCTImageLoaderTests.m uses UIImage, which doesn't exist on macOS — compile failure.
  2. RCTGzipTests.m fails to link: RCTIsGzippedData is implemented in RCTUtils.mm (Objective-C++) without extern "C", so its symbol is C++-mangled and the plain .m test can't find it. This is a real regression from the ObjC→ObjC++ conversion of RCTUtils; it affects iOS too, just nobody's noticed because no CI job runs these tests on either platform (Test All is disabled and guarded to facebook/react-native only).
  3. RNTester-macOS.xctestplan's sanitizer config makes the test runner hang. Isolated with a throwaway Swift package as a control: UBSan is fine, ASan hangs — this is an Xcode 26.0.1 AddressSanitizer runtime bug (it deadlocks re-entering its own initializer via dyld), not a bug in this repo. Dropped sanitizers from the macOS plan only; the iOS plan is untouched.

Fix (1) and (2), each with root cause and a minimal correction matching existing patterns in the codebase. Fix (3) by removing the sanitizer config from the one test plan it actually breaks on current tooling, with the evidence in the commit so it's easy to revert once Apple fixes the underlying bug.

Test Plan:

Before: xcodebuild test -workspace RNTesterPods.xcworkspace -scheme RNTester-macOS -only-testing:RNTester-macOSUnitTests fails to even launch — a build failure, then a link failure, then a hang, depending which of the three you hit first.

After: the target builds, links, launches, and runs all 192 tests. It's not fully green yet — 22 distinct tests still fail, none related to these three fixes:

  • 18 tests directly construct RCTBridge, which RN 0.83 removed (legacy architecture deletion) — real, separate test rot, left alone here on purpose to keep this PR scoped to infra.
  • 3 tests have stale URL-param string expectations.
  • 1 test pokes a private selector that no longer exists.

Also worth flagging: with UBSan briefly enabled during isolation, it caught a real SEGV in -[RCTBlobManager createFromParts:withId:] (null-pointer dispatch_async crash) — a live bug, filed separately, not touched here.

Also ran this branch's full CI matrix for real, on GitHub-hosted macOS runners against this fork: all green — build × 6 (macos/ios/visionos, static/dynamic), JS Tests, Yarn Constraints, NPM Publish Dry Run, Resolve Hermes, and the Prebuild macOS Core matrix.

Transparency note: this PR was drafted with AI assistance. I'm a human, I've personally verified each root cause and the test output above, and I'm happy to answer any questions or concerns directly.

Ty Rauber (tyrauber) and others added 3 commits September 9, 2026 19:46
RCTImageLoaderTests.m is compiled into both RNTesterUnitTests (iOS) and
RNTester-macOSUnitTests, but referenced UIImage directly, so the macOS
unit test target failed to compile:

  RCTImageLoaderTests.m:38:3: error: unknown type name 'UIImage'; did you mean 'CIImage'?

Use RCTPlatformImage, the existing per-platform alias (UIImage on iOS,
NSImage on macOS) that RCTImageLoaderCompletionBlock and
RCTImageLoaderPartialLoadBlock already use.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RCTGzipTests.m is a plain .m file that forward-declared
`extern BOOL RCTIsGzippedData(NSData *data);` and calls it. RCTUtils was
converted to Objective-C++ in a74649d, which gave the definition C++
mangling, so the test bundle no longer links:

  Undefined symbols for architecture arm64:
    "_RCTIsGzippedData", referenced from:
        -[RCTGzipTests testGzip] in RCTGzipTests.o
        -[RCTGzipTests testRequestBodyEncoding] in RCTGzipTests.o

Declare it in RCTUtils.h with RCT_EXTERN, like every other RCTUtils
function, which restores C linkage. Drop the now-redundant forward
declaration in RCTUtils.mm and the local extern in the test. No behavior
change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With any sanitizer enabled in RNTester-macOS.xctestplan, macOS unit tests
cannot be run at all on Xcode 26.0.1 / macOS 26.6.2. Two separate
toolchain failures, both reproduced:

1. Launch. A sanitizer makes Xcode build into
   Build/Products/Variant-<sanitizers>/Debug, but the scheme-driven test
   launcher does not follow it:

     xctest encountered an error (Failed to install or launch the test
     runner. (Underlying Error: Launch error. There is a problem
     launching using posix_spawn (error code: 2).))

   Reproduced with ASan+UBSan and with UBSan alone. The same build runs
   fine when driven from the generated .xctestrun, which carries absolute
   paths, so the products are good and only the launcher is confused.

2. Address Sanitizer deadlock. Past the launcher, an ASan-instrumented
   xctest bundle never starts:

     xctest encountered an error (The test runner hung before
     establishing connection.)

   sample(1) shows the ASan runtime spinning in its own initializer:

     __asan::AsanInitInternal()
       __asan::InitializeShadowMemory()
         __sanitizer::MemoryRangeIsAvailable()
           __sanitizer::get_dyld_hdr()
             dyld_shared_cache_iterate_text_swift
               _Block_copy -> malloc
                 __sanitizer_mz_malloc
                   __asan::AsanInitFromRtl()      <- re-entrant
                     __sanitizer::StaticSpinMutex::LockSlow()
                       internal_sched_yield()     <- forever

   This is not a React Native bug. A three-line Swift package with one
   trivial test fails identically with -enableAddressSanitizer YES, and
   passes without it and with -enableUndefinedBehaviorSanitizer YES.

The iOS plan (RNTester/RNTester.xctestplan) is left alone; only the macOS
plan is changed. Restore the sanitizer configuration once the toolchain
is fixed.

With this change `xcodebuild test -scheme RNTester-macOS
-only-testing:RNTester-macOSUnitTests` launches and runs all 192 tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tyrauber
Ty Rauber (tyrauber) requested a review from a team as a code owner September 10, 2026 16:15
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@tyrauber

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant