diff options
| author | Mara Mihali <maramihali@google.com> | 2021-07-20 07:30:15 +0000 |
|---|---|---|
| committer | maramihali <maramihali@google.com> | 2021-07-22 13:39:23 +0300 |
| commit | db24b250f15cce6c54025419f9c273e4d65a59b4 (patch) | |
| tree | 894beeffaaa0a41460415474b41b7bc81d3b0c8b /syz-verifier | |
| parent | 37194463516b82f00082f18f23bf949edd69bab7 (diff) | |
syz-verifier: move all files to main package
Diffstat (limited to 'syz-verifier')
| -rwxr-xr-x | syz-verifier/main.go | 20 | ||||
| -rw-r--r-- | syz-verifier/main_test.go | 140 | ||||
| -rw-r--r-- | syz-verifier/stats.go (renamed from syz-verifier/stats/stats.go) | 4 | ||||
| -rw-r--r-- | syz-verifier/stats_test.go (renamed from syz-verifier/stats/stats_test.go) | 9 | ||||
| -rw-r--r-- | syz-verifier/test_utils.go | 89 | ||||
| -rw-r--r-- | syz-verifier/verifier.go (renamed from syz-verifier/verf/verifier.go) | 7 | ||||
| -rw-r--r-- | syz-verifier/verifier_test.go (renamed from syz-verifier/verf/verifier_test.go) | 43 |
7 files changed, 148 insertions, 164 deletions
diff --git a/syz-verifier/main.go b/syz-verifier/main.go index 3b3a447d1..683942871 100755 --- a/syz-verifier/main.go +++ b/syz-verifier/main.go @@ -24,8 +24,6 @@ import ( "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/pkg/tool" "github.com/google/syzkaller/prog" - "github.com/google/syzkaller/syz-verifier/stats" - "github.com/google/syzkaller/syz-verifier/verf" "github.com/google/syzkaller/vm" ) @@ -57,7 +55,7 @@ type Verifier struct { calls map[*prog.Syscall]bool reasons map[*prog.Syscall]string reportReasons bool - stats *stats.Stats + stats *Stats statsWrite io.Writer newEnv bool } @@ -97,7 +95,7 @@ type progInfo struct { prog *prog.Prog idx int serialized []byte - res []*verf.Result + res []*Result // left contains the indices of kernels that haven't sent results for this // program yet. left map[int]bool @@ -318,7 +316,7 @@ func (srv *RPCServer) UpdateUnsupported(a *rpctype.UpdateUnsupportedArgs, r *int srv.notChecked-- if srv.notChecked == 0 { vrf.finalizeCallSet(os.Stdout) - vrf.stats = stats.InitStats(vrf.calls, vrf.statsWrite) + vrf.stats = InitStats(vrf.calls, vrf.statsWrite) vrf.choiceTable = vrf.target.BuildChoiceTable(nil, vrf.calls) srv.cond.Signal() } @@ -364,7 +362,7 @@ func (srv *RPCServer) NextExchange(a *rpctype.NextExchangeArgs, r *rpctype.NextE srv.mu.Lock() defer srv.mu.Unlock() if a.Info.Calls != nil { - res := &verf.Result{ + res := &Result{ Pool: a.Pool, Hanged: a.Hanged, Info: a.Info, @@ -405,7 +403,7 @@ func (srv *RPCServer) NextExchange(a *rpctype.NextExchangeArgs, r *rpctype.NextE // newResult is called when a Runner sends a new Result. It returns true if all // Results from the corresponding programs have been received and they can be // sent for verification. Otherwise, it returns false. -func (srv *RPCServer) newResult(res *verf.Result, prog *progInfo) bool { +func (srv *RPCServer) newResult(res *Result, prog *progInfo) bool { prog.res = append(prog.res, res) delete(prog.left, res.Pool) return len(prog.left) == 0 @@ -415,9 +413,9 @@ func (srv *RPCServer) newResult(res *verf.Result, prog *progInfo) bool { // case differences are found, it will store a result report highlighting those // in th workdir/results directory. If writing the results fails, it returns an // error. -func (vrf *Verifier) processResults(res []*verf.Result, prog *prog.Prog) { +func (vrf *Verifier) processResults(res []*Result, prog *prog.Prog) { vrf.stats.Progs++ - rr := verf.Verify(res, prog, vrf.stats) + rr := Verify(res, prog, vrf.stats) if rr == nil { return } @@ -450,7 +448,7 @@ func (vrf *Verifier) processResults(res []*verf.Result, prog *prog.Prog) { log.Printf("result-%d written successfully", oldest) } -func createReport(rr *verf.ResultReport, pools int) []byte { +func createReport(rr *ResultReport, pools int) []byte { calls := strings.Split(rr.Prog, "\n") calls = calls[:len(calls)-1] @@ -493,7 +491,7 @@ func (srv *RPCServer) newProgram(poolIdx, vmIdx int) ([]byte, int) { prog: prog, idx: progIdx, serialized: prog.Serialize(), - res: make([]*verf.Result, 0), + res: make([]*Result, 0), left: make(map[int]bool), } for idx, pool := range srv.pools { diff --git a/syz-verifier/main_test.go b/syz-verifier/main_test.go index 38126a533..b021d05ae 100644 --- a/syz-verifier/main_test.go +++ b/syz-verifier/main_test.go @@ -4,95 +4,17 @@ package main import ( "bytes" - "math/rand" "os" "path/filepath" "strings" "testing" - "time" "github.com/google/go-cmp/cmp" - "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/prog" - "github.com/google/syzkaller/syz-verifier/stats" - "github.com/google/syzkaller/syz-verifier/verf" ) -var ( - srv *RPCServer -) - -func createTestServer(t *testing.T) { - target, err := prog.GetTarget("test", "64") - if err != nil { - t.Fatalf("failed to initialise test target: %v", err) - } - vrf := Verifier{ - target: target, - choiceTable: target.DefaultChoiceTable(), - rnd: rand.New(rand.NewSource(time.Now().UnixNano())), - progIdx: 3, - } - vrf.resultsdir = makeTestResultDirectory(t) - vrf.stats = getTestStats() - srv, err = startRPCServer(&vrf) - if err != nil { - t.Fatalf("failed to initialise RPC server: %v", err) - } -} - -func getTestProgram(t *testing.T) *prog.Prog { - p := "breaks_returns()\n" + - "minimize$0(0x1, 0x1)\n" + - "test$res0()\n" - target := prog.InitTargetTest(t, "test", "64") - prog, err := target.Deserialize([]byte(p), prog.Strict) - if err != nil { - t.Fatalf("failed to deserialise test program: %v", err) - } - return prog -} - -func getTestStats() *stats.Stats { - return &stats.Stats{ - Calls: map[string]*stats.CallStats{ - "breaks_returns": {Name: "breaks_returns", States: map[int]bool{}}, - "minimize$0": {Name: "minimize$0", States: map[int]bool{}}, - "test$res0": {Name: "test$res0", States: map[int]bool{}}, - }, - } -} - -func makeCallStats(name string, occurrences, mismatches int, states map[int]bool) *stats.CallStats { - return &stats.CallStats{Name: name, - Occurrences: occurrences, - Mismatches: mismatches, - States: states} -} - -func makeTestResultDirectory(t *testing.T) string { - resultsdir := "test" - err := osutil.MkdirAll(resultsdir) - if err != nil { - t.Fatalf("failed to create results directory: %v", err) - } - resultsdir, err = filepath.Abs(resultsdir) - if err != nil { - t.Fatalf("failed to get absolute path of resultsdir: %v", err) - } - return resultsdir -} - -func makeResult(pool int, errnos []int) *verf.Result { - r := &verf.Result{Pool: pool, Info: ipc.ProgInfo{Calls: []ipc.CallInfo{}}} - for _, e := range errnos { - r.Info.Calls = append(r.Info.Calls, ipc.CallInfo{Errno: e}) - } - return r -} - func TestNewProgram(t *testing.T) { tests := []struct { name string @@ -115,7 +37,7 @@ func TestNewProgram(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - createTestServer(t) + srv := createTestServer(t) srv.pools = map[int]*poolInfo{ 1: { vmRunners: map[int][]*progInfo{ @@ -153,21 +75,21 @@ func TestNewResult(t *testing.T) { tests := []struct { name string idx int - res verf.Result + res Result left map[int]bool wantReady bool }{ { name: "Results ready for verification", idx: 3, - res: verf.Result{Pool: 1}, + res: Result{Pool: 1}, wantReady: true, left: map[int]bool{}, }, { name: "No results ready for verification", idx: 1, - res: verf.Result{Pool: 1}, + res: Result{Pool: 1}, wantReady: false, left: map[int]bool{ 2: true, @@ -176,7 +98,7 @@ func TestNewResult(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - createTestServer(t) + srv := createTestServer(t) srv.progs = map[int]*progInfo{ 1: {idx: 1, left: map[int]bool{1: true, 2: true}}, @@ -195,7 +117,7 @@ func TestNewResult(t *testing.T) { } func TestConnect(t *testing.T) { - createTestServer(t) + srv := createTestServer(t) srv.pools = map[int]*poolInfo{ 1: { vmRunners: map[int][]*progInfo{ @@ -403,22 +325,22 @@ func TestUpdateUnsupportedNotCalledTwice(t *testing.T) { func TestProcessResults(t *testing.T) { tests := []struct { name string - res []*verf.Result + res []*Result prog string wantExist bool - wantStats *stats.Stats + wantStats *Stats }{ { name: "report written", - res: []*verf.Result{ + res: []*Result{ makeResult(1, []int{1, 3, 2}), makeResult(4, []int{1, 3, 5}), }, wantExist: true, - wantStats: &stats.Stats{ + wantStats: &Stats{ TotalMismatches: 1, Progs: 1, - Calls: map[string]*stats.CallStats{ + Calls: map[string]*CallStats{ "breaks_returns": makeCallStats("breaks_returns", 1, 0, map[int]bool{}), "test$res0": makeCallStats("test$res0", 1, 1, map[int]bool{2: true, 5: true}), "minimize$0": makeCallStats("minimize$0", 1, 0, map[int]bool{}), @@ -427,13 +349,13 @@ func TestProcessResults(t *testing.T) { }, { name: "no report written", - res: []*verf.Result{ + res: []*Result{ makeResult(2, []int{11, 33, 22}), makeResult(3, []int{11, 33, 22}), }, - wantStats: &stats.Stats{ + wantStats: &Stats{ Progs: 1, - Calls: map[string]*stats.CallStats{ + Calls: map[string]*CallStats{ "breaks_returns": makeCallStats("breaks_returns", 1, 0, map[int]bool{}), "minimize$0": makeCallStats("minimize$0", 1, 0, map[int]bool{}), "test$res0": makeCallStats("test$res0", 1, 0, map[int]bool{}), @@ -446,7 +368,7 @@ func TestProcessResults(t *testing.T) { prog := getTestProgram(t) vrf := Verifier{ resultsdir: makeTestResultDirectory(t), - stats: getTestStats(), + stats: emptyTestStats(), } resultFile := filepath.Join(vrf.resultsdir, "result-0") @@ -465,20 +387,20 @@ func TestProcessResults(t *testing.T) { } func TestCreateReport(t *testing.T) { - rr := verf.ResultReport{ + rr := ResultReport{ Prog: "breaks_returns()\n" + "minimize$0(0x1, 0x1)\n" + "test$res0()\n", - Reports: []*verf.CallReport{ - {Call: "breaks_returns", States: map[int]verf.ReturnState{ + Reports: []*CallReport{ + {Call: "breaks_returns", States: map[int]ReturnState{ 1: {Errno: 1, Flags: 1}, 2: {Errno: 1, Flags: 1}, 3: {Errno: 1, Flags: 1}}}, - {Call: "minimize$0", States: map[int]verf.ReturnState{ + {Call: "minimize$0", States: map[int]ReturnState{ 1: {Errno: 3, Flags: 3}, 2: {Errno: 3, Flags: 3}, 3: {Errno: 3, Flags: 3}}}, - {Call: "test$res0", States: map[int]verf.ReturnState{ + {Call: "test$res0", States: map[int]ReturnState{ 1: {Errno: 2, Flags: 7}, 2: {Errno: 5, Flags: 3}, 3: {Errno: 22, Flags: 1}}, @@ -507,7 +429,7 @@ func TestCleanup(t *testing.T) { name string progs map[int]*progInfo wantProg *progInfo - wantStats *stats.Stats + wantStats *Stats progExists bool fileExists bool }{ @@ -522,7 +444,7 @@ func TestCleanup(t *testing.T) { idx: 4, left: map[int]bool{1: true, 2: true}, }, - wantStats: getTestStats(), + wantStats: emptyTestStats(), fileExists: false, }, { @@ -532,14 +454,14 @@ func TestCleanup(t *testing.T) { idx: 4, left: map[int]bool{0: true}, prog: prog, - res: []*verf.Result{ + res: []*Result{ makeResult(1, []int{11, 33, 22}), makeResult(2, []int{11, 33, 22}), }, }}, - wantStats: &stats.Stats{ + wantStats: &Stats{ Progs: 1, - Calls: map[string]*stats.CallStats{ + Calls: map[string]*CallStats{ "breaks_returns": makeCallStats("breaks_returns", 1, 0, map[int]bool{}), "minimize$0": makeCallStats("minimize$0", 1, 0, map[int]bool{}), "test$res0": makeCallStats("test$res0", 1, 0, map[int]bool{}), @@ -554,15 +476,15 @@ func TestCleanup(t *testing.T) { idx: 4, left: map[int]bool{0: true}, prog: prog, - res: []*verf.Result{ + res: []*Result{ makeResult(1, []int{11, 33, 44}), makeResult(2, []int{11, 33, 22}), }, }}, - wantStats: &stats.Stats{ + wantStats: &Stats{ TotalMismatches: 1, Progs: 1, - Calls: map[string]*stats.CallStats{ + Calls: map[string]*CallStats{ "breaks_returns": makeCallStats("breaks_returns", 1, 0, map[int]bool{}), "minimize$0": makeCallStats("minimize$0", 1, 0, map[int]bool{}), "test$res0": makeCallStats("test$res0", 1, 1, map[int]bool{22: true, 44: true}), @@ -576,18 +498,18 @@ func TestCleanup(t *testing.T) { 4: { idx: 4, left: map[int]bool{0: true}, - res: []*verf.Result{ + res: []*Result{ makeResult(2, []int{11, 33, 22}), }, }}, - wantStats: getTestStats(), + wantStats: emptyTestStats(), wantProg: nil, fileExists: false, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - createTestServer(t) + srv := createTestServer(t) srv.progs = test.progs srv.pools = map[int]*poolInfo{ 0: {vmRunners: map[int][]*progInfo{ diff --git a/syz-verifier/stats/stats.go b/syz-verifier/stats.go index a57c43c48..5627e6342 100644 --- a/syz-verifier/stats/stats.go +++ b/syz-verifier/stats.go @@ -1,9 +1,7 @@ // Copyright 2021 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. -// Package stats contains utilities that aid gathering statistics of -// system call mismatches in the verified programs. -package stats +package main import ( "fmt" diff --git a/syz-verifier/stats/stats_test.go b/syz-verifier/stats_test.go index 11adc9d1e..452b10876 100644 --- a/syz-verifier/stats/stats_test.go +++ b/syz-verifier/stats_test.go @@ -1,7 +1,6 @@ // Copyright 2021 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. - -package stats +package main import ( "bytes" @@ -10,7 +9,7 @@ import ( "github.com/google/go-cmp/cmp" ) -func getTestStats() *Stats { +func dummyStats() *Stats { return &Stats{ Progs: 24, TotalMismatches: 10, @@ -44,7 +43,7 @@ func TestReportCallStats(t *testing.T) { } for _, test := range tests { - s := getTestStats() + s := dummyStats() t.Run(test.name, func(t *testing.T) { got, want := s.ReportCallStats(test.call), test.report if diff := cmp.Diff(want, got); diff != "" { @@ -55,7 +54,7 @@ func TestReportCallStats(t *testing.T) { } func TestReportGlobalStats(t *testing.T) { - s := getTestStats() + s := dummyStats() out := bytes.Buffer{} s.ReportGlobalStats(&out, float64(10)) got, want := out.String(), diff --git a/syz-verifier/test_utils.go b/syz-verifier/test_utils.go new file mode 100644 index 000000000..0ff35d874 --- /dev/null +++ b/syz-verifier/test_utils.go @@ -0,0 +1,89 @@ +// Copyright 2021 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package main + +import ( + "math/rand" + "path/filepath" + "testing" + "time" + + "github.com/google/syzkaller/pkg/ipc" + "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/prog" +) + +func createTestServer(t *testing.T) *RPCServer { + target, err := prog.GetTarget("test", "64") + if err != nil { + t.Fatalf("failed to initialise test target: %v", err) + } + vrf := Verifier{ + target: target, + choiceTable: target.DefaultChoiceTable(), + rnd: rand.New(rand.NewSource(time.Now().UnixNano())), + progIdx: 3, + } + vrf.resultsdir = makeTestResultDirectory(t) + vrf.stats = emptyTestStats() + srv, err := startRPCServer(&vrf) + if err != nil { + t.Fatalf("failed to initialise RPC server: %v", err) + } + return srv +} + +func getTestProgram(t *testing.T) *prog.Prog { + p := "breaks_returns()\n" + + "minimize$0(0x1, 0x1)\n" + + "test$res0()\n" + target := prog.InitTargetTest(t, "test", "64") + prog, err := target.Deserialize([]byte(p), prog.Strict) + if err != nil { + t.Fatalf("failed to deserialise test program: %v", err) + } + return prog +} + +func makeTestResultDirectory(t *testing.T) string { + resultsdir := "test" + err := osutil.MkdirAll(resultsdir) + if err != nil { + t.Fatalf("failed to create results directory: %v", err) + } + resultsdir, err = filepath.Abs(resultsdir) + if err != nil { + t.Fatalf("failed to get absolute path of resultsdir: %v", err) + } + return resultsdir +} + +func makeResult(pool int, errnos []int, flags ...int) *Result { + r := &Result{Pool: pool, Info: ipc.ProgInfo{Calls: []ipc.CallInfo{}}} + for _, e := range errnos { + r.Info.Calls = append(r.Info.Calls, ipc.CallInfo{Errno: e}) + } + + for idx, f := range flags { + r.Info.Calls[idx].Flags = ipc.CallFlags(f) + } + return r +} + +func emptyTestStats() *Stats { + return &Stats{ + Calls: map[string]*CallStats{ + "breaks_returns": {Name: "breaks_returns", States: map[int]bool{}}, + "minimize$0": {Name: "minimize$0", States: map[int]bool{}}, + "test$res0": {Name: "test$res0", States: map[int]bool{}}, + }, + } +} + +func makeCallStats(name string, occurrences, mismatches int, states map[int]bool) *CallStats { + return &CallStats{Name: name, + Occurrences: occurrences, + Mismatches: mismatches, + States: states} +} diff --git a/syz-verifier/verf/verifier.go b/syz-verifier/verifier.go index 92c828a3f..884609b6a 100644 --- a/syz-verifier/verf/verifier.go +++ b/syz-verifier/verifier.go @@ -1,14 +1,11 @@ // Copyright 2021 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. -// Package verf contains utilities for verifying the execution of the same -// program on different kernels yield the same results. -package verf +package main import ( "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/prog" - "github.com/google/syzkaller/syz-verifier/stats" ) // Result stores the results of executing a program. @@ -49,7 +46,7 @@ type ReturnState struct { // Verify checks whether the Results of the same program, executed on different // kernels are the same. If that's not the case, it returns a ResultReport // which highlights the differences. -func Verify(res []*Result, prog *prog.Prog, s *stats.Stats) *ResultReport { +func Verify(res []*Result, prog *prog.Prog, s *Stats) *ResultReport { rr := &ResultReport{ Prog: string(prog.Serialize()), } diff --git a/syz-verifier/verf/verifier_test.go b/syz-verifier/verifier_test.go index 240e3c2ef..8e8b4f3b2 100644 --- a/syz-verifier/verf/verifier_test.go +++ b/syz-verifier/verifier_test.go @@ -1,34 +1,15 @@ // Copyright 2021 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. -package verf + +package main import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/prog" - "github.com/google/syzkaller/syz-verifier/stats" ) -func makeResult(pool int, errnos []int, flags []int) *Result { - r := &Result{Pool: pool, Info: ipc.ProgInfo{Calls: []ipc.CallInfo{}}} - for i := range errnos { - r.Info.Calls = append(r.Info.Calls, ipc.CallInfo{Errno: errnos[i], Flags: ipc.CallFlags(flags[i])}) - } - return r -} - -func getTestStats() *stats.Stats { - return &stats.Stats{ - Calls: map[string]*stats.CallStats{ - "breaks_returns": {Name: "breaks_returns", States: map[int]bool{}}, - "minimize$0": {Name: "minimize$0", States: map[int]bool{}}, - "test$res0": {Name: "test$res0", States: map[int]bool{}}, - }, - } -} - func TestVerify(t *testing.T) { p := "breaks_returns()\n" + "minimize$0(0x1, 0x1)\n" + @@ -37,16 +18,16 @@ func TestVerify(t *testing.T) { name string res []*Result wantReport *ResultReport - wantStats *stats.Stats + wantStats *Stats }{ { name: "mismatches not found in results", res: []*Result{ - makeResult(2, []int{11, 33, 22}, []int{1, 3, 3}), - makeResult(4, []int{11, 33, 22}, []int{1, 3, 3})}, + makeResult(2, []int{11, 33, 22}, []int{1, 3, 3}...), + makeResult(4, []int{11, 33, 22}, []int{1, 3, 3}...)}, wantReport: nil, - wantStats: &stats.Stats{ - Calls: map[string]*stats.CallStats{ + wantStats: &Stats{ + Calls: map[string]*CallStats{ "breaks_returns": {Name: "breaks_returns", Occurrences: 1, States: map[int]bool{}}, "minimize$0": {Name: "minimize$0", Occurrences: 1, States: map[int]bool{}}, "test$res0": {Name: "test$res0", Occurrences: 1, States: map[int]bool{}}, @@ -56,8 +37,8 @@ func TestVerify(t *testing.T) { { name: "mismatches found in results", res: []*Result{ - makeResult(1, []int{1, 3, 2}, []int{4, 7, 7}), - makeResult(4, []int{1, 3, 5}, []int{4, 7, 3}), + makeResult(1, []int{1, 3, 2}, []int{4, 7, 7}...), + makeResult(4, []int{1, 3, 5}, []int{4, 7, 3}...), }, wantReport: &ResultReport{ Prog: p, @@ -67,9 +48,9 @@ func TestVerify(t *testing.T) { {Call: "test$res0", States: map[int]ReturnState{1: {2, 7}, 4: {5, 3}}, Mismatch: true}, }, }, - wantStats: &stats.Stats{ + wantStats: &Stats{ TotalMismatches: 1, - Calls: map[string]*stats.CallStats{ + Calls: map[string]*CallStats{ "breaks_returns": {Name: "breaks_returns", Occurrences: 1, States: map[int]bool{}}, "minimize$0": {Name: "minimize$0", Occurrences: 1, States: map[int]bool{}}, "test$res0": {Name: "test$res0", Occurrences: 1, Mismatches: 1, States: map[int]bool{2: true, 5: true}}, @@ -85,7 +66,7 @@ func TestVerify(t *testing.T) { if err != nil { t.Fatalf("failed to deserialise test program: %v", err) } - stats := getTestStats() + stats := emptyTestStats() got := Verify(test.res, prog, stats) if diff := cmp.Diff(test.wantReport, got); diff != "" { t.Errorf("Verify report mismatch (-want +got):\n%s", diff) |
