-
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.3
head repository: DataDog/dd-sdk-swift-testing
compare: 2.7.4
- 9 commits
- 143 files changed
- 3 contributors
Commits on May 15, 2026
-
[SDTEST-3768] Introduce async API wrappers for the test-optimization …
…backend (#251) Replace the four synchronous service classes (Settings, KnownTests, TestManagement, ITR) with a typed-throwing async API layer under Sources/EventsExporter/API/. The new TestOptimizationApiService composes SettingsApi/KnownTestsApi/GitUploadApi/TestImpactAnalysisApi/ TestManagementApi/SpansApi/LogsApi; EventsExporter drives them synchronously through waitForAsync, which moves from DatadogSDKTesting into EventsExporter/Utils/Async.swift so it can be shared. Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 03fd3cd - Browse repository at this point
Copy the full SHA 03fd3cdView commit details
Commits on May 18, 2026
-
[SDTEST-3769] Add Telemetry API wrapper (metrics, logs, batches, app …
…lifecycle) (#256) Add an agentless Telemetry API wrapper alongside the existing TestOptimization APIs. Sends to /api/v2/apmtelemetry on instrumentation-telemetry-intake.<site>. Owns a per-runtime seq_id counter (overflow-safe via &+=) and reuses the exporter's client UUID as runtime_id so telemetry correlates with traces. Methods: - sendMetrics(_:namespace:) and sendLogs(_:) for the two metrics/logs request types. Public domain types: TelemetryMetric.{Series,Point,MetricType,Namespace} and TelemetryLog{.Level}. Point encodes as [ts, value] per spec. - sendAppStarted(products:configuration:error:installSignature:), sendAppHeartbeat(), sendAppClosing() for the lifecycle events. Public types: TelemetryError, TelemetryProductInfo, TelemetryProducts, TelemetryConfigItem, TelemetryConfigOrigin, TelemetryInstallSignature. Heartbeat / closing emit payload: {} since the envelope schema requires payload. - send(batch:) overloaded for [TelemetryBatchItem], URL, and Data. The structured variant wraps inner request_types as a message-batch with a fresh envelope; the URL/Data variants POST a pre-built envelope verbatim (matching LogsApi/SpansApi shape). Other changes: - APIServiceConfig gains serviceName/environment (used by the telemetry application/host blocks). - Synced/UnfairLock/LazySynced moved from DatadogSDKTesting/Utils/Synced.swift to EventsExporter/Utils/Synced.swift and made public so both modules share them; DatadogSDKTesting files that consume Synced now import EventsExporter. - GitUploadApi.uploadPackFiles uploads files concurrently via withThrowingTaskGroup. Telemetry is exposed but not yet consumed by EventsExporter; future work will wire it into the upload pipeline. Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for be3faff - Browse repository at this point
Copy the full SHA be3faffView commit details
Commits on May 25, 2026
-
[SDTEST-3770] New exporter pipeline (OTel-native spans, logs, coverag…
…e) (#258) * [SDTEST-3770] Port exporter file/upload pipeline to the new API wrappers Rewire SpansExporter / LogsExporter / CoverageExporter to drive the typed test-optimization API services (SpansApi / LogsApi / TestImpactAnalysisApi) instead of the legacy RequestBuilder + DataUploader stack. The exporters' public surface is unchanged — EventsExporter still exposes exportEvent, the URL-based coverage entry, the configuration-API facade, and SpanExporter conformance, so no consumer in DatadogSDKTesting had to move. Internals: - DataFormat gains DataFormatType and a JSONFileHeader-driven init that builds a `{...header..., "<batch>": [...]}` envelope around each feature's entries. - FilesOrchestrator gains FilesOrchestratorType; getWritableFile now returns (file, isNew) so FileWriter can emit `prefix` on a fresh file and `separator` on reuse. New closeWritableFile()/getAllReadableFiles() back the synchronous flush path. - FileWriter takes an injected encoder + entity label, supports update(dataFormat:) and closeCurrentFile() (under barrier); writeSync throws. - FileReader: readNextBatch -> getNextBatch() throws; markBatchAsRead throws; new getRemainingBatches() returns a Batch.Iterator. - DataUploader -> ClosureDataUploader: adapts (Data) async throws(HTTPClient.RequestError) -> Void to DataUploadStatus, driven through the existing waitForAsync shim. - DataUploadWorker: extract DataUploadWorkerType (update / flush / stop); flush() synchronously drains pending batches with retry, honoring server Retry-After via Delay.set(delay:); shutdown is now stop(). - DataUploadStatus gains waitTime + an (httpCode:headers:) init that parses Retry-After / X-RateLimit-Reset. - FeatureStorage + FeatureUpload collapse into FeatureStoreAndUpload. - TestImpactAnalysisApi gains uploadCoverage(batch:) (multipart POST to /api/v2/citestcov). - Deleted Upload/RequestBuilder.swift + Upload/MultipartFormDataRequest.swift (and their Xcode project entries). - Consolidated on JSONEncoder.apiEncoder: outputFormatting gains .withoutEscapingSlashes so existing URL/string serialization stays byte-for-byte; JSONEncoder.default() factory removed. - APIServiceConfig gains init(configuration:) so sub-exporter tests can build a SpansApiService/LogsApiService directly without duplicating the field mapping in EventsExporter. Tests: - Persistence/Upload/Spans/Logs tests updated for the new init shapes and method names; new DataFormat.formatFileContents(_:) test helper lets FileReaderTests seed a file with prefix-included contents and assert the read result preserves the prefix. - MockClosureDataUploader replaces the old DataUploader-backed mock in the upload worker/uploader tests, keeping MockHTTPClient as the request recorder. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Adopt OTel SpanExporter/LogRecordExporter conformance + Resource Phase 2 of the exporter refactor. The data path now follows the OTel contract end-to-end: the SDK delivers SpanData / ReadableLogRecord through the protocols, and the per-feature exporters read service / version / environment / SDK info from `SpanData.resource` instead of carrying them as init args. EventsExporter's facade (exportEvent, SpanExporter conformance, the configuration-API methods) is unchanged. Internals: - New OpenTelemetry+Extensions.swift: typed accessors on `Resource` (`service`, `applicationName`, `applicationVersion`, `environment`, `sdkName`, `sdkLanguage`, `sdkVersion`) backed by `SemanticConventions`, `SpanData.testSessionId/SuiteId/ModuleId`, and `ResultCodeCompatible &&` overloads on `SpanExporterResultCode` / `ExportResult`. - `DDSpan(spanData:)` drops the `serviceName` / `applicationVersion` arguments and reads them off `spanData.resource`. - `DDLog` gains an init taking `(spanId, traceId, timestamp, status, message, resource, attributes)`; `DDLog(event:span:)` and a new `DDLog(log: ReadableLogRecord, span: SpanContext)` use it. The legacy `DDLog(event:span:configuration:)` is gone. `Status` gains a `init(severity: Severity)` bridge. - `SpansExporter` conforms to `OpenTelemetrySdk.SpanExporter`. Adds `setMetadata(_:)` which rebuilds the headerful `DataFormat` (keeping the per-runtime `runtime-id` pinned) and rotates the writable file via `FeatureStoreAndUpload.update(dataFormat:)`. - `LogsExporter` conforms to `OpenTelemetrySdk.LogRecordExporter`, exposing `export(logRecords:) -> ExportResult`, `forceFlush`, and `shutdown(timeout:)`. The stored `configuration` field is gone. - New `SpanEventsLogExporterAdapter`: a `SpanExporter` that forwards each span's events to `LogsExporter.exportLogs(fromSpan:)`. Wrapped with the real `SpansExporter` inside an `OpenTelemetrySdk.MultiSpanExporter`, so calling `EventsExporter.export(spans:)` lands both spans on the spans pipeline and span events on the logs pipeline transparently. - `EventsExporter` exposes `setMetadata(_:)` on the protocol; it forwards to `spansExporter.setMetadata` (the only feature whose file header embeds metadata). - `DDTracer` builds a `Resource` populated from `Config` / `Environment` / `DDTestMonitor.tracerVersion` and passes it to `TracerProviderBuilder.with(resource:)` so the new resource-reads in the encoders see real values. Inner `DDTracer.init` gains an optional `resource` parameter, defaulting to an empty `Resource()` for existing call sites that don't supply one (e.g. tests). Tests: - New `SpanCompositesTests` covering `SpanEventsLogExporterAdapter` end-to-end against `MockBackend`. - `SpanSanitizerTests` updated for the simplified `DDSpan(spanData:)` initializer. - Pre-existing `EventsExporterTests` already covers the `MultiSpanExporter`-driven span-and-logs flow and passes unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Switch coverage to record-based export Phase 3 of the exporter refactor. The coverage pipeline now takes pre-parsed `CoverageRecord`s instead of a (URL, parser) pair, matching the OTel-style boundary used by the spans/logs exporters: the caller owns the profraw lifecycle, parses it, and hands the exporter a typed record carrying the OTel anchors and an explicit `Context` (test vs. suite). Internals: - `CoverageRecord` (public) carries `name`, parsed `CoverageInfo`, `workspacePath`, `Resource`, `InstrumentationScopeInfo`, and a `Context` enum (`.test(testSpanId:, suiteId:, sessionId:)` / `.suite(suiteSpanId:, sessionId:)`). Convenience accessors expose `sessionId`, `suiteId`, `testId`, and `isSuite`. - `CoverageExporterType` (public) — `export(coverageRecords:, explicitTimeout:) -> ExportResult`, `forceFlush(explicitTimeout:)`, `shutdown(explicitTimeout:)`, plus no-arg convenience overloads. `CoverageExporter` conforms; its old URL-based `exportCoverage(coverage:parser:...)` entry point is gone. The exporter still translates each record into a `TestCodeCoverage` on disk (suite-level records serialize `span_id: 0`). - `EventsExporterProtocol` swaps `export(coverage:parser:...)` for `export(coverageRecords:)`. `EventsExporter` no longer imports `CodeCoverageParser`. Consumer side: - `DDCoverageHelper.endTest` switches its signature from raw `UInt64`s to `SpanId`s. The helper now owns the full per-test flow: it parses the profraw via `CoverageProcessor.filesCovered(in:)`, builds a `.test` `CoverageRecord`, calls `exporter.export(coverageRecords:)`, and applies the debug-save / delete decision based on `configuration.debug.saveCodeCoverageFiles`. - `TestImpactAnalysis.testDidFinish` passes `SpanId` values straight through (no more `.rawValue` unwrapping). - `Mocks.CoverageCollector` in the test mocks adopts the new `TestCoverageCollector` signature; the existing UInt64-keyed `has` helper keeps the same shape for the assertions that still use it. Tests: - New `CoverageExporterTests` covering both the test-level and suite-level record paths end-to-end against `MockBackend`. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Add pipeline integration test (spans, logs, coverage) PipelineIntegrationTests drives all three exporter paths in a single test against MockBackend and asserts the wire payloads: - A span carrying a stdout-capture-shaped event ("logString" + a "message" attribute) — exercises the SpanEventsLogExporterAdapter that converts span events into log entries. - A native OTel LogRecord emitted via a LoggerProvider whose SimpleLogRecordProcessor is wired to LogsExporter — exercises LogsExporter.export(logRecords:) (severity -> DDLog.Status, body -> message, span context -> dd.trace_id / dd.span_id). - A CoverageRecord submitted through EventsExporter.export(coverageRecords:) — exercises the CoverageRecord -> TestCodeCoverage conversion. The test then verifies, per pipeline, that: - The span's service / version / name come from the Resource. - Both logs carry the same span context as the source span (correlation via dd.trace_id / dd.span_id) and pick up service / version from the same Resource. - The OTel log's severity (`.warn`) maps to `status: warn` on the wire. - The coverage payload carries the expected session / suite / span IDs. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Add full-stack integration tests (XCTest + Swift Testing) End-to-end coverage for the three telemetry pipelines the SDK now ships: stdout capture -> span event -> SpanEventsLogExporterAdapter -> LogsExporter, native OTel LogRecord -> LoggerProvider -> LogsExporter, and per-test code coverage -> CoverageExporter. OTel LogRecord plumbing: - EventsExporter conforms to OpenTelemetrySdk.LogRecordExporter via extension; export(logRecords:) and forceFlush(explicitTimeout:) are forwarded to the wrapped LogsExporter and shutdown(explicitTimeout:) is shared with the SpanExporter conformance. EventsExporterProtocol inherits from both SpanExporter and LogRecordExporter. - DDTracer.init builds a LoggerProviderSdk with the EventsExporter as the LogRecordExporter (NoopLogRecordExporter when traces are disabled), the same Resource as the TracerProvider, and a SimpleLogRecordProcessor; the provider is registered globally via OpenTelemetry.registerLoggerProvider, so any consumer can emit logs through `OpenTelemetry.instance.loggerProvider` and they will land in the same upload pipeline as the span-event-derived ones. Integration tests: - IntegrationTests-UnitTests links OpenTelemetryApi so the child test bundle can call OpenTelemetry directly. - IntegrationTests-UnitTests.xctestplan enables LLVM code-coverage instrumentation; without it DDCoverageHelper has nothing to parse and the coverage pipeline never fires. - IntegrationTests/UnitTests/XCTestSmokeTests.swift gains XCStdoutOTelAndCoverage, and SwiftTestingSmokeTests.swift gains STStdoutOTelAndCoverage. Each child test print()s a line (stdout pipeline), emits an OTel LogRecord at Severity.warn (OTel pipeline), and passes (coverage pipeline runs implicitly via DDCoverageHelper.endTest). - IntegrationTests/Runner/UnitTests+XCTestSmoke.swift and UnitTests+SwiftTestingSmoke.swift gain `stdoutOTelAndCoverage` outer runner tests that: * Drive the child test under the real SDK in a subprocess, with MockBackend settings enabling itr/codeCoverage and the DD_CIVISIBILITY_CODE_COVERAGE_ENABLED env var on. * Assert one test span arrived (status pass, expected resource/name/suite). * Assert both log entries are present on the wire, and that the OTel-shaped one carries status `warn` (Severity.warn -> DDLog.Status.warn). * Assert at least one coverage payload arrived. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Drive sessions/modules/suites and span events through OTel Closes the loop on the native OTel data path: the EventsExporter no longer exposes `exportEvent` or `exportLogs(fromSpan:)`, and producers hand the SDK either a `SpanData` (via SimpleSpanProcessor + SpanExporter) or a `ReadableLogRecord` (via LogRecordExporter) — the upload pipeline consumes only OTel-shaped inputs. Spans path: - Sessions / modules / suites no longer write pre-encoded `SessionEnvelope` / `ModuleEnvelope` / `SuiteEnvelope` JSON directly into the spans pipeline. Each emits an `OpenTelemetrySdk.SpanSdk` through a new `DDTracer.createLifecycleSpan(name:spanId:startTime: attributes:)` helper that wires `SpanSdk.startSpan` with the registered span processor / clock / resource and a `SpanContext` whose `spanId` is the lifecycle object's controlled id (preserving the `test_session_id` / `test_module_id` / `test_suite_id` semantics on the wire). The span flows through the registered `SimpleSpanProcessor → EventsExporter → MultiSpanExporter → SpansExporter` pipeline. - `SpansExporter.exportSpan` switches on `span.attributes["type"]` and routes to per-type encoders: `test` -> `CITestEnvelope`, `test_session_end` -> new `TestSessionEnvelope`, `test_module_end` -> new `TestModuleEnvelope`, `test_suite_end` -> new `TestSuiteEnvelope`, otherwise `SpanEnvelope`. - New `TestLifecycleEncoders.swift` adds `DDTestSessionSpan` / `DDTestModuleSpan` / `DDTestSuiteSpan` plus their envelopes. They read everything they need from `SpanData` (start, duration, status, attributes, name, resource) and reproduce today's Session/Module/Suite content byte-for-byte (no `trace_id` / `span_id` / `parent_id`, just `test_*_id` plus start/duration/error/name/resource/service/meta/metrics). - `Session`, `Module`, `Suite` drop their `Encodable` conformance and the bespoke `encode(to:)` + `*Envelope` types. Logs path: - `SpanEventsLogExporterAdapter` no longer calls `LogsExporter.exportLogs(fromSpan:)`. It builds a `ReadableLogRecord` per `SpanData.Event` (carrying span context, resource, instrumentation scope, severity inferred from the optional `status` attribute, the event's attributes, and `eventName = event.name`) and hands the batch to the wrapped `LogRecordExporter` via `export(logRecords:)`. The adapter's init now takes a `LogRecordExporter` instead of a concrete `LogsExporter`. - `LogsExporter.exportLogs(fromSpan:)` is removed. `DDLog(event:span:)` follows. - `LogsExporterTests` is rewritten against `export(logRecords:)`. `EventsExporterProtocol` loses `exportEvent`; the concrete `EventsExporter` loses both that method and the `SpanEventsLogExporterAdapter(logsExporter:)` initialiser. The remaining public surface — `export(spans:)`, `export(logRecords:)`, `export(coverageRecords:)`, `setMetadata`, the configuration-API facade — is now uniformly typed against OTel inputs. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Strengthen stdoutOTelAndCoverage assertions The integration tests now verify the SDK linked the data correctly, not just that each pipeline produced *something*: - Capture the test span's trace_id / span_id / test_session_id / test_module_id / test_suite_id from `backend.allTestSpans`. - For both logs (stdout and OTel), assert `dd.trace_id` and `dd.span_id` on the wire match the test span — confirming the SpanEventsLogExporterAdapter and LogRecordExporter pipelines both stitch span context onto the log records. - For coverage, look up the payload by `spanId == testSpan.spanId` rather than blindly grabbing the first entry, then assert `testSessionId` / `testSuiteId` match the test span. Same expectations apply to the XCTest and Swift Testing flavors. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Enable stdout/log capture in stdoutOTelAndCoverage runs `Config.enableStdoutInstrumentation` (and the companion stderr flag) default to `false`. They only flip on via `DD_CIVISIBILITY_LOGS_ENABLED` (umbrella) or the per-stream `DD_ENABLE_STDOUT_INSTRUMENTATION` / `DD_ENABLE_STDERR_INSTRUMENTATION` env vars. The integration test harness didn't set any of them, so the child SDK never installed the StdoutCapture hook — `print()` went out untouched and no `logString` span event was attached to the test span. The OTel log path (LoggerProvider) is governed by the same umbrella flag, so it was also a no-op. Add `DD_CIVISIBILITY_LOGS_ENABLED=true` to both flavor configs so the child process actually installs the capture hook and the logger provider for the duration of the test. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Emit stdout/stderr captures as OTel LogRecords The capture hooks (`StdoutCapture` / `StderrCapture`) and the test-error context path in `DDTest` used to attach their content to the active test span via `span.addEvent(name: "logString", ...)`. The `SpanEventsLogExporterAdapter` then turned those events into log entries on flush. This added a layer of indirection — log payloads piggy-backed on span data instead of being first-class telemetry. `DDTracer` now owns a cached `LoggerSdk` built from the same LoggerProvider it registers with `OpenTelemetry`, and the public `logString` / `logError` / `logString(...timeIntervalSinceSpanStart:)` APIs emit OTel `LogRecord`s directly: - `setBody(.string(message))` — message goes out as the log body, no longer as a `"message"` attribute. - `setSeverity(.info)` for stdout and step lines, `.error` for test-assertion error context. - `setAttributes(testAttributes())` — keeps `test.name` / `test.suite` / `test.module` on the log record. - `setTimestamp(...)` only when the caller provided one (e.g. the parsed system-log date or the UI-step offset). - Trace context auto-injection (`includeTraceContext` defaults to true on `LoggerBuilderSdk`) attaches the active span's `dd.trace_id` / `dd.span_id` automatically. The UI-test launch-context path (`logStringAppUITested`) no longer synthesises an aux span; it now emits a `LogRecord` with the launch `SpanContext` set explicitly via `setSpanContext(...)`. The wire shape is unchanged — the OTel `LogRecord` flows through the registered `SimpleLogRecordProcessor` to `LogsExporter`, which writes the same `DDLog` JSON as before (body becomes the `message` field, severity drives `status`, span context drives `dd.trace_id` / `dd.span_id`). The `SpanEventsLogExporterAdapter` stays in place as a general OTel convenience for consumers that attach events to their own spans, but the SDK no longer drives it from internal capture. Tests: - `StdoutCaptureTests` / `StderrCaptureTests` assert the buffer is drained and that the active span has no events (the old assertion was on `spanData.events.count == 1`). Round-trip emission is exercised by the IntegrationTests' `stdoutOTelAndCoverage` cases. - `DDTracerTests.testLogStringAppUI` flips from "an aux span must be emitted" to "no span must be emitted — the message must go out as a LogRecord" to lock in the new behaviour. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Verify stdout/stderr capture lands as OTel LogRecords The capture tests previously asserted only that the span wasn't mutated; they didn't verify the LogRecord was actually emitted. Plug in OTel's own `InMemoryLogRecordExporter` so the tests can introspect the wire payload directly. - DDTracer gains an optional `logRecordExporter: LogRecordExporter?` init parameter (defaults nil → existing behaviour). When set, the internal `LoggerProviderSdk` wires that exporter via `SimpleLogRecordProcessor` instead of the EventsExporter / NoopLogRecordExporter pair. The convenience init threads it through too. - `StdoutCaptureTests` / `StderrCaptureTests` install a DDTracer backed by an `InMemoryLogRecordExporter` as the global `DDTestMonitor.tracer` (which `StdoutCapture` / `StderrCapture` route through), drive `print()` / `StderrCapture.stderrMessage`, then assert on `getFinishedLogRecords()`: * body matches the captured text (system-log prefix stripped / newline preserved as appropriate) * severity is `.info` * `spanContext.spanId` / `traceId` correlate to the active test span — confirming the LoggerSdk's auto-injection of span context works end-to-end * for stderr the parsed system-log timestamp lands on `record.timestamp` * the empty-buffer-without-newline case produces zero records Original tracer is restored in `tearDown`. - `DDTracerTests.testLogStringAppUI` flips from "no aux span is produced" to "no aux span AND the LogRecord carries the launch traceId/spanId from env" — verifies explicit `setSpanContext(launchSpanContext)` works. `@testable import OpenTelemetrySdk` is needed because `InMemoryLogRecordExporter()` is `public` on the class but its synthesised init is `internal`. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Rework coverage architecture with OTel-style processor Mirror the spans / logs pipeline for coverage so the SDK side owns the parser dependency and the exporter module stays parser-agnostic. Also move all threading into the processor so the provider's API stays synchronous. Data shapes (EventsExporter): - `CoverageContext` enum lifted out of the old `CoverageRecord.Context` and shared between the URL-stage record and the parsed payload. - `CoverageFile { name: String, bitmap: Data }` is the wire-ready per-file entry; no `CoverageInfo` dependency. - `CoverageData` (renamed from `CoverageRecord`) is the processed payload — `files: [CoverageFile]`, plus the OTel anchors. - `CoverageRecord` is now the URL-stage input: `coverageFileURL: URL` plus the OTel anchors. Pipeline (EventsExporter): - New `CoverageProcessor` protocol — `onEnd(record:)`, `forceFlush`, `shutdown`. The protocol is the only coverage surface that lives in `EventsExporter`; concrete implementations belong on the SDK side. - `CoverageExporterType.export(coverageData:, explicitTimeout:)` replaces `export(coverageRecords:)`. `EventsExporter` conforms. - `TestCodeCoverage` builds from `[CoverageFile]` directly and does workspace-prefix stripping inline. The old `CoverageInfo`-based init and the `coveredLines` extension are gone. - `import CodeCoverageParser` removed from every file in `EventsExporter`; the Xcode project drops the package product / target dependency on `CodeCoverageParser` from the `EventsExporter` target. SDK side (DatadogSDKTesting): - `DDCoverageHelper` → `CodeCoverageProvider`. Modelled after `TracerSdk` / `LoggerSdk`: one per process, owns the LLVM gatherer and a `CoverageProcessor`. - `TestCoverageCollector` protocol reshaped from `startTest()` / `endTest(testSessionId:testSuiteId:testSpanId:)` into `startCoverage(context:) -> ActiveCoverage?`. The returned handle has a single `end()` method (idempotent) that stops LLVM gathering and ships the record. `ActiveCoverage` exposes its `context`. - New `BackgroundCoverageProcessor` (was `SimpleCoverageProcessor` before; renamed because it owns an `OperationQueue` and is no longer simple). Builds the queue from a `CodeCoveragePriority`, parses each `CoverageRecord` URL on a background op, builds `CoverageData`, and forwards to a `CoverageExporterType`. `forceFlush` / `shutdown` drain the queue before calling through to the exporter. Optional `cleanupCoverageFiles` flag controls whether the queued op deletes the profraw after parsing. - `LLVMCoverageProcessor.swift` carries a typealias for `CodeCoverage.CoverageProcessor`, isolated in a file that doesn't import `EventsExporter` so the OTel-protocol name stays unambiguous in `CodeCoverageProvider` (which imports the new protocol). - `CodeCoverageProvider` no longer owns threading — `endTest` / `stop()` become straight-line handoffs to the processor. `init` passes `cleanupCoverageFiles: !debug` so debug-saved profraws survive. `TestImpactAnalysis`: - Holds a single `Synced<CoverageState>` slot (`collector` + `active: ActiveCoverage?`) instead of a separate stored `coverage` property and ad-hoc bookkeeping. `testWillBegin` calls `collector.startCoverage(context:)` and stashes the handle; on conflict (overlapping session) it ends the prior, logs the error, and nils out the collector — once cleared coverage stays off for the rest of the run, no separate `disabled` flag needed. - `testDidFinish` pops `state.active` and calls `.end()`. - `import OpenTelemetryApi` added for `SpanId`. Tests: - `CodeCoverageModelTests` (EventsExporter) moves to `Tests/DatadogSDKTesting/Coverage/CoverageBitmapTests.swift` since the bitmap-encoding logic now lives in the SDK module (`CoverageFile.init(file:)`). - `CoverageExporterTests` / `PipelineIntegrationTests` build `CoverageData` directly and assert against `export(coverageData:)`. - `Mocks.CoverageCollector` follows the new protocol: nested `Active: ActiveCoverage` calls back into the parent on `end()`. - Stale `LLVMSimpleCoverageFormat.swift` (unused, orphaned) removed. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Isolate CoverageExporterTests from shared Caches state CoverageExporter writes to a fixed Caches subpath shared by every test in the class, so the first test's payload was re-uploaded by the second test's MockBackend reader. Wipe the directory in setUp/tearDown. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Unify TestSpan/DDSpan encoders and tighten wire shape Collapses DDTestSessionSpan / DDTestModuleSpan / DDTestSuiteSpan and the CITestEnvelope test-span path into a single TestSpan with a SpanType discriminator, leaving DDSpan responsible only for generic spans. Test/session/module/suite events now share one encoder, write the inner `type` field (matches .NET wire format), and strip resource/type/version from meta so per-event payloads carry only test-specific tags — common metadata like language/library_version/test_session.name flows through SpanMetadata instead. Test models expose a unified `attributes: [String: TestAttributeValue]` with `get(tag:)` / `get(metric:)` in place of separate `tags` / `metrics` dicts. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Ignore .claude/ agent state Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Fix integration tests for new wire shape The TestSpan refactor moved `resource` to a top-level field on the event (matching .NET) and stripped it from meta. Update the smoke integration suites to assert on `span.resource` instead of `meta[DDGenericTags.resource]`. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Forward log/coverage env vars to integration test bundle The xctestplan files declare an explicit allowlist of environment variables to forward from xcodebuild's process env to the test bundle subprocess via `$(VAR)` substitution. `DD_CIVISIBILITY_LOGS_ENABLED` and `DD_CIVISIBILITY_CODE_COVERAGE_ENABLED` weren't in the list, so the `XcodeTestRunner.Config.environment` overrides set in `stdoutOTelAndCoverage` never reached the child SDK. The umbrella logs flag stayed off, `StdoutCapture` was never installed, `print()` from the test bypassed the SDK entirely, and the OTel `LoggerProvider` was also a no-op — so backend.allLogs came back empty. Add both keys to the unit-tests and UI-tests xctestplans so the runner can flip them on per-test. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Split CodeCoverage out of Test Impact Analysis Code coverage used to be initialised inside TestImpactAnalysisFactory and driven from inside the TIA feature's per-test hooks, which meant coverage couldn't run unless TIA was also enabled — even though it has its own enable gate (`DD_CIVISIBILITY_CODE_COVERAGE_ENABLED` + backend `code_coverage` setting). Pull coverage into its own `CodeCoverage` TestHooksFeature with a matching `CodeCoverageFactory.isEnabled(config:env:remote:)`. TIA keeps its skip / correlation-id / itr-tags responsibilities; coverage owns the LLVM-gathering state machine, the `testCodeCoverageEnabled` tag and the `testCoverageLines` metric on session/module spans. DDTestMonitor now runs an independent `coverageSetup` BlockOperation off the same `updateTracerConfig` dependency and stores the feature in `coverage`, which is registered alongside `tia` in `activeFeatures`. The `AdditionalTags(codeCoverage:)` fallback now keys off `coverage == nil` rather than `tia == nil`. Also fixes the MockBackend's settings/known-tests/test-management responses to echo the request's JSON:API `data.id`. The SDK rejects responses whose id doesn't match the request's, so hardcoding `"id": "1"` broke `tracerSettings` (and cascaded into TIA / EFD / ATR / Known Tests / Test Management all logging "backend config can't be loaded") under the integration-test mock. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Drop ITR from stdoutOTelAndCoverage test config Code coverage is now a standalone feature with its own enable gate, so the integration test no longer needs to flip on ITR/TIA. Leaving `backend.settings.itrEnabled` at its default (`false`) exercises the no-TIA-but-coverage path end-to-end. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Tighten JSON:API response validation + per-service compression Refactors the JSON:API response shape into a protocol family (APIResponseAttributesHasType / HasId / BrokenId / NoId) so each endpoint can declare what shape its response id and type take instead of relying on a single static equality check. This unblocks fixtures like the known-tests / test-management responses where the backend returns a non-matching id, and the TIA endpoint where the response carries no id at all. Also lifts the Content-Encoding: deflate header out of `APIServiceConfig.defaultHeaders` and onto the per-API services (spans, logs, telemetry, TIA coverage, git pack uploads) so each endpoint can opt in conditionally based on `payloadCompression`. The HTTPClient gains a `deflate(_:)` step that inflates the request body when the header is set, and the multipart upload variant exposes `addHTTPHeader` for the per-service injection. Knock-on fix: `MockBackend.route(...)`'s `/api/v2/citestcov` endpoint now feeds the inflated body into `parseCoveragePayload`, matching how the other endpoints already handled compressed payloads. Drops the unused OpenTelemetryApi product dependency from the IntegrationTests-UnitTests target in the xcodeproj. Cleans up the commented-out `withUnsafeTemporaryAllocation` branch in `Data.deflated` (the iOS-15+/macOS-12+ implementation now ships unconditionally). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Pass exporter storage directories from the cache manager `SpansExporter`, `LogsExporter` and `CoverageExporter` no longer carry their own hardcoded `com.datadog.civisibility/{spans,logs,coverage}/v1` Caches paths. Each takes a `storage: Directory` parameter and creates a `v1` subdirectory under it. `EventsExporter.init(config:, storage:)` fans the supplied root out by calling `storage.createSubdirectory(path: "spans" / "logs" / "coverage")` and passing the result to each sub-exporter. In production, `DDTracer` pulls the root from `DDTestMonitor.cacheManager?.session(feature: "exporter")`, so the on-disk layout is now `<bundleId>/<commit>/<sessionId>/exporter/{spans,logs,coverage}/v1/...` alongside the other per-session feature dirs (`tia`, `crash`, `known_tests`, …). If the cache manager is unavailable the exporter is skipped and a log line is emitted. Tests create a fresh `Directory.temporary().createSubdirectory(path: UUID().uuidString)` per case (with `defer { try? storage.delete() }`) so they no longer share a Caches subpath. `CoverageExporterTests` lost its `wipeCoverageDirectory` setUp/tearDown plumbing — no longer needed. `LibraryConfigurationServiceThrowTests` got a small `makeExporter(baseURL:)` helper that tracks the created storages and deletes them in `tearDown`. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Make HTTPClientType async-only `HTTPClientType` now declares just the two async methods — `send(request:)` returning the `HTTPURLResponse` and `sendWithResponse(request:)` returning the body — typed-throwing `HTTPClient.RequestError`. The callback variants (`send(request:completion:)` / `sendWithResult(request:completion:)`) and the internal `runAsync` helper are gone. `HTTPClient` implements the two async methods directly via a shared `perform(_:_:)` helper that wraps `URLSession.dataTask` in a `withCheckedContinuation` and maps the (data, response, error) tuple through the existing `httpClientResult` / `httpClientResultWithData` adapters. `MockHTTPClient` mirrors the new signatures. `MockClosureDataUploader` (the sync `DataUploaderType` shim in CoreMocks) drops its `DispatchSemaphore` callback wait and bridges to async through `waitForAsync`. `HTTPClientTests` convert to `async throws` — annotated `@MainActor` so `ServerMock` still initialises on the main thread (it precondition-checks that). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Fix git upload + API response-shape corner cases - `GitUploader`: move the "already-uploaded" sentinel check from `init` into `sendGitInfo(repositoryURL:commit:)`. Constructing the uploader no longer fails when the upload has already happened; the per-call guard now returns `true` (success — nothing to do) instead of refusing to build the feature. Trailing slash on the packfile temp directory path so `git pack-objects` writes into the directory rather than treating the trailing component as a file prefix. - `GitUploadApi.uploadPackFiles`: drain the entire `withThrowingTaskGroup` with `while try await group.next() != nil {}` instead of returning after the first child — otherwise the rest of the uploads were effectively cancelled. Adds explicit `self.` capture inside the child-task closure. - API response shapes: `GitUploadApi.Commit` switches from `APIResponseAttributesHasId` to `APIResponseAttributesNoId` (the packfile commit endpoint doesn't echo an id). `Array` now conforms to `APIAttributesAutoId` / `APIAttributesNoId` / `APIResponseAttributesNoId` so collection responses get the no-id rule for free; the manual `isIdValid` override is gone. Adds `APIResponseAttributesIgnoreType` as a sibling marker to `HasType` for endpoints that don't gate on the response `type`. - `SettingsResponse`: reorder protocol conformances to `HasType, HasId` for consistency with the rest of the API services. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Make multipart filename optional, drop it where unneeded `MultipartFormURLRequest.append(data:withName:contentType:)` makes the `fileName:` parameter optional (`String? = nil`); when absent, the `Content-Disposition` header no longer carries a `filename="…"` attribute. Renames the parameter from `filename:` to `fileName:` to match Swift API guidelines. Callers that don't actually represent a file drop the argument: - `TestImpactAnalysisApi.uploadCoverage(...)` no longer attaches bogus `CoverageBatch.json` / `DummyEvent.json` filenames to the `coverage` and `event` parts. - `GitUploadApi.uploadPackFile(...)` drops the filename from the `pushedSha` JSON part (kept on the binary `packfile` part where it actually carries the commit-named blob). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] JSON-shaped debug descriptions for API request/response types `APICommonData` now refines `CustomDebugStringConvertible` with a default implementation that renders the wrapper as `{"type": "<apiType>", "id": "<id>", "attributes": <attributes>}` (omitting `id` when nil and `attributes` when `APIVoidValue`). `APIEnvelope` gets the matching `{"meta": <meta>, "data": <data>}` / `{"data": <data>}` template. Both render in snake_case-ish JSON so the `log.debug("…request: \(request)")` lines line up with the wire shape the encoder produces. Conforms the per-service Attribute types — Settings (request + response, including nested EFD and TestManagement), KnownTests (TestsRequest / TestsResponse plus the PageInfo request/response halves), TestManagement (TestsRequest / TestsResponse), TIA (TestsRequest / TestsResponse + Meta), and GitUpload's `Commit` / `CommitRequestMeta` — to `CustomDebugStringConvertible` with hand-written JSON-ish renderings. String fields are quoted, optionals print `null`, and nested `[String: JSONGeneric]` payloads (configurations, etc.) reuse `JSONGeneric.object(...).debugDescription` so they already format as JSON. Multi-field renderings split across `+`-concatenated lines for readability. `GitUploadApi.searchCommits` switches its log format from `[meta: …, data: …]` to the new JSON-shaped `{"meta": …, "data": …}` envelope. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3770] Drop Xcode 26.1 from CI; release on 26.2 The `Xcode_26.1` image on GitHub-hosted `macos-26` runners is currently broken (Apple-side toolchain regression), so drop it from the unit-tests and integration-tests matrices. The release workflow's `XCODE_VERSION` moves from 26.1 to 26.2. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * gate fix to watchOS only in the URLSessionInstrumentation --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for de9af8a - Browse repository at this point
Copy the full SHA de9af8aView commit details
Commits on May 26, 2026
-
[SDTEST-3771] Feature factories call the test-optimization API direct…
…ly (#260) * [SDTEST-3771] Feature factories call the test-optimization API directly Replace the EventsExporter helper-method surface with direct API access inside each feature factory. The SDK now owns the TestOptimizationApi, factories are async/throws, and the exporter is back to being a pure spans/logs/coverage pipeline. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3771] Retry FileWriter append once when the file vanished The upload worker reads-and-deletes files on its own queue. With the test `.readAllFiles` performance preset (minFileAgeForRead = -1) the worker can pick up a writable file in between FilesOrchestrator `getWritableFile` (which has just created it) and the actual `append`. The append then throws ENOENT and the value is lost via `LogsExporter.writeLog`'s `try?`. Recovery: drop the dangling `_currentFile` reference and retry once with a fresh file. Only the missing-file error triggers the retry; permission/disk-full errors still propagate so callers can react. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3771] Closure-scoped writable file in FilesOrchestrator Synchronise the writer and the upload-worker reader through the orchestrator instead of retrying writes that lose their file out from under them. - Replace `getWritableFile` with `withWritableFile(writeSize:body:)`. While `body` runs, the chosen file is "claimed" — the reader's `getReadableFile` / `getAllReadableFiles` filter it out, so a partial file can never be read-and-deleted by the upload worker mid-append. - Move every state-touching helper onto a private `State` struct held inside `Synced<State>`. Locked methods are reachable only through `Synced.use(_:)` / `Synced.update(_:)`, so the lock is enforced by construction rather than by naming convention. - Track active writes by file name (`Set<String>`) instead of URL — `Directory.createFile(...)` and `FileManager.contentsOfDirectory(at:)` can return URLs that differ in `/var` vs `/private/var` canonicalisation, which breaks `Set<URL>.contains(...)`. File names are unique within the directory and stable. - Bump the millisecond-resolution creation timestamp until the file name is unused so two writes in the same millisecond never collide on a path and silently overwrite each other. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [SDTEST-3771] Align GITHUB_* env vars across integration test plans The UITests and UnitTests test plans were missing GITHUB_BASE_REF, GITHUB_EVENT_PATH, GITHUB_JOB, GITHUB_RUN_ATTEMPT, GITHUB_SERVER_URL, and JOB_CHECK_RUN_ID — vars the IntegrationTests parent plan already forwards. Add them and reorder so all three plans group the CI-identity variables identically (GITHUB_* in the same order, then JOB_CHECK_RUN_ID, then DD_TEST_RUNNER / DD_API_KEY / SRCROOT, then the DD_* settings, then plan-specific feature flags). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 330a43d - Browse repository at this point
Copy the full SHA 330a43dView commit details -
[CHORE] Fix integration test runner timeout & isolate inner DerivedDa…
…ta (#261) * [CHORE] Attach xcresult bundles to integration test failures When `xcodebuild test` exits non-zero the existing logs truncate right where the failure footer lands, so we can't see which Swift Testing expectation/run actually failed. Upload the xcresults from DerivedData on failure (zipped to keep the artifact upload fast) so we have the real test events next time the matrix goes red. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * [CHORE] Defer integration test inner builds out of trait `prepare` The xcresult from the failing visionOS job had it: "The test runner timed out while preparing to run tests." xcodebuild gates the xctest runner's "ready to run" handshake on Swift Testing trait `prepare(for:)` finishing, with a multi-minute cap. `BuildProvider.prepare(for:)` was calling `XcodeTestRunner.build()` — `xcrun simctl boot` plus `xcodebuild build-for-testing` — which can run several minutes on a cold checkout, blowing the cap. Keep simulator boot in `prepare(for:)` (cheap) and move the module build into `provideScope(for:performing:)`, which runs after the preparation handshake completes and is governed by the per-test execution allowance instead. Inner xcodebuilds also get an isolated `-derivedDataPath` so their SWBBuildService can't evict cached build descriptions out from under the outer test process — that contention matched the duplicate-blueprint warnings and mid-build aborts seen in the original logs. Also collect xcresults from the new isolated DerivedData in the on-failure artifact upload. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 5d2dcc7 - Browse repository at this point
Copy the full SHA 5d2dcc7View commit details
Commits on May 27, 2026
-
[SDTEST-3812] Dedupe duplicate .datadogTesting trait on nested suites (…
…#262) When both an outer and a nested @suite carried .datadogTesting, Swift Testing chained a trait instance for each annotation level around the inner scope. Each instance opened its own retry group for tests in the inner suite, producing two test runs per test (visible as duplicate results in the Datadog UI; reported in #257). Short-circuit duplicate chained trait instances by tracking the current TestID in a task-local: when provideScope(for:testCase:) sees the same TestID an outer chained instance already set, pass straight through to function() without opening a new scope. TestID is keyed on Testing.Test.ID plus a Mirror-derived shadow of Testing.Test.Case.ID (argumentIDs / discriminator / isStable). The SPI-marked Test.Case.ID, Test.Case.Argument, Test.Parameter, and TypeInfo cannot be referenced directly, so this commit also adds Mirror-based accessors on Testing.Test.Case (ddArgumentIDs, ddArguments, ddDiscriminator, ddIsStable, ddID). SwiftTestRun.parameters now reads argument value / name / type from the Mirror data, replacing the regex-on-String(describing:) parser (and its tests). Regression coverage: DDNestedAnnotatedSuiteTests verifies a nested annotated suite reports its inner test exactly once. testParameterized remains the canary for the Mirror walk on parameterized cases. Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for e5e3555 - Browse repository at this point
Copy the full SHA e5e3555View commit details -
[CHORE] Rename test model classes with DD prefix and simplify Exporte…
…r naming (#263) * [CHORE] Rename test model classes with DD prefix and simplify Exporter naming Renames the public test model types to avoid the collision between the existing `Test` class and `Testing.Test` from the swift-testing framework, and gives the exporter a less wordy name. * `Session` -> `DDSession` * `Module` -> `DDModule` * `Suite` -> `DDSuite` * `Test` -> `DDTest` * `EventsExporter` (class) -> `Exporter` * `EventsExporterProtocol` -> `ExporterProtocol` Files renamed to match: `DDSession.swift`, `DDModule.swift`, `DDSuite.swift`, `Exporter.swift`, `DDSessionApiTests.m` (and the ObjC test class inside, which had a stale `DDTestModuleApiTests` name). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * fixed integration tests --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Configuration menu - View commit details
-
Copy full SHA for 6621844 - Browse repository at this point
Copy the full SHA 6621844View commit details -
Updated binary package version to 2.7.3 (#264)
Co-authored-by: dd-octo-sts[bot] <200755185+dd-octo-sts[bot]@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for f8e7827 - Browse repository at this point
Copy the full SHA f8e7827View commit details -
Configuration menu - View commit details
-
Copy full SHA for fe9b5e7 - Browse repository at this point
Copy the full SHA fe9b5e7View 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.3...2.7.4