Module: Kettle::Soup::Cover

Extended by:
Loaders
Includes:
Constants
Defined in:
lib/kettle/soup/cover.rb,
lib/kettle/soup/cover/loaders.rb,
lib/kettle/soup/cover/version.rb,
lib/kettle/soup/cover/constants.rb,
lib/kettle/soup/cover/formatters.rb,
lib/kettle/soup/cover/filters/gt_line_filter.rb,
lib/kettle/soup/cover/filters/lt_line_filter.rb

Defined Under Namespace

Modules: Constants, Filters, Loaders, Version Classes: Error

Constant Summary collapse

VAR_HOME_PREFIX =
%r{\A/var/home(?=/|\z)}
VERSION =

Traditional Constant Location

Version::VERSION

Constants included from Constants

Constants::CI, Constants::CLEAN_RESULTSET, Constants::CLEAN_RESULTSET_DEFAULT, Constants::COMMAND_NAME, Constants::COVERAGE_DIR, Constants::COVERAGE_ROOT_DIR, Constants::DEBUG, Constants::DO_COV, Constants::ENV_GET, Constants::FALSE, Constants::FILTER_DIRS, Constants::FORMATTERS, Constants::FORMATTER_PLUGINS, Constants::IS_CI, Constants::MERGE_TIMEOUT, Constants::MIN_COVERAGE_BRANCH, Constants::MIN_COVERAGE_HARD, Constants::MIN_COVERAGE_HARD_REQUESTED, Constants::MIN_COVERAGE_LINE, Constants::MULTI_FORMATTERS, Constants::MULTI_FORMATTERS_DEFAULT, Constants::OPEN_BIN, Constants::PREFIX, Constants::TEST_ENV_NUMBER, Constants::TRUE, Constants::TURBO_TESTS, Constants::TURBO_TESTS_DIR, Constants::TURBO_TESTS_WORKER, Constants::USE_MERGING, Constants::VERBOSE

Class Method Summary collapse

Methods included from Loaders

install_tasks, load_filters, load_formatters

Class Method Details

.clean_resultset!Object

Deletes coverage/.resultset.json (if it exists) so that stale entries from prior runs cannot pollute the current run’s coverage report. Called automatically from config.rb when CLEAN_RESULTSET is true.



63
64
65
66
# File 'lib/kettle/soup/cover.rb', line 63

def clean_resultset!
  resultset_path = SimpleCov::ResultMerger.resultset_path
  File.delete(resultset_path) if File.exist?(resultset_path)
end

.clear_coverage_dir!(coverage_dir = Constants::COVERAGE_DIR, project_root: Dir.pwd) ⇒ Object



68
69
70
# File 'lib/kettle/soup/cover.rb', line 68

def clear_coverage_dir!(coverage_dir = Constants::COVERAGE_DIR, project_root: Dir.pwd)
  FileUtils.rm_rf(File.expand_path(coverage_dir, project_root))
end

.clear_turbo_tests_coverage_dir!(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd) ⇒ Object



80
81
82
# File 'lib/kettle/soup/cover.rb', line 80

def clear_turbo_tests_coverage_dir!(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd)
  FileUtils.rm_rf(turbo_tests_coverage_dir(coverage_dir, project_root: project_root))
end

.collate_turbo_tests_coverage!(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/kettle/soup/cover.rb', line 92

def collate_turbo_tests_coverage!(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd)
  return :disabled unless turbo_tests_coverage?

  resultsets = turbo_tests_resultset_paths(coverage_dir, project_root: project_root)
  return :empty if resultsets.empty?

  require "simplecov"

  SimpleCov.collate(resultsets) do
    command_name("#{Constants::COMMAND_NAME} (turbo_tests2)")
    enable_coverage(:branch)
    primary_coverage(:branch)
    add_filter(Constants::FILTER_DIRS)
    coverage_dir(File.expand_path(coverage_dir, project_root))

    Kettle::Soup::Cover.configure_formatters!

    if Constants::MIN_COVERAGE_HARD
      minimum_coverage(
        branch: Constants::MIN_COVERAGE_BRANCH,
        line: Constants::MIN_COVERAGE_LINE
      )
    end
  end

  publish_turbo_tests_json_coverage!(coverage_dir, project_root: project_root)

  :collated
end

.configure_formatters!Object



8
9
10
11
12
13
14
15
16
17
# File 'lib/kettle/soup/cover/formatters.rb', line 8

def configure_formatters!
  if Constants::TURBO_TESTS_WORKER
    SimpleCov.at_exit { SimpleCov.result }
  elsif Constants::MULTI_FORMATTERS
    Kettle::Soup::Cover::Loaders.load_formatters
  else
    require "simplecov-html"
    SimpleCov.formatter(SimpleCov::Formatter::HTMLFormatter)
  end
end

.coverage_task_env(coverage_dir = Constants::COVERAGE_DIR, project_root: Dir.pwd) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/kettle/soup/cover.rb', line 180

def coverage_task_env(coverage_dir = Constants::COVERAGE_DIR, project_root: Dir.pwd)
  resolved_coverage_dir = File.expand_path(coverage_dir, project_root)

  {
    "K_SOUP_COV_PREFIX" => Constants::PREFIX,
    "#{Constants::PREFIX}DIR" => resolved_coverage_dir,
    "#{Constants::PREFIX}DO" => Constants::TRUE,
    "#{Constants::PREFIX}FORMATTERS" => "json",
    "#{Constants::PREFIX}MIN_HARD" => Constants::FALSE,
    "#{Constants::PREFIX}MULTI_FORMATTERS" => Constants::TRUE,
    "#{Constants::PREFIX}OPEN_BIN" => ""
  }
end

.delete_const(&block) ⇒ Object



56
57
58
# File 'lib/kettle/soup/cover.rb', line 56

def delete_const(&block)
  Constants.delete_const(&block)
end

.display_path(path) ⇒ Object



46
47
48
49
50
# File 'lib/kettle/soup/cover.rb', line 46

def display_path(path)
  return path if path.nil?

  path.to_s.sub(VAR_HOME_PREFIX, "/home")
end

.merge_branch_coverage(left, right) ⇒ Object



156
157
158
159
# File 'lib/kettle/soup/cover.rb', line 156

def merge_branch_coverage(left, right)
  length = [left.length, right.length].max
  Array.new(length) { |index| merge_branch_entry(left[index], right[index]) }.compact
end

.merge_branch_entry(left, right) ⇒ Object



161
162
163
164
165
166
167
168
169
# File 'lib/kettle/soup/cover.rb', line 161

def merge_branch_entry(left, right)
  return right unless left
  return left unless right
  return left unless left.is_a?(Hash) && right.is_a?(Hash)

  left.merge(right) do |key, left_value, right_value|
    (key == "coverage") ? merge_coverage_value(left_value, right_value) : left_value
  end
end

.merge_coverage_value(left, right) ⇒ Object



171
172
173
174
175
176
177
178
# File 'lib/kettle/soup/cover.rb', line 171

def merge_coverage_value(left, right)
  return left + right if left.is_a?(Integer) && right.is_a?(Integer)
  return left if left.is_a?(Integer)
  return right if right.is_a?(Integer)
  return left if left

  right
end

.merge_json_coverage!(target, source) ⇒ Object



142
143
144
145
146
147
148
149
# File 'lib/kettle/soup/cover.rb', line 142

def merge_json_coverage!(target, source)
  source.each do |path, coverage|
    target[path] ||= {"lines" => [], "branches" => []}
    target[path]["lines"] = merge_line_coverage(target[path]["lines"], coverage["lines"] || [])
    target[path]["branches"] = merge_branch_coverage(target[path]["branches"] || [], coverage["branches"] || [])
  end
  target
end

.merge_line_coverage(left, right) ⇒ Object



151
152
153
154
# File 'lib/kettle/soup/cover.rb', line 151

def merge_line_coverage(left, right)
  length = [left.length, right.length].max
  Array.new(length) { |index| merge_coverage_value(left[index], right[index]) }
end

.publish_turbo_tests_json_coverage!(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/kettle/soup/cover.rb', line 122

def publish_turbo_tests_json_coverage!(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd)
  paths = turbo_tests_json_paths(coverage_dir, project_root: project_root)
  return :empty if paths.empty?

  merged = {
    "meta" => {},
    "coverage" => {}
  }
  paths.sort_by { |path| File.basename(File.dirname(path)).to_i }.each do |path|
    data = JSON.parse(File.read(path))
    merged["meta"].merge!(data["meta"]) if data["meta"].is_a?(Hash)
    merge_json_coverage!(merged["coverage"], data["coverage"] || {})
  end

  output_path = File.expand_path(File.join(coverage_dir, "coverage.json"), project_root)
  FileUtils.mkdir_p(File.dirname(output_path))
  File.write(output_path, JSON.pretty_generate(merged))
  :published
end

.refresh_coverage_data!(coverage_dir = Constants::COVERAGE_DIR, project_root: Dir.pwd, out: $stdout, err: $stderr) ⇒ Object

Raises:



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/kettle/soup/cover.rb', line 194

def refresh_coverage_data!(coverage_dir = Constants::COVERAGE_DIR, project_root: Dir.pwd, out: $stdout, err: $stderr)
  clear_coverage_dir!(coverage_dir, project_root: project_root)

  coverage_task = File.expand_path("bin/rake", project_root)
  success = system(
    coverage_task_env(coverage_dir, project_root: project_root),
    coverage_task,
    "coverage",
    chdir: project_root,
    out: out,
    err: err
  )
  return if success

  raise Error, "Coverage refresh failed: #{coverage_task} coverage"
end

.reset_const(&block) ⇒ Object



52
53
54
# File 'lib/kettle/soup/cover.rb', line 52

def reset_const(&block)
  Constants.reset_const(&block)
end

.turbo_tests_coverage?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'lib/kettle/soup/cover.rb', line 76

def turbo_tests_coverage?
  Constants::DO_COV && Constants::TURBO_TESTS
end

.turbo_tests_coverage_dir(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd) ⇒ Object



72
73
74
# File 'lib/kettle/soup/cover.rb', line 72

def turbo_tests_coverage_dir(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd)
  File.expand_path(File.join(coverage_dir, Constants::TURBO_TESTS_DIR), project_root)
end

.turbo_tests_json_paths(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd) ⇒ Object



88
89
90
# File 'lib/kettle/soup/cover.rb', line 88

def turbo_tests_json_paths(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd)
  Dir[File.join(turbo_tests_coverage_dir(coverage_dir, project_root: project_root), "*", "coverage.json")]
end

.turbo_tests_resultset_paths(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd) ⇒ Object



84
85
86
# File 'lib/kettle/soup/cover.rb', line 84

def turbo_tests_resultset_paths(coverage_dir = Constants::COVERAGE_ROOT_DIR, project_root: Dir.pwd)
  Dir[File.join(turbo_tests_coverage_dir(coverage_dir, project_root: project_root), "*", ".resultset.json")]
end