summaryrefslogtreecommitdiff
path: root/unsorted/ecore/efl_net_dialer_websocket_autobahntestee.c
diff options
context:
space:
mode:
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>2016-08-30 00:28:00 -0300
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>2016-09-02 00:08:50 -0300
commit394b9411ce3fbbbf3eb241315158fe2d4c07fe64 (patch)
tree0023e866eaccca200f6fc20c5424224bc9edf021 /unsorted/ecore/efl_net_dialer_websocket_autobahntestee.c
parente6f6b94ccc8babafefdca19ce08ce8bc0f6fe678 (diff)
efl_net_dialer_websocket: EFL now does WebSocket!
The Efl.Net.Dialer.Websocket is just like other Efl.Net.Dialers: you can dial, you can close, monitor connected/address resolved and so on. And you can use WebSocket primitives and events such as text_send(), binary_send(), ping() and close_request() (since WebSockets use a close process where you should state a close reason). See efl_net_dialer_websocket_example.c Even if WebSocket is a message-based protocol (like "packets" from UDP), you can use efl_net_dialer_websocket_streaming_mode_set() to tell it to handle text or binary messages as a stream. Then all the Efl.Io.Reader and Efl.Io.Writer APIs work as expected, see efl_io_copier_example.c updates.
Diffstat (limited to 'unsorted/ecore/efl_net_dialer_websocket_autobahntestee.c')
-rw-r--r--unsorted/ecore/efl_net_dialer_websocket_autobahntestee.c693
1 files changed, 693 insertions, 0 deletions
diff --git a/unsorted/ecore/efl_net_dialer_websocket_autobahntestee.c b/unsorted/ecore/efl_net_dialer_websocket_autobahntestee.c
new file mode 100644
index 0000000..92595b7
--- /dev/null
+++ b/unsorted/ecore/efl_net_dialer_websocket_autobahntestee.c
@@ -0,0 +1,693 @@
1#define EFL_BETA_API_SUPPORT 1
2#define EFL_EO_API_SUPPORT 1
3#include <Ecore.h>
4#include <Ecore_Con.h>
5#include <Ecore_Getopt.h>
6#include <fcntl.h>
7#include <ctype.h>
8
9static int retval = EXIT_SUCCESS;
10static char *address = NULL;
11static char *agent = "efl_net_dialer_websocket";
12static unsigned int start_index = 0;
13static unsigned int end_index = UINT32_MAX;
14static unsigned int current_index = 0;
15static Eina_Bool no_report_update = EINA_FALSE;
16static Eina_List *case_tuples = NULL;
17static Eina_Bool verbose = 0;
18
19static Eo *pending = NULL;
20
21/* https://www.w3.org/International/questions/qa-forms-utf-8 */
22static Eina_Bool
23_utf8_check(const char *text)
24{
25 const unsigned char * bytes = (const unsigned char *)text;
26 while (*bytes)
27 {
28 const unsigned char c = bytes[0];
29
30 /* ascii: [\x09\x0A\x0D\x20-\x7E] */
31 if (((c >= 0x20) && (c <= 0x7e)) ||
32 (c == 0x09) || (c == 0x0a) || (c == 0x0d))
33 {
34 bytes += 1;
35 continue;
36 }
37
38 /* autobahnsuite says 0x7f is valid */
39 if (c == 0x7f)
40 {
41 bytes += 1;
42 continue;
43 }
44
45#define VALUE_BYTE_CHECK(x) ((x >= 0x80) && (x <= 0xbf))
46
47 /* non-overlong 2-byte: [\xC2-\xDF][\x80-\xBF] */
48 if ((c >= 0xc2) && (c <= 0xdf))
49 {
50 if (VALUE_BYTE_CHECK(bytes[1]))
51 {
52 bytes += 2;
53 continue;
54 }
55 }
56
57 /* excluding overlongs: \xE0[\xA0-\xBF][\x80-\xBF] */
58 if (c == 0xe0)
59 {
60 const unsigned char d = bytes[1];
61 if ((d >= 0xa0) && (d <= 0xbf))
62 {
63 if (VALUE_BYTE_CHECK(bytes[2]))
64 {
65 bytes += 3;
66 continue;
67 }
68 }
69 }
70
71 /* straight 3-byte: [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} */
72 if (((c >= 0xe1) && (c <= 0xec)) ||
73 (c == 0xee) || (c == 0xef))
74 {
75 if (VALUE_BYTE_CHECK(bytes[1]) && VALUE_BYTE_CHECK(bytes[2]))
76 {
77 bytes += 3;
78 continue;
79 }
80 }
81
82 /* excluding surrogates: \xED[\x80-\x9F][\x80-\xBF] */
83 if (c == 0xed)
84 {
85 const unsigned char d = bytes[1];
86 if ((d >= 0x80) && (d <= 0x9f))
87 {
88 if (VALUE_BYTE_CHECK(bytes[2]))
89 {
90 bytes += 3;
91 continue;
92 }
93 }
94 }
95
96 /* planes 1-3: \xF0[\x90-\xBF][\x80-\xBF]{2} */
97 if (c == 0xf0)
98 {
99 const unsigned char d = bytes[1];
100 if ((d >= 0x90) && (d <= 0xbf))
101 {
102 if (VALUE_BYTE_CHECK(bytes[2]) && VALUE_BYTE_CHECK(bytes[3]))
103 {
104 bytes += 4;
105 continue;
106 }
107 }
108 }
109 /* planes 4-15: [\xF1-\xF3][\x80-\xBF]{3} */
110 if ((c >= 0xf1) && (c <= 0xf3))
111 {
112 if (VALUE_BYTE_CHECK(bytes[1]) && VALUE_BYTE_CHECK(bytes[2]) && VALUE_BYTE_CHECK(bytes[3]))
113 {
114 bytes += 4;
115 continue;
116 }
117 }
118
119 /* plane 16: \xF4[\x80-\x8F][\x80-\xBF]{2} */
120 if (c == 0xf4)
121 {
122 const unsigned char d = bytes[1];
123 if ((d >= 0x80) && (d <= 0x8f))
124 {
125 if (VALUE_BYTE_CHECK(bytes[2]) && VALUE_BYTE_CHECK(bytes[3]))
126 {
127 bytes += 4;
128 continue;
129 }
130 }
131 }
132
133 if (verbose) fprintf(stderr, "INFO: failed unicode byte #%zd '%s'\n", (const char*)bytes - text, text);
134 return EINA_FALSE;
135 }
136
137 return EINA_TRUE;
138}
139
140static void
141_ws_pong(void *data EINA_UNUSED, const Efl_Event *event)
142{
143 Eo *dialer = event->object;
144 const char *text = event->info;
145 if (!verbose) return;
146 fprintf(stderr, "INFO: %s got PONG: %s\n",
147 efl_name_get(dialer), text);
148}
149
150static void
151_ws_closed_reason(void *data EINA_UNUSED, const Efl_Event *event)
152{
153 Eo *dialer = event->object;
154 Efl_Net_Dialer_Websocket_Closed_Reason *reason = event->info;
155
156 if (!_utf8_check(reason->message))
157 {
158 efl_net_dialer_websocket_close_request(dialer, EFL_NET_DIALER_WEBSOCKET_CLOSE_REASON_PROTOCOL_ERROR, "invalid UTF-8");
159 if (verbose) fprintf(stderr, "INFO: %s got CLOSE with invalid UTF-8\n", efl_name_get(dialer));
160 }
161
162 if (!verbose) return;
163 fprintf(stderr, "INFO: %s got CLOSE: %4d '%s'\n",
164 efl_name_get(dialer), reason->reason, reason->message);
165}
166
167static void
168_ws_message_text(void *data EINA_UNUSED, const Efl_Event *event)
169{
170 Eo *dialer = event->object;
171 const char *text = event->info;
172
173 if (!verbose) return;
174 fprintf(stderr, "INFO: %s got TEXT %zd bytes:\n%s\n",
175 efl_name_get(dialer), strlen(text), text);
176}
177
178static void
179_ws_message_binary(void *data EINA_UNUSED, const Efl_Event *event)
180{
181 Eo *dialer = event->object;
182 const Eina_Slice *slice = event->info;
183 size_t i;
184
185 if (!verbose) return;
186 fprintf(stderr, "INFO: %s got BINARY %zd bytes\n",
187 efl_name_get(dialer), slice->len);
188
189 for (i = 0; i < slice->len; i++)
190 {
191 const int c = slice->bytes[i];
192 if (isprint(c))
193 fprintf(stderr, " %#4x(%c)", c, c);
194 else
195 fprintf(stderr, " %#4x", c);
196 }
197
198 fprintf(stderr, "\n");
199}
200
201static void
202_closed(void *data EINA_UNUSED, const Efl_Event *event)
203{
204 Eo *dialer = event->object;
205
206 if (!verbose) return;
207 fprintf(stderr, "INFO: %s closed\n", efl_name_get(dialer));
208}
209
210static void
211_eos(void *data EINA_UNUSED, const Efl_Event *event)
212{
213 Eo *dialer = event->object;
214
215 if (!verbose) return;
216 fprintf(stderr, "INFO: %s eos\n", efl_name_get(dialer));
217}
218
219static void
220_connected(void *data EINA_UNUSED, const Efl_Event *event)
221{
222 Eo *dialer = event->object;
223
224 if (!verbose) return;
225 fprintf(stderr, "INFO: %s connected %s\n",
226 efl_name_get(dialer),
227 efl_net_dialer_address_dial_get(dialer));
228}
229
230static void
231_error(void *data EINA_UNUSED, const Efl_Event *event)
232{
233 Eo *dialer = event->object;
234 const Eina_Error *perr = event->info;
235 fprintf(stderr, "ERROR: %s error: %d '%s'\n",
236 efl_name_get(dialer), *perr, eina_error_msg_get(*perr));
237 retval = EXIT_FAILURE;
238}
239
240static void
241_del(void *data EINA_UNUSED, const Efl_Event *event)
242{
243 Eo *dialer = event->object;
244 if (pending == dialer)
245 pending = NULL;
246}
247
248EFL_CALLBACKS_ARRAY_DEFINE(dialer_cbs,
249 { EFL_NET_DIALER_WEBSOCKET_EVENT_PONG, _ws_pong },
250 { EFL_NET_DIALER_WEBSOCKET_EVENT_CLOSED_REASON, _ws_closed_reason },
251 { EFL_NET_DIALER_WEBSOCKET_EVENT_MESSAGE_TEXT, _ws_message_text },
252 { EFL_NET_DIALER_WEBSOCKET_EVENT_MESSAGE_BINARY, _ws_message_binary },
253 { EFL_NET_DIALER_EVENT_CONNECTED, _connected },
254 { EFL_NET_DIALER_EVENT_ERROR, _error },
255 { EFL_IO_CLOSER_EVENT_CLOSED, _closed },
256 { EFL_IO_READER_EVENT_EOS, _eos },
257 { EFL_EVENT_DEL, _del });
258
259static Eo *
260_websocket_new(const char *name)
261{
262 Eo *dialer;
263
264 dialer = efl_add(EFL_NET_DIALER_WEBSOCKET_CLASS, ecore_main_loop_get(),
265 efl_name_set(efl_self, name),
266 efl_event_callback_array_add(efl_self, dialer_cbs(), NULL));
267 if (!dialer)
268 {
269 retval = EXIT_FAILURE;
270 fprintf(stderr, "ERROR: could not create WebSockets dialer '%s'\n", name);
271 return NULL;
272 }
273
274 pending = dialer;
275
276 return dialer;
277}
278
279static void
280_closed_quit(void *data EINA_UNUSED, const Efl_Event *event)
281{
282 Eo *dialer = event->object;
283 efl_del(dialer);
284 ecore_main_loop_quit();
285}
286
287static void
288_tests_finished(void)
289{
290 Eo *dialer;
291 char url[4096];
292 int len;
293 Eina_Error err;
294
295 if (no_report_update)
296 {
297 if (verbose)
298 fprintf(stderr, "INFO: tests finished, user required to not update the reports\n");
299 ecore_main_loop_quit();
300 return;
301 }
302
303 case_tuples = eina_list_remove(case_tuples, case_tuples);
304 len = snprintf(url, sizeof(url), "%s/updateReports?agent=%s",
305 address, agent);
306 if (len < 0)
307 {
308 fprintf(stderr, "ERROR: could not create URL "
309 "'%s/updateReports?agent=%s': %s",
310 address, agent, strerror(errno));
311 ecore_main_loop_quit();
312 return;
313 }
314 else if ((size_t)len > sizeof(url))
315 {
316 fprintf(stderr, "ERROR: could not create URL "
317 "'%s/updateReports?agent=%s': no space.",
318 address, agent);
319 ecore_main_loop_quit();
320 return;
321 }
322
323 dialer = _websocket_new("update-reports");
324 if (!dialer)
325 {
326 ecore_main_loop_quit();
327 return;
328 }
329
330 efl_event_callback_add(dialer, EFL_IO_CLOSER_EVENT_CLOSED, _closed_quit, NULL);
331
332 err = efl_net_dialer_dial(dialer, url);
333 if (err != 0)
334 {
335 retval = EXIT_FAILURE;
336 fprintf(stderr, "ERROR: could not dial '%s': %s",
337 url, eina_error_msg_get(err));
338 efl_del(dialer);
339 ecore_main_loop_quit();
340 return;
341 }
342
343 if (!verbose) return;
344 fprintf(stderr, "INFO: %s '%s'\n",
345 efl_name_get(dialer), efl_net_dialer_address_dial_get(dialer));
346}
347
348static void
349_echo_text(void *data EINA_UNUSED, const Efl_Event *event)
350{
351 Eo *dialer = event->object;
352 const char *text = event->info;
353
354 if (!_utf8_check(text))
355 {
356 if (verbose) fprintf(stderr, "INFO: invalid UTF-8 sequence '%s'. Close the connection.\n", text);
357 efl_net_dialer_websocket_close_request(dialer, EFL_NET_DIALER_WEBSOCKET_CLOSE_REASON_INCONSISTENT_DATA, "invalid UTF-8");
358 return;
359 }
360
361 efl_net_dialer_websocket_text_send(dialer, text);
362}
363
364static void
365_echo_binary(void *data EINA_UNUSED, const Efl_Event *event)
366{
367 Eo *dialer = event->object;
368 const Eina_Slice *slice = event->info;
369 efl_net_dialer_websocket_binary_send(dialer, *slice);
370}
371
372static Eina_Bool _websocket_test_next_case_tuple(void);
373
374static void
375_test_next_case_closed(void *data EINA_UNUSED, const Efl_Event *event)
376{
377 Eo *dialer = event->object;
378 efl_del(dialer);
379
380 if (!_websocket_test_next_case_tuple())
381 _tests_finished();
382}
383
384EFL_CALLBACKS_ARRAY_DEFINE(_test_next_case_tuple_cbs,
385 { EFL_NET_DIALER_WEBSOCKET_EVENT_MESSAGE_TEXT, _echo_text },
386 { EFL_NET_DIALER_WEBSOCKET_EVENT_MESSAGE_BINARY, _echo_binary },
387 { EFL_IO_CLOSER_EVENT_CLOSED, _test_next_case_closed });
388
389static Eina_Bool
390_websocket_test_next_case_tuple(void)
391{
392 Eo *dialer;
393 char url[4096];
394 char name[256];
395 char *str;
396 int len;
397 Eina_Error err;
398
399 if (!case_tuples)
400 return EINA_FALSE;
401
402 str = case_tuples->data;
403 case_tuples = eina_list_remove_list(case_tuples, case_tuples);
404 len = snprintf(url, sizeof(url), "%s/runCase?casetuple=%s&agent=%s",
405 address, str, agent);
406 if (len < 0)
407 {
408 fprintf(stderr, "ERROR: could not create URL "
409 "'%s/runCase?casetuple=%s&agent=%s': %s",
410 address, str, agent, strerror(errno));
411 free(str);
412 return EINA_FALSE;
413 }
414 else if ((size_t)len > sizeof(url))
415 {
416 fprintf(stderr, "ERROR: could not create URL "
417 "'%s/runCase?casetuple=%s&agent=%s': no space.",
418 address, str, agent);
419 free(str);
420 return EINA_FALSE;
421 }
422
423 snprintf(name, sizeof(name), "test_case=%s", str);
424 free(str);
425
426 dialer = _websocket_new(name);
427 if (!dialer) return EINA_FALSE;
428
429 efl_event_callback_array_add(dialer, _test_next_case_tuple_cbs(), NULL);
430
431 err = efl_net_dialer_dial(dialer, url);
432 if (err != 0)
433 {
434 retval = EXIT_FAILURE;
435 fprintf(stderr, "ERROR: could not dial '%s': %s",
436 url, eina_error_msg_get(err));
437 efl_del(dialer);
438 return EINA_FALSE;
439 }
440
441 fprintf(stderr, "TEST: %s '%s'\n", efl_name_get(dialer), efl_net_dialer_address_dial_get(dialer));
442
443 return EINA_TRUE;
444}
445
446static Eina_Bool _websocket_test_index(unsigned int idx);
447
448static void
449_test_index_closed(void *data EINA_UNUSED, const Efl_Event *event)
450{
451 Eo *dialer = event->object;
452 efl_del(dialer);
453
454 if (!_websocket_test_index(current_index + 1))
455 _tests_finished();
456}
457
458EFL_CALLBACKS_ARRAY_DEFINE(_test_index_cbs,
459 { EFL_NET_DIALER_WEBSOCKET_EVENT_MESSAGE_TEXT, _echo_text },
460 { EFL_NET_DIALER_WEBSOCKET_EVENT_MESSAGE_BINARY, _echo_binary },
461 { EFL_IO_CLOSER_EVENT_CLOSED, _test_index_closed });
462
463static Eina_Bool
464_websocket_test_index(unsigned int idx)
465{
466 Eo *dialer;
467 char url[4096];
468 char name[64];
469 int len;
470 Eina_Error err;
471
472 if (idx > end_index)
473 return EINA_FALSE;
474
475 len = snprintf(url, sizeof(url), "%s/runCase?case=%u&agent=%s",
476 address, idx, agent);
477 if (len < 0)
478 {
479 fprintf(stderr, "ERROR: could not create URL "
480 "'%s/runCase?case=%u&agent=%s': %s",
481 address, idx, agent, strerror(errno));
482 return EINA_FALSE;
483 }
484 else if ((size_t)len > sizeof(url))
485 {
486 fprintf(stderr, "ERROR: could not create URL "
487 "'%s/runCase?case=%u&agent=%s': no space.",
488 address, idx, agent);
489 return EINA_FALSE;
490 }
491
492 snprintf(name, sizeof(name), "test_case=%u", idx);
493
494 dialer = _websocket_new(name);
495 if (!dialer) return EINA_FALSE;
496
497 efl_event_callback_array_add(dialer, _test_index_cbs(), NULL);
498
499 err = efl_net_dialer_dial(dialer, url);
500 if (err != 0)
501 {
502 retval = EXIT_FAILURE;
503 fprintf(stderr, "ERROR: could not dial '%s': %s",
504 url, eina_error_msg_get(err));
505 efl_del(dialer);
506 return EINA_FALSE;
507 }
508
509 current_index = idx;
510
511 fprintf(stderr, "TEST: %s '%s'\n", efl_name_get(dialer), efl_net_dialer_address_dial_get(dialer));
512
513 return EINA_TRUE;
514}
515
516static void
517_load_tests_text(void *data EINA_UNUSED, const Efl_Event *event)
518{
519 const char *text = event->info;
520 unsigned int n = strtoul(text, NULL, 10);
521
522 if (start_index == 0)
523 start_index = 1;
524 else if (start_index > n)
525 start_index = n;
526
527 if (end_index == 0 || end_index > n)
528 end_index = n;
529
530 if (!verbose) return;
531 fprintf(stderr, "INFO: test count: %u, start_index=%u, end_index=%u\n",
532 n, start_index, end_index);
533}
534
535static void
536_load_tests_closed(void *data EINA_UNUSED, const Efl_Event *event)
537{
538 Eo *dialer = event->object;
539 efl_del(dialer);
540
541 if (!_websocket_test_index(start_index))
542 _tests_finished();
543}
544
545EFL_CALLBACKS_ARRAY_DEFINE(_load_tests_cbs,
546 { EFL_NET_DIALER_WEBSOCKET_EVENT_MESSAGE_TEXT, _load_tests_text },
547 { EFL_IO_CLOSER_EVENT_CLOSED, _load_tests_closed });
548
549static Eina_Bool
550_websocket_load_tests(void)
551{
552 Eo *dialer;
553 char url[4096];
554 int len;
555 Eina_Error err;
556
557 len = snprintf(url, sizeof(url), "%s/getCaseCount", address);
558 if (len < 0)
559 {
560 fprintf(stderr, "ERROR: could not create URL '%s/getCaseCount': %s",
561 address, strerror(errno));
562 return EINA_FALSE;
563 }
564 else if ((size_t)len > sizeof(url))
565 {
566 fprintf(stderr, "ERROR: could not create URL '%s/getCaseCount': no space.",
567 address);
568 return EINA_FALSE;
569 }
570
571 dialer = _websocket_new("get-case-count");
572 if (!dialer) return EINA_FALSE;
573
574 efl_event_callback_array_add(dialer, _load_tests_cbs(), NULL);
575
576 err = efl_net_dialer_dial(dialer, url);
577 if (err != 0)
578 {
579 retval = EXIT_FAILURE;
580 fprintf(stderr, "ERROR: could not dial '%s': %s",
581 url, eina_error_msg_get(err));
582 efl_del(dialer);
583 return EINA_FALSE;
584 }
585
586 if (verbose) fprintf(stderr, "INFO: %s '%s'\n", efl_name_get(dialer), efl_net_dialer_address_dial_get(dialer));
587
588 return EINA_TRUE;
589}
590
591static const Ecore_Getopt options = {
592 "efl_net_dialer_websocket_autobahntestee", /* program name */
593 NULL, /* usage line */
594 "1", /* version */
595 "(C) 2016 Enlightenment Project", /* copyright */
596 "BSD 2-Clause", /* license */
597 /* long description, may be multiline and contain \n */
598 "Use Efl_Net_Dialer_Websocket to implement a testee client for the Autobahn Test Suite."
599 "\n"
600 "Autobahn Test Suite http://autobahn.ws/testsuite provides a fully automated test suite to verify client and server implementations of the WebSocket Protocol for specification conformance and implementation robustness."
601 "\n"
602 "This is a client to talk to their test server, that should be executed as:\n"
603 " wstest -m fuzzingserver\n"
604 "\n",
605 EINA_FALSE,
606 {
607 ECORE_GETOPT_STORE_UINT('s', "start-index", "when running batch, specifies the start (first) index"),
608 ECORE_GETOPT_STORE_UINT('e', "end-index", "when running batch, specifies the end (last) index"),
609 ECORE_GETOPT_STORE_TRUE('n', "no-report-update", "do not trigger autobahn to update report"),
610 ECORE_GETOPT_STORE_TRUE('v', "verbose", "print messages"),
611 ECORE_GETOPT_APPEND('t', "test-case", "the test-case tuple such as '1.2.8'", ECORE_GETOPT_TYPE_STR),
612 ECORE_GETOPT_STORE_STR('a', "agent", "the agent identifier"),
613 ECORE_GETOPT_VERSION('V', "version"),
614 ECORE_GETOPT_COPYRIGHT('C', "copyright"),
615 ECORE_GETOPT_LICENSE('L', "license"),
616 ECORE_GETOPT_HELP('h', "help"),
617 ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
618 "The address (URL) to dial, such as ws://127.0.0.1:9001", "address"),
619 ECORE_GETOPT_SENTINEL
620 }
621};
622
623int
624main(int argc, char **argv)
625{
626 Eina_Bool quit_option = EINA_FALSE;
627 Ecore_Getopt_Value values[] = {
628 ECORE_GETOPT_VALUE_UINT(start_index),
629 ECORE_GETOPT_VALUE_UINT(end_index),
630 ECORE_GETOPT_VALUE_BOOL(no_report_update),
631 ECORE_GETOPT_VALUE_BOOL(verbose),
632 ECORE_GETOPT_VALUE_LIST(case_tuples),
633 ECORE_GETOPT_VALUE_STR(agent),
634
635 /* standard block to provide version, copyright, license and help */
636 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
637 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */
638 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
639 ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
640
641 /* positional argument */
642 ECORE_GETOPT_VALUE_STR(address),
643
644 ECORE_GETOPT_VALUE_NONE /* sentinel */
645 };
646 int args;
647 Eina_Bool r;
648
649 ecore_init();
650 ecore_con_init();
651 ecore_con_url_init();
652
653 args = ecore_getopt_parse(&options, values, argc, argv);
654 if (args < 0)
655 {
656 fputs("ERROR: Could not parse command line options.\n", stderr);
657 retval = EXIT_FAILURE;
658 goto end;
659 }
660
661 if (quit_option) goto end;
662
663 args = ecore_getopt_parse_positional(&options, values, argc, argv, args);
664 if (args < 0)
665 {
666 fputs("ERROR: Could not parse positional arguments.\n", stderr);
667 retval = EXIT_FAILURE;
668 goto end;
669 }
670
671 if (case_tuples)
672 r = _websocket_test_next_case_tuple();
673 else if (start_index == end_index)
674 r = _websocket_test_index(start_index);
675 else
676 r = _websocket_load_tests();
677
678 if (r)
679 {
680 ecore_main_loop_begin();
681 if (verbose) fprintf(stderr, "INFO: main loop finished. retval=%d\n", retval);
682 }
683
684 if (pending)
685 efl_del(pending);
686
687 end:
688 ecore_con_url_shutdown();
689 ecore_con_shutdown();
690 ecore_shutdown();
691
692 return retval;
693}