Skip to content

Phase 5c: tracing and metrics — tests and doubles - #67

Merged
Wahbeh-Mohammad merged 1 commit into
mainfrom
20-phase-5c-tracing-and-metrics-tests
Sep 18, 2026
Merged

Wahbeh-Mohammad merged 1 commit into
mainfrom
20-phase-5c-tracing-and-metrics-tests

Conversation

@Wahbeh-Mohammad

Copy link
Copy Markdown
Contributor

Part of #20. Fifth PR of the nine-PR phase-5 stack — the tests for phase 5c's code PR below it. Targets that PR's branch; the phase record is the next PR up. Rebased onto 5a's stack with the code PR (byte-identical to the reviewed tip 169a511; the combined tree re-proven at 1,849 runs / 56,759 assertions, 99.97%, matrix green on all four Rubies).

What lands

19 files, +1,919 / −38: thirteen suites under gems/dexpace-core/test/dexpace/instrumentation/ mirroring lib/ one file per file (the four widened phase-4a suites gain nested cases; six new mirrors; ordering_test.rb, independence_test.rb and tracing_matrix_facts_test.rb carry no lib/ mirror and say so), plus the doubles and helpers the design's testing strategy names, one per file under test/support/:

  • Dexpace::RecordingSpan — recording: true/false, context:; readers attributes, events, errors, status, finished_at, context; #finish latches so a second call exports nothing (the OBS-21 clause NO_SPAN cannot assert).
  • Dexpace::RecordingTracerFactory / RecordingTracer — #tracer in 4a's five-argument shape, minting a fresh tracer per call (the OBS-29 1:1 clause NO_TRACER_FACTORY cannot assert); readers tracers, name, version, attributes, spans.
  • Dexpace::RecordingMeter / RecordingCounter / RecordingHistogram — fresh instrument per call; readers counters, histograms, records.
  • Dexpace::RecordingHTTPTracer — includes HTTPTracer and overrides all eleven, appending frozen [name, *args] tuples to #events; the conformant emitter OBS-29's ordering test drives.
  • Two helpers: AllocationDelta (the two-loop GC.stat(:total_allocated_objects) delta, #allocations_per_call) and FiberStorageFacts (the NIL_ASSIGNMENT_DELETES / STRING_KEY_INTERNED probes and the floor-aware assert_diagnostic_key_removed / assert_diagnostic_key_unset).

The four doubles are namespaced Dexpace::Recording* — against the tree's top-level convention, because 5b's plan consumes three of them by exactly those names and readers (P5-73). Every file's header names the requirement IDs it exercises; every class inherits DexpaceTestCase; every thread is joined; nothing uses assert_nothing_raised; GC.stat appears only inside the allocation assertions.

What the suites prove rather than restate:

  • The handshake — Bundle.members unchanged; the three singletons equal? to Bundle::NONE's and to a fresh require's; the smoke suite pins the thirteen constants under Dexpace::Instrumentation; #with re-validates through .build on 3.2.11 for the widened Data types.
  • OBS-25 as a number — a two-loop GC.stat delta of exactly 0.0 per call over the whole span protocol, the tracer path, the untraced activation path, NULL's eleven callbacks and the two instruments, with only frozen constants, Symbols and Integers crossing the loop (5b's R8: a file without the magic comment would measure the caller).
  • OBS-22 with two distinct RecordingSpans at every nesting level (a one-span test passes under an implementation that restores nothing), the throw case asserting the slot after assert_raises, and the bare handle nested and closed by hand — the non-lexical form 5b's AsyncStep needs.
  • OBS-23 — the push under Fiber[] seen by a child Thread, Fiber and Enumerator and by Thread.current[] in none; a previously-set key restored; a previously-unset key removed on 3.3+ and present-and-nil on 3.2.11, asserted floor-aware and never as the pushed value (P5-72); the non-recording branch skipping the push with a non-recording RecordingSpan, not NO_SPAN, so "still becomes current" is a real assertion; NO_SCOPE on the identity test (P5-47).
  • OBS-27 — a thousand W3C and DATADOG draws each; the zero-draw coercion reached through the injected generator seam; a seeded Random through the same seam; NONE the sentinel by identity; a flavour outside the three raising.
  • OBS-29 — a succeeding and a retry-exhausted operation through RecordingHTTPTracer: started first and once, succeeded/failed last, once and never both, retries-exhausted immediately followed by operation-failed with the same error by assert_same.
  • OBS-30 the way the sentence does not sound — a raise inside with_span, with_correlated_span, NO_TRACER#in_span and a raising bus behind CallableAdapter all propagate with every slot restored; identity of the factory, the instruments and NULL across sixteen threads.
  • OBS-34's structural half (5b's ID, not discharged here) — a subprocess via RbConfig.ruby loading the ten 5c files alone runs tracing and metrics end to end and asserts Event, Logger, Keys, Events, HTTPLogging, Step and Configuration undefined — written against the file list, not the entry point, so it still discriminates once 5b lands.
  • The matrix facts, pinned on every row — Fiber[:k] = nil deletes from 3.3.0 and retains a nil on 3.2; Fiber["k"] interns from 3.4.0 and raises TypeError on 3.2/3.3; Fiber#storage= warns per call (recorded through WarningCapture, never called by core); copy-on-write per slot; **-splat versus named keyword (≈1003 vs 1 objects per thousand calls); securerandom loads no openssl in a scrubbed subprocess.

Verification

  • bundle exec rake on Ruby 4.0.6 at the reviewed tip: exit 0, all seventeen gates green — test:gems 1,640 runs / 20,939 assertions, line coverage 99.97% (the one line is phase 2's registry race branch); honest RuboCop 332 files clean. At this rebased tip the reconciliation agent re-ran the full set: 1,849 runs / 56,759 assertions, 99.97%, honest RuboCop 371 files clean.
  • The matrix set on 3.2.11, 3.3.12 and 3.4.10 with a fresh lock: 1,640 runs, 0 failures (20,936 assertions on 3.2.11 — the matrix-facts branches differ by row). One order-dependent assertion (openssl via $LOADED_FEATURES under bundle exec's RUBYOPT on 3.4.10) was found by the matrix and fixed in this commit; every row re-run.
  • Four extra seeds on two interpreters: identical run counts. The reviewer's 28 mutations all caught at this tip, eleven on 3.2.11 as well.

Known follow-up (not blocking)

  • R0-2 — tracing_matrix_facts_test.rb:49 calls Fiber#storage= as a deliberate fact pin (twice, plus the ensure restore), each wrapped in WarningCapture.record; the brief's letter said "never in test". Could move into a subprocess like the fact-6 assertion.
  • no_span_test.rb and no_tracer_test.rb change in both this PR and the code PR: the stale-pin repair rides the code branch (the suites must be green there), the new nested cases ride here.

@Wahbeh-Mohammad
Wahbeh-Mohammad force-pushed the 20-phase-5c-tracing-and-metrics branch from e1094a8 to 5b63ef0 Compare September 18, 2026 08:55
@Wahbeh-Mohammad
Wahbeh-Mohammad force-pushed the 20-phase-5c-tracing-and-metrics-tests branch from d60425f to 0a581d5 Compare September 18, 2026 08:56
@Wahbeh-Mohammad
Wahbeh-Mohammad changed the base branch from 20-phase-5c-tracing-and-metrics to main September 18, 2026 09:01
Phase 5c (issue #20), the tests PR over the code PR. Thirteen suites
under gems/dexpace-core/test/dexpace/instrumentation/: six new
mirrors (diagnostics, scope, tracing, meter, http_tracer,
callable_adapter), four of phase 4a's widened in nested classes
(no_span, no_tracer, trace_id_flavour's GenerationTest, bundle's
SampledTest) and three with no lib mirror -- ordering_test.rb, OBS-29's
own conformance clause driven through a conformant emitter by hand;
independence_test.rb, R11's subprocess loading the ten 5c files alone
and asserting Event, Logger, Keys and Configuration undefined, written
against the file list and not the entry point so it still
discriminates once 5b lands; and tracing_matrix_facts_test.rb, the
design's floor-straddling facts as a standing test on every row.

Two of those facts do not hold on the floor and the suite pins the
boundaries rather than the 3.4.10 answer: `Fiber[:k] = nil` deletes
from 3.3 and retains a nil-valued key on 3.2, and `Fiber["k"]`
interns from 3.4 and raises TypeError on 3.2 and 3.3.
support/fiber_storage_facts.rb probes both once per process and
gives the scope and tracing suites their floor-aware "removed"
assertion -- absent where `= nil` deletes, present-and-nil and never
the pushed value on 3.2 (P5-72). support/allocation_delta.rb is the
two-loop GC.stat delta OBS-25's zero-allocation assertions share,
with the argument discipline that makes it caller-insensitive stated
for phase 8a's conformance gem to copy.

The four doubles are fakes (testing/7ecef8e8): RecordingSpan,
RecordingTracer with its factory minting a fresh tracer per operation
(P5-43), RecordingMeter minting a fresh instrument per call so the
no-op's sharing is asserted against a fake without it, and
RecordingHTTPTracer including the vocabulary and overriding all
eleven. They are namespaced Dexpace::Recording* because 5b's plan
consumes three of them by those names (P5-73). Every OBS-25 loop
passes only frozen constants, Symbols and Integers; every suite
touching Fiber[] restores its slots in a teardown the nested classes
share; every raise is asserted on the raised object.
@Wahbeh-Mohammad
Wahbeh-Mohammad force-pushed the 20-phase-5c-tracing-and-metrics-tests branch from 0a581d5 to 59711ee Compare September 18, 2026 09:03
@Wahbeh-Mohammad
Wahbeh-Mohammad merged commit d3ba3ae into main Sep 18, 2026
5 checks passed
@Wahbeh-Mohammad
Wahbeh-Mohammad deleted the 20-phase-5c-tracing-and-metrics-tests branch September 18, 2026 09:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant