From 128aa4b1e522fff02343fe56debce7c9c43805a4 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Thu, 6 Mar 2025 17:37:50 -0700 Subject: [PATCH 1/6] =?UTF-8?q?=F0=9F=90=9B=20Use=20monotonic=20time=20to?= =?UTF-8?q?=20calculate=20elapsed=20time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - See: https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/ --- Gemfile | 3 ++ Gemfile.lock | 4 ++- .../old_coverage_json_steps.rb | 2 +- lib/simplecov.rb | 3 +- lib/simplecov/result.rb | 4 +-- lib/simplecov/result_merger.rb | 4 ++- lib/simplecov/timer.rb | 31 +++++++++++++++++++ spec/result_merger_spec.rb | 4 +-- spec/result_spec.rb | 2 +- spec/simplecov_spec.rb | 2 +- 10 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 lib/simplecov/timer.rb diff --git a/Gemfile b/Gemfile index 773069fc9..9809ddc19 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,9 @@ source "https://rubygems.org" # Uncomment this to use development version of html formatter from github # gem "simplecov-html", github: "simplecov-ruby/simplecov-html" +# std lib gems removed from std lib +gem "logger" + gem "matrix" group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 799664e48..60a57db84 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -83,6 +83,7 @@ GEM json (2.9.1) json (2.9.1-java) language_server-protocol (3.17.0.3) + logger (1.6.6) matrix (0.4.2) method_source (1.1.0) mini_mime (1.1.5) @@ -180,6 +181,7 @@ DEPENDENCIES benchmark-ips capybara cucumber + logger matrix minitest pry @@ -192,4 +194,4 @@ DEPENDENCIES webrick BUNDLED WITH - 2.7.0.dev + 2.6.5 diff --git a/features/step_definitions/old_coverage_json_steps.rb b/features/step_definitions/old_coverage_json_steps.rb index 7f94f1434..2d26a7c59 100644 --- a/features/step_definitions/old_coverage_json_steps.rb +++ b/features/step_definitions/old_coverage_json_steps.rb @@ -15,7 +15,7 @@ in_current_directory do resultset_json = File.read(RESULTSET_JSON_PATH) resultset_hash = JSON.parse(resultset_json) - resultset_hash[COMMAND_NAME]["timestamp"] = Time.now.to_i + resultset_hash[COMMAND_NAME]["timestamp"] = SimpleCov::Timer.monotonic.truncate File.write(RESULTSET_JSON_PATH, JSON.pretty_generate(resultset_hash)) end end diff --git a/lib/simplecov.rb b/lib/simplecov.rb index 4250f46b4..3cb3e5819 100644 --- a/lib/simplecov.rb +++ b/lib/simplecov.rb @@ -267,7 +267,7 @@ def result_exit_status(result) # def final_result_process? # checking for ENV["TEST_ENV_NUMBER"] to determine if the tests are being run in parallel - !defined?(ParallelTests) || !ENV["TEST_ENV_NUMBER"] || ParallelTests.last_process? + !ENV["TEST_ENV_NUMBER"] || (defined?(ParallelTests) && ParallelTests.last_process?) end # @@ -448,6 +448,7 @@ def probably_running_parallel_tests? require "forwardable" require_relative "simplecov/configuration" SimpleCov.extend SimpleCov::Configuration +require_relative "simplecov/timer" require_relative "simplecov/coverage_statistics" require_relative "simplecov/exit_codes" require_relative "simplecov/profiles" diff --git a/lib/simplecov/result.rb b/lib/simplecov/result.rb index 7741678c7..70fffc65a 100644 --- a/lib/simplecov/result.rb +++ b/lib/simplecov/result.rb @@ -51,9 +51,9 @@ def format! SimpleCov.formatter.new.format(self) end - # Defines when this result has been created. Defaults to Time.now + # Defines when this result has been created. Defaults to current truncated system monotonic uptime def created_at - @created_at ||= Time.now + @created_at ||= SimpleCov::Timer.monotonic.truncate end # The command name that launched this result. diff --git a/lib/simplecov/result_merger.rb b/lib/simplecov/result_merger.rb index a6b2e92ec..9a01aba0c 100644 --- a/lib/simplecov/result_merger.rb +++ b/lib/simplecov/result_merger.rb @@ -87,7 +87,9 @@ def within_merge_timeout?(data) end def time_since_result_creation(data) - Time.now - Time.at(data.fetch("timestamp")) + timestamp = data.fetch("timestamp") + timer = SimpleCov::Timer.new(timestamp) + timer.elapsed_seconds end def create_result(command_names, coverage) diff --git a/lib/simplecov/timer.rb b/lib/simplecov/timer.rb new file mode 100644 index 000000000..300728b81 --- /dev/null +++ b/lib/simplecov/timer.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +# Use system uptime to calculate accurate and reliable elapsed time. +# +# Question: +# Why not just do: +# elapsed_time = Time.now - other_time +# Answer: +# It is not accurate or reliable. +# https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/ +module SimpleCov + class Timer + attr_accessor :start_time + + class << self + def monotonic + Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + end + + # Capture Time when instantiated + def initialize(start_time) + @start_time = start_time || self.class.monotonic + end + + # Get Elapsed Time in Seconds + def elapsed_seconds + (self.class.monotonic - @start_time).truncate + end + end +end diff --git a/spec/result_merger_spec.rb b/spec/result_merger_spec.rb index 4d4c0e712..6070c06f6 100644 --- a/spec/result_merger_spec.rb +++ b/spec/result_merger_spec.rb @@ -175,7 +175,7 @@ data = { "some command name" => { "coverage" => content, - "timestamp" => Time.now.to_i + "timestamp" => SimpleCov::Timer.monotonic.truncate } } File.open(file_path, "w+") do |f| @@ -286,7 +286,7 @@ def store_result(result, path:) end def outdated(result) - result.created_at = Time.now - 172_800 + result.created_at = SimpleCov::Timer.monotonic.truncate - 172_800 result end diff --git a/spec/result_spec.rb b/spec/result_spec.rb index ef3f37cf8..419cec77f 100644 --- a/spec/result_spec.rb +++ b/spec/result_spec.rb @@ -212,7 +212,7 @@ source_fixture("sample.rb") => {"lines" => [nil, 1, 1, 1, nil, nil, 0, 0, nil, nil]} } end - let(:created_at) { Time.now.to_i } + let(:created_at) { SimpleCov::Timer.monotonic.truncate } it "can consume multiple commands" do input = { diff --git a/spec/simplecov_spec.rb b/spec/simplecov_spec.rb index 5e3937ca4..18a566cef 100644 --- a/spec/simplecov_spec.rb +++ b/spec/simplecov_spec.rb @@ -299,7 +299,7 @@ def create_mergeable_report(name, resultset, outdated: false) result = SimpleCov::Result.new(resultset) result.command_name = name - result.created_at = Time.now - 172_800 if outdated + result.created_at = SimpleCov::Timer.monotonic.truncate - 172_800 if outdated SimpleCov::ResultMerger.store_result(result) FileUtils.mv resultset_path, "#{resultset_path}#{name}.final" end From f4822f0a5b8336fcdeeb353b88bbc5e00effb85b Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Thu, 6 Mar 2025 18:07:34 -0700 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=92=9A=20Paper=20over=20a=20bug=20in?= =?UTF-8?q?=20Rails=20v6/7=20with=20require=20"logger"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously some gems masked a bug in Rails v6 and v7 where `logger` was not required, plugging the hole by requiring it themselves. Because this app is pinned to Rails v6.1, we must require logger manually. In order to not be reliant on other libraries to fix the bug in Rails for us, we do it too. See: https://stackoverflow.com/questions/79360526/uninitialized-constant-activesupportloggerthreadsafelevellogger-nameerror --- Gemfile | 3 ++- Gemfile.lock | 2 ++ test_projects/rails/rspec_rails/Gemfile | 3 +++ test_projects/rails/rspec_rails/config/boot.rb | 7 +++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 9809ddc19..1a7f410b3 100644 --- a/Gemfile +++ b/Gemfile @@ -8,8 +8,9 @@ source "https://rubygems.org" # Uncomment this to use development version of html formatter from github # gem "simplecov-html", github: "simplecov-ruby/simplecov-html" -# std lib gems removed from std lib +# former std libs that have been ejected gem "logger" +gem "ostruct" gem "matrix" diff --git a/Gemfile.lock b/Gemfile.lock index 60a57db84..9816229d3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -93,6 +93,7 @@ GEM nokogiri (1.18.1) mini_portile2 (~> 2.8.2) racc (~> 1.4) + ostruct (0.6.1) parallel (1.26.3) parser (3.3.6.0) ast (~> 2.4.1) @@ -184,6 +185,7 @@ DEPENDENCIES logger matrix minitest + ostruct pry rackup rake diff --git a/test_projects/rails/rspec_rails/Gemfile b/test_projects/rails/rspec_rails/Gemfile index 986f08a00..d5359a066 100644 --- a/test_projects/rails/rspec_rails/Gemfile +++ b/test_projects/rails/rspec_rails/Gemfile @@ -3,6 +3,9 @@ source "https://rubygems.org" # added gems +# former std libs that have been ejected +gem "logger" +gem "ostruct" gem "rspec-rails" gem "simplecov", path: "../../.." diff --git a/test_projects/rails/rspec_rails/config/boot.rb b/test_projects/rails/rspec_rails/config/boot.rb index d69bd27dc..b43a8ca5e 100644 --- a/test_projects/rails/rspec_rails/config/boot.rb +++ b/test_projects/rails/rspec_rails/config/boot.rb @@ -1,3 +1,10 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) require "bundler/setup" # Set up gems listed in the Gemfile. + +# Previously some gems masked a bug in Rails v6 and v7 where `logger` was not required, +# plugging the hole by requiring it themselves. +# Because this app is pinned to Rails v6.1, we must require logger manually. +# In order to not be reliant on other libraries to fix the bug in Rails for us, we do it too. +# See: https://stackoverflow.com/questions/79360526/uninitialized-constant-activesupportloggerthreadsafelevellogger-nameerror +require "logger" From a49f7fe76cc9d1c6d8b2894a1efaf83342b80ac3 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Fri, 7 Mar 2025 06:48:37 -0700 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9C=A8=20Introduce=20ENV["SIMPLECOV=5FPA?= =?UTF-8?q?RALLEL"]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - explicit proxy for determining if running in parallel --- lib/simplecov.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/simplecov.rb b/lib/simplecov.rb index 3cb3e5819..493ca6501 100644 --- a/lib/simplecov.rb +++ b/lib/simplecov.rb @@ -438,7 +438,12 @@ def make_parallel_tests_available end def probably_running_parallel_tests? - ENV.fetch("TEST_ENV_NUMBER", nil) && ENV.fetch("PARALLEL_TEST_GROUPS", nil) + # As a result of the inherent difficulty of intelligently determining parallel testing, + # and for scenarios where the normal ENV variables may not get set, + # use an explicit trigger defined internally: SIMPLECOV_PARALLEL + ENV.fetch("SIMPLECOV_PARALLEL", nil) || ( + ENV.fetch("TEST_ENV_NUMBER", nil) && ENV.fetch("PARALLEL_TEST_GROUPS", nil) + ) end end end From 637043ae1f7569445f6697b688eb9c28ff3a1ae9 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Fri, 7 Mar 2025 06:49:13 -0700 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Make=20?= =?UTF-8?q?conditions=20less=20confusing=20using=20short=20circuit=20retur?= =?UTF-8?q?n=20statements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/simplecov.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/simplecov.rb b/lib/simplecov.rb index 493ca6501..fad55f613 100644 --- a/lib/simplecov.rb +++ b/lib/simplecov.rb @@ -267,14 +267,20 @@ def result_exit_status(result) # def final_result_process? # checking for ENV["TEST_ENV_NUMBER"] to determine if the tests are being run in parallel - !ENV["TEST_ENV_NUMBER"] || (defined?(ParallelTests) && ParallelTests.last_process?) + # Short circuit if not parallel + return false unless ENV["TEST_ENV_NUMBER"] + + make_parallel_tests_available + return false unless defined?(ParallelTests) + + ParallelTests.last_process? end # # @api private # def wait_for_other_processes - return unless defined?(ParallelTests) && final_result_process? + return unless final_result_process? ParallelTests.wait_for_other_processes_to_finish end From aadeea151aebc5eef184378c2dddf5f7a93765df Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Fri, 7 Mar 2025 11:09:15 -0700 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=90=9B=20Disambiguate=20time=20usage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `created_at` / `timestamp` is for wall clock time (for humans) - `started_at` is for monotonic / elapsed time (for computers) --- .../old_coverage_json_steps.rb | 3 +- lib/simplecov/combine.rb | 2 +- lib/simplecov/result.rb | 31 +++++++++++++++---- lib/simplecov/result_merger.rb | 14 +++++++-- lib/simplecov/timer.rb | 25 +++++++++++---- spec/result_merger_spec.rb | 5 +-- spec/result_spec.rb | 14 +++++++-- spec/simplecov_spec.rb | 6 ++-- 8 files changed, 76 insertions(+), 24 deletions(-) diff --git a/features/step_definitions/old_coverage_json_steps.rb b/features/step_definitions/old_coverage_json_steps.rb index 2d26a7c59..57d18491e 100644 --- a/features/step_definitions/old_coverage_json_steps.rb +++ b/features/step_definitions/old_coverage_json_steps.rb @@ -15,7 +15,8 @@ in_current_directory do resultset_json = File.read(RESULTSET_JSON_PATH) resultset_hash = JSON.parse(resultset_json) - resultset_hash[COMMAND_NAME]["timestamp"] = SimpleCov::Timer.monotonic.truncate + resultset_hash[COMMAND_NAME]["timestamp"] = SimpleCov::Timer.wall.truncate + resultset_hash[COMMAND_NAME]["started_at"] = SimpleCov::Timer.monotonic.truncate File.write(RESULTSET_JSON_PATH, JSON.pretty_generate(resultset_hash)) end end diff --git a/lib/simplecov/combine.rb b/lib/simplecov/combine.rb index 93b27c9d9..26bd557ae 100644 --- a/lib/simplecov/combine.rb +++ b/lib/simplecov/combine.rb @@ -10,7 +10,7 @@ module Combine # Combine two coverage based on the given combiner_module. # # Combiners should always be called through this interface, - # as it takes care of short-circuiting of one of the coverages is nil. + # as it takes care of short-circuiting if one of the coverages is nil. # # @return [Hash] def combine(combiner_module, coverage_a, coverage_b) diff --git a/lib/simplecov/result.rb b/lib/simplecov/result.rb index 70fffc65a..ca28314d2 100644 --- a/lib/simplecov/result.rb +++ b/lib/simplecov/result.rb @@ -15,8 +15,10 @@ class Result # Returns all files that are applicable to this result (sans filters!) as instances of SimpleCov::SourceFile. Aliased as :source_files attr_reader :files alias source_files files - # Explicitly set the Time this result has been created + # Explicitly set the Time (Wall) this result has been created attr_writer :created_at + # Explicitly set the Time (Monotonic) this result has been created for elapsed time calculations + attr_writer :started_at # Explicitly set the command name that was used for this coverage result. Defaults to SimpleCov.command_name attr_writer :command_name @@ -25,11 +27,12 @@ class Result # Initialize a new SimpleCov::Result from given Coverage.result (a Hash of filenames each containing an array of # coverage data) - def initialize(original_result, command_name: nil, created_at: nil) + def initialize(original_result, command_name: nil, created_at: nil, started_at: nil) result = original_result @original_result = result.freeze @command_name = command_name @created_at = created_at + @started_at = started_at @files = SimpleCov::FileList.new(result.map do |filename, coverage| SimpleCov::SourceFile.new(filename, JSON.parse(JSON.dump(coverage))) if File.file?(filename) end.compact.sort_by(&:filename)) @@ -51,9 +54,19 @@ def format! SimpleCov.formatter.new.format(self) end - # Defines when this result has been created. Defaults to current truncated system monotonic uptime + # Defines when this result has been created. Defaults to current wall clock time. + # Wall Clock (Realtime) is for knowing what time it is; it cannot be used for elapsed time calculations. + # Ref: https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/ + # See: #started_at def created_at - @created_at ||= SimpleCov::Timer.monotonic.truncate + @created_at ||= Time.at(SimpleCov::Timer.wall.truncate) + end + + # Monotonic Clock is for calculating elapsed time accurately. + # Ref: https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/ + # See: #created_at + def started_at + @started_at ||= SimpleCov::Timer.monotonic.truncate end # The command name that launched this result. @@ -67,7 +80,8 @@ def to_hash { command_name => { "coverage" => coverage, - "timestamp" => created_at.to_i + "timestamp" => created_at.to_i, + "started_at" => started_at.to_i } } end @@ -75,7 +89,12 @@ def to_hash # Loads a SimpleCov::Result#to_hash dump def self.from_hash(hash) hash.map do |command_name, data| - new(data.fetch("coverage"), command_name: command_name, created_at: Time.at(data["timestamp"])) + new( + data.fetch("coverage"), + command_name: command_name, + created_at: Time.at(data["timestamp"]), + started_at: data["started_at"] + ) end end diff --git a/lib/simplecov/result_merger.rb b/lib/simplecov/result_merger.rb index 9a01aba0c..29b18edec 100644 --- a/lib/simplecov/result_merger.rb +++ b/lib/simplecov/result_merger.rb @@ -87,8 +87,16 @@ def within_merge_timeout?(data) end def time_since_result_creation(data) - timestamp = data.fetch("timestamp") - timer = SimpleCov::Timer.new(timestamp) + started_at = data.fetch("started_at", nil) + timer = + if started_at + # Use monotonic time + SimpleCov::Timer.new(started_at, Process::CLOCK_MONOTONIC) + else + # Fall back to using wall clock (support transparent upgrade from old result set data) + created_at = data.fetch("timestamp", nil) + SimpleCov::Timer.new(created_at, Process::CLOCK_REALTIME) + end timer.elapsed_seconds end @@ -104,7 +112,7 @@ def merge_coverage(*results) 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) + # timestamp & started_at are 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) merged_command = memo_command + command diff --git a/lib/simplecov/timer.rb b/lib/simplecov/timer.rb index 300728b81..cebf8b73d 100644 --- a/lib/simplecov/timer.rb +++ b/lib/simplecov/timer.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Use system uptime to calculate accurate and reliable elapsed time. +# Use system uptime (monotonic time) to calculate accurate and reliable elapsed time. # # Question: # Why not just do: @@ -10,22 +10,35 @@ # https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/ module SimpleCov class Timer - attr_accessor :start_time + # Monotonic clock: Process::CLOCK_MONOTONIC + # Wall clock: Process::CLOCK_REALTIME + attr_accessor :start_time, :clock class << self def monotonic - Process.clock_gettime(Process::CLOCK_MONOTONIC) + read(Process::CLOCK_MONOTONIC) + end + + def wall + read(Process::CLOCK_REALTIME) + end + + # SimpleCov::Timer.read(Process::CLOCK_MONOTONIC) # => uptime in seconds (guaranteed directionally accurate) + # SimpleCov::Timer.read(Process::CLOCK_REALTIME) # => seconds since EPOCH (may not be directionally accurate) + def read(clock = Process::CLOCK_MONOTONIC) + Process.clock_gettime(clock) end end # Capture Time when instantiated - def initialize(start_time) - @start_time = start_time || self.class.monotonic + def initialize(start_time, clock = Process::CLOCK_MONOTONIC) + @start_time = start_time || self.class.read(clock) + @clock = clock end # Get Elapsed Time in Seconds def elapsed_seconds - (self.class.monotonic - @start_time).truncate + (self.class.read(clock) - @start_time).truncate end end end diff --git a/spec/result_merger_spec.rb b/spec/result_merger_spec.rb index 6070c06f6..c701aeda1 100644 --- a/spec/result_merger_spec.rb +++ b/spec/result_merger_spec.rb @@ -175,7 +175,8 @@ data = { "some command name" => { "coverage" => content, - "timestamp" => SimpleCov::Timer.monotonic.truncate + "timestamp" => SimpleCov::Timer.wall.truncate, + "started_at" => SimpleCov::Timer.monotonic.truncate } } File.open(file_path, "w+") do |f| @@ -286,7 +287,7 @@ def store_result(result, path:) end def outdated(result) - result.created_at = SimpleCov::Timer.monotonic.truncate - 172_800 + result.started_at = SimpleCov::Timer.monotonic.truncate - 172_800 result end diff --git a/spec/result_spec.rb b/spec/result_spec.rb index 419cec77f..d80e99b7b 100644 --- a/spec/result_spec.rb +++ b/spec/result_spec.rb @@ -95,6 +95,10 @@ expect(dumped_result.created_at.to_i).to eq(subject.created_at.to_i) end + it "has the same started_at" do + expect(dumped_result.started_at.to_i).to eq(subject.started_at.to_i) + end + it "has the same command_name" do expect(dumped_result.command_name).to eq(subject.command_name) end @@ -212,17 +216,20 @@ source_fixture("sample.rb") => {"lines" => [nil, 1, 1, 1, nil, nil, 0, 0, nil, nil]} } end - let(:created_at) { SimpleCov::Timer.monotonic.truncate } + let(:created_at) { SimpleCov::Timer.wall.truncate } + let(:started_at) { SimpleCov::Timer.monotonic.truncate } it "can consume multiple commands" do input = { "rspec" => { "coverage" => original_result, - "timestamp" => created_at + "timestamp" => created_at, + "started_at" => started_at }, "cucumber" => { "coverage" => other_result, - "timestamp" => created_at + "timestamp" => created_at, + "started_at" => started_at } } @@ -232,6 +239,7 @@ sorted = result.sort_by(&:command_name) expect(sorted.map(&:command_name)).to eq %w[cucumber rspec] expect(sorted.map(&:created_at).map(&:to_i)).to eq [created_at, created_at] + expect(sorted.map(&:started_at).map(&:to_i)).to eq [started_at, started_at] expect(sorted.map(&:original_result)).to eq [other_result, original_result] end end diff --git a/spec/simplecov_spec.rb b/spec/simplecov_spec.rb index 18a566cef..89b8fde87 100644 --- a/spec/simplecov_spec.rb +++ b/spec/simplecov_spec.rb @@ -215,7 +215,7 @@ end let(:collated) do - JSON.parse(File.read(resultset_path)).transform_values { |v| v.reject { |k| k == "timestamp" } } + JSON.parse(File.read(resultset_path)).transform_values { |v| v.reject { |k| k.start_with?("timestamp", "started_at") } } end context "when no files to be merged" do @@ -299,7 +299,9 @@ def create_mergeable_report(name, resultset, outdated: false) result = SimpleCov::Result.new(resultset) result.command_name = name - result.created_at = SimpleCov::Timer.monotonic.truncate - 172_800 if outdated + result.created_at = SimpleCov::Timer.wall.truncate + result.started_at = SimpleCov::Timer.monotonic.truncate + result.started_at -= 172_800 if outdated SimpleCov::ResultMerger.store_result(result) FileUtils.mv resultset_path, "#{resultset_path}#{name}.final" end From 418f2d656a2fc140916690e7632c785fad55daf1 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Fri, 7 Mar 2025 11:42:46 -0700 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=A7=91=E2=80=8D=F0=9F=92=BB=20Make=20?= =?UTF-8?q?conditions=20less=20confusing=20using=20short=20circuit=20retur?= =?UTF-8?q?n=20statements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/simplecov.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/simplecov.rb b/lib/simplecov.rb index fad55f613..26322559f 100644 --- a/lib/simplecov.rb +++ b/lib/simplecov.rb @@ -268,10 +268,10 @@ def result_exit_status(result) def final_result_process? # checking for ENV["TEST_ENV_NUMBER"] to determine if the tests are being run in parallel # Short circuit if not parallel - return false unless ENV["TEST_ENV_NUMBER"] + return true unless ENV["TEST_ENV_NUMBER"] make_parallel_tests_available - return false unless defined?(ParallelTests) + return true unless defined?(ParallelTests) ParallelTests.last_process? end @@ -281,6 +281,7 @@ def final_result_process? # def wait_for_other_processes return unless final_result_process? + return unless defined?(ParallelTests) ParallelTests.wait_for_other_processes_to_finish end @@ -447,14 +448,14 @@ def probably_running_parallel_tests? # As a result of the inherent difficulty of intelligently determining parallel testing, # and for scenarios where the normal ENV variables may not get set, # use an explicit trigger defined internally: SIMPLECOV_PARALLEL - ENV.fetch("SIMPLECOV_PARALLEL", nil) || ( - ENV.fetch("TEST_ENV_NUMBER", nil) && ENV.fetch("PARALLEL_TEST_GROUPS", nil) - ) + return true if ENV.fetch("SIMPLECOV_PARALLEL", nil) + + ENV.fetch("TEST_ENV_NUMBER", nil) && ENV.fetch("PARALLEL_TEST_GROUPS", nil) end end end -# requires are down here here for a load order reason I'm not sure what it is about +# requires are down here for a load order reason I'm not sure what it is about require "set" require "forwardable" require_relative "simplecov/configuration"