@@ -798,6 +798,65 @@ class SentryScopeSwiftTests: XCTestCase {
798798 XCTAssertEqual ( spanId. sentrySpanIdString, traceContext [ " span_id " ] as? String )
799799 }
800800
801+ func testGetCasedInternalSpan_SpanIsNil( ) {
802+ // -- Arrange --
803+ let scope = Scope ( )
804+
805+ // -- Act --
806+ let span = scope. getCastedInternalSpan ( )
807+
808+ // -- Assert --
809+ XCTAssertNil ( span)
810+ }
811+
812+ #if os(macOS)
813+ // We test this only on macOS because the SentrySpan init methods require a frames tracker.
814+ // As we're testing simple logic here, we can skip the other platforms.
815+ func testGetCasedInternalSpan_SpanIsOfInternalTypeSpan( ) throws {
816+ // -- Arrange --
817+ let scope = Scope ( )
818+ let span = SentrySpan ( context: SpanContext ( operation: " TEST " ) )
819+
820+ scope. span = span
821+
822+ // -- Act --
823+ let actualSpan = try XCTUnwrap ( scope. getCastedInternalSpan ( ) )
824+
825+ // -- Assert --
826+ XCTAssertEqual ( actualSpan, span)
827+ XCTAssertEqual ( actualSpan. spanId, span. spanId)
828+ }
829+
830+ func testGetCasedInternalSpan_SpanIsSubClassOfInternalTypeSpan( ) throws {
831+ // -- Arrange --
832+ let scope = Scope ( )
833+ let span = SubClassOfSentrySpan ( context: SpanContext ( operation: " TEST " ) )
834+
835+ scope. span = span
836+
837+ // -- Act --
838+ let actualSpan = try XCTUnwrap ( scope. getCastedInternalSpan ( ) )
839+
840+ // -- Assert --
841+ XCTAssertEqual ( actualSpan, span)
842+ XCTAssertEqual ( actualSpan. spanId, span. spanId)
843+ }
844+ #endif // os(macOS)
845+
846+ func testGetCasedInternalSpan_SpanIsOfDifferentType( ) {
847+ // -- Arrange --
848+ let scope = Scope ( )
849+ let span = NotOfTypeSpan ( )
850+
851+ scope. span = span
852+
853+ // -- Act --
854+ let actualSpan = scope. getCastedInternalSpan ( )
855+
856+ // -- Assert --
857+ XCTAssertNil ( actualSpan)
858+ }
859+
801860 private class TestScopeObserver : NSObject , SentryScopeObserver {
802861 var tags : [ String : String ] ?
803862 func setTags( _ tags: [ String : String ] ? ) {
@@ -863,3 +922,46 @@ class SentryScopeSwiftTests: XCTestCase {
863922 }
864923 }
865924}
925+
926+ // A minimal dummy Span implementation that is not SentrySpan.
927+ private final class NotOfTypeSpan : NSObject , Span {
928+
929+ init ( traceId: SentryId = SentryId ( ) ) {
930+ self . traceId = traceId
931+ }
932+
933+ // MARK: - Properties required by Span (set to neutral values)
934+ var traceId : SentryId = SentryId ( )
935+ var spanId : SpanId = SpanId ( )
936+ var parentSpanId : SpanId ?
937+ var sampled : SentrySampleDecision = . undecided
938+ var operation : String = " "
939+ var origin : String = " "
940+ var spanDescription : String ?
941+ var status : SentrySpanStatus = . undefined
942+ var timestamp : Date ?
943+ var startTimestamp : Date ?
944+ var data : [ String : Any ] { [ : ] }
945+ var tags : [ String : String ] { [ : ] }
946+ var isFinished : Bool { false }
947+ var traceContext : TraceContext ? { nil }
948+
949+ // MARK: - Methods required by Span (no-ops)
950+ func startChild( operation: String ) -> Span { return self }
951+ func startChild( operation: String , description: String ? ) -> Span { return self }
952+ func setData( value: Any ? , key: String ) { }
953+ func removeData( key: String ) { }
954+ func setTag( value: String , key: String ) { }
955+ func removeTag( key: String ) { }
956+ func setMeasurement( name: String , value: NSNumber ) { }
957+ func setMeasurement( name: String , value: NSNumber , unit: MeasurementUnit ) { }
958+ func finish( ) { }
959+ func finish( status: SentrySpanStatus ) { }
960+ func toTraceHeader( ) -> TraceHeader { return TraceHeader ( trace: traceId, spanId: spanId, sampled: sampled) }
961+ func baggageHttpHeader( ) -> String ? { return nil }
962+
963+ // MARK: - SentrySerializable (no-op payload)
964+ func serialize( ) -> [ String : Any ] { return [ : ] }
965+ }
966+
967+ private final class SubClassOfSentrySpan : SentrySpan { }
0 commit comments