Skip to content

Commit

Permalink
tempdir and sandboxes are now separate entities
Browse files Browse the repository at this point in the history
clar now has the concept of a _temporary directory_, which is the
temporary directory for the entirety of a `clar` invocation (a set of
test runs) and a sandbox, which is the temporary directory for a single
test invocation.

This allows us to ensure that a well-written test (that only writes into
its sandbox) doesn't poison the well for future test invocations if it
fails to clean up its sandbox.
  • Loading branch information
ethomson committed Jan 20, 2025
1 parent 62647e3 commit 0761710
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 36 deletions.
14 changes: 10 additions & 4 deletions clar.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,10 @@ static void clar_print_onabortv(const char *msg, va_list argp);
static void clar_print_onabort(const char *msg, ...);

/* From clar_sandbox.c */
static void clar_unsandbox(void);
static void clar_sandbox(void);
static void clar_tempdir_init(void);
static void clar_tempdir_shutdown(void);
static int clar_sandbox_create(const char *suite_name, const char *test_name);
static int clar_sandbox_cleanup(void);

/* From summary.h */
static struct clar_summary *clar_summary_init(const char *filename);
Expand Down Expand Up @@ -304,6 +306,8 @@ clar_run_test(

CL_TRACE(CL_TRACE__TEST__BEGIN);

clar_sandbox_create(suite->name, test->name);

_clar.last_report->start = time(NULL);
clar_time_now(&start);

Expand Down Expand Up @@ -331,6 +335,8 @@ clar_run_test(
if (cleanup->ptr != NULL)
cleanup->ptr();

clar_sandbox_cleanup();

CL_TRACE(CL_TRACE__TEST__END);

_clar.tests_ran++;
Expand Down Expand Up @@ -604,7 +610,7 @@ clar_test_init(int argc, char **argv)
if (_clar.write_summary)
_clar.summary = clar_summary_init(_clar.summary_filename);

clar_sandbox();
clar_tempdir_init();
}

int
Expand Down Expand Up @@ -636,7 +642,7 @@ clar_test_shutdown(void)
_clar.total_errors
);

clar_unsandbox();
clar_tempdir_shutdown();

if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0)
clar_abort("Failed to write the summary file '%s: %s.\n",
Expand Down
8 changes: 6 additions & 2 deletions clar.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
#define __CLAR_TEST_H__

#include <stdlib.h>
#include <limits.h>

#ifdef CLAR_WIN32_LONGPATHS
#if defined(_WIN32) && defined(CLAR_WIN32_LONGPATHS)
# define CLAR_MAX_PATH 4096
#else
#elif defined(_WIN32)
# define CLAR_MAX_PATH MAX_PATH
#else
# define CLAR_MAX_PATH PATH_MAX
#endif

#ifndef CLAR_SELFTEST
Expand Down Expand Up @@ -46,6 +49,7 @@ void clar_test_shutdown(void);
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);
Expand Down
4 changes: 2 additions & 2 deletions clar/fixtures.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const char *cl_fixture(const char *fixture_name)

void cl_fixture_sandbox(const char *fixture_name)
{
fs_copy(cl_fixture(fixture_name), _clar_path);
fs_copy(cl_fixture(fixture_name), clar_sandbox_path());
}

const char *cl_fixture_basename(const char *fixture_name)
Expand All @@ -45,6 +45,6 @@ const char *cl_fixture_basename(const char *fixture_name)

void cl_fixture_cleanup(const char *fixture_name)
{
fs_rm(fixture_path(_clar_path, cl_fixture_basename(fixture_name)));
fs_rm(fixture_path(clar_sandbox_path(), cl_fixture_basename(fixture_name)));
}
#endif
6 changes: 3 additions & 3 deletions clar/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ void
cl_fs_cleanup(void)
{
#ifdef CLAR_FIXTURE_PATH
fs_rm(fixture_path(_clar_path, "*"));
fs_rm(fixture_path(clar_tempdir_path(), "*"));
#else
((void)fs_copy); /* unused */
#endif
Expand Down Expand Up @@ -512,7 +512,7 @@ fs_rm(const char *path)
void
cl_fs_cleanup(void)
{
clar_unsandbox();
clar_sandbox();
clar_tempdir_shutdown();
clar_tempdir_init();
}
#endif
119 changes: 94 additions & 25 deletions clar/sandbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@
#include <sys/syslimits.h>
#endif

static char _clar_path[CLAR_MAX_PATH];
/*
* 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)
Expand Down Expand Up @@ -108,17 +118,17 @@ static int canonicalize_tmp_path(char *buffer)
#endif
}

static void clar_unsandbox(void)
static void clar_tempdir_shutdown(void)
{
if (_clar_path[0] == '\0')
if (_clar_tempdir[0] == '\0')
return;

cl_must_pass(chdir(".."));

fs_rm(_clar_path);
fs_rm(_clar_tempdir);
}

static int build_sandbox_path(void)
static int build_tempdir_path(void)
{
#ifdef CLAR_TMPDIR
const char path_tail[] = CLAR_TMPDIR "_XXXXXX";
Expand All @@ -128,57 +138,116 @@ static int build_sandbox_path(void)

size_t len;

if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0 ||
canonicalize_tmp_path(_clar_path) < 0)
if (find_tmp_path(_clar_tempdir, sizeof(_clar_tempdir)) < 0 ||
canonicalize_tmp_path(_clar_tempdir) < 0)
return -1;

len = strlen(_clar_path);
len = strlen(_clar_tempdir);

if (len + strlen(path_tail) + 1 > CLAR_MAX_PATH)
return -1;

if (_clar_path[len - 1] != '/')
_clar_path[len++] = '/';
if (_clar_tempdir[len - 1] != '/')
_clar_tempdir[len++] = '/';

strncpy(_clar_path + len, path_tail, sizeof(_clar_path) - len);
strncpy(_clar_tempdir + len, path_tail, sizeof(_clar_tempdir) - len);

#if defined(__MINGW32__)
if (_mktemp(_clar_path) == NULL)
if (_mktemp(_clar_tempdir) == NULL)
return -1;

if (mkdir(_clar_path, 0700) != 0)
if (mkdir(_clar_tempdir, 0700) != 0)
return -1;
#elif defined(_WIN32)
if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0)
if (_mktemp_s(_clar_tempdir, sizeof(_clar_tempdir)) != 0)
return -1;

if (mkdir(_clar_path, 0700) != 0)
if (mkdir(_clar_tempdir, 0700) != 0)
return -1;
#elif defined(__sun) || defined(__TANDEM)
if (mktemp(_clar_path) == NULL)
if (mktemp(_clar_tempdir) == NULL)
return -1;

if (mkdir(_clar_path, 0700) != 0)
if (mkdir(_clar_tempdir, 0700) != 0)
return -1;
#else
if (mkdtemp(_clar_path) == NULL)
if (mkdtemp(_clar_tempdir) == NULL)
return -1;
#endif

_clar_tempdir_len = strlen(_clar_tempdir);
return 0;
}

static void clar_sandbox(void)
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));
}

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)
{
if (_clar_path[0] == '\0' && build_sandbox_path() < 0)
clar_abort("Failed to build sandbox path.\n");
cl_assert(_clar_sandbox[0] == '\0');

if (chdir(_clar_path) != 0)
clar_abort("Failed to change into sandbox directory '%s': %s.\n",
_clar_path, strerror(errno));
cl_assert(strlen(_clar_tempdir) + strlen(suite_name) + strlen(test_name) + 2 < 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);

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');

fs_rm(_clar_sandbox);
_clar_sandbox[0] = '\0';

if (chdir(_clar_tempdir) != 0)
return -1;

return 0;
}

const char *clar_tempdir_path(void)
{
return _clar_tempdir;
}

const char *clar_sandbox_path(void)
{
return _clar_path;
return _clar_sandbox;
}

0 comments on commit 0761710

Please sign in to comment.