tests: move clar to deps

Clean up the `tests` folder to only contain _actual tests_. Since clar
is now a reasonably external project, move it to `deps`.
This commit is contained in:
Edward Thomson
2026-01-10 14:05:19 +00:00
parent 77028ee987
commit 1424585fb6
20 changed files with 2 additions and 4 deletions

1055
deps/clar/clar.c vendored Normal file

File diff suppressed because it is too large Load Diff

292
deps/clar/clar.h vendored Normal file
View File

@@ -0,0 +1,292 @@
/*
* Copyright (c) Vicent Marti. All rights reserved.
*
* This file is part of clar, distributed under the ISC license.
* For full terms see the included COPYING file.
*/
#ifndef __CLAR_TEST_H__
#define __CLAR_TEST_H__
#include <inttypes.h>
#include <stdlib.h>
#include <limits.h>
#if defined(_WIN32) && defined(CLAR_WIN32_LONGPATHS)
# define CLAR_MAX_PATH 4096
#elif defined(_WIN32)
# define CLAR_MAX_PATH MAX_PATH
#elif defined(PATH_MAX)
# define CLAR_MAX_PATH PATH_MAX
#else
# define CLAR_MAX_PATH 4096
#endif
#ifndef CLAR_SELFTEST
# define CLAR_CURRENT_FILE __FILE__
# define CLAR_CURRENT_LINE __LINE__
# define CLAR_CURRENT_FUNC __func__
#else
# define CLAR_CURRENT_FILE "file"
# define CLAR_CURRENT_LINE 42
# define CLAR_CURRENT_FUNC "func"
#endif
enum cl_test_status {
CL_TEST_OK,
CL_TEST_FAILURE,
CL_TEST_SKIP,
CL_TEST_NOTRUN,
};
enum cl_output_format {
CL_OUTPUT_CLAP,
CL_OUTPUT_TAP,
};
/** Setup clar environment */
void clar_test_init(int argc, char *argv[]);
int clar_test_run(void);
void clar_test_shutdown(void);
/** One shot setup & run */
int clar_test(int argc, char *argv[]);
const char *clar_sandbox_path(void);
const char *clar_tempdir_path(void);
void cl_set_cleanup(void (*cleanup)(void *), void *opaque);
void cl_fs_cleanup(void);
/**
* cl_trace_* is a hook to provide a simple global tracing
* mechanism.
*
* The goal here is to let main() provide clar-proper
* with a callback to optionally write log info for
* test operations into the same stream used by their
* actual tests. This would let them print test names
* and maybe performance data as they choose.
*
* The goal is NOT to alter the flow of control or to
* override test selection/skipping. (So the callback
* does not return a value.)
*
* The goal is NOT to duplicate the existing
* pass/fail/skip reporting. (So the callback
* does not accept a status/errorcode argument.)
*
*/
typedef enum cl_trace_event {
CL_TRACE__SUITE_BEGIN,
CL_TRACE__SUITE_END,
CL_TRACE__TEST__BEGIN,
CL_TRACE__TEST__END,
CL_TRACE__TEST__RUN_BEGIN,
CL_TRACE__TEST__RUN_END,
CL_TRACE__TEST__LONGJMP,
} cl_trace_event;
typedef void (cl_trace_cb)(
cl_trace_event ev,
const char *suite_name,
const char *test_name,
void *payload);
/**
* Register a callback into CLAR to send global trace events.
* Pass NULL to disable.
*/
void cl_trace_register(cl_trace_cb *cb, void *payload);
#ifdef CLAR_FIXTURE_PATH
const char *cl_fixture(const char *fixture_name);
void cl_fixture_sandbox(const char *fixture_name);
void cl_fixture_cleanup(const char *fixture_name);
const char *cl_fixture_basename(const char *fixture_name);
#endif
/**
* Invoke a helper function, which itself will use `cl_assert`
* constructs. This will preserve the stack information of the
* current call point, so that function name and line number
* information is shown from the line of the test, instead of
* the helper function.
*/
#define cl_invoke(expr) \
do { \
clar__set_invokepoint(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE); \
expr; \
clar__clear_invokepoint(); \
} while(0)
/**
* Assertion macros with explicit error message
*/
#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Function call failed: " #expr, desc, 1)
#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expected function call to fail: " #expr, desc, 1)
#define cl_assert_(expr, desc) clar__assert((expr) != 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expression is not true: " #expr, desc, 1)
/**
* Check macros with explicit error message
*/
#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Function call failed: " #expr, desc, 0)
#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expected function call to fail: " #expr, desc, 0)
#define cl_check_(expr, desc) clar__assert((expr) != 0, CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Expression is not true: " #expr, desc, 0)
/**
* Assertion macros with no error message
*/
#define cl_must_pass(expr) cl_must_pass_(expr, NULL)
#define cl_must_fail(expr) cl_must_fail_(expr, NULL)
#define cl_assert(expr) cl_assert_(expr, NULL)
/**
* Check macros with no error message
*/
#define cl_check_pass(expr) cl_check_pass_(expr, NULL)
#define cl_check_fail(expr) cl_check_fail_(expr, NULL)
#define cl_check(expr) cl_check_(expr, NULL)
/**
* Forced failure/warning
*/
#define cl_fail(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Test failed.", desc, 1)
#define cl_failf(desc,...) clar__failf(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, "Test failed.", desc, __VA_ARGS__)
#define cl_warning(desc) clar__fail(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, "Warning during test execution:", desc, 0)
#define cl_skip() clar__skip()
/**
* Typed assertion macros
*/
#define cl_assert_equal_s(s1,s2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2))
#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2))
#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len))
#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len))
#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))
#define cl_assert_compare_i_(i1, i2, cmp, error, ...) clar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \
(i1), (i2), "Expected comparison to hold: " error, __VA_ARGS__)
#define cl_assert_compare_i(i1, i2, cmp, error, fmt) do { \
intmax_t v1 = (i1), v2 = (i2); \
clar__assert_compare_i(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \
v1, v2, "Expected comparison to hold: " error, fmt, v1, v2); \
} while (0)
#define cl_assert_equal_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 " == " #i2, __VA_ARGS__)
#define cl_assert_equal_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_EQ, #i1 " == " #i2, "%"PRIdMAX " != %"PRIdMAX)
#define cl_assert_equal_i_fmt(i1, i2, fmt) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_EQ, #i1 " == " #i2, fmt " != " fmt, (int)(i1), (int)(i2))
#define cl_assert_lt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LT, #i1 " < " #i2, __VA_ARGS__)
#define cl_assert_lt_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LT, #i1 " < " #i2, "%"PRIdMAX " >= %"PRIdMAX)
#define cl_assert_le_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_LE, #i1 " <= " #i2, __VA_ARGS__)
#define cl_assert_le_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_LE, #i1 " <= " #i2, "%"PRIdMAX " > %"PRIdMAX)
#define cl_assert_gt_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GT, #i1 " > " #i2, __VA_ARGS__)
#define cl_assert_gt_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GT, #i1 " > " #i2, "%"PRIdMAX " <= %"PRIdMAX)
#define cl_assert_ge_i_(i1, i2, ...) cl_assert_compare_i_(i1, i2, CLAR_COMPARISON_GE, #i1 " >= " #i2, __VA_ARGS__)
#define cl_assert_ge_i(i1, i2) cl_assert_compare_i (i1, i2, CLAR_COMPARISON_GE, #i1 " >= " #i2, "%"PRIdMAX " < %"PRIdMAX)
#define cl_assert_compare_u_(u1, u2, cmp, error, ...) clar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \
(u1), (u2), "Expected comparison to hold: " error, __VA_ARGS__)
#define cl_assert_compare_u(u1, u2, cmp, error, fmt) do { \
uintmax_t v1 = (u1), v2 = (u2); \
clar__assert_compare_u(CLAR_CURRENT_FILE, CLAR_CURRENT_FUNC, CLAR_CURRENT_LINE, 1, cmp, \
v1, v2, "Expected comparison to hold: " error, fmt, v1, v2); \
} while (0)
#define cl_assert_equal_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_EQ, #u1 " == " #u2, __VA_ARGS__)
#define cl_assert_equal_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_EQ, #u1 " == " #u2, "%"PRIuMAX " != %"PRIuMAX)
#define cl_assert_lt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LT, #u1 " < " #u2, __VA_ARGS__)
#define cl_assert_lt_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LT, #u1 " < " #u2, "%"PRIuMAX " >= %"PRIuMAX)
#define cl_assert_le_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_LE, #u1 " <= " #u2, __VA_ARGS__)
#define cl_assert_le_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_LE, #u1 " <= " #u2, "%"PRIuMAX " > %"PRIuMAX)
#define cl_assert_gt_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GT, #u1 " > " #u2, __VA_ARGS__)
#define cl_assert_gt_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GT, #u1 " > " #u2, "%"PRIuMAX " <= %"PRIuMAX)
#define cl_assert_ge_u_(u1, u2, ...) cl_assert_compare_u_(u1, u2, CLAR_COMPARISON_GE, #u1 " >= " #u2, __VA_ARGS__)
#define cl_assert_ge_u(u1, u2) cl_assert_compare_u (u1, u2, CLAR_COMPARISON_GE, #u1 " >= " #u2, "%"PRIuMAX " < %"PRIuMAX)
#define cl_assert_equal_b(b1,b2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))
#define cl_assert_equal_p(p1,p2) clar__assert_equal(CLAR_CURRENT_FILE,CLAR_CURRENT_FUNC,CLAR_CURRENT_LINE,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))
void clar__skip(void);
void clar__fail(
const char *file,
const char *func,
size_t line,
const char *error,
const char *description,
int should_abort);
void clar__failf(
const char *file,
const char *func,
size_t line,
int should_abort,
const char *error,
const char *description,
...);
void clar__assert(
int condition,
const char *file,
const char *func,
size_t line,
const char *error,
const char *description,
int should_abort);
void clar__assert_equal(
const char *file,
const char *func,
size_t line,
const char *err,
int should_abort,
const char *fmt,
...);
enum clar_comparison {
CLAR_COMPARISON_EQ,
CLAR_COMPARISON_LT,
CLAR_COMPARISON_LE,
CLAR_COMPARISON_GT,
CLAR_COMPARISON_GE,
};
void clar__assert_compare_i(
const char *file,
const char *func,
size_t line,
int should_abort,
enum clar_comparison cmp,
intmax_t value1,
intmax_t value2,
const char *error,
const char *description,
...);
void clar__assert_compare_u(
const char *file,
const char *func,
size_t line,
int should_abort,
enum clar_comparison cmp,
uintmax_t value1,
uintmax_t value2,
const char *error,
const char *description,
...);
void clar__set_invokepoint(
const char *file,
const char *func,
size_t line);
void clar__clear_invokepoint(void);
#endif

50
deps/clar/clar/fixtures.h vendored Normal file
View File

@@ -0,0 +1,50 @@
#ifdef CLAR_FIXTURE_PATH
static const char *
fixture_path(const char *base, const char *fixture_name)
{
static char _path[CLAR_MAX_PATH];
size_t root_len;
root_len = strlen(base);
strncpy(_path, base, sizeof(_path));
if (_path[root_len - 1] != '/')
_path[root_len++] = '/';
if (fixture_name[0] == '/')
fixture_name++;
strncpy(_path + root_len,
fixture_name,
sizeof(_path) - root_len);
return _path;
}
const char *cl_fixture(const char *fixture_name)
{
return fixture_path(CLAR_FIXTURE_PATH, fixture_name);
}
void cl_fixture_sandbox(const char *fixture_name)
{
fs_copy(cl_fixture(fixture_name), clar_sandbox_path());
}
const char *cl_fixture_basename(const char *fixture_name)
{
const char *p;
for (p = fixture_name; *p; p++) {
if (p[0] == '/' && p[1] && p[1] != '/')
fixture_name = p+1;
}
return fixture_name;
}
void cl_fixture_cleanup(const char *fixture_name)
{
fs_rm(fixture_path(clar_sandbox_path(), cl_fixture_basename(fixture_name)));
}
#endif

529
deps/clar/clar/fs.h vendored Normal file
View File

@@ -0,0 +1,529 @@
/*
* By default, use a read/write loop to copy files on POSIX systems.
* On Linux, use sendfile by default as it's slightly faster. On
* macOS, we avoid fcopyfile by default because it's slightly slower.
*/
#undef USE_FCOPYFILE
#define USE_SENDFILE 1
#ifdef _WIN32
#define RM_RETRY_COUNT 5
#define RM_RETRY_DELAY 10
#ifdef __MINGW32__
/* These security-enhanced functions are not available
* in MinGW, so just use the vanilla ones */
#define wcscpy_s(a, b, c) wcscpy((a), (c))
#define wcscat_s(a, b, c) wcscat((a), (c))
#endif /* __MINGW32__ */
static int
fs__dotordotdot(WCHAR *_tocheck)
{
return _tocheck[0] == '.' &&
(_tocheck[1] == '\0' ||
(_tocheck[1] == '.' && _tocheck[2] == '\0'));
}
static int
fs_rmdir_rmdir(WCHAR *_wpath)
{
unsigned retries = 1;
while (!RemoveDirectoryW(_wpath)) {
/* Only retry when we have retries remaining, and the
* error was ERROR_DIR_NOT_EMPTY. */
if (retries++ > RM_RETRY_COUNT ||
ERROR_DIR_NOT_EMPTY != GetLastError())
return -1;
/* Give whatever has a handle to a child item some time
* to release it before trying again */
Sleep(RM_RETRY_DELAY * retries * retries);
}
return 0;
}
static void translate_path(WCHAR *path, size_t path_size)
{
size_t path_len, i;
if (wcsncmp(path, L"\\\\?\\", 4) == 0)
return;
path_len = wcslen(path);
cl_assert(path_size > path_len + 4);
for (i = path_len; i > 0; i--) {
WCHAR c = path[i - 1];
if (c == L'/')
path[i + 3] = L'\\';
else
path[i + 3] = path[i - 1];
}
path[0] = L'\\';
path[1] = L'\\';
path[2] = L'?';
path[3] = L'\\';
path[path_len + 4] = L'\0';
}
static void
fs_rmdir_helper(WCHAR *_wsource)
{
WCHAR buffer[CLAR_MAX_PATH];
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
size_t buffer_prefix_len;
/* Set up the buffer and capture the length */
wcscpy_s(buffer, CLAR_MAX_PATH, _wsource);
translate_path(buffer, CLAR_MAX_PATH);
wcscat_s(buffer, CLAR_MAX_PATH, L"\\");
buffer_prefix_len = wcslen(buffer);
/* FindFirstFile needs a wildcard to match multiple items */
wcscat_s(buffer, CLAR_MAX_PATH, L"*");
find_handle = FindFirstFileW(buffer, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
do {
/* FindFirstFile/FindNextFile gives back . and ..
* entries at the beginning */
if (fs__dotordotdot(find_data.cFileName))
continue;
wcscpy_s(buffer + buffer_prefix_len, CLAR_MAX_PATH - buffer_prefix_len, find_data.cFileName);
if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
fs_rmdir_helper(buffer);
else {
/* If set, the +R bit must be cleared before deleting */
if (FILE_ATTRIBUTE_READONLY & find_data.dwFileAttributes)
cl_assert(SetFileAttributesW(buffer, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY));
cl_assert(DeleteFileW(buffer));
}
}
while (FindNextFileW(find_handle, &find_data));
/* Ensure that we successfully completed the enumeration */
cl_assert(ERROR_NO_MORE_FILES == GetLastError());
/* Close the find handle */
FindClose(find_handle);
/* Now that the directory is empty, remove it */
cl_assert(0 == fs_rmdir_rmdir(_wsource));
}
static int
fs_rm_wait(WCHAR *_wpath)
{
unsigned retries = 1;
DWORD last_error;
do {
if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(_wpath))
last_error = GetLastError();
else
last_error = ERROR_SUCCESS;
/* Is the item gone? */
if (ERROR_FILE_NOT_FOUND == last_error ||
ERROR_PATH_NOT_FOUND == last_error)
return 0;
Sleep(RM_RETRY_DELAY * retries * retries);
}
while (retries++ <= RM_RETRY_COUNT);
return -1;
}
static void
fs_rm(const char *_source)
{
WCHAR wsource[CLAR_MAX_PATH];
DWORD attrs;
/* The input path is UTF-8. Convert it to wide characters
* for use with the Windows API */
cl_assert(MultiByteToWideChar(CP_UTF8,
MB_ERR_INVALID_CHARS,
_source,
-1, /* Indicates NULL termination */
wsource,
CLAR_MAX_PATH));
translate_path(wsource, CLAR_MAX_PATH);
/* Does the item exist? If not, we have no work to do */
attrs = GetFileAttributesW(wsource);
if (INVALID_FILE_ATTRIBUTES == attrs)
return;
if (FILE_ATTRIBUTE_DIRECTORY & attrs)
fs_rmdir_helper(wsource);
else {
/* The item is a file. Strip the +R bit */
if (FILE_ATTRIBUTE_READONLY & attrs)
cl_assert(SetFileAttributesW(wsource, attrs & ~FILE_ATTRIBUTE_READONLY));
cl_assert(DeleteFileW(wsource));
}
/* Wait for the DeleteFile or RemoveDirectory call to complete */
cl_assert(0 == fs_rm_wait(wsource));
}
static void
fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest)
{
WCHAR buf_source[CLAR_MAX_PATH], buf_dest[CLAR_MAX_PATH];
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
size_t buf_source_prefix_len, buf_dest_prefix_len;
wcscpy_s(buf_source, CLAR_MAX_PATH, _wsource);
wcscat_s(buf_source, CLAR_MAX_PATH, L"\\");
translate_path(buf_source, CLAR_MAX_PATH);
buf_source_prefix_len = wcslen(buf_source);
wcscpy_s(buf_dest, CLAR_MAX_PATH, _wdest);
wcscat_s(buf_dest, CLAR_MAX_PATH, L"\\");
translate_path(buf_dest, CLAR_MAX_PATH);
buf_dest_prefix_len = wcslen(buf_dest);
/* Get an enumerator for the items in the source. */
wcscat_s(buf_source, CLAR_MAX_PATH, L"*");
find_handle = FindFirstFileW(buf_source, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
/* Create the target directory. */
cl_assert(CreateDirectoryW(_wdest, NULL));
do {
/* FindFirstFile/FindNextFile gives back . and ..
* entries at the beginning */
if (fs__dotordotdot(find_data.cFileName))
continue;
wcscpy_s(buf_source + buf_source_prefix_len, CLAR_MAX_PATH - buf_source_prefix_len, find_data.cFileName);
wcscpy_s(buf_dest + buf_dest_prefix_len, CLAR_MAX_PATH - buf_dest_prefix_len, find_data.cFileName);
if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
fs_copydir_helper(buf_source, buf_dest);
else
cl_assert(CopyFileW(buf_source, buf_dest, TRUE));
}
while (FindNextFileW(find_handle, &find_data));
/* Ensure that we successfully completed the enumeration */
cl_assert(ERROR_NO_MORE_FILES == GetLastError());
/* Close the find handle */
FindClose(find_handle);
}
static void
fs_copy(const char *_source, const char *_dest)
{
WCHAR wsource[CLAR_MAX_PATH], wdest[CLAR_MAX_PATH];
DWORD source_attrs, dest_attrs;
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
/* The input paths are UTF-8. Convert them to wide characters
* for use with the Windows API. */
cl_assert(MultiByteToWideChar(CP_UTF8,
MB_ERR_INVALID_CHARS,
_source,
-1,
wsource,
CLAR_MAX_PATH));
cl_assert(MultiByteToWideChar(CP_UTF8,
MB_ERR_INVALID_CHARS,
_dest,
-1,
wdest,
CLAR_MAX_PATH));
translate_path(wsource, CLAR_MAX_PATH);
translate_path(wdest, CLAR_MAX_PATH);
/* Check the source for existence */
source_attrs = GetFileAttributesW(wsource);
cl_assert(INVALID_FILE_ATTRIBUTES != source_attrs);
/* Check the target for existence */
dest_attrs = GetFileAttributesW(wdest);
if (INVALID_FILE_ATTRIBUTES != dest_attrs) {
/* Target exists; append last path part of source to target.
* Use FindFirstFile to parse the path */
find_handle = FindFirstFileW(wsource, &find_data);
cl_assert(INVALID_HANDLE_VALUE != find_handle);
wcscat_s(wdest, CLAR_MAX_PATH, L"\\");
wcscat_s(wdest, CLAR_MAX_PATH, find_data.cFileName);
FindClose(find_handle);
/* Check the new target for existence */
cl_assert(INVALID_FILE_ATTRIBUTES == GetFileAttributesW(wdest));
}
if (FILE_ATTRIBUTE_DIRECTORY & source_attrs)
fs_copydir_helper(wsource, wdest);
else
cl_assert(CopyFileW(wsource, wdest, TRUE));
}
void
cl_fs_cleanup(void)
{
#ifdef CLAR_FIXTURE_PATH
fs_rm(fixture_path(clar_tempdir_path(), "*"));
#else
((void)fs_copy); /* unused */
#endif
}
#else
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(__linux__)
# include <sys/sendfile.h>
#endif
#if defined(__APPLE__)
# include <copyfile.h>
#endif
static void basename_r(const char **out, int *out_len, const char *in)
{
size_t in_len = strlen(in), start_pos;
for (in_len = strlen(in); in_len; in_len--) {
if (in[in_len - 1] != '/')
break;
}
for (start_pos = in_len; start_pos; start_pos--) {
if (in[start_pos - 1] == '/')
break;
}
cl_assert(in_len - start_pos < INT_MAX);
if (in_len - start_pos > 0) {
*out = &in[start_pos];
*out_len = (in_len - start_pos);
} else {
*out = "/";
*out_len = 1;
}
}
static char *joinpath(const char *dir, const char *base, int base_len)
{
char *out;
int len;
if (base_len == -1) {
size_t bl = strlen(base);
cl_assert(bl < INT_MAX);
base_len = (int)bl;
}
len = strlen(dir) + base_len + 2;
cl_assert(len > 0);
cl_assert(out = malloc(len));
cl_assert(snprintf(out, len, "%s/%.*s", dir, base_len, base) < len);
return out;
}
static void
fs_copydir_helper(const char *source, const char *dest, int dest_mode)
{
DIR *source_dir;
mkdir(dest, dest_mode);
cl_assert_(source_dir = opendir(source), "Could not open source dir");
while (1) {
struct dirent *d;
char *child;
errno = 0;
d = readdir(source_dir);
if (!d)
break;
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
child = joinpath(source, d->d_name, -1);
fs_copy(child, dest);
free(child);
}
cl_assert_(errno == 0, "Failed to iterate source dir");
closedir(source_dir);
}
static void
fs_copyfile_helper(const char *source, size_t source_len, const char *dest, int dest_mode)
{
int in, out;
cl_must_pass((in = open(source, O_RDONLY)));
cl_must_pass((out = open(dest, O_WRONLY|O_CREAT|O_TRUNC, dest_mode)));
#if USE_FCOPYFILE && defined(__APPLE__)
((void)(source_len)); /* unused */
cl_must_pass(fcopyfile(in, out, 0, COPYFILE_DATA));
#elif USE_SENDFILE && defined(__linux__)
{
ssize_t ret = 0;
while (source_len && (ret = sendfile(out, in, NULL, source_len)) > 0) {
source_len -= (size_t)ret;
}
cl_assert(ret >= 0);
}
#else
{
char buf[131072];
ssize_t ret;
((void)(source_len)); /* unused */
while ((ret = read(in, buf, sizeof(buf))) > 0) {
size_t len = (size_t)ret;
while (len && (ret = write(out, buf, len)) > 0) {
cl_assert(ret <= (ssize_t)len);
len -= ret;
}
cl_assert(ret >= 0);
}
cl_assert(ret == 0);
}
#endif
close(in);
close(out);
}
static void
fs_copy(const char *source, const char *_dest)
{
char *dbuf = NULL;
const char *dest = NULL;
struct stat source_st, dest_st;
cl_must_pass_(lstat(source, &source_st), "Failed to stat copy source");
if (lstat(_dest, &dest_st) == 0) {
const char *base;
int base_len;
/* Target exists and is directory; append basename */
cl_assert(S_ISDIR(dest_st.st_mode));
basename_r(&base, &base_len, source);
cl_assert(base_len < INT_MAX);
dbuf = joinpath(_dest, base, base_len);
dest = dbuf;
} else if (errno != ENOENT) {
cl_fail("Cannot copy; cannot stat destination");
} else {
dest = _dest;
}
if (S_ISDIR(source_st.st_mode)) {
fs_copydir_helper(source, dest, source_st.st_mode);
} else {
fs_copyfile_helper(source, source_st.st_size, dest, source_st.st_mode);
}
free(dbuf);
}
static void
fs_rmdir_helper(const char *path)
{
DIR *dir;
cl_assert_(dir = opendir(path), "Could not open dir");
while (1) {
struct dirent *d;
char *child;
errno = 0;
d = readdir(dir);
if (!d)
break;
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
continue;
child = joinpath(path, d->d_name, -1);
fs_rm(child);
free(child);
}
cl_assert_(errno == 0, "Failed to iterate source dir");
closedir(dir);
cl_must_pass_(rmdir(path), "Could not remove directory");
}
static void
fs_rm(const char *path)
{
struct stat st;
if (lstat(path, &st)) {
if (errno == ENOENT)
return;
cl_fail("Cannot copy; cannot stat destination");
}
if (S_ISDIR(st.st_mode)) {
fs_rmdir_helper(path);
} else {
cl_must_pass(unlink(path));
}
}
void
cl_fs_cleanup(void)
{
clar_tempdir_shutdown();
clar_tempdir_init();
}
#endif

245
deps/clar/clar/print.h vendored Normal file
View File

@@ -0,0 +1,245 @@
/* clap: clar protocol, the traditional clar output format */
static void clar_print_clap_init(int test_count, int suite_count)
{
(void)test_count;
if (_clar.verbosity < 0)
return;
printf("Loaded %d suites:\n", (int)suite_count);
printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n");
}
static void clar_print_clap_shutdown(int test_count, int suite_count, int error_count)
{
(void)test_count;
(void)suite_count;
(void)error_count;
if (_clar.verbosity >= 0)
printf("\n\n");
clar_report_all();
}
static void clar_print_indented(const char *str, int indent)
{
const char *bol, *eol;
for (bol = str; *bol; bol = eol) {
eol = strchr(bol, '\n');
if (eol)
eol++;
else
eol = bol + strlen(bol);
printf("%*s%.*s", indent, "", (int)(eol - bol), bol);
}
putc('\n', stdout);
}
static void clar_print_clap_error(int num, const struct clar_report *report, const struct clar_error *error)
{
printf(" %d) Failure:\n", num);
printf("%s::%s [%s:%"PRIuMAX"]\n",
report->suite,
report->test,
error->file,
error->line_number);
clar_print_indented(error->error_msg, 2);
if (error->description != NULL)
clar_print_indented(error->description, 2);
printf("\n");
fflush(stdout);
}
static void clar_print_clap_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status status)
{
(void)test_name;
(void)test_number;
if (_clar.verbosity < 0)
return;
if (_clar.verbosity > 1) {
printf("%s::%s: ", suite_name, test_name);
switch (status) {
case CL_TEST_OK: printf("ok\n"); break;
case CL_TEST_FAILURE: printf("fail\n"); break;
case CL_TEST_SKIP: printf("skipped\n"); break;
case CL_TEST_NOTRUN: printf("notrun\n"); break;
}
} else {
switch (status) {
case CL_TEST_OK: printf("."); break;
case CL_TEST_FAILURE: printf("F"); break;
case CL_TEST_SKIP: printf("S"); break;
case CL_TEST_NOTRUN: printf("N"); break;
}
fflush(stdout);
}
}
static void clar_print_clap_onsuite(const char *suite_name, int suite_index)
{
if (_clar.verbosity < 0)
return;
if (_clar.verbosity == 1)
printf("\n%s", suite_name);
(void)suite_index;
}
static void clar_print_clap_onabort(const char *fmt, va_list arg)
{
vfprintf(stderr, fmt, arg);
}
/* tap: test anywhere protocol format */
static void clar_print_tap_init(int test_count, int suite_count)
{
(void)test_count;
(void)suite_count;
printf("TAP version 13\n");
}
static void clar_print_tap_shutdown(int test_count, int suite_count, int error_count)
{
(void)suite_count;
(void)error_count;
printf("1..%d\n", test_count);
}
static void clar_print_tap_error(int num, const struct clar_report *report, const struct clar_error *error)
{
(void)num;
(void)report;
(void)error;
}
static void print_escaped(const char *str)
{
const char *c;
while ((c = strchr(str, '\'')) != NULL) {
printf("%.*s", (int)(c - str), str);
printf("''");
str = c + 1;
}
printf("%s", str);
}
static void clar_print_tap_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status status)
{
const struct clar_error *error = _clar.last_report->errors;
(void)test_name;
(void)test_number;
switch(status) {
case CL_TEST_OK:
printf("ok %d - %s::%s\n", test_number, suite_name, test_name);
break;
case CL_TEST_FAILURE:
printf("not ok %d - %s::%s\n", test_number, suite_name, test_name);
if (_clar.verbosity >= 0) {
printf(" ---\n");
printf(" reason: |\n");
clar_print_indented(error->error_msg, 6);
if (error->description)
clar_print_indented(error->description, 6);
printf(" at:\n");
printf(" file: '"); print_escaped(error->file); printf("'\n");
printf(" line: %" PRIuMAX "\n", error->line_number);
printf(" function: '%s'\n", error->function);
printf(" ...\n");
}
break;
case CL_TEST_SKIP:
case CL_TEST_NOTRUN:
printf("ok %d - # SKIP %s::%s\n", test_number, suite_name, test_name);
break;
}
fflush(stdout);
}
static void clar_print_tap_onsuite(const char *suite_name, int suite_index)
{
if (_clar.verbosity < 0)
return;
printf("# start of suite %d: %s\n", suite_index, suite_name);
}
static void clar_print_tap_onabort(const char *fmt, va_list arg)
{
printf("Bail out! ");
vprintf(fmt, arg);
fflush(stdout);
}
/* indirection between protocol output selection */
#define PRINT(FN, ...) do { \
switch (_clar.output_format) { \
case CL_OUTPUT_CLAP: \
clar_print_clap_##FN (__VA_ARGS__); \
break; \
case CL_OUTPUT_TAP: \
clar_print_tap_##FN (__VA_ARGS__); \
break; \
default: \
abort(); \
} \
} while (0)
static void clar_print_init(int test_count, int suite_count)
{
PRINT(init, test_count, suite_count);
}
static void clar_print_shutdown(int test_count, int suite_count, int error_count)
{
PRINT(shutdown, test_count, suite_count, error_count);
}
static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error)
{
PRINT(error, num, report, error);
}
static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status status)
{
PRINT(ontest, suite_name, test_name, test_number, status);
}
static void clar_print_onsuite(const char *suite_name, int suite_index)
{
PRINT(onsuite, suite_name, suite_index);
}
static void clar_print_onabortv(const char *msg, va_list argp)
{
PRINT(onabort, msg, argp);
}
static void clar_print_onabort(const char *msg, ...)
{
va_list argp;
va_start(argp, msg);
clar_print_onabortv(msg, argp);
va_end(argp);
}

290
deps/clar/clar/sandbox.h vendored Normal file
View File

@@ -0,0 +1,290 @@
#ifdef __APPLE__
#include <sys/syslimits.h>
#endif
/*
* The tempdir is the temporary directory for the entirety of the clar
* process execution. The sandbox is an individual temporary directory
* for the execution of an individual test. Sandboxes are deleted
* entirely after test execution to avoid pollution across tests.
*/
static char _clar_tempdir[CLAR_MAX_PATH];
static size_t _clar_tempdir_len;
static char _clar_sandbox[CLAR_MAX_PATH];
static int
is_valid_tmp_path(const char *path)
{
STAT_T st;
if (stat(path, &st) != 0)
return 0;
if (!S_ISDIR(st.st_mode))
return 0;
if (access(path, W_OK) != 0)
return 0;
return (strlen(path) < CLAR_MAX_PATH);
}
static int
find_tmp_path(char *buffer, size_t length)
{
#ifndef _WIN32
static const size_t var_count = 5;
static const char *env_vars[] = {
"CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE"
};
size_t i;
for (i = 0; i < var_count; ++i) {
const char *env = getenv(env_vars[i]);
if (!env)
continue;
if (is_valid_tmp_path(env)) {
strncpy(buffer, env, length - 1);
buffer[length - 1] = '\0';
return 0;
}
}
/* If the environment doesn't say anything, try to use /tmp */
if (is_valid_tmp_path("/tmp")) {
strncpy(buffer, "/tmp", length - 1);
buffer[length - 1] = '\0';
return 0;
}
#else
DWORD len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length);
if (len > 0 && len < (DWORD)length)
return 0;
len = GetTempPath((DWORD)length, buffer);
if (len > 0 && len < (DWORD)length)
return 0;
#endif
/* This system doesn't like us, try to use the current directory */
if (is_valid_tmp_path(".")) {
strncpy(buffer, ".", length - 1);
buffer[length - 1] = '\0';
return 0;
}
return -1;
}
static int canonicalize_tmp_path(char *buffer)
{
#ifdef _WIN32
char tmp[CLAR_MAX_PATH], *p;
DWORD ret;
ret = GetFullPathName(buffer, CLAR_MAX_PATH, tmp, NULL);
if (ret == 0 || ret > CLAR_MAX_PATH)
return -1;
ret = GetLongPathName(tmp, buffer, CLAR_MAX_PATH);
if (ret == 0 || ret > CLAR_MAX_PATH)
return -1;
/* normalize path to POSIX forward slashes */
for (p = buffer; *p; p++)
if (*p == '\\')
*p = '/';
return 0;
#elif defined(CLAR_HAS_REALPATH)
char tmp[CLAR_MAX_PATH];
if (realpath(buffer, tmp) == NULL)
return -1;
strcpy(buffer, tmp);
return 0;
#else
(void)buffer;
return 0;
#endif
}
static void clar_tempdir_shutdown(void)
{
if (_clar_tempdir[0] == '\0')
return;
cl_must_pass(chdir(".."));
fs_rm(_clar_tempdir);
}
static int build_tempdir_path(void)
{
#ifdef CLAR_TMPDIR
const char path_tail[] = CLAR_TMPDIR "_XXXXXX";
#else
const char path_tail[] = "clar_tmp_XXXXXX";
#endif
size_t len;
if (find_tmp_path(_clar_tempdir, sizeof(_clar_tempdir)) < 0 ||
canonicalize_tmp_path(_clar_tempdir) < 0)
return -1;
len = strlen(_clar_tempdir);
if (len + strlen(path_tail) + 2 > CLAR_MAX_PATH)
return -1;
if (_clar_tempdir[len - 1] != '/')
_clar_tempdir[len++] = '/';
strncpy(_clar_tempdir + len, path_tail, sizeof(_clar_tempdir) - len);
#if defined(__MINGW32__)
if (_mktemp(_clar_tempdir) == NULL)
return -1;
if (mkdir(_clar_tempdir, 0700) != 0)
return -1;
#elif defined(_WIN32)
if (_mktemp_s(_clar_tempdir, sizeof(_clar_tempdir)) != 0)
return -1;
if (mkdir(_clar_tempdir, 0700) != 0)
return -1;
#elif defined(__sun) || defined(__TANDEM) || defined(__hpux)
if (mktemp(_clar_tempdir) == NULL)
return -1;
if (mkdir(_clar_tempdir, 0700) != 0)
return -1;
#else
if (mkdtemp(_clar_tempdir) == NULL)
return -1;
#endif
_clar_tempdir_len = strlen(_clar_tempdir);
return 0;
}
static void clar_tempdir_init(void)
{
if (_clar_tempdir[0] == '\0' && build_tempdir_path() < 0)
clar_abort("Failed to build tempdir path.\n");
if (chdir(_clar_tempdir) != 0)
clar_abort("Failed to change into tempdir '%s': %s.\n",
_clar_tempdir, strerror(errno));
#if !defined(CLAR_SANDBOX_TEST_NAMES) && defined(_WIN32)
srand(clock() ^ (unsigned int)time(NULL) ^ GetCurrentProcessId() ^ GetCurrentThreadId());
#elif !defined(CLAR_SANDBOX_TEST_NAMES)
srand(clock() ^ time(NULL) ^ ((unsigned)getpid() << 16));
#endif
}
static void append(char *dst, const char *src)
{
char *d;
const char *s;
for (d = dst; *d; d++)
;
for (s = src; *s; d++, s++)
if (*s == ':')
*d = '_';
else
*d = *s;
*d = '\0';
}
static int clar_sandbox_create(const char *suite_name, const char *test_name)
{
#ifndef CLAR_SANDBOX_TEST_NAMES
char alpha[] = "0123456789abcdef";
int num = rand();
#endif
cl_assert(_clar_sandbox[0] == '\0');
/*
* We may want to use test names as sandbox directory names for
* readability, _however_ on platforms with restrictions for short
* file / folder names (eg, Windows), this may be too long.
*/
#ifdef CLAR_SANDBOX_TEST_NAMES
cl_assert(strlen(_clar_tempdir) + strlen(suite_name) + strlen(test_name) + 3 < CLAR_MAX_PATH);
strcpy(_clar_sandbox, _clar_tempdir);
_clar_sandbox[_clar_tempdir_len] = '/';
_clar_sandbox[_clar_tempdir_len + 1] = '\0';
append(_clar_sandbox, suite_name);
append(_clar_sandbox, "__");
append(_clar_sandbox, test_name);
#else
((void)suite_name);
((void)test_name);
((void)append);
cl_assert(strlen(_clar_tempdir) + 9 < CLAR_MAX_PATH);
strcpy(_clar_sandbox, _clar_tempdir);
_clar_sandbox[_clar_tempdir_len] = '/';
_clar_sandbox[_clar_tempdir_len + 1] = alpha[(num & 0xf0000000) >> 28];
_clar_sandbox[_clar_tempdir_len + 2] = alpha[(num & 0x0f000000) >> 24];
_clar_sandbox[_clar_tempdir_len + 3] = alpha[(num & 0x00f00000) >> 20];
_clar_sandbox[_clar_tempdir_len + 4] = alpha[(num & 0x000f0000) >> 16];
_clar_sandbox[_clar_tempdir_len + 5] = alpha[(num & 0x0000f000) >> 12];
_clar_sandbox[_clar_tempdir_len + 6] = alpha[(num & 0x00000f00) >> 8];
_clar_sandbox[_clar_tempdir_len + 7] = alpha[(num & 0x000000f0) >> 4];
_clar_sandbox[_clar_tempdir_len + 8] = alpha[(num & 0x0000000f) >> 0];
_clar_sandbox[_clar_tempdir_len + 9] = '\0';
#endif
if (mkdir(_clar_sandbox, 0700) != 0)
return -1;
if (chdir(_clar_sandbox) != 0)
return -1;
return 0;
}
static int clar_sandbox_cleanup(void)
{
cl_assert(_clar_sandbox[0] != '\0');
if (chdir(_clar_tempdir) != 0)
return -1;
fs_rm(_clar_sandbox);
_clar_sandbox[0] = '\0';
return 0;
}
const char *clar_tempdir_path(void)
{
return _clar_tempdir;
}
const char *clar_sandbox_path(void)
{
return _clar_sandbox;
}

140
deps/clar/clar/summary.h vendored Normal file
View File

@@ -0,0 +1,140 @@
#include <stdio.h>
#include <time.h>
static int clar_summary_close_tag(
struct clar_summary *summary, const char *tag, int indent)
{
const char *indt;
if (indent == 0) indt = "";
else if (indent == 1) indt = "\t";
else indt = "\t\t";
return fprintf(summary->fp, "%s</%s>\n", indt, tag);
}
static int clar_summary_testsuites(struct clar_summary *summary)
{
return fprintf(summary->fp, "<testsuites>\n");
}
static int clar_summary_testsuite(struct clar_summary *summary,
int idn, const char *name, time_t timestamp,
int test_count, int fail_count, int error_count)
{
struct tm tm;
char iso_dt[20];
localtime_r(&timestamp, &tm);
if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", &tm) == 0)
return -1;
return fprintf(summary->fp, "\t<testsuite"
" id=\"%d\""
" name=\"%s\""
" hostname=\"localhost\""
" timestamp=\"%s\""
" tests=\"%d\""
" failures=\"%d\""
" errors=\"%d\">\n",
idn, name, iso_dt, test_count, fail_count, error_count);
}
static int clar_summary_testcase(struct clar_summary *summary,
const char *name, const char *classname, double elapsed)
{
return fprintf(summary->fp,
"\t\t<testcase name=\"%s\" classname=\"%s\" time=\"%.2f\">\n",
name, classname, elapsed);
}
static int clar_summary_failure(struct clar_summary *summary,
const char *type, const char *message, const char *desc)
{
return fprintf(summary->fp,
"\t\t\t<failure type=\"%s\"><![CDATA[%s\n%s]]></failure>\n",
type, message, desc);
}
static int clar_summary_skipped(struct clar_summary *summary)
{
return fprintf(summary->fp, "\t\t\t<skipped />\n");
}
struct clar_summary *clar_summary_init(const char *filename)
{
struct clar_summary *summary;
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL)
clar_abort("Failed to open the summary file '%s': %s.\n",
filename, strerror(errno));
if ((summary = malloc(sizeof(struct clar_summary))) == NULL)
clar_abort("Failed to allocate summary.\n");
summary->filename = filename;
summary->fp = fp;
return summary;
}
int clar_summary_shutdown(struct clar_summary *summary)
{
struct clar_report *report;
const char *last_suite = NULL;
if (clar_summary_testsuites(summary) < 0)
goto on_error;
report = _clar.reports;
while (report != NULL) {
struct clar_error *error = report->errors;
if (last_suite == NULL || strcmp(last_suite, report->suite) != 0) {
if (clar_summary_testsuite(summary, 0, report->suite,
report->start, _clar.tests_ran, _clar.total_errors, 0) < 0)
goto on_error;
}
last_suite = report->suite;
clar_summary_testcase(summary, report->test, report->suite, report->elapsed);
while (error != NULL) {
if (clar_summary_failure(summary, "assert",
error->error_msg, error->description) < 0)
goto on_error;
error = error->next;
}
if (report->status == CL_TEST_SKIP)
clar_summary_skipped(summary);
if (clar_summary_close_tag(summary, "testcase", 2) < 0)
goto on_error;
report = report->next;
if (!report || strcmp(last_suite, report->suite) != 0) {
if (clar_summary_close_tag(summary, "testsuite", 1) < 0)
goto on_error;
}
}
if (clar_summary_close_tag(summary, "testsuites", 0) < 0 ||
fclose(summary->fp) != 0)
goto on_error;
printf("written summary file to %s\n", summary->filename);
free(summary);
return 0;
on_error:
fclose(summary->fp);
free(summary);
return -1;
}

710
deps/clar/clar_libgit2.c vendored Normal file
View File

@@ -0,0 +1,710 @@
#include "clar_libgit2.h"
#include "posix.h"
#include "fs_path.h"
#include "futils.h"
#include "git2/sys/repository.h"
void cl_git_report_failure(
int error, int expected, const char *file, const char *func, int line, const char *fncall)
{
char msg[4096];
const git_error *last = git_error_last();
if (expected)
p_snprintf(msg, 4096, "error %d (expected %d) - %s",
error, expected, last ? last->message : "<no message>");
else if (error || last)
p_snprintf(msg, 4096, "error %d - %s",
error, last ? last->message : "<no message>");
else
p_snprintf(msg, 4096, "no error, expected non-zero return");
clar__assert(0, file, func, line, fncall, msg, 1);
}
void cl_git_mkfile(const char *filename, const char *content)
{
int fd;
fd = p_creat(filename, 0666);
cl_assert(fd != -1);
if (content) {
cl_must_pass(p_write(fd, content, strlen(content)));
} else {
cl_must_pass(p_write(fd, filename, strlen(filename)));
cl_must_pass(p_write(fd, "\n", 1));
}
cl_must_pass(p_close(fd));
}
void cl_git_write2file(
const char *path, const char *content, size_t content_len,
int flags, unsigned int mode)
{
int fd;
cl_assert(path && content);
cl_assert((fd = p_open(path, flags, mode)) >= 0);
if (!content_len)
content_len = strlen(content);
cl_must_pass(p_write(fd, content, content_len));
cl_must_pass(p_close(fd));
}
void cl_git_append2file(const char *path, const char *content)
{
cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_APPEND, 0644);
}
void cl_git_rewritefile(const char *path, const char *content)
{
cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_TRUNC, 0644);
}
void cl_git_rmfile(const char *filename)
{
cl_must_pass(p_unlink(filename));
}
char *cl_getenv(const char *name)
{
git_str out = GIT_STR_INIT;
int error = git__getenv(&out, name);
cl_assert(error >= 0 || error == GIT_ENOTFOUND);
if (error == GIT_ENOTFOUND)
return NULL;
if (out.size == 0) {
char *dup = git__strdup("");
cl_assert(dup);
return dup;
}
return git_str_detach(&out);
}
bool cl_is_env_set(const char *name)
{
char *env = cl_getenv(name);
bool result = (env != NULL);
git__free(env);
return result;
}
#ifdef GIT_WIN32
#include "win32/utf-conv.h"
int cl_setenv(const char *name, const char *value)
{
wchar_t *wide_name, *wide_value = NULL;
cl_assert(git_utf8_to_16_alloc(&wide_name, name) >= 0);
if (value) {
cl_assert(git_utf8_to_16_alloc(&wide_value, value) >= 0);
cl_assert(SetEnvironmentVariableW(wide_name, wide_value));
} else {
/* Windows XP returns 0 (failed) when passing NULL for lpValue when
* lpName does not exist in the environment block. This behavior
* seems to have changed in later versions. Don't check the return value
* of SetEnvironmentVariable when passing NULL for lpValue. */
SetEnvironmentVariableW(wide_name, NULL);
}
git__free(wide_name);
git__free(wide_value);
return 0;
}
/* This function performs retries on calls to MoveFile in order
* to provide enhanced reliability in the face of antivirus
* agents that may be scanning the source (or in the case that
* the source is a directory, a child of the source). */
int cl_rename(const char *source, const char *dest)
{
git_win32_path source_utf16;
git_win32_path dest_utf16;
unsigned retries = 1;
cl_assert(git_win32_path_from_utf8(source_utf16, source) >= 0);
cl_assert(git_win32_path_from_utf8(dest_utf16, dest) >= 0);
while (!MoveFileW(source_utf16, dest_utf16)) {
/* Only retry if the error is ERROR_ACCESS_DENIED;
* this may indicate that an antivirus agent is
* preventing the rename from source to target */
if (retries > 5 ||
ERROR_ACCESS_DENIED != GetLastError())
return -1;
/* With 5 retries and a coefficient of 10ms, the maximum
* delay here is 550 ms */
Sleep(10 * retries * retries);
retries++;
}
return 0;
}
#else
#include <stdlib.h>
int cl_setenv(const char *name, const char *value)
{
return (value == NULL) ? unsetenv(name) : setenv(name, value, 1);
}
int cl_rename(const char *source, const char *dest)
{
return p_rename(source, dest);
}
#endif
static const char *_cl_sandbox = NULL;
static git_repository *_cl_repo = NULL;
git_repository *cl_git_sandbox_init(const char *sandbox)
{
/* Get the name of the sandbox folder which will be created */
const char *basename = cl_fixture_basename(sandbox);
/* Copy the whole sandbox folder from our fixtures to our test sandbox
* area. After this it can be accessed with `./sandbox`
*/
cl_fixture_sandbox(sandbox);
_cl_sandbox = sandbox;
cl_git_pass(p_chdir(basename));
/* If this is not a bare repo, then rename `sandbox/.gitted` to
* `sandbox/.git` which must be done since we cannot store a folder
* named `.git` inside the fixtures folder of our libgit2 repo.
*/
if (p_access(".gitted", F_OK) == 0)
cl_git_pass(cl_rename(".gitted", ".git"));
/* If we have `gitattributes`, rename to `.gitattributes`. This may
* be necessary if we don't want the attributes to be applied in the
* libgit2 repo, but just during testing.
*/
if (p_access("gitattributes", F_OK) == 0)
cl_git_pass(cl_rename("gitattributes", ".gitattributes"));
/* As with `gitattributes`, we may need `gitignore` just for testing. */
if (p_access("gitignore", F_OK) == 0)
cl_git_pass(cl_rename("gitignore", ".gitignore"));
cl_git_pass(p_chdir(".."));
/* Now open the sandbox repository and make it available for tests */
cl_git_pass(git_repository_open(&_cl_repo, basename));
/* Adjust configs after copying to new filesystem */
cl_git_pass(git_repository_reinit_filesystem(_cl_repo, 0));
return _cl_repo;
}
git_repository *cl_git_sandbox_init_new(const char *sandbox)
{
cl_git_pass(git_repository_init(&_cl_repo, sandbox, false));
_cl_sandbox = sandbox;
return _cl_repo;
}
git_repository *cl_git_sandbox_reopen(void)
{
if (_cl_repo) {
git_repository_free(_cl_repo);
_cl_repo = NULL;
cl_git_pass(git_repository_open(
&_cl_repo, cl_fixture_basename(_cl_sandbox)));
}
return _cl_repo;
}
void cl_git_sandbox_cleanup(void)
{
if (_cl_repo) {
git_repository_free(_cl_repo);
_cl_repo = NULL;
}
if (_cl_sandbox) {
cl_fixture_cleanup(_cl_sandbox);
_cl_sandbox = NULL;
}
}
bool cl_toggle_filemode(const char *filename)
{
struct stat st1, st2;
cl_must_pass(p_stat(filename, &st1));
cl_must_pass(p_chmod(filename, st1.st_mode ^ 0100));
cl_must_pass(p_stat(filename, &st2));
return (st1.st_mode != st2.st_mode);
}
bool cl_is_chmod_supported(void)
{
static int _is_supported = -1;
if (_is_supported < 0) {
cl_git_mkfile("filemode.t", "Test if filemode can be modified");
_is_supported = cl_toggle_filemode("filemode.t");
cl_must_pass(p_unlink("filemode.t"));
}
return _is_supported;
}
const char* cl_git_fixture_url(const char *fixturename)
{
return cl_git_path_url(cl_fixture(fixturename));
}
const char* cl_git_path_url(const char *path)
{
static char url[4096 + 1];
const char *in_buf;
git_str path_buf = GIT_STR_INIT;
git_str url_buf = GIT_STR_INIT;
cl_git_pass(git_fs_path_prettify_dir(&path_buf, path, NULL));
cl_git_pass(git_str_puts(&url_buf, "file://"));
#ifdef GIT_WIN32
/*
* A FILE uri matches the following format: file://[host]/path
* where "host" can be empty and "path" is an absolute path to the resource.
*
* In this test, no hostname is used, but we have to ensure the leading triple slashes:
*
* *nix: file:///usr/home/...
* Windows: file:///C:/Users/...
*/
cl_git_pass(git_str_putc(&url_buf, '/'));
#endif
in_buf = git_str_cstr(&path_buf);
/*
* A very hacky Url encoding that only takes care of escaping the spaces
*/
while (*in_buf) {
if (*in_buf == ' ')
cl_git_pass(git_str_puts(&url_buf, "%20"));
else
cl_git_pass(git_str_putc(&url_buf, *in_buf));
in_buf++;
}
cl_assert(url_buf.size < sizeof(url) - 1);
strncpy(url, git_str_cstr(&url_buf), sizeof(url) - 1);
url[sizeof(url) - 1] = '\0';
git_str_dispose(&url_buf);
git_str_dispose(&path_buf);
return url;
}
const char *cl_git_sandbox_path(int is_dir, ...)
{
const char *path = NULL;
static char _temp[GIT_PATH_MAX];
git_str buf = GIT_STR_INIT;
va_list arg;
cl_git_pass(git_str_sets(&buf, clar_sandbox_path()));
va_start(arg, is_dir);
while ((path = va_arg(arg, const char *)) != NULL) {
cl_git_pass(git_str_joinpath(&buf, buf.ptr, path));
}
va_end(arg);
cl_git_pass(git_fs_path_prettify(&buf, buf.ptr, NULL));
if (is_dir)
git_fs_path_to_dir(&buf);
/* make sure we won't truncate */
cl_assert(git_str_len(&buf) < sizeof(_temp));
git_str_copy_cstr(_temp, sizeof(_temp), &buf);
git_str_dispose(&buf);
return _temp;
}
typedef struct {
const char *filename;
size_t filename_len;
} remove_data;
static int remove_placeholders_recurs(void *_data, git_str *path)
{
remove_data *data = (remove_data *)_data;
size_t pathlen;
if (git_fs_path_isdir(path->ptr) == true)
return git_fs_path_direach(path, 0, remove_placeholders_recurs, data);
pathlen = path->size;
if (pathlen < data->filename_len)
return 0;
/* if path ends in '/'+filename (or equals filename) */
if (!strcmp(data->filename, path->ptr + pathlen - data->filename_len) &&
(pathlen == data->filename_len ||
path->ptr[pathlen - data->filename_len - 1] == '/'))
return p_unlink(path->ptr);
return 0;
}
int cl_git_remove_placeholders(const char *directory_path, const char *filename)
{
int error;
remove_data data;
git_str buffer = GIT_STR_INIT;
if (git_fs_path_isdir(directory_path) == false)
return -1;
if (git_str_sets(&buffer, directory_path) < 0)
return -1;
data.filename = filename;
data.filename_len = strlen(filename);
error = remove_placeholders_recurs(&data, &buffer);
git_str_dispose(&buffer);
return error;
}
#define CL_COMMIT_NAME "Libgit2 Tester"
#define CL_COMMIT_EMAIL "libgit2-test@github.com"
#define CL_COMMIT_MSG "Test commit of tree "
void cl_repo_commit_from_index(
git_oid *out,
git_repository *repo,
git_signature *sig,
git_time_t time,
const char *msg)
{
git_index *index;
git_oid commit_id, tree_id;
git_object *parent = NULL;
git_reference *ref = NULL;
git_tree *tree = NULL;
char buf[128];
int free_sig = (sig == NULL);
/* it is fine if looking up HEAD fails - we make this the first commit */
git_revparse_ext(&parent, &ref, repo, "HEAD");
/* write the index content as a tree */
cl_git_pass(git_repository_index(&index, repo));
cl_git_pass(git_index_write_tree(&tree_id, index));
cl_git_pass(git_index_write(index));
git_index_free(index);
cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
if (sig)
cl_assert(sig->name && sig->email);
else if (!time)
cl_git_pass(git_signature_now(&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL));
else
cl_git_pass(git_signature_new(
&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL, time, 0));
if (!msg) {
strcpy(buf, CL_COMMIT_MSG);
git_oid_tostr(buf + strlen(CL_COMMIT_MSG),
sizeof(buf) - strlen(CL_COMMIT_MSG), &tree_id);
msg = buf;
}
cl_git_pass(git_commit_create_v(
&commit_id, repo, ref ? git_reference_name(ref) : "HEAD",
sig, sig, NULL, msg, tree, parent ? 1 : 0, parent));
if (out)
git_oid_cpy(out, &commit_id);
git_object_free(parent);
git_reference_free(ref);
if (free_sig)
git_signature_free(sig);
git_tree_free(tree);
}
void cl_repo_set_bool(git_repository *repo, const char *cfg, int value)
{
git_config *config;
cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_set_bool(config, cfg, value != 0));
git_config_free(config);
}
int cl_repo_get_bool(git_repository *repo, const char *cfg)
{
int val = 0;
git_config *config;
cl_git_pass(git_repository_config(&config, repo));
if (git_config_get_bool(&val, config, cfg) < 0)
git_error_clear();
git_config_free(config);
return val;
}
void cl_repo_set_int(git_repository *repo, const char *cfg, int value)
{
git_config *config;
cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_set_int32(config, cfg, value));
git_config_free(config);
}
int cl_repo_get_int(git_repository *repo, const char *cfg)
{
int val = 0;
git_config *config;
cl_git_pass(git_repository_config(&config, repo));
if (git_config_get_int32(&val, config, cfg) < 0)
git_error_clear();
git_config_free(config);
return val;
}
void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value)
{
git_config *config;
cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_set_string(config, cfg, value));
git_config_free(config);
}
/* this is essentially the code from git__unescape modified slightly */
static size_t strip_cr_from_buf(char *start, size_t len)
{
char *scan, *trail, *end = start + len;
for (scan = trail = start; scan < end; trail++, scan++) {
while (*scan == '\r')
scan++; /* skip '\r' */
if (trail != scan)
*trail = *scan;
}
*trail = '\0';
return (trail - start);
}
void clar__assert_equal_file(
const char *expected_data,
size_t expected_bytes,
int ignore_cr,
const char *path,
const char *file,
const char *func,
int line)
{
char buf[4000];
ssize_t bytes, total_bytes = 0;
int fd = p_open(path, O_RDONLY | O_BINARY);
cl_assert(fd >= 0);
if (expected_data && !expected_bytes)
expected_bytes = strlen(expected_data);
while ((bytes = p_read(fd, buf, sizeof(buf))) != 0) {
clar__assert(
bytes > 0, file, func, line, "error reading from file", path, 1);
if (ignore_cr)
bytes = strip_cr_from_buf(buf, bytes);
if (memcmp(expected_data, buf, bytes) != 0) {
int pos;
for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos)
/* find differing byte offset */;
p_snprintf(
buf, sizeof(buf), "file content mismatch at byte %"PRIdZ,
(ssize_t)(total_bytes + pos));
p_close(fd);
clar__fail(file, func, line, path, buf, 1);
}
expected_data += bytes;
total_bytes += bytes;
}
p_close(fd);
clar__assert(!bytes, file, func, line, "error reading from file", path, 1);
clar__assert_equal(file, func, line, "mismatched file length", 1, "%"PRIuZ,
(size_t)expected_bytes, (size_t)total_bytes);
}
#define FAKE_HOMEDIR_NAME "cl_fake_home"
static git_buf _cl_restore_homedir = GIT_BUF_INIT;
void cl_fake_homedir_cleanup(void *payload)
{
GIT_UNUSED(payload);
if (_cl_restore_homedir.ptr) {
cl_git_pass(git_futils_rmdir_r(FAKE_HOMEDIR_NAME, NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_HOMEDIR, _cl_restore_homedir.ptr));
git_buf_dispose(&_cl_restore_homedir);
}
}
void cl_fake_homedir(git_str *out)
{
git_str path = GIT_STR_INIT;
cl_git_pass(git_libgit2_opts(
GIT_OPT_GET_HOMEDIR, &_cl_restore_homedir));
cl_set_cleanup(cl_fake_homedir_cleanup, NULL);
/* TOC/TOU but merely attempts to prevent accidental cleanup. */
cl_assert(!git_fs_path_exists(FAKE_HOMEDIR_NAME));
cl_must_pass(p_mkdir(FAKE_HOMEDIR_NAME, 0777));
cl_git_pass(git_fs_path_prettify(&path, FAKE_HOMEDIR_NAME, NULL));
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_HOMEDIR, path.ptr));
if (out)
git_str_swap(out, &path);
git_str_dispose(&path);
}
#define FAKE_GLOBALCONFIG_NAME "cl_fake_global"
static git_buf _cl_restore_globalconfig = GIT_BUF_INIT;
void cl_fake_globalconfig_cleanup(void *payload)
{
GIT_UNUSED(payload);
if (_cl_restore_globalconfig.ptr) {
cl_git_pass(git_futils_rmdir_r(FAKE_GLOBALCONFIG_NAME, NULL, GIT_RMDIR_REMOVE_FILES));
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_HOMEDIR, _cl_restore_globalconfig.ptr));
git_buf_dispose(&_cl_restore_globalconfig);
}
}
void cl_fake_globalconfig(git_str *out)
{
git_str path = GIT_STR_INIT;
cl_git_pass(git_libgit2_opts(
GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &_cl_restore_globalconfig));
cl_set_cleanup(cl_fake_globalconfig_cleanup, NULL);
/* TOC/TOU but merely attempts to prevent accidental cleanup. */
cl_assert(!git_fs_path_exists(FAKE_GLOBALCONFIG_NAME));
cl_must_pass(p_mkdir(FAKE_GLOBALCONFIG_NAME, 0777));
cl_git_pass(git_fs_path_prettify(&path, FAKE_GLOBALCONFIG_NAME, NULL));
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
if (out)
git_str_swap(out, &path);
git_str_dispose(&path);
}
void cl_sandbox_set_homedir(const char *home)
{
git_str path = GIT_STR_INIT;
if (home) {
git_libgit2_opts(GIT_OPT_SET_HOMEDIR, home);
} else {
git_str_joinpath(&path, clar_tempdir_path(), "__home");
if (!git_fs_path_exists(path.ptr))
cl_must_pass(p_mkdir(path.ptr, 0777));
git_libgit2_opts(GIT_OPT_SET_HOMEDIR, path.ptr);
}
git_str_dispose(&path);
}
void cl_sandbox_set_search_path_defaults(void)
{
git_str path = GIT_STR_INIT;
git_str_joinpath(&path, clar_tempdir_path(), "__config");
if (!git_fs_path_exists(path.ptr))
cl_must_pass(p_mkdir(path.ptr, 0777));
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr);
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr);
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr);
git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_PROGRAMDATA, path.ptr);
git_str_dispose(&path);
}
void cl_sandbox_disable_ownership_validation(void)
{
git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 0);
}
#ifdef GIT_WIN32
bool cl_sandbox_supports_8dot3(void)
{
git_str longpath = GIT_STR_INIT;
char *shortname;
bool supported;
cl_git_pass(
git_str_joinpath(&longpath, clar_sandbox_path(), "longer_than_8dot3"));
cl_git_write2file(longpath.ptr, "", 0, O_RDWR|O_CREAT, 0666);
shortname = git_win32_path_8dot3_name(longpath.ptr);
supported = (shortname != NULL);
git__free(shortname);
git_str_dispose(&longpath);
return supported;
}
#endif

287
deps/clar/clar_libgit2.h vendored Normal file
View File

@@ -0,0 +1,287 @@
#ifndef __CLAR_LIBGIT2__
#define __CLAR_LIBGIT2__
#include "clar.h"
#include <git2.h>
#include "common.h"
#include "posix.h"
#include "oid.h"
/**
* Replace for `clar_must_pass` that passes the last library error as the
* test failure message.
*
* Use this wrapper around all `git_` library calls that return error codes!
*/
#define cl_git_pass(expr) cl_git_expect((expr), 0, __FILE__, __func__, __LINE__)
#define cl_git_fail_with(error, expr) cl_git_expect((expr), error, __FILE__, __func__, __LINE__)
#define cl_git_expect(expr, expected, file, func, line) do { \
int _lg2_error; \
git_error_clear(); \
if ((_lg2_error = (expr)) != expected) \
cl_git_report_failure(_lg2_error, expected, file, func, line, "Function call failed: " #expr); \
} while (0)
/**
* Wrapper for `clar_must_fail` -- this one is
* just for consistency. Use with `git_` library
* calls that are supposed to fail!
*/
#define cl_git_fail(expr) do { \
if ((expr) == 0) \
git_error_clear(), \
cl_git_report_failure(0, 0, __FILE__, __func__, __LINE__, "Function call succeeded: " #expr); \
} while (0)
/**
* Like cl_git_pass, only for Win32 error code conventions
*/
#define cl_win32_pass(expr) do { \
int _win32_res; \
if ((_win32_res = (expr)) == 0) { \
git_error_set(GIT_ERROR_OS, "Returned: %d, system error code: %lu", _win32_res, GetLastError()); \
cl_git_report_failure(_win32_res, 0, __FILE__, __func__, __LINE__, "System call failed: " #expr); \
} \
} while(0)
/**
* Thread safe assertions; you cannot use `cl_git_report_failure` from a
* child thread since it will try to `longjmp` to abort and "the effect of
* a call to longjmp() where initialization of the jmp_buf structure was
* not performed in the calling thread is undefined."
*
* Instead, callers can provide a clar thread error context to a thread,
* which will populate and return it on failure. Callers can check the
* status with `cl_git_thread_check`.
*/
typedef struct {
int error;
const char *file;
const char *func;
int line;
const char *expr;
char error_msg[4096];
} cl_git_thread_err;
#ifdef GIT_THREADS
# define cl_git_thread_pass(threaderr, expr) cl_git_thread_pass_(threaderr, (expr), __FILE__, __func__, __LINE__)
#else
# define cl_git_thread_pass(threaderr, expr) cl_git_pass(expr)
#endif
#define cl_git_thread_pass_(__threaderr, __expr, __file, __func, __line) do { \
git_error_clear(); \
if ((((cl_git_thread_err *)__threaderr)->error = (__expr)) != 0) { \
const git_error *_last = git_error_last(); \
((cl_git_thread_err *)__threaderr)->file = __file; \
((cl_git_thread_err *)__threaderr)->func = __func; \
((cl_git_thread_err *)__threaderr)->line = __line; \
((cl_git_thread_err *)__threaderr)->expr = "Function call failed: " #__expr; \
p_snprintf(((cl_git_thread_err *)__threaderr)->error_msg, 4096, "thread 0x%" PRIxZ " - error %d - %s", \
git_thread_currentid(), ((cl_git_thread_err *)__threaderr)->error, \
_last ? _last->message : "<no message>"); \
git_thread_exit(__threaderr); \
} \
} while (0)
GIT_INLINE(void) cl_git_thread_check(void *data)
{
cl_git_thread_err *threaderr = (cl_git_thread_err *)data;
if (threaderr->error != 0)
clar__assert(0, threaderr->file, threaderr->func, threaderr->line, threaderr->expr, threaderr->error_msg, 1);
}
void cl_git_report_failure(int, int, const char *, const char *, int, const char *);
#define cl_assert_at_line(expr,file,func,line) \
clar__assert((expr) != 0, file, func, line, "Expression is not true: " #expr, NULL, 1)
GIT_INLINE(void) clar__assert_in_range(
int lo, int val, int hi,
const char *file, const char *func, int line,
const char *err, int should_abort)
{
if (lo > val || hi < val) {
char buf[128];
p_snprintf(buf, sizeof(buf), "%d not in [%d,%d]", val, lo, hi);
clar__fail(file, func, line, err, buf, should_abort);
}
}
#define cl_assert_equal_sz(sz1,sz2) do { \
size_t __sz1 = (size_t)(sz1), __sz2 = (size_t)(sz2); \
clar__assert_equal(__FILE__,__func__,__LINE__,#sz1 " != " #sz2, 1, "%"PRIuZ, __sz1, __sz2); \
} while (0)
#define cl_assert_in_range(L,V,H) \
clar__assert_in_range((L),(V),(H),__FILE__,__func__,__LINE__,"Range check: " #V " in [" #L "," #H "]", 1)
#define cl_assert_equal_file(DATA,SIZE,PATH) \
clar__assert_equal_file(DATA,SIZE,0,PATH,__FILE__,__func__,(int)__LINE__)
#define cl_assert_equal_file_ignore_cr(DATA,SIZE,PATH) \
clar__assert_equal_file(DATA,SIZE,1,PATH,__FILE__,__func__,(int)__LINE__)
void clar__assert_equal_file(
const char *expected_data,
size_t expected_size,
int ignore_cr,
const char *path,
const char *file,
const char *func,
int line);
GIT_INLINE(void) clar__assert_equal_oid(
const char *file, const char *func, int line, const char *desc,
const git_oid *one, const git_oid *two)
{
if (git_oid_equal(one, two))
return;
if (git_oid_type(one) != git_oid_type(two)) {
char err[64];
snprintf(err, 64, "different oid types: %d vs %d", git_oid_type(one), git_oid_type(two));
clar__fail(file, func, line, desc, err, 1);
} else if (git_oid_type(one) == GIT_OID_SHA1) {
char err[] = "\"........................................\" != \"........................................\"";
git_oid_fmt(&err[1], one);
git_oid_fmt(&err[47], two);
clar__fail(file, func, line, desc, err, 1);
#ifdef GIT_EXPERIMENTAL_SHA256
} else if (one->type == GIT_OID_SHA256) {
char err[] = "\"................................................................\" != \"................................................................\"";
git_oid_fmt(&err[1], one);
git_oid_fmt(&err[71], one);
clar__fail(file, func, line, desc, err, 1);
#endif
} else {
clar__fail(file, func, line, desc, "unknown oid types", 1);
}
}
GIT_INLINE(void) clar__assert_equal_oidstr(
const char *file, const char *func, int line, const char *desc,
const char *one_str, const git_oid *two)
{
git_oid one;
git_oid_t oid_type = git_oid_type(two);
if (git_oid_from_prefix(&one, one_str, git_oid_hexsize(oid_type), oid_type) < 0) {
clar__fail(file, func, line, desc, "could not parse oid string", 1);
} else {
clar__assert_equal_oid(file, func, line, desc, &one, two);
}
}
#define cl_assert_equal_oid(one, two) \
clar__assert_equal_oid(__FILE__, __func__, __LINE__, \
"OID mismatch: " #one " != " #two, (one), (two))
#define cl_assert_equal_oidstr(one_str, two) \
clar__assert_equal_oidstr(__FILE__, __func__, __LINE__, \
"OID mismatch: " #one_str " != " #two, (one_str), (two))
/*
* Some utility macros for building long strings
*/
#define REP4(STR) STR STR STR STR
#define REP15(STR) REP4(STR) REP4(STR) REP4(STR) STR STR STR
#define REP16(STR) REP4(REP4(STR))
#define REP256(STR) REP16(REP16(STR))
#define REP1024(STR) REP4(REP256(STR))
/* Write the contents of a buffer to disk */
void cl_git_mkfile(const char *filename, const char *content);
void cl_git_append2file(const char *filename, const char *new_content);
void cl_git_rewritefile(const char *filename, const char *new_content);
void cl_git_write2file(const char *path, const char *data,
size_t datalen, int flags, unsigned int mode);
void cl_git_rmfile(const char *filename);
bool cl_toggle_filemode(const char *filename);
bool cl_is_chmod_supported(void);
/* Environment wrappers */
char *cl_getenv(const char *name);
bool cl_is_env_set(const char *name);
int cl_setenv(const char *name, const char *value);
/* Reliable rename */
int cl_rename(const char *source, const char *dest);
/* Git sandbox setup helpers */
git_repository *cl_git_sandbox_init(const char *sandbox);
git_repository *cl_git_sandbox_init_new(const char *name);
void cl_git_sandbox_cleanup(void);
git_repository *cl_git_sandbox_reopen(void);
/*
* build a sandbox-relative from path segments
* is_dir will add a trailing slash
* vararg must be a NULL-terminated char * list
*/
const char *cl_git_sandbox_path(int is_dir, ...);
/* Local-repo url helpers */
const char* cl_git_fixture_url(const char *fixturename);
const char* cl_git_path_url(const char *path);
/* Test repository cleaner */
int cl_git_remove_placeholders(const char *directory_path, const char *filename);
/* commit creation helpers */
void cl_repo_commit_from_index(
git_oid *out,
git_repository *repo,
git_signature *sig,
git_time_t time,
const char *msg);
/* config setting helpers */
void cl_repo_set_bool(git_repository *repo, const char *cfg, int value);
int cl_repo_get_bool(git_repository *repo, const char *cfg);
void cl_repo_set_int(git_repository *repo, const char *cfg, int value);
int cl_repo_get_int(git_repository *repo, const char *cfg);
void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value);
/*
* set up a fake "home" directory -- automatically configures cleanup
* function to restore the home directory, although you can call it
* explicitly if you wish (with NULL).
*/
void cl_fake_homedir(git_str *);
void cl_fake_homedir_cleanup(void *);
/*
* set up a fake global configuration directory -- automatically
* configures cleanup function to restore the global config
* although you can call it explicitly if you wish (with NULL).
*/
void cl_fake_globalconfig(git_str *);
void cl_fake_globalconfig_cleanup(void *);
void cl_sandbox_set_homedir(const char *);
void cl_sandbox_set_search_path_defaults(void);
void cl_sandbox_disable_ownership_validation(void);
#ifdef GIT_WIN32
# define cl_msleep(x) Sleep(x)
#else
# define cl_msleep(x) usleep(1000 * (x))
#endif
#ifdef GIT_WIN32
bool cl_sandbox_supports_8dot3(void);
#endif
#endif

108
deps/clar/clar_libgit2_alloc.c vendored Normal file
View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "clar_libgit2_alloc.h"
static size_t bytes_available;
/*
* The clar allocator uses a tagging mechanism for pointers that
* prepends the actual pointer's number bytes as `size_t`.
*
* First, this is required in order to be able to implement
* proper bookkeeping of allocated bytes in both `free` and
* `realloc`.
*
* Second, it may also be able to spot bugs that are
* otherwise hard to grasp, as the returned pointer cannot be
* free'd directly via free(3P). Instead, one is forced to use
* the tandem of `cl__malloc` and `cl__free`, as otherwise the
* code is going to crash hard. This is considered to be a
* feature, as it helps e.g. in finding cases where by accident
* malloc(3P) and free(3P) were used instead of git__malloc and
* git__free, respectively.
*
* The downside is obviously that each allocation grows by
* sizeof(size_t) bytes. As the allocator is for testing purposes
* only, this tradeoff is considered to be perfectly fine,
* though.
*/
static void *cl__malloc(size_t len, const char *file, int line)
{
char *ptr = NULL;
size_t alloclen;
GIT_UNUSED(file);
GIT_UNUSED(line);
if (len > bytes_available)
goto out;
if (GIT_ADD_SIZET_OVERFLOW(&alloclen, len, sizeof(size_t)) ||
(ptr = malloc(alloclen)) == NULL)
goto out;
memcpy(ptr, &len, sizeof(size_t));
bytes_available -= len;
out:
return ptr ? ptr + sizeof(size_t) : NULL;
}
static void cl__free(void *ptr)
{
if (ptr) {
char *p = ptr;
size_t len;
memcpy(&len, p - sizeof(size_t), sizeof(size_t));
free(p - sizeof(size_t));
bytes_available += len;
}
}
static void *cl__realloc(void *ptr, size_t size, const char *file, int line)
{
size_t copybytes = 0;
char *p = ptr;
void *new;
if (p)
memcpy(&copybytes, p - sizeof(size_t), sizeof(size_t));
if (copybytes > size)
copybytes = size;
if ((new = cl__malloc(size, file, line)) == NULL)
goto out;
if (p) {
memcpy(new, p, copybytes);
cl__free(p);
}
out:
return new;
}
void cl_alloc_limit(size_t bytes)
{
git_allocator alloc;
alloc.gmalloc = cl__malloc;
alloc.grealloc = cl__realloc;
alloc.gfree = cl__free;
git_allocator_setup(&alloc);
bytes_available = bytes;
}
void cl_alloc_reset(void)
{
git_allocator_setup(NULL);
}

11
deps/clar/clar_libgit2_alloc.h vendored Normal file
View File

@@ -0,0 +1,11 @@
#ifndef __CLAR_LIBGIT2_ALLOC__
#define __CLAR_LIBGIT2_ALLOC__
#include "clar.h"
#include "common.h"
#include "git2/sys/alloc.h"
void cl_alloc_limit(size_t bytes);
void cl_alloc_reset(void);
#endif

30
deps/clar/clar_libgit2_timer.c vendored Normal file
View File

@@ -0,0 +1,30 @@
#include "clar_libgit2.h"
#include "clar_libgit2_timer.h"
void cl_perf_timer__init(cl_perf_timer *t)
{
memset(t, 0, sizeof(cl_perf_timer));
}
void cl_perf_timer__start(cl_perf_timer *t)
{
t->time_started = git_time_monotonic();
}
void cl_perf_timer__stop(cl_perf_timer *t)
{
uint64_t time_now = git_time_monotonic();
t->last = time_now - t->time_started;
t->sum += t->last;
}
uint64_t cl_perf_timer__last(const cl_perf_timer *t)
{
return t->last;
}
uint64_t cl_perf_timer__sum(const cl_perf_timer *t)
{
return t->sum;
}

35
deps/clar/clar_libgit2_timer.h vendored Normal file
View File

@@ -0,0 +1,35 @@
#ifndef __CLAR_LIBGIT2_TIMER__
#define __CLAR_LIBGIT2_TIMER__
struct cl_perf_timer
{
/* cumulative running time across all start..stop intervals */
uint64_t sum;
/* value of last start..stop interval */
uint64_t last;
/* clock value at start */
uint64_t time_started;
};
#define CL_PERF_TIMER_INIT {0}
typedef struct cl_perf_timer cl_perf_timer;
void cl_perf_timer__init(cl_perf_timer *t);
void cl_perf_timer__start(cl_perf_timer *t);
void cl_perf_timer__stop(cl_perf_timer *t);
/**
* return value of last start..stop interval in seconds.
*/
uint64_t cl_perf_timer__last(const cl_perf_timer *t);
/**
* return cumulative running time across all start..stop
* intervals in seconds.
*/
uint64_t cl_perf_timer__sum(const cl_perf_timer *t);
#endif /* __CLAR_LIBGIT2_TIMER__ */

263
deps/clar/clar_libgit2_trace.c vendored Normal file
View File

@@ -0,0 +1,263 @@
#include "clar_libgit2_trace.h"
#include "clar_libgit2.h"
#include "clar_libgit2_timer.h"
#include "trace.h"
struct method {
const char *name;
void (*git_trace_cb)(git_trace_level_t level, const char *msg);
void (*close)(void);
};
static const char *message_prefix(git_trace_level_t level)
{
switch (level) {
case GIT_TRACE_NONE:
return "[NONE]: ";
case GIT_TRACE_FATAL:
return "[FATAL]: ";
case GIT_TRACE_ERROR:
return "[ERROR]: ";
case GIT_TRACE_WARN:
return "[WARN]: ";
case GIT_TRACE_INFO:
return "[INFO]: ";
case GIT_TRACE_DEBUG:
return "[DEBUG]: ";
case GIT_TRACE_TRACE:
return "[TRACE]: ";
default:
return "[?????]: ";
}
}
static void _git_trace_cb__printf(git_trace_level_t level, const char *msg)
{
printf("%s%s\n", message_prefix(level), msg);
}
#if defined(GIT_WIN32)
static void _git_trace_cb__debug(git_trace_level_t level, const char *msg)
{
OutputDebugString(message_prefix(level));
OutputDebugString(msg);
OutputDebugString("\n");
printf("%s%s\n", message_prefix(level), msg);
}
#else
#define _git_trace_cb__debug _git_trace_cb__printf
#endif
static void _trace_printf_close(void)
{
fflush(stdout);
}
#define _trace_debug_close _trace_printf_close
static struct method s_methods[] = {
{ "printf", _git_trace_cb__printf, _trace_printf_close },
{ "debug", _git_trace_cb__debug, _trace_debug_close },
/* TODO add file method */
{0},
};
static int s_trace_loaded = 0;
static int s_trace_level = GIT_TRACE_NONE;
static struct method *s_trace_method = NULL;
static int s_trace_tests = 0;
static int set_method(const char *name)
{
int k;
if (!name || !*name)
name = "printf";
for (k=0; (s_methods[k].name); k++) {
if (strcmp(name, s_methods[k].name) == 0) {
s_trace_method = &s_methods[k];
return 0;
}
}
fprintf(stderr, "Unknown CLAR_TRACE_METHOD: '%s'\n", name);
return -1;
}
/**
* Lookup CLAR_TRACE_LEVEL and CLAR_TRACE_METHOD from
* the environment and set the above s_trace_* fields.
*
* If CLAR_TRACE_LEVEL is not set, we disable tracing.
*
* TODO If set, we assume GIT_TRACE_TRACE level, which
* logs everything. Later, we may want to parse the
* value of the environment variable and set a specific
* level.
*
* We assume the "printf" method. This can be changed
* with the CLAR_TRACE_METHOD environment variable.
* Currently, this is only needed on Windows for a "debug"
* version which also writes to the debug output window
* in Visual Studio.
*
* TODO add a "file" method that would open and write
* to a well-known file. This would help keep trace
* output and clar output separate.
*
*/
static void _load_trace_params(void)
{
char *sz_level;
char *sz_method;
char *sz_tests;
s_trace_loaded = 1;
sz_level = cl_getenv("CLAR_TRACE_LEVEL");
if (!sz_level || !*sz_level) {
s_trace_level = GIT_TRACE_NONE;
s_trace_method = NULL;
return;
}
/* TODO Parse sz_level and set s_trace_level. */
s_trace_level = GIT_TRACE_TRACE;
sz_method = cl_getenv("CLAR_TRACE_METHOD");
if (set_method(sz_method) < 0)
set_method(NULL);
sz_tests = cl_getenv("CLAR_TRACE_TESTS");
if (sz_tests != NULL)
s_trace_tests = 1;
}
#define HR "================================================================"
/**
* Timer to report the take spend in a test's run() method.
*/
static cl_perf_timer s_timer_run = CL_PERF_TIMER_INIT;
/**
* Timer to report total time in a test (init, run, cleanup).
*/
static cl_perf_timer s_timer_test = CL_PERF_TIMER_INIT;
static void _cl_trace_cb__event_handler(
cl_trace_event ev,
const char *suite_name,
const char *test_name,
void *payload)
{
GIT_UNUSED(payload);
if (!s_trace_tests)
return;
switch (ev) {
case CL_TRACE__SUITE_BEGIN:
git_trace(GIT_TRACE_TRACE, "\n\n%s\n%s: Begin Suite", HR, suite_name);
#if 0 && defined(GIT_DEBUG_LEAKCHECK_WIN32)
git_win32__crtdbg_stacktrace__dump(
GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK,
suite_name);
#endif
break;
case CL_TRACE__SUITE_END:
#if 0 && defined(GIT_DEBUG_LEAKCHECK_WIN32)
/* As an example of checkpointing, dump leaks within this suite.
* This may generate false positives for things like the global
* TLS error state and maybe the odb cache since they aren't
* freed until the global shutdown and outside the scope of this
* set of tests.
*
* This may under-report if the test itself uses a checkpoint.
* See tests/trace/windows/stacktrace.c
*/
git_win32__crtdbg_stacktrace__dump(
GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
suite_name);
#endif
git_trace(GIT_TRACE_TRACE, "\n\n%s: End Suite\n%s", suite_name, HR);
break;
case CL_TRACE__TEST__BEGIN:
git_trace(GIT_TRACE_TRACE, "\n%s::%s: Begin Test", suite_name, test_name);
cl_perf_timer__init(&s_timer_test);
cl_perf_timer__start(&s_timer_test);
break;
case CL_TRACE__TEST__END:
cl_perf_timer__stop(&s_timer_test);
git_trace(GIT_TRACE_TRACE, "%s::%s: End Test (%" PRIuZ " %" PRIuZ ")", suite_name, test_name,
cl_perf_timer__last(&s_timer_run),
cl_perf_timer__last(&s_timer_test));
break;
case CL_TRACE__TEST__RUN_BEGIN:
git_trace(GIT_TRACE_TRACE, "%s::%s: Begin Run", suite_name, test_name);
cl_perf_timer__init(&s_timer_run);
cl_perf_timer__start(&s_timer_run);
break;
case CL_TRACE__TEST__RUN_END:
cl_perf_timer__stop(&s_timer_run);
git_trace(GIT_TRACE_TRACE, "%s::%s: End Run", suite_name, test_name);
break;
case CL_TRACE__TEST__LONGJMP:
cl_perf_timer__stop(&s_timer_run);
git_trace(GIT_TRACE_TRACE, "%s::%s: Aborted", suite_name, test_name);
break;
default:
break;
}
}
/**
* Setup/Enable git_trace() based upon settings user's environment.
*/
void cl_global_trace_register(void)
{
if (!s_trace_loaded)
_load_trace_params();
if (s_trace_level == GIT_TRACE_NONE)
return;
if (s_trace_method == NULL)
return;
if (s_trace_method->git_trace_cb == NULL)
return;
git_trace_set(s_trace_level, s_trace_method->git_trace_cb);
cl_trace_register(_cl_trace_cb__event_handler, NULL);
}
/**
* If we turned on git_trace() earlier, turn it off.
*
* This is intended to let us close/flush any buffered
* IO if necessary.
*
*/
void cl_global_trace_disable(void)
{
cl_trace_register(NULL, NULL);
git_trace_set(GIT_TRACE_NONE, NULL);
if (s_trace_method && s_trace_method->close)
s_trace_method->close();
/* Leave s_trace_ vars set so they can restart tracing
* since we only want to hit the environment variables
* once.
*/
}

7
deps/clar/clar_libgit2_trace.h vendored Normal file
View File

@@ -0,0 +1,7 @@
#ifndef __CLAR_LIBGIT2_TRACE__
#define __CLAR_LIBGIT2_TRACE__
void cl_global_trace_register(void);
void cl_global_trace_disable(void);
#endif

329
deps/clar/generate.py vendored Normal file
View File

@@ -0,0 +1,329 @@
#!/usr/bin/env python
#
# Copyright (c) Vicent Marti. All rights reserved.
#
# This file is part of clar, distributed under the ISC license.
# For full terms see the included COPYING file.
#
from __future__ import with_statement
from string import Template
import re, fnmatch, os, sys, codecs, pickle, io
class Module(object):
class Template(object):
def __init__(self, module):
self.module = module
def _render_callback(self, cb):
if not cb:
return ' { NULL, NULL }'
return ' { "%s", &%s }' % (cb['short_name'], cb['symbol'])
class DeclarationTemplate(Template):
def render(self):
out = "\n".join("extern %s;" % cb['declaration'] for cb in self.module.callbacks) + "\n"
for initializer in self.module.initializers:
out += "extern %s;\n" % initializer['declaration']
if self.module.cleanup:
out += "extern %s;\n" % self.module.cleanup['declaration']
return out
class CallbacksTemplate(Template):
def render(self):
out = "static const struct clar_func _clar_cb_%s[] = {\n" % self.module.name
out += ",\n".join(self._render_callback(cb) for cb in self.module.callbacks)
out += "\n};\n"
return out
class InfoTemplate(Template):
def render(self):
templates = []
initializers = self.module.initializers
if len(initializers) == 0:
initializers = [ None ]
for initializer in initializers:
name = self.module.clean_name()
if initializer and initializer['short_name'].startswith('initialize_'):
variant = initializer['short_name'][len('initialize_'):]
name += " (%s)" % variant.replace('_', ' ')
template = Template(
r"""
{
"${clean_name}",
${initialize},
${cleanup},
${cb_ptr}, ${cb_count}, ${enabled}
}"""
).substitute(
clean_name = name,
initialize = self._render_callback(initializer),
cleanup = self._render_callback(self.module.cleanup),
cb_ptr = "_clar_cb_%s" % self.module.name,
cb_count = len(self.module.callbacks),
enabled = int(self.module.enabled)
)
templates.append(template)
return ','.join(templates)
def __init__(self, name):
self.name = name
self.mtime = None
self.enabled = True
self.modified = False
def clean_name(self):
return self.name.replace("_", "::")
def _skip_comments(self, text):
SKIP_COMMENTS_REGEX = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE)
def _replacer(match):
s = match.group(0)
return "" if s.startswith('/') else s
return re.sub(SKIP_COMMENTS_REGEX, _replacer, text)
def parse(self, contents):
TEST_FUNC_REGEX = r"^(void\s+(test_%s__(\w+))\s*\(\s*void\s*\))\s*\{"
contents = self._skip_comments(contents)
regex = re.compile(TEST_FUNC_REGEX % self.name, re.MULTILINE)
self.callbacks = []
self.initializers = []
self.cleanup = None
for (declaration, symbol, short_name) in regex.findall(contents):
data = {
"short_name" : short_name,
"declaration" : declaration,
"symbol" : symbol
}
if short_name.startswith('initialize'):
self.initializers.append(data)
elif short_name == 'cleanup':
self.cleanup = data
else:
self.callbacks.append(data)
return self.callbacks != []
def refresh(self, path):
self.modified = False
try:
st = os.stat(path)
# Not modified
if st.st_mtime == self.mtime:
return True
self.modified = True
self.mtime = st.st_mtime
with codecs.open(path, encoding='utf-8') as fp:
raw_content = fp.read()
except IOError:
return False
return self.parse(raw_content)
class TestSuite(object):
def __init__(self, path, output):
self.path = path
self.output = output
def maybe_generate(self, path):
if not os.path.isfile(path):
return True
if any(module.modified for module in self.modules.values()):
return True
return False
def find_modules(self):
modules = []
if os.path.isfile(self.path):
full_path = os.path.abspath(self.path)
module_name = os.path.basename(self.path)
module_name = os.path.splitext(module_name)[0]
modules.append((full_path, module_name))
else:
for root, _, files in os.walk(self.path):
module_root = root[len(self.path):]
module_root = [c for c in module_root.split(os.sep) if c]
tests_in_module = fnmatch.filter(files, "*.c")
for test_file in tests_in_module:
full_path = os.path.join(root, test_file)
module_name = "_".join(module_root + [test_file[:-2]]).replace("-", "_")
modules.append((full_path, module_name))
return modules
def load_cache(self):
path = os.path.join(self.output, '.clarcache')
cache = {}
try:
fp = open(path, 'rb')
cache = pickle.load(fp)
fp.close()
except (IOError, ValueError):
pass
return cache
def save_cache(self):
path = os.path.join(self.output, '.clarcache')
with open(path, 'wb') as cache:
pickle.dump(self.modules, cache)
def load(self, force = False):
module_data = self.find_modules()
self.modules = {} if force else self.load_cache()
for path, name in module_data:
if name not in self.modules:
self.modules[name] = Module(name)
if not self.modules[name].refresh(path):
del self.modules[name]
def disable(self, excluded):
for exclude in excluded:
for module in self.modules.values():
name = module.clean_name()
if name.startswith(exclude):
module.enabled = False
module.modified = True
def suite_count(self):
return sum(max(1, len(m.initializers)) for m in self.modules.values())
def callback_count(self):
return sum(len(module.callbacks) for module in self.modules.values())
def write(self):
if not os.path.exists(self.output):
os.makedirs(self.output)
wrote_suite = self.write_suite()
wrote_header = self.write_header()
if wrote_suite or wrote_header:
self.save_cache()
return True
return False
def write_output(self, fn, data):
if not self.maybe_generate(fn):
return False
current = None
try:
with open(fn, 'r') as input:
current = input.read()
except OSError:
pass
except IOError:
pass
if current == data:
return False
with open(fn, 'w') as output:
output.write(data)
return True
def write_suite(self):
suite_fn = os.path.join(self.output, 'clar.suite')
with io.StringIO() as suite_file:
modules = sorted(self.modules.values(), key=lambda module: module.name)
for module in modules:
t = Module.DeclarationTemplate(module)
suite_file.write(t.render())
for module in modules:
t = Module.CallbacksTemplate(module)
suite_file.write(t.render())
suites = "static struct clar_suite _clar_suites[] = {" + ','.join(
Module.InfoTemplate(module).render() for module in modules
) + "\n};\n"
suite_file.write(suites)
suite_file.write(u"static const size_t _clar_suite_count = %d;\n" % self.suite_count())
suite_file.write(u"static const size_t _clar_callback_count = %d;\n" % self.callback_count())
return self.write_output(suite_fn, suite_file.getvalue())
return False
def write_header(self):
header_fn = os.path.join(self.output, 'clar_suite.h')
with io.StringIO() as header_file:
header_file.write(u"#ifndef _____clar_suite_h_____\n")
header_file.write(u"#define _____clar_suite_h_____\n")
modules = sorted(self.modules.values(), key=lambda module: module.name)
for module in modules:
t = Module.DeclarationTemplate(module)
header_file.write(t.render())
header_file.write(u"#endif\n")
return self.write_output(header_fn, header_file.getvalue())
return False
if __name__ == '__main__':
from optparse import OptionParser
parser = OptionParser()
parser.add_option('-f', '--force', action="store_true", dest='force', default=False)
parser.add_option('-x', '--exclude', dest='excluded', action='append', default=[])
parser.add_option('-o', '--output', dest='output')
options, args = parser.parse_args()
if len(args) > 1:
print("More than one path given")
sys.exit(1)
path = args.pop() if args else '.'
if os.path.isfile(path) and not options.output:
print("Must provide --output when specifying a file")
sys.exit(1)
output = options.output or path
suite = TestSuite(path, output)
suite.load(options.force)
suite.disable(options.excluded)
if suite.write():
print("Written `clar.suite`, `clar_suite.h` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count()))

52
deps/clar/main.c vendored Normal file
View File

@@ -0,0 +1,52 @@
#include "clar_libgit2.h"
#include "clar_libgit2_trace.h"
#ifdef GIT_DEBUG_LEAKCHECK_WIN32
# include "win32/w32_leakcheck.h"
#endif
#ifdef _WIN32
int __cdecl main(int argc, char *argv[])
#else
int main(int argc, char *argv[])
#endif
{
int res;
char *at_exit_cmd;
clar_test_init(argc, argv);
res = git_libgit2_init();
if (res < 0) {
const git_error *err = git_error_last();
const char *msg = err ? err->message : "unknown failure";
fprintf(stderr, "failed to init libgit2: %s\n", msg);
return res;
}
cl_global_trace_register();
cl_sandbox_set_homedir(getenv("CLAR_HOMEDIR"));
cl_sandbox_set_search_path_defaults();
cl_sandbox_disable_ownership_validation();
/* Run the test suite */
res = clar_test_run();
clar_test_shutdown();
cl_global_trace_disable();
git_libgit2_shutdown();
#ifdef GIT_DEBUG_LEAKCHECK_WIN32
if (git_win32_leakcheck_has_leaks())
res = res || 1;
#endif
at_exit_cmd = getenv("CLAR_AT_EXIT");
if (at_exit_cmd != NULL) {
int at_exit = system(at_exit_cmd);
return res || at_exit;
}
return res;
}