Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metricdatatest package #3025

Merged
merged 41 commits into from
Jul 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
47c57bc
Use export.Aggregation instead of internal
MrAlias Jul 11, 2022
9e78415
Return an export.Aggregation instead of a slice
MrAlias Jul 12, 2022
f8ec18c
Use attribute Sets instead of KeyValues for export data
MrAlias Jul 12, 2022
67fbfdd
Merge branch 'data-attr-set' into agg-test-framework
MrAlias Jul 12, 2022
2f9d30d
Add export data type comparison testing API
MrAlias Jul 13, 2022
471f91a
Add Aggregation and Value comparison funcs
MrAlias Jul 14, 2022
522bdc0
Move export testing to own pkg
MrAlias Jul 15, 2022
c964ea5
Merge branch 'new_sdk/main' into agg-test-framework
MrAlias Jul 16, 2022
38558a0
Move exporttest to metricdatatest
MrAlias Jul 16, 2022
e251fa5
Add licenses headers to files missing them
MrAlias Jul 18, 2022
041537b
Use metricdata instead of export
MrAlias Jul 18, 2022
b138608
Rename exporttest pkg to metricdatatest
MrAlias Jul 18, 2022
ddb2850
Fix spelling errors
MrAlias Jul 18, 2022
dea1b3a
Fix lint issues
MrAlias Jul 18, 2022
01c428c
Use testing pkg to error directly
MrAlias Jul 18, 2022
fcf026e
Fix CompareAggregations
MrAlias Jul 18, 2022
1de2c4e
Generalize assertions and unexport equal checks
MrAlias Jul 18, 2022
4227e72
Abstract assert tests
MrAlias Jul 18, 2022
93ac61f
Rename all exp var to r
MrAlias Jul 18, 2022
755c900
Test AssertAggregationsEqual
MrAlias Jul 18, 2022
a932325
Comment why Value and Aggregation are separate
MrAlias Jul 18, 2022
aceec74
Test AssertValuesEqual
MrAlias Jul 18, 2022
1c2f5d9
Revert changes to metricdata/temporality.go
MrAlias Jul 18, 2022
f65075c
Expand pkg doc sentence
MrAlias Jul 18, 2022
3631507
Add license header to assertion.go
MrAlias Jul 18, 2022
0d26417
Update assertion docs
MrAlias Jul 19, 2022
85d1f60
Consolidate comparisons funcs into one file
MrAlias Jul 19, 2022
ec7106d
Consolidate and fix docs
MrAlias Jul 19, 2022
ddfe5b5
Consolidate assertion.go
MrAlias Jul 19, 2022
9b50ea9
Consolidate comparisons.go
MrAlias Jul 19, 2022
3e61b4c
make lint
MrAlias Jul 19, 2022
6cce6a6
Test with relatively static times
MrAlias Jul 19, 2022
65d0f1a
Update sdk/metric/metricdata/metricdatatest/comparisons.go
MrAlias Jul 20, 2022
f70a5e5
Drop equal return from comparison funcs
MrAlias Jul 20, 2022
0dec436
Refactor AssertEqual
MrAlias Jul 20, 2022
73e319c
Remove reasN from testDatatype func params
MrAlias Jul 20, 2022
9985b46
Consolidate AssertEqual type conversions
MrAlias Jul 20, 2022
34c4d13
Fix assertion error message
MrAlias Jul 20, 2022
f894686
Add assertion failure tests
MrAlias Jul 20, 2022
82973be
Remove unneeded strings join
MrAlias Jul 20, 2022
6e54b91
Make comment include a possessive
MrAlias Jul 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions sdk/metric/metricdata/metricdatatest/assertion.go
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 sdk/metric/metricdata/metricdatatest/assertion_fail_test.go
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)
}
215 changes: 215 additions & 0 deletions sdk/metric/metricdata/metricdatatest/assertion_test.go
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)
}
Loading