ditto

package
v0.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 20, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type BatchLaboratory added in v0.3.0

type BatchLaboratory interface {
	TestAll(repository Repository, files []*gomutatedfile.GoMutatedFile) []future.Future[result.Result[string]]
}

BatchLaboratory is handed every mutant of one file together.

One compilation can only serve several mutants if the compiler is given all of them at once, and the gated path needs exactly that. It is a separate interface rather than a change to Laboratory so that every laboratory that exists today goes on being asked one mutant at a time, unchanged.

The batch cannot be assembled lower down. Test returns a future, and a laboratory could in principle buffer and resolve later — but testingtlaboratory awaits inside each subtest, immediately, so a laboratory waiting for mutants that have not been submitted yet would wait forever.

type CommandScope added in v0.12.0

type CommandScope interface {
	Executes(repository Repository, relativePath string) bool
}

CommandScope answers whether the configured test command can execute the package that owns a source path.

It is a separate interface rather than part of Laboratory, for the reason BatchLaboratory is: a laboratory that cannot answer is still a laboratory, and the release then runs exactly what it ran before. An implementation is allowed to answer true because it does not know -- see laboratory.Executes -- and the release treats that as "everything is executed", which is what ditto assumed until it could ask.

type Diagnostic

type Diagnostic struct {
	// contains filtered or unexported fields
}

func NewUnmeasuredDiagnostic added in v0.12.0

func NewUnmeasuredDiagnostic(file *gomutatedfile.GoMutatedFile) *Diagnostic

NewUnmeasuredDiagnostic is a mutant ditto did not run, because the configured test command does not compile the package that owns it.

Nothing the command runs can kill it, so its survival is not evidence about anybody's tests: the score leaves it out of the numerator and the denominator, exactly as it leaves out a mutant that never compiled, and the report names it instead. Measured on the report that asked for it: 43 mutants, 20 survivors, 17 of them in one package the command never builds, printed as a score of 0.53 against a bar of 0.80. docs/reports/ditto-mutation-scope.md.

It carries the result ditto would have reported before it could tell the difference -- a survivor -- so a consumer that does not know about Unmeasured reads what it read before, and none of them meets a nil.

func (*Diagnostic) Address added in v0.3.0

func (d *Diagnostic) Address() string

Address and Change are what a survivor is reported as before any diff is rendered: where it is, and what it wrote there.

func (*Diagnostic) Change added in v0.3.0

func (d *Diagnostic) Change() string

func (*Diagnostic) Diff

func (d *Diagnostic) Diff(differ gomutatedfile.Differ) string

func (*Diagnostic) IsOk

func (d *Diagnostic) IsOk() bool

func (*Diagnostic) Label

func (d *Diagnostic) Label() string

func (*Diagnostic) Path added in v0.12.0

func (d *Diagnostic) Path() string

Path is the repository-relative source file this mutant came from. The report groups the mutants it could not measure by the directory this names.

func (*Diagnostic) Reason added in v0.7.0

func (d *Diagnostic) Reason() verdict.Reason

Reason is why this mutant died, and Unknown when ditto was not told.

Ditto recognises a killed mutant by a non-zero exit, which a mutant that never compiled also produces. Measured on internal/schemata/instrument.go: 78 mutants, 50 reported killed, 10 of which did not compile and 1 of which hung until its timeout -- 22% of the kills credited to tests that never ran. See docs/metrics.md, metric 2.

A survivor has no death to explain, so it answers Unknown too. The reason is about a kill.

func (*Diagnostic) Unmeasured added in v0.12.0

func (d *Diagnostic) Unmeasured() bool

Unmeasured reports a mutant that was never run because the test command does not compile the package that owns it.

Read it BEFORE IsOk. An unmeasured diagnostic is shaped like a survivor -- that is what it would have been, before ditto could tell the difference -- so a consumer that ignores this reads exactly what it read before this existed, which is the graceful half. The shipped reporter does not ignore it.

func (*Diagnostic) Virus added in v0.7.0

func (d *Diagnostic) Virus() string

Virus names the mutation operator behind this diagnostic, which is the unit a non-viable mutant is fixed in. docs/metrics.md metric 1.

type Ditto

type Ditto struct {
	// contains filtered or unexported fields
}

func New

func New(logger Logger, repository Repository, laboratory Laboratory, reporter Reporter, scopes ...CommandScope) *Ditto

func (*Ditto) Release

func (o *Ditto) Release(viri ...viruses.Virus)

Release mutates every source file and reports what each mutant did.

Mutants are kept together by the file they came from, because a laboratory that compiles once for a whole file has to receive the file's mutants at once. The order they are reported in is unchanged: sources are walked in order, and each file's mutants in the order its viruses produced them.

A file whose package the test command cannot execute is not run at all: its mutants are recorded as unmeasured, and the report says so. Running them would buy a survivor nobody can kill with a full run of the suite, and the number that came back would be a mixture of two different questions.

type Laboratory

type Laboratory interface {
	Test(repository Repository, file *gomutatedfile.GoMutatedFile) future.Future[result.Result[string]]
}

type Logger

type Logger interface {
	Logf(message string, args ...any)
}

type RefusalError added in v0.4.0

type RefusalError struct {
	// contains filtered or unexported fields
}

RefusalError is the one thing a release says by stopping.

A laboratory that finds the suite already red cannot return that finding: Test hands back a future of a result, and every value in it means something about a mutant. There is no mutant here — the run has not started, and scoring anything would be scoring a suite that tested nothing. So it panics, and the panic is the verdict rather than a defect.

It is a type rather than a string because the two entry points need different things from it. Release lets it through, so a test fails with the message printed as the panic value. Run has to turn it into an error, and recognising it by matching text would tie the entry point to a sentence somebody will reword.

func NewRefusalError added in v0.4.0

func NewRefusalError(message string) RefusalError

NewRefusalError builds the refusal a laboratory panics with.

func (RefusalError) Error added in v0.4.0

func (r RefusalError) Error() string

Error is the message, and is what both the panic printer and Run report.

type Reporter

type Reporter interface {
	AddDiagnostic(diagnostic *Diagnostic)
	Summarize() result.Result[any]
}

type Repository

type Repository interface {
	ListGoSourceFiles() []*gosourcefile.GoSourceFile
	LinkAllToTemporaryRepository(temporaryPath string) TemporaryRepository
}

type ScoreCalculator

type ScoreCalculator func(total, killed int) float32

type TemporaryRepository

type TemporaryRepository interface {
	Root() string
	Overwrite(filePath string, data []byte)
	Remove()
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL