diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index 7b787fedbfb..f6cdaf630ed 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -650,6 +650,7 @@ 84281C472A57905700EE88F2 /* SentrySample.m in Sources */ = {isa = PBXBuildFile; fileRef = 84281C452A57905700EE88F2 /* SentrySample.m */; }; 84281C622A579D0700EE88F2 /* SentryProfilerMocksSwiftCompatible.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84281C4D2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.mm */; }; 84281C632A579D0700EE88F2 /* SentryProfilerMocks.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84281C492A57933600EE88F2 /* SentryProfilerMocks.mm */; }; + 842D53102DA5A52B00D3528B /* SentryProfiledTracerConcurrencyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842D530F2DA5A52B00D3528B /* SentryProfiledTracerConcurrencyTests.swift */; }; 84302A812B5767A50027A629 /* SentryLaunchProfiling.m in Sources */ = {isa = PBXBuildFile; fileRef = 84302A7F2B5767A50027A629 /* SentryLaunchProfiling.m */; }; 8431D4552BE1745A009EAEC1 /* SentryProfileTestFixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8431D4522BE1741E009EAEC1 /* SentryProfileTestFixture.swift */; }; 8431D4562BE1745F009EAEC1 /* SentryContinuousProfilerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8446F5182BE172290040D57E /* SentryContinuousProfilerTests.swift */; }; @@ -1792,6 +1793,8 @@ 84281C4C2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryProfilerMocksSwiftCompatible.h; sourceTree = ""; }; 84281C4D2A579A0C00EE88F2 /* SentryProfilerMocksSwiftCompatible.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SentryProfilerMocksSwiftCompatible.mm; sourceTree = ""; }; 84281C642A57D36100EE88F2 /* SentryProfilerState+ObjCpp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryProfilerState+ObjCpp.h"; path = "../include/SentryProfilerState+ObjCpp.h"; sourceTree = ""; }; + 842D530E2DA5A45C00D3528B /* SentryProfiledTracerConcurrency+Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryProfiledTracerConcurrency+Test.h"; sourceTree = ""; }; + 842D530F2DA5A52B00D3528B /* SentryProfiledTracerConcurrencyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryProfiledTracerConcurrencyTests.swift; sourceTree = ""; }; 84302A7F2B5767A50027A629 /* SentryLaunchProfiling.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryLaunchProfiling.m; sourceTree = ""; }; 8431D4522BE1741E009EAEC1 /* SentryProfileTestFixture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryProfileTestFixture.swift; sourceTree = ""; }; 8431D4572BE175A1009EAEC1 /* SentryContinuousProfiler+Test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentryContinuousProfiler+Test.h"; sourceTree = ""; }; @@ -3627,7 +3630,6 @@ 8454CF8B293EAF9A006AC140 /* SentryMetricProfiler.mm */, 8459FCBD2BD73E810038E9C9 /* SentryProfilerSerialization.h */, 8459FCBF2BD73EB20038E9C9 /* SentryProfilerSerialization.mm */, - 8459FCC12BD73EEF0038E9C9 /* SentryProfilerSerialization+Test.h */, 84AF45A429A7FFA500FBB177 /* SentryProfiledTracerConcurrency.h */, 84AF45A529A7FFA500FBB177 /* SentryProfiledTracerConcurrency.mm */, 840B7EF22BBF83DF008B8120 /* SentryProfiler+Private.h */, @@ -3667,6 +3669,8 @@ 8431EFDB29B27B3D00D8DC56 /* SentryProfilerTests */ = { isa = PBXGroup; children = ( + 8459FCC12BD73EEF0038E9C9 /* SentryProfilerSerialization+Test.h */, + 842D530E2DA5A45C00D3528B /* SentryProfiledTracerConcurrency+Test.h */, 849472802971C107002603DE /* SentrySystemWrapperTests.swift */, 849472822971C2CD002603DE /* SentryNSProcessInfoWrapperTests.swift */, 849472842971C41A002603DE /* SentryNSTimerFactoryTest.swift */, @@ -3680,6 +3684,7 @@ 845CEAEE2D83F79500B6B325 /* SentryProfilingPublicAPITests.swift */, 8419C0C328C1889D001C8259 /* SentryTraceProfilerTests.swift */, 8446F5182BE172290040D57E /* SentryContinuousProfilerTests.swift */, + 842D530F2DA5A52B00D3528B /* SentryProfiledTracerConcurrencyTests.swift */, 8431D4522BE1741E009EAEC1 /* SentryProfileTestFixture.swift */, ); path = SentryProfilerTests; @@ -5560,6 +5565,7 @@ 845CEB172D8A979700B6B325 /* SentryAppStartProfilingConfigurationTests.swift in Sources */, 8431EFE529B27BAD00D8DC56 /* SentryNSProcessInfoWrapperTests.swift in Sources */, 8431EFDE29B27B5300D8DC56 /* SentryTraceProfilerTests.swift in Sources */, + 842D53102DA5A52B00D3528B /* SentryProfiledTracerConcurrencyTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Sources/Sentry/Profiling/SentryProfiledTracerConcurrency.mm b/Sources/Sentry/Profiling/SentryProfiledTracerConcurrency.mm index ba6ca8ea8f4..227fdd7774b 100644 --- a/Sources/Sentry/Profiling/SentryProfiledTracerConcurrency.mm +++ b/Sources/Sentry/Profiling/SentryProfiledTracerConcurrency.mm @@ -49,7 +49,7 @@ static NSMutableDictionary *_gTracersToProfilers; -static unsigned int _gInFlightRootSpans = 0; +unsigned int _gInFlightRootSpans = 0; namespace { diff --git a/Tests/SentryProfilerTests/SentryContinuousProfilerTests.swift b/Tests/SentryProfilerTests/SentryContinuousProfilerTests.swift index a756bd5ec7b..8090fd3700a 100644 --- a/Tests/SentryProfilerTests/SentryContinuousProfilerTests.swift +++ b/Tests/SentryProfilerTests/SentryContinuousProfilerTests.swift @@ -118,6 +118,7 @@ final class SentryContinuousProfilerTests: XCTestCase { // test that when stop is called, the profiler runs to the end of the last // chunk and transmits that before stopping func testStoppingProfilerTransmitsLastFullChunk() throws { + fixture.givenSdkWithHub() SentryContinuousProfiler.start() XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) @@ -142,6 +143,7 @@ final class SentryContinuousProfilerTests: XCTestCase { } func testChunkSerializationAfterBufferInterval() throws { + fixture.givenSdkWithHub() SentryContinuousProfiler.start() XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) @@ -202,6 +204,7 @@ private extension SentryContinuousProfilerTests { } func performContinuousProfilingTest(expectedEnvironment: String = kSentryDefaultEnvironment) throws { + fixture.givenSdkWithHub() XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) SentryContinuousProfiler.start() XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) @@ -225,15 +228,13 @@ private extension SentryContinuousProfilerTests { try runTestPart(expectedAddresses: [0x4, 0x5, 0x6], mockMetrics: SentryProfileTestFixture.MockMetric(cpuUsage: 1.23, memoryFootprint: 456, cpuEnergyUsage: 7), countMetricsReadingAtProfileStart: false) try runTestPart(expectedAddresses: [0x7, 0x8, 0x9], mockMetrics: SentryProfileTestFixture.MockMetric(cpuUsage: 9.87, memoryFootprint: 654, cpuEnergyUsage: 3), countMetricsReadingAtProfileStart: false) - XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) SentryContinuousProfiler.stop() try assertContinuousProfileStoppage() } func assertContinuousProfileStoppage() throws { XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) - fixture.currentDateProvider.advance(by: 60) - try fixture.timeoutTimerFactory.check() + try fixture.allowContinuousProfilerToStop() XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) } diff --git a/Tests/SentryProfilerTests/SentryProfileTestFixture.swift b/Tests/SentryProfilerTests/SentryProfileTestFixture.swift index 3abdb2028e7..a54c374217b 100644 --- a/Tests/SentryProfilerTests/SentryProfileTestFixture.swift +++ b/Tests/SentryProfilerTests/SentryProfileTestFixture.swift @@ -15,12 +15,13 @@ class SentryProfileTestFixture { private static let dsnAsString = TestConstants.dsnAsString(username: "SentryProfileTestFixture") let options: Options - let client: TestClient? - let hub: SentryHub + lazy var client = TestClient(options: options) + lazy var hub = SentryHub(client: client, andScope: scope) let scope = Scope() let message = "some message" let transactionName = "Some Transaction" let transactionOperation = "Some Operation" + let rootSpanDescription = "Some Root Span" let fixedRandomValue = 0.5 @@ -31,7 +32,8 @@ class SentryProfileTestFixture { var timeoutTimerFactory: TestSentryNSTimerFactory let dispatchQueueWrapper = TestSentryDispatchQueueWrapper() let notificationCenter = TestNSNotificationCenterWrapper() - + lazy var sessionTracker = SessionTracker(options: options, notificationCenter: notificationCenter) + let currentDateProvider = TestCurrentDateProvider() #if !os(macOS) @@ -75,10 +77,6 @@ class SentryProfileTestFixture { options = Options() options.dsn = SentryProfileTestFixture.dsnAsString options.debug = true - client = TestClient(options: options) - hub = SentryHub(client: client, andScope: scope) - hub.bindClient(client) - SentrySDK.setCurrentHub(hub) options.profilesSampleRate = 1.0 options.tracesSampleRate = 1.0 @@ -95,12 +93,19 @@ class SentryProfileTestFixture { } /// Advance the mock date provider, start a new transaction and return its handle. - func newTransaction(testingAppLaunchSpans: Bool = false, automaticTransaction: Bool = false, idleTimeout: TimeInterval? = nil) throws -> SentryTracer { + func newTransaction(testingAppLaunchSpans: Bool = false, automaticTransaction: Bool = false, idleTimeout: TimeInterval? = nil, rootSpan: Bool = false) throws -> SentryTracer { let operation = testingAppLaunchSpans ? SentrySpanOperationUiLoad : transactionOperation - + + let context: TransactionContext + if rootSpan { + context = TransactionContext(trace: SentryId(), spanId: SpanId(), parentId: nil, operation: operation, spanDescription: rootSpanDescription, sampled: .yes) + } else { + context = TransactionContext(name: transactionName, operation: operation) + } + if automaticTransaction { return hub.startTransaction( - with: TransactionContext(name: transactionName, operation: operation), + with: context, bindToScope: false, customSamplingContext: [:], configuration: SentryTracerConfiguration(block: { @@ -400,6 +405,22 @@ class SentryProfileTestFixture { sdkStartTimestamp: appStart, didFinishLaunchingTimestamp: didFinishLaunching) } #endif // !os(macOS) + + func givenSdkWithHub() { + hub.bindClient(client) + SentrySDK.setCurrentHub(hub) + sentry_sdkInitProfilerTasks(options, hub) + } + + func stopContinuousProfiler() throws { + SentrySDK.stopProfiler() + try allowContinuousProfilerToStop() + } + + func allowContinuousProfilerToStop() throws { + currentDateProvider.advance(by: 60) + try timeoutTimerFactory.check() + } } #endif // os(iOS) || os(macOS) || targetEnvironment(macCatalyst) diff --git a/Tests/SentryProfilerTests/SentryProfiledTracerConcurrency+Test.h b/Tests/SentryProfilerTests/SentryProfiledTracerConcurrency+Test.h new file mode 100644 index 00000000000..6ac7f1f7697 --- /dev/null +++ b/Tests/SentryProfilerTests/SentryProfiledTracerConcurrency+Test.h @@ -0,0 +1,13 @@ +#import "SentryProfilingConditionals.h" + +#if SENTRY_TARGET_PROFILING_SUPPORTED + +# if defined(SENTRY_TEST) || defined(SENTRY_TEST_CI) || defined(DEBUG) + +# import "SentryDefines.h" + +SENTRY_EXTERN unsigned int _gInFlightRootSpans; + +# endif // defined(SENTRY_TEST) || defined(SENTRY_TEST_CI) || defined(DEBUG) + +#endif // SENTRY_TARGET_PROFILING_SUPPORTED diff --git a/Tests/SentryProfilerTests/SentryProfiledTracerConcurrencyTests.swift b/Tests/SentryProfilerTests/SentryProfiledTracerConcurrencyTests.swift new file mode 100644 index 00000000000..3c6f6bf93b9 --- /dev/null +++ b/Tests/SentryProfilerTests/SentryProfiledTracerConcurrencyTests.swift @@ -0,0 +1,245 @@ +import SentryTestUtils +import XCTest + +#if os(iOS) || os(macOS) || targetEnvironment(macCatalyst) +final class SentryProfiledTracerConcurrencyTests: XCTestCase { + private var fixture: SentryProfileTestFixture! + + override func setUp() { + super.setUp() + fixture = SentryProfileTestFixture() + } + + override func tearDown() { + super.tearDown() + clearTestState() + } +} + +// MARK: Trace lifecycle UI Profiling +extension SentryProfiledTracerConcurrencyTests { + func testLaunchProfileStartAndStop() throws { + // Arrange + fixture.options.profilesSampleRate = nil + fixture.options.configureProfiling = { + $0.lifecycle = .trace + $0.sessionSampleRate = 1 + $0.profileAppStarts = true + } + fixture.givenSdkWithHub() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + _sentry_nondeduplicated_startLaunchProfile() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 1) + XCTAssertTrue(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + sentry_stopAndDiscardLaunchProfileTracer() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + + // Act + try fixture.allowContinuousProfilerToStop() + + // Assert + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + } + + func testLaunchProfileStartWithAdditionalSpan() throws { + // Arrange + fixture.options.profilesSampleRate = nil + fixture.options.configureProfiling = { + $0.lifecycle = .trace + $0.sessionSampleRate = 1 + $0.profileAppStarts = true + } + fixture.givenSdkWithHub() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + _sentry_nondeduplicated_startLaunchProfile() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 1) + XCTAssertTrue(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + let span = try fixture.newTransaction(rootSpan: true) + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 2) + XCTAssertTrue(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + span.finish() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 1) + XCTAssertTrue(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + XCTAssertNotNil(sentry_launchTracer) + sentry_stopAndDiscardLaunchProfileTracer() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + + // Act + try fixture.allowContinuousProfilerToStop() + + // Assert + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + } + + class ProfiledViewController: UIViewController {} + + func testLaunchProfileStartAndStopWithTTFD() throws { + // Arrange + fixture.options.profilesSampleRate = nil + fixture.options.configureProfiling = { + $0.lifecycle = .trace + $0.sessionSampleRate = 1 + $0.profileAppStarts = true + } + fixture.options.enableTimeToFullDisplayTracing = true + fixture.options.add(inAppInclude: "uikitcore") + SentryDependencyContainer.sharedInstance() + SentrySDK.start(options: fixture.options) + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + _sentry_nondeduplicated_startLaunchProfile() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 1) + XCTAssertTrue(SentryContinuousProfiler.isCurrentlyProfiling()) + + let uivc = ProfiledViewController(nibName: nil, bundle: nil) + uivc.loadView() + uivc.viewWillAppear(false) + fixture.displayLinkWrapper.call() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 2) + + // Act + sentry_stopAndDiscardLaunchProfileTracer() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 1) + + // Act + uivc.viewDidAppear(false) + fixture.displayLinkWrapper.call() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + + // Act + try fixture.allowContinuousProfilerToStop() + + // Assert + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + } + + func testStartAndStopTransaction() throws { + // Arrange + fixture.options.profilesSampleRate = nil + fixture.options.configureProfiling = { + $0.lifecycle = .trace + $0.sessionSampleRate = 1 + $0.profileAppStarts = true + } + fixture.givenSdkWithHub() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + let span = try fixture.newTransaction() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 1) + XCTAssertTrue(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + span.finish() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + + // Act + try fixture.allowContinuousProfilerToStop() + + // Assert + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + } + + func testManualLifecycleDoesntTrackLaunchWithSpan() throws { + // Arrange + fixture.options.profilesSampleRate = nil + fixture.options.configureProfiling = { + $0.lifecycle = .manual + $0.sessionSampleRate = 1 + $0.profileAppStarts = true + } + fixture.givenSdkWithHub() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + _sentry_nondeduplicated_startLaunchProfile() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + XCTAssertTrue(SentryContinuousProfiler.isCurrentlyProfiling()) + XCTAssertNil(sentry_launchTracer) + } + + func testManualLifecycleDoesntTrackRootSpan() throws { + // Arrange + fixture.options.profilesSampleRate = nil + fixture.options.configureProfiling = { + $0.lifecycle = .manual + $0.sessionSampleRate = 1 + $0.profileAppStarts = true + } + fixture.givenSdkWithHub() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + let span = try fixture.newTransaction() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + + // Act + span.finish() + + // Assert + XCTAssertEqual(_gInFlightRootSpans, 0) + XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) + } +} + +#endif // os(iOS) || os(macOS) || targetEnvironment(macCatalyst) diff --git a/Sources/Sentry/Profiling/SentryProfilerSerialization+Test.h b/Tests/SentryProfilerTests/SentryProfilerSerialization+Test.h similarity index 100% rename from Sources/Sentry/Profiling/SentryProfilerSerialization+Test.h rename to Tests/SentryProfilerTests/SentryProfilerSerialization+Test.h diff --git a/Tests/SentryProfilerTests/SentryProfilingPublicAPITests.swift b/Tests/SentryProfilerTests/SentryProfilingPublicAPITests.swift index 52d032fda70..7111fc13769 100644 --- a/Tests/SentryProfilerTests/SentryProfilingPublicAPITests.swift +++ b/Tests/SentryProfilerTests/SentryProfilingPublicAPITests.swift @@ -6,51 +6,15 @@ import XCTest // swiftlint:disable file_length class SentryProfilingPublicAPITests: XCTestCase { - private class Fixture { - let options: Options = { - let options = Options.noIntegrations() - options.dsn = TestConstants.dsnAsString(username: "SentrySDKTests") - options.releaseName = "1.0.0" - return options - }() - - let scope = { - let scope = Scope() - scope.setTag(value: "value", key: "key") - return scope - }() - - var sessionTracker: SessionTracker? - - var _random: TestRandom = TestRandom(value: 0.5) - var random: TestRandom { - get { - _random - } - set(newValue) { - _random = newValue - SentryDependencyContainer.sharedInstance().random = newValue - } - } - - let currentDate = TestCurrentDateProvider() - lazy var timerFactory = TestSentryNSTimerFactory(currentDateProvider: currentDate) - lazy var client = TestClient(options: options)! - lazy var hub = SentryHub(client: client, andScope: scope) - } - - private let fixture = Fixture() + private var fixture: SentryProfileTestFixture! override func setUp() { super.setUp() - SentryDependencyContainer.sharedInstance().timerFactory = fixture.timerFactory - SentryDependencyContainer.sharedInstance().dateProvider = fixture.currentDate + fixture = SentryProfileTestFixture() } - override func tearDown() { - super.tearDown() - - givenSdkWithHubButNoClient() + override func tearDownWithError() throws { + try super.tearDownWithError() if let autoSessionTracking = SentrySDK.currentHub().installedIntegrations().first(where: { it in it is SentryAutoSessionTrackingIntegration @@ -139,7 +103,7 @@ extension SentryProfilingPublicAPITests { } func testStartingContinuousProfilerV1WithSampleRateZero() throws { - givenSdkWithHub() + fixture.givenSdkWithHub() fixture.options.profilesSampleRate = 0 XCTAssertEqual(try XCTUnwrap(fixture.options.profilesSampleRate).doubleValue, 0) @@ -150,7 +114,8 @@ extension SentryProfilingPublicAPITests { } func testStartingContinuousProfilerV1WithSampleRateNil() throws { - givenSdkWithHub() + fixture.options.profilesSampleRate = nil + fixture.givenSdkWithHub() // nil is the default initial value for profilesSampleRate, so we don't have to explicitly set it on the fixture XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) @@ -158,11 +123,11 @@ extension SentryProfilingPublicAPITests { XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) // clean up - try stopProfiler() + try fixture.stopContinuousProfiler() } func testNotStartingContinuousProfilerV1WithSampleRateBlock() throws { - givenSdkWithHub() + fixture.givenSdkWithHub() fixture.options.profilesSampler = { _ in 0 } XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) @@ -171,7 +136,7 @@ extension SentryProfilingPublicAPITests { } func testNotStartingContinuousProfilerV1WithSampleRateNonZero() throws { - givenSdkWithHub() + fixture.givenSdkWithHub() fixture.options.profilesSampleRate = 1 XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) @@ -180,11 +145,12 @@ extension SentryProfilingPublicAPITests { } func testStartingAndStoppingContinuousProfilerV1() throws { - givenSdkWithHub() + fixture.options.profilesSampleRate = nil + fixture.givenSdkWithHub() SentrySDK.startProfiler() XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) - try stopProfiler() + try fixture.stopContinuousProfiler() XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) } @@ -195,7 +161,7 @@ extension SentryProfilingPublicAPITests { } func testStartingContinuousProfilerV1AfterStoppingSDK() { - givenSdkWithHub() + fixture.givenSdkWithHub() SentrySDK.close() SentrySDK.startProfiler() XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) @@ -308,10 +274,11 @@ extension SentryProfilingPublicAPITests { func testManuallyStartingAndStoppingContinuousProfilerV2Sampled() throws { // Arrange + fixture.options.profilesSampleRate = nil fixture.options.configureProfiling = { $0.sessionSampleRate = 1 } - givenSdkWithHub() + fixture.givenSdkWithHub() // Act SentrySDK.startProfiler() @@ -320,7 +287,7 @@ extension SentryProfilingPublicAPITests { XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) // Act - try stopProfilerV2() + try fixture.stopContinuousProfiler() // Assert XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) @@ -331,7 +298,7 @@ extension SentryProfilingPublicAPITests { fixture.options.configureProfiling = { $0.sessionSampleRate = 0 } - givenSdkWithHub() + fixture.givenSdkWithHub() // Act SentrySDK.startProfiler() @@ -352,7 +319,7 @@ extension SentryProfilingPublicAPITests { func testStartingContinuousProfilerV2AfterStoppingSDKDoesNotStartProfiler() { // Arrange - givenSdkWithHub() + fixture.givenSdkWithHub() // Act SentrySDK.close() @@ -365,7 +332,7 @@ extension SentryProfilingPublicAPITests { func testStartingContinuousProfilerV2WithoutContinuousProfilingEnabledDoesNotStartProfiler() { // Arrange fixture.options.profilesSampleRate = 1 - givenSdkWithHub() + fixture.givenSdkWithHub() // Act SentrySDK.startProfiler() @@ -379,7 +346,7 @@ extension SentryProfilingPublicAPITests { fixture.options.configureProfiling = { $0.lifecycle = .trace } - givenSdkWithHub() + fixture.givenSdkWithHub() // Act SentrySDK.startProfiler() @@ -401,9 +368,8 @@ extension SentryProfilingPublicAPITests { $0.sessionSampleRate = 0 $0.lifecycle = .trace } - fixture.options.tracesSampleRate = 1 - givenSdkWithHub() - fixture.currentDate.advance(by: 1) + fixture.givenSdkWithHub() + fixture.currentDateProvider.advance(by: 1) // Act span = SentrySDK.startTransaction(name: "test", operation: "test") @@ -414,24 +380,24 @@ extension SentryProfilingPublicAPITests { func testStoppingContinuousProfilerV2WithTraceLifeCycleDoesNotStopProfiler() throws { // Arrange + fixture.options.profilesSampleRate = nil fixture.options.configureProfiling = { $0.lifecycle = .trace $0.sessionSampleRate = 1 } - fixture.options.tracesSampleRate = 1 - givenSdkWithHub() - fixture.currentDate.advance(by: 1) + fixture.givenSdkWithHub() + fixture.currentDateProvider.advance(by: 1) let span = SentrySDK.startTransaction(name: "test", operation: "test") XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) // Act - using manual stop method is a no-op in trace profile lifecycle mode - try stopProfilerV2() + try fixture.stopContinuousProfiler() // Assert XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) // Arrange - fixture.currentDate.advance(by: 1) + fixture.currentDateProvider.advance(by: 1) // Act - the current profile chunk will automatically finish span.finish() @@ -440,8 +406,8 @@ extension SentryProfilingPublicAPITests { XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) // Arrange - simulate chunk completion - fixture.currentDate.advance(by: 60) - try fixture.timerFactory.check() + fixture.currentDateProvider.advance(by: 60) + try fixture.timeoutTimerFactory.check() // Assert - profiler is stopped XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) @@ -449,8 +415,7 @@ extension SentryProfilingPublicAPITests { func testStartingTransactionWithoutTraceLifecycleDoesNotStartContinuousProfilerV2() { //arrange - fixture.options.tracesSampleRate = 1 - givenSdkWithHub() + fixture.givenSdkWithHub() // Act SentrySDK.startTransaction(name: "test", operation: "test") @@ -472,7 +437,7 @@ extension SentryProfilingPublicAPITests { fixture.options.configureProfiling = { $0.lifecycle = .trace } - givenSdkWithHub() + fixture.givenSdkWithHub() // Act - hold a reference to the tracer so it doesn't dealloc, which tries to clean up any existing profilers span = SentrySDK.startTransaction(name: "test", operation: "test") @@ -485,17 +450,18 @@ extension SentryProfilingPublicAPITests { defer { // clean up do { - try stopProfilerV2() + try fixture.stopContinuousProfiler() } catch { XCTFail("Stop profiler process threw error: \(error)") } } // Arrange + fixture.options.profilesSampleRate = nil fixture.options.configureProfiling = { $0.sessionSampleRate = 1 } - givenSdkWithHub() + fixture.givenSdkWithHub() // Act SentrySDK.startProfiler() @@ -509,7 +475,7 @@ extension SentryProfilingPublicAPITests { fixture.options.configureProfiling = { $0.sessionSampleRate = 0 } - givenSdkWithHub() + fixture.givenSdkWithHub() // Act SentrySDK.startProfiler() @@ -531,8 +497,7 @@ extension SentryProfilingPublicAPITests { $0.sessionSampleRate = 0 $0.lifecycle = .trace } - fixture.options.tracesSampleRate = 1 - givenSdkWithHub() + fixture.givenSdkWithHub() // Act span = SentrySDK.startTransaction(name: "test", operation: "test") @@ -555,7 +520,7 @@ extension SentryProfilingPublicAPITests { $0.lifecycle = .trace } fixture.options.tracesSampleRate = 0 - givenSdkWithHub() + fixture.givenSdkWithHub() // Act span = SentrySDK.startTransaction(name: "test", operation: "test") @@ -570,9 +535,9 @@ extension SentryProfilingPublicAPITests { defer { // clean up span.finish() - fixture.currentDate.advance(by: 60) + fixture.currentDateProvider.advance(by: 60) do { - try fixture.timerFactory.check() + try fixture.timeoutTimerFactory.check() } catch { XCTFail("Checking timer state threw error: \(error)") } @@ -580,12 +545,12 @@ extension SentryProfilingPublicAPITests { } // Arrange + fixture.options.profilesSampleRate = nil fixture.options.configureProfiling = { $0.sessionSampleRate = 1 $0.lifecycle = .trace } - fixture.options.tracesSampleRate = 1 - givenSdkWithHub() + fixture.givenSdkWithHub() // Act span = SentrySDK.startTransaction(name: "test", operation: "test") @@ -608,7 +573,7 @@ extension SentryProfilingPublicAPITests { $0.lifecycle = .trace } fixture.options.tracesSampleRate = 0 - givenSdkWithHub() + fixture.givenSdkWithHub() // Act span = SentrySDK.startTransaction(name: "test", operation: "test") @@ -618,17 +583,16 @@ extension SentryProfilingPublicAPITests { } #if !os(macOS) - func testSessionSampleRateReevaluationOnAppBecomingActive() { + func testContinuousProfileV2SessionSampleRateReevaluationOnAppBecomingActive() { // Arrange + fixture.options.profilesSampleRate = nil fixture.options.configureProfiling = { $0.sessionSampleRate = 0.5 $0.lifecycle = .manual } - fixture.random = TestRandom(value: 0) - let nc = SentryDependencyContainer.sharedInstance().notificationCenterWrapper - fixture.sessionTracker = SessionTracker(options: fixture.options, notificationCenter: nc) + SentryDependencyContainer.sharedInstance().random = TestRandom(value: 0) fixture.sessionTracker?.start() - givenSdkWithHub() + fixture.givenSdkWithHub() // Act SentrySDK.startProfiler() @@ -637,14 +601,14 @@ extension SentryProfilingPublicAPITests { XCTAssert(SentryContinuousProfiler.isCurrentlyProfiling()) // Act - nc.post(Notification(name: UIApplication.willResignActiveNotification)) + fixture.notificationCenter.post(Notification(name: UIApplication.willResignActiveNotification)) XCTAssertFalse(SentryContinuousProfiler.isCurrentlyProfiling()) // Arrange - fixture.random = TestRandom(value: 1) + SentryDependencyContainer.sharedInstance().random = TestRandom(value: 1) // Act - nc.post(Notification(name: UIApplication.didBecomeActiveNotification)) + fixture.notificationCenter.post(Notification(name: UIApplication.didBecomeActiveNotification)) SentrySDK.startProfiler() // Assert @@ -653,29 +617,4 @@ extension SentryProfilingPublicAPITests { #endif // !os(macOS) } -private extension SentryProfilingPublicAPITests { - func givenSdkWithHub() { - SentrySDK.setCurrentHub(fixture.hub) - SentrySDK.setStart(fixture.options) - sentry_sdkInitProfilerTasks(fixture.options, fixture.hub) - } - - func givenSdkWithHubButNoClient() { - SentrySDK.setCurrentHub(SentryHub(client: nil, andScope: nil)) - SentrySDK.setStart(fixture.options) - } - - func stopProfiler() throws { - SentrySDK.stopProfiler() - fixture.currentDate.advance(by: 60) - try fixture.timerFactory.check() - } - - func stopProfilerV2() throws { - SentrySDK.stopProfiler() - fixture.currentDate.advance(by: 60) - try fixture.timerFactory.check() - } -} - #endif // os(iOS) || os(macOS) || targetEnvironment(macCatalyst) diff --git a/Tests/SentryTests/SentryTests-Bridging-Header.h b/Tests/SentryTests/SentryTests-Bridging-Header.h index be4c2e50c73..fd04c926924 100644 --- a/Tests/SentryTests/SentryTests-Bridging-Header.h +++ b/Tests/SentryTests/SentryTests-Bridging-Header.h @@ -30,6 +30,7 @@ # import "SentryContinuousProfiler.h" # import "SentryLaunchProfiling+Tests.h" # import "SentryMetricProfiler.h" +# import "SentryProfiledTracerConcurrency+Test.h" # import "SentryProfiler+Private.h" # import "SentryProfilerDefines.h" # import "SentryProfilerMocksSwiftCompatible.h"