Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions .github/workflows/ios.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,7 @@ jobs:
- name: Rust tests
run: cargo test --package swift_bridge

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

- name: "Install dependencies"
run: ./ci/mac_ci_setup.sh
- name: Run iOS tests (tsan)
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
- name: Run iOS tests
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
verify_ios:
runs-on: ubuntu-latest
needs: ["macos_tsan", "swift_hello_world", "unit_tests"]
needs: ["tests", "swift_hello_world", "unit_tests"]
if: always()
steps:
# Checkout repo to Github Actions runner
Expand All @@ -120,5 +118,5 @@ jobs:
with:
fetch-depth: 1
- run: |
./ci/check_result.sh ${{ needs.macos_tsan.result }} \
./ci/check_result.sh ${{ needs.tests.result }} \
&& ./ci/check_result.sh ${{ needs.swift_hello_world.result }}
8 changes: 4 additions & 4 deletions test/platform/swift/benchmark/ClockTimeProfiler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ final class LoggerColdConfigurationBenchmark: AnyBenchmark {
}

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

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

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

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

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

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

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

Expand Down
13 changes: 3 additions & 10 deletions test/platform/swift/unit_integration/core/ConfigurationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,13 @@ final class ConfigurationTests: XCTestCase {
}

func testConfigurationSimple() throws {
Logger.start(
withAPIKey: "api_key",
sessionStrategy: .fixed()
)
self.startLoggerWithIsolatedDirectory(apiKey: "api_key")

XCTAssertNotNil(Logger.getShared())
}

func testConfigurationDefault() throws {
Logger.start(
withAPIKey: "api_key",
sessionStrategy: .fixed()
)
self.startLoggerWithIsolatedDirectory(apiKey: "api_key")

XCTAssertNotNil(Logger.getShared())
}
Expand All @@ -48,8 +42,7 @@ final class ConfigurationTests: XCTestCase {
}

func testLoggerRootPath() throws {
let tempDir = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.appendingPathComponent(UUID().uuidString)
let tempDir = self.makeTemporaryLoggerDirectory()

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ final class LoggerSharedTests: XCTestCase {
}

let integrator = try XCTUnwrap(
Logger.start(
withAPIKey: "foo",
sessionStrategy: .fixed()
)
self.startLoggerWithIsolatedDirectory(apiKey: "foo")
)

integrator.enableIntegrations([integration])
Expand Down
11 changes: 3 additions & 8 deletions test/platform/swift/unit_integration/core/SessionURLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ final class SessionURLTests: XCTestCase {
}

func testDefaultSessionUrl() throws {
Logger.start(
withAPIKey: "api_key",
sessionStrategy: .fixed(),
configuration: .init()
)
self.startLoggerWithIsolatedDirectory(apiKey: "api_key")
let sessionID = try XCTUnwrap(Logger.sessionID)
XCTAssertEqual(Logger.sessionURL, "https://timeline.bitdrift.io/s/\(sessionID)?utm_source=sdk")
}
Expand Down Expand Up @@ -77,9 +73,8 @@ final class SessionURLTests: XCTestCase {
}

private func configureLogger(apiURL: String) throws {
Logger.start(
withAPIKey: "api_key",
sessionStrategy: .fixed(),
self.startLoggerWithIsolatedDirectory(
apiKey: "api_key",
configuration: .init(apiURL: try XCTUnwrap(URL(string: apiURL)))
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,53 @@
@testable import Capture
import CapturePassable
import Difference
import Foundation
import XCTest

extension XCTestCase {
/// Creates a unique temporary directory for logger tests.
///
/// This helper prevents "Failed to acquire directory lock" errors when multiple tests
/// create loggers in quick succession by giving each logger instance its own isolated directory.
///
/// - returns: A unique temporary directory URL
func makeTemporaryLoggerDirectory() -> URL {
return FileManager.default.temporaryDirectory
.appendingPathComponent("bitdrift_test_\(UUID().uuidString)")
}

/// Starts a logger with a unique temporary directory to avoid directory lock conflicts.
///
/// This is a convenience wrapper around `Logger.start()` that automatically provides
/// a unique directory for each test invocation.
///
/// - parameter apiKey: The API key for the logger
/// - parameter sessionStrategy: The session strategy to use
/// - parameter configuration: Optional configuration (rootFileURL will be overridden)
/// - parameter fieldProviders: Optional field providers
/// - parameter dateProvider: Optional date provider
///
/// - returns: A LoggerIntegrator instance, or nil if logger creation failed
@discardableResult
func startLoggerWithIsolatedDirectory(
apiKey: String = "test_api_key",
sessionStrategy: SessionStrategy = .fixed(),
configuration: Configuration = .init(),
fieldProviders: [FieldProvider] = [],
dateProvider: DateProvider? = nil
) -> LoggerIntegrator? {
var config = configuration
config.rootFileURL = makeTemporaryLoggerDirectory()

return Logger.start(
withAPIKey: apiKey,
sessionStrategy: sessionStrategy,
configuration: config,
fieldProviders: fieldProviders,
dateProvider: dateProvider
)
}

func assertEqual(
_ fields1: [String: String],
_ fields2: Fields?,
Expand Down
1 change: 1 addition & 0 deletions test/platform/swift/unit_integration/integrations/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ bitdrift_mobile_swift_test(
srcs = glob(["**/*.swift"]),
repository = "@capture",
tags = ["macos_only"],
use_test_host = True,
visibility = ["//visibility:public"],
deps = [
"//platform/swift/source:ios_lib",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ import CaptureMocks
import Foundation
import XCTest

// MARK: - Test Helpers

extension XCTestCase {
/// Creates a unique temporary directory for logger tests to avoid directory lock conflicts.
///
/// - returns: A unique temporary directory URL
fileprivate func makeTemporaryLoggerDirectory() -> URL {
return FileManager.default.temporaryDirectory
.appendingPathComponent("bitdrift_test_\(UUID().uuidString)")
}
}

// swiftlint:disable file_length
private final class URLSessionIncompleteDelegate: NSObject, URLSessionTaskDelegate {
var didCompleteExpectation: XCTestExpectation?
Expand All @@ -23,7 +35,10 @@ private final class URLSessionIncompleteDelegate: NSObject, URLSessionTaskDelega
private final class URLSessionCustomDelegate: NSObject, URLSessionDelegate {
var didReceiveChallenge: XCTestExpectation?

func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge
) async -> (URLSession.AuthChallengeDisposition, URLCredential?) {
self.didReceiveChallenge?.fulfill()
return (.performDefaultHandling, nil)
}
Expand Down Expand Up @@ -75,8 +90,13 @@ final class URLSessionIntegrationTests: XCTestCase {
URLSessionIntegration.shared.disableURLSessionTaskSwizzling()

Logger.resetShared(logger: self.logger)

Logger
.start(withAPIKey: "123", sessionStrategy: .fixed())?
.start(
withAPIKey: "123",
sessionStrategy: .fixed(),
configuration: .init(rootFileURL: self.makeTemporaryLoggerDirectory())
)?
.enableIntegrations([.urlSession()], disableSwizzling: !swizzle)
}

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

let logRequestExpectation = self.expectation(description: "request logged")
let logRequestExpectation = self.expectation(
description: "request logged"
)
self.logger.logRequestExpectation = logRequestExpectation
task.resume()

XCTAssertEqual(.completed, XCTWaiter().wait(for: [logRequestExpectation], timeout: 3, enforceOrder: false))
XCTAssertEqual(
.completed,
XCTWaiter().wait(for: [logRequestExpectation], timeout: 3, enforceOrder: false)
)
XCTAssertEqual(1, self.logger.logs.count)

let requestInfo = try XCTUnwrap(self.logger.logs[0].request())
Expand Down
Loading