1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-01-01 22:09:57 +02:00

Remove several metrics test helpers (#2105)

* Remove several metrics test helpers

* Lint

* Changelog

* Lint
This commit is contained in:
Joshua MacDonald 2021-07-21 10:06:38 -07:00 committed by GitHub
parent 49359495c7
commit d57c5a5604
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 162 additions and 411 deletions

View File

@ -23,6 +23,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Removed the deprecated package `go.opentelemetry.io/otel/exporters/trace/jaeger`. (#2020)
- Removed the deprecated package `go.opentelemetry.io/otel/exporters/trace/zipkin`. (#2020)
- Removed metrics test package `go.opentelemetry.io/otel/sdk/export/metric/metrictest`. (#2105)
### Fixed

View File

@ -22,7 +22,6 @@ import (
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
"go.opentelemetry.io/otel/sdk/export/metric/metrictest"
"go.opentelemetry.io/otel/sdk/metric/aggregator/histogram"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
@ -764,9 +763,11 @@ func runMetricExportTests(t *testing.T, opts []otlpmetric.Option, rs []record, e
var agg, ckpt metricsdk.Aggregator
if r.iKind.Adding() {
agg, ckpt = metrictest.Unslice2(sum.New(2))
sums := sum.New(2)
agg, ckpt = &sums[0], &sums[1]
} else {
agg, ckpt = metrictest.Unslice2(histogram.New(2, &desc, histogram.WithExplicitBoundaries(testHistogramBoundaries)))
histos := histogram.New(2, &desc, histogram.WithExplicitBoundaries(testHistogramBoundaries))
agg, ckpt = &histos[0], &histos[1]
}
ctx := context.Background()

View File

@ -29,7 +29,6 @@ import (
"go.opentelemetry.io/otel/metric/number"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
"go.opentelemetry.io/otel/sdk/export/metric/metrictest"
arrAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/exact"
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
lvAgg "go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
@ -101,18 +100,19 @@ func TestStringKeyValues(t *testing.T) {
}
func TestMinMaxSumCountValue(t *testing.T) {
mmsc, ckpt := metrictest.Unslice2(minmaxsumcount.New(2, &metric.Descriptor{}))
mmscs := minmaxsumcount.New(2, &metric.Descriptor{})
mmsc, ckpt := &mmscs[0], &mmscs[1]
assert.NoError(t, mmsc.Update(context.Background(), 1, &metric.Descriptor{}))
assert.NoError(t, mmsc.Update(context.Background(), 10, &metric.Descriptor{}))
// Prior to checkpointing ErrNoData should be returned.
_, _, _, _, err := minMaxSumCountValues(ckpt.(aggregation.MinMaxSumCount))
_, _, _, _, err := minMaxSumCountValues(ckpt)
assert.EqualError(t, err, aggregation.ErrNoData.Error())
// Checkpoint to set non-zero values
require.NoError(t, mmsc.SynchronizedMove(ckpt, &metric.Descriptor{}))
min, max, sum, count, err := minMaxSumCountValues(ckpt.(aggregation.MinMaxSumCount))
min, max, sum, count, err := minMaxSumCountValues(ckpt)
if assert.NoError(t, err) {
assert.Equal(t, min, number.NewInt64Number(1))
assert.Equal(t, max, number.NewInt64Number(10))
@ -124,7 +124,8 @@ func TestMinMaxSumCountValue(t *testing.T) {
func TestMinMaxSumCountDatapoints(t *testing.T) {
desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Int64Kind)
labels := attribute.NewSet(attribute.String("one", "1"))
mmsc, ckpt := metrictest.Unslice2(minmaxsumcount.New(2, &desc))
mmscs := minmaxsumcount.New(2, &metric.Descriptor{})
mmsc, ckpt := &mmscs[0], &mmscs[1]
assert.NoError(t, mmsc.Update(context.Background(), 1, &desc))
assert.NoError(t, mmsc.Update(context.Background(), 10, &desc))
@ -154,7 +155,7 @@ func TestMinMaxSumCountDatapoints(t *testing.T) {
},
}
record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd)
m, err := minMaxSumCount(record, ckpt.(aggregation.MinMaxSumCount))
m, err := minMaxSumCount(record, ckpt)
if assert.NoError(t, err) {
assert.Nil(t, m.GetGauge())
assert.Nil(t, m.GetSum())
@ -179,13 +180,14 @@ func TestMinMaxSumCountPropagatesErrors(t *testing.T) {
func TestSumIntDataPoints(t *testing.T) {
desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Int64Kind)
labels := attribute.NewSet(attribute.String("one", "1"))
s, ckpt := metrictest.Unslice2(sumAgg.New(2))
sums := sumAgg.New(2)
s, ckpt := &sums[0], &sums[1]
assert.NoError(t, s.Update(context.Background(), number.Number(1), &desc))
require.NoError(t, s.SynchronizedMove(ckpt, &desc))
record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd)
sum, ok := ckpt.(aggregation.Sum)
require.True(t, ok, "ckpt is not an aggregation.Sum: %T", ckpt)
value, err := sum.Sum()
value, err := ckpt.Sum()
require.NoError(t, err)
if m, err := sumPoint(record, value, record.StartTime(), record.EndTime(), export.CumulativeExportKind, true); assert.NoError(t, err) {
@ -218,13 +220,13 @@ func TestSumIntDataPoints(t *testing.T) {
func TestSumFloatDataPoints(t *testing.T) {
desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Float64Kind)
labels := attribute.NewSet(attribute.String("one", "1"))
s, ckpt := metrictest.Unslice2(sumAgg.New(2))
sums := sumAgg.New(2)
s, ckpt := &sums[0], &sums[1]
assert.NoError(t, s.Update(context.Background(), number.NewFloat64Number(1), &desc))
require.NoError(t, s.SynchronizedMove(ckpt, &desc))
record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd)
sum, ok := ckpt.(aggregation.Sum)
require.True(t, ok, "ckpt is not an aggregation.Sum: %T", ckpt)
value, err := sum.Sum()
value, err := ckpt.Sum()
require.NoError(t, err)
if m, err := sumPoint(record, value, record.StartTime(), record.EndTime(), export.DeltaExportKind, false); assert.NoError(t, err) {
@ -256,13 +258,13 @@ func TestSumFloatDataPoints(t *testing.T) {
func TestLastValueIntDataPoints(t *testing.T) {
desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Int64Kind)
labels := attribute.NewSet(attribute.String("one", "1"))
s, ckpt := metrictest.Unslice2(lvAgg.New(2))
assert.NoError(t, s.Update(context.Background(), number.Number(100), &desc))
require.NoError(t, s.SynchronizedMove(ckpt, &desc))
lvs := lvAgg.New(2)
lv, ckpt := &lvs[0], &lvs[1]
assert.NoError(t, lv.Update(context.Background(), number.Number(100), &desc))
require.NoError(t, lv.SynchronizedMove(ckpt, &desc))
record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd)
sum, ok := ckpt.(aggregation.LastValue)
require.True(t, ok, "ckpt is not an aggregation.LastValue: %T", ckpt)
value, timestamp, err := sum.LastValue()
value, timestamp, err := ckpt.LastValue()
require.NoError(t, err)
if m, err := gaugePoint(record, value, time.Time{}, timestamp); assert.NoError(t, err) {
@ -291,13 +293,13 @@ func TestLastValueIntDataPoints(t *testing.T) {
func TestExactIntDataPoints(t *testing.T) {
desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Int64Kind)
labels := attribute.NewSet(attribute.String("one", "1"))
e, ckpt := metrictest.Unslice2(arrAgg.New(2))
arrs := arrAgg.New(2)
e, ckpt := &arrs[0], &arrs[1]
assert.NoError(t, e.Update(context.Background(), number.Number(100), &desc))
require.NoError(t, e.SynchronizedMove(ckpt, &desc))
record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd)
p, ok := ckpt.(aggregation.Points)
require.True(t, ok, "ckpt is not an aggregation.Points: %T", ckpt)
pts, err := p.Points()
pts, err := ckpt.Points()
require.NoError(t, err)
if m, err := gaugeArray(record, pts); assert.NoError(t, err) {
@ -326,13 +328,12 @@ func TestExactIntDataPoints(t *testing.T) {
func TestExactFloatDataPoints(t *testing.T) {
desc := metric.NewDescriptor("", metric.ValueRecorderInstrumentKind, number.Float64Kind)
labels := attribute.NewSet(attribute.String("one", "1"))
e, ckpt := metrictest.Unslice2(arrAgg.New(2))
arrs := arrAgg.New(2)
e, ckpt := &arrs[0], &arrs[1]
assert.NoError(t, e.Update(context.Background(), number.NewFloat64Number(100), &desc))
require.NoError(t, e.SynchronizedMove(ckpt, &desc))
record := export.NewRecord(&desc, &labels, nil, ckpt.Aggregation(), intervalStart, intervalEnd)
p, ok := ckpt.(aggregation.Points)
require.True(t, ok, "ckpt is not an aggregation.Points: %T", ckpt)
pts, err := p.Points()
pts, err := ckpt.Points()
require.NoError(t, err)
if m, err := gaugeArray(record, pts); assert.NoError(t, err) {

View File

@ -30,19 +30,18 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/number"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/metrictest"
"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
"go.opentelemetry.io/otel/sdk/metric/aggregator/minmaxsumcount"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
controller "go.opentelemetry.io/otel/sdk/metric/controller/basic"
processor "go.opentelemetry.io/otel/sdk/metric/processor/basic"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
"go.opentelemetry.io/otel/sdk/resource"
)
type testFixture struct {
t *testing.T
ctx context.Context
cont *controller.Controller
meter metric.Meter
exporter *stdoutmetric.Exporter
output *bytes.Buffer
}
@ -50,6 +49,10 @@ type testFixture struct {
var testResource = resource.NewSchemaless(attribute.String("R", "V"))
func newFixture(t *testing.T, opts ...stdoutmetric.Option) testFixture {
return newFixtureWithResource(t, testResource, opts...)
}
func newFixtureWithResource(t *testing.T, res *resource.Resource, opts ...stdoutmetric.Option) testFixture {
buf := &bytes.Buffer{}
opts = append(opts, stdoutmetric.WithWriter(buf))
opts = append(opts, stdoutmetric.WithoutTimestamps())
@ -57,10 +60,22 @@ func newFixture(t *testing.T, opts ...stdoutmetric.Option) testFixture {
if err != nil {
t.Fatal("Error building fixture: ", err)
}
aggSel := processortest.AggregatorSelector()
proc := processor.New(aggSel, export.StatelessExportKindSelector())
cont := controller.New(proc,
controller.WithExporter(exp),
controller.WithResource(res),
)
ctx := context.Background()
require.NoError(t, cont.Start(ctx))
meter := cont.MeterProvider().Meter("test")
return testFixture{
t: t,
ctx: context.Background(),
ctx: ctx,
exporter: exp,
cont: cont,
meter: meter,
output: buf,
}
}
@ -69,41 +84,33 @@ func (fix testFixture) Output() string {
return strings.TrimSpace(fix.output.String())
}
func (fix testFixture) Export(checkpointSet export.CheckpointSet) {
err := fix.exporter.Export(fix.ctx, checkpointSet)
if err != nil {
fix.t.Error("export failed: ", err)
}
}
func TestStdoutTimestamp(t *testing.T) {
var buf bytes.Buffer
aggSel := processortest.AggregatorSelector()
proc := processor.New(aggSel, export.CumulativeExportKindSelector())
exporter, err := stdoutmetric.New(
stdoutmetric.WithWriter(&buf),
)
if err != nil {
t.Fatal("Invalid config: ", err)
}
cont := controller.New(proc,
controller.WithExporter(exporter),
controller.WithResource(testResource),
)
ctx := context.Background()
require.NoError(t, cont.Start(ctx))
meter := cont.MeterProvider().Meter("test")
counter := metric.Must(meter).NewInt64Counter("name.lastvalue")
before := time.Now()
// Ensure the timestamp is after before.
time.Sleep(time.Nanosecond)
checkpointSet := metrictest.NewCheckpointSet(testResource)
counter.Add(ctx, 1)
ctx := context.Background()
desc := metric.NewDescriptor("test.name", metric.ValueObserverInstrumentKind, number.Int64Kind)
lvagg, ckpt := metrictest.Unslice2(lastvalue.New(2))
aggregatortest.CheckedUpdate(t, lvagg, number.NewInt64Number(321), &desc)
require.NoError(t, lvagg.SynchronizedMove(ckpt, &desc))
checkpointSet.Add(&desc, ckpt)
if err := exporter.Export(ctx, checkpointSet); err != nil {
t.Fatal("Unexpected export error: ", err)
}
require.NoError(t, cont.Stop(ctx))
// Ensure the timestamp is before after.
time.Sleep(time.Nanosecond)
@ -131,129 +138,71 @@ func TestStdoutTimestamp(t *testing.T) {
func TestStdoutCounterFormat(t *testing.T) {
fix := newFixture(t)
checkpointSet := metrictest.NewCheckpointSet(testResource)
counter := metric.Must(fix.meter).NewInt64Counter("name.sum")
counter.Add(fix.ctx, 123, attribute.String("A", "B"), attribute.String("C", "D"))
desc := metric.NewDescriptor("test.name", metric.CounterInstrumentKind, number.Int64Kind)
require.NoError(t, fix.cont.Stop(fix.ctx))
cagg, ckpt := metrictest.Unslice2(sum.New(2))
aggregatortest.CheckedUpdate(fix.t, cagg, number.NewInt64Number(123), &desc)
require.NoError(t, cagg.SynchronizedMove(ckpt, &desc))
checkpointSet.Add(&desc, ckpt, attribute.String("A", "B"), attribute.String("C", "D"))
fix.Export(checkpointSet)
require.Equal(t, `[{"Name":"test.name{R=V,A=B,C=D}","Sum":123}]`, fix.Output())
require.Equal(t, `[{"Name":"name.sum{R=V,instrumentation.name=test,A=B,C=D}","Sum":123}]`, fix.Output())
}
func TestStdoutLastValueFormat(t *testing.T) {
fix := newFixture(t)
checkpointSet := metrictest.NewCheckpointSet(testResource)
counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue")
counter.Add(fix.ctx, 123.456, attribute.String("A", "B"), attribute.String("C", "D"))
desc := metric.NewDescriptor("test.name", metric.ValueObserverInstrumentKind, number.Float64Kind)
lvagg, ckpt := metrictest.Unslice2(lastvalue.New(2))
require.NoError(t, fix.cont.Stop(fix.ctx))
aggregatortest.CheckedUpdate(fix.t, lvagg, number.NewFloat64Number(123.456), &desc)
require.NoError(t, lvagg.SynchronizedMove(ckpt, &desc))
checkpointSet.Add(&desc, ckpt, attribute.String("A", "B"), attribute.String("C", "D"))
fix.Export(checkpointSet)
require.Equal(t, `[{"Name":"test.name{R=V,A=B,C=D}","Last":123.456}]`, fix.Output())
require.Equal(t, `[{"Name":"name.lastvalue{R=V,instrumentation.name=test,A=B,C=D}","Last":123.456}]`, fix.Output())
}
func TestStdoutMinMaxSumCount(t *testing.T) {
fix := newFixture(t)
checkpointSet := metrictest.NewCheckpointSet(testResource)
counter := metric.Must(fix.meter).NewFloat64Counter("name.minmaxsumcount")
counter.Add(fix.ctx, 123.456, attribute.String("A", "B"), attribute.String("C", "D"))
counter.Add(fix.ctx, 876.543, attribute.String("A", "B"), attribute.String("C", "D"))
desc := metric.NewDescriptor("test.name", metric.ValueRecorderInstrumentKind, number.Float64Kind)
require.NoError(t, fix.cont.Stop(fix.ctx))
magg, ckpt := metrictest.Unslice2(minmaxsumcount.New(2, &desc))
aggregatortest.CheckedUpdate(fix.t, magg, number.NewFloat64Number(123.456), &desc)
aggregatortest.CheckedUpdate(fix.t, magg, number.NewFloat64Number(876.543), &desc)
require.NoError(t, magg.SynchronizedMove(ckpt, &desc))
checkpointSet.Add(&desc, ckpt, attribute.String("A", "B"), attribute.String("C", "D"))
fix.Export(checkpointSet)
require.Equal(t, `[{"Name":"test.name{R=V,A=B,C=D}","Min":123.456,"Max":876.543,"Sum":999.999,"Count":2}]`, fix.Output())
require.Equal(t, `[{"Name":"name.minmaxsumcount{R=V,instrumentation.name=test,A=B,C=D}","Min":123.456,"Max":876.543,"Sum":999.999,"Count":2}]`, fix.Output())
}
func TestStdoutValueRecorderFormat(t *testing.T) {
fix := newFixture(t, stdoutmetric.WithPrettyPrint())
checkpointSet := metrictest.NewCheckpointSet(testResource)
desc := metric.NewDescriptor("test.name", metric.ValueRecorderInstrumentKind, number.Float64Kind)
aagg, ckpt := metrictest.Unslice2(minmaxsumcount.New(2, &desc))
inst := metric.Must(fix.meter).NewFloat64ValueRecorder("name.histogram")
for i := 0; i < 1000; i++ {
aggregatortest.CheckedUpdate(fix.t, aagg, number.NewFloat64Number(float64(i)+0.5), &desc)
inst.Record(fix.ctx, float64(i)+0.5, attribute.String("A", "B"), attribute.String("C", "D"))
}
require.NoError(t, fix.cont.Stop(fix.ctx))
require.NoError(t, aagg.SynchronizedMove(ckpt, &desc))
checkpointSet.Add(&desc, ckpt, attribute.String("A", "B"), attribute.String("C", "D"))
fix.Export(checkpointSet)
// TODO: Stdout does not export `Count` for histogram, nor the buckets.
require.Equal(t, `[
{
"Name": "test.name{R=V,A=B,C=D}",
"Min": 0.5,
"Max": 999.5,
"Sum": 500000,
"Count": 1000
"Name": "name.histogram{R=V,instrumentation.name=test,A=B,C=D}",
"Sum": 500000
}
]`, fix.Output())
}
func TestStdoutNoData(t *testing.T) {
desc := metric.NewDescriptor("test.name", metric.ValueRecorderInstrumentKind, number.Float64Kind)
runTwoAggs := func(agg, ckpt export.Aggregator) {
t.Run(fmt.Sprintf("%T", agg), func(t *testing.T) {
runTwoAggs := func(aggName string) {
t.Run(aggName, func(t *testing.T) {
t.Parallel()
fix := newFixture(t)
checkpointSet := metrictest.NewCheckpointSet(testResource)
require.NoError(t, agg.SynchronizedMove(ckpt, &desc))
checkpointSet.Add(&desc, ckpt)
fix.Export(checkpointSet)
_ = metric.Must(fix.meter).NewFloat64Counter(fmt.Sprint("name.", aggName))
require.NoError(t, fix.cont.Stop(fix.ctx))
require.Equal(t, "", fix.Output())
})
}
runTwoAggs(metrictest.Unslice2(lastvalue.New(2)))
runTwoAggs(metrictest.Unslice2(minmaxsumcount.New(2, &desc)))
}
func TestStdoutLastValueNotSet(t *testing.T) {
fix := newFixture(t)
checkpointSet := metrictest.NewCheckpointSet(testResource)
desc := metric.NewDescriptor("test.name", metric.ValueObserverInstrumentKind, number.Float64Kind)
lvagg, ckpt := metrictest.Unslice2(lastvalue.New(2))
require.NoError(t, lvagg.SynchronizedMove(ckpt, &desc))
checkpointSet.Add(&desc, lvagg, attribute.String("A", "B"), attribute.String("C", "D"))
fix.Export(checkpointSet)
require.Equal(t, "", fix.Output())
runTwoAggs("lastvalue")
runTwoAggs("minmaxsumcount")
}
func TestStdoutResource(t *testing.T) {
@ -270,41 +219,35 @@ func TestStdoutResource(t *testing.T) {
}
}
testCases := []testCase{
newCase("R1=V1,R2=V2,A=B,C=D",
newCase("R1=V1,R2=V2,instrumentation.name=test,A=B,C=D",
resource.NewSchemaless(attribute.String("R1", "V1"), attribute.String("R2", "V2")),
attribute.String("A", "B"),
attribute.String("C", "D")),
newCase("R1=V1,R2=V2",
newCase("R1=V1,R2=V2,instrumentation.name=test",
resource.NewSchemaless(attribute.String("R1", "V1"), attribute.String("R2", "V2")),
),
newCase("A=B,C=D",
newCase("instrumentation.name=test,A=B,C=D",
nil,
attribute.String("A", "B"),
attribute.String("C", "D"),
),
// We explicitly do not de-duplicate between resources
// and metric labels in this exporter.
newCase("R1=V1,R2=V2,R1=V3,R2=V4",
newCase("R1=V1,R2=V2,instrumentation.name=test,R1=V3,R2=V4",
resource.NewSchemaless(attribute.String("R1", "V1"), attribute.String("R2", "V2")),
attribute.String("R1", "V3"),
attribute.String("R2", "V4")),
}
for _, tc := range testCases {
fix := newFixture(t)
ctx := context.Background()
fix := newFixtureWithResource(t, tc.res)
checkpointSet := metrictest.NewCheckpointSet(tc.res)
counter := metric.Must(fix.meter).NewFloat64Counter("name.lastvalue")
counter.Add(ctx, 123.456, tc.attrs...)
desc := metric.NewDescriptor("test.name", metric.ValueObserverInstrumentKind, number.Float64Kind)
lvagg, ckpt := metrictest.Unslice2(lastvalue.New(2))
require.NoError(t, fix.cont.Stop(fix.ctx))
aggregatortest.CheckedUpdate(fix.t, lvagg, number.NewFloat64Number(123.456), &desc)
require.NoError(t, lvagg.SynchronizedMove(ckpt, &desc))
checkpointSet.Add(&desc, ckpt, tc.attrs...)
fix.Export(checkpointSet)
require.Equal(t, `[{"Name":"test.name{`+tc.expect+`}","Last":123.456}]`, fix.Output())
require.Equal(t, `[{"Name":"name.lastvalue{`+tc.expect+`}","Last":123.456}]`, fix.Output())
}
}

View File

@ -1,135 +0,0 @@
// 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.
package metrictest // import "go.opentelemetry.io/otel/sdk/export/metric/metrictest"
import (
"context"
"errors"
"reflect"
"sync"
"time"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/number"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
"go.opentelemetry.io/otel/sdk/resource"
)
type mapkey struct {
desc *metric.Descriptor
distinct attribute.Distinct
}
// CheckpointSet is useful for testing Exporters.
// TODO(#872): Uses of this can be replaced by processortest.Output.
type CheckpointSet struct {
sync.RWMutex
records map[mapkey]export.Record
updates []export.Record
resource *resource.Resource
}
// NoopAggregator is useful for testing Exporters.
type NoopAggregator struct{}
var _ export.Aggregator = (*NoopAggregator)(nil)
// Update implements export.Aggregator.
func (NoopAggregator) Update(context.Context, number.Number, *metric.Descriptor) error {
return nil
}
// SynchronizedMove implements export.Aggregator.
func (NoopAggregator) SynchronizedMove(export.Aggregator, *metric.Descriptor) error {
return nil
}
// Merge implements export.Aggregator.
func (NoopAggregator) Merge(export.Aggregator, *metric.Descriptor) error {
return nil
}
// Aggregation returns an interface for reading the state of this aggregator.
func (NoopAggregator) Aggregation() aggregation.Aggregation {
return NoopAggregator{}
}
// Kind implements aggregation.Aggregation.
func (NoopAggregator) Kind() aggregation.Kind {
return aggregation.Kind("Noop")
}
// NewCheckpointSet returns a test CheckpointSet that new records could be added.
// Records are grouped by their encoded labels.
func NewCheckpointSet(resource *resource.Resource) *CheckpointSet {
return &CheckpointSet{
records: make(map[mapkey]export.Record),
resource: resource,
}
}
// Reset clears the Aggregator state.
func (p *CheckpointSet) Reset() {
p.records = make(map[mapkey]export.Record)
p.updates = nil
}
// Add a new record to a CheckpointSet.
//
// If there is an existing record with the same descriptor and labels,
// the stored aggregator will be returned and should be merged.
func (p *CheckpointSet) Add(desc *metric.Descriptor, newAgg export.Aggregator, labels ...attribute.KeyValue) (agg export.Aggregator, added bool) {
elabels := attribute.NewSet(labels...)
key := mapkey{
desc: desc,
distinct: elabels.Equivalent(),
}
if record, ok := p.records[key]; ok {
return record.Aggregation().(export.Aggregator), false
}
rec := export.NewRecord(desc, &elabels, p.resource, newAgg.Aggregation(), time.Time{}, time.Time{})
p.updates = append(p.updates, rec)
p.records[key] = rec
return newAgg, true
}
// ForEach does not use ExportKindSelected: use a real Processor to
// test ExportKind functionality.
func (p *CheckpointSet) ForEach(_ export.ExportKindSelector, f func(export.Record) error) error {
for _, r := range p.updates {
if err := f(r); err != nil && !errors.Is(err, aggregation.ErrNoData) {
return err
}
}
return nil
}
// Unslice2 takes a slice of []some.Aggregator and returns a slice of []export.Aggregator
func Unslice2(sl interface{}) (one, two export.Aggregator) {
slv := reflect.ValueOf(sl)
if slv.Type().Kind() != reflect.Slice {
panic("Invalid Unslice2")
}
if slv.Len() != 2 {
panic("Invalid Unslice2: length > 2")
}
one = slv.Index(0).Addr().Interface().(export.Aggregator)
two = slv.Index(1).Addr().Interface().(export.Aggregator)
return
}

View File

@ -1,30 +0,0 @@
// 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.
package metrictest
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestUnslice(t *testing.T) {
in := make([]NoopAggregator, 2)
a, b := Unslice2(in)
require.Equal(t, a.(*NoopAggregator), &in[0])
require.Equal(t, b.(*NoopAggregator), &in[1])
}

View File

@ -26,7 +26,6 @@ import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/number"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
metricsdk "go.opentelemetry.io/otel/sdk/metric"
@ -69,16 +68,6 @@ func init() {
otel.SetErrorHandler(testHandler)
}
// correctnessProcessor could be replaced with processortest.Processor
// with a non-default aggregator selector. TODO(#872) use the
// processortest code here.
type correctnessProcessor struct {
t *testing.T
*testSelector
accumulations []export.Accumulation
}
type testSelector struct {
selector export.AggregatorSelector
newAggCount int
@ -89,28 +78,24 @@ func (ts *testSelector) AggregatorFor(desc *metric.Descriptor, aggPtrs ...*expor
processortest.AggregatorSelector().AggregatorFor(desc, aggPtrs...)
}
func newSDK(t *testing.T) (metric.Meter, *metricsdk.Accumulator, *correctnessProcessor) {
func newSDK(t *testing.T) (metric.Meter, *metricsdk.Accumulator, *testSelector, *processortest.Processor) {
testHandler.Reset()
processor := &correctnessProcessor{
t: t,
testSelector: &testSelector{selector: processortest.AggregatorSelector()},
}
testSelector := &testSelector{selector: processortest.AggregatorSelector()}
processor := processortest.NewProcessor(
testSelector,
attribute.DefaultEncoder(),
)
accum := metricsdk.NewAccumulator(
processor,
testResource,
)
meter := metric.WrapMeterImpl(accum, "test")
return meter, accum, processor
}
func (ci *correctnessProcessor) Process(accumulation export.Accumulation) error {
ci.accumulations = append(ci.accumulations, accumulation)
return nil
return meter, accum, testSelector, processor
}
func TestInputRangeCounter(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, _, processor := newSDK(t)
counter := Must(meter).NewInt64Counter("name.sum")
@ -120,19 +105,19 @@ func TestInputRangeCounter(t *testing.T) {
checkpointed := sdk.Collect(ctx)
require.Equal(t, 0, checkpointed)
processor.accumulations = nil
processor.Reset()
counter.Add(ctx, 1)
checkpointed = sdk.Collect(ctx)
sum, err := processor.accumulations[0].Aggregator().(aggregation.Sum).Sum()
require.Equal(t, int64(1), sum.AsInt64())
require.Equal(t, map[string]float64{
"name.sum//R=V": 1,
}, processor.Values())
require.Equal(t, 1, checkpointed)
require.Nil(t, err)
require.Nil(t, testHandler.Flush())
}
func TestInputRangeUpDownCounter(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, _, processor := newSDK(t)
counter := Must(meter).NewInt64UpDownCounter("name.sum")
@ -142,16 +127,16 @@ func TestInputRangeUpDownCounter(t *testing.T) {
counter.Add(ctx, 1)
checkpointed := sdk.Collect(ctx)
sum, err := processor.accumulations[0].Aggregator().(aggregation.Sum).Sum()
require.Equal(t, int64(1), sum.AsInt64())
require.Equal(t, map[string]float64{
"name.sum//R=V": 1,
}, processor.Values())
require.Equal(t, 1, checkpointed)
require.Nil(t, err)
require.Nil(t, testHandler.Flush())
}
func TestInputRangeValueRecorder(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, _, processor := newSDK(t)
valuerecorder := Must(meter).NewFloat64ValueRecorder("name.exact")
@ -164,19 +149,19 @@ func TestInputRangeValueRecorder(t *testing.T) {
valuerecorder.Record(ctx, 1)
valuerecorder.Record(ctx, 2)
processor.accumulations = nil
processor.Reset()
checkpointed = sdk.Collect(ctx)
count, err := processor.accumulations[0].Aggregator().(aggregation.Count).Count()
require.Equal(t, uint64(2), count)
require.Equal(t, map[string]float64{
"name.exact//R=V": 3,
}, processor.Values())
require.Equal(t, 1, checkpointed)
require.Nil(t, testHandler.Flush())
require.Nil(t, err)
}
func TestDisabledInstrument(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, _, processor := newSDK(t)
valuerecorder := Must(meter).NewFloat64ValueRecorder("name.disabled")
@ -184,12 +169,12 @@ func TestDisabledInstrument(t *testing.T) {
checkpointed := sdk.Collect(ctx)
require.Equal(t, 0, checkpointed)
require.Equal(t, 0, len(processor.accumulations))
require.Equal(t, map[string]float64{}, processor.Values())
}
func TestRecordNaN(t *testing.T) {
ctx := context.Background()
meter, _, _ := newSDK(t)
meter, _, _, _ := newSDK(t)
c := Must(meter).NewFloat64Counter("name.sum")
@ -200,7 +185,7 @@ func TestRecordNaN(t *testing.T) {
func TestSDKLabelsDeduplication(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, _, processor := newSDK(t)
counter := Must(meter).NewInt64Counter("name.sum")
@ -217,7 +202,7 @@ func TestSDKLabelsDeduplication(t *testing.T) {
keysB = append(keysB, attribute.Key(fmt.Sprintf("B%03d", i)))
}
var allExpect [][]attribute.KeyValue
allExpect := map[string]float64{}
for numKeys := 0; numKeys < maxKeys; numKeys++ {
var kvsA []attribute.KeyValue
@ -238,29 +223,24 @@ func TestSDKLabelsDeduplication(t *testing.T) {
counter.Add(ctx, 1, kvsA...)
counter.Add(ctx, 1, kvsA...)
allExpect = append(allExpect, expectA)
format := func(attrs []attribute.KeyValue) string {
str := attribute.DefaultEncoder().Encode(newSetIter(attrs...))
return fmt.Sprint("name.sum/", str, "/R=V")
}
allExpect[format(expectA)] += 2
if numKeys != 0 {
// In this case A and B sets are the same.
counter.Add(ctx, 1, kvsB...)
counter.Add(ctx, 1, kvsB...)
allExpect = append(allExpect, expectB)
allExpect[format(expectB)] += 2
}
}
sdk.Collect(ctx)
var actual [][]attribute.KeyValue
for _, rec := range processor.accumulations {
sum, _ := rec.Aggregator().(aggregation.Sum).Sum()
require.Equal(t, sum, number.NewInt64Number(2))
kvs := rec.Labels().ToSlice()
actual = append(actual, kvs)
}
require.ElementsMatch(t, allExpect, actual)
require.EqualValues(t, allExpect, processor.Values())
}
func newSetIter(kvs ...attribute.KeyValue) attribute.Iterator {
@ -295,7 +275,7 @@ func TestDefaultLabelEncoder(t *testing.T) {
func TestObserverCollection(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, _, processor := newSDK(t)
mult := 1
_ = Must(meter).NewFloat64ValueObserver("float.valueobserver.lastvalue", func(_ context.Context, result metric.Float64ObserverResult) {
@ -342,15 +322,11 @@ func TestObserverCollection(t *testing.T) {
})
for mult = 0; mult < 3; mult++ {
processor.accumulations = nil
processor.Reset()
collected := sdk.Collect(ctx)
require.Equal(t, collected, len(processor.accumulations))
require.Equal(t, collected, len(processor.Values()))
out := processortest.NewOutput(attribute.DefaultEncoder())
for _, rec := range processor.accumulations {
require.NoError(t, out.AddAccumulation(rec))
}
mult := float64(mult)
require.EqualValues(t, map[string]float64{
"float.valueobserver.lastvalue/A=B/R=V": -mult,
@ -367,13 +343,13 @@ func TestObserverCollection(t *testing.T) {
"float.updownsumobserver.sum/C=D/R=V": mult,
"int.updownsumobserver.sum//R=V": -mult,
"int.updownsumobserver.sum/A=B/R=V": mult,
}, out.Map())
}, processor.Values())
}
}
func TestSumObserverInputRange(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, _, processor := newSDK(t)
// TODO: these tests are testing for negative values, not for _descending values_. Fix.
_ = Must(meter).NewFloat64SumObserver("float.sumobserver.sum", func(_ context.Context, result metric.Float64ObserverResult) {
@ -392,7 +368,7 @@ func TestSumObserverInputRange(t *testing.T) {
collected := sdk.Collect(ctx)
require.Equal(t, 0, collected)
require.Equal(t, 0, len(processor.accumulations))
require.EqualValues(t, map[string]float64{}, processor.Values())
// check that the error condition was reset
require.NoError(t, testHandler.Flush())
@ -400,7 +376,7 @@ func TestSumObserverInputRange(t *testing.T) {
func TestObserverBatch(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, _, processor := newSDK(t)
var floatValueObs metric.Float64ValueObserver
var intValueObs metric.Int64ValueObserver
@ -450,12 +426,8 @@ func TestObserverBatch(t *testing.T) {
collected := sdk.Collect(ctx)
require.Equal(t, collected, len(processor.accumulations))
require.Equal(t, collected, len(processor.Values()))
out := processortest.NewOutput(attribute.DefaultEncoder())
for _, rec := range processor.accumulations {
require.NoError(t, out.AddAccumulation(rec))
}
require.EqualValues(t, map[string]float64{
"float.sumobserver.sum//R=V": 1.1,
"float.sumobserver.sum/A=B/R=V": 1000,
@ -471,12 +443,12 @@ func TestObserverBatch(t *testing.T) {
"float.valueobserver.lastvalue/C=D/R=V": -1,
"int.valueobserver.lastvalue//R=V": 1,
"int.valueobserver.lastvalue/A=B/R=V": 1,
}, out.Map())
}, processor.Values())
}
func TestRecordBatch(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, _, processor := newSDK(t)
counter1 := Must(meter).NewInt64Counter("int64.sum")
counter2 := Must(meter).NewFloat64Counter("float64.sum")
@ -497,16 +469,12 @@ func TestRecordBatch(t *testing.T) {
sdk.Collect(ctx)
out := processortest.NewOutput(attribute.DefaultEncoder())
for _, rec := range processor.accumulations {
require.NoError(t, out.AddAccumulation(rec))
}
require.EqualValues(t, map[string]float64{
"int64.sum/A=B,C=D/R=V": 1,
"float64.sum/A=B,C=D/R=V": 2,
"int64.exact/A=B,C=D/R=V": 3,
"float64.exact/A=B,C=D/R=V": 4,
}, out.Map())
}, processor.Values())
}
// TestRecordPersistence ensures that a direct-called instrument that
@ -514,7 +482,7 @@ func TestRecordBatch(t *testing.T) {
// that its encoded labels will be cached across collection intervals.
func TestRecordPersistence(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, selector, _ := newSDK(t)
c := Must(meter).NewFloat64Counter("name.sum")
b := c.Bind(attribute.String("bound", "true"))
@ -526,7 +494,7 @@ func TestRecordPersistence(t *testing.T) {
sdk.Collect(ctx)
}
require.Equal(t, 4, processor.newAggCount)
require.Equal(t, 4, selector.newAggCount)
}
func TestIncorrectInstruments(t *testing.T) {
@ -536,7 +504,7 @@ func TestIncorrectInstruments(t *testing.T) {
var observer metric.Int64ValueObserver
ctx := context.Background()
meter, sdk, _ := newSDK(t)
meter, sdk, _, processor := newSDK(t)
// Now try with uninitialized instruments.
meter.RecordBatch(ctx, nil, counter.Measurement(1))
@ -562,12 +530,13 @@ func TestIncorrectInstruments(t *testing.T) {
collected = sdk.Collect(ctx)
require.Equal(t, 0, collected)
require.EqualValues(t, map[string]float64{}, processor.Values())
require.Equal(t, metricsdk.ErrUninitializedInstrument, testHandler.Flush())
}
func TestSyncInAsync(t *testing.T) {
ctx := context.Background()
meter, sdk, processor := newSDK(t)
meter, sdk, _, processor := newSDK(t)
counter := Must(meter).NewFloat64Counter("counter.sum")
_ = Must(meter).NewInt64ValueObserver("observer.lastvalue",
@ -579,12 +548,8 @@ func TestSyncInAsync(t *testing.T) {
sdk.Collect(ctx)
out := processortest.NewOutput(attribute.DefaultEncoder())
for _, rec := range processor.accumulations {
require.NoError(t, out.AddAccumulation(rec))
}
require.EqualValues(t, map[string]float64{
"counter.sum//R=V": 100,
"observer.lastvalue//R=V": 10,
}, out.Map())
}, processor.Values())
}

View File

@ -29,8 +29,8 @@ import (
"go.opentelemetry.io/otel/metric/number"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregation"
"go.opentelemetry.io/otel/sdk/export/metric/metrictest"
sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/aggregatortest"
"go.opentelemetry.io/otel/sdk/metric/processor/basic"
"go.opentelemetry.io/otel/sdk/metric/processor/processortest"
processorTest "go.opentelemetry.io/otel/sdk/metric/processor/processortest"
@ -301,7 +301,7 @@ func TestBasicInconsistent(t *testing.T) {
b = basic.New(processorTest.AggregatorSelector(), export.StatelessExportKindSelector())
desc := metric.NewDescriptor("inst", metric.CounterInstrumentKind, number.Int64Kind)
accum := export.NewAccumulation(&desc, attribute.EmptySet(), resource.Empty(), metrictest.NoopAggregator{})
accum := export.NewAccumulation(&desc, attribute.EmptySet(), resource.Empty(), aggregatortest.NoopAggregator{})
require.Equal(t, basic.ErrInconsistentState, b.Process(accum))
// Test invalid kind:
@ -326,7 +326,7 @@ func TestBasicTimestamps(t *testing.T) {
afterNew := time.Now()
desc := metric.NewDescriptor("inst", metric.CounterInstrumentKind, number.Int64Kind)
accum := export.NewAccumulation(&desc, attribute.EmptySet(), resource.Empty(), metrictest.NoopAggregator{})
accum := export.NewAccumulation(&desc, attribute.EmptySet(), resource.Empty(), aggregatortest.NoopAggregator{})
b.StartCollection()
_ = b.Process(accum)

View File

@ -121,6 +121,11 @@ func (p *Processor) Values() map[string]float64 {
return p.output.Map()
}
// Reset clears the state of this test processor.
func (p *Processor) Reset() {
p.output.Reset()
}
// Checkpointer returns a checkpointer that computes a single
// interval.
func Checkpointer(p *Processor) export.Checkpointer {