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"
2020-05-14 01:06:03 +02:00
"go.opentelemetry.io/otel/api/kv"
2019-11-01 20:40:29 +02:00
"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-05-19 19:00:22 +02:00
// N.B. the API does not check for negative
// values, that's the SDK's responsibility.
t . Run ( "float64 counter" , func ( 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-05-14 01:06:03 +02:00
labels := [ ] kv . KeyValue { kv . String ( "A" , "B" ) }
2020-05-19 19:00:22 +02:00
c . Add ( ctx , 1994.1 , labels ... )
2020-03-27 23:06:48 +02:00
boundInstrument := c . Bind ( labels ... )
2020-05-19 19:00:22 +02:00
boundInstrument . Add ( ctx , - 742 )
2019-10-15 18:28:36 +02:00
meter . RecordBatch ( ctx , labels , c . Measurement ( 42 ) )
2020-05-19 19:00:22 +02:00
checkSyncBatches ( t , ctx , labels , mockSDK , metric . Float64NumberKind , metric . CounterKind , c . SyncImpl ( ) ,
1994.1 , - 742 , 42 ,
)
} )
t . Run ( "int64 counter" , func ( 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 ) . NewInt64Counter ( "test.counter.int" )
2019-10-15 18:28:36 +02:00
ctx := context . Background ( )
2020-05-14 01:06:03 +02:00
labels := [ ] kv . KeyValue { kv . String ( "A" , "B" ) , kv . String ( "C" , "D" ) }
2020-03-27 23:06:48 +02:00
c . Add ( ctx , 42 , labels ... )
boundInstrument := c . Bind ( labels ... )
2020-05-19 19:00:22 +02:00
boundInstrument . Add ( ctx , 4200 )
meter . RecordBatch ( ctx , labels , c . Measurement ( 420000 ) )
checkSyncBatches ( t , ctx , labels , mockSDK , metric . Int64NumberKind , metric . CounterKind , c . SyncImpl ( ) ,
42 , 4200 , 420000 ,
)
} )
t . Run ( "int64 updowncounter" , func ( t * testing . T ) {
mockSDK , meter := mockTest . NewMeter ( )
c := Must ( meter ) . NewInt64UpDownCounter ( "test.updowncounter.int" )
ctx := context . Background ( )
labels := [ ] kv . KeyValue { kv . String ( "A" , "B" ) , kv . String ( "C" , "D" ) }
c . Add ( ctx , 100 , labels ... )
boundInstrument := c . Bind ( labels ... )
boundInstrument . Add ( ctx , - 100 )
2019-10-15 18:28:36 +02:00
meter . RecordBatch ( ctx , labels , c . Measurement ( 42 ) )
2020-05-19 19:00:22 +02:00
checkSyncBatches ( t , ctx , labels , mockSDK , metric . Int64NumberKind , metric . UpDownCounterKind , c . SyncImpl ( ) ,
100 , - 100 , 42 ,
)
} )
t . Run ( "float64 updowncounter" , func ( t * testing . T ) {
mockSDK , meter := mockTest . NewMeter ( )
c := Must ( meter ) . NewFloat64UpDownCounter ( "test.updowncounter.float" )
ctx := context . Background ( )
labels := [ ] kv . KeyValue { kv . String ( "A" , "B" ) , kv . String ( "C" , "D" ) }
c . Add ( ctx , 100.1 , labels ... )
boundInstrument := c . Bind ( labels ... )
boundInstrument . Add ( ctx , - 76 )
meter . RecordBatch ( ctx , labels , c . Measurement ( - 100.1 ) )
checkSyncBatches ( t , ctx , labels , mockSDK , metric . Float64NumberKind , metric . UpDownCounterKind , c . SyncImpl ( ) ,
100.1 , - 76 , - 100.1 ,
)
} )
2019-10-15 18:28:36 +02:00
}
2020-05-16 07:11:12 +02:00
func TestValueRecorder ( t * testing . T ) {
2020-05-19 19:00:22 +02:00
t . Run ( "float64 valuerecorder" , func ( t * testing . T ) {
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-05-16 07:11:12 +02:00
m := Must ( meter ) . NewFloat64ValueRecorder ( "test.valuerecorder.float" )
2019-10-15 18:28:36 +02:00
ctx := context . Background ( )
2020-05-14 01:06:03 +02:00
labels := [ ] kv . KeyValue { }
2020-03-27 23:06:48 +02:00
m . Record ( ctx , 42 , labels ... )
boundInstrument := m . Bind ( labels ... )
2020-05-19 19:00:22 +02:00
boundInstrument . Record ( ctx , 0 )
meter . RecordBatch ( ctx , labels , m . Measurement ( - 100.5 ) )
checkSyncBatches ( t , ctx , labels , mockSDK , metric . Float64NumberKind , metric . ValueRecorderKind , m . SyncImpl ( ) ,
42 , 0 , - 100.5 ,
)
} )
t . Run ( "int64 valuerecorder" , func ( t * testing . T ) {
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-05-16 07:11:12 +02:00
m := Must ( meter ) . NewInt64ValueRecorder ( "test.valuerecorder.int" )
2019-10-15 18:28:36 +02:00
ctx := context . Background ( )
2020-05-14 01:06:03 +02:00
labels := [ ] kv . KeyValue { kv . Int ( "I" , 1 ) }
2020-05-19 19:00:22 +02:00
m . Record ( ctx , 173 , labels ... )
2020-03-27 23:06:48 +02:00
boundInstrument := m . Bind ( labels ... )
2020-05-19 19:00:22 +02:00
boundInstrument . Record ( ctx , 80 )
meter . RecordBatch ( ctx , labels , m . Measurement ( 0 ) )
checkSyncBatches ( t , ctx , labels , mockSDK , metric . Int64NumberKind , metric . ValueRecorderKind , m . SyncImpl ( ) ,
173 , 80 , 0 ,
)
} )
2019-10-15 18:28:36 +02:00
}
2020-05-18 20:03:43 +02:00
func TestObserverInstruments ( t * testing . T ) {
2020-05-19 20:49:24 +02:00
t . Run ( "float valueobserver" , func ( t * testing . T ) {
2020-05-14 01:06:03 +02:00
labels := [ ] kv . KeyValue { kv . String ( "O" , "P" ) }
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-05-20 06:33:10 +02:00
o := Must ( meter ) . RegisterFloat64ValueObserver ( "test.valueobserver.float" , func ( _ context . Context , result metric . Float64ObserverResult ) {
2020-05-19 20:49:24 +02:00
result . Observe ( 42.1 , labels ... )
2020-03-05 22:15:30 +02:00
} )
2020-03-19 21:02:46 +02:00
mockSDK . RunAsyncInstruments ( )
2020-05-19 20:49:24 +02:00
checkObserverBatch ( t , labels , mockSDK , metric . Float64NumberKind , metric . ValueObserverKind , o . AsyncImpl ( ) ,
42.1 ,
)
} )
t . Run ( "int valueobserver" , func ( t * testing . T ) {
2020-05-14 01:06:03 +02:00
labels := [ ] kv . KeyValue { }
2020-03-19 21:02:46 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-05-20 06:33:10 +02:00
o := Must ( meter ) . RegisterInt64ValueObserver ( "test.observer.int" , func ( _ context . Context , result metric . Int64ObserverResult ) {
2020-05-19 20:49:24 +02:00
result . Observe ( - 142 , labels ... )
2020-03-05 22:15:30 +02:00
} )
2020-03-19 21:02:46 +02:00
mockSDK . RunAsyncInstruments ( )
2020-05-19 20:49:24 +02:00
checkObserverBatch ( t , labels , mockSDK , metric . Int64NumberKind , metric . ValueObserverKind , o . AsyncImpl ( ) ,
- 142 ,
)
} )
t . Run ( "float sumobserver" , func ( t * testing . T ) {
labels := [ ] kv . KeyValue { kv . String ( "O" , "P" ) }
mockSDK , meter := mockTest . NewMeter ( )
2020-05-20 06:33:10 +02:00
o := Must ( meter ) . RegisterFloat64SumObserver ( "test.sumobserver.float" , func ( _ context . Context , result metric . Float64ObserverResult ) {
2020-05-19 20:49:24 +02:00
result . Observe ( 42.1 , labels ... )
} )
mockSDK . RunAsyncInstruments ( )
checkObserverBatch ( t , labels , mockSDK , metric . Float64NumberKind , metric . SumObserverKind , o . AsyncImpl ( ) ,
42.1 ,
)
} )
t . Run ( "int sumobserver" , func ( t * testing . T ) {
labels := [ ] kv . KeyValue { }
mockSDK , meter := mockTest . NewMeter ( )
2020-05-20 06:33:10 +02:00
o := Must ( meter ) . RegisterInt64SumObserver ( "test.observer.int" , func ( _ context . Context , result metric . Int64ObserverResult ) {
2020-05-19 20:49:24 +02:00
result . Observe ( - 142 , labels ... )
} )
mockSDK . RunAsyncInstruments ( )
checkObserverBatch ( t , labels , mockSDK , metric . Int64NumberKind , metric . SumObserverKind , o . AsyncImpl ( ) ,
- 142 ,
)
} )
2020-03-05 22:15:30 +02:00
}
2020-05-19 19:00:22 +02:00
func checkSyncBatches ( t * testing . T , ctx context . Context , labels [ ] kv . KeyValue , mock * mockTest . MeterImpl , nkind metric . NumberKind , mkind metric . Kind , instrument metric . InstrumentImpl , expected ... float64 ) {
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-05-19 19:00:22 +02:00
require . Equal ( t , mkind , measurement . Instrument . Descriptor ( ) . MetricKind ( ) )
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
}
2020-05-19 19:00:22 +02:00
expect := number ( t , nkind , expected [ i ] )
if measurement . Number . CompareNumber ( nkind , expect ) != 0 {
t . Errorf ( "Wrong recorded value in measurement %d in batch %d, expected %s, got %s" , j , i , expect . Emit ( nkind ) , measurement . Number . Emit ( nkind ) )
2019-10-15 18:28:36 +02:00
}
}
}
}
2020-05-18 20:03:43 +02:00
func TestBatchObserverInstruments ( t * testing . T ) {
2020-05-14 01:27:52 +02:00
mockSDK , meter := mockTest . NewMeter ( )
2020-05-18 20:03:43 +02:00
var obs1 metric . Int64ValueObserver
var obs2 metric . Float64ValueObserver
2020-05-14 01:27:52 +02:00
labels := [ ] kv . KeyValue {
kv . String ( "A" , "B" ) ,
kv . String ( "C" , "D" ) ,
}
cb := Must ( meter ) . NewBatchObserver (
2020-05-20 06:33:10 +02:00
func ( _ context . Context , result metric . BatchObserverResult ) {
2020-05-14 01:27:52 +02:00
result . Observe ( labels ,
obs1 . Observation ( 42 ) ,
obs2 . Observation ( 42.0 ) ,
)
} ,
)
2020-05-18 20:03:43 +02:00
obs1 = cb . RegisterInt64ValueObserver ( "test.observer.int" )
obs2 = cb . RegisterFloat64ValueObserver ( "test.observer.float" )
2020-05-14 01:27:52 +02:00
mockSDK . RunAsyncInstruments ( )
require . Len ( t , mockSDK . MeasurementBatches , 1 )
impl1 := obs1 . AsyncImpl ( ) . Implementation ( ) . ( * mockTest . Async )
impl2 := obs2 . AsyncImpl ( ) . Implementation ( ) . ( * mockTest . Async )
require . NotNil ( t , impl1 )
require . NotNil ( t , impl2 )
got := mockSDK . MeasurementBatches [ 0 ]
require . Equal ( t , labels , got . Labels )
require . Len ( t , got . Measurements , 2 )
m1 := got . Measurements [ 0 ]
require . Equal ( t , impl1 , m1 . Instrument . Implementation ( ) . ( * mockTest . Async ) )
2020-05-19 19:00:22 +02:00
require . Equal ( t , 0 , m1 . Number . CompareNumber ( metric . Int64NumberKind , number ( t , metric . Int64NumberKind , 42 ) ) )
2020-05-14 01:27:52 +02:00
m2 := got . Measurements [ 1 ]
require . Equal ( t , impl2 , m2 . Instrument . Implementation ( ) . ( * mockTest . Async ) )
2020-05-19 19:00:22 +02:00
require . Equal ( t , 0 , m2 . Number . CompareNumber ( metric . Float64NumberKind , number ( t , metric . Float64NumberKind , 42 ) ) )
2020-05-14 01:27:52 +02:00
}
2020-05-19 20:49:24 +02:00
func checkObserverBatch ( t * testing . T , labels [ ] kv . KeyValue , mock * mockTest . MeterImpl , nkind metric . NumberKind , mkind metric . Kind , observer metric . AsyncImpl , expected float64 ) {
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-05-19 20:49:24 +02:00
require . Equal ( t , mkind , measurement . Instrument . Descriptor ( ) . MetricKind ( ) )
2020-03-19 21:02:46 +02:00
assert . Equal ( t , o , measurement . Instrument . Implementation ( ) . ( * mockTest . Async ) )
2020-05-19 20:49:24 +02:00
ft := number ( t , nkind , expected )
assert . Equal ( t , 0 , measurement . Number . CompareNumber ( nkind , ft ) )
2020-03-05 22:15:30 +02:00
}
2020-05-19 19:00:22 +02:00
func number ( t * testing . T , kind metric . NumberKind , value float64 ) 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 :
2020-05-19 19:00:22 +02:00
return metric . NewInt64Number ( int64 ( value ) )
2020-05-11 08:44:42 +02:00
case metric . Float64NumberKind :
2020-05-19 19:00:22 +02:00
return metric . NewFloat64Number ( value )
2019-10-15 18:28:36 +02:00
}
2020-05-19 19:00:22 +02:00
panic ( "invalid number kind" )
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-05-14 01:06:03 +02:00
func ( testWrappedMeter ) RecordBatch ( context . Context , [ ] kv . 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-14 01:27:52 +02:00
func ( testWrappedMeter ) NewAsyncInstrument ( _ metric . Descriptor , _ metric . AsyncRunner ) ( 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-05-16 07:11:12 +02:00
valuerecorder , err := meter . NewInt64ValueRecorder ( "test.valuerecorder" )
2020-03-11 20:57:57 +02:00
2020-03-19 21:02:46 +02:00
require . Equal ( t , err , metric . ErrSDKReturnedNilImpl )
2020-05-16 07:11:12 +02:00
require . NotNil ( t , valuerecorder . SyncImpl ( ) )
2020-03-11 20:57:57 +02:00
2020-05-20 06:33:10 +02:00
observer , err := meter . RegisterInt64ValueObserver ( "test.observer" , func ( _ context . Context , 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 ( )
2020-05-18 20:03:43 +02:00
observer := Must ( meter ) . RegisterInt64ValueObserver ( "test.observer" , nil )
2020-03-19 21:02:46 +02:00
_ , ok := observer . AsyncImpl ( ) . ( metric . NoopAsync )
require . True ( t , ok )
2020-03-11 20:57:57 +02:00
}