Source file src/internal/trace/testtrace/expectation.go

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

View as plain text