|
| 1 | +require "simplecov" |
| 2 | +require "parallel_rspec/config" |
| 3 | + |
| 4 | +module ParallelRSpec |
| 5 | + @simplecov_enabled = false |
| 6 | + @simplecov_base_dir = nil |
| 7 | + @simplecov_formatters = nil |
| 8 | + |
| 9 | + def self.simplecov_enabled? |
| 10 | + @simplecov_enabled |
| 11 | + end |
| 12 | + |
| 13 | + def self.simplecov(profile = nil, formatters: nil, &block) |
| 14 | + ::SimpleCov.enable_for_subprocesses true |
| 15 | + |
| 16 | + base_dir = ::SimpleCov.coverage_dir |
| 17 | + |
| 18 | + force_worker_settings = lambda do |
| 19 | + # Prevent forked processes races generating the HTML report; each |
| 20 | + # process just dumps a .resultset.json that the runner collates into |
| 21 | + # the user's chosen formatter(s) at the end of the run. |
| 22 | + ::SimpleCov.print_error_status = false |
| 23 | + ::SimpleCov.formatter ::SimpleCov::Formatter::SimpleFormatter |
| 24 | + ::SimpleCov.minimum_coverage 0 |
| 25 | + end |
| 26 | + |
| 27 | + start_simplecov = lambda do |command_name, dir| |
| 28 | + ::SimpleCov.command_name command_name |
| 29 | + ::SimpleCov.coverage_dir dir |
| 30 | + ::SimpleCov.start(profile, &block) |
| 31 | + end |
| 32 | + |
| 33 | + ParallelRSpec::Config.after_fork do |worker_number| |
| 34 | + force_worker_settings.call |
| 35 | + start_simplecov.call( |
| 36 | + "RSpec Worker #{worker_number}", |
| 37 | + File.join(base_dir, "rspec_#{worker_number}"), |
| 38 | + ) |
| 39 | + end |
| 40 | + |
| 41 | + force_worker_settings.call |
| 42 | + start_simplecov.call("RSpec Server", File.join(base_dir, "rspec_server")) |
| 43 | + |
| 44 | + @simplecov_base_dir = base_dir |
| 45 | + @simplecov_formatters = formatters |
| 46 | + @simplecov_enabled = true |
| 47 | + end |
| 48 | + |
| 49 | + def self.collate_simplecov! |
| 50 | + base_dir = @simplecov_base_dir || ::SimpleCov.coverage_dir |
| 51 | + |
| 52 | + # Force the server process's resultset to disk before we glob. SimpleCov's |
| 53 | + # own at_exit handler will harmlessly re-dump it after collation finishes. |
| 54 | + ::SimpleCov.result.format! |
| 55 | + |
| 56 | + pattern = File.join(base_dir, "rspec_*", ".resultset.json") |
| 57 | + resultsets = Dir.glob(pattern) |
| 58 | + |
| 59 | + if resultsets.empty? |
| 60 | + warn "[parallel_rspec] no SimpleCov resultsets found under #{pattern}; skipping collation" |
| 61 | + return |
| 62 | + end |
| 63 | + |
| 64 | + collate_formatter = |
| 65 | + case (@simplecov_formatters || []).length |
| 66 | + when 0 then nil |
| 67 | + when 1 then @simplecov_formatters.first |
| 68 | + else ::SimpleCov::Formatter::MultiFormatter.new(@simplecov_formatters) |
| 69 | + end |
| 70 | + |
| 71 | + puts "Collating #{resultsets.size} SimpleCov resultset(s)..." |
| 72 | + |
| 73 | + ::SimpleCov.collate(resultsets) do |
| 74 | + coverage_dir base_dir |
| 75 | + formatter collate_formatter if collate_formatter |
| 76 | + end |
| 77 | + rescue => e |
| 78 | + warn "[parallel_rspec] simplecov collation failed: #{e.class}: #{e.message}" |
| 79 | + end |
| 80 | +end |
0 commit comments