1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial
3
4#include <chrono>
5#define CATCH_CONFIG_MAIN
6#include "catch2/catch.hpp"
7
8#include <slint.h>
9
10struct ModelObserver : public slint::private_api::ModelChangeListener
11{
12 void row_added(size_t index, size_t count) override
13 {
14 added_rows.push_back(x: Range { .row_index: index, .count: count });
15 }
16 void row_changed(size_t index) override { changed_rows.push_back(x: index); }
17 void row_removed(size_t index, size_t count) override
18 {
19 removed_rows.push_back(x: Range { .row_index: index, .count: count });
20 }
21 void reset() override { model_reset = true; }
22
23 void clear()
24 {
25 added_rows.clear();
26 changed_rows.clear();
27 removed_rows.clear();
28 model_reset = false;
29 }
30
31 struct Range
32 {
33 size_t row_index;
34 size_t count;
35
36 bool operator==(const Range &) const = default;
37 };
38 std::vector<Range> added_rows;
39 std::vector<size_t> changed_rows;
40 std::vector<Range> removed_rows;
41 bool model_reset = false;
42};
43
44std::ostream &operator<<(std::ostream &os, const ModelObserver::Range &value)
45{
46 os << "{ row_index: " << value.row_index << "; count: " << value.count << " }";
47 return os;
48}
49
50SCENARIO("Filtering Model")
51{
52 auto vec_model =
53 std::make_shared<slint::VectorModel<int>>(std::vector<int> { 1, 2, 3, 4, 5, 6 });
54
55 auto even_rows = std::make_shared<slint::FilterModel<int>>(
56 vec_model, [](auto value) { return value % 2 == 0; });
57
58 REQUIRE(even_rows->row_count() == 3);
59 REQUIRE(even_rows->row_data(0) == 2);
60 REQUIRE(even_rows->row_data(1) == 4);
61 REQUIRE(even_rows->row_data(2) == 6);
62}
63
64SCENARIO("Filtering Insert")
65{
66 auto vec_model =
67 std::make_shared<slint::VectorModel<int>>(std::vector<int> { 1, 2, 3, 4, 5, 6 });
68
69 auto even_rows = std::make_shared<slint::FilterModel<int>>(
70 vec_model, [](auto value) { return value % 2 == 0; });
71
72 auto observer = std::make_shared<ModelObserver>();
73 even_rows->attach_peer(observer);
74
75 REQUIRE(even_rows->row_count() == 3);
76 REQUIRE(even_rows->row_data(0) == 2);
77 REQUIRE(even_rows->row_data(1) == 4);
78 REQUIRE(even_rows->row_data(2) == 6);
79
80 vec_model->insert(2, 10);
81
82 REQUIRE(observer->added_rows.size() == 1);
83 REQUIRE(observer->added_rows[0] == ModelObserver::Range { 1, 1 });
84 REQUIRE(observer->changed_rows.empty());
85 REQUIRE(observer->removed_rows.empty());
86 REQUIRE(!observer->model_reset);
87 observer->clear();
88
89 REQUIRE(even_rows->row_count() == 4);
90 REQUIRE(even_rows->row_data(0) == 2);
91 REQUIRE(even_rows->row_data(1) == 10);
92 REQUIRE(even_rows->row_data(2) == 4);
93 REQUIRE(even_rows->row_data(3) == 6);
94
95 // insert odd number -> no change
96 vec_model->insert(0, 1);
97
98 REQUIRE(observer->added_rows.empty());
99 REQUIRE(observer->changed_rows.empty());
100 REQUIRE(observer->removed_rows.empty());
101 REQUIRE(!observer->model_reset);
102 observer->clear();
103}
104
105SCENARIO("Filtering Change")
106{
107 auto vec_model =
108 std::make_shared<slint::VectorModel<int>>(std::vector<int> { 1, 2, 3, 4, 5, 6 });
109
110 auto even_rows = std::make_shared<slint::FilterModel<int>>(
111 vec_model, [](auto value) { return value % 2 == 0; });
112
113 auto observer = std::make_shared<ModelObserver>();
114 even_rows->attach_peer(observer);
115
116 REQUIRE(even_rows->row_count() == 3);
117 REQUIRE(even_rows->row_data(0) == 2);
118 REQUIRE(even_rows->row_data(1) == 4);
119 REQUIRE(even_rows->row_data(2) == 6);
120
121 // change leading odd 1 to odd 3 -> no change
122 vec_model->set_row_data(0, 3);
123
124 REQUIRE(observer->added_rows.empty());
125 REQUIRE(observer->changed_rows.empty());
126 REQUIRE(observer->removed_rows.empty());
127 REQUIRE(!observer->model_reset);
128
129 REQUIRE(even_rows->row_count() == 3);
130 REQUIRE(even_rows->row_data(0) == 2);
131 REQUIRE(even_rows->row_data(1) == 4);
132 REQUIRE(even_rows->row_data(2) == 6);
133
134 // change trailing 6 to odd 1 -> one row less
135 vec_model->set_row_data(5, 1);
136
137 REQUIRE(observer->added_rows.empty());
138 REQUIRE(observer->changed_rows.empty());
139 REQUIRE(observer->removed_rows.size() == 1);
140 REQUIRE(observer->removed_rows[0] == ModelObserver::Range { 2, 1 });
141 REQUIRE(!observer->model_reset);
142 observer->clear();
143
144 REQUIRE(even_rows->row_count() == 2);
145 REQUIRE(even_rows->row_data(0) == 2);
146 REQUIRE(even_rows->row_data(1) == 4);
147
148 // change leading odd 3 to even 0 -> one new row
149 vec_model->set_row_data(0, 0);
150
151 REQUIRE(observer->added_rows.size() == 1);
152 REQUIRE(observer->added_rows[0] == ModelObserver::Range { 0, 1 });
153 REQUIRE(observer->changed_rows.empty());
154 REQUIRE(observer->removed_rows.empty());
155 REQUIRE(!observer->model_reset);
156 observer->clear();
157
158 REQUIRE(even_rows->row_count() == 3);
159 REQUIRE(even_rows->row_data(0) == 0);
160 REQUIRE(even_rows->row_data(1) == 2);
161 REQUIRE(even_rows->row_data(2) == 4);
162
163 // change trailing filtered 4 to even 0 -> one changed row
164 vec_model->set_row_data(3, 0);
165
166 REQUIRE(observer->added_rows.empty());
167 REQUIRE(observer->changed_rows.size() == 1);
168 REQUIRE(observer->changed_rows[0] == 2);
169 REQUIRE(observer->removed_rows.empty());
170 REQUIRE(!observer->model_reset);
171 observer->clear();
172
173 REQUIRE(even_rows->row_count() == 3);
174 REQUIRE(even_rows->row_data(0) == 0);
175 REQUIRE(even_rows->row_data(1) == 2);
176 REQUIRE(even_rows->row_data(2) == 0);
177}
178
179SCENARIO("Filtering Model Remove")
180{
181 auto vec_model =
182 std::make_shared<slint::VectorModel<int>>(std::vector<int> { 1, 2, 3, 4, 5, 6 });
183
184 auto even_rows = std::make_shared<slint::FilterModel<int>>(
185 vec_model, [](auto value) { return value % 2 == 0; });
186
187 auto observer = std::make_shared<ModelObserver>();
188 even_rows->attach_peer(observer);
189
190 REQUIRE(even_rows->row_count() == 3);
191 REQUIRE(even_rows->row_data(0) == 2);
192 REQUIRE(even_rows->row_data(1) == 4);
193 REQUIRE(even_rows->row_data(2) == 6);
194
195 // Erase unrelated row
196 vec_model->erase(0);
197
198 REQUIRE(observer->added_rows.empty());
199 REQUIRE(observer->changed_rows.empty());
200 REQUIRE(observer->removed_rows.empty());
201 REQUIRE(!observer->model_reset);
202 observer->clear();
203
204 REQUIRE(even_rows->row_count() == 3);
205 REQUIRE(even_rows->row_data(0) == 2);
206 REQUIRE(even_rows->row_data(1) == 4);
207 REQUIRE(even_rows->row_data(2) == 6);
208
209 // Erase trailing even 6
210 vec_model->erase(4);
211
212 REQUIRE(observer->added_rows.empty());
213 REQUIRE(observer->changed_rows.empty());
214 REQUIRE(observer->removed_rows.size() == 1);
215 REQUIRE(observer->removed_rows[0] == ModelObserver::Range { 2, 1 });
216 REQUIRE(!observer->model_reset);
217 observer->clear();
218
219 REQUIRE(even_rows->row_count() == 2);
220 REQUIRE(even_rows->row_data(0) == 2);
221 REQUIRE(even_rows->row_data(1) == 4);
222}
223
224SCENARIO("Mapped Model")
225{
226 auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 1, 2, 3, 4 });
227
228 auto plus_one_model = std::make_shared<slint::MapModel<int, int>>(
229 vec_model, [](auto value) { return value + 1; });
230
231 auto observer = std::make_shared<ModelObserver>();
232 plus_one_model->attach_peer(observer);
233
234 REQUIRE(plus_one_model->row_count() == 4);
235 REQUIRE(plus_one_model->row_data(0) == 2);
236 REQUIRE(plus_one_model->row_data(1) == 3);
237 REQUIRE(plus_one_model->row_data(2) == 4);
238 REQUIRE(plus_one_model->row_data(3) == 5);
239
240 vec_model->insert(0, 100);
241
242 REQUIRE(observer->added_rows.size() == 1);
243 REQUIRE(observer->added_rows[0] == ModelObserver::Range { 0, 1 });
244 REQUIRE(observer->changed_rows.empty());
245 REQUIRE(observer->removed_rows.empty());
246 REQUIRE(!observer->model_reset);
247 observer->clear();
248
249 REQUIRE(plus_one_model->row_count() == 5);
250 REQUIRE(plus_one_model->row_data(0) == 101);
251 REQUIRE(plus_one_model->row_data(1) == 2);
252 REQUIRE(plus_one_model->row_data(2) == 3);
253 REQUIRE(plus_one_model->row_data(3) == 4);
254 REQUIRE(plus_one_model->row_data(4) == 5);
255
256 vec_model->set_row_data(1, 3);
257
258 REQUIRE(observer->added_rows.empty());
259 REQUIRE(observer->changed_rows.size() == 1);
260 REQUIRE(observer->changed_rows[0] == 1);
261 REQUIRE(observer->removed_rows.empty());
262 REQUIRE(!observer->model_reset);
263 observer->clear();
264
265 REQUIRE(plus_one_model->row_count() == 5);
266 REQUIRE(plus_one_model->row_data(0) == 101);
267 REQUIRE(plus_one_model->row_data(1) == 4);
268 REQUIRE(plus_one_model->row_data(2) == 3);
269 REQUIRE(plus_one_model->row_data(3) == 4);
270 REQUIRE(plus_one_model->row_data(4) == 5);
271
272 vec_model->erase(3);
273
274 REQUIRE(observer->added_rows.empty());
275 REQUIRE(observer->changed_rows.empty());
276 REQUIRE(observer->removed_rows.size() == 1);
277 REQUIRE(observer->removed_rows[0] == ModelObserver::Range { 3, 1 });
278 REQUIRE(!observer->model_reset);
279 observer->clear();
280
281 REQUIRE(plus_one_model->row_count() == 4);
282 REQUIRE(plus_one_model->row_data(0) == 101);
283 REQUIRE(plus_one_model->row_data(1) == 4);
284 REQUIRE(plus_one_model->row_data(2) == 3);
285 REQUIRE(plus_one_model->row_data(3) == 5);
286}
287
288SCENARIO("Sorted Model Insert")
289{
290 auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 3, 4, 1, 2 });
291
292 auto sorted_model = std::make_shared<slint::SortModel<int>>(
293 vec_model, [](auto lhs, auto rhs) { return lhs < rhs; });
294
295 auto observer = std::make_shared<ModelObserver>();
296 sorted_model->attach_peer(observer);
297
298 REQUIRE(sorted_model->row_count() == 4);
299 REQUIRE(sorted_model->row_data(0) == 1);
300 REQUIRE(sorted_model->row_data(1) == 2);
301 REQUIRE(sorted_model->row_data(2) == 3);
302 REQUIRE(sorted_model->row_data(3) == 4);
303
304 vec_model->insert(0, 10);
305
306 REQUIRE(observer->added_rows.size() == 1);
307 REQUIRE(observer->added_rows[0] == ModelObserver::Range { 4, 1 });
308 REQUIRE(observer->changed_rows.empty());
309 REQUIRE(observer->removed_rows.empty());
310 REQUIRE(!observer->model_reset);
311 observer->clear();
312
313 REQUIRE(sorted_model->row_count() == 5);
314 REQUIRE(sorted_model->row_data(0) == 1);
315 REQUIRE(sorted_model->row_data(1) == 2);
316 REQUIRE(sorted_model->row_data(2) == 3);
317 REQUIRE(sorted_model->row_data(3) == 4);
318 REQUIRE(sorted_model->row_data(4) == 10);
319}
320
321SCENARIO("Sorted Model Remove")
322{
323 auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 3, 4, 1, 2 });
324
325 auto sorted_model = std::make_shared<slint::SortModel<int>>(
326 vec_model, [](auto lhs, auto rhs) { return lhs < rhs; });
327
328 auto observer = std::make_shared<ModelObserver>();
329 sorted_model->attach_peer(observer);
330
331 REQUIRE(sorted_model->row_count() == 4);
332 REQUIRE(sorted_model->row_data(0) == 1);
333 REQUIRE(sorted_model->row_data(1) == 2);
334 REQUIRE(sorted_model->row_data(2) == 3);
335 REQUIRE(sorted_model->row_data(3) == 4);
336
337 /// Remove the entry with the value 4
338 vec_model->erase(1);
339
340 REQUIRE(observer->added_rows.empty());
341 REQUIRE(observer->changed_rows.empty());
342 REQUIRE(observer->removed_rows.size() == 1);
343 REQUIRE(observer->removed_rows[0] == ModelObserver::Range { 3, 1 });
344 REQUIRE(!observer->model_reset);
345 observer->clear();
346
347 REQUIRE(sorted_model->row_count() == 3);
348 REQUIRE(sorted_model->row_data(0) == 1);
349 REQUIRE(sorted_model->row_data(1) == 2);
350 REQUIRE(sorted_model->row_data(2) == 3);
351}
352
353SCENARIO("Sorted Model Change")
354{
355 auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 3, 4, 1, 2 });
356
357 auto sorted_model = std::make_shared<slint::SortModel<int>>(
358 vec_model, [](auto lhs, auto rhs) { return lhs < rhs; });
359
360 auto observer = std::make_shared<ModelObserver>();
361 sorted_model->attach_peer(observer);
362
363 REQUIRE(sorted_model->row_count() == 4);
364 REQUIRE(sorted_model->row_data(0) == 1);
365 REQUIRE(sorted_model->row_data(1) == 2);
366 REQUIRE(sorted_model->row_data(2) == 3);
367 REQUIRE(sorted_model->row_data(3) == 4);
368
369 /// Change the entry with the value 4 to 10 -> maintain order
370 vec_model->set_row_data(1, 10);
371
372 REQUIRE(observer->added_rows.empty());
373 REQUIRE(observer->changed_rows.size() == 1);
374 REQUIRE(observer->changed_rows[0] == 3);
375 REQUIRE(observer->removed_rows.empty());
376 REQUIRE(!observer->model_reset);
377 observer->clear();
378
379 REQUIRE(sorted_model->row_count() == 4);
380 REQUIRE(sorted_model->row_data(0) == 1);
381 REQUIRE(sorted_model->row_data(1) == 2);
382 REQUIRE(sorted_model->row_data(2) == 3);
383 REQUIRE(sorted_model->row_data(3) == 10);
384
385 /// Change the entry with the value 10 to 0 -> new order with remove and insert
386 vec_model->set_row_data(1, 0);
387
388 REQUIRE(observer->added_rows.size() == 1);
389 REQUIRE(observer->added_rows[0] == ModelObserver::Range { 0, 1 });
390 REQUIRE(observer->changed_rows.empty());
391 REQUIRE(observer->removed_rows.size() == 1);
392 REQUIRE(observer->removed_rows[0] == ModelObserver::Range { 3, 1 });
393 REQUIRE(!observer->model_reset);
394 observer->clear();
395
396 REQUIRE(sorted_model->row_count() == 4);
397 REQUIRE(sorted_model->row_data(0) == 0);
398 REQUIRE(sorted_model->row_data(1) == 1);
399 REQUIRE(sorted_model->row_data(2) == 2);
400 REQUIRE(sorted_model->row_data(3) == 3);
401}
402
403SCENARIO("Reverse Model Insert")
404{
405 auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 3, 4, 1, 2 });
406
407 auto reverse_model = std::make_shared<slint::ReverseModel<int>>(vec_model);
408
409 auto observer = std::make_shared<ModelObserver>();
410 reverse_model->attach_peer(observer);
411
412 REQUIRE(reverse_model->row_count() == 4);
413 REQUIRE(reverse_model->row_data(0) == 2);
414 REQUIRE(reverse_model->row_data(1) == 1);
415 REQUIRE(reverse_model->row_data(2) == 4);
416 REQUIRE(reverse_model->row_data(3) == 3);
417
418 vec_model->insert(0, 10);
419
420 REQUIRE(observer->added_rows.size() == 1);
421 REQUIRE(observer->added_rows[0] == ModelObserver::Range { 4, 1 });
422 REQUIRE(observer->changed_rows.empty());
423 REQUIRE(observer->removed_rows.empty());
424 REQUIRE(!observer->model_reset);
425 observer->clear();
426
427 REQUIRE(reverse_model->row_count() == 5);
428 REQUIRE(reverse_model->row_data(0) == 2);
429 REQUIRE(reverse_model->row_data(1) == 1);
430 REQUIRE(reverse_model->row_data(2) == 4);
431 REQUIRE(reverse_model->row_data(3) == 3);
432 REQUIRE(reverse_model->row_data(4) == 10);
433}
434
435SCENARIO("Reverse Model Remove")
436{
437 auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 3, 4, 1, 2 });
438
439 auto reverse_model = std::make_shared<slint::ReverseModel<int>>(vec_model);
440
441 auto observer = std::make_shared<ModelObserver>();
442 reverse_model->attach_peer(observer);
443
444 REQUIRE(reverse_model->row_count() == 4);
445 REQUIRE(reverse_model->row_data(0) == 2);
446 REQUIRE(reverse_model->row_data(1) == 1);
447 REQUIRE(reverse_model->row_data(2) == 4);
448 REQUIRE(reverse_model->row_data(3) == 3);
449
450 /// Remove the entry with the value 4
451 vec_model->erase(1);
452
453 REQUIRE(observer->added_rows.empty());
454 REQUIRE(observer->changed_rows.empty());
455 REQUIRE(observer->removed_rows.size() == 1);
456 REQUIRE(observer->removed_rows[0] == ModelObserver::Range { 2, 1 });
457 REQUIRE(!observer->model_reset);
458 observer->clear();
459
460 REQUIRE(reverse_model->row_count() == 3);
461 REQUIRE(reverse_model->row_data(0) == 2);
462 REQUIRE(reverse_model->row_data(1) == 1);
463 REQUIRE(reverse_model->row_data(2) == 3);
464}
465
466SCENARIO("Reverse Model Change")
467{
468 auto vec_model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 3, 4, 1, 2 });
469
470 auto reverse_model = std::make_shared<slint::ReverseModel<int>>(vec_model);
471
472 auto observer = std::make_shared<ModelObserver>();
473 reverse_model->attach_peer(observer);
474
475 REQUIRE(reverse_model->row_count() == 4);
476 REQUIRE(reverse_model->row_data(0) == 2);
477 REQUIRE(reverse_model->row_data(1) == 1);
478 REQUIRE(reverse_model->row_data(2) == 4);
479 REQUIRE(reverse_model->row_data(3) == 3);
480
481 /// Change the entry with the value 4 to 10 -> maintain order
482 vec_model->set_row_data(1, 10);
483
484 REQUIRE(observer->added_rows.empty());
485 REQUIRE(observer->changed_rows.size() == 1);
486 REQUIRE(observer->changed_rows[0] == 2);
487 REQUIRE(observer->removed_rows.empty());
488 REQUIRE(!observer->model_reset);
489 observer->clear();
490
491 REQUIRE(reverse_model->row_count() == 4);
492 REQUIRE(reverse_model->row_data(0) == 2);
493 REQUIRE(reverse_model->row_data(1) == 1);
494 REQUIRE(reverse_model->row_data(2) == 10);
495 REQUIRE(reverse_model->row_data(3) == 3);
496}
497
498TEST_CASE("VectorModel clear and replace")
499{
500 using namespace slint::private_api;
501
502 auto model = std::make_shared<slint::VectorModel<int>>(std::vector<int> { 0, 1, 2, 3, 4 });
503
504 auto observer = std::make_shared<ModelObserver>();
505 model->attach_peer(observer);
506
507 REQUIRE(model->row_count() == 5);
508 model->clear();
509 REQUIRE(model->row_count() == 0);
510 REQUIRE(observer->added_rows.empty());
511 REQUIRE(observer->changed_rows.empty());
512 REQUIRE(observer->removed_rows.empty());
513 REQUIRE(observer->model_reset);
514 observer->clear();
515
516 model->clear();
517 REQUIRE(!observer->model_reset);
518 observer->clear();
519
520 model->set_vector({ 2, 3, 4 });
521 REQUIRE(model->row_count() == 3);
522 REQUIRE(model->row_data(1) == 3);
523 REQUIRE(observer->added_rows.empty());
524 REQUIRE(observer->changed_rows.empty());
525 REQUIRE(observer->removed_rows.empty());
526 REQUIRE(observer->model_reset);
527
528 // Test that taking a vector by value compiles
529 std::vector<int> new_data { 5, 6, 7, 8 };
530 model->set_vector(new_data);
531}
532

source code of slint/api/cpp/tests/models.cpp