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  }