sigs.k8s.io/cluster-api-provider-azure@v1.14.3/internal/test/matchers/gomega/matchers.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package gomega
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"github.com/onsi/gomega/matchers"
    25  	"github.com/onsi/gomega/types"
    26  	"sigs.k8s.io/cluster-api-provider-azure/internal/test/record"
    27  )
    28  
    29  type (
    30  	logEntryMatcher struct {
    31  		level   *int
    32  		logFunc *string
    33  		values  []interface{}
    34  	}
    35  
    36  	// LogMatcher is a Gomega matcher for logs.
    37  	LogMatcher interface {
    38  		types.GomegaMatcher
    39  		WithLevel(int) LogMatcher
    40  		WithLogFunc(string) LogMatcher
    41  	}
    42  
    43  	cmpMatcher struct {
    44  		x    interface{}
    45  		diff string
    46  	}
    47  )
    48  
    49  // DiffEq will verify cmp.Diff(expected, actual) == "" using github.com/google/go-cmp/cmp.
    50  func DiffEq(x interface{}) types.GomegaMatcher {
    51  	return &cmpMatcher{
    52  		x: x,
    53  	}
    54  }
    55  
    56  // Match returns whether the actual value matches the expected value.
    57  func (c *cmpMatcher) Match(actual interface{}) (bool, error) {
    58  	c.diff = cmp.Diff(actual, c.x)
    59  	return c.diff == "", nil
    60  }
    61  
    62  // FailWithMessage returns the matcher's diff as the failure message.
    63  func (c *cmpMatcher) FailureMessage(_ interface{}) string {
    64  	return c.diff
    65  }
    66  
    67  // NegatedFailureMessage return the matcher's diff as the negated failure message.
    68  func (c *cmpMatcher) NegatedFailureMessage(_ interface{}) string {
    69  	return c.diff
    70  }
    71  
    72  // LogContains verifies that LogEntry matches the specified values.
    73  func LogContains(values ...interface{}) LogMatcher {
    74  	return &logEntryMatcher{
    75  		values: values,
    76  	}
    77  }
    78  
    79  // WithLevel sets the log level to that specified.
    80  func (l *logEntryMatcher) WithLevel(level int) LogMatcher {
    81  	l.level = &level
    82  	return l
    83  }
    84  
    85  // WithLogFunc sets the log function to that specified.
    86  func (l *logEntryMatcher) WithLogFunc(logFunc string) LogMatcher {
    87  	l.logFunc = &logFunc
    88  	return l
    89  }
    90  
    91  // Match returns whether the actual value matches the expected value.
    92  func (l *logEntryMatcher) Match(actual interface{}) (bool, error) {
    93  	logEntry, ok := actual.(record.LogEntry)
    94  	if !ok {
    95  		return false, fmt.Errorf("LogContains matcher expects an record.LogEntry")
    96  	}
    97  	return len(l.validate(logEntry)) == 0, nil
    98  }
    99  
   100  // FailureMessage returns the specified value as a failure message.
   101  func (l *logEntryMatcher) FailureMessage(actual interface{}) string {
   102  	return failMessage(l.validate(actual))
   103  }
   104  
   105  // NegatedFailureMessage returns the specified value as a negated failure message.
   106  func (l *logEntryMatcher) NegatedFailureMessage(actual interface{}) string {
   107  	return failMessage(l.validate(actual))
   108  }
   109  
   110  func (l *logEntryMatcher) validate(actual interface{}) []error {
   111  	logEntry, ok := actual.(record.LogEntry)
   112  	if !ok {
   113  		return []error{fmt.Errorf("expected record.LogEntry, but got %T", actual)}
   114  	}
   115  
   116  	var errs []error
   117  	containsValues := matchers.ContainElementsMatcher{Elements: l.values}
   118  	ok, err := containsValues.Match(logEntry.Values)
   119  	if err != nil || !ok {
   120  		errs = append(errs, fmt.Errorf("actual log values %q didn't match expected %q", logEntry.Values, l.values))
   121  	}
   122  
   123  	if l.logFunc != nil && *l.logFunc != logEntry.LogFunc {
   124  		errs = append(errs, fmt.Errorf("actual log Func %q didn't match expected %q", logEntry.LogFunc, *l.logFunc))
   125  	}
   126  
   127  	if l.level != nil && *l.level != logEntry.Level {
   128  		errs = append(errs, fmt.Errorf("actual log level %q didn't match expected %q", logEntry.Level, *l.level))
   129  	}
   130  
   131  	return errs
   132  }
   133  
   134  func failMessage(errs []error) string {
   135  	errMsgs := make([]string, len(errs))
   136  	for i, err := range errs {
   137  		errMsgs[i] = err.Error()
   138  	}
   139  	return fmt.Sprintf("LogEntry errors: %s", strings.Join(errMsgs, ", "))
   140  }