Documentation
¶
Overview ¶
Package analysis finds functions that carry no direct unit test.
`go test -cover` measures statement coverage, which cannot distinguish a function that was tested from one that was merely executed on the way to somebody else's assertion. This package asks the stricter question: for each function declared in a package, does a TestXxx body actually mention it?
The mechanism is deliberately one question, asked once. For every identifier in a test body it consults go/types and keeps the ones that resolve to a function declared in the package under test. It never asks "is this a call?", which is an unbounded syntactic question — and so method values, method expressions, functions passed as arguments, deferred closures, range-over-func iterators, and generic instantiations all fall out for free rather than each needing a case of its own.
Index ¶
Constants ¶
const IgnoreDirective = "//tarp:ignore"
IgnoreDirective exempts the declaration it precedes. A reason is required — the escape hatch is what decides whether the tool is adoptable on a real codebase, and one sentence of justification is what keeps it from becoming a way to make the score go up.
//tarp:ignore -- talks to a live payment processor; covered by the e2e suite
Variables ¶
var ErrNoGoFiles = platformerrors.New("no Go files found")
ErrNoGoFiles is returned when the requested patterns match no Go source at all — an empty directory reads as a perfect score otherwise, which would be a lie.
var ErrNotInModule = platformerrors.New(
"no go.mod in that directory or any parent, and packages load in module mode: run `go mod init` there first",
)
ErrNotInModule is returned when the analyzed directory sits outside every Go module. The go command's own words for this are "directory prefix . does not contain main module or its selected dependencies", which names the symptom and leaves the cause to be guessed at.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// Dir is the directory the patterns are resolved against. Empty means the
// current working directory.
Dir string
// Patterns are go/packages patterns such as "./..." or a package path.
// Empty means "./...".
Patterns []string
// Strictness is how close a reference must be to count. The zero value is
// the strictest setting, which is the one worth defaulting to.
Strictness Strictness
}
Config selects what to analyze and how strictly to grade it.
type DiagnosticError ¶
type DiagnosticError struct {
Diagnostics []string
}
DiagnosticError reports that the analyzed source could not be loaded and type-checked. The old implementation parsed with parser.AllErrors and happily analyzed broken source; go/types refuses, which is an improvement, so long as the refusal is legible.
func (*DiagnosticError) Error ¶
func (e *DiagnosticError) Error() string
Error implements the error interface.
type Function ¶
type Function struct {
Package string `json:"package"`
File string `json:"file"`
Name string `json:"name"`
// PackagePath is the import path Package is the clause name of. It is what
// actually identifies a package — a module of any size holds several named
// config — and so it is what per-package grading groups on. Left out of the
// JSON with the rest of the renderer-only fields below.
PackagePath string `json:"-"`
// Path, EndLine, and Tested are carried on the Go value but left out of the
// JSON: the wire shape lists only what is missing, and it names files the
// way a person reads them. A caller that renders source instead — the
// coverage view — needs to find the file on disk, know how far the
// declaration extends, and have the verdict on every function rather than
// only the failures.
Path string `json:"-"`
Line int `json:"line"`
EndLine int `json:"-"`
Tested bool `json:"-"`
}
Function identifies a single function declaration and says whether a TestXxx body references it directly. That is a weaker claim than the one the tool is named for: a reference is evidence that a test was written for the function, not that the test asserts anything. File is relative to the analyzed directory when it sits underneath it, so reports are stable across machines and check-outs.
type Package ¶
type Package struct {
// Path is the import path, which is the identity. Name is the package
// clause, which is what a person reads.
Path string
Name string
Declared int
Tested int
}
Package is one package's grade, for reports that span more than one.
type Report ¶
type Report struct {
// Root is the module root the analyzed packages live under, absolute, or
// empty when the target sits in no module. Like Function.Path it is carried
// on the Go value and left out of the JSON: a renderer that has to state
// where a file sits — SARIF, whose URIs are relative to a declared base —
// needs a root to make paths relative to, and the analyzed directory is not
// it. The wire shape is pinned verbatim in analysis_test.go.
Root string
Functions []Function
// Sources is every file the load behind this report read, keyed by the
// import path of the package that named it and sorted within each package.
// The paths are absolute, as go/packages reports them. Carried on the Go
// value and left out of the JSON for the same reason as Root: it exists for
// a renderer that has to open the source rather than describe it.
//
// An analysis is ~99.9% package loading, so the one thing worth handing a
// caller is the loading it would otherwise repeat. The coverage view has to
// turn the package-relative names in a cover profile into files it can read,
// which is a question this list already answers. Keyed rather than a slice
// of pairs so the field costs one word: Report is passed by value to its own
// methods, and gocritic holds it to a size.
Sources map[string][]string
Warnings []string
Strictness Strictness
}
Report is the outcome of an analysis run.
Functions is sorted by file and then by declaration line, so two runs over unchanged source produce byte-identical output.
func Analyze ¶
Analyze loads the requested packages and reports which of their functions have no direct unit test.
func (Report) MarshalJSON ¶
MarshalJSON implements json.Marshaler, so that anything holding a Report can hand it to a standard encoder. The bytes themselves come from platform-go's encoding package, which keeps the content type one decision made in one place; EncodeJSON returns exactly what json.Marshal would, with no trailing newline.
The directive below is tarp's answer to the one shape it cannot see: TestReportMarshalJSON asserts this method thoroughly and never writes its name, because json.Marshal reaches it by reflection. Every Stringer, driver.Valuer, and interface satisfied for a framework's benefit reads the same way, and the honest fix is a reason naming the test, not a looser rule.
func (Report) Packages ¶
Packages groups the report by the package each function was declared in, sorted by import path so two runs over unchanged source agree.
Only packages that declared something appear: a package with nothing to grade would score a meaningless 100 and pad the table it is read from.
func (Report) Score ¶
Score is the percentage of declared functions carrying a direct test, truncated rather than rounded: two of three is 66%, and only a genuinely complete package reaches 100. A package that declares nothing scores 100.
type Strictness ¶
type Strictness uint8
Strictness selects how close a reference has to be to a declaration before it counts as a direct test. The dial only ever weakens: StrictnessFile is the default and the strongest claim the tool can make.
const ( // StrictnessFile requires the reference to live in the declaring file's test // slot: Bar declared in foo.go must be referenced from foo_test.go or // foo_internal_test.go. StrictnessFile Strictness = iota // StrictnessPackage accepts a reference from any _test.go in the package. StrictnessPackage // StrictnessAny accepts a reference anywhere in any _test.go, test helpers // included. StrictnessAny )
func ParseStrictness ¶
func ParseStrictness(raw string) (Strictness, error)
ParseStrictness converts a flag value into a Strictness.
func (Strictness) String ¶
func (s Strictness) String() string
String implements fmt.Stringer, returning the flag spelling of the level.