mirror of
https://github.com/containers/crun.git
synced 2026-02-06 09:46:18 +01:00
Add additional unit tests for utils.c to improve coverage: - test_has_prefix: Test has_prefix string function - test_has_suffix: Test has_suffix string function - test_str_join_array: Test str_join_array with various inputs - test_get_current_timestamp: Test timestamp generation - test_crun_ensure_directory: Test directory creation/existence 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
679 lines
16 KiB
C
679 lines
16 KiB
C
/*
|
|
* crun - OCI runtime written in C
|
|
*
|
|
* Copyright (C) 2017, 2018, 2019 Giuseppe Scrivano <giuseppe@scrivano.org>
|
|
* crun is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* crun is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with crun. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <libcrun/error.h>
|
|
#include <libcrun/utils.h>
|
|
#include <libcrun/cgroup.h>
|
|
#include <libcrun/cgroup-systemd.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
typedef int (*test) ();
|
|
|
|
extern int cpuset_string_to_bitmask (const char *str, char **out, size_t *out_size, libcrun_error_t *err);
|
|
|
|
static int
|
|
test_socket_pair ()
|
|
{
|
|
libcrun_error_t err = NULL;
|
|
int fds[2];
|
|
__attribute__ ((unused)) cleanup_close int fd0 = -1;
|
|
__attribute__ ((unused)) cleanup_close int fd1 = -1;
|
|
char buffer[256];
|
|
int ret = create_socket_pair (fds, &err);
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
fd0 = fds[0];
|
|
fd1 = fds[1];
|
|
|
|
if (err != NULL)
|
|
return -1;
|
|
|
|
ret = write (fds[0], "HELLO", 6);
|
|
if (ret != 6)
|
|
return -1;
|
|
|
|
ret = read (fds[1], buffer, sizeof (buffer));
|
|
if (ret != 6)
|
|
return -1;
|
|
if (strcmp (buffer, "HELLO") != 0)
|
|
return -1;
|
|
|
|
ret = write (fds[1], "WORLD", 6);
|
|
if (ret != 6)
|
|
return -1;
|
|
|
|
ret = read (fds[0], buffer, sizeof (buffer));
|
|
if (ret != 6)
|
|
return -1;
|
|
|
|
if (strcmp (buffer, "WORLD") != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_send_receive_fd ()
|
|
{
|
|
libcrun_error_t err = NULL;
|
|
int fds[2], pipes[2];
|
|
cleanup_close int fd0 = -1;
|
|
cleanup_close int fd1 = -1;
|
|
pid_t pid;
|
|
int ret = create_socket_pair (fds, &err);
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
fd0 = fds[0];
|
|
fd1 = fds[1];
|
|
|
|
if (err != NULL)
|
|
return -1;
|
|
|
|
pid = fork ();
|
|
if (pid < 0)
|
|
return -1;
|
|
|
|
if (pid)
|
|
{
|
|
cleanup_close int pipefd0 = -1;
|
|
cleanup_close int pipefd1 = -1;
|
|
char buffer[256];
|
|
const char *test_string = "TEST STRING";
|
|
if (pipe (pipes) < 0)
|
|
return -1;
|
|
|
|
pipefd0 = pipes[0];
|
|
pipefd1 = pipes[1];
|
|
|
|
close (fd0);
|
|
fd0 = -1;
|
|
|
|
if (send_fd_to_socket (fd1, pipefd0, &err) < 0)
|
|
return -1;
|
|
|
|
if (write (pipefd1, test_string, strlen (test_string) + 1) < 0)
|
|
return -1;
|
|
|
|
ret = read (fd1, buffer, sizeof (buffer));
|
|
if (ret < 0)
|
|
return -1;
|
|
|
|
if (ret != (int) strlen (test_string) + 1)
|
|
return -1;
|
|
|
|
return strcmp (buffer, test_string);
|
|
}
|
|
else
|
|
{
|
|
char buffer[256];
|
|
int ret, fd = receive_fd_from_socket (fd0, &err);
|
|
if (fd < 0)
|
|
_exit (1);
|
|
|
|
close (fd1);
|
|
fd1 = -1;
|
|
|
|
ret = read (fd, buffer, sizeof (buffer));
|
|
if (ret <= 0)
|
|
return -1;
|
|
if (write (fd0, buffer, ret) < 0)
|
|
return -1;
|
|
|
|
_exit (0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_run_process ()
|
|
{
|
|
libcrun_error_t err = NULL;
|
|
{
|
|
char *args[] = { "/bin/true", NULL };
|
|
if (run_process (args, &err) != 0)
|
|
return -1;
|
|
}
|
|
|
|
{
|
|
char *args[] = { "/bin/false", NULL };
|
|
if (run_process (args, &err) <= 0)
|
|
return -1;
|
|
if (err != NULL)
|
|
return -1;
|
|
}
|
|
|
|
{
|
|
char *args[] = { "/does/not/exist", NULL };
|
|
int r = run_process (args, &err);
|
|
if (r <= 0)
|
|
return -1;
|
|
if (err != NULL)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_dir_p ()
|
|
{
|
|
libcrun_error_t err;
|
|
if (crun_dir_p ("/usr", false, &err) <= 0)
|
|
return -1;
|
|
if (crun_dir_p ("/dev/zero", false, &err) != 0)
|
|
return -1;
|
|
if (crun_dir_p ("/hopefully/does/not/really/exist", false, &err) >= 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_write_read_file ()
|
|
{
|
|
libcrun_error_t err = NULL;
|
|
cleanup_free char *name = NULL;
|
|
size_t len;
|
|
size_t i;
|
|
int ret, failed = 0;
|
|
size_t max = 1 << 10;
|
|
cleanup_free char *written = xmalloc (max);
|
|
|
|
xasprintf (&name, "tests/write-file-%i", getpid ());
|
|
|
|
for (i = 0; i < max; i++)
|
|
written[i] = i;
|
|
|
|
for (i = 1; i <= max; i *= 2)
|
|
{
|
|
cleanup_free char *read_buf = NULL;
|
|
|
|
ret = write_file (name, written, i, &err);
|
|
if (ret < 0)
|
|
{
|
|
failed = 1;
|
|
break;
|
|
}
|
|
|
|
ret = read_all_file (name, &read_buf, &len, &err);
|
|
if (ret < 0)
|
|
{
|
|
failed = 1;
|
|
break;
|
|
}
|
|
|
|
if (len != i)
|
|
{
|
|
failed = 1;
|
|
break;
|
|
}
|
|
|
|
if (memcmp (read_buf, written, len) != 0)
|
|
{
|
|
failed = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
unlink (name);
|
|
return failed ? -1 : 0;
|
|
}
|
|
|
|
static int
|
|
test_crun_path_exists ()
|
|
{
|
|
|
|
libcrun_error_t err = NULL;
|
|
if (crun_path_exists ("/dev/null", &err) <= 0)
|
|
return -1;
|
|
if (crun_path_exists ("/usr/foo/bin/ls", &err) != 0)
|
|
return -1;
|
|
if (crun_path_exists ("/proc/sysrq-trigger", &err) != 1)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_path_is_slash_dev ()
|
|
{
|
|
if (! path_is_slash_dev ("/dev"))
|
|
return -1;
|
|
if (! path_is_slash_dev ("/dev//"))
|
|
return -1;
|
|
if (! path_is_slash_dev ("///dev///"))
|
|
return -1;
|
|
if (! path_is_slash_dev ("/dev/"))
|
|
return -1;
|
|
if (! path_is_slash_dev ("dev"))
|
|
return -1;
|
|
if (! path_is_slash_dev ("dev////"))
|
|
return -1;
|
|
if (path_is_slash_dev ("dev////foo"))
|
|
return -1;
|
|
if (path_is_slash_dev ("/dev/foo"))
|
|
return -1;
|
|
if (path_is_slash_dev ("/dev/foo/"))
|
|
return -1;
|
|
if (path_is_slash_dev ("/dev/foo/bar"))
|
|
return -1;
|
|
if (path_is_slash_dev ("///dev/foo//bar"))
|
|
return -1;
|
|
if (path_is_slash_dev ("///dev/foo/////"))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_append_paths ()
|
|
{
|
|
#define PROLOGUE() \
|
|
cleanup_free char *out = NULL; \
|
|
libcrun_error_t err = NULL; \
|
|
int ret;
|
|
|
|
#define EXPECT_STRING(exp) \
|
|
{ \
|
|
if (ret < 0 || out == NULL) \
|
|
{ \
|
|
crun_error_release (&err); \
|
|
return ret; \
|
|
} \
|
|
if (strcmp (out, exp)) \
|
|
return -1; \
|
|
}
|
|
{
|
|
PROLOGUE ();
|
|
ret = append_paths (&out, &err, "/sys/fs/cgroup/", "memory", "some/path", NULL);
|
|
EXPECT_STRING ("/sys/fs/cgroup/memory/some/path");
|
|
}
|
|
{
|
|
PROLOGUE ();
|
|
ret = append_paths (&out, &err, "/sys/fs/cgroup", "memory", "some/path", NULL);
|
|
EXPECT_STRING ("/sys/fs/cgroup/memory/some/path");
|
|
}
|
|
{
|
|
PROLOGUE ();
|
|
ret = append_paths (&out, &err, "/sys/fs/cgroup////////", "memory////////", "some/path//////", NULL);
|
|
EXPECT_STRING ("/sys/fs/cgroup/memory/some/path");
|
|
}
|
|
{
|
|
PROLOGUE ();
|
|
ret = append_paths (&out, &err, "/sys/fs/cgroup////////", "memory////////", "///////some/path//////", NULL);
|
|
EXPECT_STRING ("/sys/fs/cgroup/memory/some/path");
|
|
}
|
|
{
|
|
PROLOGUE ();
|
|
ret = append_paths (&out, &err, "", "//", "", "", "", NULL);
|
|
EXPECT_STRING ("/");
|
|
}
|
|
{
|
|
PROLOGUE ();
|
|
ret = append_paths (&out, &err, "///", "/", "", "///", "a", NULL);
|
|
EXPECT_STRING ("/a");
|
|
}
|
|
{
|
|
PROLOGUE ();
|
|
ret = append_paths (&out, &err, "////", "/////", "///", "", "", NULL);
|
|
EXPECT_STRING ("/");
|
|
}
|
|
{
|
|
PROLOGUE ();
|
|
ret = append_paths (&out, &err, "", "", "", "", "", NULL);
|
|
EXPECT_STRING ("");
|
|
}
|
|
{
|
|
PROLOGUE ();
|
|
ret = append_paths (&out, &err, "", "///sys/fs/cgroup////////", "", "some/path", NULL);
|
|
EXPECT_STRING ("/sys/fs/cgroup/some/path");
|
|
}
|
|
{
|
|
PROLOGUE ();
|
|
ret = append_paths (&out, &err, "a", "b", "c", "d", "a", "b", "c", "d", "a", "b", "c", "d",
|
|
"a", "b", "c", "d", "a", "b", "c", "d", "a", "b", "c", "d",
|
|
"a", "b", "c", "d", "a", "b", "c", "d", "a", "b", "c", "d", NULL);
|
|
if (ret == 0)
|
|
return -1;
|
|
crun_error_release (&err);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#ifdef HAVE_SYSTEMD
|
|
static int
|
|
test_parse_sd_array ()
|
|
{
|
|
char *out, *next;
|
|
libcrun_error_t err = NULL;
|
|
{
|
|
cleanup_free char *s = xstrdup (" 'firewalld.service' ");
|
|
if (parse_sd_array (s, &out, &next, &err) < 0)
|
|
{
|
|
crun_error_release (&err);
|
|
return -1;
|
|
}
|
|
if (strcmp (out, "firewalld.service"))
|
|
return -1;
|
|
if (next != NULL)
|
|
return -1;
|
|
}
|
|
{
|
|
cleanup_free char *s = xstrdup ("'fir\\ewalld.ser\\vi\\ce']");
|
|
if (parse_sd_array (s, &out, &next, &err) < 0)
|
|
{
|
|
crun_error_release (&err);
|
|
return -1;
|
|
}
|
|
if (strcmp (out, "firewalld.service"))
|
|
return -1;
|
|
if (next != NULL)
|
|
return -1;
|
|
}
|
|
{
|
|
cleanup_free char *s = xstrdup ("'fi\\rew\\alld.s\\erv\\ice', 'foo.service']");
|
|
if (parse_sd_array (s, &out, &next, &err) < 0)
|
|
{
|
|
crun_error_release (&err);
|
|
return -1;
|
|
}
|
|
if (strcmp (out, "firewalld.service"))
|
|
return -1;
|
|
if (strcmp (next, " 'foo.service']"))
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_get_scope_path ()
|
|
{
|
|
# define CHECK(x, y) \
|
|
{ \
|
|
cleanup_free char *r = x; \
|
|
if (strcmp (r, y)) \
|
|
{ \
|
|
fprintf (stderr, "expected %s, got %s\n", y, r); \
|
|
return -1; \
|
|
} \
|
|
}
|
|
|
|
CHECK (get_cgroup_scope_path ("foo.scope", "foo.scope"), "foo.scope");
|
|
CHECK (get_cgroup_scope_path ("/foo/bar/user.slice/foo.scope/a/b/c", "foo.scope"), "/foo/bar/user.slice/foo.scope");
|
|
CHECK (get_cgroup_scope_path ("/foo/bar-foo.scope/user.slice/foo.scope/a/b/c", "foo.scope"), "/foo/bar-foo.scope/user.slice/foo.scope");
|
|
CHECK (get_cgroup_scope_path ("/foo/foo.scope-bar/user.slice/foo.scope/a/b/c", "foo.scope"), "/foo/foo.scope-bar/user.slice/foo.scope");
|
|
CHECK (get_cgroup_scope_path ("////foo.scope", "foo.scope"), "////foo.scope");
|
|
|
|
# undef CHECK
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_cpuset_string_to_bitmask ()
|
|
{
|
|
libcrun_error_t err = NULL;
|
|
cleanup_free char *mask = NULL;
|
|
size_t mask_size;
|
|
|
|
if (cpuset_string_to_bitmask ("0", &mask, &mask_size, &err) < 0)
|
|
return -1;
|
|
if (mask_size != 1 || mask[0] != 0x01)
|
|
return -1;
|
|
|
|
free (mask);
|
|
mask = NULL;
|
|
|
|
if (cpuset_string_to_bitmask ("0-1", &mask, &mask_size, &err) < 0)
|
|
return -1;
|
|
if (mask_size != 1 || mask[0] != 0x03)
|
|
return -1;
|
|
|
|
free (mask);
|
|
mask = NULL;
|
|
|
|
if (cpuset_string_to_bitmask ("0,2", &mask, &mask_size, &err) < 0)
|
|
return -1;
|
|
if (mask_size != 1 || mask[0] != 0x05)
|
|
return -1;
|
|
|
|
free (mask);
|
|
mask = NULL;
|
|
|
|
if (cpuset_string_to_bitmask ("0-2", &mask, &mask_size, &err) < 0)
|
|
return -1;
|
|
if (mask_size != 1 || mask[0] != 0x07)
|
|
return -1;
|
|
|
|
free (mask);
|
|
mask = NULL;
|
|
|
|
if (cpuset_string_to_bitmask ("0,8", &mask, &mask_size, &err) < 0)
|
|
return -1;
|
|
if (mask_size != 2 || mask[0] != 0x01 || mask[1] != 0x01)
|
|
return -1;
|
|
|
|
free (mask);
|
|
mask = NULL;
|
|
|
|
if (cpuset_string_to_bitmask ("a", &mask, &mask_size, &err) == 0)
|
|
return -1;
|
|
|
|
if (cpuset_string_to_bitmask ("-1", &mask, &mask_size, &err) == 0)
|
|
return -1;
|
|
|
|
if (cpuset_string_to_bitmask ("0-", &mask, &mask_size, &err) == 0)
|
|
return -1;
|
|
|
|
if (cpuset_string_to_bitmask ("0-2,4-6", &mask, &mask_size, &err) < 0)
|
|
return -1;
|
|
if (mask_size != 1 || mask[0] != 0x77)
|
|
return -1;
|
|
|
|
free (mask);
|
|
mask = NULL;
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
test_has_prefix ()
|
|
{
|
|
if (! has_prefix ("hello world", "hello"))
|
|
return -1;
|
|
if (! has_prefix ("hello", "hello"))
|
|
return -1;
|
|
if (! has_prefix ("hello", ""))
|
|
return -1;
|
|
if (has_prefix ("hello", "world"))
|
|
return -1;
|
|
if (has_prefix ("hello", "hello world"))
|
|
return -1;
|
|
if (has_prefix ("", "hello"))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_has_suffix ()
|
|
{
|
|
if (! has_suffix ("hello world", "world"))
|
|
return -1;
|
|
if (! has_suffix ("world", "world"))
|
|
return -1;
|
|
if (! has_suffix ("hello", ""))
|
|
return -1;
|
|
if (has_suffix ("hello", "world"))
|
|
return -1;
|
|
if (has_suffix ("world", "hello world"))
|
|
return -1;
|
|
if (has_suffix ("", "hello"))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_str_join_array ()
|
|
{
|
|
{
|
|
char *array[] = { "a", "b", "c", NULL };
|
|
cleanup_free char *result = str_join_array (0, 3, array, ",");
|
|
if (result == NULL)
|
|
return -1;
|
|
if (strcmp (result, "a,b,c") != 0)
|
|
return -1;
|
|
}
|
|
{
|
|
char *array[] = { "hello", "world", NULL };
|
|
cleanup_free char *result = str_join_array (0, 2, array, " ");
|
|
if (result == NULL)
|
|
return -1;
|
|
if (strcmp (result, "hello world") != 0)
|
|
return -1;
|
|
}
|
|
{
|
|
char *array[] = { "single", NULL };
|
|
cleanup_free char *result = str_join_array (0, 1, array, ",");
|
|
if (result == NULL)
|
|
return -1;
|
|
if (strcmp (result, "single") != 0)
|
|
return -1;
|
|
}
|
|
{
|
|
/* Test with offset - size is the end index (exclusive) */
|
|
char *array[] = { "skip", "a", "b", NULL };
|
|
cleanup_free char *result = str_join_array (1, 3, array, "-");
|
|
if (result == NULL)
|
|
return -1;
|
|
if (strcmp (result, "a-b") != 0)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_get_current_timestamp ()
|
|
{
|
|
char buf[64];
|
|
|
|
get_current_timestamp (buf, sizeof (buf));
|
|
|
|
/* Check that timestamp is not empty */
|
|
if (buf[0] == '\0')
|
|
return -1;
|
|
|
|
/* Check that timestamp has expected format: starts with year (4 digits) */
|
|
if (buf[0] != '2') /* Year should start with 2 for 2xxx */
|
|
return -1;
|
|
|
|
/* Check that timestamp contains T separator (ISO 8601 format) */
|
|
if (strchr (buf, 'T') == NULL && strchr (buf, '-') == NULL)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
test_crun_ensure_directory ()
|
|
{
|
|
libcrun_error_t err = NULL;
|
|
cleanup_free char *path = NULL;
|
|
int ret;
|
|
|
|
xasprintf (&path, "/tmp/crun-test-dir-%d", getpid ());
|
|
|
|
/* Create directory */
|
|
ret = crun_ensure_directory (path, 0755, false, &err);
|
|
if (ret < 0)
|
|
{
|
|
crun_error_release (&err);
|
|
return -1;
|
|
}
|
|
|
|
/* Verify it's a directory */
|
|
ret = crun_dir_p (path, false, &err);
|
|
if (ret <= 0)
|
|
{
|
|
rmdir (path);
|
|
crun_error_release (&err);
|
|
return -1;
|
|
}
|
|
|
|
/* Creating again should succeed (already exists) */
|
|
ret = crun_ensure_directory (path, 0755, false, &err);
|
|
if (ret < 0)
|
|
{
|
|
rmdir (path);
|
|
crun_error_release (&err);
|
|
return -1;
|
|
}
|
|
|
|
/* Cleanup */
|
|
rmdir (path);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
run_and_print_test_result (const char *name, int id, test t)
|
|
{
|
|
int ret = t ();
|
|
if (ret == 0)
|
|
printf ("ok %d - %s\n", id, name);
|
|
else if (ret == 77)
|
|
printf ("ok %d - %s #SKIP\n", id, name);
|
|
else
|
|
printf ("not ok %d - %s\n", id, name);
|
|
}
|
|
|
|
#define RUN_TEST(T) \
|
|
do \
|
|
{ \
|
|
run_and_print_test_result (#T, id++, T); \
|
|
} while (0)
|
|
|
|
int
|
|
main ()
|
|
{
|
|
int id = 1;
|
|
#ifdef HAVE_SYSTEMD
|
|
printf ("1..16\n");
|
|
#else
|
|
printf ("1..13\n");
|
|
#endif
|
|
RUN_TEST (test_crun_path_exists);
|
|
RUN_TEST (test_write_read_file);
|
|
RUN_TEST (test_run_process);
|
|
RUN_TEST (test_dir_p);
|
|
RUN_TEST (test_socket_pair);
|
|
RUN_TEST (test_send_receive_fd);
|
|
RUN_TEST (test_append_paths);
|
|
RUN_TEST (test_path_is_slash_dev);
|
|
RUN_TEST (test_has_prefix);
|
|
RUN_TEST (test_has_suffix);
|
|
RUN_TEST (test_str_join_array);
|
|
RUN_TEST (test_get_current_timestamp);
|
|
RUN_TEST (test_crun_ensure_directory);
|
|
#ifdef HAVE_SYSTEMD
|
|
RUN_TEST (test_parse_sd_array);
|
|
RUN_TEST (test_get_scope_path);
|
|
RUN_TEST (test_cpuset_string_to_bitmask);
|
|
#endif
|
|
return 0;
|
|
}
|