1 /**
2 * stress.c
3 *
4 * Copyright (c) 2015
5 * libchewing Core Team. See ChangeLog for details.
6 *
7 * See the file "COPYING" for information on usage and redistribution
8 * of this file.
9 */
10
11 #include "chewing.h"
12 #include "testhelper.h"
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <signal.h>
18 #include <assert.h>
19
20 #ifndef _MSC_VER
21 #include <unistd.h>
22 #else
23 #include <io.h>
24 #endif
25
26 static int selKey_define[11] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 0 }; /* Default */
27
28 static int all_key[] = {
29 KEY_LEFT,
30 KEY_SLEFT,
31 KEY_RIGHT,
32 KEY_SRIGHT,
33 KEY_UP,
34 KEY_DOWN,
35 KEY_SPACE,
36 KEY_ENTER,
37 KEY_BACKSPACE,
38 KEY_ESC,
39 KEY_DELETE,
40 KEY_HOME,
41 KEY_END,
42 KEY_TAB,
43 KEY_CAPSLOCK,
44 KEY_NPAGE,
45 KEY_PPAGE,
46 KEY_SSPACE,
47 KEY_DBLTAB,
48 KEY_CTRL_BASE + '0',
49 KEY_CTRL_BASE + '1',
50 KEY_CTRL_BASE + '2',
51 KEY_CTRL_BASE + '3',
52 KEY_CTRL_BASE + '4',
53 KEY_CTRL_BASE + '5',
54 KEY_CTRL_BASE + '6',
55 KEY_CTRL_BASE + '7',
56 KEY_CTRL_BASE + '8',
57 KEY_CTRL_BASE + '9',
58 KEY_NUMPAD_BASE + '0',
59 KEY_NUMPAD_BASE + '1',
60 KEY_NUMPAD_BASE + '2',
61 KEY_NUMPAD_BASE + '3',
62 KEY_NUMPAD_BASE + '4',
63 KEY_NUMPAD_BASE + '5',
64 KEY_NUMPAD_BASE + '6',
65 KEY_NUMPAD_BASE + '7',
66 KEY_NUMPAD_BASE + '8',
67 KEY_NUMPAD_BASE + '9',
68 KEY_NUMPAD_BASE + '+',
69 KEY_NUMPAD_BASE + '-',
70 KEY_NUMPAD_BASE + '*',
71 KEY_NUMPAD_BASE + '/',
72 KEY_NUMPAD_BASE + '.',
73 // all isprint()
74 '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+',
75 '`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=',
76 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '|',
77 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\',
78 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"',
79 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'',
80 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
81 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
82 };
83
random256()84 static int random256()
85 {
86 return rand() % 256;
87 }
88
commit_string(ChewingContext * ctx)89 void commit_string(ChewingContext *ctx)
90 {
91 char *s;
92
93 if (chewing_commit_Check(ctx)) {
94 s = chewing_commit_String(ctx);
95 free(s);
96 }
97 }
98
get_stdin()99 int get_stdin()
100 {
101 unsigned char c;
102 int len = read(0, &c, 1);
103 if (len <= 0)
104 return EOF;
105 return c;
106 }
107
main(int argc,char * argv[])108 int main(int argc, char *argv[])
109 {
110 int i;
111 int flag_random_init = 0;
112 int flag_random_extra = 0;
113 int flag_loop = -1;
114 int (*get_input)() = &random256;
115
116 for (i = 1; i < argc; i++) {
117 if (strcmp(argv[i], "-init") == 0)
118 flag_random_init = 1;
119 else if (strcmp(argv[i], "-extra") == 0 && argv[i + 1])
120 flag_random_extra = 1;
121 else if (strcmp(argv[i], "-loop") == 0 && argv[i + 1])
122 flag_loop = atoi(argv[++i]);
123 else if (strcmp(argv[i], "-stdin") == 0)
124 get_input = &get_stdin;
125 else {
126 printf("Usage: %s [-init] [-extra] [-loop N] [-stdin]\n", argv[0]);
127 printf("\t-init Random initial configuration\n");
128 printf("\t-extra Random change all configurations during input.\n");
129 printf("\t This is usually unexpected.\n");
130 printf("\t-stdin Get random input from stdin\n");
131 printf("\t-loop N How many iterations to test (default infinite=-1)\n");
132 exit(1);
133 }
134 }
135
136 /* Initialize libchewing */
137 putenv("CHEWING_PATH=" CHEWING_DATA_PREFIX);
138 /* for the sake of testing, we should not change existing hash data */
139 putenv("CHEWING_USER_PATH=" TEST_HASH_DIR);
140
141 for (i = 0; i != flag_loop; i++) {
142 ChewingContext *ctx = chewing_new();
143
144 /* typical configuration */
145 chewing_set_KBType(ctx, chewing_KBStr2Num("KB_DEFAULT"));
146 chewing_set_candPerPage(ctx, 9);
147 chewing_set_maxChiSymbolLen(ctx, 16);
148 chewing_set_addPhraseDirection(ctx, 1);
149 chewing_set_selKey(ctx, selKey_define, 10);
150 chewing_set_spaceAsSelection(ctx, 1);
151
152 if (flag_random_init) {
153 chewing_set_KBType(ctx, get_input());
154 chewing_set_candPerPage(ctx, get_input());
155 chewing_set_maxChiSymbolLen(ctx, get_input());
156 chewing_set_addPhraseDirection(ctx, get_input());
157 chewing_set_selKey(ctx, selKey_define, get_input() % 11);
158 chewing_set_spaceAsSelection(ctx, get_input());
159 chewing_set_escCleanAllBuf(ctx, get_input());
160 chewing_set_autoShiftCur(ctx, get_input());
161 chewing_set_easySymbolInput(ctx, get_input());
162 chewing_set_phraseChoiceRearward(ctx, get_input());
163 }
164
165 while (1) {
166 /* Random value: [0, max_key) for keys, [max_key, 0xff] for
167 * configurations. Use a fixed range here because I don't want the
168 * meaning of input changed a lot frequently if we add more keys in
169 * the future. */
170 const int max_key = 192; /* arbitrary number */
171 int v = get_input();
172 if (v == EOF)
173 break;
174 assert(max_key * sizeof(all_key[0]) >= sizeof(all_key));
175 if (v >= max_key) {
176 const int typical = 2;
177 int handled = 1;
178 v = v - max_key;
179 if (flag_random_extra || v < typical) {
180 switch (v) {
181 /* typical configurations may be changed during input */
182 case 0:
183 chewing_set_ChiEngMode(ctx, get_input());
184 break;
185 case 1:
186 chewing_set_ShapeMode(ctx, get_input());
187 break;
188
189 /* usually not changed during input */
190 case 2:
191 chewing_set_KBType(ctx, get_input());
192 break;
193 case 3:
194 chewing_set_candPerPage(ctx, get_input());
195 break;
196 case 4:
197 chewing_set_maxChiSymbolLen(ctx, get_input());
198 break;
199 case 5:
200 chewing_set_addPhraseDirection(ctx, get_input());
201 break;
202 case 6:
203 chewing_set_selKey(ctx, selKey_define, get_input() % 11);
204 break;
205 case 7:
206 chewing_set_spaceAsSelection(ctx, get_input());
207 break;
208 case 8:
209 chewing_set_escCleanAllBuf(ctx, get_input());
210 break;
211 case 9:
212 chewing_set_autoShiftCur(ctx, get_input());
213 break;
214 case 10:
215 chewing_set_easySymbolInput(ctx, get_input());
216 break;
217 case 11:
218 chewing_set_phraseChoiceRearward(ctx, get_input());
219 break;
220 default:
221 handled = 0;
222 break;
223 }
224 } else {
225 handled = 0;
226 }
227 if (!handled)
228 break;
229 } else {
230 if (0 <= v && v < max_key) {
231 int key = all_key[v];
232 type_single_keystroke(ctx, key);
233 } else {
234 break;
235 }
236 }
237 commit_string(ctx);
238 }
239 chewing_delete(ctx);
240
241 if (i % 10000 == 0)
242 printf("%d\n", i);
243
244 #if !defined(_WIN32) && !defined(_WIN64) && !defined(_WIN32_WCE)
245 if (getenv("AFL_PERSISTENT"))
246 raise(SIGSTOP);
247 #endif
248 }
249
250 return 0;
251 }
252