Files
bjarneo--cliamp/ui/vis_classic_peak_test.go
wehub-resource-sync ead81af521
Deploy to GitHub Pages / deploy (push) Failing after 0s
CI / go (push) Has been cancelled
CI / build (darwin, macos-14) (push) Has been cancelled
CI / build (windows, windows-2025) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:31:13 +08:00

569 lines
16 KiB
Go

package ui
import (
"math"
"strings"
"testing"
"time"
"charm.land/lipgloss/v2"
)
const classicPeakTestGlyphs = "⎺⎻⎼⎽"
const classicPeakTestEpsilon = 1e-9
func withPanelWidth(t *testing.T, width int) {
t.Helper()
prevWidth := PanelWidth
PanelWidth = width
t.Cleanup(func() {
PanelWidth = prevWidth
})
}
func uniformBands(level float64) []float64 {
return uniformBandsN(DefaultSpectrumBands, level)
}
func uniformBandsN(count int, level float64) []float64 {
bands := make([]float64, count)
for i := range bands {
bands[i] = level
}
return bands
}
func repeatedClassicPeakSlice(width int, level float64) []float64 {
cols := classicPeakColsForWidth(width)
out := make([]float64, cols)
for i := range out {
out[i] = level
}
return out
}
func activateMode(t *testing.T, v *Visualizer, mode VisMode) visModeDriver {
t.Helper()
v.Mode = mode
driver := v.syncDriverMode()
if driver == nil {
t.Fatalf("syncDriverMode() returned nil for mode %v", mode)
}
return driver
}
func classicPeakDriverFor(t *testing.T, v *Visualizer) *classicPeakDriver {
t.Helper()
driver, ok := v.driverFor(VisClassicPeak).(*classicPeakDriver)
if !ok || driver == nil {
t.Fatal("driverFor(VisClassicPeak) did not return *classicPeakDriver")
}
return driver
}
func terrainDriverFor(t *testing.T, v *Visualizer) *terrainDriver {
t.Helper()
driver, ok := v.driverFor(VisTerrain).(*terrainDriver)
if !ok || driver == nil {
t.Fatal("driverFor(VisTerrain) did not return *terrainDriver")
}
return driver
}
func TestClassicPeakModeLookup(t *testing.T) {
got, ok := StringToVisModeExact("ClassicPeak")
if !ok || got != VisClassicPeak {
t.Fatalf("StringToVisModeExact(ClassicPeak) = (%v, %v), want (%v, true)", got, ok, VisClassicPeak)
}
v := NewVisualizer(44100)
v.Mode = VisClassicPeak
if got := v.ModeName(); got != "ClassicPeak" {
t.Fatalf("ModeName() = %q, want %q", got, "ClassicPeak")
}
}
func TestVisualizerFrameAdvancesOnTickNotRender(t *testing.T) {
v := NewVisualizer(44100)
v.Analyze(make([]float64, defaultFFTSize), spectrumAnalysisSpec(DefaultSpectrumBands))
if v.frame != 0 {
t.Fatalf("Analyze() advanced frame to %d, want 0 before tick", v.frame)
}
v.bands = uniformBands(0.5)
v.Render()
if v.frame != 0 {
t.Fatalf("Render() advanced frame to %d, want 0 before tick", v.frame)
}
v.Tick(VisTickContext{})
if v.frame != 1 {
t.Fatalf("Tick() advanced frame to %d, want 1", v.frame)
}
v.bands = uniformBands(0.5)
v.Render()
if v.frame != 1 {
t.Fatalf("Render() advanced frame to %d after tick, want 1", v.frame)
}
}
func TestClassicPeakLaunchAndSettle(t *testing.T) {
withPanelWidth(t, 8)
cols := classicPeakColsForWidth(PanelWidth)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
driver := classicPeakDriverFor(t, v)
v.bands = uniformBands(0.2)
driver.sync(v)
v.Render()
if len(driver.barPos) != cols {
t.Fatalf("initial bar count = %d, want %d", len(driver.barPos), cols)
}
if len(driver.peakPos) != cols {
t.Fatalf("initial cap count = %d, want %d", len(driver.peakPos), cols)
}
for i, pos := range driver.barPos {
if math.Abs(pos-0.2) > classicPeakTestEpsilon {
t.Fatalf("initial bar[%d] = %v, want 0.2", i, pos)
}
}
for i, pos := range driver.peakPos {
if math.Abs(pos-0.2) > classicPeakTestEpsilon {
t.Fatalf("initial cap[%d] = %v, want 0.2", i, pos)
}
if driver.peakVel[i] != 0 {
t.Fatalf("initial velocity[%d] = %v, want 0", i, driver.peakVel[i])
}
if driver.peakHold[i] != 0 {
t.Fatalf("initial hold[%d] = %v, want 0", i, driver.peakHold[i])
}
}
v.bands = uniformBands(0.8)
driver.sync(v)
for i, pos := range driver.barPos {
if math.Abs(pos-0.2) > classicPeakTestEpsilon {
t.Fatalf("bars snapped on sync[%d] = %v, want 0.2", i, pos)
}
}
for i, pos := range driver.peakPos {
if math.Abs(pos-0.8) > classicPeakTestEpsilon {
t.Fatalf("cap launch[%d] = %v, want 0.8", i, pos)
}
if driver.peakVel[i] <= 0 {
t.Fatalf("velocity on sync[%d] = %v, want > 0", i, driver.peakVel[i])
}
if driver.peakHold[i] != 0 {
t.Fatalf("hold on sync[%d] = %v, want 0", i, driver.peakHold[i])
}
}
now := time.Now()
driver.advance(v, now)
for i, pos := range driver.barPos {
if pos <= 0.2 || pos >= 0.8 {
t.Fatalf("animated bar[%d] = %v, want between 0.2 and 0.8", i, pos)
}
if driver.peakPos[i] <= 0.8 {
t.Fatalf("animated cap[%d] = %v, want > 0.8", i, driver.peakPos[i])
}
if driver.peakPos[i] <= pos {
t.Fatalf("animated cap[%d] = %v, want > bar %v", i, driver.peakPos[i], pos)
}
if driver.peakVel[i] <= 0 {
t.Fatalf("launch velocity[%d] = %v, want > 0", i, driver.peakVel[i])
}
}
v.bands = uniformBands(0.3)
driver.sync(v)
for i, pos := range driver.barPos {
if pos <= 0.3 {
t.Fatalf("settling bar[%d] = %v, want > 0.3", i, pos)
}
if driver.peakPos[i] <= pos {
t.Fatalf("settling cap[%d] = %v, want > bar %v", i, driver.peakPos[i], pos)
}
}
for step := 1; step <= 128 && driver.animating(v); step++ {
driver.advance(v, now.Add(time.Duration(step)*tickClassicPeak))
}
if driver.animating(v) {
t.Fatal("animating() = true after settle window, want false")
}
for i, pos := range driver.barPos {
if math.Abs(pos-0.3) > classicPeakVisibleEpsilon {
t.Fatalf("settled bar[%d] = %v, want about 0.3", i, pos)
}
}
for i, pos := range driver.peakPos {
if math.Abs(pos-0.3) > classicPeakVisibleEpsilon {
t.Fatalf("settled cap[%d] = %v, want about 0.3", i, pos)
}
if driver.peakVel[i] != 0 {
t.Fatalf("settled velocity[%d] = %v, want 0", i, driver.peakVel[i])
}
if driver.peakHold[i] != 0 {
t.Fatalf("settled hold[%d] = %v, want 0", i, driver.peakHold[i])
}
}
}
func TestClassicPeakHangsBrieflyAtApex(t *testing.T) {
withPanelWidth(t, 8)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
driver := classicPeakDriverFor(t, v)
v.bands = uniformBands(0.2)
driver.sync(v)
v.Render()
v.bands = uniformBands(0.8)
driver.sync(v)
now := time.Now()
foundHold := false
for step := 1; step <= 32; step++ {
driver.advance(v, now.Add(time.Duration(step)*tickClassicPeak))
if driver.peakHold[0] <= 0 {
continue
}
foundHold = true
heldPos := driver.peakPos[0]
heldVel := driver.peakVel[0]
heldFor := driver.peakHold[0]
driver.advance(v, now.Add(time.Duration(step+1)*tickClassicPeak))
if driver.peakPos[0] != heldPos {
t.Fatalf("held cap moved: got %v, want %v", driver.peakPos[0], heldPos)
}
if driver.peakVel[0] != heldVel {
t.Fatalf("held cap velocity changed: got %v, want %v", driver.peakVel[0], heldVel)
}
if driver.peakHold[0] >= heldFor {
t.Fatalf("hold timer did not decrease: got %v, want < %v", driver.peakHold[0], heldFor)
}
break
}
if !foundHold {
t.Fatal("peakHold never activated")
}
}
func TestClassicPeakDoesNotRelaunchWhileAirborne(t *testing.T) {
withPanelWidth(t, 8)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
driver := classicPeakDriverFor(t, v)
v.bands = uniformBands(0.2)
driver.sync(v)
v.Render()
v.bands = uniformBands(0.8)
driver.sync(v)
launchPos := append([]float64(nil), driver.peakPos...)
launchVel := append([]float64(nil), driver.peakVel...)
v.bands = uniformBands(0.95)
driver.sync(v)
for i, pos := range driver.peakPos {
if pos != launchPos[i] {
t.Fatalf("cap relaunched in air[%d] = %v, want %v", i, pos, launchPos[i])
}
if driver.peakVel[i] != launchVel[i] {
t.Fatalf("velocity changed in air[%d] = %v, want %v", i, driver.peakVel[i], launchVel[i])
}
}
}
func TestClassicPeakResetsOnModeSwitchAndWidthChange(t *testing.T) {
withPanelWidth(t, 6)
cols6 := classicPeakColsForWidth(PanelWidth)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
driver := classicPeakDriverFor(t, v)
v.bands = uniformBands(0.4)
driver.sync(v)
v.Render()
driver.barPos[1] = 0.7
driver.peakPos[1] = 0.9
driver.peakVel[1] = 1.2
activateMode(t, v, VisBars)
activateMode(t, v, VisClassicPeak)
driver.sync(v)
if len(driver.barPos) != cols6 {
t.Fatalf("reset bar len = %d, want %d", len(driver.barPos), cols6)
}
if len(driver.peakPos) != cols6 {
t.Fatalf("reset cap len = %d, want %d", len(driver.peakPos), cols6)
}
for i, pos := range driver.barPos {
if math.Abs(pos-0.4) > classicPeakTestEpsilon {
t.Fatalf("reset bar[%d] = %v, want 0.4", i, pos)
}
}
for i, pos := range driver.peakPos {
if math.Abs(pos-0.4) > classicPeakTestEpsilon {
t.Fatalf("reset cap[%d] = %v, want 0.4", i, pos)
}
if driver.peakVel[i] != 0 {
t.Fatalf("reset velocity[%d] = %v, want 0", i, driver.peakVel[i])
}
if driver.peakHold[i] != 0 {
t.Fatalf("reset hold[%d] = %v, want 0", i, driver.peakHold[i])
}
}
PanelWidth = 8
cols8 := classicPeakColsForWidth(PanelWidth)
driver.sync(v)
if len(driver.barPos) != cols8 {
t.Fatalf("resize bar len = %d, want %d", len(driver.barPos), cols8)
}
if len(driver.peakPos) != cols8 {
t.Fatalf("resize cap len = %d, want %d", len(driver.peakPos), cols8)
}
for i, pos := range driver.barPos {
if math.Abs(pos-0.4) > classicPeakTestEpsilon {
t.Fatalf("resize bar[%d] = %v, want 0.4", i, pos)
}
}
for i, pos := range driver.peakPos {
if math.Abs(pos-0.4) > classicPeakTestEpsilon {
t.Fatalf("resize cap[%d] = %v, want 0.4", i, pos)
}
if driver.peakVel[i] != 0 {
t.Fatalf("resize velocity[%d] = %v, want 0", i, driver.peakVel[i])
}
if driver.peakHold[i] != 0 {
t.Fatalf("resize hold[%d] = %v, want 0", i, driver.peakHold[i])
}
}
}
func TestClassicPeakAnimatingWhenCapIsAboveBar(t *testing.T) {
withPanelWidth(t, 8)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
driver := classicPeakDriverFor(t, v)
v.bands = uniformBands(0.3)
driver.barPos = repeatedClassicPeakSlice(PanelWidth, 0.3)
driver.peakPos = repeatedClassicPeakSlice(PanelWidth, 0.5)
driver.peakVel = repeatedClassicPeakSlice(PanelWidth, 0)
if !driver.animating(v) {
t.Fatal("animating() = false, want true when caps are still above the bar")
}
}
func TestClassicPeakAnimatingWhenBarsAreSettling(t *testing.T) {
withPanelWidth(t, 8)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
driver := classicPeakDriverFor(t, v)
v.bands = uniformBands(0.7)
driver.barPos = repeatedClassicPeakSlice(PanelWidth, 0.5)
driver.peakPos = repeatedClassicPeakSlice(PanelWidth, 0.5)
driver.peakVel = repeatedClassicPeakSlice(PanelWidth, 0)
if !driver.animating(v) {
t.Fatal("animating() = false, want true while bars are still easing to target")
}
}
func TestClassicPeakRetainsDetailAtRightEdge(t *testing.T) {
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
driver := classicPeakDriverFor(t, v)
bands := make([]float64, classicPeakSpectrumBands)
copy(bands[len(bands)-6:], []float64{0.55, 0.46, 0.37, 0.28, 0.19, 0.1})
v.bands = bands
levels := driver.levels(v)
if len(levels) < 4 {
t.Fatalf("levels len = %d, want at least 4", len(levels))
}
foundStep := false
for i := len(levels) - 4; i < len(levels)-1; i++ {
if math.Abs(levels[i]-levels[i+1]) > 0.01 {
foundStep = true
break
}
}
if !foundStep {
t.Fatalf("right edge flattened unexpectedly: tail=%v", levels[len(levels)-4:])
}
}
func TestClassicPeakRenderHidesLandedCaps(t *testing.T) {
withPanelWidth(t, 8)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
v.Rows = 5
v.bands = uniformBands(0.6)
out := v.Render()
if strings.ContainsAny(out, classicPeakTestGlyphs) {
t.Fatalf("Render() showed a cap even though peaks were seeded at the bar height: %q", out)
}
}
func TestClassicPeakRenderShowsAttachedCapWhileSettling(t *testing.T) {
withPanelWidth(t, 8)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
driver := classicPeakDriverFor(t, v)
v.Rows = 5
v.bands = uniformBands(0.61)
driver.barPos = repeatedClassicPeakSlice(PanelWidth, 0.61)
driver.peakPos = repeatedClassicPeakSlice(PanelWidth, 0.68)
driver.peakVel = repeatedClassicPeakSlice(PanelWidth, 0)
out := v.Render()
if !strings.ContainsAny(out, classicPeakTestGlyphs) {
t.Fatalf("Render() did not show an attached settling cap: %q", out)
}
}
func TestClassicPeakPauseFreezesStateAndClearsAnimationClock(t *testing.T) {
withPanelWidth(t, 8)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
driver := classicPeakDriverFor(t, v)
v.Rows = 5
v.bands = uniformBands(0.6)
driver.barPos = repeatedClassicPeakSlice(PanelWidth, 0.6)
driver.peakPos = repeatedClassicPeakSlice(PanelWidth, 0.82)
driver.peakVel = repeatedClassicPeakSlice(PanelWidth, 1.1)
driver.peakHold = repeatedClassicPeakSlice(PanelWidth, classicPeakApexHold)
snapshotPeak := append([]float64(nil), driver.peakPos...)
snapshotVel := append([]float64(nil), driver.peakVel...)
snapshotHold := append([]float64(nil), driver.peakHold...)
driver.lastTick = time.Now()
v.Tick(VisTickContext{Now: time.Now(), Paused: true})
for i := range driver.peakPos {
if driver.peakPos[i] != snapshotPeak[i] {
t.Fatalf("paused cap[%d] = %v, want frozen %v", i, driver.peakPos[i], snapshotPeak[i])
}
if driver.peakVel[i] != snapshotVel[i] {
t.Fatalf("paused velocity[%d] = %v, want frozen %v", i, driver.peakVel[i], snapshotVel[i])
}
if driver.peakHold[i] != snapshotHold[i] {
t.Fatalf("paused hold[%d] = %v, want frozen %v", i, driver.peakHold[i], snapshotHold[i])
}
}
if !driver.animating(v) {
t.Fatal("animating() = false, want true while caps still airborne")
}
if got := v.TickInterval(VisTickContext{Paused: true}); got != TickSlow {
t.Fatalf("TickInterval(paused) = %v, want %v", got, TickSlow)
}
}
func TestClassicPeakOverlayFreezesStateAndClearsAnimationClock(t *testing.T) {
withPanelWidth(t, 8)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
driver := classicPeakDriverFor(t, v)
v.Rows = 5
v.bands = uniformBands(0.6)
driver.barPos = repeatedClassicPeakSlice(PanelWidth, 0.6)
driver.peakPos = repeatedClassicPeakSlice(PanelWidth, 0.82)
driver.peakVel = repeatedClassicPeakSlice(PanelWidth, 1.1)
driver.peakHold = repeatedClassicPeakSlice(PanelWidth, classicPeakApexHold)
snapshotPeak := append([]float64(nil), driver.peakPos...)
snapshotVel := append([]float64(nil), driver.peakVel...)
snapshotHold := append([]float64(nil), driver.peakHold...)
driver.lastTick = time.Now()
driver.Tick(v, VisTickContext{Now: time.Now(), OverlayActive: true})
if !driver.lastTick.IsZero() {
t.Fatalf("lastTick after overlay = %v, want zero", driver.lastTick)
}
for i := range driver.peakPos {
if driver.peakPos[i] != snapshotPeak[i] {
t.Fatalf("overlay cap[%d] = %v, want frozen %v", i, driver.peakPos[i], snapshotPeak[i])
}
if driver.peakVel[i] != snapshotVel[i] {
t.Fatalf("overlay velocity[%d] = %v, want frozen %v", i, driver.peakVel[i], snapshotVel[i])
}
if driver.peakHold[i] != snapshotHold[i] {
t.Fatalf("overlay hold[%d] = %v, want frozen %v", i, driver.peakHold[i], snapshotHold[i])
}
}
if !driver.animating(v) {
t.Fatal("animating() = false, want true while overlay hides airborne caps")
}
if got := v.TickInterval(VisTickContext{OverlayActive: true}); got != TickSlow {
t.Fatalf("TickInterval(overlay) = %v, want %v", got, TickSlow)
}
}
func TestClassicPeakRenderFillsEvenWidthPanels(t *testing.T) {
withPanelWidth(t, 8)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
v.Rows = 5
v.bands = uniformBands(0.6)
out := v.Render()
for line := range strings.SplitSeq(out, "\n") {
if got := lipgloss.Width(line); got != PanelWidth {
t.Fatalf("Render() line width = %d, want %d for even panel width: %q", got, PanelWidth, line)
}
}
}
func TestClassicPeakEvenWidthKeepsBarsSeparated(t *testing.T) {
withPanelWidth(t, 8)
v := NewVisualizer(44100)
activateMode(t, v, VisClassicPeak)
v.Rows = 5
v.bands = uniformBands(0.6)
out := strings.Split(v.Render(), "\n")
if len(out) == 0 {
t.Fatalf("Render() returned too few rows: %q", out)
}
found := false
for _, line := range out {
if strings.Contains(line, "█") {
if strings.Contains(line, "███") {
t.Fatalf("Render() merged adjacent bars on even widths: %q", line)
}
found = true
break
}
}
if !found {
t.Fatalf("Render() did not render any bar rows: %q", out)
}
}