From abdc76ea2e0f5a6ea1f8597c5202cdca29c4f5f8 Mon Sep 17 00:00:00 2001 From: Bill Zissimopoulos Date: Mon, 30 Nov 2015 13:48:19 -0800 Subject: [PATCH] add winfsp-tests and ext/tlib --- .../{samples => testing}/mirror.vcxproj | 8 +- .../mirror.vcxproj.filters | 0 build/VStudio/testing/winfsp-tests.vcxproj | 184 ++++++++++++++++ .../testing/winfsp-tests.vcxproj.filters | 28 +++ build/VStudio/winfsp.sln | 15 +- build/VStudio/winfsp_dll.vcxproj | 8 +- build/VStudio/winfsp_sys.vcxproj | 8 +- ext/tlib/Commit.txt | 2 + ext/tlib/callstack.c | 75 +++++++ ext/tlib/callstack.h | 26 +++ ext/tlib/injected/allfunc.h | 13 ++ ext/tlib/injected/curlfunc.c | 35 +++ ext/tlib/injected/curlfunc.h | 24 +++ ext/tlib/injected/stdfunc.c | 29 +++ ext/tlib/injected/stdfunc.h | 23 ++ ext/tlib/injection.c | 144 +++++++++++++ ext/tlib/injection.h | 30 +++ ext/tlib/testsuite.c | 202 ++++++++++++++++++ ext/tlib/testsuite.h | 112 ++++++++++ tst/winfsp-tests/mount-test.c | 11 + tst/winfsp-tests/winfsp-tests.c | 8 + 21 files changed, 971 insertions(+), 14 deletions(-) rename build/VStudio/{samples => testing}/mirror.vcxproj (95%) rename build/VStudio/{samples => testing}/mirror.vcxproj.filters (100%) create mode 100644 build/VStudio/testing/winfsp-tests.vcxproj create mode 100644 build/VStudio/testing/winfsp-tests.vcxproj.filters create mode 100644 ext/tlib/Commit.txt create mode 100644 ext/tlib/callstack.c create mode 100644 ext/tlib/callstack.h create mode 100644 ext/tlib/injected/allfunc.h create mode 100644 ext/tlib/injected/curlfunc.c create mode 100644 ext/tlib/injected/curlfunc.h create mode 100644 ext/tlib/injected/stdfunc.c create mode 100644 ext/tlib/injected/stdfunc.h create mode 100644 ext/tlib/injection.c create mode 100644 ext/tlib/injection.h create mode 100644 ext/tlib/testsuite.c create mode 100644 ext/tlib/testsuite.h create mode 100644 tst/winfsp-tests/mount-test.c create mode 100644 tst/winfsp-tests/winfsp-tests.c diff --git a/build/VStudio/samples/mirror.vcxproj b/build/VStudio/testing/mirror.vcxproj similarity index 95% rename from build/VStudio/samples/mirror.vcxproj rename to build/VStudio/testing/mirror.vcxproj index 7d834c3a..a07cb233 100644 --- a/build/VStudio/samples/mirror.vcxproj +++ b/build/VStudio/testing/mirror.vcxproj @@ -101,7 +101,7 @@ Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\..\inc;..\..\..\src + ..\..\..\src;..\..\..\inc Console @@ -116,7 +116,7 @@ Disabled _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\..\inc;..\..\..\src + ..\..\..\src;..\..\..\inc Console @@ -133,7 +133,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\..\inc;..\..\..\src + ..\..\..\src;..\..\..\inc Console @@ -152,7 +152,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\..\inc;..\..\..\src + ..\..\..\src;..\..\..\inc Console diff --git a/build/VStudio/samples/mirror.vcxproj.filters b/build/VStudio/testing/mirror.vcxproj.filters similarity index 100% rename from build/VStudio/samples/mirror.vcxproj.filters rename to build/VStudio/testing/mirror.vcxproj.filters diff --git a/build/VStudio/testing/winfsp-tests.vcxproj b/build/VStudio/testing/winfsp-tests.vcxproj new file mode 100644 index 00000000..bac3daa6 --- /dev/null +++ b/build/VStudio/testing/winfsp-tests.vcxproj @@ -0,0 +1,184 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {262DF8CC-E7A8-4460-A22C-683CBA322C32} + Win32Proj + winfsptests + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + true + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + false + $(SolutionDir)build\$(Configuration)\ + $(SolutionDir)build\$(ProjectName).build\$(Configuration)\$(PlatformTarget)\ + $(ProjectName)-$(PlatformTarget) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\..\src;..\..\..\inc;..\..\..\ext + + + Console + true + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\..\src;..\..\..\inc;..\..\..\ext + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\..\src;..\..\..\inc;..\..\..\ext + + + Console + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\..\..\src;..\..\..\inc;..\..\..\ext + + + Console + true + true + true + + + + + false + false + false + false + TurnOffAllWarnings + TurnOffAllWarnings + TurnOffAllWarnings + TurnOffAllWarnings + + + + + + + + + + + \ No newline at end of file diff --git a/build/VStudio/testing/winfsp-tests.vcxproj.filters b/build/VStudio/testing/winfsp-tests.vcxproj.filters new file mode 100644 index 00000000..a98cfe18 --- /dev/null +++ b/build/VStudio/testing/winfsp-tests.vcxproj.filters @@ -0,0 +1,28 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {1b7df6fa-185e-4a53-bfac-a4bbf82bba70} + + + + + Source + + + Source + + + Source\tlib + + + + + Source\tlib + + + \ No newline at end of file diff --git a/build/VStudio/winfsp.sln b/build/VStudio/winfsp.sln index 328de8d2..a5976a97 100644 --- a/build/VStudio/winfsp.sln +++ b/build/VStudio/winfsp.sln @@ -7,9 +7,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winfsp.dll", "winfsp_dll.vc EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winfsp.sys", "winfsp_sys.vcxproj", "{C85C26BA-8C22-4D30-83DA-46C3548E6332}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{69439FD1-C07D-4BF1-98DC-3CCFECE53A49}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testing", "testing", "{69439FD1-C07D-4BF1-98DC-3CCFECE53A49}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mirror", "samples\mirror.vcxproj", "{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mirror", "testing\mirror.vcxproj", "{AA7190E8-877F-4827-8CDD-E0D85F83C8C1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winfsp-tests", "testing\winfsp-tests.vcxproj", "{262DF8CC-E7A8-4460-A22C-683CBA322C32}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -43,11 +45,20 @@ Global {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x64.Build.0 = Release|x64 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x86.ActiveCfg = Release|Win32 {AA7190E8-877F-4827-8CDD-E0D85F83C8C1}.Release|x86.Build.0 = Release|Win32 + {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x64.ActiveCfg = Debug|x64 + {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x64.Build.0 = Debug|x64 + {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x86.ActiveCfg = Debug|Win32 + {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Debug|x86.Build.0 = Debug|Win32 + {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x64.ActiveCfg = Release|x64 + {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x64.Build.0 = Release|x64 + {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x86.ActiveCfg = Release|Win32 + {262DF8CC-E7A8-4460-A22C-683CBA322C32}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {AA7190E8-877F-4827-8CDD-E0D85F83C8C1} = {69439FD1-C07D-4BF1-98DC-3CCFECE53A49} + {262DF8CC-E7A8-4460-A22C-683CBA322C32} = {69439FD1-C07D-4BF1-98DC-3CCFECE53A49} EndGlobalSection EndGlobal diff --git a/build/VStudio/winfsp_dll.vcxproj b/build/VStudio/winfsp_dll.vcxproj index cc07e05c..2bdac853 100644 --- a/build/VStudio/winfsp_dll.vcxproj +++ b/build/VStudio/winfsp_dll.vcxproj @@ -102,7 +102,7 @@ Disabled WINFSP_DLL_INTERNAL;WIN32;_DEBUG;_WINDOWS;_USRDLL;WINFSPDLL_EXPORTS;%(PreprocessorDefinitions) true - ..\..\inc;..\..\src + ..\..\src;..\..\inc Windows @@ -117,7 +117,7 @@ Disabled WINFSP_DLL_INTERNAL;_DEBUG;_WINDOWS;_USRDLL;WINFSPDLL_EXPORTS;%(PreprocessorDefinitions) true - ..\..\inc;..\..\src + ..\..\src;..\..\inc Windows @@ -134,7 +134,7 @@ true WINFSP_DLL_INTERNAL;WIN32;NDEBUG;_WINDOWS;_USRDLL;WINFSPDLL_EXPORTS;%(PreprocessorDefinitions) true - ..\..\inc;..\..\src + ..\..\src;..\..\inc Windows @@ -153,7 +153,7 @@ true WINFSP_DLL_INTERNAL;NDEBUG;_WINDOWS;_USRDLL;WINFSPDLL_EXPORTS;%(PreprocessorDefinitions) true - ..\..\inc;..\..\src + ..\..\src;..\..\inc Windows diff --git a/build/VStudio/winfsp_sys.vcxproj b/build/VStudio/winfsp_sys.vcxproj index 06f38c94..3c3e7ac6 100644 --- a/build/VStudio/winfsp_sys.vcxproj +++ b/build/VStudio/winfsp_sys.vcxproj @@ -96,7 +96,7 @@ - ..\..\inc;..\..\src;%(AdditionalIncludeDirectories) + ..\..\src;..\..\inc;%(AdditionalIncludeDirectories) WINFSP_SYS_INTERNAL; _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions) @@ -105,7 +105,7 @@ - ..\..\inc;..\..\src;%(AdditionalIncludeDirectories) + ..\..\src;..\..\inc;%(AdditionalIncludeDirectories) WINFSP_SYS_INTERNAL; _X86_=1;i386=1;STD_CALL;%(PreprocessorDefinitions) @@ -114,7 +114,7 @@ - ..\..\inc;..\..\src;%(AdditionalIncludeDirectories) + ..\..\src;..\..\inc;%(AdditionalIncludeDirectories) WINFSP_SYS_INTERNAL; _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) @@ -123,7 +123,7 @@ - ..\..\inc;..\..\src;%(AdditionalIncludeDirectories) + ..\..\src;..\..\inc;%(AdditionalIncludeDirectories) WINFSP_SYS_INTERNAL; _WIN64;_AMD64_;AMD64;%(PreprocessorDefinitions) diff --git a/ext/tlib/Commit.txt b/ext/tlib/Commit.txt new file mode 100644 index 00000000..8a2253e6 --- /dev/null +++ b/ext/tlib/Commit.txt @@ -0,0 +1,2 @@ +Taked from secfs.core/src/tlib codebase: + commit 16296d2ac64a7d532b1c486dd3c44af5865d4851 diff --git a/ext/tlib/callstack.c b/ext/tlib/callstack.c new file mode 100644 index 00000000..68392116 --- /dev/null +++ b/ext/tlib/callstack.c @@ -0,0 +1,75 @@ +/** + * @file tlib/callstack.c + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +#include +#include +#include +#if defined(_WIN32) || defined(_WIN64) +#include +#include +#pragma comment(lib, "dbghelp.lib") +#elif defined(__APPLE__) || defined(__linux__) +#include +#include +#else +#error tlib/callstack.c not implemented for this platform +#endif + +void tlib_callstack(size_t skip, size_t count, struct tlib_callstack_s *stack) +{ + if (count > TLIB_MAX_SYMRET) + count = TLIB_MAX_SYMRET; + size_t naddrs = skip + count; + if (naddrs > TLIB_MAX_SYMCAP) + naddrs = TLIB_MAX_SYMCAP; + memset((void *)stack->syms, 0, sizeof stack->syms); +#if defined(_WIN32) || defined(_WIN64) + /* SymInitialize()/SymFromAddr() are not thread-safe. Furthermore SymInitialize() should + * not be called more than once per process. + */ + HANDLE hproc = GetCurrentProcess(); + static SYMBOL_INFO *syminfo = 0; + void *addrs[TLIB_MAX_SYMCAP]; + size_t i = 0; + if (0 == syminfo) + { + syminfo = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO) + TLIB_MAX_SYMLEN); + if (0 == syminfo) + return; + SymInitialize(hproc, 0, TRUE); + } + memset(syminfo, 0, sizeof(SYMBOL_INFO)); + syminfo->SizeOfStruct = sizeof(SYMBOL_INFO); + syminfo->MaxNameLen = TLIB_MAX_SYMLEN; + naddrs = CaptureStackBackTrace(0, naddrs, addrs, 0); + for (void **p = addrs + skip, **endp = addrs + naddrs; endp > p; p++) + { + DWORD64 displacement = 0; + if (SymFromAddr(hproc, (DWORD64)*p, &displacement, syminfo)) + { + stack->syms[i] = stack->symbuf[i]; + strncpy(stack->symbuf[i], syminfo->Name, TLIB_MAX_SYMLEN); + stack->symbuf[i][TLIB_MAX_SYMLEN] = '\0'; + i++; + } + } +#elif defined(__APPLE__) || defined(__linux__) + void *addrs[TLIB_MAX_SYMCAP]; + naddrs = backtrace(addrs, naddrs); + size_t i = 0; + Dl_info syminfo; + for (void **p = addrs + skip, **endp = addrs + naddrs; endp > p; p++) + { + if (dladdr(*p, &syminfo) && 0 != syminfo.dli_sname) + { + stack->syms[i] = stack->symbuf[i]; + strncpy(stack->symbuf[i], syminfo.dli_sname, TLIB_MAX_SYMLEN); + stack->symbuf[i][TLIB_MAX_SYMLEN] = '\0'; + i++; + } + } +#endif +} diff --git a/ext/tlib/callstack.h b/ext/tlib/callstack.h new file mode 100644 index 00000000..78f53e13 --- /dev/null +++ b/ext/tlib/callstack.h @@ -0,0 +1,26 @@ +/** + * @file tlib/callstack.h + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +#ifndef TLIB_CALLSTACK_H_INCLUDED +#define TLIB_CALLSTACK_H_INCLUDED + +#include + +enum +{ + TLIB_MAX_SYMLEN = 63, + TLIB_MAX_SYMRET = 8, + TLIB_MAX_SYMCAP = 62, /* max number of frames to capture (Windows restriction) */ +}; + +struct tlib_callstack_s +{ + const char *syms[TLIB_MAX_SYMRET + 1]; + char symbuf[TLIB_MAX_SYMRET][TLIB_MAX_SYMLEN + 1]; +}; +void tlib_callstack(size_t skip, size_t count, struct tlib_callstack_s *stack); + +#endif diff --git a/ext/tlib/injected/allfunc.h b/ext/tlib/injected/allfunc.h new file mode 100644 index 00000000..bf6a4aa3 --- /dev/null +++ b/ext/tlib/injected/allfunc.h @@ -0,0 +1,13 @@ +/** + * @file tlib/injected/allfunc.h + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +#ifndef TLIB_INJECTED_ALLFUNC_H_INCLUDED +#define TLIB_INJECTED_ALLFUNC_H_INCLUDED + +#include +#include + +#endif diff --git a/ext/tlib/injected/curlfunc.c b/ext/tlib/injected/curlfunc.c new file mode 100644 index 00000000..4830897d --- /dev/null +++ b/ext/tlib/injected/curlfunc.c @@ -0,0 +1,35 @@ +/** + * @file tlib/injected/curlfunc.c + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +#include +#define TLIB_INJECTIONS_ENABLED +#include + +#undef curl_easy_init +#undef curl_multi_init +#undef curl_multi_add_handle +#undef curl_multi_perform + +CURL *tlib_curl_easy_init(void) +{ + TLIB_INJECT("curl_easy_init", return 0); + return curl_easy_init(); +} +CURLM *tlib_curl_multi_init(void) +{ + TLIB_INJECT("curl_multi_init", return 0); + return curl_multi_init(); +} +CURLMcode tlib_curl_multi_add_handle(CURLM *mh, CURL *eh) +{ + TLIB_INJECT("curl_multi_add_handle", return CURLM_INTERNAL_ERROR); + return curl_multi_add_handle(mh, eh); +} +CURLMcode tlib_curl_multi_perform(CURLM *mh, int *running_handles) +{ + TLIB_INJECT("curl_multi_perform", return CURLM_INTERNAL_ERROR); + return curl_multi_perform(mh, running_handles); +} diff --git a/ext/tlib/injected/curlfunc.h b/ext/tlib/injected/curlfunc.h new file mode 100644 index 00000000..a8435e1e --- /dev/null +++ b/ext/tlib/injected/curlfunc.h @@ -0,0 +1,24 @@ +/** + * @file tlib/injected/curlfunc.h + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +#ifndef TLIB_INJECTED_CURLFUNC_H_INCLUDED +#define TLIB_INJECTED_CURLFUNC_H_INCLUDED + +#include + +#if defined(TLIB_INJECTIONS_ENABLED) +#define curl_easy_init() tlib_curl_easy_init() +#define curl_multi_init() tlib_curl_multi_init() +#define curl_multi_add_handle(mh, eh) tlib_curl_multi_add_handle(mh, eh) +#define curl_multi_perform(mh, rh) tlib_curl_multi_perform(mh, rh) +#endif + +CURL *tlib_curl_easy_init(void); +CURLM *tlib_curl_multi_init(void); +CURLMcode tlib_curl_multi_add_handle(CURLM *mh, CURL *eh); +CURLMcode tlib_curl_multi_perform(CURLM *mh, int *running_handles); + +#endif diff --git a/ext/tlib/injected/stdfunc.c b/ext/tlib/injected/stdfunc.c new file mode 100644 index 00000000..ce64c538 --- /dev/null +++ b/ext/tlib/injected/stdfunc.c @@ -0,0 +1,29 @@ +/** + * @file tlib/injected/stdfunc.c + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +#include +#define TLIB_INJECTIONS_ENABLED +#include + +#undef calloc +#undef malloc +#undef realloc + +void *tlib_calloc(size_t count, size_t size) +{ + TLIB_INJECT("calloc", return 0); + return calloc(count, size); +} +void *tlib_malloc(size_t size) +{ + TLIB_INJECT("malloc", return 0); + return malloc(size); +} +void *tlib_realloc(void *ptr, size_t size) +{ + TLIB_INJECT("realloc", return 0); + return realloc(ptr, size); +} diff --git a/ext/tlib/injected/stdfunc.h b/ext/tlib/injected/stdfunc.h new file mode 100644 index 00000000..8bbf93e1 --- /dev/null +++ b/ext/tlib/injected/stdfunc.h @@ -0,0 +1,23 @@ +/** + * @file tlib/injected/stdfunc.h + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +#ifndef TLIB_INJECTED_STDFUNC_H_INCLUDED +#define TLIB_INJECTED_STDFUNC_H_INCLUDED + +#include +#include + +#if defined(TLIB_INJECTIONS_ENABLED) +#define calloc(count, size) tlib_calloc(count, size) +#define malloc(size) tlib_malloc(size) +#define realloc(ptr, size) tlib_realloc(ptr, size) +#endif + +void *tlib_calloc(size_t count, size_t size); +void *tlib_malloc(size_t size); +void *tlib_realloc(void *ptr, size_t size); + +#endif diff --git a/ext/tlib/injection.c b/ext/tlib/injection.c new file mode 100644 index 00000000..11e5adba --- /dev/null +++ b/ext/tlib/injection.c @@ -0,0 +1,144 @@ +/** + * @file tlib/injection.c + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +#include +#include +#include +#include +#include + +#undef NDEBUG +#include + +#define NBUCKETS 256 +struct injection_cond_s +{ + struct injection_cond_s *cnext; + char *sym; + unsigned trigger, count; +}; +struct injection_entry_s +{ + struct injection_entry_s *hnext; + char *name; + struct injection_cond_s *clist; +}; +struct injection_htab_s +{ + struct injection_entry_s **buckets; +}; + +static inline size_t hash_chars(const char *s, size_t length) +{ + /* djb2: see http://www.cse.yorku.ca/~oz/hash.html */ + size_t h = 5381; + for (const char *t = s + length; t > s; ++s) + h = 33 * h + *s; + return h; +} +static struct injection_htab_s *injection_htab() +{ + static struct injection_htab_s *htab; + if (0 == htab) + { + htab = calloc(1, sizeof *htab); + assert(0 != htab); + htab->buckets = calloc(NBUCKETS, sizeof(struct injection_entry_s *)); + } + return htab; +} +static struct injection_entry_s *injection_lookup(const char *name) +{ + struct injection_htab_s *htab = injection_htab(); + size_t i = hash_chars(name, strlen(name)) & (NBUCKETS - 1); + for (struct injection_entry_s *entry = htab->buckets[i]; entry; entry = entry->hnext) + if (0 == strcmp(entry->name, name)) + return entry; + return 0; +} +static struct injection_entry_s *injection_insert(const char *name) +{ + struct injection_htab_s *htab = injection_htab(); + size_t i = hash_chars(name, strlen(name)) & (NBUCKETS - 1); + struct injection_entry_s *entry = calloc(1, sizeof *entry); + assert(0 != entry); + entry->name = strdup(name); + entry->hnext = htab->buckets[i]; + htab->buckets[i] = entry; + return entry; +} +static struct injection_cond_s *injection_cond_get(struct injection_entry_s *entry, const char **syms) +{ + struct injection_cond_s *deinjection_centry = 0; + for (struct injection_cond_s *centry = entry->clist; centry; centry = centry->cnext) + if ('*' == centry->sym[0] && '\0' == centry->sym[1]) + deinjection_centry = centry; + else + { + for (const char *sym; 0 != (sym = *syms); syms++) + if (0 == strcmp(centry->sym, sym)) + return centry; + } + return deinjection_centry; +} +static void injection_cond_set(struct injection_entry_s *entry, const char *sym, unsigned trigger) +{ + for (struct injection_cond_s *centry = entry->clist; centry; centry = centry->cnext) + if (0 == strcmp(centry->sym, sym)) + { + centry->trigger = trigger; + return; + } + struct injection_cond_s *centry = calloc(1, sizeof *centry); + assert(0 != centry); + centry->sym = strdup(sym); + centry->trigger = trigger; + centry->cnext = entry->clist; + entry->clist = centry; +} +static void injection_cond_remove(struct injection_entry_s *entry, const char *sym) +{ + struct injection_cond_s **p = &entry->clist; + for (; *p; p = &(*p)->cnext) + if (0 == strcmp((*p)->sym, sym)) + break; + if (*p) /* did we find the condition? */ + { + struct injection_cond_s *q = *p; + *p = q->cnext; + free(q->sym); + free(q); + } +} + +void *tlib_injection(const char *name) +{ + struct injection_entry_s *entry = injection_lookup(name); + if (0 == entry) + entry = injection_insert(name); + return entry; +} +int tlib_injection_trace(void *injection) +{ + if (0 == ((struct injection_entry_s *)injection)->clist) + return 0; + struct tlib_callstack_s stack; + tlib_callstack(2, TLIB_MAX_SYMRET, &stack); + struct injection_cond_s *centry = injection_cond_get(injection, stack.syms); + if (0 == centry) + return 0; + return centry->count++ == centry->trigger || ~0 == centry->trigger; +} +void tlib_injection_enable(const char *name, const char *sym, unsigned trigger) +{ + struct injection_entry_s *entry = tlib_injection(name); + injection_cond_set(entry, sym, trigger); +} +void tlib_injection_disable(const char *name, const char *sym) +{ + struct injection_entry_s *entry = tlib_injection(name); + injection_cond_remove(entry, sym); +} diff --git a/ext/tlib/injection.h b/ext/tlib/injection.h new file mode 100644 index 00000000..df1cd6c7 --- /dev/null +++ b/ext/tlib/injection.h @@ -0,0 +1,30 @@ +/** + * @file tlib/injection.h + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +/* NOTE: This header may usefully be included multiple times. + * The TLIB_INJECT() macro will be redefined based on whether + * TLIB_INJECTIONS_ENABLED is defined. + */ + +#undef TLIB_INJECT +#if defined(TLIB_INJECTIONS_ENABLED) +#define TLIB_INJECT(name, stmt) \ + do\ + {\ + static void *injection = 0;\ + if (0 == injection)\ + injection = tlib_injection(name);\ + if (tlib_injection_trace(injection))\ + stmt;\ + } while (0) +#else +#define TLIB_INJECT(name, stmt) do {} while (0) +#endif + +void *tlib_injection(const char *name); +int tlib_injection_trace(void *injection); +void tlib_injection_enable(const char *name, const char *sym, unsigned trigger); +void tlib_injection_disable(const char *name, const char *sym); diff --git a/ext/tlib/testsuite.c b/ext/tlib/testsuite.c new file mode 100644 index 00000000..5d9993cc --- /dev/null +++ b/ext/tlib/testsuite.c @@ -0,0 +1,202 @@ +/** + * @file tlib/testsuite.c + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct test +{ + char name[64]; + void (*fn)(void); + int optional; + struct test *next; +}; +static struct test test_suite_sentinel = { .next = &test_suite_sentinel }; +static struct test *test_suite_tail = &test_suite_sentinel; +static struct test test_sentinel = { .next = &test_sentinel }; +static struct test *test_tail = &test_sentinel; +static void add_test_to_list(const char *name, void (*fn)(void), int optional, struct test **tail) +{ + struct test *test = calloc(1, sizeof *test); + strncpy(test->name, name, sizeof test->name - 1); + test->name[sizeof test->name - 1] = '\0'; + test->fn = fn; + test->optional = optional; + test->next = (*tail)->next; + (*tail)->next = test; + (*tail) = test; +} +void tlib_add_test_suite(const char *name, void (*fn)(void)) +{ + add_test_to_list(name, fn, 0, &test_suite_tail); +} +void tlib_add_test(const char *name, void (*fn)(void)) +{ + add_test_to_list(name, fn, 0, &test_tail); +} +void tlib_add_test_opt(const char *name, void (*fn)(void)) +{ + add_test_to_list(name, fn, 1, &test_tail); +} + +static FILE *tlib_out, *tlib_err; +static jmp_buf test_jmp_buf, *test_jmp; +static char assert_buf[256]; +static void test_printf(const char *fmt, ...); +static double run_test(struct test *test) +{ + time_t t0 = time(0); + test->fn(); + time_t t1 = time(0); + return difftime(t1, t0); +} +static void do_test_default(struct test *test, int testno) +{ + if (0 != test) + { + snprintf(assert_buf, sizeof assert_buf, "KO\n "); + char dispname[39 + 1]; + size_t displen = strlen(test->name); + if (displen > sizeof dispname - 1) + displen = sizeof dispname - 1; + memcpy(dispname, test->name, displen); + memset(dispname + displen, '.', sizeof dispname - 1 - displen); + dispname[sizeof dispname - 1] = '\0'; + test_printf("%s ", dispname); + double d = run_test(test); + test_printf("OK %.0fs\n", d); + } + else + test_printf("--- COMPLETE ---\n"); +} +static void do_test_list(struct test *test, int testno) +{ + if (0 != test) + test_printf("%s\n", test->name); +} +static void do_test_tap(struct test *test, int testno) +{ + if (0 != test) + { + snprintf(assert_buf, sizeof assert_buf, "not ok %d %s\n# ", testno + 1, test->name); + run_test(test); + test_printf("ok %d %s\n", testno + 1, test->name); + } + else + test_printf("1..%d\n", testno); +} +static void test_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + FILE *f = tlib_out ? tlib_out : stdout; + vfprintf(f, fmt, ap); + fflush(f); + va_end(ap); +} +void tlib_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + FILE *f = tlib_err ? tlib_err : stderr; + vfprintf(f, fmt, ap); + fflush(f); + va_end(ap); +} +void tlib_run_tests(int argc, char *argv[]) +{ + argc--; argv++; + void (*do_test)(struct test *, int) = do_test_default; + int match_any = 1, no_abort = 0; + unsigned long repeat = 1; + for (char **ap = argv, **aendp = ap + argc; aendp > ap; ap++) + { + const char *a = *ap; + if ('-' == a[0]) + { + if (0 == strcmp("--list", a)) + do_test = do_test_list; + else if (0 == strcmp("--tap", a)) + do_test = do_test_tap; + else if (0 == strcmp("--no-abort", a)) + no_abort = 1; + else if (0 == strcmp("--repeat-forever", a)) + repeat = ULONG_MAX; + } + else + match_any = 0; + } + for (struct test *test = test_suite_tail->next->next; 0 != test->fn; test = test->next) + test->fn(); + while (repeat--) + { + int testno = 0; + for (struct test *test = test_tail->next->next; 0 != test->fn; test = test->next) + { + int match_arg = match_any && !test->optional; + for (char **ap = argv, **aendp = ap + argc; aendp > ap; aendp--) + { + const char *a = aendp[-1]; + int sign = a[0]; + if ('+' == sign) + a++; + else if ('-' == sign) + { + if ('-' == a[1]) + continue; + a++; + } + size_t l = strlen(a); + if (0 == (0 < l && '*' == a[l - 1] ? + strncmp(test->name, a, l - 1) : strcmp(test->name, a))) + { + if ('+' == sign) + match_arg = 1; + else if ('-' == sign) + match_arg = 0; + else + match_arg = !test->optional; + break; + } + } + if (!match_arg) + continue; + assert_buf[0] = '\0'; + if (no_abort) + { + test_jmp = &test_jmp_buf; + if (0 == setjmp(*test_jmp)) + do_test(test, testno); + test_jmp = 0; + } + else + do_test(test, testno); + testno++; + } + do_test(0, testno); + } +} +void tlib__assert(const char *func, const char *file, int line, const char *expr) +{ +#if defined(_WIN64) || defined(_WIN32) + const char *p = strrchr(file, '\\'); +#else + const char *p = strrchr(file, '/'); +#endif + file = 0 != p ? p + 1 : file; + if (0 == func) + test_printf("%sASSERT(%s) failed at: %s:%d\n", assert_buf, expr, file, line); + else + test_printf("%sASSERT(%s) failed at %s:%d:%s\n", assert_buf, expr, file, line, func); + if (0 != test_jmp) + longjmp(*test_jmp, 1); +} diff --git a/ext/tlib/testsuite.h b/ext/tlib/testsuite.h new file mode 100644 index 00000000..db8d2ac7 --- /dev/null +++ b/ext/tlib/testsuite.h @@ -0,0 +1,112 @@ +/** + * @file tlib/testsuite.h + * + * @copyright 2014-2015 Bill Zissimopoulos + */ + +#ifndef TLIB_TESTSUITE_H_INCLUDED +#define TLIB_TESTSUITE_H_INCLUDED + +#include + +/** + * Assert macro + * + * This macro works similarly to the Standard C assert macro, except for the following differences: + * + *
    + *
  • The macro always checks the specified expression regardless of the NDEBUG macro.
  • + *
  • The macro aborts the execution of the current test, but not necessarily the execution of the + * whole testsuite.
  • + *
+ */ +#define ASSERT(expr)\ + (!(expr) ? (tlib__assert(__func__, __FILE__, __LINE__, #expr), abort()) : (void)0) + +/** + * Register a test suite for execution. + * + * Test suites are simple functions with prototype void testsuite(). When executed + * they register individual test cases for execution. + */ +#define TESTSUITE(fn)\ + do\ + {\ + void fn(void);\ + tlib_add_test_suite(#fn, fn);\ + } while (0) + +/** + * Register a test case for execution. + * + * Test cases are simple functions with prototype void testcase(). + */ +#define TEST(fn)\ + do\ + {\ + void fn(void);\ + tlib_add_test(#fn, fn);\ + } while (0) + +/** + * Register an optional test case for execution. + * + * Test cases are simple functions with prototype void testcase(). + * Optional tests are not executed by default. + */ +#define TEST_OPT(fn)\ + do\ + {\ + void fn(void);\ + tlib_add_test_opt(#fn, fn);\ + } while (0) + +void tlib_add_test_suite(const char *name, void (*fn)(void)); +void tlib_add_test(const char *name, void (*fn)(void)); +void tlib_add_test_opt(const char *name, void (*fn)(void)); + +/** + * Printf function. + * + * Use this to produce output in the appropriate tlib file stream. This function uses the local + * printf facilities and understands the same format strings. + */ +#if defined(__GNUC__) +void tlib_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +#else +void tlib_printf(const char *fmt, ...); +#endif +/** + * Run tests. + * + * This function will first execute all registered test suites, thus giving them the chance to + * register any test cases. It will then execute all registered test cases according to the + * command line arguments passed in argv. The command line syntax is a follows: + * + * Usage: testprog [--list][[--tap][--no-abort][--repeat-forever] [[+-]TESTNAME...] + * + *
    + *
  • --list - list tests only
  • + *
  • --tap - produce output in TAP format
  • + *
  • --no-abort - do not abort all tests when an ASSERT fails + * (only the current test is aborted)
  • + *
  • --repeat-forever - repeat tests forever
  • + *
+ * + * By default all test cases are executed unless specific test cases are named. By default optional + * test cases are not executed. To execute a specific test case specify its TESTNAME; if it is an + * optional test case specify +TESTNAME. To excluse a test case specify -TESTNAME. + * + * TESTNAME may also contain a single asterisk at the end; for example, mytest* will match all test + * cases that have names starting with "mytest". + * + * @param argc + * Argument count. + * @param argv + * Argument vector. + */ +void tlib_run_tests(int argc, char *argv[]); + +void tlib__assert(const char *func, const char *file, int line, const char *expr); + +#endif diff --git a/tst/winfsp-tests/mount-test.c b/tst/winfsp-tests/mount-test.c new file mode 100644 index 00000000..a0852ac9 --- /dev/null +++ b/tst/winfsp-tests/mount-test.c @@ -0,0 +1,11 @@ +#include +#include + +void mount_test(void) +{ +} + +void mount_tests(void) +{ + TEST(mount_test); +} diff --git a/tst/winfsp-tests/winfsp-tests.c b/tst/winfsp-tests/winfsp-tests.c new file mode 100644 index 00000000..5169683e --- /dev/null +++ b/tst/winfsp-tests/winfsp-tests.c @@ -0,0 +1,8 @@ +#include + +int main(int argc, char *argv[]) +{ + TESTSUITE(mount_tests); + tlib_run_tests(argc, argv); + return 0; +}