Module: SimpleCov::ResultMerger
| Relationships & Source Files | |
| Namespace Children | |
|
Modules:
| |
| Defined in: | lib/simplecov/result_merger.rb, lib/simplecov/result_merger/legacy_format_adapter.rb, lib/simplecov/result_merger/resultset_file.rb, lib/simplecov/result_merger/resultset_store.rb |
Overview
Singleton that is responsible for caching, loading and merging
SimpleCov::Results into a single result for coverage analysis based
upon multiple test suites.
Class Method Summary
- .concurrent_runner_entry?(entry) ⇒ Boolean
- .create_result(command_names, coverage)
- .drop_expired_results(results)
- .merge_and_store(*file_paths, ignore_timeout: false)
- .merge_coverage(*results)
- .merge_results(*file_paths, ignore_timeout: false)
- .merge_valid_results(results, ignore_timeout: false)
-
.merged_entry(existing, incoming)
If an entry with the same command_name was written AFTER our process started, a sibling test runner (typically a subprocess our parent process shelled out to) wrote it.
-
.merged_result
Gets all
SimpleCov::Resultsstored in resultset, merges them and produces a newResultwith merged coverage data and the command_name for the result consisting of a join on all source result's names. - .read_resultset
- .resultset_path
-
.store_result(result)
Saves the given
Resultin the resultset cache. - .synchronize_resultset
- .valid_results(file_path, ignore_timeout: false)
- .warn_about_expired_results(expired_command_names)
- .within_merge_timeout?(data) ⇒ Boolean
Class Method Details
.concurrent_runner_entry?(entry) ⇒ Boolean
# File 'lib/simplecov/result_merger.rb', line 137
def concurrent_runner_entry?(entry) return false unless entry.is_a?(Hash) = entry["timestamp"] process_start = SimpleCov.process_start_time && process_start && .to_i >= process_start.to_i end
.create_result(command_names, coverage)
[ GitHub ].drop_expired_results(results)
[ GitHub ]# File 'lib/simplecov/result_merger.rb', line 58
def drop_expired_results(results) fresh, expired = results.partition { |_command_name, data| within_merge_timeout?(data) } return results if expired.empty? warn_about_expired_results(expired.map(&:first)) fresh.to_h end
.merge_and_store(*file_paths, ignore_timeout: false)
[ GitHub ]# File 'lib/simplecov/result_merger.rb', line 19
def merge_and_store(*file_paths, ignore_timeout: false) result = merge_results(*file_paths, ignore_timeout: ignore_timeout) store_result(result) if result result end
.merge_coverage(*results)
[ GitHub ]# File 'lib/simplecov/result_merger.rb', line 84
def merge_coverage(*results) return [[""], nil] if results.empty? return results.first if results.size == 1 results.reduce do |(memo_command, memo_coverage), (command, coverage)| # timestamp is dropped here, which is intentional (we merge it, it gets a new time stamp as of now) merged_coverage = Combine.combine(Combine::ResultsCombiner, memo_coverage, coverage) [memo_command + command, merged_coverage] end end
.merge_results(*file_paths, ignore_timeout: false)
[ GitHub ]# File 'lib/simplecov/result_merger.rb', line 25
def merge_results(*file_paths, ignore_timeout: false) # It is intentional here that files are only read in and parsed one at a time. # # In big CI setups you might deal with 100s of CI jobs and each one producing Megabytes # of data. Reading them all in easily produces Gigabytes of memory consumption which # we want to avoid. # # For similar reasons a SimpleCov::Result is only created in the end as that'd create # even more data especially when it also reads in all source files. initial_memo = valid_results(file_paths.shift, ignore_timeout: ignore_timeout) command_names, coverage = file_paths.reduce(initial_memo) do |memo, file_path| merge_coverage(memo, valid_results(file_path, ignore_timeout: ignore_timeout)) end create_result(command_names, coverage) end
.merge_valid_results(results, ignore_timeout: false)
[ GitHub ]# File 'lib/simplecov/result_merger.rb', line 47
def merge_valid_results(results, ignore_timeout: false) results = drop_expired_results(results) unless ignore_timeout command_plus_coverage = results.map do |command_name, data| [[command_name], LegacyFormatAdapter.call(data.fetch("coverage"))] end # one file itself _might_ include multiple test runs merge_coverage(*command_plus_coverage) end
.merged_entry(existing, incoming)
If an entry with the same command_name was written AFTER our process
started, a sibling test runner (typically a subprocess our parent
process shelled out to) wrote it. Combine coverage data rather than
overwriting, so an empty parent-process result doesn't clobber the
subprocess's real data. See https://github.com/simplecov-ruby/simplecov/issues/581.
# File 'lib/simplecov/result_merger.rb', line 129
def merged_entry(existing, incoming) return incoming unless concurrent_runner_entry?(existing) incoming.merge( "coverage" => Combine.combine(Combine::ResultsCombiner, existing["coverage"], incoming["coverage"]) ) end
.merged_result
Gets all SimpleCov::Results stored in resultset, merges them and produces a new
Result with merged coverage data and the command_name
for the result consisting of a join on all source result's names
# File 'lib/simplecov/result_merger.rb', line 99
def merged_result command_names, coverage = merge_valid_results(read_resultset) create_result(command_names, coverage) end
.read_resultset
[ GitHub ]# File 'lib/simplecov/result_merger.rb', line 104
def read_resultset content = synchronize_resultset { ResultsetFile.read(resultset_path) } ResultsetFile.decode(content) end
.resultset_path
[ GitHub ]# File 'lib/simplecov/result_merger.rb', line 15
def resultset_path ResultsetStore.resultset_path end
.store_result(result)
Saves the given Result in the resultset cache
# File 'lib/simplecov/result_merger.rb', line 110
def store_result(result) # rubocop:disable Naming/PredicateMethod synchronize_resultset do # Ensure we have the latest, in case it was already cached new_resultset = read_resultset # A single result only ever has one command_name, see `SimpleCov::Result#to_hash` command_name, data = result.to_hash.first new_resultset[command_name] = merged_entry(new_resultset[command_name], data) ResultsetStore.write(new_resultset) end true end
.synchronize_resultset
[ GitHub ]# File 'lib/simplecov/result_merger.rb', line 145
def synchronize_resultset(&) ResultsetStore.synchronize(&) end
.valid_results(file_path, ignore_timeout: false)
[ GitHub ]# File 'lib/simplecov/result_merger.rb', line 43
def valid_results(file_path, ignore_timeout: false) merge_valid_results(ResultsetFile.parse(file_path), ignore_timeout: ignore_timeout) end
.warn_about_expired_results(expired_command_names)
[ GitHub ]# File 'lib/simplecov/result_merger.rb', line 70
def warn_about_expired_results(expired_command_names) warn "[SimpleCov]: Excluded #{expired_command_names.size} result(s) older than " \ "merge_timeout (#{SimpleCov.merge_timeout}s) from the merged report: " \ "#{expired_command_names.sort.join(', ')}. " \ "Increase SimpleCov.merge_timeout to include them." end
.within_merge_timeout?(data) ⇒ Boolean
# File 'lib/simplecov/result_merger.rb', line 66
def within_merge_timeout?(data) (Time.now - Time.at(data.fetch("timestamp"))) < SimpleCov.merge_timeout end