summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:23:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:23:09 +0000
commit30d479c28c831a0d4f1fdb54a9e346b0fc176be1 (patch)
treeaa35d7414ce9f1326abf6f723f6dfa5b0aa08b1d /test
parentInitial commit. (diff)
downloadapr-30d479c28c831a0d4f1fdb54a9e346b0fc176be1.tar.xz
apr-30d479c28c831a0d4f1fdb54a9e346b0fc176be1.zip
Adding upstream version 1.7.2.upstream/1.7.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test')
-rw-r--r--test/Makefile.in189
-rw-r--r--test/Makefile.win273
-rw-r--r--test/NWGNUaprtest307
-rw-r--r--test/NWGNUechod253
-rw-r--r--test/NWGNUglobalmutexchild252
-rw-r--r--test/NWGNUmakefile60
-rw-r--r--test/NWGNUmod_test254
-rw-r--r--test/NWGNUproc_child252
-rw-r--r--test/NWGNUreadchild252
-rw-r--r--test/NWGNUsockchild252
-rw-r--r--test/NWGNUsockperf253
-rw-r--r--test/NWGNUtestatmc255
-rw-r--r--test/NWGNUtryread252
-rw-r--r--test/README332
-rw-r--r--test/abts.c451
-rw-r--r--test/abts.h108
-rw-r--r--test/abts_tests.h75
-rw-r--r--test/data/file_datafile.txt1
-rw-r--r--test/data/mmap_datafile.txt1
-rw-r--r--test/echod.c134
-rw-r--r--test/globalmutexchild.c64
-rw-r--r--test/internal/Makefile.in37
-rw-r--r--test/internal/Makefile.win109
-rw-r--r--test/internal/testregex.c91
-rw-r--r--test/internal/testucs.c348
-rw-r--r--test/mod_test.c32
-rw-r--r--test/nw_misc.c23
-rw-r--r--test/occhild.c26
-rw-r--r--test/proc_child.c21
-rw-r--r--test/readchild.c46
-rw-r--r--test/sendfile.c770
-rw-r--r--test/sockchild.c90
-rw-r--r--test/sockperf.c256
-rw-r--r--test/testall.dsw137
-rw-r--r--test/testapp.c10
-rw-r--r--test/testargs.c236
-rw-r--r--test/testatomic.c961
-rw-r--r--test/testcond.c670
-rw-r--r--test/testdir.c399
-rw-r--r--test/testdll.dsp446
-rw-r--r--test/testdso.c263
-rw-r--r--test/testdup.c198
-rw-r--r--test/testencode.c1124
-rw-r--r--test/testenv.c144
-rw-r--r--test/testescape.c311
-rw-r--r--test/testfile.c1312
-rw-r--r--test/testfilecopy.c138
-rw-r--r--test/testfileinfo.c263
-rw-r--r--test/testflock.c104
-rw-r--r--test/testflock.h27
-rw-r--r--test/testfmt.c166
-rw-r--r--test/testfnmatch.c256
-rw-r--r--test/testglobalmutex.c143
-rw-r--r--test/testglobalmutex.h27
-rw-r--r--test/testhash.c541
-rw-r--r--test/testipsub.c237
-rw-r--r--test/testlfs.c378
-rw-r--r--test/testlib.dsp446
-rw-r--r--test/testlock.c556
-rw-r--r--test/testlockperf.c348
-rw-r--r--test/testmmap.c165
-rw-r--r--test/testmutexscope.c234
-rw-r--r--test/testnames.c387
-rw-r--r--test/testoc.c120
-rw-r--r--test/testpath.c138
-rw-r--r--test/testpipe.c205
-rw-r--r--test/testpoll.c965
-rw-r--r--test/testpools.c156
-rw-r--r--test/testproc.c174
-rw-r--r--test/testprocmutex.c294
-rw-r--r--test/testrand.c359
-rw-r--r--test/testshm.c332
-rw-r--r--test/testshm.h33
-rw-r--r--test/testshmconsumer.c94
-rw-r--r--test/testshmproducer.c89
-rw-r--r--test/testskiplist.c546
-rw-r--r--test/testsleep.c53
-rw-r--r--test/testsock.c702
-rw-r--r--test/testsock.h34
-rw-r--r--test/testsockets.c238
-rw-r--r--test/testsockopt.c139
-rw-r--r--test/teststr.c433
-rw-r--r--test/teststrnatcmp.c78
-rw-r--r--test/testtable.c245
-rw-r--r--test/testtemp.c55
-rw-r--r--test/testthread.c132
-rw-r--r--test/testtime.c316
-rw-r--r--test/testud.c91
-rw-r--r--test/testuser.c174
-rw-r--r--test/testutil.c61
-rw-r--r--test/testutil.h109
-rw-r--r--test/testvsn.c56
-rw-r--r--test/tryread.c49
93 files changed, 23216 insertions, 0 deletions
diff --git a/test/Makefile.in b/test/Makefile.in
new file mode 100644
index 0000000..e3b71e0
--- /dev/null
+++ b/test/Makefile.in
@@ -0,0 +1,189 @@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+# PROGRAMS includes all test programs built on this platform.
+# STDTEST_PORTABLE
+# test programs invoked via standard user interface, run on all platforms
+# TESTS
+# test modules invoked through the abts suite (./testall)
+# STDTEST_NONPORTABLE
+# test programs invoked via standard user interface, not portable
+# OTHER_PROGRAMS
+# programs such as sockperf, that have to be invoked in a special sequence
+# or with special parameters
+# TESTALL_COMPONENTS
+# programs such as globalmutexchild which the various TESTS will invoke
+# to validate process creation, pipes, dso mechanisms and so forth
+
+STDTEST_PORTABLE = \
+ testlockperf@EXEEXT@ \
+ testmutexscope@EXEEXT@ \
+ testall@EXEEXT@ \
+ sendfile@EXEEXT@
+
+TESTS = testtime.lo teststr.lo testvsn.lo testipsub.lo testshm.lo \
+ testmmap.lo testud.lo testtable.lo testsleep.lo testpools.lo \
+ testfmt.lo testfile.lo testdir.lo testfileinfo.lo testrand.lo \
+ testdso.lo testoc.lo testdup.lo testsockets.lo testproc.lo \
+ testpoll.lo testlock.lo testsockopt.lo testpipe.lo testthread.lo \
+ testhash.lo testargs.lo testnames.lo testuser.lo testpath.lo \
+ testenv.lo testprocmutex.lo testfnmatch.lo testatomic.lo testflock.lo \
+ testsock.lo testglobalmutex.lo teststrnatcmp.lo testfilecopy.lo \
+ testtemp.lo testlfs.lo testcond.lo testescape.lo testskiplist.lo \
+ testencode.lo
+
+OTHER_PROGRAMS = \
+ echod@EXEEXT@ \
+ sockperf@EXEEXT@
+
+TESTALL_COMPONENTS = \
+ globalmutexchild@EXEEXT@ \
+ libmod_test.la \
+ mod_test.la \
+ occhild@EXEEXT@ \
+ proc_child@EXEEXT@ \
+ readchild@EXEEXT@ \
+ sockchild@EXEEXT@ \
+ testshmproducer@EXEEXT@ \
+ testshmconsumer@EXEEXT@ \
+ tryread@EXEEXT@
+
+PROGRAMS = $(TESTALL_COMPONENTS) $(STDTEST_PORTABLE) $(STDTEST_NONPORTABLE) \
+ $(OTHER_PROGRAMS)
+
+TARGETS = $(PROGRAMS)
+
+# bring in rules.mk for standard functionality
+@INCLUDE_RULES@
+
+LOCAL_LIBS=../lib@APR_LIBNAME@.la
+
+CLEAN_TARGETS = testfile.tmp lfstests/*.bin \
+ data/test*.txt data/test*.dat data/apr.testshm.shm
+
+CLEAN_SUBDIRS = internal
+
+INCDIR=../include
+INCLUDES=-I$(INCDIR) -I$(srcdir)/../include
+
+# link programs using -no-install to get real executables not
+# libtool wrapper scripts which link an executable when first run.
+LINK_PROG = $(LIBTOOL) $(LTFLAGS) --mode=link --tag=CC $(COMPILE) $(LT_LDFLAGS) \
+ @LT_NO_INSTALL@ $(ALL_LDFLAGS) -o $@
+
+# STDTEST_PORTABLE;
+
+abts.lo: $(srcdir)/abts.c $(srcdir)/abts.h $(srcdir)/abts_tests.h \
+ $(srcdir)/testutil.h
+
+testutil.lo: $(srcdir)/abts.c $(srcdir)/abts.h $(srcdir)/abts_tests.h \
+ $(srcdir)/testutil.h
+
+OBJECTS_testall = abts.lo testutil.lo $(TESTS) $(LOCAL_LIBS)
+testall@EXEEXT@: $(OBJECTS_testall)
+ $(LINK_PROG) $(OBJECTS_testall) $(ALL_LIBS)
+# For VPATH builds; where we have no ./data, copy us some data
+# if we wait until 'make check', then 'make; ./testall' fails;
+ if test ! -d "./data"; then cp -r $(srcdir)/data data; fi
+
+OBJECTS_testlockperf = testlockperf.lo $(LOCAL_LIBS)
+testlockperf@EXEEXT@: $(OBJECTS_testlockperf)
+ $(LINK_PROG) $(OBJECTS_testlockperf) $(ALL_LIBS)
+
+OBJECTS_testmutexscope = testmutexscope.lo $(LOCAL_LIBS)
+testmutexscope@EXEEXT@: $(OBJECTS_testmutexscope)
+ $(LINK_PROG) $(OBJECTS_testmutexscope) $(ALL_LIBS)
+
+# OTHER_PROGRAMS;
+
+OBJECTS_echod = echod.lo $(LOCAL_LIBS)
+echod@EXEEXT@: $(OBJECTS_echod)
+ $(LINK_PROG) $(OBJECTS_echod) $(ALL_LIBS)
+
+OBJECTS_sendfile = sendfile.lo $(LOCAL_LIBS)
+sendfile@EXEEXT@: $(OBJECTS_sendfile)
+ $(LINK_PROG) $(OBJECTS_sendfile) $(ALL_LIBS)
+
+OBJECTS_sockperf = sockperf.lo $(LOCAL_LIBS)
+sockperf@EXEEXT@: $(OBJECTS_sockperf)
+ $(LINK_PROG) $(OBJECTS_sockperf) $(ALL_LIBS)
+
+# TESTALL_COMPONENTS;
+
+OBJECTS_globalmutexchild = globalmutexchild.lo $(LOCAL_LIBS)
+globalmutexchild@EXEEXT@: $(OBJECTS_globalmutexchild)
+ $(LINK_PROG) $(OBJECTS_globalmutexchild) $(ALL_LIBS)
+
+# Note -prefer-pic is only supported with libtool-1.4+
+mod_test.lo: $(srcdir)/mod_test.c
+ $(LIBTOOL) $(LTFLAGS) --mode=compile --tag=CC $(COMPILE) -prefer-pic -o $@ \
+ -c $(srcdir)/mod_test.c
+
+OBJECTS_mod_test = mod_test.lo
+mod_test.la: $(OBJECTS_mod_test) $(LOCAL_LIBS)
+ $(LIBTOOL) $(LTFLAGS) --mode=link --tag=CC $(COMPILE) -rpath `pwd` -module \
+ -avoid-version $(LT_LDFLAGS) $(ALL_LDFLAGS) -o $@ \
+ $(OBJECTS_mod_test) $(LOCAL_LIBS)
+
+OBJECTS_libmod_test = mod_test.lo $(LOCAL_LIBS)
+libmod_test.la: $(OBJECTS_libmod_test)
+ $(LIBTOOL) $(LTFLAGS) --mode=link --tag=CC $(COMPILE) -rpath `pwd` \
+ -avoid-version $(LT_LDFLAGS) $(ALL_LDFLAGS) -o $@ \
+ $(OBJECTS_libmod_test) $(ALL_LIBS)
+
+OBJECTS_occhild = occhild.lo $(LOCAL_LIBS)
+occhild@EXEEXT@: $(OBJECTS_occhild)
+ $(LINK_PROG) $(OBJECTS_occhild) $(ALL_LIBS)
+
+OBJECTS_proc_child = proc_child.lo $(LOCAL_LIBS)
+proc_child@EXEEXT@: $(OBJECTS_proc_child)
+ $(LINK_PROG) $(OBJECTS_proc_child) $(ALL_LIBS)
+
+OBJECTS_readchild = readchild.lo $(LOCAL_LIBS)
+readchild@EXEEXT@: $(OBJECTS_readchild)
+ $(LINK_PROG) $(OBJECTS_readchild) $(ALL_LIBS)
+
+OBJECTS_sockchild = sockchild.lo $(LOCAL_LIBS)
+sockchild@EXEEXT@: $(OBJECTS_sockchild)
+ $(LINK_PROG) $(OBJECTS_sockchild) $(ALL_LIBS)
+
+OBJECTS_testshmconsumer = testshmconsumer.lo $(LOCAL_LIBS)
+testshmconsumer@EXEEXT@: $(OBJECTS_testshmconsumer) $(LOCAL_LIBS)
+ $(LINK_PROG) $(OBJECTS_testshmconsumer) $(ALL_LIBS)
+
+OBJECTS_testshmproducer = testshmproducer.lo $(LOCAL_LIBS)
+testshmproducer@EXEEXT@: $(OBJECTS_testshmproducer)
+ $(LINK_PROG) $(OBJECTS_testshmproducer) $(ALL_LIBS)
+
+OBJECTS_tryread = tryread.lo $(LOCAL_LIBS)
+tryread@EXEEXT@: $(OBJECTS_tryread)
+ $(LINK_PROG) $(OBJECTS_tryread) $(ALL_LIBS)
+
+check: $(TESTALL_COMPONENTS) $(STDTEST_PORTABLE) $(STDTEST_NONPORTABLE)
+ teststatus=0; \
+ progfailed=""; \
+ for prog in $(STDTEST_PORTABLE) $(STDTEST_NONPORTABLE); do \
+ if test "$$prog" = 'sendfile@EXEEXT@'; then \
+ for mode in blocking nonblocking timeout; do \
+ ./$$prog client $$mode startserver 127.0.0.1; \
+ status=$$?; \
+ if test $$status != 0; then \
+ teststatus=$$status; \
+ progfailed="$$progfailed '$$prog mode $$mode'"; \
+ fi; \
+ done; \
+ else \
+ ./$$prog -v; \
+ status=$$?; \
+ if test $$status != 0; then \
+ teststatus=$$status; \
+ progfailed="$$progfailed $$prog"; \
+ fi; \
+ fi; \
+ done; \
+ if test $$teststatus != 0; then \
+ echo "Programs failed:$$progfailed"; \
+ fi; \
+ exit $$teststatus
+
+# DO NOT REMOVE
diff --git a/test/Makefile.win b/test/Makefile.win
new file mode 100644
index 0000000..50ea7ae
--- /dev/null
+++ b/test/Makefile.win
@@ -0,0 +1,273 @@
+# PROGRAMS includes all test programs built on this platform.
+# STDTEST_PORTABLE
+# test programs invoked via standard user interface, run on all platforms
+# TESTS
+# test modules invoked through the abts suite (./testall)
+# STDTEST_NONPORTABLE
+# test programs invoked via standard user interface, not portable
+# OTHER_PROGRAMS
+# programs such as sendfile, that have to be invoked in a special sequence
+# or with special parameters
+# TESTALL_COMPONENTS
+# programs such as globalmutexchild which the various TESTS will invoke
+# to validate process creation, pipes, dso mechanisms and so forth
+
+# Windows Specific;
+# MODEL
+# dynamic or static - refers to which set of bindings are desired
+# and controls which libraries (apr-1 or libapr-1) will be linked.
+# OUTDIR
+# the library path of the libraries, and also the path within test/
+# where all of the tests for that library will be built
+
+!IFNDEF MODEL
+MODEL=dynamic
+!ENDIF
+
+INCDIR=../include
+
+!IFNDEF OUTDIR
+!IF "$(MODEL)" == "static"
+OUTDIR=LibR
+!ELSE
+OUTDIR=Release
+!ENDIF
+
+!IF [$(COMSPEC) /c cl /nologo /? \
+ | $(SystemRoot)\System32\find.exe "x64" >NUL ] == 0
+OUTDIR=x64\$(OUTDIR)
+!ENDIF
+!ENDIF
+
+!IF !EXIST("$(OUTDIR)\.")
+!IF ([$(COMSPEC) /C mkdir $(OUTDIR)] == 0)
+!ENDIF
+!ENDIF
+
+!IFNDEF INTDIR
+INTDIR=$(OUTDIR)
+!ELSE
+!IF !EXIST("$(INTDIR)\.")
+!IF ([$(COMSPEC) /C mkdir $(INTDIR)] == 0)
+!ENDIF
+!ENDIF
+!ENDIF
+
+!MESSAGE Building tests into $(OUTDIR) for $(MODEL)
+
+STDTEST_PORTABLE = \
+ $(OUTDIR)\testapp.exe \
+ $(OUTDIR)\testall.exe \
+ $(OUTDIR)\testlockperf.exe \
+ $(OUTDIR)\testmutexscope.exe
+
+OTHER_PROGRAMS = \
+ $(OUTDIR)\echod.exe \
+ $(OUTDIR)\sendfile.exe \
+ $(OUTDIR)\sockperf.exe
+
+TESTALL_COMPONENTS = \
+ $(OUTDIR)\mod_test.dll \
+ $(OUTDIR)\occhild.exe \
+ $(OUTDIR)\readchild.exe \
+ $(OUTDIR)\proc_child.exe \
+ $(OUTDIR)\tryread.exe \
+ $(OUTDIR)\sockchild.exe \
+ $(OUTDIR)\testshmproducer.exe \
+ $(OUTDIR)\testshmconsumer.exe \
+ $(OUTDIR)\globalmutexchild.exe
+
+ALL_TESTS = $(INTDIR)\testutil.obj $(INTDIR)\testtime.obj \
+ $(INTDIR)\teststr.obj $(INTDIR)\testvsn.obj \
+ $(INTDIR)\testipsub.obj $(INTDIR)\testmmap.obj \
+ $(INTDIR)\testud.obj $(INTDIR)\testtable.obj \
+ $(INTDIR)\testsleep.obj $(INTDIR)\testpools.obj \
+ $(INTDIR)\testfmt.obj $(INTDIR)\testfile.obj \
+ $(INTDIR)\testdir.obj $(INTDIR)\testfileinfo.obj \
+ $(INTDIR)\testrand.obj $(INTDIR)\testdso.obj \
+ $(INTDIR)\testoc.obj $(INTDIR)\testdup.obj \
+ $(INTDIR)\testsockets.obj $(INTDIR)\testproc.obj \
+ $(INTDIR)\testpoll.obj $(INTDIR)\testlock.obj \
+ $(INTDIR)\testsockopt.obj $(INTDIR)\testpipe.obj \
+ $(INTDIR)\testthread.obj $(INTDIR)\testhash.obj \
+ $(INTDIR)\testargs.obj $(INTDIR)\testnames.obj \
+ $(INTDIR)\testuser.obj $(INTDIR)\testpath.obj \
+ $(INTDIR)\testenv.obj $(INTDIR)\testprocmutex.obj \
+ $(INTDIR)\testfnmatch.obj $(INTDIR)\testatomic.obj \
+ $(INTDIR)\testflock.obj $(INTDIR)\testshm.obj \
+ $(INTDIR)\testsock.obj $(INTDIR)\testglobalmutex.obj \
+ $(INTDIR)\teststrnatcmp.obj $(INTDIR)\testfilecopy.obj \
+ $(INTDIR)\testtemp.obj $(INTDIR)\testlfs.obj \
+ $(INTDIR)\testcond.obj $(INTDIR)\testescape.obj \
+ $(INTDIR)\testskiplist.obj $(INTDIR)\testencode.obj
+
+CLEAN_DATA = testfile.tmp lfstests\large.bin \
+ data\testputs.txt data\testbigfprintf.dat \
+ data\testwritev.txt data\testwritev_full.txt \
+ data\testflush.dat data\testxthread.dat \
+ data\apr.testshm.shm lfstests\buffer.bin
+
+CLEAN_BUILDDIRS = Debug Release LibD LibR 9x x64
+
+TEST_SUBDIRS = internal
+
+PROGRAMS = $(TESTALL_COMPONENTS) $(STDTEST_PORTABLE) $(STDTEST_NONPORTABLE) \
+ $(OTHER_PROGRAMS)
+
+TARGETS = $(PROGRAMS)
+
+# bring in rules.mk for standard functionality
+ALL: $(TARGETS)
+
+CL = cl.exe
+LD = link.exe
+
+!IF "$(MODEL)" == "static"
+LOCAL_LIB= ..\$(OUTDIR)\apr-1.lib
+APP_LIB= ..\$(OUTDIR)\aprapp-1.lib
+STATIC_CFLAGS = /D APR_DECLARE_STATIC
+!ELSE
+LOCAL_LIB= ..\$(OUTDIR)\libapr-1.lib
+APP_LIB= ..\$(OUTDIR)\libaprapp-1.lib
+STATIC_CFLAGS =
+!ENDIF
+
+!IFDEF _DEBUG
+DEBUG_CFLAGS = /MDd
+!ELSE
+DEBUG_CFLAGS = /MD
+!ENDIF
+
+INCLUDES=/I "$(INCDIR)" /I "$(INCDIR)/private"
+
+CFLAGS = /nologo /c /W3 /Gm /EHsc /Zi /Od $(INCLUDES) \
+ $(STATIC_CFLAGS) $(DEBUG_CFLAGS) /D "BINPATH=$(OUTDIR:\=/)" \
+ /D _DEBUG /D WIN32 /Fo"$(INTDIR)/" /FD
+
+LD_LIBS = kernel32.lib advapi32.lib ws2_32.lib wsock32.lib \
+ ole32.lib shell32.lib rpcrt4.lib
+
+LDFLAGS = /nologo /debug /subsystem:console /incremental:no
+SHLDFLAGS = /nologo /dll /debug /subsystem:windows /incremental:no
+
+.c{$(INTDIR)}.obj::
+ $(CL) $(CFLAGS) -c $< -Fd$(INTDIR)\ $(INCLUDES)
+
+# STDTEST_PORTABLE;
+
+$(OUTDIR)\testall.exe: $(ALL_TESTS) $(INTDIR)\abts.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\testapp.exe: $(INTDIR)/testapp.obj $(LOCAL_LIB) $(APP_LIB)
+ $(LD) $(LDFLAGS) /entry:wmainCRTStartup /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;2
+
+$(OUTDIR)\testlockperf.exe: $(INTDIR)\testlockperf.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\testmutexscope.exe: $(INTDIR)\testmutexscope.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+# OTHER_PROGRAMS;
+
+$(OUTDIR)\echod.exe: $(INTDIR)\echod.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\sendfile.exe: $(INTDIR)\sendfile.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\sockperf.exe: $(INTDIR)\sockperf.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+# TESTALL_COMPONENTS;
+
+$(OUTDIR)\globalmutexchild.exe: $(INTDIR)\globalmutexchild.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\mod_test.dll: $(INTDIR)/mod_test.obj $(LOCAL_LIB)
+ $(LD) $(SHLDFLAGS) /out:"$@" $** \
+ /export:print_hello /export:count_reps $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;2
+
+$(OUTDIR)\occhild.exe: $(INTDIR)\occhild.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\proc_child.exe: $(INTDIR)\proc_child.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\readchild.exe: $(INTDIR)\readchild.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\sockchild.exe: $(INTDIR)\sockchild.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\testshmconsumer.exe: $(INTDIR)\testshmconsumer.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\testshmproducer.exe: $(INTDIR)\testshmproducer.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+$(OUTDIR)\tryread.exe: $(INTDIR)\tryread.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+
+cleandata:
+ @for %f in ($(CLEAN_DATA)) do @if EXIST %f del /f %f
+
+clean: cleandata
+ @if EXIST $(INTDIR)\. rmdir /s /q $(INTDIR)
+ @if EXIST $(OUTDIR)\. rmdir /s /q $(OUTDIR)
+ @for %d in ($(TEST_SUBDIRS)) do \
+ %COMSPEC% /c "cd %%d && $(MAKE) -f Makefile.win clean" \
+
+cleanall:
+ @for %d in ($(CLEAN_BUILDDIRS) $(INTDIR) $(OUTDIR)) do \
+ @if EXIST %d\. rmdir /s /q %d
+ @for %d in ($(TEST_SUBDIRS)) do \
+ %COMSPEC% /c "cd %%d & $(MAKE) -f Makefile.win cleanall" \
+
+
+!IF "$(MODEL)" != "static"
+PATH=$(OUTDIR);..\$(OUTDIR);$(PATH)
+!ENDIF
+
+check: $(TESTALL_COMPONENTS) $(STDTEST_PORTABLE) $(STDTEST_NONPORTABLE)
+ @for %p in ($(STDTEST_PORTABLE) $(STDTEST_NONPORTABLE)) do @( \
+ echo Testing %p && %p -v || echo %p failed \
+ )
+
+checkall: check
+ @for %d in ($(TEST_SUBDIRS)) do \
+ %COMSPEC% /c "cd %%d && $(MAKE) -f Makefile.win check" \
+
+# DO NOT REMOVE
diff --git a/test/NWGNUaprtest b/test/NWGNUaprtest
new file mode 100644
index 0000000..6aaeafd
--- /dev/null
+++ b/test/NWGNUaprtest
@@ -0,0 +1,307 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APR)/include/arch/NetWare \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = aprtest
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = NLM is to test the apr layer
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# This is used by the '-screenname' directive. If left blank,
+# 'Apache for NetWare' Thread will be used.
+#
+NLM_SCREEN_NAME = aprtest
+
+#
+# If this is specified, it will override VERSION value in
+# $(APR_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE = 524288
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(APR)/misc/netware/apache.xdc. XDCData can
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/aprtest.nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+
+FILES_nlm_objs = \
+ $(OBJDIR)/abts.o \
+ $(OBJDIR)/testargs.o \
+ $(OBJDIR)/testatomic.o \
+ $(OBJDIR)/testdir.o \
+ $(OBJDIR)/testdup.o \
+ $(OBJDIR)/testdso.o \
+ $(OBJDIR)/testenv.o \
+ $(OBJDIR)/testescape.o \
+ $(OBJDIR)/testfilecopy.o \
+ $(OBJDIR)/testfileinfo.o \
+ $(OBJDIR)/testfile.o \
+ $(OBJDIR)/testflock.o \
+ $(OBJDIR)/testfmt.o \
+ $(OBJDIR)/testfnmatch.o \
+ $(OBJDIR)/testglobalmutex.o \
+ $(OBJDIR)/testhash.o \
+ $(OBJDIR)/testipsub.o \
+ $(OBJDIR)/testlfs.o \
+ $(OBJDIR)/testlock.o \
+ $(OBJDIR)/testcond.o \
+ $(OBJDIR)/testmmap.o \
+ $(OBJDIR)/testnames.o \
+ $(OBJDIR)/testoc.o \
+ $(OBJDIR)/testpath.o \
+ $(OBJDIR)/testpipe.o \
+ $(OBJDIR)/testpoll.o \
+ $(OBJDIR)/testpools.o \
+ $(OBJDIR)/testproc.o \
+ $(OBJDIR)/testprocmutex.o \
+ $(OBJDIR)/testrand.o \
+ $(OBJDIR)/testshm.o \
+ $(OBJDIR)/testskiplist.o \
+ $(OBJDIR)/testsleep.o \
+ $(OBJDIR)/testsock.o \
+ $(OBJDIR)/testsockets.o \
+ $(OBJDIR)/testsockopt.o \
+ $(OBJDIR)/teststr.o \
+ $(OBJDIR)/teststrnatcmp.o \
+ $(OBJDIR)/testtable.o \
+ $(OBJDIR)/testtemp.o \
+ $(OBJDIR)/testthread.o \
+ $(OBJDIR)/testtime.o \
+ $(OBJDIR)/testud.o \
+ $(OBJDIR)/testuser.o \
+ $(OBJDIR)/testutil.o \
+ $(OBJDIR)/testvsn.o \
+ $(OBJDIR)/nw_misc.o \
+ $(EOLIST)
+
+# Pending tests
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ Libc \
+ APRLIB \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override the default copyright.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @libc.imp \
+ @aprlib.imp \
+ $(EOLIST)
+
+# Don't link with Winsock if standard sockets are being used
+ifndef USE_STDSOCKETS
+FILES_nlm_Ximports += @ws2nlm.imp \
+ $(EOLIST)
+endif
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
diff --git a/test/NWGNUechod b/test/NWGNUechod
new file mode 100644
index 0000000..f70a53d
--- /dev/null
+++ b/test/NWGNUechod
@@ -0,0 +1,253 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APR)/include/arch/NetWare \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = echod
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = Echo Daemon NLM to test socket performance
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# This is used by the '-screenname' directive. If left blank,
+# 'Apache for NetWare' Thread will be used.
+#
+NLM_SCREEN_NAME = $(NLM_NAME)
+
+#
+# If this is specified, it will override VERSION value in
+# $(APR_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE =
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION, MULTIPLE
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(APR)/misc/netware/apache.xdc. XDCData can
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/$(NLM_NAME).nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/$(NLM_NAME).o \
+ $(OBJDIR)/nw_misc.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ Libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override the default copyright.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APR_WORK)/build/NWGNUtail.inc
+
diff --git a/test/NWGNUglobalmutexchild b/test/NWGNUglobalmutexchild
new file mode 100644
index 0000000..3db7a10
--- /dev/null
+++ b/test/NWGNUglobalmutexchild
@@ -0,0 +1,252 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APR)/include/arch/NetWare \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = globalmutexchild
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = child NLM to test the global Mutex layer
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# This is used by the '-screenname' directive. If left blank,
+# 'Apache for NetWare' Thread will be used.
+#
+NLM_SCREEN_NAME = DEFAULT
+
+#
+# If this is specified, it will override VERSION value in
+# $(APR_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE =
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION, MULTIPLE
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(APR)/misc/netware/apache.xdc. XDCData can
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/globalmutexchild.nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/globalmutexchild.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ Libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override the default copyright.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
diff --git a/test/NWGNUmakefile b/test/NWGNUmakefile
new file mode 100644
index 0000000..a1085d7
--- /dev/null
+++ b/test/NWGNUmakefile
@@ -0,0 +1,60 @@
+#
+# Declare the sub-directories to be built here
+#
+
+SUBDIRS = \
+ $(EOLIST)
+
+#
+# Get the 'head' of the build environment. This includes default targets and
+# paths to tools
+#
+
+include $(APR_WORK)/build/NWGNUhead.inc
+
+#
+# build this level's files
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/aprtest.nlm \
+ $(OBJDIR)/echod.nlm \
+ $(OBJDIR)/globalmutexchild.nlm \
+ $(OBJDIR)/mod_test.nlm \
+ $(OBJDIR)/proc_child.nlm \
+ $(OBJDIR)/readchild.nlm \
+ $(OBJDIR)/sockchild.nlm \
+ $(OBJDIR)/sockperf.nlm \
+ $(OBJDIR)/testatmc.nlm \
+ $(OBJDIR)/tryread.nlm \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+ $(call COPY,$(OBJDIR)/*.nlm,$(INSTALLBASE))
+ $(call COPYR,data,$(INSTALLBASE)/data/)
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
diff --git a/test/NWGNUmod_test b/test/NWGNUmod_test
new file mode 100644
index 0000000..b293de8
--- /dev/null
+++ b/test/NWGNUmod_test
@@ -0,0 +1,254 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APR)/include/arch/NetWare \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = mod_test
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = DSO NLM to test the apr DSO loading layer
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# This is used by the '-screenname' directive. If left blank,
+# 'Apache for NetWare' Thread will be used.
+#
+NLM_SCREEN_NAME = DEFAULT
+
+#
+# If this is specified, it will override VERSION value in
+# $(APR_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE =
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(APR)/misc/netware/apache.xdc. XDCData can
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/mod_test.nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/mod_test.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ Libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override the default copyright.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ print_hello \
+ count_reps \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
diff --git a/test/NWGNUproc_child b/test/NWGNUproc_child
new file mode 100644
index 0000000..4f3f183
--- /dev/null
+++ b/test/NWGNUproc_child
@@ -0,0 +1,252 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APR)/include/arch/NetWare \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = proc_child
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = child NLM to test the proc layer
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# This is used by the '-screenname' directive. If left blank,
+# 'Apache for NetWare' Thread will be used.
+#
+NLM_SCREEN_NAME = DEFAULT
+
+#
+# If this is specified, it will override VERSION value in
+# $(APR_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE =
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION, MULTIPLE
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(APR)/misc/netware/apache.xdc. XDCData can
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/proc_child.nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/proc_child.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ Libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override the default copyright.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
diff --git a/test/NWGNUreadchild b/test/NWGNUreadchild
new file mode 100644
index 0000000..5e6e484
--- /dev/null
+++ b/test/NWGNUreadchild
@@ -0,0 +1,252 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APR)/include/arch/NetWare \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = readchild
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = child NLM to test the pipe layer
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# This is used by the '-screenname' directive. If left blank,
+# 'Apache for NetWare' Thread will be used.
+#
+NLM_SCREEN_NAME = DEFAULT
+
+#
+# If this is specified, it will override VERSION value in
+# $(APR_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE =
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION, MULTIPLE
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(APR)/misc/netware/apache.xdc. XDCData can
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/readchild.nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/readchild.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ Libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override the default copyright.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
diff --git a/test/NWGNUsockchild b/test/NWGNUsockchild
new file mode 100644
index 0000000..312ddd1
--- /dev/null
+++ b/test/NWGNUsockchild
@@ -0,0 +1,252 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APR)/include/arch/NetWare \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = sockchild
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = socket NLM to test sockets
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# This is used by the '-screenname' directive. If left blank,
+# 'Apache for NetWare' Thread will be used.
+#
+NLM_SCREEN_NAME = DEFAULT
+
+#
+# If this is specified, it will override VERSION value in
+# $(APR_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE =
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION, MULTIPLE
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(APR)/misc/netware/apache.xdc. XDCData can
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/sockchild.nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/sockchild.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ Libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override the default copyright.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
diff --git a/test/NWGNUsockperf b/test/NWGNUsockperf
new file mode 100644
index 0000000..f289d87
--- /dev/null
+++ b/test/NWGNUsockperf
@@ -0,0 +1,253 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APR)/include/arch/NetWare \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = sockperf
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = socket NLM to test socket performance
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# This is used by the '-screenname' directive. If left blank,
+# 'Apache for NetWare' Thread will be used.
+#
+NLM_SCREEN_NAME = $(NLM_NAME)
+
+#
+# If this is specified, it will override VERSION value in
+# $(APR_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE =
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION, MULTIPLE
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(APR)/misc/netware/apache.xdc. XDCData can
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/$(NLM_NAME).nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/$(NLM_NAME).o \
+ $(OBJDIR)/nw_misc.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ Libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override the default copyright.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APR_WORK)/build/NWGNUtail.inc
+
diff --git a/test/NWGNUtestatmc b/test/NWGNUtestatmc
new file mode 100644
index 0000000..d1308ea
--- /dev/null
+++ b/test/NWGNUtestatmc
@@ -0,0 +1,255 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APR)/include/arch/NetWare \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = testatmc
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = NLM is to test the atomic functions
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# This is used by the '-screenname' directive. If left blank,
+# 'Apache for NetWare' Thread will be used.
+#
+NLM_SCREEN_NAME = $(NLM_NAME)
+
+#
+# If this is specified, it will override VERSION value in
+# $(APR_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE =
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS =
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(APR)/misc/netware/apache.xdc. XDCData can
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/testatmc.nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/testatomic.o \
+ $(OBJDIR)/nw_misc.o \
+ $(EOLIST)
+
+# Pending tests
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ Libc \
+ APRLIB \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override the default copyright.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @libc.imp \
+ @aprlib.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
diff --git a/test/NWGNUtryread b/test/NWGNUtryread
new file mode 100644
index 0000000..0ed52ea
--- /dev/null
+++ b/test/NWGNUtryread
@@ -0,0 +1,252 @@
+#
+# Make sure all needed macro's are defined
+#
+
+#
+# Get the 'head' of the build environment if necessary. This includes default
+# targets and paths to tools
+#
+
+ifndef EnvironmentDefined
+include $(APR_WORK)/build/NWGNUhead.inc
+endif
+
+#
+# These directories will be at the beginning of the include list, followed by
+# INCDIRS
+#
+XINCDIRS += \
+ $(APR)/include \
+ $(APR)/include/arch/NetWare \
+ $(EOLIST)
+
+#
+# These flags will come after CFLAGS
+#
+XCFLAGS += \
+ $(EOLIST)
+
+#
+# These defines will come after DEFINES
+#
+XDEFINES += \
+ $(EOLIST)
+
+#
+# These flags will be added to the link.opt file
+#
+XLFLAGS += \
+ $(EOLIST)
+
+#
+# These values will be appended to the correct variables based on the value of
+# RELEASE
+#
+ifeq "$(RELEASE)" "debug"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "noopt"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+ifeq "$(RELEASE)" "release"
+XINCDIRS += \
+ $(EOLIST)
+
+XCFLAGS += \
+ $(EOLIST)
+
+XDEFINES += \
+ $(EOLIST)
+
+XLFLAGS += \
+ $(EOLIST)
+endif
+
+#
+# These are used by the link target if an NLM is being generated
+# This is used by the link 'name' directive to name the nlm. If left blank
+# TARGET_nlm (see below) will be used.
+#
+NLM_NAME = tryread
+
+#
+# This is used by the link '-desc ' directive.
+# If left blank, NLM_NAME will be used.
+#
+NLM_DESCRIPTION = reader NLM to test flock
+
+#
+# This is used by the '-threadname' directive. If left blank,
+# NLM_NAME Thread will be used.
+#
+NLM_THREAD_NAME = $(NLM_NAME)
+
+#
+# This is used by the '-screenname' directive. If left blank,
+# 'Apache for NetWare' Thread will be used.
+#
+NLM_SCREEN_NAME = DEFAULT
+
+#
+# If this is specified, it will override VERSION value in
+# $(APR_WORK)/build/NWGNUenvironment.inc
+#
+NLM_VERSION =
+
+#
+# If this is specified, it will override the default of 64K
+#
+NLM_STACK_SIZE =
+
+#
+# If this is specified it will be used by the link '-entry' directive
+#
+NLM_ENTRY_SYM =
+
+#
+# If this is specified it will be used by the link '-exit' directive
+#
+NLM_EXIT_SYM =
+
+#
+# If this is specified it will be used by the link '-check' directive
+#
+NLM_CHECK_SYM =
+
+#
+# If this is specified it will be used by the link '-flags' directive
+#
+NLM_FLAGS = AUTOUNLOAD, PSEUDOPREEMPTION, MULTIPLE
+
+#
+# If this is specified it will be linked in with the XDCData option in the def
+# file instead of the default of $(APR)/misc/netware/apache.xdc. XDCData can
+# be disabled by setting APACHE_UNIPROC in the environment
+#
+XDCDATA =
+
+#
+# Declare all target files (you must add your files here)
+#
+
+#
+# If there is an NLM target, put it here
+#
+TARGET_nlm = \
+ $(OBJDIR)/tryread.nlm \
+ $(EOLIST)
+
+#
+# If there is an LIB target, put it here
+#
+TARGET_lib = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the NLM target above.
+# Paths must all use the '/' character
+#
+FILES_nlm_objs = \
+ $(OBJDIR)/tryread.o \
+ $(EOLIST)
+
+#
+# These are the LIB files needed to create the NLM target above.
+# These will be added as a library command in the link.opt file.
+#
+FILES_nlm_libs = \
+ $(PRELUDE) \
+ $(EOLIST)
+
+#
+# These are the modules that the above NLM target depends on to load.
+# These will be added as a module command in the link.opt file.
+#
+FILES_nlm_modules = \
+ aprlib \
+ Libc \
+ $(EOLIST)
+
+#
+# If the nlm has a msg file, put it's path here
+#
+FILE_nlm_msg =
+
+#
+# If the nlm has a hlp file put it's path here
+#
+FILE_nlm_hlp =
+
+#
+# If this is specified, it will override the default copyright.
+#
+FILE_nlm_copyright =
+
+#
+# Any additional imports go here
+#
+FILES_nlm_Ximports = \
+ @aprlib.imp \
+ @libc.imp \
+ $(EOLIST)
+
+#
+# Any symbols exported to here
+#
+FILES_nlm_exports = \
+ $(EOLIST)
+
+#
+# These are the OBJ files needed to create the LIB target above.
+# Paths must all use the '/' character
+#
+FILES_lib_objs = \
+ $(EOLIST)
+
+#
+# implement targets and dependancies (leave this section alone)
+#
+
+libs :: $(OBJDIR) $(TARGET_lib)
+
+nlms :: libs $(TARGET_nlm)
+
+#
+# Updated this target to create necessary directories and copy files to the
+# correct place. (See $(APR_WORK)/build/NWGNUhead.inc for examples)
+#
+install :: nlms FORCE
+
+#
+# Any specialized rules here
+#
+
+#
+# Include the 'tail' makefile that has targets that depend on variables defined
+# in this makefile
+#
+
+include $(APRBUILD)/NWGNUtail.inc
+
diff --git a/test/README b/test/README
new file mode 100644
index 0000000..408a6a2
--- /dev/null
+++ b/test/README
@@ -0,0 +1,332 @@
+Writing APR tests
+
+All APR tests should be executable in 2 ways, as an individual program, or
+as a part of the full test suite. The full test suite is controlled with
+the testall program. At the beginning of the testall.c file, there is an
+array of functions called tests. The testall program loops through this
+array calling each function. Each function returns a CuSuite variable, which
+is then added to the SuiteList. Once all Suites have been added, the SuiteList
+is executed, and the output is printed to the screen. All functions in the
+array should follow the same basic format:
+
+The Full Suite
+--------------
+
+/* The driver function. This must return a CuSuite variable, which will
+ * then be used to actually run the tests. Essentially, all Suites are a
+ * collection of tests. The driver will take each Suite, and put it in a
+ * SuiteList, which is a collection of Suites.
+ */
+CuSuite *testtime(void)
+{
+ /* The actual suite, this must be created for each test program. Please
+ * give it a useful name, that will inform the user of the feature being
+ * tested.
+ */
+ CuSuite *suite = CuSuiteNew("Test Time");
+
+ /* Each function must be added to the suite. Each function represents
+ * a single test. It is possible to test multiple features in a single
+ * function, although no tests currently do that.
+ */
+ SUITE_ADD_TEST(suite, test_now);
+ SUITE_ADD_TEST(suite, test_gmtstr);
+ SUITE_ADD_TEST(suite, test_localstr);
+ SUITE_ADD_TEST(suite, test_exp_get_gmt);
+ SUITE_ADD_TEST(suite, test_exp_get_lt);
+ SUITE_ADD_TEST(suite, test_imp_gmt);
+ SUITE_ADD_TEST(suite, test_rfcstr);
+ SUITE_ADD_TEST(suite, test_ctime);
+ SUITE_ADD_TEST(suite, test_strftime);
+ SUITE_ADD_TEST(suite, test_strftimesmall);
+ SUITE_ADD_TEST(suite, test_exp_tz);
+ SUITE_ADD_TEST(suite, test_strftimeoffset);
+
+ /* You must return the suite so that the driver knows which suites to
+ * run.
+ */
+ return suite;
+}
+
+Building the full driver
+------------------------
+
+All you need to do to build the full driver is run:
+
+ make
+
+To run it, run:
+
+ ./testall
+
+Running individual tests
+------------------------
+
+It is not possible to build individual tests, however it is possible to
+run individual tests. When running the test suite, specify the name of the
+tests that you want to run on the command line. For example:
+
+ ./testall teststr testrand
+
+Will run the Strings and Random generator tests.
+
+Reading the test suite output
+-----------------------------
+
+Once you run the test suite, you will get output like:
+
+All APR Tests:
+ Test Strings: ....
+ Test Time: ............
+
+16 tests run: 16 passed, 0 failed, 0 not implemented.
+
+Known test failures are documented in ../STATUS.
+
+There are a couple of things to look at with this. First, if you look at the
+first function in this document, you should notice that the string passed to
+the CuSuiteNew function is in the output. That is why the string should
+explain the feature you are testing.
+
+Second, this test passed completely. This is obvious in two ways. First, and
+most obvious, the summary line tells you that 16 tests were run and 16 tests
+passed. However, the results can also be found in the lines above. Every
+'.' in the output represents a passed test.
+
+If a test fails, the output will look like:
+
+All APR Tests:
+ Test Strings: ....
+ Test Time: ..F.........
+
+16 tests run: 15 passed, 1 failed, 0 not implemented.
+
+This is not very useful, because you don't know which test failed. However,
+once you know that a test failed, you can run the suite again, with the
+-v option. If you do this, you will get something like:
+
+All APR Tests:
+ Test Strings: ....
+ Test Time: ..F.........
+
+16 tests run: 15 passed, 1 failed, 0 not implemented.
+Failed tests:
+1) test_localstr: assert failed
+
+In this case, we know the test_localstr function failed, and there is an
+Assert in this that failed (I modified the test to fail for this document).
+Now, you can look at what that test does, and why it would have failed.
+
+There is one other possible output for the test suite (run with -v):
+
+All APR Tests:
+ Test Strings: ....
+ Test Time: ..N.........
+
+16 tests run: 15 passed, 0 failed, 1 not implemented.
+
+Not Implemented tests:
+
+Not Implemented tests:
+1) test_localstr: apr_time_exp_lt not implemented on this platform
+
+The 'N' means that a function has returned APR_ENOTIMPL. This should be
+treated as an error, and the function should be implemented as soon as
+possible.
+
+Adding New test Suites to the full driver
+-------------------------------------------
+
+To add a new Suite to the full driver, you must make a couple of modifications.
+
+1) Edit test_apr.h, and add the prototype for the function.
+2) Edit testall.c, and add the function and name to the tests array.
+3) Edit Makefile.in, and add the .lo file to the testall target.
+
+Once those four things are done, your tests will automatically be added
+to the suite.
+
+Writting an ABTS unit test
+--------------------------
+
+The aim of this quick and dirty Howto is to give a short introduction
+to APR (Apache Portable Runtime) unit tests, and how to write
+one. During my Google's Summer of Code 2005 project, I discovered a
+small bug in the APR-Util's date parsing routines, and I needed to
+write a unit test for the fixed code. I decided to write this
+documentation because I did not find any. Thanks to Garrett Rooney for
+his help on writing the unit test !
+
+The APR and APR-Util libraries provide a platform independent API for
+software developers. They contain a lot of modules, including network
+programming, threads, string and memory management, etc. All these
+functions need to be heavily tested so that developers can be sure the
+library is reliable.
+
+The ABTS give APR developers the ability to build a complete test
+suite for the bunch of tests they wrote, which can then be ran under
+various platforms. In this Howto, I will try teach you how to write an
+ABTS unit test.
+
+As you may probably know, a unit test is a simple routine which tests
+a very specific feature of the tested software or library. To build a
+unit test, you need three different things :
+
+ * the to-be-tested function,
+ * the input data that will be given to the function,
+ * the expected output data.
+
+The principle of a unit test is very simple : for each entry in your
+set of input data, we pass it to our function, fetch what the function
+returned and compare it to the corresponding expected output data. Of
+course, the more edge cases you can test, the better your input data
+set is.
+
+The ABTS aims to quicken the write of unit test, and make them
+available to the whole test suite by providing a set of preprocessor
+macros. Adding a unit test to a test suite can be easily done by the
+following piece of code :
+
+abts_suite *testdaterfc(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite);
+ abts_run_test(suite, test_date_rfc, NULL);
+
+ return suite;
+}
+
+Where test_date_rfc is the name of the function performing the
+test. Writing such a function is, in the light of the explanation I
+just gave, pretty much easy too. As I said, we need to check every
+entry of our input data set. That gives us a loop. For each loop
+iteration, we call our to-be-tested function, grab its result and
+compare the returned value with the expected one.
+
+Test functions must have the following prototype :
+
+static void my_test_function(abts_case *tc, void *data);
+
+The comparison step is performed by the ABTS, thus giving the
+whole test suite the correct behavior if your unit test fails. Here
+comes a list of the available test methods :
+
+ABTS_INT_EQUAL(tc, a, b)
+ABTS_INT_NEQUAL(tc, a, b)
+ABTS_STR_EQUAL(tc, a, b)
+ABTS_STR_NEQUAL(tc, a, b, c)
+ABTS_PTR_NOTNULL(tc, b)
+ABTS_PTR_EQUAL(tc, a, b)
+ABTS_TRUE(tc, b)
+ABTS_FAIL(tc, b)
+ABTS_NOT_IMPL(tc, b)
+ABTS_ASSERT(tc, a, b)
+
+The first argument, tc is a reference to the unit test currently
+processed by the test suite (passed to your test function). The other
+parameters are the data to be tested. For example, the following line
+will never make your unit test fail :
+
+ABTS_INT_EQUAL(tc, 1, 1);
+
+See, it's easy ! Let's take a look at the complete example :
+testdaterfc. We want to test our date string parser. For this, we will
+use some chosen date strings (from mail headers for example) written
+in various formats but that should all be handled by our function, and
+their equivalents in correct RFC822 format.
+
+The function we want to test returns an apr_time_t}, which will be
+directly given as input to the apr_rfc822_date() function, thus
+producing the corresponding RFC822 date string. All we need to do
+after this is to call the correct test method from the ABTS macros !
+
+You can take a look at the apr-util/test/testdaterfc.c file for the
+complete source code of this unit test.
+
+Although this Howto is very small and mostly dedicated to the
+testdaterfc unit test, I hope you'll find it useful. Good luck !
+
+Writing tests for CuTest (no longer used)
+-----------------------------------------
+
+There are a couple of rules for writing good tests for the test suite.
+
+1) All tests can determine for themselves if it passed or not. This means
+that there is no reason for the person running the test suite to interpret
+the results of the tests.
+2) Never use printf to add to the output of the test suite. The suite
+library should be able to print all of the information required to debug
+a problem.
+3) Functions should be tested with both positive and negative tests. This
+means that you should test things that should both succeed and fail.
+4) Just checking the return code does _NOT_ make a useful test. You must
+check to determine that the test actually did what you expected it to do.
+
+An example test
+---------------
+
+Finally, we will look at a quick test:
+
+/* All tests are passed a CuTest variable. This is how the suite determines
+ * if the test succeeded or failed.
+ */
+static void test_localstr(CuTest *tc)
+{
+ apr_status_t rv;
+ apr_time_exp_t xt;
+ time_t os_now;
+
+ rv = apr_time_exp_lt(&xt, now);
+ os_now = now / APR_USEC_PER_SEC;
+
+ /* If the function can return APR_ENOTIMPL, then you should check for it.
+ * This allows platform implementors to know if they have to implement
+ * the function.
+ */
+ if (rv == APR_ENOTIMPL) {
+ CuNotImpl(tc, "apr_time_exp_lt");
+ }
+
+ /* It often helps to ensure that the return code was APR_SUCESS. If it
+ * wasn't, then we know the test failed.
+ */
+ CuAssertTrue(tc, rv == APR_SUCCESS);
+
+ /* Now that we know APR thinks it worked properly, we need to check the
+ * output to ensure that we got what we expected.
+ */
+ CuAssertStrEquals(tc, "2002-08-14 12:05:36.186711 -25200 [257 Sat] DST",
+ print_time(p, &xt));
+}
+
+Notice, the same test can fail for any of a number of reasons. The first
+test to fail ends the test.
+
+CuTest
+------
+
+CuTest is an open source test suite written by Asim Jalis. It has been
+released under the zlib/libpng license. That license can be found in the
+CuTest.c and CuTest.h files.
+
+The version of CuTest that is included in the APR test suite has been modified
+from the original distribution in the following ways:
+
+1) The original distribution does not have a -v flag, the details are always
+printed.
+2) The NotImplemented result does not exist.
+3) SuiteLists do not exist. In the original distribution, you can add suites
+to suites, but it just adds the tests in the first suite to the list of tests
+in the original suite. The output wasn't as detailed as I wanted, so I created
+SuiteLists.
+
+The first two modifications have been sent to the original author of CuTest,
+but they have not been integrated into the base distribution. The SuiteList
+changes will be sent to the original author soon.
+
+The modified version of CuTest is not currently in any CVS or Subversion
+server. In time, it will be hosted at rkbloom.net.
+
+There are currently no docs for how to write tests, but the teststr and
+testtime programs should give an idea of how it is done. In time, a document
+should be written to define how tests are written.
+
diff --git a/test/abts.c b/test/abts.c
new file mode 100644
index 0000000..ef9f9db
--- /dev/null
+++ b/test/abts.c
@@ -0,0 +1,451 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "abts.h"
+#include "abts_tests.h"
+#include "testutil.h"
+
+#define ABTS_STAT_SIZE 6
+static char status[ABTS_STAT_SIZE] = {'|', '/', '-', '|', '\\', '-'};
+static int curr_char;
+static int verbose = 0;
+static int exclude = 0;
+static int quiet = 0;
+static int list_tests = 0;
+
+const char **testlist = NULL;
+
+static int find_test_name(const char *testname) {
+ int i;
+ for (i = 0; testlist[i] != NULL; i++) {
+ if (!strcmp(testlist[i], testname)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Determine if the test should be run at all */
+static int should_test_run(const char *testname) {
+ int found = 0;
+ if (list_tests == 1) {
+ return 0;
+ }
+ if (testlist == NULL) {
+ return 1;
+ }
+ found = find_test_name(testname);
+ if ((found && !exclude) || (!found && exclude)) {
+ return 1;
+ }
+ return 0;
+}
+
+static void reset_status(void)
+{
+ curr_char = 0;
+}
+
+static void update_status(void)
+{
+ if (!quiet) {
+ curr_char = (curr_char + 1) % ABTS_STAT_SIZE;
+ fprintf(stdout, "\b%c", status[curr_char]);
+ fflush(stdout);
+ }
+}
+
+static void end_suite(abts_suite *suite)
+{
+ if (suite != NULL) {
+ sub_suite *last = suite->tail;
+ if (!quiet) {
+ fprintf(stdout, "\b");
+ fflush(stdout);
+ }
+ if (last->failed == 0) {
+ fprintf(stdout, "SUCCESS\n");
+ fflush(stdout);
+ }
+ else {
+ fprintf(stdout, "FAILED %d of %d\n", last->failed, last->num_test);
+ fflush(stdout);
+ }
+ }
+}
+
+abts_suite *abts_add_suite(abts_suite *suite, const char *suite_name_full)
+{
+ sub_suite *subsuite;
+ char *p;
+ const char *suite_name;
+ curr_char = 0;
+
+ /* Only end the suite if we actually ran it */
+ if (suite && suite->tail &&!suite->tail->not_run) {
+ end_suite(suite);
+ }
+
+ subsuite = malloc(sizeof(*subsuite));
+ subsuite->num_test = 0;
+ subsuite->failed = 0;
+ subsuite->next = NULL;
+ /* suite_name_full may be an absolute path depending on __FILE__
+ * expansion */
+ suite_name = strrchr(suite_name_full, '/');
+ if (!suite_name) {
+ suite_name = strrchr(suite_name_full, '\\');
+ }
+ if (suite_name) {
+ suite_name++;
+ } else {
+ suite_name = suite_name_full;
+ }
+ p = strrchr(suite_name, '.');
+ if (p) {
+ subsuite->name = memcpy(calloc(p - suite_name + 1, 1),
+ suite_name, p - suite_name);
+ }
+ else {
+ subsuite->name = strdup(suite_name);
+ }
+
+ if (list_tests) {
+ fprintf(stdout, "%s\n", subsuite->name);
+ }
+
+ subsuite->not_run = 0;
+
+ if (suite == NULL) {
+ suite = malloc(sizeof(*suite));
+ suite->head = subsuite;
+ suite->tail = subsuite;
+ }
+ else {
+ suite->tail->next = subsuite;
+ suite->tail = subsuite;
+ }
+
+ if (!should_test_run(subsuite->name)) {
+ subsuite->not_run = 1;
+ return suite;
+ }
+
+ reset_status();
+ fprintf(stdout, "%-20s: ", subsuite->name);
+ update_status();
+ fflush(stdout);
+
+ return suite;
+}
+
+static void abts_free_suite(abts_suite *suite)
+{
+ if (suite) {
+ sub_suite *dptr, *next;
+
+ for (dptr = suite->head; dptr; dptr = next) {
+ next = dptr->next;
+ free(dptr->name);
+ free(dptr);
+ }
+
+ free(suite);
+ }
+}
+
+void abts_run_test(abts_suite *ts, test_func f, void *value)
+{
+ abts_case tc;
+ sub_suite *ss;
+
+ if (!should_test_run(ts->tail->name)) {
+ return;
+ }
+ ss = ts->tail;
+
+ tc.failed = 0;
+ tc.suite = ss;
+
+ ss->num_test++;
+ update_status();
+
+ f(&tc, value);
+
+ if (tc.failed) {
+ ss->failed++;
+ }
+}
+
+static int report(abts_suite *suite)
+{
+ int count = 0;
+ sub_suite *dptr;
+
+ if (suite && suite->tail &&!suite->tail->not_run) {
+ end_suite(suite);
+ }
+
+ for (dptr = suite->head; dptr; dptr = dptr->next) {
+ count += dptr->failed;
+ }
+
+ if (list_tests) {
+ return 0;
+ }
+
+ if (count == 0) {
+ printf("All tests passed.\n");
+ return 0;
+ }
+
+ dptr = suite->head;
+ fprintf(stdout, "%-15s\t\tTotal\tFail\tFailed %%\n", "Failed Tests");
+ fprintf(stdout, "===================================================\n");
+ while (dptr != NULL) {
+ if (dptr->failed != 0) {
+ float percent = ((float)dptr->failed / (float)dptr->num_test);
+ fprintf(stdout, "%-15s\t\t%5d\t%4d\t%6.2f%%\n", dptr->name,
+ dptr->num_test, dptr->failed, percent * 100);
+ }
+ dptr = dptr->next;
+ }
+ return 1;
+}
+
+void abts_log_message(const char *fmt, ...)
+{
+ va_list args;
+ update_status();
+
+ if (verbose) {
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+}
+
+void abts_int_equal(abts_case *tc, const int expected, const int actual, int lineno)
+{
+ update_status();
+ if (tc->failed) return;
+
+ if (expected == actual) return;
+
+ tc->failed = TRUE;
+ if (verbose) {
+ fprintf(stderr, "Line %d: expected <%d>, but saw <%d>\n", lineno, expected, actual);
+ fflush(stderr);
+ }
+}
+
+void abts_int_nequal(abts_case *tc, const int expected, const int actual, int lineno)
+{
+ update_status();
+ if (tc->failed) return;
+
+ if (expected != actual) return;
+
+ tc->failed = TRUE;
+ if (verbose) {
+ fprintf(stderr, "Line %d: expected something other than <%d>, but saw <%d>\n",
+ lineno, expected, actual);
+ fflush(stderr);
+ }
+}
+
+void abts_size_equal(abts_case *tc, size_t expected, size_t actual, int lineno)
+{
+ update_status();
+ if (tc->failed) return;
+
+ if (expected == actual) return;
+
+ tc->failed = TRUE;
+ if (verbose) {
+ /* Note that the comparison is type-exact, reporting must be a best-fit */
+ fprintf(stderr, "Line %d: expected %lu, but saw %lu\n", lineno,
+ (unsigned long)expected, (unsigned long)actual);
+ fflush(stderr);
+ }
+}
+
+void abts_str_equal(abts_case *tc, const char *expected, const char *actual, int lineno)
+{
+ update_status();
+ if (tc->failed) return;
+
+ if (!expected && !actual) return;
+ if (expected && actual)
+ if (!strcmp(expected, actual)) return;
+
+ tc->failed = TRUE;
+ if (verbose) {
+ fprintf(stderr, "Line %d: expected <%s>, but saw <%s>\n", lineno, expected, actual);
+ fflush(stderr);
+ }
+}
+
+void abts_str_nequal(abts_case *tc, const char *expected, const char *actual,
+ size_t n, int lineno)
+{
+ update_status();
+ if (tc->failed) return;
+
+ if (!strncmp(expected, actual, n)) return;
+
+ tc->failed = TRUE;
+ if (verbose) {
+ fprintf(stderr, "Line %d: expected something other than <%s>, but saw <%s>\n",
+ lineno, expected, actual);
+ fflush(stderr);
+ }
+}
+
+void abts_ptr_notnull(abts_case *tc, const void *ptr, int lineno)
+{
+ update_status();
+ if (tc->failed) return;
+
+ if (ptr != NULL) return;
+
+ tc->failed = TRUE;
+ if (verbose) {
+ fprintf(stderr, "Line %d: expected non-NULL, but saw NULL\n", lineno);
+ fflush(stderr);
+ }
+}
+
+void abts_ptr_equal(abts_case *tc, const void *expected, const void *actual, int lineno)
+{
+ update_status();
+ if (tc->failed) return;
+
+ if (expected == actual) return;
+
+ tc->failed = TRUE;
+ if (verbose) {
+ fprintf(stderr, "Line %d: expected <%p>, but saw <%p>\n", lineno, expected, actual);
+ fflush(stderr);
+ }
+}
+
+void abts_fail(abts_case *tc, const char *message, int lineno)
+{
+ update_status();
+ if (tc->failed) return;
+
+ tc->failed = TRUE;
+ if (verbose) {
+ fprintf(stderr, "Line %d: %s\n", lineno, message);
+ fflush(stderr);
+ }
+}
+
+void abts_assert(abts_case *tc, const char *message, int condition, int lineno)
+{
+ update_status();
+ if (tc->failed) return;
+
+ if (condition) return;
+
+ tc->failed = TRUE;
+ if (verbose) {
+ fprintf(stderr, "Line %d: %s\n", lineno, message);
+ fflush(stderr);
+ }
+}
+
+void abts_true(abts_case *tc, int condition, int lineno)
+{
+ update_status();
+ if (tc->failed) return;
+
+ if (condition) return;
+
+ tc->failed = TRUE;
+ if (verbose) {
+ fprintf(stderr, "Line %d: Condition is false, but expected true\n", lineno);
+ fflush(stderr);
+ }
+}
+
+void abts_not_impl(abts_case *tc, const char *message, int lineno)
+{
+ update_status();
+
+ tc->suite->not_impl++;
+ if (verbose) {
+ fprintf(stderr, "Line %d: %s\n", lineno, message);
+ fflush(stderr);
+ }
+}
+
+int main(int argc, const char *const argv[]) {
+ int i;
+ int rv;
+ int list_provided = 0;
+ abts_suite *suite = NULL;
+
+ initialize();
+
+ quiet = !isatty(STDOUT_FILENO);
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-v")) {
+ verbose = 1;
+ continue;
+ }
+ if (!strcmp(argv[i], "-x")) {
+ exclude = 1;
+ continue;
+ }
+ if (!strcmp(argv[i], "-l")) {
+ list_tests = 1;
+ continue;
+ }
+ if (!strcmp(argv[i], "-q")) {
+ quiet = 1;
+ continue;
+ }
+ if (argv[i][0] == '-') {
+ fprintf(stderr, "Invalid option: `%s'\n", argv[i]);
+ exit(1);
+ }
+ list_provided = 1;
+ }
+
+ if (list_provided) {
+ /* Waste a little space here, because it is easier than counting the
+ * number of tests listed. Besides it is at most three char *.
+ */
+ testlist = calloc(argc + 1, sizeof(char *));
+ for (i = 1; i < argc; i++) {
+ testlist[i - 1] = argv[i];
+ }
+ }
+
+ for (i = 0; i < (sizeof(alltests) / sizeof(struct testlist *)); i++) {
+ suite = alltests[i].func(suite);
+ }
+
+ rv = report(suite);
+ abts_free_suite(suite);
+ return rv;
+}
+
diff --git a/test/abts.h b/test/abts.h
new file mode 100644
index 0000000..ed1c092
--- /dev/null
+++ b/test/abts.h
@@ -0,0 +1,108 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#ifndef ABTS_H
+#define ABTS_H
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+struct sub_suite {
+ char *name;
+ int num_test;
+ int failed;
+ int not_run;
+ int not_impl;
+ struct sub_suite *next;
+};
+typedef struct sub_suite sub_suite;
+
+struct abts_suite {
+ sub_suite *head;
+ sub_suite *tail;
+};
+typedef struct abts_suite abts_suite;
+
+struct abts_case {
+ int failed;
+ sub_suite *suite;
+};
+typedef struct abts_case abts_case;
+
+typedef void (*test_func)(abts_case *tc, void *data);
+
+#define ADD_SUITE(suite) abts_add_suite(suite, __FILE__);
+
+abts_suite *abts_add_suite(abts_suite *suite, const char *suite_name);
+void abts_run_test(abts_suite *ts, test_func f, void *value);
+void abts_log_message(const char *fmt, ...);
+
+void abts_int_equal(abts_case *tc, const int expected, const int actual, int lineno);
+void abts_int_nequal(abts_case *tc, const int expected, const int actual, int lineno);
+void abts_str_equal(abts_case *tc, const char *expected, const char *actual, int lineno);
+void abts_str_nequal(abts_case *tc, const char *expected, const char *actual,
+ size_t n, int lineno);
+void abts_ptr_notnull(abts_case *tc, const void *ptr, int lineno);
+void abts_ptr_equal(abts_case *tc, const void *expected, const void *actual, int lineno);
+void abts_true(abts_case *tc, int condition, int lineno);
+void abts_fail(abts_case *tc, const char *message, int lineno);
+void abts_not_impl(abts_case *tc, const char *message, int lineno);
+void abts_assert(abts_case *tc, const char *message, int condition, int lineno);
+void abts_size_equal(abts_case *tc, size_t expected, size_t actual, int lineno);
+
+/* Convenience macros. Ryan hates these! */
+#define ABTS_INT_EQUAL(a, b, c) abts_int_equal(a, b, c, __LINE__)
+#define ABTS_INT_NEQUAL(a, b, c) abts_int_nequal(a, b, c, __LINE__)
+#define ABTS_STR_EQUAL(a, b, c) abts_str_equal(a, b, c, __LINE__)
+#define ABTS_STR_NEQUAL(a, b, c, d) abts_str_nequal(a, b, c, d, __LINE__)
+#define ABTS_PTR_NOTNULL(a, b) abts_ptr_notnull(a, b, __LINE__)
+#define ABTS_PTR_EQUAL(a, b, c) abts_ptr_equal(a, b, c, __LINE__)
+#define ABTS_TRUE(a, b) abts_true(a, b, __LINE__);
+#define ABTS_FAIL(a, b) abts_fail(a, b, __LINE__);
+#define ABTS_NOT_IMPL(a, b) abts_not_impl(a, b, __LINE__);
+#define ABTS_ASSERT(a, b, c) abts_assert(a, b, c, __LINE__);
+
+#define ABTS_SIZE_EQUAL(a, b, c) abts_size_equal(a, b, c, __LINE__)
+
+
+abts_suite *run_tests(abts_suite *suite);
+abts_suite *run_tests1(abts_suite *suite);
+
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/test/abts_tests.h b/test/abts_tests.h
new file mode 100644
index 0000000..07e2205
--- /dev/null
+++ b/test/abts_tests.h
@@ -0,0 +1,75 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef APR_TEST_INCLUDES
+#define APR_TEST_INCLUDES
+
+#include "abts.h"
+#include "testutil.h"
+
+const struct testlist {
+ abts_suite *(*func)(abts_suite *suite);
+} alltests[] = {
+ {testatomic},
+ {testdir},
+ {testdso},
+ {testdup},
+ {testencode},
+ {testenv},
+ {testescape},
+ {testfile},
+ {testfilecopy},
+ {testfileinfo},
+ {testflock},
+ {testfmt},
+ {testfnmatch},
+ {testgetopt},
+#if 0 /* not ready yet due to API issues */
+ {testglobalmutex},
+#endif
+ {testhash},
+ {testipsub},
+ {testlock},
+ {testcond},
+ {testlfs},
+ {testmmap},
+ {testnames},
+ {testoc},
+ {testpath},
+ {testpipe},
+ {testpoll},
+ {testpool},
+ {testproc},
+ {testprocmutex},
+ {testrand},
+ {testsleep},
+ {testshm},
+ {testsock},
+ {testsockets},
+ {testsockopt},
+ {teststr},
+ {teststrnatcmp},
+ {testtable},
+ {testtemp},
+ {testthread},
+ {testtime},
+ {testud},
+ {testuser},
+ {testvsn},
+ {testskiplist}
+};
+
+#endif /* APR_TEST_INCLUDES */
diff --git a/test/data/file_datafile.txt b/test/data/file_datafile.txt
new file mode 100644
index 0000000..1651a32
--- /dev/null
+++ b/test/data/file_datafile.txt
@@ -0,0 +1 @@
+This is the file data file. \ No newline at end of file
diff --git a/test/data/mmap_datafile.txt b/test/data/mmap_datafile.txt
new file mode 100644
index 0000000..50f47a6
--- /dev/null
+++ b/test/data/mmap_datafile.txt
@@ -0,0 +1 @@
+This is the MMAP data file.
diff --git a/test/echod.c b/test/echod.c
new file mode 100644
index 0000000..052e47d
--- /dev/null
+++ b/test/echod.c
@@ -0,0 +1,134 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Simple echo daemon, designed to be used for network throughput
+ * benchmarks. The aim is to allow us to monitor changes in performance
+ * of APR networking code, nothing more.
+ */
+
+#include <stdio.h>
+#include <stdlib.h> /* for atexit() */
+
+#include "apr.h"
+#include "apr_network_io.h"
+#include "apr_strings.h"
+
+#define BUF_SIZE 4096
+
+static void reportError(const char *msg, apr_status_t rv,
+ apr_pool_t *pool)
+{
+ fprintf(stderr, "%s\nError: %d\n'%s'\n", msg, rv,
+ apr_psprintf(pool, "%pm", &rv));
+}
+
+static apr_status_t talkTalk(apr_socket_t *socket, apr_pool_t *parent)
+{
+ apr_pool_t *pool;
+ apr_size_t len;
+ char *buf;
+ apr_status_t rv;
+
+ if (apr_pool_create(&pool, parent) != APR_SUCCESS)
+ return APR_ENOPOOL;
+
+
+ buf = apr_palloc(pool, BUF_SIZE);
+ if (!buf)
+ return ENOMEM;
+
+ do {
+ len = BUF_SIZE;
+ rv = apr_socket_recv(socket, buf, &len);
+ if (APR_STATUS_IS_EOF(rv) || len == 0 || rv != APR_SUCCESS)
+ break;
+ rv = apr_socket_send(socket, buf, &len);
+ if (len == 0 || rv != APR_SUCCESS)
+ break;
+ } while (rv == APR_SUCCESS);
+
+ apr_pool_clear(pool);
+ return APR_SUCCESS;
+}
+
+static apr_status_t glassToWall(apr_port_t port, apr_pool_t *parent)
+{
+ apr_sockaddr_t *sockAddr;
+ apr_socket_t *listener, *accepted;
+ apr_status_t rv;
+
+ rv = apr_socket_create(&listener, APR_INET, SOCK_STREAM, APR_PROTO_TCP,
+ parent);
+ if (rv != APR_SUCCESS) {
+ reportError("Unable to create socket", rv, parent);
+ return rv;
+ }
+
+ rv = apr_sockaddr_info_get(&sockAddr, "127.0.0.1", APR_UNSPEC,
+ port, 0, parent);
+ if (rv != APR_SUCCESS) {
+ reportError("Unable to get socket info", rv, parent);
+ apr_socket_close(listener);
+ return rv;
+ }
+
+ if ((rv = apr_socket_bind(listener, sockAddr)) != APR_SUCCESS ||
+ (rv = apr_socket_listen(listener, 5)) != APR_SUCCESS) {
+ reportError("Unable to bind or listen to socket", rv, parent);
+ apr_socket_close(listener);
+ return rv;
+ }
+
+ for (;;) {
+ rv = apr_socket_accept(&accepted, listener, parent);
+ if (rv != APR_SUCCESS) {
+ reportError("Error accepting on socket", rv, parent);
+ break;
+ }
+ printf("\tAnswering connection\n");
+ rv = talkTalk(accepted, parent);
+ apr_socket_close(accepted);
+ printf("\tConnection closed\n");
+ if (rv != APR_SUCCESS)
+ break;
+ }
+
+ apr_socket_close(listener);
+ return APR_SUCCESS;
+}
+
+int main(int argc, char **argv)
+{
+ apr_pool_t *pool;
+ apr_port_t theport = 4747;
+
+ printf("APR Test Application: echod\n");
+
+ apr_initialize();
+ atexit(apr_terminate);
+
+ apr_pool_create(&pool, NULL);
+
+ if (argc >= 2) {
+ printf("argc = %d, port = '%s'\n", argc, argv[1]);
+ theport = atoi(argv[1]);
+ }
+
+ fprintf(stdout, "Starting to listen on port %d\n", theport);
+ glassToWall(theport, pool);
+
+ return 0;
+}
diff --git a/test/globalmutexchild.c b/test/globalmutexchild.c
new file mode 100644
index 0000000..4b8737b
--- /dev/null
+++ b/test/globalmutexchild.c
@@ -0,0 +1,64 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testglobalmutex.h"
+#include "apr_pools.h"
+#include "apr_file_io.h"
+#include "apr_general.h"
+#include "apr_global_mutex.h"
+#include "apr_strings.h"
+#include "apr.h"
+
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+
+int main(int argc, const char * const argv[])
+{
+ apr_pool_t *p;
+ int i = 0;
+ apr_lockmech_e mech;
+ apr_global_mutex_t *global_lock;
+ apr_status_t rv;
+
+ apr_initialize();
+ atexit(apr_terminate);
+
+ apr_pool_create(&p, NULL);
+ if (argc >= 2) {
+ mech = (apr_lockmech_e)apr_strtoi64(argv[1], NULL, 0);
+ }
+ else {
+ mech = APR_LOCK_DEFAULT;
+ }
+ rv = apr_global_mutex_create(&global_lock, LOCKNAME, mech, p);
+ if (rv != APR_SUCCESS) {
+ exit(-rv);
+ }
+ apr_global_mutex_child_init(&global_lock, LOCKNAME, p);
+
+ while (1) {
+ apr_global_mutex_lock(global_lock);
+ if (i == MAX_ITER) {
+ apr_global_mutex_unlock(global_lock);
+ exit(i);
+ }
+ i++;
+ apr_global_mutex_unlock(global_lock);
+ }
+ exit(0);
+}
diff --git a/test/internal/Makefile.in b/test/internal/Makefile.in
new file mode 100644
index 0000000..b1f6c6a
--- /dev/null
+++ b/test/internal/Makefile.in
@@ -0,0 +1,37 @@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+NONPORTABLE = \
+ testregex@EXEEXT@
+
+PROGRAMS = \
+
+TARGETS = $(PROGRAMS) $(NONPORTABLE)
+
+# bring in rules.mk for standard functionality
+@INCLUDE_RULES@
+
+LOCAL_LIBS=../../lib@APR_LIBNAME@.la
+
+CLEAN_TARGETS = testregex@EXEEXT@
+
+INCDIR=../../include
+INCLUDES=-I$(INCDIR)
+
+CFLAGS=$(MY_CFLAGS)
+
+all: $(PROGRAMS) $(NONPORTABLE)
+
+check: $(PROGRAMS) $(NONPORTABLE)
+ for prog in $(PROGRAMS) $(NONPORTABLE); do \
+ ./$$prog; \
+ if test $$i = 255; then \
+ echo "$$prog failed"; \
+ break; \
+ fi \
+ done
+
+testregex@EXEEXT@: testregex.lo $(LOCAL_LIBS)
+ $(LINK) testregex.lo $(LOCAL_LIBS) $(ALL_LIBS)
+
+# DO NOT REMOVE
diff --git a/test/internal/Makefile.win b/test/internal/Makefile.win
new file mode 100644
index 0000000..a881f07
--- /dev/null
+++ b/test/internal/Makefile.win
@@ -0,0 +1,109 @@
+# PROGRAMS includes all test programs built on this platform.
+# STDTEST_PORTABLE
+# test programs invoked via standard user interface, run on all platforms
+# STDTEST_NONPORTABLE
+# test programs invoked via standard user interface, not portable
+# OTHER_PROGRAMS
+# programs such as sendfile, that have to be invoked in a special sequence
+# or with special parameters
+
+!IFNDEF MODEL
+MODEL=dynamic
+!ENDIF
+
+INCDIR=../../include
+
+!IFNDEF OUTDIR
+!IF "$(MODEL)" == "static"
+OUTDIR=LibR
+!ELSE
+OUTDIR=Release
+!ENDIF
+
+!IF [$(COMSPEC) /c cl /nologo /? | find "x64" >NUL ] == 0
+OUTDIR=x64\$(OUTDIR)
+!ENDIF
+!ENDIF
+
+!IF !EXIST("$(OUTDIR)\.")
+!IF ([$(COMSPEC) /C mkdir $(OUTDIR)] == 0)
+!ENDIF
+!ENDIF
+
+!IFNDEF INTDIR
+INTDIR=$(OUTDIR)
+!ELSE
+!IF !EXIST("$(INTDIR)\.")
+!IF ([$(COMSPEC) /C mkdir $(INTDIR)] == 0)
+!ENDIF
+!ENDIF
+!ENDIF
+
+!MESSAGE Building tests into $(OUTDIR) for $(MODEL)
+
+NONPORTABLE = \
+ $(OUTDIR)\testucs.exe
+
+CLEAN_BUILDDIRS = Release Debug 9x x64
+
+PROGRAMS =
+
+TARGETS = $(PROGRAMS) $(NONPORTABLE)
+
+# bring in rules.mk for standard functionality
+ALL: $(TARGETS)
+
+CL = cl.exe
+LD = link.exe
+
+!IF "$(MODEL)" == "static"
+LOCAL_LIB= ..\..\$(OUTDIR)\apr-1.lib
+STATIC_CFLAGS = /D APR_DECLARE_STATIC
+!ELSE
+LOCAL_LIB= ..\..\$(OUTDIR)\libapr-1.lib
+STATIC_CFLAGS =
+!ENDIF
+
+!IFDEF _DEBUG
+DEBUG_CFLAGS = /MDd
+!ELSE
+DEBUG_CFLAGS = /MD
+!ENDIF
+
+INCLUDES=/I "$(INCDIR)"
+
+CFLAGS = /nologo /c /W3 /Gm /EHsc /Zi /Od $(INCLUDES) \
+ $(STATIC_CFLAGS) $(DEBUG_CFLAGS) /D "BINPATH=$(OUTDIR:\=/)" \
+ /D _DEBUG /D WIN32 /Fo"$(INTDIR)/" /FD
+
+LD_LIBS = kernel32.lib advapi32.lib ws2_32.lib wsock32.lib \
+ ole32.lib shell32.lib rpcrt4.lib
+
+LDFLAGS = /nologo /debug /subsystem:console /incremental:no
+SHLDFLAGS = /nologo /dll /debug /subsystem:windows /incremental:no
+
+.c{$(INTDIR)}.obj:
+ $(CL) $(CFLAGS) -c $< -Fd$(INTDIR)\ $(INCLUDES)
+
+$(OUTDIR)\testucs.exe: $(INTDIR)\testucs.obj $(LOCAL_LIB)
+ $(LD) $(LDFLAGS) /out:"$@" $** $(LD_LIBS)
+ @if exist "$@.manifest" \
+ mt.exe -manifest "$@.manifest" -outputresource:$@;1
+
+
+clean:
+ @if EXIST $(INTDIR)\. rmdir /s /q $(INTDIR)
+ @if EXIST $(OUTDIR)\. rmdir /s /q $(OUTDIR)
+
+cleanall:
+ @for %d in ($(CLEAN_BUILDDIRS)) do @if EXIST %d\. rmdir /s /q %d
+
+
+PATH=$(OUTDIR);..\..\$(OUTDIR);$(PATH)
+
+check: $(NONPORTABLE)
+ @for %p in ($(NONPORTABLE)) do @( \
+ echo Testing %p && %p || echo %p failed \
+ )
+
+# DO NOT REMOVE
diff --git a/test/internal/testregex.c b/test/internal/testregex.c
new file mode 100644
index 0000000..20dcfde
--- /dev/null
+++ b/test/internal/testregex.c
@@ -0,0 +1,91 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "apr_strings.h"
+#include "apr_pools.h"
+#include "apr_general.h"
+#include "apr_hash.h"
+#include "apr_lib.h"
+#include "apr_time.h"
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main( int argc, char** argv) {
+ apr_pool_t *context;
+ regex_t regex;
+ int rc;
+ int i;
+ int iters;
+ apr_time_t now;
+ apr_time_t end;
+ apr_hash_t *h;
+
+
+ if (argc !=4 ) {
+ fprintf(stderr, "Usage %s match string #iterations\n",argv[0]);
+ return -1;
+ }
+ iters = atoi( argv[3]);
+
+ apr_initialize() ;
+ atexit(apr_terminate);
+ if (apr_pool_create(&context, NULL) != APR_SUCCESS) {
+ fprintf(stderr, "Something went wrong\n");
+ exit(-1);
+ }
+ rc = regcomp( &regex, argv[1], REG_EXTENDED|REG_NOSUB);
+
+
+ if (rc) {
+ char errbuf[2000];
+ regerror(rc, &regex,errbuf,2000);
+ fprintf(stderr,"Couldn't compile regex ;(\n%s\n ",errbuf);
+ return -1;
+ }
+ if ( regexec( &regex, argv[2], 0, NULL,0) == 0 ) {
+ fprintf(stderr,"Match\n");
+ }
+ else {
+ fprintf(stderr,"No Match\n");
+ }
+ now = apr_time_now();
+ for (i=0;i<iters;i++) {
+ regexec( &regex, argv[2], 0, NULL,0) ;
+ }
+ end=apr_time_now();
+ puts(apr_psprintf( context, "Time to run %d regex's %8lld\n",iters,end-now));
+ h = apr_hash_make( context);
+ for (i=0;i<70;i++) {
+ apr_hash_set(h,apr_psprintf(context, "%dkey",i),APR_HASH_KEY_STRING,"1");
+ }
+ now = apr_time_now();
+ for (i=0;i<iters;i++) {
+ apr_hash_get( h, argv[2], APR_HASH_KEY_STRING);
+ }
+ end=apr_time_now();
+ puts(apr_psprintf( context, "Time to run %d hash (no find)'s %8lld\n",iters,end-now));
+ apr_hash_set(h, argv[2],APR_HASH_KEY_STRING,"1");
+ now = apr_time_now();
+ for (i=0;i<iters;i++) {
+ apr_hash_get( h, argv[2], APR_HASH_KEY_STRING);
+ }
+ end=apr_time_now();
+ puts(apr_psprintf( context, "Time to run %d hash (find)'s %8lld\n",iters,end-now));
+
+ return 0;
+}
diff --git a/test/internal/testucs.c b/test/internal/testucs.c
new file mode 100644
index 0000000..bf8874e
--- /dev/null
+++ b/test/internal/testucs.c
@@ -0,0 +1,348 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr.h"
+#include "arch/win32/apr_arch_utf8.h"
+#include <wchar.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+struct testval {
+ unsigned char n[8];
+ apr_size_t nl;
+ wchar_t w[4];
+ apr_size_t wl;
+};
+
+#ifdef FOR_REFERENCE
+/* For reference; a table of invalid utf-8 encoded ucs-2/ucs-4 sequences.
+ * The table consists of start, end pairs for all invalid ranges.
+ * NO_UCS2_PAIRS will pass the reservered D800-DFFF values, halting at FFFF
+ * FULL_UCS4_MAPPER represents all 31 bit values to 7FFF FFFF
+ *
+ * We already tested these, because we ensure there is a 1:1 mapping across
+ * the entire range of byte values in each position of 1 to 6 byte sequences.
+ */
+struct testval malformed[] = [
+ [[0x80,], 1,], /* 10000000 64 invalid leading continuation values */
+ [[0xBF,], 1,], /* 10111111 64 invalid leading continuation values */
+ [[0xC0,0x80], 2,], /* overshort mapping of 0000 */
+ [[0xC1,0xBF], 2,], /* overshort mapping of 007F */
+ [[0xE0,0x80,0x80,], 3,], /* overshort mapping of 0000 */
+ [[0xE0,0x9F,0xBF,], 3,], /* overshort mapping of 07FF */
+#ifndef NO_UCS2_PAIRS
+ [[0xED,0xA0,0x80,], 3,], /* unexpected mapping of UCS-2 literal D800 */
+ [[0xED,0xBF,0xBF,], 3,], /* unexpected mapping of UCS-2 literal DFFF */
+#endif
+ [[0xF0,0x80,0x80,0x80,], 4,], /* overshort mapping of 0000 */
+ [[0xF0,0x8F,0xBF,0xBF,], 4,], /* overshort mapping of FFFF */
+#ifdef NO_UCS2_PAIRS
+ [[0xF0,0x90,0x80,0x80,], 4,], /* invalid too large value 0001 0000 */
+ [[0xF4,0x8F,0xBF,0xBF,], 4,], /* invalid too large value 0010 FFFF */
+#endif
+#ifndef FULL_UCS4_MAPPER
+ [[0xF4,0x90,0x80,0x80,], 4,], /* invalid too large value 0011 0000 */
+ [[0xF7,0xBF,0xBF,0xBF,], 4,], /* invalid too large value 001F FFFF */
+#endif
+ [[0xF8,0x80,0x80,0x80,0x80,], 5,], /* overshort mapping of 0000 0000 */
+ [[0xF8,0x87,0xBF,0xBF,0xBF,], 5,], /* overshort mapping of 001F FFFF */
+#ifndef FULL_UCS4_MAPPER
+ [[0xF8,0x88,0x80,0x80,0x80,], 5,], /* invalid too large value 0020 0000 */
+ [[0xFB,0xBF,0xBF,0xBF,0xBF,], 5,], /* invalid too large value 03FF FFFF */
+#endif
+ [[0xFC,0x80,0x80,0x80,0x80,0x80,], 6,], /* overshort mapping 0000 0000 */
+ [[0xFC,0x83,0xBF,0xBF,0xBF,0xBF,], 6,], /* overshort mapping 03FF FFFF */
+#ifndef FULL_UCS4_MAPPER
+ [[0xFC,0x84,0x80,0x80,0x80,0x80,], 6,], /* overshort mapping 0400 0000 */
+ [[0xFD,0xBF,0xBF,0xBF,0xBF,0xBF,], 6,], /* overshort mapping 7FFF FFFF */
+#endif
+ [[0xFE,], 1,], /* 11111110 invalid "too large" value, no 7 byte seq */
+ [[0xFF,], 1,], /* 11111111 invalid "too large" value, no 8 byte seq */
+];
+#endif /* FOR_REFERENCE */
+
+void displaynw(struct testval *f, struct testval *l)
+{
+ char x[80], *t = x;
+ int i;
+ for (i = 0; i < f->nl; ++i)
+ t += sprintf(t, "%02X ", f->n[i]);
+ *(t++) = '-';
+ for (i = 0; i < l->nl; ++i)
+ t += sprintf(t, " %02X", l->n[i]);
+ *(t++) = ' ';
+ *(t++) = '=';
+ *(t++) = ' ';
+ for (i = 0; i < f->wl; ++i)
+ t += sprintf(t, "%04X ", f->w[i]);
+ *(t++) = '-';
+ for (i = 0; i < l->wl; ++i)
+ t += sprintf(t, " %04X", l->w[i]);
+ *t = '\0';
+ puts(x);
+}
+
+/*
+ * Test every possible byte value.
+ * If the test passes or fails at this byte value we are done.
+ * Otherwise iterate test_nrange again, appending another byte.
+ */
+void test_nrange(struct testval *p)
+{
+ struct testval f, l, s;
+ apr_status_t rc;
+ int success = 0;
+
+ memcpy (&s, p, sizeof(s));
+ ++s.nl;
+
+ do {
+ apr_size_t nl = s.nl, wl = sizeof(s.w) / 2;
+ rc = apr_conv_utf8_to_ucs2(s.n, &nl, s.w, &wl);
+ s.wl = (sizeof(s.w) / 2) - wl;
+ if (!nl && rc == APR_SUCCESS) {
+ if (!success) {
+ memcpy(&f, &s, sizeof(s));
+ success = -1;
+ }
+ else {
+ if (s.wl != l.wl
+ || memcmp(s.w, l.w, (s.wl - 1) * 2) != 0
+ || s.w[s.wl - 1] != l.w[l.wl - 1] + 1) {
+ displaynw(&f, &l);
+ memcpy(&f, &s, sizeof(s));
+ }
+ }
+ memcpy(&l, &s, sizeof(s));
+ }
+ else {
+ if (success) {
+ displaynw(&f, &l);
+ success = 0;
+ }
+ if (rc == APR_INCOMPLETE) {
+ test_nrange(&s);
+ }
+ }
+ } while (++s.n[s.nl - 1]);
+
+ if (success) {
+ displaynw(&f, &l);
+ success = 0;
+ }
+}
+
+/*
+ * Test every possible word value.
+ * Once we are finished, retest every possible word value.
+ * if the test fails on the following null word, iterate test_nrange
+ * again, appending another word.
+ * This assures the output order of the two tests are in sync.
+ */
+void test_wrange(struct testval *p)
+{
+ struct testval f, l, s;
+ apr_status_t rc;
+ int success = 0;
+
+ memcpy (&s, p, sizeof(s));
+ ++s.wl;
+
+ do {
+ apr_size_t nl = sizeof(s.n), wl = s.wl;
+ rc = apr_conv_ucs2_to_utf8(s.w, &wl, s.n, &nl);
+ s.nl = sizeof(s.n) - nl;
+ if (!wl && rc == APR_SUCCESS) {
+ if (!success) {
+ memcpy(&f, &s, sizeof(s));
+ success = -1;
+ }
+ else {
+ if (s.nl != l.nl
+ || memcmp(s.n, l.n, s.nl - 1) != 0
+ || s.n[s.nl - 1] != l.n[l.nl - 1] + 1) {
+ displaynw(&f, &l);
+ memcpy(&f, &s, sizeof(s));
+ }
+ }
+ memcpy(&l, &s, sizeof(s));
+ }
+ else {
+ if (success) {
+ displaynw(&f, &l);
+ success = 0;
+ }
+ }
+ } while (++s.w[s.wl - 1]);
+
+ if (success) {
+ displaynw(&f, &l);
+ success = 0;
+ }
+
+ do {
+ apr_size_t wl = s.wl, nl = sizeof(s.n);
+ rc = apr_conv_ucs2_to_utf8(s.w, &wl, s.n, &nl);
+ s.nl = sizeof(s.n) - s.nl;
+ if (rc == APR_INCOMPLETE) {
+ test_wrange(&s);
+ }
+ } while (++s.w[s.wl - 1]);
+}
+
+/*
+ * Test every possible byte value.
+ * If the test passes or fails at this byte value we are done.
+ * Otherwise iterate test_nrange again, appending another byte.
+ */
+void test_ranges()
+{
+ struct testval ntest, wtest;
+ apr_status_t nrc, wrc;
+ apr_size_t inlen;
+ unsigned long matches = 0;
+
+ memset(&ntest, 0, sizeof(ntest));
+ ++ntest.nl;
+
+ memset(&wtest, 0, sizeof(wtest));
+ ++wtest.wl;
+
+ do {
+ do {
+ inlen = ntest.nl;
+ ntest.wl = sizeof(ntest.w) / 2;
+ nrc = apr_conv_utf8_to_ucs2(ntest.n, &inlen, ntest.w, &ntest.wl);
+ if (nrc == APR_SUCCESS) {
+ ntest.wl = (sizeof(ntest.w) / 2) - ntest.wl;
+ break;
+ }
+ if (nrc == APR_INCOMPLETE) {
+ ++ntest.nl;
+ if (ntest.nl > 6) {
+ printf ("\n\nUnexpected utf8 sequence of >6 bytes;\n");
+ exit(255);
+ }
+ continue;
+ }
+ else {
+ while (!(++ntest.n[ntest.nl - 1])) {
+ if (!(--ntest.nl))
+ break;
+ }
+ }
+ } while (ntest.nl);
+
+ do {
+ inlen = wtest.wl;
+ wtest.nl = sizeof(wtest.n);
+ wrc = apr_conv_ucs2_to_utf8(wtest.w, &inlen, wtest.n, &wtest.nl);
+ if (wrc == APR_SUCCESS) {
+ wtest.nl = sizeof(wtest.n) - wtest.nl;
+ break;
+ }
+ else {
+ if (!(++wtest.w[wtest.wl - 1])) {
+ if (wtest.wl == 1)
+ ++wtest.wl;
+ else
+ ++wtest.w[0];
+
+ /* On the second pass, ensure lead word is incomplete */
+ do {
+ inlen = 1;
+ wtest.nl = sizeof(wtest.n);
+ if (apr_conv_ucs2_to_utf8(wtest.w, &inlen, wtest.n, &wtest.nl)
+ == APR_INCOMPLETE)
+ break;
+ if (!(++wtest.w[0])) {
+ wtest.wl = 0;
+ break;
+ }
+ } while (1);
+ }
+ }
+ } while (wtest.wl);
+
+ if (!ntest.nl && !wtest.wl)
+ break;
+
+ /* Identical? */
+ if ((wtest.nl != ntest.nl)
+ || (memcmp(wtest.n, ntest.n, ntest.nl) != 0)
+ || (wtest.wl != ntest.wl)
+ || (memcmp(ntest.w, wtest.w, wtest.wl * 2) != 0)) {
+ printf ("\n\nMismatch of w/n conversion at;\n");
+ displaynw(&ntest, &wtest);
+ exit(255);
+ }
+ ++matches;
+
+ while (!(++ntest.n[ntest.nl - 1])) {
+ if (!(--ntest.nl))
+ break;
+ }
+
+ if (!(++wtest.w[wtest.wl - 1])) {
+ if (wtest.wl == 1)
+ ++wtest.wl;
+ else
+ ++wtest.w[0];
+
+ /* On the second pass, ensure lead word is incomplete */
+ do {
+ inlen = 1;
+ wtest.nl = sizeof(wtest.n);
+ if (apr_conv_ucs2_to_utf8(wtest.w, &inlen, wtest.n, &wtest.nl)
+ == APR_INCOMPLETE)
+ break;
+ if (!(++wtest.w[0])) {
+ wtest.wl = 0;
+ break;
+ }
+ } while (1);
+ }
+ } while (wtest.wl || ntest.nl);
+
+ printf ("\n\nutf8 and ucs2 sequences of %lu transformations matched OK.\n",
+ matches);
+}
+
+/*
+ * Syntax: testucs [w|n]
+ *
+ * If no arg or arg is not recognized, run equality sequence test.
+ */
+int main(int argc, char **argv)
+{
+ struct testval s;
+ memset (&s, 0, sizeof(s));
+
+ if (argc >= 2 && apr_tolower(*argv[1]) != 'w') {
+ printf ("\n\nTesting Narrow Char Ranges\n");
+ test_nrange(&s);
+ }
+ else if (argc >= 2 && apr_tolower(*argv[1]) != 'n') {
+ printf ("\n\nTesting Wide Char Ranges\n");
+ test_wrange(&s);
+ }
+ else {
+ test_ranges();
+ }
+ return 0;
+}
diff --git a/test/mod_test.c b/test/mod_test.c
new file mode 100644
index 0000000..2178e94
--- /dev/null
+++ b/test/mod_test.c
@@ -0,0 +1,32 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_strings.h"
+
+void print_hello(char str[256]);
+int count_reps(int reps);
+
+void print_hello(char str[256])
+{
+ apr_cpystrn(str, "Hello - I'm a DSO!\n", strlen("Hello - I'm a DSO!\n") + 1);
+}
+
+int count_reps(int reps)
+{
+ int i = 0;
+ for (i = 0;i < reps; i++);
+ return i;
+}
diff --git a/test/nw_misc.c b/test/nw_misc.c
new file mode 100644
index 0000000..b45f951
--- /dev/null
+++ b/test/nw_misc.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <screen.h>
+/*
+#include "testutil.h"
+*/
+
+/* function to keep the screen open if not launched from bash */
+void _NonAppStop( void )
+{
+ if (getenv("_IN_NETWARE_BASH_") == NULL) {
+ printf("\r\n<Press any key to close screen> ");
+ getcharacter();
+ }
+}
+
+/*
+static void test_not_impl(CuTest *tc)
+{
+ CuNotImpl(tc, "Test not implemented on this platform yet");
+}
+*/
+
diff --git a/test/occhild.c b/test/occhild.c
new file mode 100644
index 0000000..a96885d
--- /dev/null
+++ b/test/occhild.c
@@ -0,0 +1,26 @@
+#include "apr.h"
+#include "apr_file_io.h"
+#include "apr.h"
+
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+int main(void)
+{
+ char buf[256];
+ apr_file_t *err;
+ apr_pool_t *p;
+
+ apr_initialize();
+ atexit(apr_terminate);
+
+ apr_pool_create(&p, NULL);
+ apr_file_open_stdin(&err, p);
+
+ while (1) {
+ apr_size_t length = 256;
+ apr_file_read(err, buf, &length);
+ }
+ exit(0); /* just to keep the compiler happy */
+}
diff --git a/test/proc_child.c b/test/proc_child.c
new file mode 100644
index 0000000..9f458b9
--- /dev/null
+++ b/test/proc_child.c
@@ -0,0 +1,21 @@
+#include "apr.h"
+#include <stdio.h>
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if APR_HAVE_IO_H
+#include <io.h>
+#endif
+#include <stdlib.h>
+
+int main(void)
+{
+ char buf[256];
+ int bytes, rv = 0;
+
+ bytes = (int)read(STDIN_FILENO, buf, 256);
+ if (bytes > 0)
+ rv = write(STDOUT_FILENO, buf, (unsigned int)bytes) == bytes ? 0 : 1;
+
+ return rv;
+}
diff --git a/test/readchild.c b/test/readchild.c
new file mode 100644
index 0000000..f8443cc
--- /dev/null
+++ b/test/readchild.c
@@ -0,0 +1,46 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include "apr_file_io.h"
+
+int main(int argc, char *argv[])
+{
+ apr_file_t *in, *out;
+ apr_size_t nbytes, total_bytes;
+ apr_pool_t *p;
+ char buf[128];
+ apr_status_t rv;
+
+ apr_initialize();
+ atexit(apr_terminate);
+ apr_pool_create(&p, NULL);
+
+ apr_file_open_stdin(&in, p);
+ apr_file_open_stdout(&out, p);
+
+ total_bytes = 0;
+ nbytes = sizeof(buf);
+ while ((rv = apr_file_read(in, buf, &nbytes)) == APR_SUCCESS) {
+ total_bytes += nbytes;
+ nbytes = sizeof(buf);
+ }
+
+ apr_file_printf(out, "%" APR_SIZE_T_FMT " bytes were read\n",
+ total_bytes);
+ return 0;
+}
diff --git a/test/sendfile.c b/test/sendfile.c
new file mode 100644
index 0000000..5f49c4a
--- /dev/null
+++ b/test/sendfile.c
@@ -0,0 +1,770 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include "apr_network_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_poll.h"
+#include "apr_thread_proc.h"
+
+#include "testutil.h"
+
+#if !APR_HAS_SENDFILE
+int main(void)
+{
+ fprintf(stderr,
+ "This program won't work on this platform because there is no "
+ "support for sendfile().\n");
+ return 0;
+}
+#else /* !APR_HAS_SENDFILE */
+
+#define FILE_LENGTH 200000
+
+#define FILE_DATA_CHAR '0'
+
+#define HDR1 "1234567890ABCD\n"
+#define HDR2 "EFGH\n"
+#define HDR3_LEN 80000
+#define HDR3_CHAR '^'
+#define TRL1 "IJKLMNOPQRSTUVWXYZ\n"
+#define TRL2 "!@#$%&*()\n"
+#define TRL3_LEN 90000
+#define TRL3_CHAR '@'
+
+#define TESTSF_PORT 8021
+
+#define TESTFILE "testsf.dat"
+
+typedef enum {BLK, NONBLK, TIMEOUT} client_socket_mode_t;
+
+static void aprerr(const char *fn, apr_status_t rv)
+{
+ char buf[120];
+
+ fprintf(stderr, "%s->%d/%s\n",
+ fn, rv, apr_strerror(rv, buf, sizeof buf));
+ exit(1);
+}
+
+static void apr_setup(apr_pool_t *p, apr_socket_t **sock, int *family)
+{
+ apr_status_t rv;
+
+ *sock = NULL;
+ rv = apr_socket_create(sock, *family, SOCK_STREAM, 0, p);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_create()", rv);
+ }
+
+ if (*family == APR_UNSPEC) {
+ apr_sockaddr_t *localsa;
+
+ rv = apr_socket_addr_get(&localsa, APR_LOCAL, *sock);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_addr_get()", rv);
+ }
+ *family = localsa->family;
+ }
+}
+
+static void create_testfile(apr_pool_t *p, const char *fname)
+{
+ apr_file_t *f = NULL;
+ apr_status_t rv;
+ char buf[120];
+ int i;
+ apr_finfo_t finfo;
+
+ printf("Creating a test file...\n");
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_TRUNCATE | APR_FOPEN_BUFFERED,
+ APR_UREAD | APR_UWRITE, p);
+ if (rv) {
+ aprerr("apr_file_open()", rv);
+ }
+
+ buf[0] = FILE_DATA_CHAR;
+ buf[1] = '\0';
+ for (i = 0; i < FILE_LENGTH; i++) {
+ /* exercise apr_file_putc() and apr_file_puts() on buffered files */
+ if ((i % 2) == 0) {
+ rv = apr_file_putc(buf[0], f);
+ if (rv) {
+ aprerr("apr_file_putc()", rv);
+ }
+ }
+ else {
+ rv = apr_file_puts(buf, f);
+ if (rv) {
+ aprerr("apr_file_puts()", rv);
+ }
+ }
+ }
+
+ rv = apr_file_close(f);
+ if (rv) {
+ aprerr("apr_file_close()", rv);
+ }
+
+ rv = apr_stat(&finfo, fname, APR_FINFO_NORM, p);
+ if (rv != APR_SUCCESS && ! APR_STATUS_IS_INCOMPLETE(rv)) {
+ aprerr("apr_stat()", rv);
+ }
+
+ if (finfo.size != FILE_LENGTH) {
+ fprintf(stderr,
+ "test file %s should be %ld-bytes long\n"
+ "instead it is %ld-bytes long\n",
+ fname,
+ (long int)FILE_LENGTH,
+ (long int)finfo.size);
+ exit(1);
+ }
+}
+
+static void spawn_server(apr_pool_t *p, apr_proc_t *out_proc)
+{
+ apr_proc_t proc = {0};
+ apr_procattr_t *procattr;
+ apr_status_t rv;
+ const char *args[3];
+
+ rv = apr_procattr_create(&procattr, p);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_procattr_create()", rv);
+ }
+
+ rv = apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK,
+ APR_CHILD_BLOCK);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_procattr_io_set()", rv);
+ }
+
+ rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM_ENV);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_procattr_cmdtype_set()", rv);
+ }
+
+ rv = apr_procattr_error_check_set(procattr, 1);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_procattr_error_check_set()", rv);
+ }
+
+ args[0] = "sendfile" EXTENSION;
+ args[1] = "server";
+ args[2] = NULL;
+ rv = apr_proc_create(&proc, TESTBINPATH "sendfile" EXTENSION, args, NULL, procattr, p);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_proc_create()", rv);
+ }
+
+ *out_proc = proc;
+}
+
+static int client(apr_pool_t *p, client_socket_mode_t socket_mode,
+ const char *host, int start_server)
+{
+ apr_status_t rv, tmprv;
+ apr_socket_t *sock;
+ char buf[120];
+ apr_file_t *f = NULL;
+ apr_size_t len;
+ apr_size_t expected_len;
+ apr_off_t current_file_offset;
+ apr_hdtr_t hdtr;
+ struct iovec headers[3];
+ struct iovec trailers[3];
+ apr_size_t bytes_read;
+ apr_pollset_t *pset;
+ apr_int32_t nsocks;
+ int connect_tries = 1;
+ int i;
+ int family;
+ apr_sockaddr_t *destsa;
+ apr_proc_t server;
+ apr_interval_time_t connect_retry_interval = apr_time_from_msec(50);
+
+ if (start_server) {
+ spawn_server(p, &server);
+ connect_tries = 5; /* give it a chance to start up */
+ }
+
+ create_testfile(p, TESTFILE);
+
+ rv = apr_file_open(&f, TESTFILE, APR_FOPEN_READ, 0, p);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_file_open()", rv);
+ }
+
+ if (!host) {
+ host = "127.0.0.1";
+ }
+ family = APR_INET;
+ rv = apr_sockaddr_info_get(&destsa, host, family, TESTSF_PORT, 0, p);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_sockaddr_info_get()", rv);
+ }
+
+ while (connect_tries--) {
+ apr_setup(p, &sock, &family);
+ rv = apr_socket_connect(sock, destsa);
+ if (connect_tries && APR_STATUS_IS_ECONNREFUSED(rv)) {
+ apr_status_t tmprv = apr_socket_close(sock);
+ if (tmprv != APR_SUCCESS) {
+ aprerr("apr_socket_close()", tmprv);
+ }
+ apr_sleep(connect_retry_interval);
+ connect_retry_interval *= 2;
+ }
+ else {
+ break;
+ }
+ }
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_connect()", rv);
+ }
+
+ switch(socket_mode) {
+ case BLK:
+ /* leave it blocking */
+ break;
+ case NONBLK:
+ /* set it non-blocking */
+ rv = apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_opt_set(APR_SO_NONBLOCK)", rv);
+ }
+ break;
+ case TIMEOUT:
+ /* set a timeout */
+ rv = apr_socket_timeout_set(sock, 100 * APR_USEC_PER_SEC);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_opt_set(APR_SO_NONBLOCK)", rv);
+ exit(1);
+ }
+ break;
+ default:
+ assert(1 != 1);
+ }
+
+ printf("Sending the file...\n");
+
+ hdtr.headers = headers;
+ hdtr.numheaders = 3;
+ hdtr.headers[0].iov_base = HDR1;
+ hdtr.headers[0].iov_len = strlen(hdtr.headers[0].iov_base);
+ hdtr.headers[1].iov_base = HDR2;
+ hdtr.headers[1].iov_len = strlen(hdtr.headers[1].iov_base);
+ hdtr.headers[2].iov_base = apr_palloc(p, HDR3_LEN);
+ assert(hdtr.headers[2].iov_base);
+ memset(hdtr.headers[2].iov_base, HDR3_CHAR, HDR3_LEN);
+ hdtr.headers[2].iov_len = HDR3_LEN;
+
+ hdtr.trailers = trailers;
+ hdtr.numtrailers = 3;
+ hdtr.trailers[0].iov_base = TRL1;
+ hdtr.trailers[0].iov_len = strlen(hdtr.trailers[0].iov_base);
+ hdtr.trailers[1].iov_base = TRL2;
+ hdtr.trailers[1].iov_len = strlen(hdtr.trailers[1].iov_base);
+ hdtr.trailers[2].iov_base = apr_palloc(p, TRL3_LEN);
+ assert(hdtr.trailers[2].iov_base);
+ memset(hdtr.trailers[2].iov_base, TRL3_CHAR, TRL3_LEN);
+ hdtr.trailers[2].iov_len = TRL3_LEN;
+
+ expected_len =
+ strlen(HDR1) + strlen(HDR2) + HDR3_LEN +
+ strlen(TRL1) + strlen(TRL2) + TRL3_LEN +
+ FILE_LENGTH;
+
+ if (socket_mode == BLK) {
+ current_file_offset = 0;
+ len = FILE_LENGTH;
+ rv = apr_socket_sendfile(sock, f, &hdtr, &current_file_offset, &len, 0);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_sendfile()", rv);
+ }
+
+ printf("apr_socket_sendfile() updated offset with %ld\n",
+ (long int)current_file_offset);
+
+ printf("apr_socket_sendfile() updated len with %ld\n",
+ (long int)len);
+
+ printf("bytes really sent: %" APR_SIZE_T_FMT "\n",
+ expected_len);
+
+ if (len != expected_len) {
+ fprintf(stderr, "apr_socket_sendfile() didn't report the correct "
+ "number of bytes sent!\n");
+ exit(1);
+ }
+ }
+ else {
+ /* non-blocking... wooooooo */
+ apr_size_t total_bytes_sent;
+ apr_pollfd_t pfd;
+
+ pset = NULL;
+ rv = apr_pollset_create(&pset, 1, p, 0);
+ assert(!rv);
+ pfd.p = p;
+ pfd.desc_type = APR_POLL_SOCKET;
+ pfd.reqevents = APR_POLLOUT;
+ pfd.rtnevents = 0;
+ pfd.desc.s = sock;
+ pfd.client_data = NULL;
+
+ rv = apr_pollset_add(pset, &pfd);
+ assert(!rv);
+
+ total_bytes_sent = 0;
+ current_file_offset = 0;
+ len = FILE_LENGTH;
+ do {
+ apr_size_t tmplen;
+
+ tmplen = len; /* bytes remaining to send from the file */
+ printf("Calling apr_socket_sendfile()...\n");
+ printf("Headers (%d):\n", hdtr.numheaders);
+ for (i = 0; i < hdtr.numheaders; i++) {
+ printf("\t%ld bytes (%c)\n",
+ (long)hdtr.headers[i].iov_len,
+ *(char *)hdtr.headers[i].iov_base);
+ }
+ printf("File: %ld bytes from offset %ld\n",
+ (long)tmplen, (long)current_file_offset);
+ printf("Trailers (%d):\n", hdtr.numtrailers);
+ for (i = 0; i < hdtr.numtrailers; i++) {
+ printf("\t%ld bytes\n",
+ (long)hdtr.trailers[i].iov_len);
+ }
+
+ rv = apr_socket_sendfile(sock, f, &hdtr, &current_file_offset, &tmplen, 0);
+ printf("apr_socket_sendfile()->%d, sent %ld bytes\n", rv, (long)tmplen);
+ if (rv) {
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+ assert(tmplen == 0);
+ nsocks = 1;
+ tmprv = apr_pollset_poll(pset, -1, &nsocks, NULL);
+ assert(!tmprv);
+ assert(nsocks == 1);
+ /* continue; */
+ }
+ }
+
+ total_bytes_sent += tmplen;
+
+ /* Adjust hdtr to compensate for partially-written
+ * data.
+ */
+
+ /* First, skip over any header data which might have
+ * been written.
+ */
+ while (tmplen && hdtr.numheaders) {
+ if (tmplen >= hdtr.headers[0].iov_len) {
+ tmplen -= hdtr.headers[0].iov_len;
+ --hdtr.numheaders;
+ ++hdtr.headers;
+ }
+ else {
+ hdtr.headers[0].iov_len -= tmplen;
+ hdtr.headers[0].iov_base =
+ (char*) hdtr.headers[0].iov_base + tmplen;
+ tmplen = 0;
+ }
+ }
+
+ /* Now, skip over any file data which might have been
+ * written.
+ */
+
+ if (tmplen <= len) {
+ current_file_offset += tmplen;
+ len -= tmplen;
+ tmplen = 0;
+ }
+ else {
+ tmplen -= len;
+ len = 0;
+ current_file_offset = 0;
+ }
+
+ /* Last, skip over any trailer data which might have
+ * been written.
+ */
+
+ while (tmplen && hdtr.numtrailers) {
+ if (tmplen >= hdtr.trailers[0].iov_len) {
+ tmplen -= hdtr.trailers[0].iov_len;
+ --hdtr.numtrailers;
+ ++hdtr.trailers;
+ }
+ else {
+ hdtr.trailers[0].iov_len -= tmplen;
+ hdtr.trailers[0].iov_base =
+ (char *)hdtr.trailers[0].iov_base + tmplen;
+ tmplen = 0;
+ }
+ }
+
+ } while (total_bytes_sent < expected_len &&
+ (rv == APR_SUCCESS ||
+ (APR_STATUS_IS_EAGAIN(rv) && socket_mode != TIMEOUT)));
+ if (total_bytes_sent != expected_len) {
+ fprintf(stderr,
+ "client problem: sent %ld of %ld bytes\n",
+ (long)total_bytes_sent, (long)expected_len);
+ exit(1);
+ }
+
+ if (rv) {
+ fprintf(stderr,
+ "client problem: rv %d\n",
+ rv);
+ exit(1);
+ }
+ }
+
+ current_file_offset = 0;
+ rv = apr_file_seek(f, APR_CUR, &current_file_offset);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_file_seek()", rv);
+ }
+
+ printf("After apr_socket_sendfile(), the kernel file pointer is "
+ "at offset %ld.\n",
+ (long int)current_file_offset);
+
+ rv = apr_socket_shutdown(sock, APR_SHUTDOWN_WRITE);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_shutdown()", rv);
+ }
+
+ /* in case this is the non-blocking test, set socket timeout;
+ * we're just waiting for EOF */
+
+ rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_timeout_set()", rv);
+ }
+
+ bytes_read = 1;
+ rv = apr_socket_recv(sock, buf, &bytes_read);
+ if (rv != APR_EOF) {
+ aprerr("apr_socket_recv() (expected APR_EOF)", rv);
+ }
+ if (bytes_read != 0) {
+ fprintf(stderr, "We expected to get 0 bytes read with APR_EOF\n"
+ "but instead we read %ld bytes.\n",
+ (long int)bytes_read);
+ exit(1);
+ }
+
+ printf("client: apr_socket_sendfile() worked as expected!\n");
+
+ rv = apr_file_remove(TESTFILE, p);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_file_remove()", rv);
+ }
+
+ if (start_server) {
+ apr_exit_why_e exitwhy;
+ apr_size_t nbytes;
+ char responsebuf[1024];
+ int exitcode;
+
+ rv = apr_file_pipe_timeout_set(server.out, apr_time_from_sec(2));
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_file_pipe_timeout_set()", rv);
+ }
+ nbytes = sizeof(responsebuf);
+ rv = apr_file_read(server.out, responsebuf, &nbytes);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_file_read() messages from server", rv);
+ }
+ printf("%.*s", (int)nbytes, responsebuf);
+ rv = apr_proc_wait(&server, &exitcode, &exitwhy, APR_WAIT);
+ if (rv != APR_CHILD_DONE) {
+ aprerr("apr_proc_wait() (expected APR_CHILD_DONE)", rv);
+ }
+ if (exitcode != 0) {
+ fprintf(stderr, "sendfile server returned %d\n", exitcode);
+ exit(1);
+ }
+ }
+
+ return 0;
+}
+
+static int server(apr_pool_t *p)
+{
+ apr_status_t rv;
+ apr_socket_t *sock;
+ char buf[120];
+ int i;
+ apr_socket_t *newsock = NULL;
+ apr_size_t bytes_read;
+ apr_sockaddr_t *localsa;
+ int family;
+
+ family = APR_INET;
+ apr_setup(p, &sock, &family);
+
+ rv = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_opt_set()", rv);
+ }
+
+ rv = apr_sockaddr_info_get(&localsa, NULL, family, TESTSF_PORT, 0, p);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_sockaddr_info_get()", rv);
+ }
+
+ rv = apr_socket_bind(sock, localsa);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_bind()", rv);
+ }
+
+ rv = apr_socket_listen(sock, 5);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_listen()", rv);
+ }
+
+ printf("Waiting for a client to connect...\n");
+
+ rv = apr_socket_accept(&newsock, sock, p);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_accept()", rv);
+ }
+
+ printf("Processing a client...\n");
+
+ assert(sizeof buf > strlen(HDR1));
+ bytes_read = strlen(HDR1);
+ rv = apr_socket_recv(newsock, buf, &bytes_read);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_recv()", rv);
+ }
+ if (bytes_read != strlen(HDR1)) {
+ fprintf(stderr, "wrong data read (1)\n");
+ exit(1);
+ }
+ if (memcmp(buf, HDR1, strlen(HDR1))) {
+ fprintf(stderr, "wrong data read (2)\n");
+ fprintf(stderr, "received: `%.*s'\nexpected: `%s'\n",
+ (int)bytes_read, buf, HDR1);
+ exit(1);
+ }
+
+ assert(sizeof buf > strlen(HDR2));
+ bytes_read = strlen(HDR2);
+ rv = apr_socket_recv(newsock, buf, &bytes_read);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_recv()", rv);
+ }
+ if (bytes_read != strlen(HDR2)) {
+ fprintf(stderr, "wrong data read (3)\n");
+ exit(1);
+ }
+ if (memcmp(buf, HDR2, strlen(HDR2))) {
+ fprintf(stderr, "wrong data read (4)\n");
+ fprintf(stderr, "received: `%.*s'\nexpected: `%s'\n",
+ (int)bytes_read, buf, HDR2);
+ exit(1);
+ }
+
+ for (i = 0; i < HDR3_LEN; i++) {
+ bytes_read = 1;
+ rv = apr_socket_recv(newsock, buf, &bytes_read);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_recv()", rv);
+ }
+ if (bytes_read != 1) {
+ fprintf(stderr, "apr_socket_recv()->%ld bytes instead of 1\n",
+ (long int)bytes_read);
+ exit(1);
+ }
+ if (buf[0] != HDR3_CHAR) {
+ fprintf(stderr,
+ "problem with data read (byte %d of hdr 3):\n",
+ i);
+ fprintf(stderr, "read `%c' (0x%x) from client; expected "
+ "`%c'\n",
+ buf[0], buf[0], HDR3_CHAR);
+ exit(1);
+ }
+ }
+
+ for (i = 0; i < FILE_LENGTH; i++) {
+ bytes_read = 1;
+ rv = apr_socket_recv(newsock, buf, &bytes_read);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_recv()", rv);
+ }
+ if (bytes_read != 1) {
+ fprintf(stderr, "apr_socket_recv()->%ld bytes instead of 1\n",
+ (long int)bytes_read);
+ exit(1);
+ }
+ if (buf[0] != FILE_DATA_CHAR) {
+ fprintf(stderr,
+ "problem with data read (byte %d of file):\n",
+ i);
+ fprintf(stderr, "read `%c' (0x%x) from client; expected "
+ "`%c'\n",
+ buf[0], buf[0], FILE_DATA_CHAR);
+ exit(1);
+ }
+ }
+
+ assert(sizeof buf > strlen(TRL1));
+ bytes_read = strlen(TRL1);
+ rv = apr_socket_recv(newsock, buf, &bytes_read);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_recv()", rv);
+ }
+ if (bytes_read != strlen(TRL1)) {
+ fprintf(stderr, "wrong data read (5)\n");
+ exit(1);
+ }
+ if (memcmp(buf, TRL1, strlen(TRL1))) {
+ fprintf(stderr, "wrong data read (6)\n");
+ fprintf(stderr, "received: `%.*s'\nexpected: `%s'\n",
+ (int)bytes_read, buf, TRL1);
+ exit(1);
+ }
+
+ assert(sizeof buf > strlen(TRL2));
+ bytes_read = strlen(TRL2);
+ rv = apr_socket_recv(newsock, buf, &bytes_read);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_recv()", rv);
+ }
+ if (bytes_read != strlen(TRL2)) {
+ fprintf(stderr, "wrong data read (7)\n");
+ exit(1);
+ }
+ if (memcmp(buf, TRL2, strlen(TRL2))) {
+ fprintf(stderr, "wrong data read (8)\n");
+ fprintf(stderr, "received: `%.*s'\nexpected: `%s'\n",
+ (int)bytes_read, buf, TRL2);
+ exit(1);
+ }
+
+ for (i = 0; i < TRL3_LEN; i++) {
+ bytes_read = 1;
+ rv = apr_socket_recv(newsock, buf, &bytes_read);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_socket_recv()", rv);
+ }
+ if (bytes_read != 1) {
+ fprintf(stderr, "apr_socket_recv()->%ld bytes instead of 1\n",
+ (long int)bytes_read);
+ exit(1);
+ }
+ if (buf[0] != TRL3_CHAR) {
+ fprintf(stderr,
+ "problem with data read (byte %d of trl 3):\n",
+ i);
+ fprintf(stderr, "read `%c' (0x%x) from client; expected "
+ "`%c'\n",
+ buf[0], buf[0], TRL3_CHAR);
+ exit(1);
+ }
+ }
+
+ bytes_read = 1;
+ rv = apr_socket_recv(newsock, buf, &bytes_read);
+ if (rv != APR_EOF) {
+ aprerr("apr_socket_recv() (expected APR_EOF)", rv);
+ }
+ if (bytes_read != 0) {
+ fprintf(stderr, "We expected to get 0 bytes read with APR_EOF\n"
+ "but instead we read %ld bytes (%c).\n",
+ (long int)bytes_read, buf[0]);
+ exit(1);
+ }
+
+ printf("server: apr_socket_sendfile() worked as expected!\n");
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ apr_pool_t *p;
+ apr_status_t rv;
+
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+
+ rv = apr_initialize();
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_initialize()", rv);
+ }
+
+ atexit(apr_terminate);
+
+ rv = apr_pool_create(&p, NULL);
+ if (rv != APR_SUCCESS) {
+ aprerr("apr_pool_create()", rv);
+ }
+
+ if (argc >= 2 && !strcmp(argv[1], "client")) {
+ const char *host = NULL;
+ int mode = BLK;
+ int start_server = 0;
+ int i;
+
+ for (i = 2; i < argc; i++) {
+ if (!strcmp(argv[i], "blocking")) {
+ mode = BLK;
+ }
+ else if (!strcmp(argv[i], "timeout")) {
+ mode = TIMEOUT;
+ }
+ else if (!strcmp(argv[i], "nonblocking")) {
+ mode = NONBLK;
+ }
+ else if (!strcmp(argv[i], "startserver")) {
+ start_server = 1;
+ }
+ else {
+ host = argv[i];
+ }
+ }
+ return client(p, mode, host, start_server);
+ }
+ else if (argc == 2 && !strcmp(argv[1], "server")) {
+ return server(p);
+ }
+
+ fprintf(stderr,
+ "Usage: %s client {blocking|nonblocking|timeout} [startserver] [server-host]\n"
+ " %s server\n",
+ argv[0], argv[0]);
+ return -1;
+}
+
+#endif /* !APR_HAS_SENDFILE */
diff --git a/test/sockchild.c b/test/sockchild.c
new file mode 100644
index 0000000..a1116af
--- /dev/null
+++ b/test/sockchild.c
@@ -0,0 +1,90 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include "testsock.h"
+#include "apr_network_io.h"
+#include "apr_pools.h"
+
+int main(int argc, char *argv[])
+{
+ apr_pool_t *p;
+ apr_socket_t *sock;
+ apr_status_t rv;
+ apr_sockaddr_t *remote_sa;
+
+ apr_initialize();
+ atexit(apr_terminate);
+ apr_pool_create(&p, NULL);
+
+ if (argc < 3) {
+ exit(-1);
+ }
+
+ rv = apr_sockaddr_info_get(&remote_sa, argv[2], APR_UNSPEC, 8021, 0, p);
+ if (rv != APR_SUCCESS) {
+ exit(-1);
+ }
+
+ if (apr_socket_create(&sock, remote_sa->family, SOCK_STREAM, 0,
+ p) != APR_SUCCESS) {
+ exit(-1);
+ }
+
+ rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
+ if (rv) {
+ exit(-1);
+ }
+
+ apr_socket_connect(sock, remote_sa);
+
+ if (!strcmp("read", argv[1])) {
+ char datarecv[STRLEN];
+ apr_size_t length = STRLEN;
+ apr_status_t rv;
+
+ memset(datarecv, 0, STRLEN);
+ rv = apr_socket_recv(sock, datarecv, &length);
+ apr_socket_close(sock);
+ if (APR_STATUS_IS_TIMEUP(rv)) {
+ exit(SOCKET_TIMEOUT);
+ }
+
+ if (strcmp(datarecv, DATASTR)) {
+ exit(-1);
+ }
+
+ exit((int)length);
+ }
+ else if (!strcmp("write", argv[1])
+ || !strcmp("write_after_delay", argv[1])) {
+ apr_size_t length = strlen(DATASTR);
+
+ if (!strcmp("write_after_delay", argv[1])) {
+ apr_sleep(apr_time_from_sec(2));
+ }
+
+ apr_socket_send(sock, DATASTR, &length);
+
+ apr_socket_close(sock);
+ exit((int)length);
+ }
+ else if (!strcmp("close", argv[1])) {
+ apr_socket_close(sock);
+ exit(0);
+ }
+ exit(-1);
+}
diff --git a/test/sockperf.c b/test/sockperf.c
new file mode 100644
index 0000000..28368ef
--- /dev/null
+++ b/test/sockperf.c
@@ -0,0 +1,256 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* sockperf.c
+ * This simple network client tries to connect to an echo daemon (echod)
+ * listening on a port it supplies, then time how long it takes to
+ * reply with packets of varying sizes.
+ * It prints results once completed.
+ *
+ * To run,
+ *
+ * ./echod &
+ * ./sockperf
+ */
+
+#include <stdio.h>
+#include <stdlib.h> /* for atexit() */
+
+#include "apr.h"
+#include "apr_network_io.h"
+#include "apr_strings.h"
+
+#define MAX_ITERS 10
+#define TEST_SIZE 1024
+
+struct testSet {
+ char c;
+ apr_size_t size;
+ int iters;
+} testRuns[] = {
+ { 'a', 1, 3 },
+ { 'b', 4, 3 },
+ { 'c', 16, 5 },
+ { 'd', 64, 5 },
+ { 'e', 256, 10 },
+};
+
+struct testResult {
+ int size;
+ int iters;
+ apr_time_t msecs[MAX_ITERS];
+ apr_time_t avg;
+};
+
+static apr_int16_t testPort = 4747;
+static apr_sockaddr_t *sockAddr = NULL;
+
+static void reportError(const char *msg, apr_status_t rv,
+ apr_pool_t *pool)
+{
+ fprintf(stderr, "%s\n", msg);
+ if (rv != APR_SUCCESS)
+ fprintf(stderr, "Error: %d\n'%s'\n", rv,
+ apr_psprintf(pool, "%pm", &rv));
+
+}
+
+static void closeConnection(apr_socket_t *sock)
+{
+ apr_size_t len = 0;
+ apr_socket_send(sock, NULL, &len);
+}
+
+static apr_status_t sendRecvBuffer(apr_time_t *t, const char *buf,
+ apr_size_t size, apr_pool_t *pool)
+{
+ apr_socket_t *sock;
+ apr_status_t rv;
+ apr_size_t len = size, thistime = size;
+ char *recvBuf;
+ apr_time_t testStart = apr_time_now(), testEnd;
+ int i;
+
+ if (! sockAddr) {
+ rv = apr_sockaddr_info_get(&sockAddr, "127.0.0.1", APR_UNSPEC,
+ testPort, 0, pool);
+ if (rv != APR_SUCCESS) {
+ reportError("Unable to get socket info", rv, pool);
+ return rv;
+ }
+
+ /* make sure we can connect to daemon before we try tests */
+
+ rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, APR_PROTO_TCP,
+ pool);
+ if (rv != APR_SUCCESS) {
+ reportError("Unable to create IPv4 stream socket", rv, pool);
+ return rv;
+ }
+
+ rv = apr_socket_connect(sock, sockAddr);
+ if (rv != APR_SUCCESS) {
+ reportError("Unable to connect to echod!", rv, pool);
+ apr_socket_close(sock);
+ return rv;
+ }
+ apr_socket_close(sock);
+
+ }
+
+ recvBuf = apr_palloc(pool, size);
+ if (! recvBuf) {
+ reportError("Unable to allocate buffer", ENOMEM, pool);
+ return ENOMEM;
+ }
+
+ *t = 0;
+
+ /* START! */
+ testStart = apr_time_now();
+ rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, APR_PROTO_TCP,
+ pool);
+ if (rv != APR_SUCCESS) {
+ reportError("Unable to create IPv4 stream socket", rv, pool);
+ return rv;
+ }
+
+ rv = apr_socket_connect(sock, sockAddr);
+ if (rv != APR_SUCCESS) {
+ reportError("Unable to connect to echod!", rv, pool);
+ apr_socket_close(sock);
+ return rv;
+ }
+
+ for (i = 0; i < 3; i++) {
+
+ len = size;
+ thistime = size;
+
+ rv = apr_socket_send(sock, buf, &len);
+ if (rv != APR_SUCCESS || len != size) {
+ reportError(apr_psprintf(pool,
+ "Unable to send data correctly (iteration %d of 3)",
+ i) , rv, pool);
+ closeConnection(sock);
+ apr_socket_close(sock);
+ return rv;
+ }
+
+ do {
+ len = thistime;
+ rv = apr_socket_recv(sock, &recvBuf[size - thistime], &len);
+ if (rv != APR_SUCCESS) {
+ reportError("Error receiving from socket", rv, pool);
+ break;
+ }
+ thistime -= len;
+ } while (thistime);
+ }
+
+ closeConnection(sock);
+ apr_socket_close(sock);
+ testEnd = apr_time_now();
+ /* STOP! */
+
+ if (thistime) {
+ reportError("Received less than we sent :-(", rv, pool);
+ return rv;
+ }
+ if (strncmp(recvBuf, buf, size) != 0) {
+ reportError("Received corrupt data :-(", 0, pool);
+ printf("We sent:\n%s\nWe received:\n%s\n", buf, recvBuf);
+ return EINVAL;
+ }
+ *t = testEnd - testStart;
+ return APR_SUCCESS;
+}
+
+static apr_status_t runTest(struct testSet *ts, struct testResult *res,
+ apr_pool_t *pool)
+{
+ char *buffer;
+ apr_status_t rv = APR_SUCCESS;
+ int i;
+ apr_size_t sz = ts->size * TEST_SIZE;
+
+ buffer = apr_palloc(pool, sz);
+ if (!buffer) {
+ reportError("Unable to allocate buffer", ENOMEM, pool);
+ return ENOMEM;
+ }
+ memset(buffer, ts->c, sz);
+
+ res->iters = ts->iters > MAX_ITERS ? MAX_ITERS : ts->iters;
+
+ for (i = 0; i < res->iters; i++) {
+ apr_time_t iterTime;
+ rv = sendRecvBuffer(&iterTime, buffer, sz, pool);
+ if (rv != APR_SUCCESS) {
+ res->iters = i;
+ break;
+ }
+ res->msecs[i] = iterTime;
+ }
+
+ return rv;
+}
+
+int main(int argc, char **argv)
+{
+ apr_pool_t *pool;
+ apr_status_t rv;
+ int i;
+ int nTests = sizeof(testRuns) / sizeof(testRuns[0]);
+ struct testResult *results;
+
+ printf("APR Test Application: sockperf\n");
+
+ apr_initialize();
+ atexit(apr_terminate);
+
+ apr_pool_create(&pool, NULL);
+
+ results = (struct testResult *)apr_pcalloc(pool,
+ sizeof(*results) * nTests);
+
+ for (i = 0; i < nTests; i++) {
+ printf("Test -> %c\n", testRuns[i].c);
+ results[i].size = testRuns[i].size * (apr_size_t)TEST_SIZE;
+ rv = runTest(&testRuns[i], &results[i], pool);
+ if (rv != APR_SUCCESS) {
+ /* error already reported */
+ exit(1);
+ }
+ }
+
+ printf("Tests Complete!\n");
+ for (i = 0; i < nTests; i++) {
+ int j;
+ apr_time_t totTime = 0;
+ printf("%10d byte block:\n", results[i].size);
+ printf("\t%2d iterations : ", results[i].iters);
+ for (j = 0; j < results[i].iters; j++) {
+ printf("%6" APR_TIME_T_FMT, results[i].msecs[j]);
+ totTime += results[i].msecs[j];
+ }
+ printf("<\n");
+ printf("\t Average: %6" APR_TIME_T_FMT "\n",
+ totTime / results[i].iters);
+ }
+
+ return 0;
+}
diff --git a/test/testall.dsw b/test/testall.dsw
new file mode 100644
index 0000000..c56452a
--- /dev/null
+++ b/test/testall.dsw
@@ -0,0 +1,137 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "apr"="..\apr.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "aprapp"="..\build\aprapp.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name preaprapp
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "libapr"="..\libapr.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "libaprapp"="..\build\libaprapp.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name prelibaprapp
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "preaprapp"="..\build\preaprapp.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name apr
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "prelibaprapp"="..\build\prelibaprapp.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libapr
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "testdll"=".\testdll.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libapr
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name libaprapp
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "testlib"=".\testlib.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name apr
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name aprapp
+ End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/test/testapp.c b/test/testapp.c
new file mode 100644
index 0000000..77607aa
--- /dev/null
+++ b/test/testapp.c
@@ -0,0 +1,10 @@
+#include <apr.h>
+#include <apr_general.h>
+
+int main(int argc, const char * const * argv, const char * const *env)
+{
+ apr_app_initialize(&argc, &argv, &env);
+
+
+ apr_terminate();
+}
diff --git a/test/testargs.c b/test/testargs.c
new file mode 100644
index 0000000..cb50192
--- /dev/null
+++ b/test/testargs.c
@@ -0,0 +1,236 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_getopt.h"
+#include "apr_strings.h"
+#include "testutil.h"
+
+static void format_arg(char *str, char option, const char *arg)
+{
+ if (arg) {
+ apr_snprintf(str, 8196, "%soption: %c with %s\n", str, option, arg);
+ }
+ else {
+ apr_snprintf(str, 8196, "%soption: %c\n", str, option);
+ }
+}
+
+static void unknown_arg(void *str, const char *err, ...)
+{
+ va_list va;
+
+ va_start(va, err);
+ apr_vsnprintf(str, 8196, err, va);
+ va_end(va);
+}
+
+static void no_options_found(abts_case *tc, void *data)
+{
+ int largc = 5;
+ const char * const largv[] = {"testprog", "-a", "-b", "-c", "-d"};
+ apr_getopt_t *opt;
+ apr_status_t rv;
+ char ch;
+ const char *optarg;
+ char str[8196];
+
+ str[0] = '\0';
+ rv = apr_getopt_init(&opt, p, largc, largv);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ while (apr_getopt(opt, "abcd", &ch, &optarg) == APR_SUCCESS) {
+ switch (ch) {
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ default:
+ format_arg(str, ch, optarg);
+ }
+ }
+ ABTS_STR_EQUAL(tc, "option: a\n"
+ "option: b\n"
+ "option: c\n"
+ "option: d\n", str);
+}
+
+static void no_options(abts_case *tc, void *data)
+{
+ int largc = 5;
+ const char * const largv[] = {"testprog", "-a", "-b", "-c", "-d"};
+ apr_getopt_t *opt;
+ apr_status_t rv;
+ char ch;
+ const char *optarg;
+ char str[8196];
+
+ str[0] = '\0';
+ rv = apr_getopt_init(&opt, p, largc, largv);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ opt->errfn = unknown_arg;
+ opt->errarg = str;
+
+ while (apr_getopt(opt, "efgh", &ch, &optarg) == APR_SUCCESS) {
+ switch (ch) {
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ format_arg(str, ch, optarg);
+ break;
+ default:
+ break;
+ }
+ }
+ ABTS_STR_EQUAL(tc, "testprog: illegal option -- a\n", str);
+}
+
+static void required_option(abts_case *tc, void *data)
+{
+ int largc = 3;
+ const char * const largv[] = {"testprog", "-a", "foo"};
+ apr_getopt_t *opt;
+ apr_status_t rv;
+ char ch;
+ const char *optarg;
+ char str[8196];
+
+ str[0] = '\0';
+ rv = apr_getopt_init(&opt, p, largc, largv);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ opt->errfn = unknown_arg;
+ opt->errarg = str;
+
+ while (apr_getopt(opt, "a:", &ch, &optarg) == APR_SUCCESS) {
+ switch (ch) {
+ case 'a':
+ format_arg(str, ch, optarg);
+ break;
+ default:
+ break;
+ }
+ }
+ ABTS_STR_EQUAL(tc, "option: a with foo\n", str);
+}
+
+static void required_option_notgiven(abts_case *tc, void *data)
+{
+ int largc = 2;
+ const char * const largv[] = {"testprog", "-a"};
+ apr_getopt_t *opt;
+ apr_status_t rv;
+ char ch;
+ const char *optarg;
+ char str[8196];
+
+ str[0] = '\0';
+ rv = apr_getopt_init(&opt, p, largc, largv);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ opt->errfn = unknown_arg;
+ opt->errarg = str;
+
+ while (apr_getopt(opt, "a:", &ch, &optarg) == APR_SUCCESS) {
+ switch (ch) {
+ case 'a':
+ format_arg(str, ch, optarg);
+ break;
+ default:
+ break;
+ }
+ }
+ ABTS_STR_EQUAL(tc, "testprog: option requires an argument -- a\n", str);
+}
+
+static void optional_option(abts_case *tc, void *data)
+{
+ int largc = 3;
+ const char * const largv[] = {"testprog", "-a", "foo"};
+ apr_getopt_t *opt;
+ apr_status_t rv;
+ char ch;
+ const char *optarg;
+ char str[8196];
+
+ str[0] = '\0';
+ rv = apr_getopt_init(&opt, p, largc, largv);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ opt->errfn = unknown_arg;
+ opt->errarg = str;
+
+ while (apr_getopt(opt, "a::", &ch, &optarg) == APR_SUCCESS) {
+ switch (ch) {
+ case 'a':
+ format_arg(str, ch, optarg);
+ break;
+ default:
+ break;
+ }
+ }
+ ABTS_STR_EQUAL(tc, "option: a with foo\n", str);
+}
+
+static void optional_option_notgiven(abts_case *tc, void *data)
+{
+ int largc = 2;
+ const char * const largv[] = {"testprog", "-a"};
+ apr_getopt_t *opt;
+ apr_status_t rv;
+ char ch;
+ const char *optarg;
+ char str[8196];
+
+ str[0] = '\0';
+ rv = apr_getopt_init(&opt, p, largc, largv);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ opt->errfn = unknown_arg;
+ opt->errarg = str;
+
+ while (apr_getopt(opt, "a::", &ch, &optarg) == APR_SUCCESS) {
+ switch (ch) {
+ case 'a':
+ format_arg(str, ch, optarg);
+ break;
+ default:
+ break;
+ }
+ }
+#if 0
+/* Our version of getopt doesn't allow for optional arguments. */
+ ABTS_STR_EQUAL(tc, "option: a\n", str);
+#endif
+ ABTS_STR_EQUAL(tc, "testprog: option requires an argument -- a\n", str);
+}
+
+abts_suite *testgetopt(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, no_options, NULL);
+ abts_run_test(suite, no_options_found, NULL);
+ abts_run_test(suite, required_option, NULL);
+ abts_run_test(suite, required_option_notgiven, NULL);
+ abts_run_test(suite, optional_option, NULL);
+ abts_run_test(suite, optional_option_notgiven, NULL);
+
+ return suite;
+}
diff --git a/test/testatomic.c b/test/testatomic.c
new file mode 100644
index 0000000..bf388c7
--- /dev/null
+++ b/test/testatomic.c
@@ -0,0 +1,961 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_strings.h"
+#include "apr_thread_proc.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_atomic.h"
+#include "apr_time.h"
+
+/* Use pthread_setconcurrency where it is available and not a nullop,
+ * i.e. platforms using M:N or M:1 thread models: */
+#if APR_HAS_THREADS && \
+ ((defined(SOLARIS2) && SOLARIS2 > 6) || defined(_AIX))
+/* also HP-UX, IRIX? ... */
+#define HAVE_PTHREAD_SETCONCURRENCY
+#endif
+
+#ifdef HAVE_PTHREAD_SETCONCURRENCY
+#include <pthread.h>
+#endif
+
+static void test_init(abts_case *tc, void *data)
+{
+ APR_ASSERT_SUCCESS(tc, "Could not initliaze atomics", apr_atomic_init(p));
+}
+
+static void test_set32(abts_case *tc, void *data)
+{
+ apr_uint32_t y32;
+ apr_atomic_set32(&y32, 2);
+ ABTS_INT_EQUAL(tc, 2, y32);
+}
+
+static void test_read32(abts_case *tc, void *data)
+{
+ apr_uint32_t y32;
+ apr_atomic_set32(&y32, 2);
+ ABTS_INT_EQUAL(tc, 2, apr_atomic_read32(&y32));
+}
+
+static void test_dec32(abts_case *tc, void *data)
+{
+ apr_uint32_t y32;
+ int rv;
+
+ apr_atomic_set32(&y32, 2);
+
+ rv = apr_atomic_dec32(&y32);
+ ABTS_INT_EQUAL(tc, 1, y32);
+ ABTS_ASSERT(tc, "atomic_dec returned zero when it shouldn't", rv != 0);
+
+ rv = apr_atomic_dec32(&y32);
+ ABTS_INT_EQUAL(tc, 0, y32);
+ ABTS_ASSERT(tc, "atomic_dec didn't returned zero when it should", rv == 0);
+}
+
+static void test_xchg32(abts_case *tc, void *data)
+{
+ apr_uint32_t oldval;
+ apr_uint32_t y32;
+
+ apr_atomic_set32(&y32, 100);
+ oldval = apr_atomic_xchg32(&y32, 50);
+
+ ABTS_INT_EQUAL(tc, 100, oldval);
+ ABTS_INT_EQUAL(tc, 50, y32);
+}
+
+static void test_xchgptr(abts_case *tc, void *data)
+{
+ int a;
+ void *ref = "little piggy";
+ volatile void *target_ptr = ref;
+ void *old_ptr;
+
+ old_ptr = apr_atomic_xchgptr(&target_ptr, &a);
+ ABTS_PTR_EQUAL(tc, ref, old_ptr);
+ ABTS_PTR_EQUAL(tc, (void *)&a, (void *)target_ptr);
+}
+
+static void test_cas_equal(abts_case *tc, void *data)
+{
+ apr_uint32_t casval = 0;
+ apr_uint32_t oldval;
+
+ oldval = apr_atomic_cas32(&casval, 12, 0);
+ ABTS_INT_EQUAL(tc, 0, oldval);
+ ABTS_INT_EQUAL(tc, 12, casval);
+}
+
+static void test_cas_equal_nonnull(abts_case *tc, void *data)
+{
+ apr_uint32_t casval = 12;
+ apr_uint32_t oldval;
+
+ oldval = apr_atomic_cas32(&casval, 23, 12);
+ ABTS_INT_EQUAL(tc, 12, oldval);
+ ABTS_INT_EQUAL(tc, 23, casval);
+}
+
+static void test_cas_notequal(abts_case *tc, void *data)
+{
+ apr_uint32_t casval = 12;
+ apr_uint32_t oldval;
+
+ oldval = apr_atomic_cas32(&casval, 23, 2);
+ ABTS_INT_EQUAL(tc, 12, oldval);
+ ABTS_INT_EQUAL(tc, 12, casval);
+}
+
+static void test_casptr_equal(abts_case *tc, void *data)
+{
+ int a = 0;
+ volatile void *target_ptr = NULL;
+ void *old_ptr;
+
+ old_ptr = apr_atomic_casptr(&target_ptr, &a, NULL);
+ ABTS_PTR_EQUAL(tc, NULL, old_ptr);
+ ABTS_PTR_EQUAL(tc, (void *)&a, (void *)target_ptr);
+}
+
+static void test_casptr_equal_nonnull(abts_case *tc, void *data)
+{
+ int a = 0, b = 0;
+ volatile void *target_ptr = &a;
+ void *old_ptr;
+
+ old_ptr = apr_atomic_casptr(&target_ptr, &b, &a);
+ ABTS_PTR_EQUAL(tc, (void *)&a, old_ptr);
+ ABTS_PTR_EQUAL(tc, (void *)&b, (void *)target_ptr);
+}
+
+static void test_casptr_notequal(abts_case *tc, void *data)
+{
+ int a = 0, b = 0;
+ volatile void *target_ptr = &a;
+ void *old_ptr;
+
+ old_ptr = apr_atomic_casptr(&target_ptr, &a, &b);
+ ABTS_PTR_EQUAL(tc, (void *)&a, old_ptr);
+ ABTS_PTR_EQUAL(tc, (void *)&a, (void *)target_ptr);
+}
+
+static void test_add32(abts_case *tc, void *data)
+{
+ apr_uint32_t oldval;
+ apr_uint32_t y32;
+
+ apr_atomic_set32(&y32, 23);
+ oldval = apr_atomic_add32(&y32, 4);
+ ABTS_INT_EQUAL(tc, 23, oldval);
+ ABTS_INT_EQUAL(tc, 27, y32);
+}
+
+static void test_add32_neg(abts_case *tc, void *data)
+{
+ apr_uint32_t oldval;
+ apr_uint32_t y32;
+
+ apr_atomic_set32(&y32, 23);
+ oldval = apr_atomic_add32(&y32, -10);
+ ABTS_INT_EQUAL(tc, 23, oldval);
+ ABTS_INT_EQUAL(tc, 13, y32);
+}
+
+static void test_inc32(abts_case *tc, void *data)
+{
+ apr_uint32_t oldval;
+ apr_uint32_t y32;
+
+ apr_atomic_set32(&y32, 23);
+ oldval = apr_atomic_inc32(&y32);
+ ABTS_INT_EQUAL(tc, 23, oldval);
+ ABTS_INT_EQUAL(tc, 24, y32);
+}
+
+static void test_set_add_inc_sub(abts_case *tc, void *data)
+{
+ apr_uint32_t y32;
+
+ apr_atomic_set32(&y32, 0);
+ apr_atomic_add32(&y32, 20);
+ apr_atomic_inc32(&y32);
+ apr_atomic_sub32(&y32, 10);
+
+ ABTS_INT_EQUAL(tc, 11, y32);
+}
+
+static void test_wrap_zero(abts_case *tc, void *data)
+{
+ apr_uint32_t y32;
+ apr_uint32_t rv;
+ apr_uint32_t minus1 = (apr_uint32_t)-1;
+ char *str;
+
+ apr_atomic_set32(&y32, 0);
+ rv = apr_atomic_dec32(&y32);
+
+ ABTS_ASSERT(tc, "apr_atomic_dec32 on zero returned zero.", rv != 0);
+ str = apr_psprintf(p, "zero wrap failed: 0 - 1 = %d", y32);
+ ABTS_ASSERT(tc, str, y32 == minus1);
+}
+
+static void test_inc_neg1(abts_case *tc, void *data)
+{
+ apr_uint32_t y32 = (apr_uint32_t)-1;
+ apr_uint32_t minus1 = (apr_uint32_t)-1;
+ apr_uint32_t rv;
+ char *str;
+
+ rv = apr_atomic_inc32(&y32);
+
+ ABTS_ASSERT(tc, "apr_atomic_inc32 didn't return the old value.", rv == minus1);
+ str = apr_psprintf(p, "zero wrap failed: -1 + 1 = %d", y32);
+ ABTS_ASSERT(tc, str, y32 == 0);
+}
+
+static void test_set64(abts_case *tc, void *data)
+{
+ apr_uint64_t y64;
+ apr_atomic_set64(&y64, 2);
+ ABTS_INT_EQUAL(tc, 2, y64);
+}
+
+static void test_read64(abts_case *tc, void *data)
+{
+ apr_uint64_t y64;
+ apr_atomic_set64(&y64, 2);
+ ABTS_INT_EQUAL(tc, 2, apr_atomic_read64(&y64));
+}
+
+static void test_dec64(abts_case *tc, void *data)
+{
+ apr_uint64_t y64;
+ int rv;
+
+ apr_atomic_set64(&y64, 2);
+
+ rv = apr_atomic_dec64(&y64);
+ ABTS_INT_EQUAL(tc, 1, y64);
+ ABTS_ASSERT(tc, "atomic_dec returned zero when it shouldn't", rv != 0);
+
+ rv = apr_atomic_dec64(&y64);
+ ABTS_INT_EQUAL(tc, 0, y64);
+ ABTS_ASSERT(tc, "atomic_dec didn't returned zero when it should", rv == 0);
+}
+
+static void test_xchg64(abts_case *tc, void *data)
+{
+ apr_uint64_t oldval;
+ apr_uint64_t y64;
+
+ apr_atomic_set64(&y64, 100);
+ oldval = apr_atomic_xchg64(&y64, 50);
+
+ ABTS_INT_EQUAL(tc, 100, oldval);
+ ABTS_INT_EQUAL(tc, 50, y64);
+}
+
+static void test_add64(abts_case *tc, void *data)
+{
+ apr_uint64_t oldval;
+ apr_uint64_t y64;
+
+ apr_atomic_set64(&y64, 23);
+ oldval = apr_atomic_add64(&y64, 4);
+ ABTS_INT_EQUAL(tc, 23, oldval);
+ ABTS_INT_EQUAL(tc, 27, y64);
+}
+
+static void test_add64_neg(abts_case *tc, void *data)
+{
+ apr_uint64_t oldval;
+ apr_uint64_t y64;
+
+ apr_atomic_set64(&y64, 23);
+ oldval = apr_atomic_add64(&y64, -10);
+ ABTS_INT_EQUAL(tc, 23, oldval);
+ ABTS_INT_EQUAL(tc, 13, y64);
+}
+
+static void test_inc64(abts_case *tc, void *data)
+{
+ apr_uint64_t oldval;
+ apr_uint64_t y64;
+
+ apr_atomic_set64(&y64, 23);
+ oldval = apr_atomic_inc64(&y64);
+ ABTS_INT_EQUAL(tc, 23, oldval);
+ ABTS_INT_EQUAL(tc, 24, y64);
+}
+
+static void test_set_add_inc_sub64(abts_case *tc, void *data)
+{
+ apr_uint64_t y64;
+
+ apr_atomic_set64(&y64, 0);
+ apr_atomic_add64(&y64, 20);
+ apr_atomic_inc64(&y64);
+ apr_atomic_sub64(&y64, 10);
+
+ ABTS_INT_EQUAL(tc, 11, y64);
+}
+
+static void test_wrap_zero64(abts_case *tc, void *data)
+{
+ apr_uint64_t y64;
+ apr_uint64_t rv;
+ apr_uint64_t minus1 = (apr_uint64_t)-1;
+ char *str;
+
+ apr_atomic_set64(&y64, 0);
+ rv = apr_atomic_dec64(&y64);
+
+ ABTS_ASSERT(tc, "apr_atomic_dec64 on zero returned zero.", rv != 0);
+ str = apr_psprintf(p, "zero wrap failed: 0 - 1 = %lu", y64);
+ ABTS_ASSERT(tc, str, y64 == minus1);
+}
+
+static void test_inc_neg164(abts_case *tc, void *data)
+{
+ apr_uint64_t y64 = (apr_uint64_t)-1;
+ apr_uint64_t minus1 = (apr_uint64_t)-1;
+ apr_uint64_t rv;
+ char *str;
+
+ rv = apr_atomic_inc64(&y64);
+
+ ABTS_ASSERT(tc, "apr_atomic_inc64 didn't return the old value.", rv == minus1);
+ str = apr_psprintf(p, "zero wrap failed: -1 + 1 = %lu", y64);
+ ABTS_ASSERT(tc, str, y64 == 0);
+}
+
+
+#if APR_HAS_THREADS
+
+void *APR_THREAD_FUNC thread_func_mutex(apr_thread_t *thd, void *data);
+void *APR_THREAD_FUNC thread_func_mutex64(apr_thread_t *thd, void *data);
+void *APR_THREAD_FUNC thread_func_atomic(apr_thread_t *thd, void *data);
+void *APR_THREAD_FUNC thread_func_atomic64(apr_thread_t *thd, void *data);
+
+apr_thread_mutex_t *thread_lock;
+apr_thread_mutex_t *thread_lock64;
+volatile apr_uint32_t mutex_locks = 0;
+volatile apr_uint64_t mutex_locks64 = 0;
+volatile apr_uint32_t atomic_ops = 0;
+volatile apr_uint64_t atomic_ops64 = 0;
+apr_status_t exit_ret_val = 123; /* just some made up number to check on later */
+
+#define NUM_THREADS 40
+#define NUM_ITERATIONS 20000
+
+void *APR_THREAD_FUNC thread_func_mutex(apr_thread_t *thd, void *data)
+{
+ int i;
+
+ for (i = 0; i < NUM_ITERATIONS; i++) {
+ apr_thread_mutex_lock(thread_lock);
+ mutex_locks++;
+ apr_thread_mutex_unlock(thread_lock);
+ }
+ apr_thread_exit(thd, exit_ret_val);
+ return NULL;
+}
+
+void *APR_THREAD_FUNC thread_func_atomic(apr_thread_t *thd, void *data)
+{
+ int i;
+
+ for (i = 0; i < NUM_ITERATIONS ; i++) {
+ apr_atomic_inc32(&atomic_ops);
+ apr_atomic_add32(&atomic_ops, 2);
+ apr_atomic_dec32(&atomic_ops);
+ apr_atomic_dec32(&atomic_ops);
+ }
+ apr_thread_exit(thd, exit_ret_val);
+ return NULL;
+}
+
+static void test_atomics_threaded(abts_case *tc, void *data)
+{
+ apr_thread_t *t1[NUM_THREADS];
+ apr_thread_t *t2[NUM_THREADS];
+ apr_status_t rv;
+ int i;
+
+#ifdef HAVE_PTHREAD_SETCONCURRENCY
+ pthread_setconcurrency(8);
+#endif
+
+ rv = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "Could not create lock", rv);
+
+ for (i = 0; i < NUM_THREADS; i++) {
+ apr_status_t r1, r2;
+ r1 = apr_thread_create(&t1[i], NULL, thread_func_mutex, NULL, p);
+ r2 = apr_thread_create(&t2[i], NULL, thread_func_atomic, NULL, p);
+ ABTS_ASSERT(tc, "Failed creating threads", !r1 && !r2);
+ }
+
+ for (i = 0; i < NUM_THREADS; i++) {
+ apr_status_t s1, s2;
+ apr_thread_join(&s1, t1[i]);
+ apr_thread_join(&s2, t2[i]);
+
+ ABTS_ASSERT(tc, "Invalid return value from thread_join",
+ s1 == exit_ret_val && s2 == exit_ret_val);
+ }
+
+ ABTS_INT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS, mutex_locks);
+ ABTS_INT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS,
+ apr_atomic_read32(&atomic_ops));
+
+ rv = apr_thread_mutex_destroy(thread_lock);
+ ABTS_ASSERT(tc, "Failed creating threads", rv == APR_SUCCESS);
+}
+
+#undef NUM_THREADS
+#define NUM_THREADS 7
+
+typedef struct tbox_t tbox_t;
+typedef struct tbox_t64 tbox_t64;
+
+struct tbox_t {
+ abts_case *tc;
+ apr_uint32_t *mem;
+ apr_uint32_t preval;
+ apr_uint32_t postval;
+ apr_uint32_t loop;
+ void (*func)(tbox_t *box);
+};
+
+static APR_INLINE void busyloop_read32(tbox_t *tbox)
+{
+ apr_uint32_t val;
+
+ do {
+ val = apr_atomic_read32(tbox->mem);
+
+ if (val != tbox->preval)
+ apr_thread_yield();
+ else
+ break;
+ } while (1);
+}
+
+static void busyloop_set32(tbox_t *tbox)
+{
+ do {
+ busyloop_read32(tbox);
+ apr_atomic_set32(tbox->mem, tbox->postval);
+ } while (--tbox->loop);
+}
+
+static void busyloop_add32(tbox_t *tbox)
+{
+ apr_uint32_t val;
+
+ do {
+ busyloop_read32(tbox);
+ val = apr_atomic_add32(tbox->mem, tbox->postval);
+ apr_thread_mutex_lock(thread_lock);
+ ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
+ apr_thread_mutex_unlock(thread_lock);
+ } while (--tbox->loop);
+}
+
+static void busyloop_sub32(tbox_t *tbox)
+{
+ do {
+ busyloop_read32(tbox);
+ apr_atomic_sub32(tbox->mem, tbox->postval);
+ } while (--tbox->loop);
+}
+
+static void busyloop_inc32(tbox_t *tbox)
+{
+ apr_uint32_t val;
+
+ do {
+ busyloop_read32(tbox);
+ val = apr_atomic_inc32(tbox->mem);
+ apr_thread_mutex_lock(thread_lock);
+ ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
+ apr_thread_mutex_unlock(thread_lock);
+ } while (--tbox->loop);
+}
+
+static void busyloop_dec32(tbox_t *tbox)
+{
+ apr_uint32_t val;
+
+ do {
+ busyloop_read32(tbox);
+ val = apr_atomic_dec32(tbox->mem);
+ apr_thread_mutex_lock(thread_lock);
+ ABTS_INT_NEQUAL(tbox->tc, 0, val);
+ apr_thread_mutex_unlock(thread_lock);
+ } while (--tbox->loop);
+}
+
+static void busyloop_cas32(tbox_t *tbox)
+{
+ apr_uint32_t val;
+
+ do {
+ do {
+ val = apr_atomic_cas32(tbox->mem, tbox->postval, tbox->preval);
+
+ if (val != tbox->preval)
+ apr_thread_yield();
+ else
+ break;
+ } while (1);
+ } while (--tbox->loop);
+}
+
+static void busyloop_xchg32(tbox_t *tbox)
+{
+ apr_uint32_t val;
+
+ do {
+ busyloop_read32(tbox);
+ val = apr_atomic_xchg32(tbox->mem, tbox->postval);
+ apr_thread_mutex_lock(thread_lock);
+ ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
+ apr_thread_mutex_unlock(thread_lock);
+ } while (--tbox->loop);
+}
+
+static void *APR_THREAD_FUNC thread_func_busyloop(apr_thread_t *thd, void *data)
+{
+ tbox_t *tbox = data;
+
+ tbox->func(tbox);
+
+ apr_thread_exit(thd, 0);
+
+ return NULL;
+}
+
+static void test_atomics_busyloop_threaded(abts_case *tc, void *data)
+{
+ unsigned int i;
+ apr_status_t rv;
+ apr_uint32_t count = 0;
+ tbox_t tbox[NUM_THREADS];
+ apr_thread_t *thread[NUM_THREADS];
+
+ rv = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "Could not create lock", rv);
+
+ /* get ready */
+ for (i = 0; i < NUM_THREADS; i++) {
+ tbox[i].tc = tc;
+ tbox[i].mem = &count;
+ tbox[i].loop = 50;
+ }
+
+ tbox[0].preval = 98;
+ tbox[0].postval = 3891;
+ tbox[0].func = busyloop_add32;
+
+ tbox[1].preval = 3989;
+ tbox[1].postval = 1010;
+ tbox[1].func = busyloop_sub32;
+
+ tbox[2].preval = 2979;
+ tbox[2].postval = 0; /* not used */
+ tbox[2].func = busyloop_inc32;
+
+ tbox[3].preval = 2980;
+ tbox[3].postval = 16384;
+ tbox[3].func = busyloop_set32;
+
+ tbox[4].preval = 16384;
+ tbox[4].postval = 0; /* not used */
+ tbox[4].func = busyloop_dec32;
+
+ tbox[5].preval = 16383;
+ tbox[5].postval = 1048576;
+ tbox[5].func = busyloop_cas32;
+
+ tbox[6].preval = 1048576;
+ tbox[6].postval = 98; /* goto tbox[0] */
+ tbox[6].func = busyloop_xchg32;
+
+ /* get set */
+ for (i = 0; i < NUM_THREADS; i++) {
+ rv = apr_thread_create(&thread[i], NULL, thread_func_busyloop,
+ &tbox[i], p);
+ ABTS_ASSERT(tc, "Failed creating thread", rv == APR_SUCCESS);
+ }
+
+ /* go! */
+ apr_atomic_set32(tbox->mem, 98);
+
+ for (i = 0; i < NUM_THREADS; i++) {
+ apr_status_t retval;
+ rv = apr_thread_join(&retval, thread[i]);
+ ABTS_ASSERT(tc, "Thread join failed", rv == APR_SUCCESS);
+ ABTS_ASSERT(tc, "Invalid return value from thread_join", retval == 0);
+ }
+
+ ABTS_INT_EQUAL(tbox->tc, 98, count);
+
+ rv = apr_thread_mutex_destroy(thread_lock);
+ ABTS_ASSERT(tc, "Failed creating threads", rv == APR_SUCCESS);
+}
+
+void *APR_THREAD_FUNC thread_func_mutex64(apr_thread_t *thd, void *data)
+{
+ int i;
+
+ for (i = 0; i < NUM_ITERATIONS; i++) {
+ apr_thread_mutex_lock(thread_lock64);
+ mutex_locks64++;
+ apr_thread_mutex_unlock(thread_lock64);
+ }
+ apr_thread_exit(thd, exit_ret_val);
+ return NULL;
+}
+
+
+void *APR_THREAD_FUNC thread_func_atomic64(apr_thread_t *thd, void *data)
+{
+ int i;
+
+ for (i = 0; i < NUM_ITERATIONS ; i++) {
+ apr_atomic_inc64(&atomic_ops64);
+ apr_atomic_add64(&atomic_ops64, 2);
+ apr_atomic_dec64(&atomic_ops64);
+ apr_atomic_dec64(&atomic_ops64);
+ }
+ apr_thread_exit(thd, exit_ret_val);
+ return NULL;
+}
+
+static void test_atomics_threaded64(abts_case *tc, void *data)
+{
+ apr_thread_t *t1[NUM_THREADS];
+ apr_thread_t *t2[NUM_THREADS];
+ apr_status_t rv;
+ int i;
+
+#ifdef HAVE_PTHREAD_SETCONCURRENCY
+ pthread_setconcurrency(8);
+#endif
+
+ rv = apr_thread_mutex_create(&thread_lock64, APR_THREAD_MUTEX_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "Could not create lock", rv);
+
+ for (i = 0; i < NUM_THREADS; i++) {
+ apr_status_t r1, r2;
+ r1 = apr_thread_create(&t1[i], NULL, thread_func_mutex64, NULL, p);
+ r2 = apr_thread_create(&t2[i], NULL, thread_func_atomic64, NULL, p);
+ ABTS_ASSERT(tc, "Failed creating threads", !r1 && !r2);
+ }
+
+ for (i = 0; i < NUM_THREADS; i++) {
+ apr_status_t s1, s2;
+ apr_thread_join(&s1, t1[i]);
+ apr_thread_join(&s2, t2[i]);
+
+ ABTS_ASSERT(tc, "Invalid return value from thread_join",
+ s1 == exit_ret_val && s2 == exit_ret_val);
+ }
+
+ ABTS_INT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS, mutex_locks64);
+ ABTS_INT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS,
+ apr_atomic_read64(&atomic_ops64));
+
+ rv = apr_thread_mutex_destroy(thread_lock64);
+ ABTS_ASSERT(tc, "Failed creating threads", rv == APR_SUCCESS);
+}
+
+struct tbox_t64 {
+ abts_case *tc;
+ apr_uint64_t *mem;
+ apr_uint64_t preval;
+ apr_uint64_t postval;
+ apr_uint64_t loop;
+ void (*func)(tbox_t64 *box);
+};
+
+static APR_INLINE void busyloop_read64(tbox_t64 *tbox)
+{
+ apr_uint64_t val;
+
+ do {
+ val = apr_atomic_read64(tbox->mem);
+
+ if (val != tbox->preval)
+ apr_thread_yield();
+ else
+ break;
+ } while (1);
+}
+
+static void busyloop_set64(tbox_t64 *tbox)
+{
+ do {
+ busyloop_read64(tbox);
+ apr_atomic_set64(tbox->mem, tbox->postval);
+ } while (--tbox->loop);
+}
+
+static void busyloop_add64(tbox_t64 *tbox)
+{
+ apr_uint64_t val;
+
+ do {
+ busyloop_read64(tbox);
+ val = apr_atomic_add64(tbox->mem, tbox->postval);
+ apr_thread_mutex_lock(thread_lock64);
+ ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
+ apr_thread_mutex_unlock(thread_lock64);
+ } while (--tbox->loop);
+}
+
+static void busyloop_sub64(tbox_t64 *tbox)
+{
+ do {
+ busyloop_read64(tbox);
+ apr_atomic_sub64(tbox->mem, tbox->postval);
+ } while (--tbox->loop);
+}
+
+static void busyloop_inc64(tbox_t64 *tbox)
+{
+ apr_uint64_t val;
+
+ do {
+ busyloop_read64(tbox);
+ val = apr_atomic_inc64(tbox->mem);
+ apr_thread_mutex_lock(thread_lock64);
+ ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
+ apr_thread_mutex_unlock(thread_lock64);
+ } while (--tbox->loop);
+}
+
+static void busyloop_dec64(tbox_t64 *tbox)
+{
+ apr_uint64_t val;
+
+ do {
+ busyloop_read64(tbox);
+ val = apr_atomic_dec64(tbox->mem);
+ apr_thread_mutex_lock(thread_lock64);
+ ABTS_INT_NEQUAL(tbox->tc, 0, val);
+ apr_thread_mutex_unlock(thread_lock64);
+ } while (--tbox->loop);
+}
+
+static void busyloop_cas64(tbox_t64 *tbox)
+{
+ apr_uint64_t val;
+
+ do {
+ do {
+ val = apr_atomic_cas64(tbox->mem, tbox->postval, tbox->preval);
+
+ if (val != tbox->preval)
+ apr_thread_yield();
+ else
+ break;
+ } while (1);
+ } while (--tbox->loop);
+}
+
+static void busyloop_xchg64(tbox_t64 *tbox)
+{
+ apr_uint64_t val;
+
+ do {
+ busyloop_read64(tbox);
+ val = apr_atomic_xchg64(tbox->mem, tbox->postval);
+ apr_thread_mutex_lock(thread_lock64);
+ ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
+ apr_thread_mutex_unlock(thread_lock64);
+ } while (--tbox->loop);
+}
+
+static void *APR_THREAD_FUNC thread_func_busyloop64(apr_thread_t *thd, void *data)
+{
+ tbox_t64 *tbox = data;
+
+ tbox->func(tbox);
+
+ apr_thread_exit(thd, 0);
+
+ return NULL;
+}
+
+static void test_atomics_busyloop_threaded64(abts_case *tc, void *data)
+{
+ unsigned int i;
+ apr_status_t rv;
+ apr_uint64_t count = 0;
+ tbox_t64 tbox[NUM_THREADS];
+ apr_thread_t *thread[NUM_THREADS];
+
+ rv = apr_thread_mutex_create(&thread_lock64, APR_THREAD_MUTEX_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "Could not create lock", rv);
+
+ /* get ready */
+ for (i = 0; i < NUM_THREADS; i++) {
+ tbox[i].tc = tc;
+ tbox[i].mem = &count;
+ tbox[i].loop = 50;
+ }
+
+ tbox[0].preval = 98;
+ tbox[0].postval = 3891;
+ tbox[0].func = busyloop_add64;
+
+ tbox[1].preval = 3989;
+ tbox[1].postval = 1010;
+ tbox[1].func = busyloop_sub64;
+
+ tbox[2].preval = 2979;
+ tbox[2].postval = 0; /* not used */
+ tbox[2].func = busyloop_inc64;
+
+ tbox[3].preval = 2980;
+ tbox[3].postval = 16384;
+ tbox[3].func = busyloop_set64;
+
+ tbox[4].preval = 16384;
+ tbox[4].postval = 0; /* not used */
+ tbox[4].func = busyloop_dec64;
+
+ tbox[5].preval = 16383;
+ tbox[5].postval = 1048576;
+ tbox[5].func = busyloop_cas64;
+
+ tbox[6].preval = 1048576;
+ tbox[6].postval = 98; /* goto tbox[0] */
+ tbox[6].func = busyloop_xchg64;
+
+ /* get set */
+ for (i = 0; i < NUM_THREADS; i++) {
+ rv = apr_thread_create(&thread[i], NULL, thread_func_busyloop64,
+ &tbox[i], p);
+ ABTS_ASSERT(tc, "Failed creating thread", rv == APR_SUCCESS);
+ }
+
+ /* go! */
+ apr_atomic_set64(tbox->mem, 98);
+
+ for (i = 0; i < NUM_THREADS; i++) {
+ apr_status_t retval;
+ rv = apr_thread_join(&retval, thread[i]);
+ ABTS_ASSERT(tc, "Thread join failed", rv == APR_SUCCESS);
+ ABTS_ASSERT(tc, "Invalid return value from thread_join", retval == 0);
+ }
+
+ ABTS_INT_EQUAL(tbox->tc, 98, count);
+
+ rv = apr_thread_mutex_destroy(thread_lock64);
+ ABTS_ASSERT(tc, "Failed creating threads", rv == APR_SUCCESS);
+}
+
+static void *APR_THREAD_FUNC test_func_set64(apr_thread_t *thd, void *data)
+{
+ int i;
+
+ for (i = 0; i < 1000 * 1000; i++) {
+ apr_atomic_set64(&atomic_ops64, APR_UINT64_C(0x1111222233334444));
+ apr_atomic_set64(&atomic_ops64, APR_UINT64_C(0x4444555566667777));
+ }
+
+ apr_thread_exit(thd, APR_SUCCESS);
+ return NULL;
+}
+
+static void test_atomics_threaded_setread64(abts_case *tc, void *data)
+{
+ apr_status_t retval;
+ apr_thread_t *thread;
+ int i;
+
+ apr_atomic_set64(&atomic_ops64, APR_UINT64_C(0x1111222233334444));
+
+ apr_thread_create(&thread, NULL, test_func_set64, NULL, p);
+
+ for (i = 0; i < 1000 * 1000 * 2; i++) {
+ apr_uint64_t val = apr_atomic_read64(&atomic_ops64);
+
+ if (val != APR_UINT64_C(0x1111222233334444) &&
+ val != APR_UINT64_C(0x4444555566667777))
+ {
+ ABTS_FAIL(tc, "Unexpected value");
+ break;
+ }
+ }
+
+ apr_thread_join(&retval, thread);
+}
+
+#endif /* !APR_HAS_THREADS */
+
+abts_suite *testatomic(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_init, NULL);
+ abts_run_test(suite, test_set32, NULL);
+ abts_run_test(suite, test_read32, NULL);
+ abts_run_test(suite, test_dec32, NULL);
+ abts_run_test(suite, test_xchg32, NULL);
+ abts_run_test(suite, test_xchgptr, NULL);
+ abts_run_test(suite, test_cas_equal, NULL);
+ abts_run_test(suite, test_cas_equal_nonnull, NULL);
+ abts_run_test(suite, test_cas_notequal, NULL);
+ abts_run_test(suite, test_casptr_equal, NULL);
+ abts_run_test(suite, test_casptr_equal_nonnull, NULL);
+ abts_run_test(suite, test_casptr_notequal, NULL);
+ abts_run_test(suite, test_add32, NULL);
+ abts_run_test(suite, test_add32_neg, NULL);
+ abts_run_test(suite, test_inc32, NULL);
+ abts_run_test(suite, test_set_add_inc_sub, NULL);
+ abts_run_test(suite, test_wrap_zero, NULL);
+ abts_run_test(suite, test_inc_neg1, NULL);
+ abts_run_test(suite, test_set64, NULL);
+ abts_run_test(suite, test_read64, NULL);
+ abts_run_test(suite, test_dec64, NULL);
+ abts_run_test(suite, test_xchg64, NULL);
+ abts_run_test(suite, test_add64, NULL);
+ abts_run_test(suite, test_add64_neg, NULL);
+ abts_run_test(suite, test_inc64, NULL);
+ abts_run_test(suite, test_set_add_inc_sub64, NULL);
+ abts_run_test(suite, test_wrap_zero64, NULL);
+ abts_run_test(suite, test_inc_neg164, NULL);
+
+#if APR_HAS_THREADS
+ abts_run_test(suite, test_atomics_threaded, NULL);
+ abts_run_test(suite, test_atomics_threaded64, NULL);
+ abts_run_test(suite, test_atomics_busyloop_threaded, NULL);
+ abts_run_test(suite, test_atomics_busyloop_threaded64, NULL);
+ abts_run_test(suite, test_atomics_threaded_setread64, NULL);
+#endif
+
+ return suite;
+}
+
diff --git a/test/testcond.c b/test/testcond.c
new file mode 100644
index 0000000..b5a20bc
--- /dev/null
+++ b/test/testcond.c
@@ -0,0 +1,670 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_file_io.h"
+#include "apr_thread_proc.h"
+#include "apr_thread_mutex.h"
+#include "apr_thread_cond.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_atomic.h"
+#include "testutil.h"
+
+#define NTHREADS 10
+
+#define ABTS_SUCCESS(rv) ABTS_INT_EQUAL(tc, APR_SUCCESS, rv)
+
+#if APR_HAS_THREADS
+
+typedef struct toolbox_t toolbox_t;
+
+struct toolbox_t {
+ void *data;
+ abts_case *tc;
+ apr_thread_mutex_t *mutex;
+ apr_thread_cond_t *cond;
+ void (*func)(toolbox_t *box);
+};
+
+typedef struct toolbox_fnptr_t toolbox_fnptr_t;
+
+struct toolbox_fnptr_t {
+ void (*func)(toolbox_t *box);
+};
+
+static void lost_signal(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_thread_cond_t *cond = NULL;
+ apr_thread_mutex_t *mutex = NULL;
+
+ rv = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, mutex);
+
+ rv = apr_thread_cond_create(&cond, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, cond);
+
+ rv = apr_thread_cond_signal(cond);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_lock(mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_cond_timedwait(cond, mutex, 10000);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+
+ rv = apr_thread_mutex_unlock(mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_cond_broadcast(cond);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_lock(mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_cond_timedwait(cond, mutex, 10000);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+
+ rv = apr_thread_mutex_unlock(mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_cond_destroy(cond);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_destroy(mutex);
+ ABTS_SUCCESS(rv);
+}
+
+static void *APR_THREAD_FUNC thread_routine(apr_thread_t *thd, void *data)
+{
+ toolbox_t *box = data;
+
+ box->func(box);
+
+ apr_thread_exit(thd, 0);
+
+ return NULL;
+}
+
+static void lock_and_signal(toolbox_t *box)
+{
+ apr_status_t rv;
+ abts_case *tc = box->tc;
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_cond_signal(box->cond);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_unlock(box->mutex);
+ ABTS_SUCCESS(rv);
+}
+
+static void dynamic_binding(abts_case *tc, void *data)
+{
+ unsigned int i;
+ apr_status_t rv;
+ toolbox_t box[NTHREADS];
+ apr_thread_t *thread[NTHREADS];
+ apr_thread_mutex_t *mutex[NTHREADS];
+ apr_thread_cond_t *cond = NULL;
+
+ rv = apr_thread_cond_create(&cond, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, cond);
+
+ for (i = 0; i < NTHREADS; i++) {
+ rv = apr_thread_mutex_create(&mutex[i], APR_THREAD_MUTEX_DEFAULT, p);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_lock(mutex[i]);
+ ABTS_SUCCESS(rv);
+
+ box[i].tc = tc;
+ box[i].cond = cond;
+ box[i].mutex = mutex[i];
+ box[i].func = lock_and_signal;
+
+ rv = apr_thread_create(&thread[i], NULL, thread_routine, &box[i], p);
+ ABTS_SUCCESS(rv);
+ }
+
+ /*
+ * The dynamic binding should be preserved because we use only one waiter
+ */
+
+ for (i = 0; i < NTHREADS; i++) {
+ rv = apr_thread_cond_wait(cond, mutex[i]);
+ ABTS_SUCCESS(rv);
+ }
+
+ for (i = 0; i < NTHREADS; i++) {
+ rv = apr_thread_cond_timedwait(cond, mutex[i], 10000);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+
+ rv = apr_thread_mutex_unlock(mutex[i]);
+ ABTS_SUCCESS(rv);
+ }
+
+ for (i = 0; i < NTHREADS; i++) {
+ apr_status_t retval;
+ rv = apr_thread_join(&retval, thread[i]);
+ ABTS_SUCCESS(rv);
+ }
+
+ rv = apr_thread_cond_destroy(cond);
+ ABTS_SUCCESS(rv);
+
+ for (i = 0; i < NTHREADS; i++) {
+ rv = apr_thread_mutex_destroy(mutex[i]);
+ ABTS_SUCCESS(rv);
+ }
+}
+
+static void lock_and_wait(toolbox_t *box)
+{
+ apr_status_t rv;
+ abts_case *tc = box->tc;
+ apr_uint32_t *count = box->data;
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ apr_atomic_inc32(count);
+
+ rv = apr_thread_cond_wait(box->cond, box->mutex);
+ ABTS_SUCCESS(rv);
+
+ apr_atomic_dec32(count);
+
+ rv = apr_thread_mutex_unlock(box->mutex);
+ ABTS_SUCCESS(rv);
+}
+
+static void broadcast_threads(abts_case *tc, void *data)
+{
+ toolbox_t box;
+ unsigned int i;
+ apr_status_t rv;
+ apr_uint32_t count = 0;
+ apr_thread_cond_t *cond = NULL;
+ apr_thread_mutex_t *mutex = NULL;
+ apr_thread_t *thread[NTHREADS];
+
+ rv = apr_thread_cond_create(&cond, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, cond);
+
+ rv = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, mutex);
+
+ rv = apr_thread_mutex_lock(mutex);
+ ABTS_SUCCESS(rv);
+
+ box.tc = tc;
+ box.data = &count;
+ box.mutex = mutex;
+ box.cond = cond;
+ box.func = lock_and_wait;
+
+ for (i = 0; i < NTHREADS; i++) {
+ rv = apr_thread_create(&thread[i], NULL, thread_routine, &box, p);
+ ABTS_SUCCESS(rv);
+ }
+
+ do {
+ rv = apr_thread_mutex_unlock(mutex);
+ ABTS_SUCCESS(rv);
+ apr_sleep(100000);
+ rv = apr_thread_mutex_lock(mutex);
+ ABTS_SUCCESS(rv);
+ } while (apr_atomic_read32(&count) != NTHREADS);
+
+ rv = apr_thread_cond_broadcast(cond);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_unlock(mutex);
+ ABTS_SUCCESS(rv);
+
+ for (i = 0; i < NTHREADS; i++) {
+ apr_status_t retval;
+ rv = apr_thread_join(&retval, thread[i]);
+ ABTS_SUCCESS(rv);
+ }
+
+ ABTS_INT_EQUAL(tc, 0, count);
+
+ rv = apr_thread_cond_destroy(cond);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_destroy(mutex);
+ ABTS_SUCCESS(rv);
+}
+
+static void nested_lock_and_wait(toolbox_t *box)
+{
+ apr_status_t rv;
+ abts_case *tc = box->tc;
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_cond_wait(box->cond, box->mutex);
+ ABTS_SUCCESS(rv);
+}
+
+static void nested_lock_and_unlock(toolbox_t *box)
+{
+ apr_status_t rv;
+ abts_case *tc = box->tc;
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_cond_timedwait(box->cond, box->mutex, 2000000);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_unlock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_unlock(box->mutex);
+ ABTS_SUCCESS(rv);
+}
+
+static void nested_wait(abts_case *tc, void *data)
+{
+ toolbox_fnptr_t *fnptr = data;
+ toolbox_t box;
+ apr_status_t rv, retval;
+ apr_thread_cond_t *cond = NULL;
+ apr_thread_t *thread = NULL;
+ apr_thread_mutex_t *mutex = NULL;
+
+ rv = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, mutex);
+
+ rv = apr_thread_cond_create(&cond, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, cond);
+
+ rv = apr_thread_mutex_lock(mutex);
+ ABTS_SUCCESS(rv);
+
+ box.tc = tc;
+ box.cond = cond;
+ box.mutex = mutex;
+ box.func = fnptr->func;
+
+ rv = apr_thread_create(&thread, NULL, thread_routine, &box, p);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_unlock(mutex);
+ ABTS_SUCCESS(rv);
+
+ /* yield the processor */
+ apr_sleep(500000);
+
+ rv = apr_thread_cond_signal(cond);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_join(&retval, thread);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_trylock(mutex);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EBUSY(rv));
+
+ rv = apr_thread_mutex_trylock(mutex);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EBUSY(rv));
+}
+
+static volatile apr_uint64_t pipe_count;
+static volatile apr_uint32_t exiting;
+
+static void pipe_consumer(toolbox_t *box)
+{
+ char ch;
+ apr_status_t rv;
+ apr_size_t nbytes;
+ abts_case *tc = box->tc;
+ apr_file_t *out = box->data;
+ apr_uint32_t consumed = 0;
+
+ do {
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ while (!pipe_count && !exiting) {
+ rv = apr_thread_cond_wait(box->cond, box->mutex);
+ ABTS_SUCCESS(rv);
+ }
+
+ if (!pipe_count && exiting) {
+ rv = apr_thread_mutex_unlock(box->mutex);
+ ABTS_SUCCESS(rv);
+ break;
+ }
+
+ pipe_count--;
+ consumed++;
+
+ rv = apr_thread_mutex_unlock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_file_read_full(out, &ch, 1, &nbytes);
+ ABTS_SUCCESS(rv);
+ ABTS_SIZE_EQUAL(tc, 1, nbytes);
+ ABTS_TRUE(tc, ch == '.');
+ } while (1);
+
+ /* naive fairness test - it would be good to introduce or solidify
+ * a solid test to ensure one thread is not starved.
+ * ABTS_INT_EQUAL(tc, 1, !!consumed);
+ */
+}
+
+static void pipe_write(toolbox_t *box, char ch)
+{
+ apr_status_t rv;
+ apr_size_t nbytes;
+ abts_case *tc = box->tc;
+ apr_file_t *in = box->data;
+
+ rv = apr_file_write_full(in, &ch, 1, &nbytes);
+ ABTS_SUCCESS(rv);
+ ABTS_SIZE_EQUAL(tc, 1, nbytes);
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ if (!pipe_count) {
+ rv = apr_thread_cond_signal(box->cond);
+ ABTS_SUCCESS(rv);
+ }
+
+ pipe_count++;
+
+ rv = apr_thread_mutex_unlock(box->mutex);
+ ABTS_SUCCESS(rv);
+}
+
+static void pipe_producer(toolbox_t *box)
+{
+ apr_uint32_t loop = 500;
+
+ do {
+ pipe_write(box, '.');
+ } while (loop--);
+}
+
+static void pipe_producer_consumer(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ toolbox_t boxcons, boxprod;
+ apr_thread_t *thread[NTHREADS];
+ apr_thread_cond_t *cond = NULL;
+ apr_thread_mutex_t *mutex = NULL;
+ apr_file_t *in = NULL, *out = NULL;
+ apr_uint32_t i, ncons = (apr_uint32_t)(NTHREADS * 0.70);
+
+ rv = apr_file_pipe_create(&in, &out, p);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, mutex);
+
+ rv = apr_thread_cond_create(&cond, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, cond);
+
+ boxcons.tc = tc;
+ boxcons.data = in;
+ boxcons.mutex = mutex;
+ boxcons.cond = cond;
+ boxcons.func = pipe_consumer;
+
+ for (i = 0; i < ncons; i++) {
+ rv = apr_thread_create(&thread[i], NULL, thread_routine, &boxcons, p);
+ ABTS_SUCCESS(rv);
+ }
+
+ boxprod.tc = tc;
+ boxprod.data = out;
+ boxprod.mutex = mutex;
+ boxprod.cond = cond;
+ boxprod.func = pipe_producer;
+
+ for (; i < NTHREADS; i++) {
+ rv = apr_thread_create(&thread[i], NULL, thread_routine, &boxprod, p);
+ ABTS_SUCCESS(rv);
+ }
+
+ for (i = ncons; i < NTHREADS; i++) {
+ apr_status_t retval;
+ rv = apr_thread_join(&retval, thread[i]);
+ ABTS_SUCCESS(rv);
+ }
+
+ rv = apr_thread_mutex_lock(mutex);
+ ABTS_SUCCESS(rv);
+
+ exiting = 1;
+
+ rv = apr_thread_cond_broadcast(cond);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_unlock(mutex);
+ ABTS_SUCCESS(rv);
+
+ for (i = 0; i < ncons; i++) {
+ apr_status_t retval;
+ rv = apr_thread_join(&retval, thread[i]);
+ ABTS_SUCCESS(rv);
+ }
+
+ rv = apr_thread_cond_destroy(cond);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_destroy(mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_file_close(in);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_file_close(out);
+ ABTS_SUCCESS(rv);
+}
+
+volatile enum {
+ TOSS,
+ PING,
+ PONG,
+ OVER
+} state;
+
+static void ping(toolbox_t *box)
+{
+ apr_status_t rv;
+ abts_case *tc = box->tc;
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ if (state == TOSS)
+ state = PING;
+
+ do {
+ rv = apr_thread_cond_signal(box->cond);
+ ABTS_SUCCESS(rv);
+
+ state = PONG;
+
+ rv = apr_thread_cond_wait(box->cond, box->mutex);
+ ABTS_SUCCESS(rv);
+
+ ABTS_TRUE(tc, state == PING || state == OVER);
+ } while (state != OVER);
+
+ rv = apr_thread_mutex_unlock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_cond_broadcast(box->cond);
+ ABTS_SUCCESS(rv);
+}
+
+static void pong(toolbox_t *box)
+{
+ apr_status_t rv;
+ abts_case *tc = box->tc;
+
+ rv = apr_thread_mutex_lock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ if (state == TOSS)
+ state = PONG;
+
+ do {
+ rv = apr_thread_cond_signal(box->cond);
+ ABTS_SUCCESS(rv);
+
+ state = PING;
+
+ rv = apr_thread_cond_wait(box->cond, box->mutex);
+ ABTS_SUCCESS(rv);
+
+ ABTS_TRUE(tc, state == PONG || state == OVER);
+ } while (state != OVER);
+
+ rv = apr_thread_mutex_unlock(box->mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_cond_broadcast(box->cond);
+ ABTS_SUCCESS(rv);
+}
+
+static void ping_pong(abts_case *tc, void *data)
+{
+ apr_status_t rv, retval;
+ toolbox_t box_ping, box_pong;
+ apr_thread_cond_t *cond = NULL;
+ apr_thread_mutex_t *mutex = NULL;
+ apr_thread_t *thr_ping = NULL, *thr_pong = NULL;
+
+ rv = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, mutex);
+
+ rv = apr_thread_cond_create(&cond, p);
+ ABTS_SUCCESS(rv);
+ ABTS_PTR_NOTNULL(tc, cond);
+
+ rv = apr_thread_mutex_lock(mutex);
+ ABTS_SUCCESS(rv);
+
+ box_ping.tc = tc;
+ box_ping.data = NULL;
+ box_ping.mutex = mutex;
+ box_ping.cond = cond;
+ box_ping.func = ping;
+
+ rv = apr_thread_create(&thr_ping, NULL, thread_routine, &box_ping, p);
+ ABTS_SUCCESS(rv);
+
+ box_pong.tc = tc;
+ box_pong.data = NULL;
+ box_pong.mutex = mutex;
+ box_pong.cond = cond;
+ box_pong.func = pong;
+
+ rv = apr_thread_create(&thr_pong, NULL, thread_routine, &box_pong, p);
+ ABTS_SUCCESS(rv);
+
+ state = TOSS;
+
+ rv = apr_thread_mutex_unlock(mutex);
+ ABTS_SUCCESS(rv);
+
+ apr_sleep(3000000);
+
+ rv = apr_thread_mutex_lock(mutex);
+ ABTS_SUCCESS(rv);
+
+ state = OVER;
+
+ rv = apr_thread_mutex_unlock(mutex);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_join(&retval, thr_ping);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_join(&retval, thr_pong);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_cond_destroy(cond);
+ ABTS_SUCCESS(rv);
+
+ rv = apr_thread_mutex_destroy(mutex);
+ ABTS_SUCCESS(rv);
+}
+#endif /* !APR_HAS_THREADS */
+
+#if !APR_HAS_THREADS
+static void threads_not_impl(abts_case *tc, void *data)
+{
+ ABTS_NOT_IMPL(tc, "Threads not implemented on this platform");
+}
+#endif
+
+abts_suite *testcond(abts_suite *suite)
+{
+#if APR_HAS_THREADS
+ toolbox_fnptr_t fnptr;
+#endif
+ suite = ADD_SUITE(suite)
+
+#if !APR_HAS_THREADS
+ abts_run_test(suite, threads_not_impl, NULL);
+#else
+ abts_run_test(suite, lost_signal, NULL);
+ abts_run_test(suite, dynamic_binding, NULL);
+ abts_run_test(suite, broadcast_threads, NULL);
+ fnptr.func = nested_lock_and_wait;
+ abts_run_test(suite, nested_wait, &fnptr);
+ fnptr.func = nested_lock_and_unlock;
+ abts_run_test(suite, nested_wait, &fnptr);
+ abts_run_test(suite, pipe_producer_consumer, NULL);
+ abts_run_test(suite, ping_pong, NULL);
+#endif
+
+ return suite;
+}
diff --git a/test/testdir.c b/test/testdir.c
new file mode 100644
index 0000000..ac1bcfa
--- /dev/null
+++ b/test/testdir.c
@@ -0,0 +1,399 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_thread_proc.h"
+#include "testutil.h"
+
+static void test_mkdir(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_finfo_t finfo;
+
+ rv = apr_dir_make("data/testdir", APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_stat(&finfo, "data/testdir", APR_FINFO_TYPE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, APR_DIR, finfo.filetype);
+}
+
+static void test_mkdir_recurs(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_finfo_t finfo;
+
+ rv = apr_dir_make_recursive("data/one/two/three",
+ APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_stat(&finfo, "data/one", APR_FINFO_TYPE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, APR_DIR, finfo.filetype);
+
+ rv = apr_stat(&finfo, "data/one/two", APR_FINFO_TYPE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, APR_DIR, finfo.filetype);
+
+ rv = apr_stat(&finfo, "data/one/two/three", APR_FINFO_TYPE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, APR_DIR, finfo.filetype);
+}
+
+struct thread_data
+{
+ abts_case *tc;
+ apr_pool_t *pool;
+};
+
+static void *APR_THREAD_FUNC thread_mkdir_func(apr_thread_t *thd, void *data)
+{
+ struct thread_data *td = data;
+ apr_status_t s1, s2, s3, s4, s5;
+
+ s1 = apr_dir_make_recursive("data/prll/one/thwo/three",
+ APR_FPROT_UREAD | APR_FPROT_UWRITE | APR_FPROT_UEXECUTE,
+ td->pool);
+ s2 = apr_dir_make_recursive("data/prll/four/five/six/seven/eight",
+ APR_FPROT_UREAD | APR_FPROT_UWRITE | APR_FPROT_UEXECUTE,
+ td->pool);
+ s3 = apr_dir_make_recursive("data/prll/nine/ten",
+ APR_FPROT_UREAD | APR_FPROT_UWRITE | APR_FPROT_UEXECUTE,
+ td->pool);
+ s4 = apr_dir_make_recursive("data/prll/11/12/13/14/15/16/17/18/19/20",
+ APR_FPROT_UREAD | APR_FPROT_UWRITE | APR_FPROT_UEXECUTE,
+ td->pool);
+ s5 = apr_dir_make_recursive("data/fortytwo",
+ APR_FPROT_UREAD | APR_FPROT_UWRITE | APR_FPROT_UEXECUTE,
+ td->pool);
+
+ ABTS_INT_EQUAL(td->tc, APR_SUCCESS, s1);
+ ABTS_INT_EQUAL(td->tc, APR_SUCCESS, s2);
+ ABTS_INT_EQUAL(td->tc, APR_SUCCESS, s3);
+ ABTS_INT_EQUAL(td->tc, APR_SUCCESS, s4);
+ ABTS_INT_EQUAL(td->tc, APR_SUCCESS, s5);
+ return NULL;
+}
+
+static void test_mkdir_recurs_parallel(abts_case *tc, void *data)
+{
+ struct thread_data td1, td2, td3, td4;
+ apr_thread_t *t1, *t2, *t3, *t4;
+ apr_status_t s1, s2, s3, s4;
+
+ td1.tc = td2.tc = td3.tc = td4.tc = tc;
+ apr_pool_create(&td1.pool, p);
+ apr_pool_create(&td2.pool, p);
+ apr_pool_create(&td3.pool, p);
+ apr_pool_create(&td4.pool, p);
+
+ s1 = apr_thread_create(&t1, NULL, thread_mkdir_func, &td1, td1.pool);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s1);
+ s2 = apr_thread_create(&t2, NULL, thread_mkdir_func, &td2, td2.pool);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s2);
+ s3 = apr_thread_create(&t3, NULL, thread_mkdir_func, &td3, td3.pool);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s3);
+ s4 = apr_thread_create(&t4, NULL, thread_mkdir_func, &td4, td4.pool);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s4);
+
+ apr_thread_join(&s1, t1);
+ apr_thread_join(&s2, t2);
+ apr_thread_join(&s3, t3);
+ apr_thread_join(&s4, t4);
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s1);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s2);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s3);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s4);
+}
+
+static void test_remove(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_finfo_t finfo;
+
+ rv = apr_dir_remove("data/testdir", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_stat(&finfo, "data/testdir", APR_FINFO_TYPE, p);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));
+}
+
+static void test_removeall_fail(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_dir_remove("data/one", p);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOTEMPTY(rv));
+}
+
+static void test_removeall(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_dir_remove("data/one/two/three", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/one/two", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/one", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/one/thwo/three", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/one/thwo", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/one", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/four/five/six/seven/eight", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/four/five/six/seven", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/four/five/six", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/four/five", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/four", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/nine/ten", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/nine", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/11/12/13/14/15/16/17/18/19/20", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/11/12/13/14/15/16/17/18/19", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/11/12/13/14/15/16/17/18", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/11/12/13/14/15/16/17", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/11/12/13/14/15/16", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/11/12/13/14/15", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/11/12/13/14", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/11/12/13", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/11/12", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll/11", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/prll", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_remove("data/fortytwo", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_remove_notthere(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_dir_remove("data/notthere", p);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));
+}
+
+static void test_mkdir_twice(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_dir_make("data/testdir", APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_dir_make("data/testdir", APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EEXIST(rv));
+
+ rv = apr_dir_remove("data/testdir", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_opendir(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_dir_t *dir;
+
+ rv = apr_dir_open(&dir, "data", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ apr_dir_close(dir);
+}
+
+static void test_opendir_notthere(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_dir_t *dir;
+
+ rv = apr_dir_open(&dir, "notthere", p);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));
+}
+
+static void test_closedir(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_dir_t *dir;
+
+ rv = apr_dir_open(&dir, "data", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_dir_close(dir);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_rewind(abts_case *tc, void *data)
+{
+ apr_dir_t *dir;
+ apr_finfo_t first, second;
+
+ APR_ASSERT_SUCCESS(tc, "apr_dir_open failed", apr_dir_open(&dir, "data", p));
+
+ APR_ASSERT_SUCCESS(tc, "apr_dir_read failed",
+ apr_dir_read(&first, APR_FINFO_DIRENT, dir));
+
+ APR_ASSERT_SUCCESS(tc, "apr_dir_rewind failed", apr_dir_rewind(dir));
+
+ APR_ASSERT_SUCCESS(tc, "second apr_dir_read failed",
+ apr_dir_read(&second, APR_FINFO_DIRENT, dir));
+
+ APR_ASSERT_SUCCESS(tc, "apr_dir_close failed", apr_dir_close(dir));
+
+ ABTS_STR_EQUAL(tc, first.name, second.name);
+}
+
+/* Test for a (fixed) bug in apr_dir_read(). This bug only happened
+ in threadless cases. */
+static void test_uncleared_errno(abts_case *tc, void *data)
+{
+ apr_file_t *thefile = NULL;
+ apr_finfo_t finfo;
+ apr_int32_t finfo_flags = APR_FINFO_TYPE | APR_FINFO_NAME;
+ apr_dir_t *this_dir;
+ apr_status_t rv;
+
+ rv = apr_dir_make("dir1", APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_dir_make("dir2", APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_open(&thefile, "dir1/file1",
+ APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE, APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_close(thefile);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* Try to remove dir1. This should fail because it's not empty.
+ However, on a platform with threads disabled (such as FreeBSD),
+ `errno' will be set as a result. */
+ rv = apr_dir_remove("dir1", p);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOTEMPTY(rv));
+
+ /* Read `.' and `..' out of dir2. */
+ rv = apr_dir_open(&this_dir, "dir2", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_dir_read(&finfo, finfo_flags, this_dir);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_dir_read(&finfo, finfo_flags, this_dir);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* Now, when we attempt to do a third read of empty dir2, and the
+ underlying system readdir() returns NULL, the old value of
+ errno shouldn't cause a false alarm. We should get an ENOENT
+ back from apr_dir_read, and *not* the old errno. */
+ rv = apr_dir_read(&finfo, finfo_flags, this_dir);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));
+
+ rv = apr_dir_close(this_dir);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* Cleanup */
+ rv = apr_file_remove("dir1/file1", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_dir_remove("dir1", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_dir_remove("dir2", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+}
+
+static void test_rmkdir_nocwd(abts_case *tc, void *data)
+{
+ char *cwd, *path;
+
+ APR_ASSERT_SUCCESS(tc, "make temp dir",
+ apr_dir_make("dir3", APR_OS_DEFAULT, p));
+
+ APR_ASSERT_SUCCESS(tc, "obtain cwd", apr_filepath_get(&cwd, 0, p));
+
+ APR_ASSERT_SUCCESS(tc, "determine path to temp dir",
+ apr_filepath_merge(&path, cwd, "dir3", 0, p));
+
+ APR_ASSERT_SUCCESS(tc, "change to temp dir", apr_filepath_set(path, p));
+
+ APR_ASSERT_SUCCESS(tc, "restore cwd", apr_filepath_set(cwd, p));
+
+ APR_ASSERT_SUCCESS(tc, "remove cwd", apr_dir_remove(path, p));
+}
+
+
+abts_suite *testdir(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_mkdir, NULL);
+ abts_run_test(suite, test_mkdir_recurs, NULL);
+ abts_run_test(suite, test_mkdir_recurs_parallel, NULL);
+ abts_run_test(suite, test_remove, NULL);
+ abts_run_test(suite, test_removeall_fail, NULL);
+ abts_run_test(suite, test_removeall, NULL);
+ abts_run_test(suite, test_remove_notthere, NULL);
+ abts_run_test(suite, test_mkdir_twice, NULL);
+ abts_run_test(suite, test_rmkdir_nocwd, NULL);
+
+ abts_run_test(suite, test_rewind, NULL);
+
+ abts_run_test(suite, test_opendir, NULL);
+ abts_run_test(suite, test_opendir_notthere, NULL);
+ abts_run_test(suite, test_closedir, NULL);
+ abts_run_test(suite, test_uncleared_errno, NULL);
+
+ return suite;
+}
+
diff --git a/test/testdll.dsp b/test/testdll.dsp
new file mode 100644
index 0000000..8c8aa7f
--- /dev/null
+++ b/test/testdll.dsp
@@ -0,0 +1,446 @@
+# Microsoft Developer Studio Project File - Name="testdll" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) External Target" 0x0106
+
+CFG=testdll - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "testdll.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "testdll.mak" CFG="testdll - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "testdll - Win32 Release" (based on "Win32 (x86) External Target")
+!MESSAGE "testdll - Win32 Debug" (based on "Win32 (x86) External Target")
+!MESSAGE "testdll - Win32 Release9x" (based on "Win32 (x86) External Target")
+!MESSAGE "testdll - Win32 Debug9x" (based on "Win32 (x86) External Target")
+!MESSAGE "testdll - x64 Release" (based on "Win32 (x86) External Target")
+!MESSAGE "testdll - x64 Debug" (based on "Win32 (x86) External Target")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+
+!IF "$(CFG)" == "testdll - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=Release OUTDIR=Release MODEL=dynamic all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "Release\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=Release OUTDIR=Release MODEL=dynamic all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "Release\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "testdll - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=Debug OUTDIR=Debug MODEL=dynamic _DEBUG=1 all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "Debug\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=Debug OUTDIR=Debug MODEL=dynamic _DEBUG=1 all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "Debug\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "testdll - Win32 Release9x"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=9x\Release OUTDIR=9x\Release MODEL=dynamic all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "9x\Release\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=9x\Release OUTDIR=9x\Release MODEL=dynamic all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "9x\Release\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "testdll - Win32 Debug9x"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=9x\Debug OUTDIR=9x\Debug MODEL=dynamic _DEBUG=1 all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "9x\Debug\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=9x\Debug OUTDIR=9x\Debug MODEL=dynamic _DEBUG=1 all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "9x\Debug\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "testdll - x64 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=x64\Release OUTDIR=x64\Release MODEL=dynamic all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "x64\Release\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=x64\Release OUTDIR=x64\Release MODEL=dynamic all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "x64\Release\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "testdll - x64 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=x64\Debug OUTDIR=x64\Debug MODEL=dynamic _DEBUG=1 all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "x64\Debug\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=x64\Debug OUTDIR=x64\Debug MODEL=dynamic _DEBUG=1 all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "x64\Debug\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ENDIF
+
+# Begin Target
+
+# Name "testdll - Win32 Release"
+# Name "testdll - Win32 Debug"
+# Name "testdll - Win32 Release9x"
+# Name "testdll - Win32 Debug9x"
+# Name "testdll - x64 Release"
+# Name "testdll - x64 Debug"
+# Begin Group "testall Source Files"
+
+# PROP Default_Filter ".c"
+# Begin Source File
+
+SOURCE=.\abts.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\abts.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\abts_tests.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testapp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testargs.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testatomic.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testcond.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testdir.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testdso.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testdup.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testencode.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testenv.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testfilecopy.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testfileinfo.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testflock.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testflock.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testfmt.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testfnmatch.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testglobalmutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testglobalmutex.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testhash.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testipsub.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testlfs.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testlock.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testmmap.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testnames.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testoc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testpath.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testpipe.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testpoll.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testpools.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testproc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testrand.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testshm.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testshm.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsleep.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsock.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsock.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsockets.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsockopt.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\teststr.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\teststrnatcmp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testtable.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testtemp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testthread.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testtime.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testud.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testuser.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testutil.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testutil.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testvsn.c
+# End Source File
+# End Group
+# Begin Group "Other Source Files"
+
+# PROP Default_Filter ".c"
+# Begin Source File
+
+SOURCE=.\globalmutexchild.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_test.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\nw_misc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\occhild.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\proc_child.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\readchild.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sendfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sockchild.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testlockperf.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testmutexscope.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testprocmutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testshmconsumer.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testshmproducer.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\tryread.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\Makefile.win
+# End Source File
+# End Target
+# End Project
diff --git a/test/testdso.c b/test/testdso.c
new file mode 100644
index 0000000..0d9f27b
--- /dev/null
+++ b/test/testdso.c
@@ -0,0 +1,263 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "apr.h"
+#include "testutil.h"
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_errno.h"
+#include "apr_dso.h"
+#include "apr_strings.h"
+#include "apr_file_info.h"
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if APR_HAS_DSO
+
+#ifdef NETWARE
+# define MOD_NAME "mod_test.nlm"
+#elif defined(BEOS) || defined(__MVS__)
+# define MOD_NAME "mod_test.so"
+#elif defined(WIN32)
+# define MOD_NAME TESTBINPATH "mod_test.dll"
+#elif defined(DARWIN)
+# define MOD_NAME ".libs/mod_test.so"
+# define LIB_NAME ".libs/libmod_test.dylib"
+#elif (defined(__hpux__) || defined(__hpux)) && !defined(__ia64)
+# define MOD_NAME ".libs/mod_test.sl"
+# define LIB_NAME ".libs/libmod_test.sl"
+#elif defined(_AIX) || defined(__bsdi__)
+# define MOD_NAME ".libs/libmod_test.so"
+# define LIB_NAME ".libs/libmod_test.so"
+#else /* Every other Unix */
+# define MOD_NAME ".libs/mod_test.so"
+# define LIB_NAME ".libs/libmod_test.so"
+#endif
+
+static char *modname;
+
+static void test_load_module(abts_case *tc, void *data)
+{
+ apr_dso_handle_t *h = NULL;
+ apr_status_t status;
+ char errstr[256];
+
+ status = apr_dso_load(&h, modname, p);
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_dso_unload(h);
+}
+
+static void test_dso_sym(abts_case *tc, void *data)
+{
+ apr_dso_handle_t *h = NULL;
+ apr_dso_handle_sym_t func1 = NULL;
+ apr_status_t status;
+ void (*function)(char str[256]);
+ char teststr[256];
+ char errstr[256];
+
+ status = apr_dso_load(&h, modname, p);
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ status = apr_dso_sym(&func1, h, "print_hello");
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, func1);
+
+ if (!tc->failed) {
+ function = (void (*)(char *))func1;
+ (*function)(teststr);
+ ABTS_STR_EQUAL(tc, "Hello - I'm a DSO!\n", teststr);
+ }
+
+ apr_dso_unload(h);
+}
+
+static void test_dso_sym_return_value(abts_case *tc, void *data)
+{
+ apr_dso_handle_t *h = NULL;
+ apr_dso_handle_sym_t func1 = NULL;
+ apr_status_t status;
+ int (*function)(int);
+ char errstr[256];
+
+ status = apr_dso_load(&h, modname, p);
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ status = apr_dso_sym(&func1, h, "count_reps");
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, func1);
+
+ if (!tc->failed) {
+ function = (int (*)(int))func1;
+ status = (*function)(5);
+ ABTS_INT_EQUAL(tc, 5, status);
+ }
+
+ apr_dso_unload(h);
+}
+
+static void test_unload_module(abts_case *tc, void *data)
+{
+ apr_dso_handle_t *h = NULL;
+ apr_status_t status;
+ char errstr[256];
+ apr_dso_handle_sym_t func1 = NULL;
+
+ status = apr_dso_load(&h, modname, p);
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ status = apr_dso_unload(h);
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+
+ status = apr_dso_sym(&func1, h, "print_hello");
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ESYMNOTFOUND(status));
+}
+
+
+#ifdef LIB_NAME
+static char *libname;
+
+static void test_load_library(abts_case *tc, void *data)
+{
+ apr_dso_handle_t *h = NULL;
+ apr_status_t status;
+ char errstr[256];
+
+ status = apr_dso_load(&h, libname, p);
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_dso_unload(h);
+}
+
+static void test_dso_sym_library(abts_case *tc, void *data)
+{
+ apr_dso_handle_t *h = NULL;
+ apr_dso_handle_sym_t func1 = NULL;
+ apr_status_t status;
+ void (*function)(char str[256]);
+ char teststr[256];
+ char errstr[256];
+
+ status = apr_dso_load(&h, libname, p);
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ status = apr_dso_sym(&func1, h, "print_hello");
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, func1);
+
+ if (!tc->failed) {
+ function = (void (*)(char *))func1;
+ (*function)(teststr);
+ ABTS_STR_EQUAL(tc, "Hello - I'm a DSO!\n", teststr);
+ }
+
+ apr_dso_unload(h);
+}
+
+static void test_dso_sym_return_value_library(abts_case *tc, void *data)
+{
+ apr_dso_handle_t *h = NULL;
+ apr_dso_handle_sym_t func1 = NULL;
+ apr_status_t status;
+ int (*function)(int);
+ char errstr[256];
+
+ status = apr_dso_load(&h, libname, p);
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ status = apr_dso_sym(&func1, h, "count_reps");
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, func1);
+
+ if (!tc->failed) {
+ function = (int (*)(int))func1;
+ status = (*function)(5);
+ ABTS_INT_EQUAL(tc, 5, status);
+ }
+
+ apr_dso_unload(h);
+}
+
+static void test_unload_library(abts_case *tc, void *data)
+{
+ apr_dso_handle_t *h = NULL;
+ apr_status_t status;
+ char errstr[256];
+ apr_dso_handle_sym_t func1 = NULL;
+
+ status = apr_dso_load(&h, libname, p);
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ status = apr_dso_unload(h);
+ ABTS_ASSERT(tc, apr_dso_error(h, errstr, 256), APR_SUCCESS == status);
+
+ status = apr_dso_sym(&func1, h, "print_hello");
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ESYMNOTFOUND(status));
+}
+
+#endif /* def(LIB_NAME) */
+
+static void test_load_notthere(abts_case *tc, void *data)
+{
+ apr_dso_handle_t *h = NULL;
+ apr_status_t status;
+
+ status = apr_dso_load(&h, "No_File.so", p);
+
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EDSOOPEN(status));
+ ABTS_PTR_NOTNULL(tc, h);
+}
+
+#endif /* APR_HAS_DSO */
+
+abts_suite *testdso(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+#if APR_HAS_DSO
+ apr_filepath_merge(&modname, NULL, MOD_NAME, 0, p);
+
+ abts_run_test(suite, test_load_module, NULL);
+ abts_run_test(suite, test_dso_sym, NULL);
+ abts_run_test(suite, test_dso_sym_return_value, NULL);
+ abts_run_test(suite, test_unload_module, NULL);
+
+#ifdef LIB_NAME
+ apr_filepath_merge(&libname, NULL, LIB_NAME, 0, p);
+
+ abts_run_test(suite, test_load_library, NULL);
+ abts_run_test(suite, test_dso_sym_library, NULL);
+ abts_run_test(suite, test_dso_sym_return_value_library, NULL);
+ abts_run_test(suite, test_unload_library, NULL);
+#endif
+
+ abts_run_test(suite, test_load_notthere, NULL);
+#endif /* APR_HAS_DSO */
+
+ return suite;
+}
+
diff --git a/test/testdup.c b/test/testdup.c
new file mode 100644
index 0000000..fd88f22
--- /dev/null
+++ b/test/testdup.c
@@ -0,0 +1,198 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_errno.h"
+#include "apr_file_io.h"
+#include "testutil.h"
+
+#define TEST "Testing\n"
+#define TEST2 "Testing again\n"
+#define FILEPATH "data/"
+
+static void test_file_dup(abts_case *tc, void *data)
+{
+ apr_file_t *file1 = NULL;
+ apr_file_t *file3 = NULL;
+ apr_status_t rv;
+ apr_finfo_t finfo;
+
+ /* First, create a new file, empty... */
+ rv = apr_file_open(&file1, FILEPATH "testdup.file",
+ APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE |
+ APR_FOPEN_DELONCLOSE, APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, file1);
+
+ rv = apr_file_dup(&file3, file1, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, file3);
+
+ rv = apr_file_close(file1);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* cleanup after ourselves */
+ rv = apr_file_close(file3);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_stat(&finfo, FILEPATH "testdup.file", APR_FINFO_NORM, p);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));
+}
+
+static void test_file_readwrite(abts_case *tc, void *data)
+{
+ apr_file_t *file1 = NULL;
+ apr_file_t *file3 = NULL;
+ apr_status_t rv;
+ apr_finfo_t finfo;
+ apr_size_t txtlen = sizeof(TEST);
+ char buff[50];
+ apr_off_t fpos;
+
+ /* First, create a new file, empty... */
+ rv = apr_file_open(&file1, FILEPATH "testdup.readwrite.file",
+ APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE |
+ APR_FOPEN_DELONCLOSE, APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, file1);
+
+ rv = apr_file_dup(&file3, file1, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, file3);
+
+ rv = apr_file_write(file3, TEST, &txtlen);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, sizeof(TEST), txtlen);
+
+ fpos = 0;
+ rv = apr_file_seek(file1, APR_SET, &fpos);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "File position mismatch, expected 0", fpos == 0);
+
+ txtlen = 50;
+ rv = apr_file_read(file1, buff, &txtlen);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, TEST, buff);
+
+ /* cleanup after ourselves */
+ rv = apr_file_close(file1);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_close(file3);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_stat(&finfo, FILEPATH "testdup.readwrite.file", APR_FINFO_NORM, p);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));
+}
+
+static void test_dup2(abts_case *tc, void *data)
+{
+ apr_file_t *testfile = NULL;
+ apr_file_t *errfile = NULL;
+ apr_file_t *saveerr = NULL;
+ apr_status_t rv;
+
+ rv = apr_file_open(&testfile, FILEPATH "testdup2.file",
+ APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE |
+ APR_FOPEN_DELONCLOSE, APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, testfile);
+
+ rv = apr_file_open_stderr(&errfile, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* Set aside the real errfile */
+ rv = apr_file_dup(&saveerr, errfile, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, saveerr);
+
+ rv = apr_file_dup2(errfile, testfile, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, errfile);
+
+ apr_file_close(testfile);
+
+ rv = apr_file_dup2(errfile, saveerr, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, errfile);
+
+ apr_file_close(saveerr);
+}
+
+static void test_dup2_readwrite(abts_case *tc, void *data)
+{
+ apr_file_t *errfile = NULL;
+ apr_file_t *testfile = NULL;
+ apr_file_t *saveerr = NULL;
+ apr_status_t rv;
+ apr_size_t txtlen = sizeof(TEST);
+ char buff[50];
+ apr_off_t fpos;
+
+ rv = apr_file_open(&testfile, FILEPATH "testdup2.readwrite.file",
+ APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE |
+ APR_FOPEN_DELONCLOSE, APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, testfile);
+
+ rv = apr_file_open_stderr(&errfile, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* Set aside the real errfile */
+ rv = apr_file_dup(&saveerr, errfile, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, saveerr);
+
+ rv = apr_file_dup2(errfile, testfile, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, errfile);
+
+ txtlen = sizeof(TEST2);
+ rv = apr_file_write(errfile, TEST2, &txtlen);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, sizeof(TEST2), txtlen);
+
+ fpos = 0;
+ rv = apr_file_seek(testfile, APR_SET, &fpos);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "File position mismatch, expected 0", fpos == 0);
+
+ txtlen = 50;
+ rv = apr_file_read(testfile, buff, &txtlen);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, TEST2, buff);
+
+ apr_file_close(testfile);
+
+ rv = apr_file_dup2(errfile, saveerr, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, errfile);
+
+ apr_file_close(saveerr);
+}
+
+abts_suite *testdup(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_file_dup, NULL);
+ abts_run_test(suite, test_file_readwrite, NULL);
+ abts_run_test(suite, test_dup2, NULL);
+ abts_run_test(suite, test_dup2_readwrite, NULL);
+
+ return suite;
+}
+
diff --git a/test/testencode.c b/test/testencode.c
new file mode 100644
index 0000000..6d78310
--- /dev/null
+++ b/test/testencode.c
@@ -0,0 +1,1124 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "apr_encode.h"
+#include "apr_strings.h"
+
+#include "abts.h"
+#include "testutil.h"
+
+static const unsigned char ufoobar[] = { 'f', 'o', 'o', 'b', 'a', 'r' };
+
+static void test_encode_base64(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *src, *target;
+ const char *dest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ /*
+ * Test vectors from https://tools.ietf.org/html/rfc4648#section-10
+ */
+ src = "";
+ target = "";
+ dest = apr_pencode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "f";
+ target = "Zg==";
+ dest = apr_pencode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "f";
+ target = "Zg";
+ dest = apr_pencode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "fo";
+ target = "Zm8=";
+ dest = apr_pencode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "fo";
+ target = "Zm8";
+ dest = apr_pencode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foo";
+ target = "Zm9v";
+ dest = apr_pencode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foo";
+ target = "Zm9v";
+ dest = apr_pencode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ apr_pool_destroy(pool);
+}
+
+static void test_encode_base64_binary(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *target;
+ const char *dest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ /*
+ * Test vectors from https://tools.ietf.org/html/rfc4648#section-10
+ */
+ target = "";
+ dest = apr_pencode_base64_binary(pool, ufoobar, 0, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "Zg==";
+ dest = apr_pencode_base64_binary(pool, ufoobar, 1, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "Zg";
+ dest = apr_pencode_base64_binary(pool, ufoobar, 1, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "Zm8=";
+ dest = apr_pencode_base64_binary(pool, ufoobar, 2, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "Zm8";
+ dest = apr_pencode_base64_binary(pool, ufoobar, 2, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "Zm9v";
+ dest = apr_pencode_base64_binary(pool, ufoobar, 3, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "Zm9v";
+ dest = apr_pencode_base64_binary(pool, ufoobar, 3, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ apr_pool_destroy(pool);
+}
+
+static void test_decode_base64(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *target, *src;
+ const char *dest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ /*
+ * Test vectors from https://tools.ietf.org/html/rfc4648#section-10
+ */
+ src = "";
+ target = "";
+ dest = apr_pdecode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "Zg==";
+ target = "f";
+ dest = apr_pdecode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "Zg=";
+ target = "f";
+ dest = apr_pdecode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "Zg";
+ target = "f";
+ dest = apr_pdecode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "Zm8=";
+ target = "fo";
+ dest = apr_pdecode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "Zm8";
+ target = "fo";
+ dest = apr_pdecode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "Zm9v";
+ target = "foo";
+ dest = apr_pdecode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "Zm9v";
+ target = "foo";
+ dest = apr_pdecode_base64(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ apr_pool_destroy(pool);
+}
+
+static void test_decode_base64_binary(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *src;
+ const unsigned char *udest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ /*
+ * Test vectors from https://tools.ietf.org/html/rfc4648#section-10
+ */
+ src = "";
+ udest = apr_pdecode_base64_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base64_binary target!=dest", memcmp(ufoobar, udest, 0) == 0);
+ ABTS_INT_EQUAL(tc, len, 0);
+
+ src = "Zg==";
+ udest = apr_pdecode_base64_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base64_binary target!=dest", memcmp(ufoobar, udest, 1) == 0);
+ ABTS_INT_EQUAL(tc, len, 1);
+
+ src = "Zg=";
+ udest = apr_pdecode_base64_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base64_binary target!=dest", memcmp(ufoobar, udest, 1) == 0);
+ ABTS_INT_EQUAL(tc, len, 1);
+
+ src = "Zg";
+ udest = apr_pdecode_base64_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base64_binary target!=dest", memcmp(ufoobar, udest, 1) == 0);
+ ABTS_INT_EQUAL(tc, len, 1);
+
+ src = "Zm8=";
+ udest = apr_pdecode_base64_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base64_binary target!=dest", memcmp(ufoobar, udest, 2) == 0);
+ ABTS_INT_EQUAL(tc, len, 2);
+
+ src = "Zm8";
+ udest = apr_pdecode_base64_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base64_binary target!=dest", memcmp(ufoobar, udest, 2) == 0);
+ ABTS_INT_EQUAL(tc, len, 2);
+
+ src = "Zm9v";
+ udest = apr_pdecode_base64_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base64_binary target!=dest", memcmp(ufoobar, udest, 3) == 0);
+ ABTS_INT_EQUAL(tc, len, 3);
+
+ src = "Zm9v";
+ udest = apr_pdecode_base64_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base64_binary target!=dest", memcmp(ufoobar, udest, 3) == 0);
+ ABTS_INT_EQUAL(tc, len, 3);
+
+ apr_pool_destroy(pool);
+}
+
+static void test_encode_base32(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *src, *target;
+ const char *dest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ /*
+ * Test vectors from https://tools.ietf.org/html/rfc4648#section-10
+ */
+ src = "";
+ target = "";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "f";
+ target = "MY======";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "f";
+ target = "MY";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "f";
+ target = "CO======";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "f";
+ target = "CO";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "fo";
+ target = "MZXQ====";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "fo";
+ target = "MZXQ";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "fo";
+ target = "CPNG====";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "fo";
+ target = "CPNG";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foo";
+ target = "MZXW6===";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foo";
+ target = "MZXW6";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foo";
+ target = "CPNMU===";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foo";
+ target = "CPNMU";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foob";
+ target = "MZXW6YQ=";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foob";
+ target = "MZXW6YQ";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foob";
+ target = "CPNMUOG=";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foob";
+ target = "CPNMUOG";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "fooba";
+ target = "MZXW6YTB";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "fooba";
+ target = "MZXW6YTB";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "fooba";
+ target = "CPNMUOJ1";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "fooba";
+ target = "CPNMUOJ1";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foobar";
+ target = "MZXW6YTBOI======";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foobar";
+ target = "MZXW6YTBOI";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foobar";
+ target = "CPNMUOJ1E8======";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "foobar";
+ target = "CPNMUOJ1E8";
+ dest = apr_pencode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ apr_pool_destroy(pool);
+}
+
+static void test_encode_base32_binary(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *target;
+ const char *dest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ /*
+ * Test vectors from https://tools.ietf.org/html/rfc4648#section-10
+ */
+ target = "";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 0, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MY======";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 1, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MY";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 1, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CO======";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 1, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CO";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 1, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MZXQ====";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 2, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MZXQ";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 2, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CPNG====";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 2, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CPNG";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 2, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MZXW6===";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 3, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MZXW6";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 3, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CPNMU===";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 3, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CPNMU";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 3, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MZXW6YQ=";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 4, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MZXW6YQ";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 4, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CPNMUOG=";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 4, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CPNMUOG";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 4, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MZXW6YTB";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 5, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MZXW6YTB";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 5, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CPNMUOJ1";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 5, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CPNMUOJ1";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 5, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MZXW6YTBOI======";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 6, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "MZXW6YTBOI";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 6, APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CPNMUOJ1E8======";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 6, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ target = "CPNMUOJ1E8";
+ dest = apr_pencode_base32_binary(pool, ufoobar, 6, APR_ENCODE_BASE32HEX | APR_ENCODE_NOPADDING, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ apr_pool_destroy(pool);
+}
+
+static void test_decode_base32(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *target, *src;
+ const char *dest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ /*
+ * Test vectors from https://tools.ietf.org/html/rfc4648#section-10
+ */
+ src = "";
+ target = "";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MY======";
+ target = "f";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MY";
+ target = "f";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CO======";
+ target = "f";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CO";
+ target = "f";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MZXQ====";
+ target = "fo";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MZXQ";
+ target = "fo";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CPNG====";
+ target = "fo";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CPNG";
+ target = "fo";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MZXW6===";
+ target = "foo";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MZXW6";
+ target = "foo";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CPNMU===";
+ target = "foo";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CPNMU";
+ target = "foo";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MZXW6YQ=";
+ target = "foob";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MZXW6YQ=";
+ target = "foob";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CPNMUOG=";
+ target = "foob";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CPNMUOG";
+ target = "foob";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MZXW6YTB";
+ target = "fooba";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MZXW6YTB";
+ target = "fooba";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CPNMUOJ1";
+ target = "fooba";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CPNMUOJ1";
+ target = "fooba";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MZXW6YTBOI======";
+ target = "foobar";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "MZXW6YTBOI";
+ target = "foobar";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CPNMUOJ1E8======";
+ target = "foobar";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ src = "CPNMUOJ1E8";
+ target = "foobar";
+ dest = apr_pdecode_base32(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+
+ apr_pool_destroy(pool);
+}
+
+static void test_decode_base32_binary(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *src;
+ const unsigned char *udest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ /*
+ * Test vectors from https://tools.ietf.org/html/rfc4648#section-10
+ */
+ src = "";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 0) == 0);
+ ABTS_INT_EQUAL(tc, 0, len);
+
+ src = "MY======";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 1) == 0);
+ ABTS_INT_EQUAL(tc, 1, len);
+
+ src = "MY";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 1) == 0);
+ ABTS_INT_EQUAL(tc, 1, len);
+
+ src = "CO======";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 1) == 0);
+ ABTS_INT_EQUAL(tc, 1, len);
+
+ src = "CO";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 1) == 0);
+ ABTS_INT_EQUAL(tc, 1, len);
+
+ src = "MZXQ====";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 2) == 0);
+ ABTS_INT_EQUAL(tc, 2, len);
+
+ src = "MZXQ";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 2) == 0);
+ ABTS_INT_EQUAL(tc, 2, len);
+
+ src = "CPNG====";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 2) == 0);
+ ABTS_INT_EQUAL(tc, 2, len);
+
+ src = "CPNG";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 2) == 0);
+ ABTS_INT_EQUAL(tc, 2, len);
+
+ src = "MZXW6===";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 3) == 0);
+ ABTS_INT_EQUAL(tc, 3, len);
+
+ src = "MZXW6";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 3) == 0);
+ ABTS_INT_EQUAL(tc, 3, len);
+
+ src = "CPNMU===";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 3) == 0);
+ ABTS_INT_EQUAL(tc, 3, len);
+
+ src = "CPNMU";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 3) == 0);
+ ABTS_INT_EQUAL(tc, 3, len);
+
+ src = "MZXW6YQ=";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 4) == 0);
+ ABTS_INT_EQUAL(tc, 4, len);
+
+ src = "MZXW6YQ=";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 4) == 0);
+ ABTS_INT_EQUAL(tc, 4, len);
+
+ src = "CPNMUOG=";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 4) == 0);
+ ABTS_INT_EQUAL(tc, 4, len);
+
+ src = "CPNMUOG";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 4) == 0);
+ ABTS_INT_EQUAL(tc, 4, len);
+
+ src = "MZXW6YTB";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 5) == 0);
+ ABTS_INT_EQUAL(tc, 5, len);
+
+ src = "MZXW6YTB";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 5) == 0);
+ ABTS_INT_EQUAL(tc, 5, len);
+
+ src = "CPNMUOJ1";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 5) == 0);
+ ABTS_INT_EQUAL(tc, 5, len);
+
+ src = "CPNMUOJ1";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 5) == 0);
+ ABTS_INT_EQUAL(tc, 5, len);
+
+ src = "MZXW6YTBOI======";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 6) == 0);
+ ABTS_INT_EQUAL(tc, 6, len);
+
+ src = "MZXW6YTBOI";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 6) == 0);
+ ABTS_INT_EQUAL(tc, 6, len);
+
+ src = "CPNMUOJ1E8======";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 6) == 0);
+ ABTS_INT_EQUAL(tc, 6, len);
+
+ src = "CPNMUOJ1E8";
+ udest = apr_pdecode_base32_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_BASE32HEX, &len);
+ ABTS_ASSERT(tc, "apr_pdecode_base32_binary target!=dest", memcmp(ufoobar, udest, 6) == 0);
+ ABTS_INT_EQUAL(tc, 6, len);
+
+ apr_pool_destroy(pool);
+}
+
+static void test_encode_base16(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *src, *target;
+ const char *dest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ src = "foobar";
+ target = "666f6f626172";
+ dest = apr_pencode_base16(pool, src, APR_ENCODE_STRING, APR_ENCODE_LOWER, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_encode_base16(NULL, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "foobar";
+ target = "666F6F626172";
+ dest = apr_pencode_base16(pool, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_encode_base16(NULL, src, APR_ENCODE_STRING, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "foobar";
+ target = "66:6f:6f:62:61:72";
+ dest = apr_pencode_base16(pool, src, APR_ENCODE_STRING, APR_ENCODE_COLON | APR_ENCODE_LOWER, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_encode_base16(NULL, src, APR_ENCODE_STRING, APR_ENCODE_COLON, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "foobar";
+ target = "66:6F:6F:62:61:72";
+ dest = apr_pencode_base16(pool, src, APR_ENCODE_STRING, APR_ENCODE_COLON, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_encode_base16(NULL, src, APR_ENCODE_STRING, APR_ENCODE_COLON, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ apr_pool_destroy(pool);
+}
+
+static void test_encode_base16_binary(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *target;
+ const unsigned char usrc[] = {
+ 0xFF, 0x00, 0xFF, 0x00
+ };
+ const char *dest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ target = "ff00ff00";
+ dest = apr_pencode_base16_binary(pool, usrc, 4, APR_ENCODE_LOWER, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_encode_base16_binary(NULL, usrc, 4, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ target = "FF00FF00";
+ dest = apr_pencode_base16_binary(pool, usrc, 4, APR_ENCODE_NONE, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_encode_base16_binary(NULL, usrc, 4, APR_ENCODE_NONE, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ target = "ff:00:ff:00";
+ dest = apr_pencode_base16_binary(pool, usrc, 4, APR_ENCODE_COLON | APR_ENCODE_LOWER, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_encode_base16_binary(NULL, usrc, 4, APR_ENCODE_COLON, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ target = "FF:00:FF:00";
+ dest = apr_pencode_base16_binary(pool, usrc, 4, APR_ENCODE_COLON, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_encode_base16_binary(NULL, usrc, 4, APR_ENCODE_COLON, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ apr_pool_destroy(pool);
+}
+
+static void test_decode_base16(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *src, *target;
+ const char *dest;
+ apr_size_t len;
+
+ apr_pool_create(&pool, NULL);
+
+ src = "3A:3B:3C:3D";
+ target = ":;<=";
+ dest = apr_pdecode_base16(pool, src, APR_ENCODE_STRING, APR_ENCODE_COLON, &len);
+ ABTS_STR_EQUAL(tc, target, dest);
+ ABTS_INT_EQUAL(tc, 4, (int)len);
+ apr_decode_base16(NULL, src, APR_ENCODE_STRING, APR_ENCODE_COLON, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, (apr_size_t) 5),
+ (len == 5));
+
+ apr_pool_destroy(pool);
+}
+
+static void test_decode_base16_binary(abts_case * tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *src;
+ const unsigned char utarget[] = {
+ 0xFF, 0x00, 0xFF, 0x00
+ };
+ const unsigned char *udest;
+ apr_size_t len, vlen;
+
+ apr_pool_create(&pool, NULL);
+
+ src = "ff:00:ff:00";
+ udest = apr_pdecode_base16_binary(pool, src, APR_ENCODE_STRING, APR_ENCODE_COLON, &vlen);
+ ABTS_ASSERT(tc, "apr_pdecode_base16_binary target!=dest", memcmp(utarget, udest, 4) == 0);
+ ABTS_INT_EQUAL(tc, (int)vlen, 4);
+ apr_decode_base16_binary(NULL, src, APR_ENCODE_STRING, APR_ENCODE_COLON, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, (apr_size_t) 4),
+ (len == 4));
+
+ apr_pool_destroy(pool);
+}
+
+static void test_encode_errors(abts_case * tc, void *data)
+{
+ char dest[64];
+ apr_size_t len;
+ apr_status_t rv;
+
+ /* Can't test APR_ENOSPC without a NUL terminated buffer of
+ * length APR_SIZE_MAX / 4 * 3 and passing APR_ENCODE_STRING,
+ * which we won't even think about :)
+ */
+
+ /* base64 */
+ rv = apr_encode_base64(dest, "", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_encode_base64(dest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+
+ /* base64_binary */
+ rv = apr_encode_base64_binary(dest, (const unsigned char *)"", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_encode_base64_binary(dest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+
+ /* base32 */
+ rv = apr_encode_base32(dest, "", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_encode_base32(dest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+
+ /* base32_binary */
+ rv = apr_encode_base32_binary(dest, (const unsigned char *)"", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_encode_base32_binary(dest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+
+ /* base16 */
+ rv = apr_encode_base16(dest, "", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_encode_base16(dest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+
+ /* base16_binary */
+ rv = apr_encode_base16_binary(dest, (const unsigned char *)"", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_encode_base16_binary(dest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+}
+
+static void test_decode_errors(abts_case * tc, void *data)
+{
+ char dest[64];
+ apr_size_t len;
+ apr_status_t rv;
+ unsigned char *udest = (unsigned char *)dest;
+
+ /* base64 */
+ rv = apr_decode_base64(dest, "", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_decode_base64(dest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+ rv = apr_decode_base64(NULL, NULL, 5, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base64(dest, "ABCDE", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base64(dest, "ABCD*EF", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_BADCH, rv);
+ rv = apr_decode_base64(dest, "ABCD*EF", APR_ENCODE_STRING,
+ APR_ENCODE_RELAXED, &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, 3, len);
+
+ /* base64_binary */
+ rv = apr_decode_base64_binary(udest, "", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_decode_base64_binary(udest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+ rv = apr_decode_base64_binary(NULL, NULL, 5, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base64_binary(udest, "ABCDE", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base64_binary(udest, "ABCD*EF", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_BADCH, rv);
+ rv = apr_decode_base64_binary(udest, "ABCD*EF", APR_ENCODE_STRING,
+ APR_ENCODE_RELAXED, &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, 3, len);
+
+ /* base32 */
+ rv = apr_decode_base32(dest, "", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_decode_base32(dest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+ rv = apr_decode_base32(NULL, NULL, 9, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32(NULL, NULL, 11, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32(NULL, NULL, 14, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32(dest, "ABCDEFGHI", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32(dest, "ABCDEFGHIJK", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32(dest, "ABCDEFGHIJKLMN", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32(dest, "ABCDEFGH*IJ", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_BADCH, rv);
+ rv = apr_decode_base32(dest, "ABCEEFGH*IJ", APR_ENCODE_STRING,
+ APR_ENCODE_RELAXED, &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, 5, len);
+
+ /* base32_binary */
+ rv = apr_decode_base32_binary(udest, "", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_decode_base32_binary(udest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+ rv = apr_decode_base32_binary(NULL, NULL, 9, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32_binary(NULL, NULL, 11, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32_binary(NULL, NULL, 14, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32_binary(udest, "ABCDEFGHI", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32_binary(udest, "ABCDEFGHIJK", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32_binary(udest, "ABCDEFGHIJKLMN", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base32_binary(udest, "ABCDEFGH*IJ", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_BADCH, rv);
+ rv = apr_decode_base32_binary(udest, "ABCEEFGH*IJ", APR_ENCODE_STRING,
+ APR_ENCODE_RELAXED, &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, 5, len);
+
+ /* base16 */
+ rv = apr_decode_base16(dest, "", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_decode_base16(dest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+ rv = apr_decode_base16(NULL, NULL, 3, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base16(dest, "ABC", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base16(dest, "ABCD*EF", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_BADCH, rv);
+ rv = apr_decode_base16(dest, "ABCD*EF", APR_ENCODE_STRING,
+ APR_ENCODE_RELAXED, &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, 2, len);
+ /* base16 with colon */
+ rv = apr_decode_base16(dest, "AB:", APR_ENCODE_STRING,
+ APR_ENCODE_COLON, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base16(dest, "AB:C", APR_ENCODE_STRING,
+ APR_ENCODE_COLON, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base16(dest, "AB:CD*EF", APR_ENCODE_STRING,
+ APR_ENCODE_COLON, &len);
+ ABTS_INT_EQUAL(tc, APR_BADCH, rv);
+ rv = apr_decode_base16(dest, "AB:CD*EF", APR_ENCODE_STRING,
+ APR_ENCODE_COLON|APR_ENCODE_RELAXED, &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, 2, len);
+
+ /* base16_binary */
+ rv = apr_decode_base16_binary(udest, "", -2, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+ rv = apr_decode_base16_binary(udest, NULL, APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_NOTFOUND, rv);
+ rv = apr_decode_base16_binary(NULL, NULL, 3, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base16_binary(udest, "ABC", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base16_binary(udest, "ABCD*EF", APR_ENCODE_STRING, 0, &len);
+ ABTS_INT_EQUAL(tc, APR_BADCH, rv);
+ rv = apr_decode_base16_binary(udest, "ABCD*EF", APR_ENCODE_STRING,
+ APR_ENCODE_RELAXED, &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, 2, len);
+ /* base16_binary with colon */
+ rv = apr_decode_base16_binary(udest, "AB:", APR_ENCODE_STRING,
+ APR_ENCODE_COLON, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base16_binary(udest, "AB:C", APR_ENCODE_STRING,
+ APR_ENCODE_COLON, &len);
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ rv = apr_decode_base16_binary(udest, "AB:CD*EF", APR_ENCODE_STRING,
+ APR_ENCODE_COLON, &len);
+ ABTS_INT_EQUAL(tc, APR_BADCH, rv);
+ rv = apr_decode_base16_binary(udest, "AB:CD*EF", APR_ENCODE_STRING,
+ APR_ENCODE_COLON|APR_ENCODE_RELAXED, &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, 2, len);
+}
+
+abts_suite *testencode(abts_suite * suite)
+{
+ suite = ADD_SUITE(suite);
+
+ abts_run_test(suite, test_encode_base64, NULL);
+ abts_run_test(suite, test_encode_base64_binary, NULL);
+ abts_run_test(suite, test_decode_base64, NULL);
+ abts_run_test(suite, test_decode_base64_binary, NULL);
+ abts_run_test(suite, test_encode_base32, NULL);
+ abts_run_test(suite, test_encode_base32_binary, NULL);
+ abts_run_test(suite, test_decode_base32, NULL);
+ abts_run_test(suite, test_decode_base32_binary, NULL);
+ abts_run_test(suite, test_encode_base16, NULL);
+ abts_run_test(suite, test_encode_base16_binary, NULL);
+ abts_run_test(suite, test_decode_base16, NULL);
+ abts_run_test(suite, test_decode_base16_binary, NULL);
+ abts_run_test(suite, test_encode_errors, NULL);
+ abts_run_test(suite, test_decode_errors, NULL);
+
+ return suite;
+}
diff --git a/test/testenv.c b/test/testenv.c
new file mode 100644
index 0000000..f5a74c3
--- /dev/null
+++ b/test/testenv.c
@@ -0,0 +1,144 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_env.h"
+#include "apr_errno.h"
+#include "testutil.h"
+
+#define TEST_ENVVAR_NAME "apr_test_envvar"
+#define TEST_ENVVAR2_NAME "apr_test_envvar2"
+#define TEST_ENVVAR_VALUE "Just a value that we'll check"
+
+static int have_env_set;
+static int have_env_get;
+static int have_env_del;
+
+static void test_setenv(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_env_set(TEST_ENVVAR_NAME, TEST_ENVVAR_VALUE, p);
+ have_env_set = (rv != APR_ENOTIMPL);
+ if (!have_env_set) {
+ ABTS_NOT_IMPL(tc, "apr_env_set");
+ } else {
+ APR_ASSERT_SUCCESS(tc, "set environment variable", rv);
+ }
+}
+
+static void test_getenv(abts_case *tc, void *data)
+{
+ char *value;
+ apr_status_t rv;
+
+ if (!have_env_set) {
+ ABTS_NOT_IMPL(tc, "apr_env_set (skip test for apr_env_get)");
+ return;
+ }
+
+ rv = apr_env_get(&value, TEST_ENVVAR_NAME, p);
+ have_env_get = (rv != APR_ENOTIMPL);
+ if (!have_env_get) {
+ ABTS_NOT_IMPL(tc, "apr_env_get");
+ return;
+ }
+ APR_ASSERT_SUCCESS(tc, "get environment variable", rv);
+ ABTS_STR_EQUAL(tc, TEST_ENVVAR_VALUE, value);
+}
+
+static void test_delenv(abts_case *tc, void *data)
+{
+ char *value;
+ apr_status_t rv;
+
+ if (!have_env_set) {
+ ABTS_NOT_IMPL(tc, "apr_env_set (skip test for apr_env_delete)");
+ return;
+ }
+
+ rv = apr_env_delete(TEST_ENVVAR_NAME, p);
+ have_env_del = (rv != APR_ENOTIMPL);
+ if (!have_env_del) {
+ ABTS_NOT_IMPL(tc, "apr_env_delete");
+ return;
+ }
+ APR_ASSERT_SUCCESS(tc, "delete environment variable", rv);
+
+ if (!have_env_get) {
+ ABTS_NOT_IMPL(tc, "apr_env_get (skip sanity check for apr_env_delete)");
+ return;
+ }
+ rv = apr_env_get(&value, TEST_ENVVAR_NAME, p);
+ ABTS_INT_EQUAL(tc, APR_ENOENT, rv);
+}
+
+/** http://issues.apache.org/bugzilla/show_bug.cgi?id=40764 */
+static void test_emptyenv(abts_case *tc, void *data)
+{
+ char *value;
+ apr_status_t rv;
+
+ if (!(have_env_set && have_env_get)) {
+ ABTS_NOT_IMPL(tc, "apr_env_set (skip test_emptyenv)");
+ return;
+ }
+ /** Set empty string and test that rv != ENOENT) */
+ rv = apr_env_set(TEST_ENVVAR_NAME, "", p);
+ APR_ASSERT_SUCCESS(tc, "set environment variable", rv);
+ rv = apr_env_get(&value, TEST_ENVVAR_NAME, p);
+ APR_ASSERT_SUCCESS(tc, "get environment variable", rv);
+ ABTS_STR_EQUAL(tc, "", value);
+
+ if (!have_env_del) {
+ ABTS_NOT_IMPL(tc, "apr_env (skip recycle test_emptyenv)");
+ return;
+ }
+ /** Delete and retest */
+ rv = apr_env_delete(TEST_ENVVAR_NAME, p);
+ APR_ASSERT_SUCCESS(tc, "delete environment variable", rv);
+ rv = apr_env_get(&value, TEST_ENVVAR_NAME, p);
+ ABTS_INT_EQUAL(tc, APR_ENOENT, rv);
+
+ /** Set second variable + test*/
+ rv = apr_env_set(TEST_ENVVAR2_NAME, TEST_ENVVAR_VALUE, p);
+ APR_ASSERT_SUCCESS(tc, "set second environment variable", rv);
+ rv = apr_env_get(&value, TEST_ENVVAR2_NAME, p);
+ APR_ASSERT_SUCCESS(tc, "get second environment variable", rv);
+ ABTS_STR_EQUAL(tc, TEST_ENVVAR_VALUE, value);
+
+ /** Finally, test ENOENT (first variable) followed by second != ENOENT) */
+ rv = apr_env_get(&value, TEST_ENVVAR_NAME, p);
+ ABTS_INT_EQUAL(tc, APR_ENOENT, rv);
+ rv = apr_env_get(&value, TEST_ENVVAR2_NAME, p);
+ APR_ASSERT_SUCCESS(tc, "verify second environment variable", rv);
+ ABTS_STR_EQUAL(tc, TEST_ENVVAR_VALUE, value);
+
+ /** Cleanup */
+ apr_env_delete(TEST_ENVVAR2_NAME, p);
+}
+
+abts_suite *testenv(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_setenv, NULL);
+ abts_run_test(suite, test_getenv, NULL);
+ abts_run_test(suite, test_delenv, NULL);
+ abts_run_test(suite, test_emptyenv, NULL);
+
+ return suite;
+}
+
diff --git a/test/testescape.c b/test/testescape.c
new file mode 100644
index 0000000..6645227
--- /dev/null
+++ b/test/testescape.c
@@ -0,0 +1,311 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "apr_escape.h"
+#include "apr_strings.h"
+
+#include "abts.h"
+#include "testutil.h"
+
+static void test_escape(abts_case *tc, void *data)
+{
+ apr_pool_t *pool;
+ const char *src, *target;
+ const char *dest;
+ const void *vdest;
+ apr_size_t len, vlen;
+
+ apr_pool_create(&pool, NULL);
+
+ src = "Hello World &;`'\"|*?~<>^()[]{}$\\";
+ target = "Hello World \\&\\;\\`\\'\\\"\\|\\*\\?\\~\\<\\>\\^\\(\\)\\[\\]\\{\\}\\$\\\\";
+ dest = apr_pescape_shell(pool, src);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "shell escaped (%s) does not match expected output (%s)",
+ dest, target),
+ (strcmp(dest, target) == 0));
+ apr_escape_shell(NULL, src, APR_ESCAPE_STRING, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+#if !(defined(OS2) || defined(WIN32))
+ /* Now try with newline, which is converted to a space on OS/2 and Windows.
+ */
+ src = "Hello World &;`'\"|*?~<>^()[]{}$\\\n";
+ target = "Hello World \\&\\;\\`\\'\\\"\\|\\*\\?\\~\\<\\>\\^\\(\\)\\[\\]\\{\\}\\$\\\\\\\n";
+ dest = apr_pescape_shell(pool, src);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "shell escaped (%s) does not match expected output (%s)",
+ dest, target),
+ (strcmp(dest, target) == 0));
+ apr_escape_shell(NULL, src, APR_ESCAPE_STRING, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+#endif
+
+ src = "Hello";
+ dest = apr_punescape_url(pool, src, NULL, NULL, 0);
+ ABTS_PTR_EQUAL(tc, src, dest);
+
+ src = "Hello";
+ dest = apr_punescape_url(pool, src, NULL, NULL, 1);
+ ABTS_PTR_EQUAL(tc, src, dest);
+
+ src = "Hello%20";
+ dest = apr_punescape_url(pool, src, " ", NULL, 0);
+ ABTS_PTR_EQUAL(tc, NULL, dest);
+
+ src = "Hello%20World";
+ target = "Hello World";
+ dest = apr_punescape_url(pool, src, NULL, NULL, 0);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_unescape_url(NULL, src, APR_ESCAPE_STRING, NULL, NULL, 0, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "Hello+World";
+ target = "Hello World";
+ dest = apr_punescape_url(pool, src, NULL, NULL, 1);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_unescape_url(NULL, src, APR_ESCAPE_STRING, NULL, NULL, 1, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "Hello%20World";
+ target = "Hello%20World";
+ dest = apr_punescape_url(pool, src, NULL, " ", 0);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_unescape_url(NULL, src, APR_ESCAPE_STRING, NULL, " ", 0, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "Hello";
+ dest = apr_pescape_path_segment(pool, src);
+ ABTS_PTR_EQUAL(tc, src, dest);
+
+ src = "$-_.+!*'(),:@&=/~Hello World";
+ target = "$-_.+!*'(),:@&=%2f~Hello%20World";
+ dest = apr_pescape_path_segment(pool, src);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_escape_path_segment(NULL, src, APR_ESCAPE_STRING, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "Hello";
+ dest = apr_pescape_path(pool, src, 0);
+ ABTS_PTR_EQUAL(tc, src, dest);
+
+ src = "$-_.+!*'(),:@&=/~Hello World";
+ target = "./$-_.+!*'(),:@&=/~Hello%20World";
+ dest = apr_pescape_path(pool, src, 0);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_escape_path(NULL, src, APR_ESCAPE_STRING, 0, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "Hello";
+ dest = apr_pescape_path(pool, src, 1);
+ ABTS_PTR_EQUAL(tc, src, dest);
+
+ src = "$-_.+!*'(),:@&=/~Hello World";
+ target = "$-_.+!*'(),:@&=/~Hello%20World";
+ dest = apr_pescape_path(pool, src, 1);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_escape_path(NULL, src, APR_ESCAPE_STRING, 1, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "Hello";
+ dest = apr_pescape_urlencoded(pool, src);
+ ABTS_PTR_EQUAL(tc, src, dest);
+
+ src = "$-_.+!*'(),:@&=/~Hello World";
+ target = "%24-_.%2b%21*%27%28%29%2c%3a%40%26%3d%2f%7eHello+World";
+ dest = apr_pescape_urlencoded(pool, src);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_escape_urlencoded(NULL, src, APR_ESCAPE_STRING, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "Hello";
+ dest = apr_pescape_entity(pool, src, 0);
+ ABTS_PTR_EQUAL(tc, src, dest);
+
+ src = "\xFF<>&\'\"Hello World";
+ target = "\xFF&lt;&gt;&amp;'&quot;Hello World";
+ dest = apr_pescape_entity(pool, src, 0);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_escape_entity(NULL, src, APR_ESCAPE_STRING, 0, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+#if !APR_CHARSET_EBCDIC
+ src = "Hello";
+ dest = apr_pescape_entity(pool, src, 1);
+ ABTS_PTR_EQUAL(tc, src, dest);
+
+ src = "\xFF<>&\'\"Hello World";
+ target = "&#255&lt;&gt;&amp;'&quot;Hello World";
+ dest = apr_pescape_entity(pool, src, 1);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_escape_entity(NULL, src, APR_ESCAPE_STRING, 1, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "Hello";
+ dest = apr_punescape_entity(pool, src);
+ ABTS_PTR_EQUAL(tc, src, dest);
+
+ src = "\xFF&lt;&gt;&amp;'&quot;Hello World";
+ target = "\xFF<>&\'\"Hello World";
+ dest = apr_punescape_entity(pool, src);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_unescape_entity(NULL, src, APR_ESCAPE_STRING, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "&#255;&lt;&gt;&amp;'&quot;Hello World";
+ target = "\xFF<>&\'\"Hello World";
+ dest = apr_punescape_entity(pool, src);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_unescape_entity(NULL, src, APR_ESCAPE_STRING, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "&#32;&lt;&gt;&amp;'&quot;Hello World";
+ target = " <>&\'\"Hello World";
+ dest = apr_punescape_entity(pool, src);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_unescape_entity(NULL, src, APR_ESCAPE_STRING, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+#endif
+
+ src = "Hello";
+ dest = apr_pescape_echo(pool, src, 0);
+ ABTS_PTR_EQUAL(tc, src, dest);
+
+ src = "\a\b\f\\n\r\t\v\"Hello World\"";
+ target = "\\a\\b\\f\\\\n\\r\\t\\v\"Hello World\"";
+ dest = apr_pescape_echo(pool, src, 0);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_escape_echo(NULL, src, APR_ESCAPE_STRING, 0, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "\a\b\f\\n\r\t\v\"Hello World\"";
+ target = "\\a\\b\\f\\\\n\\r\\t\\v\\\"Hello World\\\"";
+ dest = apr_pescape_echo(pool, src, 1);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_escape_echo(NULL, src, APR_ESCAPE_STRING, 1, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "\xFF\x00\xFF\x00";
+ target = "ff00ff00";
+ dest = apr_pescape_hex(pool, src, 4, 0);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_escape_hex(NULL, src, 4, 0, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "\xFF\x00\xFF\x00";
+ target = "ff:00:ff:00";
+ dest = apr_pescape_hex(pool, src, 4, 1);
+ ABTS_STR_EQUAL(tc, target, dest);
+ apr_escape_hex(NULL, src, 4, 1, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "ff:00:ff:00";
+ target = "\xFF\x00\xFF\x00";
+ vdest = apr_punescape_hex(pool, src, 1, &vlen);
+ ABTS_ASSERT(tc, "apr_punescape_hex target!=dest", memcmp(target, vdest, 4) == 0);
+ ABTS_INT_EQUAL(tc, (int)vlen, 4);
+ apr_unescape_hex(NULL, src, APR_ESCAPE_STRING, 1, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, (apr_size_t)4),
+ (len == 4));
+
+ src = "Parens R Us (for all your parenthetical needs) plus asterisk* \"+,;<>\\";
+ target = "Parens R Us (for all your parenthetical needs) plus asterisk* \\22\\2b\\2c\\3b\\3c\\3e\\5c";
+ dest = apr_pescape_ldap(pool, src, APR_ESCAPE_STRING, APR_ESCAPE_LDAP_DN);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "ldap escaped (%s) does not match expected output (%s)",
+ dest, target),
+ (strcmp(dest, target) == 0));
+ apr_escape_ldap(NULL, src, APR_ESCAPE_STRING, APR_ESCAPE_LDAP_DN, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "Parens R Us (for all your parenthetical needs) plus asterisk* \"+,;<>\\";
+ target = "Parens R Us \\28for all your parenthetical needs\\29 plus asterisk\\2a \"+,;<>\\5c";
+ dest = apr_pescape_ldap(pool, src, APR_ESCAPE_STRING, APR_ESCAPE_LDAP_FILTER);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "ldap escaped (%s) does not match expected output (%s)",
+ dest, target),
+ (strcmp(dest, target) == 0));
+ apr_escape_ldap(NULL, src, APR_ESCAPE_STRING, APR_ESCAPE_LDAP_FILTER, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ src = "Parens R Us (for all your parenthetical needs) plus asterisk* \"+,;<>\\";
+ target = "Parens R Us \\28for all your parenthetical needs\\29 plus asterisk\\2a \\22\\2b\\2c\\3b\\3c\\3e\\5c";
+ dest = apr_pescape_ldap(pool, src, APR_ESCAPE_STRING, APR_ESCAPE_LDAP_ALL);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "ldap escaped (%s) does not match expected output (%s)",
+ dest, target),
+ (strcmp(dest, target) == 0));
+ apr_escape_ldap(NULL, src, APR_ESCAPE_STRING, APR_ESCAPE_LDAP_ALL, &len);
+ ABTS_ASSERT(tc,
+ apr_psprintf(pool, "size mismatch (%" APR_SIZE_T_FMT "!=%" APR_SIZE_T_FMT ")", len, strlen(dest) + 1),
+ (len == strlen(dest) + 1));
+
+ apr_pool_destroy(pool);
+}
+
+abts_suite *testescape(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite);
+
+ abts_run_test(suite, test_escape, NULL);
+
+ return suite;
+}
diff --git a/test/testfile.c b/test/testfile.c
new file mode 100644
index 0000000..b1e9c55
--- /dev/null
+++ b/test/testfile.c
@@ -0,0 +1,1312 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_network_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_poll.h"
+#include "apr_lib.h"
+#include "testutil.h"
+
+#define DIRNAME "data"
+#define FILENAME DIRNAME "/file_datafile.txt"
+#define TESTSTR "This is the file data file."
+
+#define TESTREAD_BLKSIZE 1024
+#define APR_BUFFERSIZE 4096 /* This should match APR's buffer size. */
+
+
+
+static void test_open_noreadwrite(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *thefile = NULL;
+
+ rv = apr_file_open(&thefile, FILENAME,
+ APR_FOPEN_CREATE | APR_FOPEN_EXCL,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_TRUE(tc, rv != APR_SUCCESS);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EACCES(rv));
+ ABTS_PTR_EQUAL(tc, NULL, thefile);
+}
+
+static void test_open_excl(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *thefile = NULL;
+
+ rv = apr_file_open(&thefile, FILENAME,
+ APR_FOPEN_CREATE | APR_FOPEN_EXCL | APR_FOPEN_WRITE,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_TRUE(tc, rv != APR_SUCCESS);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EEXIST(rv));
+ ABTS_PTR_EQUAL(tc, NULL, thefile);
+}
+
+static void test_open_read(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *filetest = NULL;
+
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_READ,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, filetest);
+ apr_file_close(filetest);
+}
+
+static void link_existing(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_file_link("data/file_datafile.txt", "data/file_datafile2.txt");
+ apr_file_remove("data/file_datafile2.txt", p);
+ ABTS_ASSERT(tc, "Couldn't create hardlink to file", rv == APR_SUCCESS);
+}
+
+static void link_nonexisting(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_file_link("data/does_not_exist.txt", "data/fake.txt");
+ ABTS_ASSERT(tc, "", rv != APR_SUCCESS);
+}
+
+static void test_read(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_size_t nbytes = 256;
+ char *str = apr_pcalloc(p, nbytes + 1);
+ apr_file_t *filetest = NULL;
+
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_READ,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+
+ APR_ASSERT_SUCCESS(tc, "Opening test file " FILENAME, rv);
+ rv = apr_file_read(filetest, str, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(TESTSTR), nbytes);
+ ABTS_STR_EQUAL(tc, TESTSTR, str);
+
+ apr_file_close(filetest);
+}
+
+static void test_readzero(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_size_t nbytes = 0;
+ char *str = NULL;
+ apr_file_t *filetest;
+
+ rv = apr_file_open(&filetest, FILENAME, APR_FOPEN_READ, APR_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "Opening test file " FILENAME, rv);
+
+ rv = apr_file_read(filetest, str, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, 0, nbytes);
+
+ apr_file_close(filetest);
+}
+
+static void test_filename(abts_case *tc, void *data)
+{
+ const char *str;
+ apr_status_t rv;
+ apr_file_t *filetest = NULL;
+
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_READ,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ APR_ASSERT_SUCCESS(tc, "Opening test file " FILENAME, rv);
+
+ rv = apr_file_name_get(&str, filetest);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, FILENAME, str);
+
+ apr_file_close(filetest);
+}
+
+static void test_fileclose(abts_case *tc, void *data)
+{
+ char str;
+ apr_status_t rv;
+ apr_size_t one = 1;
+ apr_file_t *filetest = NULL;
+
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_READ,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ APR_ASSERT_SUCCESS(tc, "Opening test file " FILENAME, rv);
+
+ rv = apr_file_close(filetest);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ /* We just closed the file, so this should fail */
+ rv = apr_file_read(filetest, &str, &one);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EBADF(rv));
+}
+
+static void test_file_remove(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *filetest = NULL;
+
+ rv = apr_file_remove(FILENAME, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_open(&filetest, FILENAME, APR_FOPEN_READ,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));
+}
+
+static void test_open_write(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *filetest = NULL;
+
+ filetest = NULL;
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_WRITE,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));
+ ABTS_PTR_EQUAL(tc, NULL, filetest);
+}
+
+static void test_open_writecreate(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *filetest = NULL;
+
+ filetest = NULL;
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_WRITE | APR_FOPEN_CREATE,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ apr_file_close(filetest);
+}
+
+static void test_write(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_size_t bytes = strlen(TESTSTR);
+ apr_file_t *filetest = NULL;
+
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_WRITE | APR_FOPEN_CREATE,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_write(filetest, TESTSTR, &bytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ apr_file_close(filetest);
+}
+
+static void test_open_readwrite(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *filetest = NULL;
+
+ filetest = NULL;
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_READ | APR_FOPEN_WRITE,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, filetest);
+
+ apr_file_close(filetest);
+}
+
+static void test_seek(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_off_t offset = 5;
+ apr_size_t nbytes = 256;
+ char *str = apr_pcalloc(p, nbytes + 1);
+ apr_file_t *filetest = NULL;
+
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_READ,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ APR_ASSERT_SUCCESS(tc, "Open test file " FILENAME, rv);
+
+ rv = apr_file_read(filetest, str, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(TESTSTR), nbytes);
+ ABTS_STR_EQUAL(tc, TESTSTR, str);
+
+ memset(str, 0, nbytes + 1);
+
+ rv = apr_file_seek(filetest, SEEK_SET, &offset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_read(filetest, str, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(TESTSTR) - 5, nbytes);
+ ABTS_STR_EQUAL(tc, TESTSTR + 5, str);
+
+ apr_file_close(filetest);
+
+ /* Test for regression of sign error bug with SEEK_END and
+ buffered files. */
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_READ | APR_FOPEN_BUFFERED,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ APR_ASSERT_SUCCESS(tc, "Open test file " FILENAME, rv);
+
+ offset = -5;
+ rv = apr_file_seek(filetest, SEEK_END, &offset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(TESTSTR) - 5, nbytes);
+
+ memset(str, 0, nbytes + 1);
+ nbytes = 256;
+ rv = apr_file_read(filetest, str, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, 5, nbytes);
+ ABTS_STR_EQUAL(tc, TESTSTR + strlen(TESTSTR) - 5, str);
+
+ apr_file_close(filetest);
+}
+
+static void test_userdata_set(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *filetest = NULL;
+
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_WRITE,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_data_set(filetest, "This is a test",
+ "test", apr_pool_cleanup_null);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ apr_file_close(filetest);
+}
+
+static void test_userdata_get(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ void *udata;
+ char *teststr;
+ apr_file_t *filetest = NULL;
+
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_WRITE,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_data_set(filetest, "This is a test",
+ "test", apr_pool_cleanup_null);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_data_get(&udata, "test", filetest);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ teststr = udata;
+ ABTS_STR_EQUAL(tc, "This is a test", teststr);
+
+ apr_file_close(filetest);
+}
+
+static void test_userdata_getnokey(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ void *teststr;
+ apr_file_t *filetest = NULL;
+
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_WRITE,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_data_get(&teststr, "nokey", filetest);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_EQUAL(tc, NULL, teststr);
+ apr_file_close(filetest);
+}
+
+static void test_buffer_set_get(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_size_t bufsize;
+ apr_file_t *filetest = NULL;
+ char * buffer;
+
+ rv = apr_file_open(&filetest, FILENAME,
+ APR_FOPEN_WRITE | APR_FOPEN_BUFFERED,
+ APR_UREAD | APR_UWRITE | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ bufsize = apr_file_buffer_size_get(filetest);
+ ABTS_SIZE_EQUAL(tc, APR_BUFFERSIZE, bufsize);
+
+ buffer = apr_pcalloc(p, 10240);
+ rv = apr_file_buffer_set(filetest, buffer, 10240);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ bufsize = apr_file_buffer_size_get(filetest);
+ ABTS_SIZE_EQUAL(tc, 10240, bufsize);
+
+ rv = apr_file_buffer_set(filetest, buffer, 12);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ bufsize = apr_file_buffer_size_get(filetest);
+ ABTS_SIZE_EQUAL(tc, 12, bufsize);
+
+ apr_file_close(filetest);
+}
+static void test_getc(abts_case *tc, void *data)
+{
+ apr_file_t *f = NULL;
+ apr_status_t rv;
+ char ch;
+
+ rv = apr_file_open(&f, FILENAME, APR_FOPEN_READ, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ apr_file_getc(&ch, f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, (int)TESTSTR[0], (int)ch);
+ apr_file_close(f);
+}
+
+static void test_ungetc(abts_case *tc, void *data)
+{
+ apr_file_t *f = NULL;
+ apr_status_t rv;
+ char ch;
+
+ rv = apr_file_open(&f, FILENAME, APR_FOPEN_READ, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ apr_file_getc(&ch, f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, (int)TESTSTR[0], (int)ch);
+
+ apr_file_ungetc('X', f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ apr_file_getc(&ch, f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 'X', (int)ch);
+
+ apr_file_close(f);
+}
+
+static void test_gets(abts_case *tc, void *data)
+{
+ apr_file_t *f = NULL;
+ apr_status_t rv;
+ char *str = apr_palloc(p, 256);
+
+ rv = apr_file_open(&f, FILENAME, APR_FOPEN_READ, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_gets(str, 256, f);
+ /* Only one line in the test file, so APR will encounter EOF on the first
+ * call to gets, but we should get APR_SUCCESS on this call and
+ * APR_EOF on the next.
+ */
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, TESTSTR, str);
+ rv = apr_file_gets(str, 256, f);
+ ABTS_INT_EQUAL(tc, APR_EOF, rv);
+ ABTS_STR_EQUAL(tc, "", str);
+ apr_file_close(f);
+}
+
+static void test_gets_buffered(abts_case *tc, void *data)
+{
+ apr_file_t *f = NULL;
+ apr_status_t rv;
+ char *str = apr_palloc(p, 256);
+
+ /* This will deadlock gets before the r524355 fix. */
+ rv = apr_file_open(&f, FILENAME, APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_XTHREAD, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_gets(str, 256, f);
+ /* Only one line in the test file, so APR will encounter EOF on the first
+ * call to gets, but we should get APR_SUCCESS on this call and
+ * APR_EOF on the next.
+ */
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, TESTSTR, str);
+ rv = apr_file_gets(str, 256, f);
+ ABTS_INT_EQUAL(tc, APR_EOF, rv);
+ ABTS_STR_EQUAL(tc, "", str);
+ apr_file_close(f);
+}
+
+static void test_bigread(abts_case *tc, void *data)
+{
+ apr_file_t *f = NULL;
+ apr_status_t rv;
+ char buf[APR_BUFFERSIZE * 2];
+ apr_size_t nbytes;
+
+ /* Create a test file with known content.
+ */
+ rv = apr_file_open(&f, "data/created_file",
+ APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_TRUNCATE,
+ APR_UREAD | APR_UWRITE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ nbytes = APR_BUFFERSIZE;
+ memset(buf, 0xFE, nbytes);
+
+ rv = apr_file_write(f, buf, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, APR_BUFFERSIZE, nbytes);
+
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ f = NULL;
+ rv = apr_file_open(&f, "data/created_file", APR_FOPEN_READ, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ nbytes = sizeof buf;
+ rv = apr_file_read(f, buf, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, APR_BUFFERSIZE, nbytes);
+
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_remove("data/created_file", p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+/* This is a horrible name for this function. We are testing APR, not how
+ * Apache uses APR. And, this function tests _way_ too much stuff.
+ */
+static void test_mod_neg(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *f;
+ const char *s;
+ int i;
+ apr_size_t nbytes;
+ char buf[8192];
+ apr_off_t cur;
+ const char *fname = "data/modneg.dat";
+
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_CREATE | APR_FOPEN_WRITE, APR_UREAD | APR_UWRITE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ s = "body56789\n";
+ nbytes = strlen(s);
+ rv = apr_file_write(f, s, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(s), nbytes);
+
+ for (i = 0; i < 7980; i++) {
+ s = "0";
+ nbytes = strlen(s);
+ rv = apr_file_write(f, s, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(s), nbytes);
+ }
+
+ s = "end456789\n";
+ nbytes = strlen(s);
+ rv = apr_file_write(f, s, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(s), nbytes);
+
+ for (i = 0; i < 10000; i++) {
+ s = "1";
+ nbytes = strlen(s);
+ rv = apr_file_write(f, s, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(s), nbytes);
+ }
+
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_open(&f, fname, APR_FOPEN_READ, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_gets(buf, 11, f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "body56789\n", buf);
+
+ cur = 0;
+ rv = apr_file_seek(f, APR_CUR, &cur);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "File Pointer Mismatch, expected 10", cur == 10);
+
+ nbytes = sizeof(buf);
+ rv = apr_file_read(f, buf, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, nbytes, sizeof(buf));
+
+ cur = -((apr_off_t)nbytes - 7980);
+ rv = apr_file_seek(f, APR_CUR, &cur);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "File Pointer Mismatch, expected 7990", cur == 7990);
+
+ rv = apr_file_gets(buf, 11, f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "end456789\n", buf);
+
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_remove(fname, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+/* Test that the contents of file FNAME are equal to data EXPECT of
+ * length EXPECTLEN. */
+static void file_contents_equal(abts_case *tc,
+ const char *fname,
+ const void *expect,
+ apr_size_t expectlen)
+{
+ void *actual = apr_palloc(p, expectlen);
+ apr_file_t *f;
+
+ APR_ASSERT_SUCCESS(tc, "open file",
+ apr_file_open(&f, fname, APR_FOPEN_READ|APR_FOPEN_BUFFERED,
+ 0, p));
+ APR_ASSERT_SUCCESS(tc, "read from file",
+ apr_file_read_full(f, actual, expectlen, NULL));
+
+ ABTS_ASSERT(tc, "matched expected file contents",
+ memcmp(expect, actual, expectlen) == 0);
+
+ APR_ASSERT_SUCCESS(tc, "close file", apr_file_close(f));
+}
+
+#define LINE1 "this is a line of text\n"
+#define LINE2 "this is a second line of text\n"
+
+static void test_puts(abts_case *tc, void *data)
+{
+ apr_file_t *f;
+ const char *fname = "data/testputs.dat";
+
+ APR_ASSERT_SUCCESS(tc, "open file for writing",
+ apr_file_open(&f, fname,
+ APR_FOPEN_WRITE|APR_FOPEN_CREATE|APR_FOPEN_TRUNCATE,
+ APR_OS_DEFAULT, p));
+
+ APR_ASSERT_SUCCESS(tc, "write line to file",
+ apr_file_puts(LINE1, f));
+ APR_ASSERT_SUCCESS(tc, "write second line to file",
+ apr_file_puts(LINE2, f));
+
+ APR_ASSERT_SUCCESS(tc, "close for writing",
+ apr_file_close(f));
+
+ file_contents_equal(tc, fname, LINE1 LINE2, strlen(LINE1 LINE2));
+}
+
+static void test_writev(abts_case *tc, void *data)
+{
+ apr_file_t *f;
+ apr_size_t nbytes;
+ struct iovec vec[5];
+ const char *fname = "data/testwritev.dat";
+
+ APR_ASSERT_SUCCESS(tc, "open file for writing",
+ apr_file_open(&f, fname,
+ APR_FOPEN_WRITE|APR_FOPEN_CREATE|APR_FOPEN_TRUNCATE,
+ APR_OS_DEFAULT, p));
+
+ vec[0].iov_base = LINE1;
+ vec[0].iov_len = strlen(LINE1);
+
+ APR_ASSERT_SUCCESS(tc, "writev of size 1 to file",
+ apr_file_writev(f, vec, 1, &nbytes));
+
+ file_contents_equal(tc, fname, LINE1, strlen(LINE1));
+
+ vec[0].iov_base = LINE1;
+ vec[0].iov_len = strlen(LINE1);
+ vec[1].iov_base = LINE2;
+ vec[1].iov_len = strlen(LINE2);
+ vec[2].iov_base = LINE1;
+ vec[2].iov_len = strlen(LINE1);
+ vec[3].iov_base = LINE1;
+ vec[3].iov_len = strlen(LINE1);
+ vec[4].iov_base = LINE2;
+ vec[4].iov_len = strlen(LINE2);
+
+ APR_ASSERT_SUCCESS(tc, "writev of size 5 to file",
+ apr_file_writev(f, vec, 5, &nbytes));
+
+ APR_ASSERT_SUCCESS(tc, "close for writing",
+ apr_file_close(f));
+
+ file_contents_equal(tc, fname, LINE1 LINE1 LINE2 LINE1 LINE1 LINE2,
+ strlen(LINE1)*4 + strlen(LINE2)*2);
+
+}
+
+static void test_writev_full(abts_case *tc, void *data)
+{
+ apr_file_t *f;
+ apr_size_t nbytes;
+ struct iovec vec[5];
+ const char *fname = "data/testwritev_full.dat";
+
+ APR_ASSERT_SUCCESS(tc, "open file for writing",
+ apr_file_open(&f, fname,
+ APR_FOPEN_WRITE|APR_FOPEN_CREATE|APR_FOPEN_TRUNCATE,
+ APR_OS_DEFAULT, p));
+
+ vec[0].iov_base = LINE1;
+ vec[0].iov_len = strlen(LINE1);
+ vec[1].iov_base = LINE2;
+ vec[1].iov_len = strlen(LINE2);
+ vec[2].iov_base = LINE1;
+ vec[2].iov_len = strlen(LINE1);
+ vec[3].iov_base = LINE1;
+ vec[3].iov_len = strlen(LINE1);
+ vec[4].iov_base = LINE2;
+ vec[4].iov_len = strlen(LINE2);
+
+ APR_ASSERT_SUCCESS(tc, "writev_full of size 5 to file",
+ apr_file_writev_full(f, vec, 5, &nbytes));
+
+ ABTS_SIZE_EQUAL(tc, strlen(LINE1)*3 + strlen(LINE2)*2, nbytes);
+
+ APR_ASSERT_SUCCESS(tc, "close for writing",
+ apr_file_close(f));
+
+ file_contents_equal(tc, fname, LINE1 LINE2 LINE1 LINE1 LINE2,
+ strlen(LINE1)*3 + strlen(LINE2)*2);
+
+}
+
+static void test_writev_buffered(abts_case *tc, void *data)
+{
+ apr_file_t *f;
+ apr_size_t nbytes;
+ struct iovec vec[2];
+ const char *fname = "data/testwritev_buffered.dat";
+
+ APR_ASSERT_SUCCESS(tc, "open file for writing",
+ apr_file_open(&f, fname,
+ APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE |
+ APR_FOPEN_BUFFERED, APR_OS_DEFAULT, p));
+
+ nbytes = strlen(TESTSTR);
+ APR_ASSERT_SUCCESS(tc, "buffered write",
+ apr_file_write(f, TESTSTR, &nbytes));
+
+ vec[0].iov_base = LINE1;
+ vec[0].iov_len = strlen(LINE1);
+ vec[1].iov_base = LINE2;
+ vec[1].iov_len = strlen(LINE2);
+
+ APR_ASSERT_SUCCESS(tc, "writev of size 2 to file",
+ apr_file_writev(f, vec, 2, &nbytes));
+
+ APR_ASSERT_SUCCESS(tc, "close for writing",
+ apr_file_close(f));
+
+ file_contents_equal(tc, fname, TESTSTR LINE1 LINE2,
+ strlen(TESTSTR) + strlen(LINE1) + strlen(LINE2));
+}
+
+static void test_writev_buffered_seek(abts_case *tc, void *data)
+{
+ apr_file_t *f;
+ apr_status_t rv;
+ apr_off_t off = 0;
+ struct iovec vec[3];
+ apr_size_t nbytes = strlen(TESTSTR);
+ char *str = apr_pcalloc(p, nbytes+1);
+ const char *fname = "data/testwritev_buffered.dat";
+
+ APR_ASSERT_SUCCESS(tc, "open file for writing",
+ apr_file_open(&f, fname,
+ APR_FOPEN_WRITE | APR_FOPEN_READ | APR_FOPEN_BUFFERED,
+ APR_OS_DEFAULT, p));
+
+ rv = apr_file_read(f, str, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, TESTSTR, str);
+ APR_ASSERT_SUCCESS(tc, "buffered seek", apr_file_seek(f, APR_SET, &off));
+
+ vec[0].iov_base = LINE1;
+ vec[0].iov_len = strlen(LINE1);
+ vec[1].iov_base = LINE2;
+ vec[1].iov_len = strlen(LINE2);
+ vec[2].iov_base = TESTSTR;
+ vec[2].iov_len = strlen(TESTSTR);
+
+ APR_ASSERT_SUCCESS(tc, "writev of size 2 to file",
+ apr_file_writev(f, vec, 3, &nbytes));
+
+ APR_ASSERT_SUCCESS(tc, "close for writing",
+ apr_file_close(f));
+
+ file_contents_equal(tc, fname, LINE1 LINE2 TESTSTR,
+ strlen(LINE1) + strlen(LINE2) + strlen(TESTSTR));
+
+ APR_ASSERT_SUCCESS(tc, "remove file", apr_file_remove(fname, p));
+}
+
+static void test_truncate(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *f;
+ const char *fname = "data/testtruncate.dat";
+ const char *s;
+ apr_size_t nbytes;
+ apr_finfo_t finfo;
+
+ apr_file_remove(fname, p);
+
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_CREATE | APR_FOPEN_WRITE, APR_UREAD | APR_UWRITE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ s = "some data";
+ nbytes = strlen(s);
+ rv = apr_file_write(f, s, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(s), nbytes);
+
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_TRUNCATE | APR_FOPEN_WRITE, APR_UREAD | APR_UWRITE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_stat(&finfo, fname, APR_FINFO_SIZE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "File size mismatch, expected 0 (empty)", finfo.size == 0);
+
+ rv = apr_file_remove(fname, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_file_trunc(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *f;
+ const char *fname = "data/testtruncate.dat";
+ const char *s;
+ apr_size_t nbytes;
+ apr_finfo_t finfo;
+
+ apr_file_remove(fname, p);
+
+ /* Test unbuffered */
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_CREATE | APR_FOPEN_READ |
+ APR_FOPEN_WRITE,
+ APR_FPROT_UREAD | APR_FPROT_UWRITE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ s = "some data";
+ nbytes = strlen(s);
+ rv = apr_file_write(f, s, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(s), nbytes);
+ rv = apr_file_trunc(f, 4);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_stat(&finfo, fname, APR_FINFO_SIZE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "File size mismatch, expected 4", finfo.size == 4);
+
+ rv = apr_file_remove(fname, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ /* Test buffered */
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_CREATE | APR_FOPEN_READ |
+ APR_FOPEN_WRITE | APR_FOPEN_BUFFERED,
+ APR_FPROT_UREAD | APR_FPROT_UWRITE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ nbytes = strlen(s);
+ rv = apr_file_write(f, s, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(s), nbytes);
+ rv = apr_file_trunc(f, 4);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_stat(&finfo, fname, APR_FINFO_SIZE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "File size mismatch, expected 4", finfo.size == 4);
+
+ rv = apr_file_remove(fname, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_file_trunc2(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *f;
+ const char *fname = "data/testtruncate.dat";
+ const char *s;
+ apr_size_t nbytes;
+ apr_finfo_t finfo;
+ char c;
+
+ apr_file_remove(fname, p);
+
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_CREATE | APR_FOPEN_READ |
+ APR_FOPEN_WRITE,
+ APR_FPROT_UREAD | APR_FPROT_UWRITE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ s = "some data";
+ nbytes = strlen(s);
+ rv = apr_file_write(f, s, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(s), nbytes);
+ rv = apr_file_trunc(f, 4);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ /* Test apr_file_info_get(). */
+ rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 4, (int)finfo.size);
+ /* EOF is not reported until the next read. */
+ rv = apr_file_eof(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_getc(&c, f);
+ ABTS_INT_EQUAL(tc, APR_EOF, rv);
+ rv = apr_file_eof(f);
+ ABTS_INT_EQUAL(tc, APR_EOF, rv);
+
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_stat(&finfo, fname, APR_FINFO_SIZE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 4, (int)finfo.size);
+
+ rv = apr_file_remove(fname, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_file_trunc_buffered_write(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *f;
+ const char *fname = "data/testtruncate_buffered_write.dat";
+ const char *s;
+ apr_size_t nbytes;
+ apr_finfo_t finfo;
+ char c;
+
+ apr_file_remove(fname, p);
+
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_CREATE | APR_FOPEN_READ |
+ APR_FOPEN_WRITE | APR_FOPEN_BUFFERED,
+ APR_FPROT_UREAD | APR_FPROT_UWRITE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ s = "some data";
+ nbytes = strlen(s);
+ rv = apr_file_write(f, s, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(s), nbytes);
+ rv = apr_file_trunc(f, 4);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ /* Test apr_file_info_get(). */
+ rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 4, (int)finfo.size);
+ /* EOF is not reported until the next read. */
+ rv = apr_file_eof(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_getc(&c, f);
+ ABTS_INT_EQUAL(tc, APR_EOF, rv);
+ rv = apr_file_eof(f);
+ ABTS_INT_EQUAL(tc, APR_EOF, rv);
+
+ rv = apr_file_close(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_stat(&finfo, fname, APR_FINFO_SIZE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 4, (int)finfo.size);
+
+ rv = apr_file_remove(fname, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_file_trunc_buffered_write2(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *f;
+ const char *fname = "data/testtruncate_buffered_write2.dat";
+ apr_finfo_t finfo;
+ char c;
+
+ apr_file_remove(fname, p);
+
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_CREATE | APR_FOPEN_READ |
+ APR_FOPEN_WRITE | APR_FOPEN_BUFFERED,
+ APR_FPROT_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "open test file", rv);
+
+ rv = apr_file_puts("abc", f);
+ APR_ASSERT_SUCCESS(tc, "write first string", rv);
+ rv = apr_file_flush(f);
+ APR_ASSERT_SUCCESS(tc, "flush", rv);
+ rv = apr_file_puts("def", f);
+ APR_ASSERT_SUCCESS(tc, "write second string", rv);
+ /* Truncate behind the write buffer. */
+ rv = apr_file_trunc(f, 2);
+ APR_ASSERT_SUCCESS(tc, "truncate the file", rv);
+ /* Test apr_file_info_get(). */
+ rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, f);
+ APR_ASSERT_SUCCESS(tc, "get file info", rv);
+ ABTS_INT_EQUAL(tc, 2, (int)finfo.size);
+ /* EOF is not reported until the next read. */
+ rv = apr_file_eof(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_getc(&c, f);
+ ABTS_INT_EQUAL(tc, APR_EOF, rv);
+ rv = apr_file_eof(f);
+ ABTS_INT_EQUAL(tc, APR_EOF, rv);
+
+ apr_file_close(f);
+
+ rv = apr_stat(&finfo, fname, APR_FINFO_SIZE, p);
+ APR_ASSERT_SUCCESS(tc, "stat file", rv);
+ ABTS_INT_EQUAL(tc, 2, (int)finfo.size);
+
+ apr_file_remove(fname, p);
+}
+
+static void test_file_trunc_buffered_read(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *f;
+ const char *fname = "data/testtruncate_buffered_read.dat";
+ apr_finfo_t finfo;
+ char c;
+
+ apr_file_remove(fname, p);
+
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_CREATE | APR_FOPEN_READ |
+ APR_FOPEN_WRITE, APR_FPROT_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "open test file", rv);
+
+ rv = apr_file_puts("abc", f);
+ APR_ASSERT_SUCCESS(tc, "write test data", rv);
+ apr_file_close(f);
+
+ rv = apr_file_open(&f, fname,
+ APR_FOPEN_READ | APR_FOPEN_WRITE |
+ APR_FOPEN_BUFFERED, APR_FPROT_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "re-open test file", rv);
+
+ /* Read to fill in the buffer. */
+ rv = apr_file_getc(&c, f);
+ APR_ASSERT_SUCCESS(tc, "read char", rv);
+ /* Truncate the file. */
+ rv = apr_file_trunc(f, 1);
+ APR_ASSERT_SUCCESS(tc, "truncate the file", rv);
+ /* Test apr_file_info_get(). */
+ rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, f);
+ APR_ASSERT_SUCCESS(tc, "get file info", rv);
+ ABTS_INT_EQUAL(tc, 1, (int)finfo.size);
+ /* EOF is not reported until the next read. */
+ rv = apr_file_eof(f);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_getc(&c, f);
+ ABTS_INT_EQUAL(tc, APR_EOF, rv);
+ rv = apr_file_eof(f);
+ ABTS_INT_EQUAL(tc, APR_EOF, rv);
+
+ apr_file_close(f);
+
+ rv = apr_stat(&finfo, fname, APR_FINFO_SIZE, p);
+ APR_ASSERT_SUCCESS(tc, "stat file", rv);
+ ABTS_INT_EQUAL(tc, 1, (int)finfo.size);
+
+ apr_file_remove(fname, p);
+}
+
+static void test_bigfprintf(abts_case *tc, void *data)
+{
+ apr_file_t *f;
+ const char *fname = "data/testbigfprintf.dat";
+ char *to_write;
+ int i;
+
+ apr_file_remove(fname, p);
+
+ APR_ASSERT_SUCCESS(tc, "open test file",
+ apr_file_open(&f, fname,
+ APR_FOPEN_CREATE|APR_FOPEN_WRITE,
+ APR_UREAD|APR_UWRITE, p));
+
+
+ to_write = malloc(HUGE_STRING_LEN + 3);
+
+ for (i = 0; i < HUGE_STRING_LEN + 1; ++i)
+ to_write[i] = 'A' + i%26;
+
+ strcpy(to_write + HUGE_STRING_LEN, "42");
+
+ i = apr_file_printf(f, "%s", to_write);
+ ABTS_INT_EQUAL(tc, HUGE_STRING_LEN + 2, i);
+
+ apr_file_close(f);
+
+ file_contents_equal(tc, fname, to_write, HUGE_STRING_LEN + 2);
+
+ free(to_write);
+}
+
+static void test_fail_write_flush(abts_case *tc, void *data)
+{
+ apr_file_t *f;
+ const char *fname = "data/testflush.dat";
+ apr_status_t rv;
+ char buf[APR_BUFFERSIZE];
+ int n;
+
+ apr_file_remove(fname, p);
+
+ APR_ASSERT_SUCCESS(tc, "open test file",
+ apr_file_open(&f, fname,
+ APR_FOPEN_CREATE|APR_FOPEN_READ|APR_FOPEN_BUFFERED,
+ APR_UREAD|APR_UWRITE, p));
+
+ memset(buf, 'A', sizeof buf);
+
+ /* Try three writes. One of these should fail when it exceeds the
+ * internal buffer and actually tries to write to the file, which
+ * was opened read-only and hence should be unwritable. */
+ for (n = 0, rv = APR_SUCCESS; n < 4 && rv == APR_SUCCESS; n++) {
+ apr_size_t bytes = sizeof buf;
+ rv = apr_file_write(f, buf, &bytes);
+ }
+
+ ABTS_ASSERT(tc, "failed to write to read-only buffered fd",
+ rv != APR_SUCCESS);
+
+ apr_file_close(f);
+}
+
+static void test_fail_read_flush(abts_case *tc, void *data)
+{
+ apr_file_t *f;
+ const char *fname = "data/testflush.dat";
+ apr_status_t rv;
+ char buf[2];
+
+ apr_file_remove(fname, p);
+
+ APR_ASSERT_SUCCESS(tc, "open test file",
+ apr_file_open(&f, fname,
+ APR_FOPEN_CREATE|APR_FOPEN_READ|APR_FOPEN_BUFFERED,
+ APR_UREAD|APR_UWRITE, p));
+
+ /* this write should be buffered. */
+ APR_ASSERT_SUCCESS(tc, "buffered write should succeed",
+ apr_file_puts("hello", f));
+
+ /* Now, trying a read should fail since the write must be flushed,
+ * and should fail with something other than EOF since the file is
+ * opened read-only. */
+ rv = apr_file_read_full(f, buf, 2, NULL);
+
+ ABTS_ASSERT(tc, "read should flush buffered write and fail",
+ rv != APR_SUCCESS && rv != APR_EOF);
+
+ /* Likewise for gets */
+ rv = apr_file_gets(buf, 2, f);
+
+ ABTS_ASSERT(tc, "gets should flush buffered write and fail",
+ rv != APR_SUCCESS && rv != APR_EOF);
+
+ /* Likewise for seek. */
+ {
+ apr_off_t offset = 0;
+
+ rv = apr_file_seek(f, APR_SET, &offset);
+ }
+
+ ABTS_ASSERT(tc, "seek should flush buffered write and fail",
+ rv != APR_SUCCESS && rv != APR_EOF);
+
+ apr_file_close(f);
+}
+
+static void test_xthread(abts_case *tc, void *data)
+{
+ apr_file_t *f;
+ const char *fname = "data/testxthread.dat";
+ apr_status_t rv;
+ apr_int32_t flags = APR_FOPEN_CREATE|APR_FOPEN_READ|APR_FOPEN_WRITE|APR_FOPEN_APPEND|APR_FOPEN_XTHREAD;
+ char buf[128] = { 0 };
+
+ /* Test for bug 38438, opening file with append + xthread and seeking to
+ the end of the file resulted in writes going to the beginning not the
+ end. */
+
+ apr_file_remove(fname, p);
+
+ APR_ASSERT_SUCCESS(tc, "open test file",
+ apr_file_open(&f, fname, flags,
+ APR_UREAD|APR_UWRITE, p));
+
+ APR_ASSERT_SUCCESS(tc, "write should succeed",
+ apr_file_puts("hello", f));
+
+ apr_file_close(f);
+
+ APR_ASSERT_SUCCESS(tc, "open test file",
+ apr_file_open(&f, fname, flags,
+ APR_UREAD|APR_UWRITE, p));
+
+ /* Seek to the end. */
+ {
+ apr_off_t offset = 0;
+
+ rv = apr_file_seek(f, APR_END, &offset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ }
+
+ APR_ASSERT_SUCCESS(tc, "more writes should succeed",
+ apr_file_puts("world", f));
+
+ /* Back to the beginning. */
+ {
+ apr_off_t offset = 0;
+
+ rv = apr_file_seek(f, APR_SET, &offset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ }
+
+ apr_file_read_full(f, buf, sizeof(buf), NULL);
+
+ ABTS_STR_EQUAL(tc, "helloworld", buf);
+
+ apr_file_close(f);
+}
+
+static void test_datasync_on_file(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *f;
+ const char *fname = DIRNAME "/testtest_datasync.dat";
+ apr_size_t bytes_written;
+
+ apr_file_remove(fname, p);
+
+ rv = apr_file_open(&f, fname, APR_FOPEN_CREATE | APR_FOPEN_WRITE,
+ APR_FPROT_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "open test file for writing", rv);
+ rv = apr_file_write_full(f, "abcdef", 6, &bytes_written);
+ APR_ASSERT_SUCCESS(tc, "write to file", rv);
+ rv = apr_file_datasync(f);
+ APR_ASSERT_SUCCESS(tc, "sync file contents", rv);
+ apr_file_close(f);
+
+ apr_file_remove(fname, p);
+}
+
+static void test_datasync_on_stream(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_file_t *f;
+ apr_size_t bytes_written;
+
+ rv = apr_file_open_stdout(&f, p);
+ APR_ASSERT_SUCCESS(tc, "open stdout", rv);
+ rv = apr_file_write_full(f, "abcdef\b\b\b\b\b\b\b", 12, &bytes_written);
+ APR_ASSERT_SUCCESS(tc, "write to stdout", rv);
+ rv = apr_file_datasync(f);
+ if (rv != APR_SUCCESS) {
+#if defined(__APPLE__)
+ ABTS_INT_EQUAL(tc, ENOTSUP, rv);
+#else
+ ABTS_INT_EQUAL(tc, APR_EINVAL, rv);
+#endif
+ }
+}
+
+abts_suite *testfile(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_open_noreadwrite, NULL);
+ abts_run_test(suite, test_open_excl, NULL);
+ abts_run_test(suite, test_open_read, NULL);
+ abts_run_test(suite, test_open_readwrite, NULL);
+ abts_run_test(suite, link_existing, NULL);
+ abts_run_test(suite, link_nonexisting, NULL);
+ abts_run_test(suite, test_read, NULL);
+ abts_run_test(suite, test_readzero, NULL);
+ abts_run_test(suite, test_seek, NULL);
+ abts_run_test(suite, test_filename, NULL);
+ abts_run_test(suite, test_fileclose, NULL);
+ abts_run_test(suite, test_file_remove, NULL);
+ abts_run_test(suite, test_open_write, NULL);
+ abts_run_test(suite, test_open_writecreate, NULL);
+ abts_run_test(suite, test_write, NULL);
+ abts_run_test(suite, test_userdata_set, NULL);
+ abts_run_test(suite, test_userdata_get, NULL);
+ abts_run_test(suite, test_userdata_getnokey, NULL);
+ abts_run_test(suite, test_getc, NULL);
+ abts_run_test(suite, test_ungetc, NULL);
+ abts_run_test(suite, test_gets, NULL);
+ abts_run_test(suite, test_gets_buffered, NULL);
+ abts_run_test(suite, test_puts, NULL);
+ abts_run_test(suite, test_writev, NULL);
+ abts_run_test(suite, test_writev_full, NULL);
+ abts_run_test(suite, test_writev_buffered, NULL);
+ abts_run_test(suite, test_writev_buffered_seek, NULL);
+ abts_run_test(suite, test_bigread, NULL);
+ abts_run_test(suite, test_mod_neg, NULL);
+ abts_run_test(suite, test_truncate, NULL);
+ abts_run_test(suite, test_file_trunc, NULL);
+ abts_run_test(suite, test_file_trunc2, NULL);
+ abts_run_test(suite, test_file_trunc_buffered_write, NULL);
+ abts_run_test(suite, test_file_trunc_buffered_write2, NULL);
+ abts_run_test(suite, test_file_trunc_buffered_read, NULL);
+ abts_run_test(suite, test_bigfprintf, NULL);
+ abts_run_test(suite, test_fail_write_flush, NULL);
+ abts_run_test(suite, test_fail_read_flush, NULL);
+ abts_run_test(suite, test_buffer_set_get, NULL);
+ abts_run_test(suite, test_xthread, NULL);
+ abts_run_test(suite, test_datasync_on_file, NULL);
+ abts_run_test(suite, test_datasync_on_stream, NULL);
+
+ return suite;
+}
+
diff --git a/test/testfilecopy.c b/test/testfilecopy.c
new file mode 100644
index 0000000..5b64bc0
--- /dev/null
+++ b/test/testfilecopy.c
@@ -0,0 +1,138 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_errno.h"
+#include "apr_pools.h"
+
+static void copy_helper(abts_case *tc, const char *from, const char * to,
+ apr_fileperms_t perms, int append, apr_pool_t *p)
+{
+ apr_status_t rv;
+ apr_status_t dest_rv;
+ apr_finfo_t copy;
+ apr_finfo_t orig;
+ apr_finfo_t dest;
+
+ dest_rv = apr_stat(&dest, to, APR_FINFO_SIZE, p);
+
+ if (!append) {
+ rv = apr_file_copy(from, to, perms, p);
+ }
+ else {
+ rv = apr_file_append(from, to, perms, p);
+ }
+ APR_ASSERT_SUCCESS(tc, "Error copying file", rv);
+
+ rv = apr_stat(&orig, from, APR_FINFO_SIZE, p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't stat original file", rv);
+
+ rv = apr_stat(&copy, to, APR_FINFO_SIZE, p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't stat copy file", rv);
+
+ if (!append) {
+ ABTS_ASSERT(tc, "File size differs", orig.size == copy.size);
+ }
+ else {
+ ABTS_ASSERT(tc, "File size differs",
+ ((dest_rv == APR_SUCCESS)
+ ? dest.size : 0) + orig.size == copy.size);
+ }
+}
+
+static void copy_short_file(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ /* make absolutely sure that the dest file doesn't exist. */
+ apr_file_remove("data/file_copy.txt", p);
+
+ copy_helper(tc, "data/file_datafile.txt", "data/file_copy.txt",
+ APR_FILE_SOURCE_PERMS, 0, p);
+ rv = apr_file_remove("data/file_copy.txt", p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't remove copy file", rv);
+}
+
+static void copy_over_existing(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ /* make absolutely sure that the dest file doesn't exist. */
+ apr_file_remove("data/file_copy.txt", p);
+
+ /* This is a cheat. I don't want to create a new file, so I just copy
+ * one file, then I copy another. If the second copy succeeds, then
+ * this works.
+ */
+ copy_helper(tc, "data/file_datafile.txt", "data/file_copy.txt",
+ APR_FILE_SOURCE_PERMS, 0, p);
+
+ copy_helper(tc, "data/mmap_datafile.txt", "data/file_copy.txt",
+ APR_FILE_SOURCE_PERMS, 0, p);
+
+ rv = apr_file_remove("data/file_copy.txt", p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't remove copy file", rv);
+}
+
+static void append_nonexist(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ /* make absolutely sure that the dest file doesn't exist. */
+ apr_file_remove("data/file_copy.txt", p);
+
+ copy_helper(tc, "data/file_datafile.txt", "data/file_copy.txt",
+ APR_FILE_SOURCE_PERMS, 0, p);
+ rv = apr_file_remove("data/file_copy.txt", p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't remove copy file", rv);
+}
+
+static void append_exist(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ /* make absolutely sure that the dest file doesn't exist. */
+ apr_file_remove("data/file_copy.txt", p);
+
+ /* This is a cheat. I don't want to create a new file, so I just copy
+ * one file, then I copy another. If the second copy succeeds, then
+ * this works.
+ */
+ copy_helper(tc, "data/file_datafile.txt", "data/file_copy.txt",
+ APR_FILE_SOURCE_PERMS, 0, p);
+
+ copy_helper(tc, "data/mmap_datafile.txt", "data/file_copy.txt",
+ APR_FILE_SOURCE_PERMS, 1, p);
+
+ rv = apr_file_remove("data/file_copy.txt", p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't remove copy file", rv);
+}
+
+abts_suite *testfilecopy(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, copy_short_file, NULL);
+ abts_run_test(suite, copy_over_existing, NULL);
+
+ abts_run_test(suite, append_nonexist, NULL);
+ abts_run_test(suite, append_exist, NULL);
+
+ return suite;
+}
+
diff --git a/test/testfileinfo.c b/test/testfileinfo.c
new file mode 100644
index 0000000..ff08593
--- /dev/null
+++ b/test/testfileinfo.c
@@ -0,0 +1,263 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_strings.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_poll.h"
+#include "apr_lib.h"
+#include "testutil.h"
+
+#define FILENAME "data/file_datafile.txt"
+#define NEWFILENAME "data/new_datafile.txt"
+#define NEWFILEDATA "This is new text in a new file."
+
+static const struct view_fileinfo
+{
+ apr_int32_t bits;
+ char *description;
+} vfi[] = {
+ {APR_FINFO_MTIME, "MTIME"},
+ {APR_FINFO_CTIME, "CTIME"},
+ {APR_FINFO_ATIME, "ATIME"},
+ {APR_FINFO_SIZE, "SIZE"},
+ {APR_FINFO_DEV, "DEV"},
+ {APR_FINFO_INODE, "INODE"},
+ {APR_FINFO_NLINK, "NLINK"},
+ {APR_FINFO_TYPE, "TYPE"},
+ {APR_FINFO_USER, "USER"},
+ {APR_FINFO_GROUP, "GROUP"},
+ {APR_FINFO_UPROT, "UPROT"},
+ {APR_FINFO_GPROT, "GPROT"},
+ {APR_FINFO_WPROT, "WPROT"},
+ {0, NULL}
+};
+
+static void finfo_equal(abts_case *tc, apr_finfo_t *f1, apr_finfo_t *f2)
+{
+ /* Minimum supported flags across all platforms (APR_FINFO_MIN) */
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo must return APR_FINFO_TYPE",
+ (f1->valid & f2->valid & APR_FINFO_TYPE));
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in filetype",
+ f1->filetype == f2->filetype);
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo must return APR_FINFO_SIZE",
+ (f1->valid & f2->valid & APR_FINFO_SIZE));
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in size",
+ f1->size == f2->size);
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo must return APR_FINFO_ATIME",
+ (f1->valid & f2->valid & APR_FINFO_ATIME));
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in atime",
+ f1->atime == f2->atime);
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo must return APR_FINFO_MTIME",
+ (f1->valid & f2->valid & APR_FINFO_MTIME));
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in mtime",
+ f1->mtime == f2->mtime);
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo must return APR_FINFO_CTIME",
+ (f1->valid & f2->valid & APR_FINFO_CTIME));
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in ctime",
+ f1->ctime == f2->ctime);
+
+ if (f1->valid & f2->valid & APR_FINFO_NAME)
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in name",
+ !strcmp(f1->name, f2->name));
+ if (f1->fname && f2->fname)
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in fname",
+ !strcmp(f1->fname, f2->fname));
+
+ /* Additional supported flags not supported on all platforms */
+ if (f1->valid & f2->valid & APR_FINFO_USER)
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in user",
+ !apr_uid_compare(f1->user, f2->user));
+ if (f1->valid & f2->valid & APR_FINFO_GROUP)
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in group",
+ !apr_gid_compare(f1->group, f2->group));
+ if (f1->valid & f2->valid & APR_FINFO_INODE)
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in inode",
+ f1->inode == f2->inode);
+ if (f1->valid & f2->valid & APR_FINFO_DEV)
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in device",
+ f1->device == f2->device);
+ if (f1->valid & f2->valid & APR_FINFO_NLINK)
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in nlink",
+ f1->nlink == f2->nlink);
+ if (f1->valid & f2->valid & APR_FINFO_CSIZE)
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in csize",
+ f1->csize == f2->csize);
+ if (f1->valid & f2->valid & APR_FINFO_PROT)
+ ABTS_ASSERT(tc, "apr_stat and apr_getfileinfo differ in protection",
+ f1->protection == f2->protection);
+}
+
+static void test_info_get(abts_case *tc, void *data)
+{
+ apr_file_t *thefile;
+ apr_finfo_t finfo;
+ apr_status_t rv;
+
+ rv = apr_file_open(&thefile, FILENAME, APR_FOPEN_READ, APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_info_get(&finfo, APR_FINFO_NORM, thefile);
+ if (APR_STATUS_IS_INCOMPLETE(rv)) {
+ char *str;
+ int i;
+ str = apr_pstrdup(p, "APR_INCOMPLETE: Missing ");
+ for (i = 0; vfi[i].bits; ++i) {
+ if (vfi[i].bits & ~finfo.valid) {
+ str = apr_pstrcat(p, str, vfi[i].description, " ", NULL);
+ }
+ }
+ ABTS_FAIL(tc, str);
+ }
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ apr_file_close(thefile);
+}
+
+static void test_stat(abts_case *tc, void *data)
+{
+ apr_finfo_t finfo;
+ apr_status_t rv;
+
+ rv = apr_stat(&finfo, FILENAME, APR_FINFO_NORM, p);
+ if (APR_STATUS_IS_INCOMPLETE(rv)) {
+ char *str;
+ int i;
+ str = apr_pstrdup(p, "APR_INCOMPLETE: Missing ");
+ for (i = 0; vfi[i].bits; ++i) {
+ if (vfi[i].bits & ~finfo.valid) {
+ str = apr_pstrcat(p, str, vfi[i].description, " ", NULL);
+ }
+ }
+ ABTS_FAIL(tc, str);
+ }
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_stat_eq_finfo(abts_case *tc, void *data)
+{
+ apr_file_t *thefile;
+ apr_finfo_t finfo;
+ apr_finfo_t stat_finfo;
+ apr_status_t rv;
+
+ rv = apr_file_open(&thefile, FILENAME, APR_FOPEN_READ, APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_info_get(&finfo, APR_FINFO_NORM, thefile);
+
+ /* Opening the file may have toggled the atime member (time last
+ * accessed), so fetch our apr_stat() after getting the fileinfo
+ * of the open file...
+ */
+ rv = apr_stat(&stat_finfo, FILENAME, APR_FINFO_NORM, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ apr_file_close(thefile);
+
+ finfo_equal(tc, &stat_finfo, &finfo);
+}
+
+static void test_buffered_write_size(abts_case *tc, void *data)
+{
+ const apr_size_t data_len = strlen(NEWFILEDATA);
+ apr_file_t *thefile;
+ apr_finfo_t finfo;
+ apr_status_t rv;
+ apr_size_t bytes;
+
+ rv = apr_file_open(&thefile, NEWFILENAME,
+ APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE
+ | APR_FOPEN_BUFFERED | APR_FOPEN_DELONCLOSE,
+ APR_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "open file", rv);
+
+ /* A funny thing happened to me the other day: I wrote something
+ * into a buffered file, then asked for its size using
+ * apr_file_info_get; and guess what? The size was 0! That's not a
+ * nice way to behave.
+ */
+ bytes = data_len;
+ rv = apr_file_write(thefile, NEWFILEDATA, &bytes);
+ APR_ASSERT_SUCCESS(tc, "write file contents", rv);
+ ABTS_TRUE(tc, data_len == bytes);
+
+ rv = apr_file_info_get(&finfo, APR_FINFO_SIZE, thefile);
+ APR_ASSERT_SUCCESS(tc, "get file size", rv);
+ ABTS_TRUE(tc, bytes == (apr_size_t) finfo.size);
+ apr_file_close(thefile);
+}
+
+static void test_mtime_set(abts_case *tc, void *data)
+{
+ apr_file_t *thefile;
+ apr_finfo_t finfo;
+ apr_time_t epoch = 0;
+ apr_status_t rv;
+
+ /* This test sort of depends on the system clock being at least
+ * marginally ccorrect; We'll be setting the modification time to
+ * the epoch.
+ */
+ rv = apr_file_open(&thefile, NEWFILENAME,
+ APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE
+ | APR_FOPEN_BUFFERED | APR_FOPEN_DELONCLOSE,
+ APR_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "open file", rv);
+
+ /* Check that the current mtime is not the epoch */
+ rv = apr_stat(&finfo, NEWFILENAME, APR_FINFO_MTIME, p);
+ if (APR_STATUS_IS_INCOMPLETE(rv)) {
+ char *str;
+ int i;
+ str = apr_pstrdup(p, "APR_INCOMPLETE: Missing ");
+ for (i = 0; vfi[i].bits; ++i) {
+ if (vfi[i].bits & ~finfo.valid) {
+ str = apr_pstrcat(p, str, vfi[i].description, " ", NULL);
+ }
+ }
+ ABTS_FAIL(tc, str);
+ }
+ APR_ASSERT_SUCCESS(tc, "get initial mtime", rv);
+ ABTS_TRUE(tc, finfo.mtime != epoch);
+
+ /* Reset the mtime to the epoch and verify the result.
+ * Note: we blindly assume that if the first apr_stat succeeded,
+ * the second one will, too.
+ */
+ rv = apr_file_mtime_set(NEWFILENAME, epoch, p);
+ APR_ASSERT_SUCCESS(tc, "set mtime", rv);
+
+ rv = apr_stat(&finfo, NEWFILENAME, APR_FINFO_MTIME, p);
+ APR_ASSERT_SUCCESS(tc, "get modified mtime", rv);
+ ABTS_TRUE(tc, finfo.mtime == epoch);
+
+ apr_file_close(thefile);
+}
+
+abts_suite *testfileinfo(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_info_get, NULL);
+ abts_run_test(suite, test_stat, NULL);
+ abts_run_test(suite, test_stat_eq_finfo, NULL);
+ abts_run_test(suite, test_buffered_write_size, NULL);
+ abts_run_test(suite, test_mtime_set, NULL);
+
+ return suite;
+}
+
diff --git a/test/testflock.c b/test/testflock.c
new file mode 100644
index 0000000..b9e8c79
--- /dev/null
+++ b/test/testflock.c
@@ -0,0 +1,104 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testflock.h"
+#include "testutil.h"
+#include "apr_pools.h"
+#include "apr_thread_proc.h"
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+
+static int launch_reader(abts_case *tc)
+{
+ apr_proc_t proc = {0};
+ apr_procattr_t *procattr;
+ const char *args[2];
+ apr_status_t rv;
+ apr_exit_why_e why;
+ int exitcode;
+
+ rv = apr_procattr_create(&procattr, p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't create procattr", rv);
+
+ rv = apr_procattr_io_set(procattr, APR_NO_PIPE, APR_NO_PIPE,
+ APR_NO_PIPE);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set io in procattr", rv);
+
+ rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM_ENV);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set copy environment", rv);
+
+ rv = apr_procattr_error_check_set(procattr, 1);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set error check in procattr", rv);
+
+ args[0] = "tryread" EXTENSION;
+ args[1] = NULL;
+ rv = apr_proc_create(&proc, TESTBINPATH "tryread" EXTENSION, args, NULL, procattr, p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't launch program", rv);
+
+ ABTS_ASSERT(tc, "wait for child process",
+ apr_proc_wait(&proc, &exitcode, &why, APR_WAIT) == APR_CHILD_DONE);
+
+ ABTS_ASSERT(tc, "child terminated normally", why == APR_PROC_EXIT);
+ return exitcode;
+}
+
+static void test_withlock(abts_case *tc, void *data)
+{
+ apr_file_t *file;
+ apr_status_t rv;
+ int code;
+
+ rv = apr_file_open(&file, TESTFILE, APR_FOPEN_WRITE|APR_FOPEN_CREATE,
+ APR_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "Could not create file.", rv);
+ ABTS_PTR_NOTNULL(tc, file);
+
+ rv = apr_file_lock(file, APR_FLOCK_EXCLUSIVE);
+ APR_ASSERT_SUCCESS(tc, "Could not lock the file.", rv);
+ ABTS_PTR_NOTNULL(tc, file);
+
+ code = launch_reader(tc);
+ ABTS_INT_EQUAL(tc, FAILED_READ, code);
+
+ (void) apr_file_close(file);
+}
+
+static void test_withoutlock(abts_case *tc, void *data)
+{
+ int code;
+
+ code = launch_reader(tc);
+ ABTS_INT_EQUAL(tc, SUCCESSFUL_READ, code);
+}
+
+static void remove_lockfile(abts_case *tc, void *data)
+{
+ APR_ASSERT_SUCCESS(tc, "Couldn't remove lock file.",
+ apr_file_remove(TESTFILE, p));
+}
+
+abts_suite *testflock(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_withlock, NULL);
+ abts_run_test(suite, test_withoutlock, NULL);
+ abts_run_test(suite, remove_lockfile, NULL);
+
+ return suite;
+}
diff --git a/test/testflock.h b/test/testflock.h
new file mode 100644
index 0000000..554a0ce
--- /dev/null
+++ b/test/testflock.h
@@ -0,0 +1,27 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TESTFLOCK
+#define TESTFLOCK
+
+#define TESTFILE "data/testfile.lock"
+
+#define FAILED_READ 0
+#define SUCCESSFUL_READ 1
+#define UNEXPECTED_ERROR 2
+
+#endif
+
diff --git a/test/testfmt.c b/test/testfmt.c
new file mode 100644
index 0000000..5b066dd
--- /dev/null
+++ b/test/testfmt.c
@@ -0,0 +1,166 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr.h"
+#include "apr_portable.h"
+#include "apr_strings.h"
+
+static void ssize_t_fmt(abts_case *tc, void *data)
+{
+ char buf[100];
+ apr_ssize_t var = 0;
+
+ sprintf(buf, "%" APR_SSIZE_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "0", buf);
+ apr_snprintf(buf, sizeof(buf), "%" APR_SSIZE_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "0", buf);
+}
+
+static void size_t_fmt(abts_case *tc, void *data)
+{
+ char buf[100];
+ apr_size_t var = 0;
+
+ sprintf(buf, "%" APR_SIZE_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "0", buf);
+ apr_snprintf(buf, sizeof(buf), "%" APR_SIZE_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "0", buf);
+}
+
+static void time_t_fmt(abts_case *tc, void *data)
+{
+ char buf[100];
+ apr_time_t var = 1;
+
+ sprintf(buf, "%" APR_TIME_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "1", buf);
+ apr_snprintf(buf, sizeof(buf), "%" APR_TIME_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "1", buf);
+}
+
+static void off_t_fmt(abts_case *tc, void *data)
+{
+ char buf[100];
+ apr_off_t var = 0;
+
+ sprintf(buf, "%" APR_OFF_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "0", buf);
+ apr_snprintf(buf, sizeof(buf), "%" APR_OFF_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "0", buf);
+}
+
+static void pid_t_fmt(abts_case *tc, void *data)
+{
+ char buf[100];
+ pid_t var = 0;
+
+ sprintf(buf, "%" APR_PID_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "0", buf);
+ apr_snprintf(buf, sizeof(buf), "%" APR_PID_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "0", buf);
+}
+
+static void int64_t_fmt(abts_case *tc, void *data)
+{
+ char buf[100];
+ apr_int64_t var = 0;
+
+ sprintf(buf, "%" APR_INT64_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "0", buf);
+ apr_snprintf(buf, sizeof(buf), "%" APR_INT64_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "0", buf);
+}
+
+static void uint64_t_fmt(abts_case *tc, void *data)
+{
+ char buf[100];
+ apr_uint64_t var = APR_UINT64_C(14000000);
+
+ sprintf(buf, "%" APR_UINT64_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "14000000", buf);
+ apr_snprintf(buf, sizeof(buf), "%" APR_UINT64_T_FMT, var);
+ ABTS_STR_EQUAL(tc, "14000000", buf);
+}
+
+static void uint64_t_hex_fmt(abts_case *tc, void *data)
+{
+ char buf[100];
+ apr_uint64_t var = APR_UINT64_C(14000000);
+
+ sprintf(buf, "%" APR_UINT64_T_HEX_FMT, var);
+ ABTS_STR_EQUAL(tc, "d59f80", buf);
+ apr_snprintf(buf, sizeof(buf), "%" APR_UINT64_T_HEX_FMT, var);
+ ABTS_STR_EQUAL(tc, "d59f80", buf);
+}
+
+static void more_int64_fmts(abts_case *tc, void *data)
+{
+ char buf[100];
+ apr_int64_t i = APR_INT64_C(-42);
+ apr_int64_t ibig = APR_INT64_C(-314159265358979323);
+ apr_uint64_t ui = APR_UINT64_C(42);
+ apr_uint64_t big = APR_UINT64_C(10267677267010969076);
+
+ apr_snprintf(buf, sizeof buf, "%" APR_INT64_T_FMT, i);
+ ABTS_STR_EQUAL(tc, "-42", buf);
+
+ apr_snprintf(buf, sizeof buf, "%" APR_UINT64_T_FMT, ui);
+ ABTS_STR_EQUAL(tc, "42", buf);
+
+ apr_snprintf(buf, sizeof buf, "%" APR_UINT64_T_FMT, big);
+ ABTS_STR_EQUAL(tc, "10267677267010969076", buf);
+
+ apr_snprintf(buf, sizeof buf, "%" APR_INT64_T_FMT, ibig);
+ ABTS_STR_EQUAL(tc, "-314159265358979323", buf);
+}
+
+static void error_fmt(abts_case *tc, void *data)
+{
+ char ebuf[150], sbuf[150], *s;
+ apr_status_t rv;
+
+ rv = APR_SUCCESS;
+ apr_strerror(rv, ebuf, sizeof ebuf);
+ apr_snprintf(sbuf, sizeof sbuf, "%pm", &rv);
+ ABTS_STR_EQUAL(tc, sbuf, ebuf);
+
+ rv = APR_ENOTIMPL;
+ s = apr_pstrcat(p, "foo-",
+ apr_strerror(rv, ebuf, sizeof ebuf),
+ "-bar", NULL);
+ apr_snprintf(sbuf, sizeof sbuf, "foo-%pm-bar", &rv);
+ ABTS_STR_EQUAL(tc, sbuf, s);
+}
+
+abts_suite *testfmt(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, ssize_t_fmt, NULL);
+ abts_run_test(suite, size_t_fmt, NULL);
+ abts_run_test(suite, time_t_fmt, NULL);
+ abts_run_test(suite, off_t_fmt, NULL);
+ abts_run_test(suite, pid_t_fmt, NULL);
+ abts_run_test(suite, int64_t_fmt, NULL);
+ abts_run_test(suite, uint64_t_fmt, NULL);
+ abts_run_test(suite, uint64_t_hex_fmt, NULL);
+ abts_run_test(suite, more_int64_fmts, NULL);
+ abts_run_test(suite, error_fmt, NULL);
+
+ return suite;
+}
+
diff --git a/test/testfnmatch.c b/test/testfnmatch.c
new file mode 100644
index 0000000..b04b33f
--- /dev/null
+++ b/test/testfnmatch.c
@@ -0,0 +1,256 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_file_info.h"
+#include "apr_fnmatch.h"
+#include "apr_tables.h"
+
+/* XXX NUM_FILES must be equal to the nummber of expected files with a
+ * .txt extension in the data directory at the time testfnmatch
+ * happens to be run (!?!). */
+
+#define NUM_FILES (2)
+
+#define APR_FNM_BITS 15
+#define APR_FNM_FAILBIT 256
+
+#define FAILS_IF(X) 0, X
+#define SUCCEEDS_IF(X) X, 256
+#define SUCCEEDS 0, 256
+#define FAILS 256, 0
+
+static struct pattern_s {
+ const char *pattern;
+ const char *string;
+ int require_flags;
+ int fail_flags;
+} patterns[] = {
+
+/* Pattern, String to Test, Flags to Match */
+ {"", "test", FAILS},
+ {"", "*", FAILS},
+ {"test", "*", FAILS},
+ {"test", "test", SUCCEEDS},
+
+ /* Remember C '\\' is a single backslash in pattern */
+ {"te\\st", "test", FAILS_IF(APR_FNM_NOESCAPE)},
+ {"te\\\\st", "te\\st", FAILS_IF(APR_FNM_NOESCAPE)},
+ {"te\\*t", "te*t", FAILS_IF(APR_FNM_NOESCAPE)},
+ {"te\\*t", "test", FAILS},
+ {"te\\?t", "te?t", FAILS_IF(APR_FNM_NOESCAPE)},
+ {"te\\?t", "test", FAILS},
+
+ {"tesT", "test", SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
+ {"test", "Test", SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
+ {"tEst", "teSt", SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
+
+ {"?est", "test", SUCCEEDS},
+ {"te?t", "test", SUCCEEDS},
+ {"tes?", "test", SUCCEEDS},
+ {"test?", "test", FAILS},
+
+ {"*", "", SUCCEEDS},
+ {"*", "test", SUCCEEDS},
+ {"*test", "test", SUCCEEDS},
+ {"*est", "test", SUCCEEDS},
+ {"*st", "test", SUCCEEDS},
+ {"t*t", "test", SUCCEEDS},
+ {"te*t", "test", SUCCEEDS},
+ {"te*st", "test", SUCCEEDS},
+ {"te*", "test", SUCCEEDS},
+ {"tes*", "test", SUCCEEDS},
+ {"test*", "test", SUCCEEDS},
+
+ {".[\\-\\t]", ".t", SUCCEEDS},
+ {"test*?*[a-z]*", "testgoop", SUCCEEDS},
+ {"te[^x]t", "test", SUCCEEDS},
+ {"te[^abc]t", "test", SUCCEEDS},
+ {"te[^x]t", "test", SUCCEEDS},
+ {"te[!x]t", "test", SUCCEEDS},
+ {"te[^x]t", "text", FAILS},
+ {"te[^\\x]t", "text", FAILS},
+ {"te[^x\\", "text", FAILS},
+ {"te[/]t", "text", FAILS},
+ {"te[S]t", "test", SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
+ {"te[r-t]t", "test", SUCCEEDS},
+ {"te[r-t]t", "teSt", SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
+ {"te[r-T]t", "test", SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
+ {"te[R-T]t", "test", SUCCEEDS_IF(APR_FNM_CASE_BLIND)},
+ {"te[r-Tz]t", "tezt", SUCCEEDS},
+ {"te[R-T]t", "tent", FAILS},
+ {"tes[]t]", "test", SUCCEEDS},
+ {"tes[t-]", "test", SUCCEEDS},
+ {"tes[t-]]", "test]", SUCCEEDS},
+ {"tes[t-]]", "test", FAILS},
+ {"tes[u-]", "test", FAILS},
+ {"tes[t-]", "tes[t-]", FAILS},
+ {"test[/-/]", "test[/-/]", SUCCEEDS_IF(APR_FNM_PATHNAME)},
+ {"test[\\/-/]", "test[/-/]", APR_FNM_PATHNAME, APR_FNM_NOESCAPE},
+ {"test[/-\\/]", "test[/-/]", APR_FNM_PATHNAME, APR_FNM_NOESCAPE},
+ {"test[/-/]", "test/", FAILS_IF(APR_FNM_PATHNAME)},
+ {"test[\\/-/]", "test/", FAILS_IF(APR_FNM_PATHNAME)},
+ {"test[/-\\/]", "test/", FAILS_IF(APR_FNM_PATHNAME)},
+
+ {"/", "", FAILS},
+ {"", "/", FAILS},
+ {"/test", "test", FAILS},
+ {"test", "/test", FAILS},
+ {"test/", "test", FAILS},
+ {"test", "test/", FAILS},
+ {"\\/test", "/test", FAILS_IF(APR_FNM_NOESCAPE)},
+ {"*test", "/test", FAILS_IF(APR_FNM_PATHNAME)},
+ {"/*/test/", "/test", FAILS},
+ {"/*/test/", "/test/test/", SUCCEEDS},
+ {"test/this", "test/", FAILS},
+ {"test/", "test/this", FAILS},
+ {"test*/this", "test/this", SUCCEEDS},
+ {"test*/this", "test/that", FAILS},
+ {"test/*this", "test/this", SUCCEEDS},
+
+ {".*", ".this", SUCCEEDS},
+ {"*", ".this", FAILS_IF(APR_FNM_PERIOD)},
+ {"?this", ".this", FAILS_IF(APR_FNM_PERIOD)},
+ {"[.]this", ".this", FAILS_IF(APR_FNM_PERIOD)},
+
+ {"test/this", "test/this", SUCCEEDS},
+ {"test?this", "test/this", FAILS_IF(APR_FNM_PATHNAME)},
+ {"test*this", "test/this", FAILS_IF(APR_FNM_PATHNAME)},
+ {"test[/]this", "test/this", FAILS_IF(APR_FNM_PATHNAME)},
+
+ {"test/.*", "test/.this", SUCCEEDS},
+ {"test/*", "test/.this", FAILS_IF(APR_FNM_PERIOD | APR_FNM_PATHNAME)},
+ {"test/?this", "test/.this", FAILS_IF(APR_FNM_PERIOD | APR_FNM_PATHNAME)},
+ {"test/[.]this", "test/.this", FAILS_IF(APR_FNM_PERIOD | APR_FNM_PATHNAME)},
+
+ {NULL, NULL, 0}
+};
+
+
+
+static void test_fnmatch(abts_case *tc, void *data)
+{
+ struct pattern_s *test = patterns;
+ char buf[80];
+ int i = APR_FNM_BITS + 1;
+ int res;
+
+ for (test = patterns; test->pattern; ++test)
+ {
+ for (i = 0; i <= APR_FNM_BITS; ++i)
+ {
+ res = apr_fnmatch(test->pattern, test->string, i);
+ if (((i & test->require_flags) != test->require_flags)
+ || ((i & test->fail_flags) == test->fail_flags)) {
+ if (res != APR_FNM_NOMATCH)
+ break;
+ }
+ else {
+ if (res != 0)
+ break;
+ }
+ }
+ if (i <= APR_FNM_BITS)
+ break;
+ }
+
+ if (i <= APR_FNM_BITS) {
+ sprintf(buf, "apr_fnmatch(\"%s\", \"%s\", %d) returns %d\n",
+ test->pattern, test->string, i, res);
+ abts_fail(tc, buf, __LINE__);
+ }
+}
+
+static void test_fnmatch_test(abts_case *tc, void *data)
+{
+ static const struct test {
+ const char *pattern;
+ int result;
+ } ft_tests[] = {
+ { "a*b", 1 },
+ { "a?", 1 },
+ { "a\\b?", 1 },
+ { "a[b-c]", 1 },
+ { "a", 0 },
+ { "a\\", 0 },
+ { NULL, 0 }
+ };
+ const struct test *t;
+
+ for (t = ft_tests; t->pattern != NULL; t++) {
+ int res = apr_fnmatch_test(t->pattern);
+
+ if (res != t->result) {
+ char buf[128];
+
+ sprintf(buf, "apr_fnmatch_test(\"%s\") = %d, expected %d\n",
+ t->pattern, res, t->result);
+ abts_fail(tc, buf, __LINE__);
+ }
+ }
+}
+
+static void test_glob(abts_case *tc, void *data)
+{
+ int i;
+ char **list;
+ apr_array_header_t *result;
+
+ APR_ASSERT_SUCCESS(tc, "glob match against data/*.txt",
+ apr_match_glob("data\\*.txt", &result, p));
+
+ ABTS_INT_EQUAL(tc, NUM_FILES, result->nelts);
+
+ list = (char **)result->elts;
+ for (i = 0; i < result->nelts; i++) {
+ char *dot = strrchr(list[i], '.');
+ ABTS_STR_EQUAL(tc, ".txt", dot);
+ }
+}
+
+static void test_glob_currdir(abts_case *tc, void *data)
+{
+ int i;
+ char **list;
+ apr_array_header_t *result;
+ apr_filepath_set("data", p);
+
+ APR_ASSERT_SUCCESS(tc, "glob match against *.txt with data as current",
+ apr_match_glob("*.txt", &result, p));
+
+
+ ABTS_INT_EQUAL(tc, NUM_FILES, result->nelts);
+
+ list = (char **)result->elts;
+ for (i = 0; i < result->nelts; i++) {
+ char *dot = strrchr(list[i], '.');
+ ABTS_STR_EQUAL(tc, ".txt", dot);
+ }
+ apr_filepath_set("..", p);
+}
+
+abts_suite *testfnmatch(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_fnmatch, NULL);
+ abts_run_test(suite, test_fnmatch_test, NULL);
+ abts_run_test(suite, test_glob, NULL);
+ abts_run_test(suite, test_glob_currdir, NULL);
+
+ return suite;
+}
+
diff --git a/test/testglobalmutex.c b/test/testglobalmutex.c
new file mode 100644
index 0000000..c79884c
--- /dev/null
+++ b/test/testglobalmutex.c
@@ -0,0 +1,143 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testglobalmutex.h"
+#include "apr_thread_proc.h"
+#include "apr_global_mutex.h"
+#include "apr_strings.h"
+#include "apr_errno.h"
+#include "testutil.h"
+
+static void launch_child(abts_case *tc, apr_lockmech_e mech,
+ apr_proc_t *proc, apr_pool_t *p)
+{
+ apr_procattr_t *procattr;
+ const char *args[3];
+ apr_status_t rv;
+
+ rv = apr_procattr_create(&procattr, p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't create procattr", rv);
+
+ rv = apr_procattr_io_set(procattr, APR_NO_PIPE, APR_NO_PIPE,
+ APR_NO_PIPE);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set io in procattr", rv);
+
+ rv = apr_procattr_error_check_set(procattr, 1);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set error check in procattr", rv);
+
+ args[0] = "globalmutexchild" EXTENSION;
+ args[1] = (const char*)apr_itoa(p, (int)mech);
+ args[2] = NULL;
+ rv = apr_proc_create(proc, TESTBINPATH "globalmutexchild" EXTENSION, args, NULL,
+ procattr, p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't launch program", rv);
+}
+
+static int wait_child(abts_case *tc, apr_proc_t *proc)
+{
+ int exitcode;
+ apr_exit_why_e why;
+
+ ABTS_ASSERT(tc, "Error waiting for child process",
+ apr_proc_wait(proc, &exitcode, &why, APR_WAIT) == APR_CHILD_DONE);
+
+ ABTS_ASSERT(tc, "child didn't terminate normally", why == APR_PROC_EXIT);
+ return exitcode;
+}
+
+/* return symbolic name for a locking meechanism */
+static const char *mutexname(apr_lockmech_e mech)
+{
+ switch (mech) {
+ case APR_LOCK_FCNTL: return "fcntl";
+ case APR_LOCK_FLOCK: return "flock";
+ case APR_LOCK_SYSVSEM: return "sysvsem";
+ case APR_LOCK_PROC_PTHREAD: return "proc_pthread";
+ case APR_LOCK_POSIXSEM: return "posixsem";
+ case APR_LOCK_DEFAULT: return "default";
+ case APR_LOCK_DEFAULT_TIMED: return "default_timed";
+ default: return "unknown";
+ }
+}
+
+static void test_exclusive(abts_case *tc, void *data)
+{
+ apr_lockmech_e mech = *(apr_lockmech_e *)data;
+ apr_proc_t p1, p2, p3, p4;
+ apr_status_t rv;
+ apr_global_mutex_t *global_lock;
+ int x = 0;
+ abts_log_message("lock mechanism is: ");
+ abts_log_message(mutexname(mech));
+
+ rv = apr_global_mutex_create(&global_lock, LOCKNAME, mech, p);
+ if (rv == APR_ENOTIMPL) {
+ /* MacOS lacks TIMED implementation, so don't fail for ENOTIMPL */
+ ABTS_NOT_IMPL(tc, "global mutex TIMED not implemented");
+ return;
+ }
+ APR_ASSERT_SUCCESS(tc, "Error creating mutex", rv);
+
+ launch_child(tc, mech, &p1, p);
+ launch_child(tc, mech, &p2, p);
+ launch_child(tc, mech, &p3, p);
+ launch_child(tc, mech, &p4, p);
+
+ x += wait_child(tc, &p1);
+ x += wait_child(tc, &p2);
+ x += wait_child(tc, &p3);
+ x += wait_child(tc, &p4);
+
+ if (x != MAX_COUNTER) {
+ char buf[200];
+ sprintf(buf, "global mutex '%s' failed: %d not %d",
+ mutexname(mech), x, MAX_COUNTER);
+ abts_fail(tc, buf, __LINE__);
+ }
+}
+
+abts_suite *testglobalmutex(abts_suite *suite)
+{
+ apr_lockmech_e mech = APR_LOCK_DEFAULT;
+
+ suite = ADD_SUITE(suite)
+ abts_run_test(suite, test_exclusive, &mech);
+#if APR_HAS_POSIXSEM_SERIALIZE
+ mech = APR_LOCK_POSIXSEM;
+ abts_run_test(suite, test_exclusive, &mech);
+#endif
+#if APR_HAS_SYSVSEM_SERIALIZE
+ mech = APR_LOCK_SYSVSEM;
+ abts_run_test(suite, test_exclusive, &mech);
+#endif
+#if APR_HAS_PROC_PTHREAD_SERIALIZE
+ mech = APR_LOCK_PROC_PTHREAD;
+ abts_run_test(suite, test_exclusive, &mech);
+#endif
+#if APR_HAS_FCNTL_SERIALIZE
+ mech = APR_LOCK_FCNTL;
+ abts_run_test(suite, test_exclusive, &mech);
+#endif
+#if APR_HAS_FLOCK_SERIALIZE
+ mech = APR_LOCK_FLOCK;
+ abts_run_test(suite, test_exclusive, &mech);
+#endif
+ mech = APR_LOCK_DEFAULT_TIMED;
+ abts_run_test(suite, test_exclusive, &mech);
+
+ return suite;
+}
+
diff --git a/test/testglobalmutex.h b/test/testglobalmutex.h
new file mode 100644
index 0000000..0270628
--- /dev/null
+++ b/test/testglobalmutex.h
@@ -0,0 +1,27 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TESTGLOBALMUTEX_H
+#define TESTGLOBALMUTEX_H
+
+/* set this to 255 so that the child processes can return it successfully. */
+#define MAX_ITER 255
+#define MAX_COUNTER (MAX_ITER * 4)
+
+#define LOCKNAME "data/apr_globalmutex.lock"
+
+#endif
+
diff --git a/test/testhash.c b/test/testhash.c
new file mode 100644
index 0000000..3c84190
--- /dev/null
+++ b/test/testhash.c
@@ -0,0 +1,541 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_hash.h"
+
+#define MAX_LTH 256
+#define MAX_DEPTH 11
+
+static int comp_string(const void *str1, const void *str2)
+{
+ return strcmp(str1,str2);
+}
+
+static void dump_hash(apr_pool_t *p, apr_hash_t *h, char str[][MAX_LTH])
+{
+ apr_hash_index_t *hi;
+ int i = 0;
+
+ for (hi = apr_hash_first(p, h); hi; hi = apr_hash_next(hi)) {
+ const char *key = apr_hash_this_key(hi);
+ apr_ssize_t len = apr_hash_this_key_len(hi);
+ char *val = apr_hash_this_val(hi);
+
+ str[i][0]='\0';
+ apr_snprintf(str[i], MAX_LTH, "%sKey %s (%" APR_SSIZE_T_FMT ") Value %s\n",
+ str[i], key, len, val);
+ i++;
+ }
+ str[i][0]='\0';
+ apr_snprintf(str[i], MAX_LTH, "%s#entries %d\n", str[i], i);
+
+ /* Sort the result strings so that they can be checked for expected results easily,
+ * without having to worry about platform quirks
+ */
+ qsort(
+ str, /* Pointer to elements */
+ i, /* number of elements */
+ MAX_LTH, /* size of one element */
+ comp_string /* Pointer to comparison routine */
+ );
+}
+
+static void sum_hash(apr_pool_t *p, apr_hash_t *h, int *pcount, int *keySum, int *valSum)
+{
+ apr_hash_index_t *hi;
+ void *val, *key;
+ int count = 0;
+
+ *keySum = 0;
+ *valSum = 0;
+ *pcount = 0;
+ for (hi = apr_hash_first(p, h); hi; hi = apr_hash_next(hi)) {
+ apr_hash_this(hi, (void*)&key, NULL, &val);
+ *valSum += *(int *)val;
+ *keySum += *(int *)key;
+ count++;
+ }
+ *pcount=count;
+}
+
+static void hash_make(abts_case *tc, void *data)
+{
+ apr_hash_t *h = NULL;
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+}
+
+static void hash_set(abts_case *tc, void *data)
+{
+ apr_hash_t *h = NULL;
+ char *result = NULL;
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_hash_set(h, "key", APR_HASH_KEY_STRING, "value");
+ result = apr_hash_get(h, "key", APR_HASH_KEY_STRING);
+ ABTS_STR_EQUAL(tc, "value", result);
+}
+
+static void hash_reset(abts_case *tc, void *data)
+{
+ apr_hash_t *h = NULL;
+ char *result = NULL;
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_hash_set(h, "key", APR_HASH_KEY_STRING, "value");
+ result = apr_hash_get(h, "key", APR_HASH_KEY_STRING);
+ ABTS_STR_EQUAL(tc, "value", result);
+
+ apr_hash_set(h, "key", APR_HASH_KEY_STRING, "new");
+ result = apr_hash_get(h, "key", APR_HASH_KEY_STRING);
+ ABTS_STR_EQUAL(tc, "new", result);
+}
+
+static void same_value(abts_case *tc, void *data)
+{
+ apr_hash_t *h = NULL;
+ char *result = NULL;
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_hash_set(h, "same1", APR_HASH_KEY_STRING, "same");
+ result = apr_hash_get(h, "same1", APR_HASH_KEY_STRING);
+ ABTS_STR_EQUAL(tc, "same", result);
+
+ apr_hash_set(h, "same2", APR_HASH_KEY_STRING, "same");
+ result = apr_hash_get(h, "same2", APR_HASH_KEY_STRING);
+ ABTS_STR_EQUAL(tc, "same", result);
+}
+
+static unsigned int hash_custom( const char *key, apr_ssize_t *klen)
+{
+ unsigned int hash = 0;
+ while( *klen ) {
+ (*klen) --;
+ hash = hash * 33 + key[ *klen ];
+ }
+ return hash;
+}
+
+static void same_value_custom(abts_case *tc, void *data)
+{
+ apr_hash_t *h = NULL;
+ char *result = NULL;
+
+ h = apr_hash_make_custom(p, hash_custom);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_hash_set(h, "same1", 5, "same");
+ result = apr_hash_get(h, "same1", 5);
+ ABTS_STR_EQUAL(tc, "same", result);
+
+ apr_hash_set(h, "same2", 5, "same");
+ result = apr_hash_get(h, "same2", 5);
+ ABTS_STR_EQUAL(tc, "same", result);
+}
+
+static void key_space(abts_case *tc, void *data)
+{
+ apr_hash_t *h = NULL;
+ char *result = NULL;
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_hash_set(h, "key with space", APR_HASH_KEY_STRING, "value");
+ result = apr_hash_get(h, "key with space", APR_HASH_KEY_STRING);
+ ABTS_STR_EQUAL(tc, "value", result);
+}
+
+static void hash_clear(abts_case *tc, void *data)
+{
+ apr_hash_t *h;
+ int i, *e;
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ for (i = 1; i <= 10; i++) {
+ e = apr_palloc(p, sizeof(int));
+ *e = i;
+ apr_hash_set(h, e, sizeof(*e), e);
+ }
+ apr_hash_clear(h);
+ i = apr_hash_count(h);
+ ABTS_INT_EQUAL(tc, 0, i);
+}
+
+/* This is kind of a hack, but I am just keeping an existing test. This is
+ * really testing apr_hash_first, apr_hash_next, and apr_hash_this which
+ * should be tested in three separate tests, but this will do for now.
+ */
+static void hash_traverse(abts_case *tc, void *data)
+{
+ apr_hash_t *h;
+ char StrArray[MAX_DEPTH][MAX_LTH];
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_hash_set(h, "OVERWRITE", APR_HASH_KEY_STRING, "should not see this");
+ apr_hash_set(h, "FOO3", APR_HASH_KEY_STRING, "bar3");
+ apr_hash_set(h, "FOO3", APR_HASH_KEY_STRING, "bar3");
+ apr_hash_set(h, "FOO1", APR_HASH_KEY_STRING, "bar1");
+ apr_hash_set(h, "FOO2", APR_HASH_KEY_STRING, "bar2");
+ apr_hash_set(h, "FOO4", APR_HASH_KEY_STRING, "bar4");
+ apr_hash_set(h, "SAME1", APR_HASH_KEY_STRING, "same");
+ apr_hash_set(h, "SAME2", APR_HASH_KEY_STRING, "same");
+ apr_hash_set(h, "OVERWRITE", APR_HASH_KEY_STRING, "Overwrite key");
+
+ dump_hash(p, h, StrArray);
+
+ ABTS_STR_EQUAL(tc, "Key FOO1 (4) Value bar1\n", StrArray[0]);
+ ABTS_STR_EQUAL(tc, "Key FOO2 (4) Value bar2\n", StrArray[1]);
+ ABTS_STR_EQUAL(tc, "Key FOO3 (4) Value bar3\n", StrArray[2]);
+ ABTS_STR_EQUAL(tc, "Key FOO4 (4) Value bar4\n", StrArray[3]);
+ ABTS_STR_EQUAL(tc, "Key OVERWRITE (9) Value Overwrite key\n", StrArray[4]);
+ ABTS_STR_EQUAL(tc, "Key SAME1 (5) Value same\n", StrArray[5]);
+ ABTS_STR_EQUAL(tc, "Key SAME2 (5) Value same\n", StrArray[6]);
+ ABTS_STR_EQUAL(tc, "#entries 7\n", StrArray[7]);
+}
+
+/* This is kind of a hack, but I am just keeping an existing test. This is
+ * really testing apr_hash_first, apr_hash_next, and apr_hash_this which
+ * should be tested in three separate tests, but this will do for now.
+ */
+static void summation_test(abts_case *tc, void *data)
+{
+ apr_hash_t *h;
+ int sumKeys, sumVal, trySumKey, trySumVal;
+ int i, j, *val, *key;
+
+ h =apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ sumKeys = 0;
+ sumVal = 0;
+ trySumKey = 0;
+ trySumVal = 0;
+
+ for (i = 0; i < 100; i++) {
+ j = i * 10 + 1;
+ sumKeys += j;
+ sumVal += i;
+ key = apr_palloc(p, sizeof(int));
+ *key = j;
+ val = apr_palloc(p, sizeof(int));
+ *val = i;
+ apr_hash_set(h, key, sizeof(int), val);
+ }
+
+ sum_hash(p, h, &i, &trySumKey, &trySumVal);
+ ABTS_INT_EQUAL(tc, 100, i);
+ ABTS_INT_EQUAL(tc, sumVal, trySumVal);
+ ABTS_INT_EQUAL(tc, sumKeys, trySumKey);
+}
+
+static void delete_key(abts_case *tc, void *data)
+{
+ apr_hash_t *h = NULL;
+ char *result = NULL;
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_hash_set(h, "key", APR_HASH_KEY_STRING, "value");
+ apr_hash_set(h, "key2", APR_HASH_KEY_STRING, "value2");
+
+ result = apr_hash_get(h, "key", APR_HASH_KEY_STRING);
+ ABTS_STR_EQUAL(tc, "value", result);
+
+ result = apr_hash_get(h, "key2", APR_HASH_KEY_STRING);
+ ABTS_STR_EQUAL(tc, "value2", result);
+
+ apr_hash_set(h, "key", APR_HASH_KEY_STRING, NULL);
+
+ result = apr_hash_get(h, "key", APR_HASH_KEY_STRING);
+ ABTS_PTR_EQUAL(tc, NULL, result);
+
+ result = apr_hash_get(h, "key2", APR_HASH_KEY_STRING);
+ ABTS_STR_EQUAL(tc, "value2", result);
+}
+
+static void hash_count_0(abts_case *tc, void *data)
+{
+ apr_hash_t *h = NULL;
+ int count;
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ count = apr_hash_count(h);
+ ABTS_INT_EQUAL(tc, 0, count);
+}
+
+static void hash_count_1(abts_case *tc, void *data)
+{
+ apr_hash_t *h = NULL;
+ int count;
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_hash_set(h, "key", APR_HASH_KEY_STRING, "value");
+
+ count = apr_hash_count(h);
+ ABTS_INT_EQUAL(tc, 1, count);
+}
+
+static void hash_count_5(abts_case *tc, void *data)
+{
+ apr_hash_t *h = NULL;
+ int count;
+
+ h = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, h);
+
+ apr_hash_set(h, "key1", APR_HASH_KEY_STRING, "value1");
+ apr_hash_set(h, "key2", APR_HASH_KEY_STRING, "value2");
+ apr_hash_set(h, "key3", APR_HASH_KEY_STRING, "value3");
+ apr_hash_set(h, "key4", APR_HASH_KEY_STRING, "value4");
+ apr_hash_set(h, "key5", APR_HASH_KEY_STRING, "value5");
+
+ count = apr_hash_count(h);
+ ABTS_INT_EQUAL(tc, 5, count);
+}
+
+static void overlay_empty(abts_case *tc, void *data)
+{
+ apr_hash_t *base = NULL;
+ apr_hash_t *overlay = NULL;
+ apr_hash_t *result = NULL;
+ int count;
+ char StrArray[MAX_DEPTH][MAX_LTH];
+
+ base = apr_hash_make(p);
+ overlay = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, base);
+ ABTS_PTR_NOTNULL(tc, overlay);
+
+ apr_hash_set(base, "key1", APR_HASH_KEY_STRING, "value1");
+ apr_hash_set(base, "key2", APR_HASH_KEY_STRING, "value2");
+ apr_hash_set(base, "key3", APR_HASH_KEY_STRING, "value3");
+ apr_hash_set(base, "key4", APR_HASH_KEY_STRING, "value4");
+ apr_hash_set(base, "key5", APR_HASH_KEY_STRING, "value5");
+
+ result = apr_hash_overlay(p, overlay, base);
+
+ count = apr_hash_count(result);
+ ABTS_INT_EQUAL(tc, 5, count);
+
+ dump_hash(p, result, StrArray);
+
+ ABTS_STR_EQUAL(tc, "Key key1 (4) Value value1\n", StrArray[0]);
+ ABTS_STR_EQUAL(tc, "Key key2 (4) Value value2\n", StrArray[1]);
+ ABTS_STR_EQUAL(tc, "Key key3 (4) Value value3\n", StrArray[2]);
+ ABTS_STR_EQUAL(tc, "Key key4 (4) Value value4\n", StrArray[3]);
+ ABTS_STR_EQUAL(tc, "Key key5 (4) Value value5\n", StrArray[4]);
+ ABTS_STR_EQUAL(tc, "#entries 5\n", StrArray[5]);
+}
+
+static void overlay_2unique(abts_case *tc, void *data)
+{
+ apr_hash_t *base = NULL;
+ apr_hash_t *overlay = NULL;
+ apr_hash_t *result = NULL;
+ int count;
+ char StrArray[MAX_DEPTH][MAX_LTH];
+
+ base = apr_hash_make(p);
+ overlay = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, base);
+ ABTS_PTR_NOTNULL(tc, overlay);
+
+ apr_hash_set(base, "base1", APR_HASH_KEY_STRING, "value1");
+ apr_hash_set(base, "base2", APR_HASH_KEY_STRING, "value2");
+ apr_hash_set(base, "base3", APR_HASH_KEY_STRING, "value3");
+ apr_hash_set(base, "base4", APR_HASH_KEY_STRING, "value4");
+ apr_hash_set(base, "base5", APR_HASH_KEY_STRING, "value5");
+
+ apr_hash_set(overlay, "overlay1", APR_HASH_KEY_STRING, "value1");
+ apr_hash_set(overlay, "overlay2", APR_HASH_KEY_STRING, "value2");
+ apr_hash_set(overlay, "overlay3", APR_HASH_KEY_STRING, "value3");
+ apr_hash_set(overlay, "overlay4", APR_HASH_KEY_STRING, "value4");
+ apr_hash_set(overlay, "overlay5", APR_HASH_KEY_STRING, "value5");
+
+ result = apr_hash_overlay(p, overlay, base);
+
+ count = apr_hash_count(result);
+ ABTS_INT_EQUAL(tc, 10, count);
+
+ dump_hash(p, result, StrArray);
+
+ ABTS_STR_EQUAL(tc, "Key base1 (5) Value value1\n", StrArray[0]);
+ ABTS_STR_EQUAL(tc, "Key base2 (5) Value value2\n", StrArray[1]);
+ ABTS_STR_EQUAL(tc, "Key base3 (5) Value value3\n", StrArray[2]);
+ ABTS_STR_EQUAL(tc, "Key base4 (5) Value value4\n", StrArray[3]);
+ ABTS_STR_EQUAL(tc, "Key base5 (5) Value value5\n", StrArray[4]);
+ ABTS_STR_EQUAL(tc, "Key overlay1 (8) Value value1\n", StrArray[5]);
+ ABTS_STR_EQUAL(tc, "Key overlay2 (8) Value value2\n", StrArray[6]);
+ ABTS_STR_EQUAL(tc, "Key overlay3 (8) Value value3\n", StrArray[7]);
+ ABTS_STR_EQUAL(tc, "Key overlay4 (8) Value value4\n", StrArray[8]);
+ ABTS_STR_EQUAL(tc, "Key overlay5 (8) Value value5\n", StrArray[9]);
+ ABTS_STR_EQUAL(tc, "#entries 10\n", StrArray[10]);
+}
+
+static void overlay_same(abts_case *tc, void *data)
+{
+ apr_hash_t *base = NULL;
+ apr_hash_t *result = NULL;
+ int count;
+ char StrArray[MAX_DEPTH][MAX_LTH];
+
+ base = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, base);
+
+ apr_hash_set(base, "base1", APR_HASH_KEY_STRING, "value1");
+ apr_hash_set(base, "base2", APR_HASH_KEY_STRING, "value2");
+ apr_hash_set(base, "base3", APR_HASH_KEY_STRING, "value3");
+ apr_hash_set(base, "base4", APR_HASH_KEY_STRING, "value4");
+ apr_hash_set(base, "base5", APR_HASH_KEY_STRING, "value5");
+
+ result = apr_hash_overlay(p, base, base);
+
+ count = apr_hash_count(result);
+ ABTS_INT_EQUAL(tc, 5, count);
+
+ dump_hash(p, result, StrArray);
+
+ ABTS_STR_EQUAL(tc, "Key base1 (5) Value value1\n", StrArray[0]);
+ ABTS_STR_EQUAL(tc, "Key base2 (5) Value value2\n", StrArray[1]);
+ ABTS_STR_EQUAL(tc, "Key base3 (5) Value value3\n", StrArray[2]);
+ ABTS_STR_EQUAL(tc, "Key base4 (5) Value value4\n", StrArray[3]);
+ ABTS_STR_EQUAL(tc, "Key base5 (5) Value value5\n", StrArray[4]);
+ ABTS_STR_EQUAL(tc, "#entries 5\n", StrArray[5]);
+}
+
+static void overlay_fetch(abts_case *tc, void *data)
+{
+ apr_hash_t *base = NULL;
+ apr_hash_t *overlay = NULL;
+ apr_hash_t *result = NULL;
+ int count;
+
+ base = apr_hash_make(p);
+ overlay = apr_hash_make(p);
+ ABTS_PTR_NOTNULL(tc, base);
+ ABTS_PTR_NOTNULL(tc, overlay);
+
+ apr_hash_set(base, "base1", APR_HASH_KEY_STRING, "value1");
+ apr_hash_set(base, "base2", APR_HASH_KEY_STRING, "value2");
+ apr_hash_set(base, "base3", APR_HASH_KEY_STRING, "value3");
+ apr_hash_set(base, "base4", APR_HASH_KEY_STRING, "value4");
+ apr_hash_set(base, "base5", APR_HASH_KEY_STRING, "value5");
+
+ apr_hash_set(overlay, "overlay1", APR_HASH_KEY_STRING, "value1");
+ apr_hash_set(overlay, "overlay2", APR_HASH_KEY_STRING, "value2");
+ apr_hash_set(overlay, "overlay3", APR_HASH_KEY_STRING, "value3");
+ apr_hash_set(overlay, "overlay4", APR_HASH_KEY_STRING, "value4");
+ apr_hash_set(overlay, "overlay5", APR_HASH_KEY_STRING, "value5");
+
+ result = apr_hash_overlay(p, overlay, base);
+
+ count = apr_hash_count(result);
+ ABTS_INT_EQUAL(tc, 10, count);
+
+ ABTS_STR_EQUAL(tc, "value1",
+ apr_hash_get(result, "base1", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value2",
+ apr_hash_get(result, "base2", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value3",
+ apr_hash_get(result, "base3", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value4",
+ apr_hash_get(result, "base4", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value5",
+ apr_hash_get(result, "base5", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value1",
+ apr_hash_get(result, "overlay1", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value2",
+ apr_hash_get(result, "overlay2", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value3",
+ apr_hash_get(result, "overlay3", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value4",
+ apr_hash_get(result, "overlay4", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value5",
+ apr_hash_get(result, "overlay5", APR_HASH_KEY_STRING));
+
+ ABTS_STR_EQUAL(tc, "value1",
+ apr_hash_get(base, "base1", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value2",
+ apr_hash_get(base, "base2", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value3",
+ apr_hash_get(base, "base3", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value4",
+ apr_hash_get(base, "base4", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value5",
+ apr_hash_get(base, "base5", APR_HASH_KEY_STRING));
+
+ ABTS_STR_EQUAL(tc, "value1",
+ apr_hash_get(overlay, "overlay1", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value2",
+ apr_hash_get(overlay, "overlay2", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value3",
+ apr_hash_get(overlay, "overlay3", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value4",
+ apr_hash_get(overlay, "overlay4", APR_HASH_KEY_STRING));
+ ABTS_STR_EQUAL(tc, "value5",
+ apr_hash_get(overlay, "overlay5", APR_HASH_KEY_STRING));
+}
+
+abts_suite *testhash(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, hash_make, NULL);
+ abts_run_test(suite, hash_set, NULL);
+ abts_run_test(suite, hash_reset, NULL);
+ abts_run_test(suite, same_value, NULL);
+ abts_run_test(suite, same_value_custom, NULL);
+ abts_run_test(suite, key_space, NULL);
+ abts_run_test(suite, delete_key, NULL);
+
+ abts_run_test(suite, hash_count_0, NULL);
+ abts_run_test(suite, hash_count_1, NULL);
+ abts_run_test(suite, hash_count_5, NULL);
+
+ abts_run_test(suite, hash_clear, NULL);
+ abts_run_test(suite, hash_traverse, NULL);
+ abts_run_test(suite, summation_test, NULL);
+
+ abts_run_test(suite, overlay_empty, NULL);
+ abts_run_test(suite, overlay_2unique, NULL);
+ abts_run_test(suite, overlay_same, NULL);
+ abts_run_test(suite, overlay_fetch, NULL);
+
+ return suite;
+}
+
diff --git a/test/testipsub.c b/test/testipsub.c
new file mode 100644
index 0000000..1182d7a
--- /dev/null
+++ b/test/testipsub.c
@@ -0,0 +1,237 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_general.h"
+#include "apr_network_io.h"
+#include "apr_errno.h"
+
+static void test_bad_input(abts_case *tc, void *data)
+{
+ struct {
+ const char *ipstr;
+ const char *mask;
+ apr_status_t expected_rv;
+ } testcases[] =
+ {
+ /* so we have a few good inputs in here; sue me */
+ {"my.host.name", NULL, APR_EINVAL}
+ ,{"127.0.0.256", NULL, APR_EBADIP}
+ ,{"127.0.0.1", NULL, APR_SUCCESS}
+ ,{"127.0.0.1", "32", APR_SUCCESS}
+ ,{"127.0.0.1", "1", APR_SUCCESS}
+ ,{"127.0.0.1", "15", APR_SUCCESS}
+ ,{"127.0.0.1", "-1", APR_EBADMASK}
+ ,{"127.0.0.1", "0", APR_EBADMASK}
+ ,{"127.0.0.1", "33", APR_EBADMASK}
+ ,{"127.0.0.1", "255.0.0.0", APR_SUCCESS}
+ ,{"127.0.0.1", "255.0", APR_EBADMASK}
+ ,{"127.0.0.1", "255.255.256.0", APR_EBADMASK}
+ ,{"127.0.0.1", "abc", APR_EBADMASK}
+ ,{"127", NULL, APR_SUCCESS}
+ ,{"127.0.0.1.2", NULL, APR_EBADIP}
+ ,{"127.0.0.1.2", "8", APR_EBADIP}
+ ,{"127", "255.0.0.0", APR_EBADIP} /* either EBADIP or EBADMASK seems fine */
+ ,{"", NULL, APR_EINVAL}
+#if APR_HAVE_IPV6
+ ,{"::1", NULL, APR_SUCCESS}
+ ,{"::1", "20", APR_SUCCESS}
+ ,{"::ffff:9.67.113.15", NULL, APR_EBADIP} /* yes, this is goodness */
+ ,{"fe80::", "16", APR_SUCCESS}
+ ,{"fe80::", "255.0.0.0", APR_EBADMASK}
+ ,{"fe80::1", "0", APR_EBADMASK}
+ ,{"fe80::1", "-1", APR_EBADMASK}
+ ,{"fe80::1", "1", APR_SUCCESS}
+ ,{"fe80::1", "33", APR_SUCCESS}
+ ,{"fe80::1", "128", APR_SUCCESS}
+ ,{"fe80::1", "129", APR_EBADMASK}
+#else
+ /* do some IPv6 stuff and verify that it fails with APR_EBADIP */
+ ,{"::ffff:9.67.113.15", NULL, APR_EBADIP}
+#endif
+ };
+ int i;
+ apr_ipsubnet_t *ipsub;
+ apr_status_t rv;
+
+ for (i = 0; i < (sizeof testcases / sizeof testcases[0]); i++) {
+ rv = apr_ipsubnet_create(&ipsub, testcases[i].ipstr, testcases[i].mask, p);
+ ABTS_INT_EQUAL(tc, testcases[i].expected_rv, rv);
+ }
+}
+
+static void test_singleton_subnets(abts_case *tc, void *data)
+{
+ const char *v4addrs[] = {
+ "127.0.0.1", "129.42.18.99", "63.161.155.20", "207.46.230.229", "64.208.42.36",
+ "198.144.203.195", "192.18.97.241", "198.137.240.91", "62.156.179.119",
+ "204.177.92.181"
+ };
+ apr_ipsubnet_t *ipsub;
+ apr_sockaddr_t *sa;
+ apr_status_t rv;
+ int i, j, rc;
+
+ for (i = 0; i < sizeof v4addrs / sizeof v4addrs[0]; i++) {
+ rv = apr_ipsubnet_create(&ipsub, v4addrs[i], NULL, p);
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ for (j = 0; j < sizeof v4addrs / sizeof v4addrs[0]; j++) {
+ rv = apr_sockaddr_info_get(&sa, v4addrs[j], APR_INET, 0, 0, p);
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ rc = apr_ipsubnet_test(ipsub, sa);
+ if (!strcmp(v4addrs[i], v4addrs[j])) {
+ ABTS_TRUE(tc, rc != 0);
+ }
+ else {
+ ABTS_TRUE(tc, rc == 0);
+ }
+ }
+ }
+
+ /* same for v6? */
+}
+
+static void test_interesting_subnets(abts_case *tc, void *data)
+{
+ struct {
+ const char *ipstr, *mask;
+ int family;
+ char *in_subnet, *not_in_subnet;
+ } testcases[] =
+ {
+ {"9.67", NULL, APR_INET, "9.67.113.15", "10.1.2.3"}
+ ,{"9.67.0.0", "16", APR_INET, "9.67.113.15", "10.1.2.3"}
+ ,{"9.67.0.0", "255.255.0.0", APR_INET, "9.67.113.15", "10.1.2.3"}
+ ,{"9.67.113.99", "16", APR_INET, "9.67.113.15", "10.1.2.3"}
+ ,{"9.67.113.99", "255.255.255.0", APR_INET, "9.67.113.15", "10.1.2.3"}
+ ,{"127", NULL, APR_INET, "127.0.0.1", "10.1.2.3"}
+ ,{"127.0.0.1", "8", APR_INET, "127.0.0.1", "10.1.2.3"}
+#if APR_HAVE_IPV6
+ ,{"38.0.0.0", "8", APR_INET6, "::ffff:38.1.1.1", "2600::1"} /* PR 54047 */
+ ,{"fe80::", "8", APR_INET6, "fe80::1", "ff01::1"}
+ ,{"ff01::", "8", APR_INET6, "ff01::1", "fe80::1"}
+ ,{"3FFE:8160::", "28", APR_INET6, "3ffE:816e:abcd:1234::1", "3ffe:8170::1"}
+ ,{"127.0.0.1", NULL, APR_INET6, "::ffff:127.0.0.1", "fe80::1"}
+ ,{"127.0.0.1", "8", APR_INET6, "::ffff:127.0.0.1", "fe80::1"}
+#endif
+ };
+ apr_ipsubnet_t *ipsub;
+ apr_sockaddr_t *sa;
+ apr_status_t rv;
+ int i, rc;
+
+ for (i = 0; i < sizeof testcases / sizeof testcases[0]; i++) {
+ rv = apr_ipsubnet_create(&ipsub, testcases[i].ipstr, testcases[i].mask, p);
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ rv = apr_sockaddr_info_get(&sa, testcases[i].in_subnet, testcases[i].family, 0, 0, p);
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ ABTS_TRUE(tc, sa != NULL);
+ if (!sa) continue;
+ rc = apr_ipsubnet_test(ipsub, sa);
+ ABTS_TRUE(tc, rc != 0);
+ rv = apr_sockaddr_info_get(&sa, testcases[i].not_in_subnet, testcases[i].family, 0, 0, p);
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ rc = apr_ipsubnet_test(ipsub, sa);
+ ABTS_TRUE(tc, rc == 0);
+ }
+}
+
+static void test_badmask_str(abts_case *tc, void *data)
+{
+ char buf[128];
+
+ ABTS_STR_EQUAL(tc, apr_strerror(APR_EBADMASK, buf, sizeof buf),
+ "The specified network mask is invalid.");
+}
+
+static void test_badip_str(abts_case *tc, void *data)
+{
+ char buf[128];
+
+ ABTS_STR_EQUAL(tc, apr_strerror(APR_EBADIP, buf, sizeof buf),
+ "The specified IP address is invalid.");
+}
+
+static void test_parse_addr_port(abts_case *tc, void *data)
+{
+ const struct {
+ const char *input;
+ apr_status_t rv;
+ const char *addr, *scope_id;
+ apr_port_t port;
+ } *test, testcases[] = {
+ /* Success cases */
+ { "localhost:80", APR_SUCCESS, "localhost", NULL, 80 }
+ ,{ "localhost", APR_SUCCESS, "localhost", NULL, 0 }
+ ,{ "www.example.com:8080", APR_SUCCESS, "www.example.com", NULL, 8080 }
+ ,{ "w:1", APR_SUCCESS, "w", NULL, 1 }
+ ,{ "127.0.0.1:80", APR_SUCCESS, "127.0.0.1", NULL, 80 }
+ ,{ "8080", APR_SUCCESS, NULL, NULL, 8080 } /* API doc has this case */
+#if APR_HAVE_IPV6
+ ,{ "[::]:80", APR_SUCCESS, "::", NULL, 80 }
+ ,{ "[::%eth0]:80", APR_SUCCESS, "::", "eth0", 80 }
+ ,{ "[::%eth0]", APR_SUCCESS, "::", "eth0", 0 }
+#endif
+
+ /* Failure cases */
+ ,{ "localhost:999999", APR_EINVAL, NULL, NULL, 0 }
+ ,{ "localhost:0", APR_EINVAL, NULL, NULL, 0 }
+#if APR_HAVE_IPV6
+ ,{ "[abc]", APR_EINVAL, NULL, NULL, 0 }
+ ,{ "[::]z:80", APR_EINVAL, NULL, NULL, 0 }
+ ,{ "[:::80", APR_EINVAL, NULL, NULL, 0 }
+ ,{ "[zzzz]:80", APR_EINVAL, NULL, NULL, 0 }
+ ,{ "[::%]:80", APR_EINVAL, NULL, NULL, 0 }
+#endif
+/* ,{ "127.0.0.1:80x", APR_EINVAL, NULL, NULL, 0 } <- should fail, doesn't */
+/* ,{ "127.0.0.1x:80", APR_EINVAL, NULL, NULL, 0 } <- maybe should fail?, doesn't */
+/* ,{ "localhost:-1", APR_EINVAL, NULL, NULL, 0 } <- should fail, doesn't */
+ };
+ unsigned i;
+
+ for (i = 0; i < (sizeof testcases / sizeof testcases[0]); i++) {
+ char *addr, *scope_id;
+ apr_port_t port;
+ apr_status_t rv;
+
+ test = &testcases[i];
+
+ rv = apr_parse_addr_port(&addr, &scope_id, &port, test->input, p);
+ ABTS_INT_EQUAL(tc, test->rv, rv);
+
+ if (test->rv != APR_SUCCESS) continue;
+
+ APR_ASSERT_SUCCESS(tc, "parse address", test->rv);
+
+ ABTS_STR_EQUAL(tc, test->addr, addr);
+ ABTS_STR_EQUAL(tc, test->scope_id, scope_id);
+ ABTS_INT_EQUAL(tc, test->port, port);
+ }
+}
+
+abts_suite *testipsub(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_bad_input, NULL);
+ abts_run_test(suite, test_singleton_subnets, NULL);
+ abts_run_test(suite, test_interesting_subnets, NULL);
+ abts_run_test(suite, test_badmask_str, NULL);
+ abts_run_test(suite, test_badip_str, NULL);
+ abts_run_test(suite, test_parse_addr_port, NULL);
+ return suite;
+}
+
diff --git a/test/testlfs.c b/test/testlfs.c
new file mode 100644
index 0000000..0fd5d98
--- /dev/null
+++ b/test/testlfs.c
@@ -0,0 +1,378 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_poll.h"
+#include "apr_strings.h"
+#include "apr_lib.h"
+#include "apr_mmap.h"
+#include "testutil.h"
+
+/* TODO: in 1.3.0 this becomes APR_HAS_SPARSE_FILES, HOWEVER we will
+ * still need to test csize before proceeding, because having sparse
+ * file support in the OS/APR does not mean this volume supports it!
+ */
+#if APR_HAS_LARGE_FILES
+
+/* Tests which create an 8GB sparse file and then check it can be used
+ * as normal. */
+
+static apr_off_t oneMB = APR_INT64_C(2) << 19;
+static apr_off_t eightGB = APR_INT64_C(2) << 32;
+
+static int madefile = 0;
+
+#define PRECOND if (!madefile) { ABTS_NOT_IMPL(tc, "Large file tests not enabled"); return; }
+
+#define TESTDIR "lfstests"
+#define TESTFILE "large.bin"
+#define TESTFN "lfstests/large.bin"
+
+static void test_open(abts_case *tc, void *data)
+{
+ apr_file_t *f;
+ apr_finfo_t testsize;
+ apr_status_t rv;
+
+ rv = apr_dir_make(TESTDIR, APR_OS_DEFAULT, p);
+ if (rv && !APR_STATUS_IS_EEXIST(rv)) {
+ APR_ASSERT_SUCCESS(tc, "make test directory", rv);
+ }
+
+ /* First attempt a 1MB sparse file so we don't tax the poor test box */
+ rv = apr_file_open(&f, TESTFN, APR_FOPEN_CREATE | APR_FOPEN_WRITE
+ | APR_FOPEN_TRUNCATE | APR_FOPEN_SPARSE,
+ APR_OS_DEFAULT, p);
+
+ APR_ASSERT_SUCCESS(tc, "open file", rv);
+
+ APR_ASSERT_SUCCESS(tc, "Truncate to 1MB", rv = apr_file_trunc(f, oneMB+1));
+
+ if (rv == APR_SUCCESS) {
+ rv = apr_file_info_get(&testsize, APR_FINFO_CSIZE, f);
+ }
+
+ /* give up if we can't determine the allocation size of the file,
+ * or if it's not an obviously small allocation but the allocation
+ * unit doesn't appear insanely large - on most platforms, it's just
+ * zero physical bytes at this point.
+ */
+ if (rv != APR_SUCCESS || (testsize.csize > oneMB
+ && testsize.csize < oneMB * 2)) {
+ ABTS_NOT_IMPL(tc, "Creation of large file (apparently not sparse)");
+
+ madefile = 0;
+ }
+ else {
+ /* Proceed with our 8GB sparse file now */
+ rv = apr_file_trunc(f, eightGB);
+
+ /* 8GB may pass rlimits or filesystem limits */
+
+ if (APR_STATUS_IS_EINVAL(rv)
+#ifdef EFBIG
+ || rv == EFBIG
+#endif
+ ) {
+ ABTS_NOT_IMPL(tc, "Creation of large file (rlimit, quota or fs)");
+ }
+ else {
+ APR_ASSERT_SUCCESS(tc, "truncate file to 8gb", rv);
+ }
+ madefile = rv == APR_SUCCESS;
+ }
+
+ APR_ASSERT_SUCCESS(tc, "close large file", apr_file_close(f));
+
+ if (!madefile) {
+ APR_ASSERT_SUCCESS(tc, "remove large file", apr_file_remove(TESTFN, p));
+ }
+}
+
+static void test_reopen(abts_case *tc, void *data)
+{
+ apr_file_t *fh;
+ apr_finfo_t finfo;
+ apr_status_t rv;
+
+ PRECOND;
+
+ rv = apr_file_open(&fh, TESTFN, APR_FOPEN_SPARSE | APR_FOPEN_READ,
+ APR_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "re-open 8GB file", rv);
+
+ APR_ASSERT_SUCCESS(tc, "file_info_get failed",
+ apr_file_info_get(&finfo, APR_FINFO_NORM, fh));
+
+ ABTS_ASSERT(tc, "file_info_get gave incorrect size",
+ finfo.size == eightGB);
+
+ APR_ASSERT_SUCCESS(tc, "re-close large file", apr_file_close(fh));
+}
+
+static void test_stat(abts_case *tc, void *data)
+{
+ apr_finfo_t finfo;
+
+ PRECOND;
+
+ APR_ASSERT_SUCCESS(tc, "stat large file",
+ apr_stat(&finfo, TESTFN, APR_FINFO_NORM, p));
+
+ ABTS_ASSERT(tc, "stat gave incorrect size", finfo.size == eightGB);
+}
+
+static void test_readdir(abts_case *tc, void *data)
+{
+ apr_dir_t *dh;
+ apr_status_t rv;
+
+ PRECOND;
+
+ APR_ASSERT_SUCCESS(tc, "open test directory",
+ apr_dir_open(&dh, TESTDIR, p));
+
+ do {
+ apr_finfo_t finfo;
+
+ rv = apr_dir_read(&finfo, APR_FINFO_MIN, dh);
+
+ if (rv == APR_SUCCESS && strcmp(finfo.name, TESTFILE) == 0) {
+ ABTS_ASSERT(tc, "apr_dir_read gave incorrect size for large file",
+ finfo.size == eightGB);
+ }
+
+ } while (rv == APR_SUCCESS);
+
+ if (!APR_STATUS_IS_ENOENT(rv)) {
+ APR_ASSERT_SUCCESS(tc, "apr_dir_read failed", rv);
+ }
+
+ APR_ASSERT_SUCCESS(tc, "close test directory",
+ apr_dir_close(dh));
+}
+
+#define TESTSTR "Hello, world."
+
+static void test_append(abts_case *tc, void *data)
+{
+ apr_file_t *fh;
+ apr_finfo_t finfo;
+ apr_status_t rv;
+
+ PRECOND;
+
+ rv = apr_file_open(&fh, TESTFN, APR_FOPEN_SPARSE | APR_FOPEN_WRITE
+ | APR_FOPEN_APPEND,
+ APR_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "open 8GB file for append", rv);
+
+ APR_ASSERT_SUCCESS(tc, "append to 8GB file",
+ apr_file_write_full(fh, TESTSTR, strlen(TESTSTR), NULL));
+
+ APR_ASSERT_SUCCESS(tc, "file_info_get failed",
+ apr_file_info_get(&finfo, APR_FINFO_NORM, fh));
+
+ ABTS_ASSERT(tc, "file_info_get gave incorrect size",
+ finfo.size == eightGB + strlen(TESTSTR));
+
+ APR_ASSERT_SUCCESS(tc, "close 8GB file", apr_file_close(fh));
+}
+
+static void test_seek(abts_case *tc, void *data)
+{
+ apr_file_t *fh;
+ apr_off_t pos;
+ apr_status_t rv;
+
+ PRECOND;
+
+ rv = apr_file_open(&fh, TESTFN, APR_FOPEN_SPARSE | APR_FOPEN_WRITE,
+ APR_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "open 8GB file for writing", rv);
+
+ pos = 0;
+ APR_ASSERT_SUCCESS(tc, "relative seek to end",
+ apr_file_seek(fh, APR_END, &pos));
+ ABTS_ASSERT(tc, "seek to END gave 8GB", pos == eightGB);
+
+ pos = eightGB;
+ APR_ASSERT_SUCCESS(tc, "seek to 8GB", apr_file_seek(fh, APR_SET, &pos));
+ ABTS_ASSERT(tc, "seek gave 8GB offset", pos == eightGB);
+
+ pos = 0;
+ APR_ASSERT_SUCCESS(tc, "relative seek to 0", apr_file_seek(fh, APR_CUR, &pos));
+ ABTS_ASSERT(tc, "relative seek gave 8GB offset", pos == eightGB);
+
+ apr_file_close(fh);
+}
+
+static void test_write(abts_case *tc, void *data)
+{
+ apr_file_t *fh;
+ apr_off_t pos = eightGB - 4;
+ apr_status_t rv;
+
+ PRECOND;
+
+ rv = apr_file_open(&fh, TESTFN, APR_FOPEN_SPARSE | APR_FOPEN_WRITE,
+ APR_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "re-open 8GB file", rv);
+
+ APR_ASSERT_SUCCESS(tc, "seek to 8GB - 4",
+ apr_file_seek(fh, APR_SET, &pos));
+ ABTS_ASSERT(tc, "seek gave 8GB-4 offset", pos == eightGB - 4);
+
+ APR_ASSERT_SUCCESS(tc, "write magic string to 8GB-4",
+ apr_file_write_full(fh, "FISH", 4, NULL));
+
+ APR_ASSERT_SUCCESS(tc, "close 8GB file", apr_file_close(fh));
+}
+
+
+#if APR_HAS_MMAP
+static void test_mmap(abts_case *tc, void *data)
+{
+ apr_mmap_t *map;
+ apr_file_t *fh;
+ apr_size_t len = 65536; /* hopefully a multiple of the page size */
+ apr_off_t off = eightGB - len;
+ apr_status_t rv;
+ void *ptr;
+
+ PRECOND;
+
+ rv = apr_file_open(&fh, TESTFN, APR_FOPEN_SPARSE | APR_FOPEN_READ,
+ APR_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "open 8gb file for mmap", rv);
+
+ APR_ASSERT_SUCCESS(tc, "mmap 8GB file",
+ apr_mmap_create(&map, fh, off, len, APR_MMAP_READ, p));
+
+ APR_ASSERT_SUCCESS(tc, "close file", apr_file_close(fh));
+
+ ABTS_ASSERT(tc, "mapped a 64K block", map->size == len);
+
+ APR_ASSERT_SUCCESS(tc, "get pointer into mmaped region",
+ apr_mmap_offset(&ptr, map, len - 4));
+ ABTS_ASSERT(tc, "pointer was not NULL", ptr != NULL);
+
+ ABTS_ASSERT(tc, "found the magic string", memcmp(ptr, "FISH", 4) == 0);
+
+ APR_ASSERT_SUCCESS(tc, "delete mmap handle", apr_mmap_delete(map));
+}
+#endif /* APR_HAS_MMAP */
+
+static void test_format(abts_case *tc, void *data)
+{
+ apr_off_t off;
+
+ PRECOND;
+
+ off = apr_atoi64(apr_off_t_toa(p, eightGB));
+
+ ABTS_ASSERT(tc, "apr_atoi64 parsed apr_off_t_toa result incorrectly",
+ off == eightGB);
+}
+
+#define TESTBUFFN TESTDIR "/buffer.bin"
+
+static void test_buffered(abts_case *tc, void *data)
+{
+ apr_off_t off;
+ apr_file_t *f;
+ apr_status_t rv;
+
+ PRECOND;
+
+ rv = apr_file_open(&f, TESTBUFFN, APR_FOPEN_CREATE | APR_FOPEN_WRITE
+ | APR_FOPEN_TRUNCATE | APR_FOPEN_BUFFERED
+ | APR_FOPEN_SPARSE,
+ APR_OS_DEFAULT, p);
+ APR_ASSERT_SUCCESS(tc, "open buffered file", rv);
+
+ APR_ASSERT_SUCCESS(tc, "truncate to 8GB",
+ apr_file_trunc(f, eightGB));
+
+ off = eightGB;
+ APR_ASSERT_SUCCESS(tc, "seek to 8GB",
+ apr_file_seek(f, APR_SET, &off));
+ ABTS_ASSERT(tc, "returned seek position still 8GB",
+ off == eightGB);
+
+ off = 0;
+ APR_ASSERT_SUCCESS(tc, "relative seek",
+ apr_file_seek(f, APR_CUR, &off));
+ ABTS_ASSERT(tc, "relative seek still at 8GB",
+ off == eightGB);
+
+ off = 0;
+ APR_ASSERT_SUCCESS(tc, "end-relative seek",
+ apr_file_seek(f, APR_END, &off));
+ ABTS_ASSERT(tc, "end-relative seek still at 8GB",
+ off == eightGB);
+
+ off = -eightGB;
+ APR_ASSERT_SUCCESS(tc, "relative seek to beginning",
+ apr_file_seek(f, APR_CUR, &off));
+ ABTS_ASSERT(tc, "seek to beginning got zero",
+ off == 0);
+
+ APR_ASSERT_SUCCESS(tc, "close buffered file",
+ apr_file_close(f));
+}
+
+#else /* !APR_HAS_LARGE_FILES */
+
+static void test_nolfs(abts_case *tc, void *data)
+{
+ if (sizeof(off_t) < 8) {
+ ABTS_NOT_IMPL(tc, "Large Files not supported");
+ }
+ else {
+ ABTS_NOT_IMPL(tc, "LFS support a no-op in 64-bit builds");
+ }
+}
+
+#endif
+
+abts_suite *testlfs(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+#if APR_HAS_LARGE_FILES
+ abts_run_test(suite, test_open, NULL);
+ abts_run_test(suite, test_reopen, NULL);
+ abts_run_test(suite, test_stat, NULL);
+ abts_run_test(suite, test_readdir, NULL);
+ abts_run_test(suite, test_seek, NULL);
+ abts_run_test(suite, test_append, NULL);
+ abts_run_test(suite, test_write, NULL);
+#if APR_HAS_MMAP
+ abts_run_test(suite, test_mmap, NULL);
+#endif
+ abts_run_test(suite, test_format, NULL);
+ abts_run_test(suite, test_buffered, NULL);
+#else
+ abts_run_test(suite, test_nolfs, NULL);
+#endif
+
+ return suite;
+}
+
diff --git a/test/testlib.dsp b/test/testlib.dsp
new file mode 100644
index 0000000..db7b641
--- /dev/null
+++ b/test/testlib.dsp
@@ -0,0 +1,446 @@
+# Microsoft Developer Studio Project File - Name="testlib" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) External Target" 0x0106
+
+CFG=testlib - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "testlib.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "testlib.mak" CFG="testlib - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "testlib - Win32 Release" (based on "Win32 (x86) External Target")
+!MESSAGE "testlib - Win32 Debug" (based on "Win32 (x86) External Target")
+!MESSAGE "testlib - Win32 Release9x" (based on "Win32 (x86) External Target")
+!MESSAGE "testlib - Win32 Debug9x" (based on "Win32 (x86) External Target")
+!MESSAGE "testlib - x64 Release" (based on "Win32 (x86) External Target")
+!MESSAGE "testlib - x64 Debug" (based on "Win32 (x86) External Target")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+
+!IF "$(CFG)" == "testlib - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=LibR OUTDIR=LibR MODEL=static all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "LibR\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=LibR OUTDIR=LibR MODEL=static all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "LibR\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "testlib - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=LibD OUTDIR=LibD MODEL=static _DEBUG=1 all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "LibD\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=LibD OUTDIR=LibD MODEL=static _DEBUG=1 all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "LibD\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "testlib - Win32 Release9x"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=9x\LibR OUTDIR=9x\LibR MODEL=static all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "9x\LibR\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=9x\LibR OUTDIR=9x\LibR MODEL=static all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "9x\LibR\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "testlib - Win32 Debug9x"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=9x\LibD OUTDIR=9x\LibD MODEL=static _DEBUG=1 all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "9x\LibD\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=9x\LibD OUTDIR=9x\LibD MODEL=static _DEBUG=1 all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "9x\LibD\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "testlib - x64 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=x64\LibR OUTDIR=x64\LibR MODEL=static all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "x64\LibR\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=x64\LibR OUTDIR=x64\LibR MODEL=static all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "x64\LibR\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ELSEIF "$(CFG)" == "testlib - x64 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ""
+# PROP BASE Intermediate_Dir ""
+# PROP BASE Cmd_Line "NMAKE /f Makefile.win INTDIR=x64\LibD OUTDIR=x64\LibD MODEL=static _DEBUG=1 all check"
+# PROP BASE Rebuild_Opt "/a"
+# PROP BASE Target_File "x64\LibD\testall.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir ""
+# PROP Cmd_Line "NMAKE /f Makefile.win INTDIR=x64\LibD OUTDIR=x64\LibD MODEL=static _DEBUG=1 all check"
+# PROP Rebuild_Opt "/a"
+# PROP Target_File "x64\LibD\testall.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ENDIF
+
+# Begin Target
+
+# Name "testlib - Win32 Release"
+# Name "testlib - Win32 Debug"
+# Name "testlib - Win32 Release9x"
+# Name "testlib - Win32 Debug9x"
+# Name "testlib - x64 Release"
+# Name "testlib - x64 Debug"
+# Begin Group "testall Source Files"
+
+# PROP Default_Filter ".c"
+# Begin Source File
+
+SOURCE=.\abts.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\abts.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\abts_tests.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testapp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testargs.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testatomic.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testcond.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testdir.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testdso.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testdup.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testenv.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testenv.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testfilecopy.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testfileinfo.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testflock.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testflock.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testfmt.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testfnmatch.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testglobalmutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testglobalmutex.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testhash.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testipsub.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testlfs.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testlock.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testmmap.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testnames.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testoc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testpath.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testpipe.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testpoll.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testpools.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testproc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testrand.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testshm.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testshm.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsleep.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsock.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsock.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsockets.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsockopt.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\teststr.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\teststrnatcmp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testtable.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testtemp.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testthread.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testtime.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testud.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testuser.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testutil.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testutil.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\testvsn.c
+# End Source File
+# End Group
+# Begin Group "Other Source Files"
+
+# PROP Default_Filter ".c"
+# Begin Source File
+
+SOURCE=.\globalmutexchild.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_test.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\nw_misc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\occhild.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\proc_child.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\readchild.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sendfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sockchild.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testlockperf.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testmutexscope.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testprocmutex.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testshmconsumer.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testshmproducer.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\tryread.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\Makefile.win
+# End Source File
+# End Target
+# End Project
diff --git a/test/testlock.c b/test/testlock.c
new file mode 100644
index 0000000..e3437c1
--- /dev/null
+++ b/test/testlock.c
@@ -0,0 +1,556 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_thread_proc.h"
+#include "apr_file_io.h"
+#include "apr_thread_mutex.h"
+#include "apr_thread_rwlock.h"
+#include "apr_thread_cond.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_getopt.h"
+#include "apr_atomic.h"
+#include "testutil.h"
+
+#if APR_HAS_THREADS
+
+#define MAX_ITER 40000
+#define MAX_COUNTER 100000
+#define MAX_RETRY 5
+
+static void *APR_THREAD_FUNC thread_rwlock_func(apr_thread_t *thd, void *data);
+static void *APR_THREAD_FUNC thread_mutex_function(apr_thread_t *thd, void *data);
+static void *APR_THREAD_FUNC thread_mutex_sleep_function(apr_thread_t *thd, void *data);
+static void *APR_THREAD_FUNC thread_cond_producer(apr_thread_t *thd, void *data);
+static void *APR_THREAD_FUNC thread_cond_consumer(apr_thread_t *thd, void *data);
+
+static apr_thread_mutex_t *thread_mutex;
+static apr_thread_rwlock_t *rwlock;
+static int i = 0, x = 0;
+
+static int buff[MAX_COUNTER];
+
+struct {
+ apr_thread_mutex_t *mutex;
+ int nput;
+ int nval;
+} put;
+
+struct {
+ apr_thread_mutex_t *mutex;
+ apr_thread_cond_t *cond;
+ int nready;
+} nready;
+
+static apr_thread_mutex_t *timeout_mutex;
+static apr_thread_cond_t *timeout_cond;
+
+static void *APR_THREAD_FUNC thread_rwlock_func(apr_thread_t *thd, void *data)
+{
+ int exitLoop = 1;
+
+ while (1)
+ {
+ apr_thread_rwlock_rdlock(rwlock);
+ if (i == MAX_ITER)
+ exitLoop = 0;
+ apr_thread_rwlock_unlock(rwlock);
+
+ if (!exitLoop)
+ break;
+
+ apr_thread_rwlock_wrlock(rwlock);
+ if (i != MAX_ITER)
+ {
+ i++;
+ x++;
+ }
+ apr_thread_rwlock_unlock(rwlock);
+ }
+ return NULL;
+}
+
+static void *APR_THREAD_FUNC thread_mutex_function(apr_thread_t *thd, void *data)
+{
+ int exitLoop = 1;
+
+ /* slight delay to allow things to settle */
+ apr_sleep (1);
+
+ while (1)
+ {
+ if (data) {
+ apr_thread_mutex_timedlock(thread_mutex, *(apr_interval_time_t *)data);
+ }
+ else {
+ apr_thread_mutex_lock(thread_mutex);
+ }
+ if (i == MAX_ITER)
+ exitLoop = 0;
+ else
+ {
+ i++;
+ x++;
+ }
+ apr_thread_mutex_unlock(thread_mutex);
+
+ if (!exitLoop)
+ break;
+ }
+ return NULL;
+}
+
+/* Sleepy-loop until f_ value matches val: */
+#define wait_for_flag(f_, val) while (apr_atomic_read32(&(f_)) != val) apr_sleep(100000)
+
+/* Helper function. Passed (apr_uint32_t *) flag as data, sets flag
+ * to one, locks the timeout_mutex, waits for *flag to be set to zero
+ * and terminates. The co-ordination could also be done via mutexes
+ * but since we're timedlocking timeout_mutex it would look like a
+ * deadlock to a mutex implementation which detects deadlocks. */
+static void *APR_THREAD_FUNC thread_mutex_sleep_function(apr_thread_t *thd, void *data)
+{
+ apr_uint32_t *flag = data;
+ apr_status_t rv;
+
+ rv = apr_thread_mutex_lock(timeout_mutex);
+ if (rv) {
+ fprintf(stderr, "testlock: failed to lock timeout mutex, errno %d\n", rv);
+ apr_thread_exit(thd, rv);
+ }
+
+ apr_atomic_set32(flag, 1);
+
+ wait_for_flag(*flag, 0);
+
+ rv = apr_thread_mutex_unlock(timeout_mutex);
+
+ apr_thread_exit(thd, APR_SUCCESS);
+ return NULL;
+}
+
+static void *APR_THREAD_FUNC thread_cond_producer(apr_thread_t *thd, void *data)
+{
+ for (;;) {
+ apr_thread_mutex_lock(put.mutex);
+ if (put.nput >= MAX_COUNTER) {
+ apr_thread_mutex_unlock(put.mutex);
+ return NULL;
+ }
+ buff[put.nput] = put.nval;
+ put.nput++;
+ put.nval++;
+ apr_thread_mutex_unlock(put.mutex);
+
+ apr_thread_mutex_lock(nready.mutex);
+ if (nready.nready == 0)
+ apr_thread_cond_signal(nready.cond);
+ nready.nready++;
+ apr_thread_mutex_unlock(nready.mutex);
+
+ *((int *) data) += 1;
+ }
+
+ return NULL;
+}
+
+static void *APR_THREAD_FUNC thread_cond_consumer(apr_thread_t *thd, void *data)
+{
+ int i;
+
+ for (i = 0; i < MAX_COUNTER; i++) {
+ apr_thread_mutex_lock(nready.mutex);
+ while (nready.nready == 0)
+ apr_thread_cond_wait(nready.cond, nready.mutex);
+ nready.nready--;
+ apr_thread_mutex_unlock(nready.mutex);
+
+ if (buff[i] != i)
+ printf("buff[%d] = %d\n", i, buff[i]);
+ }
+
+ return NULL;
+}
+
+static void test_thread_mutex(abts_case *tc, void *data)
+{
+ apr_thread_t *t1, *t2, *t3, *t4;
+ apr_status_t s1, s2, s3, s4;
+
+ s1 = apr_thread_mutex_create(&thread_mutex, APR_THREAD_MUTEX_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s1);
+ ABTS_PTR_NOTNULL(tc, thread_mutex);
+
+ i = 0;
+ x = 0;
+
+ s1 = apr_thread_create(&t1, NULL, thread_mutex_function, NULL, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s1);
+ s2 = apr_thread_create(&t2, NULL, thread_mutex_function, NULL, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s2);
+ s3 = apr_thread_create(&t3, NULL, thread_mutex_function, NULL, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s3);
+ s4 = apr_thread_create(&t4, NULL, thread_mutex_function, NULL, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s4);
+
+ apr_thread_join(&s1, t1);
+ apr_thread_join(&s2, t2);
+ apr_thread_join(&s3, t3);
+ apr_thread_join(&s4, t4);
+
+ ABTS_INT_EQUAL(tc, MAX_ITER, x);
+}
+
+#if APR_HAS_TIMEDLOCKS
+static void test_thread_timedmutex(abts_case *tc, void *data)
+{
+ apr_thread_t *t1, *t2, *t3, *t4;
+ apr_status_t s1, s2, s3, s4;
+ apr_interval_time_t timeout;
+
+ s1 = apr_thread_mutex_create(&thread_mutex, APR_THREAD_MUTEX_TIMED, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s1);
+ ABTS_PTR_NOTNULL(tc, thread_mutex);
+
+ i = 0;
+ x = 0;
+
+ timeout = apr_time_from_sec(5);
+
+ s1 = apr_thread_create(&t1, NULL, thread_mutex_function, &timeout, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s1);
+ s2 = apr_thread_create(&t2, NULL, thread_mutex_function, &timeout, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s2);
+ s3 = apr_thread_create(&t3, NULL, thread_mutex_function, &timeout, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s3);
+ s4 = apr_thread_create(&t4, NULL, thread_mutex_function, &timeout, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s4);
+
+ apr_thread_join(&s1, t1);
+ apr_thread_join(&s2, t2);
+ apr_thread_join(&s3, t3);
+ apr_thread_join(&s4, t4);
+
+ ABTS_INT_EQUAL(tc, MAX_ITER, x);
+}
+#endif
+
+static void test_thread_rwlock(abts_case *tc, void *data)
+{
+ apr_thread_t *t1, *t2, *t3, *t4;
+ apr_status_t s1, s2, s3, s4;
+
+ s1 = apr_thread_rwlock_create(&rwlock, p);
+ if (s1 == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "rwlocks not implemented");
+ return;
+ }
+ APR_ASSERT_SUCCESS(tc, "rwlock_create", s1);
+ ABTS_PTR_NOTNULL(tc, rwlock);
+
+ i = 0;
+ x = 0;
+
+ s1 = apr_thread_create(&t1, NULL, thread_rwlock_func, NULL, p);
+ APR_ASSERT_SUCCESS(tc, "create thread 1", s1);
+ s2 = apr_thread_create(&t2, NULL, thread_rwlock_func, NULL, p);
+ APR_ASSERT_SUCCESS(tc, "create thread 2", s2);
+ s3 = apr_thread_create(&t3, NULL, thread_rwlock_func, NULL, p);
+ APR_ASSERT_SUCCESS(tc, "create thread 3", s3);
+ s4 = apr_thread_create(&t4, NULL, thread_rwlock_func, NULL, p);
+ APR_ASSERT_SUCCESS(tc, "create thread 4", s4);
+
+ apr_thread_join(&s1, t1);
+ apr_thread_join(&s2, t2);
+ apr_thread_join(&s3, t3);
+ apr_thread_join(&s4, t4);
+
+ ABTS_INT_EQUAL(tc, MAX_ITER, x);
+
+ apr_thread_rwlock_destroy(rwlock);
+}
+
+static void test_cond(abts_case *tc, void *data)
+{
+ apr_thread_t *p1, *p2, *p3, *p4, *c1;
+ apr_status_t s0, s1, s2, s3, s4;
+ int count1, count2, count3, count4;
+ int sum;
+
+ APR_ASSERT_SUCCESS(tc, "create put mutex",
+ apr_thread_mutex_create(&put.mutex,
+ APR_THREAD_MUTEX_DEFAULT, p));
+ ABTS_PTR_NOTNULL(tc, put.mutex);
+
+ APR_ASSERT_SUCCESS(tc, "create nready mutex",
+ apr_thread_mutex_create(&nready.mutex,
+ APR_THREAD_MUTEX_DEFAULT, p));
+ ABTS_PTR_NOTNULL(tc, nready.mutex);
+
+ APR_ASSERT_SUCCESS(tc, "create condvar",
+ apr_thread_cond_create(&nready.cond, p));
+ ABTS_PTR_NOTNULL(tc, nready.cond);
+
+ count1 = count2 = count3 = count4 = 0;
+ put.nput = put.nval = 0;
+ nready.nready = 0;
+ i = 0;
+ x = 0;
+
+ s0 = apr_thread_create(&p1, NULL, thread_cond_producer, &count1, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s0);
+ s1 = apr_thread_create(&p2, NULL, thread_cond_producer, &count2, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s1);
+ s2 = apr_thread_create(&p3, NULL, thread_cond_producer, &count3, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s2);
+ s3 = apr_thread_create(&p4, NULL, thread_cond_producer, &count4, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s3);
+ s4 = apr_thread_create(&c1, NULL, thread_cond_consumer, NULL, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s4);
+
+ apr_thread_join(&s0, p1);
+ apr_thread_join(&s1, p2);
+ apr_thread_join(&s2, p3);
+ apr_thread_join(&s3, p4);
+ apr_thread_join(&s4, c1);
+
+ APR_ASSERT_SUCCESS(tc, "destroy condvar",
+ apr_thread_cond_destroy(nready.cond));
+
+ sum = count1 + count2 + count3 + count4;
+ /*
+ printf("count1 = %d count2 = %d count3 = %d count4 = %d\n",
+ count1, count2, count3, count4);
+ */
+ ABTS_INT_EQUAL(tc, MAX_COUNTER, sum);
+}
+
+static void test_timeoutcond(abts_case *tc, void *data)
+{
+ apr_status_t s;
+ apr_interval_time_t timeout;
+ apr_time_t begin, end;
+ int i;
+
+ s = apr_thread_mutex_create(&timeout_mutex, APR_THREAD_MUTEX_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s);
+ ABTS_PTR_NOTNULL(tc, timeout_mutex);
+
+ s = apr_thread_cond_create(&timeout_cond, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s);
+ ABTS_PTR_NOTNULL(tc, timeout_cond);
+
+ timeout = apr_time_from_sec(5);
+
+ for (i = 0; i < MAX_RETRY; i++) {
+ apr_thread_mutex_lock(timeout_mutex);
+
+ begin = apr_time_now();
+ s = apr_thread_cond_timedwait(timeout_cond, timeout_mutex, timeout);
+ end = apr_time_now();
+ apr_thread_mutex_unlock(timeout_mutex);
+
+ if (s != APR_SUCCESS && !APR_STATUS_IS_TIMEUP(s)) {
+ continue;
+ }
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(s));
+ ABTS_ASSERT(tc, "Timer returned too late", end - begin - timeout < 500000);
+ break;
+ }
+ ABTS_ASSERT(tc, "Too many retries", i < MAX_RETRY);
+ APR_ASSERT_SUCCESS(tc, "Unable to destroy the conditional",
+ apr_thread_cond_destroy(timeout_cond));
+}
+
+/* Test whether _timedlock times out appropriately. Since
+ * double-locking a non-recursive mutex has undefined behaviour, and
+ * double-locking a recursive mutex succeeds immediately, a thread is
+ * spawned to hold the lock while this thread tests whether _timedlock
+ * times out. */
+#if APR_HAS_TIMEDLOCKS
+static void test_timeoutmutex(abts_case *tc, void *data)
+{
+ apr_status_t s;
+ apr_interval_time_t timeout;
+ apr_time_t begin, end;
+ apr_thread_t *th;
+ apr_uint32_t flag = 0;
+ int i;
+
+ s = apr_thread_mutex_create(&timeout_mutex,
+ APR_THREAD_MUTEX_TIMED |
+ APR_THREAD_MUTEX_UNNESTED, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s);
+ ABTS_PTR_NOTNULL(tc, timeout_mutex);
+
+ s = apr_thread_create(&th, NULL, thread_mutex_sleep_function, &flag, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, s);
+
+ wait_for_flag(flag, 1); /* the thread will set flag to 1 once the
+ * timeout_mutex is locked. */
+
+ timeout = apr_time_from_sec(5);
+
+ for (i = 0; i < MAX_RETRY; i++) {
+ begin = apr_time_now();
+ s = apr_thread_mutex_timedlock(timeout_mutex, timeout);
+ end = apr_time_now();
+
+ if (s != APR_SUCCESS && !APR_STATUS_IS_TIMEUP(s)) {
+ continue;
+ }
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(s));
+ ABTS_ASSERT(tc, "Timer returned too late", end - begin - timeout < 1000000);
+ break;
+ }
+
+ apr_atomic_set32(&flag, 0); /* tell the thread to exit. */
+
+ APR_ASSERT_SUCCESS(tc, "join spawned thread", apr_thread_join(&s, th));
+ APR_ASSERT_SUCCESS(tc, "spawned thread terminated", s);
+
+ ABTS_ASSERT(tc, "Too many retries", i < MAX_RETRY);
+ APR_ASSERT_SUCCESS(tc, "Unable to destroy the timeout mutex",
+ apr_thread_mutex_destroy(timeout_mutex));
+}
+#endif
+
+static void test_thread_nestedmutex(abts_case *tc, void *data)
+{
+ apr_thread_mutex_t *m;
+ apr_status_t rv;
+
+ rv = apr_thread_mutex_create(&m, APR_THREAD_MUTEX_NESTED, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, m);
+
+ rv = apr_thread_mutex_lock(m);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_thread_mutex_trylock(m);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ if (rv == APR_SUCCESS)
+ {
+ rv = apr_thread_mutex_unlock(m);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ }
+
+ rv = apr_thread_mutex_unlock(m);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_thread_unnestedmutex(abts_case *tc, void *data)
+{
+ apr_thread_mutex_t *m;
+ apr_status_t rv;
+
+ rv = apr_thread_mutex_create(&m, APR_THREAD_MUTEX_UNNESTED, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, m);
+
+ rv = apr_thread_mutex_lock(m);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_thread_mutex_trylock(m);
+ ABTS_INT_EQUAL(tc, APR_EBUSY, rv);
+ if (rv == APR_SUCCESS)
+ {
+ rv = apr_thread_mutex_unlock(m);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ }
+
+ rv = apr_thread_mutex_unlock(m);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+#ifdef WIN32
+static void *APR_THREAD_FUNC
+thread_win32_abandoned_mutex_function(apr_thread_t *thd, void *data)
+{
+ apr_thread_mutex_t *mutex = data;
+ apr_status_t rv;
+
+ rv = apr_thread_mutex_lock(mutex);
+
+ /* exit from thread without unlocking mutex. */
+ apr_thread_exit(thd, rv);
+
+ return NULL;
+}
+
+static void test_win32_abandoned_mutex(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_thread_t *thread;
+ apr_thread_mutex_t *mutex;
+
+ /* Create timed mutex: APR will create Win32 mutex object in this case. */
+ rv = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_TIMED, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_thread_create(&thread, NULL, thread_win32_abandoned_mutex_function,
+ mutex, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ apr_thread_join(&rv, thread);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_thread_mutex_trylock(mutex);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_thread_mutex_unlock (mutex);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+#endif
+
+#endif /* !APR_HAS_THREADS */
+
+#if !APR_HAS_THREADS
+static void threads_not_impl(abts_case *tc, void *data)
+{
+ ABTS_NOT_IMPL(tc, "Threads not implemented on this platform");
+}
+#endif
+
+
+abts_suite *testlock(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+#if !APR_HAS_THREADS
+ abts_run_test(suite, threads_not_impl, NULL);
+#else
+ abts_run_test(suite, test_thread_mutex, NULL);
+#if APR_HAS_TIMEDLOCKS
+ abts_run_test(suite, test_thread_timedmutex, NULL);
+#endif
+ abts_run_test(suite, test_thread_nestedmutex, NULL);
+ abts_run_test(suite, test_thread_unnestedmutex, NULL);
+ abts_run_test(suite, test_thread_rwlock, NULL);
+ abts_run_test(suite, test_cond, NULL);
+ abts_run_test(suite, test_timeoutcond, NULL);
+#if APR_HAS_TIMEDLOCKS
+ abts_run_test(suite, test_timeoutmutex, NULL);
+#endif
+#ifdef WIN32
+ abts_run_test(suite, test_win32_abandoned_mutex, NULL);
+#endif
+#endif
+
+ return suite;
+}
+
diff --git a/test/testlockperf.c b/test/testlockperf.c
new file mode 100644
index 0000000..f89d790
--- /dev/null
+++ b/test/testlockperf.c
@@ -0,0 +1,348 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_thread_proc.h"
+#include "apr_thread_mutex.h"
+#include "apr_thread_rwlock.h"
+#include "apr_file_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_getopt.h"
+#include "errno.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "testutil.h"
+
+#if !APR_HAS_THREADS
+int main(void)
+{
+ printf("This program won't work on this platform because there is no "
+ "support for threads.\n");
+ return 0;
+}
+#else /* !APR_HAS_THREADS */
+
+#define DEFAULT_MAX_COUNTER 1000000
+#define MAX_THREADS 6
+
+static int verbose = 0;
+static long mutex_counter;
+static long max_counter = DEFAULT_MAX_COUNTER;
+
+static apr_thread_mutex_t *thread_lock;
+void * APR_THREAD_FUNC thread_mutex_func(apr_thread_t *thd, void *data);
+apr_status_t test_thread_mutex(int num_threads); /* apr_thread_mutex_t */
+
+static apr_thread_rwlock_t *thread_rwlock;
+void * APR_THREAD_FUNC thread_rwlock_func(apr_thread_t *thd, void *data);
+apr_status_t test_thread_rwlock(int num_threads); /* apr_thread_rwlock_t */
+
+int test_thread_mutex_nested(int num_threads);
+
+apr_pool_t *pool;
+int i = 0, x = 0;
+
+void * APR_THREAD_FUNC thread_mutex_func(apr_thread_t *thd, void *data)
+{
+ int i;
+
+ for (i = 0; i < max_counter; i++) {
+ if (data) {
+ apr_thread_mutex_timedlock(thread_lock, *(apr_interval_time_t *)data);
+ }
+ else {
+ apr_thread_mutex_lock(thread_lock);
+ }
+ mutex_counter++;
+ apr_thread_mutex_unlock(thread_lock);
+ }
+ return NULL;
+}
+
+void * APR_THREAD_FUNC thread_rwlock_func(apr_thread_t *thd, void *data)
+{
+ int i;
+
+ for (i = 0; i < max_counter; i++) {
+ apr_thread_rwlock_wrlock(thread_rwlock);
+ mutex_counter++;
+ apr_thread_rwlock_unlock(thread_rwlock);
+ }
+ return NULL;
+}
+
+int test_thread_mutex(int num_threads)
+{
+ apr_thread_t *t[MAX_THREADS];
+ apr_status_t s[MAX_THREADS];
+ apr_time_t time_start, time_stop;
+ int i;
+
+ mutex_counter = 0;
+
+ printf("apr_thread_mutex_t Tests\n");
+ printf("%-60s", " Initializing the apr_thread_mutex_t (UNNESTED)");
+ s[0] = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_UNNESTED, pool);
+ if (s[0] != APR_SUCCESS) {
+ printf("Failed!\n");
+ return s[0];
+ }
+ printf("OK\n");
+
+ apr_thread_mutex_lock(thread_lock);
+ /* set_concurrency(4)? -aaron */
+ printf(" Starting %d threads ", num_threads);
+ for (i = 0; i < num_threads; ++i) {
+ s[i] = apr_thread_create(&t[i], NULL, thread_mutex_func, NULL, pool);
+ if (s[i] != APR_SUCCESS) {
+ printf("Failed!\n");
+ return s[i];
+ }
+ }
+ printf("OK\n");
+
+ time_start = apr_time_now();
+ apr_thread_mutex_unlock(thread_lock);
+
+ /* printf("%-60s", " Waiting for threads to exit"); */
+ for (i = 0; i < num_threads; ++i) {
+ apr_thread_join(&s[i], t[i]);
+ }
+ /* printf("OK\n"); */
+
+ time_stop = apr_time_now();
+ printf("microseconds: %" APR_INT64_T_FMT " usec\n",
+ (time_stop - time_start));
+ if (mutex_counter != max_counter * num_threads)
+ printf("error: counter = %ld\n", mutex_counter);
+
+ return APR_SUCCESS;
+}
+
+int test_thread_mutex_nested(int num_threads)
+{
+ apr_thread_t *t[MAX_THREADS];
+ apr_status_t s[MAX_THREADS];
+ apr_time_t time_start, time_stop;
+ int i;
+
+ mutex_counter = 0;
+
+ printf("apr_thread_mutex_t Tests\n");
+ printf("%-60s", " Initializing the apr_thread_mutex_t (NESTED)");
+ s[0] = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_NESTED, pool);
+ if (s[0] != APR_SUCCESS) {
+ printf("Failed!\n");
+ return s[0];
+ }
+ printf("OK\n");
+
+ apr_thread_mutex_lock(thread_lock);
+ /* set_concurrency(4)? -aaron */
+ printf(" Starting %d threads ", num_threads);
+ for (i = 0; i < num_threads; ++i) {
+ s[i] = apr_thread_create(&t[i], NULL, thread_mutex_func, NULL, pool);
+ if (s[i] != APR_SUCCESS) {
+ printf("Failed!\n");
+ return s[i];
+ }
+ }
+ printf("OK\n");
+
+ time_start = apr_time_now();
+ apr_thread_mutex_unlock(thread_lock);
+
+ /* printf("%-60s", " Waiting for threads to exit"); */
+ for (i = 0; i < num_threads; ++i) {
+ apr_thread_join(&s[i], t[i]);
+ }
+ /* printf("OK\n"); */
+
+ time_stop = apr_time_now();
+ printf("microseconds: %" APR_INT64_T_FMT " usec\n",
+ (time_stop - time_start));
+ if (mutex_counter != max_counter * num_threads)
+ printf("error: counter = %ld\n", mutex_counter);
+
+ return APR_SUCCESS;
+}
+
+static int test_thread_mutex_timed(int num_threads)
+{
+ apr_thread_t *t[MAX_THREADS];
+ apr_status_t s[MAX_THREADS];
+ apr_time_t time_start, time_stop;
+ apr_time_t timeout;
+ int i;
+
+ mutex_counter = 0;
+
+ timeout = apr_time_from_sec(5);
+
+ printf("apr_thread_mutex_t Tests\n");
+ printf("%-60s", " Initializing the apr_thread_mutex_t (TIMED)");
+ s[0] = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_TIMED, pool);
+ if (s[0] != APR_SUCCESS) {
+ printf("Failed!\n");
+ return s[0];
+ }
+ printf("OK\n");
+
+ apr_thread_mutex_lock(thread_lock);
+ /* set_concurrency(4)? -aaron */
+ printf(" Starting %d threads ", num_threads);
+ for (i = 0; i < num_threads; ++i) {
+ s[i] = apr_thread_create(&t[i], NULL, thread_mutex_func, &timeout, pool);
+ if (s[i] != APR_SUCCESS) {
+ printf("Failed!\n");
+ return s[i];
+ }
+ }
+ printf("OK\n");
+
+ time_start = apr_time_now();
+ apr_thread_mutex_unlock(thread_lock);
+
+ /* printf("%-60s", " Waiting for threads to exit"); */
+ for (i = 0; i < num_threads; ++i) {
+ apr_thread_join(&s[i], t[i]);
+ }
+ /* printf("OK\n"); */
+
+ time_stop = apr_time_now();
+ printf("microseconds: %" APR_INT64_T_FMT " usec\n",
+ (time_stop - time_start));
+ if (mutex_counter != max_counter * num_threads)
+ printf("error: counter = %ld\n", mutex_counter);
+
+ return APR_SUCCESS;
+}
+
+int test_thread_rwlock(int num_threads)
+{
+ apr_thread_t *t[MAX_THREADS];
+ apr_status_t s[MAX_THREADS];
+ apr_time_t time_start, time_stop;
+ int i;
+
+ mutex_counter = 0;
+
+ printf("apr_thread_rwlock_t Tests\n");
+ printf("%-60s", " Initializing the apr_thread_rwlock_t");
+ s[0] = apr_thread_rwlock_create(&thread_rwlock, pool);
+ if (s[0] != APR_SUCCESS) {
+ printf("Failed!\n");
+ return s[0];
+ }
+ printf("OK\n");
+
+ apr_thread_rwlock_wrlock(thread_rwlock);
+ /* set_concurrency(4)? -aaron */
+ printf(" Starting %d threads ", num_threads);
+ for (i = 0; i < num_threads; ++i) {
+ s[i] = apr_thread_create(&t[i], NULL, thread_rwlock_func, NULL, pool);
+ if (s[i] != APR_SUCCESS) {
+ printf("Failed!\n");
+ return s[i];
+ }
+ }
+ printf("OK\n");
+
+ time_start = apr_time_now();
+ apr_thread_rwlock_unlock(thread_rwlock);
+
+ /* printf("%-60s", " Waiting for threads to exit"); */
+ for (i = 0; i < num_threads; ++i) {
+ apr_thread_join(&s[i], t[i]);
+ }
+ /* printf("OK\n"); */
+
+ time_stop = apr_time_now();
+ printf("microseconds: %" APR_INT64_T_FMT " usec\n",
+ (time_stop - time_start));
+ if (mutex_counter != max_counter * num_threads)
+ printf("error: counter = %ld\n", mutex_counter);
+
+ return APR_SUCCESS;
+}
+
+int main(int argc, const char * const *argv)
+{
+ apr_status_t rv;
+ char errmsg[200];
+ apr_getopt_t *opt;
+ char optchar;
+ const char *optarg;
+
+ printf("APR Lock Performance Test\n==============\n\n");
+
+ apr_initialize();
+ atexit(apr_terminate);
+
+ if (apr_pool_create(&pool, NULL) != APR_SUCCESS)
+ exit(-1);
+
+ if ((rv = apr_getopt_init(&opt, pool, argc, argv)) != APR_SUCCESS) {
+ fprintf(stderr, "Could not set up to parse options: [%d] %s\n",
+ rv, apr_strerror(rv, errmsg, sizeof errmsg));
+ exit(-1);
+ }
+
+ while ((rv = apr_getopt(opt, "c:v", &optchar, &optarg)) == APR_SUCCESS) {
+ if (optchar == 'c') {
+ max_counter = atol(optarg);
+ }
+ else if (optchar == 'v') {
+ verbose = 1;
+ }
+ }
+
+ if (rv != APR_SUCCESS && rv != APR_EOF) {
+ fprintf(stderr, "Could not parse options: [%d] %s\n",
+ rv, apr_strerror(rv, errmsg, sizeof errmsg));
+ exit(-1);
+ }
+
+ for (i = 1; i <= MAX_THREADS; ++i) {
+ if ((rv = test_thread_mutex(i)) != APR_SUCCESS) {
+ fprintf(stderr,"thread_mutex test failed : [%d] %s\n",
+ rv, apr_strerror(rv, (char*)errmsg, 200));
+ exit(-3);
+ }
+
+ if ((rv = test_thread_mutex_nested(i)) != APR_SUCCESS) {
+ fprintf(stderr,"thread_mutex (NESTED) test failed : [%d] %s\n",
+ rv, apr_strerror(rv, (char*)errmsg, 200));
+ exit(-4);
+ }
+
+ if ((rv = test_thread_mutex_timed(i)) != APR_SUCCESS) {
+ fprintf(stderr,"thread_mutex (TIMED) test failed : [%d] %s\n",
+ rv, apr_strerror(rv, (char*)errmsg, 200));
+ exit(-5);
+ }
+
+ if ((rv = test_thread_rwlock(i)) != APR_SUCCESS) {
+ fprintf(stderr,"thread_rwlock test failed : [%d] %s\n",
+ rv, apr_strerror(rv, (char*)errmsg, 200));
+ exit(-6);
+ }
+ }
+
+ return 0;
+}
+
+#endif /* !APR_HAS_THREADS */
diff --git a/test/testmmap.c b/test/testmmap.c
new file mode 100644
index 0000000..140d5c3
--- /dev/null
+++ b/test/testmmap.c
@@ -0,0 +1,165 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_mmap.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_file_io.h"
+#include "apr_strings.h"
+
+/* hmmm, what is a truly portable define for the max path
+ * length on a platform?
+ */
+#define PATH_LEN 255
+
+#if !APR_HAS_MMAP
+static void not_implemented(abts_case *tc, void *data)
+{
+ ABTS_NOT_IMPL(tc, "MMAP functions");
+}
+
+#else
+
+static char test_string[256]; /* read from the datafile */
+static apr_mmap_t *themmap = NULL;
+static apr_file_t *thefile = NULL;
+static char *file1;
+static apr_finfo_t thisfinfo;
+static apr_size_t thisfsize;
+
+static void create_filename(abts_case *tc, void *data)
+{
+ char *oldfileptr;
+
+ apr_filepath_get(&file1, 0, p);
+#ifndef NETWARE
+#ifdef WIN32
+ ABTS_TRUE(tc, file1[1] == ':');
+#else
+ ABTS_TRUE(tc, file1[0] == '/');
+#endif
+#endif
+ ABTS_TRUE(tc, file1[strlen(file1) - 1] != '/');
+
+ oldfileptr = file1;
+ file1 = apr_pstrcat(p, file1,"/data/mmap_datafile.txt" ,NULL);
+ ABTS_TRUE(tc, oldfileptr != file1);
+}
+
+static void test_file_close(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_file_close(thefile);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void read_expected_contents(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_size_t nbytes = sizeof(test_string) - 1;
+
+ rv = apr_file_read(thefile, test_string, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ test_string[nbytes] = '\0';
+ thisfsize = strlen(test_string);
+}
+
+static void test_file_open(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_file_open(&thefile, file1, APR_FOPEN_READ, APR_UREAD | APR_GREAD, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, thefile);
+}
+
+static void test_get_filesize(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_file_info_get(&thisfinfo, APR_FINFO_NORM, thefile);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "File size mismatch", thisfsize == thisfinfo.size);
+}
+
+static void test_mmap_create(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_mmap_create(&themmap, thefile, 0, (apr_size_t) thisfinfo.size,
+ APR_MMAP_READ, p);
+ ABTS_PTR_NOTNULL(tc, themmap);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_mmap_contents(abts_case *tc, void *data)
+{
+
+ ABTS_PTR_NOTNULL(tc, themmap);
+ ABTS_PTR_NOTNULL(tc, themmap->mm);
+ ABTS_SIZE_EQUAL(tc, thisfsize, themmap->size);
+
+ /* Must use nEquals since the string is not guaranteed to be NULL terminated */
+ ABTS_STR_NEQUAL(tc, themmap->mm, test_string, thisfsize);
+}
+
+static void test_mmap_delete(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ ABTS_PTR_NOTNULL(tc, themmap);
+ rv = apr_mmap_delete(themmap);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void test_mmap_offset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ void *addr;
+
+ ABTS_PTR_NOTNULL(tc, themmap);
+ rv = apr_mmap_offset(&addr, themmap, 5);
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ /* Must use nEquals since the string is not guaranteed to be NULL terminated */
+ ABTS_STR_NEQUAL(tc, addr, test_string + 5, thisfsize-5);
+}
+#endif
+
+abts_suite *testmmap(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+#if APR_HAS_MMAP
+ abts_run_test(suite, create_filename, NULL);
+ abts_run_test(suite, test_file_open, NULL);
+ abts_run_test(suite, read_expected_contents, NULL);
+ abts_run_test(suite, test_get_filesize, NULL);
+ abts_run_test(suite, test_mmap_create, NULL);
+ abts_run_test(suite, test_mmap_contents, NULL);
+ abts_run_test(suite, test_mmap_offset, NULL);
+ abts_run_test(suite, test_mmap_delete, NULL);
+ abts_run_test(suite, test_file_close, NULL);
+#else
+ abts_run_test(suite, not_implemented, NULL);
+#endif
+
+ return suite;
+}
+
diff --git a/test/testmutexscope.c b/test/testmutexscope.c
new file mode 100644
index 0000000..2120fff
--- /dev/null
+++ b/test/testmutexscope.c
@@ -0,0 +1,234 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This program won't run or check correctly if assert() is disabled. */
+#undef NDEBUG
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "apr.h"
+#include "apr_general.h"
+#include "apr_proc_mutex.h"
+#include "apr_global_mutex.h"
+#include "apr_thread_proc.h"
+
+#if !APR_HAS_THREADS
+int main(void)
+{
+ printf("This test requires APR thread support.\n");
+ return 0;
+}
+
+#else /* APR_HAS_THREADS */
+
+static apr_thread_mutex_t *thread_mutex;
+static apr_proc_mutex_t *proc_mutex;
+static apr_global_mutex_t *global_mutex;
+static apr_pool_t *p;
+static volatile int counter;
+typedef enum {TEST_GLOBAL, TEST_PROC} test_mode_e;
+
+static int lock_init(apr_lockmech_e mech, test_mode_e test_mode)
+{
+ apr_status_t rv;
+ if (test_mode == TEST_PROC) {
+ rv = apr_proc_mutex_create(&proc_mutex,
+ NULL,
+ mech,
+ p);
+ }
+ else {
+ rv = apr_global_mutex_create(&global_mutex,
+ NULL,
+ mech,
+ p);
+ }
+ return rv;
+}
+
+static void lock_destroy(test_mode_e test_mode)
+{
+ if (test_mode == TEST_PROC) {
+ assert(apr_proc_mutex_destroy(proc_mutex) == APR_SUCCESS);
+ }
+ else {
+ assert(apr_global_mutex_destroy(global_mutex) == APR_SUCCESS);
+ }
+}
+
+static void lock_grab(test_mode_e test_mode)
+{
+ if (test_mode == TEST_PROC) {
+ assert(apr_proc_mutex_lock(proc_mutex) == APR_SUCCESS);
+ }
+ else {
+ assert(apr_global_mutex_lock(global_mutex) == APR_SUCCESS);
+ }
+}
+
+static void lock_release(test_mode_e test_mode)
+{
+ if (test_mode == TEST_PROC) {
+ assert(apr_proc_mutex_unlock(proc_mutex) == APR_SUCCESS);
+ }
+ else {
+ assert(apr_global_mutex_unlock(global_mutex) == APR_SUCCESS);
+ }
+}
+
+static void * APR_THREAD_FUNC eachThread(apr_thread_t *id, void *p)
+{
+ test_mode_e test_mode = (test_mode_e)p;
+
+ lock_grab(test_mode);
+ ++counter;
+ assert(apr_thread_mutex_lock(thread_mutex) == APR_SUCCESS);
+ assert(apr_thread_mutex_unlock(thread_mutex) == APR_SUCCESS);
+ lock_release(test_mode);
+ apr_thread_exit(id, 0);
+ return NULL;
+}
+
+static void test_mech_mode(apr_lockmech_e mech, const char *mech_name,
+ test_mode_e test_mode)
+{
+ apr_thread_t *threads[20];
+ int numThreads = 5;
+ int i;
+ apr_status_t rv;
+
+ printf("Trying %s mutexes with mechanism `%s'...\n",
+ test_mode == TEST_GLOBAL ? "global" : "proc", mech_name);
+
+ assert(numThreads <= sizeof(threads) / sizeof(threads[0]));
+
+ assert(apr_pool_create(&p, NULL) == APR_SUCCESS);
+
+ assert(apr_thread_mutex_create(&thread_mutex, 0, p) == APR_SUCCESS);
+ assert(apr_thread_mutex_lock(thread_mutex) == APR_SUCCESS);
+
+ rv = lock_init(mech, test_mode);
+ if (rv != APR_SUCCESS) {
+ char errmsg[256];
+ printf("%s mutexes with mechanism `%s': %s\n",
+ test_mode == TEST_GLOBAL ? "Global" : "Proc", mech_name,
+ apr_strerror(rv, errmsg, sizeof errmsg));
+ if (rv != APR_ENOTIMPL || mech == APR_LOCK_DEFAULT) {
+ exit(1);
+ }
+ return;
+ }
+
+ counter = 0;
+
+ i = 0;
+ while (i < numThreads)
+ {
+ rv = apr_thread_create(&threads[i],
+ NULL,
+ eachThread,
+ (void *)test_mode,
+ p);
+ if (rv != APR_SUCCESS) {
+ fprintf(stderr, "apr_thread_create->%d\n", rv);
+ exit(1);
+ }
+ ++i;
+ }
+
+ apr_sleep(apr_time_from_sec(5));
+
+ if (test_mode == TEST_PROC) {
+ printf(" mutex mechanism `%s' is %sglobal in scope on this platform.\n",
+ mech_name, counter == 1 ? "" : "*NOT* ");
+ }
+ else {
+ if (counter != 1) {
+ fprintf(stderr, "\n!!!apr_global_mutex operations are broken on this "
+ "platform for mutex mechanism `%s'!\n"
+ "They don't block out threads within the same process.\n",
+ mech_name);
+ fprintf(stderr, "counter value: %d\n", counter);
+ exit(1);
+ }
+ else {
+ printf(" no problem encountered...\n");
+ }
+ }
+
+ assert(apr_thread_mutex_unlock(thread_mutex) == APR_SUCCESS);
+
+ i = 0;
+ while (i < numThreads)
+ {
+ apr_status_t ignored;
+
+ rv = apr_thread_join(&ignored,
+ threads[i]);
+ assert(rv == APR_SUCCESS);
+ ++i;
+ }
+
+ lock_destroy(test_mode);
+ apr_thread_mutex_destroy(thread_mutex);
+ apr_pool_destroy(p);
+}
+
+static void test_mech(apr_lockmech_e mech, const char *mech_name)
+{
+ test_mech_mode(mech, mech_name, TEST_PROC);
+ test_mech_mode(mech, mech_name, TEST_GLOBAL);
+}
+
+int main(void)
+{
+ struct {
+ apr_lockmech_e mech;
+ const char *mech_name;
+ } lockmechs[] = {
+ {APR_LOCK_DEFAULT, "default"}
+#if APR_HAS_FLOCK_SERIALIZE
+ ,{APR_LOCK_FLOCK, "flock"}
+#endif
+#if APR_HAS_SYSVSEM_SERIALIZE
+ ,{APR_LOCK_SYSVSEM, "sysvsem"}
+#endif
+#if APR_HAS_POSIXSEM_SERIALIZE
+ ,{APR_LOCK_POSIXSEM, "posix"}
+#endif
+#if APR_HAS_FCNTL_SERIALIZE
+ ,{APR_LOCK_FCNTL, "fcntl"}
+#endif
+#if APR_HAS_PROC_PTHREAD_SERIALIZE
+ ,{APR_LOCK_PROC_PTHREAD, "proc_pthread"}
+#endif
+ ,{APR_LOCK_DEFAULT_TIMED, "default_timed"}
+ };
+ int i;
+
+ assert(apr_initialize() == APR_SUCCESS);
+
+ for (i = 0; i < sizeof(lockmechs) / sizeof(lockmechs[0]); i++) {
+ test_mech(lockmechs[i].mech, lockmechs[i].mech_name);
+ }
+
+ apr_terminate();
+ return 0;
+}
+
+#endif /* APR_HAS_THREADS */
diff --git a/test/testnames.c b/test/testnames.c
new file mode 100644
index 0000000..4fcd3c0
--- /dev/null
+++ b/test/testnames.c
@@ -0,0 +1,387 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_file_io.h"
+#include "apr_file_info.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+
+#if defined(WIN32)
+#include <direct.h>
+#endif
+
+#if defined(WIN32) || defined(OS2)
+#define ABS_ROOT "C:/"
+#elif defined(NETWARE)
+#define ABS_ROOT "SYS:/"
+#else
+#define ABS_ROOT "/"
+#endif
+
+static void merge_aboveroot(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *dstpath = NULL;
+ char errmsg[256];
+
+ rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo", ABS_ROOT"bar", APR_FILEPATH_NOTABOVEROOT,
+ p);
+ apr_strerror(rv, errmsg, sizeof(errmsg));
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EABOVEROOT(rv));
+ ABTS_PTR_EQUAL(tc, NULL, dstpath);
+ ABTS_STR_EQUAL(tc, "The given path was above the root path", errmsg);
+}
+
+static void merge_belowroot(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *dstpath = NULL;
+
+ rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo", ABS_ROOT"foo/bar",
+ APR_FILEPATH_NOTABOVEROOT, p);
+ ABTS_PTR_NOTNULL(tc, dstpath);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, ABS_ROOT"foo/bar", dstpath);
+}
+
+static void merge_noflag(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *dstpath = NULL;
+
+ rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo", ABS_ROOT"foo/bar", 0, p);
+ ABTS_PTR_NOTNULL(tc, dstpath);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, ABS_ROOT"foo/bar", dstpath);
+}
+
+static void merge_dotdot(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *dstpath = NULL;
+
+ rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../baz", 0, p);
+ ABTS_PTR_NOTNULL(tc, dstpath);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, ABS_ROOT"foo/baz", dstpath);
+
+ rv = apr_filepath_merge(&dstpath, "", "../test", 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "../test", dstpath);
+
+ /* Very dangerous assumptions here about what the cwd is. However, let's assume
+ * that the testall is invoked from within apr/test/ so the following test should
+ * return ../test unless a previously fixed bug remains or the developer changes
+ * the case of the test directory:
+ */
+ rv = apr_filepath_merge(&dstpath, "", "../test", APR_FILEPATH_TRUENAME, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "../test", dstpath);
+}
+
+static void merge_dotdot_dotdot_dotdot(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *dstpath = NULL;
+
+ rv = apr_filepath_merge(&dstpath, "",
+ "../../..", APR_FILEPATH_TRUENAME, p);
+ ABTS_PTR_NOTNULL(tc, dstpath);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "../../..", dstpath);
+
+ rv = apr_filepath_merge(&dstpath, "",
+ "../../../", APR_FILEPATH_TRUENAME, p);
+ ABTS_PTR_NOTNULL(tc, dstpath);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "../../../", dstpath);
+}
+
+static void merge_secure(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *dstpath = NULL;
+
+ rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../bar/baz", 0, p);
+ ABTS_PTR_NOTNULL(tc, dstpath);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, ABS_ROOT"foo/bar/baz", dstpath);
+}
+
+static void merge_notrel(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *dstpath = NULL;
+
+ rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../baz",
+ APR_FILEPATH_NOTRELATIVE, p);
+ ABTS_PTR_NOTNULL(tc, dstpath);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, ABS_ROOT"foo/baz", dstpath);
+}
+
+static void merge_notrelfail(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *dstpath = NULL;
+ char errmsg[256];
+
+ rv = apr_filepath_merge(&dstpath, "foo/bar", "../baz",
+ APR_FILEPATH_NOTRELATIVE, p);
+ apr_strerror(rv, errmsg, sizeof(errmsg));
+
+ ABTS_PTR_EQUAL(tc, NULL, dstpath);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ERELATIVE(rv));
+ ABTS_STR_EQUAL(tc, "The given path is relative", errmsg);
+}
+
+static void merge_notabsfail(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *dstpath = NULL;
+ char errmsg[256];
+
+ rv = apr_filepath_merge(&dstpath, ABS_ROOT"foo/bar", "../baz",
+ APR_FILEPATH_NOTABSOLUTE, p);
+ apr_strerror(rv, errmsg, sizeof(errmsg));
+
+ ABTS_PTR_EQUAL(tc, NULL, dstpath);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EABSOLUTE(rv));
+ ABTS_STR_EQUAL(tc, "The given path is absolute", errmsg);
+}
+
+static void merge_notabs(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *dstpath = NULL;
+
+ rv = apr_filepath_merge(&dstpath, "foo/bar", "../baz",
+ APR_FILEPATH_NOTABSOLUTE, p);
+
+ ABTS_PTR_NOTNULL(tc, dstpath);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "foo/baz", dstpath);
+}
+
+#if defined (WIN32)
+static void merge_lowercasedrive(abts_case *tc, void *data)
+{
+ char current_dir[1024];
+ char current_dir_on_C[1024];
+ char *dir_on_c;
+ char *testdir;
+ apr_status_t rv;
+
+ /* Change the current directory on C: from something like "C:\dir"
+ to something like "c:\dir" to replicate the failing case. */
+ ABTS_PTR_NOTNULL(tc, _getcwd(current_dir, sizeof(current_dir)));
+
+ /* 3 stands for drive C: */
+ ABTS_PTR_NOTNULL(tc, _getdcwd(3, current_dir_on_C,
+ sizeof(current_dir_on_C)));
+
+ /* Use the same path, but now with a lower case driveletter */
+ dir_on_c = apr_pstrdup(p, current_dir_on_C);
+ dir_on_c[0] = (char)tolower(dir_on_c[0]);
+
+ chdir(dir_on_c);
+
+ /* Now merge a drive relative path with an upper case drive letter. */
+ rv = apr_filepath_merge(&testdir, NULL, "C:hi",
+ APR_FILEPATH_NOTRELATIVE, p);
+
+ /* Change back to original directory for next tests */
+ chdir("C:\\"); /* Switch to upper case */
+ chdir(current_dir_on_C); /* Switch cwd on C: */
+ chdir(current_dir); /* Switch back to original cwd */
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void merge_shortname(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *long_path;
+ char short_path[MAX_PATH+1];
+ DWORD short_len;
+ char *result_path;
+
+ /* 'A b.c' is not a valid short path, so will have multiple representations
+ when short path name generation is enabled... but its 'short' path will
+ most likely be longer than the long path */
+ rv = apr_dir_make_recursive("C:/data/short/A b.c",
+ APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_filepath_merge(&long_path, NULL, "C:/data/short/A b.c",
+ APR_FILEPATH_NOTRELATIVE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ short_len = GetShortPathName(long_path, short_path, sizeof(short_path));
+ if (short_len > MAX_PATH)
+ return; /* Unable to test. Impossible shortname */
+
+ if (! strcmp(long_path, short_path))
+ return; /* Unable to test. 8dot3name option is probably not enabled */
+
+ rv = apr_filepath_merge(&result_path, "", short_path, APR_FILEPATH_TRUENAME,
+ p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ ABTS_STR_EQUAL(tc, long_path, result_path);
+}
+#endif
+
+static void root_absolute(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ const char *root = NULL;
+ const char *path = ABS_ROOT"foo/bar";
+
+ rv = apr_filepath_root(&root, &path, 0, p);
+
+ ABTS_PTR_NOTNULL(tc, root);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, ABS_ROOT, root);
+}
+
+static void root_relative(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ const char *root = NULL;
+ const char *path = "foo/bar";
+ char errmsg[256];
+
+ rv = apr_filepath_root(&root, &path, 0, p);
+ apr_strerror(rv, errmsg, sizeof(errmsg));
+
+ ABTS_PTR_EQUAL(tc, NULL, root);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ERELATIVE(rv));
+ ABTS_STR_EQUAL(tc, "The given path is relative", errmsg);
+}
+
+static void root_from_slash(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ const char *root = NULL;
+ const char *path = "//";
+
+ rv = apr_filepath_root(&root, &path, APR_FILEPATH_TRUENAME, p);
+
+#if defined(WIN32) || defined(OS2)
+ ABTS_INT_EQUAL(tc, APR_EINCOMPLETE, rv);
+ ABTS_STR_EQUAL(tc, "//", root);
+#else
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "/", root);
+#endif
+ ABTS_STR_EQUAL(tc, "", path);
+}
+
+static void root_from_cwd_and_back(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ const char *root = NULL;
+ const char *path = "//";
+ char *origpath;
+ char *testpath;
+#if defined(WIN32) || defined(OS2) || defined(NETWARE)
+ int hadfailed;
+#endif
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_filepath_get(&origpath, 0, p));
+ path = origpath;
+ rv = apr_filepath_root(&root, &path, APR_FILEPATH_TRUENAME, p);
+
+#if defined(WIN32) || defined(OS2)
+ hadfailed = tc->failed;
+ /* It appears some mingw/cygwin and more modern builds can return
+ * a lowercase drive designation, but we canonicalize to uppercase
+ */
+ ABTS_INT_EQUAL(tc, toupper(origpath[0]), root[0]);
+ ABTS_INT_EQUAL(tc, ':', root[1]);
+ ABTS_INT_EQUAL(tc, '/', root[2]);
+ ABTS_INT_EQUAL(tc, 0, root[3]);
+ ABTS_STR_EQUAL(tc, origpath + 3, path);
+#elif defined(NETWARE)
+ ABTS_INT_EQUAL(tc, origpath[0], root[0]);
+ {
+ char *pt = strchr(root, ':');
+ ABTS_PTR_NOTNULL(tc, pt);
+ ABTS_INT_EQUAL(tc, ':', pt[0]);
+ ABTS_INT_EQUAL(tc, '/', pt[1]);
+ ABTS_INT_EQUAL(tc, 0, pt[2]);
+ pt = strchr(origpath, ':');
+ ABTS_PTR_NOTNULL(tc, pt);
+ ABTS_STR_EQUAL(tc, (pt+2), path);
+ }
+#else
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "/", root);
+ ABTS_STR_EQUAL(tc, origpath + 1, path);
+#endif
+
+ rv = apr_filepath_merge(&testpath, root, path,
+ APR_FILEPATH_TRUENAME
+ | APR_FILEPATH_NOTABOVEROOT
+ | APR_FILEPATH_NOTRELATIVE, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+#if defined(WIN32) || defined(OS2) || defined(NETWARE)
+ hadfailed = tc->failed;
+#endif
+ /* The API doesn't promise equality!!!
+ * apr_filepath_get never promised a canonical filepath.
+ * We'll emit noise under verbose so the user is aware,
+ * but translate this back to success.
+ */
+ ABTS_STR_EQUAL(tc, origpath, testpath);
+#if defined(WIN32) || defined(OS2) || defined(NETWARE)
+ if (!hadfailed) tc->failed = 0;
+#endif
+}
+
+
+abts_suite *testnames(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, merge_aboveroot, NULL);
+ abts_run_test(suite, merge_belowroot, NULL);
+ abts_run_test(suite, merge_noflag, NULL);
+ abts_run_test(suite, merge_dotdot, NULL);
+ abts_run_test(suite, merge_secure, NULL);
+ abts_run_test(suite, merge_notrel, NULL);
+ abts_run_test(suite, merge_notrelfail, NULL);
+ abts_run_test(suite, merge_notabs, NULL);
+ abts_run_test(suite, merge_notabsfail, NULL);
+ abts_run_test(suite, merge_dotdot_dotdot_dotdot, NULL);
+#if defined(WIN32)
+ abts_run_test(suite, merge_lowercasedrive, NULL);
+ abts_run_test(suite, merge_shortname, NULL);
+#endif
+
+ abts_run_test(suite, root_absolute, NULL);
+ abts_run_test(suite, root_relative, NULL);
+ abts_run_test(suite, root_from_slash, NULL);
+ abts_run_test(suite, root_from_cwd_and_back, NULL);
+
+ return suite;
+}
+
diff --git a/test/testoc.c b/test/testoc.c
new file mode 100644
index 0000000..923bd4b
--- /dev/null
+++ b/test/testoc.c
@@ -0,0 +1,120 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_thread_proc.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+
+#if APR_HAS_OTHER_CHILD
+
+static char reasonstr[256];
+
+static void ocmaint(int reason, void *data, int status)
+{
+ switch (reason) {
+ case APR_OC_REASON_DEATH:
+ apr_cpystrn(reasonstr, "APR_OC_REASON_DEATH",
+ strlen("APR_OC_REASON_DEATH") + 1);
+ break;
+ case APR_OC_REASON_LOST:
+ apr_cpystrn(reasonstr, "APR_OC_REASON_LOST",
+ strlen("APR_OC_REASON_LOST") + 1);
+ break;
+ case APR_OC_REASON_UNWRITABLE:
+ apr_cpystrn(reasonstr, "APR_OC_REASON_UNWRITEABLE",
+ strlen("APR_OC_REASON_UNWRITEABLE") + 1);
+ break;
+ case APR_OC_REASON_RESTART:
+ apr_cpystrn(reasonstr, "APR_OC_REASON_RESTART",
+ strlen("APR_OC_REASON_RESTART") + 1);
+ break;
+ }
+}
+
+#ifndef SIGKILL
+#define SIGKILL 1
+#endif
+
+/* It would be great if we could stress this stuff more, and make the test
+ * more granular.
+ */
+static void test_child_kill(abts_case *tc, void *data)
+{
+ apr_file_t *std = NULL;
+ apr_proc_t newproc;
+ apr_procattr_t *procattr = NULL;
+ const char *args[3];
+ apr_status_t rv;
+
+ args[0] = apr_pstrdup(p, "occhild" EXTENSION);
+ args[1] = apr_pstrdup(p, "-X");
+ args[2] = NULL;
+
+ rv = apr_procattr_create(&procattr, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_NO_PIPE,
+ APR_NO_PIPE);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM_ENV);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set copy environment", rv);
+
+ rv = apr_proc_create(&newproc, TESTBINPATH "occhild" EXTENSION, args, NULL, procattr, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, newproc.in);
+ ABTS_PTR_EQUAL(tc, NULL, newproc.out);
+ ABTS_PTR_EQUAL(tc, NULL, newproc.err);
+
+ std = newproc.in;
+
+ apr_proc_other_child_register(&newproc, ocmaint, NULL, std, p);
+
+ apr_sleep(apr_time_from_sec(1));
+ rv = apr_proc_kill(&newproc, SIGKILL);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* allow time for things to settle... */
+ apr_sleep(apr_time_from_sec(3));
+
+ apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
+ ABTS_STR_EQUAL(tc, "APR_OC_REASON_DEATH", reasonstr);
+}
+#else
+
+static void oc_not_impl(abts_case *tc, void *data)
+{
+ ABTS_NOT_IMPL(tc, "Other child logic not implemented on this platform");
+}
+#endif
+
+abts_suite *testoc(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+#if !APR_HAS_OTHER_CHILD
+ abts_run_test(suite, oc_not_impl, NULL);
+#else
+
+ abts_run_test(suite, test_child_kill, NULL);
+
+#endif
+ return suite;
+}
+
diff --git a/test/testpath.c b/test/testpath.c
new file mode 100644
index 0000000..b05ae99
--- /dev/null
+++ b/test/testpath.c
@@ -0,0 +1,138 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_file_info.h"
+#include "apr_errno.h"
+#include "apr_pools.h"
+#include "apr_tables.h"
+
+#if defined(WIN32) || defined(NETWARE) || defined(OS2)
+#define PSEP ";"
+#define DSEP "\\"
+#else
+#define PSEP ":"
+#define DSEP "/"
+#endif
+
+#define PX ""
+#define P1 "first path"
+#define P2 "second" DSEP "path"
+#define P3 "th ird" DSEP "path"
+#define P4 "fourth" DSEP "pa th"
+#define P5 "fifthpath"
+
+static const char *parts_in[] = { P1, P2, P3, PX, P4, P5 };
+static const char *path_in = P1 PSEP P2 PSEP P3 PSEP PX PSEP P4 PSEP P5;
+static const int parts_in_count = sizeof(parts_in)/sizeof(*parts_in);
+
+static const char *parts_out[] = { P1, P2, P3, P4, P5 };
+static const char *path_out = P1 PSEP P2 PSEP P3 PSEP P4 PSEP P5;
+static const int parts_out_count = sizeof(parts_out)/sizeof(*parts_out);
+
+static void list_split_multi(abts_case *tc, void *data)
+{
+ int i;
+ apr_status_t rv;
+ apr_array_header_t *pathelts;
+
+ pathelts = NULL;
+ rv = apr_filepath_list_split(&pathelts, path_in, p);
+ ABTS_PTR_NOTNULL(tc, pathelts);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, parts_out_count, pathelts->nelts);
+ for (i = 0; i < pathelts->nelts; ++i)
+ ABTS_STR_EQUAL(tc, parts_out[i], ((char**)pathelts->elts)[i]);
+}
+
+static void list_split_single(abts_case *tc, void *data)
+{
+ int i;
+ apr_status_t rv;
+ apr_array_header_t *pathelts;
+
+ for (i = 0; i < parts_in_count; ++i)
+ {
+ pathelts = NULL;
+ rv = apr_filepath_list_split(&pathelts, parts_in[i], p);
+ ABTS_PTR_NOTNULL(tc, pathelts);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ if (parts_in[i][0] == '\0')
+ ABTS_INT_EQUAL(tc, 0, pathelts->nelts);
+ else
+ {
+ ABTS_INT_EQUAL(tc, 1, pathelts->nelts);
+ ABTS_STR_EQUAL(tc, parts_in[i], *(char**)pathelts->elts);
+ }
+ }
+}
+
+static void list_merge_multi(abts_case *tc, void *data)
+{
+ int i;
+ char *liststr;
+ apr_status_t rv;
+ apr_array_header_t *pathelts;
+
+ pathelts = apr_array_make(p, parts_in_count, sizeof(const char*));
+ for (i = 0; i < parts_in_count; ++i)
+ *(const char**)apr_array_push(pathelts) = parts_in[i];
+
+ liststr = NULL;
+ rv = apr_filepath_list_merge(&liststr, pathelts, p);
+ ABTS_PTR_NOTNULL(tc, liststr);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, liststr, path_out);
+}
+
+static void list_merge_single(abts_case *tc, void *data)
+{
+ int i;
+ char *liststr;
+ apr_status_t rv;
+ apr_array_header_t *pathelts;
+
+ pathelts = apr_array_make(p, 1, sizeof(const char*));
+ apr_array_push(pathelts);
+ for (i = 0; i < parts_in_count; ++i)
+ {
+ *(const char**)pathelts->elts = parts_in[i];
+ liststr = NULL;
+ rv = apr_filepath_list_merge(&liststr, pathelts, p);
+ if (parts_in[i][0] == '\0')
+ ABTS_PTR_EQUAL(tc, NULL, liststr);
+ else
+ {
+ ABTS_PTR_NOTNULL(tc, liststr);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, liststr, parts_in[i]);
+ }
+ }
+}
+
+
+abts_suite *testpath(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, list_split_multi, NULL);
+ abts_run_test(suite, list_split_single, NULL);
+ abts_run_test(suite, list_merge_multi, NULL);
+ abts_run_test(suite, list_merge_single, NULL);
+
+ return suite;
+}
+
diff --git a/test/testpipe.c b/test/testpipe.c
new file mode 100644
index 0000000..a56a773
--- /dev/null
+++ b/test/testpipe.c
@@ -0,0 +1,205 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include "testutil.h"
+#include "apr_file_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_thread_proc.h"
+#include "apr_strings.h"
+
+static apr_file_t *readp = NULL;
+static apr_file_t *writep = NULL;
+
+static void create_pipe(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_file_pipe_create(&readp, &writep, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, readp);
+ ABTS_PTR_NOTNULL(tc, writep);
+}
+
+static void close_pipe(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_size_t nbytes = 256;
+ char buf[256];
+
+ rv = apr_file_close(readp);
+ rv = apr_file_close(writep);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_read(readp, buf, &nbytes);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EBADF(rv));
+}
+
+static void set_timeout(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_interval_time_t timeout;
+
+ rv = apr_file_pipe_create_pools(&readp, &writep, APR_WRITE_BLOCK, p, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, readp);
+ ABTS_PTR_NOTNULL(tc, writep);
+
+ rv = apr_file_pipe_timeout_get(writep, &timeout);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "Timeout mismatch, expected -1", timeout == -1);
+
+ rv = apr_file_pipe_timeout_set(readp, apr_time_from_sec(1));
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_pipe_timeout_get(readp, &timeout);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "Timeout mismatch, expected 1 second",
+ timeout == apr_time_from_sec(1));
+}
+
+static void read_write(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *buf;
+ apr_size_t nbytes;
+
+ nbytes = strlen("this is a test");
+ buf = (char *)apr_palloc(p, nbytes + 1);
+
+ rv = apr_file_pipe_create_pools(&readp, &writep, APR_WRITE_BLOCK, p, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, readp);
+ ABTS_PTR_NOTNULL(tc, writep);
+
+ rv = apr_file_pipe_timeout_set(readp, apr_time_from_sec(1));
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ if (!rv) {
+ rv = apr_file_read(readp, buf, &nbytes);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_SIZE_EQUAL(tc, 0, nbytes);
+ }
+}
+
+static void read_write_notimeout(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char *buf = "this is a test";
+ char *input;
+ apr_size_t nbytes;
+
+ nbytes = strlen("this is a test");
+
+ rv = apr_file_pipe_create(&readp, &writep, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, readp);
+ ABTS_PTR_NOTNULL(tc, writep);
+
+ rv = apr_file_write(writep, buf, &nbytes);
+ ABTS_SIZE_EQUAL(tc, strlen("this is a test"), nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ nbytes = 256;
+ input = apr_pcalloc(p, nbytes + 1);
+ rv = apr_file_read(readp, input, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen("this is a test"), nbytes);
+ ABTS_STR_EQUAL(tc, "this is a test", input);
+}
+
+static void test_pipe_writefull(abts_case *tc, void *data)
+{
+ int iterations = 1000;
+ int i;
+ int bytes_per_iteration = 8000;
+ char *buf = (char *)calloc(bytes_per_iteration, 1);
+ char responsebuf[128];
+ apr_size_t nbytes;
+ int bytes_processed;
+ apr_proc_t proc = {0};
+ apr_procattr_t *procattr;
+ const char *args[2];
+ apr_status_t rv;
+ apr_exit_why_e why;
+
+ rv = apr_procattr_create(&procattr, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK,
+ APR_CHILD_BLOCK);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM_ENV);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set copy environment", rv);
+
+ rv = apr_procattr_error_check_set(procattr, 1);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ args[0] = "readchild" EXTENSION;
+ args[1] = NULL;
+ rv = apr_proc_create(&proc, TESTBINPATH "readchild" EXTENSION, args, NULL, procattr, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_pipe_timeout_set(proc.in, apr_time_from_sec(10));
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_file_pipe_timeout_set(proc.out, apr_time_from_sec(10));
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ i = iterations;
+ do {
+ rv = apr_file_write_full(proc.in, buf, bytes_per_iteration, NULL);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ } while (--i);
+
+ free(buf);
+
+ rv = apr_file_close(proc.in);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ nbytes = sizeof(responsebuf);
+ rv = apr_file_read(proc.out, responsebuf, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ bytes_processed = (int)apr_strtoi64(responsebuf, NULL, 10);
+ ABTS_INT_EQUAL(tc, iterations * bytes_per_iteration, bytes_processed);
+
+ ABTS_ASSERT(tc, "wait for child process",
+ apr_proc_wait(&proc, NULL, &why, APR_WAIT) == APR_CHILD_DONE);
+
+ ABTS_ASSERT(tc, "child terminated normally", why == APR_PROC_EXIT);
+}
+
+abts_suite *testpipe(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, create_pipe, NULL);
+ abts_run_test(suite, close_pipe, NULL);
+ abts_run_test(suite, set_timeout, NULL);
+ abts_run_test(suite, close_pipe, NULL);
+ abts_run_test(suite, read_write, NULL);
+ abts_run_test(suite, close_pipe, NULL);
+ abts_run_test(suite, read_write_notimeout, NULL);
+ abts_run_test(suite, test_pipe_writefull, NULL);
+ abts_run_test(suite, close_pipe, NULL);
+
+ return suite;
+}
+
diff --git a/test/testpoll.c b/test/testpoll.c
new file mode 100644
index 0000000..9f90af2
--- /dev/null
+++ b/test/testpoll.c
@@ -0,0 +1,965 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_strings.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_network_io.h"
+#include "apr_poll.h"
+
+#define SMALL_NUM_SOCKETS 3
+/* We can't use 64 here, because some platforms *ahem* Solaris *ahem* have
+ * a default limit of 64 open file descriptors per process. If we use
+ * 64, the test will fail even though the code is correct.
+ */
+#define LARGE_NUM_SOCKETS 50
+
+static apr_socket_t *s[LARGE_NUM_SOCKETS];
+static apr_sockaddr_t *sa[LARGE_NUM_SOCKETS];
+static apr_pollset_t *pollset;
+static apr_pollcb_t *pollcb;
+
+/* ###: tests surrounded by ifdef OLD_POLL_INTERFACE either need to be
+ * converted to use the pollset interface or removed. */
+
+#ifdef OLD_POLL_INTERFACE
+static apr_pollfd_t *pollarray;
+static apr_pollfd_t *pollarray_large;
+#endif
+
+/* default_pollset_impl can be overridden temporarily to control
+ * testcases which don't specify an implementation explicitly
+ */
+static int default_pollset_impl = APR_POLLSET_DEFAULT;
+
+static void make_socket(apr_socket_t **sock, apr_sockaddr_t **sa,
+ apr_port_t port, apr_pool_t *p, abts_case *tc)
+{
+ apr_status_t rv;
+
+ rv = apr_sockaddr_info_get(sa, "127.0.0.1", APR_UNSPEC, port, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_socket_create(sock, (*sa)->family, SOCK_DGRAM, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_socket_bind((*sock), (*sa));
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+#ifdef OLD_POLL_INTERFACE
+static void check_sockets(const apr_pollfd_t *pollarray,
+ apr_socket_t **sockarray, int which, int pollin,
+ abts_case *tc)
+{
+ apr_status_t rv;
+ apr_int16_t event;
+ char *str;
+
+ rv = apr_poll_revents_get(&event, sockarray[which],
+ (apr_pollfd_t *)pollarray);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ if (pollin) {
+ str = apr_psprintf(p, "Socket %d not signalled when it should be",
+ which);
+ ABTS_ASSERT(tc, str, event & APR_POLLIN);
+ } else {
+ str = apr_psprintf(p, "Socket %d signalled when it should not be",
+ which);
+ ABTS_ASSERT(tc, str, !(event & APR_POLLIN));
+ }
+}
+#endif
+
+static void send_msg(apr_socket_t **sockarray, apr_sockaddr_t **sas, int which,
+ abts_case *tc)
+{
+ apr_size_t len = 5;
+ apr_status_t rv;
+
+ ABTS_PTR_NOTNULL(tc, sockarray[which]);
+
+ rv = apr_socket_sendto(sockarray[which], sas[which], 0, "hello", &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen("hello"), len);
+}
+
+static void recv_msg(apr_socket_t **sockarray, int which, apr_pool_t *p,
+ abts_case *tc)
+{
+ apr_size_t buflen = 5;
+ char *buffer = apr_pcalloc(p, sizeof(char) * (buflen + 1));
+ apr_sockaddr_t *recsa;
+ apr_status_t rv;
+
+ ABTS_PTR_NOTNULL(tc, sockarray[which]);
+
+ apr_sockaddr_info_get(&recsa, "127.0.0.1", APR_UNSPEC, 7770, 0, p);
+
+ rv = apr_socket_recvfrom(recsa, sockarray[which], 0, buffer, &buflen);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen("hello"), buflen);
+ ABTS_STR_EQUAL(tc, "hello", buffer);
+}
+
+
+static void create_all_sockets(abts_case *tc, void *data)
+{
+ int i;
+
+ for (i = 0; i < LARGE_NUM_SOCKETS; i++){
+ make_socket(&s[i], &sa[i], 7777 + i, p, tc);
+ }
+}
+
+#ifdef OLD_POLL_INTERFACE
+static void setup_small_poll(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int i;
+
+ rv = apr_poll_setup(&pollarray, SMALL_NUM_SOCKETS, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ for (i = 0; i < SMALL_NUM_SOCKETS;i++){
+ ABTS_INT_EQUAL(tc, 0, pollarray[i].reqevents);
+ ABTS_INT_EQUAL(tc, 0, pollarray[i].rtnevents);
+
+ rv = apr_poll_socket_add(pollarray, s[i], APR_POLLIN);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_EQUAL(tc, s[i], pollarray[i].desc.s);
+ }
+}
+
+static void setup_large_poll(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int i;
+
+ rv = apr_poll_setup(&pollarray_large, LARGE_NUM_SOCKETS, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ for (i = 0; i < LARGE_NUM_SOCKETS;i++){
+ ABTS_INT_EQUAL(tc, 0, pollarray_large[i].reqevents);
+ ABTS_INT_EQUAL(tc, 0, pollarray_large[i].rtnevents);
+
+ rv = apr_poll_socket_add(pollarray_large, s[i], APR_POLLIN);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_EQUAL(tc, s[i], pollarray_large[i].desc.s);
+ }
+}
+
+static void nomessage(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int srv = SMALL_NUM_SOCKETS;
+
+ rv = apr_poll(pollarray, SMALL_NUM_SOCKETS, &srv, 2 * APR_USEC_PER_SEC);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ check_sockets(pollarray, s, 0, 0, tc);
+ check_sockets(pollarray, s, 1, 0, tc);
+ check_sockets(pollarray, s, 2, 0, tc);
+}
+
+static void send_2(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int srv = SMALL_NUM_SOCKETS;
+
+ send_msg(s, sa, 2, tc);
+
+ rv = apr_poll(pollarray, SMALL_NUM_SOCKETS, &srv, 2 * APR_USEC_PER_SEC);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ check_sockets(pollarray, s, 0, 0, tc);
+ check_sockets(pollarray, s, 1, 0, tc);
+ check_sockets(pollarray, s, 2, 1, tc);
+}
+
+static void recv_2_send_1(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int srv = SMALL_NUM_SOCKETS;
+
+ recv_msg(s, 2, p, tc);
+ send_msg(s, sa, 1, tc);
+
+ rv = apr_poll(pollarray, SMALL_NUM_SOCKETS, &srv, 2 * APR_USEC_PER_SEC);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ check_sockets(pollarray, s, 0, 0, tc);
+ check_sockets(pollarray, s, 1, 1, tc);
+ check_sockets(pollarray, s, 2, 0, tc);
+}
+
+static void send_2_signaled_1(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int srv = SMALL_NUM_SOCKETS;
+
+ send_msg(s, sa, 2, tc);
+
+ rv = apr_poll(pollarray, SMALL_NUM_SOCKETS, &srv, 2 * APR_USEC_PER_SEC);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ check_sockets(pollarray, s, 0, 0, tc);
+ check_sockets(pollarray, s, 1, 1, tc);
+ check_sockets(pollarray, s, 2, 1, tc);
+}
+
+static void recv_1_send_0(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int srv = SMALL_NUM_SOCKETS;
+
+ recv_msg(s, 1, p, tc);
+ send_msg(s, sa, 0, tc);
+
+ rv = apr_poll(pollarray, SMALL_NUM_SOCKETS, &srv, 2 * APR_USEC_PER_SEC);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ check_sockets(pollarray, s, 0, 1, tc);
+ check_sockets(pollarray, s, 1, 0, tc);
+ check_sockets(pollarray, s, 2, 1, tc);
+}
+
+static void clear_all_signalled(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int srv = SMALL_NUM_SOCKETS;
+
+ recv_msg(s, 0, p, tc);
+ recv_msg(s, 2, p, tc);
+
+ rv = apr_poll(pollarray, SMALL_NUM_SOCKETS, &srv, 2 * APR_USEC_PER_SEC);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ check_sockets(pollarray, s, 0, 0, tc);
+ check_sockets(pollarray, s, 1, 0, tc);
+ check_sockets(pollarray, s, 2, 0, tc);
+}
+
+static void send_large_pollarray(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int lrv = LARGE_NUM_SOCKETS;
+ int i;
+
+ send_msg(s, sa, LARGE_NUM_SOCKETS - 1, tc);
+
+ rv = apr_poll(pollarray_large, LARGE_NUM_SOCKETS, &lrv,
+ 2 * APR_USEC_PER_SEC);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ for (i = 0; i < LARGE_NUM_SOCKETS; i++) {
+ if (i == (LARGE_NUM_SOCKETS - 1)) {
+ check_sockets(pollarray_large, s, i, 1, tc);
+ }
+ else {
+ check_sockets(pollarray_large, s, i, 0, tc);
+ }
+ }
+}
+
+static void recv_large_pollarray(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int lrv = LARGE_NUM_SOCKETS;
+ int i;
+
+ recv_msg(s, LARGE_NUM_SOCKETS - 1, p, tc);
+
+ rv = apr_poll(pollarray_large, LARGE_NUM_SOCKETS, &lrv,
+ 2 * APR_USEC_PER_SEC);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+
+ for (i = 0; i < LARGE_NUM_SOCKETS; i++) {
+ check_sockets(pollarray_large, s, i, 0, tc);
+ }
+}
+#endif
+
+static void setup_pollset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ rv = apr_pollset_create_ex(&pollset, LARGE_NUM_SOCKETS, p, 0,
+ default_pollset_impl);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void multi_event_pollset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_pollfd_t socket_pollfd;
+ int lrv;
+ const apr_pollfd_t *descs = NULL;
+
+ ABTS_PTR_NOTNULL(tc, s[0]);
+ socket_pollfd.desc_type = APR_POLL_SOCKET;
+ socket_pollfd.reqevents = APR_POLLIN | APR_POLLOUT;
+ socket_pollfd.desc.s = s[0];
+ socket_pollfd.client_data = s[0];
+ rv = apr_pollset_add(pollset, &socket_pollfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ send_msg(s, sa, 0, tc);
+
+ rv = apr_pollset_poll(pollset, -1, &lrv, &descs);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ if (lrv == 1) {
+ int ev = descs[0].rtnevents;
+ ABTS_PTR_EQUAL(tc, s[0], descs[0].desc.s);
+ ABTS_PTR_EQUAL(tc, s[0], descs[0].client_data);
+ ABTS_ASSERT(tc, "either or both of APR_POLLIN, APR_POLLOUT returned",
+ ((ev & APR_POLLIN) != 0) || ((ev & APR_POLLOUT) != 0));
+ }
+ else if (lrv == 2) {
+ ABTS_PTR_EQUAL(tc, s[0], descs[0].desc.s);
+ ABTS_PTR_EQUAL(tc, s[0], descs[0].client_data);
+ ABTS_PTR_EQUAL(tc, s[0], descs[1].desc.s);
+ ABTS_PTR_EQUAL(tc, s[0], descs[1].client_data);
+ ABTS_ASSERT(tc, "returned events incorrect",
+ ((descs[0].rtnevents | descs[1].rtnevents)
+ == (APR_POLLIN | APR_POLLOUT))
+ && descs[0].rtnevents != descs[1].rtnevents);
+ }
+ else {
+ ABTS_ASSERT(tc, "either one or two events returned",
+ lrv == 1 || lrv == 2);
+ }
+
+ recv_msg(s, 0, p, tc);
+
+ rv = apr_pollset_poll(pollset, 0, &lrv, &descs);
+ ABTS_INT_EQUAL(tc, 0, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_INT_EQUAL(tc, 1, lrv);
+ ABTS_PTR_EQUAL(tc, s[0], descs[0].desc.s);
+ ABTS_INT_EQUAL(tc, APR_POLLOUT, descs[0].rtnevents);
+ ABTS_PTR_EQUAL(tc, s[0], descs[0].client_data);
+
+ rv = apr_pollset_remove(pollset, &socket_pollfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void add_sockets_pollset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int i;
+
+ for (i = 0; i < LARGE_NUM_SOCKETS;i++){
+ apr_pollfd_t socket_pollfd;
+
+ ABTS_PTR_NOTNULL(tc, s[i]);
+
+ socket_pollfd.desc_type = APR_POLL_SOCKET;
+ socket_pollfd.reqevents = APR_POLLIN;
+ socket_pollfd.desc.s = s[i];
+ socket_pollfd.client_data = s[i];
+ rv = apr_pollset_add(pollset, &socket_pollfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ }
+}
+
+static void nomessage_pollset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int lrv;
+ const apr_pollfd_t *descs = NULL;
+
+ rv = apr_pollset_poll(pollset, 0, &lrv, &descs);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_INT_EQUAL(tc, 0, lrv);
+ ABTS_PTR_EQUAL(tc, NULL, descs);
+}
+
+static void send0_pollset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ const apr_pollfd_t *descs = NULL;
+ int num;
+
+ send_msg(s, sa, 0, tc);
+ rv = apr_pollset_poll(pollset, -1, &num, &descs);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 1, num);
+ ABTS_PTR_NOTNULL(tc, descs);
+
+ ABTS_PTR_EQUAL(tc, s[0], descs[0].desc.s);
+ ABTS_PTR_EQUAL(tc, s[0], descs[0].client_data);
+}
+
+static void recv0_pollset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int lrv;
+ const apr_pollfd_t *descs = NULL;
+
+ recv_msg(s, 0, p, tc);
+ rv = apr_pollset_poll(pollset, 0, &lrv, &descs);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_INT_EQUAL(tc, 0, lrv);
+ ABTS_PTR_EQUAL(tc, NULL, descs);
+}
+
+static void send_middle_pollset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ const apr_pollfd_t *descs = NULL;
+ int num;
+
+ send_msg(s, sa, 2, tc);
+ send_msg(s, sa, 5, tc);
+ rv = apr_pollset_poll(pollset, -1, &num, &descs);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, descs);
+ ABTS_ASSERT(tc, "either one or two events returned",
+ num == 1 || num == 2);
+
+ /* The poll might only see the first sent message, in which
+ * case we just don't bother checking this assertion */
+ if (num == 2) {
+ ABTS_ASSERT(tc, "Incorrect socket in result set",
+ ((descs[0].desc.s == s[2]) && (descs[1].desc.s == s[5])) ||
+ ((descs[0].desc.s == s[5]) && (descs[1].desc.s == s[2])));
+ }
+}
+
+static void clear_middle_pollset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int lrv;
+ const apr_pollfd_t *descs = NULL;
+
+ recv_msg(s, 2, p, tc);
+ recv_msg(s, 5, p, tc);
+
+ rv = apr_pollset_poll(pollset, 0, &lrv, &descs);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_INT_EQUAL(tc, 0, lrv);
+ ABTS_PTR_EQUAL(tc, NULL, descs);
+}
+
+static void send_last_pollset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ const apr_pollfd_t *descs = NULL;
+ int num;
+
+ send_msg(s, sa, LARGE_NUM_SOCKETS - 1, tc);
+ rv = apr_pollset_poll(pollset, -1, &num, &descs);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 1, num);
+ ABTS_PTR_NOTNULL(tc, descs);
+
+ ABTS_PTR_EQUAL(tc, s[LARGE_NUM_SOCKETS - 1], descs[0].desc.s);
+ ABTS_PTR_EQUAL(tc, s[LARGE_NUM_SOCKETS - 1], descs[0].client_data);
+}
+
+static void clear_last_pollset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int lrv;
+ const apr_pollfd_t *descs = NULL;
+
+ recv_msg(s, LARGE_NUM_SOCKETS - 1, p, tc);
+
+ rv = apr_pollset_poll(pollset, 0, &lrv, &descs);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_INT_EQUAL(tc, 0, lrv);
+ ABTS_PTR_EQUAL(tc, NULL, descs);
+}
+
+static void close_all_sockets(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int i;
+
+ for (i = 0; i < LARGE_NUM_SOCKETS; i++){
+ rv = apr_socket_close(s[i]);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ }
+}
+
+static void pollset_remove(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_pollset_t *pollset;
+ const apr_pollfd_t *hot_files;
+ apr_pollfd_t pfd;
+ apr_int32_t num;
+
+ rv = apr_pollset_create_ex(&pollset, 5, p, 0,
+ default_pollset_impl);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ pfd.p = p;
+ pfd.desc_type = APR_POLL_SOCKET;
+ pfd.reqevents = APR_POLLOUT;
+
+ pfd.desc.s = s[0];
+ pfd.client_data = (void *)1;
+ rv = apr_pollset_add(pollset, &pfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ pfd.desc.s = s[1];
+ pfd.client_data = (void *)2;
+ rv = apr_pollset_add(pollset, &pfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ pfd.desc.s = s[2];
+ pfd.client_data = (void *)3;
+ rv = apr_pollset_add(pollset, &pfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ pfd.desc.s = s[3];
+ pfd.client_data = (void *)4;
+ rv = apr_pollset_add(pollset, &pfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_pollset_poll(pollset, 1000, &num, &hot_files);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 4, num);
+
+ /* now remove the pollset element referring to desc s[1] */
+ pfd.desc.s = s[1];
+ pfd.client_data = (void *)999; /* not used on this call */
+ rv = apr_pollset_remove(pollset, &pfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* this time only three should match */
+ rv = apr_pollset_poll(pollset, 1000, &num, &hot_files);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 3, num);
+ ABTS_PTR_EQUAL(tc, (void *)1, hot_files[0].client_data);
+ ABTS_PTR_EQUAL(tc, s[0], hot_files[0].desc.s);
+ ABTS_PTR_EQUAL(tc, (void *)3, hot_files[1].client_data);
+ ABTS_PTR_EQUAL(tc, s[2], hot_files[1].desc.s);
+ ABTS_PTR_EQUAL(tc, (void *)4, hot_files[2].client_data);
+ ABTS_PTR_EQUAL(tc, s[3], hot_files[2].desc.s);
+
+ /* now remove the pollset elements referring to desc s[2] */
+ pfd.desc.s = s[2];
+ pfd.client_data = (void *)999; /* not used on this call */
+ rv = apr_pollset_remove(pollset, &pfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* this time only two should match */
+ rv = apr_pollset_poll(pollset, 1000, &num, &hot_files);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 2, num);
+ ABTS_ASSERT(tc, "Incorrect socket in result set",
+ ((hot_files[0].desc.s == s[0]) && (hot_files[1].desc.s == s[3])) ||
+ ((hot_files[0].desc.s == s[3]) && (hot_files[1].desc.s == s[0])));
+ ABTS_ASSERT(tc, "Incorrect client data in result set",
+ ((hot_files[0].client_data == (void *)1) &&
+ (hot_files[1].client_data == (void *)4)) ||
+ ((hot_files[0].client_data == (void *)4) &&
+ (hot_files[1].client_data == (void *)1)));
+}
+
+#define POLLCB_PREREQ \
+ do { \
+ if (pollcb == NULL) { \
+ ABTS_NOT_IMPL(tc, "pollcb interface not supported"); \
+ return; \
+ } \
+ } while (0)
+
+static void setup_pollcb(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ rv = apr_pollcb_create(&pollcb, LARGE_NUM_SOCKETS, p, 0);
+ if (rv == APR_ENOTIMPL) {
+ pollcb = NULL;
+ ABTS_NOT_IMPL(tc, "pollcb interface not supported");
+ }
+ else {
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ }
+}
+
+typedef struct pollcb_baton_t {
+ abts_case *tc;
+ int count;
+} pollcb_baton_t;
+
+static apr_status_t trigger_pollcb_cb(void *baton, apr_pollfd_t *descriptor)
+{
+ pollcb_baton_t *pcb = (pollcb_baton_t *) baton;
+ ABTS_PTR_EQUAL(pcb->tc, s[0], descriptor->desc.s);
+ ABTS_PTR_EQUAL(pcb->tc, s[0], descriptor->client_data);
+ pcb->count++;
+ return APR_SUCCESS;
+}
+
+static void trigger_pollcb(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_pollfd_t socket_pollfd;
+ pollcb_baton_t pcb;
+
+ POLLCB_PREREQ;
+
+ ABTS_PTR_NOTNULL(tc, s[0]);
+ socket_pollfd.desc_type = APR_POLL_SOCKET;
+ socket_pollfd.reqevents = APR_POLLIN;
+ socket_pollfd.desc.s = s[0];
+ socket_pollfd.client_data = s[0];
+ rv = apr_pollcb_add(pollcb, &socket_pollfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ send_msg(s, sa, 0, tc);
+ pcb.tc = tc;
+ pcb.count = 0;
+ rv = apr_pollcb_poll(pollcb, -1, trigger_pollcb_cb, &pcb);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 1, pcb.count);
+
+ rv = apr_pollcb_remove(pollcb, &socket_pollfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void timeout_pollcb(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ pollcb_baton_t pcb;
+
+ POLLCB_PREREQ;
+
+ pcb.count = 0;
+ pcb.tc = tc;
+
+ rv = apr_pollcb_poll(pollcb, 1, trigger_pollcb_cb, &pcb);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_INT_EQUAL(tc, 0, pcb.count);
+}
+
+static void timeout_pollin_pollcb(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ pollcb_baton_t pcb;
+ apr_pollfd_t socket_pollfd;
+
+ POLLCB_PREREQ;
+
+ recv_msg(s, 0, p, tc);
+
+ ABTS_PTR_NOTNULL(tc, s[0]);
+ socket_pollfd.desc_type = APR_POLL_SOCKET;
+ socket_pollfd.reqevents = APR_POLLIN;
+ socket_pollfd.desc.s = s[0];
+ socket_pollfd.client_data = s[0];
+ rv = apr_pollcb_add(pollcb, &socket_pollfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ pcb.count = 0;
+ pcb.tc = tc;
+
+ rv = apr_pollcb_poll(pollcb, 1, trigger_pollcb_cb, &pcb);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_INT_EQUAL(tc, 0, pcb.count);
+
+ rv = apr_pollcb_remove(pollcb, &socket_pollfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void pollset_default(abts_case *tc, void *data)
+{
+ apr_status_t rv1, rv2;
+ apr_pollset_t *pollset;
+
+ /* verify that APR will successfully create a pollset if an invalid method
+ * is specified as long as APR_POLLSET_NODEFAULT isn't specified
+ * (no platform has both APR_POLLSET_PORT and APR_POLLSET_KQUEUE, so at
+ * least one create call will succeed after having to switch to the default
+ * type)
+ */
+ rv1 = apr_pollset_create_ex(&pollset, 1, p, 0, APR_POLLSET_PORT);
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv1);
+ ABTS_PTR_NOTNULL(tc, pollset);
+
+ rv1 = apr_pollset_create_ex(&pollset, 1, p, 0, APR_POLLSET_KQUEUE);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv1);
+ ABTS_PTR_NOTNULL(tc, pollset);
+
+ /* verify that APR will fail to create a pollset if an invalid method is
+ * specified along with APR_POLLSET_NODEFAULT
+ * (no platform has both APR_POLLSET_PORT and APR_POLLSET_KQUEUE, so at
+ * least one create call will fail since it can't switch to the default
+ * type)
+ */
+ rv1 = apr_pollset_create_ex(&pollset, 1, p, APR_POLLSET_NODEFAULT,
+ APR_POLLSET_PORT);
+
+ if (rv1 == APR_SUCCESS) {
+ ABTS_PTR_NOTNULL(tc, pollset);
+ }
+
+ rv2 = apr_pollset_create_ex(&pollset, 1, p, APR_POLLSET_NODEFAULT,
+ APR_POLLSET_KQUEUE);
+ if (rv2 == APR_SUCCESS) {
+ ABTS_PTR_NOTNULL(tc, pollset);
+ }
+
+ ABTS_ASSERT(tc,
+ "failure using APR_POLLSET_NODEFAULT with unsupported method",
+ rv1 != APR_SUCCESS || rv2 != APR_SUCCESS);
+}
+
+static void pollcb_default(abts_case *tc, void *data)
+{
+ apr_status_t rv1, rv2;
+ apr_pollcb_t *pollcb;
+
+ /* verify that APR will successfully create a pollcb if an invalid method
+ * is specified as long as APR_POLLSET_NODEFAULT isn't specified
+ * (no platform has both APR_POLLSET_PORT and APR_POLLSET_KQUEUE, so at
+ * least one create call will succeed after having to switch to the default
+ * type)
+ */
+ rv1 = apr_pollcb_create_ex(&pollcb, 1, p, 0, APR_POLLSET_PORT);
+ if (rv1 == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "pollcb interface not supported");
+ return;
+ }
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv1);
+ ABTS_PTR_NOTNULL(tc, pollcb);
+
+ rv1 = apr_pollcb_create_ex(&pollcb, 1, p, 0, APR_POLLSET_KQUEUE);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv1);
+ ABTS_PTR_NOTNULL(tc, pollcb);
+
+ /* verify that APR will fail to create a pollcb if an invalid method is
+ * specified along with APR_POLLSET_NODEFAULT
+ * (no platform has both APR_POLLSET_PORT and APR_POLLSET_KQUEUE, so at
+ * least one create call will fail since it can't switch to the default
+ * type)
+ */
+ rv1 = apr_pollcb_create_ex(&pollcb, 1, p, APR_POLLSET_NODEFAULT,
+ APR_POLLSET_PORT);
+
+ if (rv1 == APR_SUCCESS) {
+ ABTS_PTR_NOTNULL(tc, pollcb);
+ }
+
+ rv2 = apr_pollcb_create_ex(&pollcb, 1, p, APR_POLLSET_NODEFAULT,
+ APR_POLLSET_KQUEUE);
+ if (rv2 == APR_SUCCESS) {
+ ABTS_PTR_NOTNULL(tc, pollcb);
+ }
+
+ ABTS_ASSERT(tc,
+ "failure using APR_POLLSET_NODEFAULT with unsupported method",
+ rv1 != APR_SUCCESS || rv2 != APR_SUCCESS);
+
+
+ /* verify basic behavior for another method fallback case (this caused
+ * APR to crash before r834029)
+ */
+
+ rv1 = apr_pollcb_create_ex(&pollcb, 1, p, 0, APR_POLLSET_POLL);
+ if (rv1 != APR_ENOTIMPL) {
+ ABTS_INT_EQUAL(tc, rv1, APR_SUCCESS);
+ ABTS_PTR_NOTNULL(tc, pollcb);
+ }
+}
+
+static void pollset_wakeup(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_pollfd_t socket_pollfd;
+ apr_pollset_t *pollset;
+ apr_int32_t num;
+ const apr_pollfd_t *descriptors;
+ int i;
+
+ rv = apr_pollset_create_ex(&pollset, 1, p, APR_POLLSET_WAKEABLE,
+ default_pollset_impl);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_pollset_wakeup() not supported");
+ return;
+ }
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ /* Send wakeup but no data; apr_pollset_poll() should return APR_EINTR.
+ * Do it twice to test implementations that need to re-arm the events after
+ * poll()ing (e.g. APR_POLLSET_PORT), hence verify that the wakeup pipe is
+ * still in the place afterward.
+ */
+ for (i = 0; i < 2; ++i) {
+ rv = apr_pollset_wakeup(pollset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_pollset_poll(pollset, -1, &num, &descriptors);
+ ABTS_INT_EQUAL(tc, APR_EINTR, rv);
+ }
+
+ /* send wakeup and data; apr_pollset_poll() should return APR_SUCCESS */
+ socket_pollfd.desc_type = APR_POLL_SOCKET;
+ socket_pollfd.reqevents = APR_POLLIN;
+ socket_pollfd.desc.s = s[0];
+ socket_pollfd.client_data = s[0];
+ rv = apr_pollset_add(pollset, &socket_pollfd);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ send_msg(s, sa, 0, tc); apr_sleep(1000);
+
+ rv = apr_pollset_wakeup(pollset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_pollset_poll(pollset, -1, &num, &descriptors);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 1, num);
+}
+
+/* Should never be invoked */
+static apr_status_t wakeup_pollcb_cb(void *baton, apr_pollfd_t *descriptor)
+{
+ abts_case *tc = (abts_case *) baton;
+
+ ABTS_FAIL(tc, "pollcb callback invoked on apr_pollcb_wakeup()");
+ return APR_SUCCESS;
+}
+
+static void pollcb_wakeup(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_pollcb_t *pcb;
+
+ rv = apr_pollcb_create(&pcb, 1, p, APR_POLLSET_WAKEABLE);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "pollcb interface not supported");
+ return;
+ }
+ else {
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ }
+
+ rv = apr_pollcb_wakeup(pcb);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_pollcb_poll(pcb, -1, wakeup_pollcb_cb, tc);
+ ABTS_INT_EQUAL(tc, APR_EINTR, rv);
+}
+
+static void justsleep(abts_case *tc, void *data)
+{
+ apr_int32_t nsds;
+ const apr_pollfd_t *hot_files;
+ apr_pollset_t *pollset;
+ apr_status_t rv;
+ apr_time_t t1, t2;
+ int i;
+ apr_pollset_method_e methods[] = {
+ APR_POLLSET_DEFAULT,
+ APR_POLLSET_SELECT,
+ APR_POLLSET_KQUEUE,
+ APR_POLLSET_PORT,
+ APR_POLLSET_EPOLL,
+ APR_POLLSET_POLL};
+
+ nsds = 1;
+ t1 = apr_time_now();
+ rv = apr_poll(NULL, 0, &nsds, apr_time_from_msec(200));
+ t2 = apr_time_now();
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_INT_EQUAL(tc, 0, nsds);
+ ABTS_ASSERT(tc,
+ "apr_poll() didn't sleep",
+ (t2 - t1) > apr_time_from_msec(100));
+
+ for (i = 0; i < sizeof methods / sizeof methods[0]; i++) {
+ rv = apr_pollset_create_ex(&pollset, 5, p, 0, methods[i]);
+ if (rv != APR_ENOTIMPL) {
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ nsds = 1;
+ t1 = apr_time_now();
+ rv = apr_pollset_poll(pollset, apr_time_from_msec(200), &nsds,
+ &hot_files);
+ t2 = apr_time_now();
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_INT_EQUAL(tc, 0, nsds);
+ ABTS_ASSERT(tc,
+ "apr_pollset_poll() didn't sleep",
+ (t2 - t1) > apr_time_from_msec(100));
+
+ rv = apr_pollset_destroy(pollset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ }
+
+ rv = apr_pollcb_create_ex(&pollcb, 5, p, 0, methods[0]);
+ if (rv != APR_ENOTIMPL) {
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ t1 = apr_time_now();
+ rv = apr_pollcb_poll(pollcb, apr_time_from_msec(200), NULL, NULL);
+ t2 = apr_time_now();
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_TIMEUP(rv));
+ ABTS_ASSERT(tc,
+ "apr_pollcb_poll() didn't sleep",
+ (t2 - t1) > apr_time_from_msec(100));
+
+ /* no apr_pollcb_destroy() */
+ }
+ }
+}
+
+abts_suite *testpoll(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, create_all_sockets, NULL);
+
+#ifdef OLD_POLL_INTERFACE
+ abts_run_test(suite, setup_small_poll, NULL);
+ abts_run_test(suite, setup_large_poll, NULL);
+ abts_run_test(suite, nomessage, NULL);
+ abts_run_test(suite, send_2, NULL);
+ abts_run_test(suite, recv_2_send_1, NULL);
+ abts_run_test(suite, send_2_signaled_1, NULL);
+ abts_run_test(suite, recv_1_send_0, NULL);
+ abts_run_test(suite, clear_all_signalled, NULL);
+ abts_run_test(suite, send_large_pollarray, NULL);
+ abts_run_test(suite, recv_large_pollarray, NULL);
+#endif
+
+ abts_run_test(suite, setup_pollset, NULL);
+ abts_run_test(suite, multi_event_pollset, NULL);
+ abts_run_test(suite, add_sockets_pollset, NULL);
+ abts_run_test(suite, nomessage_pollset, NULL);
+ abts_run_test(suite, send0_pollset, NULL);
+ abts_run_test(suite, recv0_pollset, NULL);
+ abts_run_test(suite, send_middle_pollset, NULL);
+ abts_run_test(suite, clear_middle_pollset, NULL);
+ abts_run_test(suite, send_last_pollset, NULL);
+ abts_run_test(suite, clear_last_pollset, NULL);
+ abts_run_test(suite, pollset_remove, NULL);
+ abts_run_test(suite, close_all_sockets, NULL);
+ abts_run_test(suite, create_all_sockets, NULL);
+ abts_run_test(suite, setup_pollcb, NULL);
+ abts_run_test(suite, trigger_pollcb, NULL);
+ abts_run_test(suite, timeout_pollcb, NULL);
+ abts_run_test(suite, timeout_pollin_pollcb, NULL);
+ abts_run_test(suite, pollset_wakeup, NULL);
+ abts_run_test(suite, pollcb_wakeup, NULL);
+ abts_run_test(suite, close_all_sockets, NULL);
+ abts_run_test(suite, pollset_default, NULL);
+ abts_run_test(suite, pollcb_default, NULL);
+ abts_run_test(suite, justsleep, NULL);
+
+ return suite;
+}
+
diff --git a/test/testpools.c b/test/testpools.c
new file mode 100644
index 0000000..dd0919d
--- /dev/null
+++ b/test/testpools.c
@@ -0,0 +1,156 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_errno.h"
+#include "apr_file_io.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "testutil.h"
+
+#define ALLOC_BYTES 1024
+
+static apr_pool_t *pmain = NULL;
+static apr_pool_t *pchild = NULL;
+
+static void alloc_bytes(abts_case *tc, void *data)
+{
+ int i;
+ char *alloc;
+
+ alloc = apr_palloc(pmain, ALLOC_BYTES);
+ ABTS_PTR_NOTNULL(tc, alloc);
+
+ for (i=0;i<ALLOC_BYTES;i++) {
+ char *ptr = alloc + i;
+ *ptr = 0xa;
+ }
+ /* This is just added to get the positive. If this test fails, the
+ * suite will seg fault.
+ */
+ ABTS_TRUE(tc, 1);
+}
+
+static void calloc_bytes(abts_case *tc, void *data)
+{
+ int i;
+ char *alloc;
+
+ alloc = apr_pcalloc(pmain, ALLOC_BYTES);
+ ABTS_PTR_NOTNULL(tc, alloc);
+
+ for (i=0;i<ALLOC_BYTES;i++) {
+ char *ptr = alloc + i;
+ ABTS_TRUE(tc, *ptr == '\0');
+ }
+}
+
+static void parent_pool(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_pool_create(&pmain, NULL);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, pmain);
+}
+
+static void child_pool(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_pool_create(&pchild, pmain);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, pchild);
+}
+
+static void test_ancestor(abts_case *tc, void *data)
+{
+ ABTS_INT_EQUAL(tc, 1, apr_pool_is_ancestor(pmain, pchild));
+}
+
+static void test_notancestor(abts_case *tc, void *data)
+{
+ ABTS_INT_EQUAL(tc, 0, apr_pool_is_ancestor(pchild, pmain));
+}
+
+static apr_status_t success_cleanup(void *data)
+{
+ return APR_SUCCESS;
+}
+
+static char *checker_data = "Hello, world.";
+
+static apr_status_t checker_cleanup(void *data)
+{
+ return data == checker_data ? APR_SUCCESS : APR_EGENERAL;
+}
+
+static void test_cleanups(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ int n;
+
+ /* do this several times to test the cleanup freelist handling. */
+ for (n = 0; n < 5; n++) {
+ apr_pool_cleanup_register(pchild, NULL, success_cleanup,
+ success_cleanup);
+ apr_pool_cleanup_register(pchild, checker_data, checker_cleanup,
+ success_cleanup);
+ apr_pool_cleanup_register(pchild, NULL, checker_cleanup,
+ success_cleanup);
+
+ rv = apr_pool_cleanup_run(p, NULL, success_cleanup);
+ ABTS_ASSERT(tc, "nullop cleanup run OK", rv == APR_SUCCESS);
+ rv = apr_pool_cleanup_run(p, checker_data, checker_cleanup);
+ ABTS_ASSERT(tc, "cleanup passed correct data", rv == APR_SUCCESS);
+ rv = apr_pool_cleanup_run(p, NULL, checker_cleanup);
+ ABTS_ASSERT(tc, "cleanup passed correct data", rv == APR_EGENERAL);
+
+ if (n == 2) {
+ /* clear the pool to check that works */
+ apr_pool_clear(pchild);
+ }
+
+ if (n % 2 == 0) {
+ /* throw another random cleanup into the mix */
+ apr_pool_cleanup_register(pchild, NULL,
+ apr_pool_cleanup_null,
+ apr_pool_cleanup_null);
+ }
+ }
+}
+
+abts_suite *testpool(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, parent_pool, NULL);
+ abts_run_test(suite, child_pool, NULL);
+ abts_run_test(suite, test_ancestor, NULL);
+ abts_run_test(suite, test_notancestor, NULL);
+ abts_run_test(suite, alloc_bytes, NULL);
+ abts_run_test(suite, calloc_bytes, NULL);
+ abts_run_test(suite, test_cleanups, NULL);
+
+ return suite;
+}
+
diff --git a/test/testproc.c b/test/testproc.c
new file mode 100644
index 0000000..096ecfd
--- /dev/null
+++ b/test/testproc.c
@@ -0,0 +1,174 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_thread_proc.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "testutil.h"
+
+#define TESTSTR "This is a test"
+
+#define PROC_CHILD_NAME TESTBINPATH "proc_child" EXTENSION
+
+static char *proc_child;
+
+static apr_proc_t newproc;
+
+static void test_create_proc(abts_case *tc, void *data)
+{
+ const char *args[2];
+ apr_procattr_t *attr;
+ apr_file_t *testfile = NULL;
+ apr_status_t rv;
+ apr_size_t length;
+ char *buf;
+
+ rv = apr_procattr_create(&attr, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_procattr_io_set(attr, APR_FULL_BLOCK, APR_FULL_BLOCK,
+ APR_NO_PIPE);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_procattr_dir_set(attr, "data");
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_procattr_cmdtype_set(attr, APR_PROGRAM_ENV);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ args[0] = "proc_child" EXTENSION;
+ args[1] = NULL;
+
+ rv = apr_proc_create(&newproc, proc_child, args, NULL,
+ attr, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ testfile = newproc.in;
+
+ length = strlen(TESTSTR);
+ rv = apr_file_write(testfile, TESTSTR, &length);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, strlen(TESTSTR), length);
+
+ testfile = newproc.out;
+ length = 256;
+ buf = apr_pcalloc(p, length);
+ rv = apr_file_read(testfile, buf, &length);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, TESTSTR, buf);
+}
+
+static void test_proc_wait(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_proc_wait(&newproc, NULL, NULL, APR_WAIT);
+ ABTS_INT_EQUAL(tc, APR_CHILD_DONE, rv);
+}
+
+static void test_file_redir(abts_case *tc, void *data)
+{
+ apr_file_t *testout = NULL;
+ apr_file_t *testerr = NULL;
+ apr_off_t offset;
+ apr_status_t rv;
+ const char *args[2];
+ apr_procattr_t *attr;
+ apr_file_t *testfile = NULL;
+ apr_size_t length;
+ char *buf;
+
+ testfile = NULL;
+ rv = apr_file_open(&testfile, "data/stdin",
+ APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_EXCL,
+ APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_open(&testout, "data/stdout",
+ APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_EXCL,
+ APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_open(&testerr, "data/stderr",
+ APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_EXCL,
+ APR_OS_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ length = strlen(TESTSTR);
+ apr_file_write(testfile, TESTSTR, &length);
+ offset = 0;
+ rv = apr_file_seek(testfile, APR_SET, &offset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_ASSERT(tc, "File position mismatch, expected 0", offset == 0);
+
+ rv = apr_procattr_create(&attr, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_procattr_child_in_set(attr, testfile, NULL);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_procattr_child_out_set(attr, testout, NULL);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_procattr_child_err_set(attr, testerr, NULL);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_procattr_dir_set(attr, "data");
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_procattr_cmdtype_set(attr, APR_PROGRAM_ENV);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ args[0] = "proc_child";
+ args[1] = NULL;
+
+ rv = apr_proc_create(&newproc, proc_child, args, NULL,
+ attr, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_proc_wait(&newproc, NULL, NULL, APR_WAIT);
+ ABTS_INT_EQUAL(tc, APR_CHILD_DONE, rv);
+
+ offset = 0;
+ rv = apr_file_seek(testout, APR_SET, &offset);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ length = 256;
+ buf = apr_pcalloc(p, length);
+ rv = apr_file_read(testout, buf, &length);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, TESTSTR, buf);
+
+
+ apr_file_close(testfile);
+ apr_file_close(testout);
+ apr_file_close(testerr);
+
+ rv = apr_file_remove("data/stdin", p);;
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_remove("data/stdout", p);;
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_file_remove("data/stderr", p);;
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+abts_suite *testproc(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ apr_filepath_merge(&proc_child, NULL, PROC_CHILD_NAME, 0, p);
+ abts_run_test(suite, test_create_proc, NULL);
+ abts_run_test(suite, test_proc_wait, NULL);
+ abts_run_test(suite, test_file_redir, NULL);
+
+ return suite;
+}
+
diff --git a/test/testprocmutex.c b/test/testprocmutex.c
new file mode 100644
index 0000000..3edb2a9
--- /dev/null
+++ b/test/testprocmutex.c
@@ -0,0 +1,294 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_shm.h"
+#include "apr_thread_proc.h"
+#include "apr_file_io.h"
+#include "apr_proc_mutex.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_getopt.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "testutil.h"
+
+#if APR_HAS_FORK
+
+#define MAX_ITER 200
+#define CHILDREN 6
+#define MAX_COUNTER (MAX_ITER * CHILDREN)
+#define MAX_WAIT_USEC (1000*1000)
+
+static apr_proc_mutex_t *proc_lock;
+static volatile int *x;
+
+typedef struct lockmech {
+ apr_lockmech_e num;
+ const char *name;
+} lockmech_t;
+
+/* a slower more racy way to implement (*x)++ */
+static int increment(int n)
+{
+ apr_sleep(1);
+ return n+1;
+}
+
+static void make_child(abts_case *tc, int trylock, apr_proc_t **proc, apr_pool_t *p)
+{
+ apr_status_t rv;
+
+ *proc = apr_pcalloc(p, sizeof(**proc));
+
+ /* slight delay to allow things to settle */
+ apr_sleep (1);
+
+ rv = apr_proc_fork(*proc, p);
+ if (rv == APR_INCHILD) {
+ int i = 0;
+ /* The parent process has setup all processes to call apr_terminate
+ * at exit. But, that means that all processes must also call
+ * apr_initialize at startup. You cannot have an unequal number
+ * of apr_terminate and apr_initialize calls. If you do, bad things
+ * will happen. In this case, the bad thing is that if the mutex
+ * is a semaphore, it will be destroyed before all of the processes
+ * die. That means that the test will most likely fail.
+ */
+ apr_initialize();
+
+ if (apr_proc_mutex_child_init(&proc_lock, NULL, p))
+ exit(1);
+
+ do {
+ if (trylock > 0) {
+ int wait_usec = 0;
+
+ while ((rv = apr_proc_mutex_trylock(proc_lock))) {
+ if (!APR_STATUS_IS_EBUSY(rv))
+ exit(1);
+ if (++wait_usec >= MAX_WAIT_USEC)
+ exit(1);
+ apr_sleep(1);
+ }
+ }
+ else if (trylock < 0) {
+ int wait_usec = 0;
+
+ while ((rv = apr_proc_mutex_timedlock(proc_lock, 1))) {
+ if (!APR_STATUS_IS_TIMEUP(rv))
+ exit(1);
+ if (++wait_usec >= MAX_WAIT_USEC)
+ exit(1);
+ }
+ }
+ else {
+ if (apr_proc_mutex_lock(proc_lock))
+ exit(1);
+ }
+
+ i++;
+ *x = increment(*x);
+ if (apr_proc_mutex_unlock(proc_lock))
+ exit(1);
+ } while (i < MAX_ITER);
+ exit(0);
+ }
+
+ ABTS_ASSERT(tc, "fork failed", rv == APR_INPARENT);
+}
+
+/* Wait for a child process and check it terminated with success. */
+static void await_child(abts_case *tc, apr_proc_t *proc)
+{
+ int code;
+ apr_exit_why_e why;
+ apr_status_t rv;
+
+ rv = apr_proc_wait(proc, &code, &why, APR_WAIT);
+ ABTS_ASSERT(tc, "child did not terminate with success",
+ rv == APR_CHILD_DONE && why == APR_PROC_EXIT && code == 0);
+}
+
+static void test_exclusive(abts_case *tc, const char *lockname,
+ lockmech_t *mech)
+{
+ apr_proc_t *child[CHILDREN];
+ apr_status_t rv;
+ int n;
+
+ rv = apr_proc_mutex_create(&proc_lock, lockname, mech->num, p);
+ if (rv == APR_ENOTIMPL) {
+ /* MacOS lacks TIMED implementation, so don't fail for ENOTIMPL */
+ fprintf(stderr, "method %s not implemented, ", mech->name);
+ return;
+ }
+ APR_ASSERT_SUCCESS(tc, "create the mutex", rv);
+
+ for (n = 0; n < CHILDREN; n++)
+ make_child(tc, 0, &child[n], p);
+
+ for (n = 0; n < CHILDREN; n++)
+ await_child(tc, child[n]);
+
+ ABTS_ASSERT(tc, "Locks don't appear to work", *x == MAX_COUNTER);
+
+ rv = apr_proc_mutex_trylock(proc_lock);
+ if (rv == APR_ENOTIMPL) {
+ fprintf(stderr, "%s_trylock() not implemented, ", mech->name);
+ ABTS_ASSERT(tc, "Default timed trylock not implemented",
+ mech->num != APR_LOCK_DEFAULT &&
+ mech->num != APR_LOCK_DEFAULT_TIMED);
+ }
+ else {
+ APR_ASSERT_SUCCESS(tc, "check for trylock", rv);
+
+ for (n = 0; n < 2; n++) {
+ rv = apr_proc_mutex_trylock(proc_lock);
+ /* Some mech (eg. flock or fcntl) may succeed when the
+ * lock is re-acquired in the same process.
+ */
+ if (rv != APR_SUCCESS) {
+ ABTS_ASSERT(tc,
+ apr_psprintf(p, "%s_trylock() should be busy => %pm",
+ mech->name, &rv),
+ APR_STATUS_IS_EBUSY(rv));
+ }
+ }
+
+ rv = apr_proc_mutex_unlock(proc_lock);
+ APR_ASSERT_SUCCESS(tc, "unlock after trylock check", rv);
+
+ *x = 0;
+
+ for (n = 0; n < CHILDREN; n++)
+ make_child(tc, 1, &child[n], p);
+
+ for (n = 0; n < CHILDREN; n++)
+ await_child(tc, child[n]);
+
+ ABTS_ASSERT(tc, "Locks don't appear to work with trylock",
+ *x == MAX_COUNTER);
+ }
+
+#if APR_HAS_TIMEDLOCKS
+ rv = apr_proc_mutex_timedlock(proc_lock, 1);
+ if (rv == APR_ENOTIMPL) {
+ fprintf(stderr, "%s_timedlock() not implemented, ", mech->name);
+ ABTS_ASSERT(tc, "Default timed timedlock not implemented",
+ mech->num != APR_LOCK_DEFAULT_TIMED);
+ }
+ else {
+ APR_ASSERT_SUCCESS(tc, "check for timedlock", rv);
+
+ for (n = 0; n < 2; n++) {
+ rv = apr_proc_mutex_timedlock(proc_lock, 1);
+ /* Some mech (eg. flock or fcntl) may succeed when the
+ * lock is re-acquired in the same process.
+ */
+ if (rv != APR_SUCCESS) {
+ ABTS_ASSERT(tc,
+ apr_psprintf(p, "%s_timedlock() should time out => %pm",
+ mech->name, &rv),
+ APR_STATUS_IS_TIMEUP(rv));
+ }
+ }
+
+ rv = apr_proc_mutex_unlock(proc_lock);
+ APR_ASSERT_SUCCESS(tc, "unlock after timedlock check", rv);
+
+ *x = 0;
+
+ for (n = 0; n < CHILDREN; n++)
+ make_child(tc, -1, &child[n], p);
+
+ for (n = 0; n < CHILDREN; n++)
+ await_child(tc, child[n]);
+
+ ABTS_ASSERT(tc, "Locks don't appear to work with timedlock",
+ *x == MAX_COUNTER);
+ }
+#endif /* APR_HAS_TIMEDLOCKS */
+}
+
+static void proc_mutex(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ const char *shmname = "tpm.shm";
+ apr_shm_t *shm;
+
+ /* Use anonymous shm if available. */
+ rv = apr_shm_create(&shm, sizeof(int), NULL, p);
+ if (rv == APR_ENOTIMPL) {
+ apr_file_remove(shmname, p);
+ rv = apr_shm_create(&shm, sizeof(int), shmname, p);
+ }
+
+ APR_ASSERT_SUCCESS(tc, "create shm segment", rv);
+ if (rv != APR_SUCCESS)
+ return;
+
+ x = apr_shm_baseaddr_get(shm);
+ test_exclusive(tc, NULL, data);
+ rv = apr_shm_destroy(shm);
+ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv);
+}
+
+
+abts_suite *testprocmutex(abts_suite *suite)
+{
+ lockmech_t lockmechs[] = {
+ {APR_LOCK_DEFAULT, "default"}
+#if APR_HAS_FLOCK_SERIALIZE
+ ,{APR_LOCK_FLOCK, "flock"}
+#endif
+#if APR_HAS_SYSVSEM_SERIALIZE
+ ,{APR_LOCK_SYSVSEM, "sysvsem"}
+#endif
+#if APR_HAS_POSIXSEM_SERIALIZE
+ ,{APR_LOCK_POSIXSEM, "posix"}
+#endif
+#if APR_HAS_FCNTL_SERIALIZE
+ ,{APR_LOCK_FCNTL, "fcntl"}
+#endif
+#if APR_HAS_PROC_PTHREAD_SERIALIZE
+ ,{APR_LOCK_PROC_PTHREAD, "proc_pthread"}
+#endif
+ ,{APR_LOCK_DEFAULT_TIMED, "default_timed"}
+ };
+ int i;
+
+ suite = ADD_SUITE(suite)
+ for (i = 0; i < sizeof(lockmechs) / sizeof(lockmechs[0]); i++) {
+ abts_run_test(suite, proc_mutex, &lockmechs[i]);
+ }
+ return suite;
+}
+
+#else /* APR_HAS_FORK */
+
+static void proc_mutex(abts_case *tc, void *data)
+{
+ ABTS_NOT_IMPL(tc, "APR lacks fork() support");
+}
+
+abts_suite *testprocmutex(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite);
+ abts_run_test(suite, proc_mutex, NULL);
+ return suite;
+}
+#endif /* APR_HAS_FORK */
diff --git a/test/testrand.c b/test/testrand.c
new file mode 100644
index 0000000..46c55d2
--- /dev/null
+++ b/test/testrand.c
@@ -0,0 +1,359 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_random.h"
+#include "apr_thread_proc.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "testutil.h"
+
+#define RANDOM_BUF_SZ 128
+
+static void hexdump(const char *msg, const unsigned char *b, int n)
+{
+ int i;
+
+ printf("\n%s", msg);
+ for (i = 0; i < n; ++i) {
+#if 0
+ if ((i & 0xf) == 0)
+ printf("%04x", i);
+ printf(" %02x", b[i]);
+ if ((i & 0xf) == 0xf)
+ printf("\n");
+#else
+ printf("0x%02x,", b[i]);
+ if ((i & 7) == 7)
+ printf("\n");
+#endif
+ }
+ printf("\n");
+}
+
+static apr_random_t *r;
+
+typedef apr_status_t APR_THREAD_FUNC rnd_fn(apr_random_t * r, void *b,
+ apr_size_t n);
+
+static void rand_run_kat(abts_case *tc, rnd_fn *f, apr_random_t *r,
+ const unsigned char expected[RANDOM_BUF_SZ])
+{
+ unsigned char c[RANDOM_BUF_SZ];
+ apr_status_t rv;
+
+ rv = f(r, c, RANDOM_BUF_SZ);
+ ABTS_INT_EQUAL(tc, 0, rv);
+ if (rv)
+ return;
+ if (memcmp(c, expected, RANDOM_BUF_SZ)) {
+ hexdump("Generated: ", c, RANDOM_BUF_SZ);
+ hexdump("Expected: ", expected, RANDOM_BUF_SZ);
+ ABTS_FAIL(tc, "Randomness mismatch");
+ }
+}
+
+#if APR_HAS_FORK
+static int rand_check_kat(rnd_fn *f, apr_random_t *r,
+ const unsigned char expected[RANDOM_BUF_SZ],
+ apr_file_t *readp, apr_file_t *writep)
+{
+ apr_size_t nbytes = RANDOM_BUF_SZ;
+ apr_size_t cmd_size = 1;
+ unsigned char c[RANDOM_BUF_SZ];
+ char ack;
+ apr_status_t rv;
+
+ rv = f(r, c, RANDOM_BUF_SZ);
+ if (rv)
+ return 2;
+ rv = 0;
+ if (memcmp(c, expected, RANDOM_BUF_SZ)) {
+ rv = 1;
+ } else {
+ hexdump("Generated: ", c, RANDOM_BUF_SZ);
+ hexdump("Previous: ", expected, RANDOM_BUF_SZ);
+ }
+ /* Report back our random values for comparison in another child */
+ apr_file_write(writep, c, &nbytes);
+ /* Wait for our parent ack the data */
+ apr_file_read(readp, &ack, &cmd_size);
+ return rv;
+}
+#endif
+
+static void rand_add_zeroes(apr_random_t *r)
+{
+ static unsigned char c[2048];
+
+ apr_random_add_entropy(r, c, sizeof c);
+}
+
+static void rand_run_seed_short(abts_case *tc, rnd_fn *f, apr_random_t *r,
+ int count)
+{
+ int i;
+ apr_status_t rv;
+ char c[1];
+
+ for (i = 0; i < count; ++i)
+ rand_add_zeroes(r);
+ rv = f(r, c, 1);
+ ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOTENOUGHENTROPY(rv));
+}
+
+static void rand_seed_short(abts_case *tc, void *data)
+{
+ r = apr_random_standard_new(p);
+ rand_run_seed_short(tc, apr_random_insecure_bytes, r, 32);
+}
+
+static void rand_kat(abts_case *tc, void *data)
+{
+ unsigned char expected[RANDOM_BUF_SZ] = {
+ 0x82, 0x04, 0xad, 0xd2, 0x0b, 0xd5, 0xac, 0xda,
+ 0x3d, 0x85, 0x58, 0x38, 0x54, 0x6b, 0x69, 0x45,
+ 0x37, 0x4c, 0xc7, 0xd7, 0x87, 0xeb, 0xbf, 0xd9,
+ 0xb1, 0xb8, 0xb8, 0x2d, 0x9b, 0x33, 0x6e, 0x97,
+ 0x04, 0x1d, 0x4c, 0xb0, 0xd1, 0xdf, 0x3d, 0xac,
+ 0xd2, 0xaa, 0xfa, 0xcd, 0x96, 0xb7, 0xcf, 0xb1,
+ 0x8e, 0x3d, 0xb3, 0xe5, 0x37, 0xa9, 0x95, 0xb4,
+ 0xaa, 0x3d, 0x11, 0x1a, 0x08, 0x20, 0x21, 0x9f,
+ 0xdb, 0x08, 0x3a, 0xb9, 0x57, 0x9f, 0xf2, 0x1f,
+ 0x27, 0xdc, 0xb6, 0xc0, 0x85, 0x08, 0x05, 0xbb,
+ 0x13, 0xbe, 0xb1, 0xe9, 0x63, 0x2a, 0xe2, 0xa4,
+ 0x23, 0x15, 0x2a, 0x10, 0xbf, 0xdf, 0x09, 0xb3,
+ 0xc7, 0xfb, 0x2d, 0x87, 0x48, 0x19, 0xfb, 0xc0,
+ 0x15, 0x8c, 0xcb, 0xc6, 0xbd, 0x89, 0x38, 0x69,
+ 0xa3, 0xae, 0xa3, 0x21, 0x58, 0x50, 0xe7, 0xc4,
+ 0x87, 0xec, 0x2e, 0xb1, 0x2d, 0x6a, 0xbd, 0x46
+ };
+
+ rand_add_zeroes(r);
+ rand_run_kat(tc, apr_random_insecure_bytes, r, expected);
+}
+
+static void rand_seed_short2(abts_case *tc, void *data)
+{
+ rand_run_seed_short(tc, apr_random_secure_bytes, r, 320);
+}
+
+static void rand_kat2(abts_case *tc, void *data)
+{
+ unsigned char expected[RANDOM_BUF_SZ] = {
+ 0x38, 0x8f, 0x01, 0x29, 0x5a, 0x5c, 0x1f, 0xa8,
+ 0x00, 0xde, 0x16, 0x4c, 0xe5, 0xf7, 0x1f, 0x58,
+ 0xc0, 0x67, 0xe2, 0x98, 0x3d, 0xde, 0x4a, 0x75,
+ 0x61, 0x3f, 0x23, 0xd8, 0x45, 0x7a, 0x10, 0x60,
+ 0x59, 0x9b, 0xd6, 0xaf, 0xcb, 0x0a, 0x2e, 0x34,
+ 0x9c, 0x39, 0x5b, 0xd0, 0xbc, 0x9a, 0xf0, 0x7b,
+ 0x7f, 0x40, 0x8b, 0x33, 0xc0, 0x0e, 0x2a, 0x56,
+ 0xfc, 0xe5, 0xab, 0xde, 0x7b, 0x13, 0xf5, 0xec,
+ 0x15, 0x68, 0xb8, 0x09, 0xbc, 0x2c, 0x15, 0xf0,
+ 0x7b, 0xef, 0x2a, 0x97, 0x19, 0xa8, 0x69, 0x51,
+ 0xdf, 0xb0, 0x5f, 0x1a, 0x4e, 0xdf, 0x42, 0x02,
+ 0x71, 0x36, 0xa7, 0x25, 0x64, 0x85, 0xe2, 0x72,
+ 0xc7, 0x87, 0x4d, 0x7d, 0x15, 0xbb, 0x15, 0xd1,
+ 0xb1, 0x62, 0x0b, 0x25, 0xd9, 0xd3, 0xd9, 0x5a,
+ 0xe3, 0x47, 0x1e, 0xae, 0x67, 0xb4, 0x19, 0x9e,
+ 0xed, 0xd2, 0xde, 0xce, 0x18, 0x70, 0x57, 0x12
+ };
+
+ rand_add_zeroes(r);
+ rand_run_kat(tc, apr_random_secure_bytes, r, expected);
+}
+
+static void rand_barrier(abts_case *tc, void *data)
+{
+ apr_random_barrier(r);
+ rand_run_seed_short(tc, apr_random_secure_bytes, r, 320);
+}
+
+static void rand_kat3(abts_case *tc, void *data)
+{
+ unsigned char expected[RANDOM_BUF_SZ] = {
+ 0xe8, 0xe7, 0xc9, 0x45, 0xe2, 0x2a, 0x54, 0xb2,
+ 0xdd, 0xe0, 0xf9, 0xbc, 0x3d, 0xf9, 0xce, 0x3c,
+ 0x4c, 0xbd, 0xc9, 0xe2, 0x20, 0x4a, 0x35, 0x1c,
+ 0x04, 0x52, 0x7f, 0xb8, 0x0f, 0x60, 0x89, 0x63,
+ 0x8a, 0xbe, 0x0a, 0x44, 0xac, 0x5d, 0xd8, 0xeb,
+ 0x24, 0x7d, 0xd1, 0xda, 0x4d, 0x86, 0x9b, 0x94,
+ 0x26, 0x56, 0x4a, 0x5e, 0x30, 0xea, 0xd4, 0xa9,
+ 0x9a, 0xdf, 0xdd, 0xb6, 0xb1, 0x15, 0xe0, 0xfa,
+ 0x28, 0xa4, 0xd6, 0x95, 0xa4, 0xf1, 0xd8, 0x6e,
+ 0xeb, 0x8c, 0xa4, 0xac, 0x34, 0xfe, 0x06, 0x92,
+ 0xc5, 0x09, 0x99, 0x86, 0xdc, 0x5a, 0x3c, 0x92,
+ 0xc8, 0x3e, 0x52, 0x00, 0x4d, 0x01, 0x43, 0x6f,
+ 0x69, 0xcf, 0xe2, 0x60, 0x9c, 0x23, 0xb3, 0xa5,
+ 0x5f, 0x51, 0x47, 0x8c, 0x07, 0xde, 0x60, 0xc6,
+ 0x04, 0xbf, 0x32, 0xd6, 0xdc, 0xb7, 0x31, 0x01,
+ 0x29, 0x51, 0x51, 0xb3, 0x19, 0x6e, 0xe4, 0xf8
+ };
+
+ rand_run_kat(tc, apr_random_insecure_bytes, r, expected);
+}
+
+static void rand_kat4(abts_case *tc, void *data)
+{
+ unsigned char expected[RANDOM_BUF_SZ] = {
+ 0x7d, 0x0e, 0xc4, 0x4e, 0x3e, 0xac, 0x86, 0x50,
+ 0x37, 0x95, 0x7a, 0x98, 0x23, 0x26, 0xa7, 0xbf,
+ 0x60, 0xfb, 0xa3, 0x70, 0x90, 0xc3, 0x58, 0xc6,
+ 0xbd, 0xd9, 0x5e, 0xa6, 0x77, 0x62, 0x7a, 0x5c,
+ 0x96, 0x83, 0x7f, 0x80, 0x3d, 0xf4, 0x9c, 0xcc,
+ 0x9b, 0x0c, 0x8c, 0xe1, 0x72, 0xa8, 0xfb, 0xc9,
+ 0xc5, 0x43, 0x91, 0xdc, 0x9d, 0x92, 0xc2, 0xce,
+ 0x1c, 0x5e, 0x36, 0xc7, 0x87, 0xb1, 0xb4, 0xa3,
+ 0xc8, 0x69, 0x76, 0xfc, 0x35, 0x75, 0xcb, 0x08,
+ 0x2f, 0xe3, 0x98, 0x76, 0x37, 0x80, 0x04, 0x5c,
+ 0xb8, 0xb0, 0x7f, 0xb2, 0xda, 0xe3, 0xa3, 0xba,
+ 0xed, 0xff, 0xf5, 0x9d, 0x3b, 0x7b, 0xf3, 0x32,
+ 0x6c, 0x50, 0xa5, 0x3e, 0xcc, 0xe1, 0x84, 0x9c,
+ 0x17, 0x9e, 0x80, 0x64, 0x09, 0xbb, 0x62, 0xf1,
+ 0x95, 0xf5, 0x2c, 0xc6, 0x9f, 0x6a, 0xee, 0x6d,
+ 0x17, 0x35, 0x5f, 0x35, 0x8d, 0x55, 0x0c, 0x07
+ };
+
+ rand_add_zeroes(r);
+ rand_run_kat(tc, apr_random_secure_bytes, r, expected);
+}
+
+#if APR_HAS_FORK
+static void rand_fork(abts_case *tc, void *data)
+{
+ apr_proc_t proc;
+ apr_status_t rv;
+ apr_size_t nbytes = RANDOM_BUF_SZ;
+ apr_size_t cmd_size = 1;
+ char cmd = 'X';
+ unsigned char expected[RANDOM_BUF_SZ] = {
+ 0xac, 0x93, 0xd2, 0x5c, 0xc7, 0xf5, 0x8d, 0xc2,
+ 0xd8, 0x8d, 0xb6, 0x7a, 0x94, 0xe1, 0x83, 0x4c,
+ 0x26, 0xe2, 0x38, 0x6d, 0xf5, 0xbd, 0x9d, 0x6e,
+ 0x91, 0x77, 0x3a, 0x4b, 0x9b, 0xef, 0x9b, 0xa3,
+ 0x9f, 0xf6, 0x6d, 0x0c, 0xdc, 0x4b, 0x02, 0xe9,
+ 0x5d, 0x3d, 0xfc, 0x92, 0x6b, 0xdf, 0xc9, 0xef,
+ 0xb9, 0xa8, 0x74, 0x09, 0xa3, 0xff, 0x64, 0x8d,
+ 0x19, 0xc1, 0x31, 0x31, 0x17, 0xe1, 0xb7, 0x7a,
+ 0xe7, 0x55, 0x14, 0x92, 0x05, 0xe3, 0x1e, 0xb8,
+ 0x9b, 0x1b, 0xdc, 0xac, 0x0e, 0x15, 0x08, 0xa2,
+ 0x93, 0x13, 0xf6, 0x04, 0xc6, 0x9d, 0xf8, 0x7f,
+ 0x26, 0x32, 0x68, 0x43, 0x2e, 0x5a, 0x4f, 0x47,
+ 0xe8, 0xf8, 0x59, 0xb7, 0xfb, 0xbe, 0x30, 0x04,
+ 0xb6, 0x63, 0x6f, 0x19, 0xf3, 0x2c, 0xd4, 0xeb,
+ 0x32, 0x8a, 0x54, 0x01, 0xd0, 0xaf, 0x3f, 0x13,
+ 0xc1, 0x7f, 0x10, 0x2e, 0x08, 0x1c, 0x28, 0x4b,
+ };
+
+ apr_file_t *readdatap = NULL;
+ apr_file_t *writedatap = NULL;
+ apr_file_t *readcmdp = NULL;
+ apr_file_t *writecmdp = NULL;
+ apr_pool_t *p;
+ int i;
+
+ apr_pool_create(&p, NULL);
+ /* Set up data pipe for children */
+ rv = apr_file_pipe_create(&readdatap, &writedatap, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, readdatap);
+ ABTS_PTR_NOTNULL(tc, writedatap);
+ /* Set up cmd pipe for children */
+ rv = apr_file_pipe_create(&readcmdp, &writecmdp, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, readcmdp);
+ ABTS_PTR_NOTNULL(tc, writecmdp);
+
+ rand_run_kat(tc, apr_random_secure_bytes, r, expected);
+
+ for (i = 0; i< 10; i++)
+ {
+ rv = apr_proc_fork(&proc, p);
+ if (rv == APR_INCHILD) {
+ int n = rand_check_kat(apr_random_secure_bytes, r, expected, readcmdp, writedatap);
+ exit(n);
+ }
+ else if (rv == APR_INPARENT) {
+ int exitcode;
+ apr_exit_why_e why;
+
+ /* Read the random data generated by child */
+ rv = apr_file_read(readdatap, expected, &nbytes);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ /* Tell child to finish */
+ rv = apr_file_write(writecmdp, &cmd, &cmd_size);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ apr_proc_wait(&proc, &exitcode, &why, APR_WAIT);
+ if (why != APR_PROC_EXIT) {
+ ABTS_FAIL(tc, "Child terminated abnormally");
+ }
+ else if (exitcode == 0) {
+ if (i == 0)
+ {
+ ABTS_FAIL(tc, "Child produced our randomness");
+ } else
+ {
+ ABTS_FAIL(tc, "Child produced randomness of previous child");
+ }
+ }
+ else if (exitcode == 2) {
+ ABTS_FAIL(tc, "Child randomness failed");
+ }
+ else if (exitcode != 1) {
+ ABTS_FAIL(tc, "Unknown child error");
+ }
+ } else {
+ ABTS_FAIL(tc, "Fork failed");
+ }
+ }
+
+}
+#endif
+
+static void rand_exists(abts_case *tc, void *data)
+{
+#if !APR_HAS_RANDOM
+ ABTS_NOT_IMPL(tc, "apr_generate_random_bytes");
+#else
+ unsigned char c[42];
+
+ /* There must be a better way to test random-ness, but I don't know
+ * what it is right now.
+ */
+ APR_ASSERT_SUCCESS(tc, "apr_generate_random_bytes failed",
+ apr_generate_random_bytes(c, sizeof c));
+#endif
+}
+
+abts_suite *testrand(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, rand_exists, NULL);
+ abts_run_test(suite, rand_seed_short, NULL);
+ abts_run_test(suite, rand_kat, NULL);
+ abts_run_test(suite, rand_seed_short2, NULL);
+ abts_run_test(suite, rand_kat2, NULL);
+ abts_run_test(suite, rand_barrier, NULL);
+ abts_run_test(suite, rand_kat3, NULL);
+ abts_run_test(suite, rand_kat4, NULL);
+#if APR_HAS_FORK
+ abts_run_test(suite, rand_fork, NULL);
+#endif
+
+ return suite;
+}
diff --git a/test/testshm.c b/test/testshm.c
new file mode 100644
index 0000000..7387069
--- /dev/null
+++ b/test/testshm.c
@@ -0,0 +1,332 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_shm.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_thread_proc.h"
+#include "apr_time.h"
+#include "testshm.h"
+#include "apr.h"
+
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if APR_HAS_SHARED_MEMORY
+
+#if APR_HAS_FORK
+static int msgwait(int sleep_sec, int first_box, int last_box)
+{
+ int i;
+ int recvd = 0;
+ apr_time_t start = apr_time_now();
+ apr_interval_time_t sleep_duration = apr_time_from_sec(sleep_sec);
+ while (apr_time_now() - start < sleep_duration) {
+ for (i = first_box; i < last_box; i++) {
+ if (boxes[i].msgavail && !strcmp(boxes[i].msg, MSG)) {
+ recvd++;
+ boxes[i].msgavail = 0; /* reset back to 0 */
+ /* reset the msg field. 1024 is a magic number and it should
+ * be a macro, but I am being lazy.
+ */
+ memset(boxes[i].msg, 0, 1024);
+ }
+ }
+ apr_sleep(apr_time_make(0, 10000)); /* 10ms */
+ }
+ return recvd;
+}
+
+static void msgput(int boxnum, char *msg)
+{
+ apr_cpystrn(boxes[boxnum].msg, msg, strlen(msg) + 1);
+ boxes[boxnum].msgavail = 1;
+}
+#endif /* APR_HAS_FORK */
+
+static void test_anon_create(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_shm_t *shm = NULL;
+
+ rv = apr_shm_create(&shm, SHARED_SIZE, NULL, p);
+ APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv);
+ ABTS_PTR_NOTNULL(tc, shm);
+
+ rv = apr_shm_destroy(shm);
+ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv);
+}
+
+static void test_check_size(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_shm_t *shm = NULL;
+ apr_size_t retsize;
+
+ rv = apr_shm_create(&shm, SHARED_SIZE, NULL, p);
+ APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv);
+ ABTS_PTR_NOTNULL(tc, shm);
+
+ retsize = apr_shm_size_get(shm);
+ ABTS_SIZE_EQUAL(tc, SHARED_SIZE, retsize);
+
+ rv = apr_shm_destroy(shm);
+ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv);
+}
+
+static void test_shm_allocate(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_shm_t *shm = NULL;
+
+ rv = apr_shm_create(&shm, SHARED_SIZE, NULL, p);
+ APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv);
+ ABTS_PTR_NOTNULL(tc, shm);
+
+ boxes = apr_shm_baseaddr_get(shm);
+ ABTS_PTR_NOTNULL(tc, boxes);
+
+ rv = apr_shm_destroy(shm);
+ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv);
+}
+
+#if APR_HAS_FORK
+static void test_anon(abts_case *tc, void *data)
+{
+ apr_proc_t proc;
+ apr_status_t rv;
+ apr_shm_t *shm;
+ apr_size_t retsize;
+ int cnt, i;
+ int recvd;
+
+ rv = apr_shm_create(&shm, SHARED_SIZE, NULL, p);
+ APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv);
+ ABTS_PTR_NOTNULL(tc, shm);
+
+ retsize = apr_shm_size_get(shm);
+ ABTS_INT_EQUAL(tc, SHARED_SIZE, retsize);
+
+ boxes = apr_shm_baseaddr_get(shm);
+ ABTS_PTR_NOTNULL(tc, boxes);
+
+ rv = apr_proc_fork(&proc, p);
+ if (rv == APR_INCHILD) { /* child */
+ int num = msgwait(5, 0, N_BOXES);
+ /* exit with the number of messages received so that the parent
+ * can check that all messages were received.
+ */
+ exit(num);
+ }
+ else if (rv == APR_INPARENT) { /* parent */
+ i = N_BOXES;
+ cnt = 0;
+ while (cnt++ < N_MESSAGES) {
+ if ((i-=3) < 0) {
+ i += N_BOXES; /* start over at the top */
+ }
+ msgput(i, MSG);
+ apr_sleep(apr_time_make(0, 10000));
+ }
+ }
+ else {
+ ABTS_FAIL(tc, "apr_proc_fork failed");
+ }
+ /* wait for the child */
+ rv = apr_proc_wait(&proc, &recvd, NULL, APR_WAIT);
+ ABTS_INT_EQUAL(tc, N_MESSAGES, recvd);
+
+ rv = apr_shm_destroy(shm);
+ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv);
+}
+#endif
+
+static void test_named(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_shm_t *shm = NULL;
+ apr_size_t retsize;
+ apr_proc_t pidproducer, pidconsumer;
+ apr_procattr_t *attr1 = NULL, *attr2 = NULL;
+ int sent, received;
+ apr_exit_why_e why;
+ const char *args[4];
+
+ apr_shm_remove(SHARED_FILENAME, p);
+
+ rv = apr_shm_create(&shm, SHARED_SIZE, SHARED_FILENAME, p);
+ APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv);
+ if (rv != APR_SUCCESS) {
+ return;
+ }
+ ABTS_PTR_NOTNULL(tc, shm);
+
+ retsize = apr_shm_size_get(shm);
+ ABTS_SIZE_EQUAL(tc, SHARED_SIZE, retsize);
+
+ boxes = apr_shm_baseaddr_get(shm);
+ ABTS_PTR_NOTNULL(tc, boxes);
+
+ rv = apr_procattr_create(&attr1, p);
+ ABTS_PTR_NOTNULL(tc, attr1);
+ APR_ASSERT_SUCCESS(tc, "Couldn't create attr1", rv);
+
+ rv = apr_procattr_cmdtype_set(attr1, APR_PROGRAM_ENV);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set copy environment", rv);
+
+ args[0] = apr_pstrdup(p, "testshmproducer" EXTENSION);
+ args[1] = NULL;
+ rv = apr_proc_create(&pidproducer, TESTBINPATH "testshmproducer" EXTENSION, args,
+ NULL, attr1, p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't launch producer", rv);
+
+ rv = apr_procattr_create(&attr2, p);
+ ABTS_PTR_NOTNULL(tc, attr2);
+ APR_ASSERT_SUCCESS(tc, "Couldn't create attr2", rv);
+
+ rv = apr_procattr_cmdtype_set(attr2, APR_PROGRAM_ENV);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set copy environment", rv);
+
+ args[0] = apr_pstrdup(p, "testshmconsumer" EXTENSION);
+ rv = apr_proc_create(&pidconsumer, TESTBINPATH "testshmconsumer" EXTENSION, args,
+ NULL, attr2, p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't launch consumer", rv);
+
+ rv = apr_proc_wait(&pidconsumer, &received, &why, APR_WAIT);
+ ABTS_INT_EQUAL(tc, APR_CHILD_DONE, rv);
+ ABTS_INT_EQUAL(tc, APR_PROC_EXIT, why);
+
+ rv = apr_proc_wait(&pidproducer, &sent, &why, APR_WAIT);
+ ABTS_INT_EQUAL(tc, APR_CHILD_DONE, rv);
+ ABTS_INT_EQUAL(tc, APR_PROC_EXIT, why);
+
+ /* Cleanup before testing that producer and consumer worked correctly.
+ * This way, if they didn't succeed, we can just run this test again
+ * without having to cleanup manually.
+ */
+ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory",
+ apr_shm_destroy(shm));
+
+ ABTS_INT_EQUAL(tc, sent, received);
+
+}
+
+static void test_named_remove(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_shm_t *shm, *shm2;
+
+ apr_shm_remove(SHARED_FILENAME, p);
+
+ rv = apr_shm_create(&shm, SHARED_SIZE, SHARED_FILENAME, p);
+ APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv);
+ if (rv != APR_SUCCESS) {
+ return;
+ }
+ ABTS_PTR_NOTNULL(tc, shm);
+
+ rv = apr_shm_remove(SHARED_FILENAME, p);
+
+ /* On platforms which acknowledge the removal of the shared resource,
+ * ensure another of the same name may be created after removal;
+ */
+ if (rv == APR_SUCCESS)
+ {
+ rv = apr_shm_create(&shm2, SHARED_SIZE, SHARED_FILENAME, p);
+ APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv);
+ if (rv != APR_SUCCESS) {
+ return;
+ }
+ ABTS_PTR_NOTNULL(tc, shm2);
+
+ rv = apr_shm_destroy(shm2);
+ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv);
+ }
+
+ rv = apr_shm_destroy(shm);
+ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv);
+
+ /* Now ensure no named resource remains which we may attach to */
+ rv = apr_shm_attach(&shm, SHARED_FILENAME, p);
+ ABTS_TRUE(tc, rv != 0);
+}
+
+static void test_named_delete(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_shm_t *shm, *shm2;
+
+ apr_shm_remove(SHARED_FILENAME, p);
+
+ rv = apr_shm_create(&shm, SHARED_SIZE, SHARED_FILENAME, p);
+ APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv);
+ if (rv != APR_SUCCESS) {
+ return;
+ }
+ ABTS_PTR_NOTNULL(tc, shm);
+
+ rv = apr_shm_delete(shm);
+
+ /* On platforms which acknowledge the removal of the shared resource,
+ * ensure another of the same name may be created after removal;
+ */
+ if (rv == APR_SUCCESS)
+ {
+ rv = apr_shm_create(&shm2, SHARED_SIZE, SHARED_FILENAME, p);
+ APR_ASSERT_SUCCESS(tc, "Error allocating shared memory block", rv);
+ if (rv != APR_SUCCESS) {
+ return;
+ }
+ ABTS_PTR_NOTNULL(tc, shm2);
+
+ rv = apr_shm_destroy(shm2);
+ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv);
+ }
+
+ rv = apr_shm_destroy(shm);
+ APR_ASSERT_SUCCESS(tc, "Error destroying shared memory block", rv);
+
+ /* Now ensure no named resource remains which we may attach to */
+ rv = apr_shm_attach(&shm, SHARED_FILENAME, p);
+ ABTS_TRUE(tc, rv != 0);
+}
+
+#endif
+
+abts_suite *testshm(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+#if APR_HAS_SHARED_MEMORY
+ abts_run_test(suite, test_anon_create, NULL);
+ abts_run_test(suite, test_check_size, NULL);
+ abts_run_test(suite, test_shm_allocate, NULL);
+#if APR_HAS_FORK
+ abts_run_test(suite, test_anon, NULL);
+#endif
+ abts_run_test(suite, test_named, NULL);
+ abts_run_test(suite, test_named_remove, NULL);
+ abts_run_test(suite, test_named_delete, NULL);
+#endif
+
+ return suite;
+}
+
+
diff --git a/test/testshm.h b/test/testshm.h
new file mode 100644
index 0000000..5b24a9d
--- /dev/null
+++ b/test/testshm.h
@@ -0,0 +1,33 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TESTSHM_H
+#define TESTSHM_H
+
+typedef struct mbox {
+ char msg[1024];
+ int msgavail;
+} mbox;
+mbox *boxes;
+
+#define N_BOXES 10
+#define SHARED_SIZE (apr_size_t)(N_BOXES * sizeof(mbox))
+#define SHARED_FILENAME "data/apr.testshm.shm"
+#define N_MESSAGES 100
+#define MSG "Sending a message"
+
+#endif
+
diff --git a/test/testshmconsumer.c b/test/testshmconsumer.c
new file mode 100644
index 0000000..6a2a3c3
--- /dev/null
+++ b/test/testshmconsumer.c
@@ -0,0 +1,94 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_shm.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "testshm.h"
+#include "apr.h"
+
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+
+#if APR_HAS_SHARED_MEMORY
+
+static int msgwait(int sleep_sec, int first_box, int last_box)
+{
+ int i;
+ int recvd = 0;
+ apr_time_t start = apr_time_now();
+ apr_interval_time_t sleep_duration = apr_time_from_sec(sleep_sec);
+ while (apr_time_now() - start < sleep_duration) {
+ for (i = first_box; i < last_box; i++) {
+ if (boxes[i].msgavail && !strcmp(boxes[i].msg, MSG)) {
+ recvd++;
+ boxes[i].msgavail = 0; /* reset back to 0 */
+ memset(boxes[i].msg, 0, 1024);
+ }
+ }
+ apr_sleep(apr_time_from_sec(1));
+ }
+ return recvd;
+}
+
+int main(void)
+{
+ apr_status_t rv;
+ apr_pool_t *pool;
+ apr_shm_t *shm;
+ int recvd;
+
+ apr_initialize();
+
+ if (apr_pool_create(&pool, NULL) != APR_SUCCESS) {
+ exit(-1);
+ }
+
+ rv = apr_shm_attach(&shm, SHARED_FILENAME, pool);
+ if (rv != APR_SUCCESS) {
+ exit(-2);
+ }
+
+ boxes = apr_shm_baseaddr_get(shm);
+
+ /* consume messages on all of the boxes */
+ recvd = msgwait(30, 0, N_BOXES); /* wait for 30 seconds for messages */
+
+ rv = apr_shm_detach(shm);
+ if (rv != APR_SUCCESS) {
+ exit(-3);
+ }
+
+ return recvd;
+}
+
+#else /* APR_HAS_SHARED_MEMORY */
+
+int main(void)
+{
+ /* Just return, this program will never be called, so we don't need
+ * to print a message
+ */
+ return 0;
+}
+
+#endif /* APR_HAS_SHARED_MEMORY */
+
diff --git a/test/testshmproducer.c b/test/testshmproducer.c
new file mode 100644
index 0000000..58eb94f
--- /dev/null
+++ b/test/testshmproducer.c
@@ -0,0 +1,89 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_shm.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "testshm.h"
+#include "apr.h"
+
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+
+#if APR_HAS_SHARED_MEMORY
+static void msgput(int boxnum, char *msg)
+{
+ apr_cpystrn(boxes[boxnum].msg, msg, strlen(msg) + 1);
+ boxes[boxnum].msgavail = 1;
+}
+
+int main(void)
+{
+ apr_status_t rv;
+ apr_pool_t *pool;
+ apr_shm_t *shm;
+ int i;
+ int sent = 0;
+
+ apr_initialize();
+
+ if (apr_pool_create(&pool, NULL) != APR_SUCCESS) {
+ exit(-1);
+ }
+
+ rv = apr_shm_attach(&shm, SHARED_FILENAME, pool);
+ if (rv != APR_SUCCESS) {
+ exit(-2);
+ }
+
+ boxes = apr_shm_baseaddr_get(shm);
+
+ /* produce messages on all of the boxes, in descending order,
+ * Yes, we could just return N_BOXES, but I want to have a double-check
+ * in this code. The original code actually sent N_BOXES - 1 messages,
+ * so rather than rely on possibly buggy code, this way we know that we
+ * are returning the right number.
+ */
+ for (i = N_BOXES - 1, sent = 0; i >= 0; i--, sent++) {
+ msgput(i, MSG);
+ apr_sleep(apr_time_from_sec(1));
+ }
+
+ rv = apr_shm_detach(shm);
+ if (rv != APR_SUCCESS) {
+ exit(-3);
+ }
+
+ return sent;
+}
+
+#else /* APR_HAS_SHARED_MEMORY */
+
+int main(void)
+{
+ /* Just return, this program will never be launched, so there is no
+ * reason to print a message.
+ */
+ return 0;
+}
+
+#endif /* APR_HAS_SHARED_MEMORY */
+
diff --git a/test/testskiplist.c b/test/testskiplist.c
new file mode 100644
index 0000000..ca4b435
--- /dev/null
+++ b/test/testskiplist.c
@@ -0,0 +1,546 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_skiplist.h"
+#if APR_HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if APR_HAVE_STRING_H
+#include <string.h>
+#endif
+
+static apr_pool_t *ptmp = NULL;
+static apr_skiplist *skiplist = NULL;
+
+static int skiplist_get_size(abts_case *tc, apr_skiplist *sl)
+{
+ size_t size = 0;
+ apr_skiplistnode *n;
+ for (n = apr_skiplist_getlist(sl); n; apr_skiplist_next(sl, &n)) {
+ ++size;
+ }
+ ABTS_TRUE(tc, size == apr_skiplist_size(sl));
+ return size;
+}
+
+static void skiplist_init(abts_case *tc, void *data)
+{
+ apr_time_t now = apr_time_now();
+ srand((unsigned int)(((now >> 32) ^ now) & 0xffffffff));
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&skiplist, p));
+ ABTS_PTR_NOTNULL(tc, skiplist);
+ apr_skiplist_set_compare(skiplist, (apr_skiplist_compare)strcmp,
+ (apr_skiplist_compare)strcmp);
+}
+
+static void skiplist_find(abts_case *tc, void *data)
+{
+ const char *val;
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "baton"));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+}
+
+static void skiplist_dontfind(abts_case *tc, void *data)
+{
+ const char *val;
+
+ val = apr_skiplist_find(skiplist, "keynotthere", NULL);
+ ABTS_PTR_EQUAL(tc, NULL, (void *)val);
+}
+
+static void skiplist_insert(abts_case *tc, void *data)
+{
+ const char *val;
+ int i, height = 0;
+
+ for (i = 0; i < 10; ++i) {
+ ABTS_PTR_EQUAL(tc, NULL, apr_skiplist_insert(skiplist, "baton"));
+ ABTS_TRUE(tc, 1 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ if (height == 0) {
+ height = apr_skiplist_height(skiplist);
+ }
+ else {
+ ABTS_INT_EQUAL(tc, height, apr_skiplist_height(skiplist));
+ }
+ }
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "foo"));
+ ABTS_TRUE(tc, 2 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "foo", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "foo", val);
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "atfirst"));
+ ABTS_TRUE(tc, 3 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "atfirst", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "atfirst", val);
+}
+
+#define NUM_ADDS 100
+static void skiplist_add(abts_case *tc, void *data)
+{
+ const char *val;
+ size_t i, n = 0;
+
+ apr_skiplist_remove_all(skiplist, NULL);
+ ABTS_TRUE(tc, 0 == skiplist_get_size(tc, skiplist));
+
+ for (i = 0; i < NUM_ADDS; ++i) {
+ n++;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "daton"));
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "daton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "daton", val);
+
+ n++;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "baton"));
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ n++;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "caton"));
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "caton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "caton", val);
+
+ n++;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "aaton"));
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "aaton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "aaton", val);
+ }
+}
+
+static void skiplist_replace(abts_case *tc, void *data)
+{
+ const char *val;
+ size_t n = skiplist_get_size(tc, skiplist);
+
+ n -= NUM_ADDS - 1;
+ apr_skiplist_replace(skiplist, "daton", NULL);
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "daton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "daton", val);
+
+ n -= NUM_ADDS - 1;
+ apr_skiplist_replace(skiplist, "baton", NULL);
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ n -= NUM_ADDS - 1;
+ apr_skiplist_replace(skiplist, "caton", NULL);
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "caton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "caton", val);
+
+ n -= NUM_ADDS - 1;
+ apr_skiplist_replace(skiplist, "aaton", NULL);
+ ABTS_TRUE(tc, n == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "aaton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "aaton", val);
+
+ ABTS_TRUE(tc, n == 4);
+}
+
+static void skiplist_destroy(abts_case *tc, void *data)
+{
+ apr_skiplist_destroy(skiplist, NULL);
+ ABTS_TRUE(tc, 0 == apr_skiplist_size(skiplist));
+ ABTS_TRUE(tc, 1 == apr_skiplist_height(skiplist));
+ ABTS_TRUE(tc, NULL == apr_skiplist_getlist(skiplist));
+}
+
+static void skiplist_size(abts_case *tc, void *data)
+{
+ const char *val;
+
+ ABTS_TRUE(tc, 0 == skiplist_get_size(tc, skiplist));
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "abc"));
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "ghi"));
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(skiplist, "def"));
+ val = apr_skiplist_find(skiplist, "abc", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "abc", val);
+ val = apr_skiplist_find(skiplist, "ghi", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "ghi", val);
+ val = apr_skiplist_find(skiplist, "def", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "def", val);
+
+ ABTS_TRUE(tc, 3 == skiplist_get_size(tc, skiplist));
+ apr_skiplist_destroy(skiplist, NULL);
+}
+
+static void skiplist_remove(abts_case *tc, void *data)
+{
+ const char *val;
+
+ ABTS_TRUE(tc, 0 == skiplist_get_size(tc, skiplist));
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "baton"));
+ ABTS_TRUE(tc, 1 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "baton"));
+ ABTS_TRUE(tc, 2 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ ABTS_TRUE(tc, apr_skiplist_remove(skiplist, "baton", NULL) != 0);
+ ABTS_TRUE(tc, 1 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(skiplist, "baton"));
+ ABTS_TRUE(tc, 2 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, "baton", val);
+
+ /* remove all "baton"s */
+ while (apr_skiplist_remove(skiplist, "baton", NULL))
+ ;
+ ABTS_TRUE(tc, 0 == skiplist_get_size(tc, skiplist));
+ val = apr_skiplist_find(skiplist, "baton", NULL);
+ ABTS_PTR_EQUAL(tc, NULL, val);
+}
+
+#define NUM_RAND (100)
+#define NUM_FIND (3 * NUM_RAND)
+static void skiplist_random_loop(abts_case *tc, void *data)
+{
+ char **batons;
+ apr_skiplist *sl;
+ const char *val;
+ int i;
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&sl, ptmp));
+ apr_skiplist_set_compare(sl, (apr_skiplist_compare)strcmp,
+ (apr_skiplist_compare)strcmp);
+ apr_skiplist_set_preheight(sl, 7);
+ ABTS_INT_EQUAL(tc, 7, apr_skiplist_preheight(sl));
+
+ batons = apr_palloc(ptmp, NUM_FIND * sizeof(char*));
+
+ for (i = 0; i < NUM_FIND; ++i) {
+ if (i < NUM_RAND) {
+ batons[i] = apr_psprintf(ptmp, "%.6u", rand() % 1000000);
+ }
+ else {
+ batons[i] = apr_pstrdup(ptmp, batons[i % NUM_RAND]);
+ }
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(sl, batons[i]));
+ val = apr_skiplist_find(sl, batons[i], NULL);
+ ABTS_PTR_NOTNULL(tc, val);
+ ABTS_STR_EQUAL(tc, batons[i], val);
+ }
+
+ apr_pool_clear(ptmp);
+}
+
+typedef struct elem {
+ int b;
+ int a;
+} elem;
+
+
+static void add_int_to_skiplist(abts_case *tc, apr_skiplist *list, int n){
+ int* a = apr_skiplist_alloc(list, sizeof(int));
+ *a = n;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(list, a));
+}
+
+static void add_elem_to_skiplist(abts_case *tc, apr_skiplist *list, elem n){
+ elem* a = apr_skiplist_alloc(list, sizeof(elem));
+ *a = n;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(list, a));
+}
+
+static int comp(void *a, void *b){
+ return *((int*) a) - *((int*) b);
+}
+
+static int scomp(void *a, void *b){
+ return ((elem*) a)->a - ((elem*) b)->a;
+}
+
+static int ecomp(void *a, void *b)
+{
+ elem const * const e1 = a;
+ elem const * const e2 = b;
+ if (e1->a < e2->a) {
+ return -1;
+ }
+ else if (e1->a > e2->a) {
+ return +1;
+ }
+ else if (e1->b < e2->b) {
+ return -1;
+ }
+ else if (e1->b > e2->b) {
+ return +1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/* Some tests below add multiple duplicates and then try to remove each one
+ * individually, in arbitrary order.
+ *
+ * Using apr_skiplist_remove_compare(..., scomp, NULL) would not work because
+ * it will likely remove any duplicate (the first one) encountered on the path,
+ * hence possibly not the expected one.
+ *
+ * Using apr_skiplist_remove_compare(..., ecomp, NULL) works provided all the
+ * duplicates (same a) don't also have the same b (which is the case in the
+ * test below), hence uniqueness is cooked in the elem itself.
+ *
+ * Another possibility is to rely on unique pointers, and then cook a remove
+ * function, like the following skiplist_remove_scomp(), which will go straight
+ * to the last duplicate (using scomp) and then iterate on the previous elems
+ * until pointers match.
+ *
+ * Providing uniqueness in the elem itself is the more clean/efficient option,
+ * but if all you have is a unique pointer the pattern in the function may be
+ * worth it ( or it's just a way to test several skiplist functionalities :)
+ */
+static void skiplist_remove_scomp(abts_case *tc, apr_skiplist *list, elem *n)
+{
+ elem *e;
+ apr_skiplistnode *iter = NULL;
+ e = apr_skiplist_last(list, n, &iter);
+ while (e && e != n) {
+ ABTS_INT_EQUAL(tc, 0, scomp(n, e));
+ e = apr_skiplist_previous(list, &iter);
+ }
+ ABTS_PTR_EQUAL(tc, n, apr_skiplist_element(iter));
+ apr_skiplist_remove_node(list, iter, NULL);
+}
+
+static void skiplist_test(abts_case *tc, void *data) {
+ int test_elems = 10;
+ int i = 0, j = 0;
+ int *val = NULL;
+ elem *val2 = NULL;
+ apr_skiplist * list = NULL;
+ apr_skiplist * list2 = NULL;
+ apr_skiplist * list3 = NULL;
+ apr_skiplist * list4 = NULL;
+ int first_forty_two = 42,
+ second_forty_two = 42;
+ apr_array_header_t *array;
+ elem t1, t2, t3, t4, t5;
+ t1.a = 1; t1.b = 1;
+ t2.a = 42; t2.b = 1;
+ t3.a = 42; t3.b = 2;
+ t4.a = 42; t4.b = 3;
+ t5.a = 142; t5.b = 1;
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&list, ptmp));
+ apr_skiplist_set_compare(list, comp, comp);
+
+ /* insert 10 objects */
+ for (i = 0; i < test_elems; ++i){
+ add_int_to_skiplist(tc, list, i);
+ }
+
+ /* remove all objects */
+ while ((val = apr_skiplist_pop(list, NULL))){
+ ABTS_INT_EQUAL(tc, *val, j++);
+ }
+
+ /* insert 10 objects again */
+ for (i = test_elems; i < test_elems+test_elems; ++i){
+ add_int_to_skiplist(tc, list, i);
+ }
+
+ j = test_elems;
+ while ((val = apr_skiplist_pop(list, NULL))){
+ ABTS_INT_EQUAL(tc, *val, j++);
+ }
+
+ /* empty */
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_PTR_EQUAL(tc, val, NULL);
+
+ add_int_to_skiplist(tc, list, 42);
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_INT_EQUAL(tc, *val, 42);
+
+ /* empty */
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_PTR_EQUAL(tc, val, NULL);
+
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(list, &first_forty_two));
+ add_int_to_skiplist(tc, list, 1);
+ add_int_to_skiplist(tc, list, 142);
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_add(list, &second_forty_two));
+ val = apr_skiplist_peek(list);
+ ABTS_INT_EQUAL(tc, *val, 1);
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_INT_EQUAL(tc, *val, 1);
+ val = apr_skiplist_peek(list);
+ ABTS_PTR_EQUAL(tc, val, &first_forty_two);
+ ABTS_INT_EQUAL(tc, *val, 42);
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_PTR_EQUAL(tc, val, &first_forty_two);
+ ABTS_INT_EQUAL(tc, *val, 42);
+ val = apr_skiplist_pop(list, NULL);
+ ABTS_PTR_EQUAL(tc, val, &second_forty_two);
+ ABTS_INT_EQUAL(tc, *val, 42);
+ val = apr_skiplist_peek(list);
+ ABTS_INT_EQUAL(tc, *val, 142);
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&list2, ptmp));
+ apr_skiplist_set_compare(list2, scomp, scomp);
+ add_elem_to_skiplist(tc, list2, t2);
+ add_elem_to_skiplist(tc, list2, t1);
+ add_elem_to_skiplist(tc, list2, t3);
+ add_elem_to_skiplist(tc, list2, t5);
+ add_elem_to_skiplist(tc, list2, t4);
+ val2 = apr_skiplist_pop(list2, NULL);
+ ABTS_INT_EQUAL(tc, val2->a, 1);
+ val2 = apr_skiplist_pop(list2, NULL);
+ ABTS_INT_EQUAL(tc, val2->a, 42);
+ ABTS_INT_EQUAL(tc, val2->b, 1);
+ val2 = apr_skiplist_pop(list2, NULL);
+ ABTS_INT_EQUAL(tc, val2->a, 42);
+ ABTS_INT_EQUAL(tc, val2->b, 2);
+ val2 = apr_skiplist_pop(list2, NULL);
+ ABTS_INT_EQUAL(tc, val2->a, 42);
+ ABTS_INT_EQUAL(tc, val2->b, 3);
+ val2 = apr_skiplist_pop(list2, NULL);
+ ABTS_INT_EQUAL(tc, val2->a, 142);
+ ABTS_INT_EQUAL(tc, val2->b, 1);
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&list3, ptmp));
+ apr_skiplist_set_compare(list3, ecomp, ecomp);
+ array = apr_array_make(ptmp, 10, sizeof(elem *));
+ for (i = 0; i < 10; ++i) {
+ elem *e = apr_palloc(ptmp, sizeof *e);
+ e->a = 4224;
+ e->b = i;
+ APR_ARRAY_PUSH(array, elem *) = e;
+ ABTS_PTR_NOTNULL(tc, apr_skiplist_insert(list3, e));
+ }
+ for (i = 0; i < 5; ++i) {
+ elem *e = APR_ARRAY_IDX(array, i, elem *);
+ val2 = apr_skiplist_find(list3, e, NULL);
+ ABTS_PTR_EQUAL(tc, e, val2);
+ ABTS_TRUE(tc, apr_skiplist_remove(list3, e, NULL) != 0);
+ }
+ for (i = 0; i < 5; ++i) {
+ elem *e = APR_ARRAY_IDX(array, 9 - i, elem *);
+ val2 = apr_skiplist_find(list3, e, NULL);
+ ABTS_PTR_EQUAL(tc, e, val2);
+ ABTS_TRUE(tc, apr_skiplist_remove(list3, e, NULL) != 0);
+ }
+
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, apr_skiplist_init(&list4, ptmp));
+ apr_skiplist_set_compare(list4, scomp, scomp);
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t1);
+ }
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t2);
+ }
+ apr_skiplist_add(list4, &t2);
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t2);
+ }
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t3);
+ }
+ apr_skiplist_add(list4, &t3);
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t3);
+ }
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t4);
+ }
+ apr_skiplist_add(list4, &t4);
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t4);
+ }
+ for (i = 0; i < 5; ++i){
+ add_elem_to_skiplist(tc, list4, t5);
+ }
+ skiplist_remove_scomp(tc, list4, &t2);
+ skiplist_remove_scomp(tc, list4, &t3);
+ skiplist_remove_scomp(tc, list4, &t4);
+
+ apr_pool_clear(ptmp);
+}
+
+
+abts_suite *testskiplist(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ apr_pool_create(&ptmp, p);
+
+ abts_run_test(suite, skiplist_init, NULL);
+ abts_run_test(suite, skiplist_find, NULL);
+ abts_run_test(suite, skiplist_dontfind, NULL);
+ abts_run_test(suite, skiplist_insert, NULL);
+ abts_run_test(suite, skiplist_add, NULL);
+ abts_run_test(suite, skiplist_replace, NULL);
+ abts_run_test(suite, skiplist_destroy, NULL);
+ abts_run_test(suite, skiplist_size, NULL);
+ abts_run_test(suite, skiplist_remove, NULL);
+ abts_run_test(suite, skiplist_random_loop, NULL);
+
+ abts_run_test(suite, skiplist_test, NULL);
+
+ apr_pool_destroy(ptmp);
+
+ return suite;
+}
+
diff --git a/test/testsleep.c b/test/testsleep.c
new file mode 100644
index 0000000..eff24dd
--- /dev/null
+++ b/test/testsleep.c
@@ -0,0 +1,53 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "time.h"
+#include "apr_thread_proc.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "testutil.h"
+
+#define SLEEP_INTERVAL 5
+
+static void sleep_one(abts_case *tc, void *data)
+{
+ time_t pretime = time(NULL);
+ time_t posttime;
+ time_t timediff;
+
+ apr_sleep(apr_time_from_sec(SLEEP_INTERVAL));
+ posttime = time(NULL);
+
+ /* normalize the timediff. We should have slept for SLEEP_INTERVAL, so
+ * we should just subtract that out.
+ */
+ timediff = posttime - pretime - SLEEP_INTERVAL;
+ ABTS_TRUE(tc, timediff >= 0);
+ ABTS_TRUE(tc, timediff <= 1);
+}
+
+abts_suite *testsleep(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, sleep_one, NULL);
+
+ return suite;
+}
+
diff --git a/test/testsock.c b/test/testsock.c
new file mode 100644
index 0000000..5c571d6
--- /dev/null
+++ b/test/testsock.c
@@ -0,0 +1,702 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "testsock.h"
+#include "apr_thread_proc.h"
+#include "apr_network_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "apr_poll.h"
+#define APR_WANT_BYTEFUNC
+#include "apr_want.h"
+
+#define UNIX_SOCKET_NAME "/tmp/apr-socket"
+#define IPV4_SOCKET_NAME "127.0.0.1"
+static char *socket_name = NULL;
+static int socket_type = APR_INET;
+
+static void launch_child(abts_case *tc, apr_proc_t *proc, const char *arg1, apr_pool_t *p)
+{
+ apr_procattr_t *procattr;
+ const char *args[4];
+ apr_status_t rv;
+
+ rv = apr_procattr_create(&procattr, p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't create procattr", rv);
+
+ rv = apr_procattr_io_set(procattr, APR_NO_PIPE, APR_NO_PIPE,
+ APR_NO_PIPE);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set io in procattr", rv);
+
+ rv = apr_procattr_error_check_set(procattr, 1);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set error check in procattr", rv);
+
+ rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM_ENV);
+ APR_ASSERT_SUCCESS(tc, "Couldn't set copy environment", rv);
+
+ args[0] = "sockchild" EXTENSION;
+ args[1] = arg1;
+ args[2] = socket_name;
+ args[3] = NULL;
+ rv = apr_proc_create(proc, TESTBINPATH "sockchild" EXTENSION, args, NULL,
+ procattr, p);
+ APR_ASSERT_SUCCESS(tc, "Couldn't launch program", rv);
+}
+
+static int wait_child(abts_case *tc, apr_proc_t *proc)
+{
+ int exitcode;
+ apr_exit_why_e why;
+
+ ABTS_ASSERT(tc, "Error waiting for child process",
+ apr_proc_wait(proc, &exitcode, &why, APR_WAIT) == APR_CHILD_DONE);
+
+ ABTS_ASSERT(tc, "child terminated normally", why == APR_PROC_EXIT);
+ return exitcode;
+}
+
+static void test_addr_info(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_sockaddr_t *sa;
+ int rc;
+
+ rv = apr_sockaddr_info_get(&sa, NULL, APR_UNSPEC, 80, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+
+ rc = apr_sockaddr_is_wildcard(sa);
+ ABTS_INT_NEQUAL(tc, 0, rc);
+
+ rv = apr_sockaddr_info_get(&sa, "127.0.0.1", APR_UNSPEC, 80, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+ ABTS_STR_EQUAL(tc, "127.0.0.1", sa->hostname);
+
+ rc = apr_sockaddr_is_wildcard(sa);
+ ABTS_INT_EQUAL(tc, 0, rc);
+
+ rv = apr_sockaddr_info_get(&sa, "127.0.0.1", APR_UNSPEC, 0, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+ ABTS_STR_EQUAL(tc, "127.0.0.1", sa->hostname);
+ ABTS_INT_EQUAL(tc, 0, sa->port);
+ ABTS_INT_EQUAL(tc, 0, ntohs(sa->sa.sin.sin_port));
+}
+
+static void test_addr_copy(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_sockaddr_t *sa1, *sa2;
+ int rc;
+ const char *hosts[] = {
+ "127.0.0.1",
+#if APR_HAVE_IPV6
+ "::1",
+#endif
+ NULL
+ }, **host = hosts;
+
+ /* Loop up to and including NULL */
+ do {
+ rv = apr_sockaddr_info_get(&sa1, *host, APR_UNSPEC, 80, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+
+ rv = apr_sockaddr_info_copy(&sa2, sa1, p);
+ APR_ASSERT_SUCCESS(tc, "Problem copying sockaddr", rv);
+
+ ABTS_PTR_NOTNULL(tc, sa1);
+ do {
+ ABTS_PTR_NOTNULL(tc, sa2);
+
+ rc = apr_sockaddr_equal(sa2, sa1);
+ ABTS_INT_NEQUAL(tc, 0, rc);
+ ABTS_INT_EQUAL(tc, 80, sa1->port);
+ ABTS_INT_EQUAL(tc, sa2->port, sa1->port);
+ ABTS_INT_EQUAL(tc, 80, ntohs(sa1->sa.sin.sin_port));
+ ABTS_INT_EQUAL(tc, ntohs(sa2->sa.sin.sin_port), ntohs(sa1->sa.sin.sin_port));
+
+ if (*host) {
+ ABTS_PTR_NOTNULL(tc, sa1->hostname);
+ ABTS_PTR_NOTNULL(tc, sa2->hostname);
+ ABTS_STR_EQUAL(tc, *host, sa1->hostname);
+ ABTS_STR_EQUAL(tc, sa1->hostname, sa2->hostname);
+ ABTS_TRUE(tc, sa1->hostname != sa2->hostname);
+ }
+ else {
+ ABTS_PTR_EQUAL(tc, NULL, sa1->hostname);
+ ABTS_PTR_EQUAL(tc, NULL, sa2->hostname);
+ }
+
+ } while ((sa2 = sa2->next, sa1 = sa1->next));
+ ABTS_PTR_EQUAL(tc, NULL, sa2);
+
+ } while (*host++);
+}
+
+static void test_serv_by_name(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_sockaddr_t *sa;
+
+ rv = apr_sockaddr_info_get(&sa, NULL, APR_UNSPEC, 0, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+
+ rv = apr_getservbyname(sa, "ftp");
+ APR_ASSERT_SUCCESS(tc, "Problem getting ftp service", rv);
+ ABTS_INT_EQUAL(tc, 21, sa->port);
+
+ rv = apr_getservbyname(sa, "complete_and_utter_rubbish");
+ APR_ASSERT_SUCCESS(tc, "Problem getting non-existent service", !rv);
+
+ rv = apr_getservbyname(sa, "telnet");
+ APR_ASSERT_SUCCESS(tc, "Problem getting telnet service", rv);
+ ABTS_INT_EQUAL(tc, 23, sa->port);
+}
+
+static apr_socket_t *setup_socket(abts_case *tc)
+{
+ apr_status_t rv;
+ apr_sockaddr_t *sa;
+ apr_socket_t *sock;
+
+ rv = apr_sockaddr_info_get(&sa, socket_name, socket_type, 8021, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+
+ rv = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, p);
+ APR_ASSERT_SUCCESS(tc, "Problem creating socket", rv);
+
+ rv = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
+ APR_ASSERT_SUCCESS(tc, "Could not set REUSEADDR on socket", rv);
+
+ rv = apr_socket_bind(sock, sa);
+ APR_ASSERT_SUCCESS(tc, "Problem binding to port", rv);
+ if (rv) return NULL;
+
+ rv = apr_socket_listen(sock, 5);
+ APR_ASSERT_SUCCESS(tc, "Problem listening on socket", rv);
+
+ return sock;
+}
+
+static void test_create_bind_listen(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock = setup_socket(tc);
+
+ if (!sock) return;
+
+ rv = apr_socket_close(sock);
+ APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv);
+}
+
+static void test_send(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock;
+ apr_socket_t *sock2;
+ apr_proc_t proc;
+ int protocol;
+ apr_size_t length;
+
+ sock = setup_socket(tc);
+ if (!sock) return;
+
+ launch_child(tc, &proc, "read", p);
+
+ rv = apr_socket_accept(&sock2, sock, p);
+ APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv);
+
+ apr_socket_protocol_get(sock2, &protocol);
+ ABTS_INT_EQUAL(tc, APR_PROTO_TCP, protocol);
+
+ length = strlen(DATASTR);
+ apr_socket_send(sock2, DATASTR, &length);
+
+ /* Make sure that the client received the data we sent */
+ ABTS_SIZE_EQUAL(tc, strlen(DATASTR), wait_child(tc, &proc));
+
+ rv = apr_socket_close(sock2);
+ APR_ASSERT_SUCCESS(tc, "Problem closing connected socket", rv);
+ rv = apr_socket_close(sock);
+ APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv);
+}
+
+static void test_recv(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock;
+ apr_socket_t *sock2;
+ apr_proc_t proc;
+ int protocol;
+ apr_size_t length = STRLEN;
+ char datastr[STRLEN];
+
+ sock = setup_socket(tc);
+ if (!sock) return;
+
+ launch_child(tc, &proc, "write", p);
+
+ rv = apr_socket_accept(&sock2, sock, p);
+ APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv);
+
+ apr_socket_protocol_get(sock2, &protocol);
+ ABTS_INT_EQUAL(tc, APR_PROTO_TCP, protocol);
+
+ memset(datastr, 0, STRLEN);
+ apr_socket_recv(sock2, datastr, &length);
+
+ /* Make sure that the server received the data we sent */
+ ABTS_STR_EQUAL(tc, DATASTR, datastr);
+ ABTS_SIZE_EQUAL(tc, strlen(datastr), wait_child(tc, &proc));
+
+ rv = apr_socket_close(sock2);
+ APR_ASSERT_SUCCESS(tc, "Problem closing connected socket", rv);
+ rv = apr_socket_close(sock);
+ APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv);
+}
+
+static void test_atreadeof(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock;
+ apr_socket_t *sock2;
+ apr_proc_t proc;
+ apr_size_t length = STRLEN;
+ char datastr[STRLEN];
+ int atreadeof = -1;
+
+ sock = setup_socket(tc);
+ if (!sock) return;
+
+ launch_child(tc, &proc, "write", p);
+
+ rv = apr_socket_accept(&sock2, sock, p);
+ APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv);
+
+ /* Check that the remote socket is still open */
+ rv = apr_socket_atreadeof(sock2, &atreadeof);
+ APR_ASSERT_SUCCESS(tc, "Determine whether at EOF, #1", rv);
+ ABTS_INT_EQUAL(tc, 0, atreadeof);
+
+ memset(datastr, 0, STRLEN);
+ apr_socket_recv(sock2, datastr, &length);
+
+ /* Make sure that the server received the data we sent */
+ ABTS_STR_EQUAL(tc, DATASTR, datastr);
+ ABTS_SIZE_EQUAL(tc, strlen(datastr), wait_child(tc, &proc));
+
+ /* The child is dead, so should be the remote socket */
+ rv = apr_socket_atreadeof(sock2, &atreadeof);
+ APR_ASSERT_SUCCESS(tc, "Determine whether at EOF, #2", rv);
+ ABTS_INT_EQUAL(tc, 1, atreadeof);
+
+ rv = apr_socket_close(sock2);
+ APR_ASSERT_SUCCESS(tc, "Problem closing connected socket", rv);
+
+ launch_child(tc, &proc, "close", p);
+
+ rv = apr_socket_accept(&sock2, sock, p);
+ APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv);
+
+ /* The child closed the socket as soon as it could... */
+ rv = apr_socket_atreadeof(sock2, &atreadeof);
+ APR_ASSERT_SUCCESS(tc, "Determine whether at EOF, #3", rv);
+ if (!atreadeof) { /* ... but perhaps not yet; wait a moment */
+ apr_sleep(apr_time_from_msec(5));
+ rv = apr_socket_atreadeof(sock2, &atreadeof);
+ APR_ASSERT_SUCCESS(tc, "Determine whether at EOF, #4", rv);
+ }
+ ABTS_INT_EQUAL(tc, 1, atreadeof);
+ wait_child(tc, &proc);
+
+ rv = apr_socket_close(sock2);
+ APR_ASSERT_SUCCESS(tc, "Problem closing connected socket", rv);
+
+ rv = apr_socket_close(sock);
+ APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv);
+}
+
+static void test_timeout(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock;
+ apr_socket_t *sock2;
+ apr_proc_t proc;
+ int protocol;
+ int exit;
+
+ sock = setup_socket(tc);
+ if (!sock) return;
+
+ launch_child(tc, &proc, "read", p);
+
+ rv = apr_socket_accept(&sock2, sock, p);
+ APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv);
+
+ apr_socket_protocol_get(sock2, &protocol);
+ ABTS_INT_EQUAL(tc, APR_PROTO_TCP, protocol);
+
+ exit = wait_child(tc, &proc);
+ ABTS_INT_EQUAL(tc, SOCKET_TIMEOUT, exit);
+
+ /* We didn't write any data, so make sure the child program returns
+ * an error.
+ */
+ rv = apr_socket_close(sock2);
+ APR_ASSERT_SUCCESS(tc, "Problem closing connected socket", rv);
+ rv = apr_socket_close(sock);
+ APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv);
+}
+
+static void test_print_addr(abts_case *tc, void *data)
+{
+ apr_sockaddr_t *sa;
+ apr_status_t rv;
+ char *s;
+
+ rv = apr_sockaddr_info_get(&sa, "0.0.0.0", APR_INET, 80, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+
+ s = apr_psprintf(p, "foo %pI bar", sa);
+
+ ABTS_STR_EQUAL(tc, "foo 0.0.0.0:80 bar", s);
+
+#if APR_HAVE_IPV6
+ rv = apr_sockaddr_info_get(&sa, "::ffff:0.0.0.0", APR_INET6, 80, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+ if (rv == APR_SUCCESS)
+ ABTS_TRUE(tc, sa != NULL);
+ if (rv == APR_SUCCESS && sa) {
+ /* sa should now be a v4-mapped IPv6 address. */
+ char buf[128];
+ int rc;
+
+ rc = apr_sockaddr_is_wildcard(sa);
+ ABTS_INT_NEQUAL(tc, 0, rc);
+
+ memset(buf, 'z', sizeof buf);
+
+ APR_ASSERT_SUCCESS(tc, "could not get IP address",
+ apr_sockaddr_ip_getbuf(buf, 22, sa));
+
+ ABTS_STR_EQUAL(tc, "0.0.0.0", buf);
+ }
+#endif
+}
+
+static void test_get_addr(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *ld, *sd, *cd;
+ apr_sockaddr_t *sa, *ca;
+ apr_pool_t *subp;
+ char *a, *b;
+
+ APR_ASSERT_SUCCESS(tc, "create subpool", apr_pool_create(&subp, p));
+
+ ld = setup_socket(tc);
+ if (!ld) return;
+
+ APR_ASSERT_SUCCESS(tc,
+ "get local address of bound socket",
+ apr_socket_addr_get(&sa, APR_LOCAL, ld));
+
+ rv = apr_socket_create(&cd, sa->family, SOCK_STREAM,
+ APR_PROTO_TCP, subp);
+ APR_ASSERT_SUCCESS(tc, "create client socket", rv);
+
+ APR_ASSERT_SUCCESS(tc, "enable non-block mode",
+ apr_socket_opt_set(cd, APR_SO_NONBLOCK, 1));
+
+ /* It is valid for a connect() on a socket with NONBLOCK set to
+ * succeed (if the connection can be established synchronously),
+ * but if it does, this test cannot proceed. */
+ rv = apr_socket_connect(cd, sa);
+ if (rv == APR_SUCCESS) {
+ apr_socket_close(ld);
+ apr_socket_close(cd);
+ ABTS_NOT_IMPL(tc, "Cannot test if connect completes "
+ "synchronously");
+ return;
+ }
+
+ if (!APR_STATUS_IS_EINPROGRESS(rv)) {
+ apr_socket_close(ld);
+ apr_socket_close(cd);
+ APR_ASSERT_SUCCESS(tc, "connect to listener", rv);
+ return;
+ }
+
+ APR_ASSERT_SUCCESS(tc, "accept connection",
+ apr_socket_accept(&sd, ld, subp));
+
+ {
+ /* wait for writability */
+ apr_pollfd_t pfd;
+ int n;
+
+ pfd.p = p;
+ pfd.desc_type = APR_POLL_SOCKET;
+ pfd.reqevents = APR_POLLOUT|APR_POLLHUP;
+ pfd.desc.s = cd;
+ pfd.client_data = NULL;
+
+ APR_ASSERT_SUCCESS(tc, "poll for connect completion",
+ apr_poll(&pfd, 1, &n, 5 * APR_USEC_PER_SEC));
+
+ }
+
+ APR_ASSERT_SUCCESS(tc, "get local address of server socket",
+ apr_socket_addr_get(&sa, APR_LOCAL, sd));
+ APR_ASSERT_SUCCESS(tc, "get remote address of client socket",
+ apr_socket_addr_get(&ca, APR_REMOTE, cd));
+
+ /* Test that the pool of the returned sockaddr objects exactly
+ * match the socket. */
+ ABTS_PTR_EQUAL(tc, subp, sa->pool);
+ ABTS_PTR_EQUAL(tc, subp, ca->pool);
+
+ /* Check equivalence. */
+ a = apr_psprintf(p, "%pI fam=%d", sa, sa->family);
+ b = apr_psprintf(p, "%pI fam=%d", ca, ca->family);
+ ABTS_STR_EQUAL(tc, a, b);
+
+ /* Check pool of returned sockaddr, as above. */
+ APR_ASSERT_SUCCESS(tc, "get local address of client socket",
+ apr_socket_addr_get(&sa, APR_LOCAL, cd));
+ APR_ASSERT_SUCCESS(tc, "get remote address of server socket",
+ apr_socket_addr_get(&ca, APR_REMOTE, sd));
+
+ /* Check equivalence. */
+ a = apr_psprintf(p, "%pI fam=%d", sa, sa->family);
+ b = apr_psprintf(p, "%pI fam=%d", ca, ca->family);
+ ABTS_STR_EQUAL(tc, a, b);
+
+ ABTS_PTR_EQUAL(tc, subp, sa->pool);
+ ABTS_PTR_EQUAL(tc, subp, ca->pool);
+
+ apr_socket_close(cd);
+ apr_socket_close(sd);
+ apr_socket_close(ld);
+
+ apr_pool_destroy(subp);
+}
+
+/* Make sure that setting a connected socket non-blocking works
+ * when the listening socket was non-blocking.
+ * If APR thinks that non-blocking is inherited but it really
+ * isn't, this testcase will fail.
+ */
+static void test_nonblock_inheritance(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock;
+ apr_socket_t *sock2;
+ apr_proc_t proc;
+ char buffer[10];
+ apr_size_t length;
+ int tries;
+
+ sock = setup_socket(tc);
+ if (!sock) return;
+
+ rv = apr_socket_opt_set(sock, APR_SO_NONBLOCK, 1);
+ APR_ASSERT_SUCCESS(tc, "Could not make listening socket nonblocking", rv);
+
+ launch_child(tc, &proc, "write_after_delay", p);
+
+ tries = 10;
+ while (tries--) {
+ rv = apr_socket_accept(&sock2, sock, p);
+ if (!APR_STATUS_IS_EAGAIN(rv)) {
+ break;
+ }
+ apr_sleep(apr_time_from_msec(50));
+ }
+ APR_ASSERT_SUCCESS(tc, "Problem with receiving connection", rv);
+
+ rv = apr_socket_opt_set(sock2, APR_SO_NONBLOCK, 1);
+ APR_ASSERT_SUCCESS(tc, "Could not make connected socket nonblocking", rv);
+
+ length = sizeof buffer;
+ rv = apr_socket_recv(sock2, buffer, &length);
+ ABTS_ASSERT(tc, "should have gotten EAGAIN", APR_STATUS_IS_EAGAIN(rv));
+
+ wait_child(tc, &proc);
+
+ rv = apr_socket_close(sock2);
+ APR_ASSERT_SUCCESS(tc, "Problem closing connected socket", rv);
+ rv = apr_socket_close(sock);
+ APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv);
+}
+
+static void test_freebind(abts_case *tc, void *data)
+{
+#ifdef IP_FREEBIND
+ apr_status_t rv;
+ apr_socket_t *sock;
+ apr_sockaddr_t *sa;
+ apr_int32_t on;
+
+ /* RFC 5737 address */
+ rv = apr_sockaddr_info_get(&sa, "192.0.2.1", APR_INET, 8080, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+
+ rv = apr_socket_create(&sock, sa->family, SOCK_STREAM, APR_PROTO_TCP, p);
+ APR_ASSERT_SUCCESS(tc, "Problem creating socket", rv);
+
+ rv = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
+ APR_ASSERT_SUCCESS(tc, "Could not set REUSEADDR on socket", rv);
+
+ rv = apr_socket_opt_set(sock, APR_SO_FREEBIND, 1);
+ APR_ASSERT_SUCCESS(tc, "Could not enable FREEBIND option", rv);
+
+ rv = apr_socket_opt_get(sock, APR_SO_FREEBIND, &on);
+ APR_ASSERT_SUCCESS(tc, "Could not retrieve FREEBIND option", rv);
+ ABTS_INT_EQUAL(tc, 1, on);
+
+ rv = apr_socket_bind(sock, sa);
+ APR_ASSERT_SUCCESS(tc, "Problem binding to port with FREEBIND", rv);
+
+ rv = apr_socket_close(sock);
+ APR_ASSERT_SUCCESS(tc, "Problem closing socket", rv);
+#endif
+}
+
+#define TEST_ZONE_ADDR "fe80::1"
+
+#ifdef __linux__
+/* Reasonable bet that "lo" will exist. */
+#define TEST_ZONE_NAME "lo"
+/* ... fill in other platforms here */
+#endif
+
+#ifdef TEST_ZONE_NAME
+#define TEST_ZONE_FULLADDR TEST_ZONE_ADDR "%" TEST_ZONE_NAME
+#endif
+
+static void test_zone(abts_case *tc, void *data)
+{
+#if APR_HAVE_IPV6
+ apr_sockaddr_t *sa;
+ apr_status_t rv;
+ const char *name = NULL;
+ apr_uint32_t id = 0;
+
+ rv = apr_sockaddr_info_get(&sa, "127.0.0.1", APR_INET, 8080, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+
+ /* Fail for an IPv4 address! */
+ ABTS_INT_EQUAL(tc, APR_EBADIP,
+ apr_sockaddr_zone_set(sa, "1"));
+ ABTS_INT_EQUAL(tc, APR_EBADIP,
+ apr_sockaddr_zone_get(sa, &name, &id, p));
+
+ rv = apr_sockaddr_info_get(&sa, "::1", APR_INET6, 8080, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+
+ /* Fail for an address which isn't link-local */
+ ABTS_INT_EQUAL(tc, APR_EBADIP, apr_sockaddr_zone_set(sa, "1"));
+
+ rv = apr_sockaddr_info_get(&sa, TEST_ZONE_ADDR, APR_INET6, 8080, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+
+ ABTS_INT_EQUAL(tc, APR_EBADIP, apr_sockaddr_zone_get(sa, &name, &id, p));
+
+#ifdef TEST_ZONE_NAME
+ {
+ apr_sockaddr_t *sa2;
+ char buf[50];
+
+ APR_ASSERT_SUCCESS(tc, "Set zone to " TEST_ZONE_NAME,
+ apr_sockaddr_zone_set(sa, TEST_ZONE_NAME));
+
+ APR_ASSERT_SUCCESS(tc, "Get zone",
+ apr_sockaddr_zone_get(sa, NULL, NULL, p));
+
+ APR_ASSERT_SUCCESS(tc, "Get zone",
+ apr_sockaddr_zone_get(sa, &name, &id, p));
+ ABTS_STR_EQUAL(tc, TEST_ZONE_NAME, name);
+ ABTS_INT_NEQUAL(tc, 0, id); /* Only guarantee is that it should be non-zero */
+
+ /* Check string translation. */
+ APR_ASSERT_SUCCESS(tc, "get IP address",
+ apr_sockaddr_ip_getbuf(buf, 50, sa));
+ ABTS_STR_EQUAL(tc, TEST_ZONE_FULLADDR, buf);
+
+ memset(buf, 'A', sizeof buf);
+ ABTS_INT_EQUAL(tc, APR_ENOSPC, apr_sockaddr_ip_getbuf(buf, strlen(TEST_ZONE_ADDR), sa));
+ ABTS_INT_EQUAL(tc, APR_ENOSPC, apr_sockaddr_ip_getbuf(buf, strlen(TEST_ZONE_FULLADDR), sa));
+
+ APR_ASSERT_SUCCESS(tc, "get IP address",
+ apr_sockaddr_ip_getbuf(buf, strlen(TEST_ZONE_FULLADDR) + 1, sa));
+ /* Check for overflow. */
+ ABTS_INT_EQUAL(tc, 'A', buf[strlen(buf) + 1]);
+
+ rv = apr_sockaddr_info_copy(&sa2, sa, p);
+ APR_ASSERT_SUCCESS(tc, "Problem copying sockaddr", rv);
+
+ /* Copy copied zone matches */
+ APR_ASSERT_SUCCESS(tc, "Get zone",
+ apr_sockaddr_zone_get(sa2, &name, &id, p));
+ ABTS_STR_EQUAL(tc, TEST_ZONE_NAME, name);
+ ABTS_INT_NEQUAL(tc, 0, id); /* Only guarantee is that it should be non-zero */
+
+ /* Should match self and copy */
+ ABTS_INT_NEQUAL(tc, 0, apr_sockaddr_equal(sa, sa));
+ ABTS_INT_NEQUAL(tc, 0, apr_sockaddr_equal(sa2, sa2));
+ ABTS_INT_NEQUAL(tc, 0, apr_sockaddr_equal(sa2, sa));
+
+ /* Should not match against copy without zone set. */
+ rv = apr_sockaddr_info_get(&sa2, TEST_ZONE_ADDR, APR_INET6, 8080, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Problem generating sockaddr", rv);
+
+ ABTS_INT_EQUAL(tc, 0, apr_sockaddr_equal(sa2, sa));
+ }
+#endif /* TEST_ZONE_NAME */
+#endif /* APR_HAVE_IPV6 */
+}
+
+abts_suite *testsock(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+ socket_name = IPV4_SOCKET_NAME;
+ abts_run_test(suite, test_addr_info, NULL);
+ abts_run_test(suite, test_addr_copy, NULL);
+ abts_run_test(suite, test_serv_by_name, NULL);
+ abts_run_test(suite, test_create_bind_listen, NULL);
+ abts_run_test(suite, test_send, NULL);
+ abts_run_test(suite, test_recv, NULL);
+ abts_run_test(suite, test_atreadeof, NULL);
+ abts_run_test(suite, test_timeout, NULL);
+ abts_run_test(suite, test_print_addr, NULL);
+ abts_run_test(suite, test_get_addr, NULL);
+ abts_run_test(suite, test_nonblock_inheritance, NULL);
+ abts_run_test(suite, test_freebind, NULL);
+ abts_run_test(suite, test_zone, NULL);
+
+#if APR_HAVE_SOCKADDR_UN
+ socket_name = UNIX_SOCKET_NAME;
+ socket_type = APR_UNIX;
+ abts_run_test(suite, test_create_bind_listen, NULL);
+ abts_run_test(suite, test_send, NULL);
+ abts_run_test(suite, test_recv, NULL);
+ abts_run_test(suite, test_timeout, NULL);
+#endif
+ return suite;
+}
+
diff --git a/test/testsock.h b/test/testsock.h
new file mode 100644
index 0000000..12a44f7
--- /dev/null
+++ b/test/testsock.h
@@ -0,0 +1,34 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TESTSOCK_H
+#define TESTSOCK_H
+
+#define DATASTR "This is a test"
+#define STRLEN 8092
+
+/* This is a hack. We can't return APR_TIMEOUT from sockchild, because
+ * Unix OSes only return the least significant 8 bits of the return code,
+ * which means that instead of receiving 70007, testsock gets 119. But,
+ * we also don't want to return -1, because we use that value for general
+ * errors from sockchild. So, we define 1 to mean that the read/write
+ * operation timed out. This means that we can't write a test that tries
+ * to send a single character between ends of the socket.
+ */
+#define SOCKET_TIMEOUT 1
+
+#endif
+
diff --git a/test/testsockets.c b/test/testsockets.c
new file mode 100644
index 0000000..2a7499a
--- /dev/null
+++ b/test/testsockets.c
@@ -0,0 +1,238 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_network_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "testutil.h"
+
+#define STRLEN 21
+
+static void tcp_socket(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock = NULL;
+ int type;
+
+ rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, sock);
+
+ rv = apr_socket_type_get(sock, &type);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, SOCK_STREAM, type);
+
+ apr_socket_close(sock);
+}
+
+static void udp_socket(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock = NULL;
+ int type;
+
+ rv = apr_socket_create(&sock, APR_INET, SOCK_DGRAM, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, sock);
+
+ rv = apr_socket_type_get(sock, &type);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, SOCK_DGRAM, type);
+
+ apr_socket_close(sock);
+}
+
+#if APR_HAVE_IPV6
+static void tcp6_socket(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock = NULL;
+
+ rv = apr_socket_create(&sock, APR_INET6, SOCK_STREAM, 0, p);
+ if (APR_STATUS_IS_EAFNOSUPPORT(rv)) {
+ ABTS_NOT_IMPL(tc, "IPv6 not enabled");
+ return;
+ }
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, sock);
+ apr_socket_close(sock);
+}
+
+static void udp6_socket(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_socket_t *sock = NULL;
+
+ rv = apr_socket_create(&sock, APR_INET6, SOCK_DGRAM, 0, p);
+ if (APR_STATUS_IS_EAFNOSUPPORT(rv)) {
+ ABTS_NOT_IMPL(tc, "IPv6 not enabled");
+ return;
+ }
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, sock);
+ apr_socket_close(sock);
+}
+#endif
+
+static void sendto_receivefrom_helper(abts_case *tc, const char *addr,
+ int family)
+{
+ apr_status_t rv;
+ apr_socket_t *sock = NULL;
+ apr_socket_t *sock2 = NULL;
+ char sendbuf[STRLEN] = "APR_INET, SOCK_DGRAM";
+ char recvbuf[80];
+ char *ip_addr;
+ apr_port_t fromport;
+ apr_sockaddr_t *from;
+ apr_sockaddr_t *to;
+ apr_size_t len = 30;
+
+ rv = apr_socket_create(&sock, family, SOCK_DGRAM, 0, p);
+#if APR_HAVE_IPV6
+ if ((family == APR_INET6) && APR_STATUS_IS_EAFNOSUPPORT(rv)) {
+ ABTS_NOT_IMPL(tc, "IPv6 not enabled");
+ return;
+ }
+#endif
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ if (rv != APR_SUCCESS)
+ return;
+ rv = apr_socket_create(&sock2, family, SOCK_DGRAM, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ if (rv != APR_SUCCESS)
+ return;
+
+ rv = apr_sockaddr_info_get(&to, addr, family, 7772, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_sockaddr_info_get(&from, addr, family, 7771, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_socket_opt_set(sock, APR_SO_REUSEADDR, 1);
+ APR_ASSERT_SUCCESS(tc, "Could not set REUSEADDR on socket", rv);
+ rv = apr_socket_opt_set(sock2, APR_SO_REUSEADDR, 1);
+ APR_ASSERT_SUCCESS(tc, "Could not set REUSEADDR on socket2", rv);
+
+ rv = apr_socket_bind(sock, to);
+ APR_ASSERT_SUCCESS(tc, "Could not bind socket", rv);
+ if (rv != APR_SUCCESS)
+ return;
+ rv = apr_mcast_hops(sock, 10);
+ APR_ASSERT_SUCCESS(tc, "Could not set multicast hops", rv);
+ if (rv != APR_SUCCESS)
+ return;
+
+ rv = apr_socket_bind(sock2, from);
+ APR_ASSERT_SUCCESS(tc, "Could not bind second socket", rv);
+ if (rv != APR_SUCCESS)
+ return;
+
+ len = STRLEN;
+ rv = apr_socket_sendto(sock2, to, 0, sendbuf, &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, STRLEN, len);
+
+ /* fill the "from" sockaddr with a random address from another
+ * family to ensure that recvfrom sets it up properly. */
+#if APR_HAVE_IPV6
+ if (family == APR_INET)
+ rv = apr_sockaddr_info_get(&from, "3ffE:816e:abcd:1234::1",
+ APR_INET6, 4242, 0, p);
+ else
+#endif
+ rv = apr_sockaddr_info_get(&from, "127.1.2.3", APR_INET, 4242, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ len = 80;
+ rv = apr_socket_recvfrom(from, sock, 0, recvbuf, &len);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_SIZE_EQUAL(tc, STRLEN, len);
+ ABTS_STR_EQUAL(tc, "APR_INET, SOCK_DGRAM", recvbuf);
+
+ apr_sockaddr_ip_get(&ip_addr, from);
+ fromport = from->port;
+ ABTS_STR_EQUAL(tc, addr, ip_addr);
+ ABTS_INT_EQUAL(tc, 7771, fromport);
+
+ apr_socket_close(sock);
+ apr_socket_close(sock2);
+}
+
+static void sendto_receivefrom(abts_case *tc, void *data)
+{
+ int failed;
+ sendto_receivefrom_helper(tc, "127.0.0.1", APR_INET);
+ failed = tc->failed; tc->failed = 0;
+ ABTS_TRUE(tc, !failed);
+}
+
+#if APR_HAVE_IPV6
+static void sendto_receivefrom6(abts_case *tc, void *data)
+{
+ int failed;
+ sendto_receivefrom_helper(tc, "::1", APR_INET6);
+ failed = tc->failed; tc->failed = 0;
+ ABTS_TRUE(tc, !failed);
+}
+#endif
+
+static void socket_userdata(abts_case *tc, void *data)
+{
+ apr_socket_t *sock1, *sock2;
+ apr_status_t rv;
+ void *user;
+ const char *key = "GENERICKEY";
+
+ rv = apr_socket_create(&sock1, AF_INET, SOCK_STREAM, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_socket_create(&sock2, AF_INET, SOCK_STREAM, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_socket_data_set(sock1, "SOCK1", key, NULL);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_socket_data_set(sock2, "SOCK2", key, NULL);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_socket_data_get(&user, key, sock1);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "SOCK1", user);
+ rv = apr_socket_data_get(&user, key, sock2);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, "SOCK2", user);
+}
+
+abts_suite *testsockets(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, tcp_socket, NULL);
+ abts_run_test(suite, udp_socket, NULL);
+
+ abts_run_test(suite, sendto_receivefrom, NULL);
+
+#if APR_HAVE_IPV6
+ abts_run_test(suite, tcp6_socket, NULL);
+ abts_run_test(suite, udp6_socket, NULL);
+
+ abts_run_test(suite, sendto_receivefrom6, NULL);
+#endif
+
+ abts_run_test(suite, socket_userdata, NULL);
+
+ return suite;
+}
+
diff --git a/test/testsockopt.c b/test/testsockopt.c
new file mode 100644
index 0000000..203e2c3
--- /dev/null
+++ b/test/testsockopt.c
@@ -0,0 +1,139 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_network_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "testutil.h"
+
+static apr_socket_t *sock = NULL;
+
+static void create_socket(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, 0, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_NOTNULL(tc, sock);
+}
+
+static void set_keepalive(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_int32_t ck;
+
+ rv = apr_socket_opt_set(sock, APR_SO_KEEPALIVE, 1);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_socket_opt_get(sock, APR_SO_KEEPALIVE, &ck);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 1, ck);
+}
+
+static void set_debug(abts_case *tc, void *data)
+{
+ apr_status_t rv1, rv2;
+ apr_int32_t ck;
+
+ /* On some platforms APR_SO_DEBUG can only be set as root; just test
+ * for get/set consistency of this option. */
+ rv1 = apr_socket_opt_set(sock, APR_SO_DEBUG, 1);
+ rv2 = apr_socket_opt_get(sock, APR_SO_DEBUG, &ck);
+ APR_ASSERT_SUCCESS(tc, "get SO_DEBUG option", rv2);
+ if (rv1 == APR_SUCCESS) {
+ ABTS_INT_EQUAL(tc, 1, ck);
+ } else {
+ ABTS_INT_EQUAL(tc, 0, ck);
+ }
+}
+
+static void remove_keepalive(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_int32_t ck;
+
+ rv = apr_socket_opt_get(sock, APR_SO_KEEPALIVE, &ck);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 1, ck);
+
+ rv = apr_socket_opt_set(sock, APR_SO_KEEPALIVE, 0);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_socket_opt_get(sock, APR_SO_KEEPALIVE, &ck);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 0, ck);
+}
+
+static void corkable(abts_case *tc, void *data)
+{
+#if !APR_HAVE_CORKABLE_TCP
+ ABTS_NOT_IMPL(tc, "TCP isn't corkable");
+#else
+ apr_status_t rv;
+ apr_int32_t ck;
+
+ rv = apr_socket_opt_set(sock, APR_TCP_NODELAY, 1);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_socket_opt_get(sock, APR_TCP_NODELAY, &ck);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 1, ck);
+
+ rv = apr_socket_opt_set(sock, APR_TCP_NOPUSH, 1);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_socket_opt_get(sock, APR_TCP_NOPUSH, &ck);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 1, ck);
+
+ rv = apr_socket_opt_get(sock, APR_TCP_NODELAY, &ck);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ /* TCP_NODELAY is now in an unknown state; it may be zero if
+ * TCP_NOPUSH and TCP_NODELAY are mutually exclusive on this
+ * platform, e.g. Linux < 2.6. */
+
+ rv = apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_socket_opt_get(sock, APR_TCP_NODELAY, &ck);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_INT_EQUAL(tc, 1, ck);
+#endif
+}
+
+static void close_socket(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_socket_close(sock);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+abts_suite *testsockopt(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, create_socket, NULL);
+ abts_run_test(suite, set_keepalive, NULL);
+ abts_run_test(suite, set_debug, NULL);
+ abts_run_test(suite, remove_keepalive, NULL);
+ abts_run_test(suite, corkable, NULL);
+ abts_run_test(suite, close_socket, NULL);
+
+ return suite;
+}
+
diff --git a/test/teststr.c b/test/teststr.c
new file mode 100644
index 0000000..432fb6b
--- /dev/null
+++ b/test/teststr.c
@@ -0,0 +1,433 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if APR_HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "apr_general.h"
+#include "apr_strings.h"
+#include "apr_cstr.h"
+#include "apr_errno.h"
+
+/* I haven't bothered to check for APR_ENOTIMPL here, AFAIK, all string
+ * functions exist on all platforms.
+ */
+
+static void test_strtok(abts_case *tc, void *data)
+{
+ struct {
+ char *input;
+ char *sep;
+ }
+ cases[] = {
+ {
+ "",
+ "Z"
+ },
+ {
+ " asdf jkl; 77889909 \r\n\1\2\3Z",
+ " \r\n\3\2\1"
+ },
+ {
+ NULL, /* but who cares if apr_strtok() segfaults? */
+ " \t"
+ },
+#if 0 /* don't do this... you deserve to segfault */
+ {
+ "a b c ",
+ NULL
+ },
+#endif
+ {
+ " a b c ",
+ ""
+ },
+ {
+ "a b c ",
+ " "
+ }
+ };
+ int curtc;
+
+ for (curtc = 0; curtc < sizeof cases / sizeof cases[0]; curtc++) {
+ char *retval1, *retval2;
+ char *str1, *str2;
+ char *state;
+
+ str1 = apr_pstrdup(p, cases[curtc].input);
+ str2 = apr_pstrdup(p, cases[curtc].input);
+
+ do {
+ retval1 = apr_strtok(str1, cases[curtc].sep, &state);
+ retval2 = strtok(str2, cases[curtc].sep);
+
+ if (!retval1) {
+ ABTS_TRUE(tc, retval2 == NULL);
+ }
+ else {
+ ABTS_TRUE(tc, retval2 != NULL);
+ ABTS_STR_EQUAL(tc, retval2, retval1);
+ }
+
+ str1 = str2 = NULL; /* make sure we pass NULL on subsequent calls */
+ } while (retval1);
+ }
+}
+
+static void snprintf_noNULL(abts_case *tc, void *data)
+{
+ char buff[100];
+ char *testing = apr_palloc(p, 10);
+
+ testing[0] = 't';
+ testing[1] = 'e';
+ testing[2] = 's';
+ testing[3] = 't';
+ testing[4] = 'i';
+ testing[5] = 'n';
+ testing[6] = 'g';
+
+ /* If this test fails, we are going to seg fault. */
+ apr_snprintf(buff, sizeof(buff), "%.*s", 7, testing);
+ ABTS_STR_NEQUAL(tc, buff, testing, 7);
+}
+
+static void snprintf_0NULL(abts_case *tc, void *data)
+{
+ int rv;
+
+ rv = apr_snprintf(NULL, 0, "%sBAR", "FOO");
+ ABTS_INT_EQUAL(tc, 6, rv);
+}
+
+static void snprintf_0nonNULL(abts_case *tc, void *data)
+{
+ int rv;
+ char *buff = "testing";
+
+ rv = apr_snprintf(buff, 0, "%sBAR", "FOO");
+ ABTS_INT_EQUAL(tc, 6, rv);
+ ABTS_ASSERT(tc, "buff unmangled", strcmp(buff, "FOOBAR") != 0);
+}
+
+static void snprintf_underflow(abts_case *tc, void *data)
+{
+ char buf[20];
+ int rv;
+
+ rv = apr_snprintf(buf, sizeof buf, "%.2f", (double)0.0001);
+ ABTS_INT_EQUAL(tc, 4, rv);
+ ABTS_STR_EQUAL(tc, "0.00", buf);
+
+ rv = apr_snprintf(buf, sizeof buf, "%.2f", (double)0.001);
+ ABTS_INT_EQUAL(tc, 4, rv);
+ ABTS_STR_EQUAL(tc, "0.00", buf);
+
+ rv = apr_snprintf(buf, sizeof buf, "%.2f", (double)0.01);
+ ABTS_INT_EQUAL(tc, 4, rv);
+ ABTS_STR_EQUAL(tc, "0.01", buf);
+}
+
+static void string_error(abts_case *tc, void *data)
+{
+ char buf[128], *rv;
+ apr_status_t n;
+
+ buf[0] = '\0';
+ rv = apr_strerror(APR_ENOENT, buf, sizeof buf);
+ ABTS_PTR_EQUAL(tc, buf, rv);
+ ABTS_TRUE(tc, strlen(buf) > 0);
+
+ rv = apr_strerror(APR_TIMEUP, buf, sizeof buf);
+ ABTS_PTR_EQUAL(tc, buf, rv);
+ ABTS_STR_EQUAL(tc, "The timeout specified has expired", buf);
+
+ /* throw some randomish numbers at it to check for robustness */
+ for (n = 1; n < 1000000; n *= 2) {
+ apr_strerror(n, buf, sizeof buf);
+ }
+}
+
+#define SIZE 180000
+static void string_long(abts_case *tc, void *data)
+{
+ char s[SIZE + 1];
+
+ memset(s, 'A', SIZE);
+ s[SIZE] = '\0';
+
+ apr_psprintf(p, "%s", s);
+}
+
+/* ### FIXME: apr.h/apr_strings.h should provide these! */
+#define MY_LLONG_MAX (APR_INT64_C(9223372036854775807))
+#define MY_LLONG_MIN (-MY_LLONG_MAX - APR_INT64_C(1))
+
+static void string_strtoi64(abts_case *tc, void *data)
+{
+ static const struct {
+ int errnum, base;
+ const char *in, *end;
+ apr_int64_t result;
+ } ts[] = {
+
+ /* base 10 tests */
+ { 0, 10, "123545", NULL, APR_INT64_C(123545) },
+ { 0, 10, " 123545", NULL, APR_INT64_C(123545) },
+ { 0, 10, " +123545", NULL, APR_INT64_C(123545) },
+ { 0, 10, "-123545", NULL, APR_INT64_C(-123545) },
+ { 0, 10, " 00000123545", NULL, APR_INT64_C(123545) },
+ { 0, 10, "123545ZZZ", "ZZZ", APR_INT64_C(123545) },
+ { 0, 10, " 123545 ", " ", APR_INT64_C(123545) },
+
+ /* base 16 tests */
+ { 0, 16, "1E299", NULL, APR_INT64_C(123545) },
+ { 0, 16, "1e299", NULL, APR_INT64_C(123545) },
+ { 0, 16, "0x1e299", NULL, APR_INT64_C(123545) },
+ { 0, 16, "0X1E299", NULL, APR_INT64_C(123545) },
+ { 0, 16, "+1e299", NULL, APR_INT64_C(123545) },
+ { 0, 16, "-1e299", NULL, APR_INT64_C(-123545) },
+ { 0, 16, " -1e299", NULL, APR_INT64_C(-123545) },
+
+ /* automatic base detection tests */
+ { 0, 0, "123545", NULL, APR_INT64_C(123545) },
+ { 0, 0, "0x1e299", NULL, APR_INT64_C(123545) },
+ { 0, 0, " 0x1e299", NULL, APR_INT64_C(123545) },
+ { 0, 0, "+0x1e299", NULL, APR_INT64_C(123545) },
+ { 0, 0, "-0x1e299", NULL, APR_INT64_C(-123545) },
+
+ /* large number tests */
+ { 0, 10, "8589934605", NULL, APR_INT64_C(8589934605) },
+ { 0, 10, "-8589934605", NULL, APR_INT64_C(-8589934605) },
+ { 0, 16, "0x20000000D", NULL, APR_INT64_C(8589934605) },
+ { 0, 16, "-0x20000000D", NULL, APR_INT64_C(-8589934605) },
+ { 0, 16, " 0x20000000D", NULL, APR_INT64_C(8589934605) },
+ { 0, 16, " 0x20000000D", NULL, APR_INT64_C(8589934605) },
+
+ /* error cases */
+ { ERANGE, 10, "999999999999999999999999999999999", "", MY_LLONG_MAX },
+ { ERANGE, 10, "-999999999999999999999999999999999", "", MY_LLONG_MIN },
+
+#if 0
+ /* C99 doesn't require EINVAL for an invalid range. */
+ { EINVAL, 99, "", (void *)-1 /* don't care */, 0 },
+#endif
+
+ /* some strtoll implementations give EINVAL when no conversion
+ * is performed. */
+ { -1 /* don't care */, 10, "zzz", "zzz", APR_INT64_C(0) },
+ { -1 /* don't care */, 10, "", NULL, APR_INT64_C(0) }
+
+ };
+ int n;
+
+ for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) {
+ char *end = "end ptr not changed";
+ apr_int64_t result;
+ int errnum;
+
+ errno = 0;
+ result = apr_strtoi64(ts[n].in, &end, ts[n].base);
+ errnum = errno;
+
+ ABTS_ASSERT(tc,
+ apr_psprintf(p, "for '%s': result was %" APR_INT64_T_FMT
+ " not %" APR_INT64_T_FMT, ts[n].in,
+ result, ts[n].result),
+ result == ts[n].result);
+
+ if (ts[n].errnum != -1) {
+ ABTS_ASSERT(tc,
+ apr_psprintf(p, "for '%s': errno was %d not %d", ts[n].in,
+ errnum, ts[n].errnum),
+ ts[n].errnum == errnum);
+ }
+
+ if (ts[n].end == NULL) {
+ /* end must point to NUL terminator of .in */
+ ABTS_PTR_EQUAL(tc, ts[n].in + strlen(ts[n].in), end);
+ } else if (ts[n].end != (void *)-1) {
+ ABTS_ASSERT(tc,
+ apr_psprintf(p, "for '%s', end was '%s' not '%s'",
+ ts[n].in, end, ts[n].end),
+ strcmp(ts[n].end, end) == 0);
+ }
+ }
+}
+
+static void string_strtoff(abts_case *tc, void *data)
+{
+ apr_off_t off;
+
+ ABTS_ASSERT(tc, "strtoff fails on out-of-range integer",
+ apr_strtoff(&off, "999999999999999999999999999999",
+ NULL, 10) != APR_SUCCESS);
+
+ ABTS_ASSERT(tc, "strtoff failed for 1234",
+ apr_strtoff(&off, "1234", NULL, 10) == APR_SUCCESS);
+
+ ABTS_ASSERT(tc, "strtoff failed to parse 1234", off == 1234);
+}
+
+/* random-ish checks for strfsize buffer overflows */
+static void overflow_strfsize(abts_case *tc, void *data)
+{
+ apr_off_t off;
+ char buf[7];
+
+ buf[5] = '$';
+ buf[6] = '@';
+
+ for (off = -9999; off < 20000; off++) {
+ apr_strfsize(off, buf);
+ }
+ for (; off < 9999999; off += 9) {
+ apr_strfsize(off, buf);
+ }
+ for (; off < 999999999; off += 999) {
+ apr_strfsize(off, buf);
+ }
+ for (off = LONG_MAX; off > 1; off /= 2) {
+ apr_strfsize(off, buf);
+ if (sizeof(apr_off_t) > sizeof(long) || off < LONG_MAX)
+ apr_strfsize(off + 1, buf);
+ apr_strfsize(off - 1, buf);
+ }
+
+ ABTS_ASSERT(tc, "strfsize overflowed", buf[5] == '$');
+ ABTS_ASSERT(tc, "strfsize overflowed", buf[6] == '@');
+}
+
+static void string_strfsize(abts_case *tc, void *data)
+{
+ static const struct {
+ apr_off_t size;
+ const char *buf;
+ } ts[] = {
+ { -1, " - " },
+ { 0, " 0 " },
+ { 666, "666 " },
+ { 1024, "1.0K" },
+ { 1536, "1.5K" },
+ { 2048, "2.0K" },
+ { 1293874, "1.2M" },
+ { 9999999, "9.5M" },
+ { 103809024, " 99M" },
+ { 1047527424, "1.0G" } /* "999M" would be more correct */
+ };
+ apr_size_t n;
+
+ for (n = 0; n < sizeof(ts)/sizeof(ts[0]); n++) {
+ char buf[6], *ret;
+
+ buf[5] = '%';
+
+ ret = apr_strfsize(ts[n].size, buf);
+ ABTS_ASSERT(tc, "strfsize returned wrong buffer", ret == buf);
+ ABTS_ASSERT(tc, "strfsize overflowed", buf[5] == '%');
+
+ ABTS_STR_EQUAL(tc, ts[n].buf, ret);
+ }
+}
+
+static void string_cpystrn(abts_case *tc, void *data)
+{
+ char buf[6], *ret;
+
+ buf[5] = 'Z';
+
+ ret = apr_cpystrn(buf, "123456", 5);
+
+ ABTS_STR_EQUAL(tc, "1234", buf);
+ ABTS_PTR_EQUAL(tc, buf + 4, ret);
+ ABTS_TRUE(tc, *ret == '\0');
+ ABTS_TRUE(tc, ret[1] == 'Z');
+}
+
+static void snprintf_overflow(abts_case *tc, void *data)
+{
+ char buf[4];
+ int rv;
+
+ buf[2] = '4';
+ buf[3] = '2';
+
+ rv = apr_snprintf(buf, 2, "%s", "a");
+ ABTS_INT_EQUAL(tc, 1, rv);
+
+ rv = apr_snprintf(buf, 2, "%s", "abcd");
+ ABTS_INT_EQUAL(tc, 1, rv);
+
+ ABTS_STR_EQUAL(tc, "a", buf);
+
+ /* Check the buffer really hasn't been overflowed. */
+ ABTS_TRUE(tc, buf[2] == '4' && buf[3] == '2');
+}
+
+static void skip_prefix(abts_case *tc, void *data)
+{
+ ABTS_STR_EQUAL(tc, apr_cstr_skip_prefix("12345", "12345"), "");
+ ABTS_STR_EQUAL(tc, apr_cstr_skip_prefix("12345", "123"), "45");
+ ABTS_STR_EQUAL(tc, apr_cstr_skip_prefix("12345", ""), "12345");
+ ABTS_STR_EQUAL(tc, apr_cstr_skip_prefix("12345", "23"), NULL);
+ ABTS_STR_EQUAL(tc, apr_cstr_skip_prefix("1", "12"), NULL);
+ ABTS_STR_EQUAL(tc, apr_cstr_skip_prefix("", ""), "");
+ ABTS_STR_EQUAL(tc, apr_cstr_skip_prefix("", "12"), NULL);
+}
+
+static void pstrcat(abts_case *tc, void *data)
+{
+ ABTS_STR_EQUAL(tc, apr_pstrcat(p, "a", "bc", "def", NULL),
+ "abcdef");
+ ABTS_STR_EQUAL(tc, apr_pstrcat(p, NULL), "");
+ ABTS_STR_EQUAL(tc, apr_pstrcat(p,
+ "a", "b", "c", "d", "e",
+ "f", "g", "h", "i", "j",
+ "1", "2", "3", "4", "5",
+ NULL),
+ "abcdefghij12345");
+}
+
+abts_suite *teststr(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, snprintf_0NULL, NULL);
+ abts_run_test(suite, snprintf_0nonNULL, NULL);
+ abts_run_test(suite, snprintf_noNULL, NULL);
+ abts_run_test(suite, snprintf_underflow, NULL);
+ abts_run_test(suite, test_strtok, NULL);
+ abts_run_test(suite, string_error, NULL);
+ abts_run_test(suite, string_long, NULL);
+ abts_run_test(suite, string_strtoi64, NULL);
+ abts_run_test(suite, string_strtoff, NULL);
+ abts_run_test(suite, overflow_strfsize, NULL);
+ abts_run_test(suite, string_strfsize, NULL);
+ abts_run_test(suite, string_cpystrn, NULL);
+ abts_run_test(suite, snprintf_overflow, NULL);
+ abts_run_test(suite, skip_prefix, NULL);
+ abts_run_test(suite, pstrcat, NULL);
+
+ return suite;
+}
+
diff --git a/test/teststrnatcmp.c b/test/teststrnatcmp.c
new file mode 100644
index 0000000..3a5e4c6
--- /dev/null
+++ b/test/teststrnatcmp.c
@@ -0,0 +1,78 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_file_io.h"
+#include "apr_errno.h"
+#include "apr_strings.h"
+#include "testutil.h"
+
+static void less0(abts_case *tc, void *data)
+{
+ int rv = apr_strnatcmp("a", "b");
+ ABTS_ASSERT(tc, "didn't compare simple strings properly", rv < 0);
+}
+
+static void str_equal(abts_case *tc, void *data)
+{
+ int rv = apr_strnatcmp("a", "a");
+ ABTS_ASSERT(tc, "didn't compare simple strings properly", rv == 0);
+}
+
+static void more0(abts_case *tc, void *data)
+{
+ int rv = apr_strnatcmp("b", "a");
+ ABTS_ASSERT(tc, "didn't compare simple strings properly", rv > 0);
+}
+
+static void less_ignore_case(abts_case *tc, void *data)
+{
+ int rv = apr_strnatcasecmp("a", "B");
+ ABTS_ASSERT(tc, "didn't compare simple strings properly", rv < 0);
+}
+
+static void str_equal_ignore_case(abts_case *tc, void *data)
+{
+ int rv = apr_strnatcasecmp("a", "A");
+ ABTS_ASSERT(tc, "didn't compare simple strings properly", rv == 0);
+}
+
+static void more_ignore_case(abts_case *tc, void *data)
+{
+ int rv = apr_strnatcasecmp("b", "A");
+ ABTS_ASSERT(tc, "didn't compare simple strings properly", rv > 0);
+}
+
+static void natcmp(abts_case *tc, void *data)
+{
+ int rv = apr_strnatcasecmp("a2", "a10");
+ ABTS_ASSERT(tc, "didn't compare simple strings properly", rv < 0);
+}
+
+abts_suite *teststrnatcmp(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, less0, NULL);
+ abts_run_test(suite, str_equal, NULL);
+ abts_run_test(suite, more0, NULL);
+ abts_run_test(suite, less_ignore_case, NULL);
+ abts_run_test(suite, str_equal_ignore_case, NULL);
+ abts_run_test(suite, more_ignore_case, NULL);
+ abts_run_test(suite, natcmp, NULL);
+
+ return suite;
+}
+
diff --git a/test/testtable.c b/test/testtable.c
new file mode 100644
index 0000000..0a9960f
--- /dev/null
+++ b/test/testtable.c
@@ -0,0 +1,245 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_general.h"
+#include "apr_pools.h"
+#include "apr_tables.h"
+#if APR_HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if APR_HAVE_STRING_H
+#include <string.h>
+#endif
+
+static apr_array_header_t *a1 = NULL;
+static apr_table_t *t1 = NULL;
+
+static void array_clear(abts_case *tc, void *data)
+{
+ a1 = apr_array_make(p, 2, sizeof(const char *));
+ APR_ARRAY_PUSH(a1, const char *) = "foo";
+ APR_ARRAY_PUSH(a1, const char *) = "bar";
+ apr_array_clear(a1);
+ ABTS_INT_EQUAL(tc, 0, a1->nelts);
+}
+
+static void table_make(abts_case *tc, void *data)
+{
+ t1 = apr_table_make(p, 5);
+ ABTS_PTR_NOTNULL(tc, t1);
+}
+
+static void table_get(abts_case *tc, void *data)
+{
+ const char *val;
+
+ apr_table_set(t1, "foo", "bar");
+ val = apr_table_get(t1, "foo");
+ ABTS_STR_EQUAL(tc, "bar", val);
+}
+
+static void table_getm(abts_case *tc, void *data)
+{
+ const char *orig, *val;
+ apr_pool_t *subp;
+
+ apr_pool_create(&subp, p);
+
+ orig = "bar";
+ apr_table_setn(t1, "foo", orig);
+ val = apr_table_getm(subp, t1, "foo");
+ ABTS_PTR_EQUAL(tc, orig, val);
+ ABTS_STR_EQUAL(tc, "bar", val);
+ apr_table_add(t1, "foo", "baz");
+ val = apr_table_getm(subp, t1, "foo");
+ ABTS_STR_EQUAL(tc, "bar,baz", val);
+
+ apr_pool_destroy(subp);
+}
+
+static void table_set(abts_case *tc, void *data)
+{
+ const char *val;
+
+ apr_table_set(t1, "setkey", "bar");
+ apr_table_set(t1, "setkey", "2ndtry");
+ val = apr_table_get(t1, "setkey");
+ ABTS_STR_EQUAL(tc, "2ndtry", val);
+}
+
+static void table_getnotthere(abts_case *tc, void *data)
+{
+ const char *val;
+
+ val = apr_table_get(t1, "keynotthere");
+ ABTS_PTR_EQUAL(tc, NULL, (void *)val);
+}
+
+static void table_add(abts_case *tc, void *data)
+{
+ const char *val;
+
+ apr_table_add(t1, "addkey", "bar");
+ apr_table_add(t1, "addkey", "foo");
+ val = apr_table_get(t1, "addkey");
+ ABTS_STR_EQUAL(tc, "bar", val);
+
+}
+
+static void table_nelts(abts_case *tc, void *data)
+{
+ const char *val;
+ apr_table_t *t = apr_table_make(p, 1);
+
+ apr_table_set(t, "abc", "def");
+ apr_table_set(t, "def", "abc");
+ apr_table_set(t, "foo", "zzz");
+ val = apr_table_get(t, "foo");
+ ABTS_STR_EQUAL(tc, "zzz", val);
+ val = apr_table_get(t, "abc");
+ ABTS_STR_EQUAL(tc, "def", val);
+ val = apr_table_get(t, "def");
+ ABTS_STR_EQUAL(tc, "abc", val);
+ ABTS_INT_EQUAL(tc, 3, apr_table_elts(t)->nelts);
+}
+
+static void table_clear(abts_case *tc, void *data)
+{
+ apr_table_clear(t1);
+ ABTS_INT_EQUAL(tc, 0, apr_table_elts(t1)->nelts);
+}
+
+static void table_unset(abts_case *tc, void *data)
+{
+ const char *val;
+ apr_table_t *t = apr_table_make(p, 1);
+
+ apr_table_set(t, "a", "1");
+ apr_table_set(t, "b", "2");
+ apr_table_unset(t, "b");
+ ABTS_INT_EQUAL(tc, 1, apr_table_elts(t)->nelts);
+ val = apr_table_get(t, "a");
+ ABTS_STR_EQUAL(tc, "1", val);
+ val = apr_table_get(t, "b");
+ ABTS_PTR_EQUAL(tc, (void *)NULL, (void *)val);
+}
+
+static void table_overlap(abts_case *tc, void *data)
+{
+ const char *val;
+ apr_table_t *t1 = apr_table_make(p, 1);
+ apr_table_t *t2 = apr_table_make(p, 1);
+
+ apr_table_addn(t1, "a", "0");
+ apr_table_addn(t1, "g", "7");
+ apr_table_addn(t2, "a", "1");
+ apr_table_addn(t2, "b", "2");
+ apr_table_addn(t2, "c", "3");
+ apr_table_addn(t2, "b", "2.0");
+ apr_table_addn(t2, "d", "4");
+ apr_table_addn(t2, "e", "5");
+ apr_table_addn(t2, "b", "2.");
+ apr_table_addn(t2, "f", "6");
+ apr_table_overlap(t1, t2, APR_OVERLAP_TABLES_SET);
+
+ ABTS_INT_EQUAL(tc, 7, apr_table_elts(t1)->nelts);
+ val = apr_table_get(t1, "a");
+ ABTS_STR_EQUAL(tc, "1", val);
+ val = apr_table_get(t1, "b");
+ ABTS_STR_EQUAL(tc, "2.", val);
+ val = apr_table_get(t1, "c");
+ ABTS_STR_EQUAL(tc, "3", val);
+ val = apr_table_get(t1, "d");
+ ABTS_STR_EQUAL(tc, "4", val);
+ val = apr_table_get(t1, "e");
+ ABTS_STR_EQUAL(tc, "5", val);
+ val = apr_table_get(t1, "f");
+ ABTS_STR_EQUAL(tc, "6", val);
+ val = apr_table_get(t1, "g");
+ ABTS_STR_EQUAL(tc, "7", val);
+}
+
+static void table_overlap2(abts_case *tc, void *data)
+{
+ apr_pool_t *subp;
+ apr_table_t *t1, *t2;
+
+ apr_pool_create(&subp, p);
+
+ t1 = apr_table_make(subp, 1);
+ t2 = apr_table_make(p, 1);
+ apr_table_addn(t1, "t1", "one");
+ apr_table_addn(t2, "t2", "two");
+
+ apr_table_overlap(t1, t2, APR_OVERLAP_TABLES_SET);
+
+ ABTS_INT_EQUAL(tc, 2, apr_table_elts(t1)->nelts);
+
+ ABTS_STR_EQUAL(tc, "one", apr_table_get(t1, "t1"));
+ ABTS_STR_EQUAL(tc, "two", apr_table_get(t1, "t2"));
+
+}
+
+static void table_overlap3(abts_case *tc, void *data)
+{
+ apr_pool_t *subp;
+ apr_table_t *t1, *t2;
+
+ apr_pool_create(&subp, p);
+
+ t1 = apr_table_make(subp, 1);
+ t2 = apr_table_make(p, 1);
+ apr_table_addn(t1, "t1", "one");
+ apr_table_addn(t1, "t1", "overlay");
+ apr_table_addn(t2, "t2", "two");
+ apr_table_addn(t2, "t2", "overlay");
+
+ apr_table_overlap(t1, t2, APR_OVERLAP_TABLES_ADD);
+
+ ABTS_INT_EQUAL(tc, 4, apr_table_elts(t1)->nelts);
+
+ ABTS_STR_EQUAL(tc, "one", apr_table_get(t1, "t1"));
+ ABTS_STR_EQUAL(tc, "two", apr_table_get(t1, "t2"));
+
+}
+
+abts_suite *testtable(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, array_clear, NULL);
+ abts_run_test(suite, table_make, NULL);
+ abts_run_test(suite, table_get, NULL);
+ abts_run_test(suite, table_getm, NULL);
+ abts_run_test(suite, table_set, NULL);
+ abts_run_test(suite, table_getnotthere, NULL);
+ abts_run_test(suite, table_add, NULL);
+ abts_run_test(suite, table_nelts, NULL);
+ abts_run_test(suite, table_clear, NULL);
+ abts_run_test(suite, table_unset, NULL);
+ abts_run_test(suite, table_overlap, NULL);
+ abts_run_test(suite, table_overlap2, NULL);
+ abts_run_test(suite, table_overlap3, NULL);
+
+ return suite;
+}
+
diff --git a/test/testtemp.c b/test/testtemp.c
new file mode 100644
index 0000000..1f1143e
--- /dev/null
+++ b/test/testtemp.c
@@ -0,0 +1,55 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_file_io.h"
+#include "apr_strings.h"
+
+static void test_temp_dir(abts_case *tc, void *data)
+{
+ const char *tempdir = NULL;
+ apr_status_t rv;
+
+ rv = apr_temp_dir_get(&tempdir, p);
+ APR_ASSERT_SUCCESS(tc, "Error finding Temporary Directory", rv);
+ ABTS_PTR_NOTNULL(tc, tempdir);
+}
+
+static void test_mktemp(abts_case *tc, void *data)
+{
+ apr_file_t *f = NULL;
+ const char *tempdir = NULL;
+ char *filetemplate;
+ apr_status_t rv;
+
+ rv = apr_temp_dir_get(&tempdir, p);
+ APR_ASSERT_SUCCESS(tc, "Error finding Temporary Directory", rv);
+
+ filetemplate = apr_pstrcat(p, tempdir, "/tempfileXXXXXX", NULL);
+ rv = apr_file_mktemp(&f, filetemplate, 0, p);
+ APR_ASSERT_SUCCESS(tc, "Error opening Temporary file", rv);
+}
+
+abts_suite *testtemp(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_temp_dir, NULL);
+ abts_run_test(suite, test_mktemp, NULL);
+
+ return suite;
+}
+
diff --git a/test/testthread.c b/test/testthread.c
new file mode 100644
index 0000000..f3df367
--- /dev/null
+++ b/test/testthread.c
@@ -0,0 +1,132 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_thread_proc.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_time.h"
+#include "testutil.h"
+
+#if APR_HAS_THREADS
+
+static apr_thread_mutex_t *thread_lock;
+static apr_thread_once_t *control = NULL;
+static int x = 0;
+static int value = 0;
+
+static apr_thread_t *t1;
+static apr_thread_t *t2;
+static apr_thread_t *t3;
+static apr_thread_t *t4;
+
+/* just some made up number to check on later */
+static apr_status_t exit_ret_val = 123;
+
+static void init_func(void)
+{
+ value++;
+}
+
+static void * APR_THREAD_FUNC thread_func1(apr_thread_t *thd, void *data)
+{
+ int i;
+
+ apr_thread_once(control, init_func);
+
+ for (i = 0; i < 10000; i++) {
+ apr_thread_mutex_lock(thread_lock);
+ x++;
+ apr_thread_mutex_unlock(thread_lock);
+ }
+ apr_thread_exit(thd, exit_ret_val);
+ return NULL;
+}
+
+static void thread_init(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_thread_once_init(&control, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+ rv = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_DEFAULT, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void create_threads(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_thread_create(&t1, NULL, thread_func1, NULL, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_thread_create(&t2, NULL, thread_func1, NULL, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_thread_create(&t3, NULL, thread_func1, NULL, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ rv = apr_thread_create(&t4, NULL, thread_func1, NULL, p);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void join_threads(abts_case *tc, void *data)
+{
+ apr_status_t s;
+
+ apr_thread_join(&s, t1);
+ ABTS_INT_EQUAL(tc, exit_ret_val, s);
+ apr_thread_join(&s, t2);
+ ABTS_INT_EQUAL(tc, exit_ret_val, s);
+ apr_thread_join(&s, t3);
+ ABTS_INT_EQUAL(tc, exit_ret_val, s);
+ apr_thread_join(&s, t4);
+ ABTS_INT_EQUAL(tc, exit_ret_val, s);
+}
+
+static void check_locks(abts_case *tc, void *data)
+{
+ ABTS_INT_EQUAL(tc, 40000, x);
+}
+
+static void check_thread_once(abts_case *tc, void *data)
+{
+ ABTS_INT_EQUAL(tc, 1, value);
+}
+
+#else
+
+static void threads_not_impl(abts_case *tc, void *data)
+{
+ ABTS_NOT_IMPL(tc, "Threads not implemented on this platform");
+}
+
+#endif
+
+abts_suite *testthread(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+#if !APR_HAS_THREADS
+ abts_run_test(suite, threads_not_impl, NULL);
+#else
+ abts_run_test(suite, thread_init, NULL);
+ abts_run_test(suite, create_threads, NULL);
+ abts_run_test(suite, join_threads, NULL);
+ abts_run_test(suite, check_locks, NULL);
+ abts_run_test(suite, check_thread_once, NULL);
+#endif
+
+ return suite;
+}
+
diff --git a/test/testtime.c b/test/testtime.c
new file mode 100644
index 0000000..fadef97
--- /dev/null
+++ b/test/testtime.c
@@ -0,0 +1,316 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_time.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "testutil.h"
+#include "apr_strings.h"
+#include <time.h>
+
+#define STR_SIZE 45
+
+/* The time value is used throughout the tests, so just make this a global.
+ * Also, we need a single value that we can test for the positive tests, so
+ * I chose the number below, it corresponds to:
+ * 2002-09-14 12:05:36.186711 -25200 [257 Sat].
+ * Which happens to be when I wrote the new tests.
+ */
+static apr_time_t now = APR_INT64_C(1032030336186711);
+/* 2012-08-11 16:00:55.151600 -14400 [224 Sat] DST */
+static apr_time_t leap_year_now = APR_INT64_C(1344715255151600);
+
+static char* print_time (apr_pool_t *pool, const apr_time_exp_t *xt)
+{
+ return apr_psprintf (pool,
+ "%04d-%02d-%02d %02d:%02d:%02d.%06d %+05d [%d %s]%s",
+ xt->tm_year + 1900,
+ xt->tm_mon + 1,
+ xt->tm_mday,
+ xt->tm_hour,
+ xt->tm_min,
+ xt->tm_sec,
+ xt->tm_usec,
+ xt->tm_gmtoff,
+ xt->tm_yday + 1,
+ apr_day_snames[xt->tm_wday],
+ (xt->tm_isdst ? " DST" : ""));
+}
+
+
+static void test_now(abts_case *tc, void *data)
+{
+ apr_time_t timediff;
+ apr_time_t current;
+ time_t os_now;
+
+ current = apr_time_now();
+ time(&os_now);
+
+ timediff = os_now - (current / APR_USEC_PER_SEC);
+ /* Even though these are called so close together, there is the chance
+ * that the time will be slightly off, so accept anything between -1 and
+ * 1 second.
+ */
+ ABTS_ASSERT(tc, "apr_time and OS time do not agree",
+ (timediff > -2) && (timediff < 2));
+}
+
+static void test_gmtstr(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_time_exp_t xt;
+
+ rv = apr_time_exp_gmt(&xt, now);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_time_exp_gmt");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ ABTS_STR_EQUAL(tc, "2002-09-14 19:05:36.186711 +0000 [257 Sat]",
+ print_time(p, &xt));
+}
+
+static void test_exp_lt(abts_case *tc, void *data)
+{
+ apr_time_t test_times[] = {0, 0, 0};
+ int i;
+
+ test_times[0] = now;
+ test_times[1] = leap_year_now;
+
+ for (i = 0; test_times[i] != 0; i++) {
+ apr_status_t rv;
+ apr_time_exp_t xt;
+ time_t posix_secs = (time_t)apr_time_sec(test_times[i]);
+ struct tm *posix_exp = localtime(&posix_secs);
+
+ rv = apr_time_exp_lt(&xt, test_times[i]);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_time_exp_lt");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+
+#define CHK_FIELD(f) \
+ ABTS_ASSERT(tc, "Mismatch in " #f, posix_exp->f == xt.f)
+
+ CHK_FIELD(tm_sec);
+ CHK_FIELD(tm_min);
+ CHK_FIELD(tm_hour);
+ CHK_FIELD(tm_mday);
+ CHK_FIELD(tm_mon);
+ CHK_FIELD(tm_year);
+ CHK_FIELD(tm_wday);
+ CHK_FIELD(tm_yday);
+ CHK_FIELD(tm_isdst);
+#undef CHK_FIELD
+ }
+}
+
+static void test_exp_get_gmt(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_time_exp_t xt;
+ apr_time_t imp;
+ apr_int64_t hr_off_64;
+
+ rv = apr_time_exp_gmt(&xt, now);
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ rv = apr_time_exp_get(&imp, &xt);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_time_exp_get");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ hr_off_64 = (apr_int64_t) xt.tm_gmtoff * APR_USEC_PER_SEC;
+ ABTS_TRUE(tc, now + hr_off_64 == imp);
+}
+
+static void test_exp_get_lt(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_time_exp_t xt;
+ apr_time_t imp;
+ apr_int64_t hr_off_64;
+
+ rv = apr_time_exp_lt(&xt, now);
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ rv = apr_time_exp_get(&imp, &xt);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_time_exp_get");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ hr_off_64 = (apr_int64_t) xt.tm_gmtoff * APR_USEC_PER_SEC;
+ ABTS_TRUE(tc, now + hr_off_64 == imp);
+}
+
+static void test_imp_gmt(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_time_exp_t xt;
+ apr_time_t imp;
+
+ rv = apr_time_exp_gmt(&xt, now);
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ rv = apr_time_exp_gmt_get(&imp, &xt);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_time_exp_gmt_get");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ ABTS_TRUE(tc, now == imp);
+}
+
+static void test_rfcstr(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char str[STR_SIZE];
+
+ rv = apr_rfc822_date(str, now);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_rfc822_date");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ ABTS_STR_EQUAL(tc, "Sat, 14 Sep 2002 19:05:36 GMT", str);
+}
+
+static void test_ctime(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ char apr_str[STR_SIZE];
+ char libc_str[STR_SIZE];
+ apr_time_t now_sec = apr_time_sec(now);
+ time_t posix_sec = (time_t) now_sec;
+
+ rv = apr_ctime(apr_str, now);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_ctime");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ strcpy(libc_str, ctime(&posix_sec));
+ *strchr(libc_str, '\n') = '\0';
+
+ ABTS_STR_EQUAL(tc, libc_str, apr_str);
+}
+
+static void test_strftime(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_time_exp_t xt;
+ char *str = NULL;
+ apr_size_t sz;
+
+ rv = apr_time_exp_gmt(&xt, now);
+ str = apr_palloc(p, STR_SIZE + 1);
+ rv = apr_strftime(str, &sz, STR_SIZE, "%R %A %d %B %Y", &xt);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_strftime");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ ABTS_STR_EQUAL(tc, "19:05 Saturday 14 September 2002", str);
+}
+
+static void test_strftimesmall(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_time_exp_t xt;
+ char str[STR_SIZE];
+ apr_size_t sz;
+
+ rv = apr_time_exp_gmt(&xt, now);
+ rv = apr_strftime(str, &sz, STR_SIZE, "%T", &xt);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_strftime");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ ABTS_STR_EQUAL(tc, "19:05:36", str);
+}
+
+static void test_exp_tz(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_time_exp_t xt;
+ apr_int32_t hr_off = -5 * 3600; /* 5 hours in seconds */
+
+ rv = apr_time_exp_tz(&xt, now, hr_off);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_time_exp_tz");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+ ABTS_TRUE(tc, (xt.tm_usec == 186711) &&
+ (xt.tm_sec == 36) &&
+ (xt.tm_min == 5) &&
+ (xt.tm_hour == 14) &&
+ (xt.tm_mday == 14) &&
+ (xt.tm_mon == 8) &&
+ (xt.tm_year == 102) &&
+ (xt.tm_wday == 6) &&
+ (xt.tm_yday == 256));
+}
+
+static void test_strftimeoffset(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ apr_time_exp_t xt;
+ char str[STR_SIZE];
+ apr_size_t sz;
+ apr_int32_t hr_off = -5 * 3600; /* 5 hours in seconds */
+
+ apr_time_exp_tz(&xt, now, hr_off);
+ rv = apr_strftime(str, &sz, STR_SIZE, "%T", &xt);
+ if (rv == APR_ENOTIMPL) {
+ ABTS_NOT_IMPL(tc, "apr_strftime");
+ }
+ ABTS_TRUE(tc, rv == APR_SUCCESS);
+}
+
+/* 0.9.4 and earlier rejected valid dates in 2038 */
+static void test_2038(abts_case *tc, void *data)
+{
+ apr_time_exp_t xt;
+ apr_time_t t;
+
+ /* 2038-01-19T03:14:07.000000Z */
+ xt.tm_year = 138;
+ xt.tm_mon = 0;
+ xt.tm_mday = 19;
+ xt.tm_hour = 3;
+ xt.tm_min = 14;
+ xt.tm_sec = 7;
+
+ APR_ASSERT_SUCCESS(tc, "explode January 19th, 2038",
+ apr_time_exp_get(&t, &xt));
+}
+
+abts_suite *testtime(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_now, NULL);
+ abts_run_test(suite, test_gmtstr, NULL);
+ abts_run_test(suite, test_exp_lt, NULL);
+ abts_run_test(suite, test_exp_get_gmt, NULL);
+ abts_run_test(suite, test_exp_get_lt, NULL);
+ abts_run_test(suite, test_imp_gmt, NULL);
+ abts_run_test(suite, test_rfcstr, NULL);
+ abts_run_test(suite, test_ctime, NULL);
+ abts_run_test(suite, test_strftime, NULL);
+ abts_run_test(suite, test_strftimesmall, NULL);
+ abts_run_test(suite, test_exp_tz, NULL);
+ abts_run_test(suite, test_strftimeoffset, NULL);
+ abts_run_test(suite, test_2038, NULL);
+
+ return suite;
+}
+
diff --git a/test/testud.c b/test/testud.c
new file mode 100644
index 0000000..7132d6c
--- /dev/null
+++ b/test/testud.c
@@ -0,0 +1,91 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "apr_file_io.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_lib.h"
+#include "apr_strings.h"
+#include "testutil.h"
+
+static apr_pool_t *pool;
+static char *testdata;
+static int cleanup_called = 0;
+
+static apr_status_t string_cleanup(void *data)
+{
+ cleanup_called = 1;
+ return APR_SUCCESS;
+}
+
+static void set_userdata(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+
+ rv = apr_pool_userdata_set(testdata, "TEST", string_cleanup, pool);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+}
+
+static void get_userdata(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ void *retdata;
+
+ rv = apr_pool_userdata_get(&retdata, "TEST", pool);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_STR_EQUAL(tc, testdata, retdata);
+}
+
+static void get_nonexistkey(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ void *retdata;
+
+ rv = apr_pool_userdata_get(&retdata, "DOESNTEXIST", pool);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_EQUAL(tc, NULL, retdata);
+}
+
+static void post_pool_clear(abts_case *tc, void *data)
+{
+ apr_status_t rv;
+ void *retdata;
+
+ rv = apr_pool_userdata_get(&retdata, "DOESNTEXIST", pool);
+ ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+ ABTS_PTR_EQUAL(tc, NULL, retdata);
+}
+
+abts_suite *testud(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ apr_pool_create(&pool, p);
+ testdata = apr_pstrdup(pool, "This is a test\n");
+
+ abts_run_test(suite, set_userdata, NULL);
+ abts_run_test(suite, get_userdata, NULL);
+ abts_run_test(suite, get_nonexistkey, NULL);
+
+ apr_pool_clear(pool);
+
+ abts_run_test(suite, post_pool_clear, NULL);
+
+ return suite;
+}
+
diff --git a/test/testuser.c b/test/testuser.c
new file mode 100644
index 0000000..e75782e
--- /dev/null
+++ b/test/testuser.c
@@ -0,0 +1,174 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testutil.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "apr_user.h"
+
+#if APR_HAS_USER
+static void uid_current(abts_case *tc, void *data)
+{
+ apr_uid_t uid;
+ apr_gid_t gid;
+
+ APR_ASSERT_SUCCESS(tc, "apr_uid_current failed",
+ apr_uid_current(&uid, &gid, p));
+}
+
+static void username(abts_case *tc, void *data)
+{
+ apr_uid_t uid;
+ apr_gid_t gid;
+ apr_uid_t retreived_uid;
+ apr_gid_t retreived_gid;
+ char *uname = NULL;
+
+ APR_ASSERT_SUCCESS(tc, "apr_uid_current failed",
+ apr_uid_current(&uid, &gid, p));
+
+ APR_ASSERT_SUCCESS(tc, "apr_uid_name_get failed",
+ apr_uid_name_get(&uname, uid, p));
+ ABTS_PTR_NOTNULL(tc, uname);
+
+ if (uname == NULL)
+ return;
+
+ APR_ASSERT_SUCCESS(tc, "apr_uid_get failed",
+ apr_uid_get(&retreived_uid, &retreived_gid, uname, p));
+
+ APR_ASSERT_SUCCESS(tc, "apr_uid_compare failed",
+ apr_uid_compare(uid, retreived_uid));
+#ifdef WIN32
+ /* ### this fudge was added for Win32 but makes the test return NotImpl
+ * on Unix if run as root, when !gid is also true. */
+ if (!gid || !retreived_gid) {
+ /* The function had no way to recover the gid (this would have been
+ * an ENOTIMPL if apr_uid_ functions didn't try to double-up and
+ * also return apr_gid_t values, which was bogus.
+ */
+ if (!gid) {
+ ABTS_NOT_IMPL(tc, "Groups from apr_uid_current");
+ }
+ else {
+ ABTS_NOT_IMPL(tc, "Groups from apr_uid_get");
+ }
+ }
+ else {
+#endif
+ APR_ASSERT_SUCCESS(tc, "apr_gid_compare failed",
+ apr_gid_compare(gid, retreived_gid));
+#ifdef WIN32
+ }
+#endif
+}
+
+static void groupname(abts_case *tc, void *data)
+{
+ apr_uid_t uid;
+ apr_gid_t gid;
+ apr_gid_t retreived_gid;
+ char *gname = NULL;
+
+ APR_ASSERT_SUCCESS(tc, "apr_uid_current failed",
+ apr_uid_current(&uid, &gid, p));
+
+ APR_ASSERT_SUCCESS(tc, "apr_gid_name_get failed",
+ apr_gid_name_get(&gname, gid, p));
+ ABTS_PTR_NOTNULL(tc, gname);
+
+ if (gname == NULL)
+ return;
+
+ APR_ASSERT_SUCCESS(tc, "apr_gid_get failed",
+ apr_gid_get(&retreived_gid, gname, p));
+
+ APR_ASSERT_SUCCESS(tc, "apr_gid_compare failed",
+ apr_gid_compare(gid, retreived_gid));
+}
+
+#ifdef APR_UID_GID_NUMERIC
+
+static void fail_userinfo(abts_case *tc, void *data)
+{
+ apr_uid_t uid;
+ apr_gid_t gid;
+ apr_status_t rv;
+ char *tmp;
+
+ errno = 0;
+ gid = uid = 9999999;
+ tmp = NULL;
+ rv = apr_uid_name_get(&tmp, uid, p);
+ ABTS_ASSERT(tc, "apr_uid_name_get should fail or "
+ "return a user name",
+ rv != APR_SUCCESS || tmp != NULL);
+
+ errno = 0;
+ tmp = NULL;
+ rv = apr_gid_name_get(&tmp, gid, p);
+ ABTS_ASSERT(tc, "apr_gid_name_get should fail or "
+ "return a group name",
+ rv != APR_SUCCESS || tmp != NULL);
+
+ gid = 424242;
+ errno = 0;
+ rv = apr_gid_get(&gid, "I_AM_NOT_A_GROUP", p);
+ ABTS_ASSERT(tc, "apr_gid_get should fail or "
+ "set a group number",
+ rv != APR_SUCCESS || gid == 424242);
+
+ gid = uid = 424242;
+ errno = 0;
+ rv = apr_uid_get(&uid, &gid, "I_AM_NOT_A_USER", p);
+ ABTS_ASSERT(tc, "apr_gid_get should fail or "
+ "set a user and group number",
+ rv != APR_SUCCESS || uid == 424242 || gid == 4242442);
+
+ errno = 0;
+ tmp = NULL;
+ rv = apr_uid_homepath_get(&tmp, "I_AM_NOT_A_USER", p);
+ ABTS_ASSERT(tc, "apr_uid_homepath_get should fail or "
+ "set a path name",
+ rv != APR_SUCCESS || tmp != NULL);
+}
+
+#endif
+
+#else
+static void users_not_impl(abts_case *tc, void *data)
+{
+ ABTS_NOT_IMPL(tc, "Users not implemented on this platform");
+}
+#endif
+
+abts_suite *testuser(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+#if !APR_HAS_USER
+ abts_run_test(suite, users_not_impl, NULL);
+#else
+ abts_run_test(suite, uid_current, NULL);
+ abts_run_test(suite, username, NULL);
+ abts_run_test(suite, groupname, NULL);
+#ifdef APR_UID_GID_NUMERIC
+ abts_run_test(suite, fail_userinfo, NULL);
+#endif
+#endif
+
+ return suite;
+}
diff --git a/test/testutil.c b/test/testutil.c
new file mode 100644
index 0000000..2986cd5
--- /dev/null
+++ b/test/testutil.c
@@ -0,0 +1,61 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#if defined(_MSC_VER)
+#include <crtdbg.h>
+#endif
+
+#include "abts.h"
+#include "testutil.h"
+#include "apr_pools.h"
+
+apr_pool_t *p;
+
+void apr_assert_success(abts_case* tc, const char* context, apr_status_t rv,
+ int lineno)
+{
+ if (rv == APR_ENOTIMPL) {
+ abts_not_impl(tc, context, lineno);
+ } else if (rv != APR_SUCCESS) {
+ char buf[STRING_MAX], ebuf[128];
+ sprintf(buf, "%s (%d): %s\n", context, rv,
+ apr_strerror(rv, ebuf, sizeof ebuf));
+ abts_fail(tc, buf, lineno);
+ }
+}
+
+void initialize(void) {
+ apr_initialize();
+ atexit(apr_terminate);
+
+ apr_pool_create(&p, NULL);
+
+#if _MSC_VER >= 1400
+ /* In release mode: Redirect abort() errors to stderr */
+ _set_error_mode(_OUT_TO_STDERR);
+
+ /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr.
+ (Ignored in release builds) */
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+#endif /* _MSC_VER >= 1400 */
+}
diff --git a/test/testutil.h b/test/testutil.h
new file mode 100644
index 0000000..4ff9475
--- /dev/null
+++ b/test/testutil.h
@@ -0,0 +1,109 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_pools.h"
+#include "apr_general.h"
+#include "abts.h"
+
+#ifndef APR_TEST_UTIL
+#define APR_TEST_UTIL
+
+/* XXX: FIXME - these all should become much more utilitarian
+ * and part of apr, itself
+ */
+
+#ifdef WIN32
+#ifdef BINPATH
+#define TESTBINPATH APR_STRINGIFY(BINPATH) "/"
+#else
+#define TESTBINPATH ""
+#endif
+#else
+#define TESTBINPATH "./"
+#endif
+
+#ifdef WIN32
+#define EXTENSION ".exe"
+#elif NETWARE
+#define EXTENSION ".nlm"
+#else
+#define EXTENSION
+#endif
+
+#define STRING_MAX 8096
+
+/* Some simple functions to make the test apps easier to write and
+ * a bit more consistent...
+ */
+
+extern apr_pool_t *p;
+
+/* Assert that RV is an APR_SUCCESS value; else fail giving strerror
+ * for RV and CONTEXT message. */
+void apr_assert_success(abts_case* tc, const char *context,
+ apr_status_t rv, int lineno);
+#define APR_ASSERT_SUCCESS(tc, ctxt, rv) \
+ apr_assert_success(tc, ctxt, rv, __LINE__)
+
+void initialize(void);
+
+abts_suite *testatomic(abts_suite *suite);
+abts_suite *testdir(abts_suite *suite);
+abts_suite *testdso(abts_suite *suite);
+abts_suite *testdup(abts_suite *suite);
+abts_suite *testencode(abts_suite *suite);
+abts_suite *testenv(abts_suite *suite);
+abts_suite *testescape(abts_suite *suite);
+abts_suite *testfile(abts_suite *suite);
+abts_suite *testfilecopy(abts_suite *suite);
+abts_suite *testfileinfo(abts_suite *suite);
+abts_suite *testflock(abts_suite *suite);
+abts_suite *testfmt(abts_suite *suite);
+abts_suite *testfnmatch(abts_suite *suite);
+abts_suite *testgetopt(abts_suite *suite);
+abts_suite *testglobalmutex(abts_suite *suite);
+abts_suite *testhash(abts_suite *suite);
+abts_suite *testipsub(abts_suite *suite);
+abts_suite *testlock(abts_suite *suite);
+abts_suite *testcond(abts_suite *suite);
+abts_suite *testlfs(abts_suite *suite);
+abts_suite *testmmap(abts_suite *suite);
+abts_suite *testnames(abts_suite *suite);
+abts_suite *testoc(abts_suite *suite);
+abts_suite *testpath(abts_suite *suite);
+abts_suite *testpipe(abts_suite *suite);
+abts_suite *testpoll(abts_suite *suite);
+abts_suite *testpool(abts_suite *suite);
+abts_suite *testproc(abts_suite *suite);
+abts_suite *testprocmutex(abts_suite *suite);
+abts_suite *testrand(abts_suite *suite);
+abts_suite *testsleep(abts_suite *suite);
+abts_suite *testshm(abts_suite *suite);
+abts_suite *testsock(abts_suite *suite);
+abts_suite *testsockets(abts_suite *suite);
+abts_suite *testsockopt(abts_suite *suite);
+abts_suite *teststr(abts_suite *suite);
+abts_suite *teststrnatcmp(abts_suite *suite);
+abts_suite *testtable(abts_suite *suite);
+abts_suite *testtemp(abts_suite *suite);
+abts_suite *testthread(abts_suite *suite);
+abts_suite *testtime(abts_suite *suite);
+abts_suite *testud(abts_suite *suite);
+abts_suite *testuser(abts_suite *suite);
+abts_suite *testvsn(abts_suite *suite);
+abts_suite *testskiplist(abts_suite *suite);
+
+#endif /* APR_TEST_INCLUDES */
diff --git a/test/testvsn.c b/test/testvsn.c
new file mode 100644
index 0000000..dbc218a
--- /dev/null
+++ b/test/testvsn.c
@@ -0,0 +1,56 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include "testutil.h"
+#include "apr_version.h"
+#include "apr_general.h"
+
+
+static void test_strings(abts_case *tc, void *data)
+{
+ ABTS_STR_EQUAL(tc, APR_VERSION_STRING, apr_version_string());
+}
+
+#ifdef APR_IS_DEV_VERSION
+# define IS_DEV 1
+#else
+# define IS_DEV 0
+#endif
+
+static void test_ints(abts_case *tc, void *data)
+{
+ apr_version_t vsn;
+
+ apr_version(&vsn);
+
+ ABTS_INT_EQUAL(tc, APR_MAJOR_VERSION, vsn.major);
+ ABTS_INT_EQUAL(tc, APR_MINOR_VERSION, vsn.minor);
+ ABTS_INT_EQUAL(tc, APR_PATCH_VERSION, vsn.patch);
+ ABTS_INT_EQUAL(tc, IS_DEV, vsn.is_dev);
+}
+
+abts_suite *testvsn(abts_suite *suite)
+{
+ suite = ADD_SUITE(suite)
+
+ abts_run_test(suite, test_strings, NULL);
+ abts_run_test(suite, test_ints, NULL);
+
+ return suite;
+}
+
diff --git a/test/tryread.c b/test/tryread.c
new file mode 100644
index 0000000..569d647
--- /dev/null
+++ b/test/tryread.c
@@ -0,0 +1,49 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "testflock.h"
+#include "apr_pools.h"
+#include "apr_file_io.h"
+#include "apr_general.h"
+#include "apr.h"
+
+#if APR_HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+int main(int argc, const char * const *argv)
+{
+ apr_file_t *file;
+ apr_status_t status;
+ apr_pool_t *p;
+
+ apr_initialize();
+ apr_pool_create(&p, NULL);
+
+ if (apr_file_open(&file, TESTFILE, APR_FOPEN_WRITE, APR_OS_DEFAULT, p)
+ != APR_SUCCESS) {
+
+ exit(UNEXPECTED_ERROR);
+ }
+ status = apr_file_lock(file, APR_FLOCK_EXCLUSIVE | APR_FLOCK_NONBLOCK);
+ if (status == APR_SUCCESS) {
+ exit(SUCCESSFUL_READ);
+ }
+ if (APR_STATUS_IS_EAGAIN(status)) {
+ exit(FAILED_READ);
+ }
+ exit(UNEXPECTED_ERROR);
+}