mirror of
https://github.com/libgit2/libgit2.git
synced 2026-06-22 06:26:26 +00:00
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:
1055
deps/clar/clar.c
vendored
Normal file
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
292
deps/clar/clar.h
vendored
Normal 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
50
deps/clar/clar/fixtures.h
vendored
Normal 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
529
deps/clar/clar/fs.h
vendored
Normal 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
245
deps/clar/clar/print.h
vendored
Normal 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
290
deps/clar/clar/sandbox.h
vendored
Normal 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
140
deps/clar/clar/summary.h
vendored
Normal 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(×tamp, &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
710
deps/clar/clar_libgit2.c
vendored
Normal 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
287
deps/clar/clar_libgit2.h
vendored
Normal 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
108
deps/clar/clar_libgit2_alloc.c
vendored
Normal 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(©bytes, 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
11
deps/clar/clar_libgit2_alloc.h
vendored
Normal 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
30
deps/clar/clar_libgit2_timer.c
vendored
Normal 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
35
deps/clar/clar_libgit2_timer.h
vendored
Normal 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
263
deps/clar/clar_libgit2_trace.c
vendored
Normal 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
7
deps/clar/clar_libgit2_trace.h
vendored
Normal 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
329
deps/clar/generate.py
vendored
Normal 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
52
deps/clar/main.c
vendored
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user