]> Cypherpunks.ru repositories - gostls13.git/blob - src/testing/internal/testdeps/deps.go
[dev.fuzz] internal/fuzz: refactor CorpusEntry 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         "regexp"
22         "runtime/pprof"
23         "strings"
24         "sync"
25         "time"
26 )
27
28 // TestDeps is an implementation of the testing.testDeps interface,
29 // suitable for passing to testing.MainStart.
30 type TestDeps struct{}
31
32 var matchPat string
33 var matchRe *regexp.Regexp
34
35 func (TestDeps) MatchString(pat, str string) (result bool, err error) {
36         if matchRe == nil || matchPat != pat {
37                 matchPat = pat
38                 matchRe, err = regexp.Compile(matchPat)
39                 if err != nil {
40                         return
41                 }
42         }
43         return matchRe.MatchString(str), nil
44 }
45
46 func (TestDeps) StartCPUProfile(w io.Writer) error {
47         return pprof.StartCPUProfile(w)
48 }
49
50 func (TestDeps) StopCPUProfile() {
51         pprof.StopCPUProfile()
52 }
53
54 func (TestDeps) WriteProfileTo(name string, w io.Writer, debug int) error {
55         return pprof.Lookup(name).WriteTo(w, debug)
56 }
57
58 // ImportPath is the import path of the testing binary, set by the generated main function.
59 var ImportPath string
60
61 func (TestDeps) ImportPath() string {
62         return ImportPath
63 }
64
65 // testLog implements testlog.Interface, logging actions by package os.
66 type testLog struct {
67         mu  sync.Mutex
68         w   *bufio.Writer
69         set bool
70 }
71
72 func (l *testLog) Getenv(key string) {
73         l.add("getenv", key)
74 }
75
76 func (l *testLog) Open(name string) {
77         l.add("open", name)
78 }
79
80 func (l *testLog) Stat(name string) {
81         l.add("stat", name)
82 }
83
84 func (l *testLog) Chdir(name string) {
85         l.add("chdir", name)
86 }
87
88 // add adds the (op, name) pair to the test log.
89 func (l *testLog) add(op, name string) {
90         if strings.Contains(name, "\n") || name == "" {
91                 return
92         }
93
94         l.mu.Lock()
95         defer l.mu.Unlock()
96         if l.w == nil {
97                 return
98         }
99         l.w.WriteString(op)
100         l.w.WriteByte(' ')
101         l.w.WriteString(name)
102         l.w.WriteByte('\n')
103 }
104
105 var log testLog
106
107 func (TestDeps) StartTestLog(w io.Writer) {
108         log.mu.Lock()
109         log.w = bufio.NewWriter(w)
110         if !log.set {
111                 // Tests that define TestMain and then run m.Run multiple times
112                 // will call StartTestLog/StopTestLog multiple times.
113                 // Checking log.set avoids calling testlog.SetLogger multiple times
114                 // (which will panic) and also avoids writing the header multiple times.
115                 log.set = true
116                 testlog.SetLogger(&log)
117                 log.w.WriteString("# test log\n") // known to cmd/go/internal/test/test.go
118         }
119         log.mu.Unlock()
120 }
121
122 func (TestDeps) StopTestLog() error {
123         log.mu.Lock()
124         defer log.mu.Unlock()
125         err := log.w.Flush()
126         log.w = nil
127         return err
128 }
129
130 // SetPanicOnExit0 tells the os package whether to panic on os.Exit(0).
131 func (TestDeps) SetPanicOnExit0(v bool) {
132         testlog.SetPanicOnExit0(v)
133 }
134
135 func (TestDeps) CoordinateFuzzing(timeout time.Duration, parallel int, seed []fuzz.CorpusEntry, corpusDir, cacheDir string) error {
136         // Fuzzing may be interrupted with a timeout or if the user presses ^C.
137         // In either case, we'll stop worker processes gracefully and save
138         // crashers and interesting values.
139         ctx, cancel := context.WithCancel(context.Background())
140         if timeout > 0 {
141                 ctx, cancel = context.WithTimeout(ctx, timeout)
142         }
143         interruptC := make(chan os.Signal, 1)
144         signal.Notify(interruptC, os.Interrupt)
145         go func() {
146                 <-interruptC
147                 cancel()
148         }()
149         defer func() { interruptC <- os.Interrupt }()
150
151         err := fuzz.CoordinateFuzzing(ctx, parallel, seed, corpusDir, cacheDir)
152         if err == ctx.Err() {
153                 return nil
154         }
155         return err
156 }
157
158 func (TestDeps) RunFuzzWorker(fn func([]byte) 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 := context.WithCancel(context.Background())
166         interruptC := make(chan os.Signal, 1)
167         signal.Notify(interruptC, os.Interrupt)
168         go func() {
169                 <-interruptC
170                 cancel()
171         }()
172         defer func() { interruptC <- os.Interrupt }()
173
174         err := fuzz.RunFuzzWorker(ctx, fn)
175         if err == ctx.Err() {
176                 return nil
177         }
178         return nil
179 }
180
181 func (TestDeps) ReadCorpus(dir string) ([]fuzz.CorpusEntry, error) {
182         return fuzz.ReadCorpus(dir)
183 }