-
Notifications
You must be signed in to change notification settings - Fork 7
Comparing changes
Open a pull request
base repository: DataDog/dd-sdk-swift-testing
base: 2.7.4
head repository: DataDog/dd-sdk-swift-testing
compare: 2.7.5
- 19 commits
- 132 files changed
- 3 contributors
Commits on Jun 1, 2026
-
Merge pull request #266 from DataDog/yehor.popovych/sdtest-3772-telem…
…etry-exporter [SDTEST-3772] Implement TelemetryExporter
Configuration menu - View commit details
-
Copy full SHA for 4c0c7fd - Browse repository at this point
Copy the full SHA 4c0c7fdView commit details -
[SDTEST-3773] Add TelemetryLogExporter bridging OTel logs to Telemetr…
…yExporter (#267) Implements LogRecordExporter that adapts ReadableLogRecord to TelemetryLog and forwards batches to TelemetryExporter for storage and upload. Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 003d90c - Browse repository at this point
Copy the full SHA 003d90cView commit details
Commits on Jun 2, 2026
-
Remove unused EncodableValue helper (#270)
Drop `EncodableValue` and its test helpers (`Encoding.swift`, `EncodableValueTests.swift`), updating `JSONEncoderTests` and the Xcode project accordingly. Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 9c21331 - Browse repository at this point
Copy the full SHA 9c21331View commit details -
[SDTEST-3774] Add TelemetryMetricExporter bridging OTel metrics to Te…
…lemetryExporter (#268) * [SDTEST-3774] Add TelemetryMetricExporter bridging OTel metrics to TelemetryExporter Implement MetricExporter that converts OTel MetricData to TelemetryMetric and forwards to TelemetryExporter for batching and upload, following the same pattern as TelemetryLogExporter. - Gauge/Sum types map to gauge or count series - Histogram/ExponentialHistogram/Summary emit .count and .sum sub-series - Attributes are converted to sorted tag arrays - getAggregationTemporality returns cumulative for all instrument types Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Add TelemetryLogExporterTests Covers message extraction (body, message attribute, eventName, default), severity→level mapping (error/fatal→ERROR, warn→WARN, others→DEBUG), stack trace extraction, attribute→tag conversion, timestamp fallback, multiple records, and forceFlush. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Fix DataUploadWorker.flush() returning false when queue is empty An empty flush (no pending batches) is a success — there was nothing to upload, so the operation trivially succeeded. Initialising result=true instead of false makes flush() consistent with that expectation and allows forceFlush()/flush() callers to distinguish "nothing to do" from a real upload failure. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Add TelemetryDistribution type for the distributions telemetry request Implements the distributions payload type per the telemetry spec: - TelemetryDistribution with its own Namespace (tracers/profilers/rum/appsec) and Series carrying raw sample values ([Double]) rather than [timestamp,value] pairs - Wired into TelemetryRequestType enum, TelemetryMessageBatch decoder, and TelemetryApi - sendDistributions() added to TelemetryApi protocol and TelemetryApiService - TelemetryExporterTests extended to cover distributions round-trip and mixed-type batches Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Use distributions for OTel histogram and exponential-histogram metrics Histogram types carry sampled data, so they map naturally to the distributions request type rather than the generate-metrics count/sum pair. Conversion strategy: - Histogram (explicit boundaries): reconstruct approximate sample values using bucket midpoints; edge buckets use min/max when available. - ExponentialHistogram: geometric midpoint of each exponential bucket (base^(offset+i+0.5) where base = 2^(2^-scale)). - Summary: kept as generate-metrics count+sum (already pre-aggregated). TelemetryMetricExporter.export(metrics:) now emits a TelemetryMetric payload for scalar types and a separate TelemetryDistribution payload for histogram types when both are present in the same batch. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Fall back to boundary edge for histogram underflow bucket without min When a histogram's underflow (first) bucket has no recorded min, use the first explicit boundary as the representative value instead of 0. A value in the underflow bucket is <= boundaries[0], so the boundary itself is a closer approximation than 0 — especially for histograms whose boundaries don't start near zero. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Request delta temporality for counters and histograms The exporter previously declared cumulative temporality for all instruments. Under cumulative, OTel emits running totals since process start on every collection, which is wrong for the DD targets: - DD `count` sums submitted values per interval, so feeding it cumulative snapshots inflates the count. - `distributions` expect the interval's raw samples, so reconstructing from cumulative bucket counts re-emits the entire sample set each cycle. Mirror OTel's deltaPreferred() selector: up/down counters (mapped to DD gauge) stay cumulative so the current running total is reported; counters, histograms, and gauges request delta. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Allow per-metric namespace via the OTel Resource Read the telemetry namespace from each metric's Resource (attribute key "dd.telemetry.namespace") and set it as the per-series namespace override. This lets a meter provider route its metrics to a specific namespace without a dedicated exporter instance. When the attribute is absent or its value isn't valid for the target payload (e.g. a generate-metrics-only namespace on a distribution), the override is omitted and the exporter's configured payload-level namespace applies. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Expose telemetry namespace as a Resource accessor Replace the ad-hoc TelemetryMetricResourceKeys constant and MetricConversion namespace helpers with a typed `Resource.telemetryNamespace` getter/setter in OpenTelemetry+Extensions, matching the existing applicationName/environment/etc. accessors. The converter now reads metric.resource.telemetryNamespace and maps it to the payload-specific Namespace enum via init(rawValue:). Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Use typed, per-payload namespace accessors on Resource Split the single string-based telemetryNamespace into two typed accessors, one per payload type with its own resource key: - telemetryMetricNamespace: TelemetryMetric.Namespace? (key "dd.telemetry.metric.namespace") - telemetryDistributionNamespace: TelemetryDistribution.Namespace? (key "dd.telemetry.distribution.namespace") Getters/setters work directly with the enum types (no String at the call site), and the separate keys let metrics and distributions carry independent namespaces — necessary since the two enums accept different value sets. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for ee8887b - Browse repository at this point
Copy the full SHA ee8887bView commit details
Commits on Jun 3, 2026
-
Trim span tags, metadata and log attribute values to backend 5000-cha…
…r limit (#269) The backend accepts at most 5000 characters per tag for spans and span metadata. Add the limit as `AttributesSanitizer.Constraints.maxAttributeValueLength` (the common sanitizer for all features) and enforce it across: - Span tags: trim values via `AttributesSanitizer.sanitizeValues`, reused by both `DDSpan` (`SpanSanitizer`) and `TestSpan`. Non-numeric values are serialized to their `description` (the representation the encoders emit) and truncated; numeric values pass through as metrics. - Span metadata: trim keys and string values via `SpanMetadata.trimmed(toMaxLength:)`, applied when the file header is built/updated in `SpansExporter`. - Log attribute values: trim string values in `LogSanitizer`, referencing the shared constant. - `TestError` crash-log splitting now uses the shared constant instead of a hardcoded 5000. Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 9b16daa - Browse repository at this point
Copy the full SHA 9b16daaView commit details -
[SDTEST-3775] Add common telemetry manager with counter/histogram ins…
…tances (#271) * [SDTEST-3775] Add common telemetry manager with counter/histogram instances Introduce a `Telemetry` manager (DatadogSDKTesting) that owns a MeterProvider wired to the Datadog instrumentation-telemetry metric pipeline (PeriodicMetricReader -> TelemetryMetricExporter -> TelemetryExporter, under the `civisibility` namespace) and provides an instrument for every CI Visibility metric. Metrics are exposed as a typed, discoverable tree - `telemetry.metrics.git.command.add(command: .getRepository)` - where each metric's add()/record() names exactly the tags it accepts and tag values are typed enums/Bool/Int rendered via the existing `SpanAttributeConvertible`. The metric set is taken from the shared instrumentation-telemetry spec and cross-checked against dd-trace-go (59 metrics: 37 counters + 22 distributions). This only provides the instances; features do not record into them yet. - EventsExporter: make TelemetryMetricExporter / TelemetryLogExporter public; add `civisibility` to the metric/distribution namespace enums. - Config: add DD_INSTRUMENTATION_TELEMETRY_ENABLED (default true) and DD_TELEMETRY_HEARTBEAT_INTERVAL (seconds, clamped 1...3600, default 60). - SessionManager: build the Telemetry manager (gated by the enable flag) and thread it through the optional SessionConfig.telemetry; shut it down on stop. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Fix flaky testExportMultipleItems_batchedInOneFile_uploadedAsOneEnvelope The `appendToOneFile` storage mock used `minFileAgeForRead: -1`, making the still-open file immediately eligible for the periodic upload worker (firing every 0.05s). When the worker fired between the test's synchronous writes it uploaded and deleted the file after only some entries, splitting the batch across uploads and intermittently failing the 4-entry assertion. `flush()` drains via `getAllReadableFiles()`, which ignores `minFileAgeForRead`, so setting it to `.greatestFiniteMagnitude` keeps the periodic worker from ever racing the writes while flush still drains the whole file as one batch — exactly the behaviour under test. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for f35d42c - Browse repository at this point
Copy the full SHA f35d42cView commit details -
[SDTEST-3776] Wire telemetry manager through the SDK to gather reques…
…t/upload metrics (#272) * [SDTEST-3776] Wire telemetry manager through the SDK to gather request/upload metrics Introduce telemetry observer hooks in EventsExporter and wire the common `Telemetry` manager through the tracer so the network/storage layers and the API-calling features can record CI Visibility metrics. EventsExporter (producer side — telemetry-agnostic): - Add `RequestObserver` (HTTP request: duration, request/response bytes, status), `UploadObserver` (background batch lifecycle) and `PayloadObserver` (serialization time + event count) protocols. - Measure request facts once in `HTTPClient.perform`; thread an optional `observer` through every API method (with `@inlinable` no-observer conveniences) and the upload pipeline (`DataUploadWorker`, `FileWriter`). - Bundle them as `ExporterObservers` plumbed through `Exporter` to the spans / coverage sub-exporters. - Fix `*ApiService` to depend on `HTTPClientType` instead of the concrete `HTTPClient` (they bypassed the injectable protocol). DatadogSDKTesting (consumer side): - Create the `Telemetry` manager with the tracer (before the exporter) so it can observe the upload pipeline and reach the feature factories; expose it as `DDTracer.telemetry` and source `SessionConfig.telemetry` from it. - Add observer adapters mapping the neutral callbacks to `Telemetry.metrics.*` with a shared `statusCode -> error_type` derivation, plus per-family request observers. - Gather `endpoint_payload.*` (via the exporter observers) and the API-request families `git_requests.*` / `itr_skippable_tests.*` / `known_tests.*` / `test_management_tests.*` (via per-call request observers threaded into the git uploader, TIA / known-tests / test-management factories and the settings fetch). Reuse a single `ExporterConfiguration` for the exporter and telemetry, now on the default performance preset. Response item counts and the local feature metrics (events, git commands, ITR decisions, code coverage) remain for SDTEST-3777. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Add telemetry architecture doc for SDTEST-3777 handoff Describes the metric tree (3775), the observer-hook wiring (3776), what is gathered today vs. the remaining work for SDTEST-3777 (response item counts, endpoint_payload.dropped, local feature metrics), the cross-module rationale, lifecycle, gotchas and a file map. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for e9db1c7 - Browse repository at this point
Copy the full SHA e9db1c7View commit details -
Report endpoint_payload.dropped when the orchestrator removes a store…
…d payload (#273) `FilesOrchestrator` silently deletes stored batches that are never uploaded — too old (`maxFileAgeForRead`) or purged to keep the directory under `maxDirectorySize`. Add an `onDrop` callback (invoked with the file's byte size, outside the state lock) wired to `UploadObserver.uploadDropped`, so those removals are counted as `endpoint_payload.dropped`. Successful upload deletions (`delete(readableFile:)`) are not reported. Wired for spans and coverage. Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 633388a - Browse repository at this point
Copy the full SHA 633388aView commit details
Commits on Jun 5, 2026
-
[SDTEST-3777] Gather remaining CI Visibility instrumentation telemetr…
…y metrics (#274) * [SDTEST-3777] Gather remaining CI Visibility instrumentation telemetry metrics - event_created/event_finished/test_session via TelemetryEventsFeature (fires at start hooks matching dd-trace-go; reads CI provider from SessionConfig.env) - events.enqueuedForSerialization via PayloadObserver.eventEnqueued() per write - events.manualApiEvents in @objc manual-API entry points - git.command/command_ms/command_errors via GitUploader.recordedGitCommand helper (real exit codes via public Spawn.RunError.exitCode; 128+signal convention) - git_requests.objects_pack_files, settings_response (with impacted_tests_enabled from JSON), response counts for TIA/known-tests/test-management - itr.skipped/unskippable/forced_run in TestImpactAnalysis hooks - code_coverage.started/finished/is_empty/errors/files through the coverage stack - error_type granularity: RequestObserver carries transportError so URLError.timedOut maps to .timeout instead of .network - App-lifecycle protocol: app-started (direct, with SDK config snapshot), heartbeat timer, app-closing batched before final flush - SessionConfig now holds env: Environment and config: Config; removed duplicate platform/service/metrics properties; TestSession/Module/Suite protocols gained var configuration so features access CI/git without touching DDTestMonitor Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * [SDTEST-3777] Add git.commit_sha_match / git.commit_sha_discrepancy metrics Detects commit-SHA and repository-URL discrepancies between the three git info providers (ci_provider, local_git, user_supplied) while assembling Environment.git. Discrepancies are stored directly on the Git struct as [ShaDiscrepancy] so callers access env.git.discrepancies / shaMatched without a separate wrapper type. Emitted once per session start via TelemetryEventsFeature (auto path) and DDSession.start() (manual path). Also moves FileWriter.eventEnqueued() before encoder.encode() so it fires at the point an event enters the write queue rather than inside the file orchestrator closure. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Move git SHA check emission to TestSession extension Extract emitGitShaCheck(to:) into a TestSession extension so both the auto-instrumented path (TelemetryEventsFeature.testSessionWillStart) and the manual API path (DDSession.start) share a single call site. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Fold TestModule/TestSuite configuration into protocol-extension defaults The configuration accessor on modules and suites just walked up the parent chain (session.configuration / module.configuration), duplicating a value already reachable via the existing session/module accessors. Drop the per-type declarations and the TestModule/TestSuite protocol requirements in favor of default implementations, mirroring the existing `session` convenience extensions. Only TestSession keeps configuration as a real (stored) requirement. Applied the same cleanup to the test mocks. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Fix test compilation errors after Telemetry and SessionConfig changes - Telemetry: make telemetryExporter optional so the new test-only init (testMetricExporter:resource:exportInterval:) can skip lifecycle (app-started, heartbeat, app-closing) without needing a real TelemetryExporter or TelemetryApi - TelemetryTests: use new test init; add missing transportError: nil to requestFinished calls - SwiftTestingTraitTests: update SessionConfig construction to the new (env:config:) signature, removing the removed platform/service/metrics parameters Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Fix FileWriterTests: add eventEnqueued() to RecordingPayloadObserver PayloadObserver gained eventEnqueued() when the per-event enqueue hook was added; update the test spy to conform. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Fix NTPClock crash and move Telemetry test init to test code Add SyncingClock wrapper: wraps any Clock and replaces the inner clock with DateClock on sync failure (via Synced<any Clock>). `now` always works — no crash regardless of sync state. Removes all DDTestMonitor.clock = DateClock() replacement sites. DDTestMonitor.clock is now always a SyncingClock. The convenience init in DDTracer calls sync() before creating the API service so the date provider is ready when app-started fires. Remove the test-only init from production Telemetry.swift. Move it to TelemetryTests.swift as a convenience init extension that delegates to the production init using a NoopTelemetryApi stub. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Fix testSettingsService_succeedsOnValidResponse: add impacted_tests_enabled to mock response The validSettingsBody helper was missing the impacted_tests_enabled field added to SettingsResponse; JSON decoding now requires it, so the test was failing with a keyNotFound error wrapped as a communication error. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * better clock types * Drop redundant config parameter from manager observer protocols TestSessionManagerObserver and TestModuleManagerObserver took the config alongside the session/module, but the model now exposes it directly via session.configuration / module.configuration. Drop the `with config:` parameter from all six methods and read it off the model inside the only real conformer (SessionAndModuleObserver). This also removes the now-unused stored `config` from DDModule's Stateless/StatefulManager and their initializers, since they only held it to forward to the observer. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Collapse TestSessionManager session/config tuple into a single session The manager exposed session, config, and a sessionAndConfig tuple, but every consumer split the tuple and stored config separately even though the session now carries it via session.configuration. Reduce the protocol to a single `session` getter and drop config/sessionAndConfig plus the derivation extension. Removes the now-redundant duplicate config storage that rode alongside the model in DDXCTestObserver.ModuleContext, SwiftTestingContext's ModuleContext and the suite factory, and SwiftTestingSuiteContext (whose configuration is now derived from suite.configuration). Callers read config off the model instead. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for ad6793e - Browse repository at this point
Copy the full SHA ad6793eView commit details
Commits on Jun 8, 2026
-
Replace OTel metrics SDK with a custom telemetry collector (#275)
* Replace OTel metrics SDK with a custom telemetry collector Telemetry distributions are sent to the DD intake as raw sample arrays so the backend computes the summary. The OTel-histogram path bucketed samples and then reconstructed approximate values from bucket midpoints on export — lossy (distorts percentiles) and complex (separate explicit/exponential code paths). Drop the OpenTelemetry metrics SDK (meter provider, views, periodic reader, the TelemetryMetricExporter bridge) in favor of a small lock-protected MetricStore: counters accumulate a per-interval delta, distributions keep raw samples, and each drain takes-and-resets the buffers (delta semantics). The store is drained to the durable TelemetryExporter via a new TelemetryPayloadExporter sink protocol. Two robustness refinements: - A short flush interval (10s) shrinks the window of in-memory metrics a crash could lose — export() persists to disk immediately and upload happens later on the exporter's own schedule, so this adds no network traffic. - When buffered distribution samples reach a cap, record() force-flushes so a burst is persisted rather than dropped (dd-trace-go drops on a full buffer). The metric tree and tag enums are unchanged; only the Counter/Distribution/ Factory handle internals now target the store. Tests rewritten against an in-memory payload sink; ARCHITECTURE.md updated. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Make telemetry flush interval and distribution buffer size configurable Wire the two collector tunables to Config so they can be overridden by env vars, mirroring DD_TELEMETRY_HEARTBEAT_INTERVAL: - DD_TELEMETRY_FLUSH_INTERVAL (seconds, clamped 1...3600, default 10) - DD_TELEMETRY_DISTRIBUTION_BUFFER_SIZE (samples, clamped >= 1, default 2048) DDTracer threads them from Config into Telemetry; the hardcoded defaults remain as the fallback for direct construction. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Key telemetry metrics by a tag Set instead of a sorted array Set equality/hashing is intrinsically order-independent, so the metric key no longer relies on remembering to sort tags at record time, and duplicate "key:value" entries collapse. The wire array is sorted once per series at emit time for deterministic output. Matches dd-trace-go semantics (same name + different tags = separate series). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Guard MetricStore state with Synced instead of a bare UnfairLock Wraps the counters/distributions/sample-count in a single Synced<State>, matching the locking pattern used elsewhere in the SDK (e.g. DDModule) rather than hand-rolling an UnfairLock. Behavior is unchanged: take-and-reset on flush via `defer { s = State() }`. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Raise default distribution buffer cap to 65536 The cap is a global safety backstop for bursts; the 10s timer does the normal draining. Samples are 8-byte Doubles, so a generous cap is cheap (65536 ≈ 512 KB) and keeps force-flush from triggering under normal CI telemetry volume. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Stamp telemetry metric points with the SDK clock, not Date() MetricStore now takes the NTP-synced SDK clock (DDTestMonitor.clock) and uses clock.now for count-series timestamps instead of a raw Date(), matching the rest of the SDK's time handling. Tests pass a DateClock(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Remove unused telemetry namespace properties from Resource telemetryMetricNamespace / telemetryDistributionNamespace were only read by the deleted OTel TelemetryMetricExporter bridge. The custom collector stamps the .civisibility namespace directly on each payload, so these Resource accessors have no remaining readers. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Merge duplicated metric types in the telemetry tree Most metrics differed only in name, not shape: an untagged counter, an untagged distribution, or a counter tagged solely by error_type / event_type / endpoint. Replace the ~30 near-identical leaf structs with six reusable handle types (NoTagCounter, NoTagDistribution, ErrorTypeCounter, EventTypeCounter, EndpointCounter, EndpointDistribution) plus matching Factory helpers. Metrics with a unique tag signature (events.created/finished, session.started, git.command*, settings_response, requests_errors, code_coverage library counter) keep their own struct. Call sites and wire output are unchanged; TelemetryMetrics.swift drops from 682 to 513 lines. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Add missing impacted_tests_enabled to mock backend settings response #274 added a required `impactedTestsEnabled` field to the SDK's SettingsResponse model but didn't update the integration-test mock backend, so the settings response it returned no longer decoded — failing every integration test that boots the SDK against the mock. Emit `impacted_tests_enabled` from MockBackend.buildSettingsResponse (configurable via Settings.impactedTestsEnabled, default false). Matches the field set the SDK decoder requires, as covered by LibraryConfigurationServiceThrowTests. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Add telemetry integration coverage; flush telemetry synchronously at shutdown Verifying telemetry end-to-end surfaced a real bug: Telemetry.shutdown() drained the in-memory store to the exporter and enqueued app-closing, but only called exporter.shutdown() (stop the worker) — never exporter.flush(). Since flush() is what synchronously uploads, the final batch (self-metrics + app-closing) sat on disk unsent at process exit; only the direct app-started POST reliably reached the backend. Add exporter.flush() before shutdown so the last batch is uploaded. Tooling to assert telemetry reaches the backend: - MockBackend.parseTelemetry decodes the captured /api/v2/apmtelemetry bodies (both the direct single-payload form and the message-batch envelope) into typed TelemetrySeries + observed event types, exposed via Requests.telemetry* accessors. - MockBackendTelemetryParsingTests unit-tests the parser against the wire format. - A telemetryReported integration test asserts app-started/app-closing events and generate-metrics (test_session) round-trip to the backend over a basic run. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 083936c - Browse repository at this point
Copy the full SHA 083936cView commit details -
Configuration menu - View commit details
-
Copy full SHA for 117d696 - Browse repository at this point
Copy the full SHA 117d696View commit details -
Refactor SpanMetadata.allTest from array to single SpanType value (#276)
Replace the loop over `SpanType.allTest: [Self]` with a single `"test_levels"` span type, allowing tag assignment to all test levels in one call instead of iterating over four types. Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 2cc3e85 - Browse repository at this point
Copy the full SHA 2cc3e85View commit details
Commits on Jun 9, 2026
-
Configuration menu - View commit details
-
Copy full SHA for 796d43b - Browse repository at this point
Copy the full SHA 796d43bView commit details
Commits on Jun 15, 2026
-
[SDTEST-3830] CI Visibility instrumentation telemetry: metrics + logs (…
…#279) * [SDTEST-3830] Resolve correct product version for app/framework under test DDTracer read Bundle.main directly, which is the bare xctest runner for host-less test bundles, so it reported a wrong/unknown application version. Add Bundle.productUnderTest, a resolver that picks the bundle representing the product being tested and reports its version, with priority: 1. DD_VERSION override (quick-exit: skips all discovery when set) 2. Bundle.main when it is an .app (app-/UI-hosted tests) 3. a dynamic framework named after XCODE_SCHEME_NAME, co-located with the .xctest bundle (sibling on macOS/simulator/device, or embedded), found among loaded frameworks or probed on disk (static frameworks ship in the products folder but are never loaded into memory) 4. the .xctest bundle version (static/SPM-merged products) 5. "<unknown>" The selection policy is a pure function over value-type descriptors so it is fully unit-tested. Adds DD_VERSION as a configuration key. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Detect Swift/runtime version via RuntimeInfo; recreate tracer on shutdown Replace the Mach-O LC_BUILD_VERSION compiler-version scanner (CompilerVersion.c/h) with RuntimeInfo: an Xcode/SPM enum exposing runtimeName, runtimeVersion and swiftVersion. It maps the XCTest bundle's DTXcode to a Swift version (Xcode 26+), falling back to a single `swiftc --version` spawn only when needed (avoids the subprocess on macOS for both Xcode and SPM). XCODE_SCHEME_NAME selects the case. Thread the runtimeName/runtimeVersion through APIServiceConfig and TestOptimizationApiService so TelemetryApplication reports them instead of hardcoding "swift". Make DDTestMonitor.tracer lazily recreatable: SessionManager.endSession now shuts the tracer down (which tears down the global OpenTelemetry provider), and DDTracer.deinit restores the default providers. The next session rebuilds a tracer that re-registers a live SDK provider, so spans stay SpanSdk instead of crashing the `as! SpanSdk` casts. The backing store is guarded by Synced to keep the lazy init/reset race-free. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Fix tracer-shutdown deadlock, network swizzling crash; refine device/runtime info - shutdownTracer(): clear the tracer slot under the lock but run shutdown() outside it. shutdown() flushes/logs/uploads and stdout/stderr capture routes back through DDTestMonitor.tracer, so holding the non-reentrant lock during shutdown deadlocked. - URLSessionInstrumentation: check URLSessionDelegate conformance via the ObjC runtime (InstrumentationUtils.classConforms, walking the superclass chain with class_conformsToProtocol) instead of a Swift `is` cast. The cast messages every class from objc_getClassList() and aborted on pathological internal classes (__NSGenericDeallocHandler, __NSAtom, __NSMessageBuilder). - EnvironmentTests: update to the refactored PlatformUtils API (getPlatformVersion) and assert runtime tags against getRuntimeInfo(). - Device/Platform/RuntimeInfo refactor wiring (getDeviceInfo/getKernelInfo). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Tie tracer lifecycle to the monitor; make network instrumentation self-contained Move the tracer off the global lazy accessor and onto the DDTestMonitor instance: it's created in init and torn down with the monitor (stop() flushes and shuts it down; DDTracer.deinit restores the default OpenTelemetry providers). Removed the static `tracer` accessor entirely — callers use `DDTestMonitor.instance?.tracer`, so no tracer is created when no monitor is installed (this is what caused the accidental backend connection), and there's no lock to deadlock on during shutdown. Session-scoped span creators (DDSession/DDModule/DDSuite/DDTest) get the tracer from SessionConfig.tracer, captured from the monitor when the session bootstraps. Ambient callers (stdout/stderr capture, crash, XCUI, instrumentation control) use the optional instance accessor and no-op without a monitor. DDNetworkInstrumentation is now self-contained: injectHeaders / recordPayload / disableNetworkCallStack / enableNetworkCallStackSymbolicated are its own properties passed at init, and the URLSessionInstrumentationConfiguration callbacks capture self weakly to break a retain cycle (config -> closures -> self -> urlSessionInstrumentation -> config). DDCrashes.install takes the tracer and returns CrashInformation? instead of reaching into DDTestMonitor.instance; the monitor stores the result. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Move upload-outcome logging into DataUploadWorker Restore the upload logging path: DataUploadWorker logs each batch's success/retry/drop with a failure description (DataUploadStatus.failureDescription), threaded through FeatureStoreAndUpload and the feature exporters. Remove the now-redundant per-request logs in the API services and the orphaned clientLibraryVersionHeader call. Drop the obsolete DeviceTests. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Simplify telemetry: own metric instruments + log handler Move telemetry metrics and logs off OpenTelemetry into hand-written stores, mirroring the existing MetricStore rationale (the DD intake wants raw samples / minimal log values, not OTel-shaped data). Metrics: drop the Typed* prefix — Counter<Tags>/Distribution<Tags> are now the only instruments, each paired with a per-metric MetricTags type and a constrained extension exposing the typed add/record. Removes the parallel untyped Counter/ Distribution/Factory and the one-off wrapper structs. Logs: add a LogStore + telemetry.logs.error/warn/debug handler on Telemetry, deduplicating identical logs into one entry with a count and draining on the shared flush timer as the `logs` request type. Tags reuse the metric rendering. Delete the now-unused OTel TelemetryLogExporter bridge and its test. Also send DD-Client-Library-Version on the telemetry request directly, and document both stores in ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Log request body and response data in HTTPClient debug mode Capture the uncompressed request body before deflate and log it alongside the response. Render payloads as UTF-8 text (JSON in nearly all cases), falling back to a `<binary N bytes>` marker when the bytes aren't valid UTF-8. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Log request/response headers; capture only needed log fields Add request and response headers to the HTTPClient debug log. Capture just the url, headers, and uncompressed body into a small LogFields value up front (only when debug is on), so the completion handler no longer retains the full URLRequest for the request's duration. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Send telemetry metric point timestamps as integer seconds TelemetryMetric.Series.Point.timestamp is now Int64 unix seconds (rounded), matching the intake's expected format, with a `time:` convenience init that rounds a TimeInterval. MetricStore stamps points with the rounded clock value. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Redact API/application keys from logged headers Replace DD-API-KEY and DD-APPLICATION-KEY values with **** in HTTPClient debug header logs (names sourced from HTTPHeader.Field, matched case-insensitively). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Disable telemetry debug flag Flip debugTelemetry to false for release; backend debug processing was only needed while validating the telemetry pipeline. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Upload unit-test JUnit reports to Datadog The SDK can't instrument its own test run, so report unit-test results by uploading the JUnit reports xcbeautify produces. Give each scheme/platform a distinct report filename so the two unit-test schemes no longer overwrite one junit.xml, then upload build/reports via datadog-ci in the unit-tests workflow (runs on failure too; skips gracefully when DD_API_KEY is absent, e.g. fork PRs). Use `npx --yes @datadog/datadog-ci` instead of a global install, here and in the codeQL SARIF upload. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * [SDTEST-3830] Parse backtick-quoted test function names with spaces The function-name regex used `\S+` for Swift symbols, truncating a backtick- quoted name with spaces (e.g. `failing parameterized test`(_:)) at the first space, so the source-line parser missed the function. Match backtick-quoted segments as a unit, and split the module on the last dot before the first backtick so a dot inside a quoted name isn't taken as the module separator. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for a145074 - Browse repository at this point
Copy the full SHA a145074View commit details -
Add granular timing logs to diagnose slow Install Test Monitor on sim…
…ulators Add Log.measure() calls inside installTestMonitor() and DDTracer.init to break down where the ~50s setup time is spent on simulator platforms. New measurements: - DDTestMonitor init - resolveHostname (developerMachineHostName / hostname subprocess) - NTP clock sync (waitForAsync on FallbackClock.sync) - TestOptimizationApiService init - Exporter init Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for f91c204 - Browse repository at this point
Copy the full SHA f91c204View commit details -
Fix intermittent tvOS crash enumerating Obj-C classes during URLSessi…
…on instrumentation (#281) * Fix tvOS crash enumerating Obj-C classes during URLSession instrumentation injectInNSURLClasses() enumerated every registered Obj-C class via objc_getClassList() and called class_copyMethodList on each. That list can include classes whose superclass chain references an unrealized stub class (weak-linked classes from unloaded frameworks, or relative-method-list stub classes). class_copyMethodList realizes the class, realization walks into the stub superclass, and the runtime aborts with "Attempt to use unknown class". Newer runtimes (Xcode 26.4 / tvOS) ship more of these stubs, which is why it surfaced now. Replace the unused, prefix-based objc_getSafeClassList with a real safety filter that keeps only classes whose entire superclass chain is itself in the class list, and use it from injectInNSURLClasses. Reading class_getSuperclass does not realize the class, so the filter never touches an unknown class. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * small log fix * Fix intermittent class-enumeration crash; drop superclass-walk workaround Root cause: objc_getClassList(buf, n) writes at most n entries but returns the total number of registered classes. During app/test launch other threads load frameworks and register classes concurrently, so the second call can return a count larger than the buffer we sized from the first call. Iterating to that larger count read uninitialized memory and produced garbage "class" pointers, which crashed intermittently as "Attempt to use unknown class" / bad access the moment they were dereferenced. Clamp the loop to the allocated capacity. This was the actual bug, so the earlier superclass-chain safe-list workaround is no longer needed and is removed (it also made things worse by dereferencing the garbage pointers extra times). Keep the classConforms optimization, which skips class_copyMethodList for the thousands of classes that don't adopt URLSessionDelegate, and drop the unused objc_getSafeClassList helper and unused imports. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> * Grow-and-retry objc_getClassList so no concurrently-added class is dropped Instead of clamping to the initial buffer capacity (which would silently drop classes registered mid-scan, possibly missing real URLSession delegates), keep re-querying and growing the buffer until objc_getClassList's returned total fits what we allocated. This captures every class while still never reading past the buffer. Small headroom lets it settle in a single retry under typical concurrent framework loading. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 4f6f247 - Browse repository at this point
Copy the full SHA 4f6f247View commit details
Commits on Jun 16, 2026
-
Propagate simulator device/OS tags from inner test spans to runner sp…
…ans (#283) * Propagate simulator device/OS tags from inner test spans to runner spans The integration test runner always executes on macOS, so its DDTest spans were tagged with macOS device info even when the inner tests ran on a simulator (iOS, tvOS, watchOS, visionOS). After xcodebuild finishes, copy os.platform, os.architecture, os.version, device.name, and device.model from the first received span onto DDTest.current — but only when the inner platform is not macOS, where the runner tags are already correct. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Fix simulator device tag propagation: read from envelope metadata directly Device/OS tags are written into the envelope-level metadata under the "test_levels" key by the SDK. SpanEvent.extend only merges metadata keyed by span.type ("test", "span", etc.) and "*", so span.meta never contains os.platform — making the previous guard always fail and the whole block get skipped. Fix: read from envelope.metadata["test_levels"] directly. For each device tag key, prefer a value found in individual span meta (spans can override via set(tag:)), falling back to the envelope metadata value. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Fix span metadata merge: add test_levels priority, scope to test* spans only Merge priority (highest to lowest): span.meta > metadata[span.type] > metadata["test_levels"] > metadata["*"] metadata["test_levels"] carries device/OS tags shared across all test-level events. It is applied only to test* spans (.test, suiteEnd, moduleEnd, sessionEnd) — not to generic "span" events where it does not belong. With test_levels now correctly merged into extended spans, the runner's device tag propagation can read from allSpans.meta directly (reverted from the previous direct envelope.metadata["test_levels"] workaround). Co-Authored-By: Claude Sonnet 4.6 <[email protected]> --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 65b0cd5 - Browse repository at this point
Copy the full SHA 65b0cd5View commit details
Commits on Jun 17, 2026
-
Fix 40+ second simulator test setup delay (#282)
* Add loadSourceCodeInfo and activeFeatures timing measurements Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Measure lag between log.measure start and closure execution Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Measure config and env static property init time in installTestMonitor Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Fix 16s simulator setup delay: move git fetch off the critical path Environment.init() called mergeHeadInfo() synchronously, which runs `git fetch --deepen=1` on simulators. On CI simulators this fetch times out (~16s), blocking the entire test setup. Fix: remove mergeHeadInfo from Environment.init() and schedule it on instrumentationWorkQueue instead. setupCrashHandler() already waits for this queue before the session span is created, so the commit author/message metadata is still available when needed. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Add queue-wait logs; move mergeHeadInfo to Environment.fetchMergeHeadInfo - Wrap all waitUntilAllOperationsAreFinished calls with Log.measure so queue drain time is visible in debug output - Add Environment.fetchMergeHeadInfo(log:) instance method that calls mergeHeadInfo and updates self.git, wrapped in Log.measure - Add DDTestMonitor.loadGitCommitInfo() that schedules fetchMergeHeadInfo on instrumentationWorkQueue — called from installTestMonitor, not gated by disableSourceLocation (commit metadata is unrelated to source location) Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Add granular timing logs inside Environment.init Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * start loading of merge head ASAP * Fix build: use local var to avoid capturing self before init completes Log.measure closures inside Environment.init that referenced the stored property `sourceRoot` caused "self captured before all members initialized". Introduce a local `sourceRootValue` binding and use it inside the closures. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Fix 26s GitHub CI reader delay: add depth limit to glob expansion getJobId() expands /**/_diag globs by recursively scanning /Users/runner/actions-runner/, which on a CI machine can be a huge directory tree. Added a maxWildcardDepth=3 cap to expandGlobSegments so the ** wildcard stops expanding after 3 levels — the _diag directory is always 1-2 levels deep, so this preserves full functionality. Also add per-reader timing to env-ciReaders loop. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Move git ops to gitUploadQueue with proper ordering - mergeHeadInfo moves from instrumentationWorkQueue to gitUploadQueue (first operation); reference saved in mergeHeadOperation - gitStatusUpToDate (git subprocess) moves inside the upload operation so it no longer blocks test startup - uploadOp gets a dependency on mergeHeadOperation so env.git is fully populated before the upload runs - updateTracerConfig: when ITR does not require git upload, wait only for mergeHeadOperation (not the full queue) to get commit metadata Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Optimise GithubCI job-ID scan: lazy glob walk with early exit Replace the collect-all-then-search approach in getJobId with a FileManager.searchGlob extension that short-circuits as soon as the first Worker_ log match is found. Key changes: - FileManagerExtensions.swift: new FileManager.searchGlob(_:maxWildcardDepth:body:) walks a glob pattern (supporting **) calling body on each matched directory; returns true and stops on the first body hit. ** with a tail only calls body on dirs matching the full subpath; ** as the last segment calls body on every dir in the subtree. Walking starts from the longest concrete prefix before the first **, not from /. - GithubCI.swift: getJobId iterates _diagnosticDirs lazily via searchGlob and returns as soon as a match is found, so specific dirs (no **) are checked before the broad ** patterns. - FileManagerExtensionsTests.swift + fixtures/glob/: 12 tests covering exact match, missing path, tail vs no-tail body-call semantics, early exit, depth capping, and scoped root. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Make searchGlob generic: body returns T? instead of Bool body returning nil means "keep going"; any non-nil value stops the walk and is returned directly, eliminating the need for callers to capture a mutable variable to carry the result. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> * Rename searchGlob closure param from body to matcher; adopt linter fixes Co-Authored-By: Claude Sonnet 4.6 <[email protected]> --------- Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 0ccc4d2 - Browse repository at this point
Copy the full SHA 0ccc4d2View commit details -
Configuration menu - View commit details
-
Copy full SHA for 9a7ff7e - Browse repository at this point
Copy the full SHA 9a7ff7eView commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff 2.7.4...2.7.5