-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Use export.Aggregation instead of internal * Return an export.Aggregation instead of a slice * Use attribute Sets instead of KeyValues for export data Attribute Sets have stronger guarantees about the uniqueness of their keys and more functionality. We already ensure attributes are stored as Sets by the aggregator which will produce these data types. Instead of converting to a KeyValue slice, keep the data as a Set. Any user of the data can always call the ToSlice method to use the data as a slice of KeyValues. * Add export data type comparison testing API * Add Aggregation and Value comparison funcs * Move export testing to own pkg * Move exporttest to metricdatatest * Add licenses headers to files missing them * Use metricdata instead of export Fix merge of new_sdk/main * Rename exporttest pkg to metricdatatest * Fix spelling errors * Fix lint issues * Use testing pkg to error directly Include Helper() method calls to correct the call-stack. * Fix CompareAggregations Set equal to true by default * Generalize assertions and unexport equal checks * Abstract assert tests * Rename all exp var to r * Test AssertAggregationsEqual * Comment why Value and Aggregation are separate * Test AssertValuesEqual * Revert changes to metricdata/temporality.go * Expand pkg doc sentence * Add license header to assertion.go * Update assertion docs * Consolidate comparisons funcs into one file * Consolidate and fix docs * Consolidate assertion.go * Consolidate comparisons.go * make lint * Test with relatively static times * Update sdk/metric/metricdata/metricdatatest/comparisons.go Co-authored-by: Aaron Clawson <[email protected]> * Drop equal return from comparison funcs * Refactor AssertEqual * Remove reasN from testDatatype func params * Consolidate AssertEqual type conversions * Fix assertion error message * Add assertion failure tests * Remove unneeded strings join * Make comment include a possessive Co-authored-by: Aaron Clawson <[email protected]>
- Loading branch information
1 parent
98f88ca
commit 1dbe1fc
Showing
4 changed files
with
776 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
//go:build go1.18 | ||
// +build go1.18 | ||
|
||
// Package metricdatatest provides testing functionality for use with the | ||
// metricdata package. | ||
package metricdatatest // import "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"go.opentelemetry.io/otel/sdk/metric/metricdata" | ||
) | ||
|
||
// Datatypes are the concrete data-types the metricdata package provides. | ||
type Datatypes interface { | ||
metricdata.DataPoint | metricdata.Float64 | metricdata.Gauge | metricdata.Histogram | metricdata.HistogramDataPoint | metricdata.Int64 | metricdata.Metrics | metricdata.ResourceMetrics | metricdata.ScopeMetrics | metricdata.Sum | ||
|
||
// Interface types are not allowed in union types, therefore the | ||
// Aggregation and Value type from metricdata are not included here. | ||
} | ||
|
||
// AssertEqual asserts that the two concrete data-types from the metricdata | ||
// package are equal. | ||
func AssertEqual[T Datatypes](t *testing.T, expected, actual T) bool { | ||
t.Helper() | ||
|
||
// Generic types cannot be type asserted. Use an interface instead. | ||
aIface := interface{}(actual) | ||
|
||
var r []string | ||
switch e := interface{}(expected).(type) { | ||
case metricdata.DataPoint: | ||
r = equalDataPoints(e, aIface.(metricdata.DataPoint)) | ||
case metricdata.Float64: | ||
r = equalFloat64(e, aIface.(metricdata.Float64)) | ||
case metricdata.Gauge: | ||
r = equalGauges(e, aIface.(metricdata.Gauge)) | ||
case metricdata.Histogram: | ||
r = equalHistograms(e, aIface.(metricdata.Histogram)) | ||
case metricdata.HistogramDataPoint: | ||
r = equalHistogramDataPoints(e, aIface.(metricdata.HistogramDataPoint)) | ||
case metricdata.Int64: | ||
r = equalInt64(e, aIface.(metricdata.Int64)) | ||
case metricdata.Metrics: | ||
r = equalMetrics(e, aIface.(metricdata.Metrics)) | ||
case metricdata.ResourceMetrics: | ||
r = equalResourceMetrics(e, aIface.(metricdata.ResourceMetrics)) | ||
case metricdata.ScopeMetrics: | ||
r = equalScopeMetrics(e, aIface.(metricdata.ScopeMetrics)) | ||
case metricdata.Sum: | ||
r = equalSums(e, aIface.(metricdata.Sum)) | ||
default: | ||
// We control all types passed to this, panic to signal developers | ||
// early they changed things in an incompatible way. | ||
panic(fmt.Sprintf("unknown types: %T", expected)) | ||
} | ||
|
||
if len(r) > 0 { | ||
t.Error(r) | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
// AssertAggregationsEqual asserts that two Aggregations are equal. | ||
func AssertAggregationsEqual(t *testing.T, expected, actual metricdata.Aggregation) bool { | ||
t.Helper() | ||
if r := equalAggregations(expected, actual); len(r) > 0 { | ||
t.Error(r) | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
// AssertValuesEqual asserts that two Values are equal. | ||
func AssertValuesEqual(t *testing.T, expected, actual metricdata.Value) bool { | ||
t.Helper() | ||
if r := equalValues(expected, actual); len(r) > 0 { | ||
t.Error(r) | ||
return false | ||
} | ||
return true | ||
} |
63 changes: 63 additions & 0 deletions
63
sdk/metric/metricdata/metricdatatest/assertion_fail_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
//go:build go1.18 && tests_fail | ||
// +build go1.18,tests_fail | ||
|
||
package metricdatatest // import "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
// These tests are used to develop the failure messages of this package's | ||
// assertions. They can be run with the following. | ||
// | ||
// go test -tags tests_fail ./... | ||
|
||
func testFailDatatype[T Datatypes](a, b T) func(*testing.T) { | ||
return func(t *testing.T) { | ||
AssertEqual(t, a, b) | ||
} | ||
} | ||
|
||
func TestFailAssertEqual(t *testing.T) { | ||
t.Run("ResourceMetrics", testFailDatatype(resourceMetricsA, resourceMetricsB)) | ||
t.Run("ScopeMetrics", testFailDatatype(scopeMetricsA, scopeMetricsB)) | ||
t.Run("Metrics", testFailDatatype(metricsA, metricsB)) | ||
t.Run("Histogram", testFailDatatype(histogramA, histogramB)) | ||
t.Run("Sum", testFailDatatype(sumA, sumB)) | ||
t.Run("Gauge", testFailDatatype(gaugeA, gaugeB)) | ||
t.Run("HistogramDataPoint", testFailDatatype(histogramDataPointA, histogramDataPointB)) | ||
t.Run("DataPoint", testFailDatatype(dataPointsA, dataPointsB)) | ||
t.Run("Int64", testFailDatatype(int64A, int64B)) | ||
t.Run("Float64", testFailDatatype(float64A, float64B)) | ||
} | ||
|
||
func TestFailAssertAggregationsEqual(t *testing.T) { | ||
AssertAggregationsEqual(t, sumA, nil) | ||
AssertAggregationsEqual(t, sumA, gaugeA) | ||
AssertAggregationsEqual(t, unknownAggregation{}, unknownAggregation{}) | ||
AssertAggregationsEqual(t, sumA, sumB) | ||
AssertAggregationsEqual(t, gaugeA, gaugeB) | ||
AssertAggregationsEqual(t, histogramA, histogramB) | ||
} | ||
|
||
func TestFailAssertValuesEqual(t *testing.T) { | ||
AssertValuesEqual(t, int64A, nil) | ||
AssertValuesEqual(t, int64A, float64A) | ||
AssertValuesEqual(t, unknownValue{}, unknownValue{}) | ||
AssertValuesEqual(t, int64A, int64B) | ||
AssertValuesEqual(t, float64A, float64B) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
//go:build go1.18 | ||
// +build go1.18 | ||
|
||
package metricdatatest // import "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
"go.opentelemetry.io/otel/metric/unit" | ||
"go.opentelemetry.io/otel/sdk/instrumentation" | ||
"go.opentelemetry.io/otel/sdk/metric/metricdata" | ||
"go.opentelemetry.io/otel/sdk/resource" | ||
) | ||
|
||
var ( | ||
attrA = attribute.NewSet(attribute.Bool("A", true)) | ||
attrB = attribute.NewSet(attribute.Bool("B", true)) | ||
|
||
float64A = metricdata.Float64(-1.0) | ||
float64B = metricdata.Float64(2.0) | ||
|
||
int64A = metricdata.Int64(-1) | ||
int64B = metricdata.Int64(2) | ||
|
||
startA = time.Now() | ||
startB = startA.Add(time.Millisecond) | ||
endA = startA.Add(time.Second) | ||
endB = startB.Add(time.Second) | ||
|
||
dataPointsA = metricdata.DataPoint{ | ||
Attributes: attrA, | ||
StartTime: startA, | ||
Time: endA, | ||
Value: int64A, | ||
} | ||
dataPointsB = metricdata.DataPoint{ | ||
Attributes: attrB, | ||
StartTime: startB, | ||
Time: endB, | ||
Value: float64B, | ||
} | ||
|
||
max, min = 99.0, 3. | ||
histogramDataPointA = metricdata.HistogramDataPoint{ | ||
Attributes: attrA, | ||
StartTime: startA, | ||
Time: endA, | ||
Count: 2, | ||
Bounds: []float64{0, 10}, | ||
BucketCounts: []uint64{1, 1}, | ||
Sum: 2, | ||
} | ||
histogramDataPointB = metricdata.HistogramDataPoint{ | ||
Attributes: attrB, | ||
StartTime: startB, | ||
Time: endB, | ||
Count: 3, | ||
Bounds: []float64{0, 10, 100}, | ||
BucketCounts: []uint64{1, 1, 1}, | ||
Max: &max, | ||
Min: &min, | ||
Sum: 3, | ||
} | ||
|
||
gaugeA = metricdata.Gauge{DataPoints: []metricdata.DataPoint{dataPointsA}} | ||
gaugeB = metricdata.Gauge{DataPoints: []metricdata.DataPoint{dataPointsB}} | ||
|
||
sumA = metricdata.Sum{ | ||
Temporality: metricdata.CumulativeTemporality, | ||
IsMonotonic: true, | ||
DataPoints: []metricdata.DataPoint{dataPointsA}, | ||
} | ||
sumB = metricdata.Sum{ | ||
Temporality: metricdata.DeltaTemporality, | ||
IsMonotonic: false, | ||
DataPoints: []metricdata.DataPoint{dataPointsB}, | ||
} | ||
|
||
histogramA = metricdata.Histogram{ | ||
Temporality: metricdata.CumulativeTemporality, | ||
DataPoints: []metricdata.HistogramDataPoint{histogramDataPointA}, | ||
} | ||
histogramB = metricdata.Histogram{ | ||
Temporality: metricdata.DeltaTemporality, | ||
DataPoints: []metricdata.HistogramDataPoint{histogramDataPointB}, | ||
} | ||
|
||
metricsA = metricdata.Metrics{ | ||
Name: "A", | ||
Description: "A desc", | ||
Unit: unit.Dimensionless, | ||
Data: sumA, | ||
} | ||
metricsB = metricdata.Metrics{ | ||
Name: "B", | ||
Description: "B desc", | ||
Unit: unit.Bytes, | ||
Data: gaugeB, | ||
} | ||
|
||
scopeMetricsA = metricdata.ScopeMetrics{ | ||
Scope: instrumentation.Scope{Name: "A"}, | ||
Metrics: []metricdata.Metrics{metricsA}, | ||
} | ||
scopeMetricsB = metricdata.ScopeMetrics{ | ||
Scope: instrumentation.Scope{Name: "B"}, | ||
Metrics: []metricdata.Metrics{metricsB}, | ||
} | ||
|
||
resourceMetricsA = metricdata.ResourceMetrics{ | ||
Resource: resource.NewSchemaless(attribute.String("resource", "A")), | ||
ScopeMetrics: []metricdata.ScopeMetrics{scopeMetricsA}, | ||
} | ||
resourceMetricsB = metricdata.ResourceMetrics{ | ||
Resource: resource.NewSchemaless(attribute.String("resource", "B")), | ||
ScopeMetrics: []metricdata.ScopeMetrics{scopeMetricsB}, | ||
} | ||
) | ||
|
||
type equalFunc[T Datatypes] func(T, T) []string | ||
|
||
func testDatatype[T Datatypes](a, b T, f equalFunc[T]) func(*testing.T) { | ||
return func(t *testing.T) { | ||
AssertEqual(t, a, a) | ||
AssertEqual(t, b, b) | ||
|
||
r := f(a, b) | ||
assert.Greaterf(t, len(r), 0, "%v == %v", a, b) | ||
} | ||
} | ||
|
||
func TestAssertEqual(t *testing.T) { | ||
t.Run("ResourceMetrics", testDatatype(resourceMetricsA, resourceMetricsB, equalResourceMetrics)) | ||
t.Run("ScopeMetrics", testDatatype(scopeMetricsA, scopeMetricsB, equalScopeMetrics)) | ||
t.Run("Metrics", testDatatype(metricsA, metricsB, equalMetrics)) | ||
t.Run("Histogram", testDatatype(histogramA, histogramB, equalHistograms)) | ||
t.Run("Sum", testDatatype(sumA, sumB, equalSums)) | ||
t.Run("Gauge", testDatatype(gaugeA, gaugeB, equalGauges)) | ||
t.Run("HistogramDataPoint", testDatatype(histogramDataPointA, histogramDataPointB, equalHistogramDataPoints)) | ||
t.Run("DataPoint", testDatatype(dataPointsA, dataPointsB, equalDataPoints)) | ||
t.Run("Int64", testDatatype(int64A, int64B, equalInt64)) | ||
t.Run("Float64", testDatatype(float64A, float64B, equalFloat64)) | ||
} | ||
|
||
type unknownAggregation struct { | ||
metricdata.Aggregation | ||
} | ||
|
||
func TestAssertAggregationsEqual(t *testing.T) { | ||
AssertAggregationsEqual(t, nil, nil) | ||
AssertAggregationsEqual(t, sumA, sumA) | ||
AssertAggregationsEqual(t, gaugeA, gaugeA) | ||
AssertAggregationsEqual(t, histogramA, histogramA) | ||
|
||
r := equalAggregations(sumA, nil) | ||
assert.Len(t, r, 1, "should return nil comparison mismatch only") | ||
|
||
r = equalAggregations(sumA, gaugeA) | ||
assert.Len(t, r, 1, "should return with type mismatch only") | ||
|
||
r = equalAggregations(unknownAggregation{}, unknownAggregation{}) | ||
assert.Len(t, r, 1, "should return with unknown aggregation only") | ||
|
||
r = equalAggregations(sumA, sumB) | ||
assert.Greaterf(t, len(r), 0, "%v == %v", sumA, sumB) | ||
|
||
r = equalAggregations(gaugeA, gaugeB) | ||
assert.Greaterf(t, len(r), 0, "%v == %v", gaugeA, gaugeB) | ||
|
||
r = equalAggregations(histogramA, histogramB) | ||
assert.Greaterf(t, len(r), 0, "%v == %v", histogramA, histogramB) | ||
} | ||
|
||
type unknownValue struct { | ||
metricdata.Value | ||
} | ||
|
||
func TestAssertValuesEqual(t *testing.T) { | ||
AssertValuesEqual(t, nil, nil) | ||
AssertValuesEqual(t, int64A, int64A) | ||
AssertValuesEqual(t, float64A, float64A) | ||
|
||
r := equalValues(int64A, nil) | ||
assert.Len(t, r, 1, "should return nil comparison mismatch only") | ||
|
||
r = equalValues(int64A, float64A) | ||
assert.Len(t, r, 1, "should return with type mismatch only") | ||
|
||
r = equalValues(unknownValue{}, unknownValue{}) | ||
assert.Len(t, r, 1, "should return with unknown value only") | ||
|
||
r = equalValues(int64A, int64B) | ||
assert.Greaterf(t, len(r), 0, "%v == %v", int64A, int64B) | ||
|
||
r = equalValues(float64A, float64B) | ||
assert.Greaterf(t, len(r), 0, "%v == %v", float64A, float64B) | ||
} |
Oops, something went wrong.