Skip to content

Commit

Permalink
lib-sieve: util: Add test suite for realpath.
Browse files Browse the repository at this point in the history
  • Loading branch information
stephanbosch committed Feb 13, 2018
1 parent b15a498 commit 682e40a
Show file tree
Hide file tree
Showing 2 changed files with 305 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/lib-sieve/util/Makefile.am
Expand Up @@ -21,3 +21,25 @@ headers = \

pkginc_libdir=$(dovecot_pkgincludedir)/sieve
pkginc_lib_HEADERS = $(headers)

test_programs = \
test-realpath

noinst_PROGRAMS = $(test_programs)

test_libs = \
libsieve_util.la \
$(LIBDOVECOT)
test_deps = \
libsieve_util.la \
$(LIBDOVECOT_DEPS)

test_realpath_SOURCES = test-realpath.c
test_realpath_LDADD = $(test_libs)
test_realpath_DEPENDENCIES = $(test_deps)

check: check-am check-test
check-test: all-am
for bin in $(test_programs); do \
if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
done
283 changes: 283 additions & 0 deletions src/lib-sieve/util/test-realpath.c
@@ -0,0 +1,283 @@
/* Copyright (c) 2009-2018 Pigeonhole authors, see the included COPYING file */

#include "test-lib.h"
#include "abspath.h"
#include "realpath.h"
#include "unlink-directory.h"
#include "str.h"

#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>

#define TEMP_DIRNAME ".test-realpath"

static const char *tmpdir;
static const char *cwd;
static const char *link1;
static const char *link2;
static const char *link3;
static const char *link4;

static void test_local_path(void)
{
const char *expected = t_strconcat(cwd, "/README.md", NULL);
const char *npath = NULL;
test_assert(t_normpath_to("README.md", cwd, &npath) == 0);
test_assert_strcmp(npath, expected);
}

static void test_absolute_path_no_change(void)
{
const char *npath = NULL;
test_assert(t_normpath_to("/", "/", &npath) == 0);
test_assert_strcmp(npath, "/");

test_assert(t_normpath_to(cwd, cwd, &npath) == 0);
test_assert_strcmp(npath, cwd);
}

static int path_height(const char *p)
{
int n;
for (n = 0; *p != '\0'; ++p)
n += *p == '/';
return n;
}

static void test_travel_to_root(void)
{
int l = path_height(cwd);
const char *npath = cwd;
for (npath = cwd; l != 0; l--) {
test_assert_idx(t_normpath_to("../", npath, &npath) == 0, l);
}
test_assert_strcmp(npath, "/");
}

static void test_extra_slashes(void)
{
const char *npath = NULL;
test_assert(t_normpath_to(".", cwd, &npath) == 0);
test_assert_strcmp(npath, cwd);

test_assert(t_normpath_to("./", cwd, &npath) == 0);
test_assert_strcmp(npath, cwd);

test_assert(t_normpath_to(".////", cwd, &npath) == 0);
test_assert_strcmp(npath, cwd);
}

static void test_nonexistent_path(void)
{
const char *npath = NULL;
const char *expected = t_strconcat(cwd, "/nonexistent", NULL);
test_assert(t_normpath_to("nonexistent", cwd, &npath) == 0);
test_assert_strcmp(npath, expected);
test_assert(t_realpath_to("nonexistent", cwd, &npath) == -1);
}

static void test_relative_dotdot(void)
{
const char *rel_path = "../"TEMP_DIRNAME;
const char *npath = NULL;
test_assert(t_normpath_to(rel_path, tmpdir, &npath) == 0);
test_assert_strcmp(npath, tmpdir);

test_assert(t_normpath_to("..", tmpdir, &npath) == 0);
test_assert_strcmp(npath, cwd);

test_assert(t_normpath_to("../", tmpdir, &npath) == 0);
test_assert_strcmp(npath, cwd);

test_assert(t_normpath_to("../.", tmpdir, &npath) == 0);
test_assert_strcmp(npath, cwd);
}

static void test_link1(void)
{
const char *old_dir, *npath = NULL;
test_assert(t_realpath_to(link1, "/", &npath) == 0);
test_assert_strcmp(npath, tmpdir);

/* .../link1/link1/child */
test_assert(t_realpath_to(t_strconcat(link1, "/link1/child", NULL),
"/", &npath) == 0);
test_assert_strcmp(npath, t_strconcat(tmpdir, "/child", NULL));

/* relative link1/link1/child */
if (t_get_current_dir(&old_dir) < 0)
i_fatal("t_get_working_dir() failed: %m");
if (chdir(tmpdir) < 0)
i_fatal("chdir(%s) failed: %m", tmpdir);
test_assert(t_realpath(t_strconcat("link1", "/link1/child", NULL),
&npath) == 0);
if (chdir(old_dir) < 0)
i_fatal("chdir(%s) failed: %m", old_dir);
}

static void test_link4(void)
{
const char *npath = NULL;

test_assert(t_realpath_to(t_strconcat(link1, "/link4/child", NULL),
"/", &npath) == 0);
test_assert_strcmp(npath, t_strconcat(tmpdir, "/child", NULL));
}

static void test_link_loop(void)
{
const char *npath = NULL;
errno = 0;
test_assert(t_realpath_to(link2, "/", &npath) == -1);
test_assert(errno == ELOOP);
}

static void test_abspath_vs_normpath(void)
{
const char *abs = t_abspath_to("../../bin", "/usr/lib/");
test_assert_strcmp(abs, "/usr/lib//../../bin");

const char *norm = NULL;
test_assert(t_normpath_to("../../bin", "/usr///lib/", &norm) == 0);
test_assert_strcmp(norm, "/bin");
}

static void create_links(const char *tmpdir)
{
link1 = t_strconcat(tmpdir, "/link1", NULL);
if (symlink(tmpdir, link1) < 0)
i_fatal("symlink(%s, %s) failed: %m", tmpdir, link1);

const char *link1_child = t_strconcat(link1, "/child", NULL);
int fd = creat(link1_child, 0600);
if (fd == -1)
i_fatal("creat(%s) failed: %m", link1_child);
i_close_fd(&fd);

/* link2 and link3 point to each other to create a loop */
link2 = t_strconcat(tmpdir, "/link2", NULL);
link3 = t_strconcat(tmpdir, "/link3", NULL);
if (symlink(link3, link2) < 0)
i_fatal("symlink(%s, %s) failed: %m", link3, link2);
if (symlink(link2, link3) < 0)
i_fatal("symlink(%s, %s) failed: %m", link2, link3);

/* link4 points to link1 */
link4 = t_strconcat(tmpdir, "/link4", NULL);
if (symlink("link1", link4) < 0)
i_fatal("symlink(link1, %s) failed: %m", link4);
}

static void test_link_alloc(void)
{
#define COMPONENT_COMPONENT "/component-component"
const char *o_tmpdir;

/* idea here is to make sure component-component
would optimally hit to the nearest_power value.
it has to be big enough to cause requirement for
allocation in t_realpath. */
string_t *basedir = t_str_new(256);
str_append(basedir, cwd);
str_append(basedir, "/"TEMP_DIRNAME);
size_t len = nearest_power(I_MAX(127, str_len(basedir) + strlen(COMPONENT_COMPONENT) + 1)) -
strlen(COMPONENT_COMPONENT);

while(str_len(basedir) < len) {
str_append(basedir, COMPONENT_COMPONENT);
(void)mkdir(str_c(basedir), 0700);
}
o_tmpdir = tmpdir;
tmpdir = str_c(basedir);

create_links(tmpdir);

test_link1();
test_link_loop();

tmpdir = o_tmpdir;
}

static void test_link_alloc2(void)
{
const char *o_tmpdir;

/* try enough different sized base directory lengths so the code
hits the different reallocations and tests for off-by-one errors */
string_t *basedir = t_str_new(256);
str_append(basedir, cwd);
str_append(basedir, "/"TEMP_DIRNAME);
str_append_c(basedir, '/');
size_t base_len = str_len(basedir);

o_tmpdir = tmpdir;
/* path_normalize() initially allocates 128 bytes, so we'll test paths
up to that length+1. */
unsigned char buf[128+1];
memset(buf, 'x', sizeof(buf));
for (size_t i = 1; i <= sizeof(buf); i++) {
str_truncate(basedir, base_len);
str_append_n(basedir, buf, i);
tmpdir = str_c(basedir);
(void)mkdir(str_c(basedir), 0700);

create_links(tmpdir);
test_link1();
test_link_loop();
}
tmpdir = o_tmpdir;
}

static void test_cleanup(void)
{
if (unlink_directory(tmpdir, UNLINK_DIRECTORY_FLAG_RMDIR) < 0)
i_error("unlink_directory() failed: %m");
}

static void test_init(void)
{
test_assert(t_get_current_dir(&cwd) == 0);
tmpdir = t_strconcat(cwd, "/"TEMP_DIRNAME, NULL);

test_cleanup();
if (mkdir(tmpdir, 0700) < 0) {
i_fatal("mkdir: %m");
}

create_links(tmpdir);
}

static void test_realpath(void)
{
test_begin("realpath");
alarm(20);
test_init();
test_local_path();
test_absolute_path_no_change();
test_travel_to_root();
test_extra_slashes();
test_nonexistent_path();
test_relative_dotdot();
test_link1();
test_link4();
test_link_loop();
test_abspath_vs_normpath();
test_link_alloc();
test_link_alloc2();
test_cleanup();
alarm(0);
test_end();
}

int main(void)
{
static void (*test_functions[])(void) = {
test_realpath,
NULL
};
return test_run(test_functions);
}

0 comments on commit 682e40a

Please sign in to comment.