Tests: avoid race-condition in leak detection#2806
Conversation
ff7b650 to
e00e9b2
Compare
There was a problem hiding this comment.
Bonus points for also not capturing self here.
There was a problem hiding this comment.
didn't know it was possible to capture this as a boolean, I thought this block would capture strongly instead. How does this work? like, doesn't it need the object in order to reference when re-evaluating?
There was a problem hiding this comment.
The references are weak though (defined above). The previous implementation used an @autoclosure, so expect(purchases) was converted to expect { purchases }.
Internally it was using reflection (possibly String.init(describing:)) to print a value for that object.
Now when it generates a description from the subject of the expectation, it's just a boolean.
Let me know if that makes sense.
Codecov Report
@@ Coverage Diff @@
## main #2806 +/- ##
==========================================
- Coverage 86.56% 86.51% -0.05%
==========================================
Files 215 215
Lines 15484 15472 -12
==========================================
- Hits 13403 13386 -17
- Misses 2081 2086 +5 |
Fixes https://app.circleci.com/pipelines/github/RevenueCat/purchases-ios/12839/workflows/2b1e24b9-428a-40ed-bcfd-a94d7591236d/jobs/89720/steps I noticed when downloading the `.xcresult` that the tests had crashed with this: ``` Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libswiftCore.dylib 0x10bc64589 swift::runtime::AccessSet::insert(swift::runtime::Access*, void*, void*, swift::ExclusivityFlags) + 73 1 libswiftCore.dylib 0x10bc647e2 swift_beginAccess + 66 2 BackendIntegrationTests 0x13ab37c76 default argument 1 of Expectation.toEventually(_:timeout:pollInterval:description:) + 54 3 BackendIntegrationTests 0x13ab37b44 closure #1 in BaseBackendIntegrationTests.verifyPurchasesDoesNotLeak() + 276 (BaseBackendIntegrationTests.swift:167) ``` Turns out that because of the way it was written, normally `Nimble` tries to print the instance during a failure: ``` BasePurchasesTests.swift:116: error: -[UnitTests.PurchasesConfiguringTests testSettingTheDelegateAfterInitializationSendsCachedCustomerInfo] : failed - Purchases has leaked expected to eventually be nil, got <<RCPurchases: 0x125d13bf0>> ``` That crash points to a race condition inside Swift's runtime. To avoid that, this changes the expectation so `Nimble` would only try to print "expected true, got false", with our own provided description. Therefore making sure that it doesn't try to access the instance while it's being deallocated.
e00e9b2 to
e14d7d2
Compare
Looks like #2806 didn't work. I still see this race-condition crash: ``` Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libswiftCore.dylib 0x10bc64589 swift::runtime::AccessSet::insert(swift::runtime::Access*, void*, void*, swift::ExclusivityFlags) + 73 1 libswiftCore.dylib 0x10bc647e2 swift_beginAccess + 66 2 BackendIntegrationTests 0x13ab37c76 default argument 1 of Expectation.toEventually(_:timeout:pollInterval:description:) + 54 3 BackendIntegrationTests 0x13ab37b44 closure #1 in BaseBackendIntegrationTests.verifyPurchasesDoesNotLeak() + 276 (BaseBackendIntegrationTests.swift:167) ``` This slight refactor matches the implementation in `BasePurchasesTests`, and I haven't the crash there. So hopefully this will work.
Looks like #2806 didn't work. I still see this race-condition crash: ``` Thread 0 Crashed:: Dispatch queue: com.apple.main-thread 0 libswiftCore.dylib 0x10bc64589 swift::runtime::AccessSet::insert(swift::runtime::Access*, void*, void*, swift::ExclusivityFlags) + 73 1 libswiftCore.dylib 0x10bc647e2 swift_beginAccess + 66 2 BackendIntegrationTests 0x13ab37c76 default argument 1 of Expectation.toEventually(_:timeout:pollInterval:description:) + 54 3 BackendIntegrationTests 0x13ab37b44 closure #1 in BaseBackendIntegrationTests.verifyPurchasesDoesNotLeak() + 276 (BaseBackendIntegrationTests.swift:167) ``` This slight refactor matches the implementation in `BasePurchasesTests`, and I haven't the crash there. So hopefully this will work. I filed swiftlang/swift#67361 with the crash.
**This is an automatic release.** ### Dependency Updates * Bump fastlane from 2.213.0 to 2.214.0 (#2824) via dependabot[bot] (@dependabot[bot]) ### Other Changes * `MainThreadMonitor`: don't crash if there is no test in progress (#2838) via NachoSoto (@NachoSoto) * `CI`: fixed Fastlane APITester lanes (#2836) via NachoSoto (@NachoSoto) * `Integration Tests`: workaround Swift runtime crash (#2826) via NachoSoto (@NachoSoto) * `@EnsureNonEmptyArrayDecodable` (#2831) via NachoSoto (@NachoSoto) * `iOS 17`: added tests for simulating cancellations (#2597) via NachoSoto (@NachoSoto) * `CI`: make all `Codecov` jobs `informational` (#2828) via NachoSoto (@NachoSoto) * `MainThreadMonitor`: check deadlocks only ever N seconds (#2820) via NachoSoto (@NachoSoto) * New `@NonEmptyStringDecodable` (#2819) via NachoSoto (@NachoSoto) * `MockDeviceCache`: avoid using real `UserDefaults` (#2814) via NachoSoto (@NachoSoto) * `throwAssertion`: fixed Xcode 15 compilation (#2813) via NachoSoto (@NachoSoto) * `CustomEntitlementsComputation`: fixed API testers (#2815) via NachoSoto (@NachoSoto) * `PackageTypeTests`: fixed iOS 12 (#2807) via NachoSoto (@NachoSoto) * `Tests`: avoid race-condition in leak detection (#2806) via NachoSoto (@NachoSoto) * Revert "`Unit Tests`: removed leak detection" (#2805) via NachoSoto (@NachoSoto) * `PackageType: Codable` implementation (#2797) via NachoSoto (@NachoSoto) * `SystemInfo.init` no longer `throws` (#2803) via NachoSoto (@NachoSoto) * `Trusted Entitlements`: add support for signing `POST` body (#2753) via NachoSoto (@NachoSoto) * `Tests`: unified default timeouts (#2801) via NachoSoto (@NachoSoto) * `Tests`: removed forced-unwrap (#2799) via NachoSoto (@NachoSoto) * `Tests`: added missing `super.setUp()` (#2804) via NachoSoto (@NachoSoto) * Replaced `FatalErrorUtil` with `Nimble` (#2802) via NachoSoto (@NachoSoto) * `Tests`: fixed another flaky test (#2795) via NachoSoto (@NachoSoto) * `TimingUtil`: improved tests by using `Clock` (#2794) via NachoSoto (@NachoSoto) * `IgnoreDecodeErrors`: log decoding error (#2778) via NachoSoto (@NachoSoto) * `TestLogHandler`: changed all tests to explicitly deinitialize it (#2784) via NachoSoto (@NachoSoto) * `LocalReceiptParserStoreKitTests`: fixed flaky test failure (#2785) via NachoSoto (@NachoSoto) * `Unit Tests`: removed leak detection (#2792) via NachoSoto (@NachoSoto) * `Tests`: fixed another flaky failure with asynchronous check (#2786) via NachoSoto (@NachoSoto) --------- Co-authored-by: NachoSoto <ignaciosoto90@gmail.com>
Fixes https://app.circleci.com/pipelines/github/RevenueCat/purchases-ios/12839/workflows/2b1e24b9-428a-40ed-bcfd-a94d7591236d/jobs/89720/steps
I noticed when downloading the
.xcresultthat the tests had crashed with this:Turns out that because of the way it was written, normally
Nimbletries to print the instance during a failure:That crash points to a race condition inside Swift's runtime. To avoid that, this changes the expectation so
Nimblewould only try to print "expected true, got false", with our own provided description. Therefore making sure that it doesn't try to access the instance while it's being deallocated.