diff options
Diffstat (limited to 'test/test_path.c')
-rw-r--r-- | test/test_path.c | 948 |
1 files changed, 948 insertions, 0 deletions
diff --git a/test/test_path.c b/test/test_path.c new file mode 100644 index 0000000..5c98c32 --- /dev/null +++ b/test/test_path.c @@ -0,0 +1,948 @@ +// Copyright 2020-2022 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#undef NDEBUG + +#include "zix/path.h" +#include "zix/string_view.h" + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +/// Abort if `string` doesn't equal `expected` +static bool +equal(char* const string, const char* const expected) +{ + const bool result = + (!string && !expected) || (string && expected && !strcmp(string, expected)); + + free(string); + return result; +} + +/// Abort if `string` doesn't equal `expected` with preferred separators +static bool +match(char* const string, const char* const expected) +{ + char* const e = expected ? zix_path_preferred(NULL, expected) : NULL; + char* const s = string ? zix_path_preferred(NULL, string) : NULL; + + const bool result = (!s && !e) || (s && e && !strcmp(s, e)); + + free(s); + free(e); + free(string); + return result; +} + +/// Abort if `view` doesn't equal `expected` +static bool +view_equal(const ZixStringView view, const char* const expected) +{ + const bool result = + (!view.length && !expected) || + (view.length && expected && strlen(expected) == view.length && + !strncmp(view.data, expected, view.length)); + + return result; +} + +static void +test_path_join(void) +{ + // Trivial cases + assert(equal(zix_path_join(NULL, "", ""), "")); + assert(equal(zix_path_join(NULL, "", "/b/"), "/b/")); + assert(equal(zix_path_join(NULL, "", "b"), "b")); + assert(equal(zix_path_join(NULL, "/", ""), "/")); + assert(equal(zix_path_join(NULL, "/a/", ""), "/a/")); + assert(equal(zix_path_join(NULL, "/a/b/", ""), "/a/b/")); + assert(equal(zix_path_join(NULL, "a/", ""), "a/")); + assert(equal(zix_path_join(NULL, "a/b/", ""), "a/b/")); + + // Null is treated like an empty path + assert(equal(zix_path_join(NULL, "/", NULL), "/")); + assert(equal(zix_path_join(NULL, "/a/", NULL), "/a/")); + assert(equal(zix_path_join(NULL, "/a/b/", NULL), "/a/b/")); + assert(equal(zix_path_join(NULL, "a/", NULL), "a/")); + assert(equal(zix_path_join(NULL, "a/b/", NULL), "a/b/")); + assert(equal(zix_path_join(NULL, NULL, "/b"), "/b")); + assert(equal(zix_path_join(NULL, NULL, "/b/c"), "/b/c")); + assert(equal(zix_path_join(NULL, NULL, "b"), "b")); + assert(equal(zix_path_join(NULL, NULL, "b/c"), "b/c")); + assert(equal(zix_path_join(NULL, NULL, NULL), "")); + + // Joining an absolute path discards the left path + assert(equal(zix_path_join(NULL, "/a", "/b"), "/b")); + assert(equal(zix_path_join(NULL, "/a", "/b/"), "/b/")); + assert(equal(zix_path_join(NULL, "a", "/b"), "/b")); + assert(equal(zix_path_join(NULL, "a/", "/b"), "/b")); + + // Concatenation cases + assert(equal(zix_path_join(NULL, "/a/", "b"), "/a/b")); + assert(equal(zix_path_join(NULL, "/a/", "b/"), "/a/b/")); + assert(equal(zix_path_join(NULL, "a/", "b"), "a/b")); + assert(equal(zix_path_join(NULL, "a/", "b/"), "a/b/")); + assert(equal(zix_path_join(NULL, "/a/c/", "/b/d"), "/b/d")); + assert(equal(zix_path_join(NULL, "/a/c/", "b"), "/a/c/b")); + +#ifdef _WIN32 + // "C:" is a drive letter, "//host" is a share, backslash is a separator + assert(equal(zix_path_join(NULL, "//host", ""), "//host\\")); + assert(equal(zix_path_join(NULL, "//host", "a"), "//host\\a")); + assert(equal(zix_path_join(NULL, "//host/", "a"), "//host/a")); + assert(equal(zix_path_join(NULL, "/a", ""), "/a\\")); + assert(equal(zix_path_join(NULL, "/a", "b"), "/a\\b")); + assert(equal(zix_path_join(NULL, "/a", "b/"), "/a\\b/")); + assert(equal(zix_path_join(NULL, "/a", NULL), "/a\\")); + assert(equal(zix_path_join(NULL, "/a/b", NULL), "/a/b\\")); + assert(equal(zix_path_join(NULL, "/a/c", "b"), "/a/c\\b")); + assert(equal(zix_path_join(NULL, "C:", ""), "C:")); + assert(equal(zix_path_join(NULL, "C:/a", "/b"), "C:/b")); + assert(equal(zix_path_join(NULL, "C:/a", "C:/b"), "C:/b")); + assert(equal(zix_path_join(NULL, "C:/a", "C:b"), "C:/a\\b")); + assert(equal(zix_path_join(NULL, "C:/a", "D:b"), "D:b")); + assert(equal(zix_path_join(NULL, "C:/a", "b"), "C:/a\\b")); + assert(equal(zix_path_join(NULL, "C:/a", "b/"), "C:/a\\b/")); + assert(equal(zix_path_join(NULL, "C:/a", "c:/b"), "c:/b")); + assert(equal(zix_path_join(NULL, "C:/a", "c:b"), "c:b")); + assert(equal(zix_path_join(NULL, "C:\\a", "b"), "C:\\a\\b")); + assert(equal(zix_path_join(NULL, "C:\\a", "b\\"), "C:\\a\\b\\")); + assert(equal(zix_path_join(NULL, "C:a", "/b"), "C:/b")); + assert(equal(zix_path_join(NULL, "C:a", "C:/b"), "C:/b")); + assert(equal(zix_path_join(NULL, "C:a", "C:\\b"), "C:\\b")); + assert(equal(zix_path_join(NULL, "C:a", "C:b"), "C:a\\b")); + assert(equal(zix_path_join(NULL, "C:a", "b"), "C:a\\b")); + assert(equal(zix_path_join(NULL, "C:a", "c:/b"), "c:/b")); + assert(equal(zix_path_join(NULL, "C:a", "c:\\b"), "c:\\b")); + assert(equal(zix_path_join(NULL, "C:a", "c:b"), "c:b")); + assert(equal(zix_path_join(NULL, "a", ""), "a\\")); + assert(equal(zix_path_join(NULL, "a", "C:"), "C:")); + assert(equal(zix_path_join(NULL, "a", "C:/b"), "C:/b")); + assert(equal(zix_path_join(NULL, "a", "C:\\b"), "C:\\b")); + assert(equal(zix_path_join(NULL, "a", "\\b"), "\\b")); + assert(equal(zix_path_join(NULL, "a", "b"), "a\\b")); + assert(equal(zix_path_join(NULL, "a", "b/"), "a\\b/")); + assert(equal(zix_path_join(NULL, "a", NULL), "a\\")); + assert(equal(zix_path_join(NULL, "a/b", ""), "a/b\\")); + assert(equal(zix_path_join(NULL, "a/b", NULL), "a/b\\")); + assert(equal(zix_path_join(NULL, "a\\", "\\b"), "\\b")); + assert(equal(zix_path_join(NULL, "a\\", "b"), "a\\b")); +#else + // "C:" isn't special, "//host" has extra slashes, slash is the only separator + assert(equal(zix_path_join(NULL, "//host", ""), "//host/")); + assert(equal(zix_path_join(NULL, "//host", "a"), "//host/a")); + assert(equal(zix_path_join(NULL, "//host/", "a"), "//host/a")); + assert(equal(zix_path_join(NULL, "/a", ""), "/a/")); + assert(equal(zix_path_join(NULL, "/a", "b"), "/a/b")); + assert(equal(zix_path_join(NULL, "/a", "b/"), "/a/b/")); + assert(equal(zix_path_join(NULL, "/a", NULL), "/a/")); + assert(equal(zix_path_join(NULL, "/a/b", NULL), "/a/b/")); + assert(equal(zix_path_join(NULL, "/a/c", "b"), "/a/c/b")); + assert(equal(zix_path_join(NULL, "C:", ""), "C:/")); + assert(equal(zix_path_join(NULL, "C:/a", "/b"), "/b")); + assert(equal(zix_path_join(NULL, "C:/a", "C:/b"), "C:/a/C:/b")); + assert(equal(zix_path_join(NULL, "C:/a", "C:b"), "C:/a/C:b")); + assert(equal(zix_path_join(NULL, "C:/a", "D:b"), "C:/a/D:b")); + assert(equal(zix_path_join(NULL, "C:/a", "b"), "C:/a/b")); + assert(equal(zix_path_join(NULL, "C:/a", "b/"), "C:/a/b/")); + assert(equal(zix_path_join(NULL, "C:/a", "c:/b"), "C:/a/c:/b")); + assert(equal(zix_path_join(NULL, "C:/a", "c:b"), "C:/a/c:b")); + assert(equal(zix_path_join(NULL, "C:\\a", "b"), "C:\\a/b")); + assert(equal(zix_path_join(NULL, "C:\\a", "b\\"), "C:\\a/b\\")); + assert(equal(zix_path_join(NULL, "C:a", "/b"), "/b")); + assert(equal(zix_path_join(NULL, "C:a", "C:/b"), "C:a/C:/b")); + assert(equal(zix_path_join(NULL, "C:a", "C:\\b"), "C:a/C:\\b")); + assert(equal(zix_path_join(NULL, "C:a", "C:b"), "C:a/C:b")); + assert(equal(zix_path_join(NULL, "C:a", "b"), "C:a/b")); + assert(equal(zix_path_join(NULL, "C:a", "c:/b"), "C:a/c:/b")); + assert(equal(zix_path_join(NULL, "C:a", "c:\\b"), "C:a/c:\\b")); + assert(equal(zix_path_join(NULL, "C:a", "c:b"), "C:a/c:b")); + assert(equal(zix_path_join(NULL, "a", ""), "a/")); + assert(equal(zix_path_join(NULL, "a", "C:"), "a/C:")); + assert(equal(zix_path_join(NULL, "a", "C:/b"), "a/C:/b")); + assert(equal(zix_path_join(NULL, "a", "C:\\b"), "a/C:\\b")); + assert(equal(zix_path_join(NULL, "a", "\\b"), "a/\\b")); + assert(equal(zix_path_join(NULL, "a", "b"), "a/b")); + assert(equal(zix_path_join(NULL, "a", "b/"), "a/b/")); + assert(equal(zix_path_join(NULL, "a", NULL), "a/")); + assert(equal(zix_path_join(NULL, "a/b", ""), "a/b/")); + assert(equal(zix_path_join(NULL, "a/b", NULL), "a/b/")); + assert(equal(zix_path_join(NULL, "a\\", "\\b"), "a\\/\\b")); + assert(equal(zix_path_join(NULL, "a\\", "b"), "a\\/b")); +#endif +} + +static void +test_path_preferred(void) +{ + assert(equal(zix_path_preferred(NULL, ""), "")); + assert(equal(zix_path_preferred(NULL, "some-name"), "some-name")); + assert(equal(zix_path_preferred(NULL, "some_name"), "some_name")); + +#ifdef _WIN32 + // Backslash is the preferred separator + assert(equal(zix_path_preferred(NULL, "/"), "\\")); + assert(equal(zix_path_preferred(NULL, "/."), "\\.")); + assert(equal(zix_path_preferred(NULL, "//a"), "\\\\a")); + assert(equal(zix_path_preferred(NULL, "//a/"), "\\\\a\\")); + assert(equal(zix_path_preferred(NULL, "//a//"), "\\\\a\\\\")); + assert(equal(zix_path_preferred(NULL, "/a//b/c/"), "\\a\\\\b\\c\\")); + assert(equal(zix_path_preferred(NULL, "/a/b/c/"), "\\a\\b\\c\\")); + assert(equal(zix_path_preferred(NULL, "\\"), "\\")); + assert( + equal(zix_path_preferred(NULL, "\\a\\//b\\/c\\"), "\\a\\\\\\b\\\\c\\")); + assert(equal(zix_path_preferred(NULL, "\\a\\/b\\/c\\"), "\\a\\\\b\\\\c\\")); + assert(equal(zix_path_preferred(NULL, "\\a\\b\\c\\"), "\\a\\b\\c\\")); +#else + // Slash is the only separator + assert(equal(zix_path_preferred(NULL, "/"), "/")); + assert(equal(zix_path_preferred(NULL, "/."), "/.")); + assert(equal(zix_path_preferred(NULL, "//a"), "//a")); + assert(equal(zix_path_preferred(NULL, "//a/"), "//a/")); + assert(equal(zix_path_preferred(NULL, "//a//"), "//a//")); + assert(equal(zix_path_preferred(NULL, "/a//b/c/"), "/a//b/c/")); + assert(equal(zix_path_preferred(NULL, "/a/b/c/"), "/a/b/c/")); + assert(equal(zix_path_preferred(NULL, "\\"), "\\")); + assert(equal(zix_path_preferred(NULL, "\\a\\//b\\/c\\"), "\\a\\//b\\/c\\")); + assert(equal(zix_path_preferred(NULL, "\\a\\/b\\/c\\"), "\\a\\/b\\/c\\")); + assert(equal(zix_path_preferred(NULL, "\\a\\b\\c\\"), "\\a\\b\\c\\")); +#endif +} + +static void +test_path_lexically_normal(void) +{ + // Identities + assert(equal(zix_path_lexically_normal(NULL, ""), "")); + assert(equal(zix_path_lexically_normal(NULL, "."), ".")); + assert(equal(zix_path_lexically_normal(NULL, ".."), "..")); + assert(match(zix_path_lexically_normal(NULL, "../.."), "../..")); + assert(match(zix_path_lexically_normal(NULL, "/a/b/"), "/a/b/")); + assert(match(zix_path_lexically_normal(NULL, "/a/b/c"), "/a/b/c")); + assert(match(zix_path_lexically_normal(NULL, "a/b"), "a/b")); + + // "Out of bounds" leading dot-dot entries are removed + assert(match(zix_path_lexically_normal(NULL, "/../"), "/")); + assert(match(zix_path_lexically_normal(NULL, "/../.."), "/")); + assert(match(zix_path_lexically_normal(NULL, "/../../"), "/")); + + // Windows drive letters are preserved + assert(equal(zix_path_lexically_normal(NULL, "C:"), "C:")); + assert(equal(zix_path_lexically_normal(NULL, "C:a"), "C:a")); + assert(match(zix_path_lexically_normal(NULL, "C:/"), "C:/")); + assert(match(zix_path_lexically_normal(NULL, "C:/a"), "C:/a")); + assert(match(zix_path_lexically_normal(NULL, "C:/a/"), "C:/a/")); + assert(match(zix_path_lexically_normal(NULL, "C:/a/b"), "C:/a/b")); + assert(match(zix_path_lexically_normal(NULL, "C:a/"), "C:a/")); + + // Slashes are replaced with a single preferred-separator + assert(match(zix_path_lexically_normal(NULL, "/"), "/")); + assert(match(zix_path_lexically_normal(NULL, "//"), "/")); + assert(match(zix_path_lexically_normal(NULL, "///"), "/")); + assert(match(zix_path_lexically_normal(NULL, "///a///b/////"), "/a/b/")); + assert(match(zix_path_lexically_normal(NULL, "/a/b//c"), "/a/b/c")); + assert(match(zix_path_lexically_normal(NULL, "a///b"), "a/b")); + assert(match(zix_path_lexically_normal(NULL, "a//b"), "a/b")); + assert(match(zix_path_lexically_normal(NULL, "a/b"), "a/b")); + assert(match(zix_path_lexically_normal(NULL, "a/b/"), "a/b/")); + assert(match(zix_path_lexically_normal(NULL, "a/b//"), "a/b/")); + assert(match(zix_path_lexically_normal(NULL, "a/b///"), "a/b/")); + + // Dot directories are removed + assert(equal(zix_path_lexically_normal(NULL, "./.."), "..")); + assert(match(zix_path_lexically_normal(NULL, ".//a//.//./b/.//"), "a/b/")); + assert(match(zix_path_lexically_normal(NULL, "./a/././b/./"), "a/b/")); + assert(match(zix_path_lexically_normal(NULL, "/."), "/")); + assert(match(zix_path_lexically_normal(NULL, "/a/b/./"), "/a/b/")); + assert(match(zix_path_lexically_normal(NULL, "a/."), "a/")); + assert(match(zix_path_lexically_normal(NULL, "a/./b/."), "a/b/")); + assert(match(zix_path_lexically_normal(NULL, "a/b/."), "a/b/")); + assert(match(zix_path_lexically_normal(NULL, "a/b/./"), "a/b/")); + + // Real entries before a dot-dot entry are removed + assert(equal(zix_path_lexically_normal(NULL, "a/.."), ".")); + assert(equal(zix_path_lexically_normal(NULL, "a/../"), ".")); + assert(equal(zix_path_lexically_normal(NULL, "a/b/../.."), ".")); + assert(equal(zix_path_lexically_normal(NULL, "a/b/../../"), ".")); + assert(match(zix_path_lexically_normal(NULL, "/a/b/c/../"), "/a/b/")); + assert(match(zix_path_lexically_normal(NULL, "/a/b/c/../d"), "/a/b/d")); + assert(match(zix_path_lexically_normal(NULL, "/a/b/c/../d/"), "/a/b/d/")); + assert(match(zix_path_lexically_normal(NULL, "a/b/.."), "a/")); + assert(match(zix_path_lexically_normal(NULL, "a/b/../"), "a/")); + assert(match(zix_path_lexically_normal(NULL, "a/b/./.."), "a/")); + assert(match(zix_path_lexically_normal(NULL, "a/b/./../"), "a/")); + assert(match(zix_path_lexically_normal(NULL, "a/b/c/../.."), "a/")); + assert(match(zix_path_lexically_normal(NULL, "a/b/c/../../"), "a/")); + + // Both dot directories and dot-dot entries + assert(match(zix_path_lexically_normal(NULL, "a/.///b/../"), "a/")); + assert(match(zix_path_lexically_normal(NULL, "a/./b/.."), "a/")); + + // Dot-dot entries after a root are removed + assert(match(zix_path_lexically_normal(NULL, "/.."), "/")); + assert(match(zix_path_lexically_normal(NULL, "/../"), "/")); + assert(match(zix_path_lexically_normal(NULL, "/../.."), "/")); + assert(match(zix_path_lexically_normal(NULL, "/../../"), "/")); + assert(match(zix_path_lexically_normal(NULL, "/../a"), "/a")); + assert(match(zix_path_lexically_normal(NULL, "/../a/../.."), "/")); + assert(match(zix_path_lexically_normal(NULL, "/a/../.."), "/")); + + // No trailing separator after a trailing dot-dot entry + assert(equal(zix_path_lexically_normal(NULL, "../"), "..")); + assert(match(zix_path_lexically_normal(NULL, "../../"), "../..")); + assert(match(zix_path_lexically_normal(NULL, "a/../b/../..///"), "..")); + assert( + match(zix_path_lexically_normal(NULL, "a/../b/..//..///../"), "../..")); + + // Effectively empty paths are a dot + assert(equal(zix_path_lexically_normal(NULL, "."), ".")); + assert(equal(zix_path_lexically_normal(NULL, "./"), ".")); + assert(equal(zix_path_lexically_normal(NULL, "./."), ".")); + assert(equal(zix_path_lexically_normal(NULL, "a/.."), ".")); + +#ifdef _WIN32 + + // Paths like "C:/filename" have a drive letter + assert(equal(zix_path_lexically_normal(NULL, "C:/a\\b"), "C:\\a\\b")); + assert(equal(zix_path_lexically_normal(NULL, "C:\\"), "C:\\")); + assert(equal(zix_path_lexically_normal(NULL, "C:\\a"), "C:\\a")); + assert(equal(zix_path_lexically_normal(NULL, "C:\\a/"), "C:\\a\\")); + assert(equal(zix_path_lexically_normal(NULL, "C:\\a\\"), "C:\\a\\")); + assert(equal(zix_path_lexically_normal(NULL, "C:a\\"), "C:a\\")); + + // Paths like "//host/dir/" have a network root + assert(equal(zix_path_lexically_normal(NULL, "//a/"), "\\\\a\\")); + assert(equal(zix_path_lexically_normal(NULL, "//a/.."), "\\\\a\\")); + assert(equal(zix_path_lexically_normal(NULL, "//a/b/"), "\\\\a\\b\\")); + assert(equal(zix_path_lexically_normal(NULL, "//a/b/."), "\\\\a\\b\\")); + +#else + + // "C:" is just a strange directory or file name prefix + assert(equal(zix_path_lexically_normal(NULL, "C:/a\\b"), "C:/a\\b")); + assert(equal(zix_path_lexically_normal(NULL, "C:\\"), "C:\\")); + assert(equal(zix_path_lexically_normal(NULL, "C:\\a"), "C:\\a")); + assert(equal(zix_path_lexically_normal(NULL, "C:\\a/"), "C:\\a/")); + assert(equal(zix_path_lexically_normal(NULL, "C:\\a\\"), "C:\\a\\")); + assert(equal(zix_path_lexically_normal(NULL, "C:a\\"), "C:a\\")); + + // Paths like "//host/dir/" have redundant leading slashes + assert(equal(zix_path_lexically_normal(NULL, "//a/"), "/a/")); + assert(equal(zix_path_lexically_normal(NULL, "//a/.."), "/")); + assert(equal(zix_path_lexically_normal(NULL, "//a/b/"), "/a/b/")); + assert(equal(zix_path_lexically_normal(NULL, "//a/b/."), "/a/b/")); + +#endif +} + +static void +test_path_lexically_relative(void) +{ + assert(equal(zix_path_lexically_relative(NULL, "", ""), ".")); + assert(equal(zix_path_lexically_relative(NULL, "", "."), ".")); + assert(equal(zix_path_lexically_relative(NULL, ".", ""), ".")); + assert(equal(zix_path_lexically_relative(NULL, ".", "."), ".")); + assert(equal(zix_path_lexically_relative(NULL, "//base", "a"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "//host", "//host"), ".")); + assert(equal(zix_path_lexically_relative(NULL, "//host", "a"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "//host/", "//host/"), ".")); + assert(equal(zix_path_lexically_relative(NULL, "//host/", "a"), NULL)); + assert( + equal(zix_path_lexically_relative(NULL, "//host/a/b", "//host/a/b"), ".")); + assert(equal(zix_path_lexically_relative(NULL, "/a/b", "/a/"), "b")); + assert(equal(zix_path_lexically_relative(NULL, "C:/a/b", "C:/a/"), "b")); + assert(equal(zix_path_lexically_relative(NULL, "a", "/"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "a", "//host"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "a", "//host/"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "a", "a"), ".")); + assert(equal(zix_path_lexically_relative(NULL, "a/b", "/a/b"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "a/b", "a/b"), ".")); + assert(equal(zix_path_lexically_relative(NULL, "a/b/c", "a"), "b/c")); + assert(equal(zix_path_lexically_relative(NULL, "a/b/c", "a/b/c"), ".")); + assert(equal(zix_path_lexically_relative(NULL, "a/b/c/", "a/b/c/"), ".")); + assert(match(zix_path_lexically_relative(NULL, "../", "../"), ".")); + assert(match(zix_path_lexically_relative(NULL, "../", "./"), "../")); + assert(match(zix_path_lexically_relative(NULL, "../", "a"), "../../")); + assert( + match(zix_path_lexically_relative(NULL, "../../a", "../b"), "../../a")); + assert(match(zix_path_lexically_relative(NULL, "../a", "../b"), "../a")); + assert(match(zix_path_lexically_relative(NULL, "/a", "/b/c/"), "../../a")); + assert(match(zix_path_lexically_relative(NULL, "/a/b/c", "/a/b/d/"), "../c")); + assert( + match(zix_path_lexically_relative(NULL, "/a/b/c", "/a/b/d/e/"), "../../c")); + assert(match(zix_path_lexically_relative(NULL, "/a/b/c", "/a/d"), "../b/c")); + assert(match(zix_path_lexically_relative(NULL, "/a/d", "/a/b/c"), "../../d")); + assert( + match(zix_path_lexically_relative(NULL, "C:/D/", "C:/D/F.txt"), "../")); + assert(match(zix_path_lexically_relative(NULL, "C:/D/F", "C:/D/S/"), "../F")); + assert(match(zix_path_lexically_relative(NULL, "C:/D/F", "C:/G"), "../D/F")); + assert( + match(zix_path_lexically_relative(NULL, "C:/D/F.txt", "C:/D/"), "F.txt")); + assert(match(zix_path_lexically_relative(NULL, "C:/E", "C:/D/G"), "../../E")); + assert( + match(zix_path_lexically_relative(NULL, "C:/a", "C:/b/c/"), "../../a")); + assert( + match(zix_path_lexically_relative(NULL, "C:/a/b/c", "C:/a/b/d/"), "../c")); + assert(match(zix_path_lexically_relative(NULL, "a/b", "c/d"), "../../a/b")); + assert(match(zix_path_lexically_relative(NULL, "a/b/c", "../"), NULL)); + assert(match(zix_path_lexically_relative(NULL, "a/b/c", "../../"), NULL)); + assert( + match(zix_path_lexically_relative(NULL, "a/b/c", "a/b/c/x/y"), "../..")); + +#ifdef _WIN32 + assert(equal(zix_path_lexically_relative(NULL, "/", "a"), "/")); + assert(equal(zix_path_lexically_relative(NULL, "//host", "//host/"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "//host/", "//host"), "/")); + assert(equal(zix_path_lexically_relative(NULL, "//host/", "/a"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "/a", "//host"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "/a", "//host/"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "C:/D/", "C:F.txt"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "C:/D/S/", "F.txt"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "C:F", "C:/D/"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "C:F", "D:G"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "F", "C:/D/S/"), NULL)); + assert(match(zix_path_lexically_relative(NULL, "C:../", "C:../"), ".")); + assert(match(zix_path_lexically_relative(NULL, "C:../", "C:./"), "../")); + assert(match(zix_path_lexically_relative(NULL, "C:../", "C:a"), "../../")); + assert( + match(zix_path_lexically_relative(NULL, "C:../../a", "C:../b"), "../../a")); + assert(match(zix_path_lexically_relative(NULL, "C:../a", "C:../b"), "../a")); + assert(match(zix_path_lexically_relative(NULL, "C:a/b/c", "C:../"), NULL)); + assert(match(zix_path_lexically_relative(NULL, "C:a/b/c", "C:../../"), NULL)); +#else + assert(equal(zix_path_lexically_relative(NULL, "/", "a"), NULL)); + assert(equal(zix_path_lexically_relative(NULL, "//host", "//host/"), ".")); + assert(equal(zix_path_lexically_relative(NULL, "//host/", "//host"), ".")); + assert(equal(zix_path_lexically_relative(NULL, "//host/", "/a"), "../host/")); + assert(equal(zix_path_lexically_relative(NULL, "/a", "//host"), "../a")); + assert(equal(zix_path_lexically_relative(NULL, "/a", "//host/"), "../a")); + assert( + equal(zix_path_lexically_relative(NULL, "C:/D/", "C:F.txt"), "../C:/D/")); + assert( + equal(zix_path_lexically_relative(NULL, "C:/D/S/", "F.txt"), "../C:/D/S/")); + assert(equal(zix_path_lexically_relative(NULL, "C:F", "C:/D/"), "../../C:F")); + assert(equal(zix_path_lexically_relative(NULL, "C:F", "D:G"), "../C:F")); + assert( + equal(zix_path_lexically_relative(NULL, "F", "C:/D/S/"), "../../../F")); + assert(match(zix_path_lexically_relative(NULL, "C:../", "C:../"), ".")); + assert(match(zix_path_lexically_relative(NULL, "C:../", "C:./"), "../C:../")); + assert(match(zix_path_lexically_relative(NULL, "C:../", "C:a"), "../C:../")); + assert( + match(zix_path_lexically_relative(NULL, "C:../../a", "C:../b"), "../../a")); + assert(match(zix_path_lexically_relative(NULL, "C:../a", "C:../b"), "../a")); + assert( + match(zix_path_lexically_relative(NULL, "C:a/b/c", "C:../"), "../C:a/b/c")); + assert( + match(zix_path_lexically_relative(NULL, "C:a/b/c", "C:../../"), "C:a/b/c")); +#endif +} + +static void +test_path_root_name(void) +{ + // Relative paths with no root + assert(view_equal(zix_path_root_name(""), NULL)); + assert(view_equal(zix_path_root_name("."), NULL)); + assert(view_equal(zix_path_root_name(".."), NULL)); + assert(view_equal(zix_path_root_name("../"), NULL)); + assert(view_equal(zix_path_root_name("./"), NULL)); + assert(view_equal(zix_path_root_name("NONDRIVE:"), NULL)); + assert(view_equal(zix_path_root_name("a"), NULL)); + assert(view_equal(zix_path_root_name("a/"), NULL)); + assert(view_equal(zix_path_root_name("a/."), NULL)); + assert(view_equal(zix_path_root_name("a/.."), NULL)); + assert(view_equal(zix_path_root_name("a/../"), NULL)); + assert(view_equal(zix_path_root_name("a/../b"), NULL)); + assert(view_equal(zix_path_root_name("a/./"), NULL)); + assert(view_equal(zix_path_root_name("a/./b"), NULL)); + assert(view_equal(zix_path_root_name("a/b"), NULL)); + + // Absolute paths with a POSIX-style root + assert(view_equal(zix_path_root_name("/"), NULL)); + assert(view_equal(zix_path_root_name("/."), NULL)); + assert(view_equal(zix_path_root_name("/.."), NULL)); + assert(view_equal(zix_path_root_name("//"), NULL)); + assert(view_equal(zix_path_root_name("///a///"), NULL)); + assert(view_equal(zix_path_root_name("///a///b"), NULL)); + assert(view_equal(zix_path_root_name("/a"), NULL)); + assert(view_equal(zix_path_root_name("/a/"), NULL)); + assert(view_equal(zix_path_root_name("/a//b"), NULL)); + +#ifdef _WIN32 + + // Paths like "C:/filename" have a drive letter + assert(view_equal(zix_path_root_name("C:"), "C:")); + assert(view_equal(zix_path_root_name("C:/"), "C:")); + assert(view_equal(zix_path_root_name("C:/a"), "C:")); + assert(view_equal(zix_path_root_name("C:/a/"), "C:")); + assert(view_equal(zix_path_root_name("C:/a/b"), "C:")); + assert(view_equal(zix_path_root_name("C:a"), "C:")); + assert(view_equal(zix_path_root_name("C:a/"), "C:")); + + // Paths like "//host/" are network roots + assert(view_equal(zix_path_root_name("//host"), "//host")); + assert(view_equal(zix_path_root_name("//host/"), "//host")); + assert(view_equal(zix_path_root_name("//host/a"), "//host")); + + // Backslash is a directory separator + assert(view_equal(zix_path_root_name("C:/a\\b"), "C:")); + assert(view_equal(zix_path_root_name("C:\\"), "C:")); + assert(view_equal(zix_path_root_name("C:\\a"), "C:")); + assert(view_equal(zix_path_root_name("C:\\a/"), "C:")); + assert(view_equal(zix_path_root_name("C:\\a\\"), "C:")); + assert(view_equal(zix_path_root_name("C:a\\"), "C:")); + +#else + + // "C:" is just a strange directory or file name prefix + assert(view_equal(zix_path_root_name("C:"), NULL)); + assert(view_equal(zix_path_root_name("C:/"), NULL)); + assert(view_equal(zix_path_root_name("C:/a"), NULL)); + assert(view_equal(zix_path_root_name("C:/a/"), NULL)); + assert(view_equal(zix_path_root_name("C:/a/b"), NULL)); + assert(view_equal(zix_path_root_name("C:a"), NULL)); + assert(view_equal(zix_path_root_name("C:a/"), NULL)); + + // Paths like "//host/" have redundant leading slashes + assert(view_equal(zix_path_root_name("//host"), NULL)); + assert(view_equal(zix_path_root_name("//host/"), NULL)); + assert(view_equal(zix_path_root_name("//host/a"), NULL)); + + // Backslash is a character in a directory or file name + assert(view_equal(zix_path_root_name("C:/a\\b"), NULL)); + assert(view_equal(zix_path_root_name("C:\\"), NULL)); + assert(view_equal(zix_path_root_name("C:\\a"), NULL)); + assert(view_equal(zix_path_root_name("C:\\a/"), NULL)); + assert(view_equal(zix_path_root_name("C:\\a\\"), NULL)); + assert(view_equal(zix_path_root_name("C:a\\"), NULL)); + +#endif +} + +static void +test_path_root(void) +{ + // Relative paths with no root + assert(view_equal(zix_path_root_path(""), NULL)); + assert(view_equal(zix_path_root_path("."), NULL)); + assert(view_equal(zix_path_root_path(".."), NULL)); + assert(view_equal(zix_path_root_path("../"), NULL)); + assert(view_equal(zix_path_root_path("./"), NULL)); + assert(view_equal(zix_path_root_path("NONDRIVE:"), NULL)); + assert(view_equal(zix_path_root_path("a"), NULL)); + assert(view_equal(zix_path_root_path("a/"), NULL)); + assert(view_equal(zix_path_root_path("a/."), NULL)); + assert(view_equal(zix_path_root_path("a/.."), NULL)); + assert(view_equal(zix_path_root_path("a/../"), NULL)); + assert(view_equal(zix_path_root_path("a/../b"), NULL)); + assert(view_equal(zix_path_root_path("a/./"), NULL)); + assert(view_equal(zix_path_root_path("a/./b"), NULL)); + assert(view_equal(zix_path_root_path("a/b"), NULL)); + + // Absolute paths with a POSIX-style root + assert(view_equal(zix_path_root_path("/"), "/")); + assert(view_equal(zix_path_root_path("/."), "/")); + assert(view_equal(zix_path_root_path("/.."), "/")); + assert(view_equal(zix_path_root_path("//"), "/")); + assert(view_equal(zix_path_root_path("///a///"), "/")); + assert(view_equal(zix_path_root_path("///a///b"), "/")); + assert(view_equal(zix_path_root_path("/a"), "/")); + assert(view_equal(zix_path_root_path("/a/"), "/")); + assert(view_equal(zix_path_root_path("/a//b"), "/")); + +#ifdef _WIN32 + + // Paths like "C:/filename" have a drive letter + assert(view_equal(zix_path_root_path("C:"), "C:")); + assert(view_equal(zix_path_root_path("C:/"), "C:/")); + assert(view_equal(zix_path_root_path("C:/a"), "C:/")); + assert(view_equal(zix_path_root_path("C:/a/"), "C:/")); + assert(view_equal(zix_path_root_path("C:/a/b"), "C:/")); + assert(view_equal(zix_path_root_path("C:a"), "C:")); + assert(view_equal(zix_path_root_path("C:a/"), "C:")); + + // Paths like "//host/" are network roots + assert(view_equal(zix_path_root_path("//host"), "//host")); + assert(view_equal(zix_path_root_path("//host/"), "//host/")); + assert(view_equal(zix_path_root_path("//host/a"), "//host/")); + + // Backslash is a directory separator + assert(view_equal(zix_path_root_path("C:/a\\b"), "C:/")); + assert(view_equal(zix_path_root_path("C:\\"), "C:\\")); + assert(view_equal(zix_path_root_path("C:\\a"), "C:\\")); + assert(view_equal(zix_path_root_path("C:\\a/"), "C:\\")); + assert(view_equal(zix_path_root_path("C:\\a\\"), "C:\\")); + assert(view_equal(zix_path_root_path("C:a\\"), "C:")); + +#else + + // "C:" is just a strange directory or file name prefix + assert(view_equal(zix_path_root_path("C:"), NULL)); + assert(view_equal(zix_path_root_path("C:/"), NULL)); + assert(view_equal(zix_path_root_path("C:/a"), NULL)); + assert(view_equal(zix_path_root_path("C:/a/"), NULL)); + assert(view_equal(zix_path_root_path("C:/a/b"), NULL)); + assert(view_equal(zix_path_root_path("C:a"), NULL)); + assert(view_equal(zix_path_root_path("C:a/"), NULL)); + + // Paths like "//host/" have redundant leading slashes + assert(view_equal(zix_path_root_path("//host"), "/")); + assert(view_equal(zix_path_root_path("//host/"), "/")); + assert(view_equal(zix_path_root_path("//host/a"), "/")); + + // Backslash is a character in a directory or file name + assert(view_equal(zix_path_root_path("C:/a\\b"), NULL)); + assert(view_equal(zix_path_root_path("C:\\"), NULL)); + assert(view_equal(zix_path_root_path("C:\\a"), NULL)); + assert(view_equal(zix_path_root_path("C:\\a/"), NULL)); + assert(view_equal(zix_path_root_path("C:\\a\\"), NULL)); + assert(view_equal(zix_path_root_path("C:a\\"), NULL)); + +#endif +} + +static void +test_path_parent(void) +{ + // Absolute paths + assert(view_equal(zix_path_parent_path("/"), "/")); + assert(view_equal(zix_path_parent_path("/."), "/")); + assert(view_equal(zix_path_parent_path("/.."), "/")); + assert(view_equal(zix_path_parent_path("//"), "/")); + assert(view_equal(zix_path_parent_path("/a"), "/")); + assert(view_equal(zix_path_parent_path("/a/"), "/a")); + assert(view_equal(zix_path_parent_path("/a//b"), "/a")); + + // Relative paths with no parent + assert(view_equal(zix_path_parent_path(""), NULL)); + assert(view_equal(zix_path_parent_path("."), NULL)); + assert(view_equal(zix_path_parent_path(".."), NULL)); + assert(view_equal(zix_path_parent_path("NONDRIVE:"), NULL)); + assert(view_equal(zix_path_parent_path("a"), NULL)); + + // Relative paths with a parent + assert(view_equal(zix_path_parent_path("../"), "..")); + assert(view_equal(zix_path_parent_path("./"), ".")); + assert(view_equal(zix_path_parent_path("a/"), "a")); + assert(view_equal(zix_path_parent_path("a/b"), "a")); + + // Superfluous leading and trailing separators + assert(view_equal(zix_path_parent_path("///a///"), "/a")); + assert(view_equal(zix_path_parent_path("///a///b"), "/a")); + + // Relative paths with dot and dot-dot entries + assert(view_equal(zix_path_parent_path("a/."), "a")); + assert(view_equal(zix_path_parent_path("a/.."), "a")); + assert(view_equal(zix_path_parent_path("a/../"), "a/..")); + assert(view_equal(zix_path_parent_path("a/../b"), "a/..")); + assert(view_equal(zix_path_parent_path("a/./"), "a/.")); + assert(view_equal(zix_path_parent_path("a/./b"), "a/.")); + +#ifdef _WIN32 + + // Paths like "C:/filename" have a drive letter + assert(view_equal(zix_path_parent_path("C:"), "C:")); + assert(view_equal(zix_path_parent_path("C:/"), "C:/")); + assert(view_equal(zix_path_parent_path("C:/a"), "C:/")); + assert(view_equal(zix_path_parent_path("C:/a/"), "C:/a")); + assert(view_equal(zix_path_parent_path("C:/a/b"), "C:/a")); + assert(view_equal(zix_path_parent_path("C:/a\\b"), "C:/a")); + assert(view_equal(zix_path_parent_path("C:\\"), "C:\\")); + assert(view_equal(zix_path_parent_path("C:\\a"), "C:\\")); + assert(view_equal(zix_path_parent_path("C:\\a/"), "C:\\a")); + assert(view_equal(zix_path_parent_path("C:\\a\\"), "C:\\a")); + assert(view_equal(zix_path_parent_path("C:a"), "C:")); + assert(view_equal(zix_path_parent_path("C:a/"), "C:a")); + assert(view_equal(zix_path_parent_path("C:a\\"), "C:a")); + + // Paths like "//host/" are network shares + assert(view_equal(zix_path_parent_path("//host"), "//host")); + assert(view_equal(zix_path_parent_path("//host/"), "//host/")); + assert(view_equal(zix_path_parent_path("//host/a"), "//host/")); + +#else + + // "C:" is just a strange directory or file name prefix + assert(view_equal(zix_path_parent_path("C:"), NULL)); + assert(view_equal(zix_path_parent_path("C:/"), "C:")); + assert(view_equal(zix_path_parent_path("C:/a"), "C:")); + assert(view_equal(zix_path_parent_path("C:/a/"), "C:/a")); + assert(view_equal(zix_path_parent_path("C:/a/b"), "C:/a")); + assert(view_equal(zix_path_parent_path("C:/a\\b"), "C:")); + assert(view_equal(zix_path_parent_path("C:\\"), NULL)); + assert(view_equal(zix_path_parent_path("C:\\a"), NULL)); + assert(view_equal(zix_path_parent_path("C:\\a/"), "C:\\a")); + assert(view_equal(zix_path_parent_path("C:\\a\\"), NULL)); + assert(view_equal(zix_path_parent_path("C:a"), NULL)); + assert(view_equal(zix_path_parent_path("C:a/"), "C:a")); + assert(view_equal(zix_path_parent_path("C:a\\"), NULL)); + + // Paths like "//host/" have redundant leading slashes + assert(view_equal(zix_path_parent_path("//host"), "/")); + assert(view_equal(zix_path_parent_path("//host/"), "/host")); + assert(view_equal(zix_path_parent_path("//host/a"), "/host")); + +#endif +} + +static void +test_path_filename(void) +{ + // Cases from <https://en.cppreference.com/w/cpp/filesystem/path/filename> + assert(view_equal(zix_path_filename("."), ".")); + assert(view_equal(zix_path_filename(".."), "..")); + assert(view_equal(zix_path_filename("/"), NULL)); + assert(view_equal(zix_path_filename("/foo/."), ".")); + assert(view_equal(zix_path_filename("/foo/.."), "..")); + assert(view_equal(zix_path_filename("/foo/.bar"), ".bar")); + assert(view_equal(zix_path_filename("/foo/bar.txt"), "bar.txt")); + assert(view_equal(zix_path_filename("/foo/bar/"), NULL)); + + // Identities + assert(view_equal(zix_path_filename("."), ".")); + assert(view_equal(zix_path_filename(".."), "..")); + assert(view_equal(zix_path_filename("a"), "a")); + + // Absolute paths without filenames + assert(view_equal(zix_path_filename("/"), NULL)); + assert(view_equal(zix_path_filename("//"), NULL)); + assert(view_equal(zix_path_filename("///a///"), NULL)); + assert(view_equal(zix_path_filename("/a/"), NULL)); + + // Absolute paths with filenames + assert(view_equal(zix_path_filename("///a///b"), "b")); + assert(view_equal(zix_path_filename("/a"), "a")); + assert(view_equal(zix_path_filename("/a//b"), "b")); + + // Relative paths without filenames + assert(view_equal(zix_path_filename(""), NULL)); + assert(view_equal(zix_path_filename("../"), NULL)); + assert(view_equal(zix_path_filename("./"), NULL)); + assert(view_equal(zix_path_filename("a/"), NULL)); + + // Relative paths with filenames + assert(view_equal(zix_path_filename("a/b"), "b")); + + // Windows absolute network paths conveniently work generically + assert(view_equal(zix_path_filename("//host/"), NULL)); + assert(view_equal(zix_path_filename("//host/a"), "a")); + + // Paths with dot and dot-dot entries + assert(view_equal(zix_path_filename("/."), ".")); + assert(view_equal(zix_path_filename("/.."), "..")); + assert(view_equal(zix_path_filename("a/."), ".")); + assert(view_equal(zix_path_filename("a/.."), "..")); + assert(view_equal(zix_path_filename("a/../b"), "b")); + assert(view_equal(zix_path_filename("a/./b"), "b")); + + // Paths with colons (maybe drive letters) that conveniently work generically + assert(view_equal(zix_path_filename("C:/"), NULL)); + assert(view_equal(zix_path_filename("C:/a"), "a")); + assert(view_equal(zix_path_filename("C:/a/"), NULL)); + assert(view_equal(zix_path_filename("C:/a/b"), "b")); + assert(view_equal(zix_path_filename("C:a/"), NULL)); + assert(view_equal(zix_path_filename("NONDRIVE:"), "NONDRIVE:")); + +#ifdef _WIN32 + + // Relative paths can have a drive letter like "C:file" + assert(view_equal(zix_path_filename("C:"), NULL)); + assert(view_equal(zix_path_filename("C:a"), "a")); + assert(view_equal(zix_path_filename("C:a\\"), NULL)); + + // Paths like "//host" are network roots + assert(view_equal(zix_path_filename("//host"), NULL)); + + // Backslash is a directory separator + assert(view_equal(zix_path_filename("C:/a\\b"), "b")); + assert(view_equal(zix_path_filename("C:\\"), NULL)); + assert(view_equal(zix_path_filename("C:\\a"), "a")); + assert(view_equal(zix_path_filename("C:\\a/"), NULL)); + assert(view_equal(zix_path_filename("C:\\a\\"), NULL)); + assert(view_equal(zix_path_filename("C:\\a\\b"), "b")); + assert(view_equal(zix_path_filename("a\\b"), "b")); + +#else + + // "C:" is just a strange directory or file name prefix + assert(view_equal(zix_path_filename("C:"), "C:")); + assert(view_equal(zix_path_filename("C:a"), "C:a")); + assert(view_equal(zix_path_filename("C:a\\"), "C:a\\")); + + // Redundant reading slashes are ignored + assert(view_equal(zix_path_filename("//host"), "host")); + + // Backslash is just a strange character in a directory or file name + assert(view_equal(zix_path_filename("C:/a\\b"), "a\\b")); + assert(view_equal(zix_path_filename("C:\\"), "C:\\")); + assert(view_equal(zix_path_filename("C:\\a"), "C:\\a")); + assert(view_equal(zix_path_filename("C:\\a/"), NULL)); + assert(view_equal(zix_path_filename("C:\\a\\"), "C:\\a\\")); + assert(view_equal(zix_path_filename("C:\\a\\b"), "C:\\a\\b")); + assert(view_equal(zix_path_filename("a\\b"), "a\\b")); + +#endif +} + +static void +test_path_stem(void) +{ + assert(view_equal(zix_path_stem(""), NULL)); + assert(view_equal(zix_path_stem("."), ".")); + assert(view_equal(zix_path_stem(".."), "..")); + assert(view_equal(zix_path_stem(".a"), ".a")); + assert(view_equal(zix_path_stem(".hidden"), ".hidden")); + assert(view_equal(zix_path_stem(".hidden.txt"), ".hidden")); + assert(view_equal(zix_path_stem("/"), NULL)); + assert(view_equal(zix_path_stem("//host/name"), "name")); + assert(view_equal(zix_path_stem("/a."), "a")); + assert(view_equal(zix_path_stem("/a.txt"), "a")); + assert(view_equal(zix_path_stem("/a/."), ".")); + assert(view_equal(zix_path_stem("/a/.."), "..")); + assert(view_equal(zix_path_stem("/a/.hidden"), ".hidden")); + assert(view_equal(zix_path_stem("/a/b."), "b")); + assert(view_equal(zix_path_stem("/a/b.tar.gz"), "b.tar")); + assert(view_equal(zix_path_stem("/a/b.txt"), "b")); + assert(view_equal(zix_path_stem("/a/b/.hidden"), ".hidden")); + assert(view_equal(zix_path_stem("C:/name"), "name")); + assert(view_equal(zix_path_stem("C:dir/name"), "name")); + assert(view_equal(zix_path_stem("a"), "a")); + assert(view_equal(zix_path_stem("a."), "a")); + assert(view_equal(zix_path_stem("a..txt"), "a.")); + assert(view_equal(zix_path_stem("a.txt"), "a")); + assert(view_equal(zix_path_stem("a/."), ".")); + assert(view_equal(zix_path_stem("a/.."), "..")); + assert(view_equal(zix_path_stem("a/b."), "b")); + assert(view_equal(zix_path_stem("a/b.tar.gz"), "b.tar")); + assert(view_equal(zix_path_stem("a/b.txt"), "b")); + +#ifdef _WIN32 + assert(view_equal(zix_path_stem("C:."), ".")); + assert(view_equal(zix_path_stem("C:.a"), ".a")); + assert(view_equal(zix_path_stem("C:a"), "a")); +#else + assert(view_equal(zix_path_stem("C:."), "C:")); + assert(view_equal(zix_path_stem("C:.a"), "C:")); + assert(view_equal(zix_path_stem("C:a"), "C:a")); +#endif +} + +static void +test_path_extension(void) +{ + assert(view_equal(zix_path_extension(""), NULL)); + assert(view_equal(zix_path_extension("."), NULL)); + assert(view_equal(zix_path_extension(".."), NULL)); + assert(view_equal(zix_path_extension(".a"), NULL)); + assert(view_equal(zix_path_extension(".hidden"), NULL)); + assert(view_equal(zix_path_extension(".hidden.txt"), ".txt")); + assert(view_equal(zix_path_extension("/"), NULL)); + assert(view_equal(zix_path_extension("/a."), ".")); + assert(view_equal(zix_path_extension("/a.txt"), ".txt")); + assert(view_equal(zix_path_extension("/a/."), NULL)); + assert(view_equal(zix_path_extension("/a/.."), NULL)); + assert(view_equal(zix_path_extension("/a/.hidden"), NULL)); + assert(view_equal(zix_path_extension("/a/b."), ".")); + assert(view_equal(zix_path_extension("/a/b.tar.gz"), ".gz")); + assert(view_equal(zix_path_extension("/a/b.txt"), ".txt")); + assert(view_equal(zix_path_extension("/a/b/.hidden"), NULL)); + assert(view_equal(zix_path_extension("C:/.hidden.txt"), ".txt")); + assert(view_equal(zix_path_extension("C:a.txt"), ".txt")); + assert(view_equal(zix_path_extension("a"), NULL)); + assert(view_equal(zix_path_extension("a."), ".")); + assert(view_equal(zix_path_extension("a..txt"), ".txt")); + assert(view_equal(zix_path_extension("a.tar.gz"), ".gz")); + assert(view_equal(zix_path_extension("a/."), NULL)); + assert(view_equal(zix_path_extension("a/.."), NULL)); + assert(view_equal(zix_path_extension("a/b."), ".")); + assert(view_equal(zix_path_extension("a/b.tar.gz"), ".gz")); + assert(view_equal(zix_path_extension("a/b.txt"), ".txt")); + +#ifdef _WIN32 + assert(view_equal(zix_path_extension("C:."), NULL)); + assert(view_equal(zix_path_extension("C:/.hidden"), NULL)); + assert(view_equal(zix_path_extension("C:/a.txt"), ".txt")); +#else + assert(view_equal(zix_path_extension("C:."), ".")); + assert(view_equal(zix_path_extension("C:/.hidden"), NULL)); + assert(view_equal(zix_path_extension("C:/a.txt"), ".txt")); +#endif +} + +static void +test_path_is_absolute(void) +{ + assert(!zix_path_is_absolute(".")); + assert(!zix_path_is_absolute("..")); + assert(!zix_path_is_absolute("../")); + assert(!zix_path_is_absolute("../a")); + assert(!zix_path_is_absolute("../a/")); + assert(!zix_path_is_absolute("a")); + assert(!zix_path_is_absolute("a/b")); + assert(!zix_path_is_relative("//host/a")); + assert(zix_path_is_absolute("//host/a")); + assert(zix_path_is_relative(".")); + assert(zix_path_is_relative("..")); + assert(zix_path_is_relative("../")); + assert(zix_path_is_relative("../a")); + assert(zix_path_is_relative("../a/")); + assert(zix_path_is_relative("a")); + assert(zix_path_is_relative("a/b")); + +#ifdef _WIN32 + // Paths starting with root names are absolute + assert(!zix_path_is_absolute("/")); + assert(!zix_path_is_absolute("/a")); + assert(!zix_path_is_absolute("/a/b")); + assert(!zix_path_is_relative("C:/a/b")); + assert(!zix_path_is_relative("C:\\a\\b")); + assert(!zix_path_is_relative("D:/a/b")); + assert(!zix_path_is_relative("D:\\a\\b")); + assert(zix_path_is_absolute("C:/a/b")); + assert(zix_path_is_absolute("C:\\a\\b")); + assert(zix_path_is_absolute("D:/a/b")); + assert(zix_path_is_absolute("D:\\a\\b")); + assert(zix_path_is_relative("/")); + assert(zix_path_is_relative("/a")); + assert(zix_path_is_relative("/a/b")); +#else + // Paths starting with slashes are absolute + assert(!zix_path_is_absolute("C:/a/b")); + assert(!zix_path_is_absolute("C:\\a\\b")); + assert(!zix_path_is_absolute("D:/a/b")); + assert(!zix_path_is_absolute("D:\\a\\b")); + assert(!zix_path_is_relative("/")); + assert(!zix_path_is_relative("/a")); + assert(!zix_path_is_relative("/a/b")); + assert(zix_path_is_absolute("/")); + assert(zix_path_is_absolute("/a")); + assert(zix_path_is_absolute("/a/b")); + assert(zix_path_is_relative("C:/a/b")); + assert(zix_path_is_relative("C:\\a\\b")); + assert(zix_path_is_relative("D:/a/b")); + assert(zix_path_is_relative("D:\\a\\b")); + +#endif +} + +int +main(void) +{ + test_path_root_name(); + test_path_root(); + test_path_parent(); + test_path_filename(); + test_path_stem(); + test_path_extension(); + test_path_is_absolute(); + + test_path_join(); + test_path_preferred(); + test_path_lexically_normal(); + test_path_lexically_relative(); + + return 0; +} |