blob: 8b74e06efc7e98ee99d83721d045f6a95816d81d [file] [log] [blame] [edit]
#ifndef TEST_UTIL_COMMON_H
#define TEST_UTIL_COMMON_H
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <nih-dbus/test_dbus.h>
#include <nih/timer.h>
#include <nih/main.h>
#include <nih/child.h>
#include <nih/io.h>
#include <nih/list.h>
#include <nih/hash.h>
#include <nih/tree.h>
/**
* TEST_DIR_MODE:
*
* Mode to use when creating test directories.
**/
#define TEST_DIR_MODE 0750
#define BUFFER_SIZE 1024
/**
* TEST_EXIT_TIME:
*
* Maximum time we expect upstart to wait in the QUIESCE_PHASE_WAIT
* phase.
**/
#define TEST_EXIT_TIME 5
/**
* TEST_QUIESCE_KILL_PHASE:
*
* Maximum time we expect upstart to wait in the QUIESCE_PHASE_KILL
* phase.
**/
#define TEST_QUIESCE_KILL_PHASE 15
#define TEST_QUIESCE_TOTAL_WAIT_TIME (TEST_EXIT_TIME + TEST_QUIESCE_KILL_PHASE)
/**
* TEST_MAIN_LOOP_TIMEOUT_SECS:
*
* Number of seconds to wait until the main loop is exited in error.
*
* To avoid a test failure, all main loops must exit within this
* number of seconds.
**/
#define TEST_MAIN_LOOP_TIMEOUT_SECS 5
/* A 'reasonable' path, but which also contains a marker at the end so
* we know when we're looking at a PATH these tests have set.
*/
#define TEST_INITCTL_DEFAULT_PATH "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/wibble"
/* Default value for TERM if not already set */
#define TEST_INITCTL_DEFAULT_TERM "linux"
#ifdef ENABLE_CGROUPS
#define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
#endif /* ENABLE_CGROUPS */
/* TEST_ENSURE_CLEAN_ENV:
*
* Ensure the environment is as pristine as possible (to avoid follow-on
* errors caused by not freeing objects in a previous test, say)
*/
#define TEST_ENSURE_CLEAN_ENV() \
{ \
setvbuf(stdout, NULL, _IONBF, 0); \
\
if (job_classes) { \
TEST_HASH_EMPTY (job_classes); \
} \
\
if (conf_sources) { \
TEST_LIST_EMPTY (conf_sources); \
} \
\
if (nih_io_watches) { \
TEST_LIST_EMPTY (nih_io_watches); \
} \
\
if (nih_timers) { \
TEST_LIST_EMPTY (nih_timers); \
} \
\
if (events) { \
TEST_LIST_EMPTY (events); \
} \
}
/**
* TEST_WATCH_LOOP:
*
* Loop for NihIo object Updates, and process them until no watches
* left.
*/
#define TEST_WATCH_LOOP() \
{ \
/* Loop until we've fed all of the data. */ \
int first = TRUE; \
for (;;) { \
fd_set readfds, writefds, exceptfds; \
int nfds; \
\
nfds = 0; \
FD_ZERO (&readfds); \
FD_ZERO (&writefds); \
FD_ZERO (&exceptfds); \
\
nih_io_select_fds (&nfds, &readfds, \
&writefds, &exceptfds); \
if (! nfds) { \
if (first) \
TEST_FAILED ("expected to have " \
"data to feed."); \
break; \
} \
first = FALSE; \
\
select (nfds, &readfds, &writefds, &exceptfds, NULL); \
\
nih_io_handle_fds (&readfds, &writefds, &exceptfds); \
} \
}
/**
* _TEST_WATCH_UPDATE:
* @force: if TRUE, force an update,
* @timeout: struct timeval pointer, or NULL if no timeout required.
*
* Request NIH look for a file event relating to any NihIo objects,
* with an optional timeout. Behaviour can be forced via @force.
**/
#define _TEST_WATCH_UPDATE(force, timeout) \
{ \
int nfds = 0; \
int ret = 0; \
fd_set readfds, writefds, exceptfds; \
\
FD_ZERO (&readfds); \
FD_ZERO (&writefds); \
FD_ZERO (&exceptfds); \
\
nih_io_select_fds (&nfds, &readfds, &writefds, &exceptfds); \
if (! force) { \
ret = select (nfds, &readfds, &writefds, \
&exceptfds, timeout); \
} \
if (force || ret > 0) \
nih_io_handle_fds (&readfds, &writefds, &exceptfds); \
}
/**
* TEST_WATCH_UPDATE:
*
* Request NIH look for a file event relating to any NihIo objects,
* */
#define TEST_WATCH_UPDATE() \
_TEST_WATCH_UPDATE (0, NULL)
/**
* TEST_WATCH_UPDATE_TIMEOUT:
* @timeout: struct timeval pointer.
*
* Request NIH look for a file event relating to any NihIo objects
* within time period @timeout.
**/
#define TEST_WATCH_UPDATE_TIMEOUT(timeout) \
_TEST_WATCH_UPDATE (0, timeout)
/**
* TEST_WATCH_UPDATE_TIMEOUT_SECS:
* @secs: seconds to wait before timeout.
*
* Request NIH look for a file event relating to any NihIo objects
* within @secs timeout.
**/
#define TEST_WATCH_UPDATE_TIMEOUT_SECS(secs) \
{ \
struct timeval _t; \
_t.tv_sec = secs; \
_t.tv_usec = 0; \
_TEST_WATCH_UPDATE (0, &_t); \
}
/**
* TEST_FORCE_WATCH_UPDATE:
*
* Force NIH to look for a file event relating to any NihIo objects.
**/
#define TEST_FORCE_WATCH_UPDATE() \
_TEST_WATCH_UPDATE (1, NULL)
/**
* ENSURE_DIRECTORY_EMPTY:
* @path: Full path to a directory.
*
* Ensure specified directory is empty.
**/
#define ENSURE_DIRECTORY_EMPTY(path) \
{ \
DIR *dp = NULL; \
struct dirent *file = NULL; \
int count = 0; \
\
dp = opendir (path); \
TEST_NE_P (dp, NULL); \
\
while((file = readdir (dp))) { \
if (!strcmp (".", file->d_name) || \
!strcmp ("..", file->d_name)) \
continue; \
count++; \
} \
\
closedir (dp); \
\
TEST_EQ (count, 0); \
}
/**
* TEST_RESET_MAIN_LOOP:
*
* Reset main loop and associated test variables.
**/
#define TEST_RESET_MAIN_LOOP() \
if (nih_main_loop_functions) { \
nih_free (nih_main_loop_functions); \
nih_main_loop_functions = NULL; \
} \
if (nih_child_watches) { \
nih_free (nih_child_watches); \
nih_child_watches = NULL; \
} \
if (nih_timers) { \
nih_free (nih_timers); \
nih_timers = NULL; \
} \
nih_child_init (); \
nih_main_loop_init (); \
nih_timer_init (); \
nih_io_init ()
/**
* obj_string_check:
*
* @a: first object,
* @b: second object,
* @name: name of string element.
*
* Compare string element @name in objects @a and @b.
*
* Returns: 0 if strings are identical
* (or both NULL), else 1.
**/
#define obj_string_check(a, b, name) \
string_check ((a)->name, (b)->name)
/**
* obj_num_check:
*
* @a: first object,
* @b: second object.
* @name: name of numeric element.
*
* Compare numeric element @name in objects @a and @b.
*
* Returns: 0 if @a and @b are identical, else 1.
**/
#define obj_num_check(a, b, name) \
(a->name != b->name)
/**
* TEST_CMP_INT_ARRAYS:
* @a: first array,
* @b: second array,
* @sizea: size of @a,
* @sizeb: size of @b.
*
* Compare integer arrays @a and @b for equivalence.
*
* Returns: 0 if arrays are identical, else -1.
**/
#define TEST_CMP_INT_ARRAYS(a, b, sizea, sizeb) \
({int ret = 0; \
size_t __i; \
if (sizea == sizeb) { \
for (__i = 0; \
__i < sizea; \
__i++) { \
if ((a)[__i] != (b)[__i]) { \
ret = -1; \
break; \
} \
} \
} else \
ret = -1; \
ret;})
/**
* TEST_CMP_STR_ARRAYS:
* @a: first string array,
* @b: second string array,
* @sizea: length of @a,
* @sizeb: length of @b.
*
* Compare string arrays @a and @b for equivalence.
*
* Returns: 0 if arrays are identical, else -1.
**/
#define TEST_CMP_STR_ARRAYS(a, b, sizea, sizeb) \
({ int ret = 0; \
if (sizea == sizeb) { \
for (size_t __i = 0; \
__i < sizea; \
__i++) { \
if (strcmp (a[__i], b[__i])) { \
ret = -1; \
break; \
} \
} \
} else \
ret = -1; \
ret;})
/**
* TEST_TWO_LISTS_FOREACH:
* @list1: entry in the first list to iterate,
* @list2: entry in the second list to iterate,
* @iter1: name of iterator variable for @list1,
* @iter2: name of iterator variable for @list2.
*
* Dual version of NIH_LIST_FOREACH() which iterates
* two lists in tandem.
**/
#define TEST_TWO_LISTS_FOREACH(list1, list2, iter1, iter2) \
for (NihList *iter1 = (list1)->next, \
*iter2 = (list2)->next; \
iter1 != (list1) && iter2 != (list2); \
iter1 = iter1->next, \
iter2 = iter2->next)
/**
* TEST_TWO_HASHES_FOREACH:
* @hash1: entry in the first hash to iterate,
* @hash2: entry in the second hash to iterate,
* @iter1: name of iterator variable for @hash1,
* @iter2: name of iterator variable for @hash2.
*
* Dual version of NIH_HASH_FOREACH() which iterates
* two hashes in tandem.
**/
#define TEST_TWO_HASHES_FOREACH(hash1, hash2, iter1, iter2) \
for (size_t _##iter##_i = 0; _##iter##_i < (hash1)->size; \
_##iter##_i++) \
TEST_TWO_LISTS_FOREACH (&(hash1)->bins[_##iter##_i], \
&(hash2)->bins[_##iter##_i], \
iter1, iter2)
/**
* TEST_TWO_TREES_FOREACH:
* @tree1: root of the first tree to iterate,
* @tree2: root of the second tree to iterate,
* @iter1: name of iterator variable for @tree1,
* @iter2: name of iterator variable for @tree2.
*
* Dual version of NIH_TREE_FOREACH() which walks
* two trees in tandem.
**/
#define TEST_TWO_TREES_FOREACH(tree1, tree2, iter1, iter2) \
for (NihTree *iter1 = nih_tree_next (tree1, NULL), \
*iter2 = nih_tree_next (tree2, NULL); \
iter1 != NULL && iter2 != NULL; \
iter1 = nih_tree_next (tree1, iter1), \
iter2 = nih_tree_next (tree2, iter2))
/**
* TEST_ARRAY_SIZE:
* @array: array.
*
* Determine size of specified array.
*
* Returns: array size.
**/
#define TEST_ARRAY_SIZE(array) \
(sizeof (array) / sizeof (array[0]))
/**
* START_UPSTART:
*
* @pid: pid_t that will contain pid of running instance on success,
* @user_mode: TRUE for Session Init (or FALSE to use D-Bus
* session bus).
*
* Start an instance of Upstart and return PID in @pid.
**/
#define START_UPSTART(pid, user_mode) \
start_upstart_common (&(pid), user_mode, FALSE, NULL, NULL, NULL)
/**
* KILL_UPSTART:
*
* @pid: pid of upstart to kill,
* @signo: signal number to send to @pid,
* @wait: TRUE to wait for @pid to die.
*
* Send specified signal to upstart process @pid.
**/
#define KILL_UPSTART(pid, signo, wait) \
{ \
int status; \
assert (pid); \
assert (signo); \
\
assert0 (kill (pid, signo)); \
if (wait) { \
TEST_EQ (waitpid (pid, &status, 0), pid); \
TEST_TRUE (WIFSIGNALED (status)); \
TEST_EQ (WTERMSIG (status), signo); \
} \
/* reset since a subsequent start could specify a different \
* user_mode value. \
*/ \
test_user_mode = FALSE; \
}
/**
* STOP_UPSTART:
*
* @pid: pid of upstart to kill.
*
* Stop upstart process @pid.
**/
#define STOP_UPSTART(pid) \
KILL_UPSTART (pid, SIGKILL, TRUE)
/**
* REEXEC_UPSTART:
*
* @pid: pid of upstart,
* @user: TRUE if @pid refers to a Session Init, else FALSE.
*
* Force upstart to perform a re-exec.
**/
#define REEXEC_UPSTART(pid, user) \
if (user) { \
session_init_reexec (pid); \
} else { \
KILL_UPSTART (pid, SIGTERM, FALSE); \
} \
wait_for_upstart (user ? pid : FALSE)
/**
* RUN_COMMAND:
*
* @parent: pointer to parent object,
* @cmd: string representing command to run,
* @result: "char ***" pointer which will contain an array of string
* values corresponding to lines of standard output generated by @cmd,
* @len: size_t pointer which will be set to length of @result.
*
* Run a command and return its standard output. It is the callers
* responsibility to free @result. Errors from setting up the @cmd process are
* fatal. Returns the waitpid()-like status otherwise.
*
* Note: trailing '\n' characters are removed in returned command
* output.
*
* Returns the exit status of the command.
**/
#define RUN_COMMAND(parent, cmd, result, len) \
({ \
FILE *f; \
char buffer[BUFFER_SIZE]; \
char **ret; \
int res; \
\
assert (cmd[0]); \
\
*(result) = nih_str_array_new (parent); \
TEST_NE_P (*result, NULL); \
*(len) = 0; \
\
f = popen (cmd, "r"); \
TEST_NE_P (f, NULL); \
\
while (1) { \
if (! fgets (buffer, BUFFER_SIZE, f)) { \
if (ferror (f) && errno == EINTR) { \
clearerr(f); \
continue; \
} \
break; \
} \
size_t l = strlen (buffer)-1; \
\
if ( buffer[l] == '\n') \
buffer[l] = '\0'; \
ret = nih_str_array_add (result, parent, len, \
buffer); \
TEST_NE_P (ret, NULL); \
} \
if (ferror (f)) \
TEST_FAILED ("fgets() failure: %s", \
strerror (errno)); \
\
TEST_NE ((res = pclose (f)), -1); \
res; \
})
#define TEST_RUN_COMMAND(parent, cmd, result, len) \
TEST_EQ (RUN_COMMAND(parent, cmd, result, len), 0)
/**
* CREATE_FILE:
*
* @dirname: directory name (assumed to already exist),
* @name: name of file to create (no leading slash),
* @contents: string contents of @name.
*
* Create a file in the specified directory with the specified
* contents.
*
* Notes: A newline character is added in the case where @contents does
* not end with one.
**/
#define CREATE_FILE(dirname, name, contents) \
{ \
FILE *f; \
char filename[PATH_MAX]; \
\
assert (dirname[0]); \
assert (name[0]); \
\
strcpy (filename, dirname); \
if ( name[0] != '/' ) \
strcat (filename, "/"); \
strcat (filename, name); \
f = fopen (filename, "w"); \
TEST_NE_P (f, NULL); \
fprintf (f, "%s", contents); \
if ( contents[strlen(contents)-1] != '\n') \
fprintf (f, "\n"); \
fclose (f); \
}
/**
* DELETE_FILE:
*
* @dirname: directory in which file to delete exists,
* @name: name of file in @dirname to delete.
*
* Delete specified file.
*
**/
#define DELETE_FILE(dirname, name) \
{ \
char filename[PATH_MAX]; \
\
assert (dirname[0]); \
assert (name[0]); \
\
strcpy (filename, dirname); \
if ( name[0] != '/' ) \
strcat (filename, "/"); \
strcat (filename, name); \
\
TEST_EQ (unlink (filename), 0); \
}
/**
* _WAIT_FOR_FILE():
*
* @path: full path to file to look for,
* @sleep_msecs: number of milliseconds to sleep per loop,
* @loops: number of times to check for file.
*
* Wait for a reasonable period of time for @path to be created.
*
* Abort if file does not appear within (sleep_msecs * loops) milliseconds.
*
* XXX:WARNING: this is intrinsically racy since although the file has
* been _created_, it has not necessarily been fully written at the
* point this macro signifies success. For that we need inotify or
* similar.
**/
#define _WAIT_FOR_FILE(path, sleep_msecs, loops) \
{ \
int ok; \
struct stat statbuf; \
\
assert (path[0]); \
\
/* Wait for log to be created */ \
ok = FALSE; \
for (int i = 0; i <= loops; i++) { \
if (i != 0) \
msleep (sleep_msecs); \
if (! stat (path, &statbuf)) { \
ok = TRUE; \
break; \
} \
} \
TEST_EQ (ok, TRUE); \
}
/**
* WAIT_FOR_FILE():
*
* @path: full path to file to look for.
*
* Wait for a "reasonable period of time" for @path to be created.
*
* Abort if file does not appear within.
**/
#define WAIT_FOR_FILE(path) \
_WAIT_FOR_FILE (path, 100, 50)
/**
* TEST_STR_MATCH:
* @_string: string to check,
* @_pattern: pattern to expect.
*
* Check that @_string matches the glob pattern @_pattern, which
* should include the terminating newline if one is expected.
*
* Notes: Analagous to TEST_FILE_MATCH().
**/
#define TEST_STR_MATCH(_string, _pattern) \
do { \
if (fnmatch ((_pattern), _string, 0)) \
TEST_FAILED ("wrong string value, " \
"expected '%s' got '%s'", \
(_pattern), _string); \
} while (0)
/**
* _TEST_STR_ARRAY_CONTAINS:
*
* @_array: string array,
* @_pattern: pattern to expect,
* @_invert: invert meaning.
*
* Check that atleast 1 element in @_array matches @_pattern.
*
* If @_invert is TRUE, ensure @_pattern is _NOT_ found in @_array.
**/
#define _TEST_STR_ARRAY_CONTAINS(_array, _pattern, _invert) \
do { \
char **p; \
int got = FALSE; \
\
for (p = _array; p && *p; p++) { \
\
if (! fnmatch ((_pattern), *p, 0)) { \
got = TRUE; \
break; \
} \
} \
\
if (_invert) { \
if (got) { \
TEST_FAILED ("wrong content in array " \
"%p (%s), '%s' found unexpectedly", \
(_array), #_array, (_pattern)); \
} \
} else { \
if (! got) { \
TEST_FAILED ("wrong content in array " \
"%p (%s), '%s' not found", \
(_array), #_array, (_pattern)); \
} \
} \
} while (0)
/**
* _TEST_FILE_CONTAINS:
* @_file: FILE to read from,
* @_pattern: pattern to expect,
* @_invert: invert meaning.
*
* Check that any line in file @_file matches the glob pattern @_pattern, which
* should include the terminating newline if one is expected.
*
* If @_invert is TRUE, ensure @_pattern is _NOT_ found in @_file.
**/
#define _TEST_FILE_CONTAINS(_file, _pattern, _invert) \
do { \
char buffer[1024]; \
int got = FALSE; \
int ret; \
rewind (_file); \
while (fgets (buffer, sizeof (buffer), _file)) { \
\
ret = fnmatch ((_pattern), buffer, 0); \
\
if (! ret) { \
got = TRUE; \
break; \
} \
} \
\
if (_invert) { \
if (got) { \
TEST_FAILED ("wrong content in file " \
"%p (%s), '%s' found unexpectedly", \
(_file), #_file, (_pattern)); \
} \
} else { \
if (! got) { \
TEST_FAILED ("wrong content in file " \
"%p (%s), '%s' not found", \
(_file), #_file, (_pattern)); \
} \
} \
} while (0)
/**
* TEST_FILE_CONTAINS:
* @_file: FILE to read from,
* @_pattern: pattern to expect.
*
* Check that any line in file @_file matches the glob pattern @_pattern, which
* should include the terminating newline if one is expected.
*
**/
#define TEST_FILE_CONTAINS(_file, _pattern) \
_TEST_FILE_CONTAINS(_file, _pattern, FALSE)
/**
* TEST_FILE_NOT_CONTAINS:
* @_file: FILE to read from,
* @_pattern: pattern NOT to expect.
*
* Check that no line in file @_file does NOT match the glob pattern @_pattern,
* which should include the terminating newline if one is expected.
*
**/
#define TEST_FILE_NOT_CONTAINS(_file, _pattern) \
_TEST_FILE_CONTAINS(_file, _pattern, TRUE)
/**
* TEST_STR_ARRAY_CONTAINS:
*
* @_array: string array,
* @_pattern: pattern to expect.
*
* Check that atleast 1 element in @_array matches @_pattern.
**/
#define TEST_STR_ARRAY_CONTAINS(_array, _pattern) \
_TEST_STR_ARRAY_CONTAINS (_array, _pattern, FALSE)
/**
* TEST_STR_ARRAY_NOT_CONTAINS:
*
* @_array: string array,
* @_pattern: pattern to expect.
*
* Check that no element in @_array matches @_pattern.
**/
#define TEST_STR_ARRAY_NOT_CONTAINS(_array, _pattern) \
_TEST_STR_ARRAY_CONTAINS (_array, _pattern, TRUE)
extern int test_user_mode;
static inline void
msleep (unsigned int msecs) {
struct timespec req = {
.tv_sec = msecs / 1000,
.tv_nsec = (msecs % 1000) * 1000 * 1000,
};
nanosleep (&req, NULL);
}
/**
* NihTreeHandler:
* @node: tree entry being visited,
* @data: data pointer.
*
* A tree handler is a function called for each tree node
* when iterating over a tree.
*
* Returns: TRUE if tree entry process correctly, else FALSE.
**/
typedef int (*NihTreeHandler) (NihTree *node, void *data);
/* Prototypes */
int set_upstart_session (pid_t session_init_pid)
__attribute__ ((warn_unused_result));
void wait_for_upstart (int session_init_pid);
void session_init_reexec (int session_init_pid);
int have_timed_waitpid (void)
__attribute__ ((warn_unused_result));
pid_t timed_waitpid (pid_t pid, time_t timeout)
__attribute__ ((warn_unused_result));
char * get_initctl (void)
__attribute__ ((warn_unused_result));
void _start_upstart (pid_t *pid, int user, char * const *args);
void start_upstart_common (pid_t *pid, int user, int inherit_env,
const char *confdir, const char *logdir,
char * const *extra);
void start_upstart (pid_t *pid);
pid_t job_to_pid (const char *job)
__attribute__ ((warn_unused_result));
int string_check (const char *a, const char *b)
__attribute__ ((warn_unused_result));
const char *get_upstart_binary (void)
__attribute__ ((warn_unused_result));
const char *get_initctl_binary (void)
__attribute__ ((warn_unused_result));
int strcmp_compar (const void *a, const void *b)
__attribute__ ((warn_unused_result));
char *get_session_file (const char *xdg_runtime_dir, pid_t pid)
__attribute__ ((warn_unused_result));
int in_chroot (void)
__attribute__ ((warn_unused_result));
int dbus_configured (void)
__attribute__ ((warn_unused_result));
char *search_and_replace (void *parent, const char *str,
const char *from, const char *to)
__attribute__ ((warn_unused_result));
int file_exists (const char *path)
__attribute__ ((warn_unused_result));
void test_common_setup (void);
void test_common_cleanup (void);
void timer_cb (void *data, NihTimer *timer);
void test_job_process_handler (void *data, pid_t pid,
NihChildEvents event, int status);
void test_main_loop_func (void *data, NihMainLoopFunc *self);
int fd_valid (int fd)
__attribute__ ((warn_unused_result));
NihIoBuffer *read_from_fd (void *parent, int fd)
__attribute__ ((warn_unused_result));
typedef int (*NihListHandler) (NihList *entry, void *data);
int test_list_handler_generic (NihList *entry, void *data)
__attribute__ ((unused, noinline));
int test_list_foreach (const NihList *list, size_t *len,
NihListHandler handler, void *data)
__attribute__((unused));
size_t test_list_count (const NihList *list)
__attribute__((warn_unused_result, unused));
NihList *test_list_get_index (NihList *list, size_t count)
__attribute__((warn_unused_result, unused));
int test_hash_foreach (const NihHash *hash, size_t *len,
NihListHandler handler, void *data)
__attribute__((unused));
size_t test_hash_count (const NihHash *hash)
__attribute__((warn_unused_result, unused));
int test_tree_foreach (NihTree *tree, size_t *len,
NihTreeHandler handler, void *data)
__attribute__((unused));
size_t test_tree_count (NihTree *tree)
__attribute__((warn_unused_result, unused));
int connect_to_cgmanager (void)
__attribute__((warn_unused_result));
void disconnect_cgmanager (void);
char *get_pid_cgroup (const char *controller, pid_t pid)
__attribute__((warn_unused_result));
int setup_cgroup_sandbox (void)
__attribute__((warn_unused_result));
#endif /* TEST_UTIL_COMMON_H */