diff --git a/internal/config/concurrency_limits_internal_test.go b/internal/config/concurrency_limits_internal_test.go index 9639bdc..33634a4 100644 --- a/internal/config/concurrency_limits_internal_test.go +++ b/internal/config/concurrency_limits_internal_test.go @@ -33,6 +33,35 @@ func TestOmittedConcurrencyLimitsUseDefaults(t *testing.T) { } } +// TestOmittedMaxConcurrentProcessingIsHalfTheCPUsAndAtLeastOne checks +// that an omitted max_concurrent_processing is half the CPUs Go uses, +// rounded down, and 1 when Go uses one CPU. +// +//nolint:paralleltest // it sets GOMAXPROCS, which other tests read +func TestOmittedMaxConcurrentProcessingIsHalfTheCPUsAndAtLeastOne(t *testing.T) { + cpusBefore := runtime.GOMAXPROCS(0) + t.Cleanup(func() { runtime.GOMAXPROCS(cpusBefore) }) + + for _, tc := range []struct{ cpus, want int }{ + {1, 1}, {2, 1}, {3, 1}, {8, 4}, + } { + runtime.GOMAXPROCS(tc.cpus) + + c, err := configFromYAML(t, signingKeyLine) + if err != nil { + t.Fatalf("minimal config should be valid, got error: %v", err) + } + + t.Logf("GOMAXPROCS %d: MaxConcurrentProcessing %d", + tc.cpus, c.MaxConcurrentProcessing) + + if c.MaxConcurrentProcessing != tc.want { + t.Errorf("GOMAXPROCS %d: MaxConcurrentProcessing = %d, want %d", + tc.cpus, c.MaxConcurrentProcessing, tc.want) + } + } +} + // TestExplicitConcurrencyLimitsAreUsed checks that valid values for the // two limits are used as given. func TestExplicitConcurrencyLimitsAreUsed(t *testing.T) { diff --git a/internal/imageprocessor/max_concurrent_processing_internal_test.go b/internal/imageprocessor/max_concurrent_processing_internal_test.go index ba5388c..a174752 100644 --- a/internal/imageprocessor/max_concurrent_processing_internal_test.go +++ b/internal/imageprocessor/max_concurrent_processing_internal_test.go @@ -132,6 +132,29 @@ func TestNewDefaultsMaxConcurrentProcessingToCPUs(t *testing.T) { } } +// TestNewDefaultIsHalfTheCPUsAndAtLeastOne checks that New, given no +// MaxConcurrentProcessing, makes one slot for every two CPUs Go uses, +// rounded down, and one slot when Go uses one CPU. +// +//nolint:paralleltest // it sets GOMAXPROCS, which other tests read +func TestNewDefaultIsHalfTheCPUsAndAtLeastOne(t *testing.T) { + cpusBefore := runtime.GOMAXPROCS(0) + t.Cleanup(func() { runtime.GOMAXPROCS(cpusBefore) }) + + for _, tc := range []struct{ cpus, want int }{ + {1, 1}, {2, 1}, {3, 1}, {8, 4}, + } { + runtime.GOMAXPROCS(tc.cpus) + + got := cap(New(Params{}).processingSemaphore) + t.Logf("GOMAXPROCS %d: %d slots", tc.cpus, got) + + if got != tc.want { + t.Errorf("GOMAXPROCS %d: %d slots, want %d", tc.cpus, got, tc.want) + } + } +} + // TestProcessNeverExceedsMaxConcurrentProcessing starts more Process calls // than MaxConcurrentProcessing allows and holds the first ones inside // Process until the test lets them go. No more than the limit may be