ui: basic map scroller
This commit is contained in:
@@ -0,0 +1,365 @@
|
||||
package world
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWrap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value int
|
||||
size int
|
||||
want int
|
||||
}{
|
||||
{name: "zero", value: 0, size: 10, want: 0},
|
||||
{name: "inside range", value: 7, size: 10, want: 7},
|
||||
{name: "equal to size", value: 10, size: 10, want: 0},
|
||||
{name: "greater than size", value: 27, size: 10, want: 7},
|
||||
{name: "negative one", value: -1, size: 10, want: 9},
|
||||
{name: "negative many", value: -23, size: 10, want: 7},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := wrap(tt.value, tt.size); got != tt.want {
|
||||
t.Fatalf("wrap(%d, %d) = %d, want %d", tt.value, tt.size, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClamp(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value int
|
||||
minValue int
|
||||
maxValue int
|
||||
want int
|
||||
}{
|
||||
{name: "below range", value: -5, minValue: 0, maxValue: 10, want: 0},
|
||||
{name: "inside range", value: 5, minValue: 0, maxValue: 10, want: 5},
|
||||
{name: "above range", value: 15, minValue: 0, maxValue: 10, want: 10},
|
||||
{name: "equal min", value: 0, minValue: 0, maxValue: 10, want: 0},
|
||||
{name: "equal max", value: 10, minValue: 0, maxValue: 10, want: 10},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := clamp(tt.value, tt.minValue, tt.maxValue); got != tt.want {
|
||||
t.Fatalf("clamp(%d, %d, %d) = %d, want %d", tt.value, tt.minValue, tt.maxValue, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCeilDiv(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
a int
|
||||
b int
|
||||
want int
|
||||
}{
|
||||
{a: 1, b: 1, want: 1},
|
||||
{a: 1, b: 2, want: 1},
|
||||
{a: 2, b: 2, want: 1},
|
||||
{a: 3, b: 2, want: 2},
|
||||
{a: 10, b: 3, want: 4},
|
||||
{a: 16, b: 8, want: 2},
|
||||
{a: 17, b: 8, want: 3},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := ceilDiv(tt.a, tt.b); got != tt.want {
|
||||
t.Fatalf("ceilDiv(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloorDiv(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Equal(t, 0, floorDiv(0, 10))
|
||||
require.Equal(t, 0, floorDiv(1, 10))
|
||||
require.Equal(t, 0, floorDiv(9, 10))
|
||||
require.Equal(t, 1, floorDiv(10, 10))
|
||||
require.Equal(t, 1, floorDiv(19, 10))
|
||||
|
||||
require.Equal(t, -1, floorDiv(-1, 10))
|
||||
require.Equal(t, -1, floorDiv(-9, 10))
|
||||
require.Equal(t, -1, floorDiv(-10, 10))
|
||||
require.Equal(t, -2, floorDiv(-11, 10))
|
||||
|
||||
require.Panics(t, func() { _ = floorDiv(1, 0) })
|
||||
require.Panics(t, func() { _ = floorDiv(1, -1) })
|
||||
}
|
||||
|
||||
func TestFixedPoint(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
v float64
|
||||
want int
|
||||
}{
|
||||
{name: "zero", v: 0, want: 0},
|
||||
{name: "integer", v: 3, want: 3000},
|
||||
{name: "fraction", v: 1.234, want: 1234},
|
||||
{name: "round down", v: 1.2344, want: 1234},
|
||||
{name: "round up", v: 1.2345, want: 1235},
|
||||
{name: "negative", v: -1.2345, want: -1235},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := fixedPoint(tt.v); got != tt.want {
|
||||
t.Fatalf("fixedPoint(%f) = %d, want %d", tt.v, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
v int
|
||||
want int
|
||||
}{
|
||||
{v: 0, want: 0},
|
||||
{v: 7, want: 7},
|
||||
{v: -7, want: 7},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
if got := abs(tt.v); got != tt.want {
|
||||
t.Fatalf("abs(%d) = %d, want %d", tt.v, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPixelSpanToWorldFixed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
spanPx int
|
||||
zoomFp int
|
||||
want int
|
||||
}{
|
||||
{name: "1x zoom", spanPx: 100, zoomFp: SCALE, want: 100 * SCALE},
|
||||
{name: "2x zoom", spanPx: 100, zoomFp: 2 * SCALE, want: 50 * SCALE},
|
||||
{name: "half zoom", spanPx: 100, zoomFp: SCALE / 2, want: 200 * SCALE},
|
||||
{name: "fractional result truncation", spanPx: 1, zoomFp: 3 * SCALE, want: 333},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := PixelSpanToWorldFixed(tt.spanPx, tt.zoomFp); got != tt.want {
|
||||
t.Fatalf("PixelSpanToWorldFixed(%d, %d) = %d, want %d", tt.spanPx, tt.zoomFp, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorldToCellPanicsOnInvalidGrid(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cells int
|
||||
cellSize int
|
||||
}{
|
||||
{name: "zero cells", cells: 0, cellSize: 1},
|
||||
{name: "negative cells", cells: -1, cellSize: 1},
|
||||
{name: "zero cell size", cells: 1, cellSize: 0},
|
||||
{name: "negative cell size", cells: 1, cellSize: -1},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
t.Fatalf("worldToCell did not panic for cells=%d cellSize=%d", tt.cells, tt.cellSize)
|
||||
}
|
||||
}()
|
||||
|
||||
_ = worldToCell(0, 1000, tt.cells, tt.cellSize)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestShortestWrappedDelta(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
from int
|
||||
to int
|
||||
size int
|
||||
wantA int
|
||||
wantB int
|
||||
}{
|
||||
{name: "no wrap forward", from: 1000, to: 3000, size: 10000, wantA: 1000, wantB: 3000},
|
||||
{name: "no wrap backward", from: 3000, to: 1000, size: 10000, wantA: 3000, wantB: 1000},
|
||||
{name: "wrap forward over half", from: 1000, to: 7000, size: 10000, wantA: 11000, wantB: 7000},
|
||||
{name: "wrap backward over half", from: 7000, to: 1000, size: 10000, wantA: 7000, wantB: 11000},
|
||||
{name: "tie positive half wraps", from: 1000, to: 6000, size: 10000, wantA: 11000, wantB: 6000},
|
||||
{name: "tie negative half stays", from: 6000, to: 1000, size: 10000, wantA: 6000, wantB: 1000},
|
||||
{name: "just below positive half does not wrap", from: 1000, to: 5999, size: 10000, wantA: 1000, wantB: 5999},
|
||||
{name: "just beyond negative half wraps", from: 6001, to: 1000, size: 10000, wantA: 6001, wantB: 11000},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gotA, gotB := shortestWrappedDelta(tt.from, tt.to, tt.size)
|
||||
if gotA != tt.wantA || gotB != tt.wantB {
|
||||
t.Fatalf("shortestWrappedDelta(%d, %d, %d) = (%d, %d), want (%d, %d)",
|
||||
tt.from, tt.to, tt.size, gotA, gotB, tt.wantA, tt.wantB)
|
||||
}
|
||||
|
||||
delta := gotB - gotA
|
||||
half := tt.size / 2
|
||||
if delta < -half || delta >= half {
|
||||
t.Fatalf("normalized delta %d is outside [-%d, %d)", delta, half, half)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCameraZoomToWorldFixed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cameraZoom float64
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "neutral zoom",
|
||||
cameraZoom: 1.0,
|
||||
want: SCALE,
|
||||
},
|
||||
{
|
||||
name: "integer zoom",
|
||||
cameraZoom: 2.0,
|
||||
want: 2 * SCALE,
|
||||
},
|
||||
{
|
||||
name: "fractional zoom",
|
||||
cameraZoom: 1.25,
|
||||
want: 1250,
|
||||
},
|
||||
{
|
||||
name: "minimum configured zoom value shape",
|
||||
cameraZoom: 0.25,
|
||||
want: SCALE / 4,
|
||||
},
|
||||
{
|
||||
name: "round down",
|
||||
cameraZoom: 1.2344,
|
||||
want: 1234,
|
||||
},
|
||||
{
|
||||
name: "round up",
|
||||
cameraZoom: 1.2345,
|
||||
want: 1235,
|
||||
},
|
||||
{
|
||||
name: "very small but still positive after rounding",
|
||||
cameraZoom: 0.0006,
|
||||
want: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := cameraZoomToWorldFixed(tt.cameraZoom)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCameraZoomToWorldFixedReturnsError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cameraZoom float64
|
||||
}{
|
||||
{
|
||||
name: "zero",
|
||||
cameraZoom: 0,
|
||||
},
|
||||
{
|
||||
name: "negative",
|
||||
cameraZoom: -1,
|
||||
},
|
||||
{
|
||||
name: "nan",
|
||||
cameraZoom: math.NaN(),
|
||||
},
|
||||
{
|
||||
name: "positive infinity",
|
||||
cameraZoom: math.Inf(1),
|
||||
},
|
||||
{
|
||||
name: "negative infinity",
|
||||
cameraZoom: math.Inf(-1),
|
||||
},
|
||||
{
|
||||
name: "positive but rounds to zero",
|
||||
cameraZoom: 0.0004,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, err := cameraZoomToWorldFixed(tt.cameraZoom)
|
||||
require.ErrorIs(t, err, errInvalidCameraZoom)
|
||||
require.Zero(t, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustCameraZoomToWorldFixed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Equal(t, 1250, mustCameraZoomToWorldFixed(1.25))
|
||||
|
||||
require.Panics(t, func() {
|
||||
_ = mustCameraZoomToWorldFixed(0)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user