github.com/paketo-buildpacks/packit@v1.3.2-0.20211206231111-86b75c657449/matchers/contain_lines.go (about)

     1  package matchers
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"regexp"
     7  	"strings"
     8  
     9  	"github.com/onsi/gomega/format"
    10  	"github.com/onsi/gomega/types"
    11  )
    12  
    13  func ContainLines(expected ...interface{}) types.GomegaMatcher {
    14  	return &containLinesMatcher{
    15  		expected: expected,
    16  	}
    17  }
    18  
    19  type containLinesMatcher struct {
    20  	expected []interface{}
    21  }
    22  
    23  func (matcher *containLinesMatcher) Match(actual interface{}) (success bool, err error) {
    24  	_, ok := actual.(string)
    25  	if !ok {
    26  		_, ok := actual.(fmt.Stringer)
    27  		if !ok {
    28  			return false, fmt.Errorf("ContainLinesMatcher requires a string or fmt.Stringer. Got actual: %s", format.Object(actual, 1))
    29  		}
    30  	}
    31  
    32  	actualLines := matcher.lines(actual)
    33  
    34  	for currentActualLineIndex := 0; currentActualLineIndex < len(actualLines); currentActualLineIndex++ {
    35  		currentActualLine := actualLines[currentActualLineIndex]
    36  		currentExpectedLine := matcher.expected[currentActualLineIndex]
    37  
    38  		match, err := matcher.compare(currentActualLine, currentExpectedLine)
    39  		if err != nil {
    40  			return false, err
    41  		}
    42  
    43  		if match {
    44  			if currentActualLineIndex+1 == len(matcher.expected) {
    45  				return true, nil
    46  			}
    47  		} else {
    48  			if len(actualLines) > 1 {
    49  				actualLines = actualLines[1:]
    50  				currentActualLineIndex = -1
    51  			}
    52  		}
    53  	}
    54  
    55  	return false, nil
    56  }
    57  
    58  func (matcher *containLinesMatcher) compare(actual string, expected interface{}) (bool, error) {
    59  	if m, ok := expected.(types.GomegaMatcher); ok {
    60  		match, err := m.Match(actual)
    61  		if err != nil {
    62  			return false, err
    63  		}
    64  
    65  		return match, nil
    66  	}
    67  
    68  	return reflect.DeepEqual(actual, expected), nil
    69  }
    70  
    71  func (matcher *containLinesMatcher) lines(actual interface{}) []string {
    72  	raw, ok := actual.(string)
    73  	if !ok {
    74  		raw = actual.(fmt.Stringer).String()
    75  	}
    76  
    77  	re := regexp.MustCompile(`^\[[a-z]+\]\s`)
    78  
    79  	var lines []string
    80  	for _, line := range strings.Split(raw, "\n") {
    81  		lines = append(lines, re.ReplaceAllString(line, ""))
    82  	}
    83  
    84  	return lines
    85  }
    86  
    87  func (matcher *containLinesMatcher) FailureMessage(actual interface{}) (message string) {
    88  	actualLines := "\n" + strings.Join(matcher.lines(actual), "\n")
    89  	missing := matcher.linesMatching(actual, false)
    90  	if len(missing) > 0 {
    91  		return fmt.Sprintf("Expected\n%s\nto contain lines\n%s\nbut missing\n%s", format.Object(actualLines, 1), format.Object(matcher.expected, 1), format.Object(missing, 1))
    92  	}
    93  
    94  	return fmt.Sprintf("Expected\n%s\nto contain lines\n%s\nall lines appear, but may be misordered", format.Object(actualLines, 1), format.Object(matcher.expected, 1))
    95  }
    96  
    97  func (matcher *containLinesMatcher) NegatedFailureMessage(actual interface{}) (message string) {
    98  	actualLines := "\n" + strings.Join(matcher.lines(actual), "\n")
    99  	missing := matcher.linesMatching(actual, true)
   100  
   101  	return fmt.Sprintf("Expected\n%s\nnot to contain lines\n%s\nbut includes\n%s", format.Object(actualLines, 1), format.Object(matcher.expected, 1), format.Object(missing, 1))
   102  }
   103  
   104  func (matcher *containLinesMatcher) linesMatching(actual interface{}, matching bool) []interface{} {
   105  	var set []interface{}
   106  	for _, expected := range matcher.expected {
   107  		var match bool
   108  		for _, line := range matcher.lines(actual) {
   109  			if ok, _ := matcher.compare(line, expected); ok {
   110  				match = true
   111  			}
   112  		}
   113  
   114  		if match == matching {
   115  			set = append(set, expected)
   116  		}
   117  	}
   118  
   119  	return set
   120  }