From ccf3caef75edcdf7280991dde0f03e64d6ad5ed1 Mon Sep 17 00:00:00 2001 From: odubajDT <93584209+odubajDT@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:31:46 +0100 Subject: [PATCH] [pkg/pdatatest] support Profiles signal comparison (#36273) #### Description #### Link to tracking issue Fixes #36232 --------- Signed-off-by: odubajDT --- .chloggen/profiles-pdata.yaml | 27 + pkg/pdatatest/go.mod | 1 + pkg/pdatatest/go.sum | 2 + pkg/pdatatest/internal/util.go | 20 + pkg/pdatatest/pmetrictest/options.go | 30 +- pkg/pdatatest/pprofiletest/options.go | 215 ++ pkg/pdatatest/pprofiletest/package_test.go | 14 + pkg/pdatatest/pprofiletest/profiles.go | 858 ++++++++ pkg/pdatatest/pprofiletest/profiles_test.go | 2000 +++++++++++++++++++ 9 files changed, 3142 insertions(+), 25 deletions(-) create mode 100644 .chloggen/profiles-pdata.yaml create mode 100644 pkg/pdatatest/pprofiletest/options.go create mode 100644 pkg/pdatatest/pprofiletest/package_test.go create mode 100644 pkg/pdatatest/pprofiletest/profiles.go create mode 100644 pkg/pdatatest/pprofiletest/profiles_test.go diff --git a/.chloggen/profiles-pdata.yaml b/.chloggen/profiles-pdata.yaml new file mode 100644 index 000000000000..631f5e603a5a --- /dev/null +++ b/.chloggen/profiles-pdata.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: pkg/pdatatest + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Add support for Profiles signal comparison." + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36232] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/pkg/pdatatest/go.mod b/pkg/pdatatest/go.mod index 1fa2521ad123..47f5a5b66f9b 100644 --- a/pkg/pdatatest/go.mod +++ b/pkg/pdatatest/go.mod @@ -7,6 +7,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.115.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/collector/pdata v1.21.1-0.20241206185113-3f3e208e71b8 + go.opentelemetry.io/collector/pdata/pprofile v0.115.1-0.20241206185113-3f3e208e71b8 go.uber.org/goleak v1.3.0 go.uber.org/multierr v1.11.0 ) diff --git a/pkg/pdatatest/go.sum b/pkg/pdatatest/go.sum index 6d68c4eeac41..292df9eba42a 100644 --- a/pkg/pdatatest/go.sum +++ b/pkg/pdatatest/go.sum @@ -33,6 +33,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/collector/pdata v1.21.1-0.20241206185113-3f3e208e71b8 h1:PUaCJ1XIIomqXvFBF6hMFikhZIwoBc57UP7xlaRT//I= go.opentelemetry.io/collector/pdata v1.21.1-0.20241206185113-3f3e208e71b8/go.mod h1:GKb1/zocKJMvxKbS+sl0W85lxhYBTFJ6h6I1tphVyDU= +go.opentelemetry.io/collector/pdata/pprofile v0.115.1-0.20241206185113-3f3e208e71b8 h1:+RGyM6ZhtNHRaiNbIiJ82Ik6TFmk3BCOgLvhw509Hc4= +go.opentelemetry.io/collector/pdata/pprofile v0.115.1-0.20241206185113-3f3e208e71b8/go.mod h1:jGzdNfO0XTtfLjXCL/uCC1livg1LlfR+ix2WE/z3RpQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= diff --git a/pkg/pdatatest/internal/util.go b/pkg/pdatatest/internal/util.go index f9b96dfc7b6a..9213ab8dbbff 100644 --- a/pkg/pdatatest/internal/util.go +++ b/pkg/pdatatest/internal/util.go @@ -7,6 +7,7 @@ import ( "fmt" "reflect" "regexp" + "sort" "go.opentelemetry.io/collector/pdata/pcommon" "go.uber.org/multierr" @@ -88,3 +89,22 @@ func CompareDroppedAttributesCount(expected, actual uint32) error { } return nil } + +func OrderMapByKey(input map[string]any) map[string]any { + // Create a slice to hold the keys + keys := make([]string, 0, len(input)) + for k := range input { + keys = append(keys, k) + } + + // Sort the keys + sort.Strings(keys) + + // Create a new map to hold the sorted key-value pairs + orderedMap := make(map[string]any, len(input)) + for _, k := range keys { + orderedMap[k] = input[k] + } + + return orderedMap +} diff --git a/pkg/pdatatest/pmetrictest/options.go b/pkg/pdatatest/pmetrictest/options.go index 883e8fa2b3b1..3a6ce4cb9651 100644 --- a/pkg/pdatatest/pmetrictest/options.go +++ b/pkg/pdatatest/pmetrictest/options.go @@ -8,7 +8,6 @@ import ( "fmt" "math" "regexp" - "sort" "time" "go.opentelemetry.io/collector/pdata/pcommon" @@ -275,27 +274,27 @@ func orderDatapointAttributes(metrics pmetric.Metrics) { switch msl.At(g).Type() { case pmetric.MetricTypeGauge: for k := 0; k < msl.At(g).Gauge().DataPoints().Len(); k++ { - rawOrdered := orderMapByKey(msl.At(g).Gauge().DataPoints().At(k).Attributes().AsRaw()) + rawOrdered := internal.OrderMapByKey(msl.At(g).Gauge().DataPoints().At(k).Attributes().AsRaw()) _ = msl.At(g).Gauge().DataPoints().At(k).Attributes().FromRaw(rawOrdered) } case pmetric.MetricTypeSum: for k := 0; k < msl.At(g).Sum().DataPoints().Len(); k++ { - rawOrdered := orderMapByKey(msl.At(g).Sum().DataPoints().At(k).Attributes().AsRaw()) + rawOrdered := internal.OrderMapByKey(msl.At(g).Sum().DataPoints().At(k).Attributes().AsRaw()) _ = msl.At(g).Sum().DataPoints().At(k).Attributes().FromRaw(rawOrdered) } case pmetric.MetricTypeHistogram: for k := 0; k < msl.At(g).Histogram().DataPoints().Len(); k++ { - rawOrdered := orderMapByKey(msl.At(g).Histogram().DataPoints().At(k).Attributes().AsRaw()) + rawOrdered := internal.OrderMapByKey(msl.At(g).Histogram().DataPoints().At(k).Attributes().AsRaw()) _ = msl.At(g).Histogram().DataPoints().At(k).Attributes().FromRaw(rawOrdered) } case pmetric.MetricTypeExponentialHistogram: for k := 0; k < msl.At(g).ExponentialHistogram().DataPoints().Len(); k++ { - rawOrdered := orderMapByKey(msl.At(g).ExponentialHistogram().DataPoints().At(k).Attributes().AsRaw()) + rawOrdered := internal.OrderMapByKey(msl.At(g).ExponentialHistogram().DataPoints().At(k).Attributes().AsRaw()) _ = msl.At(g).ExponentialHistogram().DataPoints().At(k).Attributes().FromRaw(rawOrdered) } case pmetric.MetricTypeSummary: for k := 0; k < msl.At(g).Summary().DataPoints().Len(); k++ { - rawOrdered := orderMapByKey(msl.At(g).Summary().DataPoints().At(k).Attributes().AsRaw()) + rawOrdered := internal.OrderMapByKey(msl.At(g).Summary().DataPoints().At(k).Attributes().AsRaw()) _ = msl.At(g).Summary().DataPoints().At(k).Attributes().FromRaw(rawOrdered) } case pmetric.MetricTypeEmpty: @@ -305,25 +304,6 @@ func orderDatapointAttributes(metrics pmetric.Metrics) { } } -func orderMapByKey(input map[string]any) map[string]any { - // Create a slice to hold the keys - keys := make([]string, 0, len(input)) - for k := range input { - keys = append(keys, k) - } - - // Sort the keys - sort.Strings(keys) - - // Create a new map to hold the sorted key-value pairs - orderedMap := make(map[string]any, len(input)) - for _, k := range keys { - orderedMap[k] = input[k] - } - - return orderedMap -} - func maskMetricAttributeValue(metrics pmetric.Metrics, attributeName string, metricNames []string) { rms := metrics.ResourceMetrics() for i := 0; i < rms.Len(); i++ { diff --git a/pkg/pdatatest/pprofiletest/options.go b/pkg/pdatatest/pprofiletest/options.go new file mode 100644 index 000000000000..17545953fa48 --- /dev/null +++ b/pkg/pdatatest/pprofiletest/options.go @@ -0,0 +1,215 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package pprofiletest // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pprofiletest" + +import ( + "bytes" + "time" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/internal" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil" +) + +// CompareProfilesOption can be used to mutate expected and/or actual profiles before comparing. +type CompareProfilesOption interface { + applyOnProfiles(expected, actual pprofile.Profiles) +} + +type compareProfilesOptionFunc func(expected, actual pprofile.Profiles) + +func (f compareProfilesOptionFunc) applyOnProfiles(expected, actual pprofile.Profiles) { + f(expected, actual) +} + +// IgnoreResourceAttributeValue is a CompareProfilesOption that removes a resource attribute +// from all resources. +func IgnoreResourceAttributeValue(attributeName string) CompareProfilesOption { + return ignoreResourceAttributeValue{ + attributeName: attributeName, + } +} + +type ignoreResourceAttributeValue struct { + attributeName string +} + +func (opt ignoreResourceAttributeValue) applyOnProfiles(expected, actual pprofile.Profiles) { + opt.maskProfilesResourceAttributeValue(expected) + opt.maskProfilesResourceAttributeValue(actual) +} + +func (opt ignoreResourceAttributeValue) maskProfilesResourceAttributeValue(profiles pprofile.Profiles) { + rls := profiles.ResourceProfiles() + for i := 0; i < rls.Len(); i++ { + internal.MaskResourceAttributeValue(rls.At(i).Resource(), opt.attributeName) + } +} + +// IgnoreResourceAttributeValue is a CompareProfilesOption that removes a resource attribute +// from all resources. +func IgnoreScopeAttributeValue(attributeName string) CompareProfilesOption { + return ignoreScopeAttributeValue{ + attributeName: attributeName, + } +} + +type ignoreScopeAttributeValue struct { + attributeName string +} + +func (opt ignoreScopeAttributeValue) applyOnProfiles(expected, actual pprofile.Profiles) { + opt.maskProfilesScopeAttributeValue(expected) + opt.maskProfilesScopeAttributeValue(actual) +} + +func (opt ignoreScopeAttributeValue) maskProfilesScopeAttributeValue(profiles pprofile.Profiles) { + rls := profiles.ResourceProfiles() + for i := 0; i < profiles.ResourceProfiles().Len(); i++ { + sls := rls.At(i).ScopeProfiles() + for j := 0; j < sls.Len(); j++ { + lr := sls.At(j) + val, exists := lr.Scope().Attributes().Get(opt.attributeName) + if exists { + val.SetEmptyBytes() + } + } + } +} + +// IgnoreProfileAttributeValue is a CompareProfilesOption that sets the value of an attribute +// to empty bytes for every profile +func IgnoreProfileAttributeValue(attributeName string) CompareProfilesOption { + return ignoreProfileAttributeValue{ + attributeName: attributeName, + } +} + +type ignoreProfileAttributeValue struct { + attributeName string +} + +func (opt ignoreProfileAttributeValue) applyOnProfiles(expected, actual pprofile.Profiles) { + opt.maskProfileAttributeValue(expected) + opt.maskProfileAttributeValue(actual) +} + +func (opt ignoreProfileAttributeValue) maskProfileAttributeValue(profiles pprofile.Profiles) { + rls := profiles.ResourceProfiles() + for i := 0; i < profiles.ResourceProfiles().Len(); i++ { + sls := rls.At(i).ScopeProfiles() + for j := 0; j < sls.Len(); j++ { + lrs := sls.At(j).Profiles() + for k := 0; k < lrs.Len(); k++ { + lr := lrs.At(k) + val, exists := lr.Attributes().Get(opt.attributeName) + if exists { + val.SetEmptyBytes() + } + } + } + } +} + +// IgnoreProfileTimestampValues is a CompareProfilesOption that sets the value of start timestamp +// and duration to empty bytes for every profile +func IgnoreProfileTimestampValues() CompareProfilesOption { + return ignoreProfileTimestampValues{} +} + +type ignoreProfileTimestampValues struct{} + +func (opt ignoreProfileTimestampValues) applyOnProfiles(expected, actual pprofile.Profiles) { + opt.maskProfileTimestampValues(expected) + opt.maskProfileTimestampValues(actual) +} + +func (opt ignoreProfileTimestampValues) maskProfileTimestampValues(profiles pprofile.Profiles) { + rls := profiles.ResourceProfiles() + for i := 0; i < profiles.ResourceProfiles().Len(); i++ { + sls := rls.At(i).ScopeProfiles() + for j := 0; j < sls.Len(); j++ { + lrs := sls.At(j).Profiles() + for k := 0; k < lrs.Len(); k++ { + lr := lrs.At(k) + lr.SetStartTime(pcommon.NewTimestampFromTime(time.Time{})) + lr.SetDuration(pcommon.NewTimestampFromTime(time.Time{})) + } + } + } +} + +// IgnoreResourceProfilesOrder is a CompareProfilesOption that ignores the order of resource traces/metrics/profiles. +func IgnoreResourceProfilesOrder() CompareProfilesOption { + return compareProfilesOptionFunc(func(expected, actual pprofile.Profiles) { + sortResourceProfilesSlice(expected.ResourceProfiles()) + sortResourceProfilesSlice(actual.ResourceProfiles()) + }) +} + +func sortResourceProfilesSlice(rls pprofile.ResourceProfilesSlice) { + rls.Sort(func(a, b pprofile.ResourceProfiles) bool { + if a.SchemaUrl() != b.SchemaUrl() { + return a.SchemaUrl() < b.SchemaUrl() + } + aAttrs := pdatautil.MapHash(a.Resource().Attributes()) + bAttrs := pdatautil.MapHash(b.Resource().Attributes()) + return bytes.Compare(aAttrs[:], bAttrs[:]) < 0 + }) +} + +// IgnoreScopeProfilesOrder is a CompareProfilesOption that ignores the order of instrumentation scope traces/metrics/profiles. +func IgnoreScopeProfilesOrder() CompareProfilesOption { + return compareProfilesOptionFunc(func(expected, actual pprofile.Profiles) { + sortScopeProfilesSlices(expected) + sortScopeProfilesSlices(actual) + }) +} + +func sortScopeProfilesSlices(ls pprofile.Profiles) { + for i := 0; i < ls.ResourceProfiles().Len(); i++ { + ls.ResourceProfiles().At(i).ScopeProfiles().Sort(func(a, b pprofile.ScopeProfiles) bool { + if a.SchemaUrl() != b.SchemaUrl() { + return a.SchemaUrl() < b.SchemaUrl() + } + if a.Scope().Name() != b.Scope().Name() { + return a.Scope().Name() < b.Scope().Name() + } + return a.Scope().Version() < b.Scope().Version() + }) + } +} + +// IgnoreProfilesOrder is a CompareProfilesOption that ignores the order of profile records. +func IgnoreProfilesOrder() CompareProfilesOption { + return compareProfilesOptionFunc(func(expected, actual pprofile.Profiles) { + sortProfileSlices(expected) + sortProfileSlices(actual) + }) +} + +func sortProfileSlices(ls pprofile.Profiles) { + for i := 0; i < ls.ResourceProfiles().Len(); i++ { + for j := 0; j < ls.ResourceProfiles().At(i).ScopeProfiles().Len(); j++ { + ls.ResourceProfiles().At(i).ScopeProfiles().At(j).Profiles().Sort(func(a, b pprofile.Profile) bool { + if a.StartTime() != b.StartTime() { + return a.StartTime() < b.StartTime() + } + if a.Duration() != b.Duration() { + return a.Duration() < b.Duration() + } + as := a.ProfileID() + bs := b.ProfileID() + if !bytes.Equal(as[:], bs[:]) { + return bytes.Compare(as[:], bs[:]) < 0 + } + aAttrs := pdatautil.MapHash(a.Attributes()) + bAttrs := pdatautil.MapHash(b.Attributes()) + return bytes.Compare(aAttrs[:], bAttrs[:]) < 0 + }) + } + } +} diff --git a/pkg/pdatatest/pprofiletest/package_test.go b/pkg/pdatatest/pprofiletest/package_test.go new file mode 100644 index 000000000000..453a8bc92eb6 --- /dev/null +++ b/pkg/pdatatest/pprofiletest/package_test.go @@ -0,0 +1,14 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package pprofiletest + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/pkg/pdatatest/pprofiletest/profiles.go b/pkg/pdatatest/pprofiletest/profiles.go new file mode 100644 index 000000000000..dc1222b8e4d0 --- /dev/null +++ b/pkg/pdatatest/pprofiletest/profiles.go @@ -0,0 +1,858 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package pprofiletest // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pprofiletest" + +import ( + "bytes" + "fmt" + "reflect" + + "go.opentelemetry.io/collector/pdata/pprofile" + "go.uber.org/multierr" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/internal" +) + +// CompareProfiles compares each part of two given Profiles and returns +// an error if they don't match. The error describes what didn't match. +func CompareProfiles(expected, actual pprofile.Profiles, options ...CompareProfilesOption) error { + exp, act := pprofile.NewProfiles(), pprofile.NewProfiles() + expected.CopyTo(exp) + actual.CopyTo(act) + + for _, option := range options { + option.applyOnProfiles(exp, act) + } + + if exp.IsReadOnly() != act.IsReadOnly() { + return fmt.Errorf("readOnly state differs from expected '%t'", exp.IsReadOnly()) + } + + if exp.SampleCount() != act.SampleCount() { + return fmt.Errorf("sample count state differs from expected '%d', actual '%d'", exp.SampleCount(), act.SampleCount()) + } + + expectedProfiles, actualProfiles := exp.ResourceProfiles(), act.ResourceProfiles() + if expectedProfiles.Len() != actualProfiles.Len() { + return fmt.Errorf("number of resources doesn't match expected: %d, actual: %d", + expectedProfiles.Len(), actualProfiles.Len()) + } + + numResources := expectedProfiles.Len() + + // Keep track of matching resources so that each can only be matched once + matchingResources := make(map[pprofile.ResourceProfiles]pprofile.ResourceProfiles, numResources) + + var errs error + var outOfOrderErrs error + for e := 0; e < numResources; e++ { + er := expectedProfiles.At(e) + var foundMatch bool + for a := 0; a < numResources; a++ { + ar := actualProfiles.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[actualProfiles.At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf("unexpected resource: %v", actualProfiles.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"`, er.Resource().Attributes().AsRaw()) + errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareResourceProfiles(er, ar))) + } + + return errs +} + +// CompareResourceProfiles compares each part of two given ResourceProfiles and returns +// an error if they don't match. The error describes what didn't match. +func CompareResourceProfiles(expected, actual pprofile.ResourceProfiles) error { + errs := multierr.Combine( + internal.CompareResource(expected.Resource(), actual.Resource()), + internal.CompareSchemaURL(expected.SchemaUrl(), actual.SchemaUrl()), + ) + + esls := expected.ScopeProfiles() + asls := actual.ScopeProfiles() + + if esls.Len() != asls.Len() { + errs = multierr.Append(errs, fmt.Errorf("number of scopes doesn't match expected: %d, actual: %d", esls.Len(), + asls.Len())) + return errs + } + + numScopeProfiles := esls.Len() + + // Keep track of matching scope profiles so that each container can only be matched once + matchingScopeProfiles := make(map[pprofile.ScopeProfiles]pprofile.ScopeProfiles, numScopeProfiles) + + var outOfOrderErrs error + for e := 0; e < numScopeProfiles; e++ { + esl := expected.ScopeProfiles().At(e) + var foundMatch bool + for a := 0; a < numScopeProfiles; a++ { + asl := actual.ScopeProfiles().At(a) + if _, ok := matchingScopeProfiles[asl]; ok { + continue + } + if esl.Scope().Name() == asl.Scope().Name() { + foundMatch = true + matchingScopeProfiles[asl] = esl + if e != a { + outOfOrderErrs = multierr.Append(outOfOrderErrs, + fmt.Errorf("scopes are out of order: scope %s expected at index %d, found at index %d", + esl.Scope().Name(), e, a)) + } + break + } + } + if !foundMatch { + errs = multierr.Append(errs, fmt.Errorf("missing expected scope: %s", esl.Scope().Name())) + } + } + + for i := 0; i < numScopeProfiles; i++ { + if _, ok := matchingScopeProfiles[actual.ScopeProfiles().At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf("unexpected scope: %s", actual.ScopeProfiles().At(i).Scope().Name())) + } + } + + if errs != nil { + return errs + } + if outOfOrderErrs != nil { + return outOfOrderErrs + } + + for i := 0; i < esls.Len(); i++ { + errPrefix := fmt.Sprintf(`scope "%s"`, esls.At(i).Scope().Name()) + errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareScopeProfiles(esls.At(i), asls.At(i)))) + } + + return errs +} + +// CompareScopeProfiles compares each part of two given ProfilesSlices and returns +// an error if they don't match. The error describes what didn't match. +func CompareScopeProfiles(expected, actual pprofile.ScopeProfiles) error { + errs := multierr.Combine( + internal.CompareInstrumentationScope(expected.Scope(), actual.Scope()), + internal.CompareSchemaURL(expected.SchemaUrl(), actual.SchemaUrl()), + ) + + if expected.Profiles().Len() != actual.Profiles().Len() { + errs = multierr.Append(errs, fmt.Errorf("number of profiles doesn't match expected: %d, actual: %d", + expected.Profiles().Len(), actual.Profiles().Len())) + return errs + } + + numProfiles := expected.Profiles().Len() + + // Keep track of matching containers so that each container can only be matched once + matchingProfiles := make(map[pprofile.Profile]pprofile.Profile, numProfiles) + + var outOfOrderErrs error + for e := 0; e < numProfiles; e++ { + elr := expected.Profiles().At(e) + var foundMatch bool + for a := 0; a < numProfiles; a++ { + alr := actual.Profiles().At(a) + if _, ok := matchingProfiles[alr]; ok { + continue + } + if reflect.DeepEqual(elr.Attributes().AsRaw(), alr.Attributes().AsRaw()) { + foundMatch = true + matchingProfiles[alr] = elr + if e != a { + outOfOrderErrs = multierr.Append(outOfOrderErrs, + fmt.Errorf(`profiles are out of order: profile "%v" expected at index %d, found at index %d`, + elr.Attributes().AsRaw(), e, a)) + } + break + } + } + if !foundMatch { + errs = multierr.Append(errs, fmt.Errorf("missing expected profile: %v", elr.Attributes().AsRaw())) + } + } + + for i := 0; i < numProfiles; i++ { + if _, ok := matchingProfiles[actual.Profiles().At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf("unexpected profile: %v", + actual.Profiles().At(i).Attributes().AsRaw())) + } + } + + if errs != nil { + return errs + } + if outOfOrderErrs != nil { + return outOfOrderErrs + } + + for alr, elr := range matchingProfiles { + errPrefix := fmt.Sprintf(`profile "%v"`, elr.Attributes().AsRaw()) + errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareProfile(elr, alr))) + } + return errs +} + +func CompareProfile(expected, actual pprofile.Profile) error { + errs := multierr.Combine( + internal.CompareAttributes(expected.Attributes(), actual.Attributes()), + internal.CompareDroppedAttributesCount(expected.DroppedAttributesCount(), actual.DroppedAttributesCount()), + ) + + if expected.ProfileID().String() != actual.ProfileID().String() { + errs = multierr.Append(errs, fmt.Errorf("profileID does not match expected '%s', actual '%s'", expected.ProfileID().String(), actual.ProfileID().String())) + } + + if expected.StartTime() != actual.StartTime() { + errs = multierr.Append(errs, fmt.Errorf("start timestamp doesn't match expected: %d, "+"actual: %d", expected.StartTime(), actual.StartTime())) + } + + if !reflect.DeepEqual(expected.LocationIndices(), actual.LocationIndices()) { + errs = multierr.Append(errs, fmt.Errorf("locationIndicies do not match expected")) + } + + if !reflect.DeepEqual(expected.CommentStrindices(), actual.CommentStrindices()) { + errs = multierr.Append(errs, fmt.Errorf("comment does not match expected")) + } + + if expected.Time() != actual.Time() { + errs = multierr.Append(errs, fmt.Errorf("time doesn't match expected: %d, actual: %d", expected.Time(), actual.Time())) + } + + if !reflect.DeepEqual(expected.StringTable(), actual.StringTable()) { + errs = multierr.Append(errs, fmt.Errorf("stringTable does not match expected")) + } + + if expected.OriginalPayloadFormat() != actual.OriginalPayloadFormat() { + errs = multierr.Append(errs, fmt.Errorf("originalPayloadFormat does not match expected '%s', actual '%s'", expected.OriginalPayloadFormat(), actual.OriginalPayloadFormat())) + } + + if !bytes.Equal(expected.OriginalPayload().AsRaw(), actual.OriginalPayload().AsRaw()) { + errs = multierr.Append(errs, fmt.Errorf("keepFrames does not match expected '%s', actual '%s'", expected.OriginalPayload().AsRaw(), actual.OriginalPayload().AsRaw())) + } + + if expected.StartTime() != actual.StartTime() { + errs = multierr.Append(errs, fmt.Errorf("startTime doesn't match expected: %d, actual: %d", expected.StartTime(), actual.StartTime())) + } + + if expected.Duration() != actual.Duration() { + errs = multierr.Append(errs, fmt.Errorf("duration doesn't match expected: %d, actual: %d", expected.Duration(), actual.Duration())) + } + + if expected.Period() != actual.Period() { + errs = multierr.Append(errs, fmt.Errorf("period does not match expected '%d', actual '%d'", expected.Period(), actual.Period())) + } + + if expected.DefaultSampleTypeStrindex() != actual.DefaultSampleTypeStrindex() { + errs = multierr.Append(errs, fmt.Errorf("defaultSampleType does not match expected '%d', actual '%d'", expected.DefaultSampleTypeStrindex(), actual.DefaultSampleTypeStrindex())) + } + + if expected.PeriodType().TypeStrindex() != actual.PeriodType().TypeStrindex() || + expected.PeriodType().UnitStrindex() != actual.PeriodType().UnitStrindex() || + expected.PeriodType().AggregationTemporality() != actual.PeriodType().AggregationTemporality() { + errs = multierr.Append(errs, fmt.Errorf("periodType does not match expected 'unit: %d, type: %d, aggregationTemporality: %d', actual 'unit: %d, type: %d,"+ + "aggregationTemporality: %d'", expected.PeriodType().UnitStrindex(), expected.PeriodType().TypeStrindex(), expected.PeriodType().AggregationTemporality(), + actual.PeriodType().UnitStrindex(), actual.PeriodType().TypeStrindex(), actual.PeriodType().AggregationTemporality())) + } + + errs = multierr.Append(errs, internal.AddErrPrefix("sampleType", CompareProfileValueTypeSlice(expected.SampleType(), actual.SampleType()))) + + errs = multierr.Append(errs, internal.AddErrPrefix("sample", CompareProfileSampleSlice(expected.Sample(), actual.Sample()))) + + errs = multierr.Append(errs, internal.AddErrPrefix("mapping", CompareProfileMappingSlice(expected.MappingTable(), actual.MappingTable()))) + + errs = multierr.Append(errs, internal.AddErrPrefix("location", CompareProfileLocationSlice(expected.LocationTable(), actual.LocationTable()))) + + errs = multierr.Append(errs, internal.AddErrPrefix("function", CompareProfileFunctionSlice(expected.FunctionTable(), actual.FunctionTable()))) + + errs = multierr.Append(errs, internal.AddErrPrefix("attributeUnits", CompareProfileAttributeUnitSlice(expected.AttributeUnits(), actual.AttributeUnits()))) + + errs = multierr.Append(errs, internal.AddErrPrefix("linkTable", CompareProfileLinkSlice(expected.LinkTable(), actual.LinkTable()))) + + return errs +} + +func CompareProfileValueTypeSlice(expected, actual pprofile.ValueTypeSlice) error { + var errs error + if expected.Len() != actual.Len() { + errs = multierr.Append(errs, fmt.Errorf("number of valueTypes doesn't match expected: %d, actual: %d", + expected.Len(), actual.Len())) + return errs + } + + numValueTypes := expected.Len() + + matchingValueTypes := make(map[pprofile.ValueType]pprofile.ValueType, numValueTypes) + + var outOfOrderErrs error + for e := 0; e < numValueTypes; e++ { + elr := expected.At(e) + var foundMatch bool + for a := 0; a < numValueTypes; a++ { + alr := actual.At(a) + if _, ok := matchingValueTypes[alr]; ok { + continue + } + if elr.TypeStrindex() == alr.TypeStrindex() && elr.UnitStrindex() == alr.UnitStrindex() { + foundMatch = true + matchingValueTypes[alr] = elr + if e != a { + outOfOrderErrs = multierr.Append(outOfOrderErrs, + fmt.Errorf(`valueTypes are out of order: valueType "unit: %d, type: %d, aggregationTemporality: %d" expected at index %d, found at index %d`, + elr.UnitStrindex(), elr.TypeStrindex(), elr.AggregationTemporality(), e, a)) + } + break + } + } + if !foundMatch { + errs = multierr.Append(errs, fmt.Errorf(`missing expected valueType "unit: %d, type: %d, aggregationTemporality: %d"`, elr.UnitStrindex(), elr.TypeStrindex(), elr.AggregationTemporality())) + } + } + + for i := 0; i < numValueTypes; i++ { + if _, ok := matchingValueTypes[actual.At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf(`unexpected valueType "unit: %d, type: %d, aggregationTemporality: %d"`, + actual.At(i).UnitStrindex(), actual.At(i).TypeStrindex(), actual.At(i).AggregationTemporality())) + } + } + + if errs != nil { + return errs + } + if outOfOrderErrs != nil { + return outOfOrderErrs + } + + for alr, elr := range matchingValueTypes { + if !isValueTypeEqual(elr, alr) { + errs = multierr.Append(errs, fmt.Errorf(`expected valueType "unit: %d, type: %d, aggregationTemporality: %d",`+ + `got "unit: %d, type: %d, aggregationTemporality: %d"`, elr.UnitStrindex(), elr.TypeStrindex(), elr.AggregationTemporality(), + alr.UnitStrindex(), alr.TypeStrindex(), alr.AggregationTemporality())) + } + } + + return errs +} + +func isValueTypeEqual(expected, actual pprofile.ValueType) bool { + return expected.TypeStrindex() == actual.TypeStrindex() && + expected.UnitStrindex() == actual.UnitStrindex() && + expected.AggregationTemporality() == actual.AggregationTemporality() +} + +func CompareProfileSampleSlice(expected, actual pprofile.SampleSlice) error { + var errs error + if expected.Len() != actual.Len() { + errs = multierr.Append(errs, fmt.Errorf("number of samples doesn't match expected: %d, actual: %d", + expected.Len(), actual.Len())) + return errs + } + + numSlice := expected.Len() + + matchingItems := make(map[pprofile.Sample]pprofile.Sample, numSlice) + + var outOfOrderErrs error + for e := 0; e < numSlice; e++ { + elr := expected.At(e) + var foundMatch bool + for a := 0; a < numSlice; a++ { + alr := actual.At(a) + if _, ok := matchingItems[alr]; ok { + continue + } + if reflect.DeepEqual(elr.AttributeIndices().AsRaw(), alr.AttributeIndices().AsRaw()) { + foundMatch = true + matchingItems[alr] = elr + if e != a { + outOfOrderErrs = multierr.Append(outOfOrderErrs, + fmt.Errorf(`samples are out of order: sample "attributes: %v" expected at index %d, found at index %d`, + elr.AttributeIndices().AsRaw(), e, a)) + } + break + } + } + if !foundMatch { + errs = multierr.Append(errs, fmt.Errorf(`missing expected sample "attributes: %v"`, elr.AttributeIndices().AsRaw())) + } + } + + for i := 0; i < numSlice; i++ { + if _, ok := matchingItems[actual.At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf(`unexpected sample "attributes: %v"`, + actual.At(i).AttributeIndices().AsRaw())) + } + } + + if errs != nil { + return errs + } + if outOfOrderErrs != nil { + return outOfOrderErrs + } + + for alr, elr := range matchingItems { + errPrefix := fmt.Sprintf(`sample "attributes: %v"`, elr.AttributeIndices().AsRaw()) + errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareProfileSample(elr, alr))) + } + + return errs +} + +func CompareProfileSample(expected, actual pprofile.Sample) error { + var errs error + if expected.LocationsStartIndex() != actual.LocationsStartIndex() { + errs = multierr.Append(errs, fmt.Errorf("expected locationStartIndex '%d', got '%d'", expected.LocationsStartIndex(), actual.LocationsStartIndex())) + } + + if expected.LocationsLength() != actual.LocationsLength() { + errs = multierr.Append(errs, fmt.Errorf("expected locationLenght '%d', got '%d'", expected.LocationsLength(), actual.LocationsLength())) + } + + if !reflect.DeepEqual(expected.TimestampsUnixNano().AsRaw(), actual.TimestampsUnixNano().AsRaw()) { + errs = multierr.Append(errs, fmt.Errorf("expected timestampUnixNano '%v', got '%v'", expected.TimestampsUnixNano().AsRaw(), actual.TimestampsUnixNano().AsRaw())) + } + + if !reflect.DeepEqual(expected.Value().AsRaw(), actual.Value().AsRaw()) { + errs = multierr.Append(errs, fmt.Errorf("expected value '%v', got '%v'", expected.Value().AsRaw(), actual.Value().AsRaw())) + } + + if !reflect.DeepEqual(expected.TimestampsUnixNano().AsRaw(), actual.TimestampsUnixNano().AsRaw()) { + errs = multierr.Append(errs, fmt.Errorf("expected timestampUnixNano '%v', got '%v'", expected.TimestampsUnixNano().AsRaw(), actual.TimestampsUnixNano().AsRaw())) + } + + if !reflect.DeepEqual(expected.AttributeIndices().AsRaw(), actual.AttributeIndices().AsRaw()) { + errs = multierr.Append(errs, fmt.Errorf("expected attributes '%v', got '%v'", expected.AttributeIndices().AsRaw(), actual.AttributeIndices().AsRaw())) + } + + return errs +} + +func CompareProfileMappingSlice(expected, actual pprofile.MappingSlice) error { + var errs error + if expected.Len() != actual.Len() { + errs = multierr.Append(errs, fmt.Errorf("number of mappings doesn't match expected: %d, actual: %d", + expected.Len(), actual.Len())) + return errs + } + + numItems := expected.Len() + + matchingItems := make(map[pprofile.Mapping]pprofile.Mapping, numItems) + + var outOfOrderErrs error + for e := 0; e < numItems; e++ { + elr := expected.At(e) + var foundMatch bool + for a := 0; a < numItems; a++ { + alr := actual.At(a) + if _, ok := matchingItems[alr]; ok { + continue + } + if reflect.DeepEqual(elr.AttributeIndices().AsRaw(), alr.AttributeIndices().AsRaw()) { + foundMatch = true + matchingItems[alr] = elr + if e != a { + outOfOrderErrs = multierr.Append(outOfOrderErrs, + fmt.Errorf(`mappings are out of order: mapping "attributes: %v" expected at index %d, found at index %d`, + elr.AttributeIndices().AsRaw(), e, a)) + } + break + } + } + if !foundMatch { + errs = multierr.Append(errs, fmt.Errorf(`missing expected mapping "attributes: %v"`, elr.AttributeIndices().AsRaw())) + } + } + + for i := 0; i < numItems; i++ { + if _, ok := matchingItems[actual.At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf(`unexpected profile mapping "attributes: %v"`, + actual.At(i).AttributeIndices().AsRaw())) + } + } + + if errs != nil { + return errs + } + if outOfOrderErrs != nil { + return outOfOrderErrs + } + + for alr, elr := range matchingItems { + if !isMappingEqual(elr, alr) { + errs = multierr.Append(errs, fmt.Errorf(`mapping with "attributes: %v", does not match expected`, + alr.AttributeIndices().AsRaw())) + } + } + + return errs +} + +func isMappingEqual(expected, actual pprofile.Mapping) bool { + return expected.MemoryStart() == actual.MemoryStart() && + expected.MemoryLimit() == actual.MemoryLimit() && + expected.FileOffset() == actual.FileOffset() && + expected.FilenameStrindex() == actual.FilenameStrindex() && + reflect.DeepEqual(expected.AttributeIndices().AsRaw(), actual.AttributeIndices().AsRaw()) && + expected.HasFunctions() == actual.HasFunctions() && + expected.HasFilenames() == actual.HasFilenames() && + expected.HasLineNumbers() == actual.HasLineNumbers() && + expected.HasInlineFrames() == actual.HasInlineFrames() +} + +func CompareProfileFunctionSlice(expected, actual pprofile.FunctionSlice) error { + var errs error + if expected.Len() != actual.Len() { + errs = multierr.Append(errs, fmt.Errorf("number of functions doesn't match expected: %d, actual: %d", + expected.Len(), actual.Len())) + return errs + } + + numItems := expected.Len() + + matchingItems := make(map[pprofile.Function]pprofile.Function, numItems) + + var outOfOrderErrs error + for e := 0; e < numItems; e++ { + elr := expected.At(e) + var foundMatch bool + for a := 0; a < numItems; a++ { + alr := actual.At(a) + if _, ok := matchingItems[alr]; ok { + continue + } + if elr.NameStrindex() == alr.NameStrindex() { + foundMatch = true + matchingItems[alr] = elr + if e != a { + outOfOrderErrs = multierr.Append(outOfOrderErrs, + fmt.Errorf(`functions are out of order: function "name: %d" expected at index %d, found at index %d`, + elr.NameStrindex(), e, a)) + } + break + } + } + if !foundMatch { + errs = multierr.Append(errs, fmt.Errorf(`missing expected function "name: %d"`, elr.NameStrindex())) + } + } + + for i := 0; i < numItems; i++ { + if _, ok := matchingItems[actual.At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf(`unexpected profile function "name: %d"`, + actual.At(i).NameStrindex())) + } + } + + if errs != nil { + return errs + } + if outOfOrderErrs != nil { + return outOfOrderErrs + } + + for alr, elr := range matchingItems { + if !isFunctionEqual(elr, alr) { + errs = multierr.Append(errs, fmt.Errorf(`function with "name: %d" does not match expected`, alr.NameStrindex())) + } + } + + return errs +} + +func isFunctionEqual(expected, actual pprofile.Function) bool { + return expected.NameStrindex() == actual.NameStrindex() && + expected.SystemNameStrindex() == actual.SystemNameStrindex() && + expected.StartLine() == actual.StartLine() && + expected.FilenameStrindex() == actual.FilenameStrindex() +} + +func CompareProfileLocationSlice(expected, actual pprofile.LocationSlice) error { + var errs error + if expected.Len() != actual.Len() { + errs = multierr.Append(errs, fmt.Errorf("number of locations doesn't match expected: %d, actual: %d", + expected.Len(), actual.Len())) + return errs + } + + numItems := expected.Len() + + matchingItems := make(map[pprofile.Location]pprofile.Location, numItems) + + var outOfOrderErrs error + for e := 0; e < numItems; e++ { + elr := expected.At(e) + var foundMatch bool + for a := 0; a < numItems; a++ { + alr := actual.At(a) + if _, ok := matchingItems[alr]; ok { + continue + } + if reflect.DeepEqual(elr.AttributeIndices().AsRaw(), alr.AttributeIndices().AsRaw()) { + foundMatch = true + matchingItems[alr] = elr + if e != a { + outOfOrderErrs = multierr.Append(outOfOrderErrs, + fmt.Errorf(`locations are out of order: location "attributes: %v" expected at index %d, found at index %d`, + elr.AttributeIndices().AsRaw(), e, a)) + } + break + } + } + if !foundMatch { + errs = multierr.Append(errs, fmt.Errorf(`missing expected location "attributes: %v"`, elr.AttributeIndices().AsRaw())) + } + } + + for i := 0; i < numItems; i++ { + if _, ok := matchingItems[actual.At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf(`unexpected location "attributes: %v"`, + actual.At(i).AttributeIndices().AsRaw())) + } + } + + if errs != nil { + return errs + } + if outOfOrderErrs != nil { + return outOfOrderErrs + } + + for alr, elr := range matchingItems { + errPrefix := fmt.Sprintf(`location "attributes: %v"`, elr.AttributeIndices().AsRaw()) + errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareProfileLocation(elr, alr))) + } + + return errs +} + +func CompareProfileLocation(expected, actual pprofile.Location) error { + var errs error + + if expected.MappingIndex() != actual.MappingIndex() { + errs = multierr.Append(errs, fmt.Errorf("expected mappingIndex '%d', got '%d'", expected.MappingIndex(), actual.MappingIndex())) + } + + if expected.Address() != actual.Address() { + errs = multierr.Append(errs, fmt.Errorf("expected address '%d', got '%d'", expected.Address(), actual.Address())) + } + + if expected.IsFolded() != actual.IsFolded() { + errs = multierr.Append(errs, fmt.Errorf("expected isFolded '%v', got '%v'", expected.IsFolded(), actual.IsFolded())) + } + + if !reflect.DeepEqual(expected.AttributeIndices().AsRaw(), actual.AttributeIndices().AsRaw()) { + errs = multierr.Append(errs, fmt.Errorf("expected attributes '%v', got '%v'", expected.AttributeIndices().AsRaw(), actual.AttributeIndices().AsRaw())) + } + + errPrefix := fmt.Sprintf(`line of location with "attributes: %v"`, expected.AttributeIndices().AsRaw()) + errs = multierr.Append(errs, internal.AddErrPrefix(errPrefix, CompareProfileLineSlice(expected.Line(), actual.Line()))) + + return errs +} + +func CompareProfileLineSlice(expected, actual pprofile.LineSlice) error { + var errs error + if expected.Len() != actual.Len() { + errs = multierr.Append(errs, fmt.Errorf("number of lines doesn't match expected: %d, actual: %d", + expected.Len(), actual.Len())) + return errs + } + + numItems := expected.Len() + + matchingItems := make(map[pprofile.Line]pprofile.Line, numItems) + + var outOfOrderErrs error + for e := 0; e < numItems; e++ { + elr := expected.At(e) + var foundMatch bool + for a := 0; a < numItems; a++ { + alr := actual.At(a) + if _, ok := matchingItems[alr]; ok { + continue + } + if elr.FunctionIndex() == alr.FunctionIndex() { + foundMatch = true + matchingItems[alr] = elr + if e != a { + outOfOrderErrs = multierr.Append(outOfOrderErrs, + fmt.Errorf(`lines are out of order: line "functionIndex: %d" expected at index %d, found at index %d`, + elr.FunctionIndex(), e, a)) + } + break + } + } + if !foundMatch { + errs = multierr.Append(errs, fmt.Errorf(`missing expected line "functionIndex: %d"`, elr.FunctionIndex())) + } + } + + for i := 0; i < numItems; i++ { + if _, ok := matchingItems[actual.At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf(`unexpected profile line "functionIndex: %d"`, + actual.At(i).FunctionIndex())) + } + } + + if errs != nil { + return errs + } + if outOfOrderErrs != nil { + return outOfOrderErrs + } + + for alr, elr := range matchingItems { + if !isLineEqual(elr, alr) { + errs = multierr.Append(errs, fmt.Errorf(`line with "functionIndex: %d" does not match expected`, alr.FunctionIndex())) + } + } + + return errs +} + +func isLineEqual(expected, actual pprofile.Line) bool { + return expected.FunctionIndex() == actual.FunctionIndex() && + expected.Line() == actual.Line() && + expected.Column() == actual.Column() +} + +func CompareProfileAttributeUnitSlice(expected, actual pprofile.AttributeUnitSlice) error { + var errs error + if expected.Len() != actual.Len() { + errs = multierr.Append(errs, fmt.Errorf("number of attributeUnits doesn't match expected: %d, actual: %d", + expected.Len(), actual.Len())) + return errs + } + + numItems := expected.Len() + + matchingItems := make(map[pprofile.AttributeUnit]pprofile.AttributeUnit, numItems) + + var outOfOrderErrs error + for e := 0; e < numItems; e++ { + elr := expected.At(e) + var foundMatch bool + for a := 0; a < numItems; a++ { + alr := actual.At(a) + if _, ok := matchingItems[alr]; ok { + continue + } + if elr.AttributeKeyStrindex() == alr.AttributeKeyStrindex() && elr.UnitStrindex() == alr.UnitStrindex() { + foundMatch = true + matchingItems[alr] = elr + if e != a { + outOfOrderErrs = multierr.Append(outOfOrderErrs, + fmt.Errorf(`attributeUnits are out of order: attributeUnit "attributeKey: %d" expected at index %d, found at index %d`, + elr.AttributeKeyStrindex(), e, a)) + } + break + } + } + if !foundMatch { + errs = multierr.Append(errs, fmt.Errorf(`missing expected attributeUnit "attributeKey: %d"`, elr.AttributeKeyStrindex())) + } + } + + for i := 0; i < numItems; i++ { + if _, ok := matchingItems[actual.At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf(`unexpected profile attributeUnit "attributeKey: %d"`, + actual.At(i).AttributeKeyStrindex())) + } + } + + if errs != nil { + return errs + } + if outOfOrderErrs != nil { + return outOfOrderErrs + } + + return errs +} + +func CompareProfileLinkSlice(expected, actual pprofile.LinkSlice) error { + var errs error + if expected.Len() != actual.Len() { + errs = multierr.Append(errs, fmt.Errorf("number of links doesn't match expected: %d, actual: %d", + expected.Len(), actual.Len())) + return errs + } + + numItems := expected.Len() + + matchingItems := make(map[pprofile.Link]pprofile.Link, numItems) + + var outOfOrderErrs error + for e := 0; e < numItems; e++ { + elr := expected.At(e) + var foundMatch bool + for a := 0; a < numItems; a++ { + alr := actual.At(a) + if _, ok := matchingItems[alr]; ok { + continue + } + if elr.TraceID().String() == alr.TraceID().String() && elr.SpanID().String() == alr.SpanID().String() { + foundMatch = true + matchingItems[alr] = elr + if e != a { + outOfOrderErrs = multierr.Append(outOfOrderErrs, + fmt.Errorf(`links are out of order: link "spanId: %s, traceId: %s" expected at index %d, found at index %d`, + elr.SpanID().String(), elr.TraceID().String(), e, a)) + } + break + } + } + if !foundMatch { + errs = multierr.Append(errs, fmt.Errorf(`missing expected link "spanId: %s, traceId: %s"`, elr.SpanID().String(), elr.TraceID().String())) + } + } + + for i := 0; i < numItems; i++ { + if _, ok := matchingItems[actual.At(i)]; !ok { + errs = multierr.Append(errs, fmt.Errorf(`unexpected profile link "spanId: %s, traceId: %s"`, + actual.At(i).SpanID().String(), actual.At(i).TraceID().String())) + } + } + + if errs != nil { + return errs + } + if outOfOrderErrs != nil { + return outOfOrderErrs + } + + return errs +} diff --git a/pkg/pdatatest/pprofiletest/profiles_test.go b/pkg/pdatatest/pprofiletest/profiles_test.go new file mode 100644 index 000000000000..509434b41f6b --- /dev/null +++ b/pkg/pdatatest/pprofiletest/profiles_test.go @@ -0,0 +1,2000 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package pprofiletest + +import ( + "errors" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pprofile" + "go.uber.org/multierr" +) + +func TestCompareProfiles(t *testing.T) { + timestamp1 := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC) + timestamp2 := timestamp1.Add(5 * time.Second) + tcs := []struct { + name string + expected pprofile.Profiles + actual pprofile.Profiles + compareOptions []CompareProfilesOption + withoutOptions error + withOptions error + }{ + { + name: "empty", + expected: func() pprofile.Profiles { + p := pprofile.NewProfiles() + return p + }(), + actual: func() pprofile.Profiles { + p := pprofile.NewProfiles() + return p + }(), + }, + { + name: "equal", + expected: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty().Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetProfileID(pprofile.NewProfileIDEmpty()) + return p + }(), + actual: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty().Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetProfileID(pprofile.NewProfileIDEmpty()) + return p + }(), + }, + { + name: "resource order", + expected: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty().Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetProfileID(pprofile.NewProfileIDEmpty()) + rl2 := p.ResourceProfiles().AppendEmpty() + rl2.Resource().Attributes().PutStr("key2", "value2") + l2 := rl2.ScopeProfiles().AppendEmpty().Profiles().AppendEmpty() + l2.Attributes().PutStr("scope-attr2", "value2") + l2.SetProfileID(pprofile.NewProfileIDEmpty()) + return p + }(), + actual: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl2 := p.ResourceProfiles().AppendEmpty() + rl2.Resource().Attributes().PutStr("key2", "value2") + l2 := rl2.ScopeProfiles().AppendEmpty().Profiles().AppendEmpty() + l2.Attributes().PutStr("scope-attr2", "value2") + l2.SetProfileID(pprofile.NewProfileIDEmpty()) + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty().Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetProfileID(pprofile.NewProfileIDEmpty()) + return p + }(), + withoutOptions: multierr.Combine( + errors.New(`resources are out of order: resource "map[key1:value1]" expected at index 0, found at index 1`), + errors.New(`resources are out of order: resource "map[key2:value2]" expected at index 1, found at index 0`), + ), + compareOptions: []CompareProfilesOption{ + IgnoreResourceProfilesOrder(), + }, + }, + { + name: "resource masked attribute", + expected: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + return p + }(), + actual: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value2") + return p + }(), + withoutOptions: multierr.Combine( + errors.New(`missing expected resource: map[key1:value1]`), + errors.New(`unexpected resource: map[key1:value2]`), + ), + compareOptions: []CompareProfilesOption{ + IgnoreResourceAttributeValue("key1"), + }, + }, + { + name: "resource scope order", + expected: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty().Scope() + l.SetName("scope1") + l.Attributes().PutStr("scope-attr1", "value1") + l2 := rl.ScopeProfiles().AppendEmpty().Scope() + l2.Attributes().PutStr("scope-attr2", "value2") + l2.SetName("scope2") + return p + }(), + actual: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l2 := rl.ScopeProfiles().AppendEmpty().Scope() + l2.Attributes().PutStr("scope-attr2", "value2") + l2.SetName("scope2") + l := rl.ScopeProfiles().AppendEmpty().Scope() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetName("scope1") + return p + }(), + withoutOptions: errors.New(`resource "map[key1:value1]": scopes are out of order: scope scope1 expected at index 0, found at index 1; resource "map[key1:value1]": scopes are out of order: scope scope2 expected at index 1, found at index 0`), + compareOptions: []CompareProfilesOption{ + IgnoreScopeProfilesOrder(), + }, + }, + { + name: "mask scope attribute", + expected: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty().Scope() + l.SetName("scope1") + l.Attributes().PutStr("scope-attr1", "value1") + l2 := rl.ScopeProfiles().AppendEmpty().Scope() + l2.Attributes().PutStr("scope-attr2", "value2") + l2.SetName("scope2") + return p + }(), + actual: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty().Scope() + l.Attributes().PutStr("scope-attr1", "value12") + l.SetName("scope1") + l2 := rl.ScopeProfiles().AppendEmpty().Scope() + l2.Attributes().PutStr("scope-attr2", "value22") + l2.SetName("scope2") + return p + }(), + withoutOptions: errors.New(`resource "map[key1:value1]": scope "scope1": attributes don't match expected: map[scope-attr1:value1], actual: map[scope-attr1:value12]; resource "map[key1:value1]": scope "scope2": attributes don't match expected: map[scope-attr2:value2], actual: map[scope-attr2:value22]`), + compareOptions: []CompareProfilesOption{ + IgnoreScopeAttributeValue("scope-attr2"), + IgnoreScopeAttributeValue("scope-attr1"), + }, + }, + { + name: "ignore profile order", + expected: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty() + l.Scope().SetName("scope1") + pc := l.Profiles().AppendEmpty() + pc.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + pc.Attributes().PutStr("continer-attr1", "value1") + pc2 := l.Profiles().AppendEmpty() + pc2.SetProfileID(pprofile.ProfileID([]byte("profileid1111112"))) + pc2.Attributes().PutStr("continer-attr2", "value2") + return p + }(), + actual: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty() + l.Scope().SetName("scope1") + pc2 := l.Profiles().AppendEmpty() + pc2.SetProfileID(pprofile.ProfileID([]byte("profileid1111112"))) + pc2.Attributes().PutStr("continer-attr2", "value2") + pc := l.Profiles().AppendEmpty() + pc.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + pc.Attributes().PutStr("continer-attr1", "value1") + return p + }(), + withoutOptions: errors.New(`resource "map[key1:value1]": scope "scope1": profiles are out of order: profile "map[continer-attr1:value1]" expected at index 0, found at index 1; resource "map[key1:value1]": scope "scope1": profiles are out of order: profile "map[continer-attr2:value2]" expected at index 1, found at index 0`), + compareOptions: []CompareProfilesOption{ + IgnoreProfilesOrder(), + }, + }, + { + name: "ignore profile attribute value", + expected: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty() + l.Scope().SetName("scope1") + pc := l.Profiles().AppendEmpty() + pc.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + pc.Attributes().PutStr("container-attr1", "value1") + pc2 := l.Profiles().AppendEmpty() + pc2.SetProfileID(pprofile.ProfileID([]byte("profileid1111112"))) + pc2.Attributes().PutStr("container-attr2", "value2") + return p + }(), + actual: func() pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty() + l.Scope().SetName("scope1") + pc := l.Profiles().AppendEmpty() + pc.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + pc.Attributes().PutStr("container-attr1", "value3") + pc2 := l.Profiles().AppendEmpty() + pc2.SetProfileID(pprofile.ProfileID([]byte("profileid1111112"))) + pc2.Attributes().PutStr("container-attr2", "value4") + return p + }(), + withoutOptions: errors.New(`resource "map[key1:value1]": scope "scope1": missing expected profile: map[container-attr1:value1]; resource "map[key1:value1]": scope "scope1": missing expected profile: map[container-attr2:value2]; resource "map[key1:value1]": scope "scope1": unexpected profile: map[container-attr1:value3]; resource "map[key1:value1]": scope "scope1": unexpected profile: map[container-attr2:value4]`), + compareOptions: []CompareProfilesOption{ + IgnoreProfileAttributeValue("container-attr2"), + IgnoreProfileAttributeValue("container-attr1"), + }, + }, + { + name: "ignore profile timestamp values", + expected: func(timestamp time.Time) pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty() + l.Scope().SetName("scope1") + pc := l.Profiles().AppendEmpty() + pc.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + pc.Attributes().PutStr("container-attr1", "value1") + pc.SetStartTime(pcommon.NewTimestampFromTime(timestamp)) + pc.SetDuration(pcommon.NewTimestampFromTime(timestamp2)) + return p + }(timestamp1), + actual: func(timestamp time.Time) pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty() + l.Scope().SetName("scope1") + pc := l.Profiles().AppendEmpty() + pc.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + pc.Attributes().PutStr("container-attr1", "value1") + pc.SetStartTime(pcommon.NewTimestampFromTime(timestamp)) + pc.SetDuration(pcommon.NewTimestampFromTime(timestamp2)) + return p + }(timestamp2), + withoutOptions: errors.New(`resource "map[key1:value1]": scope "scope1": profile "map[container-attr1:value1]": start timestamp doesn't match expected: 1577836800000000000, actual: 1577836805000000000; resource "map[key1:value1]": scope "scope1": profile "map[container-attr1:value1]": time doesn't match expected: 1577836800000000000, actual: 1577836805000000000; resource "map[key1:value1]": scope "scope1": profile "map[container-attr1:value1]": startTime doesn't match expected: 1577836800000000000, actual: 1577836805000000000`), + compareOptions: []CompareProfilesOption{ + IgnoreProfileTimestampValues(), + }, + }, + { + name: "not equal without options", + expected: func(timestamp time.Time) pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty() + l.Scope().SetName("scope1") + pc := l.Profiles().AppendEmpty() + pc.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + pc.Attributes().PutStr("container-attr1", "value1") + pc.SetStartTime(pcommon.NewTimestampFromTime(timestamp)) + pc.SetDuration(pcommon.NewTimestampFromTime(timestamp.Add(5 * time.Second))) + return p + }(timestamp1), + actual: func(timestamp time.Time) pprofile.Profiles { + p := pprofile.NewProfiles() + rl := p.ResourceProfiles().AppendEmpty() + rl.Resource().Attributes().PutStr("key1", "value2") + l := rl.ScopeProfiles().AppendEmpty() + l.Scope().SetName("scope1") + pc := l.Profiles().AppendEmpty() + pc.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + pc.Attributes().PutStr("container-attr1", "value2") + pc.SetStartTime(pcommon.NewTimestampFromTime(timestamp)) + pc.SetDuration(pcommon.NewTimestampFromTime(timestamp.Add(5 * time.Second))) + return p + }(timestamp2), + withoutOptions: errors.New(`missing expected resource: map[key1:value1]; unexpected resource: map[key1:value2]`), + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + err := CompareProfiles(tc.expected, tc.actual) + if tc.withoutOptions == nil { + assert.NoError(t, err) + } else { + require.EqualError(t, err, tc.withoutOptions.Error()) + } + + if tc.compareOptions == nil { + return + } + + err = CompareProfiles(tc.expected, tc.actual, tc.compareOptions...) + if tc.withOptions == nil { + assert.NoError(t, err) + } else { + require.EqualError(t, err, tc.withOptions.Error()) + } + }) + } +} + +func TestCompareResourceProfiles(t *testing.T) { + tests := []struct { + name string + expected pprofile.ResourceProfiles + actual pprofile.ResourceProfiles + err error + }{ + { + name: "equal", + expected: func() pprofile.ResourceProfiles { + rl := pprofile.NewResourceProfiles() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty().Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetProfileID(pprofile.NewProfileIDEmpty()) + return rl + }(), + actual: func() pprofile.ResourceProfiles { + rl := pprofile.NewResourceProfiles() + rl.Resource().Attributes().PutStr("key1", "value1") + l := rl.ScopeProfiles().AppendEmpty().Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetProfileID(pprofile.NewProfileIDEmpty()) + return rl + }(), + }, + { + name: "resource-attributes-mismatch", + expected: func() pprofile.ResourceProfiles { + rl := pprofile.NewResourceProfiles() + rl.Resource().Attributes().PutStr("key1", "value1") + rl.Resource().Attributes().PutStr("key2", "value2") + return rl + }(), + actual: func() pprofile.ResourceProfiles { + rl := pprofile.NewResourceProfiles() + rl.Resource().Attributes().PutStr("key1", "value1") + return rl + }(), + err: errors.New("attributes don't match expected: map[key1:value1 key2:value2], actual: map[key1:value1]"), + }, + { + name: "resource-schema-url-mismatch", + expected: func() pprofile.ResourceProfiles { + rl := pprofile.NewResourceProfiles() + rl.SetSchemaUrl("schema-url") + return rl + }(), + actual: func() pprofile.ResourceProfiles { + rl := pprofile.NewResourceProfiles() + rl.SetSchemaUrl("schema-url-2") + return rl + }(), + err: errors.New("schema url doesn't match expected: schema-url, actual: schema-url-2"), + }, + { + name: "scope-profiles-number-mismatch", + expected: func() pprofile.ResourceProfiles { + rl := pprofile.NewResourceProfiles() + rl.ScopeProfiles().AppendEmpty() + rl.ScopeProfiles().AppendEmpty() + return rl + }(), + actual: func() pprofile.ResourceProfiles { + rl := pprofile.NewResourceProfiles() + rl.ScopeProfiles().AppendEmpty() + return rl + }(), + err: errors.New("number of scopes doesn't match expected: 2, actual: 1"), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareResourceProfiles(test.expected, test.actual)) + }) + } +} + +func TestCompareScopeProfiles(t *testing.T) { + tests := []struct { + name string + expected pprofile.ScopeProfiles + actual pprofile.ScopeProfiles + err error + }{ + { + name: "equal", + expected: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + sl.Scope().SetName("scope-name") + l := sl.Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetProfileID(pprofile.NewProfileIDEmpty()) + return sl + }(), + actual: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + sl.Scope().SetName("scope-name") + l := sl.Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetProfileID(pprofile.NewProfileIDEmpty()) + return sl + }(), + }, + { + name: "scope-name-mismatch", + expected: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + sl.Scope().SetName("scope-name") + return sl + }(), + actual: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + sl.Scope().SetName("scope-name-2") + return sl + }(), + err: errors.New("name doesn't match expected: scope-name, actual: scope-name-2"), + }, + { + name: "scope-version-mismatch", + expected: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + sl.Scope().SetVersion("scope-version") + return sl + }(), + actual: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + sl.Scope().SetVersion("scope-version-2") + return sl + }(), + err: errors.New("version doesn't match expected: scope-version, actual: scope-version-2"), + }, + { + name: "scope-attributes-mismatch", + expected: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + sl.Scope().Attributes().PutStr("scope-attr1", "value1") + sl.Scope().Attributes().PutStr("scope-attr2", "value2") + return sl + }(), + actual: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + sl.Scope().Attributes().PutStr("scope-attr1", "value1") + sl.Scope().SetDroppedAttributesCount(1) + return sl + }(), + err: multierr.Combine( + errors.New("attributes don't match expected: map[scope-attr1:value1 scope-attr2:value2], "+ + "actual: map[scope-attr1:value1]"), + errors.New("dropped attributes count doesn't match expected: 0, actual: 1"), + ), + }, + { + name: "scope-schema-url-mismatch", + expected: func() pprofile.ScopeProfiles { + rl := pprofile.NewScopeProfiles() + rl.SetSchemaUrl("schema-url") + return rl + }(), + actual: func() pprofile.ScopeProfiles { + rl := pprofile.NewScopeProfiles() + rl.SetSchemaUrl("schema-url-2") + return rl + }(), + err: errors.New("schema url doesn't match expected: schema-url, actual: schema-url-2"), + }, + { + name: "profiles-number-mismatch", + expected: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + sl.Profiles().AppendEmpty() + sl.Profiles().AppendEmpty() + return sl + }(), + actual: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + sl.Profiles().AppendEmpty() + return sl + }(), + err: errors.New("number of profiles doesn't match expected: 2, actual: 1"), + }, + { + name: "profile-records-order-mismatch", + expected: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + l := sl.Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + l = sl.Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr2", "value2") + l.SetProfileID(pprofile.ProfileID([]byte("profileid1111112"))) + return sl + }(), + actual: func() pprofile.ScopeProfiles { + sl := pprofile.NewScopeProfiles() + l := sl.Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr2", "value2") + l.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + l = sl.Profiles().AppendEmpty() + l.Attributes().PutStr("scope-attr1", "value1") + l.SetProfileID(pprofile.ProfileID([]byte("profileid1111113"))) + return sl + }(), + err: multierr.Combine( + errors.New(`profiles are out of order: profile "map[scope-attr1:value1]" expected at index 0, found at index 1`), + errors.New(`profiles are out of order: profile "map[scope-attr2:value2]" expected at index 1, found at index 0`), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareScopeProfiles(test.expected, test.actual)) + }) + } +} + +func TestCompareProfile(t *testing.T) { + tests := []struct { + name string + expected pprofile.Profile + actual pprofile.Profile + err error + }{ + { + name: "empty", + expected: func() pprofile.Profile { + l := pprofile.NewProfile() + return l + }(), + actual: func() pprofile.Profile { + l := pprofile.NewProfile() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.Profile { + l := pprofile.NewProfile() + l.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + l.SetDroppedAttributesCount(2) + l.SetDefaultSampleTypeStrindex(1) + l.Attributes().PutStr("key", "val") + l.SetPeriod(1) + s := l.SampleType().AppendEmpty() + s.SetTypeStrindex(1) + s.SetUnitStrindex(1) + a := l.AttributeUnits().AppendEmpty() + a.SetAttributeKeyStrindex(1) + a.SetUnitStrindex(1) + return l + }(), + actual: func() pprofile.Profile { + l := pprofile.NewProfile() + l.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + l.SetDroppedAttributesCount(2) + l.SetDefaultSampleTypeStrindex(1) + l.Attributes().PutStr("key", "val") + l.SetPeriod(1) + s := l.SampleType().AppendEmpty() + s.SetTypeStrindex(1) + s.SetUnitStrindex(1) + a := l.AttributeUnits().AppendEmpty() + a.SetAttributeKeyStrindex(1) + a.SetUnitStrindex(1) + return l + }(), + }, + { + name: "not equal", + expected: func() pprofile.Profile { + l := pprofile.NewProfile() + l.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + l.SetDroppedAttributesCount(2) + l.SetDefaultSampleTypeStrindex(1) + l.Attributes().PutStr("key", "val") + l.SetPeriod(1) + s := l.SampleType().AppendEmpty() + s.SetTypeStrindex(1) + s.SetUnitStrindex(1) + a := l.AttributeUnits().AppendEmpty() + a.SetAttributeKeyStrindex(1) + a.SetUnitStrindex(1) + return l + }(), + actual: func() pprofile.Profile { + l := pprofile.NewProfile() + l.SetProfileID(pprofile.ProfileID([]byte("profileid1111111"))) + l.SetDroppedAttributesCount(2) + l.SetDefaultSampleTypeStrindex(1) + l.Attributes().PutStr("key1", "val1") + l.SetPeriod(2) + s := l.SampleType().AppendEmpty() + s.SetTypeStrindex(2) + s.SetUnitStrindex(2) + a := l.AttributeUnits().AppendEmpty() + a.SetAttributeKeyStrindex(2) + a.SetUnitStrindex(2) + return l + }(), + err: multierr.Combine( + errors.New(`attributes don't match expected: map[key:val], actual: map[key1:val1]`), + errors.New(`period does not match expected '1', actual '2'`), + fmt.Errorf(`sampleType: %w`, fmt.Errorf(`missing expected valueType "unit: 1, type: 1, aggregationTemporality: 0"`)), + fmt.Errorf(`sampleType: %w`, fmt.Errorf(`unexpected valueType "unit: 2, type: 2, aggregationTemporality: 0"`)), + fmt.Errorf(`attributeUnits: %w`, fmt.Errorf(`missing expected attributeUnit "attributeKey: 1"`)), + fmt.Errorf(`attributeUnits: %w`, fmt.Errorf(`unexpected profile attributeUnit "attributeKey: 2"`)), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfile(test.expected, test.actual)) + }) + } +} + +func TestCompareProfileValueTypeSlice(t *testing.T) { + tests := []struct { + name string + expected pprofile.ValueTypeSlice + actual pprofile.ValueTypeSlice + err error + }{ + { + name: "empty", + expected: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + return l + }(), + actual: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + i1 := l.AppendEmpty() + i1.SetTypeStrindex(1) + i1.SetUnitStrindex(1) + i1.SetAggregationTemporality(1) + i2 := l.AppendEmpty() + i2.SetTypeStrindex(2) + i2.SetUnitStrindex(2) + i2.SetAggregationTemporality(1) + return l + }(), + actual: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + i1 := l.AppendEmpty() + i1.SetTypeStrindex(1) + i1.SetUnitStrindex(1) + i1.SetAggregationTemporality(1) + i2 := l.AppendEmpty() + i2.SetTypeStrindex(2) + i2.SetUnitStrindex(2) + i2.SetAggregationTemporality(1) + return l + }(), + }, + { + name: "equal wrong order", + expected: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + i1 := l.AppendEmpty() + i1.SetTypeStrindex(1) + i1.SetUnitStrindex(1) + i1.SetAggregationTemporality(1) + i2 := l.AppendEmpty() + i2.SetTypeStrindex(2) + i2.SetUnitStrindex(2) + i2.SetAggregationTemporality(1) + return l + }(), + actual: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + i2 := l.AppendEmpty() + i2.SetTypeStrindex(2) + i2.SetUnitStrindex(2) + i2.SetAggregationTemporality(1) + i1 := l.AppendEmpty() + i1.SetTypeStrindex(1) + i1.SetUnitStrindex(1) + i1.SetAggregationTemporality(1) + return l + }(), + err: multierr.Combine( + errors.New(`valueTypes are out of order: valueType "unit: 1, type: 1, aggregationTemporality: 1" expected at index 0, found at index 1`), + errors.New(`valueTypes are out of order: valueType "unit: 2, type: 2, aggregationTemporality: 1" expected at index 1, found at index 0`), + ), + }, + { + name: "wrong length", + expected: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + i1 := l.AppendEmpty() + i1.SetTypeStrindex(1) + i1.SetUnitStrindex(1) + i1.SetAggregationTemporality(1) + return l + }(), + actual: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + i1 := l.AppendEmpty() + i1.SetTypeStrindex(1) + i1.SetUnitStrindex(1) + i1.SetAggregationTemporality(1) + i2 := l.AppendEmpty() + i2.SetTypeStrindex(2) + i2.SetUnitStrindex(2) + i2.SetAggregationTemporality(1) + return l + }(), + err: multierr.Combine( + errors.New(`number of valueTypes doesn't match expected: 1, actual: 2`), + ), + }, + { + name: "not equal - does not match expected", + expected: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + i1 := l.AppendEmpty() + i1.SetTypeStrindex(1) + i1.SetUnitStrindex(1) + i1.SetAggregationTemporality(1) + i2 := l.AppendEmpty() + i2.SetTypeStrindex(2) + i2.SetUnitStrindex(2) + i2.SetAggregationTemporality(1) + return l + }(), + actual: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + i1 := l.AppendEmpty() + i1.SetTypeStrindex(1) + i1.SetUnitStrindex(1) + i1.SetAggregationTemporality(1) + i2 := l.AppendEmpty() + i2.SetTypeStrindex(2) + i2.SetUnitStrindex(2) + i2.SetAggregationTemporality(2) + return l + }(), + err: multierr.Combine( + errors.New(`expected valueType "unit: 2, type: 2, aggregationTemporality: 1",got "unit: 2, type: 2, aggregationTemporality: 2"`), + ), + }, + { + name: "not equal - missing", + expected: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + i1 := l.AppendEmpty() + i1.SetTypeStrindex(1) + i1.SetUnitStrindex(1) + i1.SetAggregationTemporality(1) + i2 := l.AppendEmpty() + i2.SetTypeStrindex(2) + i2.SetUnitStrindex(2) + i2.SetAggregationTemporality(1) + return l + }(), + actual: func() pprofile.ValueTypeSlice { + l := pprofile.NewValueTypeSlice() + i1 := l.AppendEmpty() + i1.SetTypeStrindex(1) + i1.SetUnitStrindex(1) + i1.SetAggregationTemporality(1) + i2 := l.AppendEmpty() + i2.SetTypeStrindex(3) + i2.SetUnitStrindex(3) + i2.SetAggregationTemporality(1) + return l + }(), + err: multierr.Combine( + errors.New(`missing expected valueType "unit: 2, type: 2, aggregationTemporality: 1"`), + errors.New(`unexpected valueType "unit: 3, type: 3, aggregationTemporality: 1"`), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfileValueTypeSlice(test.expected, test.actual)) + }) + } +} + +func TestCompareProfileSampleSlice(t *testing.T) { + tests := []struct { + name string + expected pprofile.SampleSlice + actual pprofile.SampleSlice + err error + }{ + { + name: "empty", + expected: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + return l + }(), + actual: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + i1 := l.AppendEmpty() + i1.SetLocationsLength(1) + i1.AttributeIndices().Append(1, 2) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetLocationsLength(2) + return l + }(), + actual: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + i1 := l.AppendEmpty() + i1.SetLocationsLength(1) + i1.AttributeIndices().Append(1, 2) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetLocationsLength(2) + return l + }(), + }, + { + name: "equal wrong order", + expected: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + i1 := l.AppendEmpty() + i1.SetLocationsLength(1) + i1.AttributeIndices().Append(1, 2) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetLocationsLength(2) + return l + }(), + actual: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetLocationsLength(2) + i1 := l.AppendEmpty() + i1.SetLocationsLength(1) + i1.AttributeIndices().Append(1, 2) + return l + }(), + err: multierr.Combine( + errors.New(`samples are out of order: sample "attributes: [1 2]" expected at index 0, found at index 1`), + errors.New(`samples are out of order: sample "attributes: [1 2 3]" expected at index 1, found at index 0`), + ), + }, + { + name: "wrong length", + expected: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + i1 := l.AppendEmpty() + i1.SetLocationsLength(1) + i1.AttributeIndices().Append(1, 2) + return l + }(), + actual: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + i1 := l.AppendEmpty() + i1.SetLocationsLength(1) + i1.AttributeIndices().Append(1, 2) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetLocationsLength(2) + return l + }(), + err: multierr.Combine( + errors.New(`number of samples doesn't match expected: 1, actual: 2`), + ), + }, + { + name: "not equal - does not match expected", + expected: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + i1 := l.AppendEmpty() + i1.SetLocationsLength(1) + i1.AttributeIndices().Append(1, 2) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetLocationsLength(2) + return l + }(), + actual: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + i1 := l.AppendEmpty() + i1.SetLocationsLength(1) + i1.AttributeIndices().Append(1, 2) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetLocationsLength(3) + return l + }(), + err: multierr.Combine( + fmt.Errorf(`sample "attributes: [1 2 3]": %w`, fmt.Errorf(`expected locationLenght '2', got '3'`)), + ), + }, + { + name: "not equal - missing", + expected: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + i1 := l.AppendEmpty() + i1.SetLocationsLength(1) + i1.AttributeIndices().Append(1, 2) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetLocationsLength(2) + return l + }(), + actual: func() pprofile.SampleSlice { + l := pprofile.NewSampleSlice() + i1 := l.AppendEmpty() + i1.SetLocationsLength(1) + i1.AttributeIndices().Append(1, 2) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3, 5) + i2.SetLocationsLength(3) + return l + }(), + err: multierr.Combine( + errors.New(`missing expected sample "attributes: [1 2 3]"`), + errors.New(`unexpected sample "attributes: [1 2 3 5]"`), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfileSampleSlice(test.expected, test.actual)) + }) + } +} + +func TestCompareProfileSample(t *testing.T) { + tests := []struct { + name string + expected pprofile.Sample + actual pprofile.Sample + err error + }{ + { + name: "empty", + expected: func() pprofile.Sample { + l := pprofile.NewSample() + return l + }(), + actual: func() pprofile.Sample { + l := pprofile.NewSample() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.Sample { + l := pprofile.NewSample() + l.SetLocationsStartIndex(1) + l.SetLocationsLength(1) + l.AttributeIndices().Append(1, 2) + return l + }(), + actual: func() pprofile.Sample { + l := pprofile.NewSample() + l.SetLocationsStartIndex(1) + l.SetLocationsLength(1) + l.AttributeIndices().Append(1, 2) + return l + }(), + }, + { + name: "not equal", + expected: func() pprofile.Sample { + l := pprofile.NewSample() + l.SetLocationsStartIndex(1) + l.SetLocationsLength(1) + l.AttributeIndices().Append(1, 2) + return l + }(), + actual: func() pprofile.Sample { + l := pprofile.NewSample() + l.SetLocationsStartIndex(2) + l.SetLocationsLength(3) + l.AttributeIndices().Append(1, 2, 3) + return l + }(), + err: multierr.Combine( + errors.New(`expected locationStartIndex '1', got '2'`), + errors.New(`expected locationLenght '1', got '3'`), + errors.New(`expected attributes '[1 2]', got '[1 2 3]'`), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfileSample(test.expected, test.actual)) + }) + } +} + +func TestCompareProfileMappingSlice(t *testing.T) { + tests := []struct { + name string + expected pprofile.MappingSlice + actual pprofile.MappingSlice + err error + }{ + { + name: "empty", + expected: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + return l + }(), + actual: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1) + i2.SetFilenameStrindex(2) + return l + }(), + actual: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1) + i2.SetFilenameStrindex(2) + return l + }(), + }, + { + name: "equal wrong order", + expected: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1) + i2.SetFilenameStrindex(2) + return l + }(), + actual: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1) + i2.SetFilenameStrindex(2) + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetFilenameStrindex(1) + return l + }(), + err: multierr.Combine( + errors.New(`mappings are out of order: mapping "attributes: [1 2]" expected at index 0, found at index 1`), + errors.New(`mappings are out of order: mapping "attributes: [1]" expected at index 1, found at index 0`), + ), + }, + { + name: "wrong length", + expected: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + i2 := l.AppendEmpty() + i2.SetFilenameStrindex(2) + return l + }(), + actual: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1) + i2.SetFilenameStrindex(2) + return l + }(), + err: multierr.Combine( + errors.New(`number of mappings doesn't match expected: 1, actual: 2`), + ), + }, + { + name: "not equal - does not match expected", + expected: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1) + i2.SetFilenameStrindex(2) + return l + }(), + actual: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1) + i2.SetFilenameStrindex(3) + return l + }(), + err: multierr.Combine( + errors.New(`mapping with "attributes: [1]", does not match expected`), + ), + }, + { + name: "not equal - missing", + expected: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1) + i2.SetFilenameStrindex(2) + return l + }(), + actual: func() pprofile.MappingSlice { + l := pprofile.NewMappingSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2) + i2.SetFilenameStrindex(2) + return l + }(), + err: multierr.Combine( + errors.New(`missing expected mapping "attributes: [1]"`), + errors.New(`unexpected profile mapping "attributes: [1 2]"`), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfileMappingSlice(test.expected, test.actual)) + }) + } +} + +func TestCompareProfileFunctionSlice(t *testing.T) { + tests := []struct { + name string + expected pprofile.FunctionSlice + actual pprofile.FunctionSlice + err error + }{ + { + name: "empty", + expected: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + return l + }(), + actual: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + i1 := l.AppendEmpty() + i1.SetNameStrindex(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.SetNameStrindex(2) + i2.SetFilenameStrindex(2) + return l + }(), + actual: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + i1 := l.AppendEmpty() + i1.SetNameStrindex(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.SetNameStrindex(2) + i2.SetFilenameStrindex(2) + return l + }(), + }, + { + name: "equal wrong order", + expected: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + i1 := l.AppendEmpty() + i1.SetNameStrindex(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.SetNameStrindex(2) + i2.SetFilenameStrindex(2) + return l + }(), + actual: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + i2 := l.AppendEmpty() + i2.SetNameStrindex(2) + i2.SetFilenameStrindex(2) + i1 := l.AppendEmpty() + i1.SetNameStrindex(1) + i1.SetFilenameStrindex(1) + return l + }(), + err: multierr.Combine( + errors.New(`functions are out of order: function "name: 1" expected at index 0, found at index 1`), + errors.New(`functions are out of order: function "name: 2" expected at index 1, found at index 0`), + ), + }, + { + name: "wrong length", + expected: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + i2 := l.AppendEmpty() + i2.SetNameStrindex(2) + i2.SetFilenameStrindex(2) + return l + }(), + actual: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + i1 := l.AppendEmpty() + i1.SetNameStrindex(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.SetNameStrindex(2) + i2.SetFilenameStrindex(2) + return l + }(), + err: multierr.Combine( + errors.New(`number of functions doesn't match expected: 1, actual: 2`), + ), + }, + { + name: "not equal - does not match expected", + expected: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + i1 := l.AppendEmpty() + i1.SetNameStrindex(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.SetNameStrindex(2) + i2.SetFilenameStrindex(2) + return l + }(), + actual: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + i1 := l.AppendEmpty() + i1.SetNameStrindex(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.SetNameStrindex(2) + i2.SetFilenameStrindex(3) + return l + }(), + err: multierr.Combine( + errors.New(`function with "name: 2" does not match expected`), + ), + }, + { + name: "not equal - missing", + expected: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + i1 := l.AppendEmpty() + i1.SetNameStrindex(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.SetNameStrindex(2) + i2.SetFilenameStrindex(2) + return l + }(), + actual: func() pprofile.FunctionSlice { + l := pprofile.NewFunctionSlice() + i1 := l.AppendEmpty() + i1.SetNameStrindex(1) + i1.SetFilenameStrindex(1) + i2 := l.AppendEmpty() + i2.SetNameStrindex(3) + i2.SetFilenameStrindex(3) + return l + }(), + err: multierr.Combine( + errors.New(`missing expected function "name: 2"`), + errors.New(`unexpected profile function "name: 3"`), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfileFunctionSlice(test.expected, test.actual)) + }) + } +} + +func TestCompareProfileLocationSlice(t *testing.T) { + tests := []struct { + name string + expected pprofile.LocationSlice + actual pprofile.LocationSlice + err error + }{ + { + name: "empty", + expected: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + return l + }(), + actual: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetMappingIndex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetMappingIndex(2) + return l + }(), + actual: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetMappingIndex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetMappingIndex(2) + return l + }(), + }, + { + name: "equal wrong order", + expected: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetMappingIndex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetMappingIndex(2) + return l + }(), + actual: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetMappingIndex(2) + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetMappingIndex(1) + return l + }(), + err: multierr.Combine( + errors.New(`locations are out of order: location "attributes: [1 2]" expected at index 0, found at index 1`), + errors.New(`locations are out of order: location "attributes: [1 2 3]" expected at index 1, found at index 0`), + ), + }, + { + name: "wrong length", + expected: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetMappingIndex(2) + return l + }(), + actual: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetMappingIndex(2) + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetMappingIndex(1) + return l + }(), + err: multierr.Combine( + errors.New(`number of locations doesn't match expected: 1, actual: 2`), + ), + }, + { + name: "not equal - does not match expected", + expected: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetMappingIndex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetMappingIndex(2) + return l + }(), + actual: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetMappingIndex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetMappingIndex(3) + return l + }(), + err: multierr.Combine( + fmt.Errorf(`location "attributes: [1 2 3]": %w`, fmt.Errorf(`expected mappingIndex '2', got '3'`)), + ), + }, + { + name: "not equal - missing", + expected: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetMappingIndex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3) + i2.SetMappingIndex(2) + return l + }(), + actual: func() pprofile.LocationSlice { + l := pprofile.NewLocationSlice() + i1 := l.AppendEmpty() + i1.AttributeIndices().Append(1, 2) + i1.SetMappingIndex(1) + i2 := l.AppendEmpty() + i2.AttributeIndices().Append(1, 2, 3, 5) + i2.SetMappingIndex(2) + return l + }(), + err: multierr.Combine( + errors.New(`missing expected location "attributes: [1 2 3]"`), + errors.New(`unexpected location "attributes: [1 2 3 5]"`), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfileLocationSlice(test.expected, test.actual)) + }) + } +} + +func TestCompareProfileLocation(t *testing.T) { + tests := []struct { + name string + expected pprofile.Location + actual pprofile.Location + err error + }{ + { + name: "empty", + expected: func() pprofile.Location { + l := pprofile.NewLocation() + return l + }(), + actual: func() pprofile.Location { + l := pprofile.NewLocation() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.Location { + l := pprofile.NewLocation() + l.SetAddress(2) + l.SetIsFolded(true) + l.SetMappingIndex(4) + l.AttributeIndices().Append(1, 2, 3) + l.Line().AppendEmpty().Line() + return l + }(), + actual: func() pprofile.Location { + l := pprofile.NewLocation() + l.SetAddress(2) + l.SetIsFolded(true) + l.SetMappingIndex(4) + l.AttributeIndices().Append(1, 2, 3) + l.Line().AppendEmpty() + return l + }(), + }, + { + name: "not equal", + expected: func() pprofile.Location { + l := pprofile.NewLocation() + l.SetAddress(3) + l.SetIsFolded(false) + l.SetMappingIndex(2) + l.AttributeIndices().Append(1, 2, 3, 4) + l.Line().AppendEmpty().SetFunctionIndex(3) + return l + }(), + actual: func() pprofile.Location { + l := pprofile.NewLocation() + l.SetAddress(2) + l.SetIsFolded(true) + l.SetMappingIndex(4) + l.AttributeIndices().Append(1, 2, 3) + l.Line().AppendEmpty().Line() + return l + }(), + err: multierr.Combine( + errors.New(`expected mappingIndex '2', got '4'`), + errors.New(`expected address '3', got '2'`), + errors.New(`expected isFolded 'false', got 'true'`), + errors.New(`expected attributes '[1 2 3 4]', got '[1 2 3]'`), + fmt.Errorf(`line of location with "attributes: [1 2 3 4]": %w`, fmt.Errorf(`missing expected line "functionIndex: 3"`)), + fmt.Errorf(`line of location with "attributes: [1 2 3 4]": %w`, fmt.Errorf(`unexpected profile line "functionIndex: 0"`)), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfileLocation(test.expected, test.actual)) + }) + } +} + +func TestCompareProfileLineSlice(t *testing.T) { + tests := []struct { + name string + expected pprofile.LineSlice + actual pprofile.LineSlice + err error + }{ + { + name: "empty", + expected: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + return l + }(), + actual: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + i1 := l.AppendEmpty() + i1.SetFunctionIndex(1) + i1.SetLine(3) + i1.SetColumn(3) + i2 := l.AppendEmpty() + i2.SetFunctionIndex(2) + i2.SetLine(4) + i2.SetColumn(4) + return l + }(), + actual: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + i1 := l.AppendEmpty() + i1.SetFunctionIndex(1) + i1.SetLine(3) + i1.SetColumn(3) + i2 := l.AppendEmpty() + i2.SetFunctionIndex(2) + i2.SetLine(4) + i2.SetColumn(4) + return l + }(), + }, + { + name: "equal wrong order", + expected: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + i1 := l.AppendEmpty() + i1.SetFunctionIndex(1) + i1.SetLine(3) + i1.SetColumn(3) + i2 := l.AppendEmpty() + i2.SetFunctionIndex(2) + i2.SetLine(4) + i2.SetColumn(4) + return l + }(), + actual: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + i2 := l.AppendEmpty() + i2.SetFunctionIndex(2) + i2.SetLine(4) + i2.SetColumn(4) + i1 := l.AppendEmpty() + i1.SetFunctionIndex(1) + i1.SetLine(3) + i1.SetColumn(3) + return l + }(), + err: multierr.Combine( + errors.New(`lines are out of order: line "functionIndex: 1" expected at index 0, found at index 1`), + errors.New(`lines are out of order: line "functionIndex: 2" expected at index 1, found at index 0`), + ), + }, + { + name: "wrong length", + expected: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + i1 := l.AppendEmpty() + i1.SetFunctionIndex(1) + i1.SetLine(3) + i1.SetColumn(3) + return l + }(), + actual: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + i1 := l.AppendEmpty() + i1.SetFunctionIndex(1) + i1.SetLine(3) + i1.SetColumn(3) + i2 := l.AppendEmpty() + i2.SetFunctionIndex(2) + i2.SetLine(4) + i2.SetColumn(4) + return l + }(), + err: multierr.Combine( + errors.New(`number of lines doesn't match expected: 1, actual: 2`), + ), + }, + { + name: "not equal - does not match expected", + expected: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + i1 := l.AppendEmpty() + i1.SetFunctionIndex(1) + i1.SetLine(3) + i1.SetColumn(3) + i2 := l.AppendEmpty() + i2.SetFunctionIndex(2) + i2.SetLine(4) + i2.SetColumn(4) + return l + }(), + actual: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + i1 := l.AppendEmpty() + i1.SetFunctionIndex(1) + i1.SetLine(3) + i1.SetColumn(3) + i2 := l.AppendEmpty() + i2.SetFunctionIndex(2) + i2.SetLine(5) + i2.SetColumn(5) + return l + }(), + err: multierr.Combine( + errors.New(`line with "functionIndex: 2" does not match expected`), + ), + }, + { + name: "not equal - missing", + expected: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + i1 := l.AppendEmpty() + i1.SetFunctionIndex(1) + i1.SetLine(3) + i1.SetColumn(3) + i2 := l.AppendEmpty() + i2.SetFunctionIndex(2) + i2.SetLine(4) + i2.SetColumn(4) + return l + }(), + actual: func() pprofile.LineSlice { + l := pprofile.NewLineSlice() + i1 := l.AppendEmpty() + i1.SetFunctionIndex(1) + i1.SetLine(3) + i1.SetColumn(3) + i2 := l.AppendEmpty() + i2.SetFunctionIndex(3) + i2.SetLine(5) + i2.SetColumn(5) + return l + }(), + err: multierr.Combine( + errors.New(`missing expected line "functionIndex: 2"`), + errors.New(`unexpected profile line "functionIndex: 3"`), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfileLineSlice(test.expected, test.actual)) + }) + } +} + +func TestCompareProfileAttributeUnitSlice(t *testing.T) { + tests := []struct { + name string + expected pprofile.AttributeUnitSlice + actual pprofile.AttributeUnitSlice + err error + }{ + { + name: "empty", + expected: func() pprofile.AttributeUnitSlice { + l := pprofile.NewAttributeUnitSlice() + return l + }(), + actual: func() pprofile.AttributeUnitSlice { + l := pprofile.NewAttributeUnitSlice() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.AttributeUnitSlice { + l := pprofile.NewAttributeUnitSlice() + i1 := l.AppendEmpty() + i1.SetAttributeKeyStrindex(2) + i1.SetUnitStrindex(3) + i2 := l.AppendEmpty() + i2.SetAttributeKeyStrindex(4) + i2.SetUnitStrindex(5) + return l + }(), + actual: func() pprofile.AttributeUnitSlice { + l := pprofile.NewAttributeUnitSlice() + i1 := l.AppendEmpty() + i1.SetAttributeKeyStrindex(2) + i1.SetUnitStrindex(3) + i2 := l.AppendEmpty() + i2.SetAttributeKeyStrindex(4) + i2.SetUnitStrindex(5) + return l + }(), + }, + { + name: "equal wrong order", + expected: func() pprofile.AttributeUnitSlice { + l := pprofile.NewAttributeUnitSlice() + i1 := l.AppendEmpty() + i1.SetAttributeKeyStrindex(2) + i1.SetUnitStrindex(3) + i2 := l.AppendEmpty() + i2.SetAttributeKeyStrindex(4) + i2.SetUnitStrindex(5) + return l + }(), + actual: func() pprofile.AttributeUnitSlice { + l := pprofile.NewAttributeUnitSlice() + i2 := l.AppendEmpty() + i2.SetAttributeKeyStrindex(4) + i2.SetUnitStrindex(5) + i1 := l.AppendEmpty() + i1.SetAttributeKeyStrindex(2) + i1.SetUnitStrindex(3) + return l + }(), + err: multierr.Combine( + errors.New(`attributeUnits are out of order: attributeUnit "attributeKey: 2" expected at index 0, found at index 1`), + errors.New(`attributeUnits are out of order: attributeUnit "attributeKey: 4" expected at index 1, found at index 0`), + ), + }, + { + name: "wrong length", + expected: func() pprofile.AttributeUnitSlice { + l := pprofile.NewAttributeUnitSlice() + i1 := l.AppendEmpty() + i1.SetAttributeKeyStrindex(2) + i1.SetUnitStrindex(3) + return l + }(), + actual: func() pprofile.AttributeUnitSlice { + l := pprofile.NewAttributeUnitSlice() + i1 := l.AppendEmpty() + i1.SetAttributeKeyStrindex(2) + i1.SetUnitStrindex(3) + i2 := l.AppendEmpty() + i2.SetAttributeKeyStrindex(4) + i2.SetUnitStrindex(5) + return l + }(), + err: multierr.Combine( + errors.New(`number of attributeUnits doesn't match expected: 1, actual: 2`), + ), + }, + { + name: "not equal", + expected: func() pprofile.AttributeUnitSlice { + l := pprofile.NewAttributeUnitSlice() + i1 := l.AppendEmpty() + i1.SetAttributeKeyStrindex(2) + i1.SetUnitStrindex(3) + i2 := l.AppendEmpty() + i2.SetAttributeKeyStrindex(4) + i2.SetUnitStrindex(5) + return l + }(), + actual: func() pprofile.AttributeUnitSlice { + l := pprofile.NewAttributeUnitSlice() + i1 := l.AppendEmpty() + i1.SetAttributeKeyStrindex(2) + i1.SetUnitStrindex(3) + i2 := l.AppendEmpty() + i2.SetAttributeKeyStrindex(6) + i2.SetUnitStrindex(7) + return l + }(), + err: multierr.Combine( + errors.New(`missing expected attributeUnit "attributeKey: 4"`), + errors.New(`unexpected profile attributeUnit "attributeKey: 6"`), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfileAttributeUnitSlice(test.expected, test.actual)) + }) + } +} + +func TestCompareProfileLinkSlice(t *testing.T) { + tests := []struct { + name string + expected pprofile.LinkSlice + actual pprofile.LinkSlice + err error + }{ + { + name: "empty", + expected: func() pprofile.LinkSlice { + l := pprofile.NewLinkSlice() + return l + }(), + actual: func() pprofile.LinkSlice { + l := pprofile.NewLinkSlice() + return l + }(), + }, + { + name: "equal", + expected: func() pprofile.LinkSlice { + l := pprofile.NewLinkSlice() + i1 := l.AppendEmpty() + i1.SetSpanID(pcommon.SpanID([]byte("spanidnn"))) + i1.SetTraceID(pcommon.TraceID([]byte("traceidnnnnnnnnn"))) + i2 := l.AppendEmpty() + i2.SetSpanID(pcommon.SpanID([]byte("spanidn2"))) + i2.SetTraceID(pcommon.TraceID([]byte("traceid2nnnnnnnn"))) + return l + }(), + actual: func() pprofile.LinkSlice { + l := pprofile.NewLinkSlice() + i1 := l.AppendEmpty() + i1.SetSpanID(pcommon.SpanID([]byte("spanidnn"))) + i1.SetTraceID(pcommon.TraceID([]byte("traceidnnnnnnnnn"))) + i2 := l.AppendEmpty() + i2.SetSpanID(pcommon.SpanID([]byte("spanidn2"))) + i2.SetTraceID(pcommon.TraceID([]byte("traceid2nnnnnnnn"))) + return l + }(), + }, + { + name: "equal wrong order", + expected: func() pprofile.LinkSlice { + l := pprofile.NewLinkSlice() + i1 := l.AppendEmpty() + i1.SetSpanID(pcommon.SpanID([]byte("spanidnn"))) + i1.SetTraceID(pcommon.TraceID([]byte("traceidnnnnnnnnn"))) + i2 := l.AppendEmpty() + i2.SetSpanID(pcommon.SpanID([]byte("spanidn2"))) + i2.SetTraceID(pcommon.TraceID([]byte("traceid2nnnnnnnn"))) + return l + }(), + actual: func() pprofile.LinkSlice { + l := pprofile.NewLinkSlice() + i2 := l.AppendEmpty() + i2.SetSpanID(pcommon.SpanID([]byte("spanidn2"))) + i2.SetTraceID(pcommon.TraceID([]byte("traceid2nnnnnnnn"))) + i1 := l.AppendEmpty() + i1.SetSpanID(pcommon.SpanID([]byte("spanidnn"))) + i1.SetTraceID(pcommon.TraceID([]byte("traceidnnnnnnnnn"))) + return l + }(), + err: multierr.Combine( + errors.New(`links are out of order: link "spanId: 7370616e69646e6e, traceId: 747261636569646e6e6e6e6e6e6e6e6e" expected at index 0, found at index 1`), + errors.New(`links are out of order: link "spanId: 7370616e69646e32, traceId: 74726163656964326e6e6e6e6e6e6e6e" expected at index 1, found at index 0`), + ), + }, + { + name: "wrong length", + expected: func() pprofile.LinkSlice { + l := pprofile.NewLinkSlice() + i1 := l.AppendEmpty() + i1.SetSpanID(pcommon.SpanID([]byte("spanidnn"))) + i1.SetTraceID(pcommon.TraceID([]byte("traceidnnnnnnnnn"))) + return l + }(), + actual: func() pprofile.LinkSlice { + l := pprofile.NewLinkSlice() + i2 := l.AppendEmpty() + i2.SetSpanID(pcommon.SpanID([]byte("spanidn2"))) + i2.SetTraceID(pcommon.TraceID([]byte("traceid2nnnnnnnn"))) + i1 := l.AppendEmpty() + i1.SetSpanID(pcommon.SpanID([]byte("spanidnn"))) + i1.SetTraceID(pcommon.TraceID([]byte("traceidnnnnnnnnn"))) + return l + }(), + err: multierr.Combine( + errors.New(`number of links doesn't match expected: 1, actual: 2`), + ), + }, + { + name: "not equal", + expected: func() pprofile.LinkSlice { + l := pprofile.NewLinkSlice() + i1 := l.AppendEmpty() + i1.SetSpanID(pcommon.SpanID([]byte("spanidnn"))) + i1.SetTraceID(pcommon.TraceID([]byte("traceidnnnnnnnnn"))) + i2 := l.AppendEmpty() + i2.SetSpanID(pcommon.SpanID([]byte("spanidn3"))) + i2.SetTraceID(pcommon.TraceID([]byte("traceid3nnnnnnnn"))) + return l + }(), + actual: func() pprofile.LinkSlice { + l := pprofile.NewLinkSlice() + i2 := l.AppendEmpty() + i2.SetSpanID(pcommon.SpanID([]byte("spanidn2"))) + i2.SetTraceID(pcommon.TraceID([]byte("traceid2nnnnnnnn"))) + i1 := l.AppendEmpty() + i1.SetSpanID(pcommon.SpanID([]byte("spanidnn"))) + i1.SetTraceID(pcommon.TraceID([]byte("traceidnnnnnnnnn"))) + return l + }(), + err: multierr.Combine( + errors.New(`missing expected link "spanId: 7370616e69646e33, traceId: 74726163656964336e6e6e6e6e6e6e6e"`), + errors.New(`unexpected profile link "spanId: 7370616e69646e32, traceId: 74726163656964326e6e6e6e6e6e6e6e"`), + ), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + require.Equal(t, test.err, CompareProfileLinkSlice(test.expected, test.actual)) + }) + } +}