Skip to content

Commit 8bdbce1

Browse files
authored
ios: reenable integration tests (#800)
1 parent 5d7b28a commit 8bdbce1

8 files changed

Lines changed: 90 additions & 37 deletions

File tree

.github/workflows/ios.yaml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,7 @@ jobs:
9494
- name: Rust tests
9595
run: cargo test --package swift_bridge
9696

97-
macos_tsan:
98-
# Note: tsan has been disabled because it's too flaky.
99-
# https://bitdrift.slack.com/archives/C058ZD2M2CQ/p1755792021318899?thread_ts=1755784965.888369&cid=C058ZD2M2CQ
97+
tests:
10098
runs-on: macos-14
10199
needs: "pre_check"
102100
if: needs.pre_check.outputs.should_run == 'true'
@@ -107,11 +105,11 @@ jobs:
107105

108106
- name: "Install dependencies"
109107
run: ./ci/mac_ci_setup.sh
110-
- name: Run iOS tests (tsan)
111-
run: env -u ANDROID_NDK_HOME ./bazelw test $(./bazelw query 'kind(ios_unit_test, //test/platform/swift/unit_integration/core/...)') --test_tag_filters=macos_only --test_output=errors --config ci --config ios
108+
- name: Run iOS tests
109+
run: env -u ANDROID_NDK_HOME ./bazelw test $(./bazelw query 'kind(ios_unit_test, //test/platform/swift/unit_integration/...)') --test_tag_filters=macos_only --test_output=errors --config ci --config ios
112110
verify_ios:
113111
runs-on: ubuntu-latest
114-
needs: ["macos_tsan", "swift_hello_world", "unit_tests"]
112+
needs: ["tests", "swift_hello_world", "unit_tests"]
115113
if: always()
116114
steps:
117115
# Checkout repo to Github Actions runner
@@ -120,5 +118,5 @@ jobs:
120118
with:
121119
fetch-depth: 1
122120
- run: |
123-
./ci/check_result.sh ${{ needs.macos_tsan.result }} \
121+
./ci/check_result.sh ${{ needs.tests.result }} \
124122
&& ./ci/check_result.sh ${{ needs.swift_hello_world.result }}

test/platform/swift/benchmark/ClockTimeProfiler.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ final class LoggerColdConfigurationBenchmark: AnyBenchmark {
115115
}
116116

117117
public let kPreConfigLogBenchmark = BenchmarkSuite(name: "Logging - Pre-Config") { suite in
118-
guard let logger = try? Logger.make() else {
118+
guard let logger = try? Logger.make(directoryURL: try? makeTmpDirectory()) else {
119119
return assertionFailure("failed to create a logger")
120120
}
121121

@@ -131,7 +131,7 @@ public let kPreConfigLogBenchmark = BenchmarkSuite(name: "Logging - Pre-Config")
131131
)
132132
}
133133

134-
guard let logger5Fields = try? Logger.make() else {
134+
guard let logger5Fields = try? Logger.make(directoryURL: try? makeTmpDirectory()) else {
135135
return assertionFailure("failed to create a logger")
136136
}
137137

@@ -153,7 +153,7 @@ public let kPreConfigLogBenchmark = BenchmarkSuite(name: "Logging - Pre-Config")
153153
)
154154
}
155155

156-
guard let logger10Fields = try? Logger.make() else {
156+
guard let logger10Fields = try? Logger.make(directoryURL: try? makeTmpDirectory()) else {
157157
return assertionFailure("failed to create a logger")
158158
}
159159

@@ -180,7 +180,7 @@ public let kPreConfigLogBenchmark = BenchmarkSuite(name: "Logging - Pre-Config")
180180
)
181181
}
182182

183-
guard let loggerOverflow = try? Logger.make() else {
183+
guard let loggerOverflow = try? Logger.make(directoryURL: try? makeTmpDirectory()) else {
184184
return assertionFailure("failed to create a logger")
185185
}
186186

test/platform/swift/unit_integration/core/ConfigurationTests.swift

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,13 @@ final class ConfigurationTests: XCTestCase {
2525
}
2626

2727
func testConfigurationSimple() throws {
28-
Logger.start(
29-
withAPIKey: "api_key",
30-
sessionStrategy: .fixed()
31-
)
28+
self.startLoggerWithIsolatedDirectory(apiKey: "api_key")
3229

3330
XCTAssertNotNil(Logger.getShared())
3431
}
3532

3633
func testConfigurationDefault() throws {
37-
Logger.start(
38-
withAPIKey: "api_key",
39-
sessionStrategy: .fixed()
40-
)
34+
self.startLoggerWithIsolatedDirectory(apiKey: "api_key")
4135

4236
XCTAssertNotNil(Logger.getShared())
4337
}
@@ -48,8 +42,7 @@ final class ConfigurationTests: XCTestCase {
4842
}
4943

5044
func testLoggerRootPath() throws {
51-
let tempDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
52-
.appendingPathComponent(UUID().uuidString)
45+
let tempDir = self.makeTemporaryLoggerDirectory()
5346

5447
XCTAssertFalse(FileManager.default.fileExists(atPath: tempDir.path))
5548

test/platform/swift/unit_integration/core/LoggerSharedTests.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@ final class LoggerSharedTests: XCTestCase {
2727
}
2828

2929
let integrator = try XCTUnwrap(
30-
Logger.start(
31-
withAPIKey: "foo",
32-
sessionStrategy: .fixed()
33-
)
30+
self.startLoggerWithIsolatedDirectory(apiKey: "foo")
3431
)
3532

3633
integrator.enableIntegrations([integration])

test/platform/swift/unit_integration/core/SessionURLTests.swift

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ final class SessionURLTests: XCTestCase {
1616
}
1717

1818
func testDefaultSessionUrl() throws {
19-
Logger.start(
20-
withAPIKey: "api_key",
21-
sessionStrategy: .fixed(),
22-
configuration: .init()
23-
)
19+
self.startLoggerWithIsolatedDirectory(apiKey: "api_key")
2420
let sessionID = try XCTUnwrap(Logger.sessionID)
2521
XCTAssertEqual(Logger.sessionURL, "https://timeline.bitdrift.io/s/\(sessionID)?utm_source=sdk")
2622
}
@@ -77,9 +73,8 @@ final class SessionURLTests: XCTestCase {
7773
}
7874

7975
private func configureLogger(apiURL: String) throws {
80-
Logger.start(
81-
withAPIKey: "api_key",
82-
sessionStrategy: .fixed(),
76+
self.startLoggerWithIsolatedDirectory(
77+
apiKey: "api_key",
8378
configuration: .init(apiURL: try XCTUnwrap(URL(string: apiURL)))
8479
)
8580
}

test/platform/swift/unit_integration/core/helpers/XCTestCase+Extensions.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,53 @@
88
@testable import Capture
99
import CapturePassable
1010
import Difference
11+
import Foundation
1112
import XCTest
1213

1314
extension XCTestCase {
15+
/// Creates a unique temporary directory for logger tests.
16+
///
17+
/// This helper prevents "Failed to acquire directory lock" errors when multiple tests
18+
/// create loggers in quick succession by giving each logger instance its own isolated directory.
19+
///
20+
/// - returns: A unique temporary directory URL
21+
func makeTemporaryLoggerDirectory() -> URL {
22+
return FileManager.default.temporaryDirectory
23+
.appendingPathComponent("bitdrift_test_\(UUID().uuidString)")
24+
}
25+
26+
/// Starts a logger with a unique temporary directory to avoid directory lock conflicts.
27+
///
28+
/// This is a convenience wrapper around `Logger.start()` that automatically provides
29+
/// a unique directory for each test invocation.
30+
///
31+
/// - parameter apiKey: The API key for the logger
32+
/// - parameter sessionStrategy: The session strategy to use
33+
/// - parameter configuration: Optional configuration (rootFileURL will be overridden)
34+
/// - parameter fieldProviders: Optional field providers
35+
/// - parameter dateProvider: Optional date provider
36+
///
37+
/// - returns: A LoggerIntegrator instance, or nil if logger creation failed
38+
@discardableResult
39+
func startLoggerWithIsolatedDirectory(
40+
apiKey: String = "test_api_key",
41+
sessionStrategy: SessionStrategy = .fixed(),
42+
configuration: Configuration = .init(),
43+
fieldProviders: [FieldProvider] = [],
44+
dateProvider: DateProvider? = nil
45+
) -> LoggerIntegrator? {
46+
var config = configuration
47+
config.rootFileURL = makeTemporaryLoggerDirectory()
48+
49+
return Logger.start(
50+
withAPIKey: apiKey,
51+
sessionStrategy: sessionStrategy,
52+
configuration: config,
53+
fieldProviders: fieldProviders,
54+
dateProvider: dateProvider
55+
)
56+
}
57+
1458
func assertEqual(
1559
_ fields1: [String: String],
1660
_ fields2: Fields?,

test/platform/swift/unit_integration/integrations/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ bitdrift_mobile_swift_test(
55
srcs = glob(["**/*.swift"]),
66
repository = "@capture",
77
tags = ["macos_only"],
8+
use_test_host = True,
89
visibility = ["//visibility:public"],
910
deps = [
1011
"//platform/swift/source:ios_lib",

test/platform/swift/unit_integration/integrations/URLSessionIntegrationTests.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ import CaptureMocks
1111
import Foundation
1212
import XCTest
1313

14+
// MARK: - Test Helpers
15+
16+
extension XCTestCase {
17+
/// Creates a unique temporary directory for logger tests to avoid directory lock conflicts.
18+
///
19+
/// - returns: A unique temporary directory URL
20+
fileprivate func makeTemporaryLoggerDirectory() -> URL {
21+
return FileManager.default.temporaryDirectory
22+
.appendingPathComponent("bitdrift_test_\(UUID().uuidString)")
23+
}
24+
}
25+
1426
// swiftlint:disable file_length
1527
private final class URLSessionIncompleteDelegate: NSObject, URLSessionTaskDelegate {
1628
var didCompleteExpectation: XCTestExpectation?
@@ -23,7 +35,10 @@ private final class URLSessionIncompleteDelegate: NSObject, URLSessionTaskDelega
2335
private final class URLSessionCustomDelegate: NSObject, URLSessionDelegate {
2436
var didReceiveChallenge: XCTestExpectation?
2537

26-
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
38+
func urlSession(
39+
_ session: URLSession,
40+
didReceive challenge: URLAuthenticationChallenge
41+
) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
2742
self.didReceiveChallenge?.fulfill()
2843
return (.performDefaultHandling, nil)
2944
}
@@ -75,8 +90,13 @@ final class URLSessionIntegrationTests: XCTestCase {
7590
URLSessionIntegration.shared.disableURLSessionTaskSwizzling()
7691

7792
Logger.resetShared(logger: self.logger)
93+
7894
Logger
79-
.start(withAPIKey: "123", sessionStrategy: .fixed())?
95+
.start(
96+
withAPIKey: "123",
97+
sessionStrategy: .fixed(),
98+
configuration: .init(rootFileURL: self.makeTemporaryLoggerDirectory())
99+
)?
80100
.enableIntegrations([.urlSession()], disableSwizzling: !swizzle)
81101
}
82102

@@ -180,11 +200,16 @@ final class URLSessionIntegrationTests: XCTestCase {
180200
let session = URLSession(configuration: .background(withIdentifier: "w00t"))
181201
let task = session.dataTask(with: self.makeURL())
182202

183-
let logRequestExpectation = self.expectation(description: "request logged")
203+
let logRequestExpectation = self.expectation(
204+
description: "request logged"
205+
)
184206
self.logger.logRequestExpectation = logRequestExpectation
185207
task.resume()
186208

187-
XCTAssertEqual(.completed, XCTWaiter().wait(for: [logRequestExpectation], timeout: 3, enforceOrder: false))
209+
XCTAssertEqual(
210+
.completed,
211+
XCTWaiter().wait(for: [logRequestExpectation], timeout: 3, enforceOrder: false)
212+
)
188213
XCTAssertEqual(1, self.logger.logs.count)
189214

190215
let requestInfo = try XCTUnwrap(self.logger.logs[0].request())

0 commit comments

Comments
 (0)