define playback retry rules and states
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
package playback
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
func (p RetryPolicy) canRetry(failedAttempts int) bool {
|
||||||
|
return p.MaxAttempts == 0 || failedAttempts < p.MaxAttempts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p RetryPolicy) retryDelay(failedAttempts int) time.Duration {
|
||||||
|
delay := p.InitialDelay
|
||||||
|
|
||||||
|
for attempt := 1; attempt < failedAttempts; attempt++ {
|
||||||
|
if delay >= p.MaxDelay/2 {
|
||||||
|
return p.MaxDelay
|
||||||
|
}
|
||||||
|
delay *= 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if delay > p.MaxDelay {
|
||||||
|
return p.MaxDelay
|
||||||
|
}
|
||||||
|
return delay
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package playback
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFiniteAttempts(t *testing.T) {
|
||||||
|
rp := RetryPolicy{
|
||||||
|
MaxAttempts: 3,
|
||||||
|
InitialDelay: 500 * time.Millisecond,
|
||||||
|
MaxDelay: 10 * time.Second,
|
||||||
|
}
|
||||||
|
if !rp.canRetry(1) {
|
||||||
|
t.Fatal("MaxAttempts=3, failed=1, but can't retry")
|
||||||
|
}
|
||||||
|
if !rp.canRetry(2) {
|
||||||
|
t.Fatal("MaxAttempts=3, failed=2, but can't retry")
|
||||||
|
}
|
||||||
|
if rp.canRetry(3) {
|
||||||
|
t.Fatal("MaxAttempts=3, failed=3, but can retry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOneAllowedAttempt(t *testing.T) {
|
||||||
|
rp := RetryPolicy{
|
||||||
|
MaxAttempts: 1,
|
||||||
|
InitialDelay: 500 * time.Millisecond,
|
||||||
|
MaxDelay: 10 * time.Second,
|
||||||
|
}
|
||||||
|
if rp.canRetry(1) {
|
||||||
|
t.Fatal("MaxAttempts=1, failed=1, but can retry")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnlimitedAttempts(t *testing.T) {
|
||||||
|
rp := RetryPolicy{
|
||||||
|
MaxAttempts: 0,
|
||||||
|
InitialDelay: 500 * time.Millisecond,
|
||||||
|
MaxDelay: 10 * time.Second,
|
||||||
|
}
|
||||||
|
for failedAttempts := 1; failedAttempts <= 10; failedAttempts++ {
|
||||||
|
if !rp.canRetry(failedAttempts) {
|
||||||
|
t.Fatalf(
|
||||||
|
"canRetry(%d) = false for unlimited policy",
|
||||||
|
failedAttempts,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackoff(t *testing.T) {
|
||||||
|
rp := RetryPolicy{
|
||||||
|
MaxAttempts: 0,
|
||||||
|
InitialDelay: 500 * time.Millisecond,
|
||||||
|
MaxDelay: 10 * time.Second,
|
||||||
|
}
|
||||||
|
want := []time.Duration{
|
||||||
|
500 * time.Millisecond,
|
||||||
|
1 * time.Second,
|
||||||
|
2 * time.Second,
|
||||||
|
4 * time.Second,
|
||||||
|
8 * time.Second,
|
||||||
|
10 * time.Second,
|
||||||
|
10 * time.Second,
|
||||||
|
}
|
||||||
|
for i, wantDelay := range want {
|
||||||
|
failedAttempts := i + 1
|
||||||
|
got := rp.retryDelay(failedAttempts)
|
||||||
|
if got != wantDelay {
|
||||||
|
t.Errorf(
|
||||||
|
"retryDelay(%d) = %s, want %s",
|
||||||
|
failedAttempts,
|
||||||
|
got,
|
||||||
|
wantDelay,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRetryDelayLargeFailureCount(t *testing.T) {
|
||||||
|
policy := RetryPolicy{
|
||||||
|
MaxAttempts: 0,
|
||||||
|
InitialDelay: 500 * time.Millisecond,
|
||||||
|
MaxDelay: 10 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := policy.retryDelay(1_000_000); got != policy.MaxDelay {
|
||||||
|
t.Fatalf("retryDelay() = %s, want cap %s", got, policy.MaxDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package playback
|
||||||
|
|
||||||
|
type State uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
StateIdle State = iota
|
||||||
|
StateConnecting
|
||||||
|
StatePlaying
|
||||||
|
StateReconnecting
|
||||||
|
StateFailed
|
||||||
|
StateStopping
|
||||||
|
)
|
||||||
@@ -12,7 +12,7 @@ func TestKindOfThroughWrapping(t *testing.T) {
|
|||||||
outer := fmt.Errorf("worker failed: %w", wrapped)
|
outer := fmt.Errorf("worker failed: %w", wrapped)
|
||||||
|
|
||||||
if got := KindOf(outer); got != ErrorKindUnavailable {
|
if got := KindOf(outer); got != ErrorKindUnavailable {
|
||||||
t.Fatalf("errorKind() = %v, want %v", got, ErrorKindUnavailable)
|
t.Fatalf("KindOf() = %v, want %v", got, ErrorKindUnavailable)
|
||||||
}
|
}
|
||||||
if !errors.Is(outer, base) {
|
if !errors.Is(outer, base) {
|
||||||
t.Fatal("wrapped error does not preserve its cause")
|
t.Fatal("wrapped error does not preserve its cause")
|
||||||
@@ -24,7 +24,7 @@ func TestKindOfThroughWrapping(t *testing.T) {
|
|||||||
|
|
||||||
func TestKindOfUnknown(t *testing.T) {
|
func TestKindOfUnknown(t *testing.T) {
|
||||||
if got := KindOf(errors.New("ordinary error")); got != ErrorKindUnknown {
|
if got := KindOf(errors.New("ordinary error")); got != ErrorKindUnknown {
|
||||||
t.Fatalf("errorKind() = %v, want %v", got, ErrorKindUnknown)
|
t.Fatalf("KindOf() = %v, want %v", got, ErrorKindUnknown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "mxl-player/internal/imgui"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
imgui.New()
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user