Initial commit

This commit is contained in:
jiapengyu
2026-07-27 13:37:48 +08:00
commit ecba71e2a5
39123 changed files with 5989154 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
static const char *
fixture_path(const char *base, const char *fixture_name)
{
static char _path[4096];
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;
}
#ifdef CLAR_FIXTURE_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_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_path, cl_fixture_basename(fixture_name)));
}
#endif
+317
View File
@@ -0,0 +1,317 @@
#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
fs_rmdir_helper(WCHAR *_wsource)
{
git_win32_path buffer;
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
size_t buffer_prefix_len;
/* Set up the buffer and capture the length */
wcscpy_s(buffer, GIT_WIN_PATH_UTF16, _wsource);
wcscat_s(buffer, GIT_WIN_PATH_UTF16, L"\\");
buffer_prefix_len = wcslen(buffer);
/* FindFirstFile needs a wildcard to match multiple items */
wcscat_s(buffer, GIT_WIN_PATH_UTF16, 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, GIT_WIN_PATH_UTF16 - 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)
{
git_win32_path wsource;
DWORD attrs;
/* The input path is UTF-8. Convert it to wide characters
* for use with the Windows API */
cl_assert(git_win32_path_from_utf8(wsource, _source));
/* 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)
{
git_win32_path buf_source, buf_dest;
HANDLE find_handle;
WIN32_FIND_DATAW find_data;
size_t buf_source_prefix_len, buf_dest_prefix_len;
wcscpy_s(buf_source, GIT_WIN_PATH_UTF16, _wsource);
wcscat_s(buf_source, GIT_WIN_PATH_UTF16, L"\\");
buf_source_prefix_len = wcslen(buf_source);
wcscpy_s(buf_dest, GIT_WIN_PATH_UTF16, _wdest);
wcscat_s(buf_dest, GIT_WIN_PATH_UTF16, L"\\");
buf_dest_prefix_len = wcslen(buf_dest);
/* Get an enumerator for the items in the source. */
wcscat_s(buf_source, GIT_WIN_PATH_UTF16, 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, GIT_WIN_PATH_UTF16 - buf_source_prefix_len, find_data.cFileName);
wcscpy_s(buf_dest + buf_dest_prefix_len, GIT_WIN_PATH_UTF16 - 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)
{
git_win32_path wsource, wdest;
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(git_win32_path_from_utf8(wsource, _source));
cl_assert(git_win32_path_from_utf8(wdest, _dest));
/* 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, GIT_WIN_PATH_UTF16, L"\\");
wcscat_s(wdest, GIT_WIN_PATH_UTF16, 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)
{
fs_rm(fixture_path(_clar_path, "*"));
}
#else
#include <errno.h>
#include <string.h>
static int
shell_out(char * const argv[])
{
int status, piderr;
pid_t pid;
pid = fork();
if (pid < 0) {
fprintf(stderr,
"System error: `fork()` call failed (%d) - %s\n",
errno, strerror(errno));
exit(-1);
}
if (pid == 0) {
execv(argv[0], argv);
}
do {
piderr = waitpid(pid, &status, WUNTRACED);
} while (piderr < 0 && (errno == EAGAIN || errno == EINTR));
return WEXITSTATUS(status);
}
static void
fs_copy(const char *_source, const char *dest)
{
char *argv[5];
char *source;
size_t source_len;
source = strdup(_source);
source_len = strlen(source);
if (source[source_len - 1] == '/')
source[source_len - 1] = 0;
argv[0] = "/bin/cp";
argv[1] = "-R";
argv[2] = source;
argv[3] = (char *)dest;
argv[4] = NULL;
cl_must_pass_(
shell_out(argv),
"Failed to copy test fixtures to sandbox"
);
free(source);
}
static void
fs_rm(const char *source)
{
char *argv[4];
argv[0] = "/bin/rm";
argv[1] = "-Rf";
argv[2] = (char *)source;
argv[3] = NULL;
cl_must_pass_(
shell_out(argv),
"Failed to cleanup the sandbox"
);
}
void
cl_fs_cleanup(void)
{
clar_unsandbox();
clar_sandbox();
}
#endif
+67
View File
@@ -0,0 +1,67 @@
static void clar_print_init(int test_count, int suite_count, const char *suite_names)
{
(void)test_count;
printf("Loaded %d suites: %s\n", (int)suite_count, suite_names);
printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n");
}
static void clar_print_shutdown(int test_count, int suite_count, int error_count)
{
(void)test_count;
(void)suite_count;
(void)error_count;
printf("\n\n");
clar_report_all();
}
static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error)
{
printf(" %d) Failure:\n", num);
printf("%s::%s [%s:%"PRIuZ"]\n",
report->suite,
report->test,
error->file,
error->line_number);
printf(" %s\n", error->error_msg);
if (error->description != NULL)
printf(" %s\n", error->description);
printf("\n");
fflush(stdout);
}
static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status status)
{
(void)test_name;
(void)test_number;
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_onsuite(const char *suite_name, int suite_index)
{
if (_clar.report_suite_names)
printf("\n%s", suite_name);
(void)suite_index;
}
static void clar_print_onabort(const char *msg, ...)
{
va_list argp;
va_start(argp, msg);
vfprintf(stderr, msg, argp);
va_end(argp);
}
+151
View File
@@ -0,0 +1,151 @@
#ifdef __APPLE__
#include <sys/syslimits.h>
#endif
static char _clar_path[4096];
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;
return (access(path, W_OK) == 0);
}
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)) {
#ifdef __APPLE__
if (length >= PATH_MAX && realpath(env, buffer) != NULL)
return 0;
#endif
strncpy(buffer, env, length);
return 0;
}
}
/* If the environment doesn't say anything, try to use /tmp */
if (is_valid_tmp_path("/tmp")) {
#ifdef __APPLE__
if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL)
return 0;
#endif
strncpy(buffer, "/tmp", length);
return 0;
}
#else
DWORD env_len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length);
if (env_len > 0 && env_len < (DWORD)length)
return 0;
if (GetTempPath((DWORD)length, buffer))
return 0;
#endif
/* This system doesn't like us, try to use the current directory */
if (is_valid_tmp_path(".")) {
strncpy(buffer, ".", length);
return 0;
}
return -1;
}
static void clar_unsandbox(void)
{
if (_clar_path[0] == '\0')
return;
cl_must_pass(chdir(".."));
fs_rm(_clar_path);
}
static int build_sandbox_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_path, sizeof(_clar_path)) < 0)
return -1;
len = strlen(_clar_path);
#ifdef _WIN32
{ /* normalize path to POSIX forward slashes */
size_t i;
for (i = 0; i < len; ++i) {
if (_clar_path[i] == '\\')
_clar_path[i] = '/';
}
}
#endif
if (_clar_path[len - 1] != '/') {
_clar_path[len++] = '/';
}
strncpy(_clar_path + len, path_tail, sizeof(_clar_path) - len);
#if defined(__MINGW32__)
if (_mktemp(_clar_path) == NULL)
return -1;
if (mkdir(_clar_path, 0700) != 0)
return -1;
#elif defined(_WIN32)
if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0)
return -1;
if (mkdir(_clar_path, 0700) != 0)
return -1;
#else
if (mkdtemp(_clar_path) == NULL)
return -1;
#endif
return 0;
}
static int clar_sandbox(void)
{
if (_clar_path[0] == '\0' && build_sandbox_path() < 0)
return -1;
if (chdir(_clar_path) != 0)
return -1;
return 0;
}
const char *clar_sandbox_path(void)
{
return _clar_path;
}
+134
View File
@@ -0,0 +1,134 @@
#include <stdio.h>
#include <time.h>
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);
}
int clar_summary_testsuites(struct clar_summary *summary)
{
return fprintf(summary->fp, "<testsuites>\n");
}
int clar_summary_testsuite(struct clar_summary *summary,
int idn, const char *name, const char *pkg, time_t timestamp,
double elapsed, int test_count, int fail_count, int error_count)
{
struct tm *tm = localtime(&timestamp);
char iso_dt[20];
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\""
" package=\"%s\""
" hostname=\"localhost\""
" timestamp=\"%s\""
" time=\"%.2f\""
" tests=\"%d\""
" failures=\"%d\""
" errors=\"%d\">\n",
idn, name, pkg, iso_dt, elapsed, test_count, fail_count, error_count);
}
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);
}
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);
}
struct clar_summary *clar_summary_init(const char *filename)
{
struct clar_summary *summary;
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL)
return NULL;
if ((summary = malloc(sizeof(struct clar_summary))) == NULL) {
fclose(fp);
return NULL;
}
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, "",
time(NULL), 0, _clar.tests_ran, _clar.total_errors, 0) < 0)
goto on_error;
}
last_suite = report->suite;
clar_summary_testcase(summary, report->test, "what", 0);
while (error != NULL) {
if (clar_summary_failure(summary, "assert",
error->error_msg, error->description) < 0)
goto on_error;
error = error->next;
}
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;
}