pkg/pdatatest/pmetrictest/metrics.go (731 lines of code) (raw):

// Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 package pmetrictest // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" import ( "errors" "fmt" "reflect" "go.opentelemetry.io/collector/pdata/pmetric" "go.uber.org/multierr" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/internal" ) func CompareMetrics(expected, actual pmetric.Metrics, options ...CompareMetricsOption) error { exp, act := pmetric.NewMetrics(), pmetric.NewMetrics() expected.CopyTo(exp) actual.CopyTo(act) for _, option := range options { option.applyOnMetrics(exp, act) } expectedMetrics, actualMetrics := exp.ResourceMetrics(), act.ResourceMetrics() if expectedMetrics.Len() != actualMetrics.Len() { return fmt.Errorf("number of resources doesn't match expected: %d, actual: %d", expectedMetrics.Len(), actualMetrics.Len()) } numResources := expectedMetrics.Len() // Keep track of matching resources so that each can only be matched once matchingResources := make(map[pmetric.ResourceMetrics]pmetric.ResourceMetrics, numResources) var errs error var outOfOrderErrs error for e := 0; e < numResources; e++ { er := expectedMetrics.At(e) var foundMatch bool for a := 0; a < numResources; a++ { ar := actualMetrics.At(a) if _, ok := matchingResources[ar]; ok { continue } if reflect.DeepEqual(er.Resource().Attributes().AsRaw(), ar.Resource().Attributes().AsRaw()) { foundMatch = true matchingResources[ar] = er if e != a { outOfOrderErrs = multierr.Append(outOfOrderErrs, fmt.Errorf(`resources are out of order: resource "%v" expected at index %d, found at index %d`, er.Resource().Attributes().AsRaw(), e, a)) } break } } if !foundMatch { errs = multierr.Append(errs, fmt.Errorf("missing expected resource: %v", er.Resource().Attributes().AsRaw())) } } for i := 0; i < numResources; i++ { if _, ok := matchingResources[actualMetrics.At(i)]; !ok { errs = multierr.Append(errs, fmt.Errorf("unexpected resource: %v", actualMetrics.At(i).Resource().Attributes().AsRaw())) } } if errs != nil { return errs } if outOfOrderErrs != nil { return outOfOrderErrs } for ar, er := range matchingResources { errPrefix := fmt.Sprintf(`resource "%v"`, ar.Resource().Attributes().AsRaw()) errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareResourceMetrics(er, ar))) } return errs } func CompareResourceMetrics(expected, actual pmetric.ResourceMetrics) error { errs := multierr.Combine( internal.CompareResource(expected.Resource(), actual.Resource()), internal.CompareSchemaURL(expected.SchemaUrl(), actual.SchemaUrl()), ) esms := expected.ScopeMetrics() asms := actual.ScopeMetrics() if esms.Len() != asms.Len() { errs = multierr.Append(errs, fmt.Errorf("number of scopes doesn't match expected: %d, actual: %d", esms.Len(), asms.Len())) return errs } numScopeMetrics := expected.ScopeMetrics().Len() // Keep track of matching resources so that each can only be matched once matchingResources := make(map[pmetric.ScopeMetrics]pmetric.ScopeMetrics, numScopeMetrics) var outOfOrderErrs error for e := 0; e < numScopeMetrics; e++ { esm := esms.At(e) var foundMatch bool for a := 0; a < numScopeMetrics; a++ { asm := asms.At(a) if _, ok := matchingResources[asm]; ok { continue } if esm.Scope().Name() == asm.Scope().Name() { foundMatch = true matchingResources[asm] = esm if e != a { outOfOrderErrs = multierr.Append(outOfOrderErrs, fmt.Errorf(`scopes are out of order: scope "%s" expected at index %d, found at index %d`, esm.Scope().Name(), e, a)) } break } } if !foundMatch { errs = multierr.Append(errs, fmt.Errorf("missing expected scope: %s", esm.Scope().Name())) } } for i := 0; i < numScopeMetrics; i++ { if _, ok := matchingResources[actual.ScopeMetrics().At(i)]; !ok { errs = multierr.Append(errs, fmt.Errorf("unexpected scope: %s", actual.ScopeMetrics().At(i).Scope().Name())) } } if errs != nil { return errs } if outOfOrderErrs != nil { return outOfOrderErrs } for i := 0; i < esms.Len(); i++ { errPrefix := fmt.Sprintf(`scope "%s"`, esms.At(i).Scope().Name()) errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareScopeMetrics(esms.At(i), asms.At(i)))) } return errs } // CompareScopeMetrics compares each part of two given ScopeMetrics and returns // an error if they don't match. The error describes what didn't match. The // expected and actual values are clones before options are applied. func CompareScopeMetrics(expected, actual pmetric.ScopeMetrics) error { errs := multierr.Combine( internal.CompareInstrumentationScope(expected.Scope(), actual.Scope()), internal.CompareSchemaURL(expected.SchemaUrl(), actual.SchemaUrl()), ) ems := expected.Metrics() ams := actual.Metrics() if ems.Len() != ams.Len() { errs = multierr.Append(errs, fmt.Errorf("number of metrics doesn't match expected: %d, actual: %d", ems.Len(), ams.Len())) return errs } numMetrics := ems.Len() // Keep track of matching records so that each record can only be matched once matchingMetrics := make(map[pmetric.Metric]pmetric.Metric, numMetrics) var outOfOrderErrs error for e := 0; e < numMetrics; e++ { em := ems.At(e) var foundMatch bool for a := 0; a < numMetrics; a++ { am := ams.At(a) if _, ok := matchingMetrics[am]; ok { continue } if em.Name() == am.Name() { foundMatch = true matchingMetrics[am] = em if e != a { outOfOrderErrs = multierr.Append(outOfOrderErrs, fmt.Errorf(`metrics are out of order: metric "%s" expected at index %d, found at index %d`, em.Name(), e, a)) } break } } if !foundMatch { errs = multierr.Append(errs, fmt.Errorf("missing expected metric: %s", em.Name())) } } for i := 0; i < numMetrics; i++ { if _, ok := matchingMetrics[ams.At(i)]; !ok { errs = multierr.Append(errs, fmt.Errorf("unexpected metric: %s", ams.At(i).Name())) } } if errs != nil { return errs } if outOfOrderErrs != nil { return outOfOrderErrs } for i := 0; i < numMetrics; i++ { errPrefix := fmt.Sprintf(`metric "%s"`, ems.At(i).Name()) errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareMetric(ems.At(i), ams.At(i)))) } return errs } func CompareMetric(expected pmetric.Metric, actual pmetric.Metric) error { var errs error if actual.Name() != expected.Name() { errs = multierr.Append(errs, fmt.Errorf("name doesn't match expected: %s, actual: %s", expected.Name(), actual.Name())) } if actual.Description() != expected.Description() { errs = multierr.Append(errs, fmt.Errorf("description doesn't match expected: %s, actual: %s", expected.Description(), actual.Description())) } if actual.Unit() != expected.Unit() { errs = multierr.Append(errs, fmt.Errorf("unit doesn't match expected: %s, actual: %s", expected.Unit(), actual.Unit())) } if actual.Type() != expected.Type() { errs = multierr.Append(errs, fmt.Errorf("type doesn't match expected: %s, actual: %s", expected.Type(), actual.Type())) return errs } //exhaustive:enforce switch actual.Type() { case pmetric.MetricTypeGauge: errs = multierr.Append(errs, compareNumberDataPointSlices(expected.Gauge().DataPoints(), actual.Gauge().DataPoints())) case pmetric.MetricTypeSum: if actual.Sum().AggregationTemporality() != expected.Sum().AggregationTemporality() { errs = multierr.Append(errs, fmt.Errorf("aggregation temporality doesn't match expected: %s, actual: %s", expected.Sum().AggregationTemporality(), actual.Sum().AggregationTemporality())) } if actual.Sum().IsMonotonic() != expected.Sum().IsMonotonic() { errs = multierr.Append(errs, fmt.Errorf("is monotonic doesn't match expected: %t, actual: %t", expected.Sum().IsMonotonic(), actual.Sum().IsMonotonic())) } errs = multierr.Append(errs, compareNumberDataPointSlices(expected.Sum().DataPoints(), actual.Sum().DataPoints())) case pmetric.MetricTypeHistogram: if actual.Histogram().AggregationTemporality() != expected.Histogram().AggregationTemporality() { errs = multierr.Append(errs, fmt.Errorf("aggregation temporality doesn't match expected: %s, actual: %s", expected.Histogram().AggregationTemporality(), actual.Histogram().AggregationTemporality())) } errs = multierr.Append(errs, compareHistogramDataPointSlices(expected.Histogram().DataPoints(), actual.Histogram().DataPoints())) case pmetric.MetricTypeExponentialHistogram: if actual.ExponentialHistogram().AggregationTemporality() != expected.ExponentialHistogram().AggregationTemporality() { errs = multierr.Append(errs, fmt.Errorf("aggregation temporality doesn't match expected: %s, actual: %s", expected.ExponentialHistogram().AggregationTemporality(), actual.ExponentialHistogram().AggregationTemporality())) } errs = multierr.Append(errs, compareExponentialHistogramDataPointSlice(expected.ExponentialHistogram().DataPoints(), actual.ExponentialHistogram().DataPoints())) case pmetric.MetricTypeSummary: errs = multierr.Append(errs, compareSummaryDataPointSlices(expected.Summary().DataPoints(), actual.Summary().DataPoints())) case pmetric.MetricTypeEmpty: } return errs } // compareNumberDataPointSlices compares each part of two given NumberDataPointSlices and returns // an error if they don't match. The error describes what didn't match. func compareNumberDataPointSlices(expected, actual pmetric.NumberDataPointSlice) error { if expected.Len() != actual.Len() { return fmt.Errorf("number of datapoints doesn't match expected: %d, actual: %d", expected.Len(), actual.Len()) } numPoints := expected.Len() // Keep track of matching data points so that each point can only be matched once matchingDPS := make(map[pmetric.NumberDataPoint]pmetric.NumberDataPoint, numPoints) var errs error var outOfOrderErrs error for e := 0; e < numPoints; e++ { edp := expected.At(e) var foundMatch bool for a := 0; a < numPoints; a++ { adp := actual.At(a) if _, ok := matchingDPS[adp]; ok { continue } if reflect.DeepEqual(edp.Attributes().AsRaw(), adp.Attributes().AsRaw()) { foundMatch = true matchingDPS[adp] = edp if e != a { outOfOrderErrs = multierr.Append(outOfOrderErrs, fmt.Errorf("datapoints are out of order: "+ `datapoint "%v" expected at index %d, found at index %d`, edp.Attributes().AsRaw(), e, a)) } break } } if !foundMatch { errs = multierr.Append(errs, fmt.Errorf("missing expected datapoint: %v", edp.Attributes().AsRaw())) } } for i := 0; i < numPoints; i++ { if _, ok := matchingDPS[actual.At(i)]; !ok { errs = multierr.Append(errs, fmt.Errorf("unexpected datapoint: %v", actual.At(i).Attributes().AsRaw())) } } if errs != nil { return errs } if outOfOrderErrs != nil { return outOfOrderErrs } for adp, edp := range matchingDPS { errPrefix := fmt.Sprintf(`datapoint "%v"`, adp.Attributes().AsRaw()) errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareNumberDataPoint(edp, adp))) } return errs } // CompareNumberDataPoint compares each part of two given NumberDataPoints and returns // an error if they don't match. The error describes what didn't match. func CompareNumberDataPoint(expected, actual pmetric.NumberDataPoint) error { errs := internal.CompareAttributes(expected.Attributes(), actual.Attributes()) if expected.StartTimestamp() != actual.StartTimestamp() { errs = multierr.Append(errs, fmt.Errorf("start timestamp doesn't match expected: %d, "+ "actual: %d", expected.StartTimestamp(), actual.StartTimestamp())) } if expected.Timestamp() != actual.Timestamp() { errs = multierr.Append(errs, fmt.Errorf("timestamp doesn't match expected: %d, actual: %d", expected.Timestamp(), actual.Timestamp())) } if expected.ValueType() != actual.ValueType() { errs = multierr.Append(errs, fmt.Errorf("value type doesn't match expected: %s, actual: %s", expected.ValueType(), actual.ValueType())) } else { if expected.IntValue() != actual.IntValue() { errs = multierr.Append(errs, fmt.Errorf("int value doesn't match expected: %d, actual: %d", expected.IntValue(), actual.IntValue())) } if expected.DoubleValue() != actual.DoubleValue() { errs = multierr.Append(errs, fmt.Errorf("double value doesn't match expected: %f, actual: %f", expected.DoubleValue(), actual.DoubleValue())) } } if expected.Flags() != actual.Flags() { errs = multierr.Append(errs, fmt.Errorf("flags don't match expected: %d, actual: %d", expected.Flags(), actual.Flags())) } errs = multierr.Append(errs, compareExemplarSlice(expected.Exemplars(), actual.Exemplars())) return errs } // compareExemplarSlice compares each part of two given ExemplarSlice and returns // an error if they don't match. The error describes what didn't match. func compareExemplarSlice(expected, actual pmetric.ExemplarSlice) error { if expected.Len() != actual.Len() { return fmt.Errorf("number of exemplars doesn't match expected: %d, actual: %d", expected.Len(), actual.Len()) } numExemplars := expected.Len() // Keep track of matching exemplars so that each exemplar can only be matched once matchingExs := make(map[pmetric.Exemplar]pmetric.Exemplar, numExemplars) var errs error var outOfOrderErrs error for e := 0; e < numExemplars; e++ { eex := expected.At(e) var foundMatch bool for a := 0; a < numExemplars; a++ { aex := actual.At(a) if _, ok := matchingExs[aex]; ok { continue } if reflect.DeepEqual(eex.FilteredAttributes().AsRaw(), aex.FilteredAttributes().AsRaw()) { foundMatch = true matchingExs[aex] = eex if e != a { outOfOrderErrs = multierr.Append(outOfOrderErrs, fmt.Errorf("exemplars are out of order: "+ "exemplar %v expected at index %d, found at index %d", eex.FilteredAttributes().AsRaw(), e, a)) } break } } if !foundMatch { errs = multierr.Append(errs, fmt.Errorf("missing expected exemplar: %v", eex.FilteredAttributes().AsRaw())) } } for i := 0; i < numExemplars; i++ { if _, ok := matchingExs[actual.At(i)]; !ok { errs = multierr.Append(errs, fmt.Errorf("unexpected exemplar: %v", actual.At(i).FilteredAttributes().AsRaw())) } } if errs != nil { return errs } if outOfOrderErrs != nil { return outOfOrderErrs } for aex, eex := range matchingExs { errPrefix := fmt.Sprintf(`exemplar %v`, eex.FilteredAttributes().AsRaw()) errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareExemplar(eex, aex))) } return errs } // CompareExemplar compares each part of two given pmetric.Exemplar and returns // an error if they don't match. The error describes what didn't match. func CompareExemplar(expected, actual pmetric.Exemplar) error { var errs error if expected.ValueType() != actual.ValueType() { errs = multierr.Append(errs, fmt.Errorf("value type doesn't match: expected type: %s, actual type: %s", expected.ValueType(), actual.ValueType())) } else { if expected.DoubleValue() != actual.DoubleValue() { errs = multierr.Append(errs, fmt.Errorf("double value doesn't match expected: %f, actual: %f", expected.DoubleValue(), actual.DoubleValue())) } if expected.IntValue() != actual.IntValue() { errs = multierr.Append(errs, fmt.Errorf("int value doesn't match expected: %d, actual: %d", expected.IntValue(), actual.IntValue())) } } if expected.Timestamp() != actual.Timestamp() { errs = multierr.Append(errs, fmt.Errorf("timestamp doesn't match expected: %d, actual: %d", expected.Timestamp(), actual.Timestamp())) } if expected.TraceID() != actual.TraceID() { errs = multierr.Append(errs, fmt.Errorf("trace ID doesn't match expected: %s, actual: %s", expected.TraceID(), actual.TraceID())) } if expected.SpanID() != actual.SpanID() { errs = multierr.Append(errs, fmt.Errorf("span ID doesn't match expected: %s, actual: %s", expected.SpanID(), actual.SpanID())) } return errs } // compareHistogramDataPointSlices compares each part of two given HistogramDataPointSlices and returns // an error if they don't match. The error describes what didn't match. func compareHistogramDataPointSlices(expected, actual pmetric.HistogramDataPointSlice) error { if expected.Len() != actual.Len() { return fmt.Errorf("number of datapoints doesn't match expected: %d, actual: %d", expected.Len(), actual.Len()) } numPoints := expected.Len() // Keep track of matching data points so that each point can only be matched once matchingDPS := make(map[pmetric.HistogramDataPoint]pmetric.HistogramDataPoint, numPoints) var errs error var outOfOrderErrs error for e := 0; e < numPoints; e++ { edp := expected.At(e) var foundMatch bool for a := 0; a < numPoints; a++ { adp := actual.At(a) if _, ok := matchingDPS[adp]; ok { continue } if reflect.DeepEqual(edp.Attributes().AsRaw(), adp.Attributes().AsRaw()) { foundMatch = true matchingDPS[adp] = edp if e != a { outOfOrderErrs = multierr.Append(outOfOrderErrs, fmt.Errorf(`datapoint "%v" expected at index %d, found at index %d`, edp.Attributes().AsRaw(), e, a)) } break } } if !foundMatch { errs = multierr.Append(errs, fmt.Errorf("missing expected datapoint: %v", edp.Attributes().AsRaw())) } } for i := 0; i < numPoints; i++ { if _, ok := matchingDPS[actual.At(i)]; !ok { errs = multierr.Append(errs, fmt.Errorf("unexpected datapoint: %v", actual.At(i).Attributes().AsRaw())) } } if errs != nil { return errs } if outOfOrderErrs != nil { return outOfOrderErrs } for adp, edp := range matchingDPS { errPrefix := fmt.Sprintf(`datapoint "%v"`, adp.Attributes().AsRaw()) errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareHistogramDataPoints(edp, adp))) } return errs } // CompareHistogramDataPoints compares each part of two given HistogramDataPoints and returns // an error if they don't match. The error describes what didn't match. func CompareHistogramDataPoints(expected, actual pmetric.HistogramDataPoint) error { errs := internal.CompareAttributes(expected.Attributes(), actual.Attributes()) if expected.HasSum() != actual.HasSum() || expected.Sum() != actual.Sum() { errStr := "sum doesn't match expected: " if expected.HasSum() { errStr += fmt.Sprintf("%f", expected.Sum()) } else { errStr += "<unset>" } errStr += ", actual: " if actual.HasSum() { errStr += fmt.Sprintf("%f", actual.Sum()) } else { errStr += "<unset>" } errs = multierr.Append(errs, errors.New(errStr)) } if expected.HasMin() != actual.HasMin() || expected.Min() != actual.Min() { errStr := "min doesn't match expected: " if expected.HasMin() { errStr += fmt.Sprintf("%f", expected.Min()) } else { errStr += "<unset>" } errStr += ", actual: " if actual.HasMin() { errStr += fmt.Sprintf("%f", actual.Min()) } else { errStr += "<unset>" } errs = multierr.Append(errs, errors.New(errStr)) } if expected.HasMax() != actual.HasMax() || expected.Max() != actual.Max() { errStr := "max doesn't match expected: " if expected.HasMax() { errStr += fmt.Sprintf("%f", expected.Min()) } else { errStr += "<unset>" } errStr += ", actual: " if actual.HasMax() { errStr += fmt.Sprintf("%f", actual.Min()) } else { errStr += "<unset>" } errs = multierr.Append(errs, errors.New(errStr)) } if expected.Count() != actual.Count() { errs = multierr.Append(errs, fmt.Errorf("count doesn't match expected: %d, actual: %d", expected.Count(), actual.Count())) } if expected.StartTimestamp() != actual.StartTimestamp() { errs = multierr.Append(errs, fmt.Errorf("start timestamp doesn't match expected: %d, actual: %d", expected.StartTimestamp(), actual.StartTimestamp())) } if expected.Timestamp() != actual.Timestamp() { errs = multierr.Append(errs, fmt.Errorf("timestamp doesn't match expected: %d, actual: %d", expected.Timestamp(), actual.Timestamp())) } if expected.Flags() != actual.Flags() { errs = multierr.Append(errs, fmt.Errorf("flags don't match expected: %d, actual: %d", expected.Flags(), actual.Flags())) } if !reflect.DeepEqual(expected.BucketCounts(), actual.BucketCounts()) { errs = multierr.Append(errs, fmt.Errorf("bucket counts don't match expected: %v, actual: %v", expected.BucketCounts().AsRaw(), actual.BucketCounts().AsRaw())) } if !reflect.DeepEqual(expected.ExplicitBounds(), actual.ExplicitBounds()) { errs = multierr.Append(errs, fmt.Errorf("explicit bounds don't match expected: %v, "+ "actual: %v", expected.ExplicitBounds().AsRaw(), actual.ExplicitBounds().AsRaw())) } errs = multierr.Append(errs, compareExemplarSlice(expected.Exemplars(), actual.Exemplars())) return errs } // compareExponentialHistogramDataPointSlice compares each part of two given ExponentialHistogramDataPointSlices and // returns an error if they don't match. The error describes what didn't match. func compareExponentialHistogramDataPointSlice(expected, actual pmetric.ExponentialHistogramDataPointSlice) error { if expected.Len() != actual.Len() { return fmt.Errorf("number of datapoints doesn't match expected: %d, actual: %d", expected.Len(), actual.Len()) } numPoints := expected.Len() // Keep track of matching data points so that each point can only be matched once matchingDPS := make(map[pmetric.ExponentialHistogramDataPoint]pmetric.ExponentialHistogramDataPoint, numPoints) var errs error var outOfOrderErrs error for e := 0; e < numPoints; e++ { edp := expected.At(e) var foundMatch bool for a := 0; a < numPoints; a++ { adp := actual.At(a) if _, ok := matchingDPS[adp]; ok { continue } if reflect.DeepEqual(edp.Attributes().AsRaw(), adp.Attributes().AsRaw()) { foundMatch = true matchingDPS[adp] = edp if e != a { outOfOrderErrs = multierr.Append(outOfOrderErrs, fmt.Errorf(`datapoint "%v" expected at index %d, found at index %d`, edp.Attributes().AsRaw(), e, a)) } break } } if !foundMatch { errs = multierr.Append(errs, fmt.Errorf("missing expected datapoint: %v", edp.Attributes().AsRaw())) } } for i := 0; i < numPoints; i++ { if _, ok := matchingDPS[actual.At(i)]; !ok { errs = multierr.Append(errs, fmt.Errorf("unexpected datapoint: %v", actual.At(i).Attributes().AsRaw())) } } if errs != nil { return errs } if outOfOrderErrs != nil { return outOfOrderErrs } for adp, edp := range matchingDPS { errPrefix := fmt.Sprintf(`datapoint "%v"`, adp.Attributes().AsRaw()) errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareExponentialHistogramDataPoint(edp, adp))) } return errs } // CompareExponentialHistogramDataPoint compares each part of two given ExponentialHistogramDataPoints and returns // an error if they don't match. The error describes what didn't match. func CompareExponentialHistogramDataPoint(expected, actual pmetric.ExponentialHistogramDataPoint) error { errs := internal.CompareAttributes(expected.Attributes(), actual.Attributes()) if expected.HasSum() != actual.HasSum() || expected.Sum() != actual.Sum() { errStr := "sum doesn't match expected: " if expected.HasSum() { errStr += fmt.Sprintf("%f", expected.Sum()) } else { errStr += "<unset>" } errStr += ", actual: " if actual.HasSum() { errStr += fmt.Sprintf("%f", actual.Sum()) } else { errStr += "<unset>" } errs = multierr.Append(errs, errors.New(errStr)) } if expected.HasMin() != actual.HasMin() || expected.Min() != actual.Min() { errStr := "min doesn't match expected: " if expected.HasMin() { errStr += fmt.Sprintf("%f", expected.Min()) } else { errStr += "<unset>" } errStr += ", actual: " if actual.HasMin() { errStr += fmt.Sprintf("%f", actual.Min()) } else { errStr += "<unset>" } errs = multierr.Append(errs, errors.New(errStr)) } if expected.HasMax() != actual.HasMax() || expected.Max() != actual.Max() { errStr := "max doesn't match expected: " if expected.HasMax() { errStr += fmt.Sprintf("%f", expected.Min()) } else { errStr += "<unset>" } errStr += ", actual: " if actual.HasMax() { errStr += fmt.Sprintf("%f", actual.Min()) } else { errStr += "<unset>" } errs = multierr.Append(errs, errors.New(errStr)) } if expected.Count() != actual.Count() { errs = multierr.Append(errs, fmt.Errorf("count doesn't match expected: %d, actual: %d", expected.Count(), actual.Count())) } if expected.ZeroCount() != actual.ZeroCount() { errs = multierr.Append(errs, fmt.Errorf("zero count doesn't match expected: %d, actual: %d", expected.ZeroCount(), actual.ZeroCount())) } if expected.StartTimestamp() != actual.StartTimestamp() { errs = multierr.Append(errs, fmt.Errorf("start timestamp doesn't match expected: %d, "+ "actual: %d", expected.StartTimestamp(), actual.StartTimestamp())) } if expected.Timestamp() != actual.Timestamp() { errs = multierr.Append(errs, fmt.Errorf("timestamp doesn't match expected: %d, actual: %d", expected.Timestamp(), actual.Timestamp())) } if expected.Flags() != actual.Flags() { errs = multierr.Append(errs, fmt.Errorf("flags don't match expected: %d, actual: %d", expected.Flags(), actual.Flags())) } if expected.Scale() != actual.Scale() { errs = multierr.Append(errs, fmt.Errorf("scale doesn't match expected: %v, actual: %v", expected.Scale(), actual.Scale())) } if expected.Negative().Offset() != actual.Negative().Offset() { errs = multierr.Append(errs, fmt.Errorf("negative offset doesn't match expected: %v, "+ "actual: %v", expected.Negative().Offset(), actual.Negative().Offset())) } if !reflect.DeepEqual(expected.Negative().BucketCounts(), actual.Negative().BucketCounts()) { errs = multierr.Append(errs, fmt.Errorf("negative bucket counts don't match expected: %v, "+ "actual: %v", expected.Negative().BucketCounts().AsRaw(), actual.Negative().BucketCounts().AsRaw())) } if expected.Positive().Offset() != actual.Positive().Offset() { errs = multierr.Append(errs, fmt.Errorf("positive offset doesn't match expected: %v, "+ "actual: %v", expected.Positive().Offset(), actual.Positive().Offset())) } if !reflect.DeepEqual(expected.Positive().BucketCounts(), actual.Positive().BucketCounts()) { errs = multierr.Append(errs, fmt.Errorf("positive bucket counts don't match expected: %v, "+ "actual: %v", expected.Positive().BucketCounts().AsRaw(), actual.Positive().BucketCounts().AsRaw())) } errs = multierr.Append(errs, compareExemplarSlice(expected.Exemplars(), actual.Exemplars())) return errs } // compareSummaryDataPointSlices compares each part of two given SummaryDataPoint slices and returns // an error if they don't match. The error describes what didn't match. func compareSummaryDataPointSlices(expected, actual pmetric.SummaryDataPointSlice) error { numPoints := expected.Len() if numPoints != actual.Len() { return fmt.Errorf("metric datapoint slice length doesn't match expected: %d, actual: %d", numPoints, actual.Len()) } matchingDPS := map[pmetric.SummaryDataPoint]pmetric.SummaryDataPoint{} var errs error var outOfOrderErrs error for e := 0; e < numPoints; e++ { edp := expected.At(e) var foundMatch bool for a := 0; a < numPoints; a++ { adp := actual.At(a) if _, ok := matchingDPS[adp]; ok { continue } if reflect.DeepEqual(edp.Attributes().AsRaw(), adp.Attributes().AsRaw()) { foundMatch = true matchingDPS[adp] = edp if e != a { outOfOrderErrs = multierr.Append(outOfOrderErrs, fmt.Errorf(`datapoint "%v" expected at index %d, found at index %d`, edp.Attributes().AsRaw(), e, a)) } break } } if !foundMatch { errs = multierr.Append(errs, fmt.Errorf("missing expected datapoint: %v", edp.Attributes().AsRaw())) } } for i := 0; i < numPoints; i++ { if _, ok := matchingDPS[actual.At(i)]; !ok { errs = multierr.Append(errs, fmt.Errorf("unexpected datapoint: %v", actual.At(i).Attributes().AsRaw())) } } if errs != nil { return errs } if outOfOrderErrs != nil { return outOfOrderErrs } for adp, edp := range matchingDPS { errPrefix := fmt.Sprintf(`datapoint "%v"`, adp.Attributes().AsRaw()) errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareSummaryDataPoint(edp, adp))) } return errs } // CompareSummaryDataPoint compares each part of two given SummaryDataPoint and returns // an error if they don't match. The error describes what didn't match. func CompareSummaryDataPoint(expected, actual pmetric.SummaryDataPoint) error { errs := internal.CompareAttributes(expected.Attributes(), actual.Attributes()) if expected.Count() != actual.Count() { errs = multierr.Append(errs, fmt.Errorf("count doesn't match expected: %d, actual: %d", expected.Count(), actual.Count())) } if expected.Sum() != actual.Sum() { errs = multierr.Append(errs, fmt.Errorf("sum doesn't match expected: %f, actual: %f", expected.Sum(), actual.Sum())) } if expected.StartTimestamp() != actual.StartTimestamp() { errs = multierr.Append(errs, fmt.Errorf("start timestamp doesn't match expected: %d, "+ "actual: %d", expected.StartTimestamp(), actual.StartTimestamp())) } if expected.Timestamp() != actual.Timestamp() { errs = multierr.Append(errs, fmt.Errorf("timestamp doesn't match expected: %d, actual: %d", expected.Timestamp(), actual.Timestamp())) } if expected.Flags() != actual.Flags() { errs = multierr.Append(errs, fmt.Errorf("flags don't match expected: %d, actual: %d", expected.Flags(), actual.Flags())) } if expected.QuantileValues().Len() != actual.QuantileValues().Len() { errs = multierr.Append(errs, fmt.Errorf("quantile values length doesn't match expected: %d, "+ "actual: %d", expected.QuantileValues().Len(), actual.QuantileValues().Len())) return errs } for i := 0; i < expected.QuantileValues().Len(); i++ { eqv, acv := expected.QuantileValues().At(i), actual.QuantileValues().At(i) if eqv.Quantile() != acv.Quantile() { errs = multierr.Append(errs, fmt.Errorf("quantile doesn't match expected: %f, "+ "actual: %f", eqv.Quantile(), acv.Quantile())) } if eqv.Value() != acv.Value() { errs = multierr.Append(errs, fmt.Errorf("value at quantile %f doesn't match expected: %f, actual: %f", eqv.Quantile(), eqv.Value(), acv.Value())) } } return errs }