1 #include <glib.h>
2 #include <clutter/clutter.h>
3 #include <string.h>
4
5 #include "tests/clutter-test-utils.h"
6
7 typedef struct {
8 gunichar unichar;
9 const char bytes[6];
10 gint nbytes;
11 } TestData;
12
13 static const TestData
14 test_text_data[] = {
15 { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */
16 { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */
17 };
18
19 static void
text_utf8_validation(void)20 text_utf8_validation (void)
21 {
22 int i;
23
24 for (i = 0; i < G_N_ELEMENTS (test_text_data); i++)
25 {
26 const TestData *t = &test_text_data[i];
27 gunichar unichar;
28 char bytes[6];
29 int nbytes;
30
31 g_assert (g_unichar_validate (t->unichar));
32
33 nbytes = g_unichar_to_utf8 (t->unichar, bytes);
34 bytes[nbytes] = '\0';
35 g_assert_cmpint (nbytes, ==, t->nbytes);
36 g_assert (memcmp (t->bytes, bytes, nbytes) == 0);
37
38 unichar = g_utf8_get_char_validated (bytes, nbytes);
39 g_assert_cmpint (unichar, ==, t->unichar);
40 }
41 }
42
43 static int
get_nbytes(ClutterText * text)44 get_nbytes (ClutterText *text)
45 {
46 const char *s = clutter_text_get_text (text);
47 return strlen (s);
48 }
49
50 static int
get_nchars(ClutterText * text)51 get_nchars (ClutterText *text)
52 {
53 const char *s = clutter_text_get_text (text);
54 g_assert (g_utf8_validate (s, -1, NULL));
55 return g_utf8_strlen (s, -1);
56 }
57
58 #define DONT_MOVE_CURSOR (-2)
59
60 static void
insert_unichar(ClutterText * text,gunichar unichar,int position)61 insert_unichar (ClutterText *text, gunichar unichar, int position)
62 {
63 if (position > DONT_MOVE_CURSOR)
64 {
65 clutter_text_set_cursor_position (text, position);
66 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, position);
67 }
68
69 clutter_text_insert_unichar (text, unichar);
70 }
71
72 static void
text_set_empty(void)73 text_set_empty (void)
74 {
75 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
76 g_object_ref_sink (text);
77
78 g_assert_cmpstr (clutter_text_get_text (text), ==, "");
79 g_assert_cmpint (*clutter_text_get_text (text), ==, '\0');
80 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1);
81
82 clutter_text_set_text (text, "");
83 g_assert_cmpint (get_nchars (text), ==, 0);
84 g_assert_cmpint (get_nbytes (text), ==, 0);
85 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1);
86
87 clutter_actor_destroy (CLUTTER_ACTOR (text));
88 }
89
90 static void
text_set_text(void)91 text_set_text (void)
92 {
93 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
94 g_object_ref_sink (text);
95
96 clutter_text_set_text (text, "abcdef");
97 g_assert_cmpint (get_nchars (text), ==, 6);
98 g_assert_cmpint (get_nbytes (text), ==, 6);
99 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1);
100
101 clutter_text_set_cursor_position (text, 5);
102 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 5);
103
104 /* FIXME: cursor position should be -1?
105 clutter_text_set_text (text, "");
106 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1);
107 */
108
109 clutter_actor_destroy (CLUTTER_ACTOR (text));
110 }
111
112 static void
text_append_some(void)113 text_append_some (void)
114 {
115 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
116 int i;
117
118 g_object_ref_sink (text);
119
120 for (i = 0; i < G_N_ELEMENTS (test_text_data); i++)
121 {
122 const TestData *t = &test_text_data[i];
123 int j;
124
125 for (j = 1; j <= 4; j++)
126 {
127 insert_unichar (text, t->unichar, DONT_MOVE_CURSOR);
128
129 g_assert_cmpint (get_nchars (text), ==, j);
130 g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes);
131 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1);
132 }
133
134 clutter_text_set_text (text, "");
135 }
136
137 clutter_actor_destroy (CLUTTER_ACTOR (text));
138 }
139
140 static void
text_prepend_some(void)141 text_prepend_some (void)
142 {
143 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
144 int i;
145
146 g_object_ref_sink (text);
147
148 for (i = 0; i < G_N_ELEMENTS (test_text_data); i++)
149 {
150 const TestData *t = &test_text_data[i];
151 int j;
152
153 clutter_text_insert_unichar (text, t->unichar);
154
155 g_assert_cmpint (get_nchars (text), ==, 1);
156 g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes);
157 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1);
158
159 for (j = 2; j <= 4; j++)
160 {
161 insert_unichar (text, t->unichar, 0);
162
163 g_assert_cmpint (get_nchars (text), ==, j);
164 g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes);
165 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1);
166 }
167
168 clutter_text_set_text (text, "");
169 }
170
171 clutter_actor_destroy (CLUTTER_ACTOR (text));
172 }
173
174 static void
text_insert(void)175 text_insert (void)
176 {
177 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
178 int i;
179
180 g_object_ref_sink (text);
181
182 for (i = 0; i < G_N_ELEMENTS (test_text_data); i++)
183 {
184 const TestData *t = &test_text_data[i];
185
186 clutter_text_insert_unichar (text, t->unichar);
187 clutter_text_insert_unichar (text, t->unichar);
188
189 insert_unichar (text, t->unichar, 1);
190
191 g_assert_cmpint (get_nchars (text), ==, 3);
192 g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes);
193 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 2);
194
195 clutter_text_set_text (text, "");
196 }
197
198 clutter_actor_destroy (CLUTTER_ACTOR (text));
199 }
200
201 static void
text_delete_chars(void)202 text_delete_chars (void)
203 {
204 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
205 int i;
206
207 g_object_ref_sink (text);
208
209 for (i = 0; i < G_N_ELEMENTS (test_text_data); i++)
210 {
211 const TestData *t = &test_text_data[i];
212 int j;
213
214 for (j = 0; j < 4; j++)
215 clutter_text_insert_unichar (text, t->unichar);
216
217 if (!g_test_quiet ())
218 g_print ("text: %s\n", clutter_text_get_text (text));
219
220 clutter_text_set_cursor_position (text, 2);
221 clutter_text_delete_chars (text, 1);
222 if (!g_test_quiet ())
223 g_print ("text: %s (cursor at: %d)\n",
224 clutter_text_get_text (text),
225 clutter_text_get_cursor_position (text));
226 g_assert_cmpint (get_nchars (text), ==, 3);
227 g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes);
228 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1);
229
230 clutter_text_set_cursor_position (text, 2);
231 clutter_text_delete_chars (text, 1);
232 if (!g_test_quiet ())
233 g_print ("text: %s (cursor at: %d)\n",
234 clutter_text_get_text (text),
235 clutter_text_get_cursor_position (text));
236 g_assert_cmpint (get_nchars (text), ==, 2);
237 g_assert_cmpint (get_nbytes (text), ==, 2 * t->nbytes);
238 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1);
239
240 clutter_text_set_text (text, "");
241 }
242
243 clutter_actor_destroy (CLUTTER_ACTOR (text));
244 }
245
246 static void
text_get_chars(void)247 text_get_chars (void)
248 {
249 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
250 gchar *chars;
251
252 g_object_ref_sink (text);
253
254 clutter_text_set_text (text, "00abcdef11");
255 g_assert_cmpint (get_nchars (text), ==, 10);
256 g_assert_cmpint (get_nbytes (text), ==, 10);
257 g_assert_cmpstr (clutter_text_get_text (text), ==, "00abcdef11");
258
259 chars = clutter_text_get_chars (text, 2, -1);
260 g_assert_cmpstr (chars, ==, "abcdef11");
261 g_free (chars);
262
263 chars = clutter_text_get_chars (text, 0, 8);
264 g_assert_cmpstr (chars, ==, "00abcdef");
265 g_free (chars);
266
267 chars = clutter_text_get_chars (text, 2, 8);
268 g_assert_cmpstr (chars, ==, "abcdef");
269 g_free (chars);
270
271 chars = clutter_text_get_chars (text, 8, 12);
272 g_assert_cmpstr (chars, ==, "11");
273 g_free (chars);
274
275 clutter_actor_destroy (CLUTTER_ACTOR (text));
276 }
277
278 static void
text_delete_text(void)279 text_delete_text (void)
280 {
281 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
282 int i;
283
284 g_object_ref_sink (text);
285
286 for (i = 0; i < G_N_ELEMENTS (test_text_data); i++)
287 {
288 const TestData *t = &test_text_data[i];
289 int j;
290
291 for (j = 0; j < 4; j++)
292 clutter_text_insert_unichar (text, t->unichar);
293
294 clutter_text_set_cursor_position (text, 3);
295 clutter_text_delete_text (text, 2, 4);
296
297 g_assert_cmpint (get_nchars (text), ==, 2);
298 g_assert_cmpint (get_nbytes (text), ==, 2 * t->nbytes);
299
300 /* FIXME: cursor position should be -1?
301 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1);
302 */
303
304 clutter_text_set_text (text, "");
305 }
306
307 clutter_actor_destroy (CLUTTER_ACTOR (text));
308 }
309
310 static void
text_password_char(void)311 text_password_char (void)
312 {
313 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
314
315 g_object_ref_sink (text);
316
317 g_assert_cmpint (clutter_text_get_password_char (text), ==, 0);
318
319 clutter_text_set_text (text, "hello");
320 g_assert_cmpstr (clutter_text_get_text (text), ==, "hello");
321
322 clutter_text_set_password_char (text, '*');
323 g_assert_cmpint (clutter_text_get_password_char (text), ==, '*');
324
325 g_assert_cmpstr (clutter_text_get_text (text), ==, "hello");
326
327 clutter_actor_destroy (CLUTTER_ACTOR (text));
328 }
329
330 static ClutterEvent *
init_event(void)331 init_event (void)
332 {
333 ClutterEvent *retval = clutter_event_new (CLUTTER_KEY_PRESS);
334
335 clutter_event_set_time (retval, CLUTTER_CURRENT_TIME);
336 clutter_event_set_flags (retval, CLUTTER_EVENT_FLAG_SYNTHETIC);
337
338 return retval;
339 }
340
341 static void
send_keyval(ClutterText * text,int keyval)342 send_keyval (ClutterText *text, int keyval)
343 {
344 ClutterEvent *event = init_event ();
345
346 /* Unicode should be ignored for cursor keys etc. */
347 clutter_event_set_key_unicode (event, 0);
348 clutter_event_set_key_symbol (event, keyval);
349
350 clutter_actor_event (CLUTTER_ACTOR (text), event, FALSE);
351
352 clutter_event_free (event);
353 }
354
355 static void
send_unichar(ClutterText * text,gunichar unichar)356 send_unichar (ClutterText *text, gunichar unichar)
357 {
358 ClutterEvent *event = init_event ();
359
360 /* Key symbol should be ignored for printable characters */
361 clutter_event_set_key_symbol (event, 0);
362 clutter_event_set_key_unicode (event, unichar);
363
364 clutter_actor_event (CLUTTER_ACTOR (text), event, FALSE);
365
366 clutter_event_free (event);
367 }
368
369 static void
text_cursor(void)370 text_cursor (void)
371 {
372 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
373 int i;
374
375 g_object_ref_sink (text);
376
377 /* only editable entries listen to events */
378 clutter_text_set_editable (text, TRUE);
379
380 for (i = 0; i < G_N_ELEMENTS (test_text_data); i++)
381 {
382 const TestData *t = &test_text_data[i];
383 int j;
384
385 for (j = 0; j < 4; ++j)
386 clutter_text_insert_unichar (text, t->unichar);
387
388 clutter_text_set_cursor_position (text, 2);
389
390 /* test cursor moves and is clamped */
391 send_keyval (text, CLUTTER_KEY_Left);
392 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1);
393
394 send_keyval (text, CLUTTER_KEY_Left);
395 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 0);
396
397 send_keyval (text, CLUTTER_KEY_Left);
398 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 0);
399
400 /* delete text containing the cursor */
401 clutter_text_set_cursor_position (text, 3);
402 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 3);
403
404 clutter_text_delete_text (text, 2, 4);
405 send_keyval (text, CLUTTER_KEY_Left);
406
407 /* FIXME: cursor position should be -1?
408 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1);
409 */
410
411 clutter_text_set_text (text, "");
412 }
413
414 clutter_actor_destroy (CLUTTER_ACTOR (text));
415 }
416
417 static void
text_event(void)418 text_event (void)
419 {
420 ClutterText *text = CLUTTER_TEXT (clutter_text_new ());
421 int i;
422
423 g_object_ref_sink (text);
424
425 /* only editable entries listen to events */
426 clutter_text_set_editable (text, TRUE);
427
428 for (i = 0; i < G_N_ELEMENTS (test_text_data); i++)
429 {
430 const TestData *t = &test_text_data[i];
431
432 send_unichar (text, t->unichar);
433
434 g_assert_cmpint (get_nchars (text), ==, 1);
435 g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes);
436 g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1);
437
438 clutter_text_set_text (text, "");
439 }
440
441 clutter_actor_destroy (CLUTTER_ACTOR (text));
442 }
443
444 static inline void
validate_markup_attributes(ClutterText * text,PangoAttrType attr_type,int start_index,int end_index)445 validate_markup_attributes (ClutterText *text,
446 PangoAttrType attr_type,
447 int start_index,
448 int end_index)
449 {
450 PangoLayout *layout;
451 PangoAttrList *attrs;
452 PangoAttrIterator *iter;
453
454 layout = clutter_text_get_layout (text);
455 g_assert (layout != NULL);
456
457 attrs = pango_layout_get_attributes (layout);
458 g_assert (attrs != NULL);
459
460 iter = pango_attr_list_get_iterator (attrs);
461 while (pango_attr_iterator_next (iter))
462 {
463 GSList *attributes = pango_attr_iterator_get_attrs (iter);
464 PangoAttribute *a;
465
466 if (attributes == NULL)
467 break;
468
469 g_assert (attributes->data != NULL);
470
471 a = attributes->data;
472
473 if (a->klass->type == PANGO_ATTR_SCALE)
474 {
475 PangoAttrFloat *scale = (PangoAttrFloat*) a;
476 float resource_scale;
477
478 resource_scale = clutter_actor_get_resource_scale (CLUTTER_ACTOR (text));
479
480 g_assert_cmpfloat (scale->value, ==, resource_scale);
481 g_slist_free_full (attributes, (GDestroyNotify) pango_attribute_destroy);
482 continue;
483 }
484
485 g_assert (a->klass->type == attr_type);
486 g_assert_cmpint (a->start_index, ==, start_index);
487 g_assert_cmpint (a->end_index, ==, end_index);
488
489 g_slist_free_full (attributes, (GDestroyNotify) pango_attribute_destroy);
490 }
491
492 pango_attr_iterator_destroy (iter);
493 }
494
495 static void
text_idempotent_use_markup(void)496 text_idempotent_use_markup (void)
497 {
498 ClutterText *text;
499 const char *contents = "foo <b>bar</b>";
500 const char *display = "foo bar";
501 int bar_start_index = strstr (display, "bar") - display;
502 int bar_end_index = bar_start_index + strlen ("bar");
503
504 /* case 1: text -> use_markup */
505 if (!g_test_quiet ())
506 g_print ("text: '%s' -> use-markup: TRUE\n", contents);
507
508 text = g_object_new (CLUTTER_TYPE_TEXT,
509 "text", contents, "use-markup", TRUE,
510 NULL);
511 g_object_ref_sink (text);
512
513 if (!g_test_quiet ())
514 g_print ("Contents: '%s' (expected: '%s')\n",
515 clutter_text_get_text (text),
516 display);
517
518 g_assert_cmpstr (clutter_text_get_text (text), ==, display);
519
520 validate_markup_attributes (text,
521 PANGO_ATTR_WEIGHT,
522 bar_start_index,
523 bar_end_index);
524
525 clutter_actor_destroy (CLUTTER_ACTOR (text));
526
527 /* case 2: use_markup -> text */
528 if (!g_test_quiet ())
529 g_print ("use-markup: TRUE -> text: '%s'\n", contents);
530
531 text = g_object_new (CLUTTER_TYPE_TEXT,
532 "use-markup", TRUE, "text", contents,
533 NULL);
534
535 if (!g_test_quiet ())
536 g_print ("Contents: '%s' (expected: '%s')\n",
537 clutter_text_get_text (text),
538 display);
539
540 g_assert_cmpstr (clutter_text_get_text (text), ==, display);
541
542 validate_markup_attributes (text,
543 PANGO_ATTR_WEIGHT,
544 bar_start_index,
545 bar_end_index);
546
547 clutter_actor_destroy (CLUTTER_ACTOR (text));
548 }
549
550 CLUTTER_TEST_SUITE (
551 CLUTTER_TEST_UNIT ("/text/utf8-validation", text_utf8_validation)
552 CLUTTER_TEST_UNIT ("/text/set-empty", text_set_empty)
553 CLUTTER_TEST_UNIT ("/text/set-text", text_set_text)
554 CLUTTER_TEST_UNIT ("/text/append-some", text_append_some)
555 CLUTTER_TEST_UNIT ("/text/prepend-some", text_prepend_some)
556 CLUTTER_TEST_UNIT ("/text/insert", text_insert)
557 CLUTTER_TEST_UNIT ("/text/delete-chars", text_delete_chars)
558 CLUTTER_TEST_UNIT ("/text/get-chars", text_get_chars)
559 CLUTTER_TEST_UNIT ("/text/delete-text", text_delete_text)
560 CLUTTER_TEST_UNIT ("/text/password-char", text_password_char)
561 CLUTTER_TEST_UNIT ("/text/cursor", text_cursor)
562 CLUTTER_TEST_UNIT ("/text/event", text_event)
563 CLUTTER_TEST_UNIT ("/text/idempotent-use-markup", text_idempotent_use_markup)
564 )
565