processor/lsmintervalprocessor/internal/data/datatest/equal.go (80 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 // This is a copy of the internal module from opentelemetry-collector-contrib: // https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/deltatocumulativeprocessor/internal/data package datatest // import "github.com/elastic/opentelemetry-collector-components/processor/lsmintervalprocessor/internal/data/datatest" import ( "reflect" "strings" "testing" "github.com/stretchr/testify/require" "github.com/elastic/opentelemetry-collector-components/processor/lsmintervalprocessor/internal/data/datatest/compare" "github.com/elastic/opentelemetry-collector-components/processor/lsmintervalprocessor/internal/data/expo" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" ) // T is the testing helper. Most notably it provides [T.Equal] type T struct { testing.TB } func New(tb testing.TB) T { return T{TB: tb} } // Equal reports whether want and got are deeply equal. // // Unlike [reflect.DeepEqual] it first recursively checks exported fields // and "getters", which are defined as an exported method with: // - exactly zero input arguments // - exactly one return value // - does not start with 'Append' // - does not start with 'Clone' // - does not return a function // // If this yields differences, those are reported and the test fails. // If the compared values are [pmetric.ExponentialHistogramDataPoint], then // [pmetrictest.CompareExponentialHistogramDataPoint] is also called. // // If no differences are found, it falls back to [assert.Equal]. // // This was done to aid readability when comparing deeply nested [pmetric]/[pcommon] types, // because in many cases [assert.Equal] output was found to be barely understandable. func (is T) Equal(want, got any) { is.Helper() equal(is.TB, want, got, "") } func (is T) Equalf(want, got any, name string) { is.Helper() equal(is.TB, want, got, name) } func equal(tb testing.TB, want, got any, name string) bool { tb.Helper() require.IsType(tb, want, got) vw := reflect.ValueOf(want) vg := reflect.ValueOf(got) if vw.Kind() != reflect.Struct { ok := compare.Equal(want, got) if !ok { tb.Errorf("%s: %+v != %+v", name, want, got) } return ok } ok := true // compare all "getters" of the struct for i := 0; i < vw.NumMethod(); i++ { mname := vw.Type().Method(i).Name fname := strings.TrimPrefix(name+"."+mname+"()", ".") mw := vw.Method(i) mg := vg.Method(i) // only compare "getters" if mw.Type().NumIn() != 0 || mw.Type().NumOut() != 1 { continue } // skip equality check for methods returning functions ret := mw.Type().Out(0) if ret.Kind() == reflect.Func { continue } // Append(Empty) fails above heuristic, exclude it if strings.HasPrefix(mname, "Append") || strings.HasPrefix(mname, "Clone") { continue } rw := mw.Call(nil)[0].Interface() rg := mg.Call(nil)[0].Interface() ok = equal(tb, rw, rg, fname) && ok } // compare all exported fields of the struct for i := 0; i < vw.NumField(); i++ { if !vw.Type().Field(i).IsExported() { continue } fname := name + "." + vw.Type().Field(i).Name fw := vw.Field(i).Interface() fg := vg.Field(i).Interface() ok = equal(tb, fw, fg, fname) && ok } if !ok { return false } if _, ok := want.(expo.DataPoint); ok { err := pmetrictest.CompareExponentialHistogramDataPoint(want.(expo.DataPoint), got.(expo.DataPoint)) if err != nil { tb.Error(err) } } // fallback to a full deep-equal for rare cases (unexported fields, etc) if diff := compare.Diff(want, got); diff != "" { tb.Error(diff) return false } return true }