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