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
inc_counter(gpointer data)25 inc_counter (gpointer data)
26 {
27 guint *counter = data;
28
29 *counter += 1;
30 }
31
32 static void
test_property(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, "search");
44 watch = gtk_expression_watch (expr, filter, inc_counter, &counter, NULL);
45
46 ret = gtk_expression_evaluate (expr, filter, &value);
47 g_assert_true (ret);
48 g_assert_cmpstr (g_value_get_string (&value), ==, NULL);
49 g_value_unset (&value);
50
51 gtk_string_filter_set_search (filter, "Hello World");
52 g_assert_cmpint (counter, ==, 1);
53 counter = 0;
54
55 ret = gtk_expression_evaluate (expr, filter , &value);
56 g_assert_true (ret);
57 g_assert_cmpstr (g_value_get_string (&value), ==, "Hello World");
58 g_value_unset (&value);
59
60 gtk_expression_watch_unwatch (watch);
61 g_assert_cmpint (counter, ==, 0);
62
63 gtk_expression_unref (expr);
64 g_object_unref (filter);
65 }
66
67 static void
test_interface_property(void)68 test_interface_property (void)
69 {
70 GtkExpression *expr;
71
72 expr = gtk_property_expression_new (GTK_TYPE_ORIENTABLE, NULL, "orientation");
73 g_assert_cmpstr (gtk_property_expression_get_pspec (expr)->name, ==, "orientation");
74 gtk_expression_unref (expr);
75 }
76
77 static char *
print_filter_info(GtkStringFilter * filter,const char * search,gboolean ignore_case,GtkStringFilterMatchMode match_mode)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 ("OK");
88 }
89
90 static void
test_cclosure(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 (gtk_string_filter_new (NULL));
101 pexpr[0] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, "search");
102 pexpr[1] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, "ignore-case");
103 pexpr[2] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, NULL, "match-mode");
104 expr = gtk_cclosure_expression_new (G_TYPE_STRING,
105 NULL,
106 3,
107 pexpr,
108 G_CALLBACK (print_filter_info),
109 NULL,
110 NULL);
111 watch = gtk_expression_watch (expr, filter, inc_counter, &counter, NULL);
112
113 ret = gtk_expression_evaluate (expr, filter, &value);
114 g_assert_true (ret);
115 g_assert_cmpstr (g_value_get_string (&value), ==, "OK");
116 g_value_unset (&value);
117
118 gtk_string_filter_set_search (filter, "Hello World");
119 g_assert_cmpint (counter, ==, 1);
120 ret = gtk_expression_evaluate (expr, filter , &value);
121 g_assert_true (ret);
122 g_assert_cmpstr (g_value_get_string (&value), ==, "OK");
123 g_value_unset (&value);
124
125 gtk_string_filter_set_ignore_case (filter, FALSE);
126 g_assert_cmpint (counter, ==, 2);
127 ret = gtk_expression_evaluate (expr, filter , &value);
128 g_assert_true (ret);
129 g_assert_cmpstr (g_value_get_string (&value), ==, "OK");
130 g_value_unset (&value);
131
132 gtk_string_filter_set_search (filter, "Hello");
133 gtk_string_filter_set_ignore_case (filter, TRUE);
134 gtk_string_filter_set_match_mode (filter, GTK_STRING_FILTER_MATCH_MODE_EXACT);
135 g_assert_cmpint (counter, ==, 5);
136 ret = gtk_expression_evaluate (expr, filter , &value);
137 g_assert_true (ret);
138 g_assert_cmpstr (g_value_get_string (&value), ==, "OK");
139 g_value_unset (&value);
140
141 gtk_expression_watch_unwatch (watch);
142 g_assert_cmpint (counter, ==, 5);
143
144 gtk_expression_unref (expr);
145 g_object_unref (filter);
146 }
147
148 static char *
make_string(void)149 make_string (void)
150 {
151 return g_strdup ("Hello");
152 }
153
154 static void
test_closure(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, 0, NULL);
164 ret = gtk_expression_evaluate (expr, NULL, &value);
165 g_assert_true (ret);
166 g_assert_cmpstr (g_value_get_string (&value), ==, "Hello");
167 g_value_unset (&value);
168
169 gtk_expression_unref (expr);
170 }
171
172 static void
test_constant(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 (expr, NULL, &value);
185 g_assert_true (res);
186 g_assert_cmpint (g_value_get_int (&value), ==, 22);
187
188 v = gtk_constant_expression_get_value (expr);
189 g_assert_cmpint (g_value_get_int (v), ==, 22);
190
191 gtk_expression_unref (expr);
192 }
193
194 /* Test that object expressions fail to evaluate when
195 * the object is gone.
196 */
197 static void
test_object(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 (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 (expr, NULL, &value);
213 g_assert_true (res);
214 g_assert_true (g_value_get_object (&value) == obj);
215 g_value_unset (&value);
216
217 o = gtk_object_expression_get_object (expr);
218 g_assert_true (o == obj);
219
220 g_clear_object (&obj);
221 res = gtk_expression_evaluate (expr, NULL, &value);
222 g_assert_false (res);
223
224 gtk_expression_unref (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
test_nested(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 (filter, "word");
255 list = G_LIST_MODEL (g_list_store_new (G_TYPE_OBJECT));
256 filtered = gtk_filter_list_model_new (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, list_expr, "filter");
260 expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, filter_expr, "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 (expr, NULL, &value);
266 g_assert_true (res);
267 g_assert_cmpstr (g_value_get_string (&value), ==, "word");
268 g_value_unset (&value);
269
270 watch = gtk_expression_watch (expr, NULL, inc_counter, &counter, NULL);
271 gtk_string_filter_set_search (GTK_STRING_FILTER (filter), "salad");
272 g_assert_cmpint (counter, ==, 1);
273 counter = 0;
274
275 res = gtk_expression_evaluate (expr, NULL, &value);
276 g_assert_true (res);
277 g_assert_cmpstr (g_value_get_string (&value), ==, "salad");
278 g_value_unset (&value);
279
280 gtk_filter_list_model_set_filter (filtered, GTK_FILTER (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 (filter, "salad");
286 gtk_filter_list_model_set_filter (filtered, GTK_FILTER (filter));
287 g_assert_cmpint (counter, ==, 1);
288 counter = 0;
289
290 res = gtk_expression_evaluate (expr, NULL, &value);
291 g_assert_true (res);
292 g_assert_cmpstr (g_value_get_string (&value), ==, "salad");
293 g_value_unset (&value);
294
295 gtk_string_filter_set_search (filter, "bar");
296 g_assert_cmpint (counter, ==, 1);
297 counter = 0;
298
299 res = gtk_expression_evaluate (expr, NULL, &value);
300 g_assert_true (res);
301 g_assert_cmpstr (g_value_get_string (&value), ==, "bar");
302 g_value_unset (&value);
303
304 gtk_filter_list_model_set_filter (filtered, NULL);
305 g_assert_cmpint (counter, ==, 1);
306 counter = 0;
307
308 res = gtk_expression_evaluate (expr, NULL, &value);
309 g_assert_false (res);
310
311 gtk_expression_watch_unwatch (watch);
312 g_assert_cmpint (counter, ==, 0);
313
314 g_object_unref (filtered);
315 gtk_expression_unref (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
test_nested_this_destroyed(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 (filter, "word");
342 list = G_LIST_MODEL (g_list_store_new (G_TYPE_OBJECT));
343 filtered = gtk_filter_list_model_new (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, list_expr, "filter");
347 expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, filter_expr, "search");
348
349 watch = gtk_expression_watch (expr, filter, inc_counter, &counter, NULL);
350 gtk_expression_watch_ref (watch);
351 res = gtk_expression_watch_evaluate (watch, &value);
352 g_assert_true (res);
353 g_assert_cmpstr (g_value_get_string (&value), ==, "word");
354 g_value_unset (&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 (filter, "salad");
361 gtk_filter_list_model_set_filter (filtered, GTK_FILTER (filter));
362 g_assert_cmpint (counter, ==, 1);
363 counter = 0;
364
365 res = gtk_expression_watch_evaluate (watch, &value);
366 g_assert_false (res);
367
368 gtk_string_filter_set_search (filter, "bar");
369 g_assert_cmpint (counter, ==, 0);
370
371 gtk_filter_list_model_set_filter (filtered, NULL);
372 g_assert_cmpint (counter, ==, 0);
373
374 res = gtk_expression_watch_evaluate (watch, &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 (filtered);
384 g_object_unref (filter);
385 gtk_expression_unref (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
test_type_mismatch(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 (gtk_any_filter_new ());
400
401 expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, gtk_constant_expression_new (GTK_TYPE_ANY_FILTER, filter), "search");
402
403 res = gtk_expression_evaluate (expr, NULL, &value);
404 g_assert_false (res);
405 g_assert_false (G_IS_VALUE (&value));
406
407 gtk_expression_unref (expr);
408 g_object_unref (filter);
409 }
410
411 /* Some basic tests around 'this' */
412 static void
test_this(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, "search");
422
423 filter = gtk_string_filter_new (NULL);
424 gtk_string_filter_set_search (filter, "word");
425
426 filter2 = gtk_string_filter_new (NULL);
427 gtk_string_filter_set_search (filter2, "sausage");
428
429 res = gtk_expression_evaluate (expr, filter, &value);
430 g_assert_true (res);
431 g_assert_cmpstr (g_value_get_string (&value), ==, "word");
432 g_value_unset (&value);
433
434 res = gtk_expression_evaluate (expr, filter2, &value);
435 g_assert_true (res);
436 g_assert_cmpstr (g_value_get_string (&value), ==, "sausage");
437 g_value_unset (&value);
438
439 gtk_expression_unref (expr);
440 g_object_unref (filter2);
441 g_object_unref (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
test_constant_watch_this_destroyed(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 (expr, this, inc_counter, &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 (expr);
464 }
465
466 /* Basic test of gtk_expression_bind */
467 static void
test_bind(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, "search");
478
479 target = gtk_string_filter_new (NULL);
480 gtk_string_filter_set_search (target, "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 (source, "sausage");
485
486 watch = gtk_expression_bind (expr, target, "search", 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 (source, "salad");
491 g_assert_cmpstr (gtk_string_filter_get_search (target), ==, "salad");
492 res = gtk_expression_watch_evaluate (watch, &value);
493 g_assert_true (res);
494 g_assert_cmpstr (g_value_get_string (&value), ==, "salad");
495 g_value_unset (&value);
496
497 g_object_unref (source);
498 g_assert_cmpstr (gtk_string_filter_get_search (target), ==, "salad");
499 res = gtk_expression_watch_evaluate (watch, &value);
500 g_assert_false (res);
501 g_assert_false (G_IS_VALUE (&value));
502
503 g_object_unref (target);
504 gtk_expression_watch_unref (watch);
505 }
506
507 /* Another test of bind, this time we watch ourselves */
508 static void
test_bind_self(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 "ignore-case");
517
518 filter = gtk_string_filter_new (NULL);
519 gtk_string_filter_set_search (filter, "word");
520 g_assert_cmpstr (gtk_string_filter_get_search (filter), ==, "word");
521
522 gtk_expression_bind (expr, filter, "search", filter);
523 g_assert_cmpstr (gtk_string_filter_get_search (filter), ==, "TRUE");
524
525 g_object_unref (filter);
526 }
527
528 /* Test bind does the right memory management if the target's
529 * dispose() kills the source */
530 static void
test_bind_child(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 "filter");
540
541 filter = gtk_string_filter_new (NULL);
542 child = gtk_filter_list_model_new (NULL, GTK_FILTER (filter));
543 target = gtk_filter_list_model_new (G_LIST_MODEL (child), NULL);
544
545 gtk_expression_bind (expr, target, "filter", 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 (child, GTK_FILTER (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 (filter);
553
554 g_object_unref (target);
555 }
556
557 /* Another test of gtk_expression_bind that exercises the subwatch code paths */
558 static void
test_nested_bind(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 (filter2, "sausage");
573
574 list = G_LIST_MODEL (g_list_store_new (G_TYPE_OBJECT));
575 filtered = gtk_filter_list_model_new (list, g_object_ref (GTK_FILTER (filter2)));
576
577 filter_expr = gtk_property_expression_new (GTK_TYPE_FILTER_LIST_MODEL,
578 gtk_object_expression_new (G_OBJECT (filtered)),
579 "filter");
580 expr = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, gtk_expression_ref (filter_expr), "search");
581
582 filter = gtk_string_filter_new (NULL);
583 gtk_string_filter_set_search (filter, "word");
584 g_assert_cmpstr (gtk_string_filter_get_search (filter), ==, "word");
585
586 gtk_expression_bind (gtk_expression_ref (expr), filter, "search", NULL);
587
588 gtk_string_filter_set_search (filter2, "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 (filter3, "banana");
593 gtk_filter_list_model_set_filter (filtered, GTK_FILTER (filter3));
594
595 /* check that the expressions evaluate correctly */
596 res = gtk_expression_evaluate (filter_expr, NULL, &value);
597 g_assert_true (res);
598 g_assert_true (g_value_get_object (&value) == filter3);
599 g_value_unset (&value);
600
601 res = gtk_expression_evaluate (expr, NULL, &value);
602 g_assert_true (res);
603 g_assert_cmpstr (g_value_get_string (&value), ==, "banana");
604 g_value_unset (&value);
605
606 /* and the bind too */
607 g_assert_cmpstr (gtk_string_filter_get_search (filter), ==, "banana");
608
609 g_object_unref (filter);
610 g_object_unref (filter2);
611 g_object_unref (filter3);
612 g_object_unref (filtered);
613
614 gtk_expression_unref (expr);
615 gtk_expression_unref (filter_expr);
616 }
617
618 static char *
some_cb(gpointer this,const char * search,gboolean ignore_case,gpointer data)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 (search, -1);
629 else
630 return g_strdup (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
test_double_bind(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, gtk_expression_ref (filter_expr), "search");
651 params[1] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, gtk_expression_ref (filter_expr), "ignore-case");
652 expr = gtk_cclosure_expression_new (G_TYPE_STRING,
653 NULL,
654 2, params,
655 (GCallback)some_cb,
656 NULL, NULL);
657
658 gtk_expression_bind (gtk_expression_ref (expr), filter2, "search", NULL);
659
660 gtk_string_filter_set_search (filter1, "Banana");
661 g_assert_cmpstr (gtk_string_filter_get_search (filter2), ==, "banana");
662
663 gtk_string_filter_set_ignore_case (filter1, FALSE);
664 g_assert_cmpstr (gtk_string_filter_get_search (filter2), ==, "Banana");
665
666 gtk_expression_unref (expr);
667 gtk_expression_unref (filter_expr);
668
669 g_object_unref (filter1);
670 g_object_unref (filter2);
671 }
672
673 /* Test that having multiple binds on the same object works. */
674 static void
test_binds(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, gtk_expression_ref (filter1_expr), "search");
694 params[1] = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, gtk_expression_ref (filter2_expr), "ignore-case");
695 expr = gtk_cclosure_expression_new (G_TYPE_STRING,
696 NULL,
697 2, params,
698 (GCallback)some_cb,
699 NULL, NULL);
700
701 expr2 = gtk_property_expression_new (GTK_TYPE_STRING_FILTER, gtk_expression_ref (filter2_expr), "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 (gtk_expression_ref (expr), filter3, "search", NULL);
707 gtk_expression_bind (gtk_expression_ref (expr2), filter3, "ignore-case", NULL);
708
709 gtk_string_filter_set_search (filter1, "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 (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 (filter1);
719
720 gtk_string_filter_set_ignore_case (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 (expr);
725 gtk_expression_unref (expr2);
726 gtk_expression_unref (filter1_expr);
727 gtk_expression_unref (filter2_expr);
728
729 g_object_unref (filter2);
730 g_object_unref (filter3);
731 }
732
733 /* test that binds work ok with object expressions */
734 static void
test_bind_object(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 (G_LIST_MODEL (store), NULL);
745
746 expr = gtk_object_expression_new (G_OBJECT (filter));
747
748 gtk_expression_bind (gtk_expression_ref (expr), model, "filter", NULL);
749
750 g_assert_true (gtk_filter_list_model_get_filter (model) == GTK_FILTER (filter));
751
752 g_object_unref (filter);
753
754 g_assert_true (gtk_filter_list_model_get_filter (model) == GTK_FILTER (filter));
755
756 gtk_expression_unref (expr);
757 g_object_unref (model);
758 }
759
760 static void
test_value(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, GTK_TYPE_EXPRESSION);
769 gtk_value_take_expression (&value, expr);
770 g_assert_true (G_VALUE_TYPE (&value) == GTK_TYPE_EXPRESSION);
771
772 expr = gtk_value_dup_expression (&value);
773 gtk_expression_unref (expr);
774
775 expr = gtk_constant_expression_new (G_TYPE_INT, 23);
776 gtk_value_set_expression (&value, expr);
777 gtk_expression_unref (expr);
778
779 g_value_unset (&value);
780 }
781
782 int
main(int argc,char * argv[])783 main (int argc, char *argv[])
784 {
785 gtk_test_init (&argc, &argv, NULL);
786 setlocale (LC_ALL, "C");
787
788 g_test_add_func ("/expression/property", test_property);
789 g_test_add_func ("/expression/interface-property", test_interface_property);
790 g_test_add_func ("/expression/cclosure", test_cclosure);
791 g_test_add_func ("/expression/closure", test_closure);
792 g_test_add_func ("/expression/constant", test_constant);
793 g_test_add_func ("/expression/constant-watch-this-destroyed", test_constant_watch_this_destroyed);
794 g_test_add_func ("/expression/object", test_object);
795 g_test_add_func ("/expression/nested", test_nested);
796 g_test_add_func ("/expression/nested-this-destroyed", test_nested_this_destroyed);
797 g_test_add_func ("/expression/type-mismatch", test_type_mismatch);
798 g_test_add_func ("/expression/this", test_this);
799 g_test_add_func ("/expression/bind", test_bind);
800 g_test_add_func ("/expression/bind-self", test_bind_self);
801 g_test_add_func ("/expression/bind-child", test_bind_child);
802 g_test_add_func ("/expression/nested-bind", test_nested_bind);
803 g_test_add_func ("/expression/double-bind", test_double_bind);
804 g_test_add_func ("/expression/binds", test_binds);
805 g_test_add_func ("/expression/bind-object", test_bind_object);
806 g_test_add_func ("/expression/value", test_value);
807
808 return g_test_run ();
809 }
810