Skip to content

Commit fddb6b5

Browse files
Merge pull request #2794 from DataDog/marroz/feat-15256-trace-deterministic-sample
RUM-15256 Make trace sampling deterministic based on the RUM session ID
2 parents f369b1c + 81f5103 commit fddb6b5

26 files changed

Lines changed: 724 additions & 114 deletions

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Unreleased
22

33
- [FIX] Fix stack overflow crash when RUM scroll tracking is used alongside third-party delegate proxy libraries (e.g. RxSwift). See [#2791][]
4+
- [IMPROVEMENT] Trace now uses deterministic sampling for custom spans, based on the RUM session ID when available.
5+
- [FIX] When there is an active span, RUM session tracking uses it as the basis for the resource parent span and sampling decision. Starting in this release, if the active span is not sampled, session tracking falls back to making its own sampling decision based on the RUM session tracking sampling rate.
46

57
# 3.8.3 / 26-03-2026
68

Datadog/Datadog.xcodeproj/project.pbxproj

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,20 @@
1212
0904F9F62EE1DA6800ED9A22 /* UIKitExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0904F9F32EE1DA6800ED9A22 /* UIKitExtensionsTests.swift */; };
1313
093AC3292F56F8AA00267CE1 /* ActiveSpanProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 093AC3282F56F8AA00267CE1 /* ActiveSpanProvider.swift */; };
1414
093AC32A2F56F8AA00267CE1 /* ActiveSpanProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 093AC3282F56F8AA00267CE1 /* ActiveSpanProvider.swift */; };
15+
094B553F2F76C63100BAB8B5 /* RUMResourceTraceIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094B553E2F76C63100BAB8B5 /* RUMResourceTraceIntegrationTests.swift */; };
16+
094B55402F76C63100BAB8B5 /* RUMResourceTraceIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094B553E2F76C63100BAB8B5 /* RUMResourceTraceIntegrationTests.swift */; };
17+
095AE9552F7D4E9F00C332AE /* TracerSamplerProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095AE9542F7D4E9F00C332AE /* TracerSamplerProvider.swift */; };
18+
095AE9562F7D4E9F00C332AE /* SamplingDecision.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095AE9522F7D4E9F00C332AE /* SamplingDecision.swift */; };
19+
095AE9572F7D4E9F00C332AE /* SamplerProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095AE9532F7D4E9F00C332AE /* SamplerProvider.swift */; };
20+
095AE9582F7D4E9F00C332AE /* TracerSamplerProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095AE9542F7D4E9F00C332AE /* TracerSamplerProvider.swift */; };
21+
095AE9592F7D4E9F00C332AE /* SamplingDecision.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095AE9522F7D4E9F00C332AE /* SamplingDecision.swift */; };
22+
095AE95A2F7D4E9F00C332AE /* SamplerProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095AE9532F7D4E9F00C332AE /* SamplerProvider.swift */; };
1523
09A9369D2F0EB989000B6379 /* SamplingMechanismType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A9369A2F0EB989000B6379 /* SamplingMechanismType.swift */; };
1624
09A9369E2F0EB989000B6379 /* SamplingPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A9369B2F0EB989000B6379 /* SamplingPriority.swift */; };
1725
09A9369F2F0EB989000B6379 /* SpanContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A9369C2F0EB989000B6379 /* SpanContext.swift */; };
1826
09A936A02F0EB989000B6379 /* SamplingMechanismType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A9369A2F0EB989000B6379 /* SamplingMechanismType.swift */; };
1927
09A936A12F0EB989000B6379 /* SamplingPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A9369B2F0EB989000B6379 /* SamplingPriority.swift */; };
2028
09A936A22F0EB989000B6379 /* SpanContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09A9369C2F0EB989000B6379 /* SpanContext.swift */; };
21-
09D2B7EB2EF4258D0089F05B /* SamplingDecision.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D2B7EA2EF4258D0089F05B /* SamplingDecision.swift */; };
22-
09D2B7EC2EF4258D0089F05B /* SamplingDecision.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D2B7EA2EF4258D0089F05B /* SamplingDecision.swift */; };
2329
11030D5F2D959EAD00732D5F /* DatadogLogs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D207317C29A5226A00ECBF94 /* DatadogLogs.framework */; };
2430
11030D642D959EC300732D5F /* DatadogLogs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D20731B429A5279D00ECBF94 /* DatadogLogs.framework */; };
2531
11030D692D959F1700732D5F /* DatadogWebViewTracking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3CE119FE29F7BE0100202522 /* DatadogWebViewTracking.framework */; };
@@ -2773,10 +2779,13 @@
27732779
/* Begin PBXFileReference section */
27742780
0904F9F32EE1DA6800ED9A22 /* UIKitExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitExtensionsTests.swift; sourceTree = "<group>"; };
27752781
093AC3282F56F8AA00267CE1 /* ActiveSpanProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveSpanProvider.swift; sourceTree = "<group>"; };
2782+
094B553E2F76C63100BAB8B5 /* RUMResourceTraceIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RUMResourceTraceIntegrationTests.swift; sourceTree = "<group>"; };
2783+
095AE9522F7D4E9F00C332AE /* SamplingDecision.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SamplingDecision.swift; sourceTree = "<group>"; };
2784+
095AE9532F7D4E9F00C332AE /* SamplerProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SamplerProvider.swift; sourceTree = "<group>"; };
2785+
095AE9542F7D4E9F00C332AE /* TracerSamplerProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracerSamplerProvider.swift; sourceTree = "<group>"; };
27762786
09A9369A2F0EB989000B6379 /* SamplingMechanismType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SamplingMechanismType.swift; sourceTree = "<group>"; };
27772787
09A9369B2F0EB989000B6379 /* SamplingPriority.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SamplingPriority.swift; sourceTree = "<group>"; };
27782788
09A9369C2F0EB989000B6379 /* SpanContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpanContext.swift; sourceTree = "<group>"; };
2779-
09D2B7EA2EF4258D0089F05B /* SamplingDecision.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SamplingDecision.swift; sourceTree = "<group>"; };
27802789
11014EACF1FB9927DAD57822 /* EvaluationMocks.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = EvaluationMocks.swift; sourceTree = "<group>"; };
27812790
11030D752D96EC5300732D5F /* ViewHitchesMetric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewHitchesMetric.swift; sourceTree = "<group>"; };
27822791
110311752EF96ED000750DD4 /* DDLogs+apiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "DDLogs+apiTests.m"; sourceTree = "<group>"; };
@@ -4398,6 +4407,16 @@
43984407
/* End PBXFrameworksBuildPhase section */
43994408

44004409
/* Begin PBXGroup section */
4410+
095AE9512F7D30BD00C332AE /* Sampling */ = {
4411+
isa = PBXGroup;
4412+
children = (
4413+
095AE9522F7D4E9F00C332AE /* SamplingDecision.swift */,
4414+
095AE9532F7D4E9F00C332AE /* SamplerProvider.swift */,
4415+
095AE9542F7D4E9F00C332AE /* TracerSamplerProvider.swift */,
4416+
);
4417+
path = Sampling;
4418+
sourceTree = "<group>";
4419+
};
44014420
111201C02E93C13000375DA3 /* AppState */ = {
44024421
isa = PBXGroup;
44034422
children = (
@@ -6706,6 +6725,7 @@
67066725
D2552AF42BBC47D900A45725 /* WebEventIntegrationTests.swift */,
67076726
61DCC84C2C05D4E500CB59E5 /* SDKMetrics */,
67086727
D2AB895D2F71780900040F58 /* DeterministicSamplingIntegrationTests.swift */,
6728+
094B553E2F76C63100BAB8B5 /* RUMResourceTraceIntegrationTests.swift */,
67096729
);
67106730
path = RUM;
67116731
sourceTree = "<group>";
@@ -7408,14 +7428,14 @@
74087428
61A2CC382A44B0EA0000FF25 /* Trace.swift */,
74097429
61A2CC352A44B0A20000FF25 /* TraceConfiguration.swift */,
74107430
61A2CC3B2A44BED30000FF25 /* Tracer.swift */,
7411-
09D2B7EA2EF4258D0089F05B /* SamplingDecision.swift */,
74127431
D2546C0629AF55CE0054E00B /* Feature */,
74137432
D21C26E928AF9D22005DD405 /* Integrations */,
74147433
11F55FEA2DCE501A00DE4944 /* Objc */,
74157434
3C6C7FDE2B459AAA006F5CBC /* OpenTelemetry */,
74167435
61E909E524A24DD3005EA2DE /* OpenTracing */,
74177436
618D9DE5263AD77600A3FAD2 /* Scrubbing */,
74187437
61C5A8A324509FAA00DA608C /* Span */,
7438+
095AE9512F7D30BD00C332AE /* Sampling */,
74197439
61C5A87A24509A0C00DA608C /* Utils */,
74207440
);
74217441
name = DatadogTrace;
@@ -9585,6 +9605,7 @@
95859605
118248652D908CE300E3D16F /* AnonymousIdentifierTests.swift in Sources */,
95869606
118248662D908CF400E3D16F /* RUMViewHitchesIntegrationTests.swift in Sources */,
95879607
118248672D908CF400E3D16F /* RUMAttributesIntegrationTests.swift in Sources */,
9608+
094B553F2F76C63100BAB8B5 /* RUMResourceTraceIntegrationTests.swift in Sources */,
95889609
6119DDDE2DD24AB700DA80F9 /* AppRun.swift in Sources */,
95899610
6119DDDF2DD24AB700DA80F9 /* AppRunStep+Fixtures.swift in Sources */,
95909611
6119DDE02DD24AB700DA80F9 /* AppRunner.swift in Sources */,
@@ -9622,6 +9643,7 @@
96229643
files = (
96239644
118249272D9096B400E3D16F /* CoreTelemetryIntegrationTests.swift in Sources */,
96249645
118249282D9096B400E3D16F /* CoreMetricsIntegrationTests.swift in Sources */,
9646+
094B55402F76C63100BAB8B5 /* RUMResourceTraceIntegrationTests.swift in Sources */,
96259647
118249292D9096B400E3D16F /* WebLogIntegrationTests.swift in Sources */,
96269648
1182492A2D9096B400E3D16F /* AppHangsMonitoringTests.swift in Sources */,
96279649
1182492B2D9096B400E3D16F /* Datadog+MultipleInstancesIntegrationTests.swift in Sources */,
@@ -10961,6 +10983,9 @@
1096110983
61A2CC3C2A44BED30000FF25 /* Tracer.swift in Sources */,
1096210984
D2C1A50229C4C4CB00946C31 /* Casting.swift in Sources */,
1096310985
3CC6AD182B4F07DD00015B18 /* OTelAttributeValue+Datadog.swift in Sources */,
10986+
095AE9582F7D4E9F00C332AE /* TracerSamplerProvider.swift in Sources */,
10987+
095AE9592F7D4E9F00C332AE /* SamplingDecision.swift in Sources */,
10988+
095AE95A2F7D4E9F00C332AE /* SamplerProvider.swift in Sources */,
1096410989
D2C1A50C29C4C4CB00946C31 /* DDNoOps.swift in Sources */,
1096510990
D2C1A4FC29C4C4CB00946C31 /* RequestBuilder.swift in Sources */,
1096610991
D2C1A50D29C4C4CB00946C31 /* SpanTagsReducer.swift in Sources */,
@@ -10981,7 +11006,6 @@
1098111006
D2C1A51429C4C53F00946C31 /* OTSpanContext.swift in Sources */,
1098211007
3C6C7FEB2B459AAA006F5CBC /* OTelTraceId+Datadog.swift in Sources */,
1098311008
3C6C7FE92B459AAA006F5CBC /* OTelSpanBuilder.swift in Sources */,
10984-
09D2B7EC2EF4258D0089F05B /* SamplingDecision.swift in Sources */,
1098511009
D2C1A51329C4C53F00946C31 /* OTReference.swift in Sources */,
1098611010
3C6C7FEF2B459AAA006F5CBC /* OTelSpanId+Datadog.swift in Sources */,
1098711011
D2C1A4FB29C4C4CB00946C31 /* MessageReceivers.swift in Sources */,
@@ -11270,6 +11294,9 @@
1127011294
61A2CC3D2A44BED30000FF25 /* Tracer.swift in Sources */,
1127111295
D2C1A53829C4F2DF00946C31 /* Casting.swift in Sources */,
1127211296
3CC6AD192B4F07DD00015B18 /* OTelAttributeValue+Datadog.swift in Sources */,
11297+
095AE9552F7D4E9F00C332AE /* TracerSamplerProvider.swift in Sources */,
11298+
095AE9562F7D4E9F00C332AE /* SamplingDecision.swift in Sources */,
11299+
095AE9572F7D4E9F00C332AE /* SamplerProvider.swift in Sources */,
1127311300
D2C1A53929C4F2DF00946C31 /* DDNoOps.swift in Sources */,
1127411301
D2C1A53A29C4F2DF00946C31 /* RequestBuilder.swift in Sources */,
1127511302
D2C1A53B29C4F2DF00946C31 /* SpanTagsReducer.swift in Sources */,
@@ -11290,7 +11317,6 @@
1129011317
D2C1A53E29C4F2DF00946C31 /* OTSpanContext.swift in Sources */,
1129111318
3C6C7FEC2B459AAA006F5CBC /* OTelTraceId+Datadog.swift in Sources */,
1129211319
3C6C7FEA2B459AAA006F5CBC /* OTelSpanBuilder.swift in Sources */,
11293-
09D2B7EB2EF4258D0089F05B /* SamplingDecision.swift in Sources */,
1129411320
D2C1A53F29C4F2DF00946C31 /* OTReference.swift in Sources */,
1129511321
3C6C7FF02B459AAA006F5CBC /* OTelSpanId+Datadog.swift in Sources */,
1129611322
D2C1A54129C4F2DF00946C31 /* MessageReceivers.swift in Sources */,

Datadog/IntegrationUnitTests/RUM/DeterministicSamplingIntegrationTests.swift

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import DatadogInternal
99
import TestUtilities
1010
@testable import DatadogCore
1111
@testable import DatadogRUM
12+
@testable import DatadogTrace
1213

1314
#if os(iOS)
1415
@_spi(Internal)
@@ -116,4 +117,155 @@ class DeterministicSamplingIntegrationTests: XCTestCase {
116117
XCTAssertTrue(srData.isEmpty, "SR must not record: UUID is excluded by the combined 40% rate")
117118
}
118119
#endif
120+
121+
// MARK: - Trace (manually created spans) sampling with RUM Session ID
122+
// MARK: 1. Trace rate is lower than session rate
123+
124+
/*
125+
Session sampled -> Trace may or may not be sampled
126+
Session not sampled -> Trace not sampled
127+
*/
128+
func testManuallyCreatedSpan_traceRateLowerThanSessionRate_sampledSession() throws {
129+
// Session is sampled, trace is not.
130+
let sessionUUID = try makeValidatedSessionID()
131+
enabledRUMWith(samplingRate: 60, traceSamplingRate: .random(in: 0...50), sessionUUID: sessionUUID)
132+
let span = createViewAndSpan()
133+
try assert(rumViewsExist: true, span: span, spansExist: false)
134+
}
135+
136+
func testManuallyCreatedSpan_traceRateLowerThanSessionRate_nonSampledSession() throws {
137+
// Session is NOT sampled, trace is also not.
138+
let sessionUUID = try makeValidatedSessionID()
139+
enabledRUMWith(samplingRate: 50, traceSamplingRate: .random(in: 0...50), sessionUUID: sessionUUID)
140+
let span = createViewAndSpan()
141+
try assert(rumViewsExist: false, span: span, spansExist: false)
142+
}
143+
144+
func testManuallyCreatedSpan_traceRateLowerThanSessionRate_random() throws {
145+
let sessionUUID = UUID()
146+
let sampler = DeterministicSampler(uuid: sessionUUID, samplingRate: .random(in: 0...100))
147+
let traceSampler = DeterministicSampler(uuid: sessionUUID, samplingRate: .random(in: 0...Float.random(in: 0...(min(50, sampler.samplingRate)))))
148+
enabledRUMWith(samplingRate: sampler.samplingRate, traceSamplingRate: traceSampler.samplingRate, sessionUUID: sessionUUID)
149+
let span = createViewAndSpan()
150+
try assert(rumViewsExist: sampler.isSampled, span: span, spansExist: traceSampler.isSampled)
151+
XCTAssert(
152+
sampler.isSampled /* Session sampled -> Trace may or may not be sampled */
153+
|| (traceSampler.isSampled == false) /* Session not sampled -> Trace not sampled */
154+
)
155+
}
156+
157+
// MARK: 2. Trace rate is equal to session rate
158+
159+
/*
160+
Session sampled -> Trace is sampled
161+
Session not sampled -> Trace not sampled
162+
*/
163+
func testManuallyCreatedSpan_traceRateEqualToSessionRate_sampledSession() throws {
164+
let sessionUUID = try makeValidatedSessionID()
165+
enabledRUMWith(samplingRate: 60, traceSamplingRate: 60, sessionUUID: sessionUUID)
166+
let span = createViewAndSpan()
167+
try assert(rumViewsExist: true, span: span, spansExist: true)
168+
}
169+
170+
func testManuallyCreatedSpan_traceRateEqualToSessionRate_nonSampledSession() throws {
171+
let sessionUUID = try makeValidatedSessionID()
172+
enabledRUMWith(samplingRate: 50, traceSamplingRate: 50, sessionUUID: sessionUUID)
173+
let span = createViewAndSpan()
174+
try assert(rumViewsExist: false, span: span, spansExist: false)
175+
}
176+
177+
func testManuallyCreatedSpan_traceRateEqualToSessionRate_random() throws {
178+
let sessionUUID = UUID()
179+
let sampler = DeterministicSampler(uuid: sessionUUID, samplingRate: .random(in: 0...100))
180+
enabledRUMWith(samplingRate: sampler.samplingRate, traceSamplingRate: sampler.samplingRate, sessionUUID: sessionUUID)
181+
let span = createViewAndSpan()
182+
try assert(rumViewsExist: sampler.isSampled, span: span, spansExist: sampler.isSampled)
183+
}
184+
185+
// MARK: 3. Trace rate is higher than session rate
186+
187+
/*
188+
Session sampled -> Trace is sampled
189+
Session not sampled -> Trace may or may not be sampled
190+
*/
191+
func testManuallyCreatedSpan_traceRateHigherThanSessionRate_sampledSession() throws {
192+
// Session is sampled, trace is.
193+
let sessionUUID = try makeValidatedSessionID()
194+
enabledRUMWith(samplingRate: 60, traceSamplingRate: .random(in: 60...100), sessionUUID: sessionUUID)
195+
let span = createViewAndSpan()
196+
try assert(rumViewsExist: true, span: span, spansExist: true)
197+
}
198+
199+
func testManuallyCreatedSpan_traceRateHigherThanSessionRate_nonSampledSession() throws {
200+
// Session is NOT sampled, trace is.
201+
let sessionUUID = try makeValidatedSessionID()
202+
enabledRUMWith(samplingRate: 50, traceSamplingRate: .random(in: 60...100), sessionUUID: sessionUUID)
203+
let span = createViewAndSpan()
204+
try assert(rumViewsExist: false, span: span, spansExist: true)
205+
}
206+
207+
func testManuallyCreatedSpan_traceRateHigherThanSessionRate_random() throws {
208+
let sessionUUID = UUID()
209+
let sampler = DeterministicSampler(uuid: sessionUUID, samplingRate: .random(in: 0...100))
210+
let traceSampler = DeterministicSampler(uuid: sessionUUID, samplingRate: Float.random(in: (max(60, sampler.samplingRate))...100))
211+
enabledRUMWith(samplingRate: sampler.samplingRate, traceSamplingRate: traceSampler.samplingRate, sessionUUID: sessionUUID)
212+
let span = createViewAndSpan()
213+
try assert(rumViewsExist: sampler.isSampled, span: span, spansExist: traceSampler.isSampled)
214+
XCTAssert(
215+
sampler.isSampled == false /* Session not sampled -> Trace may or may not be sampled */
216+
|| traceSampler.isSampled /* Session sampled -> Trace is sampled */
217+
)
218+
}
219+
220+
// MARK: Helper methods
221+
222+
private func makeValidatedSessionID() throws -> UUID {
223+
// This session ID is not sampled at 50%, but it is sampled at 60%.
224+
let sessionUUID = UUID(uuidString: "c5b3c4ab-fa4a-4de9-8199-a522131ec48a")!
225+
226+
// Preconditions guard — if hash properties ever change, fail loudly
227+
let canary1 = DeterministicSampler(uuid: sessionUUID, samplingRate: 50)
228+
try XCTSkipUnless(canary1.isSampled == false, "Precondition: UUID must NOT be sampled at 50%")
229+
let canary2 = DeterministicSampler(uuid: sessionUUID, samplingRate: 60)
230+
try XCTSkipUnless(canary2.isSampled == true, "Precondition: UUID must be sampled at 60%")
231+
232+
return sessionUUID
233+
}
234+
235+
private func enabledRUMWith(samplingRate rumSamplingRate: Float, traceSamplingRate: Float, sessionUUID: UUID) {
236+
var rumConfig = RUM.Configuration(applicationID: "test-app-id")
237+
rumConfig.sessionSampleRate = rumSamplingRate
238+
rumConfig.uuidGenerator = RUMUUIDGeneratorMock(uuid: RUMUUID(rawValue: sessionUUID))
239+
RUM.enable(with: rumConfig, in: core)
240+
241+
let traceConfig = Trace.Configuration(sampleRate: traceSamplingRate)
242+
Trace.enable(with: traceConfig, in: core)
243+
244+
// Wait until all the pieces injected in the context and shared between modules are in place.
245+
core.flush()
246+
}
247+
248+
private func createViewAndSpan(customSpanSampleRate: Float? = nil) -> OTSpan {
249+
let tracer = Tracer.shared(in: core)
250+
251+
RUMMonitor.shared(in: core).startView(key: "test-view", name: "TestView")
252+
let span = tracer.startRootSpan(operationName: "Test Action", customSampleRate: customSpanSampleRate)
253+
span.finish()
254+
_ = core.waitAndReturnEventsData(ofFeature: RUMFeature.name)
255+
_ = core.waitAndReturnEventsData(ofFeature: TraceFeature.name)
256+
257+
return span
258+
}
259+
260+
private func assert(rumViewsExist: Bool, span: OTSpan, spansExist: Bool) throws {
261+
// The RUM view must exist (session sampled at 60%)
262+
let rumMatchers = try core.waitAndReturnRUMEventMatchers()
263+
XCTAssert(rumMatchers.filterRUMEvents(ofType: RUMViewEvent.self).isEmpty != rumViewsExist)
264+
265+
let traceMatchers = try core.waitAndReturnSpanMatchers()
266+
XCTAssert(traceMatchers.isEmpty != spansExist)
267+
268+
let dd = try XCTUnwrap(span.context.dd)
269+
XCTAssert(dd.samplingDecision.samplingPriority.isKept == spansExist)
270+
}
119271
}

0 commit comments

Comments
 (0)