e0e362d700
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
129 lines
3.9 KiB
Go
129 lines
3.9 KiB
Go
// Copyright 2026 Alibaba Group Holding Ltd.
|
|
//
|
|
// 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.
|
|
|
|
package supervisor
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// fixedRNG returns a constant value to make jitter deterministic.
|
|
func fixedRNG(v float64) func() float64 { return func() float64 { return v } }
|
|
|
|
func TestNextBackoff_NoJitterDoublesAndClamps(t *testing.T) {
|
|
min := 1 * time.Second
|
|
max := 8 * time.Second
|
|
noJitter := fixedRNG(0.5) // would be zero delta anyway with jitter=0
|
|
|
|
cases := []struct {
|
|
prev time.Duration
|
|
want time.Duration
|
|
}{
|
|
{0, min}, // initial
|
|
{-1 * time.Second, min}, // negative
|
|
{500 * time.Millisecond, min}, // below min after doubling -> clamp up
|
|
{1 * time.Second, 2 * time.Second},
|
|
{2 * time.Second, 4 * time.Second},
|
|
{4 * time.Second, 8 * time.Second},
|
|
{8 * time.Second, max}, // would be 16s -> clamp to max
|
|
{100 * time.Second, max},
|
|
}
|
|
for _, c := range cases {
|
|
got := nextBackoff(c.prev, min, max, 0, noJitter)
|
|
if got != c.want {
|
|
t.Errorf("nextBackoff(prev=%s) = %s, want %s", c.prev, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNextBackoff_JitterWithinBounds(t *testing.T) {
|
|
min := 1 * time.Second
|
|
max := 10 * time.Second
|
|
jitter := 0.5
|
|
|
|
// rng=0 -> delta = -1 * 0.5 * d = -50%
|
|
// rng=0.5 -> delta = 0
|
|
// rng=1- -> delta ≈ +50%
|
|
// Approximate by checking the two extremes plus the midpoint.
|
|
for _, v := range []float64{0.0, 0.5, 0.9999} {
|
|
got := nextBackoff(2*time.Second, min, max, jitter, fixedRNG(v))
|
|
// Base after doubling = 4s. jitter range ±2s. So [2s, 6s].
|
|
if got < 2*time.Second || got > 6*time.Second {
|
|
t.Errorf("rng=%v: got %s, want in [2s, 6s]", v, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNextBackoff_JitterClampsToMax(t *testing.T) {
|
|
min := 1 * time.Second
|
|
max := 5 * time.Second
|
|
// Base = max after doubling. Positive jitter must not push above max.
|
|
got := nextBackoff(max, min, max, 0.5, fixedRNG(0.9999))
|
|
if got > max {
|
|
t.Errorf("got %s > max %s", got, max)
|
|
}
|
|
}
|
|
|
|
func TestNextBackoff_JitterClampsToMin(t *testing.T) {
|
|
min := 2 * time.Second
|
|
max := 10 * time.Second
|
|
// Base = min. Negative jitter must not push below min.
|
|
got := nextBackoff(0, min, max, 0.5, fixedRNG(0.0))
|
|
if got < min {
|
|
t.Errorf("got %s < min %s", got, min)
|
|
}
|
|
}
|
|
|
|
// Spec.BackoffJitter is *float64 specifically so callers can pass &zero to
|
|
// disable jitter; verify the underlying nextBackoff respects jitter=0.
|
|
func TestNextBackoff_JitterZeroDisablesJitter(t *testing.T) {
|
|
min := 1 * time.Second
|
|
max := 16 * time.Second
|
|
// With jitter=0, output must be the exact doubled value regardless of rng.
|
|
for _, rng := range []float64{0.0, 0.5, 0.9999} {
|
|
got := nextBackoff(2*time.Second, min, max, 0, fixedRNG(rng))
|
|
if got != 4*time.Second {
|
|
t.Errorf("rng=%v: got %s, want exactly 4s", rng, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestApplyDefaults_BackoffJitter(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
in *float64
|
|
want float64
|
|
}{
|
|
{"nil applies default", nil, defaultBackoffJitter},
|
|
{"zero stays zero", floatPtr(0), 0},
|
|
{"explicit value preserved", floatPtr(0.25), 0.25},
|
|
{"negative clamped to zero", floatPtr(-0.5), 0},
|
|
}
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
s := Spec{Cmd: "/bin/true", BackoffJitter: c.in}
|
|
s.applyDefaults()
|
|
if s.BackoffJitter == nil {
|
|
t.Fatal("BackoffJitter still nil after applyDefaults")
|
|
}
|
|
if *s.BackoffJitter != c.want {
|
|
t.Errorf("BackoffJitter = %v, want %v", *s.BackoffJitter, c.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func floatPtr(v float64) *float64 { return &v }
|