1/*
2 * Copyright 2018, Sean Healy
3 * Distributed under the terms of the MIT License.
4 */
5
6
7
8#include "common.h"
9
10#include <Application.h>
11#include <Window.h>
12#include <TestUtils.h>
13
14
15#include <OutlineListView.h>
16
17BListItem* gExpected[16];
18int gIndex = 0;
19int gCount = 0;
20
21
22template<>
23struct CppUnit::assertion_traits<BListItem*>
24{
25	static bool equal(const BListItem* x, const BListItem* y) {
26		return x == y;
27	}
28
29	static string toString(const BListItem* x) {
30		if (x == NULL)
31			return "(null)";
32		return ((BStringItem*)x)->Text();
33	}
34};
35
36
37BListItem*
38CheckExpected(BListItem* item, void* arg)
39{
40	BStringItem* str = (BStringItem*)item;
41	fprintf(stderr, "Item @%d: %s\n", gIndex, str == NULL ? "(null)" : str->Text());
42
43	CHK(gIndex < gCount);
44	CPPUNIT_ASSERT_EQUAL(gExpected[gIndex], item);
45	gIndex++;
46	return NULL;
47}
48
49
50BListItem*
51FillExpected(BListItem* item, void* arg)
52{
53	gExpected[gCount] = item;
54	gCount++;
55	return NULL;
56}
57
58
59void
60CheckItemsUnder(BOutlineListView* view, BListItem* superitem, bool oneLevelOnly)
61{
62	for (int i = 0; i < gCount; i++)
63		CPPUNIT_ASSERT_EQUAL(gExpected[i], view->ItemUnderAt(superitem, oneLevelOnly, i));
64
65	// Check that we don't get more items
66	CPPUNIT_ASSERT_EQUAL((BListItem*)NULL, view->ItemUnderAt(superitem, oneLevelOnly, gCount));
67}
68
69
70class OutlineListViewTest: public TestCase
71{
72	public:
73		OutlineListViewTest() {}
74		OutlineListViewTest(std::string name) : TestCase(name) {}
75
76		void EachItemUnder();
77		void AddUnder();
78		void ItemUnderAt();
79
80		static Test* Suite();
81
82	private:
83		static BOutlineListView* _SetupTest(const char* name);
84};
85
86
87void
88OutlineListViewTest::EachItemUnder()
89{
90	BOutlineListView* view = _SetupTest("OutlineListView_EachItemUnder");
91
92	// First test is easy
93	gExpected[0] = view->FullListItemAt(6);
94	gExpected[1] = view->FullListItemAt(8);
95	gExpected[2] = view->FullListItemAt(9);
96	gCount = 3;
97	gIndex = 0;
98
99	fprintf(stderr, "Easy test\n");
100	view->EachItemUnder(view->FullListItemAt(5), true, CheckExpected, NULL);
101	CPPUNIT_ASSERT_EQUAL(gCount, view->CountItemsUnder(view->FullListItemAt(5), true));
102
103	// Check that collapsing an item does not change the outcome
104	gIndex = 0;
105	view->Collapse(view->FullListItemAt(0));
106
107	fprintf(stderr, "One collapsed\n");
108	view->EachItemUnder(view->FullListItemAt(5), true, CheckExpected, NULL);
109
110	gIndex = 0;
111	view->Collapse(view->FullListItemAt(5));
112
113	fprintf(stderr, "Two collapsed\n");
114	view->EachItemUnder(view->FullListItemAt(5), true, CheckExpected, NULL);
115	CPPUNIT_ASSERT_EQUAL(gCount, view->CountItemsUnder(view->FullListItemAt(5), true));
116
117	// Also check deeper levels
118	gExpected[1] = view->FullListItemAt(7);
119	gExpected[2] = view->FullListItemAt(8);
120	gExpected[3] = view->FullListItemAt(9);
121	gCount = 4;
122	gIndex = 0;
123
124	fprintf(stderr, "All levels\n");
125	view->EachItemUnder(view->FullListItemAt(5), false, CheckExpected, NULL);
126	CPPUNIT_ASSERT_EQUAL(gCount, view->CountItemsUnder(view->FullListItemAt(5), false));
127
128	view->Expand(view->FullListItemAt(5));
129	view->Collapse(view->FullListItemAt(6));
130	gIndex = 0;
131
132	fprintf(stderr, "All levels with a collapsed sublevel\n");
133	view->EachItemUnder(view->FullListItemAt(5), false, CheckExpected, NULL);
134	CPPUNIT_ASSERT_EQUAL(gCount, view->CountItemsUnder(view->FullListItemAt(5), false));
135
136	// NULL is the parent of level 0 items
137	gExpected[0] = view->FullListItemAt(0);
138	gExpected[1] = view->FullListItemAt(5);
139	gExpected[2] = view->FullListItemAt(10);
140	gCount = 3;
141	gIndex = 0;
142
143	fprintf(stderr, "Level 0\n");
144	view->EachItemUnder(NULL, true, CheckExpected, NULL);
145	CPPUNIT_ASSERT_EQUAL(gCount, view->CountItemsUnder(NULL, true));
146
147	// No visits when the item is not in the list
148	BListItem* notfound = new BStringItem("Not found");
149	gCount = 0;
150	gIndex = 0;
151
152	fprintf(stderr, "Item not in the list\n");
153	view->EachItemUnder(notfound, true, CheckExpected, NULL);
154	CPPUNIT_ASSERT_EQUAL(gCount, view->CountItemsUnder(notfound, true));
155	view->EachItemUnder(notfound, false, CheckExpected, NULL);
156	CPPUNIT_ASSERT_EQUAL(gCount, view->CountItemsUnder(notfound, false));
157
158	// Don't actually run anything
159	delete view->Window();
160}
161
162
163void
164OutlineListViewTest::AddUnder()
165{
166	BOutlineListView* view = _SetupTest("OutlineListView_AddUnder");
167
168	BListItem* one = view->FullListItemAt(0);
169	BListItem* oneA = view->FullListItemAt(1);
170	BListItem* oneA0 = new BStringItem("One-A-0");
171	BListItem* oneA1 = view->FullListItemAt(2);
172
173	int32 count = view->FullListCountItems();
174
175	BListItem* last = view->FullListItemAt(count - 1);
176	BListItem* newLast = new BStringItem("NewLast");
177
178	view->AddUnder(newLast, NULL);
179	view->AddUnder(oneA0, oneA);
180
181	fprintf(stderr, "Count\n");
182	CPPUNIT_ASSERT_EQUAL(count + 2, view->FullListCountItems());
183
184	fprintf(stderr, "Insertion order\n");
185	CPPUNIT_ASSERT_EQUAL(one, view->FullListItemAt(0));
186	CPPUNIT_ASSERT_EQUAL(oneA, view->FullListItemAt(1));
187	CPPUNIT_ASSERT_EQUAL(oneA0, view->FullListItemAt(2));
188	CPPUNIT_ASSERT_EQUAL(oneA1, view->FullListItemAt(3));
189	CPPUNIT_ASSERT_EQUAL(last, view->FullListItemAt(count));
190	CPPUNIT_ASSERT_EQUAL(newLast, view->FullListItemAt(count + 1));
191
192	fprintf(stderr, "Levels\n");
193	CPPUNIT_ASSERT_EQUAL(0, one->OutlineLevel());
194	CPPUNIT_ASSERT_EQUAL(1, oneA->OutlineLevel());
195	CPPUNIT_ASSERT_EQUAL(2, oneA0->OutlineLevel());
196	CPPUNIT_ASSERT_EQUAL(2, oneA1->OutlineLevel());
197	CPPUNIT_ASSERT_EQUAL(0, newLast->OutlineLevel());
198
199	// Don't actually run anything
200	delete view->Window();
201}
202
203
204void
205OutlineListViewTest::ItemUnderAt()
206{
207	BOutlineListView* view = _SetupTest("OutlineListView_ItemUnderAt");
208
209	// EachItemUnder has already been checked, we can use it to know what to expect
210	gCount = 0;
211	view->EachItemUnder(view->FullListItemAt(5), true, FillExpected, NULL);
212
213	fprintf(stderr, "Easy test\n");
214	CheckItemsUnder(view, view->FullListItemAt(5), true);
215
216	// Check that collapsing an item does not change the outcome
217	view->Collapse(view->FullListItemAt(0));
218
219	fprintf(stderr, "One collapsed\n");
220	CheckItemsUnder(view, view->FullListItemAt(5), true);
221
222	view->Collapse(view->FullListItemAt(5));
223
224	fprintf(stderr, "Two collapsed\n");
225	CheckItemsUnder(view, view->FullListItemAt(5), true);
226
227	// Also check deeper levels
228	gCount = 0;
229	view->EachItemUnder(view->FullListItemAt(5), false, FillExpected, NULL);
230
231	fprintf(stderr, "All levels\n");
232	CheckItemsUnder(view, view->FullListItemAt(5), false);
233
234	view->Expand(view->FullListItemAt(5));
235	view->Collapse(view->FullListItemAt(6));
236
237	fprintf(stderr, "All levels with a collapsed sublevel\n");
238	CheckItemsUnder(view, view->FullListItemAt(5), false);
239
240	// NULL is the parent of level 0 items
241	gCount = 0;
242	view->EachItemUnder(NULL, true, FillExpected, NULL);
243
244	fprintf(stderr, "Level 0\n");
245	CheckItemsUnder(view, NULL, true);
246
247	// Get NULL when the item is not in the list
248	BListItem* notfound = new BStringItem("Not found");
249	fprintf(stderr, "Item not in the list\n");
250	CPPUNIT_ASSERT_EQUAL((BListItem*)NULL, view->ItemUnderAt(notfound, true, 0));
251	CPPUNIT_ASSERT_EQUAL((BListItem*)NULL, view->ItemUnderAt(notfound, false, 0));
252
253	// Don't actually run anything
254	delete view->Window();
255}
256
257
258Test*
259OutlineListViewTest::Suite()
260{
261	TestSuite* SuiteOfTests = new TestSuite;
262
263	ADD_TEST4(BOutlineListView, SuiteOfTests, OutlineListViewTest, EachItemUnder);
264	ADD_TEST4(BOutlineListView, SuiteOfTests, OutlineListViewTest, AddUnder);
265	ADD_TEST4(BOutlineListView, SuiteOfTests, OutlineListViewTest, ItemUnderAt);
266
267	return SuiteOfTests;
268}
269
270
271BOutlineListView*
272OutlineListViewTest::_SetupTest(const char* name)
273{
274	if (be_app == NULL)
275		new BApplication("application/x-vnd.OutlineListView.test");
276
277	BWindow* window = new BWindow(BRect(50, 50, 550, 550), name,
278		B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE, 0);
279
280	BOutlineListView* view = new BOutlineListView(BRect(5, 5, 495, 495), "View",
281		B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL);
282	window->AddChild(view);
283
284	view->AddItem(new BStringItem("One", 0));
285	view->AddItem(new BStringItem("One-A", 1));
286	view->AddItem(new BStringItem("One-A-1", 2));
287	view->AddItem(new BStringItem("One-B", 1));
288	view->AddItem(new BStringItem("One-C", 1));
289
290	view->AddItem(new BStringItem("Two", 0));
291	view->AddItem(new BStringItem("Two-A", 1));
292	view->AddItem(new BStringItem("Two-A-1", 2));
293	view->AddItem(new BStringItem("Two-B", 1));
294	view->AddItem(new BStringItem("Two-C", 1));
295
296	view->AddItem(new BStringItem("Three", 0));
297	view->AddItem(new BStringItem("Three-A", 1));
298	view->AddItem(new BStringItem("Three-A-1", 2));
299	view->AddItem(new BStringItem("Three-B", 1));
300	view->AddItem(new BStringItem("Three-C", 1));
301
302	return view;
303}
304
305
306CppUnit::Test* OutlineListViewTestSuite()
307{
308	CppUnit::TestSuite* testSuite = new CppUnit::TestSuite();
309
310	testSuite->addTest(OutlineListViewTest::Suite());
311
312	return testSuite;
313}
314