1 | /* |
2 | * Copyright © 2019 Benjamin Otte |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Authors: Benjamin Otte <otte@gnome.org> |
18 | */ |
19 | |
20 | #include <locale.h> |
21 | |
22 | #include <gtk/gtk.h> |
23 | |
24 | static void |
25 | inc_counter (gpointer data) |
26 | { |
27 | guint *counter = data; |
28 | |
29 | *counter += 1; |
30 | } |
31 | |
32 | static void |
33 | test_property (void) |
34 | { |
35 | GValue value = G_VALUE_INIT; |
36 | GtkExpression *expr; |
37 | GtkExpressionWatch *watch; |
38 | GtkStringFilter *filter; |
39 | guint counter = 0; |
40 | gboolean ret; |
41 | |
42 | filter = gtk_string_filter_new (NULL); |
43 | expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, property_name: "search" ); |
44 | watch = gtk_expression_watch (self: expr, this_: filter, notify: inc_counter, user_data: &counter, NULL); |
45 | |
46 | ret = gtk_expression_evaluate (self: expr, this_: filter, value: &value); |
47 | g_assert_true (ret); |
48 | g_assert_cmpstr (g_value_get_string (&value), ==, NULL); |
49 | g_value_unset (value: &value); |
50 | |
51 | gtk_string_filter_set_search (self: filter, search: "Hello World" ); |
52 | g_assert_cmpint (counter, ==, 1); |
53 | counter = 0; |
54 | |
55 | ret = gtk_expression_evaluate (self: expr, this_: filter , value: &value); |
56 | g_assert_true (ret); |
57 | g_assert_cmpstr (g_value_get_string (&value), ==, "Hello World" ); |
58 | g_value_unset (value: &value); |
59 | |
60 | gtk_expression_watch_unwatch (watch); |
61 | g_assert_cmpint (counter, ==, 0); |
62 | |
63 | gtk_expression_unref (self: expr); |
64 | g_object_unref (object: filter); |
65 | } |
66 | |
67 | static void |
68 | test_interface_property (void) |
69 | { |
70 | GtkExpression *expr; |
71 | |
72 | expr = gtk_property_expression_new (GTK_TYPE_ORIENTABLE, NULL, property_name: "orientation" ); |
73 | g_assert_cmpstr (gtk_property_expression_get_pspec (expr)->name, ==, "orientation" ); |
74 | gtk_expression_unref (self: expr); |
75 | } |
76 | |
77 | static char * |
78 | print_filter_info (GtkStringFilter *filter, |
79 | const char *search, |
80 | gboolean ignore_case, |
81 | GtkStringFilterMatchMode match_mode) |
82 | { |
83 | g_assert_cmpstr (search, ==, gtk_string_filter_get_search (filter)); |
84 | g_assert_cmpint (ignore_case, ==, gtk_string_filter_get_ignore_case (filter)); |
85 | g_assert_cmpint (match_mode, ==, gtk_string_filter_get_match_mode (filter)); |
86 | |
87 | return g_strdup (str: "OK" ); |
88 | } |
89 | |
90 | static void |
91 | test_cclosure (void) |
92 | { |
93 | GValue value = G_VALUE_INIT; |
94 | GtkExpression *expr, *pexpr[3]; |
95 | GtkExpressionWatch *watch; |
96 | GtkStringFilter *filter; |
97 | guint counter = 0; |
98 | gboolean ret; |
99 | |
100 | filter = GTK_STRING_FILTER (ptr: gtk_string_filter_new (NULL)); |
101 | pexpr[0] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, property_name: "search" ); |
102 | pexpr[1] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, property_name: "ignore-case" ); |
103 | pexpr[2] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, property_name: "match-mode" ); |
104 | expr = gtk_cclosure_expression_new (G_TYPE_STRING, |
105 | NULL, |
106 | n_params: 3, |
107 | params: pexpr, |
108 | G_CALLBACK (print_filter_info), |
109 | NULL, |
110 | NULL); |
111 | watch = gtk_expression_watch (self: expr, this_: filter, notify: inc_counter, user_data: &counter, NULL); |
112 | |
113 | ret = gtk_expression_evaluate (self: expr, this_: filter, value: &value); |
114 | g_assert_true (ret); |
115 | g_assert_cmpstr (g_value_get_string (&value), ==, "OK" ); |
116 | g_value_unset (value: &value); |
117 | |
118 | gtk_string_filter_set_search (self: filter, search: "Hello World" ); |
119 | g_assert_cmpint (counter, ==, 1); |
120 | ret = gtk_expression_evaluate (self: expr, this_: filter , value: &value); |
121 | g_assert_true (ret); |
122 | g_assert_cmpstr (g_value_get_string (&value), ==, "OK" ); |
123 | g_value_unset (value: &value); |
124 | |
125 | gtk_string_filter_set_ignore_case (self: filter, FALSE); |
126 | g_assert_cmpint (counter, ==, 2); |
127 | ret = gtk_expression_evaluate (self: expr, this_: filter , value: &value); |
128 | g_assert_true (ret); |
129 | g_assert_cmpstr (g_value_get_string (&value), ==, "OK" ); |
130 | g_value_unset (value: &value); |
131 | |
132 | gtk_string_filter_set_search (self: filter, search: "Hello" ); |
133 | gtk_string_filter_set_ignore_case (self: filter, TRUE); |
134 | gtk_string_filter_set_match_mode (self: filter, mode: GTK_STRING_FILTER_MATCH_MODE_EXACT); |
135 | g_assert_cmpint (counter, ==, 5); |
136 | ret = gtk_expression_evaluate (self: expr, this_: filter , value: &value); |
137 | g_assert_true (ret); |
138 | g_assert_cmpstr (g_value_get_string (&value), ==, "OK" ); |
139 | g_value_unset (value: &value); |
140 | |
141 | gtk_expression_watch_unwatch (watch); |
142 | g_assert_cmpint (counter, ==, 5); |
143 | |
144 | gtk_expression_unref (self: expr); |
145 | g_object_unref (object: filter); |
146 | } |
147 | |
148 | static char * |
149 | make_string (void) |
150 | { |
151 | return g_strdup (str: "Hello" ); |
152 | } |
153 | |
154 | static void |
155 | test_closure (void) |
156 | { |
157 | GValue value = G_VALUE_INIT; |
158 | GtkExpression *expr; |
159 | GClosure *closure; |
160 | gboolean ret; |
161 | |
162 | closure = g_cclosure_new (G_CALLBACK (make_string), NULL, NULL); |
163 | expr = gtk_closure_expression_new (G_TYPE_STRING, closure, n_params: 0, NULL); |
164 | ret = gtk_expression_evaluate (self: expr, NULL, value: &value); |
165 | g_assert_true (ret); |
166 | g_assert_cmpstr (g_value_get_string (&value), ==, "Hello" ); |
167 | g_value_unset (value: &value); |
168 | |
169 | gtk_expression_unref (self: expr); |
170 | } |
171 | |
172 | static void |
173 | test_constant (void) |
174 | { |
175 | GtkExpression *expr; |
176 | GValue value = G_VALUE_INIT; |
177 | const GValue *v; |
178 | gboolean res; |
179 | |
180 | expr = gtk_constant_expression_new (G_TYPE_INT, 22); |
181 | g_assert_cmpint (gtk_expression_get_value_type (expr), ==, G_TYPE_INT); |
182 | g_assert_true (gtk_expression_is_static (expr)); |
183 | |
184 | res = gtk_expression_evaluate (self: expr, NULL, value: &value); |
185 | g_assert_true (res); |
186 | g_assert_cmpint (g_value_get_int (&value), ==, 22); |
187 | |
188 | v = gtk_constant_expression_get_value (expression: expr); |
189 | g_assert_cmpint (g_value_get_int (v), ==, 22); |
190 | |
191 | gtk_expression_unref (self: expr); |
192 | } |
193 | |
194 | /* Test that object expressions fail to evaluate when |
195 | * the object is gone. |
196 | */ |
197 | static void |
198 | test_object (void) |
199 | { |
200 | GtkExpression *expr; |
201 | GObject *obj; |
202 | GValue value = G_VALUE_INIT; |
203 | gboolean res; |
204 | GObject *o; |
205 | |
206 | obj = G_OBJECT (gtk_string_filter_new (NULL)); |
207 | |
208 | expr = gtk_object_expression_new (object: obj); |
209 | g_assert_true (!gtk_expression_is_static (expr)); |
210 | g_assert_cmpint (gtk_expression_get_value_type (expr), ==, GTK_TYPE_STRING_FILTER); |
211 | |
212 | res = gtk_expression_evaluate (self: expr, NULL, value: &value); |
213 | g_assert_true (res); |
214 | g_assert_true (g_value_get_object (&value) == obj); |
215 | g_value_unset (value: &value); |
216 | |
217 | o = gtk_object_expression_get_object (expression: expr); |
218 | g_assert_true (o == obj); |
219 | |
220 | g_clear_object (&obj); |
221 | res = gtk_expression_evaluate (self: expr, NULL, value: &value); |
222 | g_assert_false (res); |
223 | |
224 | gtk_expression_unref (self: expr); |
225 | } |
226 | |
227 | /* Some basic tests that nested expressions work; in particular test |
228 | * that watching works when things change deeper in the expression tree |
229 | * |
230 | * The setup we use is GtkFilterListModel -> GtkFilter -> "search" property, |
231 | * which gives us an expression tree like |
232 | * |
233 | * GtkPropertyExpression "search" |
234 | * -> GtkPropertyExpression "filter" |
235 | * -> GtkObjectExpression listmodel |
236 | * |
237 | * We test setting both the search property and the filter property. |
238 | */ |
239 | static void |
240 | test_nested (void) |
241 | { |
242 | GtkExpression *list_expr; |
243 | GtkExpression *filter_expr; |
244 | GtkExpression *expr; |
245 | GtkStringFilter *filter; |
246 | GListModel *list; |
247 | GtkFilterListModel *filtered; |
248 | GValue value = G_VALUE_INIT; |
249 | gboolean res; |
250 | GtkExpressionWatch *watch; |
251 | guint counter = 0; |
252 | |
253 | filter = gtk_string_filter_new (NULL); |
254 | gtk_string_filter_set_search (self: filter, search: "word" ); |
255 | list = G_LIST_MODEL (ptr: g_list_store_new (G_TYPE_OBJECT)); |
256 | filtered = gtk_filter_list_model_new (model: list, g_object_ref (GTK_FILTER (filter))); |
257 | |
258 | list_expr = gtk_object_expression_new (G_OBJECT (filtered)); |
259 | filter_expr = gtk_property_expression_new (GTK_TYPE_FILTER_LIST_MODEL, expression: list_expr, property_name: "filter" ); |
260 | expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, expression: filter_expr, property_name: "search" ); |
261 | |
262 | g_assert_true (!gtk_expression_is_static (expr)); |
263 | g_assert_cmpint (gtk_expression_get_value_type (expr), ==, G_TYPE_STRING); |
264 | |
265 | res = gtk_expression_evaluate (self: expr, NULL, value: &value); |
266 | g_assert_true (res); |
267 | g_assert_cmpstr (g_value_get_string (&value), ==, "word" ); |
268 | g_value_unset (value: &value); |
269 | |
270 | watch = gtk_expression_watch (self: expr, NULL, notify: inc_counter, user_data: &counter, NULL); |
271 | gtk_string_filter_set_search (self: GTK_STRING_FILTER (ptr: filter), search: "salad" ); |
272 | g_assert_cmpint (counter, ==, 1); |
273 | counter = 0; |
274 | |
275 | res = gtk_expression_evaluate (self: expr, NULL, value: &value); |
276 | g_assert_true (res); |
277 | g_assert_cmpstr (g_value_get_string (&value), ==, "salad" ); |
278 | g_value_unset (value: &value); |
279 | |
280 | gtk_filter_list_model_set_filter (self: filtered, filter: GTK_FILTER (ptr: filter)); |
281 | g_assert_cmpint (counter, ==, 0); |
282 | |
283 | g_clear_object (&filter); |
284 | filter = gtk_string_filter_new (NULL); |
285 | gtk_string_filter_set_search (self: filter, search: "salad" ); |
286 | gtk_filter_list_model_set_filter (self: filtered, filter: GTK_FILTER (ptr: filter)); |
287 | g_assert_cmpint (counter, ==, 1); |
288 | counter = 0; |
289 | |
290 | res = gtk_expression_evaluate (self: expr, NULL, value: &value); |
291 | g_assert_true (res); |
292 | g_assert_cmpstr (g_value_get_string (&value), ==, "salad" ); |
293 | g_value_unset (value: &value); |
294 | |
295 | gtk_string_filter_set_search (self: filter, search: "bar" ); |
296 | g_assert_cmpint (counter, ==, 1); |
297 | counter = 0; |
298 | |
299 | res = gtk_expression_evaluate (self: expr, NULL, value: &value); |
300 | g_assert_true (res); |
301 | g_assert_cmpstr (g_value_get_string (&value), ==, "bar" ); |
302 | g_value_unset (value: &value); |
303 | |
304 | gtk_filter_list_model_set_filter (self: filtered, NULL); |
305 | g_assert_cmpint (counter, ==, 1); |
306 | counter = 0; |
307 | |
308 | res = gtk_expression_evaluate (self: expr, NULL, value: &value); |
309 | g_assert_false (res); |
310 | |
311 | gtk_expression_watch_unwatch (watch); |
312 | g_assert_cmpint (counter, ==, 0); |
313 | |
314 | g_object_unref (object: filtered); |
315 | gtk_expression_unref (self: expr); |
316 | } |
317 | |
318 | /* This test uses the same setup as the last test, but |
319 | * passes the filter as the "this" object when creating |
320 | * the watch. |
321 | * |
322 | * So when we set a new filter and the old one gets desroyed, |
323 | * the watch should invalidate itself because its this object |
324 | * is gone. |
325 | */ |
326 | static void |
327 | test_nested_this_destroyed (void) |
328 | { |
329 | GtkExpression *list_expr; |
330 | GtkExpression *filter_expr; |
331 | GtkExpression *expr; |
332 | GtkStringFilter *filter; |
333 | GListModel *list; |
334 | GtkFilterListModel *filtered; |
335 | GValue value = G_VALUE_INIT; |
336 | gboolean res; |
337 | GtkExpressionWatch *watch; |
338 | guint counter = 0; |
339 | |
340 | filter = gtk_string_filter_new (NULL); |
341 | gtk_string_filter_set_search (self: filter, search: "word" ); |
342 | list = G_LIST_MODEL (ptr: g_list_store_new (G_TYPE_OBJECT)); |
343 | filtered = gtk_filter_list_model_new (model: list, g_object_ref (GTK_FILTER (filter))); |
344 | |
345 | list_expr = gtk_object_expression_new (G_OBJECT (filtered)); |
346 | filter_expr = gtk_property_expression_new (GTK_TYPE_FILTER_LIST_MODEL, expression: list_expr, property_name: "filter" ); |
347 | expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, expression: filter_expr, property_name: "search" ); |
348 | |
349 | watch = gtk_expression_watch (self: expr, this_: filter, notify: inc_counter, user_data: &counter, NULL); |
350 | gtk_expression_watch_ref (watch); |
351 | res = gtk_expression_watch_evaluate (watch, value: &value); |
352 | g_assert_true (res); |
353 | g_assert_cmpstr (g_value_get_string (&value), ==, "word" ); |
354 | g_value_unset (value: &value); |
355 | |
356 | g_clear_object (&filter); |
357 | g_assert_cmpint (counter, ==, 0); |
358 | |
359 | filter = gtk_string_filter_new (NULL); |
360 | gtk_string_filter_set_search (self: filter, search: "salad" ); |
361 | gtk_filter_list_model_set_filter (self: filtered, filter: GTK_FILTER (ptr: filter)); |
362 | g_assert_cmpint (counter, ==, 1); |
363 | counter = 0; |
364 | |
365 | res = gtk_expression_watch_evaluate (watch, value: &value); |
366 | g_assert_false (res); |
367 | |
368 | gtk_string_filter_set_search (self: filter, search: "bar" ); |
369 | g_assert_cmpint (counter, ==, 0); |
370 | |
371 | gtk_filter_list_model_set_filter (self: filtered, NULL); |
372 | g_assert_cmpint (counter, ==, 0); |
373 | |
374 | res = gtk_expression_watch_evaluate (watch, value: &value); |
375 | g_assert_false (res); |
376 | g_assert_false (G_IS_VALUE (&value)); |
377 | |
378 | /* We unwatch on purpose here to make sure it doesn't do bad things. */ |
379 | gtk_expression_watch_unwatch (watch); |
380 | gtk_expression_watch_unref (watch); |
381 | g_assert_cmpint (counter, ==, 0); |
382 | |
383 | g_object_unref (object: filtered); |
384 | g_object_unref (object: filter); |
385 | gtk_expression_unref (self: expr); |
386 | } |
387 | |
388 | /* Test that property expressions fail to evaluate if the |
389 | * expression evaluates to an object of the wrong type |
390 | */ |
391 | static void |
392 | test_type_mismatch (void) |
393 | { |
394 | GtkFilter *filter; |
395 | GtkExpression *expr; |
396 | GValue value = G_VALUE_INIT; |
397 | gboolean res; |
398 | |
399 | filter = GTK_FILTER (ptr: gtk_any_filter_new ()); |
400 | |
401 | expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, expression: gtk_constant_expression_new (GTK_TYPE_ANY_FILTER, filter), property_name: "search" ); |
402 | |
403 | res = gtk_expression_evaluate (self: expr, NULL, value: &value); |
404 | g_assert_false (res); |
405 | g_assert_false (G_IS_VALUE (&value)); |
406 | |
407 | gtk_expression_unref (self: expr); |
408 | g_object_unref (object: filter); |
409 | } |
410 | |
411 | /* Some basic tests around 'this' */ |
412 | static void |
413 | test_this (void) |
414 | { |
415 | GtkStringFilter *filter; |
416 | GtkStringFilter *filter2; |
417 | GtkExpression *expr; |
418 | GValue value = G_VALUE_INIT; |
419 | gboolean res; |
420 | |
421 | expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, property_name: "search" ); |
422 | |
423 | filter = gtk_string_filter_new (NULL); |
424 | gtk_string_filter_set_search (self: filter, search: "word" ); |
425 | |
426 | filter2 = gtk_string_filter_new (NULL); |
427 | gtk_string_filter_set_search (self: filter2, search: "sausage" ); |
428 | |
429 | res = gtk_expression_evaluate (self: expr, this_: filter, value: &value); |
430 | g_assert_true (res); |
431 | g_assert_cmpstr (g_value_get_string (&value), ==, "word" ); |
432 | g_value_unset (value: &value); |
433 | |
434 | res = gtk_expression_evaluate (self: expr, this_: filter2, value: &value); |
435 | g_assert_true (res); |
436 | g_assert_cmpstr (g_value_get_string (&value), ==, "sausage" ); |
437 | g_value_unset (value: &value); |
438 | |
439 | gtk_expression_unref (self: expr); |
440 | g_object_unref (object: filter2); |
441 | g_object_unref (object: filter); |
442 | } |
443 | |
444 | /* Check that even for static expressions, watches can be created |
445 | * and destroying the "this" argument does invalidate the |
446 | * expression. |
447 | */ |
448 | static void |
449 | test_constant_watch_this_destroyed (void) |
450 | { |
451 | GtkExpression *expr; |
452 | GObject *this; |
453 | guint counter = 0; |
454 | |
455 | this = g_object_new (G_TYPE_OBJECT, NULL); |
456 | expr = gtk_constant_expression_new (G_TYPE_INT, 42); |
457 | gtk_expression_watch (self: expr, this_: this, notify: inc_counter, user_data: &counter, NULL); |
458 | g_assert_cmpint (counter, ==, 0); |
459 | |
460 | g_clear_object (&this); |
461 | g_assert_cmpint (counter, ==, 1); |
462 | |
463 | gtk_expression_unref (self: expr); |
464 | } |
465 | |
466 | /* Basic test of gtk_expression_bind */ |
467 | static void |
468 | test_bind (void) |
469 | { |
470 | GtkStringFilter *target; |
471 | GtkStringFilter *source; |
472 | GtkExpression *expr; |
473 | GtkExpressionWatch *watch; |
474 | GValue value = G_VALUE_INIT; |
475 | gboolean res; |
476 | |
477 | expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, property_name: "search" ); |
478 | |
479 | target = gtk_string_filter_new (NULL); |
480 | gtk_string_filter_set_search (self: target, search: "word" ); |
481 | g_assert_cmpstr (gtk_string_filter_get_search (target), ==, "word" ); |
482 | |
483 | source = gtk_string_filter_new (NULL); |
484 | gtk_string_filter_set_search (self: source, search: "sausage" ); |
485 | |
486 | watch = gtk_expression_bind (self: expr, target, property: "search" , this_: source); |
487 | gtk_expression_watch_ref (watch); |
488 | g_assert_cmpstr (gtk_string_filter_get_search (target), ==, "sausage" ); |
489 | |
490 | gtk_string_filter_set_search (self: source, search: "salad" ); |
491 | g_assert_cmpstr (gtk_string_filter_get_search (target), ==, "salad" ); |
492 | res = gtk_expression_watch_evaluate (watch, value: &value); |
493 | g_assert_true (res); |
494 | g_assert_cmpstr (g_value_get_string (&value), ==, "salad" ); |
495 | g_value_unset (value: &value); |
496 | |
497 | g_object_unref (object: source); |
498 | g_assert_cmpstr (gtk_string_filter_get_search (target), ==, "salad" ); |
499 | res = gtk_expression_watch_evaluate (watch, value: &value); |
500 | g_assert_false (res); |
501 | g_assert_false (G_IS_VALUE (&value)); |
502 | |
503 | g_object_unref (object: target); |
504 | gtk_expression_watch_unref (watch); |
505 | } |
506 | |
507 | /* Another test of bind, this time we watch ourselves */ |
508 | static void |
509 | test_bind_self (void) |
510 | { |
511 | GtkStringFilter *filter; |
512 | GtkExpression *expr; |
513 | |
514 | expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, |
515 | NULL, |
516 | property_name: "ignore-case" ); |
517 | |
518 | filter = gtk_string_filter_new (NULL); |
519 | gtk_string_filter_set_search (self: filter, search: "word" ); |
520 | g_assert_cmpstr (gtk_string_filter_get_search (filter), ==, "word" ); |
521 | |
522 | gtk_expression_bind (self: expr, target: filter, property: "search" , this_: filter); |
523 | g_assert_cmpstr (gtk_string_filter_get_search (filter), ==, "TRUE" ); |
524 | |
525 | g_object_unref (object: filter); |
526 | } |
527 | |
528 | /* Test bind does the right memory management if the target's |
529 | * dispose() kills the source */ |
530 | static void |
531 | test_bind_child (void) |
532 | { |
533 | GtkStringFilter *filter; |
534 | GtkFilterListModel *child, *target; |
535 | GtkExpression *expr; |
536 | |
537 | expr = gtk_property_expression_new (GTK_TYPE_FILTER_LIST_MODEL, |
538 | NULL, |
539 | property_name: "filter" ); |
540 | |
541 | filter = gtk_string_filter_new (NULL); |
542 | child = gtk_filter_list_model_new (NULL, filter: GTK_FILTER (ptr: filter)); |
543 | target = gtk_filter_list_model_new (model: G_LIST_MODEL (ptr: child), NULL); |
544 | |
545 | gtk_expression_bind (self: expr, target, property: "filter" , this_: child); |
546 | g_assert_true (gtk_filter_list_model_get_filter (child) == gtk_filter_list_model_get_filter (target)); |
547 | |
548 | filter = gtk_string_filter_new (NULL); |
549 | gtk_filter_list_model_set_filter (self: child, filter: GTK_FILTER (ptr: filter)); |
550 | g_assert_true (GTK_FILTER (filter) == gtk_filter_list_model_get_filter (target)); |
551 | g_assert_true (gtk_filter_list_model_get_filter (child) == gtk_filter_list_model_get_filter (target)); |
552 | g_object_unref (object: filter); |
553 | |
554 | g_object_unref (object: target); |
555 | } |
556 | |
557 | /* Another test of gtk_expression_bind that exercises the subwatch code paths */ |
558 | static void |
559 | test_nested_bind (void) |
560 | { |
561 | GtkStringFilter *filter; |
562 | GtkStringFilter *filter2; |
563 | GtkStringFilter *filter3; |
564 | GListModel *list; |
565 | GtkFilterListModel *filtered; |
566 | GtkExpression *expr; |
567 | GtkExpression *filter_expr; |
568 | gboolean res; |
569 | GValue value = G_VALUE_INIT; |
570 | |
571 | filter2 = gtk_string_filter_new (NULL); |
572 | gtk_string_filter_set_search (self: filter2, search: "sausage" ); |
573 | |
574 | list = G_LIST_MODEL (ptr: g_list_store_new (G_TYPE_OBJECT)); |
575 | filtered = gtk_filter_list_model_new (model: list, g_object_ref (GTK_FILTER (filter2))); |
576 | |
577 | filter_expr = gtk_property_expression_new (GTK_TYPE_FILTER_LIST_MODEL, |
578 | expression: gtk_object_expression_new (G_OBJECT (filtered)), |
579 | property_name: "filter" ); |
580 | expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, expression: gtk_expression_ref (self: filter_expr), property_name: "search" ); |
581 | |
582 | filter = gtk_string_filter_new (NULL); |
583 | gtk_string_filter_set_search (self: filter, search: "word" ); |
584 | g_assert_cmpstr (gtk_string_filter_get_search (filter), ==, "word" ); |
585 | |
586 | gtk_expression_bind (self: gtk_expression_ref (self: expr), target: filter, property: "search" , NULL); |
587 | |
588 | gtk_string_filter_set_search (self: filter2, search: "sausage" ); |
589 | g_assert_cmpstr (gtk_string_filter_get_search (filter), ==, "sausage" ); |
590 | |
591 | filter3 = gtk_string_filter_new (NULL); |
592 | gtk_string_filter_set_search (self: filter3, search: "banana" ); |
593 | gtk_filter_list_model_set_filter (self: filtered, filter: GTK_FILTER (ptr: filter3)); |
594 | |
595 | /* check that the expressions evaluate correctly */ |
596 | res = gtk_expression_evaluate (self: filter_expr, NULL, value: &value); |
597 | g_assert_true (res); |
598 | g_assert_true (g_value_get_object (&value) == filter3); |
599 | g_value_unset (value: &value); |
600 | |
601 | res = gtk_expression_evaluate (self: expr, NULL, value: &value); |
602 | g_assert_true (res); |
603 | g_assert_cmpstr (g_value_get_string (&value), ==, "banana" ); |
604 | g_value_unset (value: &value); |
605 | |
606 | /* and the bind too */ |
607 | g_assert_cmpstr (gtk_string_filter_get_search (filter), ==, "banana" ); |
608 | |
609 | g_object_unref (object: filter); |
610 | g_object_unref (object: filter2); |
611 | g_object_unref (object: filter3); |
612 | g_object_unref (object: filtered); |
613 | |
614 | gtk_expression_unref (self: expr); |
615 | gtk_expression_unref (self: filter_expr); |
616 | } |
617 | |
618 | static char * |
619 | some_cb (gpointer this, |
620 | const char *search, |
621 | gboolean ignore_case, |
622 | gpointer data) |
623 | { |
624 | if (!search) |
625 | return NULL; |
626 | |
627 | if (ignore_case) |
628 | return g_utf8_strdown (str: search, len: -1); |
629 | else |
630 | return g_strdup (str: search); |
631 | } |
632 | |
633 | /* Test that things work as expected when the same object is used multiple times in an |
634 | * expression or its subexpressions. |
635 | */ |
636 | static void |
637 | test_double_bind (void) |
638 | { |
639 | GtkStringFilter *filter1; |
640 | GtkStringFilter *filter2; |
641 | GtkExpression *expr; |
642 | GtkExpression *filter_expr; |
643 | GtkExpression *params[2]; |
644 | |
645 | filter1 = gtk_string_filter_new (NULL); |
646 | filter2 = gtk_string_filter_new (NULL); |
647 | |
648 | filter_expr = gtk_object_expression_new (G_OBJECT (filter1)); |
649 | |
650 | params[0] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, expression: gtk_expression_ref (self: filter_expr), property_name: "search" ); |
651 | params[1] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, expression: gtk_expression_ref (self: filter_expr), property_name: "ignore-case" ); |
652 | expr = gtk_cclosure_expression_new (G_TYPE_STRING, |
653 | NULL, |
654 | n_params: 2, params, |
655 | callback_func: (GCallback)some_cb, |
656 | NULL, NULL); |
657 | |
658 | gtk_expression_bind (self: gtk_expression_ref (self: expr), target: filter2, property: "search" , NULL); |
659 | |
660 | gtk_string_filter_set_search (self: filter1, search: "Banana" ); |
661 | g_assert_cmpstr (gtk_string_filter_get_search (filter2), ==, "banana" ); |
662 | |
663 | gtk_string_filter_set_ignore_case (self: filter1, FALSE); |
664 | g_assert_cmpstr (gtk_string_filter_get_search (filter2), ==, "Banana" ); |
665 | |
666 | gtk_expression_unref (self: expr); |
667 | gtk_expression_unref (self: filter_expr); |
668 | |
669 | g_object_unref (object: filter1); |
670 | g_object_unref (object: filter2); |
671 | } |
672 | |
673 | /* Test that having multiple binds on the same object works. */ |
674 | static void |
675 | test_binds (void) |
676 | { |
677 | GtkStringFilter *filter1; |
678 | GtkStringFilter *filter2; |
679 | GtkStringFilter *filter3; |
680 | GtkExpression *expr; |
681 | GtkExpression *expr2; |
682 | GtkExpression *filter1_expr; |
683 | GtkExpression *filter2_expr; |
684 | GtkExpression *params[2]; |
685 | |
686 | filter1 = gtk_string_filter_new (NULL); |
687 | filter2 = gtk_string_filter_new (NULL); |
688 | filter3 = gtk_string_filter_new (NULL); |
689 | |
690 | filter1_expr = gtk_object_expression_new (G_OBJECT (filter1)); |
691 | filter2_expr = gtk_object_expression_new (G_OBJECT (filter2)); |
692 | |
693 | params[0] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, expression: gtk_expression_ref (self: filter1_expr), property_name: "search" ); |
694 | params[1] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, expression: gtk_expression_ref (self: filter2_expr), property_name: "ignore-case" ); |
695 | expr = gtk_cclosure_expression_new (G_TYPE_STRING, |
696 | NULL, |
697 | n_params: 2, params, |
698 | callback_func: (GCallback)some_cb, |
699 | NULL, NULL); |
700 | |
701 | expr2 = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, expression: gtk_expression_ref (self: filter2_expr), property_name: "ignore-case" ); |
702 | |
703 | g_assert_true (gtk_property_expression_get_expression (expr2) == filter2_expr); |
704 | g_assert_cmpstr (gtk_property_expression_get_pspec (expr2)->name, ==, "ignore-case" ); |
705 | |
706 | gtk_expression_bind (self: gtk_expression_ref (self: expr), target: filter3, property: "search" , NULL); |
707 | gtk_expression_bind (self: gtk_expression_ref (self: expr2), target: filter3, property: "ignore-case" , NULL); |
708 | |
709 | gtk_string_filter_set_search (self: filter1, search: "Banana" ); |
710 | g_assert_cmpstr (gtk_string_filter_get_search (filter3), ==, "banana" ); |
711 | g_assert_true (gtk_string_filter_get_ignore_case (filter3)); |
712 | |
713 | gtk_string_filter_set_ignore_case (self: filter2, FALSE); |
714 | g_assert_cmpstr (gtk_string_filter_get_search (filter3), ==, "Banana" ); |
715 | g_assert_false (gtk_string_filter_get_ignore_case (filter3)); |
716 | |
717 | /* invalidate the first bind */ |
718 | g_object_unref (object: filter1); |
719 | |
720 | gtk_string_filter_set_ignore_case (self: filter2, TRUE); |
721 | g_assert_cmpstr (gtk_string_filter_get_search (filter3), ==, "Banana" ); |
722 | g_assert_true (gtk_string_filter_get_ignore_case (filter3)); |
723 | |
724 | gtk_expression_unref (self: expr); |
725 | gtk_expression_unref (self: expr2); |
726 | gtk_expression_unref (self: filter1_expr); |
727 | gtk_expression_unref (self: filter2_expr); |
728 | |
729 | g_object_unref (object: filter2); |
730 | g_object_unref (object: filter3); |
731 | } |
732 | |
733 | /* test that binds work ok with object expressions */ |
734 | static void |
735 | test_bind_object (void) |
736 | { |
737 | GtkStringFilter *filter; |
738 | GListStore *store; |
739 | GtkFilterListModel *model; |
740 | GtkExpression *expr; |
741 | |
742 | filter = gtk_string_filter_new (NULL); |
743 | store = g_list_store_new (G_TYPE_OBJECT); |
744 | model = gtk_filter_list_model_new (model: G_LIST_MODEL (ptr: store), NULL); |
745 | |
746 | expr = gtk_object_expression_new (G_OBJECT (filter)); |
747 | |
748 | gtk_expression_bind (self: gtk_expression_ref (self: expr), target: model, property: "filter" , NULL); |
749 | |
750 | g_assert_true (gtk_filter_list_model_get_filter (model) == GTK_FILTER (filter)); |
751 | |
752 | g_object_unref (object: filter); |
753 | |
754 | g_assert_true (gtk_filter_list_model_get_filter (model) == GTK_FILTER (filter)); |
755 | |
756 | gtk_expression_unref (self: expr); |
757 | g_object_unref (object: model); |
758 | } |
759 | |
760 | static void |
761 | test_value (void) |
762 | { |
763 | GValue value = G_VALUE_INIT; |
764 | GtkExpression *expr; |
765 | |
766 | expr = gtk_constant_expression_new (G_TYPE_INT, 22); |
767 | |
768 | g_value_init (value: &value, GTK_TYPE_EXPRESSION); |
769 | gtk_value_take_expression (value: &value, expression: expr); |
770 | g_assert_true (G_VALUE_TYPE (&value) == GTK_TYPE_EXPRESSION); |
771 | |
772 | expr = gtk_value_dup_expression (value: &value); |
773 | gtk_expression_unref (self: expr); |
774 | |
775 | expr = gtk_constant_expression_new (G_TYPE_INT, 23); |
776 | gtk_value_set_expression (value: &value, expression: expr); |
777 | gtk_expression_unref (self: expr); |
778 | |
779 | g_value_unset (value: &value); |
780 | } |
781 | |
782 | int |
783 | main (int argc, char *argv[]) |
784 | { |
785 | gtk_test_init (argcp: &argc, argvp: &argv, NULL); |
786 | setlocale (LC_ALL, locale: "C" ); |
787 | |
788 | g_test_add_func (testpath: "/expression/property" , test_func: test_property); |
789 | g_test_add_func (testpath: "/expression/interface-property" , test_func: test_interface_property); |
790 | g_test_add_func (testpath: "/expression/cclosure" , test_func: test_cclosure); |
791 | g_test_add_func (testpath: "/expression/closure" , test_func: test_closure); |
792 | g_test_add_func (testpath: "/expression/constant" , test_func: test_constant); |
793 | g_test_add_func (testpath: "/expression/constant-watch-this-destroyed" , test_func: test_constant_watch_this_destroyed); |
794 | g_test_add_func (testpath: "/expression/object" , test_func: test_object); |
795 | g_test_add_func (testpath: "/expression/nested" , test_func: test_nested); |
796 | g_test_add_func (testpath: "/expression/nested-this-destroyed" , test_func: test_nested_this_destroyed); |
797 | g_test_add_func (testpath: "/expression/type-mismatch" , test_func: test_type_mismatch); |
798 | g_test_add_func (testpath: "/expression/this" , test_func: test_this); |
799 | g_test_add_func (testpath: "/expression/bind" , test_func: test_bind); |
800 | g_test_add_func (testpath: "/expression/bind-self" , test_func: test_bind_self); |
801 | g_test_add_func (testpath: "/expression/bind-child" , test_func: test_bind_child); |
802 | g_test_add_func (testpath: "/expression/nested-bind" , test_func: test_nested_bind); |
803 | g_test_add_func (testpath: "/expression/double-bind" , test_func: test_double_bind); |
804 | g_test_add_func (testpath: "/expression/binds" , test_func: test_binds); |
805 | g_test_add_func (testpath: "/expression/bind-object" , test_func: test_bind_object); |
806 | g_test_add_func (testpath: "/expression/value" , test_func: test_value); |
807 | |
808 | return g_test_run (); |
809 | } |
810 | |