/internal/chunkedfile/chunkedfile.go
https://github.com/google/starlark-go · Go · 124 lines · 74 code · 11 blank · 39 comment · 15 complexity · 05fe069e6bc8dcc270f3064772a1da8e MD5 · raw file
- // Copyright 2017 The Bazel Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // Package chunkedfile provides utilities for testing that source code
- // errors are reported in the appropriate places.
- //
- // A chunked file consists of several chunks of input text separated by
- // "---" lines. Each chunk is an input to the program under test, such
- // as an evaluator. Lines containing "###" are interpreted as
- // expectations of failure: the following text is a Go string literal
- // denoting a regular expression that should match the failure message.
- //
- // Example:
- //
- // x = 1 / 0 ### "division by zero"
- // ---
- // x = 1
- // print(x + "") ### "int + string not supported"
- //
- // A client test feeds each chunk of text into the program under test,
- // then calls chunk.GotError for each error that actually occurred. Any
- // discrepancy between the actual and expected errors is reported using
- // the client's reporter, which is typically a testing.T.
- package chunkedfile // import "go.starlark.net/internal/chunkedfile"
- import (
- "fmt"
- "io/ioutil"
- "regexp"
- "strconv"
- "strings"
- )
- const debug = false
- // A Chunk is a portion of a source file.
- // It contains a set of expected errors.
- type Chunk struct {
- Source string
- filename string
- report Reporter
- wantErrs map[int]*regexp.Regexp
- }
- // Reporter is implemented by *testing.T.
- type Reporter interface {
- Errorf(format string, args ...interface{})
- }
- // Read parses a chunked file and returns its chunks.
- // It reports failures using the reporter.
- //
- // Error messages of the form "file.star:line:col: ..." are prefixed
- // by a newline so that the Go source position added by (*testing.T).Errorf
- // appears on a separate line so as not to confused editors.
- func Read(filename string, report Reporter) (chunks []Chunk) {
- data, err := ioutil.ReadFile(filename)
- if err != nil {
- report.Errorf("%s", err)
- return
- }
- linenum := 1
- for i, chunk := range strings.Split(string(data), "\n---\n") {
- if debug {
- fmt.Printf("chunk %d at line %d: %s\n", i, linenum, chunk)
- }
- // Pad with newlines so the line numbers match the original file.
- src := strings.Repeat("\n", linenum-1) + chunk
- wantErrs := make(map[int]*regexp.Regexp)
- // Parse comments of the form:
- // ### "expected error".
- lines := strings.Split(chunk, "\n")
- for j := 0; j < len(lines); j, linenum = j+1, linenum+1 {
- line := lines[j]
- hashes := strings.Index(line, "###")
- if hashes < 0 {
- continue
- }
- rest := strings.TrimSpace(line[hashes+len("###"):])
- pattern, err := strconv.Unquote(rest)
- if err != nil {
- report.Errorf("\n%s:%d: not a quoted regexp: %s", filename, linenum, rest)
- continue
- }
- rx, err := regexp.Compile(pattern)
- if err != nil {
- report.Errorf("\n%s:%d: %v", filename, linenum, err)
- continue
- }
- wantErrs[linenum] = rx
- if debug {
- fmt.Printf("\t%d\t%s\n", linenum, rx)
- }
- }
- linenum++
- chunks = append(chunks, Chunk{src, filename, report, wantErrs})
- }
- return chunks
- }
- // GotError should be called by the client to report an error at a particular line.
- // GotError reports unexpected errors to the chunk's reporter.
- func (chunk *Chunk) GotError(linenum int, msg string) {
- if rx, ok := chunk.wantErrs[linenum]; ok {
- delete(chunk.wantErrs, linenum)
- if !rx.MatchString(msg) {
- chunk.report.Errorf("\n%s:%d: error %q does not match pattern %q", chunk.filename, linenum, msg, rx)
- }
- } else {
- chunk.report.Errorf("\n%s:%d: unexpected error: %v", chunk.filename, linenum, msg)
- }
- }
- // Done should be called by the client to indicate that the chunk has no more errors.
- // Done reports expected errors that did not occur to the chunk's reporter.
- func (chunk *Chunk) Done() {
- for linenum, rx := range chunk.wantErrs {
- chunk.report.Errorf("\n%s:%d: expected error matching %q", chunk.filename, linenum, rx)
- }
- }