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"
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-04-24 18:44:21 +02:00
name string
opts [ ] metric . Option
desc string
unit unit . Unit
2019-10-15 18:28:36 +02:00
}
testcases := [ ] testcase {
{
2020-04-24 18:44:21 +02:00
name : "no opts" ,
opts : nil ,
desc : "" ,
unit : "" ,
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-04-24 18:44:21 +02:00
desc : "stuff" ,
unit : "" ,
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-04-24 18:44:21 +02:00
desc : "things" ,
unit : "" ,
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-04-24 18:44:21 +02:00
desc : "" ,
unit : "s" ,
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-04-24 18:44:21 +02:00
desc : "" ,
unit : "h" ,
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 ,
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 ( )
2020-03-27 23:06:48 +02:00
labels := [ ] core . KeyValue { key . String ( "A" , "B" ) }
c . Add ( ctx , 42 , labels ... )
boundInstrument := c . Bind ( labels ... )
2019-12-28 02:30:19 +02:00
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-05-11 08:44:42 +02:00
checkBatches ( t , ctx , labels , mockSDK , metric . 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 ( )
2020-03-27 23:06:48 +02:00
labels := [ ] core . KeyValue { key . String ( "A" , "B" ) , key . String ( "C" , "D" ) }
c . Add ( ctx , 42 , labels ... )
boundInstrument := c . Bind ( labels ... )
2019-12-28 02:30:19 +02:00
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-05-11 08:44:42 +02:00
checkBatches ( t , ctx , labels , mockSDK , metric . 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 ( )
2020-03-27 23:06:48 +02:00
labels := [ ] core . KeyValue { }
m . Record ( ctx , 42 , labels ... )
boundInstrument := m . Bind ( labels ... )
2019-12-28 02:30:19 +02:00
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-05-11 08:44:42 +02:00
checkBatches ( t , ctx , labels , mockSDK , metric . 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 ( )
2020-03-27 23:06:48 +02:00
labels := [ ] core . KeyValue { key . Int ( "I" , 1 ) }
m . Record ( ctx , 42 , labels ... )
boundInstrument := m . Bind ( labels ... )
2019-12-28 02:30:19 +02:00
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-05-11 08:44:42 +02:00
checkBatches ( t , ctx , labels , mockSDK , metric . 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-27 23:06:48 +02:00
labels := [ ] core . KeyValue { key . String ( "O" , "P" ) }
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-03-11 20:57:57 +02:00
o := Must ( meter ) . RegisterFloat64Observer ( "test.observer.float" , func ( result metric . Float64ObserverResult ) {
2020-03-27 23:06:48 +02:00
result . Observe ( 42 , labels ... )
2020-03-05 22:15:30 +02:00
} )
t . Log ( "Testing float observer" )
2020-03-19 21:02:46 +02:00
mockSDK . RunAsyncInstruments ( )
2020-05-11 08:44:42 +02:00
checkObserverBatch ( t , labels , mockSDK , metric . Float64NumberKind , o . AsyncImpl ( ) )
2020-03-05 22:15:30 +02:00
}
{
2020-03-27 23:06:48 +02:00
labels := [ ] core . KeyValue { }
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-03-11 20:57:57 +02:00
o := Must ( meter ) . RegisterInt64Observer ( "test.observer.int" , func ( result metric . Int64ObserverResult ) {
2020-03-27 23:06:48 +02:00
result . Observe ( 42 , labels ... )
2020-03-05 22:15:30 +02:00
} )
t . Log ( "Testing int observer" )
2020-03-19 21:02:46 +02:00
mockSDK . RunAsyncInstruments ( )
2020-05-11 08:44:42 +02:00
checkObserverBatch ( t , labels , mockSDK , metric . Int64NumberKind , o . AsyncImpl ( ) )
2020-03-05 22:15:30 +02:00
}
}
2020-05-11 08:44:42 +02:00
func checkBatches ( t * testing . T , ctx context . Context , labels [ ] core . KeyValue , mock * mockTest . MeterImpl , kind metric . 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 )
2020-03-27 23:06:48 +02:00
for i , got := range mock . MeasurementBatches {
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
}
2020-03-27 23:06:48 +02:00
if ! assert . Equal ( t , got . Labels , labels ) {
t . Errorf ( "Wrong recorded label set in batch %d, expected %v, got %v" , i , labels , got . Labels )
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-05-11 08:44:42 +02:00
func checkObserverBatch ( t * testing . T , labels [ ] core . KeyValue , mock * mockTest . MeterImpl , kind metric . 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
got := mock . MeasurementBatches [ 0 ]
2020-03-27 23:06:48 +02:00
assert . Equal ( t , labels , got . Labels )
2020-03-05 22:15:30 +02:00
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 ) )
}
2020-05-11 08:44:42 +02:00
func fortyTwo ( t * testing . T , kind metric . NumberKind ) metric . Number {
2020-03-05 22:15:30 +02:00
t . Helper ( )
2019-10-15 18:28:36 +02:00
switch kind {
2020-05-11 08:44:42 +02:00
case metric . Int64NumberKind :
return metric . NewInt64Number ( 42 )
case metric . Float64NumberKind :
return metric . NewFloat64Number ( 42 )
2019-10-15 18:28:36 +02:00
}
t . Errorf ( "Invalid value kind %q" , kind )
2020-05-11 08:44:42 +02:00
return metric . 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-27 23:06:48 +02:00
func ( testWrappedMeter ) RecordBatch ( context . Context , [ ] core . KeyValue , ... 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
}
2020-05-11 08:44:42 +02:00
func ( testWrappedMeter ) NewAsyncInstrument ( _ metric . Descriptor , _ func ( func ( metric . Number , [ ] core . KeyValue ) ) ) ( metric . AsyncImpl , error ) {
2020-03-19 21:02:46 +02:00
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
}