Skip to content

Commit

Permalink
[pkg/pdatatest] Do not ignore timestamps in CompareMetrics, use optio…
Browse files Browse the repository at this point in the history
…ns (open-telemetry#17983)

It's pretty common to ignore timestamps when using CompareMetrics in tests for scrapers, but it's not obvious behavior for other use cases. We should provide options to explicitly ignore timestamps. That will make the Compare... functions provide clean comparison with not implicit behavior.
  • Loading branch information
dmitryax committed Jan 24, 2023
1 parent 5b2be2a commit 27e1370
Show file tree
Hide file tree
Showing 58 changed files with 415 additions and 190 deletions.
11 changes: 11 additions & 0 deletions .chloggen/pmetrictest-dont-ignore-timestamps.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 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: Do not ignore timestamps implicitly, provide explicit options for that.

# One or more tracking issues related to the change
issues: [17865]
3 changes: 2 additions & 1 deletion pkg/pdatatest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ func TestMetricsScraper(t *testing.T) {
expectedFile, err := readMetrics(filepath.Join("testdata", "expected.json"))
require.NoError(err)

require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics))
require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreStartTimestamp(),
pmetrictest.IgnoreTimestamp()))))
}
```

Expand Down
6 changes: 6 additions & 0 deletions pkg/pdatatest/pmetrictest/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,12 @@ func compareNumberDataPointSlices(expected, actual pmetric.NumberDataPointSlice)
// CompareNumberDataPoint compares each part of two given NumberDataPoints and returns
// an error if they don't match. The error describes what didn't match.
func CompareNumberDataPoint(expected, actual pmetric.NumberDataPoint) error {
if expected.StartTimestamp() != actual.StartTimestamp() {
return fmt.Errorf("metric datapoint StartTimestamp doesn't match expected: %d, actual: %d", expected.StartTimestamp(), actual.StartTimestamp())
}
if expected.Timestamp() != actual.Timestamp() {
return fmt.Errorf("metric datapoint Timestamp doesn't match expected: %d, actual: %d", expected.Timestamp(), actual.Timestamp())
}
if expected.ValueType() != actual.ValueType() {
return fmt.Errorf("metric datapoint types don't match: expected type: %s, actual type: %s", expected.ValueType(), actual.ValueType())
}
Expand Down
27 changes: 26 additions & 1 deletion pkg/pdatatest/pmetrictest/metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,10 +510,35 @@ func TestCompareMetrics(t *testing.T) {
),
},
},
{
name: "ignore-start-timestamp",
compareOptions: []CompareMetricsOption{
IgnoreStartTimestamp(),
},
withoutOptions: internal.Expectation{
Err: multierr.Combine(
errors.New("ResourceMetrics with attributes map[] does not match expected"),
errors.New(`ScopeMetrics with scope name "" does not match expected`),
errors.New("metric gauge.one does not match expected"),
errors.New("datapoint with attributes: map[], does not match expected"),
errors.New("metric datapoint StartTimestamp doesn't match expected: 0, actual: 11651379494838206464"),
),
Reason: "Timestamps are always ignored, so no error is expected.",
},
},
{
name: "ignore-timestamp",
compareOptions: []CompareMetricsOption{
IgnoreTimestamp(),
},
withoutOptions: internal.Expectation{
Err: nil,
Err: multierr.Combine(
errors.New("ResourceMetrics with attributes map[] does not match expected"),
errors.New(`ScopeMetrics with scope name "" does not match expected`),
errors.New("metric gauge.one does not match expected"),
errors.New("datapoint with attributes: map[], does not match expected"),
errors.New("metric datapoint Timestamp doesn't match expected: 0, actual: 11651379494838206464"),
),
Reason: "Timestamps are always ignored, so no error is expected.",
},
},
Expand Down
85 changes: 85 additions & 0 deletions pkg/pdatatest/pmetrictest/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package pmetrictest // import "github.com/open-telemetry/opentelemetry-collector
import (
"bytes"
"fmt"
"time"

"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/pmetric"
Expand Down Expand Up @@ -89,6 +90,90 @@ func maskDataPointSliceValues(dataPoints pmetric.NumberDataPointSlice) {
}
}

// IgnoreTimestamp is a CompareMetricsOption that clears Timestamp fields on all the data points.
func IgnoreTimestamp() CompareMetricsOption {
return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) {
now := pcommon.NewTimestampFromTime(time.Now())
maskTimestamp(expected, now)
maskTimestamp(actual, now)
})
}

func maskTimestamp(metrics pmetric.Metrics, ts pcommon.Timestamp) {
rms := metrics.ResourceMetrics()
for i := 0; i < rms.Len(); i++ {
for j := 0; j < rms.At(i).ScopeMetrics().Len(); j++ {
for k := 0; k < rms.At(i).ScopeMetrics().At(j).Metrics().Len(); k++ {
m := rms.At(i).ScopeMetrics().At(j).Metrics().At(k)
switch m.Type() {
case pmetric.MetricTypeGauge:
for l := 0; l < m.Gauge().DataPoints().Len(); l++ {
m.Gauge().DataPoints().At(l).SetTimestamp(ts)
}
case pmetric.MetricTypeSum:
for l := 0; l < m.Sum().DataPoints().Len(); l++ {
m.Sum().DataPoints().At(l).SetTimestamp(ts)
}
case pmetric.MetricTypeHistogram:
for l := 0; l < m.Histogram().DataPoints().Len(); l++ {
m.Histogram().DataPoints().At(l).SetTimestamp(ts)
}
case pmetric.MetricTypeExponentialHistogram:
for l := 0; l < m.ExponentialHistogram().DataPoints().Len(); l++ {
m.ExponentialHistogram().DataPoints().At(l).SetTimestamp(ts)
}
case pmetric.MetricTypeSummary:
for l := 0; l < m.Summary().DataPoints().Len(); l++ {
m.Summary().DataPoints().At(l).SetTimestamp(ts)
}
}
}
}
}
}

// IgnoreStartTimestamp is a CompareMetricsOption that clears StartTimestamp fields on all the data points.
func IgnoreStartTimestamp() CompareMetricsOption {
return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) {
now := pcommon.NewTimestampFromTime(time.Now())
maskStartTimestamp(expected, now)
maskStartTimestamp(actual, now)
})
}

func maskStartTimestamp(metrics pmetric.Metrics, ts pcommon.Timestamp) {
rms := metrics.ResourceMetrics()
for i := 0; i < rms.Len(); i++ {
for j := 0; j < rms.At(i).ScopeMetrics().Len(); j++ {
for k := 0; k < rms.At(i).ScopeMetrics().At(j).Metrics().Len(); k++ {
m := rms.At(i).ScopeMetrics().At(j).Metrics().At(k)
switch m.Type() {
case pmetric.MetricTypeGauge:
for l := 0; l < m.Gauge().DataPoints().Len(); l++ {
m.Gauge().DataPoints().At(l).SetStartTimestamp(ts)
}
case pmetric.MetricTypeSum:
for l := 0; l < m.Sum().DataPoints().Len(); l++ {
m.Sum().DataPoints().At(l).SetStartTimestamp(ts)
}
case pmetric.MetricTypeHistogram:
for l := 0; l < m.Histogram().DataPoints().Len(); l++ {
m.Histogram().DataPoints().At(l).SetStartTimestamp(ts)
}
case pmetric.MetricTypeExponentialHistogram:
for l := 0; l < m.ExponentialHistogram().DataPoints().Len(); l++ {
m.ExponentialHistogram().DataPoints().At(l).SetStartTimestamp(ts)
}
case pmetric.MetricTypeSummary:
for l := 0; l < m.Summary().DataPoints().Len(); l++ {
m.Summary().DataPoints().At(l).SetStartTimestamp(ts)
}
}
}
}
}
}

// IgnoreMetricAttributeValue is a CompareMetricsOption that clears value of the metric attribute.
func IgnoreMetricAttributeValue(attributeName string, metricNames ...string) CompareMetricsOption {
return compareMetricsOptionFunc(func(expected, actual pmetric.Metrics) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"resourceMetrics": [
{
"scopeMetrics": [
{
"metrics": [
{
"name": "gauge.one",
"gauge": {
"dataPoints": [
{
"asDouble": 123.456,
"startTimeUnixNano": "11651379494838206464"
}
]
}
},
{
"name": "sum.one",
"sum": {
"dataPoints": [
{
"asInt": 123,
"startTimeUnixNano": "11651379494838206464"
}
]
}
}
]
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"resourceMetrics": [
{
"scopeMetrics": [
{
"metrics": [
{
"name": "gauge.one",
"gauge": {
"dataPoints": [
{
"asDouble": 123.456,
"startTimeUnixNano": "0"
}
]
}
},
{
"name": "sum.one",
"sum": {
"dataPoints": [
{
"asInt": 123,
"startTimeUnixNano": "0"
}
]
}
}
]
}
]
}
]
}
4 changes: 2 additions & 2 deletions receiver/aerospikereceiver/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ func TestAerospikeIntegration(t *testing.T) {

require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreMetricValues(),
pmetrictest.IgnoreResourceAttributeValue("aerospike.node.name"),
pmetrictest.IgnoreMetricDataPointsOrder()))
pmetrictest.IgnoreMetricDataPointsOrder(), pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp()))

// now do a run in cluster mode
cfg.CollectClusterMetrics = true
Expand All @@ -348,6 +348,6 @@ func TestAerospikeIntegration(t *testing.T) {

require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreMetricValues(),
pmetrictest.IgnoreResourceAttributeValue("aerospike.node.name"),
pmetrictest.IgnoreMetricDataPointsOrder()))
pmetrictest.IgnoreMetricDataPointsOrder(), pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp()))

}
2 changes: 1 addition & 1 deletion receiver/aerospikereceiver/scraper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func TestScrape_CollectClusterMetrics(t *testing.T) {

expectedMetrics := expectedMB.Emit()
require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreResourceMetricsOrder(),
pmetrictest.IgnoreMetricDataPointsOrder()))
pmetrictest.IgnoreMetricDataPointsOrder(), pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp()))

require.NoError(t, receiver.shutdown(context.Background()))

Expand Down
2 changes: 1 addition & 1 deletion receiver/apachereceiver/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func TestApacheIntegration(t *testing.T) {
require.NoError(t, err)

require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreMetricValues(),
pmetrictest.IgnoreMetricDataPointsOrder()))
pmetrictest.IgnoreMetricDataPointsOrder(), pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp()))
}

func getContainer(t *testing.T, req testcontainers.ContainerRequest) testcontainers.Container {
Expand Down
2 changes: 1 addition & 1 deletion receiver/apachereceiver/scraper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func TestScraper(t *testing.T) {

// The port is random, so we shouldn't check if this value matches.
require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics,
pmetrictest.IgnoreMetricDataPointsOrder()))
pmetrictest.IgnoreMetricDataPointsOrder(), pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp()))
}

func TestScraperFailedStart(t *testing.T) {
Expand Down
4 changes: 3 additions & 1 deletion receiver/bigipreceiver/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ func TestBigIpIntegration(t *testing.T) {
expectedMetrics, err := golden.ReadMetrics(expectedFile)
require.NoError(t, err)

require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreResourceMetricsOrder(), pmetrictest.IgnoreMetricValues()))
require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics,
pmetrictest.IgnoreResourceMetricsOrder(), pmetrictest.IgnoreMetricValues(),
pmetrictest.IgnoreStartTimestamp(), pmetrictest.IgnoreTimestamp()))
}

const (
Expand Down
4 changes: 3 additions & 1 deletion receiver/bigipreceiver/scraper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,9 @@ func TestScaperScrape(t *testing.T) {

expectedMetrics := tc.expectedMetricGen(t)

err = pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreResourceMetricsOrder())
err = pmetrictest.CompareMetrics(expectedMetrics, actualMetrics,
pmetrictest.IgnoreResourceMetricsOrder(), pmetrictest.IgnoreStartTimestamp(),
pmetrictest.IgnoreTimestamp())
require.NoError(t, err)
})
}
Expand Down
Loading

0 comments on commit 27e1370

Please sign in to comment.