package sentry import ( "fmt" "sync" "testing" ) func TestFixedRateSampler(t *testing.T) { ctx := NewTestContext(ClientOptions{}) rootSpan := StartSpan(ctx, "root") t.Run("UniformRate", func(t *testing.T) { // The sample decision for the root span should observe the configured // rate. tests := []struct { Rate float64 Tolerance float64 }{ {0.0, 0.0}, {0.25, 0.1}, {0.5, 0.1}, {0.75, 0.1}, {1.0, 0.0}, } for _, tt := range tests { tt := tt t.Run(fmt.Sprint(tt.Rate), func(t *testing.T) { got := repeatedSample(func(_ SamplingContext) float64 { return tt.Rate }, SamplingContext{Span: rootSpan}, 10000) if got < tt.Rate*(1-tt.Tolerance) || got > tt.Rate*(1+tt.Tolerance) { t.Errorf("got rootSpan sample rate %.2f, want %.2f±%.0f%%", got, tt.Rate, 100*tt.Tolerance) } }) } }) t.Run("Concurrency", func(_ *testing.T) { // This test is for use with -race to catch data races. var wg sync.WaitGroup for i := 0; i < 32; i++ { wg.Add(1) go func() { defer wg.Done() repeatedSample(func(_ SamplingContext) float64 { return 0.5 }, SamplingContext{Span: rootSpan}, 10000) }() } wg.Wait() }) } func repeatedSample(sampler TracesSampler, ctx SamplingContext, count int) (observedRate float64) { var n float64 for i := 0; i < count; i++ { sampleRate := sampler.Sample(ctx) if rng.Float64() < sampleRate { n++ } } return n / float64(count) }