]> Pileus Git - ~andy/gtk/blob - tests/testtextbuffer.c
0e29a0768f6e0c115214bc57cd498fbafdcbe078
[~andy/gtk] / tests / testtextbuffer.c
1 /* Simplistic test suite */
2
3
4 #include <stdio.h>
5 #include <string.h>
6
7 #include <gtk/gtk.h>
8 #include "../gtk/gtktexttypes.h" /* Private header, for UNKNOWN_CHAR */
9
10 static void
11 gtk_text_iter_spew (const GtkTextIter *iter, const gchar *desc)
12 {
13   g_print (" %20s: line %d / char %d / line char %d / line byte %d\n",
14            desc,
15            gtk_text_iter_get_line (iter),
16            gtk_text_iter_get_offset (iter),
17            gtk_text_iter_get_line_offset (iter),
18            gtk_text_iter_get_line_index (iter));
19 }
20
21 static void fill_buffer (GtkTextBuffer *buffer);
22
23 static void run_tests (GtkTextBuffer *buffer);
24
25 static void check_get_set_text (GtkTextBuffer *buffer,
26                                 const char    *str);
27
28
29 static void line_separator_tests (void);
30
31 static void logical_motion_tests (void);
32
33 int
34 main (int argc, char** argv)
35 {
36   GtkTextBuffer *buffer;
37   int n;
38   gunichar ch;
39   GtkTextIter start, end;
40   
41   gtk_init (&argc, &argv);
42
43   /* Check UTF8 unknown char thing */
44   g_assert (g_utf8_strlen (gtk_text_unknown_char_utf8, 3) == 1);
45   ch = g_utf8_get_char (gtk_text_unknown_char_utf8);
46   g_assert (ch == GTK_TEXT_UNKNOWN_CHAR);
47
48   /* First, we turn on btree debugging. */
49   gtk_debug_flags |= GTK_DEBUG_TEXT;
50
51   /* Check some line separator stuff */
52   line_separator_tests ();
53
54   /* Check log attr motion */
55   logical_motion_tests ();
56   
57   /* Create a buffer */
58   buffer = gtk_text_buffer_new (NULL);
59
60   /* Check that buffer starts with one empty line and zero chars */
61
62   n = gtk_text_buffer_get_line_count (buffer);
63   if (n != 1)
64     g_error ("%d lines, expected 1", n);
65
66   n = gtk_text_buffer_get_char_count (buffer);
67   if (n != 0)
68     g_error ("%d chars, expected 0", n);
69
70   /* Run gruesome alien test suite on buffer */
71   run_tests (buffer);
72
73   /* Check set/get text */
74   check_get_set_text (buffer, "Hello");
75   check_get_set_text (buffer, "Hello\n");
76   check_get_set_text (buffer, "Hello\r\n");
77   check_get_set_text (buffer, "Hello\r");
78   check_get_set_text (buffer, "Hello\nBar\nFoo");
79   check_get_set_text (buffer, "Hello\nBar\nFoo\n");
80   
81   /* Put stuff in the buffer */
82
83   fill_buffer (buffer);
84
85   /* Subject stuff-bloated buffer to further torment */
86   run_tests (buffer);
87
88   /* Delete all stuff from the buffer */
89   gtk_text_buffer_get_bounds (buffer, &start, &end);
90   gtk_text_buffer_delete (buffer, &start, &end);
91
92   /* Check buffer for emptiness (note that a single
93      empty line always remains in the buffer) */
94   n = gtk_text_buffer_get_line_count (buffer);
95   if (n != 1)
96     g_error ("%d lines, expected 1", n);
97
98   n = gtk_text_buffer_get_char_count (buffer);
99   if (n != 0)
100     g_error ("%d chars, expected 0", n);
101
102   run_tests (buffer);
103
104   g_object_unref (G_OBJECT (buffer));
105   
106   g_print ("All tests passed.\n");
107
108   return 0;
109 }
110
111 static void
112 check_get_set_text (GtkTextBuffer *buffer,
113                     const char    *str)
114 {
115   GtkTextIter start, end;
116   char *text;
117   int n;
118   
119   gtk_text_buffer_set_text (buffer, str, -1);
120   if (gtk_text_buffer_get_char_count (buffer) != g_utf8_strlen (str, -1))
121     g_error ("Wrong number of chars (%d not %d)",
122              gtk_text_buffer_get_char_count (buffer),
123              (int) g_utf8_strlen (str, -1));
124   gtk_text_buffer_get_bounds (buffer, &start, &end);
125   text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
126   if (strcmp (text, str) != 0)
127     g_error ("Got '%s' as buffer contents", text);
128   g_free (text);
129
130   gtk_text_buffer_set_text (buffer, "", -1);
131
132   n = gtk_text_buffer_get_line_count (buffer);
133   if (n != 1)
134     g_error ("%d lines, expected 1", n);
135
136   n = gtk_text_buffer_get_char_count (buffer);
137   if (n != 0)
138     g_error ("%d chars, expected 0", n);
139 }
140
141 static gint
142 count_toggles_at_iter (GtkTextIter *iter,
143                        GtkTextTag  *of_tag)
144 {
145   GSList *tags;
146   GSList *tmp;
147   gint count = 0;
148   
149   /* get toggle-ons and toggle-offs */
150   tags = gtk_text_iter_get_toggled_tags (iter, TRUE);
151   tags = g_slist_concat (tags,
152                          gtk_text_iter_get_toggled_tags (iter, FALSE));
153   
154   tmp = tags;
155   while (tmp != NULL)
156     {
157       if (of_tag == NULL)
158         ++count;
159       else if (of_tag == tmp->data)
160         ++count;
161       
162       tmp = g_slist_next (tmp);
163     }
164   
165   g_slist_free (tags);
166
167   return count;
168 }
169      
170 static gint
171 count_toggles_in_buffer (GtkTextBuffer *buffer,
172                          GtkTextTag    *of_tag)
173 {
174   GtkTextIter iter;
175   gint count = 0;
176   
177   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
178   do
179     {
180       count += count_toggles_at_iter (&iter, of_tag);
181     }
182   while (gtk_text_iter_forward_char (&iter));
183
184   /* Do the end iterator, because forward_char won't return TRUE
185    * on it.
186    */
187   count += count_toggles_at_iter (&iter, of_tag);
188   
189   return count;
190 }
191
192 static void
193 check_specific_tag (GtkTextBuffer *buffer,
194                     const gchar   *tag_name)
195 {
196   GtkTextIter iter;
197   GtkTextTag *tag;
198   gboolean state;
199   gint count;
200   gint buffer_count;
201   gint last_offset;
202   
203   tag = gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table (buffer),
204                                    tag_name);
205
206   buffer_count = count_toggles_in_buffer (buffer, tag);
207   
208   state = FALSE;
209   count = 0;
210
211   last_offset = -1;
212   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
213   if (gtk_text_iter_toggles_tag (&iter, tag) ||
214       gtk_text_iter_forward_to_tag_toggle (&iter, tag))
215     {
216       do
217         {
218           gint this_offset;
219           
220           ++count;
221
222           this_offset = gtk_text_iter_get_offset (&iter);
223
224           if (this_offset <= last_offset)
225             g_error ("forward_to_tag_toggle moved in wrong direction");
226
227           last_offset = this_offset;
228           
229           if (gtk_text_iter_begins_tag (&iter, tag))
230             {
231               if (state)
232                 g_error ("Tag %p is already on, and was toggled on?", tag);
233               state = TRUE;
234             }          
235           else if (gtk_text_iter_ends_tag (&iter, tag))
236             {
237               if (!state)
238                 g_error ("Tag %p toggled off, but wasn't toggled on?", tag);
239               state = FALSE;
240             }
241           else
242             g_error ("forward_to_tag_toggle went to a location without a toggle");
243         }
244       while (gtk_text_iter_forward_to_tag_toggle (&iter, tag));
245     }
246
247   if (count != buffer_count)
248     g_error ("Counted %d tags iterating by char, %d iterating by tag toggle\n",
249              buffer_count, count);
250   
251   state = FALSE;
252   count = 0;
253   
254   gtk_text_buffer_get_end_iter (buffer, &iter);
255   last_offset = gtk_text_iter_get_offset (&iter);
256   if (gtk_text_iter_toggles_tag (&iter, tag) ||
257       gtk_text_iter_backward_to_tag_toggle (&iter, tag))
258     {
259       do
260         {
261           gint this_offset;
262           
263           ++count;
264
265           this_offset = gtk_text_iter_get_offset (&iter);
266           
267           if (this_offset >= last_offset)
268             g_error ("backward_to_tag_toggle moved in wrong direction");
269           
270           last_offset = this_offset;
271
272           if (gtk_text_iter_begins_tag (&iter, tag))
273             {
274               if (!state)
275                 g_error ("Tag %p wasn't on when we got to the on toggle going backward?", tag);
276               state = FALSE;
277             }
278           else if (gtk_text_iter_ends_tag (&iter, tag))
279             {
280               if (state)
281                 g_error ("Tag %p off toggle, but we were already inside a tag?", tag);
282               state = TRUE;
283             }
284           else
285             g_error ("backward_to_tag_toggle went to a location without a toggle");
286         }
287       while (gtk_text_iter_backward_to_tag_toggle (&iter, tag));
288     }
289
290   if (count != buffer_count)
291     g_error ("Counted %d tags iterating by char, %d iterating by tag toggle\n",
292              buffer_count, count);
293
294 }
295
296 static void
297 run_tests (GtkTextBuffer *buffer)
298 {
299   GtkTextIter iter;
300   GtkTextIter start;
301   GtkTextIter end;
302   GtkTextIter mark;
303   gint i, j;
304   gint num_chars;
305   GtkTextMark *bar_mark;
306   GtkTextTag *tag;
307   GHashTable *tag_states;
308   gint count;
309   gint buffer_count;
310   
311   gtk_text_buffer_get_bounds (buffer, &start, &end);
312
313   /* Check that walking the tree via chars and via iterators produces
314    * the same number of indexable locations.
315    */
316   num_chars = gtk_text_buffer_get_char_count (buffer);
317   iter = start;
318   bar_mark = gtk_text_buffer_create_mark (buffer, "bar", &iter, FALSE);
319   i = 0;
320   while (i < num_chars)
321     {
322       GtkTextIter current;
323       GtkTextMark *foo_mark;
324
325       gtk_text_buffer_get_iter_at_offset (buffer, &current, i);
326
327       if (!gtk_text_iter_equal (&iter, &current))
328         {
329           g_error ("get_char_index didn't return current iter");
330         }
331
332       j = gtk_text_iter_get_offset (&iter);
333
334       if (i != j)
335         {
336           g_error ("iter converted to %d not %d", j, i);
337         }
338
339       /* get/set mark */
340       gtk_text_buffer_get_iter_at_mark (buffer, &mark, bar_mark);
341
342       if (!gtk_text_iter_equal (&iter, &mark))
343         {
344           gtk_text_iter_spew (&iter, "iter");
345           gtk_text_iter_spew (&mark, "mark");
346           g_error ("Mark not moved to the right place.");
347         }
348
349       foo_mark = gtk_text_buffer_create_mark (buffer, "foo", &iter, FALSE);
350       gtk_text_buffer_get_iter_at_mark (buffer, &mark, foo_mark);
351       gtk_text_buffer_delete_mark (buffer, foo_mark);
352
353       if (!gtk_text_iter_equal (&iter, &mark))
354         {
355           gtk_text_iter_spew (&iter, "iter");
356           gtk_text_iter_spew (&mark, "mark");
357           g_error ("Mark not created in the right place.");
358         }
359
360       if (gtk_text_iter_is_end (&iter))
361         g_error ("iterators ran out before chars (offset %d of %d)",
362                  i, num_chars);
363
364       gtk_text_iter_forward_char (&iter);
365
366       gtk_text_buffer_move_mark (buffer, bar_mark, &iter);
367
368       ++i;
369     }
370
371   if (!gtk_text_iter_equal (&iter, &end))
372     g_error ("Iterating over all chars didn't end with the end iter");
373
374   /* Do the tree-walk backward
375    */
376   num_chars = gtk_text_buffer_get_char_count (buffer);
377   gtk_text_buffer_get_iter_at_offset (buffer, &iter, -1);
378
379   gtk_text_buffer_move_mark (buffer, bar_mark, &iter);
380
381   i = num_chars;
382
383   if (!gtk_text_iter_equal (&iter, &end))
384     g_error ("iter at char -1 is not equal to the end iterator");
385
386   while (i >= 0)
387     {
388       GtkTextIter current;
389       GtkTextMark *foo_mark;
390
391       gtk_text_buffer_get_iter_at_offset (buffer, &current, i);
392
393       if (!gtk_text_iter_equal (&iter, &current))
394         {
395           g_error ("get_char_index didn't return current iter while going backward");
396         }
397       j = gtk_text_iter_get_offset (&iter);
398
399       if (i != j)
400         {
401           g_error ("going backward, iter converted to %d not %d", j, i);
402         }
403
404       /* get/set mark */
405       gtk_text_buffer_get_iter_at_mark (buffer, &mark, bar_mark);
406
407       if (!gtk_text_iter_equal (&iter, &mark))
408         {
409           gtk_text_iter_spew (&iter, "iter");
410           gtk_text_iter_spew (&mark, "mark");
411           g_error ("Mark not moved to the right place.");
412         }
413
414       foo_mark = gtk_text_buffer_create_mark (buffer, "foo", &iter, FALSE);
415       gtk_text_buffer_get_iter_at_mark (buffer, &mark, foo_mark);
416       gtk_text_buffer_delete_mark (buffer, foo_mark);
417
418       if (!gtk_text_iter_equal (&iter, &mark))
419         {
420           gtk_text_iter_spew (&iter, "iter");
421           gtk_text_iter_spew (&mark, "mark");
422           g_error ("Mark not created in the right place.");
423         }
424
425       if (i > 0)
426         {
427           if (!gtk_text_iter_backward_char (&iter))
428             g_error ("iterators ran out before char indexes");
429
430           gtk_text_buffer_move_mark (buffer, bar_mark, &iter);
431         }
432       else
433         {
434           if (gtk_text_iter_backward_char (&iter))
435             g_error ("went backward from 0?");
436         }
437
438       --i;
439     }
440
441   if (!gtk_text_iter_equal (&iter, &start))
442     g_error ("Iterating backward over all chars didn't end with the start iter");
443
444   /*
445    * Check that get_line_count returns the same number of lines
446    * as walking the tree by line
447    */
448   i = 1; /* include current (first) line */
449   gtk_text_buffer_get_iter_at_line (buffer, &iter, 0);
450   while (gtk_text_iter_forward_line (&iter))
451     ++i;
452
453   if (i != gtk_text_buffer_get_line_count (buffer))
454     g_error ("Counted %d lines, buffer has %d", i,
455              gtk_text_buffer_get_line_count (buffer));
456
457   /*
458    * Check that moving over tag toggles thinks about working.
459    */
460
461   buffer_count = count_toggles_in_buffer (buffer, NULL);
462   
463   tag_states = g_hash_table_new (NULL, NULL);
464   count = 0;
465   
466   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
467   if (gtk_text_iter_toggles_tag (&iter, NULL) ||
468       gtk_text_iter_forward_to_tag_toggle (&iter, NULL))
469     {
470       do
471         {
472           GSList *tags;
473           GSList *tmp;
474           gboolean found_some = FALSE;
475           
476           /* get toggled-on tags */
477           tags = gtk_text_iter_get_toggled_tags (&iter, TRUE);
478
479           if (tags)
480             found_some = TRUE;
481           
482           tmp = tags;
483           while (tmp != NULL)
484             {
485               ++count;
486               
487               tag = tmp->data;
488               
489               if (g_hash_table_lookup (tag_states, tag))
490                 g_error ("Tag %p is already on, and was toggled on?", tag);
491
492               g_hash_table_insert (tag_states, tag, GINT_TO_POINTER (TRUE));
493           
494               tmp = g_slist_next (tmp);
495             }
496
497           g_slist_free (tags);
498       
499           /* get toggled-off tags */
500           tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
501
502           if (tags)
503             found_some = TRUE;
504           
505           tmp = tags;
506           while (tmp != NULL)
507             {
508               ++count;
509               
510               tag = tmp->data;
511
512               if (!g_hash_table_lookup (tag_states, tag))
513                 g_error ("Tag %p is already off, and was toggled off?", tag);
514
515               g_hash_table_remove (tag_states, tag);
516           
517               tmp = g_slist_next (tmp);
518             }
519
520           g_slist_free (tags);
521
522           if (!found_some)
523             g_error ("No tags found going forward to tag toggle.");
524
525         }
526       while (gtk_text_iter_forward_to_tag_toggle (&iter, NULL));
527     }
528   
529   g_hash_table_destroy (tag_states);
530
531   if (count != buffer_count)
532     g_error ("Counted %d tags iterating by char, %d iterating by tag toggle\n",
533              buffer_count, count);
534   
535   /* Go backward; here TRUE in the hash means we saw
536    * an off toggle last.
537    */
538   
539   tag_states = g_hash_table_new (NULL, NULL);
540   count = 0;
541   
542   gtk_text_buffer_get_end_iter (buffer, &iter);
543   if (gtk_text_iter_toggles_tag (&iter, NULL) ||
544       gtk_text_iter_backward_to_tag_toggle (&iter, NULL))
545     {
546       do
547         {
548           GSList *tags;
549           GSList *tmp;
550           gboolean found_some = FALSE;
551           
552           /* get toggled-off tags */
553           tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
554
555           if (tags)
556             found_some = TRUE;
557           
558           tmp = tags;
559           while (tmp != NULL)
560             {
561               ++count;
562               
563               tag = tmp->data;
564
565               if (g_hash_table_lookup (tag_states, tag))
566                 g_error ("Tag %p has two off-toggles in a row?", tag);
567           
568               g_hash_table_insert (tag_states, tag, GINT_TO_POINTER (TRUE));
569           
570               tmp = g_slist_next (tmp);
571             }
572
573           g_slist_free (tags);
574       
575           /* get toggled-on tags */
576           tags = gtk_text_iter_get_toggled_tags (&iter, TRUE);
577
578           if (tags)
579             found_some = TRUE;
580           
581           tmp = tags;
582           while (tmp != NULL)
583             {
584               ++count;
585               
586               tag = tmp->data;
587
588               if (!g_hash_table_lookup (tag_states, tag))
589                 g_error ("Tag %p was toggled on, but saw no off-toggle?", tag);
590
591               g_hash_table_remove (tag_states, tag);
592           
593               tmp = g_slist_next (tmp);
594             }
595
596           g_slist_free (tags);
597
598           if (!found_some)
599             g_error ("No tags found going backward to tag toggle.");
600         }
601       while (gtk_text_iter_backward_to_tag_toggle (&iter, NULL));
602     }
603   
604   g_hash_table_destroy (tag_states);
605
606   if (count != buffer_count)
607     g_error ("Counted %d tags iterating by char, %d iterating by tag toggle\n",
608              buffer_count, count);
609
610   check_specific_tag (buffer, "fg_red");
611   check_specific_tag (buffer, "bg_green");
612   check_specific_tag (buffer, "front_tag");
613   check_specific_tag (buffer, "center_tag");
614   check_specific_tag (buffer, "end_tag");
615 }
616
617
618 static const char  *book_closed_xpm[] = {
619 "16 16 6 1",
620 "       c None s None",
621 ".      c black",
622 "X      c red",
623 "o      c yellow",
624 "O      c #808080",
625 "#      c white",
626 "                ",
627 "       ..       ",
628 "     ..XX.      ",
629 "   ..XXXXX.     ",
630 " ..XXXXXXXX.    ",
631 ".ooXXXXXXXXX.   ",
632 "..ooXXXXXXXXX.  ",
633 ".X.ooXXXXXXXXX. ",
634 ".XX.ooXXXXXX..  ",
635 " .XX.ooXXX..#O  ",
636 "  .XX.oo..##OO. ",
637 "   .XX..##OO..  ",
638 "    .X.#OO..    ",
639 "     ..O..      ",
640 "      ..        ",
641 "                "};
642
643 static void
644 fill_buffer (GtkTextBuffer *buffer)
645 {
646   GtkTextTag *tag;
647   GdkColor color, color2;
648   GtkTextIter iter;
649   GtkTextIter iter2;
650   GdkPixbuf *pixbuf;
651   int i;
652
653   color.red = color.green = 0;
654   color.blue = 0xffff;
655   color2.red = 0xfff;
656   color2.blue = 0x0;
657   color2.green = 0;
658   
659   gtk_text_buffer_create_tag (buffer, "fg_blue",
660                               "foreground_gdk", &color,
661                               "background_gdk", &color2,
662                               "font", "-*-courier-bold-r-*-*-30-*-*-*-*-*-*-*",
663                               NULL);
664
665   color.blue = color.green = 0;
666   color.red = 0xffff;
667   
668   gtk_text_buffer_create_tag (buffer, "fg_red",
669                               "rise", -4,
670                               "foreground_gdk", &color,
671                               NULL);
672
673   color.blue = color.red = 0;
674   color.green = 0xffff;
675   
676   gtk_text_buffer_create_tag (buffer, "bg_green",
677                               "background_gdk", &color,
678                               "font", "-*-courier-bold-r-*-*-10-*-*-*-*-*-*-*",
679                               NULL);
680
681   pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
682
683   g_assert (pixbuf != NULL);
684
685   i = 0;
686   while (i < 10)
687     {
688       gchar *str;
689
690       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
691
692       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
693
694       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 1);
695
696       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
697
698       str = g_strdup_printf ("%d Hello World!\nwoo woo woo woo woo woo woo woo\n",
699                             i);
700
701       gtk_text_buffer_insert (buffer, &iter, str, -1);
702
703       g_free (str);
704
705       gtk_text_buffer_insert (buffer, &iter,
706                               "(Hello World!)\nfoo foo Hello this is some text we are using to text word wrap. It has punctuation! gee; blah - hmm, great.\nnew line\n\n"
707                               /* This is UTF8 stuff, Emacs doesn't
708                                  really know how to display it */
709                               "Spanish (Español) Â¡Hola! / French (Français) Bonjour, Salut / German (Deutsch Süd) Grüß Gott (testing Latin-1 chars encoded in UTF8)\nThai (we can't display this, just making sure we don't crash)  (ภาษาไทย)  à¸ªà¸§à¸±à¸ªà¸”ีครับ, à¸ªà¸§à¸±à¸ªà¸”ีค่ะ\n",
710                               -1);
711
712       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
713       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
714
715       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 4);
716
717       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
718
719       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 7);
720
721       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
722
723       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 8);
724
725       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
726
727       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 8);
728       iter2 = iter;
729       gtk_text_iter_forward_chars (&iter2, 10);
730
731       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
732
733       gtk_text_iter_forward_chars (&iter, 7);
734       gtk_text_iter_forward_chars (&iter2, 10);
735
736       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
737
738       gtk_text_iter_forward_chars (&iter, 12);
739       gtk_text_iter_forward_chars (&iter2, 10);
740
741       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
742
743       gtk_text_iter_forward_chars (&iter, 10);
744       gtk_text_iter_forward_chars (&iter2, 15);
745
746       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
747       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
748
749       gtk_text_iter_forward_chars (&iter, 20);
750       gtk_text_iter_forward_chars (&iter2, 20);
751
752       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
753       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
754
755       gtk_text_iter_backward_chars (&iter, 25);
756       gtk_text_iter_forward_chars (&iter2, 5);
757
758       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
759       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
760
761       gtk_text_iter_forward_chars (&iter, 15);
762       gtk_text_iter_backward_chars (&iter2, 10);
763
764       gtk_text_buffer_remove_tag_by_name (buffer, "fg_red", &iter, &iter2);
765       gtk_text_buffer_remove_tag_by_name (buffer, "fg_blue", &iter, &iter2);
766
767       ++i;
768     }
769
770   /* Put in tags that are just at the beginning, and just near the end,
771    * and just near the middle.
772    */
773   tag = gtk_text_buffer_create_tag (buffer, "front_tag", NULL);
774   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 3);
775   gtk_text_buffer_get_iter_at_offset (buffer, &iter2, 300);
776
777   gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);  
778   
779   tag = gtk_text_buffer_create_tag (buffer, "end_tag", NULL);
780   gtk_text_buffer_get_end_iter (buffer, &iter2);
781   gtk_text_iter_backward_chars (&iter2, 12);
782   iter = iter2;
783   gtk_text_iter_backward_chars (&iter, 157);
784
785   gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);
786   
787   tag = gtk_text_buffer_create_tag (buffer, "center_tag", NULL);
788   gtk_text_buffer_get_iter_at_offset (buffer, &iter,
789                                       gtk_text_buffer_get_char_count (buffer)/2);
790   gtk_text_iter_backward_chars (&iter, 37);
791   iter2 = iter;
792   gtk_text_iter_forward_chars (&iter2, 57);
793
794   gtk_text_buffer_apply_tag (buffer, tag, &iter, &iter2);  
795
796   gdk_pixbuf_unref (pixbuf);
797 }
798
799
800 /*
801  * Line separator tests (initially to avoid regression on bugzilla #57428)
802  */
803
804 static void
805 test_line_separation (const char* str,
806                       gboolean    expect_next_line,
807                       gboolean    expect_end_iter,
808                       int         expected_line_count,
809                       int         expected_line_break,
810                       int         expected_next_line_start)
811 {
812   GtkTextIter iter;
813   GtkTextBuffer* buffer;
814   gboolean on_next_line;
815   gboolean on_end_iter;
816   gint new_pos;
817
818   buffer = gtk_text_buffer_new (NULL);
819
820   gtk_text_buffer_set_text (buffer, str, -1);
821   gtk_text_buffer_get_iter_at_offset (buffer, &iter, expected_line_break);
822
823   g_assert (gtk_text_iter_ends_line (&iter) || gtk_text_iter_is_end (&iter));
824
825   g_assert (gtk_text_buffer_get_line_count (buffer) == expected_line_count);
826   
827   on_next_line = gtk_text_iter_forward_line (&iter);
828
829   g_assert (expect_next_line == on_next_line);
830
831   on_end_iter = gtk_text_iter_is_end (&iter);
832
833   g_assert (on_end_iter == expect_end_iter);
834   
835   new_pos = gtk_text_iter_get_offset (&iter);
836     
837   if (on_next_line)
838     g_assert (expected_next_line_start == new_pos);
839
840   ++expected_line_break;
841   while (expected_line_break < expected_next_line_start)
842     {
843       gtk_text_buffer_get_iter_at_offset (buffer, &iter, expected_line_break);
844
845       g_assert (!gtk_text_iter_ends_line (&iter));
846
847       on_next_line = gtk_text_iter_forward_line (&iter);
848         
849       g_assert (expect_next_line == on_next_line);
850         
851       new_pos = gtk_text_iter_get_offset (&iter);
852         
853       if (on_next_line)
854         g_assert (expected_next_line_start == new_pos);
855         
856       ++expected_line_break;
857     }
858
859   /* FIXME tests for backward line */
860   
861   g_object_unref (G_OBJECT (buffer));
862 }
863
864
865 static void
866 line_separator_tests (void)
867 {
868   char *str;
869   char buf[7] = { '\0', };
870
871   /* Only one character has type G_UNICODE_PARAGRAPH_SEPARATOR in
872    * Unicode 3.0; update this if that changes.
873    */
874 #define PARAGRAPH_SEPARATOR 0x2029
875   
876   test_line_separation ("line", FALSE, TRUE, 1, 4, 4);
877   test_line_separation ("line\r\n", FALSE, TRUE, 2, 4, 6);
878   test_line_separation ("line\r", FALSE, TRUE, 2, 4, 5);
879   test_line_separation ("line\n", FALSE, TRUE, 2, 4, 5);
880   test_line_separation ("line\rqw", TRUE, FALSE, 2, 4, 5);
881   test_line_separation ("line\nqw", TRUE, FALSE, 2, 4, 5);
882   test_line_separation ("line\r\nqw", TRUE, FALSE, 2, 4, 6);
883   
884   g_unichar_to_utf8 (PARAGRAPH_SEPARATOR, buf);
885   
886   str = g_strdup_printf ("line%s", buf);
887   test_line_separation (str, FALSE, TRUE, 2, 4, 5);
888   g_free (str);
889   str = g_strdup_printf ("line%sqw", buf);
890   test_line_separation (str, TRUE, FALSE, 2, 4, 5);
891   g_free (str);
892
893   g_print ("Line separator tests passed\n");
894 }
895
896 static void
897 logical_motion_tests (void)
898 {
899   char *str;
900   char buf1[7] = { '\0', };
901   char buf2[7] = { '\0', };
902   char buf3[7] = { '\0', };
903   int expected[30];
904   int expected_steps;
905   int i;
906   GtkTextBuffer *buffer;
907   GtkTextIter iter;
908   
909   buffer = gtk_text_buffer_new (NULL);
910   
911 #define LEADING_JAMO 0x1111
912 #define VOWEL_JAMO 0x1167
913 #define TRAILING_JAMO 0x11B9
914   
915   g_unichar_to_utf8 (LEADING_JAMO, buf1);
916   g_unichar_to_utf8 (VOWEL_JAMO, buf2);
917   g_unichar_to_utf8 (TRAILING_JAMO, buf3);
918
919   /* Build the string "abc<leading><vowel><trailing>def\r\nxyz" */
920   str = g_strconcat ("abc", buf1, buf2, buf3, "def\r\nxyz", NULL);
921   gtk_text_buffer_set_text (buffer, str, -1);
922   g_free (str);
923   
924   /* Check cursor positions */
925   memset (expected, 0, sizeof (expected));
926   expected[0] = 0;    /* before 'a' */
927   expected[1] = 1;    /* before 'b' */
928   expected[2] = 2;    /* before 'c' */
929   expected[3] = 3;    /* before jamo */
930   expected[4] = 6;    /* before 'd' */
931   expected[5] = 7;    /* before 'e' */
932   expected[6] = 8;    /* before 'f' */
933   expected[7] = 9;    /* before '\r' */
934   expected[8] = 11;   /* before 'x' */
935   expected[9] = 12;   /* before 'y' */
936   expected[10] = 13;  /* before 'z' */
937   expected[11] = 14;  /* after 'z' (only matters going backward) */
938   expected_steps = 11;
939   
940   gtk_text_buffer_get_start_iter (buffer, &iter);
941   i = 0;
942   do
943     {
944       int pos;
945
946       pos = gtk_text_iter_get_offset (&iter);
947       
948       if (pos != expected[i])
949         {
950           g_error ("Cursor position %d, expected %d",
951                    pos, expected[i]);
952         }
953
954       ++i;      
955     }
956   while (gtk_text_iter_forward_cursor_position (&iter));
957
958   if (!gtk_text_iter_is_end (&iter))
959     g_error ("Expected to stop at the end iterator\n");
960
961   if (!gtk_text_iter_is_cursor_position (&iter))
962     g_error ("Should be a cursor position before the end iterator");
963   
964   if (i != expected_steps)
965     g_error ("Expected %d steps, there were actually %d\n", expected_steps, i);
966
967   i = expected_steps;
968   do
969     {
970       int pos;
971
972       pos = gtk_text_iter_get_offset (&iter);
973       
974       if (pos != expected[i])
975         {
976           g_error ("Moving backward, cursor position %d, expected %d",
977                    pos, expected[i]);
978         }
979
980       /* g_print ("%d = %d\n", pos, expected[i]); */
981       
982       --i;
983     }
984   while (gtk_text_iter_backward_cursor_position (&iter));
985
986   if (i != -1)
987     g_error ("Expected %d steps, there were actually %d\n", expected_steps - i, i);
988
989   if (!gtk_text_iter_is_start (&iter))
990     g_error ("Expected to stop at the start iterator\n");
991
992
993   /* Check sentence boundaries */
994   
995   gtk_text_buffer_set_text (buffer, "Hi.\nHi. \nHi! Hi. Hi? Hi.", -1);
996
997   memset (expected, 0, sizeof (expected));
998
999   expected[0] = 0;    /* before first Hi */
1000   expected[1] = 3;    /* After first . */
1001   expected[2] = 7;    /* After second . */
1002   expected[3] = 12;   /* After ! */
1003   expected[4] = 16;   /* After third . */
1004   expected[5] = 20;   /* After ? */
1005   
1006   expected_steps = 6;
1007   
1008   gtk_text_buffer_get_start_iter (buffer, &iter);
1009   i = 0;
1010   do
1011     {
1012       int pos;
1013
1014       pos = gtk_text_iter_get_offset (&iter);
1015
1016       if (pos != expected[i])
1017         {
1018           g_error ("Sentence position %d, expected %d",
1019                    pos, expected[i]);
1020         }
1021
1022       if (i != 0 &&
1023           !gtk_text_iter_is_end (&iter) &&
1024           !gtk_text_iter_ends_sentence (&iter))
1025         g_error ("Iterator at %d should end a sentence", pos);
1026       
1027       ++i;
1028     }
1029   while (gtk_text_iter_forward_sentence_end (&iter));
1030
1031   if (i != expected_steps)
1032     g_error ("Expected %d steps, there were actually %d\n", expected_steps, i);
1033
1034   if (!gtk_text_iter_is_end (&iter))
1035     g_error ("Expected to stop at the end iterator\n");
1036   
1037   gtk_text_buffer_set_text (buffer, "Hi.\nHi. \nHi! Hi. Hi? Hi.", -1);
1038
1039   memset (expected, 0, sizeof (expected));
1040
1041   expected[0] = 24;
1042   expected[1] = 21;
1043   expected[2] = 17;
1044   expected[3] = 13;
1045   expected[4] = 9;
1046   expected[5] = 4;
1047   expected[6] = 0;
1048   
1049   expected_steps = 7;
1050   
1051   gtk_text_buffer_get_end_iter (buffer, &iter);
1052   i = 0;
1053   do
1054     {
1055       int pos;
1056
1057       pos = gtk_text_iter_get_offset (&iter);
1058
1059       if (pos != expected[i])
1060         {
1061           g_error ("Sentence position %d, expected %d",
1062                    pos, expected[i]);
1063         }
1064
1065       if (pos != 0 &&
1066           !gtk_text_iter_is_end (&iter) &&
1067           !gtk_text_iter_starts_sentence (&iter))
1068         g_error ("Iterator at %d should start a sentence", pos);
1069       
1070       ++i;
1071     }
1072   while (gtk_text_iter_backward_sentence_start (&iter));
1073
1074   if (i != expected_steps)
1075     g_error ("Expected %d steps, there were actually %d\n", expected_steps, i);
1076
1077   if (gtk_text_iter_get_offset (&iter) != 0)
1078     g_error ("Expected to stop at the start iterator\n");
1079   
1080   g_print ("Logical motion tests passed\n");
1081
1082   g_object_unref (G_OBJECT (buffer));
1083 }