diff options
author | Elliott Sales de Andrade <[email protected]> | 2022-02-05 03:49:55 -0500 |
---|---|---|
committer | Ron Evans <[email protected]> | 2022-04-09 09:16:37 +0200 |
commit | 9cedd78d9ce0cd5802051a34cdef78662be78f86 (patch) | |
tree | cb9ec059717e6c67b2868ceb678f22128d209fd4 /src | |
parent | 20a27746b2e7764ebc74522a86bed9f3f565e56e (diff) | |
download | tinygo-9cedd78d9ce0cd5802051a34cdef78662be78f86.tar.gz tinygo-9cedd78d9ce0cd5802051a34cdef78662be78f86.zip |
Add a shim for internal/fuzz
This simply shadows the real code temporarily to see what else is
broken. It only defines a single type to fix testing/internal/testdeps.
Diffstat (limited to 'src')
-rw-r--r-- | src/internal/fuzz/fuzz.go | 133 | ||||
-rw-r--r-- | src/testing/fuzz.go | 143 | ||||
-rw-r--r-- | src/testing/testing.go | 9 | ||||
-rw-r--r-- | src/testing/testing_go118.go | 16 | ||||
-rw-r--r-- | src/testing/testing_other.go | 16 |
5 files changed, 308 insertions, 9 deletions
diff --git a/src/internal/fuzz/fuzz.go b/src/internal/fuzz/fuzz.go new file mode 100644 index 000000000..ee6e0544b --- /dev/null +++ b/src/internal/fuzz/fuzz.go @@ -0,0 +1,133 @@ +// Package fuzz is a shim to allow compilation against Go 1.18. +// It only defines a single type to work with testing/internal/testdeps, +// and hide all the other new dependencies from this package. +package fuzz + +import ( + "context" + "errors" + "io" + "reflect" + "time" +) + +// CorpusEntry represents an individual input for fuzzing. +// +// We must use an equivalent type in the testing and testing/internal/testdeps +// packages, but testing can't import this package directly, and we don't want +// to export this type from testing. Instead, we use the same struct type and +// use a type alias (not a defined type) for convenience. +type CorpusEntry = struct { + Parent string + + // Path is the path of the corpus file, if the entry was loaded from disk. + // For other entries, including seed values provided by f.Add, Path is the + // name of the test, e.g. seed#0 or its hash. + Path string + + // Data is the raw input data. Data should only be populated for seed + // values. For on-disk corpus files, Data will be nil, as it will be loaded + // from disk using Path. + Data []byte + + // Values is the unmarshaled values from a corpus file. + Values []any + + Generation int + + // IsSeed indicates whether this entry is part of the seed corpus. + IsSeed bool +} + +// CoordinateFuzzingOpts is a set of arguments for CoordinateFuzzing. +// The zero value is valid for each field unless specified otherwise. +type CoordinateFuzzingOpts struct { + // Log is a writer for logging progress messages and warnings. + // If nil, io.Discard will be used instead. + Log io.Writer + + // Timeout is the amount of wall clock time to spend fuzzing after the corpus + // has loaded. If zero, there will be no time limit. + Timeout time.Duration + + // Limit is the number of random values to generate and test. If zero, + // there will be no limit on the number of generated values. + Limit int64 + + // MinimizeTimeout is the amount of wall clock time to spend minimizing + // after discovering a crasher. If zero, there will be no time limit. If + // MinimizeTimeout and MinimizeLimit are both zero, then minimization will + // be disabled. + MinimizeTimeout time.Duration + + // MinimizeLimit is the maximum number of calls to the fuzz function to be + // made while minimizing after finding a crash. If zero, there will be no + // limit. Calls to the fuzz function made when minimizing also count toward + // Limit. If MinimizeTimeout and MinimizeLimit are both zero, then + // minimization will be disabled. + MinimizeLimit int64 + + // parallel is the number of worker processes to run in parallel. If zero, + // CoordinateFuzzing will run GOMAXPROCS workers. + Parallel int + + // Seed is a list of seed values added by the fuzz target with testing.F.Add + // and in testdata. + Seed []CorpusEntry + + // Types is the list of types which make up a corpus entry. + // Types must be set and must match values in Seed. + Types []reflect.Type + + // CorpusDir is a directory where files containing values that crash the + // code being tested may be written. CorpusDir must be set. + CorpusDir string + + // CacheDir is a directory containing additional "interesting" values. + // The fuzzer may derive new values from these, and may write new values here. + CacheDir string +} + +// CoordinateFuzzing creates several worker processes and communicates with +// them to test random inputs that could trigger crashes and expose bugs. +// The worker processes run the same binary in the same directory with the +// same environment variables as the coordinator process. Workers also run +// with the same arguments as the coordinator, except with the -test.fuzzworker +// flag prepended to the argument list. +// +// If a crash occurs, the function will return an error containing information +// about the crash, which can be reported to the user. +func CoordinateFuzzing(ctx context.Context, opts CoordinateFuzzingOpts) (err error) { + return errors.New("not implemented") +} + +// ReadCorpus reads the corpus from the provided dir. The returned corpus +// entries are guaranteed to match the given types. Any malformed files will +// be saved in a MalformedCorpusError and returned, along with the most recent +// error. +func ReadCorpus(dir string, types []reflect.Type) ([]CorpusEntry, error) { + return nil, errors.New("not implemented") +} + +// CheckCorpus verifies that the types in vals match the expected types +// provided. +func CheckCorpus(vals []any, types []reflect.Type) error { + return errors.New("not implemented") +} + +func ResetCoverage() {} +func SnapshotCoverage() {} + +// RunFuzzWorker is called in a worker process to communicate with the +// coordinator process in order to fuzz random inputs. RunFuzzWorker loops +// until the coordinator tells it to stop. +// +// fn is a wrapper on the fuzz function. It may return an error to indicate +// a given input "crashed". The coordinator will also record a crasher if +// the function times out or terminates the process. +// +// RunFuzzWorker returns an error if it could not communicate with the +// coordinator process. +func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error { + return errors.New("not implemented") +} diff --git a/src/testing/fuzz.go b/src/testing/fuzz.go new file mode 100644 index 000000000..76ad6d945 --- /dev/null +++ b/src/testing/fuzz.go @@ -0,0 +1,143 @@ +package testing + +import ( + "errors" + "fmt" + "reflect" + "time" +) + +// InternalFuzzTarget is an internal type but exported because it is +// cross-package; it is part of the implementation of the "go test" command. +type InternalFuzzTarget struct { + Name string + Fn func(f *F) +} + +// F is a type passed to fuzz tests. +// +// Fuzz tests run generated inputs against a provided fuzz target, which can +// find and report potential bugs in the code being tested. +// +// A fuzz test runs the seed corpus by default, which includes entries provided +// by (*F).Add and entries in the testdata/fuzz/<FuzzTestName> directory. After +// any necessary setup and calls to (*F).Add, the fuzz test must then call +// (*F).Fuzz to provide the fuzz target. See the testing package documentation +// for an example, and see the F.Fuzz and F.Add method documentation for +// details. +// +// *F methods can only be called before (*F).Fuzz. Once the test is +// executing the fuzz target, only (*T) methods can be used. The only *F methods +// that are allowed in the (*F).Fuzz function are (*F).Failed and (*F).Name. +type F struct { + common + fuzzContext *fuzzContext + testContext *testContext + + // inFuzzFn is true when the fuzz function is running. Most F methods cannot + // be called when inFuzzFn is true. + inFuzzFn bool + + // corpus is a set of seed corpus entries, added with F.Add and loaded + // from testdata. + corpus []corpusEntry + + result fuzzResult + fuzzCalled bool +} + +// corpusEntry is an alias to the same type as internal/fuzz.CorpusEntry. +// We use a type alias because we don't want to export this type, and we can't +// import internal/fuzz from testing. +type corpusEntry = struct { + Parent string + Path string + Data []byte + Values []interface{} + Generation int + IsSeed bool +} + +// Add will add the arguments to the seed corpus for the fuzz test. This will be +// a no-op if called after or within the fuzz target, and args must match the +// arguments for the fuzz target. +func (f *F) Add(args ...interface{}) { + var values []interface{} + for i := range args { + if t := reflect.TypeOf(args[i]); !supportedTypes[t] { + panic(fmt.Sprintf("testing: unsupported type to Add %v", t)) + } + values = append(values, args[i]) + } + f.corpus = append(f.corpus, corpusEntry{Values: values, IsSeed: true, Path: fmt.Sprintf("seed#%d", len(f.corpus))}) +} + +// supportedTypes represents all of the supported types which can be fuzzed. +var supportedTypes = map[reflect.Type]bool{ + reflect.TypeOf(([]byte)("")): true, + reflect.TypeOf((string)("")): true, + reflect.TypeOf((bool)(false)): true, + reflect.TypeOf((byte)(0)): true, + reflect.TypeOf((rune)(0)): true, + reflect.TypeOf((float32)(0)): true, + reflect.TypeOf((float64)(0)): true, + reflect.TypeOf((int)(0)): true, + reflect.TypeOf((int8)(0)): true, + reflect.TypeOf((int16)(0)): true, + reflect.TypeOf((int32)(0)): true, + reflect.TypeOf((int64)(0)): true, + reflect.TypeOf((uint)(0)): true, + reflect.TypeOf((uint8)(0)): true, + reflect.TypeOf((uint16)(0)): true, + reflect.TypeOf((uint32)(0)): true, + reflect.TypeOf((uint64)(0)): true, +} + +// Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of +// arguments, those arguments will be added to the seed corpus. +// +// ff must be a function with no return value whose first argument is *T and +// whose remaining arguments are the types to be fuzzed. +// For example: +// +// f.Fuzz(func(t *testing.T, b []byte, i int) { ... }) +// +// The following types are allowed: []byte, string, bool, byte, rune, float32, +// float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64. +// More types may be supported in the future. +// +// ff must not call any *F methods, e.g. (*F).Log, (*F).Error, (*F).Skip. Use +// the corresponding *T method instead. The only *F methods that are allowed in +// the (*F).Fuzz function are (*F).Failed and (*F).Name. +// +// This function should be fast and deterministic, and its behavior should not +// depend on shared state. No mutatable input arguments, or pointers to them, +// should be retained between executions of the fuzz function, as the memory +// backing them may be mutated during a subsequent invocation. ff must not +// modify the underlying data of the arguments provided by the fuzzing engine. +// +// When fuzzing, F.Fuzz does not return until a problem is found, time runs out +// (set with -fuzztime), or the test process is interrupted by a signal. F.Fuzz +// should be called exactly once, unless F.Skip or F.Fail is called beforehand. +func (f *F) Fuzz(ff interface{}) { + f.failed = true + f.result.N = 0 + f.result.T = 0 + f.result.Error = errors.New("operation not implemented") + return +} + +// fuzzContext holds fields common to all fuzz tests. +type fuzzContext struct { + deps testDeps + mode fuzzMode +} + +type fuzzMode uint8 + +// fuzzResult contains the results of a fuzz run. +type fuzzResult struct { + N int // The number of iterations. + T time.Duration // The total time taken. + Error error // Error is the error from the failing input +} diff --git a/src/testing/testing.go b/src/testing/testing.go index 243632354..045ee5beb 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -430,15 +430,6 @@ type testDeps interface { MatchString(pat, str string) (bool, error) } -func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M { - Init() - return &M{ - Tests: tests, - Benchmarks: benchmarks, - deps: deps.(testDeps), - } -} - // Run runs the tests. It returns an exit code to pass to os.Exit. func (m *M) Run() (code int) { defer func() { diff --git a/src/testing/testing_go118.go b/src/testing/testing_go118.go new file mode 100644 index 000000000..1c2c835be --- /dev/null +++ b/src/testing/testing_go118.go @@ -0,0 +1,16 @@ +//go:build go1.18 +// +build go1.18 + +package testing + +// MainStart is meant for use by tests generated by 'go test'. +// It is not meant to be called directly and is not subject to the Go 1 compatibility document. +// It may change signature from release to release. +func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M { + Init() + return &M{ + Tests: tests, + Benchmarks: benchmarks, + deps: deps.(testDeps), + } +} diff --git a/src/testing/testing_other.go b/src/testing/testing_other.go new file mode 100644 index 000000000..875115e96 --- /dev/null +++ b/src/testing/testing_other.go @@ -0,0 +1,16 @@ +//go:build !go1.18 +// +build !go1.18 + +package testing + +// MainStart is meant for use by tests generated by 'go test'. +// It is not meant to be called directly and is not subject to the Go 1 compatibility document. +// It may change signature from release to release. +func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M { + Init() + return &M{ + Tests: tests, + Benchmarks: benchmarks, + deps: deps.(testDeps), + } +} |