module EasyCov

Constants

TOP_PID

Attributes

path[RW]
root[RW]

Public Class Methods

checkpoint() click to toggle source

Write coverage to disk and restart

# File lib/easycov.rb, line 50
def checkpoint
  dump()
  start()
end
detect_path!() click to toggle source

Detect the coverage path

Uses the first of Dir.pwd/.coverage or Dir.pwd/coverage

# File lib/easycov.rb, line 73
def detect_path!
  %w{.coverage coverage}.each { |c|
    cov_dir = File.join(Dir.pwd, c)
    if File.directory?(cov_dir) then
      EasyCov.path = cov_dir
      return
    end
  }
end
dump() click to toggle source

Dump coverage to disk in a thread-safe way

# File lib/easycov.rb, line 28
def dump
  return if ENV["DISABLE_EASYCOV"] == "1"
  Coverage.start # always make sure we are started

  FileUtils.mkdir_p(@path)

  if ENV["PARALLEL_EASYCOV"] == "1" then
    # in parallel mode, write output to separate files for each process
    # to be merged later, via #merge
    write_json(File.join(@path, ".tmp.#{$$}.resultset.json"))
    return
  end

  # default is to lock the output file
  output = File.join(@path, ".resultset.json")
  EasyCov.lock(output) do
    write_json(output)
  end

end
filter(&block) click to toggle source

Add filter block

# File lib/easycov.rb, line 61
def filter(&block)
  filters << block
end
filters() click to toggle source

List of filters

# File lib/easycov.rb, line 56
def filters
  @filters ||= []
end
install_exit_hook() click to toggle source
# File lib/easycov.rb, line 83
def install_exit_hook
  return if ENV["DISABLE_EASYCOV"] == "1"
  Kernel.at_exit do
    EasyCov.checkpoint
  end
end
lock(lockfile, &block) click to toggle source

Create a flock on the given file

@param [String] lockfile to lock on @param [Block]

# File lib/easycov.rb, line 118
def lock(lockfile, &block)
  lockfile = "#{lockfile}.lock"

  FileUtils.touch(lockfile)
  lock = File.new(lockfile)
  lock.flock(File::LOCK_EX)

  block.call()

  begin
    lock.flock(File::LOCK_UN)
  rescue
  end

  begin
    File.delete(lockfile)
  rescue
  end
end
merge!() click to toggle source

Merge all temporary coverage files into main file

# File lib/easycov.rb, line 91
def merge!

  output = File.join(@path, ".resultset.json")
  files = [ output ] + Dir.glob(File.join(@path, ".tmp.*.resultset.json"))

  data = {}
  files.each do |f|
    begin
      next if !File.size? f
      data.merge!(MultiJson.load(File.read(f)))
    rescue Oj::ParseError
    ensure
      File.delete(f) if File.exists? f
    end
  end

  prune(data)

  # write to final dest
  File.open(output+".tmp", 'w'){ |f| f.write(MultiJson.dump(data)) }
  File.rename(output+".tmp", output)
end
path=(path) click to toggle source

Set path to coverage dir

# File lib/easycov.rb, line 66
def path=(path)
  @path = File.expand_path(path)
end
start() click to toggle source

Start coverage engine Can be run multiple times without side-effect.

# File lib/easycov.rb, line 19
def start
  return if ENV["DISABLE_EASYCOV"] == "1"
  @resolve_symlinks = true if @resolve_symlinks.nil?
  @path ||= File.expand_path("coverage")
  @root ||= Dir.pwd # only set first time
  Coverage.start
end

Private Class Methods

apply_filters(result) click to toggle source

Apply filters

# File lib/easycov.rb, line 150
def apply_filters(result)

  ret = {}

  if @resolve_symlinks then
    # resolve any symlinks in paths
    result.each do |file,cov|
      next if not File.exists? file
      f = File.realpath(file)
      if f != file then
        ret[f] = cov
      else
        ret[file] = cov
      end
    end

  else
    ret = result.dup
  end

  # apply filters
  filters.each do |filter|
    ret.delete_if { |file, cov|
      filter.call(file)
    }
  end

  return ret
end
prune(data) click to toggle source
# File lib/easycov.rb, line 142
def prune(data)
  cutoff = Time.new.to_i - 3600
  data.reject! { |name, result|
    result["timestamp"] < cutoff
  }
end
write_json(output) click to toggle source
# File lib/easycov.rb, line 180
def write_json(output)
  # load existing if avail
  data = File.exists?(output) ? MultiJson.load(File.read(output)) : {}

  # merge our data
  result = apply_filters(Coverage.result)

  time = Time.new
  name = "Test #{time.strftime('%Y%m%d.%H%M%S')} #{Random.rand(1_000_000)}"
  data[name] = {
    :coverage  => result,
    :timestamp => time.to_i
  }

  # write to tmp file then move, in case we err out
  File.open(output+".tmp", 'w'){ |f| f.write(MultiJson.dump(data)) }
  File.rename(output+".tmp", output)
end