mirror of
https://github.com/l5yth/potato-mesh.git
synced 2026-10-31 03:51:58 +01:00
99 lines
4.2 KiB
Ruby
99 lines
4.2 KiB
Ruby
# Copyright © 2025-26 l5yth & contributors
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
# frozen_string_literal: true
|
|
|
|
require "simplecov"
|
|
|
|
# Coverage gate of the Ruby suite (SPEC CV2): the LCOV report Codecov reads,
|
|
# with branch data, and the floor a full-suite run must hold.
|
|
module CoverageGate
|
|
# SimpleCov formatter writing +coverage/lcov.info+: a +DA+ record for each
|
|
# relevant line and a +BRDA+ record for each branch arm, filed on the line
|
|
# SimpleCov reports the branch on (SPEC CV6).
|
|
class LcovFormatter
|
|
# Report file name inside SimpleCov's coverage directory.
|
|
FILENAME = "lcov.info"
|
|
|
|
# Write the report for +result+.
|
|
#
|
|
# @param result [SimpleCov::Result] the merged coverage result.
|
|
# @return [String] path of the written report.
|
|
def format(result)
|
|
path = File.join(SimpleCov.coverage_path, FILENAME)
|
|
File.write(path, self.class.render(result.files))
|
|
path
|
|
end
|
|
|
|
# Render source files as LCOV records.
|
|
#
|
|
# @param files [Array<SimpleCov::SourceFile>] measured source files.
|
|
# @return [String] one record per file.
|
|
def self.render(files)
|
|
files.map { |file| render_file(file) }.join
|
|
end
|
|
|
|
# Render one source file. Lines that never count and skipped lines or
|
|
# branches are left out, as SimpleCov leaves them out of its totals.
|
|
#
|
|
# @param file [SimpleCov::SourceFile] a measured source file.
|
|
# @return [String] its LCOV record.
|
|
def self.render_file(file)
|
|
lines = file.lines.reject { |line| line.never? || line.skipped? }
|
|
branches = file.branches.reject(&:skipped?)
|
|
record = +"TN:\nSF:#{file.filename}\n"
|
|
lines.each { |line| record << "DA:#{line.line_number},#{line.coverage}\n" }
|
|
branches.group_by(&:report_line).each do |line_number, arms|
|
|
arms.each_with_index { |arm, index| record << "BRDA:#{line_number},0,#{index},#{arm.coverage}\n" }
|
|
end
|
|
record << "BRF:#{branches.size}\nBRH:#{branches.count(&:covered?)}\n"
|
|
record << "LF:#{lines.size}\nLH:#{lines.count(&:covered?)}\nend_of_record\n"
|
|
end
|
|
end
|
|
|
|
module_function
|
|
|
|
# Whether this run loads every spec file under +spec_dir+ with no inclusion
|
|
# filter (a line number, +-e+ or +--tag+). Only such a run measures what
|
|
# the floor measures.
|
|
#
|
|
# @param configuration [RSpec::Core::Configuration] the running suite.
|
|
# @param spec_dir [String] the +spec/+ directory.
|
|
# @return [Boolean] true for a full-suite run.
|
|
def full_suite?(configuration, spec_dir)
|
|
expected = Dir[File.join(spec_dir, "**", "*_spec.rb")].map { |path| File.expand_path(path) }.sort
|
|
running = configuration.files_to_run.map { |path| File.expand_path(path) }.sort
|
|
running == expected && configuration.filter_manager.inclusions.empty?
|
|
end
|
|
|
|
# Make a run exit non-zero below the floor (SPEC CV2): a full-suite run, and
|
|
# any run when +COVERAGE_GATE=1+. CI sets it, so a CI run that loads fewer
|
|
# spec files fails closed. A local partial run keeps its exit status: its
|
|
# coverage is not the suite's.
|
|
#
|
|
# @param simplecov [#minimum_coverage] SimpleCov, or a stand-in in specs.
|
|
# @param configuration [RSpec::Core::Configuration] the running suite.
|
|
# @param spec_dir [String] the +spec/+ directory.
|
|
# @param line [Float] minimum line coverage, in percent.
|
|
# @param branch [Float] minimum branch coverage, in percent.
|
|
# @param env [Hash{String => String}] the environment, +ENV+ by default.
|
|
# @return [Boolean] true when the floor applies to this run.
|
|
def apply_floor(simplecov, configuration, spec_dir:, line:, branch:, env: ENV)
|
|
return false unless env["COVERAGE_GATE"] == "1" || full_suite?(configuration, spec_dir)
|
|
|
|
simplecov.minimum_coverage(line: line, branch: branch)
|
|
true
|
|
end
|
|
end
|