1 /*
2 * epos/src/tests/testbench.cc
3 * (c) 1998-2005 Jirka Hanika <geo@cuni.cz>
4 *
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License in doc/COPYING for more details.
14
15 * This file implements a framework for single purpose TTSCP clients. See
16 * doc/english/ttscp.sgml for a preliminary technical specification.
17 * The Epos developers may use these clients to test a particular
18 * TTSCP implementation on a UNIX platform; using them for other
19 * purposes is discouraged: this client may deliberately violate
20 * the TTSCP specification in order to test the error recovery
21 * procedures of Epos or another TTSCP server.
22 */
23
24 #include "testbench.h"
25
26 #define TTSCP_PORT 8789
27
28 const char *COMMENT_LINES = "#;\n\r";
29 const char *WHITESPACE = " \t\r";
30
31 // const char *output_file = "/dev/dsp";
32
33 int ctrld[10];
34 int datad[10]; /* file descriptors for the control and data connections */
35 char *data = NULL;
36
37 //#define SCRATCH_SPACE 16384
38 //char scratch[SCRATCH_SPACE + 2];
39
40 #define CONNECTION_PAIRS 10
41 #define UNUSED_PAIR 9
42
43 char *chandle[CONNECTION_PAIRS];
44 char *dhandle[CONNECTION_PAIRS];
45 bool used[CONNECTION_PAIRS];
46
47 const char *testname = "";
48
get_data_handle(int which)49 const char* get_data_handle(int which)
50 {
51 if (which < 0 || which >= CONNECTION_PAIRS)
52 shriek("Invalid data handle index");
53 return dhandle[which];
54 }
55
56
57
58 int connect_socket(unsigned int, int);
59 static void stops();
60
shriek(char * txt)61 void shriek(char *txt)
62 {
63 if (!*testname) {
64 fprintf(stderr, "Client side error: %s\n", txt);
65 exit(1);
66 }
67 fprintf(stderr, "Failed test %s test\n", testname);
68 fprintf(stderr, "Reason: %s\n", txt);
69 perror("Last error");
70
71 sleep(1);
72 testname = "";
73 connect_socket(0, TTSCP_PORT);
74 stops();
75 exit(1);
76 }
77
shriek(int,char * txt)78 void shriek(int, char *txt)
79 {
80 shriek(txt);
81 }
82
83
84 #define EPOS_COMMON_H // this is a lie
85 #include "client.cc"
86 //#define sputs(x,y) {printf("Sent to %d: %s\n", y, x); sputs(x,y);}
87
get_result(int c)88 int get_result(int c)
89 {
90 char *mess;
91
92 while (sgets(scratch, scfg->scratch_size, ctrld[c])) {
93 scratch[scfg->scratch_size] = 0;
94 // printf("[%-20s] Received on %d: %s\n", testname, c, scratch);
95 mess = scratch+strspn(scratch, "0123456789x ");
96 switch(*scratch) {
97 case '1': continue;
98 case '2': return 2;
99 case '3': break;
100 case '4': if (*mess && strcmp(mess, "interrupted")) printf("%s\n", mess);
101 return 4;
102 case '6': if (!strncmp(scratch, "600 ", 4)) {
103 // exit(0);
104 return 2;
105 } /* else fall through */
106 case '8': if (*mess) printf("%s\n", mess);
107 follow_server_down(false); // never returns
108 case '5':
109 case '7':
110 case '9':
111 case '0': printf("%s\n", scratch); shriek("Unhandled response code");
112 default : ;
113 }
114 if (*mess) printf("%s\n", mess);
115 }
116 return 8; /* guessing */
117 }
118
generic_command(int c,char * cmd)119 void generic_command(int c, char *cmd)
120 {
121 if (c < 0 || c > CONNECTION_PAIRS)
122 shriek("Invalid index");
123 sputs(cmd, ctrld[c]);
124 sputs("\r\n", ctrld[c]);
125 }
126
generic_appl(int c,int d,const char * data,int data_len)127 void generic_appl(int c, int d, const char *data, int data_len)
128 {
129 sputs("appl ", ctrld[c]);
130 sprintf(scratch, "%d", data_len);
131 sputs(scratch, ctrld[c]);
132 sputs("\r\n", ctrld[c]);
133 ywrite(datad[d], data, data_len);
134 }
135
spk_strm(int c,int d)136 void spk_strm(int c, int d)
137 {
138 sputs("strm $", ctrld[c]);
139 sputs(dhandle[d], ctrld[c]);
140 sputs(":raw:rules:diphs:synth:#localsound", ctrld[c]);
141 sputs("\r\n", ctrld[c]);
142 }
143
spk_appl(int c,int d,const char * data,int data_len)144 void spk_appl(int c, int d, const char *data, int data_len)
145 {
146 generic_appl(c, d, data, data_len);
147 }
148
spk_appl(int c,int d,const char * data)149 void spk_appl(int c, int d, const char *data)
150 {
151 spk_appl(c, d, data, (int)strlen(data));
152 }
153
spk_intr(int c,int broken)154 void spk_intr(int c, int broken)
155 {
156 sputs("intr ", ctrld[c]);
157 sputs(chandle[broken], ctrld[c]);
158 sputs("\r\n", ctrld[c]);
159 }
160
161
xscr_strm(int c,int d)162 char *xscr_strm(int c, int d)
163 {
164 int s = ctrld[c];
165 sputs("strm $", s);
166 sputs(dhandle[d], s);
167 sputs(":raw:rules:print:$", s);
168 sputs(dhandle[d], s);
169 sputs("\r\n", s);
170 if (get_result(c) > 2) shriek("Could not set up the stream");
171
172 }
173
get_data(int c,int d)174 char *get_data(int c, int d)
175 {
176 char *b = NULL;
177 int size = 0;
178 while (sgets(scratch, scfg->scratch_size, ctrld[c])) {
179 scratch[scfg->scratch_size] = 0;
180 if (strchr("2468", *scratch)) { /* all done, write result */
181 if (*scratch != '2') shriek(scratch);
182 if (!size) shriek("No processed data received");
183 b[size] = 0;
184 return b;
185 }
186 if (!strncmp(scratch, "123 ", 4)) {
187 int count;
188 sgets(scratch, scfg->scratch_size, ctrld[c]);
189 scratch[scfg->scratch_size] = 0;
190 sscanf(scratch, "%d", &count);
191 b = size ? (char *)realloc(b, size + count + 1) : (char *)malloc(count + 1);
192 int limit = size + count;
193 while (size < limit)
194 size += yread(datad[d], b + size, limit - size);
195 }
196 }
197 if (size) shriek("Disconnect during transmit");
198 else shriek("Disconnect before transmit");
199 return NULL;
200 }
201
202
xscr_appl(int c,int d,const char * data,int data_len)203 char *xscr_appl(int c, int d, const char *data, int data_len)
204 {
205 generic_appl(c, d, data, data_len);
206 return get_data(c, d);
207 }
208
xscr_appl(int c,int d,const char * data)209 char *xscr_appl(int c, int d, const char *data)
210 {
211 return xscr_appl(c, d, data, (int)strlen(data));
212 }
213
214
215
much_data()216 char *much_data()
217 {
218 char *buffer = (char *)malloc(MUCH_SPACE + 1024);
219 return buffer;
220 }
221
setl(int c,const char * name,const char * value)222 void setl(int c, const char *name, const char *value)
223 {
224 xmit_option(name, value, ctrld[c]);
225 }
226
just_connect_socket()227 int just_connect_socket()
228 {
229 return just_connect_socket(0, TTSCP_PORT);
230 }
231
send_to_epos(char * what,int socket)232 void send_to_epos(char *what, int socket)
233 {
234 sputs(what, socket);
235 }
236
237
perform_test()238 void perform_test()
239 {
240 testname = test_name;
241 test_body();
242 testname = "";
243 }
244
245 #define strfy(x) #x
246 #define stringify(x) strfy(x)
247
248 char * const exec_argv[] = {
249 "eposd",
250 "--forking=off",
251 "--listen_port=" stringify(TTSCP_PORT),
252 "--debug_password=make_check",
253 "--base_dir=" stringify(SOURCEDIR) "/../../cfg",
254 "--language=czech",
255 "--voice=kubec-vq",
256 NULL
257 };
258 char * const exec_envp[] = {
259 NULL
260 };
261
262 int eposd_pid = 0;
263 int patience = 40;
264
init_winsock()265 void init_winsock()
266 {
267 #if defined(HAVE_WINSOCK_H) || defined(HAVE_WINSOCK2_H)
268 if (WSAStartup(MAKEWORD(2,0), (LPWSADATA)scratch)) shriek(464, "No winsock");
269 #endif
270 }
271
init_server()272 void init_server()
273 {
274 if (just_connect_socket() == -1) {
275 if ((eposd_pid = fork())) do usleep(250000); while (just_connect_socket() == -1 && --patience);
276 else execve("../eposd", exec_argv, exec_envp);
277 }
278 }
279
init_connection_pair(int i)280 void init_connection_pair(int i)
281 {
282 ctrld[i] = connect_socket(0, TTSCP_PORT); // This will be the data connection...
283 dhandle[i] = get_handle(ctrld[i]);
284 sputs("data ", ctrld[i]);
285 sputs(chandle[UNUSED_PAIR], ctrld[i]);
286 sputs("\r\n", ctrld[i]);
287 if (get_result(i) > 2) shriek("Couldn't data");
288 datad[i] = ctrld[i]; // ...no sooner, as get_result() doesn't work on data conns
289 ctrld[i] = connect_socket(0, TTSCP_PORT); // This will be the control connection.
290 chandle[i] = get_handle(ctrld[i]);
291 used[i] = true;
292 }
293
init()294 void init()
295 {
296 init_winsock();
297 init_server();
298
299 ctrld[UNUSED_PAIR] = connect_socket(0, TTSCP_PORT);
300 chandle[UNUSED_PAIR] = get_handle(ctrld[UNUSED_PAIR]);
301
302 for (int i = 0; i < CONNECTION_PAIRS; i++) {
303 used[i] = false;
304 }
305 init_connection_pair(0);
306 }
307
cleanup()308 void cleanup()
309 {
310 testname = "closing everything";
311
312 for (int i = 0; i < UNUSED_PAIR; i++) {
313 if (used[i]) {
314 sputs("delh ", ctrld[i]);
315 sputs(dhandle[i], ctrld[i]);
316 sputs("\r\ndone\r\n", ctrld[i]);
317 if (get_result(i) > 2) shriek("Could not delete a data connection handle");
318 if (get_result(i) > 2) shriek("Could not shut down a control connection");
319 close(datad[i]);
320 close(ctrld[i]);
321 }
322 }
323 close(ctrld[UNUSED_PAIR]);
324 }
325
326 #ifdef HAVE_GETTIMEOFDAY
327
328 #ifdef HAVE_SYS_TIME_H
329 #include <sys/time.h>
330 #endif
331
332 static struct timeval start, stop;
starts()333 static void starts()
334 {
335 if (gettimeofday(&start, NULL)) shriek("profiler fails");
336 }
337
stops()338 static void stops()
339 {
340 if (gettimeofday(&stop, NULL)) shriek("profiler fails");
341 int duration = stop.tv_sec - start.tv_sec;
342 duration *= 1000000;
343 duration += stop.tv_usec - start.tv_usec;
344 printf("%8ldms ", duration / 1000);
345 }
346
347 #else // HAVE_GETTIMEOFDAY
348
starts()349 static void starts() {};
stops()350 static void stops() {};
351
352 #endif // HAVE_GETTIMEOFDAY
353
354
355
main(int argc,char ** argv)356 int main(int argc, char **argv)
357 {
358 if (argc != 1) shriek("No arguments allowed");
359
360 starts();
361
362 init();
363 perform_test();
364 cleanup();
365
366 stops();
367
368 return 0;
369 }
370
testbench_exit(bool success=true)371 void testbench_exit(bool success = true)
372 {
373 stops();
374 exit(success ? 0 : 3);
375 }
376
follow_server_down(bool success)377 void follow_server_down(bool success)
378 {
379 int s;
380 do {
381 s = just_connect_socket();
382 if (s == -1) {
383 usleep(30 * 1000);
384 testbench_exit(success);
385 }
386 close(s);
387 usleep(100 * 1000);
388 } while (1);
389 }
390
391
392
393
394 #ifndef HAVE_TERMINATE
395
terminate(void)396 void terminate(void)
397 {
398 abort();
399 }
400
401 #endif
402
403