doodle/pkg/drawtool/history_test.go

133 lines
3.5 KiB
Go

package drawtool
import (
"testing"
"git.kirsle.net/apps/doodle/lib/render"
)
func TestHistory(t *testing.T) {
// Test assertion helpers.
shouldBool := func(note string, expect, actual bool) {
if actual != expect {
t.Errorf(
"Unexpected boolean result (%s)\n"+
"Expected: %+v\n"+
" Got: %+v",
note,
expect,
actual,
)
}
}
shouldInt := func(note string, expect, actual int) {
if actual != expect {
t.Errorf(
"Unexpected integer result (%s)\n"+
"Expected: %+v\n"+
" Got: %+v",
note,
expect,
actual,
)
}
}
shouldPoint := func(note string, expect render.Point, actual *Stroke) {
if actual == nil {
t.Errorf("Missing history stroke for shouldPoint(%s)", note)
return
}
if actual.PointA != expect {
t.Errorf(
"Unexpected point result (%s)\n"+
"Expected: %+v\n"+
" Got: %+v",
note,
expect,
actual.PointA,
)
}
}
var H = NewHistory(10)
// Add and remove and re-add the first element.
H.AddStroke(&Stroke{
PointA: render.NewPoint(999, 999),
})
shouldInt("first element", 1, H.Size())
shouldBool("can undo first element", true, H.Undo())
shouldBool("latest should be null", true, H.Latest() == nil)
H = NewHistory(10)
shouldBool("can't Undo with fresh history", false, H.Undo())
shouldInt("size should be zero", 0, H.Size())
H.AddStroke(&Stroke{
PointA: render.NewPoint(1, 1),
})
shouldInt("after first stroke", 1, H.Size())
shouldPoint("head is the newest point", render.NewPoint(1, 1), H.Latest())
H.AddStroke(&Stroke{
PointA: render.NewPoint(2, 2),
})
shouldInt("after second stroke", 2, H.Size())
shouldPoint("head is the newest point", render.NewPoint(2, 2), H.Latest())
// Undo.
shouldBool("undo second stroke", true, H.Undo())
shouldInt("after undo the future stroke is still part of the size", 2, H.Size())
shouldPoint("after undo, the newest point", render.NewPoint(1, 1), H.Latest())
// Redo.
shouldBool("redo second stroke", true, H.Redo())
shouldInt("after redo second stroke, size is still the same", 2, H.Size())
shouldPoint("after redo, the newest point", render.NewPoint(2, 2), H.Latest())
// Another redo must fail.
shouldBool("redo when there is nothing to redo", false, H.Redo())
// Add a few more points.
for i := 3; i <= 6; i++ {
H.AddStroke(&Stroke{
PointA: render.NewPoint(int32(i), int32(i)),
})
}
shouldInt("after adding more strokes", 6, H.Size())
shouldPoint("last point added", render.NewPoint(6, 6), H.Latest())
// Undo a few times.
shouldBool("undo^1", true, H.Undo())
shouldBool("undo^2", true, H.Undo())
shouldBool("undo^3", true, H.Undo())
shouldInt("after a few undos, the size still contains future history", 6, H.Size())
// A new stroke invalidates the future history.
H.AddStroke(&Stroke{
PointA: render.NewPoint(7, 7),
})
shouldInt("after new history, size is recapped to tail", 4, H.Size())
shouldBool("can't Redo after new point added", false, H.Redo())
// Overflow past our history size to test rollover.
for i := 8; i <= 16; i++ {
H.AddStroke(&Stroke{
PointA: render.NewPoint(int32(i), int32(i)),
})
}
shouldInt("after tons of new history, size is capped out", 10, H.Size())
shouldPoint("after overflow, latest point", render.NewPoint(16, 16), H.Latest())
shouldPoint("after overflow, first point", render.NewPoint(7, 7), H.Oldest())
// Undo back to beginning.
for i := 0; i < H.Size(); i++ {
shouldBool("bulk undo to beginning", true, H.Undo())
}
shouldBool("after bulk undo, tail", true, H.Latest() == nil)
shouldBool("can't undo further", false, H.Undo())
}