The queue card now shows an estimated time to clear: current job remaining + sum(pending source durations) / current speed. Durations come from a path+mtime-keyed cache that probes each file once in the background (ffprobe format=duration, header-only), so the 1s poll never re-probes; entries are pruned to the live queue. No speed (idle/held) -> no estimate; files still being probed mark it partial (shown as '~Xh+'). Closes #9
63 lines
1.8 KiB
Go
63 lines
1.8 KiB
Go
package server
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestQueueETASeconds(t *testing.T) {
|
|
// No speed → can't extrapolate.
|
|
if got := queueETASeconds(100, 3600, 0); got != 0 {
|
|
t.Errorf("speed 0: got %d, want 0", got)
|
|
}
|
|
// current remaining 600s + 1800s of queued source at 0.5x (=3600s) = 4200s.
|
|
if got := queueETASeconds(600, 1800, 0.5); got != 4200 {
|
|
t.Errorf("got %d, want 4200", got)
|
|
}
|
|
// At 2x, 1800s of source encodes in 900s; + 600 remaining = 1500.
|
|
if got := queueETASeconds(600, 1800, 2); got != 1500 {
|
|
t.Errorf("got %d, want 1500", got)
|
|
}
|
|
// Empty queue → just the current job's remaining.
|
|
if got := queueETASeconds(600, 0, 1); got != 600 {
|
|
t.Errorf("got %d, want 600", got)
|
|
}
|
|
}
|
|
|
|
// waitCached polls Get (the probe resolves on a background goroutine).
|
|
func waitCached(c *durationCache, p string, mt time.Time) (float64, bool) {
|
|
for i := 0; i < 200; i++ {
|
|
if sec, ok := c.Get(p, mt); ok {
|
|
return sec, true
|
|
}
|
|
time.Sleep(2 * time.Millisecond)
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func TestDurationCache(t *testing.T) {
|
|
c := newDurationCache(func(p string) (float64, error) { return 42, nil })
|
|
|
|
mt := time.Unix(1000, 0)
|
|
if _, ok := c.Get("/a.mkv", mt); ok {
|
|
t.Fatal("first Get should miss")
|
|
}
|
|
if sec, ok := waitCached(c, "/a.mkv", mt); !ok || sec != 42 {
|
|
t.Fatalf("after probe: got %v ok=%v, want 42 true", sec, ok)
|
|
}
|
|
|
|
// A changed mtime invalidates the entry.
|
|
if _, ok := c.Get("/a.mkv", time.Unix(2000, 0)); ok {
|
|
t.Error("changed mtime should miss")
|
|
}
|
|
if _, ok := waitCached(c, "/a.mkv", time.Unix(2000, 0)); !ok {
|
|
t.Fatal("re-probe under new mtime should cache")
|
|
}
|
|
|
|
// Retain drops entries for paths not in the keep set.
|
|
c.Retain([]string{"/b.mkv"})
|
|
if _, ok := c.Get("/a.mkv", time.Unix(2000, 0)); ok {
|
|
t.Error("Retain should have dropped /a.mkv")
|
|
}
|
|
}
|