aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorElliott Sales de Andrade <[email protected]>2022-02-05 03:49:55 -0500
committerRon Evans <[email protected]>2022-04-09 09:16:37 +0200
commit9cedd78d9ce0cd5802051a34cdef78662be78f86 (patch)
treecb9ec059717e6c67b2868ceb678f22128d209fd4 /src
parent20a27746b2e7764ebc74522a86bed9f3f565e56e (diff)
downloadtinygo-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.go133
-rw-r--r--src/testing/fuzz.go143
-rw-r--r--src/testing/testing.go9
-rw-r--r--src/testing/testing_go118.go16
-rw-r--r--src/testing/testing_other.go16
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),
+ }
+}