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