| // Copyright The OpenTelemetry Authors | |
| // SPDX-License-Identifier: Apache-2.0 | |
| package logtest | |
| import ( | |
| "context" | |
| "reflect" | |
| "github.com/google/go-cmp/cmp" | |
| "github.com/google/go-cmp/cmp/cmpopts" | |
| "go.opentelemetry.io/otel/attribute" | |
| ) | |
| // TestingT reports failure messages. | |
| // *testing.T implements this interface. | |
| type TestingT interface { | |
| Errorf(format string, args ...any) | |
| } | |
| // AssertEqual asserts that the two concrete data types from the logtest package | |
| // are equal. | |
| func AssertEqual[T Recording | Record](t TestingT, want, got T, opts ...AssertOption) bool { | |
| if h, ok := t.(interface{ Helper() }); ok { | |
| h.Helper() | |
| } | |
| var cfg assertConfig | |
| for _, opt := range opts { | |
| cfg = opt.apply(cfg) | |
| } | |
| cmpOpts := []cmp.Option{ | |
| cmp.Comparer(func(x, y context.Context) bool { return x == y }), // Compare context. | |
| cmp.Comparer(func(x, y attribute.Value) bool { return reflect.DeepEqual(x, y) }), | |
| cmpopts.SortSlices( | |
| func(a, b attribute.KeyValue) bool { return a.Key < b.Key }, | |
| ), // Unordered compare of the key values. | |
| cmpopts.EquateErrors(), | |
| cmpopts.EquateEmpty(), // Empty and nil collections are equal. | |
| } | |
| cmpOpts = append(cmpOpts, cfg.cmpOpts...) | |
| if diff := cmp.Diff(want, got, cmpOpts...); diff != "" { | |
| msg := "mismatch (-want +got):\n%s" | |
| if cfg.msg != "" { | |
| msg = cfg.msg + "\n" + msg | |
| } | |
| args := make([]any, 0, len(cfg.args)+1) | |
| args = append(args, cfg.args...) | |
| args = append(args, diff) | |
| t.Errorf(msg, args...) | |
| return false | |
| } | |
| return true | |
| } | |
| type assertConfig struct { | |
| cmpOpts []cmp.Option | |
| msg string | |
| args []any | |
| } | |
| // AssertOption allows for fine-grained control over how AssertEqual operates. | |
| type AssertOption interface { | |
| apply(cfg assertConfig) assertConfig | |
| } | |
| type fnOption func(cfg assertConfig) assertConfig | |
| func (fn fnOption) apply(cfg assertConfig) assertConfig { | |
| return fn(cfg) | |
| } | |
| // Transform applies the transformation function f, which converts values of | |
| // one type to another. f must not mutate values of type A in any way. | |
| func Transform[A, B any](f func(A) B) AssertOption { | |
| return fnOption(func(cfg assertConfig) assertConfig { | |
| cfg.cmpOpts = append(cfg.cmpOpts, cmp.Transformer("", f)) | |
| return cfg | |
| }) | |
| } | |
| // Desc prepends the given text to an assertion failure message. | |
| // The text is formatted with the args using fmt.Sprintf. | |
| func Desc(text string, args ...any) AssertOption { | |
| return fnOption(func(cfg assertConfig) assertConfig { | |
| cfg.msg = text | |
| cfg.args = args | |
| return cfg | |
| }) | |
| } |