package ui import "testing" func TestRectEdgesAndEmptiness(t *testing.T) { r := Rect{X: 2, Y: 3, W: 10, H: 5} if got := r.Right(); got != 12 { t.Errorf("Right() = %d, want 12", got) } if got := r.Bottom(); got != 8 { t.Errorf("Bottom() = %d, want 8", got) } if r.IsEmpty() { t.Error("IsEmpty() = true on a rectangle with a size") } for _, empty := range []Rect{{W: 0, H: 5}, {W: 5, H: 0}, {W: -1, H: 5}} { if !empty.IsEmpty() { t.Errorf("IsEmpty() = false for %+v", empty) } } } func TestRectContainsExcludesTheFarEdges(t *testing.T) { r := Rect{X: 1, Y: 1, W: 3, H: 2} inside := [][2]int{{1, 1}, {3, 2}, {2, 1}} for _, p := range inside { if !r.Contains(p[0], p[1]) { t.Errorf("Contains(%d, %d) = false, want true", p[0], p[1]) } } outside := [][2]int{{0, 1}, {4, 1}, {1, 0}, {1, 3}} for _, p := range outside { if r.Contains(p[0], p[1]) { t.Errorf("Contains(%d, %d) = true, want false", p[0], p[1]) } } } func TestRectIntersect(t *testing.T) { tests := []struct { name string a, b Rect want Rect }{ { name: "overlapping", a: Rect{X: 0, Y: 0, W: 10, H: 10}, b: Rect{X: 5, Y: 5, W: 10, H: 10}, want: Rect{X: 5, Y: 5, W: 5, H: 5}, }, { name: "one inside the other", a: Rect{X: 0, Y: 0, W: 10, H: 10}, b: Rect{X: 2, Y: 2, W: 3, H: 3}, want: Rect{X: 2, Y: 2, W: 3, H: 3}, }, { name: "touching but not overlapping", a: Rect{X: 0, Y: 0, W: 5, H: 5}, b: Rect{X: 5, Y: 0, W: 5, H: 5}, want: Rect{X: 5, Y: 0}, }, { name: "wholly apart", a: Rect{X: 0, Y: 0, W: 2, H: 2}, b: Rect{X: 10, Y: 10, W: 2, H: 2}, want: Rect{X: 10, Y: 10}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := tc.a.Intersect(tc.b) if got != tc.want { t.Errorf("Intersect() = %+v, want %+v", got, tc.want) } if tc.want.IsEmpty() && !got.IsEmpty() { t.Error("the intersection should be empty") } }) } } func TestRectInsetMoveAndWithSize(t *testing.T) { r := Rect{X: 5, Y: 5, W: 20, H: 10} if got := r.Inset(1, 1); got != (Rect{X: 6, Y: 6, W: 18, H: 8}) { t.Errorf("Inset(1, 1) = %+v", got) } if got := r.Move(-2, 3); got != (Rect{X: 3, Y: 8, W: 20, H: 10}) { t.Errorf("Move(-2, 3) = %+v", got) } if got := r.WithSize(4, 4); got != (Rect{X: 5, Y: 5, W: 4, H: 4}) { t.Errorf("WithSize(4, 4) = %+v", got) } } func TestRectCenteredIn(t *testing.T) { outer := Rect{X: 0, Y: 0, W: 80, H: 24} got := Rect{W: 20, H: 10}.CenteredIn(outer) if got != (Rect{X: 30, Y: 7, W: 20, H: 10}) { t.Errorf("CenteredIn() = %+v, want it centred", got) } } func TestRectClampIntoMovesWithoutResizing(t *testing.T) { outer := Rect{X: 0, Y: 1, W: 80, H: 23} tests := []struct { name string give Rect want Rect }{ {"past the right edge", Rect{X: 75, Y: 5, W: 20, H: 5}, Rect{X: 60, Y: 5, W: 20, H: 5}}, {"past the bottom", Rect{X: 0, Y: 22, W: 10, H: 5}, Rect{X: 0, Y: 19, W: 10, H: 5}}, {"above the top", Rect{X: 0, Y: -3, W: 10, H: 5}, Rect{X: 0, Y: 1, W: 10, H: 5}}, {"already inside", Rect{X: 4, Y: 4, W: 10, H: 5}, Rect{X: 4, Y: 4, W: 10, H: 5}}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := tc.give.ClampInto(outer) if got != tc.want { t.Errorf("ClampInto() = %+v, want %+v", got, tc.want) } if got.W != tc.give.W || got.H != tc.give.H { t.Error("ClampInto resized the rectangle; it must only move it") } }) } } func TestClampIntoAWindowLargerThanTheScreenStaysAtTheOrigin(t *testing.T) { outer := Rect{X: 0, Y: 0, W: 20, H: 10} got := Rect{X: 5, Y: 5, W: 100, H: 100}.ClampInto(outer) if got.X != 0 || got.Y != 0 { t.Errorf("ClampInto() = %+v, want it pinned to the top-left corner", got) } }