Skip to content

Commit 6082d33

Browse files
committed
pkg/manager/diff: add tests
Refactor the package to support testing and add a number of basic tests.
1 parent 7c4a09a commit 6082d33

File tree

5 files changed

+497
-32
lines changed

5 files changed

+497
-32
lines changed

pkg/manager/diff/diff_test.go

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
package diff
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/google/syzkaller/pkg/flatrpc"
9+
"github.com/google/syzkaller/pkg/manager"
10+
"github.com/google/syzkaller/pkg/mgrconfig"
11+
"github.com/google/syzkaller/pkg/report"
12+
"github.com/google/syzkaller/pkg/repro"
13+
"github.com/google/syzkaller/prog"
14+
_ "github.com/google/syzkaller/prog/test"
15+
"github.com/google/syzkaller/vm"
16+
"github.com/google/syzkaller/vm/dispatcher"
17+
)
18+
19+
type testEnv struct {
20+
t *testing.T
21+
ctx context.Context
22+
cancel context.CancelFunc
23+
diffCtx *diffContext
24+
base *MockKernel
25+
new *MockKernel
26+
done chan error
27+
}
28+
29+
func newTestEnv(t *testing.T, cfg *Config) *testEnv {
30+
if cfg == nil {
31+
cfg = &Config{}
32+
}
33+
if cfg.Store == nil {
34+
cfg.Store = &manager.DiffFuzzerStore{BasePath: t.TempDir()}
35+
}
36+
if cfg.PatchedOnly == nil {
37+
cfg.PatchedOnly = make(chan *Bug, 1)
38+
}
39+
if cfg.BaseCrashes == nil {
40+
cfg.BaseCrashes = make(chan string, 1)
41+
}
42+
if cfg.runner == nil {
43+
// Default to a no-op runner if none provided.
44+
cfg.runner = newMockRunner(nil)
45+
}
46+
47+
diffCtx := &diffContext{
48+
cfg: *cfg,
49+
doneRepro: make(chan *manager.ReproResult, 1),
50+
store: cfg.Store,
51+
reproAttempts: map[string]int{},
52+
patchedOnly: cfg.PatchedOnly,
53+
}
54+
55+
base := &MockKernel{
56+
CrashesCh: make(chan *report.Report, 1),
57+
LoopFunc: func(ctx context.Context) error { return nil },
58+
}
59+
newKernel := &MockKernel{
60+
CrashesCh: make(chan *report.Report, 1),
61+
LoopFunc: func(ctx context.Context) error { return nil },
62+
}
63+
64+
newKernel.PoolVal = dispatcher.NewPool[*vm.Instance](1, func(ctx context.Context, index int) (*vm.Instance, error) {
65+
return &vm.Instance{}, nil
66+
}, func(ctx context.Context, inst *vm.Instance, upd dispatcher.UpdateInfo) {
67+
})
68+
newKernel.ConfigVal = &mgrconfig.Config{}
69+
70+
diffCtx.base = base
71+
diffCtx.new = newKernel
72+
73+
ctx, cancel := context.WithCancel(context.Background())
74+
75+
return &testEnv{
76+
t: t,
77+
ctx: ctx,
78+
cancel: cancel,
79+
diffCtx: diffCtx,
80+
base: base,
81+
new: newKernel,
82+
done: make(chan error, 1),
83+
}
84+
}
85+
86+
func (env *testEnv) start() {
87+
go func() {
88+
env.done <- env.diffCtx.Loop(env.ctx)
89+
}()
90+
}
91+
92+
func (env *testEnv) close() {
93+
env.cancel()
94+
select {
95+
case <-env.done:
96+
case <-time.After(5 * time.Second):
97+
env.t.Error("timeout waiting for diffCtx loop to exit")
98+
}
99+
}
100+
101+
func (env *testEnv) waitForStatus(title string, status manager.DiffBugStatus) {
102+
env.t.Helper()
103+
start := time.Now()
104+
for time.Since(start) < 15*time.Second {
105+
for _, bug := range env.diffCtx.store.List() {
106+
if bug.Title == title && bug.Status == status {
107+
return
108+
}
109+
}
110+
time.Sleep(10 * time.Millisecond)
111+
}
112+
env.t.Fatalf("timed out waiting for status: %s", status)
113+
}
114+
115+
type MockKernel struct {
116+
LoopFunc func(ctx context.Context) error
117+
CrashesCh chan *report.Report
118+
TriageProgressVal float64
119+
ProgsPerAreaVal map[string]int
120+
CoverFiltersVal manager.CoverageFilters
121+
ConfigVal *mgrconfig.Config
122+
PoolVal *vm.Dispatcher
123+
FeaturesVal flatrpc.Feature
124+
ReporterVal *report.Reporter
125+
}
126+
127+
func (mk *MockKernel) Loop(ctx context.Context) error {
128+
if mk.LoopFunc != nil {
129+
return mk.LoopFunc(ctx)
130+
}
131+
<-ctx.Done()
132+
return nil
133+
}
134+
135+
func (mk *MockKernel) Crashes() <-chan *report.Report {
136+
return mk.CrashesCh
137+
}
138+
139+
func (mk *MockKernel) TriageProgress() float64 {
140+
return mk.TriageProgressVal
141+
}
142+
143+
func (mk *MockKernel) ProgsPerArea() map[string]int {
144+
return mk.ProgsPerAreaVal
145+
}
146+
147+
func (mk *MockKernel) CoverFilters() manager.CoverageFilters {
148+
return mk.CoverFiltersVal
149+
}
150+
151+
func (mk *MockKernel) Config() *mgrconfig.Config {
152+
return mk.ConfigVal
153+
}
154+
155+
func (mk *MockKernel) Pool() *vm.Dispatcher {
156+
return mk.PoolVal
157+
}
158+
159+
func (mk *MockKernel) Features() flatrpc.Feature {
160+
return mk.FeaturesVal
161+
}
162+
163+
func (mk *MockKernel) Reporter() *report.Reporter {
164+
return mk.ReporterVal
165+
}
166+
167+
func (mk *MockKernel) FinishCorpusTriage() {
168+
mk.TriageProgressVal = 1.0
169+
}
170+
171+
type mockRunner struct {
172+
runFunc func(ctx context.Context, k Kernel, r *repro.Result, fullRepro bool)
173+
doneCh chan reproRunnerResult
174+
}
175+
176+
func newMockRunner(cb func(context.Context, Kernel, *repro.Result, bool)) *mockRunner {
177+
return &mockRunner{
178+
runFunc: cb,
179+
doneCh: make(chan reproRunnerResult, 1),
180+
}
181+
}
182+
183+
func (m *mockRunner) Run(ctx context.Context, k Kernel, r *repro.Result, fullRepro bool) {
184+
if m.runFunc != nil {
185+
m.runFunc(ctx, k, r, fullRepro)
186+
}
187+
}
188+
189+
func (m *mockRunner) Results() <-chan reproRunnerResult {
190+
return m.doneCh
191+
}
192+
193+
func mockRepro(title string, err error) func(context.Context, []byte, repro.Environment) (
194+
*repro.Result, *repro.Stats, error) {
195+
return mockReproCallback(title, err, nil)
196+
}
197+
198+
func mockReproCallback(title string, returnErr error, callback func()) func(context.Context, []byte, repro.Environment) (
199+
*repro.Result, *repro.Stats, error) {
200+
return func(ctx context.Context, crashLog []byte, env repro.Environment) (*repro.Result, *repro.Stats, error) {
201+
if callback != nil {
202+
callback()
203+
}
204+
if returnErr != nil {
205+
return nil, nil, returnErr
206+
}
207+
target, err := prog.GetTarget("test", "64")
208+
if err != nil {
209+
return nil, nil, err
210+
}
211+
return &repro.Result{
212+
Report: &report.Report{Title: title},
213+
Prog: target.DataMmapProg(),
214+
}, &repro.Stats{}, nil
215+
}
216+
}

pkg/manager/diff/kernel.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ func (kc *kernelContext) runInstance(ctx context.Context, inst *vm.Instance,
297297
return nil, err
298298
}
299299

300-
func (kc *kernelContext) triageProgress() float64 {
300+
func (kc *kernelContext) TriageProgress() float64 {
301301
fuzzer := kc.fuzzer.Load()
302302
if fuzzer == nil {
303303
return 0
@@ -310,10 +310,34 @@ func (kc *kernelContext) triageProgress() float64 {
310310
return 1.0 - float64(fuzzer.CandidatesToTriage())/float64(total)
311311
}
312312

313-
func (kc *kernelContext) progsPerArea() map[string]int {
313+
func (kc *kernelContext) ProgsPerArea() map[string]int {
314314
fuzzer := kc.fuzzer.Load()
315315
if fuzzer == nil {
316316
return nil
317317
}
318318
return fuzzer.Config.Corpus.ProgsPerArea()
319319
}
320+
321+
func (kc *kernelContext) Crashes() <-chan *report.Report {
322+
return kc.crashes
323+
}
324+
325+
func (kc *kernelContext) CoverFilters() manager.CoverageFilters {
326+
return kc.coverFilters
327+
}
328+
329+
func (kc *kernelContext) Config() *mgrconfig.Config {
330+
return kc.cfg
331+
}
332+
333+
func (kc *kernelContext) Pool() *vm.Dispatcher {
334+
return kc.pool
335+
}
336+
337+
func (kc *kernelContext) Features() flatrpc.Feature {
338+
return kc.features
339+
}
340+
341+
func (kc *kernelContext) Reporter() *report.Reporter {
342+
return kc.reporter
343+
}

0 commit comments

Comments
 (0)