lib/simplecov/result_merger/resultset_store.rb



# frozen_string_literal: true

require "fileutils"
require "json"

module SimpleCov
  module ResultMerger
    # Reads and writes the persistent `.resultset.json` cache, including
    # file-lock synchronization between processes and atomic temp-file
    # renames so concurrent readers don't observe a truncated file.
    module ResultsetStore
    module_function

      def resultset_path
        File.join(SimpleCov.coverage_path, ".resultset.json")
      end

      def writelock_path
        File.join(SimpleCov.coverage_path, ".resultset.json.lock")
      end

      def write(resultset)
        FileUtils.mkdir_p(SimpleCov.coverage_path)
        temp_path = "#{resultset_path}.#{Process.pid}.tmp"
        File.open(temp_path, "w") { |f| f.puts JSON.pretty_generate(resultset) }
        File.rename(temp_path, resultset_path)
      end

      # Ensure only one process is reading or writing the resultset at
      # any given time. Reentrant: the lock is acquired once per outer
      # call no matter how deeply nested.
      def synchronize(&)
        return yield if @locked

        @locked = true
        with_flock(&)
      ensure
        @locked = false
      end

      def with_flock
        FileUtils.mkdir_p(SimpleCov.coverage_path)
        File.open(writelock_path, "w+") do |f|
          f.flock(File::LOCK_EX)
          yield
        end
      end
    end
  end
end