You've already forked opentelemetry-go
							
							
				mirror of
				https://github.com/open-telemetry/opentelemetry-go.git
				synced 2025-10-31 00:07:40 +02:00 
			
		
		
		
	styleguide: tests goroutine leaks and naming (#4348)
* internal/global: Fix goroutine leaks in tests
This commit is contained in:
		| @@ -556,6 +556,13 @@ functionality should be added, each one will need their own super-set | ||||
| interfaces and will duplicate the pattern. For this reason, the simple targeted | ||||
| interface that defines the specific functionality should be preferred. | ||||
|  | ||||
| ### Testing | ||||
|  | ||||
| The tests should never leak goroutines. | ||||
|  | ||||
| Use the term `ConcurrentSafe` in the test name when it aims to verify the | ||||
| absence of race conditions. | ||||
|  | ||||
| ## Approvers and Maintainers | ||||
|  | ||||
| ### Approvers | ||||
|   | ||||
| @@ -227,7 +227,7 @@ func TestRetryNotEnabled(t *testing.T) { | ||||
| 	}), assert.AnError) | ||||
| } | ||||
|  | ||||
| func TestConcurrentRetry(t *testing.T) { | ||||
| func TestRetryConcurrentSafe(t *testing.T) { | ||||
| 	ev := func(error) (bool, time.Duration) { return true, 0 } | ||||
| 	reqFunc := Config{ | ||||
| 		Enabled: true, | ||||
|   | ||||
| @@ -56,7 +56,7 @@ func (c *client) Shutdown(context.Context) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func TestExporterClientConcurrency(t *testing.T) { | ||||
| func TestExporterClientConcurrentSafe(t *testing.T) { | ||||
| 	const goroutines = 5 | ||||
|  | ||||
| 	exp := New(&client{}) | ||||
|   | ||||
| @@ -330,7 +330,7 @@ func TestDeadlineContext(t *testing.T) { | ||||
| 	assert.Empty(t, mc.GetSpans()) | ||||
| } | ||||
|  | ||||
| func TestStopWhileExporting(t *testing.T) { | ||||
| func TestStopWhileExportingConcurrentSafe(t *testing.T) { | ||||
| 	statuses := make([]int, 0, 5) | ||||
| 	for i := 0; i < cap(statuses); i++ { | ||||
| 		statuses = append(statuses, http.StatusTooManyRequests) | ||||
|   | ||||
| @@ -19,7 +19,7 @@ import ( | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/suite" | ||||
| @@ -123,9 +123,24 @@ func TestHandlerTestSuite(t *testing.T) { | ||||
| 	suite.Run(t, new(HandlerTestSuite)) | ||||
| } | ||||
|  | ||||
| func TestHandlerRace(t *testing.T) { | ||||
| 	go SetErrorHandler(&ErrLogger{log.New(os.Stderr, "", 0)}) | ||||
| 	go Handle(errors.New("error")) | ||||
| func TestHandlerConcurrentSafe(t *testing.T) { | ||||
| 	// In order not to pollute the test output. | ||||
| 	SetErrorHandler(&ErrLogger{log.New(io.Discard, "", 0)}) | ||||
|  | ||||
| 	var wg sync.WaitGroup | ||||
| 	wg.Add(1) | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		SetErrorHandler(&ErrLogger{log.New(io.Discard, "", 0)}) | ||||
| 	}() | ||||
| 	wg.Add(1) | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		Handle(errors.New("error")) | ||||
| 	}() | ||||
|  | ||||
| 	wg.Wait() | ||||
| 	reset() | ||||
| } | ||||
|  | ||||
| func BenchmarkErrorHandler(b *testing.B) { | ||||
|   | ||||
| @@ -23,9 +23,11 @@ import ( | ||||
| 	"go.opentelemetry.io/otel/metric/noop" | ||||
| ) | ||||
|  | ||||
| func testFloat64Race(interact func(float64), setDelegate func(metric.Meter)) { | ||||
| func testFloat64ConcurrentSafe(interact func(float64), setDelegate func(metric.Meter)) { | ||||
| 	done := make(chan struct{}) | ||||
| 	finish := make(chan struct{}) | ||||
| 	go func() { | ||||
| 		defer close(done) | ||||
| 		for { | ||||
| 			interact(1) | ||||
| 			select { | ||||
| @@ -38,11 +40,14 @@ func testFloat64Race(interact func(float64), setDelegate func(metric.Meter)) { | ||||
|  | ||||
| 	setDelegate(noop.NewMeterProvider().Meter("")) | ||||
| 	close(finish) | ||||
| 	<-done | ||||
| } | ||||
|  | ||||
| func testInt64Race(interact func(int64), setDelegate func(metric.Meter)) { | ||||
| func testInt64ConcurrentSafe(interact func(int64), setDelegate func(metric.Meter)) { | ||||
| 	done := make(chan struct{}) | ||||
| 	finish := make(chan struct{}) | ||||
| 	go func() { | ||||
| 		defer close(done) | ||||
| 		for { | ||||
| 			interact(1) | ||||
| 			select { | ||||
| @@ -55,27 +60,28 @@ func testInt64Race(interact func(int64), setDelegate func(metric.Meter)) { | ||||
|  | ||||
| 	setDelegate(noop.NewMeterProvider().Meter("")) | ||||
| 	close(finish) | ||||
| 	<-done | ||||
| } | ||||
|  | ||||
| func TestAsyncInstrumentSetDelegateRace(t *testing.T) { | ||||
| func TestAsyncInstrumentSetDelegateConcurrentSafe(t *testing.T) { | ||||
| 	// Float64 Instruments | ||||
| 	t.Run("Float64", func(t *testing.T) { | ||||
| 		t.Run("Counter", func(t *testing.T) { | ||||
| 			delegate := &afCounter{} | ||||
| 			f := func(float64) { _ = delegate.Unwrap() } | ||||
| 			testFloat64Race(f, delegate.setDelegate) | ||||
| 			testFloat64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("UpDownCounter", func(t *testing.T) { | ||||
| 			delegate := &afUpDownCounter{} | ||||
| 			f := func(float64) { _ = delegate.Unwrap() } | ||||
| 			testFloat64Race(f, delegate.setDelegate) | ||||
| 			testFloat64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("Gauge", func(t *testing.T) { | ||||
| 			delegate := &afGauge{} | ||||
| 			f := func(float64) { _ = delegate.Unwrap() } | ||||
| 			testFloat64Race(f, delegate.setDelegate) | ||||
| 			testFloat64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
| 	}) | ||||
|  | ||||
| @@ -85,42 +91,42 @@ func TestAsyncInstrumentSetDelegateRace(t *testing.T) { | ||||
| 		t.Run("Counter", func(t *testing.T) { | ||||
| 			delegate := &aiCounter{} | ||||
| 			f := func(int64) { _ = delegate.Unwrap() } | ||||
| 			testInt64Race(f, delegate.setDelegate) | ||||
| 			testInt64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("UpDownCounter", func(t *testing.T) { | ||||
| 			delegate := &aiUpDownCounter{} | ||||
| 			f := func(int64) { _ = delegate.Unwrap() } | ||||
| 			testInt64Race(f, delegate.setDelegate) | ||||
| 			testInt64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("Gauge", func(t *testing.T) { | ||||
| 			delegate := &aiGauge{} | ||||
| 			f := func(int64) { _ = delegate.Unwrap() } | ||||
| 			testInt64Race(f, delegate.setDelegate) | ||||
| 			testInt64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestSyncInstrumentSetDelegateRace(t *testing.T) { | ||||
| func TestSyncInstrumentSetDelegateConcurrentSafe(t *testing.T) { | ||||
| 	// Float64 Instruments | ||||
| 	t.Run("Float64", func(t *testing.T) { | ||||
| 		t.Run("Counter", func(t *testing.T) { | ||||
| 			delegate := &sfCounter{} | ||||
| 			f := func(v float64) { delegate.Add(context.Background(), v) } | ||||
| 			testFloat64Race(f, delegate.setDelegate) | ||||
| 			testFloat64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("UpDownCounter", func(t *testing.T) { | ||||
| 			delegate := &sfUpDownCounter{} | ||||
| 			f := func(v float64) { delegate.Add(context.Background(), v) } | ||||
| 			testFloat64Race(f, delegate.setDelegate) | ||||
| 			testFloat64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("Histogram", func(t *testing.T) { | ||||
| 			delegate := &sfHistogram{} | ||||
| 			f := func(v float64) { delegate.Record(context.Background(), v) } | ||||
| 			testFloat64Race(f, delegate.setDelegate) | ||||
| 			testFloat64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
| 	}) | ||||
|  | ||||
| @@ -130,19 +136,19 @@ func TestSyncInstrumentSetDelegateRace(t *testing.T) { | ||||
| 		t.Run("Counter", func(t *testing.T) { | ||||
| 			delegate := &siCounter{} | ||||
| 			f := func(v int64) { delegate.Add(context.Background(), v) } | ||||
| 			testInt64Race(f, delegate.setDelegate) | ||||
| 			testInt64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("UpDownCounter", func(t *testing.T) { | ||||
| 			delegate := &siUpDownCounter{} | ||||
| 			f := func(v int64) { delegate.Add(context.Background(), v) } | ||||
| 			testInt64Race(f, delegate.setDelegate) | ||||
| 			testInt64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("Histogram", func(t *testing.T) { | ||||
| 			delegate := &siHistogram{} | ||||
| 			f := func(v int64) { delegate.Record(context.Background(), v) } | ||||
| 			testInt64Race(f, delegate.setDelegate) | ||||
| 			testInt64ConcurrentSafe(f, delegate.setDelegate) | ||||
| 		}) | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -17,8 +17,9 @@ package global | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"sync" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/go-logr/logr" | ||||
| @@ -29,9 +30,21 @@ import ( | ||||
| 	"github.com/go-logr/stdr" | ||||
| ) | ||||
|  | ||||
| func TestRace(t *testing.T) { | ||||
| 	go SetLogger(stdr.New(log.New(os.Stderr, "", 0))) | ||||
| 	go Info("") | ||||
| func TestLoggerConcurrentSafe(t *testing.T) { | ||||
| 	var wg sync.WaitGroup | ||||
| 	wg.Add(1) | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		SetLogger(stdr.New(log.New(io.Discard, "", 0))) | ||||
| 	}() | ||||
| 	wg.Add(1) | ||||
| 	go func() { | ||||
| 		defer wg.Done() | ||||
| 		Info("") | ||||
| 	}() | ||||
|  | ||||
| 	wg.Wait() | ||||
| 	reset() | ||||
| } | ||||
|  | ||||
| func TestLogLevel(t *testing.T) { | ||||
|   | ||||
| @@ -27,10 +27,12 @@ import ( | ||||
| 	"go.opentelemetry.io/otel/metric/noop" | ||||
| ) | ||||
|  | ||||
| func TestMeterProviderRace(t *testing.T) { | ||||
| func TestMeterProviderConcurrentSafe(t *testing.T) { | ||||
| 	mp := &meterProvider{} | ||||
| 	done := make(chan struct{}) | ||||
| 	finish := make(chan struct{}) | ||||
| 	go func() { | ||||
| 		defer close(done) | ||||
| 		for i := 0; ; i++ { | ||||
| 			mp.Meter(fmt.Sprintf("a%d", i)) | ||||
| 			select { | ||||
| @@ -43,19 +45,22 @@ func TestMeterProviderRace(t *testing.T) { | ||||
|  | ||||
| 	mp.setDelegate(noop.NewMeterProvider()) | ||||
| 	close(finish) | ||||
| 	<-done | ||||
| } | ||||
|  | ||||
| var zeroCallback metric.Callback = func(ctx context.Context, or metric.Observer) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func TestMeterRace(t *testing.T) { | ||||
| func TestMeterConcurrentSafe(t *testing.T) { | ||||
| 	mtr := &meter{} | ||||
|  | ||||
| 	wg := &sync.WaitGroup{} | ||||
| 	wg.Add(1) | ||||
| 	done := make(chan struct{}) | ||||
| 	finish := make(chan struct{}) | ||||
| 	go func() { | ||||
| 		defer close(done) | ||||
| 		for i, once := 0, false; ; i++ { | ||||
| 			name := fmt.Sprintf("a%d", i) | ||||
| 			_, _ = mtr.Float64ObservableCounter(name) | ||||
| @@ -86,17 +91,20 @@ func TestMeterRace(t *testing.T) { | ||||
| 	wg.Wait() | ||||
| 	mtr.setDelegate(noop.NewMeterProvider()) | ||||
| 	close(finish) | ||||
| 	<-done | ||||
| } | ||||
|  | ||||
| func TestUnregisterRace(t *testing.T) { | ||||
| func TestUnregisterConcurrentSafe(t *testing.T) { | ||||
| 	mtr := &meter{} | ||||
| 	reg, err := mtr.RegisterCallback(zeroCallback) | ||||
| 	require.NoError(t, err) | ||||
|  | ||||
| 	wg := &sync.WaitGroup{} | ||||
| 	wg.Add(1) | ||||
| 	done := make(chan struct{}) | ||||
| 	finish := make(chan struct{}) | ||||
| 	go func() { | ||||
| 		defer close(done) | ||||
| 		for i, once := 0, false; ; i++ { | ||||
| 			_ = reg.Unregister() | ||||
| 			if !once { | ||||
| @@ -115,6 +123,7 @@ func TestUnregisterRace(t *testing.T) { | ||||
| 	wg.Wait() | ||||
| 	mtr.setDelegate(noop.NewMeterProvider()) | ||||
| 	close(finish) | ||||
| 	<-done | ||||
| } | ||||
|  | ||||
| func testSetupAllInstrumentTypes(t *testing.T, m metric.Meter) (metric.Float64Counter, metric.Float64ObservableCounter) { | ||||
|   | ||||
| @@ -101,7 +101,7 @@ func testConfAttr(newConf func(...MeasurementOption) attrConf) func(t *testing.T | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestWithAttributesRace(t *testing.T) { | ||||
| func TestWithAttributesConcurrentSafe(t *testing.T) { | ||||
| 	attrs := []attribute.KeyValue{ | ||||
| 		attribute.String("user", "Alice"), | ||||
| 		attribute.Bool("admin", true), | ||||
|   | ||||
| @@ -40,7 +40,7 @@ func TestCache(t *testing.T) { | ||||
| 	assert.Equal(t, v1, c.Lookup(k1, func() int { return v1 }), "non-existing key") | ||||
| } | ||||
|  | ||||
| func TestCacheConcurrency(t *testing.T) { | ||||
| func TestCacheConcurrentSafe(t *testing.T) { | ||||
| 	const ( | ||||
| 		key        = "k" | ||||
| 		goroutines = 10 | ||||
|   | ||||
| @@ -78,7 +78,7 @@ func (at *aggregatorTester[N]) Run(a aggregator[N], incr setMap[N], eFunc expect | ||||
| 			}) | ||||
| 		}) | ||||
|  | ||||
| 		t.Run("Correctness", func(t *testing.T) { | ||||
| 		t.Run("CorrectnessConcurrentSafe", func(t *testing.T) { | ||||
| 			for i := 0; i < at.CycleN; i++ { | ||||
| 				var wg sync.WaitGroup | ||||
| 				wg.Add(at.GoroutineN) | ||||
|   | ||||
| @@ -37,7 +37,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| // A meter should be able to make instruments concurrently. | ||||
| func TestMeterInstrumentConcurrency(t *testing.T) { | ||||
| func TestMeterInstrumentConcurrentSafe(t *testing.T) { | ||||
| 	wg := &sync.WaitGroup{} | ||||
| 	wg.Add(12) | ||||
|  | ||||
|   | ||||
| @@ -81,7 +81,7 @@ func TestPipelineUsesResource(t *testing.T) { | ||||
| 	assert.Equal(t, res, output.Resource) | ||||
| } | ||||
|  | ||||
| func TestPipelineConcurrency(t *testing.T) { | ||||
| func TestPipelineConcurrentSafe(t *testing.T) { | ||||
| 	pipe := newPipeline(nil, nil, nil) | ||||
| 	ctx := context.Background() | ||||
| 	var output metricdata.ResourceMetrics | ||||
|   | ||||
| @@ -157,7 +157,7 @@ func (ts *readerTestSuite) TestSDKFailureBlocksExternalProducer() { | ||||
| 	ts.Equal(metricdata.ResourceMetrics{}, m) | ||||
| } | ||||
|  | ||||
| func (ts *readerTestSuite) TestMethodConcurrency() { | ||||
| func (ts *readerTestSuite) TestMethodConcurrentSafe() { | ||||
| 	// Requires the race-detector (a default test option for the project). | ||||
|  | ||||
| 	// All reader methods should be concurrent-safe. | ||||
|   | ||||
| @@ -120,7 +120,7 @@ func TestSimpleSpanProcessorShutdown(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSimpleSpanProcessorShutdownOnEndConcurrency(t *testing.T) { | ||||
| func TestSimpleSpanProcessorShutdownOnEndConcurrentSafe(t *testing.T) { | ||||
| 	exporter := &testExporter{} | ||||
| 	ssp := sdktrace.NewSimpleSpanProcessor(exporter) | ||||
| 	tp := basicTracerProvider(t) | ||||
| @@ -153,7 +153,7 @@ func TestSimpleSpanProcessorShutdownOnEndConcurrency(t *testing.T) { | ||||
| 	<-done | ||||
| } | ||||
|  | ||||
| func TestSimpleSpanProcessorShutdownOnEndRace(t *testing.T) { | ||||
| func TestSimpleSpanProcessorShutdownOnEndConcurrentSafe2(t *testing.T) { | ||||
| 	exporter := &testExporter{} | ||||
| 	ssp := sdktrace.NewSimpleSpanProcessor(exporter) | ||||
| 	tp := basicTracerProvider(t) | ||||
|   | ||||
| @@ -99,7 +99,7 @@ func runConcurrently(funcs ...func()) { | ||||
| 	wg.Wait() | ||||
| } | ||||
|  | ||||
| func TestEndingConcurrency(t *testing.T) { | ||||
| func TestEndingConcurrentSafe(t *testing.T) { | ||||
| 	sr := NewSpanRecorder() | ||||
|  | ||||
| 	runConcurrently( | ||||
| @@ -111,7 +111,7 @@ func TestEndingConcurrency(t *testing.T) { | ||||
| 	assert.Len(t, sr.Ended(), 2) | ||||
| } | ||||
|  | ||||
| func TestStartingConcurrency(t *testing.T) { | ||||
| func TestStartingConcurrentSafe(t *testing.T) { | ||||
| 	sr := NewSpanRecorder() | ||||
|  | ||||
| 	ctx := context.Background() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user