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