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
11 changes: 10 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ jobs:
xcode: ${{matrix.xcode}}
platform: ${{matrix.platform}}
test-destination-os: ${{matrix.test-destination-os || 'latest'}}
timeout: 20
timeout: ${{matrix.timeout || 20}}
device: ${{matrix.device || ''}}
scheme: ${{matrix.scheme || 'Sentry'}}
run_on_cirrus_labs: ${{ !contains(matrix.runs-on, 'macos-') }}
Expand Down Expand Up @@ -370,6 +370,15 @@ jobs:
device: "Apple TV"
scheme: "Sentry"

# visionOS 26
- name: visionOS 26 Sentry
runs-on: macos-26
xcode: "26.1.1"
test-destination-os: "26.1"
platform: "visionOS"
scheme: "Sentry"
timeout: 30

# This check validates that either all unit tests passed or were skipped, which allows us
# to make unit tests a required check with only running the unit tests when required.
# So, we don't have to run unit tests, for example, for Changelog or ReadMe changes.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
#import "SentryDefines.h"

#if TARGET_OS_IOS || TARGET_OS_TV
# define SENTRY_UIKIT_AVAILABLE 1
#else
# define SENTRY_UIKIT_AVAILABLE 0
#endif

#if SENTRY_HAS_UIKIT
# import "SentryAppStartTracker.h"
# import "SentryDefaultUIViewControllerPerformanceTracker.h"
Expand Down
8 changes: 6 additions & 2 deletions SentryTestUtils/Sources/TestDisplayLinkWrapper.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation
@_spi(Private) @testable import Sentry

#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
#if os(iOS) || os(tvOS) || os(visionOS) || targetEnvironment(macCatalyst)

public enum GPUFrame {
case normal
Expand All @@ -18,7 +18,7 @@ public enum FrameRate: UInt64 {
}
}

@_spi(Private) public class TestDisplayLinkWrapper: SentryDisplayLinkWrapper, SentryReplayDisplayLinkWrapper {
@_spi(Private) public class TestDisplayLinkWrapper: SentryDisplayLinkWrapper {
public var target: AnyObject!
public var selector: Selector!
public var currentFrameRate: FrameRate = .low
Expand Down Expand Up @@ -146,3 +146,7 @@ public enum FrameRate: UInt64 {
}

#endif

#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
extension TestDisplayLinkWrapper: SentryReplayDisplayLinkWrapper {}
#endif
18 changes: 18 additions & 0 deletions Tests/SentryTests/Helper/SentryDeviceTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ - (void)testCPUArchitecture
// TODO: create a watch UI test target to test this branch as it cannot run on the watch
// simulator
SENTRY_ASSERT_CONTAINS(arch, @"arm"); // Real Watches
#elif TARGET_OS_VISION
# if TARGET_OS_SIMULATOR
# if TARGET_CPU_ARM64
SENTRY_ASSERT_CONTAINS(arch, @"arm"); // Vision Pro simulator on M1 macs
# elif TARGET_CPU_X86_64
SENTRY_ASSERT_CONTAINS(arch, @"x86"); // Vision Pro simulator on Intel macs
# else
XCTFail(@"Unexpected CPU type on test host.");
# endif // TARGET_CPU_ARM64
# else
SENTRY_ASSERT_CONTAINS(arch, @"arm"); // Real Vision Pro devices
# endif
#else
XCTFail(@"Unexpected device OS");
#endif
Expand Down Expand Up @@ -127,6 +139,8 @@ - (void)testOSName
#elif TARGET_OS_WATCH
// TODO: create a watch UI test target to test this branch
SENTRY_ASSERT_EQUAL(osName, @"watchOS");
#elif TARGET_OS_VISION
SENTRY_ASSERT_EQUAL(osName, @"visionOS");
#else
XCTFail(@"Unexpected device OS");
#endif
Expand Down Expand Up @@ -161,6 +175,8 @@ - (void)testDeviceModel
// TODO: create a watch UI test target to test this branch as it cannot run on the watch
// simulator
SENTRY_ASSERT_CONTAINS(modelName, @"Watch");
#elif TARGET_OS_VISION
SENTRY_ASSERT_CONTAINS(modelName, @"RealityDevice");
#else
XCTFail(@"Unexpected target OS");
#endif
Expand Down Expand Up @@ -205,6 +221,8 @@ - (void)testSimulatedDeviceModel
// TODO: create a watch UI test target to test this branch as it cannot run on the watch
// simulator
SENTRY_ASSERT_CONTAINS(modelName, @"Watch");
# elif TARGET_OS_VISION
SENTRY_ASSERT_CONTAINS(modelName, @"RealityDevice");
# else
XCTFail(@"Unexpected device OS");
# endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class SentryANRTrackingIntegrationTests: SentrySDKIntegrationTestsBase {

let tracker = try XCTUnwrap(Dynamic(sut).tracker.asAnyObject as? SentryANRTracker)

#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
#if os(iOS) || os(tvOS) || os(visionOS) || targetEnvironment(macCatalyst)
XCTAssertTrue(tracker.helper is SentryANRTrackerV2, "Expected SentryANRTrackerV2, but got \(type(of: tracker))")
#else
XCTAssertTrue(tracker.helper is SentryANRTrackerV1, "Expected SentryANRTrackerV1 on macOS, but got \(type(of: tracker))")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import Foundation
@_spi(Private) import SentryTestUtils
import XCTest

#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)

#if os(iOS) || os(tvOS) || os(visionOS) || targetEnvironment(macCatalyst)
class TestDelayedWrapper: SentryDelayedFramesTracker {}

class SentryTimeToDisplayTrackerTest: XCTestCase {
Expand All @@ -16,8 +15,13 @@ class SentryTimeToDisplayTrackerTest: XCTestCase {
let framesTracker: SentryFramesTracker

init() throws {
framesTracker = SentryFramesTracker(displayLinkWrapper: displayLinkWrapper, dateProvider: dateProvider, dispatchQueueWrapper: dispatchQueue,
notificationCenter: TestNSNotificationCenterWrapper(), delayedFramesTracker: TestDelayedWrapper(keepDelayedFramesDuration: 0, dateProvider: dateProvider))
framesTracker = SentryFramesTracker(
displayLinkWrapper: displayLinkWrapper,
dateProvider: dateProvider,
dispatchQueueWrapper: dispatchQueue,
notificationCenter: TestNSNotificationCenterWrapper(),
delayedFramesTracker: TestDelayedWrapper(keepDelayedFramesDuration: 0, dateProvider: dateProvider)
)
SentryDependencyContainer.sharedInstance().framesTracker = framesTracker
framesTracker.start()

Expand Down Expand Up @@ -50,6 +54,9 @@ class SentryTimeToDisplayTrackerTest: XCTestCase {

override func setUpWithError() throws {
try super.setUpWithError()
// Ensure app start measurement is cleared before each test to avoid interference
// from previous tests that might have set it
SentrySDKInternal.setAppStartMeasurement(nil)
fixture = try Fixture()
}

Expand Down Expand Up @@ -574,9 +581,15 @@ class SentryTimeToDisplayTrackerTest: XCTestCase {
XCTAssertEqual(self.fixture.framesTracker.listenersCount, 0, "Frames tracker listener should be removed")
}

private func assertMeasurement(tracer: SentryTracer, name: String, duration: TimeInterval) {
XCTAssertEqual(tracer.measurements[name]?.value, NSNumber(value: duration))
XCTAssertEqual(tracer.measurements[name]?.unit?.unit, "millisecond")
private func assertMeasurement(
tracer: SentryTracer,
name: String,
duration: TimeInterval,
file: StaticString = #file,
line: UInt = #line
) {
XCTAssertEqual(tracer.measurements[name]?.value, NSNumber(value: duration), file: file, line: line)
XCTAssertEqual(tracer.measurements[name]?.unit?.unit, "millisecond", file: file, line: line)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class SentrySessionTrackerTests: XCTestCase {
let client: TestClient!
let sentryCrash: TestSentryCrashWrapper

#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
let _application: TestSentryUIApplication
#else
let _application: TestSentryNSApplication
Expand All @@ -38,7 +38,7 @@ class SentrySessionTrackerTests: XCTestCase {

sentryCrash = TestSentryCrashWrapper(processInfoWrapper: ProcessInfo.processInfo)

#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
_application = TestSentryUIApplication()
_application.unsafeApplicationState = .inactive
#else
Expand Down Expand Up @@ -537,7 +537,7 @@ class SentrySessionTrackerTests: XCTestCase {
// MARK: - Helpers

private func startSutInAppDelegate() {
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
// The Sentry SDK should be initialized in the `UIAppDelegate.didFinishLaunchingWithOptions`
// At this point the application state is `inactive`, because the app just launched but did not
// become the active app yet.
Expand Down Expand Up @@ -565,7 +565,7 @@ class SentrySessionTrackerTests: XCTestCase {
// We remove the observers, to ensure future notifications are not handled.
sut.removeObservers()

#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
// When the app stops, the app state is `inactive`.
// This can be observed by viewing the application state in `UIAppDelegate.applicationDidEnterBackground`.
fixture._application.unsafeApplicationState = .inactive
Expand All @@ -580,7 +580,7 @@ class SentrySessionTrackerTests: XCTestCase {

private func crashSut() {
sut.removeObservers()
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
// When the app stops, the app state is `inactive`.
//
// This can be observed by viewing the application state in `UIAppDelegate.applicationDidEnterBackground`.
Expand All @@ -600,7 +600,7 @@ class SentrySessionTrackerTests: XCTestCase {
}

private func goToForeground() {
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
// When the app becomes active, the app state is `active`.
// This can be observed by viewing the application state in `UIAppDelegate.applicationDidBecomeActive`.
fixture._application.unsafeApplicationState = .active
Expand All @@ -622,7 +622,7 @@ class SentrySessionTrackerTests: XCTestCase {
private func goToBackground() {
// Before an app goes to background, it is still active and will resign from being active.
willResignActive()
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
// It is expected that the app state is background when the didEnterBackground is called
fixture._application.unsafeApplicationState = .background
fixture.notificationCenter
Expand All @@ -642,7 +642,7 @@ class SentrySessionTrackerTests: XCTestCase {
}

private func willResignActive() {
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
// When the app is about to resign being active, it is still active.
//
// This can be observed by viewing the application state in `UIAppDelegate.applicationWillResignActive`.
Expand All @@ -665,7 +665,7 @@ class SentrySessionTrackerTests: XCTestCase {

private func hybridSdkDidBecomeActive() {
// When an app did become active, it is in the active state.
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
fixture._application.unsafeApplicationState = .active
#else
fixture._application.setIsActive(true)
Expand All @@ -687,7 +687,7 @@ class SentrySessionTrackerTests: XCTestCase {
}

private func willTerminate() {
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
// When terminating an app, it will first move to the background and then terminate.
//
// This can be observed by viewing the application state in `UIAppDelegate.applicationWillTerminate`.
Expand Down Expand Up @@ -876,7 +876,7 @@ class SentrySessionTrackerTests: XCTestCase {

private func postHybridSdkDidBecomeActiveNotification() {
// When the hybrid SDK posts this notification, the app should be in active state
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS)
#if os(iOS) || targetEnvironment(macCatalyst) || os(tvOS) || os(visionOS)
fixture._application.unsafeApplicationState = .active
#else
fixture._application.setIsActive(true)
Expand Down
15 changes: 9 additions & 6 deletions Tests/SentryTests/PrivateSentrySDKOnlyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@
XCTAssertEqual(PrivateSentrySDKOnly.options.enabled, true)
}

#if !os(tvOS) && !os(watchOS)
#if !os(tvOS) && !os(watchOS) && !os(visionOS)
/**
* Smoke Tests profiling via PrivateSentrySDKOnly. Actual profiling unit tests are done elsewhere.
*/
Expand Down Expand Up @@ -268,7 +268,7 @@
let traceIdA = SentryId()

let startTime = PrivateSentrySDKOnly.startProfiler(forTrace: traceIdA)
XCTAssertGreaterThan(startTime, 0)

Check failure on line 271 in Tests/SentryTests/PrivateSentrySDKOnlyTests.swift

View workflow job for this annotation

GitHub Actions / Unit iOS 26 Sentry / Unit iOS 26 Sentry

testProfilingStartAndCollect, XCTAssertGreaterThan failed: ("0") is not greater than ("0")

Check failure on line 271 in Tests/SentryTests/PrivateSentrySDKOnlyTests.swift

View workflow job for this annotation

GitHub Actions / Unit macOS 15 Sentry / Unit macOS 15 Sentry

testProfilingStartAndCollect, XCTAssertGreaterThan failed: ("0") is not greater than ("0")

Check failure on line 271 in Tests/SentryTests/PrivateSentrySDKOnlyTests.swift

View workflow job for this annotation

GitHub Actions / Unit Catalyst 15 Sentry / Unit Catalyst 15 Sentry

testProfilingStartAndCollect, XCTAssertGreaterThan failed: ("0") is not greater than ("0")
Thread.sleep(forTimeInterval: 0.2)
let payload = PrivateSentrySDKOnly.collectProfileBetween(startTime, and: startTime + 200_000_000, forTrace: traceIdA)
XCTAssertNotNil(payload)
Expand Down Expand Up @@ -349,6 +349,7 @@
#endif

#if canImport(UIKit)
#if SENTRY_TARGET_REPLAY_SUPPORTED
func testCaptureReplayShouldCallReplayIntegration() throws {
guard #available(iOS 16.0, tvOS 16.0, *) else { return }

Expand Down Expand Up @@ -443,6 +444,13 @@
XCTAssertTrue(redactBuilder.isRedactContainerClassTestOnly(RedactContainer.self))
}

private func getFirstIntegrationAsReplay() throws -> SentrySessionReplayIntegration {
return try XCTUnwrap(SentrySDKInternal.currentHub().installedIntegrations().first as? SentrySessionReplayIntegration)
}

private let VALID_REPLAY_ID = "0eac7ab503354dd5819b03e263627a29"
#endif // SENTRY_TARGET_REPLAY_SUPPORTED

func testAddExtraSdkPackages() throws {
PrivateSentrySDKOnly.addSdkPackage("package1", version: "version1")
PrivateSentrySDKOnly.addSdkPackage("package2", version: "version2")
Expand All @@ -462,11 +470,6 @@
}
}

private func getFirstIntegrationAsReplay() throws -> SentrySessionReplayIntegration {
return try XCTUnwrap(SentrySDKInternal.currentHub().installedIntegrations().first as? SentrySessionReplayIntegration)
}

private let VALID_REPLAY_ID = "0eac7ab503354dd5819b03e263627a29"
#endif

private func getUnhandledExceptionEnvelope() -> SentryEnvelope {
Expand Down
6 changes: 3 additions & 3 deletions Tests/SentryTests/Protocol/TestData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Sentry
@_spi(Private) import Sentry
@_spi(Private) import SentryTestUtils

#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
#if os(iOS) || os(tvOS) || os(visionOS) || targetEnvironment(macCatalyst)
import UIKit
#endif

Expand Down Expand Up @@ -340,8 +340,8 @@ class TestData {
return request
}

#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) || os(visionOS)

static func getAppStartMeasurement(
type: SentryAppStartType,
appStartTimestamp: Date = TestData.timestamp,
Expand Down
4 changes: 2 additions & 2 deletions Tests/SentryTests/SentryClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1953,7 +1953,7 @@ class SentryClientTests: XCTestCase {
if !SentryDependencyContainer.sharedInstance().crashWrapper.isBeingTraced {
expectedIntegrations = ["ANRTracking"] + expectedIntegrations
}
#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
#if os(iOS) || os(tvOS) || os(visionOS) || targetEnvironment(macCatalyst)
expectedIntegrations.append("FramesTracking")
#endif // os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)

Expand Down Expand Up @@ -2531,7 +2531,7 @@ private extension SentryClientTests {
}

private func getSpan(operation: String, tracer: SentryTracer) -> Span {
#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
#if os(iOS) || os(tvOS) || os(visionOS) || targetEnvironment(macCatalyst)
return SentrySpan(tracer: tracer, context: SpanContext(operation: operation), framesTracker: nil)
#else
return SentrySpan(tracer: tracer, context: SpanContext(operation: operation))
Expand Down
5 changes: 4 additions & 1 deletion Tests/SentryTests/SentryOptionsTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -721,8 +721,10 @@ - (void)assertDefaultValues:(SentryOptions *)options
XCTAssertEqual(options.enablePreWarmedAppStartTracing, YES);
XCTAssertEqual(options.attachViewHierarchy, NO);
XCTAssertEqual(options.reportAccessibilityIdentifier, YES);
# if SENTRY_TARGET_REPLAY_SUPPORTED
XCTAssertEqual(options.sessionReplay.onErrorSampleRate, 0);
XCTAssertEqual(options.sessionReplay.sessionSampleRate, 0);
# endif // SENTRY_TARGET_REPLAY_SUPPORTED
#endif // SENTRY_HAS_UIKIT
XCTAssertTrue(options.enableAppHangTracking);
XCTAssertEqual(options.appHangTimeoutInterval, 2);
Expand Down Expand Up @@ -893,6 +895,7 @@ - (void)testEnablePreWarmedAppStartTracking
[self testBooleanField:@"enablePreWarmedAppStartTracing" defaultValue:YES];
}

# if SENTRY_TARGET_REPLAY_SUPPORTED
- (void)testSessionReplaySettingsInit
{
if (@available(iOS 16.0, tvOS 16.0, *)) {
Expand All @@ -912,7 +915,7 @@ - (void)testSessionReplaySettingsDefaults
XCTAssertEqual(options.sessionReplay.onErrorSampleRate, 0);
}
}

# endif // SENTRY_TARGET_REPLAY_SUPPORTED
#endif // SENTRY_HAS_UIKIT

#if SENTRY_HAS_METRIC_KIT
Expand Down
2 changes: 1 addition & 1 deletion Tests/SentryTests/SentrySDKTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class SentrySDKTests: XCTestCase {
if !SentryDependencyContainer.sharedInstance().crashWrapper.isBeingTraced {
expectedIntegrations.append("SentryANRTrackingIntegration")
}
#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
#if os(iOS) || os(tvOS) || os(visionOS) || targetEnvironment(macCatalyst)
expectedIntegrations.append("SentryFramesTrackingIntegration")
#endif // os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)

Expand Down
Loading
Loading