1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
|
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package metricdatatest provides testing functionality for use with the
// metricdata package.
package metricdatatest // import "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest"
import (
"fmt"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
)
// Datatypes are the concrete data-types the metricdata package provides.
type Datatypes interface {
metricdata.DataPoint[float64] |
metricdata.DataPoint[int64] |
metricdata.Gauge[float64] |
metricdata.Gauge[int64] |
metricdata.Histogram[float64] |
metricdata.Histogram[int64] |
metricdata.HistogramDataPoint[float64] |
metricdata.HistogramDataPoint[int64] |
metricdata.Extrema[int64] |
metricdata.Extrema[float64] |
metricdata.Metrics |
metricdata.ResourceMetrics |
metricdata.ScopeMetrics |
metricdata.Sum[float64] |
metricdata.Sum[int64] |
metricdata.Exemplar[float64] |
metricdata.Exemplar[int64] |
metricdata.ExponentialHistogram[float64] |
metricdata.ExponentialHistogram[int64] |
metricdata.ExponentialHistogramDataPoint[float64] |
metricdata.ExponentialHistogramDataPoint[int64] |
metricdata.ExponentialBucket |
metricdata.Summary |
metricdata.SummaryDataPoint |
metricdata.QuantileValue
// Interface types are not allowed in union types, therefore the
// Aggregation and Value type from metricdata are not included here.
}
// TestingT is an interface that implements [testing.T], but without the
// private method of [testing.TB], so other testing packages can rely on it as
// well.
// The methods in this interface must match the [testing.TB] interface.
type TestingT interface {
Helper()
// DO NOT CHANGE: any modification will not be backwards compatible and
// must never be done outside of a new major release.
Error(...any)
// DO NOT CHANGE: any modification will not be backwards compatible and
// must never be done outside of a new major release.
}
type config struct {
ignoreTimestamp bool
ignoreExemplars bool
ignoreValue bool
}
func newConfig(opts []Option) config {
var cfg config
for _, opt := range opts {
cfg = opt.apply(cfg)
}
return cfg
}
// Option allows for fine grain control over how AssertEqual operates.
type Option interface {
apply(cfg config) config
}
type fnOption func(cfg config) config
func (fn fnOption) apply(cfg config) config {
return fn(cfg)
}
// IgnoreTimestamp disables checking if timestamps are different.
func IgnoreTimestamp() Option {
return fnOption(func(cfg config) config {
cfg.ignoreTimestamp = true
return cfg
})
}
// IgnoreExemplars disables checking if Exemplars are different.
func IgnoreExemplars() Option {
return fnOption(func(cfg config) config {
cfg.ignoreExemplars = true
return cfg
})
}
// IgnoreValue disables checking if values are different. This can be
// useful for non-deterministic values, like measured durations.
//
// This will ignore the value and trace information for Exemplars;
// the buckets, zero count, scale, sum, max, min, and counts of
// ExponentialHistogramDataPoints; the buckets, sum, count, max,
// and min of HistogramDataPoints; the value of DataPoints.
func IgnoreValue() Option {
return fnOption(func(cfg config) config {
cfg.ignoreValue = true
return cfg
})
}
// AssertEqual asserts that the two concrete data-types from the metricdata
// package are equal.
func AssertEqual[T Datatypes](t TestingT, expected, actual T, opts ...Option) bool {
t.Helper()
cfg := newConfig(opts)
// Generic types cannot be type asserted. Use an interface instead.
aIface := interface{}(actual)
var r []string
switch e := interface{}(expected).(type) {
case metricdata.Exemplar[int64]:
r = equalExemplars(e, aIface.(metricdata.Exemplar[int64]), cfg)
case metricdata.Exemplar[float64]:
r = equalExemplars(e, aIface.(metricdata.Exemplar[float64]), cfg)
case metricdata.DataPoint[int64]:
r = equalDataPoints(e, aIface.(metricdata.DataPoint[int64]), cfg)
case metricdata.DataPoint[float64]:
r = equalDataPoints(e, aIface.(metricdata.DataPoint[float64]), cfg)
case metricdata.Gauge[int64]:
r = equalGauges(e, aIface.(metricdata.Gauge[int64]), cfg)
case metricdata.Gauge[float64]:
r = equalGauges(e, aIface.(metricdata.Gauge[float64]), cfg)
case metricdata.Histogram[float64]:
r = equalHistograms(e, aIface.(metricdata.Histogram[float64]), cfg)
case metricdata.Histogram[int64]:
r = equalHistograms(e, aIface.(metricdata.Histogram[int64]), cfg)
case metricdata.HistogramDataPoint[float64]:
r = equalHistogramDataPoints(e, aIface.(metricdata.HistogramDataPoint[float64]), cfg)
case metricdata.HistogramDataPoint[int64]:
r = equalHistogramDataPoints(e, aIface.(metricdata.HistogramDataPoint[int64]), cfg)
case metricdata.Extrema[int64]:
r = equalExtrema(e, aIface.(metricdata.Extrema[int64]), cfg)
case metricdata.Extrema[float64]:
r = equalExtrema(e, aIface.(metricdata.Extrema[float64]), cfg)
case metricdata.Metrics:
r = equalMetrics(e, aIface.(metricdata.Metrics), cfg)
case metricdata.ResourceMetrics:
r = equalResourceMetrics(e, aIface.(metricdata.ResourceMetrics), cfg)
case metricdata.ScopeMetrics:
r = equalScopeMetrics(e, aIface.(metricdata.ScopeMetrics), cfg)
case metricdata.Sum[int64]:
r = equalSums(e, aIface.(metricdata.Sum[int64]), cfg)
case metricdata.Sum[float64]:
r = equalSums(e, aIface.(metricdata.Sum[float64]), cfg)
case metricdata.ExponentialHistogram[float64]:
r = equalExponentialHistograms(e, aIface.(metricdata.ExponentialHistogram[float64]), cfg)
case metricdata.ExponentialHistogram[int64]:
r = equalExponentialHistograms(e, aIface.(metricdata.ExponentialHistogram[int64]), cfg)
case metricdata.ExponentialHistogramDataPoint[float64]:
r = equalExponentialHistogramDataPoints(e, aIface.(metricdata.ExponentialHistogramDataPoint[float64]), cfg)
case metricdata.ExponentialHistogramDataPoint[int64]:
r = equalExponentialHistogramDataPoints(e, aIface.(metricdata.ExponentialHistogramDataPoint[int64]), cfg)
case metricdata.ExponentialBucket:
r = equalExponentialBuckets(e, aIface.(metricdata.ExponentialBucket), cfg)
case metricdata.Summary:
r = equalSummary(e, aIface.(metricdata.Summary), cfg)
case metricdata.SummaryDataPoint:
r = equalSummaryDataPoint(e, aIface.(metricdata.SummaryDataPoint), cfg)
case metricdata.QuantileValue:
r = equalQuantileValue(e, aIface.(metricdata.QuantileValue), cfg)
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 TestingT, expected, actual metricdata.Aggregation, opts ...Option) bool {
t.Helper()
cfg := newConfig(opts)
if r := equalAggregations(expected, actual, cfg); len(r) > 0 {
t.Error(r)
return false
}
return true
}
// AssertHasAttributes asserts that all Datapoints or HistogramDataPoints have all passed attrs.
func AssertHasAttributes[T Datatypes](t TestingT, actual T, attrs ...attribute.KeyValue) bool {
t.Helper()
var reasons []string
switch e := interface{}(actual).(type) {
case metricdata.Exemplar[int64]:
reasons = hasAttributesExemplars(e, attrs...)
case metricdata.Exemplar[float64]:
reasons = hasAttributesExemplars(e, attrs...)
case metricdata.DataPoint[int64]:
reasons = hasAttributesDataPoints(e, attrs...)
case metricdata.DataPoint[float64]:
reasons = hasAttributesDataPoints(e, attrs...)
case metricdata.Gauge[int64]:
reasons = hasAttributesGauge(e, attrs...)
case metricdata.Gauge[float64]:
reasons = hasAttributesGauge(e, attrs...)
case metricdata.Sum[int64]:
reasons = hasAttributesSum(e, attrs...)
case metricdata.Sum[float64]:
reasons = hasAttributesSum(e, attrs...)
case metricdata.HistogramDataPoint[int64]:
reasons = hasAttributesHistogramDataPoints(e, attrs...)
case metricdata.HistogramDataPoint[float64]:
reasons = hasAttributesHistogramDataPoints(e, attrs...)
case metricdata.Extrema[int64], metricdata.Extrema[float64]:
// Nothing to check.
case metricdata.Histogram[int64]:
reasons = hasAttributesHistogram(e, attrs...)
case metricdata.Histogram[float64]:
reasons = hasAttributesHistogram(e, attrs...)
case metricdata.Metrics:
reasons = hasAttributesMetrics(e, attrs...)
case metricdata.ScopeMetrics:
reasons = hasAttributesScopeMetrics(e, attrs...)
case metricdata.ResourceMetrics:
reasons = hasAttributesResourceMetrics(e, attrs...)
case metricdata.ExponentialHistogram[int64]:
reasons = hasAttributesExponentialHistogram(e, attrs...)
case metricdata.ExponentialHistogram[float64]:
reasons = hasAttributesExponentialHistogram(e, attrs...)
case metricdata.ExponentialHistogramDataPoint[int64]:
reasons = hasAttributesExponentialHistogramDataPoints(e, attrs...)
case metricdata.ExponentialHistogramDataPoint[float64]:
reasons = hasAttributesExponentialHistogramDataPoints(e, attrs...)
case metricdata.ExponentialBucket:
// Nothing to check.
case metricdata.Summary:
reasons = hasAttributesSummary(e, attrs...)
case metricdata.SummaryDataPoint:
reasons = hasAttributesSummaryDataPoint(e, attrs...)
case metricdata.QuantileValue:
// Nothing to check.
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", actual))
}
if len(reasons) > 0 {
t.Error(reasons)
return false
}
return true
}
|