Skip to content

Commit

Permalink
Generalize tests to metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
punya committed Oct 16, 2023
1 parent 3ae284a commit 155c74e
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 56 deletions.
27 changes: 20 additions & 7 deletions exporters/autoexport/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ import (
"github.com/stretchr/testify/assert"

"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/trace"
)

func TestOTLPExporterReturnedWhenNoEnvOrFallbackExporterConfigured(t *testing.T) {
exporter, err := NewSpanExporter(context.Background())
assert.NoError(t, err)
assertOTLPHTTPExporter(t, exporter)
assertOTLPHTTPSpanExporter(t, exporter)
}

func TestFallbackExporterReturnedWhenNoEnvExporterConfigured(t *testing.T) {
Expand All @@ -51,7 +52,7 @@ func TestEnvExporterIsPreferredOverFallbackExporter(t *testing.T) {
WithFallbackSpanExporter(testExporter),
)
assert.NoError(t, err)
assertOTLPHTTPExporter(t, exporter)
assertOTLPHTTPSpanExporter(t, exporter)
}

func TestEnvExporterOTLPOverHTTP(t *testing.T) {
Expand All @@ -60,7 +61,7 @@ func TestEnvExporterOTLPOverHTTP(t *testing.T) {

exporter, err := NewSpanExporter(context.Background())
assert.NoError(t, err)
assertOTLPHTTPExporter(t, exporter)
assertOTLPHTTPSpanExporter(t, exporter)
}

func TestEnvExporterOTLPOverGRPC(t *testing.T) {
Expand All @@ -69,15 +70,15 @@ func TestEnvExporterOTLPOverGRPC(t *testing.T) {

exporter, err := NewSpanExporter(context.Background())
assert.NoError(t, err)
assertOTLPGRPCExporter(t, exporter)
assertOTLPGRPCSpanExporter(t, exporter)
}

func TestEnvExporterOTLPOverGRPCOnlyProtocol(t *testing.T) {
t.Setenv("OTEL_EXPORTER_OTLP_PROTOCOL", "grpc")

exporter, err := NewSpanExporter(context.Background())
assert.NoError(t, err)
assertOTLPGRPCExporter(t, exporter)
assertOTLPGRPCSpanExporter(t, exporter)
}

func TestEnvExporterOTLPInvalidProtocol(t *testing.T) {
Expand All @@ -97,7 +98,19 @@ func TestEnvExporterNone(t *testing.T) {
assert.True(t, IsNoneSpanExporter(exporter))
}

func assertOTLPHTTPExporter(t *testing.T, got trace.SpanExporter) {
func assertOTLPHTTPMetricReader(t *testing.T, got metric.Reader) {
t.Helper()

if !assert.IsType(t, metric.NewPeriodicReader(nil), got) {
return
}

// Implementation detail hack. This may break when bumping OTLP exporter modules as it uses unexported API.
clientType := reflect.Indirect(reflect.ValueOf(got)).FieldByName("exporter").Elem().Type().String()
assert.Equal(t, "*internal.exporter", clientType)
}

func assertOTLPHTTPSpanExporter(t *testing.T, got trace.SpanExporter) {
t.Helper()

if !assert.IsType(t, &otlptrace.Exporter{}, got) {
Expand All @@ -111,7 +124,7 @@ func assertOTLPHTTPExporter(t *testing.T, got trace.SpanExporter) {
assert.False(t, IsNoneSpanExporter(got))
}

func assertOTLPGRPCExporter(t *testing.T, got trace.SpanExporter) {
func assertOTLPGRPCSpanExporter(t *testing.T, got trace.SpanExporter) {
t.Helper()

if !assert.IsType(t, &otlptrace.Exporter{}, got) {
Expand Down
141 changes: 92 additions & 49 deletions exporters/autoexport/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ import (
"go.opentelemetry.io/otel/sdk/trace"
)

type testType struct{ string }

func factory(val string) func(ctx context.Context) (*testType, error) {
return func(ctx context.Context) (*testType, error) { return &testType{val}, nil }
}

func newTestRegistry() registry[*testType] {
return registry[*testType]{
names: map[string]func(context.Context) (*testType, error){
"": factory(""),
"otlp": factory("otlp"),
"none": factory("none"),
},
}
}

var stdoutMetricFactory = func(ctx context.Context) (metric.Reader, error) {
exp, err := stdoutmetric.New()
if err != nil {
Expand All @@ -46,45 +62,27 @@ var stdoutSpanFactory = func(ctx context.Context) (trace.SpanExporter, error) {
}

func TestCanStoreExporterFactory(t *testing.T) {
t.Run("spans", func(t *testing.T) {
r := newSpanExporterRegistry()
assert.NotPanics(t, func() {
require.NoError(t, r.store("first", stdoutSpanFactory))
})
})
t.Run("metrics", func(t *testing.T) {
r := newMetricReaderRegistry()
assert.NotPanics(t, func() {
require.NoError(t, r.store("first", stdoutMetricFactory))
})
r := newTestRegistry()
assert.NotPanics(t, func() {
require.NoError(t, r.store("first", factory("first")))
})
}

func TestLoadOfUnknownExporterReturnsError(t *testing.T) {
t.Run("spans", func(t *testing.T) {
r := newSpanExporterRegistry()
assert.NotPanics(t, func() {
exp, err := r.load(context.Background(), "non-existent")
assert.Equal(t, err, errUnknownExporter, "empty registry should hold nothing")
assert.Nil(t, exp, "non-nil exporter returned")
})
})
t.Run("metrics", func(t *testing.T) {
r := newMetricReaderRegistry()
assert.NotPanics(t, func() {
exp, err := r.load(context.Background(), "non-existent")
assert.Equal(t, err, errUnknownExporter, "empty registry should hold nothing")
assert.Nil(t, exp, "non-nil exporter returned")
})
r := newTestRegistry()
assert.NotPanics(t, func() {
exp, err := r.load(context.Background(), "non-existent")
assert.Equal(t, err, errUnknownExporter, "empty registry should hold nothing")
assert.Nil(t, exp, "non-nil exporter returned")
})
}

func TestRegistryIsConcurrentSafe(t *testing.T) {
const exporterName = "stdout"

r := newSpanExporterRegistry()
r := newTestRegistry()
assert.NotPanics(t, func() {
require.NoError(t, r.store(exporterName, stdoutSpanFactory))
require.NoError(t, r.store(exporterName, factory("stdout")))
})

var wg sync.WaitGroup
Expand All @@ -93,63 +91,108 @@ func TestRegistryIsConcurrentSafe(t *testing.T) {
go func() {
defer wg.Done()
assert.NotPanics(t, func() {
require.ErrorIs(t, r.store(exporterName, stdoutSpanFactory), errDuplicateRegistration)
require.ErrorIs(t, r.store(exporterName, factory("stdout")), errDuplicateRegistration)
})
}()

wg.Add(1)
go func() {
defer wg.Done()
assert.NotPanics(t, func() {
exp, err := r.load(context.Background(), exporterName)
_, err := r.load(context.Background(), exporterName)
assert.NoError(t, err, "missing exporter in registry")
assert.IsType(t, &stdouttrace.Exporter{}, exp)
})
}()

wg.Wait()
}

func TestSubsequentCallsToGetExporterReturnsNewInstances(t *testing.T) {
type funcs[T any] struct {
makeExporter func(context.Context, string) (T, error)
assertOTLPHTTP func(*testing.T, T)
registerFactory func(string, func(context.Context) (T, error))
stdoutFactory func(ctx context.Context) (T, error)
registry *registry[T]
}

var (
spanFuncs = funcs[trace.SpanExporter]{
makeExporter: spanExporter,
assertOTLPHTTP: assertOTLPHTTPSpanExporter,
registerFactory: RegisterSpanExporter,
stdoutFactory: stdoutSpanFactory,
registry: &spanExporterRegistry,
}

metricFuncs = funcs[metric.Reader]{
makeExporter: metricReader,
assertOTLPHTTP: assertOTLPHTTPMetricReader,
registerFactory: RegisterMetricReader,
stdoutFactory: stdoutMetricFactory,
registry: &metricReaderRegistry,
}
)

func (f funcs[T]) testSubsequentCallsToGetExporterReturnsNewInstances(t *testing.T) {
const exporterType = "otlp"
exp1, err := spanExporter(context.Background(), exporterType)

exp1, err := f.makeExporter(context.Background(), exporterType)
assert.NoError(t, err)
assertOTLPHTTPExporter(t, exp1)
f.assertOTLPHTTP(t, exp1)

exp2, err := spanExporter(context.Background(), exporterType)
assert.NoError(t, err)
assertOTLPHTTPExporter(t, exp2)
assertOTLPHTTPSpanExporter(t, exp2)

assert.NotSame(t, exp1, exp2)
}

func TestDefaultOTLPExporterFactoriesAreAutomaticallyRegistered(t *testing.T) {
exp1, err := spanExporter(context.Background(), "")
func TestSubsequentCallsToGetExporterReturnsNewInstances(t *testing.T) {
t.Run("spans", spanFuncs.testSubsequentCallsToGetExporterReturnsNewInstances)
t.Run("metrics", metricFuncs.testSubsequentCallsToGetExporterReturnsNewInstances)
}

func (f funcs[T]) testDefaultOTLPExporterFactoriesAreAutomaticallyRegistered(t *testing.T) {
exp1, err := f.makeExporter(context.Background(), "")
assert.Nil(t, err)
assertOTLPHTTPExporter(t, exp1)
f.assertOTLPHTTP(t, exp1)

exp2, err := spanExporter(context.Background(), "otlp")
exp2, err := f.makeExporter(context.Background(), "otlp")
assert.Nil(t, err)
assertOTLPHTTPExporter(t, exp2)
f.assertOTLPHTTP(t, exp2)
}

func TestEnvRegistryCanRegisterExporterFactory(t *testing.T) {
func TestDefaultOTLPExporterFactoriesAreAutomaticallyRegistered(t *testing.T) {
t.Run("spans", spanFuncs.testDefaultOTLPExporterFactoriesAreAutomaticallyRegistered)
t.Run("metrics", metricFuncs.testDefaultOTLPExporterFactoriesAreAutomaticallyRegistered)
}

func (f funcs[T]) testEnvRegistryCanRegisterExporterFactory(t *testing.T) {
const exporterName = "custom"
RegisterSpanExporter(exporterName, stdoutSpanFactory)
t.Cleanup(func() { spanExporterRegistry.drop(exporterName) })
f.registerFactory(exporterName, f.stdoutFactory)
t.Cleanup(func() { f.registry.drop(exporterName) })

exp, err := spanExporterRegistry.load(context.Background(), exporterName)
_, err := f.registry.load(context.Background(), exporterName)
assert.Nil(t, err, "missing exporter in envRegistry")
assert.IsType(t, &stdouttrace.Exporter{}, exp)
}

func TestEnvRegistryPanicsOnDuplicateRegisterCalls(t *testing.T) {
func TestEnvRegistryCanRegisterExporterFactory(t *testing.T) {
t.Run("spans", spanFuncs.testEnvRegistryCanRegisterExporterFactory)
t.Run("metrics", metricFuncs.testEnvRegistryCanRegisterExporterFactory)
}

func (f funcs[T]) testEnvRegistryPanicsOnDuplicateRegisterCalls(t *testing.T) {
const exporterName = "custom"
RegisterSpanExporter(exporterName, stdoutSpanFactory)
t.Cleanup(func() { spanExporterRegistry.drop(exporterName) })
f.registerFactory(exporterName, f.stdoutFactory)
t.Cleanup(func() { f.registry.drop(exporterName) })

errString := fmt.Sprintf("%s: %q", errDuplicateRegistration, exporterName)
assert.PanicsWithError(t, errString, func() {
RegisterSpanExporter(exporterName, stdoutSpanFactory)
f.registerFactory(exporterName, f.stdoutFactory)
})
}

func TestEnvRegistryPanicsOnDuplicateRegisterCalls(t *testing.T) {
t.Run("spans", spanFuncs.testEnvRegistryPanicsOnDuplicateRegisterCalls)
t.Run("metrics", metricFuncs.testEnvRegistryPanicsOnDuplicateRegisterCalls)
}

0 comments on commit 155c74e

Please sign in to comment.