/* * Copyright (C) 2014 Steve Harris et al. (see AUTHORS) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * $Id$ */ /* * This is some testcase code - it exercises the internals of liblo, so its not * a good example to learn from, see examples/ for example code */ #include #include #include #include #include #ifdef _MSC_VER #define snprintf _snprintf #else #include #endif #ifdef WIN32 #include #include #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "lo_types_internal.h" #include "lo_internal.h" #include "lo/lo.h" #if defined(WIN32) || defined(_MSC_VER) #define PATHDELIM "\\" #define EXTEXE ".exe" #define SLEEP_MS(x) Sleep(x) #else #define PATHDELIM "/" #define EXTEXE "" #define SLEEP_MS(x) usleep((x)*1000) #endif #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif #define TEST(cond) if (!(cond)) { fprintf(stderr, "FAILED " #cond \ " at %s:%d\n", __FILE__, __LINE__); \ exit(1); } \ else { printf("passed " #cond "\n"); } #define DOING(s) printf("\n == " s "() ==\n\n") #define HANDLER(h) printf(" <-- " h "_handler()\n") union end_test32 { uint32_t i; char c[4]; }; union end_test64 { uint64_t i; char c[8]; }; static int done = 0; static int bundle_count = 0; static int pattern_count = 0; static int reply_count = 0; static int subtest_count = 0; static int subtest_reply_count = 0; static int error_okay = 0; static int tcp_done = 0; char testdata[5] = "ABCD"; static int jitter_count = 0; static float jitter_total = 0.0f; static float jitter_max = 0.0f; static float jitter_min = 1000.0f; uint8_t midi_data[4] = { 0xff, 0xf7, 0xAA, 0x00 }; void exitcheck(void); void test_deserialise(void); void test_validation(lo_address a); void test_multicast(lo_server_thread st); void error(int num, const char *m, const char *path); void rep_error(int num, const char *m, const char *path); int generic_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int foo_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int reply_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int lots_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int coerce_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int bundle_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int timestamp_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int jitter_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int pattern_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int subtest_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int subtest_reply_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int quit_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data); int test_varargs(lo_address a, const char *path, const char *types, ...); int test_version(); void test_types(); void test_url(); void test_address(); void test_blob(); void test_server_thread(lo_server_thread *pst, lo_address *pa); void test_message(lo_address a); void test_pattern(lo_address a); void test_subtest(lo_server_thread st); void test_bundle(lo_server_thread st, lo_address a); void test_nonblock(); void test_unix_sockets(); void test_tcp(); void test_tcp_nonblock(); void cleanup(lo_server_thread st, lo_address a); int main() { #ifdef ENABLE_NETWORK_TESTS lo_server_thread st; lo_address a; #endif atexit(exitcheck); test_version(); test_deserialise(); test_types(); test_url(); test_address(); test_blob(); #ifdef ENABLE_NETWORK_TESTS test_server_thread(&st, &a); test_validation(a); test_multicast(st); test_message(a); test_pattern(a); test_subtest(st); test_bundle(st, a); test_nonblock(); test_unix_sockets(); test_tcp(); test_tcp_nonblock(); cleanup(st, a); #else done = 1; #endif return 0; } void exitcheck(void) { if (!done) { fprintf(stderr, "\ntest run not completed\n" PACKAGE_NAME " test FAILED\n"); } else { printf(PACKAGE_NAME " test PASSED\n"); } } void error(int num, const char *msg, const char *path) { fprintf(stderr, "liblo server error %d in %s: %s", num, path, msg); if (!error_okay) exit(1); else printf(" (expected)\n"); } void rep_error(int num, const char *msg, const char *path) { if (num != 9904) { error(num, msg, path); } } int generic_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { int i; HANDLER("generic"); printf("path: <%s>\n", path); for (i = 0; i < argc; i++) { printf("arg %d '%c' ", i, types[i]); lo_arg_pp((lo_type) types[i], argv[i]); printf("\n"); } printf("\n"); return 1; } int foo_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { HANDLER("foo"); lo_server serv = (lo_server) user_data; lo_address src = lo_message_get_source(data); char *url = lo_address_get_url(src); char *server_url = lo_server_get_url(serv); printf("Address of us: %s\n", server_url); printf("%s <- f:%f, i:%d\n", path, argv[0]->f, argv[1]->i); if (lo_send_from(src, serv, LO_TT_IMMEDIATE, "/reply", "s", "a reply") == -1) { printf("OSC reply error %d: %s\nSending to %s\n", lo_address_errno(src), lo_address_errstr(src), url); exit(1); } else { printf("Reply sent to %s\n\n", url); } free(server_url); free(url); return 0; } int reply_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { HANDLER("reply"); lo_address src = lo_message_get_source(data); char *url = lo_address_get_url(src); printf("Reply received from %s\n", url); free(url); reply_count++; return 0; } int lots_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { lo_blob b; unsigned char *d; HANDLER("lots"); if (strcmp(path, "/lotsofformats")) { fprintf(stderr, "path != /lotsofformats\n"); exit(1); } printf("path = %s\n", path); TEST(types[0] == 'f' && argv[0]->f == 0.12345678f); TEST(types[1] == 'i' && argv[1]->i == 123); TEST(types[2] == 's' && !strcmp(&argv[2]->s, "123")); b = (lo_blob) argv[3]; d = (unsigned char *) lo_blob_dataptr(b); TEST(types[3] == 'b' && lo_blob_datasize(b) == 5); TEST(d[0] == 'A' && d[1] == 'B' && d[2] == 'C' && d[3] == 'D'); d = argv[4]->m; TEST(d[0] == 0xff && d[1] == 0xf7 && d[2] == 0xaa && d[3] == 0x00); TEST(types[5] == 'h' && argv[5]->h == 0x0123456789ABCDEFULL); TEST(types[6] == 't' && argv[6]->t.sec == 1 && argv[6]->t.frac == 0x80000000); TEST(types[7] == 'd' && argv[7]->d == 0.9999); TEST(types[8] == 'S' && !strcmp(&argv[8]->S, "sym")); printf("char: %d\n", argv[9]->c); TEST(types[9] == 'c' && argv[9]->c == 'X'); TEST(types[10] == 'c' && argv[10]->c == 'Y'); TEST(types[11] == 'T'); TEST(types[12] == 'F'); TEST(types[13] == 'N'); TEST(types[14] == 'I'); printf("\n"); return 0; } int coerce_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { HANDLER("coerce"); printf("path = %s\n", path); TEST(types[0] == 'd' && fabs(argv[0]->d - 0.1) < FLT_EPSILON); TEST(types[1] == 'f' && fabs(argv[1]->f - 0.2) < FLT_EPSILON); TEST(types[2] == 'h' && argv[2]->h == 123); TEST(types[3] == 'i' && argv[3]->i == 124); TEST(types[4] == 'S' && !strcmp(&argv[4]->S, "aaa")); TEST(types[5] == 's' && !strcmp(&argv[5]->s, "bbb")); printf("\n"); return 0; } int bundle_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { HANDLER("bundle"); bundle_count++; printf("received bundle\n"); return 0; } int timestamp_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { int bundled = argv[0]->i; lo_timetag ts, arg_ts; HANDLER("timestamp"); ts = lo_message_get_timestamp(data); arg_ts = argv[1]->t; if (bundled) { TEST((ts.sec == arg_ts.sec) && (ts.frac == arg_ts.frac)); } else { TEST(ts.sec == LO_TT_IMMEDIATE.sec && ts.frac == LO_TT_IMMEDIATE.frac); } return 0; } int jitter_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { lo_timetag now; float jitter; HANDLER("jitter"); lo_timetag_now(&now); jitter = fabs(lo_timetag_diff(now, argv[0]->t)); jitter_count++; //printf("jitter: %f\n", jitter); printf("%d expected: %x:%x received %x:%x\n", argv[1]->i, argv[0]->t.sec, argv[0]->t.frac, now.sec, now.frac); jitter_total += jitter; if (jitter > jitter_max) jitter_max = jitter; if (jitter < jitter_min) jitter_min = jitter; return 0; } int pattern_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { HANDLER("pattern"); pattern_count++; printf("pattern matched %s\n", (char*) user_data); return 0; } int subtest_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { lo_address a; HANDLER("subtest"); a = lo_message_get_source(data); subtest_count++; lo_send_from(a, lo_server_thread_get_server((lo_server_thread)user_data), LO_TT_IMMEDIATE, "/subtest", "i", subtest_count); return 0; } int subtest_reply_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { HANDLER("subtest_reply"); subtest_reply_count++; return 0; } int quit_handler(const char *path, const char *types, lo_arg ** argv, int argc, lo_message data, void *user_data) { HANDLER("quit"); done = 1; return 0; } int test_version() { int major, minor, lt_maj, lt_min, lt_bug; char extra[256]; char string[256]; DOING("test_version"); lo_version(string, 256, &major, &minor, extra, 256, <_maj, <_min, <_bug); printf("liblo version string `%s'\n", string); printf("liblo version: %d.%d%s\n", major, minor, extra); printf("liblo libtool version: %d.%d.%d\n", lt_maj, lt_min, lt_bug); printf("\n"); return 0; } int test_varargs(lo_address a, const char *path, const char *types, ...) { va_list ap; lo_message m; int error; DOING("test_varargs"); m = lo_message_new(); va_start(ap, types); if ((error = lo_message_add_varargs(m, types, ap)) == 0) lo_send_message(a, path, m); else printf("lo_message_add_varargs returned %d\n", error); lo_message_free(m); return error < 0; } void replace_char(char *str, size_t size, const char find, const char replace) { char *p = str; while (size--) { if (find == *p) { *p = replace; } ++p; } } void test_deserialise() { char *buf, *buf2, *tmp; const char *types = NULL, *path; lo_arg **argv = NULL; size_t len, size; char data[256]; int result = 0; lo_blob btest; lo_timetag tt = { 0x1, 0x80000000 }; lo_blob b = NULL; DOING("test_deserialise"); btest = lo_blob_new(sizeof(testdata), testdata); // build a message lo_message msg = lo_message_new(); TEST(0 == lo_message_get_argc(msg)); lo_message_add_float(msg, 0.12345678f); // 0 f lo_message_add_int32(msg, 123); // 1 i lo_message_add_string(msg, "123"); // 2 s lo_message_add_blob(msg, btest); // 3 b lo_message_add_midi(msg, midi_data); // 4 m lo_message_add_int64(msg, 0x0123456789abcdefULL); // 5 h lo_message_add_timetag(msg, tt); // 6 t lo_message_add_double(msg, 0.9999); // 7 d lo_message_add_symbol(msg, "sym"); // 8 S lo_message_add_char(msg, 'X'); // 9 c lo_message_add_char(msg, 'Y'); // 10 c lo_message_add_true(msg); // 11 T lo_message_add_false(msg); // 12 F lo_message_add_nil(msg); // 13 N lo_message_add_infinitum(msg); // 14 I // test types, args TEST(15 == lo_message_get_argc(msg)); types = lo_message_get_types(msg); TEST(NULL != types); argv = lo_message_get_argv(msg); TEST(NULL != argv); TEST('f' == types[0] && fabs(argv[0]->f - 0.12345678f) < FLT_EPSILON); TEST('i' == types[1] && 123 == argv[1]->i); TEST('s' == types[2] && !strcmp(&argv[2]->s, "123")); TEST('b' == types[3]); b = (lo_blob) argv[3]; TEST(lo_blob_datasize(b) == sizeof(testdata)); TEST(12 == lo_blobsize(b)); TEST(!memcmp(lo_blob_dataptr(b), &testdata, sizeof(testdata))); TEST('m' == types[4] && !memcmp(&argv[4]->m, midi_data, 4)); TEST('h' == types[5] && 0x0123456789abcdefULL == argv[5]->h); TEST('t' == types[6] && 1 == argv[6]->t.sec && 0x80000000 == argv[6]->t.frac); TEST('d' == types[7] && fabs(argv[7]->d - 0.9999) < FLT_EPSILON); TEST('S' == types[8] && !strcmp(&argv[8]->s, "sym")); TEST('c' == types[9] && 'X' == argv[9]->c); TEST('c' == types[10] && 'Y' == argv[10]->c); TEST('T' == types[11] && NULL == argv[11]); TEST('F' == types[12] && NULL == argv[12]); TEST('N' == types[13] && NULL == argv[13]); TEST('I' == types[14] && NULL == argv[14]); // serialise it len = lo_message_length(msg, "/foo"); printf("serialise message_length=%d\n", (int) len); buf = (char*) calloc(len, sizeof(char)); size = 0; tmp = (char*) lo_message_serialise(msg, "/foo", buf, &size); TEST(tmp == buf && size == len && 92 == len); lo_message_free(msg); // deserialise it printf("deserialise\n"); path = lo_get_path(buf, len); TEST(NULL != path && !strcmp(path, "/foo")); msg = lo_message_deserialise(buf, size, NULL); TEST(NULL != msg); // repeat same test as above TEST(15 == lo_message_get_argc(msg)); types = lo_message_get_types(msg); TEST(NULL != types); argv = lo_message_get_argv(msg); TEST(NULL != argv); TEST('f' == types[0] && fabs(argv[0]->f - 0.12345678f) < FLT_EPSILON); TEST('i' == types[1] && 123 == argv[1]->i); TEST('s' == types[2] && !strcmp(&argv[2]->s, "123")); TEST('b' == types[3]); b = (lo_blob) argv[3]; TEST(lo_blob_datasize(b) == sizeof(testdata)); TEST(12 == lo_blobsize(b)); TEST(!memcmp(lo_blob_dataptr(b), &testdata, sizeof(testdata))); TEST('m' == types[4] && !memcmp(&argv[4]->m, midi_data, 4)); TEST('h' == types[5] && 0x0123456789abcdefULL == argv[5]->h); TEST('t' == types[6] && 1 == argv[6]->t.sec && 0x80000000 == argv[6]->t.frac); TEST('d' == types[7] && fabs(argv[7]->d - 0.9999) < FLT_EPSILON); TEST('S' == types[8] && !strcmp(&argv[8]->s, "sym")); TEST('c' == types[9] && 'X' == argv[9]->c); TEST('c' == types[10] && 'Y' == argv[10]->c); TEST('T' == types[11] && NULL == argv[11]); TEST('F' == types[12] && NULL == argv[12]); TEST('N' == types[13] && NULL == argv[13]); TEST('I' == types[14] && NULL == argv[14]); // serialise it again, compare len = lo_message_length(msg, "/foo"); printf("serialise message_length=%d\n", (int) len); buf2 = (char*) calloc(len, sizeof(char)); size = 0; tmp = (char*) lo_message_serialise(msg, "/foo", buf2, &size); TEST(tmp == buf2 && size == len && 92 == len); TEST(!memcmp(buf, buf2, len)); lo_message_free(msg); lo_blob_free(btest); free(buf); free(buf2); // deserialise failure tests with invalid message data msg = lo_message_deserialise(data, 0, &result); // 0 size TEST(NULL == msg && LO_ESIZE == result); snprintf(data, 256, "%s", "/foo"); // unterminated path string msg = lo_message_deserialise(data, 4, &result); TEST(NULL == msg && LO_EINVALIDPATH == result); snprintf(data, 256, "%s", "/f_o"); // non-0 in pad area msg = lo_message_deserialise(data, 4, &result); TEST(NULL == msg && LO_EINVALIDPATH == result); snprintf(data, 256, "%s", "/t__"); // types missing replace_char(data, 4, '_', '\0'); msg = lo_message_deserialise(data, 4, &result); TEST(NULL == msg && LO_ENOTYPE == result); snprintf(data, 256, "%s%s", "/t__", "____"); // types empty replace_char(data, 8, '_', '\0'); msg = lo_message_deserialise(data, 8, &result); TEST(NULL == msg && LO_EBADTYPE == result); snprintf(data, 256, "%s%s", "/t__", ",f_"); // short message replace_char(data, 7, '_', '\0'); msg = lo_message_deserialise(data, 7, &result); TEST(NULL == msg && LO_EINVALIDTYPE == result); snprintf(data, 256, "%s%s", "/t__", "ifi_"); // types missing comma replace_char(data, 8, '_', '\0'); msg = lo_message_deserialise(data, 8, &result); TEST(NULL == msg && LO_EBADTYPE == result); snprintf(data, 256, "%s%s", "/t__", ",ifi"); // types unterminated replace_char(data, 8, '_', '\0'); msg = lo_message_deserialise(data, 8, &result); TEST(NULL == msg && LO_EINVALIDTYPE == result); snprintf(data, 256, "%s%s", "/t__", ",ii_"); // not enough arg data replace_char(data, 8, '_', '\0'); msg = lo_message_deserialise(data, 12, &result); TEST(NULL == msg && LO_EINVALIDARG == result); snprintf(data, 256, "%s%s", "/t__", ",ii_"); // not enough arg data again replace_char(data, 8, '_', '\0'); msg = lo_message_deserialise(data, 15, &result); TEST(NULL == msg && LO_EINVALIDARG == result); snprintf(data, 256, "%s%s", "/t__", ",f__"); // too much arg data replace_char(data, 8, '_', '\0'); msg = lo_message_deserialise(data, 16, &result); TEST(NULL == msg && LO_ESIZE == result); snprintf(data, 256, "%s%s", "/t__", ",bs_"); // blob longer than msg length replace_char(data, 8, '_', '\0'); *(uint32_t *) (data + 8) = lo_htoo32((uint32_t) 99999); msg = lo_message_deserialise(data, 256, &result); TEST(NULL == msg && LO_EINVALIDARG == result); } void test_validation(lo_address a) { /* packet crafted to crash a lo_server when no input validation is performed */ char mem[] = { "/\0\0\0,bs\0,\x00\x0F\x42\x3F" }; // OSC: "/" ",bs" 999999 int eok = error_okay; int sock = a->socket; /* This code won't work with MSVC because the lo_client_sockets data structure * is not explicitly made available to external programs. We could expose it * in debug mode, perhaps, but let's just skip this test for now. (Can be tested * on Windows using MingW.) */ #ifdef _MSC_VER return; #else DOING("test_validation"); /* Note: lo_client_sockets is not available when liblo is compiled * as a DLL. */ #if !defined(WIN32) && !defined(_MSC_VER) if (sock == -1) sock = lo_client_sockets.udp; #endif if (sock == -1) { fprintf(stderr, "Warning: Couldn't get socket in test_validation(), " "lo_client_sockets.udp not supported on Windows, %s:%d\n", __FILE__, __LINE__); return; } error_okay = 1; if (sendto(sock, (void*)&mem, sizeof(mem), MSG_NOSIGNAL, a->ai->ai_addr, a->ai->ai_addrlen) == -1) { fprintf(stderr, "Error sending packet in test_validation(), %s:%d\n", __FILE__, __LINE__); } SLEEP_MS(10); error_okay = eok; #endif } void test_multicast(lo_server_thread st) { lo_server ms; lo_address ma; DOING("test_multicast"); /* test multicast server and sender */ /* message is sent from st otherwise reply doesn't work */ ms = lo_server_new_multicast("224.0.1.1", "15432", error); ma = lo_address_new("224.0.1.1", "15432"); lo_address_set_ttl(ma, 1); lo_server_add_method(ms, "/foo/bar", "fi", foo_handler, ms); lo_server_add_method(ms, "/reply", "s", reply_handler, NULL); if (lo_send_from(ma, lo_server_thread_get_server(st), LO_TT_IMMEDIATE, "/foo/bar", "ff", 0.12345678f, 23.0f) == -1) { printf("multicast send error %d: %s\n", lo_address_errno(ma), lo_address_errstr(ma)); exit(1); } TEST(lo_server_recv(ms) == 24); lo_server_free(ms); lo_address_free(ma); } int test_received = 0; int ok_received = 0; int recv_times = 0; int test_handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message m, void *data) { HANDLER("test"); printf("/test, %d, %s\n", argv[0]->i, &argv[1]->s); test_received += 1; return 0; } int ok_handler(const char *path, const char *types, lo_arg **argv, int argc, lo_message m, void *data) { HANDLER("ok"); ok_received += 1; return 0; } #ifdef HAVE_WIN32_THREADS unsigned __stdcall test_tcp_thread(void *context) #else void *test_tcp_thread(void *context) #endif { lo_server s; printf("TCP thread started.\n"); s = lo_server_new_with_proto("9000", LO_TCP, error); if (!s) { printf("Aborting thread, s=%p\n", s); #ifdef HAVE_WIN32_THREADS return 1; #else return (void*)1; #endif } lo_server_add_method(s, "/test", "is", test_handler, 0); lo_server_add_method(s, "/ok", "", ok_handler, 0); while (!(done || tcp_done)) { printf("lo_server_recv_noblock()\n"); recv_times += 1; if (!lo_server_recv_noblock(s, 0)) SLEEP_MS(300); lo_server_max_msg_size(s, 1024); } printf("Freeing.\n"); lo_server_free(s); printf("Done. Thread ending.\n"); return 0; } #define SLIP_END '\xC0' /* indicates end of packet */ void test_tcp_halfsend(int stream_type) { char prefixmsg[] = {0,0,0,0, '/','t','e','s','t',0,0,0, ',','i','s',0, 0,0,0,4, 'b','l','a','h',0,0,0,0, 0,0,0,0, '/','o','k',0, ',',0,0,0}; char slipmsg[] = {'/','t','e','s','t',0,0,0, ',','i','s',0, 0,0,0,4, 'b','l','a','h',0,0,0,0,SLIP_END, '/','o','k',0, ',',0,0,0,SLIP_END}; char *msg; int msglen; struct sockaddr_in sa; int rc; int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); exit(1); } memset(&sa, 0, sizeof(struct sockaddr_in)); sa.sin_addr.s_addr = inet_addr("127.0.0.1"); sa.sin_port = htons(9000); sa.sin_family = AF_INET; rc = connect(sock, (struct sockaddr*)&sa, sizeof(struct sockaddr_in)); if (rc) { perror("Error connecting"); closesocket(sock); exit(1); } printf("Connected, sending...\n"); switch (stream_type) { case 0: printf("Testing a count-prefix stream.\n"); msg = prefixmsg; msglen = sizeof(prefixmsg); *(uint32_t*)msg = htonl(24); *(uint32_t*)(msg+28) = htonl(8); break; case 1: printf("Testing a SLIP stream.\n"); msg = slipmsg; msglen = sizeof(slipmsg); break; default: closesocket(sock); return; } if (0) { printf("Sending everything in one big chunk.\n"); rc = send(sock, msg, msglen, 0); if (rc != msglen) printf("Error sending, rc = %d\n", rc); else printf("Sent.\n"); } else { rc = send(sock, msg, 13, 0); if (rc != 13) printf("Error sending, rc = %d\n", rc); else printf("Sent.\n"); SLEEP_MS(1000); rc = send(sock, msg+13, 20, 0); if (rc != 20) printf("Error sending2, rc = %d\n", rc); else printf("Sent2.\n"); SLEEP_MS(1000); rc = send(sock, msg+33, msglen-33, 0); if (rc != (msglen-33)) printf("Error sending3, rc = %d\n", rc); else printf("Sent3.\n"); } closesocket(sock); } void test_tcp_nonblock() { #ifdef HAVE_WIN32_THREADS unsigned retval; HANDLE thread; #else void *retval; pthread_t thread; #endif DOING("test_tcp_nonblock"); tcp_done = 0; #ifdef HAVE_WIN32_THREADS if (!(thread=(HANDLE)_beginthreadex(NULL, 0, &test_tcp_thread, 0, 0, NULL))) #else if (pthread_create(&thread, 0, test_tcp_thread, 0)) #endif { perror("pthread_create"); exit(1); } SLEEP_MS(1000); test_tcp_halfsend(0); SLEEP_MS(1000); test_tcp_halfsend(1); SLEEP_MS(1000); tcp_done = 1; #ifdef HAVE_WIN32_THREADS retval = WaitForSingleObject(thread, INFINITE); CloseHandle(thread); printf("Thread joined, retval=%u\n", retval); #else pthread_join(thread, &retval); printf("Thread joined, retval=%p\n", retval); #endif TEST(retval == 0); TEST(test_received == 2); TEST(ok_received == 2); TEST(recv_times > 10); } void test_types() { union end_test32 et32; union end_test64 et64; DOING("test_types"); TEST(sizeof(float) == sizeof(int32_t)); TEST(sizeof(double) == sizeof(int64_t)); et32.i = 0x23242526U; et32.i = lo_htoo32(et32.i); if (et32.c[0] != 0x23 || et32.c[1] != 0x24 || et32.c[2] != 0x25 || et32.c[3] != 0x26) { fprintf(stderr, "failed 32bit endian conversion test\n"); fprintf(stderr, "0x23242526 -> %X\n", et32.i); exit(1); } else { printf("passed 32bit endian conversion test\n"); } et64.i = 0x232425262728292AULL; et64.i = lo_htoo64(et64.i); if (et64.c[0] != 0x23 || et64.c[1] != 0x24 || et64.c[2] != 0x25 || et64.c[3] != 0x26 || et64.c[4] != 0x27 || et64.c[5] != 0x28 || et64.c[6] != 0x29 || et64.c[7] != 0x2A) { fprintf(stderr, "failed 64bit endian conversion\n"); fprintf(stderr, "0x232425262728292A -> %" PRINTF_LL "X\n", (long long unsigned int) et64.i); exit(1); } else { printf("passed 64bit endian conversion\n"); } printf("\n"); } void test_url() { int proto; char *path, *protocol, *host, *port; DOING("test_url"); /* OSC URL tests */ path = lo_url_get_path("osc.udp://localhost:9999/a/path/is/here"); if (strcmp(path, "/a/path/is/here")) { printf("failed lo_url_get_path() test1\n"); printf("'%s' != '/a/path/is/here'\n", path); exit(1); } else { printf("passed lo_url_get_path() test1\n"); } free(path); protocol = lo_url_get_protocol("osc.udp://localhost:9999/a/path/is/here"); if (strcmp(protocol, "udp")) { printf("failed lo_url_get_protocol() test1\n"); printf("'%s' != 'udp'\n", protocol); exit(1); } else { printf("passed lo_url_get_protocol() test1\n"); } free(protocol); protocol = lo_url_get_protocol("osc.tcp://localhost:9999/a/path/is/here"); if (strcmp(protocol, "tcp")) { printf("failed lo_url_get_protocol() test2\n"); printf("'%s' != 'tcp'\n", protocol); exit(1); } else { printf("passed lo_url_get_protocol() test2\n"); } free(protocol); protocol = lo_url_get_protocol ("osc.udp://[::ffff:localhost]:9999/a/path/is/here"); if (strcmp(protocol, "udp")) { printf("failed lo_url_get_protocol() test1 (IPv6)\n"); printf("'%s' != 'udp'\n", protocol); exit(1); } else { printf("passed lo_url_get_protocol() test1 (IPv6)\n"); } free(protocol); proto = lo_url_get_protocol_id("osc.udp://localhost:9999/a/path/is/here"); if (proto != LO_UDP) { printf("failed lo_url_get_protocol_id() test1\n"); printf("'%d' != LO_UDP\n", proto); exit(1); } else { printf("passed lo_url_get_protocol_id() test1\n"); } proto = lo_url_get_protocol_id("osc.tcp://localhost:9999/a/path/is/here"); if (proto != LO_TCP) { printf("failed lo_url_get_protocol_id() test2\n"); printf("'%d' != LO_TCP\n", proto); exit(1); } else { printf("passed lo_url_get_protocol_id() test2\n"); } proto = lo_url_get_protocol_id ("osc.invalid://localhost:9999/a/path/is/here"); if (proto != -1) { printf("failed lo_url_get_protocol_id() test3\n"); printf("'%d' != -1\n", proto); exit(1); } else { printf("passed lo_url_get_protocol_id() test3\n"); } proto = lo_url_get_protocol_id ("osc.udp://[::ffff:localhost]:9999/a/path/is/here"); if (proto != LO_UDP) { printf("failed lo_url_get_protocol_id() test1 (IPv6)\n"); printf("'%d' != LO_UDP\n", proto); exit(1); } else { printf("passed lo_url_get_protocol_id() test1 (IPv6)\n"); } host = lo_url_get_hostname ("osc.udp://foo.example.com:9999/a/path/is/here"); if (strcmp(host, "foo.example.com")) { printf("failed lo_url_get_hostname() test1\n"); printf("'%s' != 'foo.example.com'\n", host); exit(1); } else { printf("passed lo_url_get_hostname() test1\n"); } free(host); host = lo_url_get_hostname ("osc.udp://[0000::::0001]:9999/a/path/is/here"); if (strcmp(host, "0000::::0001")) { printf("failed lo_url_get_hostname() test2 (IPv6)\n"); printf("'%s' != '0000::::0001'\n", host); exit(1); } else { printf("passed lo_url_get_hostname() test2 (IPv6)\n"); } free(host); port = lo_url_get_port("osc.udp://localhost:9999/a/path/is/here"); if (strcmp(port, "9999")) { printf("failed lo_url_get_port() test1\n"); printf("'%s' != '9999'\n", port); exit(1); } else { printf("passed lo_url_get_port() test1\n"); } free(port); port = lo_url_get_port ("osc.udp://[::ffff:127.0.0.1]:9999/a/path/is/here"); if (strcmp(port, "9999")) { printf("failed lo_url_get_port() test1 (IPv6)\n"); printf("'%s' != '9999'\n", port); exit(1); } else { printf("passed lo_url_get_port() test1 (IPv6)\n"); } free(port); printf("\n"); } void test_address() { const char *host, *port; char *server_url; lo_address a; int proto; DOING("test_address"); a = lo_address_new_from_url("osc://localhost/"); TEST(a != NULL); lo_address_free(a); a = lo_address_new_from_url("osc.://localhost/"); TEST(a == NULL); a = lo_address_new_from_url("osc.tcp://foo.example.com:9999/"); host = lo_address_get_hostname(a); if (strcmp(host, "foo.example.com")) { printf("failed lo_address_get_hostname() test\n"); printf("'%s' != 'foo.example.com'\n", host); exit(1); } else { printf("passed lo_address_get_hostname() test\n"); } port = lo_address_get_port(a); if (strcmp(port, "9999")) { printf("failed lo_address_get_port() test\n"); printf("'%s' != '9999'\n", port); exit(1); } else { printf("passed lo_address_get_port() test\n"); } proto = lo_address_get_protocol(a); if (proto != LO_TCP) { printf("failed lo_address_get_protocol() test\n"); printf("'%d' != '%d'\n", proto, LO_TCP); exit(1); } else { printf("passed lo_address_get_protocol() test\n"); } server_url = lo_address_get_url(a); if (strcmp(server_url, "osc.tcp://foo.example.com:9999/")) { printf("failed lo_address_get_url() test\n"); printf("'%s' != '%s'\n", server_url, "osc.tcp://foo.example.com:9999/"); exit(1); } else { printf("passed lo_address_get_url() test\n"); } free(server_url); lo_address_free(a); printf("\n"); } void test_blob() { lo_blob btest; DOING("test_blob"); btest = lo_blob_new(sizeof(testdata), testdata); /* Test blob sizes */ if (lo_blob_datasize(btest) != 5 || lo_blobsize(btest) != 12) { printf("blob is %d (%d) bytes long, should be 5 (12)\n", lo_blob_datasize(btest), lo_blobsize(btest)); lo_arg_pp(LO_BLOB, btest); printf(" <- blob\n"); exit(1); } lo_blob_free(btest); } void test_server_thread(lo_server_thread *pst, lo_address *pa) { lo_address a; char *server_url; lo_server_thread st, sta, stb; lo_blob btest; lo_timetag tt = { 0x1, 0x80000000 }; DOING("test_server_thread"); btest = lo_blob_new(sizeof(testdata), testdata); sta = lo_server_thread_new("7591", error); stb = lo_server_thread_new("7591", rep_error); if (stb) { fprintf(stderr, "FAILED: create bad server thread object!\n"); exit(1); } lo_server_thread_free(sta); /* leak check */ st = lo_server_thread_new(NULL, error); if (!st) { printf("Error creating server thread\n"); exit(1); } lo_server_thread_start(st); SLEEP_MS(4); lo_server_thread_stop(st); lo_server_thread_free(st); st = lo_server_thread_new(NULL, error); lo_server_thread_start(st); lo_server_thread_stop(st); lo_server_thread_free(st); st = lo_server_thread_new(NULL, error); lo_server_thread_free(st); st = lo_server_thread_new(NULL, error); lo_server_thread_free(st); st = lo_server_thread_new(NULL, error); /* Server method handler tests */ server_url = lo_server_thread_get_url(st); printf("Server URL: %s\n", server_url); a = lo_address_new_from_url(server_url); free(server_url); /* add method that will match the path /foo/bar, with two numbers, coerced * to float and int */ lo_server_thread_add_method(st, "/foo/bar", "fi", foo_handler, lo_server_thread_get_server(st)); lo_server_thread_add_method(st, "/reply", "s", reply_handler, NULL); lo_server_thread_add_method(st, "/lotsofformats", "fisbmhtdSccTFNI", lots_handler, NULL); lo_server_thread_add_method(st, "/coerce", "dfhiSs", coerce_handler, NULL); lo_server_thread_add_method(st, "/bundle", NULL, bundle_handler, NULL); lo_server_thread_add_method(st, "/timestamp", NULL, timestamp_handler, NULL); lo_method jit = lo_server_thread_add_method(st, "/jitter", "ti", jitter_handler, NULL); lo_server_thread_add_method(st, "/pattern/foo", NULL, pattern_handler, "foo"); lo_server_thread_add_method(st, "/pattern/bar", NULL, pattern_handler, "bar"); lo_server_thread_add_method(st, "/pattern/baz", NULL, pattern_handler, "baz"); lo_server_thread_add_method(st, "/subtest", "i", subtest_handler, st); lo_server_thread_add_method(st, "/subtest-reply", "i", subtest_reply_handler, NULL); /* add method that will match any path and args */ lo_server_thread_add_method(st, NULL, NULL, generic_handler, NULL); /* add method that will match the path /quit with no args */ lo_server_thread_add_method(st, "/quit", "", quit_handler, NULL); /* check that the thread restarts */ lo_server_thread_start(st); lo_server_thread_stop(st); lo_server_thread_start(st); if (lo_send(a, "/foo/bar", "ff", 0.12345678f, 23.0f) < 0) { printf("OSC error A %d: %s\n", lo_address_errno(a), lo_address_errstr(a)); exit(1); } if (lo_send(a, "/foo/bar", "ff", 0.12345678f, 23.0f) < 0) { printf("OSC error B %d: %s\n", lo_address_errno(a), lo_address_errstr(a)); exit(1); } lo_send(a, "/", "i", 242); lo_send(a, "/pattern/", "i", 243); #ifndef _MSC_VER /* MS compiler refuses to compile this case */ lo_send(a, "/bar", "ff", 0.12345678f, 1.0 / 0.0); #endif lo_send(a, "/lotsofformats", "fisbmhtdSccTFNI", 0.12345678f, 123, "123", btest, midi_data, 0x0123456789abcdefULL, tt, 0.9999, "sym", 'X', 'Y'); lo_send(a, "/coerce", "fdihsS", 0.1f, 0.2, 123, 124LL, "aaa", "bbb"); lo_send(a, "/coerce", "ffffss", 0.1f, 0.2f, 123.0, 124.0, "aaa", "bbb"); lo_send(a, "/coerce", "ddddSS", 0.1, 0.2, 123.0, 124.0, "aaa", "bbb"); lo_send(a, "/a/b/c/d", "sfsff", "one", 0.12345678f, "three", -0.00000023001f, 1.0); lo_send(a, "/a/b/c/d", "b", btest); /* Delete methods */ lo_server_thread_del_method(st, "/coerce", "dfhiSs"); TEST (lo_server_thread_del_lo_method(st, jit) == 0); TEST (lo_server_thread_del_lo_method(st, jit) != 0); { lo_method m; lo_server s = lo_server_new(NULL, error); lo_server_del_method(s, NULL, NULL); TEST (m = lo_server_add_method(s, NULL, NULL, generic_handler, NULL)); TEST (lo_server_del_lo_method(s, m) == 0); TEST (lo_server_del_lo_method(s, m) != 0); lo_server_free(s); } lo_blob_free(btest); *pst = st; *pa = a; } void test_message(lo_address a) { lo_blob btest; lo_timetag tt = { 0x1, 0x80000000 }; lo_message m; DOING("test_message"); btest = lo_blob_new(sizeof(testdata), testdata); TEST(test_varargs (a, "/lotsofformats", "fisbmhtdSccTFNI", 0.12345678f, 123, "123", btest, midi_data, 0x0123456789abcdefULL, tt, 0.9999, "sym", 'X', 'Y', LO_ARGS_END) == 0); #ifdef __GNUC__ #ifndef USE_ANSI_C // Note: Lack of support for variable-argument macros in non-GCC compilers // does not allow us to test for these conditions. // too many args TEST(test_varargs(a, "/lotsofformats", "f", 0.12345678f, 123, "123", btest, midi_data, 0x0123456789abcdefULL, tt, 0.9999, "sym", 'X', 'Y', LO_ARGS_END) != 0); // too many types TEST(test_varargs (a, "/lotsofformats", "fisbmhtdSccTFNI", 0.12345678f, 123, "123", btest, midi_data, 0x0123456789abcdefULL, tt, 0.5, LO_ARGS_END) != 0); #endif #endif // test lo_message_add m = lo_message_new(); TEST(lo_message_add(m, "fisbmhtdSccTFNI", 0.12345678f, 123, "123", btest, midi_data, 0x0123456789abcdefULL, tt, 0.9999, "sym", 'X', 'Y') == 0); lo_send_message(a, "/lotsofformats", m); lo_message_free(m); lo_blob_free(btest); } void test_pattern(lo_address a) { DOING("test_pattern"); lo_send(a, "/pattern/*", "s", "a"); lo_send(a, "/pattern/ba[rz]", "s", "b"); } void test_subtest(lo_server_thread st) { char cmd[2048], *server_url; int i, rc; DOING("test_subtest"); server_url = lo_server_thread_get_url(st); #ifdef WIN32 { char cwd[2048]; _getcwd(cwd, 2048); snprintf(cmd, 2048, "%s" PATHDELIM "subtest" EXTEXE, cwd); } printf("spawning subtest with `%s'\n", cmd); for (i=0; i<2; i++) { int j=0; rc = _spawnl( _P_NOWAIT, cmd, cmd, server_url, NULL ); if (rc == -1) { fprintf(stderr, "Cannot execute subtest command (%d)\n", i); exit(1); } else while (subtest_count < 1 && j < 20) { SLEEP_MS(100); j++; } if (j >= 20) { fprintf(stderr, "Never got a message from subtest (%d) " "after 2 seconds.\n", i); exit(1); } } #else sprintf(cmd, "." PATHDELIM "subtest" EXTEXE " %s", server_url); printf("executing subtest with `%s'\n", cmd); for (i=0; i<2; i++) { int j=0; rc = system(cmd); if (rc == -1) { fprintf(stderr, "Cannot execute subtest command (%d)\n", i); exit(1); } else if (rc > 0) { fprintf(stderr, "subtest command returned %d\n", rc); exit(1); } while (subtest_count < 1 && j < 20) { usleep(100000); j++; } } #endif free(server_url); i = 20*1000; while (subtest_reply_count != 22 && --i > 0) { SLEEP_MS(10); } TEST(reply_count == 3); TEST(pattern_count == 5); TEST(subtest_count == 2); TEST(subtest_reply_count == 22); printf("\n"); } void test_bundle(lo_server_thread st, lo_address a) { lo_bundle b; lo_timetag sched; lo_message m1, m2; const char *p; int i, tries; lo_timetag t = { 10, 0xFFFFFFFC }; DOING("test_bundle"); b = lo_bundle_new(t); m1 = lo_message_new(); lo_message_add_string(m1, "abcdefghijklmnopqrstuvwxyz"); lo_message_add_string(m1, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); lo_bundle_add_message(b, "/bundle", m1); lo_send_bundle(a, b); /* This should be safe for multiple copies of the same message. */ lo_bundle_free_messages(b); { lo_timetag t = { 1, 2 }; b = lo_bundle_new(t); } m1 = lo_message_new(); lo_message_add_int32(m1, 23); lo_message_add_string(m1, "23"); lo_bundle_add_message(b, "/bundle", m1); m2 = lo_message_new(); lo_message_add_string(m2, "24"); lo_message_add_int32(m2, 24); lo_bundle_add_message(b, "/bundle", m2); lo_bundle_add_message(b, "/bundle", m1); TEST(lo_bundle_count(b)==3); TEST(lo_bundle_get_message(b,1,&p)==m2); TEST(strcmp(p, "/bundle")==0); TEST(lo_send_bundle(a, b) == 88); /* Should fail to add a bundle recursively */ TEST(lo_bundle_add_bundle(b, b) != 0) /* But we can create a nested bundle and it should free * successfully. */ { lo_bundle b2 = 0; { lo_timetag t = { 10, 0xFFFFFFFE }; b2 = lo_bundle_new(t); } lo_bundle_add_message(b2, "/bundle", m1); TEST(lo_bundle_add_bundle(b2, b) == 0); /* Test freeing out-of-order copies of messages in a bundle. */ lo_bundle_free_recursive(b2); } { lo_timetag t = { 10, 0xFFFFFFFE }; b = lo_bundle_new(t); } m1 = lo_message_new(); lo_message_add_string(m1, "abcdefghijklmnopqrstuvwxyz"); lo_message_add_string(m1, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); lo_bundle_add_message(b, "/bundle", m1); lo_send_bundle(a, b); lo_message_free(m1); lo_bundle_free(b); lo_timetag_now(&sched); sched.sec += 5; b = lo_bundle_new(sched); m1 = lo_message_new(); lo_message_add_string(m1, "future"); lo_message_add_string(m1, "time"); lo_message_add_string(m1, "test"); lo_bundle_add_message(b, "/bundle", m1); lo_send_bundle(a, b); lo_message_free(m1); lo_bundle_free(b); lo_send_timestamped(a, sched, "/bundle", "s", "lo_send_timestamped() test"); /* test bundle timestamp ends up in message struct (and doesn't end up in unbundled messages) */ lo_timetag_now(&sched); lo_send_timestamped(a, sched, "/timestamp", "it", 1, sched); lo_send(a, "/timestamp", "it", 0, sched); #define JITTER_ITS 25 /* jitter tests */ { lo_timetag stamps[JITTER_ITS]; lo_timetag now; for (i = 0; i < JITTER_ITS; i++) { lo_timetag_now(&now); stamps[i] = now; stamps[i].sec += 1; stamps[i].frac = rand(); lo_send_timestamped(a, stamps[i], "/jitter", "ti", stamps[i], i); } } SLEEP_MS(2000); TEST(lo_server_thread_events_pending(st)); tries = 20; while (lo_server_thread_events_pending(st) && (--tries > 0)) { printf("pending events, wait...\n"); fflush(stdout); SLEEP_MS(1000); } if (tries == 0) { printf("server thread still has pending" " events after 20 seconds!\n"); exit(1); } TEST(bundle_count == 7); printf("\n"); printf("bundle timing jitter results:\n" "max jitter = %fs\n" "avg jitter = %fs\n" "min jitter = %fs\n\n", jitter_max, jitter_total / (float) jitter_count, jitter_min); } void test_nonblock() { lo_server s; char *server_url; lo_address a; DOING("test_nonblock"); s = lo_server_new(NULL, error); server_url = lo_server_get_url(s); lo_server_add_method(s, NULL, NULL, generic_handler, NULL); a = lo_address_new_from_url(server_url); TEST(lo_server_recv_noblock(s, 0) == 0); printf("Testing noblock API on %s\n", server_url); lo_send(a, "/non-block-test", "f", 23.0); int tries = 1000; while (!lo_server_recv_noblock(s, 10) && --tries > 0) { } if (tries == 0) { printf("lo_server_recv_noblock() test failed\n"); exit(1); } free(server_url); lo_server_free(s); lo_address_free(a); } void test_unix_sockets() { #if !defined(WIN32) && !defined(_MSC_VER) lo_address ua; lo_server us; char *addr; DOING("test_unix_sockets"); unlink("/tmp/testlo.osc"); us = lo_server_new_with_proto("/tmp/testlo.osc", LO_UNIX, error); ua = lo_address_new_from_url("osc.unix:///tmp/testlo.osc"); TEST(lo_server_get_protocol(us) == LO_UNIX); TEST(lo_send(ua, "/unix", "f", 23.0) == 16); TEST(lo_server_recv(us) == 16); addr = lo_server_get_url(us); TEST(!strcmp("osc.unix:////tmp/testlo.osc", addr)); free(addr); lo_address_free(ua); ua = lo_address_new_with_proto(LO_UNIX, NULL, "/tmp/testlo.osc"); TEST(lo_send(ua, "/unix", "f", 23.0) == 16); TEST(lo_server_recv(us) == 16); lo_server_free(us); lo_address_free(ua); #endif } void test_tcp() { /* TCP tests */ lo_address ta; lo_server ts; char *addr; DOING("test_tcp"); ts = lo_server_new_with_proto(NULL, LO_TCP, error); addr = lo_server_get_url(ts); ta = lo_address_new_from_url(addr); if (lo_address_errno(ta)) { printf("err: %s\n", lo_address_errstr(ta)); exit(1); } TEST(lo_server_get_protocol(ts) == LO_TCP); TEST(lo_send(ta, "/tcp", "f", 23.0) == 16); TEST(lo_send(ta, "/tcp", "f", 23.0) == 16); TEST(lo_server_recv(ts) == 16); TEST(lo_server_recv(ts) == 16); free(addr); lo_server_free(ts); lo_address_free(ta); } void cleanup(lo_server_thread st, lo_address a) { int tries; DOING("cleanup"); /* exit */ lo_send(a, "/quit", NULL); tries = 20*1000; while (!done && (--tries > 0)) { SLEEP_MS(1); } if (tries == 0) { printf("Took too long to quit\n"); exit(1); } lo_address_free(a); lo_server_thread_free(st); } /* vi:set ts=8 sts=4 sw=4: */