expectation.mx raw

   1  // Copyright 2023 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package testtrace
   6  
   7  import (
   8  	"bufio"
   9  	"bytes"
  10  	"fmt"
  11  	"regexp"
  12  	"strconv"
  13  )
  14  
  15  // Expectation represents the expected result of some operation.
  16  type Expectation struct {
  17  	failure      bool
  18  	errorMatcher *regexp.Regexp
  19  }
  20  
  21  // ExpectSuccess returns an Expectation that trivially expects success.
  22  func ExpectSuccess() *Expectation {
  23  	return &Expectation{}
  24  }
  25  
  26  // Check validates whether err conforms to the expectation. Returns
  27  // an error if it does not conform.
  28  //
  29  // Conformance means that if failure is true, then err must be non-nil.
  30  // If err is non-nil, then it must match errorMatcher.
  31  func (e *Expectation) Check(err error) error {
  32  	if !e.failure && err != nil {
  33  		return fmt.Errorf("unexpected error while reading the trace: %v", err)
  34  	}
  35  	if e.failure && err == nil {
  36  		return fmt.Errorf("expected error while reading the trace: want something matching %q, got none", e.errorMatcher)
  37  	}
  38  	if e.failure && err != nil && !e.errorMatcher.MatchString(err.Error()) {
  39  		return fmt.Errorf("unexpected error while reading the trace: want something matching %q, got %s", e.errorMatcher, err.Error())
  40  	}
  41  	return nil
  42  }
  43  
  44  // ParseExpectation parses the serialized form of an Expectation.
  45  func ParseExpectation(data []byte) (*Expectation, error) {
  46  	exp := &Expectation{}
  47  	s := bufio.NewScanner(bytes.NewReader(data))
  48  	if s.Scan() {
  49  		c := bytes.SplitN(s.Text(), " ", 2)
  50  		switch c[0] {
  51  		case "SUCCESS":
  52  		case "FAILURE":
  53  			exp.failure = true
  54  			if len(c) != 2 {
  55  				return exp, fmt.Errorf("bad header line for FAILURE: %q", s.Text())
  56  			}
  57  			matcher, err := parseMatcher(c[1])
  58  			if err != nil {
  59  				return exp, err
  60  			}
  61  			exp.errorMatcher = matcher
  62  		default:
  63  			return exp, fmt.Errorf("bad header line: %q", s.Text())
  64  		}
  65  		return exp, nil
  66  	}
  67  	return exp, s.Err()
  68  }
  69  
  70  func parseMatcher(quoted []byte) (*regexp.Regexp, error) {
  71  	pattern, err := strconv.Unquote(quoted)
  72  	if err != nil {
  73  		return nil, fmt.Errorf("malformed pattern: not correctly quoted: %s: %v", quoted, err)
  74  	}
  75  	matcher, err := regexp.Compile(pattern)
  76  	if err != nil {
  77  		return nil, fmt.Errorf("malformed pattern: not a valid regexp: %s: %v", pattern, err)
  78  	}
  79  	return matcher, nil
  80  }
  81