github.com/arnodel/golua@v0.0.0-20230215163904-e0b5347eaaa1/luatesting/linechecker.go (about) 1 package luatesting 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "regexp" 8 ) 9 10 var expectedPtn = regexp.MustCompile(`^ *--> [=~].*$`) 11 12 type LineChecker struct { 13 SourceLineno int 14 lineChecker 15 } 16 17 type lineChecker interface { 18 CheckLine([]byte) error 19 } 20 21 type LiteralLineChecker []byte 22 23 func (c LiteralLineChecker) CheckLine(output []byte) error { 24 expected := []byte(c) 25 if bytes.Equal(output, expected) { 26 return nil 27 } 28 return fmt.Errorf("expected: %q, got: %q", expected, output) 29 } 30 31 type RegexLineChecker regexp.Regexp 32 33 func (c *RegexLineChecker) CheckLine(output []byte) error { 34 ptn := (*regexp.Regexp)(c) 35 if ptn.Match(output) { 36 return nil 37 } 38 return fmt.Errorf("expected regex: %s, got %q", ptn, output) 39 } 40 41 func ExtractLineCheckers(source []byte) []LineChecker { 42 var ( 43 scanner = bufio.NewScanner(bytes.NewReader(source)) 44 lineno = 0 45 checkers []LineChecker 46 ) 47 for scanner.Scan() { 48 lineno++ 49 var ( 50 line = scanner.Bytes() 51 lc lineChecker 52 ) 53 if !expectedPtn.Match(line) { 54 continue 55 } 56 line = bytes.TrimLeft(line, " ") 57 switch line[4] { 58 case '=': 59 lit := make([]byte, len(line)-5) 60 copy(lit, line[5:]) 61 lc = LiteralLineChecker(lit) 62 case '~': 63 lc = (*RegexLineChecker)(regexp.MustCompile(string(line[5:]))) 64 default: 65 panic("We shouldn't get there") 66 } 67 checkers = append(checkers, LineChecker{SourceLineno: lineno, lineChecker: lc}) 68 } 69 return checkers 70 } 71 72 func CheckLines(output []byte, checkers []LineChecker) error { 73 // On windows we may get \r\n instead of \n 74 output = normalizeNewLines(output) 75 if len(output) > 0 && output[len(output)-1] == '\n' { 76 output = output[:len(output)-1] 77 } 78 lines := bytes.Split(output, []byte{'\n'}) 79 for i, line := range lines { 80 if i >= len(checkers) { 81 return fmt.Errorf("Extra output line #%d: %q", i+1, line) 82 } 83 if err := checkers[i].CheckLine(line); err != nil { 84 return fmt.Errorf("[output line %d, source line %d] %s", i+1, checkers[i].SourceLineno, err) 85 } 86 } 87 if len(checkers) > len(lines) { 88 return fmt.Errorf("Expected %d output lines, got %d", len(checkers), len(lines)) 89 } 90 return nil 91 } 92 93 var newLines = regexp.MustCompile(`(?s)\r\n|\n\r|\r`) 94 95 func normalizeNewLines(b []byte) []byte { 96 if bytes.IndexByte(b, '\r') == -1 { 97 return b 98 } 99 return newLines.ReplaceAllLiteral(b, []byte{'\n'}) 100 }