Documentation
¶
Overview ¶
Package fuzz is the target-agnostic half of tergo's property fuzzer: an action alphabet, a seeded generator that biases toward the shapes that have actually broken this codebase, a driver loop, and a shrinker.
It knows nothing about tergo itself. A caller supplies a Target, which applies one Action and reports which invariants the resulting state violates, and the loop here handles seeding, reproduction, and minimisation. Two targets exist: an in-process one that drives app.OS through its real bubbletea Update and reads the invariants off the model (internal/app), and a tuitest one that replays the same stream against a real binary in a PTY (e2e/tui). The alphabet is shared so a failure found by either is a repro the other can also run.
Nothing in the shipped binary imports this package; it exists for `go test`.
Index ¶
- Constants
- func Script(as []Action) string
- type Action
- func Generate(seed uint64, n int) []Action
- func GenerateBytes(b []byte, n int) []Action
- func GenerateBytesFloor(b []byte, n, minW, minH int) []Action
- func GenerateFloor(seed uint64, n, minW, minH int) []Action
- func ParseAction(line string) (Action, error)
- func ParseScript(s string) ([]Action, error)
- type Config
- type Generator
- type Kind
- type NopObserver
- type Observer
- type Result
- type RuleInfo
- type RuleLister
- type Target
- type Violation
Constants ¶
const ( ButtonNone = iota ButtonLeft ButtonRight ButtonMiddle )
Mouse buttons, matching the order the generator picks from.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Action ¶
Action is one step of a run. The three integers and the string cover every kind, which keeps the repro format a single flat line per action and the shrinker's per-field simplification uniform.
func GenerateBytes ¶
GenerateBytes is Generate for a coverage-guided input.
func GenerateBytesFloor ¶
GenerateBytesFloor is GenerateBytes with a host-size floor.
func GenerateFloor ¶
GenerateFloor is Generate with a lower bound on the host sizes it picks.
func ParseAction ¶
ParseAction reads back one line of String's output.
func ParseScript ¶
ParseScript reads back Script's output, skipping blanks and # comments so a maintainer can annotate a saved repro.
type Config ¶
type Config struct {
// Seed identifies the run. It is printed on failure and re-running it
// reproduces the finding exactly.
Seed uint64
// Steps is how many actions to generate when Actions is empty.
Steps int
// MinWidth and MinHeight floor the host sizes the generator picks, which is
// how a campaign steps over a bug class it has already reported in order to
// reach the rest of the space.
MinWidth, MinHeight int
// Weights overrides the generator's action weights, indexed by Kind. A
// target that can express actions the other cannot spends its budget there
// rather than on the alphabet the cheaper target already covers exhaustively.
Weights []int
// Actions overrides generation, which is how the coverage-guided entry
// point and a saved repro both drive the same loop.
Actions []Action
// NoShrink reports the raw failing sequence. Only useful when a target's
// Reset is too slow to replay hundreds of times.
NoShrink bool
// ShrinkBudget caps predicate replays. Zero picks a default scaled to the
// sequence length.
ShrinkBudget int
Observer Observer
}
Config is one fuzzing run.
type Generator ¶
type Generator struct {
// contains filtered or unexported fields
}
Generator turns a source into a stream of actions. It carries the small amount of state needed to emit sequences that are awkward on purpose: whether a mouse button is currently held, so it can slip a host resize or a detach between a press and its release.
func NewByteGenerator ¶
NewByteGenerator decodes a `go test -fuzz` byte slice into the same alphabet.
func NewGenerator ¶
NewGenerator seeds a generator for the local loop. The same seed always yields the same run.
func (*Generator) Bias ¶
Bias replaces the action weights, indexed by Kind. A short slice leaves the kinds past its end at their default, so a caller names only what it wants to move. Nil keeps the defaults, which is what makes it safe to wire straight through from a Config field that most callers never set.
func (*Generator) Floor ¶
Floor restricts the host sizes the generator produces. Zero means no floor, which is the campaign that hunts degenerate viewports.
type Kind ¶
type Kind uint8
Kind is one action in the alphabet. The set covers what a user does and, more to the point, what has broken: the leader chords, drags that end outside the target they started on, host resizes landing mid-gesture, and the runtime settings flips that make a pane's border appear under a guest that was never told about it.
const ( Key Kind = iota // a single key press, S is the key name Chord // the leader key then S, as two presses Text // S typed a rune at a time, for rename fields MousePress // A,B is the cell, C is the button MouseMotion // A,B is the cell, C is the button held (0 = none) MouseRelease // A,B is the cell, C is the button MouseWheel // A,B is the cell, C is the direction Resize // A,B is the new host size in cells NewPane // ClosePane // A selects which pane, modulo the count ZoomPane // FocusPane // A selects which pane, modulo the count MovePane // A is a direction index SwitchWorkspace // A is the workspace, 1..NumWorkspaces SwitchSession // A selects a session, modulo the count ToggleTiling // LayoutMode // A selects bsp/master-stack/scrolling ToggleSidebar // SidebarCollapse // SidebarPosition // A picks left or right OpenOverlay // A selects which overlay CloseOverlay // Rename // S is the new name Detach // Attach // Setting // A selects a runtime setting, B its new value Tick // one maintenance tick Guest // S is written to the focused pane's emulator AltScreen // A odd enters the alternate screen, B picks the pane Burst // A lines out of the pane B picks, to outrun a buffer SecondClient // a second client attaches to the same live session DaemonRestart // the daemon goes away and its sessions come back )
type NopObserver ¶
type NopObserver struct{}
NopObserver is the default. Embed it to implement only the methods a display cares about.
func (NopObserver) Done ¶
func (NopObserver) Done(Result)
func (NopObserver) Start ¶
func (NopObserver) Start(uint64, int)
type Observer ¶
type Observer interface {
Start(seed uint64, steps int)
Step(i int, a Action, vs []Violation)
Rule(step int, rule string, ok bool)
Shrink(pass string, size int, accepted bool)
Done(r Result)
}
Observer watches a run. It is the only seam a display attaches to, and it is deliberately narrow and read only: the driver hands out what happened and takes nothing back, so no display can change what the fuzzer does. Every method is called from the driver's goroutine and must not block.
The four calls are the whole vocabulary:
Start once, before the first action Step one action executed, with whatever it broke Rule one invariant checked and its result, for every rule, every step Shrink one minimisation candidate and whether it was kept Done once, with the final result and the minimal repro
type Result ¶
type Result struct {
Seed uint64
Failed bool
Step int // the index of the action that broke it, in the minimal sequence
Violations []Violation
// Actions is the minimal sequence that still breaks the same rule, or the
// whole run when it passed.
Actions []Action
Executed int
Replays int
}
Result is what a run found.
type RuleInfo ¶
RuleInfo describes one invariant. Name is what a Violation carries, so a display can map a failure onto the exact rule that produced it; Family groups related rules for presentation; Doc is the one line a display shows to say what went wrong in words rather than in an identifier.
Family is a field rather than a prefix on Name because a prefix is a parsing convention nothing enforces: a typo makes a phantom group that looks deliberate. A field is checked by the compiler and listed in one place.
type RuleLister ¶
type RuleLister interface{ Rules() []RuleInfo }
RuleLister is an optional Target capability. A target that can name its rules gets per-rule results reported to the Observer; one that cannot still gets Step, carrying whichever rules actually broke. It is optional so the oracle never has to know an observer exists.
The names must be the ones Violations carry, and in the order Check applies them, because that is what makes "everything after the break went unrun" true.
type Target ¶
Target is the system under test. The driver owns sequencing, seeding, and minimisation; a Target owns nothing but "put me back at the start", "do this one thing", and "which invariants are broken right now".
Check runs after every action, so it must be cheap enough to run thousands of times. Reset must return the target to a state that depends only on the actions replayed since, or a shrunk repro will not reproduce.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package apptarget drives a real app.OS through its real bubbletea Update as a fuzz.Target, using nothing but package app's exported surface.
|
Package apptarget drives a real app.OS through its real bubbletea Update as a fuzz.Target, using nothing but package app's exported surface. |
|
Package vis draws a fuzzing run while it happens.
|
Package vis draws a fuzzing run while it happens. |