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