Documentation
¶
Overview ¶
Package simplecov is a pure-Go (CGO-free) reimplementation of the deterministic core of Ruby's SimpleCov coverage gem — the result engine.
SimpleCov splits into two halves: a collector that instruments the running program and records, per file, how many times each line (and branch) was hit; and a result engine that models that raw hit data, merges it across runs, filters and groups the files, formats a summary, and enforces coverage thresholds. Everything in the second half is deterministic and needs no Ruby interpreter, so it lives here as pure Go. The collector is the host's job: in go-embedded-ruby/rbgo the VM already tracks execution, so it feeds this package the per-file line-hit arrays and this package does the rest.
The coverage seam ¶
The unit of input is a FileCoverage: a slice of Hit (one per source line; a Hit is either an integer hit count or "not coverable" — null in the resultset) and, optionally, Branches. rbgo builds a map[filename]FileCoverage from its VM coverage tables and hands it to SimpleCov.NewResult; nothing in this package instruments code or opens Ruby.
Value model ¶
- Hit — one line's coverage: coverable+count, or null.
- Branches — nested condition→branch→count map (branch coverage).
- SourceFile — one file: covered/missed/never lines, percent, strength.
- FileList — a collection of SourceFile with aggregate metrics.
- Result — a named, timestamped FileList, filtered and grouped.
- Resultset — the .resultset.json shape: command → coverage + timestamp.
Ruby surface it stands in for ¶
SimpleCov.start -> New(opts...)
SimpleCov.add_filter "…" / %r{…} / {…} -> AddStringFilter / AddRegexpFilter / AddBlockFilter
SimpleCov.add_group "Models", "app/…" -> AddGroup
SimpleCov.minimum_coverage 90 -> MinimumCoverage
SimpleCov.minimum_coverage_by_file 80 -> MinimumCoverageByFile
SimpleCov.maximum_coverage_drop 5 -> MaximumCoverageDrop
SimpleCov.refuse_coverage_drop -> RefuseCoverageDrop
SimpleCov::Result / SourceFile / FileList-> Result / SourceFile / FileList
.resultset.json (de)serialise -> ParseResultset / Resultset.JSON
SimpleCov::ResultMerger -> MergeResults / (*SimpleCov).MergeStored
SimpleCov::Formatter::SimpleFormatter -> SimpleFormatter
Seams ¶
The filesystem (FS) and the clock (a func() time.Time) are injectable, so resultset load/store, source loading, timestamps and the merge staleness window are all deterministic under test. The default FS is os-backed and the default clock is time.Now.
Index ¶
- Constants
- type ArrayFilter
- type BlockFilter
- type Branches
- type Check
- type CommandResult
- type Criterion
- type ExitCode
- type FS
- type FileCoverage
- type FileList
- func (fl *FileList) BranchesCoveredPercent() float64
- func (fl *FileList) CoveredBranches() int
- func (fl *FileList) CoveredLinesCount() int
- func (fl *FileList) CoveredPercent() float64
- func (fl *FileList) CoveredPercentages() []float64
- func (fl *FileList) CoveredStrength() float64
- func (fl *FileList) Files() []*SourceFile
- func (fl *FileList) LeastCoveredFile() *SourceFile
- func (fl *FileList) Len() int
- func (fl *FileList) LinesOfCode() int
- func (fl *FileList) MissedBranches() int
- func (fl *FileList) MissedLinesCount() int
- func (fl *FileList) NeverLinesCount() int
- func (fl *FileList) TotalBranches() int
- type Filter
- type Formatter
- type Hit
- type NamedFileList
- type OSFS
- type Option
- type RegexFilter
- type Result
- func (r *Result) BranchesCoveredPercent() float64
- func (r *Result) CoveredLines() int
- func (r *Result) CoveredPercent() float64
- func (r *Result) CoveredPercentFor(c Criterion) float64
- func (r *Result) CoveredStrength() float64
- func (r *Result) Groups() []NamedFileList
- func (r *Result) LeastCoveredFile() *SourceFile
- func (r *Result) MissedLines() int
- func (r *Result) ToResultset() Resultset
- func (r *Result) TotalLines() int
- type Resultset
- type SimpleCov
- func (sc *SimpleCov) AddBlockFilter(fn func(*SourceFile) bool)
- func (sc *SimpleCov) AddFilter(f Filter)
- func (sc *SimpleCov) AddGroup(name string, filter Filter)
- func (sc *SimpleCov) AddRegexpFilter(re *regexp.Regexp)
- func (sc *SimpleCov) AddStringFilter(arg string)
- func (sc *SimpleCov) AddStringGroup(name, arg string)
- func (sc *SimpleCov) Check() Check
- func (sc *SimpleCov) Filtered(fl *FileList) *FileList
- func (sc *SimpleCov) Grouped(fl *FileList) []NamedFileList
- func (sc *SimpleCov) LoadResultset(path string) (Resultset, error)
- func (sc *SimpleCov) LoadSource(sf *SourceFile) error
- func (sc *SimpleCov) MaximumCoverageDrop(c Criterion, pct float64)
- func (sc *SimpleCov) MergeStored(rs Resultset) *Result
- func (sc *SimpleCov) MinimumCoverage(c Criterion, pct float64)
- func (sc *SimpleCov) MinimumCoverageByFile(c Criterion, pct float64)
- func (sc *SimpleCov) NewResult(commandName string, cov map[string]FileCoverage) *Result
- func (sc *SimpleCov) RefuseCoverageDrop(criteria ...Criterion)
- func (sc *SimpleCov) RunChecks(result, previous *Result) (ExitCode, []Violation)
- func (sc *SimpleCov) StoreResultset(path string, rs Resultset) error
- type SimpleFormatter
- type SourceFile
- func (s *SourceFile) BranchesCoveredPercent() float64
- func (s *SourceFile) CoveredBranches() int
- func (s *SourceFile) CoveredLinesCount() int
- func (s *SourceFile) CoveredPercent() float64
- func (s *SourceFile) CoveredStrength() float64
- func (s *SourceFile) LinesOfCode() int
- func (s *SourceFile) LinesStrength() int
- func (s *SourceFile) MissedBranches() int
- func (s *SourceFile) MissedLinesCount() int
- func (s *SourceFile) NeverLinesCount() int
- func (s *SourceFile) ProjectFilename(root string) string
- func (s *SourceFile) RelevantLines() int
- func (s *SourceFile) TotalBranches() int
- type StringFilter
- type Violation
- type ViolationKind
Constants ¶
const DefaultMergeTimeout = 600 * time.Second
DefaultMergeTimeout is SimpleCov's default merge window: results older than this (relative to the clock) are pruned before merging.
const DefaultResultsetPath = ".resultset.json"
DefaultResultsetPath is SimpleCov's default resultset filename.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ArrayFilter ¶
type ArrayFilter struct {
Filters []Filter
}
ArrayFilter matches when any of its member filters match, mirroring SimpleCov::ArrayFilter.
func (ArrayFilter) Matches ¶
func (f ArrayFilter) Matches(s *SourceFile) bool
Matches reports whether any member filter matches.
type BlockFilter ¶
type BlockFilter struct {
Fn func(*SourceFile) bool
}
BlockFilter matches when its function returns true for the file, mirroring SimpleCov::BlockFilter (add_filter { |source_file| … }).
func (BlockFilter) Matches ¶
func (f BlockFilter) Matches(s *SourceFile) bool
Matches delegates to the block.
type Branches ¶
Branches models SimpleCov's branch-coverage table for one file: a mapping from a condition key (e.g. "[:if, 0, 3, 6, 3, 21]") to that condition's branches (e.g. "[:then, 1, …]" and "[:else, 2, …]"), each carrying its hit count. Every inner entry is one branch; a branch with count 0 is missed, >0 is covered.
func (Branches) CoveredPercent ¶
CoveredPercent is covered/total * 100. A file with no branches is 100%.
type Check ¶
type Check struct {
MinimumCoverage map[Criterion]float64
MinimumCoverageByFile map[Criterion]float64
MaximumCoverageDrop map[Criterion]float64
}
Check holds the configured coverage thresholds. Each map is keyed by criterion (Line/Branch); an absent criterion means "no gate on that dimension".
type CommandResult ¶
type CommandResult struct {
Coverage map[string]FileCoverage `json:"coverage"`
Timestamp int64 `json:"timestamp"`
}
CommandResult is one command's entry in a resultset: the per-file coverage and the Unix timestamp at which it was recorded.
type ExitCode ¶
type ExitCode int
ExitCode mirrors SimpleCov::ExitCodes: the process exit status a coverage run yields.
const ( // Success — all thresholds met. Success ExitCode = 0 // Exception — an error occurred (reserved; parity with SimpleCov). Exception ExitCode = 1 // MinimumCoverage — an overall or per-file minimum was not met. MinimumCoverage ExitCode = 2 // MaximumCoverageDrop — coverage dropped more than allowed vs the last run. MaximumCoverageDrop ExitCode = 3 )
type FS ¶
type FS interface {
ReadFile(name string) ([]byte, error)
WriteFile(name string, data []byte, perm fs.FileMode) error
}
FS is the filesystem seam: resultset load/store and source loading go through it, so tests can inject a deterministic, in-memory filesystem and exercise error branches. The default implementation (OSFS) is os-backed.
type FileCoverage ¶
FileCoverage is the raw per-file coverage the host (rbgo's VM) feeds in: one Hit per source line and, optionally, branch coverage. It is also the on-disk shape inside a resultset's "coverage" map.
func MergeCoverage ¶
func MergeCoverage(a, b FileCoverage) FileCoverage
MergeCoverage merges two files' coverage (lines and branches).
func (FileCoverage) MarshalJSON ¶
func (fc FileCoverage) MarshalJSON() ([]byte, error)
MarshalJSON emits a file's coverage in SimpleCov's current nested form, {"lines":[…]}, adding "branches" only when branch data is present.
func (*FileCoverage) UnmarshalJSON ¶
func (fc *FileCoverage) UnmarshalJSON(b []byte) error
UnmarshalJSON accepts both SimpleCov's current nested form ({"lines":[…]}) and the legacy bare-array form ([null,1,0]).
type FileList ¶
type FileList struct {
// contains filtered or unexported fields
}
FileList is SimpleCov::FileList: an ordered collection of SourceFile with aggregate coverage metrics.
func NewFileList ¶
func NewFileList(files ...*SourceFile) *FileList
NewFileList builds a FileList over the given source files (order preserved).
func (*FileList) BranchesCoveredPercent ¶
BranchesCoveredPercent is total covered / total branches * 100 (100 with no branches).
func (*FileList) CoveredBranches ¶
CoveredBranches is the total executed branches across all files.
func (*FileList) CoveredLinesCount ¶
CoveredLinesCount is the total covered coverable lines across all files.
func (*FileList) CoveredPercent ¶
CoveredPercent is total covered / total coverable * 100. An empty list, or one with no coverable lines, is 100%.
func (*FileList) CoveredPercentages ¶
CoveredPercentages is each file's CoveredPercent, in file order.
func (*FileList) CoveredStrength ¶
CoveredStrength is the average hit count over all coverable lines, rounded to one decimal. Zero for an empty list or one with no coverable lines.
func (*FileList) Files ¶
func (fl *FileList) Files() []*SourceFile
Files returns the underlying source files (do not mutate).
func (*FileList) LeastCoveredFile ¶
func (fl *FileList) LeastCoveredFile() *SourceFile
LeastCoveredFile is the file with the lowest CoveredPercent, or nil if empty.
func (*FileList) LinesOfCode ¶
LinesOfCode is the total coverable lines across all files.
func (*FileList) MissedBranches ¶
MissedBranches is the total never-executed branches across all files.
func (*FileList) MissedLinesCount ¶
MissedLinesCount is the total missed coverable lines across all files.
func (*FileList) NeverLinesCount ¶
NeverLinesCount is the total non-coverable lines across all files.
func (*FileList) TotalBranches ¶
TotalBranches is the total branch count across all files.
type Filter ¶
type Filter interface {
Matches(*SourceFile) bool
}
Filter decides whether a source file matches a rule. A filter added via AddFilter excludes matching files from a result; a filter attached to a group selects the files that belong to that group. This mirrors SimpleCov::Filter.
type Formatter ¶
Formatter renders a result to text. It is the seam SimpleCov's formatter registry fills; the HTML formatter is out of scope, but any consumer can implement this interface.
type Hit ¶
Hit is one source line's coverage datum. SimpleCov stores each line as either an integer hit count (0 = executed zero times = missed, >0 = covered) or null, meaning the line is not coverable (blank, comment, structural). Valid==false models null; Valid==true carries Count.
func Uncoverable ¶
func Uncoverable() Hit
Uncoverable returns a non-coverable line (null in the resultset).
func (Hit) MarshalJSON ¶
MarshalJSON encodes a coverable line as its integer count and a non-coverable line as JSON null, matching SimpleCov's resultset line arrays.
func (*Hit) UnmarshalJSON ¶
UnmarshalJSON decodes a resultset line entry: null → non-coverable, an integer → coverable with that count.
type NamedFileList ¶
NamedFileList is a group name paired with its files, as produced by grouping.
type OSFS ¶
type OSFS struct{}
OSFS is the production FS, backed by the os package.
type Option ¶
type Option func(*SimpleCov)
Option configures a SimpleCov at construction.
func WithCommandName ¶
WithCommandName sets the default command name for results.
func WithMergeTimeout ¶
WithMergeTimeout overrides the merge staleness window.
type RegexFilter ¶
RegexFilter matches when its pattern matches the file's absolute filename, mirroring SimpleCov::RegexFilter.
func (RegexFilter) Matches ¶
func (f RegexFilter) Matches(s *SourceFile) bool
Matches reports whether the pattern matches the file's filename.
type Result ¶
type Result struct {
Files *FileList
CommandName string
CreatedAt time.Time
// contains filtered or unexported fields
}
Result is SimpleCov::Result: a named, timestamped, filtered and grouped view over a set of source files.
func MergeResults ¶
MergeResults combines several results into one, summing coverage per file across the inputs. The merged command name is the sorted, de-duplicated set of input command names joined by ", " and the timestamp is the most recent input. Merging an empty slice yields an empty result.
func NewResult ¶
NewResult builds an unfiltered, ungrouped result directly from raw coverage, stamped at t. Use (*SimpleCov).NewResult to also apply filters and groups.
func (*Result) BranchesCoveredPercent ¶
BranchesCoveredPercent is the result's overall branch coverage percentage.
func (*Result) CoveredLines ¶
CoveredLines is the total covered coverable lines.
func (*Result) CoveredPercent ¶
CoveredPercent is the result's overall line coverage percentage.
func (*Result) CoveredPercentFor ¶
CoveredPercentFor returns the result's coverage percentage for the criterion.
func (*Result) CoveredStrength ¶
CoveredStrength is the result's overall average hit count per coverable line.
func (*Result) Groups ¶
func (r *Result) Groups() []NamedFileList
Groups returns the result's named groups in definition order (empty when no groups were configured), matching SimpleCov::Result#groups.
func (*Result) LeastCoveredFile ¶
func (r *Result) LeastCoveredFile() *SourceFile
LeastCoveredFile is the least-covered file, or nil when there are none.
func (*Result) MissedLines ¶
MissedLines is the total missed coverable lines.
func (*Result) ToResultset ¶
ToResultset serialises a result back into a single-command resultset, ready to merge into or replace an on-disk .resultset.json.
func (*Result) TotalLines ¶
TotalLines is the total coverable lines (covered + missed).
type Resultset ¶
type Resultset map[string]CommandResult
Resultset is SimpleCov's .resultset.json shape: a map from command name to that command's coverage and timestamp, e.g.
{ "RSpec": { "coverage": { "/a.rb": {"lines":[null,1,0]} }, "timestamp": 172… } }
func ParseResultset ¶
ParseResultset decodes .resultset.json bytes.
type SimpleCov ¶
type SimpleCov struct {
CommandName string
Root string
MergeTimeout time.Duration
// contains filtered or unexported fields
}
SimpleCov is the configuration object and orchestrator — the Go stand-in for the Ruby SimpleCov module. Build one with New, configure filters, groups and thresholds, then turn raw coverage into a Result with NewResult. The FS and clock seams make every step deterministic.
func New ¶
New builds a SimpleCov with SimpleCov's defaults (os FS, time.Now clock, 600s merge timeout), then applies the options.
func (*SimpleCov) AddBlockFilter ¶
func (sc *SimpleCov) AddBlockFilter(fn func(*SourceFile) bool)
AddBlockFilter excludes files for which fn returns true.
func (*SimpleCov) AddGroup ¶
AddGroup registers a named group selected by filter (SimpleCov.add_group).
func (*SimpleCov) AddRegexpFilter ¶
AddRegexpFilter excludes files whose filename matches re.
func (*SimpleCov) AddStringFilter ¶
AddStringFilter excludes files whose project path contains arg.
func (*SimpleCov) AddStringGroup ¶
AddStringGroup registers a group selecting files whose project path contains arg — the common SimpleCov.add_group "Name", "path" form.
func (*SimpleCov) Filtered ¶
Filtered returns a copy of fl with every file matching any exclusion filter removed (SimpleCov.filtered).
func (*SimpleCov) Grouped ¶
func (sc *SimpleCov) Grouped(fl *FileList) []NamedFileList
Grouped partitions fl into the configured groups, in definition order, appending an "Ungrouped" bucket for files in no group. With no groups configured it returns nil, matching SimpleCov.grouped.
func (*SimpleCov) LoadResultset ¶
LoadResultset reads and parses a resultset file through the FS seam. A missing file is not an error: it yields an empty resultset, matching SimpleCov's treatment of a first run.
func (*SimpleCov) LoadSource ¶
func (sc *SimpleCov) LoadSource(sf *SourceFile) error
LoadSource fills sf.Src by reading sf.Filename through the FS seam.
func (*SimpleCov) MaximumCoverageDrop ¶
MaximumCoverageDrop sets the largest allowed drop vs the last run (maximum_coverage_drop).
func (*SimpleCov) MergeStored ¶
MergeStored merges the fresh commands in a resultset into a single result, dropping any command whose timestamp is older than the merge timeout relative to the injected clock — mirroring SimpleCov::ResultMerger's stale-result pruning. Commands are merged in command-name order for determinism.
func (*SimpleCov) MinimumCoverage ¶
MinimumCoverage sets the overall minimum for a criterion (minimum_coverage).
func (*SimpleCov) MinimumCoverageByFile ¶
MinimumCoverageByFile sets the per-file minimum (minimum_coverage_by_file).
func (*SimpleCov) NewResult ¶
func (sc *SimpleCov) NewResult(commandName string, cov map[string]FileCoverage) *Result
NewResult builds a result from raw coverage: it constructs the source files, applies exclusion filters, groups the survivors, and stamps the result with the clock. An empty commandName falls back to the configured CommandName.
func (*SimpleCov) RefuseCoverageDrop ¶
RefuseCoverageDrop forbids any drop (drop threshold 0) for the given criteria, defaulting to Line when none are named (refuse_coverage_drop).
type SimpleFormatter ¶
type SimpleFormatter struct{}
SimpleFormatter reproduces SimpleCov::Formatter::SimpleFormatter: for each group it prints the group name, a rule, then one line per file with its coverage percentage. As in SimpleCov, a result with no configured groups produces no output.
func (SimpleFormatter) Format ¶
func (SimpleFormatter) Format(r *Result) string
Format renders the result.
type SourceFile ¶
SourceFile is SimpleCov::SourceFile: one covered file's line and branch data, plus optional source text. Filename is absolute (as SimpleCov stores it); Src, when loaded through the FS seam, holds the file's lines for rendering.
func (*SourceFile) BranchesCoveredPercent ¶
func (s *SourceFile) BranchesCoveredPercent() float64
BranchesCoveredPercent is covered/total branches * 100 (100 with no branches).
func (*SourceFile) CoveredBranches ¶
func (s *SourceFile) CoveredBranches() int
CoveredBranches is the number of executed branches.
func (*SourceFile) CoveredLinesCount ¶
func (s *SourceFile) CoveredLinesCount() int
CoveredLinesCount is the number of coverable lines hit at least once.
func (*SourceFile) CoveredPercent ¶
func (s *SourceFile) CoveredPercent() float64
CoveredPercent is covered/relevant * 100. A file with no coverable lines is reported as 100%, matching SimpleCov's no_lines? shortcut.
func (*SourceFile) CoveredStrength ¶
func (s *SourceFile) CoveredStrength() float64
CoveredStrength is the average hit count over coverable lines, rounded to one decimal (SimpleCov's covered_strength). Zero when there are no coverable lines.
func (*SourceFile) LinesOfCode ¶
func (s *SourceFile) LinesOfCode() int
LinesOfCode is an alias for RelevantLines, matching SimpleCov's terminology.
func (*SourceFile) LinesStrength ¶
func (s *SourceFile) LinesStrength() int
LinesStrength is the sum of hit counts over coverable lines.
func (*SourceFile) MissedBranches ¶
func (s *SourceFile) MissedBranches() int
MissedBranches is the number of never-executed branches.
func (*SourceFile) MissedLinesCount ¶
func (s *SourceFile) MissedLinesCount() int
MissedLinesCount is the number of coverable lines never hit.
func (*SourceFile) NeverLinesCount ¶
func (s *SourceFile) NeverLinesCount() int
NeverLinesCount is the number of non-coverable (null) lines.
func (*SourceFile) ProjectFilename ¶
func (s *SourceFile) ProjectFilename(root string) string
ProjectFilename is Filename with a leading root prefix stripped, mirroring SimpleCov's project_filename used by string filters and groups.
func (*SourceFile) RelevantLines ¶
func (s *SourceFile) RelevantLines() int
RelevantLines is the number of coverable lines (covered + missed); SimpleCov's lines_of_code.
func (*SourceFile) TotalBranches ¶
func (s *SourceFile) TotalBranches() int
TotalBranches is the file's branch count.
type StringFilter ¶
StringFilter matches when its argument is a substring of the file's project-relative path, mirroring SimpleCov::StringFilter (which compares against project_filename, i.e. the path with the root stripped).
func (StringFilter) Matches ¶
func (f StringFilter) Matches(s *SourceFile) bool
Matches reports whether Arg occurs in the file's project filename.
type Violation ¶
type Violation struct {
Kind ViolationKind
Criterion Criterion
File string // set only for BelowMinimumByFile
Actual float64
Expected float64
}
Violation records a single failed threshold check.
type ViolationKind ¶
type ViolationKind int
ViolationKind classifies a threshold breach.
const ( // BelowMinimum — overall coverage below minimum_coverage. BelowMinimum ViolationKind = iota // BelowMinimumByFile — a file below minimum_coverage_by_file. BelowMinimumByFile // ExceededDrop — coverage dropped more than maximum_coverage_drop. ExceededDrop )
