diff options
Diffstat (limited to 'test')
93 files changed, 24381 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..e03ccc4 --- /dev/null +++ b/test/abts.c @@ -0,0 +1,506 @@ +/* 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->skipped > 0) { + fprintf(stdout, "SKIPPED %d of %d\n", last->skipped, last->num_test); + 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->skipped = 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.skipped = 0; + tc.suite = ss; + + ss->num_test++; + update_status(); + + f(&tc, value); + + if (tc.failed) { + ss->failed++; + } + + if (tc.skipped) { + ss->skipped++; + } +} + +static void report_summary(const char *kind_header, + const char *name_header, + const char *percent_header) +{ + fprintf(stdout, "%-15s\t\tTotal\t%s\t%s\n", + kind_header, name_header, percent_header); + fprintf(stdout, "===================================================\n"); +} + +static int report(abts_suite *suite) +{ + int failed_count = 0; + int skipped_count = 0; + sub_suite *dptr; + + if (suite && suite->tail &&!suite->tail->not_run) { + end_suite(suite); + } + + for (dptr = suite->head; dptr; dptr = dptr->next) { + failed_count += dptr->failed; + } + + for (dptr = suite->head; dptr; dptr = dptr->next) { + skipped_count += dptr->skipped; + } + + if (list_tests) { + return 0; + } + + /* Report skipped tests */ + if (skipped_count > 0) { + dptr = suite->head; + report_summary("Skipped Tests", "Skip", "Skipped %"); + while (dptr != NULL) { + if (dptr->skipped != 0) { + float percent = ((float)dptr->skipped / (float)dptr->num_test); + fprintf(stdout, "%-15s\t\t%5d\t%4d\t%8.2f%%\n", dptr->name, + dptr->num_test, dptr->skipped, percent * 100); + } + dptr = dptr->next; + } + } + + if (failed_count == 0) { + printf("All tests passed.\n"); + return 0; + } + + /* Report failed tests */ + dptr = suite->head; + if (skipped_count > 0) { + fprintf(stdout, "\n"); + } + report_summary("Failed Tests", "Fail", " Failed %"); + 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%8.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); + } +} + +#define IMPL_abts_T_equal(T, NAME, FMT, CAST) \ +void abts_##NAME##_equal(abts_case *tc, const T expected, const T actual, int lineno) \ +{ \ + update_status(); \ + if (tc->failed) return; \ + \ + if (expected == actual) return; \ + \ + tc->failed = TRUE; \ + if (verbose) { \ + fprintf(stderr, "Line %d: expected <%" FMT ">, but saw <%" FMT ">\n", \ + lineno, CAST expected, CAST actual); \ + fflush(stderr); \ + } \ +} +IMPL_abts_T_equal(int, int, "d", (int)) +IMPL_abts_T_equal(unsigned int, uint, "u", (unsigned int)) +IMPL_abts_T_equal(long, long, "ld", (long)) +IMPL_abts_T_equal(unsigned long, ulong, "lu", (unsigned long)) +IMPL_abts_T_equal(long long, llong, "lld", (long long)) +IMPL_abts_T_equal(unsigned long long, ullong, "llu", (unsigned long long)) +IMPL_abts_T_equal(size_t, size, "lu", (unsigned long)) + +#define IMPL_abts_T_nequal(T, NAME, FMT, CAST) \ +void abts_##NAME##_nequal(abts_case *tc, const T expected, const T 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 <%" FMT ">, " \ + "but saw <%" FMT ">\n", \ + lineno, CAST expected, CAST actual); \ + fflush(stderr); \ + } \ +} +IMPL_abts_T_nequal(int, int, "d", (int)) +IMPL_abts_T_nequal(unsigned int, uint, "u", (unsigned int)) +IMPL_abts_T_nequal(long, long, "ld", (long)) +IMPL_abts_T_nequal(unsigned long, ulong, "lu", (unsigned long)) +IMPL_abts_T_nequal(long long, llong, "lld", (long long)) +IMPL_abts_T_nequal(unsigned long long, ullong, "llu", (unsigned long long)) +IMPL_abts_T_nequal(size_t, size, "lu", (unsigned long)) + +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_skip(abts_case *tc, const char *message, int lineno) +{ + update_status(); + if (tc->skipped) return; + + tc->skipped = TRUE; + if (verbose) { + fprintf(stderr, "\bSKIP: 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..dffb0c5 --- /dev/null +++ b/test/abts.h @@ -0,0 +1,149 @@ +/* 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 skipped; + 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; + int skipped; + 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_uint_equal(abts_case *tc, const unsigned int expected, + const unsigned int actual, int lineno); +void abts_uint_nequal(abts_case *tc, const unsigned int expected, + const unsigned int actual, int lineno); +void abts_long_equal(abts_case *tc, const long expected, + const long actual, int lineno); +void abts_long_nequal(abts_case *tc, const long expected, + const long actual, int lineno); +void abts_ulong_equal(abts_case *tc, const unsigned long expected, + const unsigned long actual, int lineno); +void abts_ulong_nequal(abts_case *tc, const unsigned long expected, + const unsigned long actual, int lineno); +void abts_llong_equal(abts_case *tc, const long long expected, + const long long actual, int lineno); +void abts_llong_nequal(abts_case *tc, const long long expected, + const long long actual, int lineno); +void abts_ullong_equal(abts_case *tc, const unsigned long long expected, + const unsigned long long actual, int lineno); +void abts_ullong_nequal(abts_case *tc, const unsigned long long expected, + const unsigned long long actual, int lineno); +void abts_size_equal(abts_case *tc, size_t expected, size_t actual, int lineno); +void abts_size_nequal(abts_case *tc, size_t expected, size_t 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_skip(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); + +/* 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_UINT_EQUAL(a, b, c) abts_uint_equal(a, b, c, __LINE__) +#define ABTS_UINT_NEQUAL(a, b, c) abts_uint_nequal(a, b, c, __LINE__) +#define ABTS_LONG_EQUAL(a, b, c) abts_long_equal(a, b, c, __LINE__) +#define ABTS_LONG_NEQUAL(a, b, c) abts_long_nequal(a, b, c, __LINE__) +#define ABTS_ULONG_EQUAL(a, b, c) abts_ulong_equal(a, b, c, __LINE__) +#define ABTS_ULONG_NEQUAL(a, b, c) abts_ulong_nequal(a, b, c, __LINE__) +#define ABTS_LLONG_EQUAL(a, b, c) abts_llong_equal(a, b, c, __LINE__) +#define ABTS_LLONG_NEQUAL(a, b, c) abts_llong_nequal(a, b, c, __LINE__) +#define ABTS_ULLONG_EQUAL(a, b, c) abts_ullong_equal(a, b, c, __LINE__) +#define ABTS_ULLONG_NEQUAL(a, b, c) abts_ullong_nequal(a, b, c, __LINE__) +#define ABTS_SIZE_EQUAL(a, b, c) abts_size_equal(a, b, c, __LINE__) +#define ABTS_SIZE_NEQUAL(a, b, c) abts_size_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__); + + +/* When skipping tests, make a reference to the test data parameter + to avoid unused variable warnings. */ +#define ABTS_SKIP(tc, data, msg) do { \ + ((void)(data)); \ + abts_skip((tc), (msg), __LINE__); \ + } while (0) + +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( ®ex, argv[1], REG_EXTENDED|REG_NOSUB); + + + if (rc) { + char errbuf[2000]; + regerror(rc, ®ex,errbuf,2000); + fprintf(stderr,"Couldn't compile regex ;(\n%s\n ",errbuf); + return -1; + } + if ( regexec( ®ex, 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( ®ex, 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, ¤t_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, ¤t_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, ¤t_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..efcc148 --- /dev/null +++ b/test/testatomic.c @@ -0,0 +1,978 @@ +/* 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_UINT_EQUAL(tc, 2, y32); +} + +static void test_read32(abts_case *tc, void *data) +{ + apr_uint32_t y32; + apr_atomic_set32(&y32, 2); + ABTS_UINT_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_UINT_EQUAL(tc, 1, y32); + ABTS_ASSERT(tc, "atomic_dec returned zero when it shouldn't", rv != 0); + + rv = apr_atomic_dec32(&y32); + ABTS_UINT_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_UINT_EQUAL(tc, 100, oldval); + ABTS_UINT_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_UINT_EQUAL(tc, 0, oldval); + ABTS_UINT_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_UINT_EQUAL(tc, 12, oldval); + ABTS_UINT_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_UINT_EQUAL(tc, 12, oldval); + ABTS_UINT_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_UINT_EQUAL(tc, 23, oldval); + ABTS_UINT_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_UINT_EQUAL(tc, 23, oldval); + ABTS_UINT_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_UINT_EQUAL(tc, 23, oldval); + ABTS_UINT_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_UINT_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_ULLONG_EQUAL(tc, 2, y64); +} + +static void test_read64(abts_case *tc, void *data) +{ + apr_uint64_t y64; + apr_atomic_set64(&y64, 2); + ABTS_ULLONG_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_ULLONG_EQUAL(tc, 1, y64); + ABTS_ASSERT(tc, "atomic_dec returned zero when it shouldn't", rv != 0); + + rv = apr_atomic_dec64(&y64); + ABTS_ULLONG_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_ULLONG_EQUAL(tc, 100, oldval); + ABTS_ULLONG_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_ULLONG_EQUAL(tc, 23, oldval); + ABTS_ULLONG_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_ULLONG_EQUAL(tc, 23, oldval); + ABTS_ULLONG_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_ULLONG_EQUAL(tc, 23, oldval); + ABTS_ULLONG_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_ULLONG_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; +apr_uint32_t mutex_locks = 0; +apr_uint64_t mutex_locks64 = 0; +apr_uint32_t atomic_ops = 0; +apr_uint64_t atomic_ops64 = 0; +#ifndef CACHELINE_SIZE +#define CACHELINE_SIZE 64 +#endif +struct { + char pad[CACHELINE_SIZE - 5]; + apr_uint64_t ops64; +} atomic_pad __attribute__((aligned(CACHELINE_SIZE))); + +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_UINT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS, mutex_locks); + ABTS_UINT_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_UINT_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_UINT_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_UINT_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_UINT_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_UINT_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) +{ + apr_uint64_t *ops64 = data; + int i; + + for (i = 0; i < NUM_ITERATIONS ; i++) { + apr_atomic_inc64(ops64); + apr_atomic_add64(ops64, 2); + apr_atomic_dec64(ops64); + apr_atomic_dec64(ops64); + } + apr_thread_exit(thd, exit_ret_val); + return NULL; +} + +static void test_atomics_threaded64(abts_case *tc, void *data) +{ + apr_uint64_t *ops64 = 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 + + mutex_locks64 = 0; + apr_atomic_set64(ops64, 0); + + 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, ops64, 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_ULLONG_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS, mutex_locks64); + ABTS_ULLONG_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS, + apr_atomic_read64(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_ULLONG_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_ULLONG_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_ULLONG_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_ULLONG_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_ULLONG_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) +{ + apr_uint64_t *ops64 = data; + int i; + + for (i = 0; i < 1000 * 1000; i++) { + apr_atomic_set64(ops64, APR_UINT64_C(0x1111222233334444)); + apr_atomic_set64(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_uint64_t *ops64 = data; + apr_thread_t *thread; + apr_status_t retval; + int i; + + apr_atomic_set64(ops64, APR_UINT64_C(0x1111222233334444)); + + apr_thread_create(&thread, NULL, test_func_set64, ops64, p); + + for (i = 0; i < 1000 * 1000 * 2; i++) { + apr_uint64_t val = apr_atomic_read64(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, &atomic_ops64); + abts_run_test(suite, test_atomics_threaded64, &atomic_pad.ops64); + 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, &atomic_ops64); + abts_run_test(suite, test_atomics_threaded_setread64, &atomic_pad.ops64); +#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..95b559d --- /dev/null +++ b/test/testdir.c @@ -0,0 +1,412 @@ +/* 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); +} + +static void mkdir_func(abts_case *tc, apr_pool_t *pool) +{ + 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, + pool); + s2 = apr_dir_make_recursive("data/prll/four/five/six/seven/eight", + APR_FPROT_UREAD | APR_FPROT_UWRITE | APR_FPROT_UEXECUTE, + pool); + s3 = apr_dir_make_recursive("data/prll/nine/ten", + APR_FPROT_UREAD | APR_FPROT_UWRITE | APR_FPROT_UEXECUTE, + 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, + pool); + s5 = apr_dir_make_recursive("data/fortytwo", + APR_FPROT_UREAD | APR_FPROT_UWRITE | APR_FPROT_UEXECUTE, + pool); + + 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); + ABTS_INT_EQUAL(tc, APR_SUCCESS, s5); +} + +#if APR_HAS_THREADS +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; + mkdir_func(td->tc, td->pool); + return NULL; +} +#endif /* APR_HAS_THREADS */ + +static void test_mkdir_recurs_parallel(abts_case *tc, void *data) +{ +#if APR_HAS_THREADS + 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); +#else + /* Create the test directories for test_removeall anwyay. */ + mkdir_func(tc, p); + ABTS_SKIP(tc, data, "This test requires APR thread support."); +#endif /* APR_HAS_THREADS */ +} + +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..ca9ddb3 --- /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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_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_SIZE_EQUAL(tc, 4, 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_SIZE_EQUAL(tc, 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..bd6c072 --- /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<>&'"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 = "ÿ<>&'"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<>&'"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 = "ÿ<>&'"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 = " <>&'"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..f135bed --- /dev/null +++ b/test/testfile.c @@ -0,0 +1,2298 @@ +/* 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 "apr_strings.h" +#include "apr_thread_proc.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); + /* Calling gets after EOF should return EOF. */ + 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); + /* Calling gets after EOF should return EOF. */ + 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_empty(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testgets_empty.dat"; + char buf[256]; + + 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", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ, APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "re-open test file", rv); + + rv = apr_file_gets(buf, sizeof(buf), f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_STR_EQUAL(tc, "", buf); + /* Calling gets after EOF should return EOF. */ + rv = apr_file_gets(buf, sizeof(buf), f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_STR_EQUAL(tc, "", buf); + apr_file_close(f); +} + +static void test_gets_multiline(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testgets_multiline.dat"; + char buf[256]; + + 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", rv); + rv = apr_file_puts("a\nb\n", f); + APR_ASSERT_SUCCESS(tc, "write test data", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ, APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "re-open test file", rv); + + memset(buf, 0, sizeof(buf)); + rv = apr_file_gets(buf, sizeof(buf), f); + APR_ASSERT_SUCCESS(tc, "read first line", rv); + ABTS_STR_EQUAL(tc, "a\n", buf); + + memset(buf, 0, sizeof(buf)); + rv = apr_file_gets(buf, sizeof(buf), f); + APR_ASSERT_SUCCESS(tc, "read second line", rv); + ABTS_STR_EQUAL(tc, "b\n", buf); + + rv = apr_file_gets(buf, sizeof(buf), f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_STR_EQUAL(tc, "", buf); + /* Calling gets after EOF should return EOF. */ + rv = apr_file_gets(buf, sizeof(buf), f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_STR_EQUAL(tc, "", buf); + apr_file_close(f); +} + +static void test_gets_small_buf(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testgets_small_buf.dat"; + char buf[2]; + + 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", rv); + rv = apr_file_puts("ab\n", f); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ, APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "re-open test file", rv); + /* Buffer is too small to hold the full line, test that gets properly + * returns the line content character by character. + */ + memset(buf, 0, sizeof(buf)); + rv = apr_file_gets(buf, sizeof(buf), f); + APR_ASSERT_SUCCESS(tc, "read first chunk", rv); + ABTS_STR_EQUAL(tc, "a", buf); + + memset(buf, 0, sizeof(buf)); + rv = apr_file_gets(buf, sizeof(buf), f); + APR_ASSERT_SUCCESS(tc, "read second chunk", rv); + ABTS_STR_EQUAL(tc, "b", buf); + + memset(buf, 0, sizeof(buf)); + rv = apr_file_gets(buf, sizeof(buf), f); + APR_ASSERT_SUCCESS(tc, "read third chunk", rv); + ABTS_STR_EQUAL(tc, "\n", buf); + + rv = apr_file_gets(buf, sizeof(buf), f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_STR_EQUAL(tc, "", buf); + /* Calling gets after EOF should return EOF. */ + rv = apr_file_gets(buf, sizeof(buf), f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_STR_EQUAL(tc, "", buf); + apr_file_close(f); +} + +static void test_gets_ungetc(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testgets_ungetc.dat"; + char buf[256]; + + 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", rv); + rv = apr_file_puts("a\n", f); + APR_ASSERT_SUCCESS(tc, "write test data", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ, APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "re-open test file", rv); + + rv = apr_file_ungetc('b', f); + APR_ASSERT_SUCCESS(tc, "call ungetc", rv); + memset(buf, 0, sizeof(buf)); + rv = apr_file_gets(buf, sizeof(buf), f); + APR_ASSERT_SUCCESS(tc, "read line", rv); + ABTS_STR_EQUAL(tc, "ba\n", buf); + + rv = apr_file_ungetc('\n', f); + APR_ASSERT_SUCCESS(tc, "call ungetc with EOL", rv); + memset(buf, 0, sizeof(buf)); + rv = apr_file_gets(buf, sizeof(buf), f); + APR_ASSERT_SUCCESS(tc, "read line", rv); + ABTS_STR_EQUAL(tc, "\n", buf); + + rv = apr_file_gets(buf, sizeof(buf), f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_STR_EQUAL(tc, "", buf); + /* Calling gets after EOF should return EOF. */ + rv = apr_file_gets(buf, sizeof(buf), f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_STR_EQUAL(tc, "", buf); + apr_file_close(f); +} + +static void test_gets_buffered_big(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testgets_buffered_big.dat"; + char hugestr[APR_BUFFERSIZE + 2]; + char buf[APR_BUFFERSIZE + 2]; + + 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", rv); + /* Test an edge case with a buffered file and the line that exceeds + * the default buffer size by 1 (the line itself fits into the buffer, + * but the line + EOL does not). + */ + memset(hugestr, 'a', sizeof(hugestr)); + hugestr[sizeof(hugestr) - 2] = '\n'; + hugestr[sizeof(hugestr) - 1] = '\0'; + rv = apr_file_puts(hugestr, f); + APR_ASSERT_SUCCESS(tc, "write first line", rv); + rv = apr_file_puts("b\n", f); + APR_ASSERT_SUCCESS(tc, "write second line", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "re-open test file", rv); + + memset(buf, 0, sizeof(buf)); + rv = apr_file_gets(buf, sizeof(buf), f); + APR_ASSERT_SUCCESS(tc, "read first line", rv); + ABTS_STR_EQUAL(tc, hugestr, buf); + + memset(buf, 0, sizeof(buf)); + rv = apr_file_gets(buf, sizeof(buf), f); + APR_ASSERT_SUCCESS(tc, "read second line", rv); + ABTS_STR_EQUAL(tc, "b\n", buf); + + rv = apr_file_gets(buf, sizeof(buf), f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_STR_EQUAL(tc, "", buf); + /* Calling gets after EOF should return EOF. */ + rv = apr_file_gets(buf, sizeof(buf), f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_STR_EQUAL(tc, "", buf); + 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 + } +} + +#if APR_HAS_THREADS +typedef struct thread_file_append_ctx_t { + apr_pool_t *pool; + const char *fname; + apr_size_t chunksize; + char val; + int num_writes; + char *errmsg; +} thread_file_append_ctx_t; + +static void * APR_THREAD_FUNC thread_file_append_func(apr_thread_t *thd, void *data) +{ + thread_file_append_ctx_t *ctx = data; + apr_status_t rv; + apr_file_t *f; + int i; + char *writebuf; + char *readbuf; + + rv = apr_file_open(&f, ctx->fname, + APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_APPEND, + APR_FPROT_OS_DEFAULT, ctx->pool); + if (rv) { + apr_thread_exit(thd, rv); + return NULL; + } + + writebuf = apr_palloc(ctx->pool, ctx->chunksize); + memset(writebuf, ctx->val, ctx->chunksize); + readbuf = apr_palloc(ctx->pool, ctx->chunksize); + + for (i = 0; i < ctx->num_writes; i++) { + apr_size_t bytes_written; + apr_size_t bytes_read; + apr_off_t offset; + + rv = apr_file_write_full(f, writebuf, ctx->chunksize, &bytes_written); + if (rv) { + apr_thread_exit(thd, rv); + return NULL; + } + /* After writing the data, seek back from the current offset and + * verify what we just wrote. */ + offset = -((apr_off_t)ctx->chunksize); + rv = apr_file_seek(f, APR_CUR, &offset); + if (rv) { + apr_thread_exit(thd, rv); + return NULL; + } + rv = apr_file_read_full(f, readbuf, ctx->chunksize, &bytes_read); + if (rv) { + apr_thread_exit(thd, rv); + return NULL; + } + if (memcmp(readbuf, writebuf, ctx->chunksize) != 0) { + ctx->errmsg = apr_psprintf( + ctx->pool, + "Unexpected data at file offset %" APR_OFF_T_FMT, + offset); + apr_thread_exit(thd, APR_SUCCESS); + return NULL; + } + } + + apr_file_close(f); + apr_thread_exit(thd, APR_SUCCESS); + + return NULL; +} +#endif /* APR_HAS_THREADS */ + +static void test_atomic_append(abts_case *tc, void *data) +{ +#if APR_HAS_THREADS + apr_status_t rv; + apr_status_t thread_rv; + apr_file_t *f; + const char *fname = "data/testatomic_append.dat"; + unsigned int seed; + thread_file_append_ctx_t ctx1 = {0}; + thread_file_append_ctx_t ctx2 = {0}; + apr_thread_t *t1; + apr_thread_t *t2; + + apr_file_remove(fname, p); + + rv = apr_file_open(&f, fname, APR_FOPEN_WRITE | APR_FOPEN_CREATE, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "create file", rv); + apr_file_close(f); + + seed = (unsigned int)apr_time_now(); + abts_log_message("Random seed for test_atomic_append() is %u", seed); + srand(seed); + + /* Create two threads appending data to the same file. */ + apr_pool_create(&ctx1.pool, p); + ctx1.fname = fname; + ctx1.chunksize = 1 + rand() % 8192; + ctx1.val = 'A'; + ctx1.num_writes = 1000; + rv = apr_thread_create(&t1, NULL, thread_file_append_func, &ctx1, p); + APR_ASSERT_SUCCESS(tc, "create thread", rv); + + apr_pool_create(&ctx2.pool, p); + ctx2.fname = fname; + ctx2.chunksize = 1 + rand() % 8192; + ctx2.val = 'B'; + ctx2.num_writes = 1000; + rv = apr_thread_create(&t2, NULL, thread_file_append_func, &ctx2, p); + APR_ASSERT_SUCCESS(tc, "create thread", rv); + + rv = apr_thread_join(&thread_rv, t1); + APR_ASSERT_SUCCESS(tc, "join thread", rv); + APR_ASSERT_SUCCESS(tc, "no thread errors", thread_rv); + if (ctx1.errmsg) { + ABTS_FAIL(tc, ctx1.errmsg); + } + rv = apr_thread_join(&thread_rv, t2); + APR_ASSERT_SUCCESS(tc, "join thread", rv); + APR_ASSERT_SUCCESS(tc, "no thread errors", thread_rv); + if (ctx2.errmsg) { + ABTS_FAIL(tc, ctx2.errmsg); + } + + apr_file_remove(fname, p); +#else + ABTS_SKIP(tc, data, "This test requires APR thread support."); +#endif /* APR_HAS_THREADS */ +} + +static void test_append_locked(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testappend_locked.dat"; + apr_size_t bytes_written; + apr_size_t bytes_read; + char buf[64] = {0}; + + apr_file_remove(fname, p); + + rv = apr_file_open(&f, fname, + APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_APPEND, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "create file", rv); + + rv = apr_file_lock(f, APR_FLOCK_EXCLUSIVE); + APR_ASSERT_SUCCESS(tc, "lock file", rv); + + /* PR50058: Appending to a locked file should not deadlock. */ + rv = apr_file_write_full(f, "abc", 3, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + + apr_file_unlock(f); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ, APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open file", rv); + + rv = apr_file_read_full(f, buf, sizeof(buf), &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, 3, (int)bytes_read); + ABTS_STR_EQUAL(tc, "abc", buf); + + apr_file_close(f); + apr_file_remove(fname, p); +} + +static void test_large_write_buffered(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testlarge_write_buffered.dat"; + apr_size_t len; + apr_size_t bytes_written; + apr_size_t bytes_read; + char *buf; + char *buf2; + + apr_file_remove(fname, p); + + rv = apr_file_open(&f, fname, + APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for writing", rv); + + /* Test a single large write. */ + len = 80000; + buf = apr_palloc(p, len); + memset(buf, 'a', len); + rv = apr_file_write_full(f, buf, len, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + ABTS_INT_EQUAL(tc, (int)len, (int)bytes_written); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + buf2 = apr_palloc(p, len + 1); + rv = apr_file_read_full(f, buf2, len + 1, &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, (int)len, (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf, buf2, len) == 0); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_two_large_writes_buffered(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testtwo_large_writes_buffered.dat"; + apr_size_t len; + apr_size_t bytes_written; + apr_size_t bytes_read; + char *buf; + char *buf2; + + apr_file_remove(fname, p); + + rv = apr_file_open(&f, fname, + APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for writing", rv); + + /* Test two consecutive large writes. */ + len = 80000; + buf = apr_palloc(p, len); + memset(buf, 'a', len); + + rv = apr_file_write_full(f, buf, len / 2, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + ABTS_INT_EQUAL(tc, (int)(len / 2), (int)bytes_written); + + rv = apr_file_write_full(f, buf, len / 2, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + ABTS_INT_EQUAL(tc, (int)(len / 2), (int)bytes_written); + + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + buf2 = apr_palloc(p, len + 1); + rv = apr_file_read_full(f, buf2, len + 1, &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, (int) len, (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf, buf2, len) == 0); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_small_and_large_writes_buffered(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testtwo_large_writes_buffered.dat"; + apr_size_t len; + apr_size_t bytes_written; + apr_size_t bytes_read; + char *buf; + char *buf2; + + apr_file_remove(fname, p); + + rv = apr_file_open(&f, fname, + APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for writing", rv); + + /* Test small write followed by a large write. */ + len = 80000; + buf = apr_palloc(p, len); + memset(buf, 'a', len); + + rv = apr_file_write_full(f, buf, 5, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + ABTS_INT_EQUAL(tc, 5, (int)bytes_written); + + rv = apr_file_write_full(f, buf, len - 5, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + ABTS_INT_EQUAL(tc, (int)(len - 5), (int)bytes_written); + + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + buf2 = apr_palloc(p, len + 1); + rv = apr_file_read_full(f, buf2, len + 1, &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, (int) len, (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf, buf2, len) == 0); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_write_buffered_spanning_over_bufsize(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testwrite_buffered_spanning_over_bufsize.dat"; + apr_size_t len; + apr_size_t bytes_written; + apr_size_t bytes_read; + char *buf; + char *buf2; + + apr_file_remove(fname, p); + + rv = apr_file_open(&f, fname, + APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for writing", rv); + + /* Test three writes than span over the default buffer size. */ + len = APR_BUFFERSIZE + 1; + buf = apr_palloc(p, len); + memset(buf, 'a', len); + + rv = apr_file_write_full(f, buf, APR_BUFFERSIZE - 1, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + ABTS_INT_EQUAL(tc, APR_BUFFERSIZE - 1, (int)bytes_written); + + rv = apr_file_write_full(f, buf, 2, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + ABTS_INT_EQUAL(tc, 2, (int)bytes_written); + + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + buf2 = apr_palloc(p, len + 1); + rv = apr_file_read_full(f, buf2, len + 1, &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, (int)len, (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf, buf2, len) == 0); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_empty_read_buffered(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testempty_read_buffered.dat"; + apr_size_t len; + apr_size_t bytes_read; + char buf[64]; + + 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, "create empty test file", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + /* Test an empty read. */ + len = 1; + rv = apr_file_read_full(f, buf, len, &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, 0, (int)bytes_read); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_large_read_buffered(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testlarge_read_buffered.dat"; + apr_size_t len; + apr_size_t bytes_written; + apr_size_t bytes_read; + char *buf; + char *buf2; + + 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); + len = 80000; + buf = apr_palloc(p, len); + memset(buf, 'a', len); + rv = apr_file_write_full(f, buf, len, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + /* Test a single large read. */ + buf2 = apr_palloc(p, len); + rv = apr_file_read_full(f, buf2, len, &bytes_read); + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_INT_EQUAL(tc, (int)len, (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf, buf2, bytes_read) == 0); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + /* Test that we receive an EOF. */ + len = 1; + rv = apr_file_read_full(f, buf2, len, &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, 0, (int)bytes_read); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_two_large_reads_buffered(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testtwo_large_reads_buffered.dat"; + apr_size_t len; + apr_size_t bytes_written; + apr_size_t bytes_read; + char *buf; + char *buf2; + + 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); + len = 80000; + buf = apr_palloc(p, len); + memset(buf, 'a', len); + rv = apr_file_write_full(f, buf, len, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + /* Test two consecutive large reads. */ + buf2 = apr_palloc(p, len); + memset(buf2, 0, len); + rv = apr_file_read_full(f, buf2, len / 2, &bytes_read); + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_INT_EQUAL(tc, (int)(len / 2), (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf, buf2, bytes_read) == 0); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + memset(buf2, 0, len); + rv = apr_file_read_full(f, buf2, len / 2, &bytes_read); + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_INT_EQUAL(tc, (int)(len / 2), (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf + len / 2, buf2, bytes_read) == 0); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + /* Test that we receive an EOF. */ + len = 1; + rv = apr_file_read_full(f, buf2, len, &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, 0, (int)bytes_read); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_small_and_large_reads_buffered(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testtwo_large_reads_buffered.dat"; + apr_size_t len; + apr_size_t bytes_written; + apr_size_t bytes_read; + char *buf; + char *buf2; + + 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); + len = 80000; + buf = apr_palloc(p, len); + memset(buf, 'a', len); + rv = apr_file_write_full(f, buf, len, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + /* Test small read followed by a large read. */ + buf2 = apr_palloc(p, len); + memset(buf2, 0, len); + rv = apr_file_read_full(f, buf2, 5, &bytes_read); + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_INT_EQUAL(tc, 5, (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf, buf2, bytes_read) == 0); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + memset(buf2, 0, len); + rv = apr_file_read_full(f, buf2, len - 5, &bytes_read); + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_INT_EQUAL(tc, (int)(len - 5), (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf + 5, buf2, bytes_read) == 0); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + /* Test that we receive an EOF. */ + len = 1; + rv = apr_file_read_full(f, buf2, len, &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, 0, (int)bytes_read); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_read_buffered_spanning_over_bufsize(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testread_buffered_spanning_over_bufsize.dat"; + apr_size_t len; + apr_size_t bytes_written; + apr_size_t bytes_read; + char *buf; + char *buf2; + + 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); + len = APR_BUFFERSIZE + 1; + buf = apr_palloc(p, len); + memset(buf, 'a', len); + rv = apr_file_write_full(f, buf, len, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + /* Test reads than span over the default buffer size. */ + buf2 = apr_palloc(p, len); + memset(buf2, 0, len); + rv = apr_file_read_full(f, buf2, APR_BUFFERSIZE - 1, &bytes_read); + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_INT_EQUAL(tc, APR_BUFFERSIZE - 1, (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf, buf2, bytes_read) == 0); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + memset(buf2, 0, len); + rv = apr_file_read_full(f, buf2, 2, &bytes_read); + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_INT_EQUAL(tc, 2, (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf, buf2, bytes_read) == 0); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + /* Test that we receive an EOF. */ + len = 1; + rv = apr_file_read_full(f, buf2, len, &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, 0, (int)bytes_read); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_single_byte_reads_buffered(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testsingle_byte_reads_buffered.dat"; + apr_size_t len; + apr_size_t bytes_written; + apr_size_t bytes_read; + char *buf; + apr_size_t total; + + 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); + len = 40000; + buf = apr_palloc(p, len); + memset(buf, 'a', len); + rv = apr_file_write_full(f, buf, len, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + total = 0; + while (1) { + memset(buf, 0, len); + rv = apr_file_read_full(f, buf, 1, &bytes_read); + if (rv == APR_EOF) { + break; + } + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_INT_EQUAL(tc, 1, (int)bytes_read); + ABTS_INT_EQUAL(tc, 'a', buf[0]); + total += bytes_read; + } + ABTS_INT_EQUAL(tc, (int)len, (int)total); + + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_read_buffered_seek(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testtest_read_buffered_seek.dat"; + apr_size_t len; + apr_size_t bytes_written; + apr_size_t bytes_read; + char buf[64]; + apr_off_t off; + + 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); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open test file for reading", rv); + + /* Read one byte. */ + memset(buf, 0, sizeof(buf)); + rv = apr_file_read_full(f, buf, 1, &bytes_read); + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_INT_EQUAL(tc, 1, (int)bytes_read); + ABTS_INT_EQUAL(tc, 'a', buf[0]); + + /* Seek into the middle of the file. */ + off = 3; + rv = apr_file_seek(f, APR_SET, &off); + APR_ASSERT_SUCCESS(tc, "change file read offset", rv); + ABTS_INT_EQUAL(tc, 3, (int)off); + + /* Read three bytes. */ + memset(buf, 0, sizeof(buf)); + rv = apr_file_read_full(f, buf, 3, &bytes_read); + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_INT_EQUAL(tc, 3, (int)bytes_read); + ABTS_TRUE(tc, memcmp(buf, "def", bytes_read) == 0); + + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + /* Test that we receive an EOF. */ + len = 1; + rv = apr_file_read_full(f, buf, len, &bytes_read); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + ABTS_INT_EQUAL(tc, 0, (int)bytes_read); + rv = apr_file_eof(f); + ABTS_INT_EQUAL(tc, APR_EOF, rv); + apr_file_close(f); + + apr_file_remove(fname, p); +} + +static void test_append_buffered(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_file_t *f; + const char *fname = "data/testappend_buffered.dat"; + apr_size_t bytes_written; + char buf[64]; + + apr_file_remove(fname, p); + + /* Create file with contents. */ + rv = apr_file_open(&f, fname, APR_FOPEN_WRITE | APR_FOPEN_CREATE, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "create file", rv); + + rv = apr_file_write_full(f, "abc", 3, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + apr_file_close(f); + + /* Re-open it with APR_FOPEN_APPEND and APR_FOPEN_BUFFERED. */ + rv = apr_file_open(&f, fname, + APR_FOPEN_READ | APR_FOPEN_WRITE | + APR_FOPEN_APPEND | APR_FOPEN_BUFFERED, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open file", rv); + + /* Append to the file. */ + rv = apr_file_write_full(f, "def", 3, &bytes_written); + APR_ASSERT_SUCCESS(tc, "write to file", rv); + apr_file_close(f); + + rv = apr_file_open(&f, fname, APR_FOPEN_READ, + APR_FPROT_OS_DEFAULT, p); + APR_ASSERT_SUCCESS(tc, "open file", rv); + + /* Test the file contents. */ + rv = apr_file_gets(buf, sizeof(buf), f); + APR_ASSERT_SUCCESS(tc, "read from file", rv); + ABTS_STR_EQUAL(tc, "abcdef", buf); + + apr_file_close(f); + apr_file_remove(fname, p); +} + +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_gets_empty, NULL); + abts_run_test(suite, test_gets_multiline, NULL); + abts_run_test(suite, test_gets_small_buf, NULL); + abts_run_test(suite, test_gets_ungetc, NULL); + abts_run_test(suite, test_gets_buffered_big, 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); + abts_run_test(suite, test_atomic_append, NULL); + abts_run_test(suite, test_append_locked, NULL); + abts_run_test(suite, test_large_write_buffered, NULL); + abts_run_test(suite, test_two_large_writes_buffered, NULL); + abts_run_test(suite, test_small_and_large_writes_buffered, NULL); + abts_run_test(suite, test_write_buffered_spanning_over_bufsize, NULL); + abts_run_test(suite, test_empty_read_buffered, NULL); + abts_run_test(suite, test_large_read_buffered, NULL); + abts_run_test(suite, test_two_large_reads_buffered, NULL); + abts_run_test(suite, test_small_and_large_reads_buffered, NULL); + abts_run_test(suite, test_read_buffered_spanning_over_bufsize, NULL); + abts_run_test(suite, test_single_byte_reads_buffered, NULL); + abts_run_test(suite, test_read_buffered_seek, NULL); + abts_run_test(suite, test_append_buffered, 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(©, 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..8dc1afc --- /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)(apr_uintptr_t)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 *)(apr_uintptr_t)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..960b6a7 --- /dev/null +++ b/test/testpoll.c @@ -0,0 +1,981 @@ +/* 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" + +#if defined(__linux__) +#include "arch/unix/apr_private.h" +#endif +#ifndef HAVE_EPOLL_WAIT_RELIABLE_TIMEOUT +#define HAVE_EPOLL_WAIT_RELIABLE_TIMEOUT 0 +#endif + +#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); +} + +#define JUSTSLEEP_DELAY apr_time_from_msec(200) +#if HAVE_EPOLL_WAIT_RELIABLE_TIMEOUT +#define JUSTSLEEP_ENOUGH(ts, te) \ + ((te) - (ts) >= JUSTSLEEP_DELAY) +#else +#define JUSTSLEEP_JIFFY apr_time_from_msec(10) +#define JUSTSLEEP_ENOUGH(ts, te) \ + ((te) - (ts) >= JUSTSLEEP_DELAY - JUSTSLEEP_JIFFY) +#endif + +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, JUSTSLEEP_DELAY); + 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", + JUSTSLEEP_ENOUGH(t1, t2)); + + 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, JUSTSLEEP_DELAY, &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", + JUSTSLEEP_ENOUGH(t1, t2)); + + 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, JUSTSLEEP_DELAY, 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", + JUSTSLEEP_ENOUGH(t1, t2)); + + /* 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..3c32465 --- /dev/null +++ b/test/testproc.c @@ -0,0 +1,177 @@ +/* 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); + + rv = apr_file_close(testfile); + ABTS_INT_EQUAL(tc, APR_SUCCESS, rv); + + 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..3b97471 --- /dev/null +++ b/test/testshm.c @@ -0,0 +1,340 @@ +/* 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 + +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); + memset(boxes, 0, SHARED_SIZE); + + 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); + memset(boxes, 0, SHARED_SIZE); + + rv = apr_proc_fork(&proc, p); + if (rv == APR_INCHILD) { /* child */ + int num = msgwait("anon_test", N_MESSAGES, + 5, /* wait for 5s */ + 10 /* with 10ms spin delay */); + /* 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 */ + } + if (!msgput("anon_test", i)) { + cnt--; + } + apr_sleep(apr_time_from_msec(10)); + } + } + 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); + memset(boxes, 0, SHARED_SIZE); + + 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); +} + +static void test_named_perms(abts_case *tc, void *data) +{ + apr_status_t rv; + apr_shm_t *shm; + apr_uid_t uid; + apr_gid_t gid; + + 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_uid_current(&uid, &gid, p); + APR_ASSERT_SUCCESS(tc, "retrieve current uid/gid", rv); + if (rv) return; + + rv = apr_shm_perms_set(shm, + APR_FPROT_UREAD|APR_FPROT_UWRITE, uid, gid); + apr_shm_destroy(shm); + + if (rv == APR_ENOTIMPL) + ABTS_SKIP(tc, data, "apr_shm_perms_set not implemented for named shm"); + else + APR_ASSERT_SUCCESS(tc, "Could not change permissions of shm segment", rv); +} + +#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); + abts_run_test(suite, test_named_perms, NULL); +#endif + + return suite; +} + + diff --git a/test/testshm.h b/test/testshm.h new file mode 100644 index 0000000..4d4f0c5 --- /dev/null +++ b/test/testshm.h @@ -0,0 +1,83 @@ +/* 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 + +#include "apr.h" +#include "apr_time.h" +#include "apr_atomic.h" +#include "apr_strings.h" + +#include <assert.h> + +typedef struct mbox { + char msg[1024]; + apr_uint32_t msgavail; +} mbox; +mbox *boxes; + +#define N_BOXES 10 +#define N_MESSAGES 100 +#define SHARED_SIZE (apr_size_t)(N_BOXES * sizeof(mbox)) +#define SHARED_FILENAME "data/apr.testshm.shm" +#define MSG "Sending a message" + +#if APR_HAS_SHARED_MEMORY + +static APR_INLINE +int msgwait(const char *msg, int count, int duration, int sleep_ms) +{ + int recvd = 0, i; + apr_time_t start = apr_time_now(); + apr_interval_time_t sleep_duration = apr_time_from_sec(duration); + apr_interval_time_t sleep_delay = apr_time_from_msec(sleep_ms); + while (apr_time_now() - start < sleep_duration) { + for (i = 0; i < N_BOXES; i++) { + if (apr_atomic_cas32(&boxes[i].msgavail, 0, 1) == 1) { + if (msg) { + assert(strcmp(boxes[i].msg, msg) == 0); + } + *boxes[i].msg = '\0'; + if (++recvd == count) { + return recvd; + } + } + } + apr_sleep(sleep_delay); + } + return recvd; +} + +static APR_INLINE +int msgput(const char *msg, int boxnum) +{ + if (apr_atomic_cas32(&boxes[boxnum].msgavail, -1, 0) != 0) { + return 0; + } + if (msg) { + apr_cpystrn(boxes[boxnum].msg, msg, sizeof(boxes[boxnum].msg)); + } + else { + *boxes[boxnum].msg = '\0'; + } + apr_atomic_set32(&boxes[boxnum].msgavail, 1); + return 1; +} + +#endif /* APR_HAS_SHARED_MEMORY */ + +#endif diff --git a/test/testshmconsumer.c b/test/testshmconsumer.c new file mode 100644 index 0000000..bf70e72 --- /dev/null +++ b/test/testshmconsumer.c @@ -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. + */ + +#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 + +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(MSG, N_MESSAGES, 30, 1); + + 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..eb61e76 --- /dev/null +++ b/test/testshmproducer.c @@ -0,0 +1,84 @@ +/* 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 + +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(MSG, i); + 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); +} |