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



446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/rspec_tracer/storage/json_backend.rb', line 446

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_cache_hit_reason!(state[:cache_hit_reason], snapshot.cache_hit_reason || {})
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.



389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/rspec_tracer/storage/json_backend.rb', line 389

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)

  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]
  )
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.



420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# File 'lib/rspec_tracer/storage/json_backend.rb', line 420

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: {},
    cache_hit_reason: Hash.new(0)
  }
end

.merge_cache_hit_reason!(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.

Sum per-worker reason counts. parallel_tests partitions examples across workers; each worker’s filtered_examples tally is disjoint by example_id, so sum is the right combine rule (a “Files changed” count from worker A plus the same reason’s count from worker B = total examples that ran for that reason across the suite).



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

def self.merge_cache_hit_reason!(target, source)
  source.each { |reason, count| target[reason] += count }
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].



473
474
475
476
477
478
# File 'lib/rspec_tracer/storage/json_backend.rb', line 473

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.



492
493
494
495
496
497
498
499
500
501
502
# File 'lib/rspec_tracer/storage/json_backend.rb', line 492

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

.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.



506
507
508
509
510
511
512
# File 'lib/rspec_tracer/storage/json_backend.rb', line 506

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