]> Cypherpunks.ru repositories - gostls13.git/blob - src/testing/internal/testdeps/deps.go
[dev.fuzz] internal/fuzz: move CoordinateFuzzing args into struct type
[gostls13.git] / src / testing / internal / testdeps / deps.go
1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package testdeps provides access to dependencies needed by test execution.
6 //
7 // This package is imported by the generated main package, which passes
8 // TestDeps into testing.Main. This allows tests to use packages at run time
9 // without making those packages direct dependencies of package testing.
10 // Direct dependencies of package testing are harder to write tests for.
11 package testdeps
12
13 import (
14         "bufio"
15         "context"
16         "internal/fuzz"
17         "internal/testlog"
18         "io"
19         "os"
20         "os/signal"
21         "reflect"
22         "regexp"
23         "runtime/pprof"
24         "strings"
25         "sync"
26         "time"
27 )
28
29 // TestDeps is an implementation of the testing.testDeps interface,
30 // suitable for passing to testing.MainStart.
31 type TestDeps struct{}
32
33 var matchPat string
34 var matchRe *regexp.Regexp
35
36 func (TestDeps) MatchString(pat, str string) (result bool, err error) {
37         if matchRe == nil || matchPat != pat {
38                 matchPat = pat
39                 matchRe, err = regexp.Compile(matchPat)
40                 if err != nil {
41                         return
42                 }
43         }
44         return matchRe.MatchString(str), nil
45 }
46
47 func (TestDeps) StartCPUProfile(w io.Writer) error {
48         return pprof.StartCPUProfile(w)
49 }
50
51 func (TestDeps) StopCPUProfile() {
52         pprof.StopCPUProfile()
53 }
54
55 func (TestDeps) WriteProfileTo(name string, w io.Writer, debug int) error {
56         return pprof.Lookup(name).WriteTo(w, debug)
57 }
58
59 // ImportPath is the import path of the testing binary, set by the generated main function.
60 var ImportPath string
61
62 func (TestDeps) ImportPath() string {
63         return ImportPath
64 }
65
66 // testLog implements testlog.Interface, logging actions by package os.
67 type testLog struct {
68         mu  sync.Mutex
69         w   *bufio.Writer
70         set bool
71 }
72
73 func (l *testLog) Getenv(key string) {
74         l.add("getenv", key)
75 }
76
77 func (l *testLog) Open(name string) {
78         l.add("open", name)
79 }
80
81 func (l *testLog) Stat(name string) {
82         l.add("stat", name)
83 }
84
85 func (l *testLog) Chdir(name string) {
86         l.add("chdir", name)
87 }
88
89 // add adds the (op, name) pair to the test log.
90 func (l *testLog) add(op, name string) {
91         if strings.Contains(name, "\n") || name == "" {
92                 return
93         }
94
95         l.mu.Lock()
96         defer l.mu.Unlock()
97         if l.w == nil {
98                 return
99         }
100         l.w.WriteString(op)
101         l.w.WriteByte(' ')
102         l.w.WriteString(name)
103         l.w.WriteByte('\n')
104 }
105
106 var log testLog
107
108 func (TestDeps) StartTestLog(w io.Writer) {
109         log.mu.Lock()
110         log.w = bufio.NewWriter(w)
111         if !log.set {
112                 // Tests that define TestMain and then run m.Run multiple times
113                 // will call StartTestLog/StopTestLog multiple times.
114                 // Checking log.set avoids calling testlog.SetLogger multiple times
115                 // (which will panic) and also avoids writing the header multiple times.
116                 log.set = true
117                 testlog.SetLogger(&log)
118                 log.w.WriteString("# test log\n") // known to cmd/go/internal/test/test.go
119         }
120         log.mu.Unlock()
121 }
122
123 func (TestDeps) StopTestLog() error {
124         log.mu.Lock()
125         defer log.mu.Unlock()
126         err := log.w.Flush()
127         log.w = nil
128         return err
129 }
130
131 // SetPanicOnExit0 tells the os package whether to panic on os.Exit(0).
132 func (TestDeps) SetPanicOnExit0(v bool) {
133         testlog.SetPanicOnExit0(v)
134 }
135
136 func (TestDeps) CoordinateFuzzing(timeout time.Duration, count int64, parallel int, seed []fuzz.CorpusEntry, types []reflect.Type, corpusDir, cacheDir string) (err error) {
137         // Fuzzing may be interrupted with a timeout or if the user presses ^C.
138         // In either case, we'll stop worker processes gracefully and save
139         // crashers and interesting values.
140         ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
141         defer cancel()
142         err = fuzz.CoordinateFuzzing(ctx, fuzz.CoordinateFuzzingOpts{
143                 Log:       os.Stderr,
144                 Timeout:   timeout,
145                 Count:     count,
146                 Parallel:  parallel,
147                 Seed:      seed,
148                 Types:     types,
149                 CorpusDir: corpusDir,
150                 CacheDir:  cacheDir,
151         })
152         if err == ctx.Err() {
153                 return nil
154         }
155         return err
156 }
157
158 func (TestDeps) RunFuzzWorker(fn func(fuzz.CorpusEntry) error) error {
159         // Worker processes may or may not receive a signal when the user presses ^C
160         // On POSIX operating systems, a signal sent to a process group is delivered
161         // to all processes in that group. This is not the case on Windows.
162         // If the worker is interrupted, return quickly and without error.
163         // If only the coordinator process is interrupted, it tells each worker
164         // process to stop by closing its "fuzz_in" pipe.
165         ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
166         defer cancel()
167         err := fuzz.RunFuzzWorker(ctx, fn)
168         if err == ctx.Err() {
169                 return nil
170         }
171         return err
172 }
173
174 func (TestDeps) ReadCorpus(dir string, types []reflect.Type) ([]fuzz.CorpusEntry, error) {
175         return fuzz.ReadCorpus(dir, types)
176 }