1// Copyright 2017 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4package ast 5 6import ( 7 "bufio" 8 "bytes" 9 "io/ioutil" 10 "path/filepath" 11 "regexp" 12 "strings" 13 "testing" 14) 15 16type ErrorMatcher struct { 17 Data []byte 18 expect []*errorDesc 19 got []*errorDesc 20} 21 22type errorDesc struct { 23 file string 24 line int 25 col int 26 text string 27 matched bool 28} 29 30func NewErrorMatcher(t *testing.T, file string) *ErrorMatcher { 31 data, err := ioutil.ReadFile(file) 32 if err != nil { 33 t.Fatalf("failed to open input file: %v", err) 34 } 35 var stripped []byte 36 var errors []*errorDesc 37 s := bufio.NewScanner(bytes.NewReader(data)) 38 for i := 1; s.Scan(); i++ { 39 ln := s.Bytes() 40 for { 41 pos := bytes.LastIndex(ln, []byte("###")) 42 if pos == -1 { 43 break 44 } 45 errors = append(errors, &errorDesc{ 46 file: filepath.Base(file), 47 line: i, 48 text: strings.TrimSpace(string(ln[pos+3:])), 49 }) 50 ln = ln[:pos] 51 } 52 stripped = append(stripped, ln...) 53 stripped = append(stripped, '\n') 54 } 55 if err := s.Err(); err != nil { 56 t.Fatalf("failed to scan input file: %v", err) 57 } 58 return &ErrorMatcher{ 59 Data: stripped, 60 expect: errors, 61 } 62} 63 64var errorLocationRe = regexp.MustCompile(`at [a-z][a-z0-9]+\.txt:[0-9]+:[0-9]+`) 65 66func (em *ErrorMatcher) ErrorHandler(pos Pos, msg string) { 67 if match := errorLocationRe.FindStringSubmatchIndex(msg); match != nil { 68 msg = msg[0:match[0]] + "at LOCATION" + msg[match[1]:] 69 } 70 em.got = append(em.got, &errorDesc{ 71 file: pos.File, 72 line: pos.Line, 73 col: pos.Col, 74 text: msg, 75 }) 76} 77 78func (em *ErrorMatcher) Count() int { 79 return len(em.got) 80} 81 82func (em *ErrorMatcher) Check(t *testing.T) { 83nextErr: 84 for _, e := range em.got { 85 for _, want := range em.expect { 86 if want.matched || want.line != e.line || want.text != e.text { 87 continue 88 } 89 want.matched = true 90 continue nextErr 91 } 92 t.Errorf("unexpected error:\n%v:%v:%v: %v", e.file, e.line, e.col, e.text) 93 } 94 for _, want := range em.expect { 95 if want.matched { 96 continue 97 } 98 t.Errorf("unmatched error:\n%v:%v: %v", want.file, want.line, want.text) 99 } 100} 101 102func (em *ErrorMatcher) DumpErrors(t *testing.T) { 103 for _, e := range em.got { 104 t.Logf("%v:%v:%v: %v", e.file, e.line, e.col, e.text) 105 } 106} 107