Module: RSpecTracer::Storage::JsonBackend::Merger Private

Defined in:
lib/rspec_tracer/storage/json_backend.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Stateless snapshot union. parallel_tests partitions spec files across workers, so example IDs are disjoint in practice - the merge collision rules (first-wins for metadata, sum-of-ints for per-line coverage) only fire on collaborating workers that happened to observe the same input file.

Class Method Summary collapse

Class Method Details

.absorb(state, snapshot) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Union every field from one peer snapshot into the running state. Each field has a distinct combine rule (merge-first-wins, Set#merge, concat, or summing coverage strengths), so the branching is inherent to the shape. Decomposing per-field would scatter the merge contract. rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity



458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/rspec_tracer/storage/json_backend.rb', line 458

def self.absorb(state, snapshot)
  state[:all_examples].merge!(snapshot.all_examples || {}) { |_, v, _| v }
  (snapshot.duplicate_examples || {}).each do |id, entries|
    state[:duplicate_examples][id].concat(entries)
  end
  state[:interrupted_examples].merge(snapshot.interrupted_examples || Set.new)
  state[:flaky_examples].merge(snapshot.flaky_examples || Set.new)
  state[:failed_examples].merge(snapshot.failed_examples || Set.new)
  state[:pending_examples].merge(snapshot.pending_examples || Set.new)
  state[:skipped_examples].merge(snapshot.skipped_examples || Set.new)
  state[:all_files].merge!(snapshot.all_files || {}) { |_, v, _| v }
  (snapshot.dependency || {}).each do |id, paths|
    state[:dependency][id].merge(paths)
  end
  merge_examples_coverage!(state[:examples_coverage], snapshot.examples_coverage || {})
  state[:boot_set].merge!(snapshot.boot_set || {})
  state[:wsi_snapshot].merge!(snapshot.wsi_snapshot || {})
  state[:env_snapshot].merge!(snapshot.env_snapshot || {})
  merge_env_dependency!(state[:env_dependency], snapshot.env_dependency || {})
  merge_filtered_examples!(state[:filtered_examples], snapshot.filtered_examples || {})
end

.build_merged_snapshot(state, schema_version:) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal helper for the tracer pipeline.



406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/rspec_tracer/storage/json_backend.rb', line 406

def self.build_merged_snapshot(state, schema_version:)
  Snapshot.new(
    schema_version: schema_version,
    run_id: state[:run_id],
    all_examples: state[:all_examples],
    duplicate_examples: state[:duplicate_examples],
    interrupted_examples: state[:interrupted_examples],
    flaky_examples: state[:flaky_examples],
    failed_examples: state[:failed_examples],
    pending_examples: state[:pending_examples],
    skipped_examples: state[:skipped_examples],
    all_files: state[:all_files],
    dependency: state[:dependency],
    reverse_dependency: state[:reverse_dependency],
    examples_coverage: state[:examples_coverage],
    boot_set: state[:boot_set],
    wsi_snapshot: state[:wsi_snapshot],
    env_snapshot: state[:env_snapshot],
    env_dependency: state[:env_dependency],
    cache_hit_reason: state[:cache_hit_reason],
    filtered_examples: state[:filtered_examples]
  )
end

.call(snapshots, schema_version:) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal helper for the tracer pipeline.



393
394
395
396
397
398
399
400
401
402
# File 'lib/rspec_tracer/storage/json_backend.rb', line 393

def self.call(snapshots, schema_version:)
  state = empty_state
  snapshots.each { |s| absorb(state, s) }

  state[:reverse_dependency] = reverse_of(state[:dependency])
  state[:run_id] = Digest::MD5.hexdigest(state[:all_examples].keys.sort.to_json)
  state[:cache_hit_reason] = state[:filtered_examples].values.tally

  build_merged_snapshot(state, schema_version: schema_version)
end

.empty_stateObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal helper for the tracer pipeline.



432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
# File 'lib/rspec_tracer/storage/json_backend.rb', line 432

def self.empty_state
  {
    all_examples: {},
    duplicate_examples: Hash.new { |h, k| h[k] = [] },
    interrupted_examples: Set.new,
    flaky_examples: Set.new,
    failed_examples: Set.new,
    pending_examples: Set.new,
    skipped_examples: Set.new,
    all_files: {},
    dependency: Hash.new { |h, k| h[k] = Set.new },
    examples_coverage: {},
    boot_set: {},
    wsi_snapshot: {},
    env_snapshot: {},
    env_dependency: {},
    filtered_examples: {}
  }
end

.merge_env_dependency!(target, source) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Per-example env attribution unions set-wise: an example that declared ‘tracks: { env: [A, B] }` on one worker and `tracks: { env: [B, C] }` on another (edge case; parallel_tests workers rarely run the same example) collapses to [A, B, C].



485
486
487
488
489
490
# File 'lib/rspec_tracer/storage/json_backend.rb', line 485

def self.merge_env_dependency!(target, source)
  source.each do |id, names|
    existing = target[id] || []
    target[id] = (existing | Array(names)).sort
  end
end

.merge_examples_coverage!(target, source) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal helper for the tracer pipeline.



510
511
512
513
514
515
516
517
518
519
520
# File 'lib/rspec_tracer/storage/json_backend.rb', line 510

def self.merge_examples_coverage!(target, source)
  source.each do |id, per_file|
    entry = target[id] ||= {}
    per_file.each do |file_path, lines|
      file_entry = entry[file_path] ||= {}
      lines.each do |line_key, strength|
        file_entry[line_key] = (file_entry[line_key] || 0) + (strength || 0)
      end
    end
  end
end

.merge_filtered_examples!(target, source) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Merge per-worker filtered_examples by example_id. Every parallel_tests worker independently runs Filter.select against the same global previous-run snapshot (Engine#compute_filter_decisions walks the registry seeded from ‘prev` and the filter intersects against `prev.all_examples.keys`), so every worker’s filtered_examples hash is IDENTICAL. First-write-wins collapses the duplicate ids; ‘Merger.call` re-derives `cache_hit_reason` as the values-tally of the merged hash so the merged cache_hit_reason is the per-id-correct count (not the N-fold-inflated sum of identical per-worker tallies that the pre-#193 merge produced).



504
505
506
# File 'lib/rspec_tracer/storage/json_backend.rb', line 504

def self.merge_filtered_examples!(target, source)
  source.each { |id, reason| target[id] ||= reason }
end

.reverse_of(dependency) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Internal helper for the tracer pipeline.



524
525
526
527
528
529
530
# File 'lib/rspec_tracer/storage/json_backend.rb', line 524

def self.reverse_of(dependency)
  reverse = Hash.new { |h, k| h[k] = Set.new }
  dependency.each do |id, file_names|
    file_names.each { |name| reverse[name] << id }
  end
  reverse
end