]> Cypherpunks.ru repositories - gostls13.git/blob - src/testing/internal/testdeps/deps.go
testing: add available godoc link
[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(
137         timeout time.Duration,
138         limit int64,
139         minimizeTimeout time.Duration,
140         minimizeLimit int64,
141         parallel int,
142         seed []fuzz.CorpusEntry,
143         types []reflect.Type,
144         corpusDir,
145         cacheDir string) (err error) {
146         // Fuzzing may be interrupted with a timeout or if the user presses ^C.
147         // In either case, we'll stop worker processes gracefully and save
148         // crashers and interesting values.
149         ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
150         defer cancel()
151         err = fuzz.CoordinateFuzzing(ctx, fuzz.CoordinateFuzzingOpts{
152                 Log:             os.Stderr,
153                 Timeout:         timeout,
154                 Limit:           limit,
155                 MinimizeTimeout: minimizeTimeout,
156                 MinimizeLimit:   minimizeLimit,
157                 Parallel:        parallel,
158                 Seed:            seed,
159                 Types:           types,
160                 CorpusDir:       corpusDir,
161                 CacheDir:        cacheDir,
162         })
163         if err == ctx.Err() {
164                 return nil
165         }
166         return err
167 }
168
169 func (TestDeps) RunFuzzWorker(fn func(fuzz.CorpusEntry) error) error {
170         // Worker processes may or may not receive a signal when the user presses ^C
171         // On POSIX operating systems, a signal sent to a process group is delivered
172         // to all processes in that group. This is not the case on Windows.
173         // If the worker is interrupted, return quickly and without error.
174         // If only the coordinator process is interrupted, it tells each worker
175         // process to stop by closing its "fuzz_in" pipe.
176         ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
177         defer cancel()
178         err := fuzz.RunFuzzWorker(ctx, fn)
179         if err == ctx.Err() {
180                 return nil
181         }
182         return err
183 }
184
185 func (TestDeps) ReadCorpus(dir string, types []reflect.Type) ([]fuzz.CorpusEntry, error) {
186         return fuzz.ReadCorpus(dir, types)
187 }
188
189 func (TestDeps) CheckCorpus(vals []any, types []reflect.Type) error {
190         return fuzz.CheckCorpus(vals, types)
191 }
192
193 func (TestDeps) ResetCoverage() {
194         fuzz.ResetCoverage()
195 }
196
197 func (TestDeps) SnapshotCoverage() {
198         fuzz.SnapshotCoverage()
199 }