2020-03-24 07:41:10 +02:00
// Copyright The OpenTelemetry Authors
2019-10-15 18:28:36 +02:00
//
// 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.
2019-10-30 21:59:34 +02:00
package metric_test
2019-10-15 18:28:36 +02:00
import (
"context"
2020-03-11 20:57:57 +02:00
"errors"
2019-10-15 18:28:36 +02:00
"fmt"
"testing"
2019-11-01 20:40:29 +02:00
"go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/api/unit"
2020-03-19 21:02:46 +02:00
mockTest "go.opentelemetry.io/otel/internal/metric"
2020-03-20 17:58:32 +02:00
"go.opentelemetry.io/otel/sdk/resource"
2019-10-23 08:29:24 +02:00
"github.com/google/go-cmp/cmp"
2020-03-05 22:15:30 +02:00
"github.com/stretchr/testify/assert"
2020-03-11 20:57:57 +02:00
"github.com/stretchr/testify/require"
2019-10-15 18:28:36 +02:00
)
2020-03-11 20:57:57 +02:00
var Must = metric . Must
2020-03-12 05:21:34 +02:00
func TestOptions ( t * testing . T ) {
2019-10-15 18:28:36 +02:00
type testcase struct {
2020-03-20 17:58:32 +02:00
name string
opts [ ] metric . Option
keys [ ] core . Key
desc string
unit unit . Unit
resource resource . Resource
2019-10-15 18:28:36 +02:00
}
testcases := [ ] testcase {
{
2020-03-20 17:58:32 +02:00
name : "no opts" ,
opts : nil ,
keys : nil ,
desc : "" ,
unit : "" ,
resource : resource . Resource { } ,
2019-10-15 18:28:36 +02:00
} ,
{
name : "keys keys keys" ,
2020-03-12 05:21:34 +02:00
opts : [ ] metric . Option {
2019-10-30 21:59:34 +02:00
metric . WithKeys ( key . New ( "foo" ) , key . New ( "foo2" ) ) ,
metric . WithKeys ( key . New ( "bar" ) , key . New ( "bar2" ) ) ,
metric . WithKeys ( key . New ( "baz" ) , key . New ( "baz2" ) ) ,
2019-10-15 18:28:36 +02:00
} ,
keys : [ ] core . Key {
2019-10-17 07:49:58 +02:00
key . New ( "foo" ) , key . New ( "foo2" ) ,
key . New ( "bar" ) , key . New ( "bar2" ) ,
key . New ( "baz" ) , key . New ( "baz2" ) ,
2019-10-15 18:28:36 +02:00
} ,
2020-03-20 17:58:32 +02:00
desc : "" ,
unit : "" ,
resource : resource . Resource { } ,
2019-10-15 18:28:36 +02:00
} ,
{
name : "description" ,
2020-03-12 05:21:34 +02:00
opts : [ ] metric . Option {
2019-10-30 21:59:34 +02:00
metric . WithDescription ( "stuff" ) ,
2019-10-15 18:28:36 +02:00
} ,
2020-03-20 17:58:32 +02:00
keys : nil ,
desc : "stuff" ,
unit : "" ,
resource : resource . Resource { } ,
2019-10-15 18:28:36 +02:00
} ,
{
name : "description override" ,
2020-03-12 05:21:34 +02:00
opts : [ ] metric . Option {
2019-10-30 21:59:34 +02:00
metric . WithDescription ( "stuff" ) ,
metric . WithDescription ( "things" ) ,
2019-10-15 18:28:36 +02:00
} ,
2020-03-20 17:58:32 +02:00
keys : nil ,
desc : "things" ,
unit : "" ,
resource : resource . Resource { } ,
2019-10-15 18:28:36 +02:00
} ,
{
name : "unit" ,
2020-03-12 05:21:34 +02:00
opts : [ ] metric . Option {
2019-10-30 21:59:34 +02:00
metric . WithUnit ( "s" ) ,
2019-10-15 18:28:36 +02:00
} ,
2020-03-20 17:58:32 +02:00
keys : nil ,
desc : "" ,
unit : "s" ,
resource : resource . Resource { } ,
2019-10-15 18:28:36 +02:00
} ,
{
name : "unit override" ,
2020-03-12 05:21:34 +02:00
opts : [ ] metric . Option {
2019-10-30 21:59:34 +02:00
metric . WithUnit ( "s" ) ,
metric . WithUnit ( "h" ) ,
2019-10-15 18:28:36 +02:00
} ,
2020-03-20 17:58:32 +02:00
keys : nil ,
desc : "" ,
unit : "h" ,
resource : resource . Resource { } ,
} ,
{
name : "resource override" ,
opts : [ ] metric . Option {
metric . WithResource ( * resource . New ( key . New ( "name" ) . String ( "test-name" ) ) ) ,
} ,
keys : nil ,
desc : "" ,
unit : "" ,
resource : * resource . New ( key . New ( "name" ) . String ( "test-name" ) ) ,
2019-10-15 18:28:36 +02:00
} ,
}
for idx , tt := range testcases {
t . Logf ( "Testing counter case %s (%d)" , tt . name , idx )
2020-03-12 05:21:34 +02:00
if diff := cmp . Diff ( metric . Configure ( tt . opts ) , metric . Config {
2020-03-05 22:15:30 +02:00
Description : tt . desc ,
Unit : tt . unit ,
Keys : tt . keys ,
2020-03-20 17:58:32 +02:00
Resource : tt . resource ,
2020-03-12 05:21:34 +02:00
} ) ; diff != "" {
t . Errorf ( "Compare options: -got +want %s" , diff )
}
2019-10-15 18:28:36 +02:00
}
}
func TestCounter ( t * testing . T ) {
{
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-03-11 20:57:57 +02:00
c := Must ( meter ) . NewFloat64Counter ( "test.counter.float" )
2019-10-15 18:28:36 +02:00
ctx := context . Background ( )
2019-10-29 22:27:22 +02:00
labels := meter . Labels ( )
2019-10-15 18:28:36 +02:00
c . Add ( ctx , 42 , labels )
2019-12-28 02:30:19 +02:00
boundInstrument := c . Bind ( labels )
boundInstrument . Add ( ctx , 42 )
2019-10-15 18:28:36 +02:00
meter . RecordBatch ( ctx , labels , c . Measurement ( 42 ) )
t . Log ( "Testing float counter" )
2020-03-19 21:02:46 +02:00
checkBatches ( t , ctx , labels , mockSDK , core . Float64NumberKind , c . SyncImpl ( ) )
2019-10-15 18:28:36 +02:00
}
{
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-03-11 20:57:57 +02:00
c := Must ( meter ) . NewInt64Counter ( "test.counter.int" )
2019-10-15 18:28:36 +02:00
ctx := context . Background ( )
2019-10-29 22:27:22 +02:00
labels := meter . Labels ( )
2019-10-15 18:28:36 +02:00
c . Add ( ctx , 42 , labels )
2019-12-28 02:30:19 +02:00
boundInstrument := c . Bind ( labels )
boundInstrument . Add ( ctx , 42 )
2019-10-15 18:28:36 +02:00
meter . RecordBatch ( ctx , labels , c . Measurement ( 42 ) )
t . Log ( "Testing int counter" )
2020-03-19 21:02:46 +02:00
checkBatches ( t , ctx , labels , mockSDK , core . Int64NumberKind , c . SyncImpl ( ) )
2019-10-15 18:28:36 +02:00
}
}
func TestMeasure ( t * testing . T ) {
{
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-03-11 20:57:57 +02:00
m := Must ( meter ) . NewFloat64Measure ( "test.measure.float" )
2019-10-15 18:28:36 +02:00
ctx := context . Background ( )
2019-10-29 22:27:22 +02:00
labels := meter . Labels ( )
2019-10-15 18:28:36 +02:00
m . Record ( ctx , 42 , labels )
2019-12-28 02:30:19 +02:00
boundInstrument := m . Bind ( labels )
boundInstrument . Record ( ctx , 42 )
2019-10-15 18:28:36 +02:00
meter . RecordBatch ( ctx , labels , m . Measurement ( 42 ) )
t . Log ( "Testing float measure" )
2020-03-19 21:02:46 +02:00
checkBatches ( t , ctx , labels , mockSDK , core . Float64NumberKind , m . SyncImpl ( ) )
2019-10-15 18:28:36 +02:00
}
{
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-03-11 20:57:57 +02:00
m := Must ( meter ) . NewInt64Measure ( "test.measure.int" )
2019-10-15 18:28:36 +02:00
ctx := context . Background ( )
2019-10-29 22:27:22 +02:00
labels := meter . Labels ( )
2019-10-15 18:28:36 +02:00
m . Record ( ctx , 42 , labels )
2019-12-28 02:30:19 +02:00
boundInstrument := m . Bind ( labels )
boundInstrument . Record ( ctx , 42 )
2019-10-15 18:28:36 +02:00
meter . RecordBatch ( ctx , labels , m . Measurement ( 42 ) )
t . Log ( "Testing int measure" )
2020-03-19 21:02:46 +02:00
checkBatches ( t , ctx , labels , mockSDK , core . Int64NumberKind , m . SyncImpl ( ) )
2019-10-15 18:28:36 +02:00
}
}
2020-03-05 22:15:30 +02:00
func TestObserver ( t * testing . T ) {
{
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-03-05 22:15:30 +02:00
labels := meter . Labels ( )
2020-03-11 20:57:57 +02:00
o := Must ( meter ) . RegisterFloat64Observer ( "test.observer.float" , func ( result metric . Float64ObserverResult ) {
2020-03-05 22:15:30 +02:00
result . Observe ( 42 , labels )
} )
t . Log ( "Testing float observer" )
2020-03-19 21:02:46 +02:00
mockSDK . RunAsyncInstruments ( )
checkObserverBatch ( t , labels , mockSDK , core . Float64NumberKind , o . AsyncImpl ( ) )
2020-03-05 22:15:30 +02:00
}
{
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-03-05 22:15:30 +02:00
labels := meter . Labels ( )
2020-03-11 20:57:57 +02:00
o := Must ( meter ) . RegisterInt64Observer ( "test.observer.int" , func ( result metric . Int64ObserverResult ) {
2020-03-05 22:15:30 +02:00
result . Observe ( 42 , labels )
} )
t . Log ( "Testing int observer" )
2020-03-19 21:02:46 +02:00
mockSDK . RunAsyncInstruments ( )
checkObserverBatch ( t , labels , mockSDK , core . Int64NumberKind , o . AsyncImpl ( ) )
2020-03-05 22:15:30 +02:00
}
}
2020-03-24 19:54:08 +02:00
func checkBatches ( t * testing . T , ctx context . Context , labels metric . LabelSet , mock * mockTest . MeterImpl , kind core . NumberKind , instrument metric . InstrumentImpl ) {
2019-10-16 19:24:38 +02:00
t . Helper ( )
2020-03-19 21:02:46 +02:00
if len ( mock . MeasurementBatches ) != 3 {
t . Errorf ( "Expected 3 recorded measurement batches, got %d" , len ( mock . MeasurementBatches ) )
2019-10-15 18:28:36 +02:00
}
2020-03-19 21:02:46 +02:00
ourInstrument := instrument . Implementation ( ) . ( * mockTest . Sync )
ourLabelSet := labels . ( * mockTest . LabelSet )
2019-10-15 18:28:36 +02:00
minLen := 3
2020-03-19 21:02:46 +02:00
if minLen > len ( mock . MeasurementBatches ) {
minLen = len ( mock . MeasurementBatches )
2019-10-15 18:28:36 +02:00
}
for i := 0 ; i < minLen ; i ++ {
2020-03-19 21:02:46 +02:00
got := mock . MeasurementBatches [ i ]
2019-10-30 21:59:34 +02:00
if got . Ctx != ctx {
2019-10-15 18:28:36 +02:00
d := func ( c context . Context ) string {
return fmt . Sprintf ( "(ptr: %p, ctx %#v)" , c , c )
}
2019-10-30 21:59:34 +02:00
t . Errorf ( "Wrong recorded context in batch %d, expected %s, got %s" , i , d ( ctx ) , d ( got . Ctx ) )
2019-10-15 18:28:36 +02:00
}
2019-10-30 21:59:34 +02:00
if got . LabelSet != ourLabelSet {
2020-03-19 21:02:46 +02:00
d := func ( l * mockTest . LabelSet ) string {
2019-10-30 21:59:34 +02:00
return fmt . Sprintf ( "(ptr: %p, labels %#v)" , l , l . Labels )
2019-10-15 18:28:36 +02:00
}
2019-10-30 21:59:34 +02:00
t . Errorf ( "Wrong recorded label set in batch %d, expected %s, got %s" , i , d ( ourLabelSet ) , d ( got . LabelSet ) )
2019-10-15 18:28:36 +02:00
}
2019-10-30 21:59:34 +02:00
if len ( got . Measurements ) != 1 {
t . Errorf ( "Expected 1 measurement in batch %d, got %d" , i , len ( got . Measurements ) )
2019-10-15 18:28:36 +02:00
}
minMLen := 1
2019-10-30 21:59:34 +02:00
if minMLen > len ( got . Measurements ) {
minMLen = len ( got . Measurements )
2019-10-15 18:28:36 +02:00
}
for j := 0 ; j < minMLen ; j ++ {
2019-10-30 21:59:34 +02:00
measurement := got . Measurements [ j ]
2020-03-19 21:02:46 +02:00
if measurement . Instrument . Implementation ( ) != ourInstrument {
d := func ( iface interface { } ) string {
i := iface . ( * mockTest . Instrument )
2019-10-23 08:29:24 +02:00
return fmt . Sprintf ( "(ptr: %p, instrument %#v)" , i , i )
2019-10-15 18:28:36 +02:00
}
2020-03-19 21:02:46 +02:00
t . Errorf ( "Wrong recorded instrument in measurement %d in batch %d, expected %s, got %s" , j , i , d ( ourInstrument ) , d ( measurement . Instrument . Implementation ( ) ) )
2019-10-15 18:28:36 +02:00
}
2019-10-23 08:29:24 +02:00
ft := fortyTwo ( t , kind )
2019-10-30 21:59:34 +02:00
if measurement . Number . CompareNumber ( kind , ft ) != 0 {
t . Errorf ( "Wrong recorded value in measurement %d in batch %d, expected %s, got %s" , j , i , ft . Emit ( kind ) , measurement . Number . Emit ( kind ) )
2019-10-15 18:28:36 +02:00
}
}
}
}
2020-03-24 19:54:08 +02:00
func checkObserverBatch ( t * testing . T , labels metric . LabelSet , mock * mockTest . MeterImpl , kind core . NumberKind , observer metric . AsyncImpl ) {
2020-03-05 22:15:30 +02:00
t . Helper ( )
2020-03-19 21:02:46 +02:00
assert . Len ( t , mock . MeasurementBatches , 1 )
if len ( mock . MeasurementBatches ) < 1 {
2020-03-05 22:15:30 +02:00
return
}
2020-03-19 21:02:46 +02:00
o := observer . Implementation ( ) . ( * mockTest . Async )
2020-03-05 22:15:30 +02:00
if ! assert . NotNil ( t , o ) {
return
}
2020-03-19 21:02:46 +02:00
ourLabelSet := labels . ( * mockTest . LabelSet )
got := mock . MeasurementBatches [ 0 ]
2020-03-05 22:15:30 +02:00
assert . Equal ( t , ourLabelSet , got . LabelSet )
assert . Len ( t , got . Measurements , 1 )
if len ( got . Measurements ) < 1 {
return
}
measurement := got . Measurements [ 0 ]
2020-03-19 21:02:46 +02:00
assert . Equal ( t , o , measurement . Instrument . Implementation ( ) . ( * mockTest . Async ) )
2020-03-05 22:15:30 +02:00
ft := fortyTwo ( t , kind )
assert . Equal ( t , 0 , measurement . Number . CompareNumber ( kind , ft ) )
}
2019-10-29 22:27:22 +02:00
func fortyTwo ( t * testing . T , kind core . NumberKind ) core . Number {
2020-03-05 22:15:30 +02:00
t . Helper ( )
2019-10-15 18:28:36 +02:00
switch kind {
2019-10-29 22:27:22 +02:00
case core . Int64NumberKind :
return core . NewInt64Number ( 42 )
case core . Float64NumberKind :
return core . NewFloat64Number ( 42 )
2019-10-15 18:28:36 +02:00
}
t . Errorf ( "Invalid value kind %q" , kind )
2019-10-29 22:27:22 +02:00
return core . NewInt64Number ( 0 )
2019-10-15 18:28:36 +02:00
}
2020-03-11 20:57:57 +02:00
2020-03-19 21:02:46 +02:00
type testWrappedMeter struct {
}
var _ metric . MeterImpl = testWrappedMeter { }
2020-03-11 20:57:57 +02:00
2020-03-19 21:02:46 +02:00
func ( testWrappedMeter ) Labels ( ... core . KeyValue ) metric . LabelSet {
return nil
2020-03-11 20:57:57 +02:00
}
2020-03-19 21:02:46 +02:00
func ( testWrappedMeter ) RecordBatch ( context . Context , metric . LabelSet , ... metric . Measurement ) {
2020-03-11 20:57:57 +02:00
}
2020-03-19 21:02:46 +02:00
func ( testWrappedMeter ) NewSyncInstrument ( _ metric . Descriptor ) ( metric . SyncImpl , error ) {
return nil , nil
}
func ( testWrappedMeter ) NewAsyncInstrument ( _ metric . Descriptor , _ func ( func ( core . Number , metric . LabelSet ) ) ) ( metric . AsyncImpl , error ) {
return nil , errors . New ( "Test wrap error" )
}
2020-03-11 20:57:57 +02:00
2020-03-19 21:02:46 +02:00
func TestWrappedInstrumentError ( t * testing . T ) {
impl := & testWrappedMeter { }
2020-03-24 19:54:08 +02:00
meter := metric . WrapMeterImpl ( impl , "test" )
2020-03-11 20:57:57 +02:00
2020-03-19 21:02:46 +02:00
measure , err := meter . NewInt64Measure ( "test.measure" )
2020-03-11 20:57:57 +02:00
2020-03-19 21:02:46 +02:00
require . Equal ( t , err , metric . ErrSDKReturnedNilImpl )
require . NotNil ( t , measure . SyncImpl ( ) )
2020-03-11 20:57:57 +02:00
2020-03-19 21:02:46 +02:00
observer , err := meter . RegisterInt64Observer ( "test.observer" , func ( result metric . Int64ObserverResult ) { } )
2020-03-11 20:57:57 +02:00
require . NotNil ( t , err )
2020-03-19 21:02:46 +02:00
require . NotNil ( t , observer . AsyncImpl ( ) )
}
func TestNilCallbackObserverNoop ( t * testing . T ) {
// Tests that a nil callback yields a no-op observer without error.
_ , meter := mockTest . NewMeter ( )
observer := Must ( meter ) . RegisterInt64Observer ( "test.observer" , nil )
_ , ok := observer . AsyncImpl ( ) . ( metric . NoopAsync )
require . True ( t , ok )
2020-03-11 20:57:57 +02:00
}