Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: DataDog/dd-sdk-swift-testing
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2.7.3
Choose a base ref
...
head repository: DataDog/dd-sdk-swift-testing
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2.7.4
Choose a head ref
  • 9 commits
  • 143 files changed
  • 3 contributors

Commits on May 15, 2026

  1. [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]>
    ypopovych and claude authored May 15, 2026
    Configuration menu
    Copy the full SHA
    03fd3cd View commit details
    Browse the repository at this point in the history

Commits on May 18, 2026

  1. [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]>
    ypopovych and claude authored May 18, 2026
    Configuration menu
    Copy the full SHA
    be3faff View commit details
    Browse the repository at this point in the history

Commits on May 25, 2026

  1. [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]>
    ypopovych and claude authored May 25, 2026
    Configuration menu
    Copy the full SHA
    de9af8a View commit details
    Browse the repository at this point in the history

Commits on May 26, 2026

  1. [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]>
    ypopovych and claude authored May 26, 2026
    Configuration menu
    Copy the full SHA
    330a43d View commit details
    Browse the repository at this point in the history
  2. [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]>
    ypopovych and claude authored May 26, 2026
    Configuration menu
    Copy the full SHA
    5d2dcc7 View commit details
    Browse the repository at this point in the history

Commits on May 27, 2026

  1. [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]>
    ypopovych and claude authored May 27, 2026
    Configuration menu
    Copy the full SHA
    e5e3555 View commit details
    Browse the repository at this point in the history
  2. [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]>
    ypopovych and claude authored May 27, 2026
    Configuration menu
    Copy the full SHA
    6621844 View commit details
    Browse the repository at this point in the history
  3. 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>
    ypopovych and dd-octo-sts[bot] authored May 27, 2026
    Configuration menu
    Copy the full SHA
    f8e7827 View commit details
    Browse the repository at this point in the history
  4. Configuration menu
    Copy the full SHA
    fe9b5e7 View commit details
    Browse the repository at this point in the history
Loading