[SCM] liblo/master: Pick patches from upstream disabling network tests

fsateler at users.alioth.debian.org fsateler at users.alioth.debian.org
Mon Feb 10 14:51:56 UTC 2014


The following commit has been merged in the master branch:
commit b981c08d885d3ac433075f332b17bca436f2ad4f
Author: Felipe Sateler <fsateler at debian.org>
Date:   Mon Feb 10 11:45:05 2014 -0300

    Pick patches from upstream disabling network tests
    
    those fail on buildd

diff --git a/debian/patches/Add-several-configure-options-for-the-sake-of-modula.patch b/debian/patches/Add-several-configure-options-for-the-sake-of-modula.patch
new file mode 100644
index 0000000..dfab3d5
--- /dev/null
+++ b/debian/patches/Add-several-configure-options-for-the-sake-of-modula.patch
@@ -0,0 +1,142 @@
+From 02f7754e245080626a590e43c190dfd4abe0d72f Mon Sep 17 00:00:00 2001
+From: Stephen Sinclair <radarsat1 at gmail.com>
+Date: Fri, 7 Feb 2014 13:55:54 +0100
+Subject: [PATCH 3/5] Add several configure options for the sake of modularity.
+
+This adds the following configure options:
+
+  --disable-tests         Disable compiling test programs
+  --disable-network-tests Disable compiling network-based tests
+  --disable-tools         Disable compiling tools
+  --disable-examples      Disable compiling examples
+
+Additionally, it adds some code to configure.ac to print out some
+information about the current build.  The --disable-network-tests flag
+is not yet implemented in testlo.
+---
+ Makefile.am         |  6 +++++-
+ build/config-msvc.h |  3 +++
+ configure.ac        | 37 +++++++++++++++++++++++++++++++++++++
+ src/Makefile.am     | 11 ++++++++++-
+ 4 files changed, 55 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 7108987..e5e31cb 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1,4 +1,8 @@
+-SUBDIRS = src examples lo build @DOXYGEN@
++SUBDIRS = src
++if COMPILE_EXAMPLES
++SUBDIRS += examples
++endif
++SUBDIRS += lo build @DOXYGEN@
+ 
+ EXTRA_DIST = libtool ltmain.sh autogen.sh
+ 
+diff --git a/build/config-msvc.h b/build/config-msvc.h
+index feb92cb..e55af42 100644
+--- a/build/config-msvc.h
++++ b/build/config-msvc.h
+@@ -4,6 +4,9 @@
+ /* Define this to enable ipv6. */
+ /* #undef ENABLE_IPV6 */
+ 
++/* Define this to enable network tests. */
++#define ENABLE_NETWORK_TESTS 1
++
+ /* Define this to enable threads. */
+ @DEFTHREADS@#define ENABLE_THREADS 1
+ 
+diff --git a/configure.ac b/configure.ac
+index c487d18..7be87bb 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -224,6 +224,43 @@ if test x"$enable_debug" = "xyes"; then
+ 	CXXFLAGS="$CF -O0 -g -Wall -Werror -DDEBUG"
+ fi
+ 
++AC_ARG_ENABLE(tests, [  --disable-tests         Disable compiling test programs])
++AC_ARG_ENABLE(network-tests, [  --disable-network-tests Disable compiling network-based tests])
++AC_ARG_ENABLE(tools, [  --disable-tools         Disable compiling tools])
++AC_ARG_ENABLE(examples, [  --disable-examples      Disable compiling examples])
++
++AM_CONDITIONAL([COMPILE_TESTS],[test x$enable_tests != xno])
++AM_CONDITIONAL([COMPILE_TOOLS],[test x$enable_tools != xno])
++AM_CONDITIONAL([COMPILE_EXAMPLES],[test x$enable_examples != xno])
++
++if ! test x$enable_network_tests = xno; then
++  AC_DEFINE(ENABLE_NETWORK_TESTS, [1],
++    [Define this to enable network tests.])
++fi
++
++# Display some information about this build
++echo
++echo About this liblo build:
++echo
++echo CC=\"$CC\"
++echo CXX=\"$CXX\"
++echo DOXYGEN=\"$DOXYGEN\"
++echo CFLAGS=\"$CFLAGS\"
++echo CXXFLAGS=\"$CXXFLAGS\"
++echo LDFLAGS=\"$LDFLAGS\"
++echo LIBS=\"$LIBS\"
++echo
++test x$LO_BIGENDIAN = x0 && echo '== Building for little-endian architecture'
++test x$LO_BIGENDIAN = x1 && echo '== Building for big-endian architecture'
++test x$enable_debug = xyes && echo '== Compiling in debug mode'
++test x$enable_tests = xno && echo '== Disabled tests'
++test x$enable_networktests = xno && echo '== Disabled network-based tests'
++test x$enable_tools = xno && echo '== Disabled tools'
++test x$enable_examples = xno && echo '== Disabled examples'
++test x$HAVE_LAMBDA = xyes && echo '== Compiling C++ bindings' || echo '== Not compiling C++ bindings'
++test x$DOXYGEN = x && echo '== Disabled documentation (no doxygen found)'
++echo
++
+ AC_CONFIG_FILES([
+ 	Makefile
+ 	src/Makefile
+diff --git a/src/Makefile.am b/src/Makefile.am
+index d84818e..824f8bd 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -1,4 +1,7 @@
+-SUBDIRS = . tools
++SUBDIRS = .
++if COMPILE_TOOLS
++SUBDIRS += tools
++endif
+ 
+ MAINTAINERCLEANFILES = Makefile.in
+ 
+@@ -25,6 +28,7 @@ liblo_la_LDFLAGS  = $(lt_windows) -export-dynamic -version-info @LO_SO_VERSION@
+ 
+ # Test programs depend on server threads, so skip them if threads are
+ # disabled.
++if COMPILE_TESTS
+ if ENABLE_THREADS
+ noinst_PROGRAMS = testlo subtest test_bidirectional_tcp
+ TESTS = testlo test_bidirectional_tcp
+@@ -33,6 +37,7 @@ noinst_PROGRAMS += cpp_test
+ TESTS += cpp_test
+ endif
+ endif
++endif
+ 
+ testlo_CFLAGS = -Wall -I$(top_srcdir)
+ testlo_SOURCES = testlo.c
+@@ -51,7 +56,11 @@ cpp_test_SOURCES = cpp_test.cpp
+ cpp_test_LDADD = liblo.la
+ 
+ test: all
++if COMPILE_TESTS
+ 	for i in $(TESTS); do echo Running $$i; if ! ./$$i; then exit 1; fi; done
++endif
+ 
+ memtest: all
++if COMPILE_TESTS
+ 	LD_LIBRARY_PATH=.libs valgrind --tool=memcheck .libs/testlo
++endif
+-- 
+1.9.rc1
+
diff --git a/debian/patches/Ensure-error-is-correctly-ignore-if-SO_REUSEPORT-is-.patch b/debian/patches/Ensure-error-is-correctly-ignore-if-SO_REUSEPORT-is-.patch
new file mode 100644
index 0000000..1bee6e8
--- /dev/null
+++ b/debian/patches/Ensure-error-is-correctly-ignore-if-SO_REUSEPORT-is-.patch
@@ -0,0 +1,86 @@
+From e5ba4c1708a51f4e19743f4a89c27598f991ce2d Mon Sep 17 00:00:00 2001
+From: Stephen Sinclair <radarsat1 at gmail.com>
+Date: Sun, 9 Feb 2014 00:55:19 +0100
+Subject: [PATCH 5/5] Ensure error is correctly ignore if SO_REUSEPORT is not
+ supported.
+
+Don't try to use it again if it was reported as unsupported.
+Apparently this can only be detected at run-time since kernel headers
+may not correspond with the running system.
+
+It could potentially be disabled entirely for Linux, but seems to
+cause failures with multicast on OS X if it is not set.
+---
+ src/server.c | 18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+diff --git a/src/server.c b/src/server.c
+index 3d01e88..407260e 100644
+--- a/src/server.c
++++ b/src/server.c
+@@ -78,6 +78,7 @@ typedef struct {
+ } queued_msg_list;
+ 
+ struct lo_cs lo_client_sockets = { -1, -1 };
++static int reuseport_supported = 1;
+ 
+ static int lo_can_coerce_spec(const char *a, const char *b);
+ static int lo_can_coerce(char a, char b);
+@@ -189,26 +190,28 @@ static unsigned int get_family_PF(const char *ip, const char *port)
+ }
+ #endif
+ 
+-static int lo_server_setsock_reuseaddr(lo_server s)
++static int lo_server_setsock_reuseaddr(lo_server s, int do_throw)
+ {
+     unsigned int yes = 1;
+     if (setsockopt(s->sockets[0].fd, SOL_SOCKET, SO_REUSEADDR,
+                    (char*)&yes, sizeof(yes)) < 0) {
+         int err = geterror();
+-        lo_throw(s, err, strerror(err), "setsockopt(SO_REUSEADDR)");
++		if (do_throw)
++			lo_throw(s, err, strerror(err), "setsockopt(SO_REUSEADDR)");
+         return err;
+     }
+     return 0;
+ }
+ 
+-static int lo_server_setsock_reuseport(lo_server s)
++static int lo_server_setsock_reuseport(lo_server s, int do_throw)
+ {
+ #ifdef SO_REUSEPORT
+     unsigned int yes = 1;
+     if (setsockopt(s->sockets[0].fd, SOL_SOCKET, SO_REUSEPORT,
+                    &yes, sizeof(yes)) < 0) {
+         int err = geterror();
+-        lo_throw(s, err, strerror(err), "setsockopt(SO_REUSEPORT)");
++		if (do_throw)
++			lo_throw(s, err, strerror(err), "setsockopt(SO_REUSEPORT)");
+         return err;
+     }
+ #endif
+@@ -485,17 +488,18 @@ lo_server lo_server_new_with_proto_internal(const char *group,
+         if (group != NULL
+             || proto == LO_TCP)
+         {
+-            err = lo_server_setsock_reuseaddr(s);
++            err = lo_server_setsock_reuseaddr(s, 1);
+             if (err) {
+                 lo_server_free(s);
+                 return NULL;
+             }
+         }
+ 
+-        if (group != NULL)
++        if (group != NULL && reuseport_supported)
+         {
+             /* Ignore the error if SO_REUSEPORT wasn't successful. */
+-            lo_server_setsock_reuseport(s);
++            if (lo_server_setsock_reuseport(s, 0))
++				reuseport_supported = 0;
+         }
+ 
+ #if defined(WIN32) || defined(_MSC_VER)
+-- 
+1.9.rc1
+
diff --git a/debian/patches/Patch-for-missing-include-for-FreeBSD-from-SF-user-n.patch b/debian/patches/Patch-for-missing-include-for-FreeBSD-from-SF-user-n.patch
new file mode 100644
index 0000000..5b8c949
--- /dev/null
+++ b/debian/patches/Patch-for-missing-include-for-FreeBSD-from-SF-user-n.patch
@@ -0,0 +1,25 @@
+From 0b8977f3cd97ee06dae73be4a792f1bfd66c2cfe Mon Sep 17 00:00:00 2001
+From: Stephen Sinclair <radarsat1 at gmail.com>
+Date: Tue, 4 Feb 2014 08:46:11 +0100
+Subject: [PATCH 2/5] Patch for missing #include for FreeBSD from SF user
+ nemysisbsd.
+
+---
+ src/lo_types_internal.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/lo_types_internal.h b/src/lo_types_internal.h
+index dcaae54..5ef3b95 100644
+--- a/src/lo_types_internal.h
++++ b/src/lo_types_internal.h
+@@ -20,6 +20,7 @@
+ #else
+ #define closesocket close
+ #include <netdb.h>
++#include <netinet/in.h>
+ #include <arpa/inet.h>
+ #endif
+ 
+-- 
+1.9.rc1
+
diff --git a/debian/patches/Refactor-testlo-breaking-up-huge-list-of-tests-into-.patch b/debian/patches/Refactor-testlo-breaking-up-huge-list-of-tests-into-.patch
new file mode 100644
index 0000000..6a126f9
--- /dev/null
+++ b/debian/patches/Refactor-testlo-breaking-up-huge-list-of-tests-into-.patch
@@ -0,0 +1,2863 @@
+From 5c9e76a4fb505caf8e81e098971e265cc2cb26ba Mon Sep 17 00:00:00 2001
+From: Stephen Sinclair <radarsat1 at gmail.com>
+Date: Sat, 8 Feb 2014 13:58:29 +0100
+Subject: [PATCH 4/5] Refactor testlo, breaking up huge list of tests into
+ smaller functions.
+
+Also fixes any infinite wait loops and adds switch to disable
+network-based tests for when testlo must be tested on a firewalled or
+otherwise network-restricted environment such as the Debian build
+farm.
+---
+ configure.ac    |    4 +-
+ src/Makefile.am |    7 +-
+ src/subtest.c   |   25 +-
+ src/testlo.c    | 2411 +++++++++++++++++++++++++++++--------------------------
+ 4 files changed, 1300 insertions(+), 1147 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index 7be87bb..36ebc0d 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -232,6 +232,7 @@ AC_ARG_ENABLE(examples, [  --disable-examples      Disable compiling examples])
+ AM_CONDITIONAL([COMPILE_TESTS],[test x$enable_tests != xno])
+ AM_CONDITIONAL([COMPILE_TOOLS],[test x$enable_tools != xno])
+ AM_CONDITIONAL([COMPILE_EXAMPLES],[test x$enable_examples != xno])
++AM_CONDITIONAL([ENABLE_NETWORK_TESTS],[test x$enable_network_tests != xno])
+ 
+ if ! test x$enable_network_tests = xno; then
+   AC_DEFINE(ENABLE_NETWORK_TESTS, [1],
+@@ -244,7 +245,6 @@ echo About this liblo build:
+ echo
+ echo CC=\"$CC\"
+ echo CXX=\"$CXX\"
+-echo DOXYGEN=\"$DOXYGEN\"
+ echo CFLAGS=\"$CFLAGS\"
+ echo CXXFLAGS=\"$CXXFLAGS\"
+ echo LDFLAGS=\"$LDFLAGS\"
+@@ -254,7 +254,7 @@ test x$LO_BIGENDIAN = x0 && echo '== Building for little-endian architecture'
+ test x$LO_BIGENDIAN = x1 && echo '== Building for big-endian architecture'
+ test x$enable_debug = xyes && echo '== Compiling in debug mode'
+ test x$enable_tests = xno && echo '== Disabled tests'
+-test x$enable_networktests = xno && echo '== Disabled network-based tests'
++test x$enable_network_tests = xno && echo '== Disabled network-based tests'
+ test x$enable_tools = xno && echo '== Disabled tools'
+ test x$enable_examples = xno && echo '== Disabled examples'
+ test x$HAVE_LAMBDA = xyes && echo '== Compiling C++ bindings' || echo '== Not compiling C++ bindings'
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 824f8bd..b107a5e 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -31,13 +31,18 @@ liblo_la_LDFLAGS  = $(lt_windows) -export-dynamic -version-info @LO_SO_VERSION@
+ if COMPILE_TESTS
+ if ENABLE_THREADS
+ noinst_PROGRAMS = testlo subtest test_bidirectional_tcp
+-TESTS = testlo test_bidirectional_tcp
++TESTS = testlo
+ if HAVE_LAMBDA
+ noinst_PROGRAMS += cpp_test
++endif
++if ENABLE_NETWORK_TESTS
++TESTS += test_bidirectional_tcp
++if HAVE_LAMBDA
+ TESTS += cpp_test
+ endif
+ endif
+ endif
++endif
+ 
+ testlo_CFLAGS = -Wall -I$(top_srcdir)
+ testlo_SOURCES = testlo.c
+diff --git a/src/subtest.c b/src/subtest.c
+index f8e03e4..0259372 100644
+--- a/src/subtest.c
++++ b/src/subtest.c
+@@ -23,6 +23,8 @@
+ 
+ #include "lo/lo.h"
+ 
++static int subtest_count = 0;
++
+ int subtest_handler(const char *path, const char *types, lo_arg ** argv,
+                     int argc, lo_message data, void *user_data);
+ 
+@@ -30,6 +32,7 @@ int main(int argc, char *argv[])
+ {
+     lo_server_thread st;
+     lo_address t;
++    int tries;
+ 
+     printf("entered subtest\n");
+ 
+@@ -48,11 +51,19 @@ int main(int argc, char *argv[])
+     t = lo_address_new_from_url(argv[1]);
+     lo_send(t, "/subtest", "i", 0xf00);
+ 
++    tries = 400;
++    while (subtest_count == 0 && (--tries > 0)) {
+ #if defined(WIN32) || defined(_MSC_VER)
+-    Sleep(4000);
++        Sleep(10);
+ #else
+-    sleep(4);
++        usleep(10000);
+ #endif
++    }
++
++    if (tries == 0) {
++        printf("subtest: too many tries\n");
++        exit(1);
++    }
+ 
+     return 0;
+ }
+@@ -65,6 +76,7 @@ int subtest_handler(const char *path, const char *types, lo_arg ** argv,
+     static char *uri = NULL;
+ 
+     printf("subtest: got reply (%s)\n", path);
++
+     if (!uri) {
+         uri = lo_address_get_url(a);
+     } else {
+@@ -77,7 +89,9 @@ int subtest_handler(const char *path, const char *types, lo_arg ** argv,
+         }
+         free(new_uri);
+     }
++
+     lo_send(a, "/subtest-reply", "i", 0xbaa);
++
+     if (lo_address_errno(a)) {
+         fprintf(stderr, "subtest error %d: %s\n", lo_address_errno(a),
+                 lo_address_errstr(a));
+@@ -86,15 +100,16 @@ int subtest_handler(const char *path, const char *types, lo_arg ** argv,
+     }
+ 
+     for (i = 0; i < 10; i++) {
++        lo_send(a, "/subtest-reply", "i", 0xbaa + i);
+ #if defined(WIN32) || defined(_MSC_VER)
+-        /* TODO: Wait time of 2.233 not easily doable in Windows */
+         Sleep(2);
+ #else
+-        usleep(2233);
++        usleep(2000);
+ #endif
+-        lo_send(a, "/subtest-reply", "i", 0xbaa + i);
+     }
+ 
++    subtest_count ++;
++
+     return 0;
+ }
+ 
+diff --git a/src/testlo.c b/src/testlo.c
+index f7ecfa6..51898cf 100644
+--- a/src/testlo.c
++++ b/src/testlo.c
+@@ -47,9 +47,11 @@
+ #if defined(WIN32) || defined(_MSC_VER)
+ #define PATHDELIM "\\"
+ #define EXTEXE ".exe"
++#defnie SLEEP_MS(x) Sleep(x)
+ #else
+ #define PATHDELIM "/"
+ #define EXTEXE ""
++#define SLEEP_MS(x) usleep((x)*1000)
+ #endif
+ 
+ #ifndef MSG_NOSIGNAL
+@@ -57,9 +59,13 @@
+ #endif
+ 
+ #define TEST(cond) if (!(cond)) { fprintf(stderr, "FAILED " #cond \
+-					  " at %s:%d\n", __FILE__, __LINE__); \
+-				  exit(1); } \
+-		   else { printf("passed " #cond "\n"); }
++                      " 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;
+@@ -78,6 +84,7 @@ 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] = "ABCDE";
+ 
+@@ -86,6 +93,8 @@ 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);
+@@ -131,1398 +140,1522 @@ 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_tcp_nonblock();
+-
+ 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()
+ {
+-    lo_blob btest = lo_blob_new(sizeof(testdata), testdata);
+-    lo_server_thread st, sta, stb;
+-    lo_server s = lo_server_new(NULL, error);
+-    lo_bundle b;
+-    lo_message m1, m2;
+-    char *server_url, *path, *protocol, *host, *port;
+-    const char *host2, *port2;
++#ifdef ENABLE_NETWORK_TESTS
++    lo_server_thread st;
+     lo_address a;
+-    uint8_t midi_data[4] = { 0xff, 0xf7, 0xAA, 0x00 };
+-    union end_test32 et32;
+-    union end_test64 et64;
+-    lo_timetag tt = { 0x1, 0x80000000 }, sched;
+-    int count;
+-    int proto;
+-    char cmd[256];
+-    const char *p;
+-    int i, rc;
++#endif
+ 
+-    test_version();
++    atexit(exitcheck);
+ 
++    test_version();
+     test_deserialise();
++    test_types();
++    test_url();
++    test_address();
++    test_blob();
+ 
+-    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);
+-    lo_server_thread_start(st);
+-#if defined(WIN32) || defined(_MSC_VER)
+-    Sleep(4);
++#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(st);
++    test_unix_sockets();
++    test_tcp();
++    test_tcp_nonblock();
++    cleanup(st, a);
+ #else
+-    usleep(4000);
++	done = 1;
+ #endif
+-    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);
+-
+-    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);
+-
+-
+-    atexit(exitcheck);
+ 
+-    printf("type tests\n");
+-    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");
+-    }
++    return 0;
++}
+ 
+-    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);
++void exitcheck(void)
++{
++    if (!done) {
++        fprintf(stderr, "\ntest run not completed\n" PACKAGE_NAME
++                " test FAILED\n");
+     } else {
+-        printf("passed 64bit endian conversion\n");
++        printf(PACKAGE_NAME " test PASSED\n");
+     }
+-    printf("\n");
++}
+ 
+-    /* 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);
++void error(int num, const char *msg, const char *path)
++{
++    printf("liblo server error %d in %s: %s", num, path, msg);
++    if (!error_okay)
+         exit(1);
+-    } else {
+-        printf("passed lo_url_get_path() test1\n");
+-    }
+-    free(path);
++    else
++        printf(" (expected)\n");
++}
+ 
+-    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");
++void rep_error(int num, const char *msg, const char *path)
++{
++    if (num != 9904) {
++        error(num, msg, path);
+     }
+-    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);
++int generic_handler(const char *path, const char *types, lo_arg ** argv,
++                    int argc, lo_message data, void *user_data)
++{
++    int i;
++    HANDLER("generic");
+ 
+-    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");
++    printf("path: <%s>\n", path);
++    for (i = 0; i < argc; i++) {
++        printf("arg %d '%c' ", i, types[i]);
++        lo_arg_pp(types[i], argv[i]);
++        printf("\n");
+     }
+-    free(protocol);
++    printf("\n");
+ 
+-    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");
+-    }
++    return 1;
++}
+ 
+-    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);
++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("passed lo_url_get_protocol_id() test2\n");
++        printf("Reply sent to %s\n\n", url);
+     }
++    free(server_url);
++    free(url);
+ 
+-    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");
+-    }
++    return 0;
++}
+ 
+-    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");
+-    }
++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++;
+ 
+-    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);
++    return 0;
++}
+ 
+-    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);
++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");
+ 
+-    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);
++    if (strcmp(path, "/lotsofformats")) {
++        fprintf(stderr, "path != /lotsofformats\n");
+         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("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 = 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[4] == 'E');
++    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");
+ 
+-    a = lo_address_new_from_url("osc.tcp://foo.example.com:9999/");
+-    host2 = lo_address_get_hostname(a);
+-    if (strcmp(host2, "foo.example.com")) {
+-        printf("failed lo_address_get_hostname() test\n");
+-        printf("'%s' != 'foo.example.com'\n", host2);
+-        exit(1);
+-    } else {
+-        printf("passed lo_address_get_hostname() test\n");
+-    }
++    return 0;
++}
+ 
+-    port2 = lo_address_get_port(a);
+-    if (strcmp(port2, "9999")) {
+-        printf("failed lo_address_get_port() test\n");
+-        printf("'%s' != '9999'\n", port2);
+-        exit(1);
+-    } else {
+-        printf("passed lo_address_get_port() test\n");
+-    }
++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");
+ 
+-    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");
+-    }
++    ts = lo_message_get_timestamp(data);
++    arg_ts = argv[1]->t;
+ 
+-    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);
++    if (bundled) {
++        TEST((ts.sec == arg_ts.sec) && (ts.frac == arg_ts.frac));
+     } else {
+-        printf("passed lo_address_get_url() test\n");
++        TEST(ts.sec == LO_TT_IMMEDIATE.sec
++             && ts.frac == LO_TT_IMMEDIATE.frac);
+     }
+-    free(server_url);
+-    lo_address_free(a);
+-    printf("\n");
++    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;
+ 
+-    /* Test blod 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);
+-    }
++    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;
++}
+ 
+-    /* Server method handler tests */
+-    server_url = lo_server_thread_get_url(st);
+-    a = lo_address_new_from_url(server_url);
+-    printf("Server URL: %s\n", server_url);
+-    free(server_url);
++int pattern_handler(const char *path, const char *types, lo_arg ** argv,
++                    int argc, lo_message data, void *user_data)
++{
++    HANDLER("pattern");
+ 
+-    /* add method that will match the path /foo/bar, with two numbers, coerced
+-     * to float and int */
++    pattern_count++;
++    printf("pattern matched %s\n", (char *) user_data);
+ 
+-    lo_server_thread_add_method(st, "/foo/bar", "fi", foo_handler,
+-                                lo_server_thread_get_server(st));
++    return 0;
++}
+ 
+-    lo_server_thread_add_method(st, "/reply", "s", reply_handler, NULL);
++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");
+ 
+-    lo_server_thread_add_method(st, "/lotsofformats", "fisbmhtdSccTFNI",
+-                                lots_handler, NULL);
++    a = lo_message_get_source(data);
+ 
+-    lo_server_thread_add_method(st, "/coerce", "dfhiSs",
+-                                coerce_handler, NULL);
++    subtest_count++;
++    lo_send_from(a, lo_server_thread_get_server(user_data),
++                 LO_TT_IMMEDIATE, "/subtest", "i", subtest_count);
+ 
+-    lo_server_thread_add_method(st, "/bundle", NULL, bundle_handler, NULL);
+-    lo_server_thread_add_method(st, "/timestamp", NULL,
+-                                timestamp_handler, NULL);
+-    lo_server_thread_add_method(st, "/jitter", "ti", jitter_handler, NULL);
++    return 0;
++}
+ 
+-    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");
++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++;
+ 
+-    lo_server_thread_add_method(st, "/subtest", "i", subtest_handler, st);
++    return 0;
++}
+ 
+-    lo_server_thread_add_method(st, "/subtest-reply", "i",
+-                                subtest_reply_handler, NULL);
++int quit_handler(const char *path, const char *types, lo_arg ** argv,
++                 int argc, lo_message data, void *user_data)
++{
++    HANDLER("quit");
++    done = 1;
+ 
+-    /* add method that will match any path and args */
+-    lo_server_thread_add_method(st, NULL, NULL, generic_handler, NULL);
++    return 0;
++}
+ 
+-    /* add method that will match the path /quit with no args */
+-    lo_server_thread_add_method(st, "/quit", "", quit_handler, NULL);
++int test_version()
++{
++    int major, minor, lt_maj, lt_min, lt_bug;
++    char extra[256];
++    char string[256];
++    DOING("test_version");
+ 
+-    /* check that the thread restarts */
+-    lo_server_thread_start(st);
+-    lo_server_thread_stop(st);
+-    lo_server_thread_start(st);
++    lo_version(string, 256,
++               &major, &minor, extra, 256,
++               &lt_maj, &lt_min, &lt_bug);
+ 
+-    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);
+-    }
++    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");
+ 
+-    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);
+-    }
++    return 0;
++}
+ 
+-    test_validation(a);
+-    test_multicast(st);
++int test_varargs(lo_address a, const char *path, const char *types, ...)
++{
++    va_list ap;
++    lo_message m;
++    int error;
+ 
+-    lo_send(a, "/", "i", 242);
+-    lo_send(a, "/pattern/", "i", 243);
++    DOING("test_varargs");
+ 
+-#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);
++    m = lo_message_new();
+ 
+-    TEST(test_varargs
+-         (a, "/lotsofformats", "fisbmhtdSccTFNI", 0.12345678f, 123, "123",
+-          btest, midi_data, 0x0123456789abcdefULL, tt, 0.9999, "sym", 'X',
+-          'Y', LO_ARGS_END) == 0);
++    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;
++}
+ 
+-#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.
++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;
++    }
++}
+ 
+-    // 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
++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;
+ 
+-    // test lo_message_add
+-    m1 = lo_message_new();
+-    TEST(lo_message_add(m1, "fisbmhtdSccTFNI", 0.12345678f, 123, "123",
+-                        btest, midi_data, 0x0123456789abcdefULL, tt,
+-                        0.9999, "sym", 'X', 'Y') == 0);
+-    lo_send_message(a, "/lotsofformats", m1);
+-    lo_message_free(m1);
++    lo_blob btest;
++    lo_timetag tt = { 0x1, 0x80000000 };
++    lo_blob b = NULL;
+ 
+-    lo_blob_free(btest);
++    DOING("test_deserialise");
+ 
+-    lo_send(a, "/pattern/*", "s", "a");
+-    lo_send(a, "/pattern/ba[rz]", "s", "b");
++    btest = lo_blob_new(sizeof(testdata), testdata);
+ 
+-    server_url = lo_server_thread_get_url(st);
++    // 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
+ 
+-#ifdef WIN32
+-    {
+-        char cwd[2048];
+-        _getcwd(cwd, 2048);
+-        sprintf(cmd, "%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(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++) {
+-        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);
+-        }
+-    }
+-#endif
++    // 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]);
+ 
+-    free(server_url);
++    // serialise it
++    len = lo_message_length(msg, "/foo");
++    printf("serialise message_length=%d\n", (int) len);
++    buf = calloc(len, sizeof(char));
++    size = 0;
++    tmp = lo_message_serialise(msg, "/foo", buf, &size);
++    TEST(tmp == buf && size == len && 92 == len);
++    lo_message_free(msg);
+ 
+-#if defined(WIN32) || defined(_MSC_VER)
+-    Sleep(2000);
+-#else
+-    sleep(2);
+-#endif
+-    TEST(reply_count == 3);
+-    TEST(pattern_count == 5);
+-    TEST(subtest_count == 2);
+-    TEST(subtest_reply_count == 22);
+-    printf("\n");
++    // 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);
+ 
+-    {
+-        lo_timetag t = { 10, 0xFFFFFFFC };
+-        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);
++    // 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]);
+ 
+-    /* This should be safe for multiple copies of the same message. */
+-    lo_bundle_free_messages(b);
++    // serialise it again, compare
++    len = lo_message_length(msg, "/foo");
++    printf("serialise message_length=%d\n", (int) len);
++    buf2 = calloc(len, sizeof(char));
++    size = 0;
++    tmp = lo_message_serialise(msg, "/foo", buf2, &size);
++    TEST(tmp == buf2 && size == len && 92 == len);
++    TEST(!memcmp(buf, buf2, len));
++    lo_message_free(msg);
+ 
+-    {
+-        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);
++    lo_blob_free(btest);
++    free(buf);
++    free(buf2);
+ 
+-/* 
+-    lo_send_bundle(a, b);
+-    if (a->errnum) {
+-	printf("error %d: %s\n", a->errnum, a->errstr);
+-	exit(1);
+-    }
+-*/
+-    TEST(lo_send_bundle(a, b) == 88);
++    // deserialise failure tests with invalid message data
+ 
+-    /* Should fail to add a bundle recursively */
+-    TEST(lo_bundle_add_bundle(b, b) != 0)
++    msg = lo_message_deserialise(data, 0, &result);     // 0 size
++    TEST(NULL == msg && LO_ESIZE == result);
+ 
+-    /* 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);
++    snprintf(data, 256, "%s", "/foo");  // unterminated path string
++    msg = lo_message_deserialise(data, 4, &result);
++    TEST(NULL == msg && LO_EINVALIDPATH == result);
+ 
+-        /* Test freeing out-of-order copies of messages in a bundle. */
+-        lo_bundle_free_recursive(b2);
+-    }
++    snprintf(data, 256, "%s", "/f_o");  // non-0 in pad area
++    msg = lo_message_deserialise(data, 4, &result);
++    TEST(NULL == msg && LO_EINVALIDPATH == result);
+ 
+-    {
+-        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);
++    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);
+ 
+-    lo_timetag_now(&sched);
++    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);
+ 
+-    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);
++    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);
+ 
+-    lo_send_bundle(a, b);
+-    lo_message_free(m1);
+-    lo_bundle_free(b);
++    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);
++}
+ 
+-    lo_send_timestamped(a, sched, "/bundle", "s",
+-                        "lo_send_timestamped() test");
++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;
+ 
+-    /* 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);
++    /* 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
+ 
+-#define JITTER_ITS 25
+-    /* jitter tests */
+-    {
+-        lo_timetag stamps[JITTER_ITS];
+-        lo_timetag now;
++    DOING("test_validation");
+ 
+-        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);
+-        }
++    /* 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;
+     }
+ 
+-#if defined(WIN32) || defined(_MSC_VER)
+-    Sleep(2000);
+-#else
+-    sleep(2);
++    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
++}
+ 
+-    lo_address_free(a);
++void test_multicast(lo_server_thread st)
++{
++    lo_server ms;
++    lo_address ma;
+ 
+-    TEST(lo_server_thread_events_pending(st));
++    DOING("test_multicast");
+ 
+-    while (lo_server_thread_events_pending(st)) {
+-        printf("pending events, wait...\n");
+-#if defined(WIN32) || defined(_MSC_VER)
+-        fflush(stdout);
+-        Sleep(1000);
+-#else
+-        sleep(1);
+-#endif
++    /* 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);
++}
+ 
+-    TEST(bundle_count == 7);
+-    printf("\n");
++int test_received = 0;
++int ok_received = 0;
++int recv_times = 0;
+ 
+-    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);
++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;
++}
+ 
+-    server_url = lo_server_get_url(s);
++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;
++}
+ 
+-    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);
++void *test_tcp_thread(void *context)
++{
++    lo_server s;
++    printf("TCP thread started.\n");
+ 
+-    count = 0;
+-    while (!lo_server_recv_noblock(s, 10) && count++ < 1000) {
++    s = lo_server_new_with_proto("9000", LO_TCP, error);
++    if (!s) {
++        printf("Aborting thread, s=%p\n", s);
++        return (void*)1;
+     }
+-    if (count >= 1000) {
+-        printf("lo_server_recv_noblock() test failed\n");
+ 
+-        exit(1);
+-    }
++    lo_server_add_method(s, "/test", "is", test_handler, 0);
++    lo_server_add_method(s, "/ok", "", ok_handler, 0);
+ 
+-    /* Delete methods */
+-    lo_server_thread_del_method(st, "/coerce", "dfhiSs");
+-    lo_server_del_method(s, NULL, NULL);
++    while (!(done || tcp_done)) {
++        printf("lo_server_recv_noblock()\n");
++        recv_times += 1;
++        if (!lo_server_recv_noblock(s, 0))
++            SLEEP_MS(300);
++    }
+ 
+-    lo_address_free(a);
++    printf("Freeing.\n");
+     lo_server_free(s);
+-    free(server_url);
++    printf("Done. Thread ending.\n");
++    return 0;
++}
+ 
+-#if !defined(WIN32) && !defined(_MSC_VER)
+-    {                           /* UNIX domain tests */
+-        lo_address ua;
+-        lo_server us;
+-        char *addr;
+-
+-        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
++#define SLIP_END        0300    /* indicates end of packet */
+ 
+-    {                           /* TCP tests */
+-        lo_address ta;
+-        lo_server ts;
+-        char *addr;
++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};
+ 
+-        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);
+-    }
++    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};
+ 
+-    server_url = lo_server_thread_get_url(st);
+-    a = lo_address_new_from_url(server_url);
+-    /* exit */
+-    lo_send(a, "/quit", NULL);
+-    lo_address_free(a);
++    char *msg;
++    int msglen;
+ 
+-    while (!done) {
+-#if defined(WIN32) || defined(_MSC_VER)
+-        Sleep(1);
+-#else
+-        usleep(1000);
+-#endif
++    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);
+     }
+ 
+-    lo_server_thread_free(st);
+-    free(server_url);
+-
+-    TEST(test_tcp_nonblock()==0)
++    printf("Connected, sending...\n");
+ 
+-    return 0;
+-}
++    switch (stream_type)
++    {
++    case 0:
++        printf("Testing a count-prefix stream.\n");
++        msg = prefixmsg;
++        msglen = sizeof(prefixmsg);
+ 
+-void exitcheck(void)
+-{
+-    if (!done) {
+-        fprintf(stderr, "\ntest run not completed\n" PACKAGE_NAME
+-                " test FAILED\n");
+-    } else {
+-        printf(PACKAGE_NAME " test PASSED\n");
++        *(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;
+     }
+-}
+ 
+-void error(int num, const char *msg, const char *path)
+-{
+-    printf("liblo server error %d in %s: %s", num, path, msg);
+-    if (!error_okay)
+-        exit(1);
+-    else
+-        printf(" (expected)\n");
+-}
++    if (0)
++    {
++        printf("Sending everything in one big chunk.\n");
+ 
+-void rep_error(int num, const char *msg, const char *path)
+-{
+-    if (num != 9904) {
+-        error(num, msg, path);
++        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");
+ 
+-int generic_handler(const char *path, const char *types, lo_arg ** argv,
+-                    int argc, lo_message data, void *user_data)
+-{
+-    int i;
++        SLEEP_MS(1000);
+ 
+-    printf("path: <%s>\n", path);
+-    for (i = 0; i < argc; i++) {
+-        printf("arg %d '%c' ", i, types[i]);
+-        lo_arg_pp(types[i], argv[i]);
+-        printf("\n");
++        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");
+     }
+-    printf("\n");
+ 
+-    return 1;
++    closesocket(sock);
+ }
+ 
+-int foo_handler(const char *path, const char *types, lo_arg ** argv,
+-                int argc, lo_message data, void *user_data)
++void test_tcp_nonblock()
+ {
+-    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);
++    void *retval;
++    pthread_t thread;
++
++    DOING("test_tcp_nonblock");
++
++    tcp_done = 0;
++    if (pthread_create(&thread, 0, test_tcp_thread, 0))
++    {
++        perror("pthread_create");
+         exit(1);
+-    } else {
+-        printf("Reply sent to %s\n\n", url);
+     }
+-    free(server_url);
+-    free(url);
+ 
+-    return 0;
+-}
++    SLEEP_MS(1000);
++    test_tcp_halfsend(0);
++    SLEEP_MS(1000);
++    test_tcp_halfsend(1);
++    SLEEP_MS(1000);
+ 
+-int reply_handler(const char *path, const char *types, lo_arg ** argv,
+-                  int argc, lo_message data, void *user_data)
+-{
+-    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++;
++    tcp_done = 1;
++    pthread_join(thread, &retval);
++    printf("Thread joined, retval=%p\n", retval);
+ 
+-    return 0;
++    TEST(retval == 0);
++    TEST(test_received == 2);
++    TEST(ok_received == 2);
++    TEST(recv_times > 10);
+ }
+ 
+-int lots_handler(const char *path, const char *types, lo_arg ** argv,
+-                 int argc, lo_message data, void *user_data)
++void test_types()
+ {
+-    lo_blob b;
+-    unsigned char *d;
++    union end_test32 et32;
++    union end_test64 et64;
+ 
+-    if (strcmp(path, "/lotsofformats")) {
+-        fprintf(stderr, "path != /lotsofformats\n");
++    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");
+     }
+-    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 = 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[4] == 'E');
+-    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');
+ 
++    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");
+-
+-    return 0;
+ }
+ 
+-int coerce_handler(const char *path, const char *types, lo_arg ** argv,
+-                   int argc, lo_message data, void *user_data)
++void test_url()
+ {
+-    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");
++    int proto;
++    char *path, *protocol, *host, *port;
+ 
+-    return 0;
+-}
++    DOING("test_url");
+ 
+-int bundle_handler(const char *path, const char *types, lo_arg ** argv,
+-                   int argc, lo_message data, void *user_data)
+-{
+-    bundle_count++;
+-    printf("received bundle\n");
++    /* 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);
+ 
+-    return 0;
+-}
++    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);
+ 
+-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;
++    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);
+ 
+-    lo_timetag ts, arg_ts;
+-    ts = lo_message_get_timestamp(data);
+-    arg_ts = argv[1]->t;
++    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);
+ 
+-    if (bundled) {
+-        TEST((ts.sec == arg_ts.sec) && (ts.frac == arg_ts.frac));
++    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 {
+-        TEST(ts.sec == LO_TT_IMMEDIATE.sec
+-             && ts.frac == LO_TT_IMMEDIATE.frac);
++        printf("passed lo_url_get_protocol_id() test1\n");
+     }
+-    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;
++    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");
++    }
+ 
+-    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;
++    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");
++    }
+ 
+-    return 0;
+-}
++    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");
++    }
+ 
+-int pattern_handler(const char *path, const char *types, lo_arg ** argv,
+-                    int argc, lo_message data, void *user_data)
+-{
+-    pattern_count++;
+-    printf("pattern matched %s\n", (char *) user_data);
++    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);
+ 
+-    return 0;
++    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");
+ }
+ 
+-int subtest_handler(const char *path, const char *types, lo_arg ** argv,
+-                    int argc, lo_message data, void *user_data)
++void test_address()
+ {
+-    lo_address a = lo_message_get_source(data);
++    const char *host, *port;
++    char *server_url;
++    lo_address a;
++    int proto;
+ 
+-    subtest_count++;
+-    printf("got subtest message %d\n", subtest_count);
+-    lo_send_from(a, lo_server_thread_get_server(user_data),
+-                 LO_TT_IMMEDIATE, "/subtest", "i", subtest_count);
++    DOING("test_address");
+ 
+-    return 0;
+-}
++    a = lo_address_new_from_url("osc://localhost/");
++    TEST(a != NULL);
++    lo_address_free(a);
+ 
+-int subtest_reply_handler(const char *path, const char *types,
+-                          lo_arg ** argv, int argc, lo_message data,
+-                          void *user_data)
+-{
+-    subtest_reply_count++;
+-    //printf("got subtest reply message %d\n", subtest_reply_count);
++    a = lo_address_new_from_url("osc.://localhost/");
++    TEST(a == NULL);
+ 
+-    return 0;
+-}
++    a = lo_address_new_from_url("osc.tcp://foo.example.com:9999/");
+ 
+-int quit_handler(const char *path, const char *types, lo_arg ** argv,
+-                 int argc, lo_message data, void *user_data)
+-{
+-    done = 1;
++    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");
++    }
+ 
+-    return 0;
+-}
++    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");
++    }
+ 
+-int test_version()
+-{
+-    int major, minor, lt_maj, lt_min, lt_bug;
+-    char extra[256];
+-    char string[256];
++    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");
++    }
+ 
+-    lo_version(string, 256,
+-               &major, &minor, extra, 256,
+-               &lt_maj, &lt_min, &lt_bug);
++    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");
++    }
+ 
+-    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");
++    free(server_url);
++    lo_address_free(a);
+ 
+-    return 0;
++    printf("\n");
+ }
+ 
+-int test_varargs(lo_address a, const char *path, const char *types, ...)
++void test_blob()
+ {
+-    va_list ap;
+-    lo_message m = lo_message_new();
+-    int error;
+-    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;
+-}
++    lo_blob btest;
+ 
+-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;
++    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_deserialise()
++void test_server_thread(lo_server_thread *pst, lo_address *pa)
+ {
+-    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_blob_new(sizeof(testdata), testdata);
+-    uint8_t midi_data[4] = { 0xff, 0xf7, 0xAA, 0x00 };
++    lo_address a;
++    char *server_url;
++    lo_server_thread st, sta, stb;
++    lo_blob btest;
+     lo_timetag tt = { 0x1, 0x80000000 };
+-    lo_blob b = NULL;
+ 
+-    // 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
++    DOING("test_server_thread");
+ 
+-    // 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]);
++    btest = lo_blob_new(sizeof(testdata), testdata);
+ 
+-    // serialise it
+-    len = lo_message_length(msg, "/foo");
+-    printf("serialise message_length=%d\n", (int) len);
+-    buf = calloc(len, sizeof(char));
+-    size = 0;
+-    tmp = lo_message_serialise(msg, "/foo", buf, &size);
+-    TEST(tmp == buf && size == len && 92 == len);
+-    lo_message_free(msg);
++    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);
+ 
+-    // 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);
++    /* leak check */
++    st = lo_server_thread_new(NULL, error);
++    if (!st) {
++        printf("Error creating server thread\n");
++        exit(1);
++    }
+ 
+-    // 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]);
++    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);
+ 
+-    // serialise it again, compare
+-    len = lo_message_length(msg, "/foo");
+-    printf("serialise message_length=%d\n", (int) len);
+-    buf2 = calloc(len, sizeof(char));
+-    size = 0;
+-    tmp = lo_message_serialise(msg, "/foo", buf2, &size);
+-    TEST(tmp == buf2 && size == len && 92 == len);
+-    TEST(!memcmp(buf, buf2, len));
+-    lo_message_free(msg);
++    lo_server_thread_add_method(st, "/lotsofformats", "fisbmhtdSccTFNI",
++                                lots_handler, NULL);
+ 
+-    lo_blob_free(btest);
+-    free(buf);
+-    free(buf2);
++    lo_server_thread_add_method(st, "/coerce", "dfhiSs",
++                                coerce_handler, NULL);
+ 
+-    // deserialise failure tests with invalid message data
++    lo_server_thread_add_method(st, "/bundle", NULL, bundle_handler, NULL);
++    lo_server_thread_add_method(st, "/timestamp", NULL,
++                                timestamp_handler, NULL);
++    lo_server_thread_add_method(st, "/jitter", "ti", jitter_handler, NULL);
+ 
+-    msg = lo_message_deserialise(data, 0, &result);     // 0 size
+-    TEST(NULL == msg && LO_ESIZE == result);
++    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");
+ 
+-    snprintf(data, 256, "%s", "/foo");  // unterminated path string
+-    msg = lo_message_deserialise(data, 4, &result);
+-    TEST(NULL == msg && LO_EINVALIDPATH == result);
++    lo_server_thread_add_method(st, "/subtest", "i", subtest_handler, st);
+ 
+-    snprintf(data, 256, "%s", "/f_o");  // non-0 in pad area
+-    msg = lo_message_deserialise(data, 4, &result);
+-    TEST(NULL == msg && LO_EINVALIDPATH == result);
++    lo_server_thread_add_method(st, "/subtest-reply", "i",
++                                subtest_reply_handler, NULL);
+ 
+-    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);
++    /* add method that will match any path and args */
++    lo_server_thread_add_method(st, NULL, NULL, generic_handler, NULL);
+ 
+-    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);
++    /* add method that will match the path /quit with no args */
++    lo_server_thread_add_method(st, "/quit", "", quit_handler, NULL);
+ 
+-    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);
++    /* check that the thread restarts */
++    lo_server_thread_start(st);
++    lo_server_thread_stop(st);
++    lo_server_thread_start(st);
+ 
+-    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);
++    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);
++    }
+ 
+-    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);
++    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);
++    }
+ 
+-    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);
++    lo_send(a, "/", "i", 242);
++    lo_send(a, "/pattern/", "i", 243);
+ 
+-    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);
++#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);
+ 
+-    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);
++    /* Delete methods */
++    lo_server_thread_del_method(st, "/coerce", "dfhiSs");
+ 
+-    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);
++    {
++        lo_server s = lo_server_new(NULL, error);
++        lo_server_del_method(s, NULL, NULL);
++        lo_server_free(s);
++    }
++
++    lo_blob_free(btest);
++
++    *pst = st;
++    *pa = a;
+ }
+ 
+-void test_validation(lo_address a)
++void test_message(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;
++    lo_blob btest;
++    lo_timetag tt = { 0x1, 0x80000000 };
++    lo_message m;
+ 
+-    /* 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_message");
+ 
+-    printf("validation\n");
++    btest = lo_blob_new(sizeof(testdata), testdata);
+ 
+-    /* 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;
+-    }
++    TEST(test_varargs
++         (a, "/lotsofformats", "fisbmhtdSccTFNI", 0.12345678f, 123, "123",
++          btest, midi_data, 0x0123456789abcdefULL, tt, 0.9999, "sym", 'X',
++          'Y', LO_ARGS_END) == 0);
+ 
+-    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__);
+-    }
+-#if defined(WIN32) || defined(_MSC_VER)
+-    Sleep(10);
+-#else
+-    usleep(10000);
++#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
+-    error_okay = eok;
+ #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_multicast(lo_server_thread st)
++void test_pattern(lo_address a)
+ {
+-    /* test multicast server and sender */
+-    /* message is sent from st otherwise reply doesn't work */
+-    lo_server ms = lo_server_new_multicast("224.0.1.1", "15432", error);
+-    lo_address 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);
++    DOING("test_pattern");
++    lo_send(a, "/pattern/*", "s", "a");
++    lo_send(a, "/pattern/ba[rz]", "s", "b");
+ }
+ 
+-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)
++void test_subtest(lo_server_thread st)
+ {
+-    printf("/test, %d, %s\n", argv[0]->i, &argv[1]->s);
+-    test_received += 1;
+-    return 0;
++    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");
+ }
+ 
+-int ok_handler(const char *path, const char *types,
+-                 lo_arg **argv, int argc, lo_message m,
+-                 void *data)
++void test_bundle(lo_server_thread st, lo_address a)
+ {
+-    printf("/ok\n");
+-    ok_received += 1;
+-    return 0;
+-}
++    lo_bundle b;
++    lo_timetag sched;
++    lo_message m1, m2;
++    const char *p;
++    int i, tries;
+ 
+-void *test_tcp_thread(void *context)
+-{
+-	lo_server s;
+-    printf("Thread started.\n");
++    lo_timetag t = { 10, 0xFFFFFFFC };
+ 
+-    s = lo_server_new_with_proto("9000", LO_TCP, error);
+-    if (!s) {
+-        printf("Aborting thread, s=%p\n", s);
+-        return (void*)1;
+-    }
++    DOING("test_bundle");
+ 
+-    lo_server_add_method(s, "/test", "is", test_handler, 0);
+-    lo_server_add_method(s, "/ok", "", ok_handler, 0);
++    b = lo_bundle_new(t);
+ 
+-    while (!done) {
+-        printf("lo_server_recv_noblock()\n");
+-        recv_times += 1;
+-        if (!lo_server_recv_noblock(s, 0))
+-#if defined(WIN32) || defined(_MSC_VER)
+-			Sleep(1000);
+-#else
+-            sleep(1);
+-#endif
+-    }
++    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);
+ 
+-    printf("Freeing.\n");
+-    lo_server_free(s);
+-    printf("Done. Thread ending.\n");
+-    return 0;
+-}
++    /* This should be safe for multiple copies of the same message. */
++    lo_bundle_free_messages(b);
+ 
+-#define SLIP_END        0300    /* indicates end of packet */
++    {
++        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);
+ 
+-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};
++    TEST(lo_bundle_count(b)==3);
++    TEST(lo_bundle_get_message(b,1,&p)==m2);
++    TEST(strcmp(p, "/bundle")==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};
++    TEST(lo_send_bundle(a, b) == 88);
+ 
+-    char *msg;
+-    int msglen;
++    /* Should fail to add a bundle recursively */
++    TEST(lo_bundle_add_bundle(b, b) != 0)
+ 
+-	struct sockaddr_in sa;
+-	int rc;
+-    int sock = socket(AF_INET, SOCK_STREAM, 0);
+-    if (sock < 0) {
+-        perror("socket");
+-        exit(1);
++    /* 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);
+     }
+-    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);
++
++    {
++        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);
+ 
+-    printf("Connected, sending...\n");
++    lo_timetag_now(&sched);
+ 
+-    switch (stream_type)
+-    {
+-    case 0:
+-        printf("Testing a count-prefix stream.\n");
+-        msg = prefixmsg;
+-        msglen = sizeof(prefixmsg);
++    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);
+ 
+-        *(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;
+-    }
++    lo_send_bundle(a, b);
++    lo_message_free(m1);
++    lo_bundle_free(b);
+ 
+-    if (0)
++    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 */
+     {
+-        printf("Sending everything in one big chunk.\n");
++        lo_timetag stamps[JITTER_ITS];
++        lo_timetag now;
+ 
+-        rc = send(sock, msg, msglen, 0);
+-        if (rc != msglen) printf("Error sending, rc = %d\n", rc);
+-        else printf("Sent.\n");
++        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);
++        }
+     }
+-    else
+-    {
+-        rc = send(sock, msg, 13, 0);
+-        if (rc != 13) printf("Error sending, rc = %d\n", rc);
+-        else printf("Sent.\n");
+ 
+-#if defined(WIN32) || defined(_MSC_VER)
+-        Sleep(3000);
+-#else
+-        sleep(3);
+-#endif
++    SLEEP_MS(2000);
+ 
+-        rc = send(sock, msg+13, 20, 0);
+-        if (rc != 20) printf("Error sending2, rc = %d\n", rc);
+-        else printf("Sent2.\n");
++    TEST(lo_server_thread_events_pending(st));
+ 
+-#if defined(WIN32) || defined(_MSC_VER)
+-        Sleep(3000);
+-#else
+-        sleep(3);
+-#endif
++    tries = 20;
++    while (lo_server_thread_events_pending(st)
++           && (--tries > 0))
++    {
++        printf("pending events, wait...\n");
++        fflush(stdout);
++        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");
++    if (tries == 0) {
++        printf("server thread still has pending"
++               " events after 20 seconds!\n");
++        exit(1);
+     }
+ 
+-    closesocket(sock);
++    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);
+ }
+ 
+-int test_tcp_nonblock()
++void test_nonblock()
+ {
+-    void *retval;
+-    pthread_t thread;
++    lo_server s;
++    char *server_url;
++    lo_address a;
+ 
+-	printf("Testing TCP non-blocking behaviour.\n");
++    DOING("test_nonblock");
+ 
+-    done = 0;
+-    if (pthread_create(&thread, 0, test_tcp_thread, 0))
+-	{
+-		perror("pthread_create");
+-		return 1;
+-	}
++    s = lo_server_new(NULL, error);
++    server_url = lo_server_get_url(s);
+ 
+-#if defined(WIN32) || defined(_MSC_VER)
+-        Sleep(1000);
+-#else
+-        sleep(1);
+-#endif
++    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);
+ 
+-    test_tcp_halfsend(0);
++    int tries = 1000;
++    while (!lo_server_recv_noblock(s, 10) && --tries > 0)
++    {
++    }
+ 
+-#if defined(WIN32) || defined(_MSC_VER)
+-        Sleep(1000);
+-#else
+-        sleep(1);
+-#endif
++    if (tries == 0) {
++        printf("lo_server_recv_noblock() test failed\n");
++        exit(1);
++    }
+ 
+-    test_tcp_halfsend(1);
++    free(server_url);
++    lo_server_free(s);
++    lo_address_free(a);
++}
+ 
+-#if defined(WIN32) || defined(_MSC_VER)
+-        Sleep(1000);
+-#else
+-        sleep(1);
++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
++}
+ 
+-    done = 1;
+-    pthread_join(thread, &retval);
+-    printf("Thread joined, retval=%p\n", retval);
++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);
+ 
+-    return (retval!=0
+-            && test_received == 2
+-            && ok_received == 2
+-            && recv_times == 19);
++    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: */
+-- 
+1.9.rc1
+
diff --git a/debian/patches/no-so-reuseport.patch b/debian/patches/no-so-reuseport.patch
deleted file mode 100644
index 79ea487..0000000
--- a/debian/patches/no-so-reuseport.patch
+++ /dev/null
@@ -1,16 +0,0 @@
-Description: Disable SO_REUSEPORT usage unconditionally
-The correct solution would be a non-fatal runtime check, currently being
-discussed with upstream. This patch evidently cannot be forwarded as-is.
-Forwarded: no
-Author: Felipe Sateler <fsateler at debian.org>
---- a/src/server.c
-+++ b/src/server.c
-@@ -203,7 +203,7 @@ static int lo_server_setsock_reuseaddr(l
- 
- static int lo_server_setsock_reuseport(lo_server s)
- {
--#ifdef SO_REUSEPORT
-+#if 0
-     unsigned int yes = 1;
-     if (setsockopt(s->sockets[0].fd, SOL_SOCKET, SO_REUSEPORT,
-                    &yes, sizeof(yes)) < 0) {
diff --git a/debian/patches/series b/debian/patches/series
index 747f730..7099707 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1 +1,4 @@
-no-so-reuseport.patch
+Patch-for-missing-include-for-FreeBSD-from-SF-user-n.patch
+Add-several-configure-options-for-the-sake-of-modula.patch
+Refactor-testlo-breaking-up-huge-list-of-tests-into-.patch
+Ensure-error-is-correctly-ignore-if-SO_REUSEPORT-is-.patch
diff --git a/debian/rules b/debian/rules
index 4a087ce..2999a9e 100755
--- a/debian/rules
+++ b/debian/rules
@@ -39,5 +39,8 @@ DEB_UPSTREAM_URL = http://downloads.sourceforge.net/liblo
 DEB_UPSTREAM_TARBALL_MD5 = e2a4391a08b49bb316c03e2034e06fa2
 
 #DEB_SHLIBDEPS_INCLUDE = lib/.libs
-DEB_CONFIGURE_USER_FLAGS = --enable-static
+DEB_CONFIGURE_USER_FLAGS = --enable-static \
+			   --disable-network-tests \
+			   --enable-tests \
+			   --enable-examples
 DEB_MAKE_CHECK_TARGET=test

-- 
liblo packaging



More information about the pkg-multimedia-commits mailing list