1package tk
2
3import (
4	"reflect"
5	"testing"
6
7	"src.elv.sh/pkg/cli/term"
8	"src.elv.sh/pkg/ui"
9)
10
11// renderTest is a test case to be used in TestRenderer.
12type renderTest struct {
13	Name   string
14	Given  Renderer
15	Width  int
16	Height int
17	Want   interface{ Buffer() *term.Buffer }
18}
19
20// testRender runs the given Renderer tests.
21func testRender(t *testing.T, tests []renderTest) {
22	t.Helper()
23	for _, test := range tests {
24		t.Run(test.Name, func(t *testing.T) {
25			t.Helper()
26			buf := test.Given.Render(test.Width, test.Height)
27			wantBuf := test.Want.Buffer()
28			if !reflect.DeepEqual(buf, wantBuf) {
29				t.Errorf("Buffer mismatch")
30				t.Logf("Got: %s", buf.TTYString())
31				t.Logf("Want: %s", wantBuf.TTYString())
32			}
33		})
34	}
35}
36
37// handleTest is a test case to be used in testHandle.
38type handleTest struct {
39	Name   string
40	Given  Handler
41	Event  term.Event
42	Events []term.Event
43
44	WantNewState  interface{}
45	WantUnhandled bool
46}
47
48// testHandle runs the given Handler tests.
49func testHandle(t *testing.T, tests []handleTest) {
50	t.Helper()
51
52	for _, test := range tests {
53		t.Run(test.Name, func(t *testing.T) {
54			t.Helper()
55
56			handler := test.Given
57			oldState := getState(handler)
58			defer setState(handler, oldState)
59
60			var handled bool
61			switch {
62			case test.Event != nil && test.Events != nil:
63				t.Fatal("Malformed test case: both Event and Events non-nil:",
64					test.Event, test.Events)
65			case test.Event == nil && test.Events == nil:
66				t.Fatal("Malformed test case: both Event and Events nil")
67			case test.Event != nil:
68				handled = handler.Handle(test.Event)
69			default: // test.Events != nil
70				for _, event := range test.Events {
71					handled = handler.Handle(event)
72				}
73			}
74			if handled != !test.WantUnhandled {
75				t.Errorf("Got handled %v, want %v", handled, !test.WantUnhandled)
76			}
77			if test.WantNewState != nil {
78				state := getState(test.Given)
79				if !reflect.DeepEqual(state, test.WantNewState) {
80					t.Errorf("Got state %v, want %v", state, test.WantNewState)
81				}
82			}
83		})
84	}
85}
86
87func getState(v interface{}) interface{} {
88	return reflectState(v).Interface()
89}
90
91func setState(v, state interface{}) {
92	reflectState(v).Set(reflect.ValueOf(state))
93}
94
95func reflectState(v interface{}) reflect.Value {
96	rv := reflect.ValueOf(v)
97	if rv.Kind() == reflect.Ptr {
98		rv = reflect.Indirect(rv)
99	}
100	return rv.FieldByName("State")
101}
102
103// Test for the test utilities.
104
105func TestTestRender(t *testing.T) {
106	testRender(t, []renderTest{
107		{
108			Name:  "test",
109			Given: &testWidget{text: ui.T("test")},
110			Width: 10, Height: 10,
111
112			Want: term.NewBufferBuilder(10).Write("test"),
113		},
114	})
115}
116
117type testHandlerWithState struct {
118	State testHandlerState
119}
120
121type testHandlerState struct {
122	last  term.Event
123	total int
124}
125
126func (h *testHandlerWithState) Handle(e term.Event) bool {
127	if e == term.K('x') {
128		return false
129	}
130	h.State.last = e
131	h.State.total++
132	return true
133}
134
135func TestTestHandle(t *testing.T) {
136	testHandle(t, []handleTest{
137		{
138			Name:  "WantNewState",
139			Given: &testHandlerWithState{},
140			Event: term.K('a'),
141
142			WantNewState: testHandlerState{last: term.K('a'), total: 1},
143		},
144		{
145			Name:   "Multiple events",
146			Given:  &testHandlerWithState{},
147			Events: []term.Event{term.K('a'), term.K('b')},
148
149			WantNewState: testHandlerState{last: term.K('b'), total: 2},
150		},
151		{
152			Name:  "WantUnhaneld",
153			Given: &testHandlerWithState{},
154			Event: term.K('x'),
155
156			WantUnhandled: true,
157		},
158	})
159}
160