Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
lib-sieve: util: Add test suite for realpath.
- Loading branch information
1 parent
b15a498
commit 682e40a
Showing
2 changed files
with
305 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
|