From 086b29314305f1de2a11cfd6b38cdd5fd30e3f76 Mon Sep 17 00:00:00 2001
From: Rich Megginson <rmeggins@redhat.com>
Date: Fri, 6 Mar 2015 09:37:27 -0700
Subject: [PATCH] Ticket #48105 create tests - tests subdir - test
 server/client programs - test scripts

https://fedorahosted.org/389/ticket/48105
Reviewed by: ???
Branch: master
Fix Description: Added a tests subdirectory for test code, scripts,
data, etc.  Added a "check" make target.  This is a standard automake
target for running tests.  Note that the tests are not built and
run until you do a "make check".
automake testing:
https://www.gnu.org/software/automake/manual/automake.html#Tests
Platforms tested: Fedora 20
Flag Day: no
Doc impact: no
---
 .gitignore                        |  13 ++
 Makefile.am                       |  18 ++
 Makefile.in                       | 464 ++++++++++++++++++++++++++++++++++++--
 file-test.sh                      |  58 -----
 ns_tls.c                          |   2 +
 ns_tls.h                          |   2 -
 put.py                            |  16 --
 socket-event-test-threads.c       |  99 --------
 socket-event-test.c               | 220 ------------------
 test-driver                       | 127 +++++++++++
 test-protocol.c                   | 426 ----------------------------------
 test-protocol.h                   |  10 -
 tests/file-test.sh                |  67 ++++++
 tests/put.py                      |  16 ++
 tests/socket-event-test-threads.c | 101 +++++++++
 tests/test-protocol.c             | 425 ++++++++++++++++++++++++++++++++++
 tests/test-protocol.h             |  10 +
 17 files changed, 1229 insertions(+), 845 deletions(-)
 create mode 100644 .gitignore
 delete mode 100755 file-test.sh
 delete mode 100644 put.py
 delete mode 100644 socket-event-test-threads.c
 delete mode 100644 socket-event-test.c
 create mode 100755 test-driver
 delete mode 100644 test-protocol.c
 delete mode 100644 test-protocol.h
 create mode 100755 tests/file-test.sh
 create mode 100644 tests/put.py
 create mode 100644 tests/socket-event-test-threads.c
 create mode 100644 tests/test-protocol.c
 create mode 100644 tests/test-protocol.h

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cee8d70
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+.cproject
+.project
+.settings
+autom4te.cache
+*.patch
+*~
+liblfds/GNUmakefile
+liblfds/obj/
+m4/libtool.m4
+m4/ltoptions.m4
+m4/ltsugar.m4
+m4/ltversion.m4
+m4/lt~obsolete.m4
diff --git a/Makefile.am b/Makefile.am
index e154e3e..14a8212 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,6 +2,8 @@
 ACLOCAL_AMFLAGS = -I m4
 SUBDIRS = @lfds_subdir@
 
+TESTS = file-test.sh
+
 #------------------------
 # Compiler Flags
 #------------------------
@@ -49,6 +51,22 @@ libnunc_stans_la_SOURCES = ns_thrpool.c \
 libnunc_stans_la_CPPFLAGS = $(AM_CPPFLAGS)
 libnunc_stans_la_LIBADD = $(NSPR_LINK) $(LFDS_LINK) $(LIBEVENT_LINK) $(LIBTEVENT_LINK)
 
+#////////////////////////////////////////////////////////////////
+#
+#   Tests
+#
+#////////////////////////////////////////////////////////////////
+check_PROGRAMS = socket-event-test-threads
+check_SCRIPTS = tests/file-test.sh tests/put.py
+file-test.sh: $(srcdir)/tests/file-test.sh put.py
+	cp $(srcdir)/tests/file-test.sh $@
+put.py: $(srcdir)/tests/put.py
+	cp $^ $@
+
+socket_event_test_threads_SOURCES = tests/socket-event-test-threads.c tests/test-protocol.c ns_tls.c
+socket_event_test_threads_CPPFLAGS = $(AM_CPPFLAGS) -I/usr/include/nss3
+socket_event_test_threads_LDADD = libnunc-stans.la -lssl3 -lnss3 -lnssutil3
+
 # generate documentation from these files
 generate_doc_files = $(srcdir)/nunc-stans.h
 install-data-hook: $(srcdir)/nunc-stans.doxy
diff --git a/Makefile.in b/Makefile.in
index 93a1c85..efd363e 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -79,11 +79,13 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
+check_PROGRAMS = socket-event-test-threads$(EXEEXT)
 subdir = .
 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
 	$(top_srcdir)/configure $(am__configure_deps) \
 	$(srcdir)/config.h.in $(srcdir)/nunc-stans.pc.in depcomp \
-	compile config.guess config.sub install-sh missing ltmain.sh
+	test-driver compile config.guess config.sub install-sh missing \
+	ltmain.sh
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
 	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
@@ -139,6 +141,13 @@ AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
 am__v_lt_1 = 
+am__dirstamp = $(am__leading_dot)dirstamp
+am_socket_event_test_threads_OBJECTS = tests/socket_event_test_threads-socket-event-test-threads.$(OBJEXT) \
+	tests/socket_event_test_threads-test-protocol.$(OBJEXT) \
+	socket_event_test_threads-ns_tls.$(OBJEXT)
+socket_event_test_threads_OBJECTS =  \
+	$(am_socket_event_test_threads_OBJECTS)
+socket_event_test_threads_DEPENDENCIES = libnunc-stans.la
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -173,8 +182,10 @@ AM_V_CCLD = $(am__v_CCLD_@AM_V@)
 am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
 am__v_CCLD_0 = @echo "  CCLD    " $@;
 am__v_CCLD_1 = 
-SOURCES = $(libnunc_stans_la_SOURCES)
-DIST_SOURCES = $(libnunc_stans_la_SOURCES)
+SOURCES = $(libnunc_stans_la_SOURCES) \
+	$(socket_event_test_threads_SOURCES)
+DIST_SOURCES = $(libnunc_stans_la_SOURCES) \
+	$(socket_event_test_threads_SOURCES)
 RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
 	ctags-recursive dvi-recursive html-recursive info-recursive \
 	install-data-recursive install-dvi-recursive \
@@ -196,7 +207,7 @@ am__recursive_targets = \
   $(RECURSIVE_CLEAN_TARGETS) \
   $(am__extra_recursive_targets)
 AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
-	cscope distdir dist dist-all distcheck
+	cscope check recheck distdir dist dist-all distcheck
 am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
 	$(LISP)config.h.in
 # Read a list of newline-separated strings from the standard input,
@@ -218,6 +229,182 @@ am__define_uniq_tagged_files = \
 ETAGS = etags
 CTAGS = ctags
 CSCOPE = cscope
+am__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red='[0;31m'; \
+    grn='[0;32m'; \
+    lgn='[1;32m'; \
+    blu='[1;34m'; \
+    mgn='[0;35m'; \
+    brg='[1m'; \
+    std='[m'; \
+  fi; \
+}
+am__recheck_rx = ^[ 	]*:recheck:[ 	]*
+am__global_test_result_rx = ^[ 	]*:global-test-result:[ 	]*
+am__copy_in_global_log_rx = ^[ 	]*:copy-in-global-log:[ 	]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+  recheck = 1; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+        { \
+          if ((getline line2 < ($$0 ".log")) < 0) \
+	    recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+        { \
+          recheck = 0; \
+          break; \
+        } \
+      else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+        { \
+          break; \
+        } \
+    }; \
+  if (recheck) \
+    print $$0; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+  print "fatal: making $@: " msg | "cat >&2"; \
+  exit 1; \
+} \
+function rst_section(header) \
+{ \
+  print header; \
+  len = length(header); \
+  for (i = 1; i <= len; i = i + 1) \
+    printf "="; \
+  printf "\n\n"; \
+} \
+{ \
+  copy_in_global_log = 1; \
+  global_test_result = "RUN"; \
+  while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+    { \
+      if (rc < 0) \
+         fatal("failed to read from " $$0 ".trs"); \
+      if (line ~ /$(am__global_test_result_rx)/) \
+        { \
+          sub("$(am__global_test_result_rx)", "", line); \
+          sub("[ 	]*$$", "", line); \
+          global_test_result = line; \
+        } \
+      else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+        copy_in_global_log = 0; \
+    }; \
+  if (copy_in_global_log) \
+    { \
+      rst_section(global_test_result ": " $$0); \
+      while ((rc = (getline line < ($$0 ".log"))) != 0) \
+      { \
+        if (rc < 0) \
+          fatal("failed to read from " $$0 ".log"); \
+        print line; \
+      }; \
+      printf "\n"; \
+    }; \
+  close ($$0 ".trs"); \
+  close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/   &   /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this.  Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+  --color-tests "$$am__color_tests" \
+  --enable-hard-errors "$$am__enable_hard_errors" \
+  --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test.  Creates the
+# directory for the log if needed.  Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log.  Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT.  Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup);					\
+$(am__vpath_adj_setup) $(am__vpath_adj)			\
+$(am__tty_colors);					\
+srcdir=$(srcdir); export srcdir;			\
+case "$@" in						\
+  */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;;	\
+    *) am__odir=.;; 					\
+esac;							\
+test "x$$am__odir" = x"." || test -d "$$am__odir" 	\
+  || $(MKDIR_P) "$$am__odir" || exit $$?;		\
+if test -f "./$$f"; then dir=./;			\
+elif test -f "$$f"; then dir=;				\
+else dir="$(srcdir)/"; fi;				\
+tst=$$dir$$f; log='$@'; 				\
+if test -n '$(DISABLE_HARD_ERRORS)'; then		\
+  am__enable_hard_errors=no; 				\
+else							\
+  am__enable_hard_errors=yes; 				\
+fi; 							\
+case " $(XFAIL_TESTS) " in				\
+  *[\ \	]$$f[\ \	]* | *[\ \	]$$dir$$f[\ \	]*) \
+    am__expect_failure=yes;;				\
+  *)							\
+    am__expect_failure=no;;				\
+esac; 							\
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed).  The result is saved in the shell variable
+# '$bases'.  This honors runtime overriding of TESTS and TEST_LOGS.  Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+  bases='$(TEST_LOGS)'; \
+  bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+  bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+  case '$@' in \
+    */*) \
+      case '$*' in \
+        */*) b='$*';; \
+          *) b=`echo '$@' | sed 's/\.log$$//'`; \
+       esac;; \
+    *) \
+      b='$*';; \
+  esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+	$(TEST_LOG_FLAGS)
 DIST_SUBDIRS = $(SUBDIRS)
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 distdir = $(PACKAGE)-$(VERSION)
@@ -390,6 +577,7 @@ top_srcdir = @top_srcdir@
 # look for included m4 files in the ./m4/ directory
 ACLOCAL_AMFLAGS = -I m4
 SUBDIRS = @lfds_subdir@
+TESTS = file-test.sh
 
 #------------------------
 # Compiler Flags
@@ -436,6 +624,10 @@ libnunc_stans_la_SOURCES = ns_thrpool.c \
 
 libnunc_stans_la_CPPFLAGS = $(AM_CPPFLAGS)
 libnunc_stans_la_LIBADD = $(NSPR_LINK) $(LFDS_LINK) $(LIBEVENT_LINK) $(LIBTEVENT_LINK)
+check_SCRIPTS = tests/file-test.sh tests/put.py
+socket_event_test_threads_SOURCES = tests/socket-event-test-threads.c tests/test-protocol.c ns_tls.c
+socket_event_test_threads_CPPFLAGS = $(AM_CPPFLAGS) -I/usr/include/nss3
+socket_event_test_threads_LDADD = libnunc-stans.la -lssl3 -lnss3 -lnssutil3
 
 # generate documentation from these files
 generate_doc_files = $(srcdir)/nunc-stans.h
@@ -443,7 +635,7 @@ all: config.h
 	$(MAKE) $(AM_MAKEFLAGS) all-recursive
 
 .SUFFIXES:
-.SUFFIXES: .c .lo .o .obj
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
 am--refresh: Makefile
 	@:
 $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
@@ -534,8 +726,32 @@ clean-libLTLIBRARIES:
 libnunc-stans.la: $(libnunc_stans_la_OBJECTS) $(libnunc_stans_la_DEPENDENCIES) $(EXTRA_libnunc_stans_la_DEPENDENCIES) 
 	$(AM_V_CCLD)$(LINK) -rpath $(libdir) $(libnunc_stans_la_OBJECTS) $(libnunc_stans_la_LIBADD) $(LIBS)
 
+clean-checkPROGRAMS:
+	@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+tests/$(am__dirstamp):
+	@$(MKDIR_P) tests
+	@: > tests/$(am__dirstamp)
+tests/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) tests/$(DEPDIR)
+	@: > tests/$(DEPDIR)/$(am__dirstamp)
+tests/socket_event_test_threads-socket-event-test-threads.$(OBJEXT):  \
+	tests/$(am__dirstamp) tests/$(DEPDIR)/$(am__dirstamp)
+tests/socket_event_test_threads-test-protocol.$(OBJEXT):  \
+	tests/$(am__dirstamp) tests/$(DEPDIR)/$(am__dirstamp)
+
+socket-event-test-threads$(EXEEXT): $(socket_event_test_threads_OBJECTS) $(socket_event_test_threads_DEPENDENCIES) $(EXTRA_socket_event_test_threads_DEPENDENCIES) 
+	@rm -f socket-event-test-threads$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(socket_event_test_threads_OBJECTS) $(socket_event_test_threads_LDADD) $(LIBS)
+
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
+	-rm -f tests/*.$(OBJEXT)
 
 distclean-compile:
 	-rm -f *.tab.c
@@ -543,6 +759,9 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnunc_stans_la-ns_event_fw_event.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnunc_stans_la-ns_event_fw_tevent.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnunc_stans_la-ns_thrpool.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_event_test_threads-ns_tls.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/socket_event_test_threads-socket-event-test-threads.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@tests/$(DEPDIR)/socket_event_test_threads-test-protocol.Po@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@@ -589,6 +808,48 @@ libnunc_stans_la-ns_event_fw_tevent.lo: ns_event_fw_tevent.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnunc_stans_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libnunc_stans_la-ns_event_fw_tevent.lo `test -f 'ns_event_fw_tevent.c' || echo '$(srcdir)/'`ns_event_fw_tevent.c
 
+tests/socket_event_test_threads-socket-event-test-threads.o: tests/socket-event-test-threads.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tests/socket_event_test_threads-socket-event-test-threads.o -MD -MP -MF tests/$(DEPDIR)/socket_event_test_threads-socket-event-test-threads.Tpo -c -o tests/socket_event_test_threads-socket-event-test-threads.o `test -f 'tests/socket-event-test-threads.c' || echo '$(srcdir)/'`tests/socket-event-test-threads.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/socket_event_test_threads-socket-event-test-threads.Tpo tests/$(DEPDIR)/socket_event_test_threads-socket-event-test-threads.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/socket-event-test-threads.c' object='tests/socket_event_test_threads-socket-event-test-threads.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tests/socket_event_test_threads-socket-event-test-threads.o `test -f 'tests/socket-event-test-threads.c' || echo '$(srcdir)/'`tests/socket-event-test-threads.c
+
+tests/socket_event_test_threads-socket-event-test-threads.obj: tests/socket-event-test-threads.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tests/socket_event_test_threads-socket-event-test-threads.obj -MD -MP -MF tests/$(DEPDIR)/socket_event_test_threads-socket-event-test-threads.Tpo -c -o tests/socket_event_test_threads-socket-event-test-threads.obj `if test -f 'tests/socket-event-test-threads.c'; then $(CYGPATH_W) 'tests/socket-event-test-threads.c'; else $(CYGPATH_W) '$(srcdir)/tests/socket-event-test-threads.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/socket_event_test_threads-socket-event-test-threads.Tpo tests/$(DEPDIR)/socket_event_test_threads-socket-event-test-threads.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/socket-event-test-threads.c' object='tests/socket_event_test_threads-socket-event-test-threads.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tests/socket_event_test_threads-socket-event-test-threads.obj `if test -f 'tests/socket-event-test-threads.c'; then $(CYGPATH_W) 'tests/socket-event-test-threads.c'; else $(CYGPATH_W) '$(srcdir)/tests/socket-event-test-threads.c'; fi`
+
+tests/socket_event_test_threads-test-protocol.o: tests/test-protocol.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tests/socket_event_test_threads-test-protocol.o -MD -MP -MF tests/$(DEPDIR)/socket_event_test_threads-test-protocol.Tpo -c -o tests/socket_event_test_threads-test-protocol.o `test -f 'tests/test-protocol.c' || echo '$(srcdir)/'`tests/test-protocol.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/socket_event_test_threads-test-protocol.Tpo tests/$(DEPDIR)/socket_event_test_threads-test-protocol.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/test-protocol.c' object='tests/socket_event_test_threads-test-protocol.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tests/socket_event_test_threads-test-protocol.o `test -f 'tests/test-protocol.c' || echo '$(srcdir)/'`tests/test-protocol.c
+
+tests/socket_event_test_threads-test-protocol.obj: tests/test-protocol.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tests/socket_event_test_threads-test-protocol.obj -MD -MP -MF tests/$(DEPDIR)/socket_event_test_threads-test-protocol.Tpo -c -o tests/socket_event_test_threads-test-protocol.obj `if test -f 'tests/test-protocol.c'; then $(CYGPATH_W) 'tests/test-protocol.c'; else $(CYGPATH_W) '$(srcdir)/tests/test-protocol.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) tests/$(DEPDIR)/socket_event_test_threads-test-protocol.Tpo tests/$(DEPDIR)/socket_event_test_threads-test-protocol.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='tests/test-protocol.c' object='tests/socket_event_test_threads-test-protocol.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tests/socket_event_test_threads-test-protocol.obj `if test -f 'tests/test-protocol.c'; then $(CYGPATH_W) 'tests/test-protocol.c'; else $(CYGPATH_W) '$(srcdir)/tests/test-protocol.c'; fi`
+
+socket_event_test_threads-ns_tls.o: ns_tls.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT socket_event_test_threads-ns_tls.o -MD -MP -MF $(DEPDIR)/socket_event_test_threads-ns_tls.Tpo -c -o socket_event_test_threads-ns_tls.o `test -f 'ns_tls.c' || echo '$(srcdir)/'`ns_tls.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/socket_event_test_threads-ns_tls.Tpo $(DEPDIR)/socket_event_test_threads-ns_tls.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='ns_tls.c' object='socket_event_test_threads-ns_tls.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o socket_event_test_threads-ns_tls.o `test -f 'ns_tls.c' || echo '$(srcdir)/'`ns_tls.c
+
+socket_event_test_threads-ns_tls.obj: ns_tls.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT socket_event_test_threads-ns_tls.obj -MD -MP -MF $(DEPDIR)/socket_event_test_threads-ns_tls.Tpo -c -o socket_event_test_threads-ns_tls.obj `if test -f 'ns_tls.c'; then $(CYGPATH_W) 'ns_tls.c'; else $(CYGPATH_W) '$(srcdir)/ns_tls.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/socket_event_test_threads-ns_tls.Tpo $(DEPDIR)/socket_event_test_threads-ns_tls.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='ns_tls.c' object='socket_event_test_threads-ns_tls.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(socket_event_test_threads_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o socket_event_test_threads-ns_tls.obj `if test -f 'ns_tls.c'; then $(CYGPATH_W) 'ns_tls.c'; else $(CYGPATH_W) '$(srcdir)/ns_tls.c'; fi`
+
 mostlyclean-libtool:
 	-rm -f *.lo
 
@@ -725,6 +986,169 @@ distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 	-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
 
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'.  Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+	rm -f $< $@
+	$(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+	@:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+	@$(am__set_TESTS_bases); \
+	am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+	redo_bases=`for i in $$bases; do \
+	              am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+	            done`; \
+	if test -n "$$redo_bases"; then \
+	  redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+	  redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+	  if $(am__make_dryrun); then :; else \
+	    rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+	  fi; \
+	fi; \
+	if test -n "$$am__remaking_logs"; then \
+	  echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+	       "recursion detected" >&2; \
+	else \
+	  am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+	fi; \
+	if $(am__make_dryrun); then :; else \
+	  st=0;  \
+	  errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+	  for i in $$redo_bases; do \
+	    test -f $$i.trs && test -r $$i.trs \
+	      || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+	    test -f $$i.log && test -r $$i.log \
+	      || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+	  done; \
+	  test $$st -eq 0 || exit 1; \
+	fi
+	@$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+	ws='[ 	]'; \
+	results=`for b in $$bases; do echo $$b.trs; done`; \
+	test -n "$$results" || results=/dev/null; \
+	all=`  grep "^$$ws*:test-result:"           $$results | wc -l`; \
+	pass=` grep "^$$ws*:test-result:$$ws*PASS"  $$results | wc -l`; \
+	fail=` grep "^$$ws*:test-result:$$ws*FAIL"  $$results | wc -l`; \
+	skip=` grep "^$$ws*:test-result:$$ws*SKIP"  $$results | wc -l`; \
+	xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+	xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+	error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+	if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+	  success=true; \
+	else \
+	  success=false; \
+	fi; \
+	br='==================='; br=$$br$$br$$br$$br; \
+	result_count () \
+	{ \
+	    if test x"$$1" = x"--maybe-color"; then \
+	      maybe_colorize=yes; \
+	    elif test x"$$1" = x"--no-color"; then \
+	      maybe_colorize=no; \
+	    else \
+	      echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+	    fi; \
+	    shift; \
+	    desc=$$1 count=$$2; \
+	    if test $$maybe_colorize = yes && test $$count -gt 0; then \
+	      color_start=$$3 color_end=$$std; \
+	    else \
+	      color_start= color_end=; \
+	    fi; \
+	    echo "$${color_start}# $$desc $$count$${color_end}"; \
+	}; \
+	create_testsuite_report () \
+	{ \
+	  result_count $$1 "TOTAL:" $$all   "$$brg"; \
+	  result_count $$1 "PASS: " $$pass  "$$grn"; \
+	  result_count $$1 "SKIP: " $$skip  "$$blu"; \
+	  result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+	  result_count $$1 "FAIL: " $$fail  "$$red"; \
+	  result_count $$1 "XPASS:" $$xpass "$$red"; \
+	  result_count $$1 "ERROR:" $$error "$$mgn"; \
+	}; \
+	{								\
+	  echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" |	\
+	    $(am__rst_title);						\
+	  create_testsuite_report --no-color;				\
+	  echo;								\
+	  echo ".. contents:: :depth: 2";				\
+	  echo;								\
+	  for b in $$bases; do echo $$b; done				\
+	    | $(am__create_global_log);					\
+	} >$(TEST_SUITE_LOG).tmp || exit 1;				\
+	mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG);			\
+	if $$success; then						\
+	  col="$$grn";							\
+	 else								\
+	  col="$$red";							\
+	  test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG);		\
+	fi;								\
+	echo "$${col}$$br$${std}"; 					\
+	echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}";	\
+	echo "$${col}$$br$${std}"; 					\
+	create_testsuite_report --maybe-color;				\
+	echo "$$col$$br$$std";						\
+	if $$success; then :; else					\
+	  echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}";		\
+	  if test -n "$(PACKAGE_BUGREPORT)"; then			\
+	    echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}";	\
+	  fi;								\
+	  echo "$$col$$br$$std";					\
+	fi;								\
+	$$success || exit 1
+
+check-TESTS:
+	@list='$(RECHECK_LOGS)';           test -z "$$list" || rm -f $$list
+	@list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+	log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+	exit $$?;
+recheck: all $(check_PROGRAMS) $(check_SCRIPTS)
+	@test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+	@set +e; $(am__set_TESTS_bases); \
+	bases=`for i in $$bases; do echo $$i; done \
+	         | $(am__list_recheck_tests)` || exit 1; \
+	log_list=`for i in $$bases; do echo $$i.log; done`; \
+	log_list=`echo $$log_list`; \
+	$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+	        am__force_recheck=am--force-recheck \
+	        TEST_LOGS="$$log_list"; \
+	exit $$?
+file-test.sh.log: file-test.sh
+	@p='file-test.sh'; \
+	b='file-test.sh'; \
+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+	@p='$<'; \
+	$(am__set_b); \
+	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+	--log-file $$b.log --trs-file $$b.trs \
+	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+	"$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@	@p='$<'; \
+@am__EXEEXT_TRUE@	$(am__set_b); \
+@am__EXEEXT_TRUE@	$(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@	--log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@	$(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@	"$$tst" $(AM_TESTS_FD_REDIRECT)
+
 distdir: $(DISTFILES)
 	$(am__remove_distdir)
 	test -d "$(distdir)" || mkdir "$(distdir)"
@@ -907,6 +1331,8 @@ distcleancheck: distclean
 	       $(distcleancheck_listfiles) ; \
 	       exit 1; } >&2
 check-am: all-am
+	$(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(check_SCRIPTS)
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
 check: check-recursive
 all-am: Makefile $(LTLIBRARIES) $(DATA) config.h
 installdirs: installdirs-recursive
@@ -934,24 +1360,29 @@ install-strip:
 	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
 	fi
 mostlyclean-generic:
+	-test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+	-test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+	-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
 
 clean-generic:
 
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
 	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+	-rm -f tests/$(DEPDIR)/$(am__dirstamp)
+	-rm -f tests/$(am__dirstamp)
 
 maintainer-clean-generic:
 	@echo "This command is intended for maintainers to use"
 	@echo "it deletes files that may require special tools to rebuild."
 clean: clean-recursive
 
-clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
-	mostlyclean-am
+clean-am: clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+	clean-libtool mostlyclean-am
 
 distclean: distclean-recursive
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-	-rm -rf ./$(DEPDIR)
+	-rm -rf ./$(DEPDIR) tests/$(DEPDIR)
 	-rm -f Makefile
 distclean-am: clean-am distclean-compile distclean-generic \
 	distclean-hdr distclean-libtool distclean-tags
@@ -1000,7 +1431,7 @@ installcheck-am:
 maintainer-clean: maintainer-clean-recursive
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
 	-rm -rf $(top_srcdir)/autom4te.cache
-	-rm -rf ./$(DEPDIR)
+	-rm -rf ./$(DEPDIR) tests/$(DEPDIR)
 	-rm -f Makefile
 maintainer-clean-am: distclean-am maintainer-clean-generic
 
@@ -1019,11 +1450,12 @@ ps-am:
 
 uninstall-am: uninstall-libLTLIBRARIES uninstall-nuncstansincDATA
 
-.MAKE: $(am__recursive_targets) all install-am install-data-am \
-	install-strip
+.MAKE: $(am__recursive_targets) all check-am install-am \
+	install-data-am install-strip
 
 .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
-	am--refresh check check-am clean clean-cscope clean-generic \
+	am--refresh check check-TESTS check-am clean \
+	clean-checkPROGRAMS clean-cscope clean-generic \
 	clean-libLTLIBRARIES clean-libtool cscope cscopelist-am ctags \
 	ctags-am dist dist-all dist-bzip2 dist-gzip dist-lzip \
 	dist-shar dist-tarZ dist-xz dist-zip distcheck distclean \
@@ -1039,9 +1471,13 @@ uninstall-am: uninstall-libLTLIBRARIES uninstall-nuncstansincDATA
 	installdirs installdirs-am maintainer-clean \
 	maintainer-clean-generic mostlyclean mostlyclean-compile \
 	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
-	tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \
-	uninstall-nuncstansincDATA
+	recheck tags tags-am uninstall uninstall-am \
+	uninstall-libLTLIBRARIES uninstall-nuncstansincDATA
 
+file-test.sh: $(srcdir)/tests/file-test.sh put.py
+	cp $(srcdir)/tests/file-test.sh $@
+put.py: $(srcdir)/tests/put.py
+	cp $^ $@
 install-data-hook: $(srcdir)/nunc-stans.doxy
 	if [ ! -d $(DESTDIR)$(htmldir) ] ; then mkdir -p $(DESTDIR)$(htmldir) ; fi
 	if [ ! -d $(DESTDIR)$(mandir) ] ; then mkdir -p $(DESTDIR)$(mandir) ; fi
diff --git a/file-test.sh b/file-test.sh
deleted file mode 100755
index 27de855..0000000
--- a/file-test.sh
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/bin/sh
-
-set -o errexit
-smallsizes="1 2 64 128 1024 8192 65536"
-largesizes="$smallsizes 8388608 67108864 536870912"
-
-NPROC=${NPROC:-8}
-
-if [ $NPROC -gt 4 ] ; then
-    sizes="$smallsizes"
-else
-    sizes="$largesizes"
-fi
-
-make_file() {
-    if [ $1 -lt 512 ] ; then
-        count=$1
-        bs=1
-    else
-        bs=512
-        count=`expr $1 / 512`
-    fi
-    dd if=/dev/zero of=$2 count=$count bs=$bs > /dev/null 2>&1
-}
-
-run_file_tests() {
-    set +x
-    indir=$1/in
-    outdir=$1/out.$2
-    mkdir -p $outdir
-    for size in $sizes ; do
-        infile=$indir/file.$size.in
-        outfile=$outdir/file.$size.out
-#        curl -s -T $infile -o $outfile http://localhost:10000 || { echo error $! uploading file $infile ; exit 1 ; }
-        
-#        cmp -s $infile $outfile || { echo error files $infile and $outfile differ ; exit 1 ; }
-        python put.py $infile || exit 1
-    done
-}
-
-testdir=`mktemp -d`
-indir=$testdir/in
-if [ ! -d $indir ] ; then mkdir -p $indir ; fi
-for size in $sizes ; do
-    infile=$indir/file.$size.in
-    make_file $size $infile
-done
-
-ii=$NPROC
-while [ $ii -gt 0 ] ; do
-    echo proc $ii
-    run_file_tests $testdir $ii &
-    ii=`expr $ii - 1`
-done || : # why does while fail????
-echo waiting
-wait || echo no procs to wait for
-rm -rf $testdir
-echo done
diff --git a/ns_tls.c b/ns_tls.c
index 50e3d91..cab8387 100644
--- a/ns_tls.c
+++ b/ns_tls.c
@@ -8,6 +8,8 @@
 #include "keyhi.h"
 
 #include "ns_tls.h"
+#include <syslog.h>
+#include "ns_private.h"
 
 struct ns_sec_ctx_t {
     NSSInitContext *initctx;
diff --git a/ns_tls.h b/ns_tls.h
index 82de529..4c02d51 100644
--- a/ns_tls.h
+++ b/ns_tls.h
@@ -4,8 +4,6 @@
 #include "nspr.h"
 #include "prerror.h"
 #include "prio.h"
-#include "ns_private.h"
-#include <syslog.h>
 
 /* should be in sec_ctx.h */
 struct ns_sec_ctx_t;
diff --git a/put.py b/put.py
deleted file mode 100644
index 677cdae..0000000
--- a/put.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import sys
-import mmap
-import requests
-
-for fn in sys.argv[1:]:
-    f = open(fn)
-    buf = mmap.mmap(f.fileno(), 0, mmap.MAP_PRIVATE, mmap.PROT_READ)
-    inlen = len(buf)
-    resp = requests.put("http://localhost:10000", buf)
-    f.close()
-    outlen = len(resp.text)
-    if inlen != outlen:
-        raise Exception("Error: file %s input length %d does not match output length %d" %
-                        (fn, inlen, outlen))
-
-sys.exit(0)
diff --git a/socket-event-test-threads.c b/socket-event-test-threads.c
deleted file mode 100644
index 9766ba6..0000000
--- a/socket-event-test-threads.c
+++ /dev/null
@@ -1,99 +0,0 @@
-
-#include <string.h>
-#include <signal.h>
-
-#include "nspr.h"
-#include "plgetopt.h"
-#include "plstr.h"
-#include "ns_private.h"
-#include "ns_tls.h"
-#include "ns_event_fw.h"
-
-#include "test-protocol.h"
-
-static void
-test_shutdown(ns_job_t *job)
-{
-    fprintf(stderr, "Received shutdown signal\n");
-
-    /* Stop our listener job first. */
-    ns_job_done((ns_job_t *)job->data);
-
-    /* Shutdown the thread pool. */
-    ns_thrpool_shutdown(job->tp);
-
-    /* NGK - this might leak, but we can't really finish the job if the event loop is gone! */
-    /* Finish the shutdown signal job. */
-    ns_job_done(job);
-}
-
-int
-main (int argc, char **argv)
-{
-    ns_thrpool_t *tp;
-    struct ns_sec_ctx_t *sc = NULL;
-    char *progname = argv[0];
-    PRInt32 maxthreads = 3;
-#ifdef USE_SSL
-    char *secdir, *certname, *prefix;
-#endif
-
-    (void)progname; /* TODO */
-    ns_job_t *listen_job = NULL;
-    argv++;
-
-    if (getenv("MAX_THREADS")) {
-        maxthreads = atoi(getenv("MAX_THREADS"));
-    }
-    tp = ns_thrpool_new(maxthreads, maxthreads, 0, 1024);
-
-#ifdef USE_SSL /* NGK - disabled ssl for testing */
-    secdir = *argv++;
-    certname = *argv++;
-    prefix = NULL;
-    sc = ns_tls_init(secdir, prefix, certname, PR_FALSE);
-#endif
-
-    /* arguments are listen ports and/or unix domain listen file names */
-    for (; argv && *argv; ++argv) {
-        PRFileDesc *listenfd = NULL;
-        PRNetAddr netaddr;
-        int portnum = atoi(*argv);
-        PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
-
-        if (portnum == 0) { /* unix socket */
-            netaddr.local.family = PR_AF_LOCAL;
-            PL_strncpyz(netaddr.local.path, *argv, sizeof(netaddr.local.path));
-        } else {
-            netaddr.inet.family = PR_AF_INET;
-            netaddr.inet.port = PR_htons(portnum);
-            netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
-        }
-        listenfd = PR_OpenTCPSocket(PR_NetAddrFamily(&netaddr));
-        PR_SetSocketOption(listenfd, &prsod);
-        prsod.option = PR_SockOpt_Reuseaddr;
-        PR_SetSocketOption(listenfd, &prsod);
-        /*        add_sec_layer(&listenfd, sc);*/
-        PR_Bind(listenfd, &netaddr);
-        PR_Listen(listenfd, 32);
-
-        /* start the listener job */
-        listen_job = test_protocol_init(tp, listenfd, sc);
-
-        /* set up our signal handler for handling shutdown */
-        ns_add_signal_job(tp, SIGTERM, NS_JOB_THREAD, test_shutdown, listen_job, NULL);
-    }
-
-    if (ns_thrpool_wait(tp)) {
-        /* error */
-    }
-
-    ns_thrpool_destroy(tp);
-
-/* NGK - disabled SSL for testing
-    ns_tls_done(sc);
-*/
-    fprintf(stderr, "Shutdown.  Exiting.\n");
-
-    return (0);
-}
diff --git a/socket-event-test.c b/socket-event-test.c
deleted file mode 100644
index 1ed5ffb..0000000
--- a/socket-event-test.c
+++ /dev/null
@@ -1,220 +0,0 @@
-
-#include "nspr.h"
-#include "plgetopt.h"
-#include "plstr.h"
-#include "private/pprio.h"
-#include <event.h>
-
-struct listen_ctx {
-    PRFileDesc *listenfd;
-    struct event *ev;
-};
-
-#define LEN_BYTES 4
-
-struct conn_ctx {
-    PRFileDesc *connfd;
-    struct event *ev;
-    size_t offset;
-    size_t len;
-#define CONN_BUFFER_SIZE BUFSIZ
-    char buffer[CONN_BUFFER_SIZE];
-};
-
-static struct listen_ctx *
-listen_ctx_new(PRFileDesc *listenfd, struct event *ev)
-{
-    struct listen_ctx *listenctx = PR_NEW(struct listen_ctx);
-    listenctx->listenfd = listenfd;
-    listenctx->ev = ev;
-    return listenctx;
-}
-
-static struct conn_ctx *
-conn_ctx_new(PRFileDesc *connfd, struct event *ev)
-{
-    struct conn_ctx *connctx = PR_NEW(struct conn_ctx);
-    connctx->connfd = connfd;
-    connctx->ev = ev;
-    connctx->offset = 0;
-    connctx->len = 0;
-    return connctx;
-}
-
-static void
-conn_ctx_done(struct conn_ctx *connctx)
-{
-    if (connctx->ev) {
-	event_del(connctx->ev);
-	PR_DELETE(connctx->ev);
-    }
-    if (connctx->connfd) {
-	PR_Close(connctx->connfd);
-	connctx->connfd = NULL;
-    }
-    connctx->offset = 0;
-}
-
-static void
-conn_ctx_free(struct conn_ctx **connctx)
-{
-    conn_ctx_done(*connctx);
-    PR_DELETE(*connctx);
-}
-
-static void
-conn_write(int fd, short event, void *arg)
-{
-    struct conn_ctx *connctx = (struct conn_ctx *)arg;
-    PRInt32 len, nbytes;
-    short evtype = 0;
-    
-    PR_ASSERT(arg);
-    PR_ASSERT(fd == PR_FileDesc2NativeHandle(connctx->connfd));
-    nbytes = connctx->len-connctx->offset;
-    len = PR_Write(connctx->connfd, connctx->buffer+connctx->offset, nbytes);
-    if (len < 0) {
-	PRErrorCode prerr = PR_GetError();
-	(void)prerr; /* TODO */
-	/* error */
-	conn_ctx_free(&connctx);
-    } else if (len == 0) {
-	/* closed */
-	conn_ctx_free(&connctx);
-    } else {
-	connctx->offset += len;
-	if (connctx->offset < connctx->len) {
-	    /* reschedule write */
-	    evtype = EV_WRITE;
-	} else if (connctx->offset == connctx->len) {
-	    /* done */
-	    /* delete event */
-	    conn_ctx_free(&connctx);
-	} else {
-	    /* buffer overflow error */
-	}
-    }
-    if (evtype) {
-	event_add(connctx->ev, NULL);
-    }
-}
-
-static void
-conn_read(int fd, short event, void *arg)
-{
-    struct conn_ctx *connctx = (struct conn_ctx *)arg;
-    PRInt32 len, nbytes;
-    short evtype = 0;
-    
-    PR_ASSERT(arg);
-    PR_ASSERT(fd == PR_FileDesc2NativeHandle(connctx->connfd));
-    if (connctx->len) {
-	nbytes = connctx->len-connctx->offset;
-    } else {
-	nbytes = CONN_BUFFER_SIZE-connctx->offset;
-    }
-    len = PR_Read(connctx->connfd, connctx->buffer+connctx->offset, nbytes);
-    if (len < 0) {
-	PRErrorCode prerr = PR_GetError();
-        (void)prerr; /* TODO */
-	/* error */
-	conn_ctx_free(&connctx);
-    } else if (len == 0) {
-	/* closed */
-	conn_ctx_free(&connctx);
-    } else {
-	connctx->offset += len;
-	if (!connctx->len && (connctx->offset >= LEN_BYTES)) {
-	    PRUint32 *val = (PRUint32 *)connctx->buffer;
-	    connctx->len = (PRInt32)PR_ntohl(*val);
-	    nbytes = connctx->len-connctx->offset;
-	}
-	if (len < nbytes) {
-	    /* reschedule read */
-	    evtype = EV_READ;
-	} else {
-	    /* write now */
-	    evtype = EV_WRITE;
-	    connctx->offset = 0;
-	    event_set(connctx->ev, PR_FileDesc2NativeHandle(connctx->connfd), evtype, conn_write, connctx);
-	}
-    }
-    if (evtype) {
-	event_add(connctx->ev, NULL);
-    }
-}
-
-static void
-listen_accept(int fd, short event, void *arg)
-{
-    PRFileDesc *connfd = NULL;
-    struct listen_ctx *listenctx = (struct listen_ctx *)arg;
-    struct event *ev;
-    struct conn_ctx *connctx;
-    PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
-
-    PR_ASSERT(arg);
-    PR_ASSERT(fd == PR_FileDesc2NativeHandle(listenctx->listenfd));
-
-    /* accept and add read event for new connection */
-    connfd = PR_Accept(listenctx->listenfd, NULL, PR_INTERVAL_NO_TIMEOUT);
-    if (connfd) {
-	PR_SetSocketOption(connfd, &prsod);
-	ev = PR_NEW(struct event);
-	connctx = conn_ctx_new(connfd, ev);
-	event_set(ev, PR_FileDesc2NativeHandle(connfd), EV_READ, conn_read, connctx);
-	event_add(ev, NULL);
-    }
-    /* re-arm listen_accept */
-    event_add(listenctx->ev, NULL);
-
-    return;
-}
-
-int
-main (int argc, char **argv)
-{
-    char *progname = argv[0];
-    argv++;
-
-    (void)progname; /* TODO */
-    /* Initalize the event library */
-    event_init();
-
-    /* arguments are listen ports and/or unix domain listen file names */
-    for (; argv && *argv; ++argv) {
-	struct listen_ctx *listenctx;
-	struct event *ev;
-	PRFileDesc *listenfd = NULL;
-	PRNetAddr netaddr;
-	int portnum = atoi(*argv);
-	PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
-
-	if (portnum == 0) { /* unix socket */
-	    netaddr.local.family = PR_AF_LOCAL;
-	    PL_strncpyz(netaddr.local.path, *argv, sizeof(netaddr.local.path));
-	} else {
-	    netaddr.inet.family = PR_AF_INET;
-	    netaddr.inet.port = PR_htons(portnum);
-	    netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
-	}
-	listenfd = PR_OpenTCPSocket(PR_NetAddrFamily(&netaddr));
-	PR_SetSocketOption(listenfd, &prsod);
-	prsod.option = PR_SockOpt_Reuseaddr;
-	PR_SetSocketOption(listenfd, &prsod);
-	PR_Bind(listenfd, &netaddr);
-	PR_Listen(listenfd, 32);
-
-	/* create the event for accepting new connections from this socket */
-	ev = PR_NEW(struct event);
-	listenctx = listen_ctx_new(listenfd, ev);
-	
-	event_set(ev, PR_FileDesc2NativeHandle(listenfd), EV_READ, listen_accept, listenctx);
-	event_add(ev, NULL);
-    }
-
-    event_dispatch();
-
-    return (0);
-}
-
diff --git a/test-driver b/test-driver
new file mode 100755
index 0000000..32bf39e
--- /dev/null
+++ b/test-driver
@@ -0,0 +1,127 @@
+#! /bin/sh
+# test-driver - basic testsuite driver script.
+
+scriptversion=2012-06-27.10; # UTC
+
+# Copyright (C) 2011-2013 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+# Make unconditional expansion of undefined variables an error.  This
+# helps a lot in preventing typo-related bugs.
+set -u
+
+usage_error ()
+{
+  echo "$0: $*" >&2
+  print_usage >&2
+  exit 2
+}
+
+print_usage ()
+{
+  cat <<END
+Usage:
+  test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
+              [--expect-failure={yes|no}] [--color-tests={yes|no}]
+              [--enable-hard-errors={yes|no}] [--] TEST-SCRIPT
+The '--test-name', '--log-file' and '--trs-file' options are mandatory.
+END
+}
+
+# TODO: better error handling in option parsing (in particular, ensure
+# TODO: $log_file, $trs_file and $test_name are defined).
+test_name= # Used for reporting.
+log_file=  # Where to save the output of the test script.
+trs_file=  # Where to save the metadata of the test run.
+expect_failure=no
+color_tests=no
+enable_hard_errors=yes
+while test $# -gt 0; do
+  case $1 in
+  --help) print_usage; exit $?;;
+  --version) echo "test-driver $scriptversion"; exit $?;;
+  --test-name) test_name=$2; shift;;
+  --log-file) log_file=$2; shift;;
+  --trs-file) trs_file=$2; shift;;
+  --color-tests) color_tests=$2; shift;;
+  --expect-failure) expect_failure=$2; shift;;
+  --enable-hard-errors) enable_hard_errors=$2; shift;;
+  --) shift; break;;
+  -*) usage_error "invalid option: '$1'";;
+  esac
+  shift
+done
+
+if test $color_tests = yes; then
+  # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
+  red='[0;31m' # Red.
+  grn='[0;32m' # Green.
+  lgn='[1;32m' # Light green.
+  blu='[1;34m' # Blue.
+  mgn='[0;35m' # Magenta.
+  std='[m'     # No color.
+else
+  red= grn= lgn= blu= mgn= std=
+fi
+
+do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
+trap "st=129; $do_exit" 1
+trap "st=130; $do_exit" 2
+trap "st=141; $do_exit" 13
+trap "st=143; $do_exit" 15
+
+# Test script is run here.
+"$@" >$log_file 2>&1
+estatus=$?
+if test $enable_hard_errors = no && test $estatus -eq 99; then
+  estatus=1
+fi
+
+case $estatus:$expect_failure in
+  0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
+  0:*)   col=$grn res=PASS  recheck=no  gcopy=no;;
+  77:*)  col=$blu res=SKIP  recheck=no  gcopy=yes;;
+  99:*)  col=$mgn res=ERROR recheck=yes gcopy=yes;;
+  *:yes) col=$lgn res=XFAIL recheck=no  gcopy=yes;;
+  *:*)   col=$red res=FAIL  recheck=yes gcopy=yes;;
+esac
+
+# Report outcome to console.
+echo "${col}${res}${std}: $test_name"
+
+# Register the test result, and other relevant metadata.
+echo ":test-result: $res" > $trs_file
+echo ":global-test-result: $res" >> $trs_file
+echo ":recheck: $recheck" >> $trs_file
+echo ":copy-in-global-log: $gcopy" >> $trs_file
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/test-protocol.c b/test-protocol.c
deleted file mode 100644
index afe5f65..0000000
--- a/test-protocol.c
+++ /dev/null
@@ -1,426 +0,0 @@
-#include <string.h>
-#include <unistd.h>
-#include <plstr.h>
-
-#include "ns_event_fw.h"
-
-#include "test-protocol.h"
-
-#define STATE_READING 1
-#define STATE_WRITING 2
-
-struct conn_ctx {
-    size_t offset; /* current offset into buffer for reading or writing */
-    size_t len; /* size of buffer */
-    size_t needed; /* content-length + start of body */
-    size_t body; /* when reading, offset from buffer of beginning of http body */
-    size_t cl; /* http content-length when reading */
-#define CONN_BUFFER_SIZE BUFSIZ /* default buffer size */
-    char *buffer;
-    PRBool need_sec_layer_before_next_read;
-    struct ns_sec_ctx_t *sc;
-    PRBool state;
-};
-
-static void conn_read(ns_job_t *job);
-static void conn_write(ns_job_t *job);
-static const char header1[] = {"HTTP/1.1 200 OK\r\nDate: Fri, 31 Dec 1969 23:59:59 GMT\r\nContent-type:text/plain\r\nContent-Length:"};
-static const char header2[] = {"\r\n\r\n"};
-static size_t header1len = sizeof(header1) - 1; /* for null */
-static size_t header2len = sizeof(header2) - 1; /* for null */
-/* flags to apply to io events */
-static int USE_POLL = 0;
-/* test with poll() - see if poll and epoll can co-exist */
-static ns_job_type_t readtype = NS_JOB_READ|NS_JOB_PERSIST;
-static ns_job_type_t writetype = NS_JOB_WRITE|NS_JOB_PERSIST;
-#define NS_POLL_READ_TIMEOUT 10000 /* for USE_POLL - 10 seconds */
-#define NS_POLL_WRITE_TIMEOUT 10000 /* for USE_POLL - 10 seconds */
-#define PR_WOULD_BLOCK(iii) (iii == PR_PENDING_INTERRUPT_ERROR) || (iii == PR_WOULD_BLOCK_ERROR)
-
-static struct conn_ctx *
-conn_ctx_new()
-{
-    struct conn_ctx *connctx = PR_NEW(struct conn_ctx);
-    connctx->offset = 0;
-    connctx->len = 0;
-    connctx->buffer = NULL;
-    connctx->needed = 0;
-    connctx->body = 0;
-    connctx->cl = 0;
-    connctx->need_sec_layer_before_next_read = PR_FALSE;
-    connctx->sc = NULL;
-    connctx->state = STATE_READING; /* start off reading */
-    return connctx;
-}
-
-static void
-conn_ctx_reset(struct conn_ctx *connctx)
-{
-    connctx->offset = 0;
-    connctx->needed = 0;
-    connctx->body = 0;
-    connctx->cl = 0;
-    connctx->state = STATE_READING;
-}
-
-static void
-conn_ctx_done(struct conn_ctx *connctx)
-{
-    free(connctx->buffer);
-    connctx->buffer = NULL;
-    memset(connctx, 0, sizeof(*connctx));
-}
-
-static void
-conn_ctx_free(struct conn_ctx **connctx)
-{
-    conn_ctx_done(*connctx);
-    PR_DELETE(*connctx);
-}
-
-static void
-long_time_consuming_job(ns_job_t *job)
-{
-    struct conn_ctx *connctx;
-
-    PR_ASSERT(job);
-    PR_ASSERT(job->data);
-    connctx = (struct conn_ctx *)job->data;
-
-#ifdef TEST_WRITE_DELAY
-    fprintf(stderr, "long_time_consuming_job: sleeping for 10 seconds . . .\n");
-    sleep(10);
-    fprintf(stderr, "long_time_consuming_job: finished - ready to write\n");
-#endif
-    /* we are ready to write - make the job persistent so we don't have
-       to re-arm it - remove the thread flag so the io will occur
-       in the event thread 
-    */
-    connctx->state = STATE_WRITING;
-    ns_job_modify(job, writetype); /* execute write job */
-}
-
-static int
-dopoll(ns_job_t *job, int isread)
-{
-    int ret = 0;
-    struct PRPollDesc pr_pd;
-    int msec = isread ? NS_POLL_READ_TIMEOUT : NS_POLL_WRITE_TIMEOUT;
-    PRIntervalTime timeout = PR_MillisecondsToInterval(msec);
-
-    pr_pd.fd = job->fd;
-    pr_pd.in_flags = isread ? PR_POLL_READ : PR_POLL_WRITE;
-    pr_pd.out_flags = 0;
-    ret = PR_Poll(&pr_pd, 1, timeout);
-    if (ret == 0) { /* timeout */
-        ret = -1;
-        fprintf(stderr, "poll timeout\n");
-   } else if (ret < 0) { /* error */
-        ret = -1;
-        fprintf(stderr, "poll error %d\n", PR_GetError());
-    } else { /* success */
-        ret = 0;
-    }
-    return ret;
-}
-
-static void
-conn_write(ns_job_t *job)
-{
-    struct conn_ctx *connctx;
-    PRInt32 len;
-    
-    PR_ASSERT(job);
-    PR_ASSERT(job->data);
-    connctx = (struct conn_ctx *)job->data;
-    if (NS_JOB_IS_TIMER(job->output_job_type)) {
-        /* we timed out waiting for the client - close */
-        conn_ctx_free(&connctx);
-        ns_job_done(job);
-        return;
-    }
-
-retry:
-    len = PR_Write(job->fd, connctx->buffer+connctx->offset, connctx->needed);
-    if (len < 0) {
-        PRErrorCode prerr = PR_GetError();
-        if (PR_WOULD_BLOCK(prerr)) {
-            /* ok */
-            /* reschedule write */
-            if (!NS_JOB_IS_PERSIST(job->job_type)) {
-                ns_job_rearm(job);
-            }
-            if (USE_POLL) {
-                if (0 == dopoll(job, 0)) {
-                    goto retry;
-                } else {
-                    /* error or timeout */
-                    conn_ctx_free(&connctx);
-                    ns_job_done(job);
-                }
-            }
-        } else {
-            /* error */
-            conn_ctx_free(&connctx);
-            ns_job_done(job);
-        }
-    } else if (len == 0) {
-        /* closed */
-        conn_ctx_free(&connctx);
-        ns_job_done(job);
-    } else {
-        connctx->offset += len;
-        connctx->needed -= len;
-        if (connctx->needed) {
-            /* reschedule write */
-            if (!NS_JOB_IS_PERSIST(job->job_type)) {
-                ns_job_rearm(job);
-            }
-            if (USE_POLL) {
-                if (0 == dopoll(job, 0)) {
-                    goto retry;
-                } else {
-                    /* error or timeout */
-                    conn_ctx_free(&connctx);
-                    ns_job_done(job);
-                }
-            }
-        } else if (0 == connctx->needed) {
-            /* done - schedule read */
-            conn_ctx_reset(connctx);
-            ns_job_modify(job, readtype);
-        }
-    }
-}
-
-static size_t
-get_content_length_and_body_offset(struct conn_ctx *connctx)
-{
-    /* Parse the input, starting at the beginning of the buffer.
-     * Stop when we detect two consecutive \n's (or \r\n's)
-     * as this signifies the end of the headers.  Then, look
-     * for the Content-Length in the headers and return the value.
-     */
-    size_t cl = 0;
-    char *ptr;
-
-    if (connctx->cl) {
-        return connctx->cl; /* already have it */
-    }
-
-    if ((ptr = PL_strnstr(connctx->buffer, "\n\n", connctx->offset))) {
-        ptr += 2;
-    } else if ((ptr = PL_strnstr(connctx->buffer, "\r\n\r\n", connctx->offset))) {
-        ptr += 4;
-    } else {
-        return cl; /* no body yet */
-    }
-    /* pos is now the offset of the beginning of the body */
-    connctx->body = ptr - connctx->buffer;
-    /* end of headers found - look for content-length */
-#define CL_STRING "content-length:"
-    if ((ptr = PL_strncasestr(connctx->buffer, CL_STRING, connctx->body))) {
-        ptr += sizeof(CL_STRING) - 1; /* skip to first char after colon */
-        while(*ptr == ' ') ++ptr; /* skip whitespace */
-        cl = strtoul(ptr, NULL, 10); /* should be a number here */
-        connctx->cl = cl;
-    } else {
-        fprintf(stderr, "Error: input contains no Content-Length:\n");
-    }
-    return cl;
-}
-
-/* it is assumed the reads are persistent, so
-   no need to re-arm */
-static void
-conn_read(ns_job_t *job)
-{
-    struct conn_ctx *connctx;
-    PRInt32 len, nbytes;
-    
-    PR_ASSERT(job);
-    PR_ASSERT(job->data);
-
-    connctx = (struct conn_ctx *)job->data;
-    if (NS_JOB_IS_TIMER(job->output_job_type)) {
-        /* we timed out waiting for the client - close */
-        conn_ctx_free(&connctx);
-        ns_job_done(job);
-        return;
-    }
-
-    /* do we need to add a security layer before the next read? */
-    if (connctx->need_sec_layer_before_next_read) {
-        connctx->need_sec_layer_before_next_read = PR_FALSE;
-        PRErrorCode prerr = ns_add_sec_layer(job->fd, connctx->sc);
-        if (prerr != PR_SUCCESS) {
-            /* handle error */
-            conn_ctx_free(&connctx);
-            ns_job_done(job);
-            return;
-        }
-    }
-
-retry:
-    /* figure out how many bytes we need to read */
-    if (connctx->needed) {
-        nbytes = connctx->needed - connctx->offset;
-    } else {
-        nbytes = CONN_BUFFER_SIZE;
-    }
-    /* increase size of buffer if necessary */
-    if ((nbytes + connctx->offset) > connctx->len) {
-        connctx->len = nbytes + connctx->offset;
-        connctx->buffer = (char *)realloc(connctx->buffer, connctx->len * sizeof(char));
-    }
-    len = PR_Read(job->fd, connctx->buffer+connctx->offset, nbytes);
-    if (len < 0) {
-        PRErrorCode prerr = PR_GetError();
-        if (PR_WOULD_BLOCK(prerr)) {
-            /* need more data in socket */
-            if (USE_POLL) {
-                if (0 == dopoll(job, 1)) {
-                    goto retry;
-                } else {
-                    /* error or timeout */
-                    conn_ctx_free(&connctx);
-                    ns_job_done(job);
-                    return;
-                }
-            }
-            /* reschedule read */
-            if (!NS_JOB_IS_PERSIST(job->job_type)) {
-                ns_job_rearm(job);
-            }
-            return;
-        } else {
-            /* error */
-            conn_ctx_free(&connctx);
-            ns_job_done(job);
-            return;
-        }
-    } else if (len == 0) {
-        /* closed */
-        conn_ctx_free(&connctx);
-        ns_job_done(job);
-        return;
-    } else {
-        connctx->offset += len;
-        if (get_content_length_and_body_offset(connctx)) {
-            /* we have the content-length and the headers */
-            if (!connctx->needed) {
-                connctx->needed = connctx->body + connctx->cl;
-            }
-            /* see if we have all of the content as well */
-            if (connctx->offset >= connctx->needed) {
-                /* done - generate and write response */
-                int offsetdiff;
-                size_t numdigits;
-
-                /* number of bytes in string rep of content length */
-                numdigits = (connctx->cl >= 1000000000) ? 10 : (connctx->cl >= 100000000) ? 9 :
-                    (connctx->cl >= 10000000) ? 8 : (connctx->cl >= 1000000) ? 7 :
-                    (connctx->cl >= 100000) ? 6 : (connctx->cl >= 10000) ? 5 :
-                    (connctx->cl >= 1000) ? 4 : (connctx->cl >= 100) ? 3 :
-                    (connctx->cl >= 10) ? 2 : 1;
-                /* number of bytes we need to write */
-                connctx->needed = header1len + numdigits + header2len + connctx->cl;
-                /* start at the body offset - work backwards - need space for header1 + numdigits + header2 */
-                offsetdiff = connctx->body - (header1len + numdigits + header2len);
-                if (offsetdiff < 0) {
-                    /* may need more room - response headers are larger than request headers */
-                    if ((header1len + numdigits + header2len + connctx->cl) > connctx->len) {
-                        connctx->len = header1len + numdigits + header2len + connctx->cl;
-                        connctx->buffer = (char *)realloc(connctx->buffer, connctx->len);
-                    }
-                    /* move the body up enough to accommodate the headers */
-                    memmove(connctx->buffer + connctx->body - offsetdiff,
-                            connctx->buffer + connctx->body, connctx->cl);
-                    connctx->offset = 0;
-                } else { /* new header size <= old header size */
-                    connctx->offset = offsetdiff;
-                }
-                memcpy(connctx->buffer + connctx->offset, header1, header1len);
-                sprintf(connctx->buffer + connctx->offset + header1len, "%lu", connctx->cl);
-                memcpy(connctx->buffer + connctx->offset + header1len + numdigits, header2, header2len);
-                connctx->state = 0; /* not reading any more */
-                ns_job_modify(job, NS_JOB_THREAD); /* execute as threaded job for time consuming task */
-                return;
-            }
-        }
-        goto retry; /* keep reading until we get all data OR EAGAIN OR error */
-    }
-}
-
-static void
-conn_handler(ns_job_t *job)
-{
-    struct conn_ctx *connctx;
-    
-    PR_ASSERT(job);
-    PR_ASSERT(job->data);
-
-    connctx = (struct conn_ctx *)job->data;
-    if (connctx->state == STATE_READING) {
-        PR_ASSERT(NS_JOB_IS_READ(job->output_job_type));
-        conn_read(job);
-    } else if (connctx->state == STATE_WRITING) {
-        PR_ASSERT(NS_JOB_IS_WRITE(job->output_job_type));
-        conn_write(job);
-    } else {
-        long_time_consuming_job(job);
-    }
-}
-
-static void
-listen_accept(ns_job_t *job)
-{
-    PRFileDesc *connfd = NULL;
-    struct conn_ctx *connctx;
-    PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
-
-    PR_ASSERT(job);
-
-    /* accept and add read event for new connection */
-    connfd = PR_Accept(job->fd, NULL, PR_INTERVAL_NO_WAIT);
-    if (connfd) {
-        struct timeval tv = {1, 0};
-        PR_SetSocketOption(connfd, &prsod);
-        connctx = conn_ctx_new();
-        connctx->sc = (struct ns_sec_ctx_t *)job->data;
-        ns_add_io_timeout_job(job->tp, connfd, &tv, readtype, conn_handler, connctx, NULL);
-    } else {
-        PRErrorCode prerr = PR_GetError();
-        if (PR_WOULD_BLOCK(prerr)) {
-            /* ok - fall through */
-        } else {
-            /* error - bail out */
-            return;
-        }
-    }
-    /* re-arm listen_accept */
-    /* ns_job_rearm(job); not needed if persistent */
-
-    return;
-}
-
-ns_job_t *
-test_protocol_init(ns_thrpool_t *tp, PRFileDesc *listenfd, struct ns_sec_ctx_t *sc)
-{
-    ns_job_t *listen_job = NULL;
-
-    if (getenv("USE_POLL") && (getenv("USE_POLL")[0] != '0')) {
-        USE_POLL = 1;
-        readtype = NS_JOB_READ;
-        writetype = NS_JOB_WRITE;
-    } else {
-        USE_POLL = 0;
-        readtype = NS_JOB_READ|NS_JOB_PERSIST;
-        writetype = NS_JOB_WRITE|NS_JOB_PERSIST;
-    }
-
-    /* add_sec_layer(listenfd, sc); */
-    ns_add_io_job(tp, listenfd, NS_JOB_ACCEPT|NS_JOB_PERSIST, listen_accept, sc, &listen_job);
-
-    return listen_job;
-}
diff --git a/test-protocol.h b/test-protocol.h
deleted file mode 100644
index a2e5c3e..0000000
--- a/test-protocol.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef TEST_PROTOCOL_H
-#define TEST_PROTOCOL_H
-
-#include "ns_private.h"
-#include "prio.h"
-#include "ns_tls.h"
-
-ns_job_t *test_protocol_init(ns_thrpool_t *tp, PRFileDesc *listenfd, struct ns_sec_ctx_t *sc);
-
-#endif /* TEST_PROTOCOL_H */
diff --git a/tests/file-test.sh b/tests/file-test.sh
new file mode 100755
index 0000000..4f53fa1
--- /dev/null
+++ b/tests/file-test.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+set -o errexit
+
+echo Begin $0
+pwd
+
+PORTNUMS=10000
+MAX_THREADS=3 ./socket-event-test-threads $PORTNUMS & SERVERPID=$!
+
+smallsizes="1 2 64 128 1024 8192 65536"
+largesizes="$smallsizes 8388608 67108864 536870912"
+
+NPROC=${NPROC:-8}
+
+if [ $NPROC -gt 4 ] ; then
+    sizes="$smallsizes"
+else
+    sizes="$largesizes"
+fi
+
+make_file() {
+    if [ $1 -lt 512 ] ; then
+        count=$1
+        bs=1
+    else
+        bs=512
+        count=`expr $1 / 512`
+    fi
+    dd if=/dev/zero of=$2 count=$count bs=$bs > /dev/null 2>&1
+}
+
+run_file_tests() {
+    indir=$1/in
+    outdir=$1/out.$2
+    mkdir -p $outdir
+    for size in $sizes ; do
+        infile=$indir/file.$size.in
+        outfile=$outdir/file.$size.out
+#        curl -s -T $infile -o $outfile http://localhost:10000 || { echo error $! uploading file $infile ; exit 1 ; }
+        
+#        cmp -s $infile $outfile || { echo error files $infile and $outfile differ ; exit 1 ; }
+        python put.py $infile || exit 1
+    done
+}
+
+testdir=`mktemp -d`
+indir=$testdir/in
+if [ ! -d $indir ] ; then mkdir -p $indir ; fi
+for size in $sizes ; do
+    infile=$indir/file.$size.in
+    make_file $size $infile
+done
+
+ii=$NPROC
+PIDS=""
+while [ $ii -gt 0 ] ; do
+    echo proc $ii
+    run_file_tests $testdir $ii & PIDS="$PIDS $!"
+    ii=`expr $ii - 1`
+done || : # why does while fail????
+echo waiting
+wait $PIDS || echo no procs to wait for
+kill $SERVERPID
+wait || echo server exited already
+rm -rf $testdir
+echo done
diff --git a/tests/put.py b/tests/put.py
new file mode 100644
index 0000000..677cdae
--- /dev/null
+++ b/tests/put.py
@@ -0,0 +1,16 @@
+import sys
+import mmap
+import requests
+
+for fn in sys.argv[1:]:
+    f = open(fn)
+    buf = mmap.mmap(f.fileno(), 0, mmap.MAP_PRIVATE, mmap.PROT_READ)
+    inlen = len(buf)
+    resp = requests.put("http://localhost:10000", buf)
+    f.close()
+    outlen = len(resp.text)
+    if inlen != outlen:
+        raise Exception("Error: file %s input length %d does not match output length %d" %
+                        (fn, inlen, outlen))
+
+sys.exit(0)
diff --git a/tests/socket-event-test-threads.c b/tests/socket-event-test-threads.c
new file mode 100644
index 0000000..2f44a98
--- /dev/null
+++ b/tests/socket-event-test-threads.c
@@ -0,0 +1,101 @@
+
+#include <string.h>
+#include <signal.h>
+
+#include "nspr.h"
+#include "plgetopt.h"
+#include "plstr.h"
+#include "nunc-stans.h"
+#include "ns_tls.h"
+
+#include "test-protocol.h"
+
+static void
+test_shutdown(struct ns_job_t *job)
+{
+    fprintf(stderr, "Received shutdown signal\n");
+
+    /* Stop our listener job first. */
+    ns_job_done((struct ns_job_t *)ns_job_get_data(job));
+
+    /* Shutdown the thread pool. */
+    ns_thrpool_shutdown(ns_job_get_tp(job));
+
+    /* NGK - this might leak, but we can't really finish the job if the event loop is gone! */
+    /* Finish the shutdown signal job. */
+    ns_job_done(job);
+}
+
+int
+main (int argc, char **argv)
+{
+    ns_thrpool_t *tp;
+    struct ns_sec_ctx_t *sc = NULL;
+    char *progname = argv[0];
+    struct ns_thrpool_config ns_config;
+#ifdef USE_SSL
+    char *secdir, *certname, *prefix;
+#endif
+
+    (void)progname; /* TODO */
+    struct ns_job_t *listen_job = NULL;
+    argv++;
+
+    ns_thrpool_config_init(&ns_config);
+    if (getenv("MAX_THREADS")) {
+        ns_config.max_threads = atoi(getenv("MAX_THREADS"));
+    } else {
+        ns_config.max_threads = 3;
+    }
+    tp = ns_thrpool_new(&ns_config);
+
+#ifdef USE_SSL /* NGK - disabled ssl for testing */
+    secdir = *argv++;
+    certname = *argv++;
+    prefix = NULL;
+    sc = ns_tls_init(secdir, prefix, certname, PR_FALSE);
+#endif
+
+    /* arguments are listen ports and/or unix domain listen file names */
+    for (; argv && *argv; ++argv) {
+        PRFileDesc *listenfd = NULL;
+        PRNetAddr netaddr;
+        int portnum = atoi(*argv);
+        PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
+
+        if (portnum == 0) { /* unix socket */
+            netaddr.local.family = PR_AF_LOCAL;
+            PL_strncpyz(netaddr.local.path, *argv, sizeof(netaddr.local.path));
+        } else {
+            netaddr.inet.family = PR_AF_INET;
+            netaddr.inet.port = PR_htons(portnum);
+            netaddr.inet.ip = PR_htonl(PR_INADDR_ANY);
+        }
+        listenfd = PR_OpenTCPSocket(PR_NetAddrFamily(&netaddr));
+        PR_SetSocketOption(listenfd, &prsod);
+        prsod.option = PR_SockOpt_Reuseaddr;
+        PR_SetSocketOption(listenfd, &prsod);
+        /*        add_sec_layer(&listenfd, sc);*/
+        PR_Bind(listenfd, &netaddr);
+        PR_Listen(listenfd, 32);
+
+        /* start the listener job */
+        listen_job = test_protocol_init(tp, listenfd, sc);
+
+        /* set up our signal handler for handling shutdown */
+        ns_add_signal_job(tp, SIGTERM, NS_JOB_THREAD, test_shutdown, listen_job, NULL);
+    }
+
+    if (ns_thrpool_wait(tp)) {
+        /* error */
+    }
+
+    ns_thrpool_destroy(tp);
+
+/* NGK - disabled SSL for testing
+    ns_tls_done(sc);
+*/
+    fprintf(stderr, "Shutdown.  Exiting.\n");
+
+    return (0);
+}
diff --git a/tests/test-protocol.c b/tests/test-protocol.c
new file mode 100644
index 0000000..cf03445
--- /dev/null
+++ b/tests/test-protocol.c
@@ -0,0 +1,425 @@
+#include <string.h>
+#include <unistd.h>
+#include <plstr.h>
+
+#include "test-protocol.h"
+
+#define STATE_READING 1
+#define STATE_WRITING 2
+
+typedef struct ns_job_t ns_job_t;
+
+struct conn_ctx {
+    size_t offset; /* current offset into buffer for reading or writing */
+    size_t len; /* size of buffer */
+    size_t needed; /* content-length + start of body */
+    size_t body; /* when reading, offset from buffer of beginning of http body */
+    size_t cl; /* http content-length when reading */
+#define CONN_BUFFER_SIZE BUFSIZ /* default buffer size */
+    char *buffer;
+    PRBool need_sec_layer_before_next_read;
+    struct ns_sec_ctx_t *sc;
+    PRBool state;
+};
+
+static void conn_read(ns_job_t *job);
+static void conn_write(ns_job_t *job);
+static const char header1[] = {"HTTP/1.1 200 OK\r\nDate: Fri, 31 Dec 1969 23:59:59 GMT\r\nContent-type:text/plain\r\nContent-Length:"};
+static const char header2[] = {"\r\n\r\n"};
+static size_t header1len = sizeof(header1) - 1; /* for null */
+static size_t header2len = sizeof(header2) - 1; /* for null */
+/* flags to apply to io events */
+static int USE_POLL = 0;
+/* test with poll() - see if poll and epoll can co-exist */
+static ns_job_type_t readtype = NS_JOB_READ|NS_JOB_PERSIST;
+static ns_job_type_t writetype = NS_JOB_WRITE|NS_JOB_PERSIST;
+#define NS_POLL_READ_TIMEOUT 10000 /* for USE_POLL - 10 seconds */
+#define NS_POLL_WRITE_TIMEOUT 10000 /* for USE_POLL - 10 seconds */
+#define PR_WOULD_BLOCK(iii) (iii == PR_PENDING_INTERRUPT_ERROR) || (iii == PR_WOULD_BLOCK_ERROR)
+
+static struct conn_ctx *
+conn_ctx_new()
+{
+    struct conn_ctx *connctx = PR_NEW(struct conn_ctx);
+    connctx->offset = 0;
+    connctx->len = 0;
+    connctx->buffer = NULL;
+    connctx->needed = 0;
+    connctx->body = 0;
+    connctx->cl = 0;
+    connctx->need_sec_layer_before_next_read = PR_FALSE;
+    connctx->sc = NULL;
+    connctx->state = STATE_READING; /* start off reading */
+    return connctx;
+}
+
+static void
+conn_ctx_reset(struct conn_ctx *connctx)
+{
+    connctx->offset = 0;
+    connctx->needed = 0;
+    connctx->body = 0;
+    connctx->cl = 0;
+    connctx->state = STATE_READING;
+}
+
+static void
+conn_ctx_done(struct conn_ctx *connctx)
+{
+    free(connctx->buffer);
+    connctx->buffer = NULL;
+    memset(connctx, 0, sizeof(*connctx));
+}
+
+static void
+conn_ctx_free(struct conn_ctx **connctx)
+{
+    conn_ctx_done(*connctx);
+    PR_DELETE(*connctx);
+}
+
+static void
+long_time_consuming_job(ns_job_t *job)
+{
+    struct conn_ctx *connctx;
+
+    PR_ASSERT(job);
+    PR_ASSERT(ns_job_get_data(job));
+    connctx = (struct conn_ctx *)ns_job_get_data(job);
+
+#ifdef TEST_WRITE_DELAY
+    fprintf(stderr, "long_time_consuming_job: sleeping for 10 seconds . . .\n");
+    sleep(10);
+    fprintf(stderr, "long_time_consuming_job: finished - ready to write\n");
+#endif
+    /* we are ready to write - make the job persistent so we don't have
+       to re-arm it - remove the thread flag so the io will occur
+       in the event thread 
+    */
+    connctx->state = STATE_WRITING;
+    ns_job_modify(job, writetype); /* execute write job */
+}
+
+static int
+dopoll(ns_job_t *job, int isread)
+{
+    int ret = 0;
+    struct PRPollDesc pr_pd;
+    int msec = isread ? NS_POLL_READ_TIMEOUT : NS_POLL_WRITE_TIMEOUT;
+    PRIntervalTime timeout = PR_MillisecondsToInterval(msec);
+
+    pr_pd.fd = ns_job_get_fd(job);
+    pr_pd.in_flags = isread ? PR_POLL_READ : PR_POLL_WRITE;
+    pr_pd.out_flags = 0;
+    ret = PR_Poll(&pr_pd, 1, timeout);
+    if (ret == 0) { /* timeout */
+        ret = -1;
+        fprintf(stderr, "poll timeout\n");
+   } else if (ret < 0) { /* error */
+        ret = -1;
+        fprintf(stderr, "poll error %d\n", PR_GetError());
+    } else { /* success */
+        ret = 0;
+    }
+    return ret;
+}
+
+static void
+conn_write(ns_job_t *job)
+{
+    struct conn_ctx *connctx;
+    PRInt32 len;
+    
+    PR_ASSERT(job);
+    PR_ASSERT(ns_job_get_data(job));
+    connctx = (struct conn_ctx *)ns_job_get_data(job);
+    if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) {
+        /* we timed out waiting for the client - close */
+        conn_ctx_free(&connctx);
+        ns_job_done(job);
+        return;
+    }
+
+retry:
+    len = PR_Write(ns_job_get_fd(job), connctx->buffer+connctx->offset, connctx->needed);
+    if (len < 0) {
+        PRErrorCode prerr = PR_GetError();
+        if (PR_WOULD_BLOCK(prerr)) {
+            /* ok */
+            /* reschedule write */
+            if (!NS_JOB_IS_PERSIST(ns_job_get_type(job))) {
+                ns_job_rearm(job);
+            }
+            if (USE_POLL) {
+                if (0 == dopoll(job, 0)) {
+                    goto retry;
+                } else {
+                    /* error or timeout */
+                    conn_ctx_free(&connctx);
+                    ns_job_done(job);
+                }
+            }
+        } else {
+            /* error */
+            conn_ctx_free(&connctx);
+            ns_job_done(job);
+        }
+    } else if (len == 0) {
+        /* closed */
+        conn_ctx_free(&connctx);
+        ns_job_done(job);
+    } else {
+        connctx->offset += len;
+        connctx->needed -= len;
+        if (connctx->needed) {
+            /* reschedule write */
+            if (!NS_JOB_IS_PERSIST(ns_job_get_type(job))) {
+                ns_job_rearm(job);
+            }
+            if (USE_POLL) {
+                if (0 == dopoll(job, 0)) {
+                    goto retry;
+                } else {
+                    /* error or timeout */
+                    conn_ctx_free(&connctx);
+                    ns_job_done(job);
+                }
+            }
+        } else if (0 == connctx->needed) {
+            /* done - schedule read */
+            conn_ctx_reset(connctx);
+            ns_job_modify(job, readtype);
+        }
+    }
+}
+
+static size_t
+get_content_length_and_body_offset(struct conn_ctx *connctx)
+{
+    /* Parse the input, starting at the beginning of the buffer.
+     * Stop when we detect two consecutive \n's (or \r\n's)
+     * as this signifies the end of the headers.  Then, look
+     * for the Content-Length in the headers and return the value.
+     */
+    size_t cl = 0;
+    char *ptr;
+
+    if (connctx->cl) {
+        return connctx->cl; /* already have it */
+    }
+
+    if ((ptr = PL_strnstr(connctx->buffer, "\n\n", connctx->offset))) {
+        ptr += 2;
+    } else if ((ptr = PL_strnstr(connctx->buffer, "\r\n\r\n", connctx->offset))) {
+        ptr += 4;
+    } else {
+        return cl; /* no body yet */
+    }
+    /* pos is now the offset of the beginning of the body */
+    connctx->body = ptr - connctx->buffer;
+    /* end of headers found - look for content-length */
+#define CL_STRING "content-length:"
+    if ((ptr = PL_strncasestr(connctx->buffer, CL_STRING, connctx->body))) {
+        ptr += sizeof(CL_STRING) - 1; /* skip to first char after colon */
+        while(*ptr == ' ') ++ptr; /* skip whitespace */
+        cl = strtoul(ptr, NULL, 10); /* should be a number here */
+        connctx->cl = cl;
+    } else {
+        fprintf(stderr, "Error: input contains no Content-Length:\n");
+    }
+    return cl;
+}
+
+/* it is assumed the reads are persistent, so
+   no need to re-arm */
+static void
+conn_read(ns_job_t *job)
+{
+    struct conn_ctx *connctx;
+    PRInt32 len, nbytes;
+    
+    PR_ASSERT(job);
+    PR_ASSERT(ns_job_get_data(job));
+
+    connctx = (struct conn_ctx *)ns_job_get_data(job);
+    if (NS_JOB_IS_TIMER(ns_job_get_output_type(job))) {
+        /* we timed out waiting for the client - close */
+        conn_ctx_free(&connctx);
+        ns_job_done(job);
+        return;
+    }
+
+    /* do we need to add a security layer before the next read? */
+    if (connctx->need_sec_layer_before_next_read) {
+        connctx->need_sec_layer_before_next_read = PR_FALSE;
+        PRErrorCode prerr = ns_add_sec_layer(ns_job_get_fd(job), connctx->sc);
+        if (prerr != PR_SUCCESS) {
+            /* handle error */
+            conn_ctx_free(&connctx);
+            ns_job_done(job);
+            return;
+        }
+    }
+
+retry:
+    /* figure out how many bytes we need to read */
+    if (connctx->needed) {
+        nbytes = connctx->needed - connctx->offset;
+    } else {
+        nbytes = CONN_BUFFER_SIZE;
+    }
+    /* increase size of buffer if necessary */
+    if ((nbytes + connctx->offset) > connctx->len) {
+        connctx->len = nbytes + connctx->offset;
+        connctx->buffer = (char *)realloc(connctx->buffer, connctx->len * sizeof(char));
+    }
+    len = PR_Read(ns_job_get_fd(job), connctx->buffer+connctx->offset, nbytes);
+    if (len < 0) {
+        PRErrorCode prerr = PR_GetError();
+        if (PR_WOULD_BLOCK(prerr)) {
+            /* need more data in socket */
+            if (USE_POLL) {
+                if (0 == dopoll(job, 1)) {
+                    goto retry;
+                } else {
+                    /* error or timeout */
+                    conn_ctx_free(&connctx);
+                    ns_job_done(job);
+                    return;
+                }
+            }
+            /* reschedule read */
+            if (!NS_JOB_IS_PERSIST(ns_job_get_type(job))) {
+                ns_job_rearm(job);
+            }
+            return;
+        } else {
+            /* error */
+            conn_ctx_free(&connctx);
+            ns_job_done(job);
+            return;
+        }
+    } else if (len == 0) {
+        /* closed */
+        conn_ctx_free(&connctx);
+        ns_job_done(job);
+        return;
+    } else {
+        connctx->offset += len;
+        if (get_content_length_and_body_offset(connctx)) {
+            /* we have the content-length and the headers */
+            if (!connctx->needed) {
+                connctx->needed = connctx->body + connctx->cl;
+            }
+            /* see if we have all of the content as well */
+            if (connctx->offset >= connctx->needed) {
+                /* done - generate and write response */
+                int offsetdiff;
+                size_t numdigits;
+
+                /* number of bytes in string rep of content length */
+                numdigits = (connctx->cl >= 1000000000) ? 10 : (connctx->cl >= 100000000) ? 9 :
+                    (connctx->cl >= 10000000) ? 8 : (connctx->cl >= 1000000) ? 7 :
+                    (connctx->cl >= 100000) ? 6 : (connctx->cl >= 10000) ? 5 :
+                    (connctx->cl >= 1000) ? 4 : (connctx->cl >= 100) ? 3 :
+                    (connctx->cl >= 10) ? 2 : 1;
+                /* number of bytes we need to write */
+                connctx->needed = header1len + numdigits + header2len + connctx->cl;
+                /* start at the body offset - work backwards - need space for header1 + numdigits + header2 */
+                offsetdiff = connctx->body - (header1len + numdigits + header2len);
+                if (offsetdiff < 0) {
+                    /* may need more room - response headers are larger than request headers */
+                    if ((header1len + numdigits + header2len + connctx->cl) > connctx->len) {
+                        connctx->len = header1len + numdigits + header2len + connctx->cl;
+                        connctx->buffer = (char *)realloc(connctx->buffer, connctx->len);
+                    }
+                    /* move the body up enough to accommodate the headers */
+                    memmove(connctx->buffer + connctx->body - offsetdiff,
+                            connctx->buffer + connctx->body, connctx->cl);
+                    connctx->offset = 0;
+                } else { /* new header size <= old header size */
+                    connctx->offset = offsetdiff;
+                }
+                memcpy(connctx->buffer + connctx->offset, header1, header1len);
+                sprintf(connctx->buffer + connctx->offset + header1len, "%lu", connctx->cl);
+                memcpy(connctx->buffer + connctx->offset + header1len + numdigits, header2, header2len);
+                connctx->state = 0; /* not reading any more */
+                ns_job_modify(job, NS_JOB_THREAD); /* execute as threaded job for time consuming task */
+                return;
+            }
+        }
+        goto retry; /* keep reading until we get all data OR EAGAIN OR error */
+    }
+}
+
+static void
+conn_handler(ns_job_t *job)
+{
+    struct conn_ctx *connctx;
+    
+    PR_ASSERT(job);
+    PR_ASSERT(ns_job_get_data(job));
+
+    connctx = (struct conn_ctx *)ns_job_get_data(job);
+    if (connctx->state == STATE_READING) {
+        PR_ASSERT(NS_JOB_IS_READ(ns_job_get_output_type(job)));
+        conn_read(job);
+    } else if (connctx->state == STATE_WRITING) {
+        PR_ASSERT(NS_JOB_IS_WRITE(ns_job_get_output_type(job)));
+        conn_write(job);
+    } else {
+        long_time_consuming_job(job);
+    }
+}
+
+static void
+listen_accept(ns_job_t *job)
+{
+    PRFileDesc *connfd = NULL;
+    struct conn_ctx *connctx;
+    PRSocketOptionData prsod = {PR_SockOpt_Nonblocking, {PR_TRUE}};
+
+    PR_ASSERT(job);
+
+    /* accept and add read event for new connection */
+    connfd = PR_Accept(ns_job_get_fd(job), NULL, PR_INTERVAL_NO_WAIT);
+    if (connfd) {
+        PR_SetSocketOption(connfd, &prsod);
+        connctx = conn_ctx_new();
+        connctx->sc = (struct ns_sec_ctx_t *)ns_job_get_data(job);
+        ns_add_io_job(ns_job_get_tp(job), connfd, readtype, conn_handler, connctx, NULL);
+    } else {
+        PRErrorCode prerr = PR_GetError();
+        if (PR_WOULD_BLOCK(prerr)) {
+            /* ok - fall through */
+        } else {
+            /* error - bail out */
+            return;
+        }
+    }
+    /* re-arm listen_accept */
+    /* ns_job_rearm(job); not needed if persistent */
+
+    return;
+}
+
+ns_job_t *
+test_protocol_init(ns_thrpool_t *tp, PRFileDesc *listenfd, struct ns_sec_ctx_t *sc)
+{
+    ns_job_t *listen_job = NULL;
+
+    if (getenv("USE_POLL") && (getenv("USE_POLL")[0] != '0')) {
+        USE_POLL = 1;
+        readtype = NS_JOB_READ;
+        writetype = NS_JOB_WRITE;
+    } else {
+        USE_POLL = 0;
+        readtype = NS_JOB_READ|NS_JOB_PERSIST;
+        writetype = NS_JOB_WRITE|NS_JOB_PERSIST;
+    }
+
+    /* add_sec_layer(listenfd, sc); */
+    ns_add_io_job(tp, listenfd, NS_JOB_ACCEPT|NS_JOB_PERSIST, listen_accept, sc, &listen_job);
+
+    return listen_job;
+}
diff --git a/tests/test-protocol.h b/tests/test-protocol.h
new file mode 100644
index 0000000..4e5b346
--- /dev/null
+++ b/tests/test-protocol.h
@@ -0,0 +1,10 @@
+#ifndef TEST_PROTOCOL_H
+#define TEST_PROTOCOL_H
+
+#include "nunc-stans.h"
+#include "prio.h"
+#include "ns_tls.h"
+
+struct ns_job_t *test_protocol_init(ns_thrpool_t *tp, PRFileDesc *listenfd, struct ns_sec_ctx_t *sc);
+
+#endif /* TEST_PROTOCOL_H */
-- 
1.9.3

