1 #include "gtktexthistoryprivate.h"
2 
3 #if 0
4 # define DEBUG_COMMANDS
5 #endif
6 
7 typedef struct
8 {
9   GtkTextHistory *history;
10   GString *buf;
11   struct {
12     int insert;
13     int bound;
14   } selection;
15   guint can_redo : 1;
16   guint can_undo : 1;
17   guint is_modified : 1;
18 } Text;
19 
20 enum {
21   IGNORE = 0,
22   SET = 1,
23   UNSET = 2,
24 };
25 
26 enum {
27   IGNORE_SELECT = 0,
28   DO_SELECT = 1,
29 };
30 
31 enum {
32   INSERT = 1,
33   INSERT_SEQ,
34   BACKSPACE,
35   DELETE_KEY,
36   UNDO,
37   REDO,
38   BEGIN_IRREVERSIBLE,
39   END_IRREVERSIBLE,
40   BEGIN_USER,
41   END_USER,
42   MODIFIED,
43   UNMODIFIED,
44   SELECT,
45   CHECK_SELECT,
46   SET_MAX_UNDO,
47 };
48 
49 typedef struct
50 {
51   int kind;
52   int location;
53   int end_location;
54   const char *text;
55   const char *expected;
56   int can_undo;
57   int can_redo;
58   int is_modified;
59   int select;
60 } Command;
61 
62 static void
do_change_state(gpointer funcs_data,gboolean is_modified,gboolean can_undo,gboolean can_redo)63 do_change_state (gpointer funcs_data,
64                  gboolean is_modified,
65                  gboolean can_undo,
66                  gboolean can_redo)
67 {
68   Text *text = funcs_data;
69 
70   text->is_modified = is_modified;
71   text->can_undo = can_undo;
72   text->can_redo = can_redo;
73 }
74 
75 static void
do_insert(gpointer funcs_data,guint begin,guint end,const char * text,guint len)76 do_insert (gpointer    funcs_data,
77            guint       begin,
78            guint       end,
79            const char *text,
80            guint       len)
81 {
82   Text *t = funcs_data;
83 
84 #ifdef DEBUG_COMMANDS
85   g_printerr ("Insert Into '%s' (begin=%u end=%u text=%s)\n",
86               t->buf->str, begin, end, text);
87 #endif
88 
89   g_string_insert_len (t->buf, begin, text, len);
90 }
91 
92 static void
do_delete(gpointer funcs_data,guint begin,guint end,const char * expected_text,guint len)93 do_delete (gpointer     funcs_data,
94            guint        begin,
95            guint        end,
96            const char *expected_text,
97            guint        len)
98 {
99   Text *t = funcs_data;
100 
101 #ifdef DEBUG_COMMANDS
102   g_printerr ("Delete(begin=%u end=%u expected_text=%s)\n", begin, end, expected_text);
103 #endif
104 
105   if (end < begin)
106     {
107       guint tmp = end;
108       end = begin;
109       begin = tmp;
110     }
111 
112   g_assert_cmpint (memcmp (t->buf->str + begin, expected_text, len), ==, 0);
113 
114   if (end >= t->buf->len)
115     {
116       t->buf->len = begin;
117       t->buf->str[begin] = 0;
118       return;
119     }
120 
121   memmove (t->buf->str + begin,
122            t->buf->str + end,
123            t->buf->len - end);
124   g_string_truncate (t->buf, t->buf->len - (end - begin));
125 }
126 
127 static void
do_select(gpointer funcs_data,int selection_insert,int selection_bound)128 do_select (gpointer funcs_data,
129            int      selection_insert,
130            int      selection_bound)
131 {
132   Text *text = funcs_data;
133 
134   text->selection.insert = selection_insert;
135   text->selection.bound = selection_bound;
136 }
137 
138 static GtkTextHistoryFuncs funcs = {
139   do_change_state,
140   do_insert,
141   do_delete,
142   do_select,
143 };
144 
145 static Text *
text_new(void)146 text_new (void)
147 {
148   Text *text = g_slice_new0 (Text);
149 
150   text->history = gtk_text_history_new (&funcs, text);
151   text->buf = g_string_new (NULL);
152   text->selection.insert = -1;
153   text->selection.bound = -1;
154 
155   return text;
156 }
157 
158 static void
text_free(Text * text)159 text_free (Text *text)
160 {
161   g_object_unref (text->history);
162   g_string_free (text->buf, TRUE);
163   g_slice_free (Text, text);
164 }
165 
166 static void
command_insert(const Command * cmd,Text * text)167 command_insert (const Command *cmd,
168                 Text          *text)
169 {
170   do_insert (text,
171              cmd->location,
172              cmd->location + g_utf8_strlen (cmd->text, -1),
173              cmd->text,
174              strlen (cmd->text));
175   gtk_text_history_text_inserted (text->history, cmd->location, cmd->text, -1);
176 }
177 
178 static void
command_delete_key(const Command * cmd,Text * text)179 command_delete_key (const Command *cmd,
180                     Text          *text)
181 {
182   do_delete (text,
183              cmd->location,
184              cmd->end_location,
185              cmd->text,
186              strlen (cmd->text));
187   gtk_text_history_text_deleted (text->history,
188                                  cmd->location,
189                                  cmd->end_location,
190                                  cmd->text,
191                                  ABS (cmd->end_location - cmd->location));
192 }
193 
194 static void
command_undo(const Command * cmd,Text * text)195 command_undo (const Command *cmd,
196               Text          *text)
197 {
198   gtk_text_history_undo (text->history);
199 }
200 
201 static void
command_redo(const Command * cmd,Text * text)202 command_redo (const Command *cmd,
203               Text          *text)
204 {
205   gtk_text_history_redo (text->history);
206 }
207 
208 static void
set_selection(Text * text,int begin,int end)209 set_selection (Text *text,
210                int   begin,
211                int   end)
212 {
213   gtk_text_history_selection_changed (text->history, begin, end);
214 }
215 
216 static void
run_test(const Command * commands,guint n_commands,guint max_undo)217 run_test (const Command *commands,
218           guint          n_commands,
219           guint          max_undo)
220 {
221   Text *text = text_new ();
222 
223   if (max_undo)
224     gtk_text_history_set_max_undo_levels (text->history, max_undo);
225 
226   for (guint i = 0; i < n_commands; i++)
227     {
228       const Command *cmd = &commands[i];
229 
230 #ifdef DEBUG_COMMANDS
231       g_printerr ("%d: %d\n", i, cmd->kind);
232 #endif
233 
234       switch (cmd->kind)
235         {
236         case INSERT:
237           command_insert (cmd, text);
238           break;
239 
240         case INSERT_SEQ:
241           for (guint j = 0; cmd->text[j]; j++)
242             {
243               const char seqtext[2] = { cmd->text[j], 0 };
244               Command seq = { INSERT, cmd->location + j, 1, seqtext, NULL };
245               command_insert (&seq, text);
246             }
247           break;
248 
249         case DELETE_KEY:
250           if (cmd->select == DO_SELECT)
251             set_selection (text, cmd->location, cmd->end_location);
252           else if (strlen (cmd->text) == 1)
253             set_selection (text, cmd->location, -1);
254           else
255             set_selection (text, -1, -1);
256           command_delete_key (cmd, text);
257           break;
258 
259         case BACKSPACE:
260           if (cmd->select == DO_SELECT)
261             set_selection (text, cmd->location, cmd->end_location);
262           else if (strlen (cmd->text) == 1)
263             set_selection (text, cmd->end_location, -1);
264           else
265             set_selection (text, -1, -1);
266           command_delete_key (cmd, text);
267           break;
268 
269         case UNDO:
270           command_undo (cmd, text);
271           break;
272 
273         case REDO:
274           command_redo (cmd, text);
275           break;
276 
277         case BEGIN_USER:
278           gtk_text_history_begin_user_action (text->history);
279           break;
280 
281         case END_USER:
282           gtk_text_history_end_user_action (text->history);
283           break;
284 
285         case BEGIN_IRREVERSIBLE:
286           gtk_text_history_begin_irreversible_action (text->history);
287           break;
288 
289         case END_IRREVERSIBLE:
290           gtk_text_history_end_irreversible_action (text->history);
291           break;
292 
293         case MODIFIED:
294           gtk_text_history_modified_changed (text->history, TRUE);
295           break;
296 
297         case UNMODIFIED:
298           gtk_text_history_modified_changed (text->history, FALSE);
299           break;
300 
301         case SELECT:
302           gtk_text_history_selection_changed (text->history,
303                                               cmd->location,
304                                               cmd->end_location);
305           break;
306 
307         case CHECK_SELECT:
308           g_assert_cmpint (text->selection.insert, ==, cmd->location);
309           g_assert_cmpint (text->selection.bound, ==, cmd->end_location);
310           break;
311 
312         case SET_MAX_UNDO:
313           /* Not ideal use of location, but fine */
314           gtk_text_history_set_max_undo_levels (text->history, cmd->location);
315           break;
316 
317         default:
318           break;
319         }
320 
321       if (cmd->expected)
322         g_assert_cmpstr (text->buf->str, ==, cmd->expected);
323 
324       if (cmd->can_redo == SET)
325         g_assert_cmpint (text->can_redo, ==, TRUE);
326       else if (cmd->can_redo == UNSET)
327         g_assert_cmpint (text->can_redo, ==, FALSE);
328 
329       if (cmd->can_undo == SET)
330         g_assert_cmpint (text->can_undo, ==, TRUE);
331       else if (cmd->can_undo == UNSET)
332         g_assert_cmpint (text->can_undo, ==, FALSE);
333 
334       if (cmd->is_modified == SET)
335         g_assert_cmpint (text->is_modified, ==, TRUE);
336       else if (cmd->is_modified == UNSET)
337         g_assert_cmpint (text->is_modified, ==, FALSE);
338     }
339 
340   text_free (text);
341 }
342 
343 static void
test1(void)344 test1 (void)
345 {
346   static const Command commands[] = {
347     { INSERT, 0, -1, "test", "test", SET, UNSET },
348     { INSERT, 2, -1, "s", "tesst", SET, UNSET },
349     { INSERT, 3, -1, "ss", "tesssst", SET, UNSET },
350     { DELETE_KEY, 2, 5, "sss", "test", SET, UNSET },
351     { UNDO, -1, -1, NULL, "tesssst", SET, SET },
352     { REDO, -1, -1, NULL, "test", SET, UNSET },
353     { UNDO, -1, -1, NULL, "tesssst", SET, SET },
354     { DELETE_KEY, 0, 7, "tesssst", "", SET, UNSET },
355     { INSERT, 0, -1, "z", "z", SET, UNSET },
356     { UNDO, -1, -1, NULL, "", SET, SET },
357     { UNDO, -1, -1, NULL, "tesssst", SET, SET },
358     { UNDO, -1, -1, NULL, "test", SET, SET },
359   };
360 
361   run_test (commands, G_N_ELEMENTS (commands), 0);
362 }
363 
364 static void
test2(void)365 test2 (void)
366 {
367   static const Command commands[] = {
368     { BEGIN_IRREVERSIBLE, -1, -1, NULL, "", UNSET, UNSET },
369     { INSERT, 0, -1, "this is a test", "this is a test", UNSET, UNSET },
370     { END_IRREVERSIBLE, -1, -1, NULL, "this is a test", UNSET, UNSET },
371     { UNDO, -1, -1, NULL, "this is a test", UNSET, UNSET },
372     { REDO, -1, -1, NULL, "this is a test", UNSET, UNSET },
373     { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET },
374     { INSERT, 0, -1, "first", "firstthis is a test", UNSET, UNSET },
375     { INSERT, 5, -1, " ", "first this is a test", UNSET, UNSET },
376     { END_USER, -1, -1, NULL, "first this is a test", SET, UNSET },
377     { UNDO, -1, -1, NULL, "this is a test", UNSET, SET },
378     { UNDO, -1, -1, NULL, "this is a test", UNSET, SET },
379     { REDO, -1, -1, NULL, "first this is a test", SET, UNSET },
380     { UNDO, -1, -1, NULL, "this is a test", UNSET, SET },
381   };
382 
383   run_test (commands, G_N_ELEMENTS (commands), 0);
384 }
385 
386 static void
test3(void)387 test3 (void)
388 {
389   static const Command commands[] = {
390     { INSERT_SEQ, 0, -1, "this is a test of insertions.", "this is a test of insertions.", SET, UNSET },
391     { UNDO, -1, -1, NULL, "this is a test of", SET, SET },
392     { UNDO, -1, -1, NULL, "this is a test", SET, SET },
393     { UNDO, -1, -1, NULL, "this is a", SET, SET },
394     { UNDO, -1, -1, NULL, "this is", SET, SET },
395     { UNDO, -1, -1, NULL, "this", SET, SET },
396     { UNDO, -1, -1, NULL, "", UNSET, SET },
397     { UNDO, -1, -1, NULL, "" , UNSET, SET },
398     { REDO, -1, -1, NULL, "this", SET, SET },
399     { REDO, -1, -1, NULL, "this is", SET, SET },
400     { REDO, -1, -1, NULL, "this is a", SET, SET },
401     { REDO, -1, -1, NULL, "this is a test", SET, SET },
402     { REDO, -1, -1, NULL, "this is a test of", SET, SET },
403     { REDO, -1, -1, NULL, "this is a test of insertions.", SET, UNSET },
404   };
405 
406   run_test (commands, G_N_ELEMENTS (commands), 0);
407 }
408 
409 static void
test4(void)410 test4 (void)
411 {
412   static const Command commands[] = {
413     { INSERT, 0, -1, "initial text", "initial text", SET, UNSET },
414     /* Barrier */
415     { BEGIN_IRREVERSIBLE, -1, -1, NULL, NULL, UNSET, UNSET },
416     { END_IRREVERSIBLE, -1, -1, NULL, NULL, UNSET, UNSET },
417     { INSERT, 0, -1, "more text ", "more text initial text", SET, UNSET },
418     { UNDO, -1, -1, NULL, "initial text", UNSET, SET },
419     { UNDO, -1, -1, NULL, "initial text", UNSET, SET },
420     { REDO, -1, -1, NULL, "more text initial text", SET, UNSET },
421     /* Barrier */
422     { BEGIN_IRREVERSIBLE, UNSET, UNSET },
423     { END_IRREVERSIBLE, UNSET, UNSET },
424     { UNDO, -1, -1, NULL, "more text initial text", UNSET, UNSET },
425   };
426 
427   run_test (commands, G_N_ELEMENTS (commands), 0);
428 }
429 
430 static void
test5(void)431 test5 (void)
432 {
433   static const Command commands[] = {
434     { INSERT, 0, -1, "initial text", "initial text", SET, UNSET },
435     { DELETE_KEY, 0, 12, "initial text", "", SET, UNSET },
436     /* Add empty nested user action (should get ignored) */
437     { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET },
438       { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET },
439         { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET },
440         { END_USER, -1, -1, NULL, NULL, UNSET, UNSET },
441       { END_USER, -1, -1, NULL, NULL, UNSET, UNSET },
442     { END_USER, -1, -1, NULL, NULL, SET, UNSET },
443     { UNDO, -1, -1, NULL, "initial text" },
444   };
445 
446   run_test (commands, G_N_ELEMENTS (commands), 0);
447 }
448 
449 static void
test6(void)450 test6 (void)
451 {
452   static const Command commands[] = {
453     { INSERT_SEQ, 0, -1, " \t\t    this is some text", " \t\t    this is some text", SET, UNSET },
454     { UNDO, -1, -1, NULL, " \t\t    this is some", SET, SET },
455     { UNDO, -1, -1, NULL, " \t\t    this is", SET, SET },
456     { UNDO, -1, -1, NULL, " \t\t    this", SET, SET },
457     { UNDO, -1, -1, NULL, "", UNSET, SET },
458     { UNDO, -1, -1, NULL, "", UNSET, SET },
459   };
460 
461   run_test (commands, G_N_ELEMENTS (commands), 0);
462 }
463 
464 static void
test7(void)465 test7 (void)
466 {
467   static const Command commands[] = {
468     { MODIFIED, -1, -1, NULL, NULL, UNSET, UNSET, SET },
469     { UNMODIFIED, -1, -1, NULL, NULL, UNSET, UNSET, UNSET },
470     { INSERT, 0, -1, "foo bar", "foo bar", SET, UNSET, UNSET },
471     { MODIFIED, -1, -1, NULL, NULL, SET, UNSET, SET },
472     { UNDO, -1, -1, NULL, "", UNSET, SET, UNSET },
473     { REDO, -1, -1, NULL, "foo bar", SET, UNSET, SET },
474     { UNDO, -1, -1, NULL, "", UNSET, SET, UNSET },
475     { REDO, -1, -1, NULL, "foo bar", SET, UNSET, SET },
476   };
477 
478   run_test (commands, G_N_ELEMENTS (commands), 0);
479 }
480 
481 static void
test8(void)482 test8 (void)
483 {
484   static const Command commands[] = {
485     { INSERT, 0, -1, "foo bar", "foo bar", SET, UNSET, UNSET },
486     { MODIFIED, -1, -1, NULL, NULL, SET, UNSET, SET },
487     { INSERT, 0, -1, "f", "ffoo bar", SET, UNSET, SET },
488     { UNMODIFIED, -1, -1, NULL, NULL, SET, UNSET, UNSET },
489     { UNDO, -1, -1, NULL, "foo bar", SET, SET, SET },
490     { UNDO, -1, -1, NULL, "", UNSET, SET, SET },
491     { REDO, -1, -1, NULL, "foo bar", SET, SET, SET },
492     { REDO, -1, -1, NULL, "ffoo bar", SET, UNSET, UNSET },
493   };
494 
495   run_test (commands, G_N_ELEMENTS (commands), 0);
496 }
497 
498 static void
test9(void)499 test9 (void)
500 {
501   static const Command commands[] = {
502     { INSERT, 0, -1, "foo bar", "foo bar", SET, UNSET, UNSET },
503     { DELETE_KEY, 0, 3, "foo", " bar", SET, UNSET, UNSET, DO_SELECT },
504     { DELETE_KEY, 0, 4, " bar", "", SET, UNSET, UNSET, DO_SELECT },
505     { UNDO, -1, -1, NULL, " bar", SET, SET, UNSET },
506     { CHECK_SELECT, 0, 4, NULL, " bar", SET, SET, UNSET },
507     { UNDO, -1, -1, NULL, "foo bar", SET, SET, UNSET },
508     { CHECK_SELECT, 0, 3, NULL, "foo bar", SET, SET, UNSET },
509     { BEGIN_IRREVERSIBLE, -1, -1, NULL, "foo bar", UNSET, UNSET, UNSET },
510     { END_IRREVERSIBLE, -1, -1, NULL, "foo bar", UNSET, UNSET, UNSET },
511   };
512 
513   run_test (commands, G_N_ELEMENTS (commands), 0);
514 }
515 
516 static void
test10(void)517 test10 (void)
518 {
519   static const Command commands[] = {
520     { BEGIN_USER }, { INSERT, 0, -1, "t", "t", UNSET, UNSET, UNSET }, { END_USER },
521     { BEGIN_USER }, { INSERT, 1, -1, " ", "t ", UNSET, UNSET, UNSET }, { END_USER },
522     { BEGIN_USER }, { INSERT, 2, -1, "t", "t t", UNSET, UNSET, UNSET }, { END_USER },
523     { BEGIN_USER }, { INSERT, 3, -1, "h", "t th", UNSET, UNSET, UNSET }, { END_USER },
524     { BEGIN_USER }, { INSERT, 4, -1, "i", "t thi", UNSET, UNSET, UNSET }, { END_USER },
525     { BEGIN_USER }, { INSERT, 5, -1, "s", "t this", UNSET, UNSET, UNSET }, { END_USER },
526   };
527 
528   run_test (commands, G_N_ELEMENTS (commands), 0);
529 }
530 
531 static void
test11(void)532 test11 (void)
533 {
534   /* Test backspace */
535   static const Command commands[] = {
536     { INSERT_SEQ, 0, -1, "insert some text", "insert some text", SET, UNSET, UNSET },
537     { BACKSPACE, 15, 16, "t", "insert some tex", SET, UNSET, UNSET },
538     { BACKSPACE, 14, 15, "x", "insert some te", SET, UNSET, UNSET },
539     { BACKSPACE, 13, 14, "e", "insert some t", SET, UNSET, UNSET },
540     { BACKSPACE, 12, 13, "t", "insert some ", SET, UNSET, UNSET },
541     { UNDO, -1, -1, NULL, "insert some text", SET, SET, UNSET },
542   };
543 
544   run_test (commands, G_N_ELEMENTS (commands), 0);
545 }
546 
547 static void
test12(void)548 test12 (void)
549 {
550   static const Command commands[] = {
551     { INSERT_SEQ, 0, -1, "this is a test\nmore", "this is a test\nmore", SET, UNSET, UNSET },
552     { UNDO, -1, -1, NULL, "this is a test\n", SET, SET, UNSET },
553     { UNDO, -1, -1, NULL, "this is a test", SET, SET, UNSET },
554     { UNDO, -1, -1, NULL, "this is a", SET, SET, UNSET },
555     { UNDO, -1, -1, NULL, "this is", SET, SET, UNSET },
556     { UNDO, -1, -1, NULL, "this", SET, SET, UNSET },
557     { UNDO, -1, -1, NULL, "", UNSET, SET, UNSET },
558   };
559 
560   run_test (commands, G_N_ELEMENTS (commands), 0);
561 }
562 
563 static void
test13(void)564 test13 (void)
565 {
566   static const Command commands[] = {
567     { INSERT_SEQ, 0, -1, "this is a test\nmore", "this is a test\nmore", SET, UNSET, UNSET },
568     { UNDO, -1, -1, NULL, "this is a test\n", SET, SET, UNSET },
569     { UNDO, -1, -1, NULL, "this is a test", SET, SET, UNSET },
570     { UNDO, -1, -1, NULL, "this is a", UNSET, SET, UNSET },
571     { UNDO, -1, -1, NULL, "this is a", UNSET, SET, UNSET },
572     { SET_MAX_UNDO, 2, -1, NULL, "this is a", UNSET, SET, UNSET },
573     { REDO, -1, -1, NULL, "this is a test", SET, SET, UNSET },
574     { REDO, -1, -1, NULL, "this is a test\n", SET, UNSET, UNSET },
575     { REDO, -1, -1, NULL, "this is a test\n", SET, UNSET, UNSET },
576   };
577 
578   run_test (commands, G_N_ELEMENTS (commands), 3);
579 }
580 
581 static void
test14(void)582 test14 (void)
583 {
584   char *fill = g_strnfill (1024, 'x');
585   char *fill_after = g_strnfill (1025, 'x');
586   char *fill_after_2 = g_strdup_printf ("%s word", fill_after);
587   const Command commands[] = {
588     { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET, UNSET },
589     { INSERT, 0, -1, fill, fill, UNSET, UNSET, UNSET },
590     { END_USER, -1, -1, NULL, NULL, SET, UNSET, UNSET },
591     { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET, UNSET },
592     { INSERT, 0, -1, "x", fill_after, UNSET, UNSET, UNSET },
593     { END_USER, -1, -1, NULL, NULL, SET, UNSET, UNSET },
594     { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET, UNSET },
595     { INSERT_SEQ, strlen(fill_after), -1, " word", fill_after_2, UNSET, UNSET, UNSET },
596     { END_USER, -1, -1, NULL, NULL, SET, UNSET, UNSET },
597     { UNDO, -1, -1, NULL, fill_after, SET, SET, UNSET },
598     { UNDO, -1, -1, NULL, fill, SET, SET, UNSET },
599     { UNDO, -1, -1, NULL, "", UNSET, SET, UNSET },
600   };
601 
602   run_test (commands, G_N_ELEMENTS (commands), 0);
603 
604   g_free (fill);
605   g_free (fill_after);
606   g_free (fill_after_2);
607 }
608 
609 static void
test_issue_4276(void)610 test_issue_4276 (void)
611 {
612   const Command commands[] = {
613     { INSERT, 0, -1, "this is some text", "this is some text", SET, UNSET, UNSET },
614     { SELECT, 0, 17, NULL, "this is some text", SET, UNSET, UNSET },
615     { BEGIN_USER, -1, -1, NULL, NULL, UNSET, UNSET, UNSET },
616     { DELETE_KEY, 0, 17, "this is some text", "", UNSET, UNSET, UNSET },
617     { INSERT, 0, -1, "z", "z", UNSET, UNSET, UNSET },
618     { END_USER, -1, -1, NULL, NULL, SET, UNSET, UNSET },
619     { INSERT, 1, -1, "zzz", "zzzz", SET, UNSET, UNSET },
620     { UNDO, -1, -1, NULL, "z", SET, SET, UNSET },
621   };
622 
623   run_test (commands, G_N_ELEMENTS (commands), 0);
624 }
625 
626 int
main(int argc,char * argv[])627 main (int   argc,
628       char *argv[])
629 {
630   (g_test_init) (&argc, &argv, NULL);
631 
632   g_test_add_func ("/Gtk/TextHistory/test1", test1);
633   g_test_add_func ("/Gtk/TextHistory/test2", test2);
634   g_test_add_func ("/Gtk/TextHistory/test3", test3);
635   g_test_add_func ("/Gtk/TextHistory/test4", test4);
636   g_test_add_func ("/Gtk/TextHistory/test5", test5);
637   g_test_add_func ("/Gtk/TextHistory/test6", test6);
638   g_test_add_func ("/Gtk/TextHistory/test7", test7);
639   g_test_add_func ("/Gtk/TextHistory/test8", test8);
640   g_test_add_func ("/Gtk/TextHistory/test9", test9);
641   g_test_add_func ("/Gtk/TextHistory/test10", test10);
642   g_test_add_func ("/Gtk/TextHistory/test11", test11);
643   g_test_add_func ("/Gtk/TextHistory/test12", test12);
644   g_test_add_func ("/Gtk/TextHistory/test13", test13);
645   g_test_add_func ("/Gtk/TextHistory/test14", test14);
646   g_test_add_func ("/Gtk/TextHistory/issue_4276", test_issue_4276);
647 
648   return g_test_run ();
649 }
650