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