Home | History | Annotate | Download | only in ast
      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 
      4 package ast
      5 
      6 import (
      7 	"bufio"
      8 	"bytes"
      9 	"io/ioutil"
     10 	"path/filepath"
     11 	"regexp"
     12 	"strings"
     13 	"testing"
     14 )
     15 
     16 type ErrorMatcher struct {
     17 	Data   []byte
     18 	expect []*errorDesc
     19 	got    []*errorDesc
     20 }
     21 
     22 type errorDesc struct {
     23 	file    string
     24 	line    int
     25 	col     int
     26 	text    string
     27 	matched bool
     28 }
     29 
     30 func 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 
     64 var errorLocationRe = regexp.MustCompile(`at [a-z][a-z0-9]+\.txt:[0-9]+:[0-9]+`)
     65 
     66 func (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 
     78 func (em *ErrorMatcher) Count() int {
     79 	return len(em.got)
     80 }
     81 
     82 func (em *ErrorMatcher) Check(t *testing.T) {
     83 nextErr:
     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 
    102 func (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