diff options
Diffstat (limited to 'source/blender/blenlib/tests/BLI_path_util_test.cc')
-rw-r--r-- | source/blender/blenlib/tests/BLI_path_util_test.cc | 444 |
1 files changed, 324 insertions, 120 deletions
diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc index 89e537235db..293d353efcc 100644 --- a/source/blender/blenlib/tests/BLI_path_util_test.cc +++ b/source/blender/blenlib/tests/BLI_path_util_test.cc @@ -9,69 +9,166 @@ #include "BLI_string.h" /* -------------------------------------------------------------------- */ -/* tests */ +/** \name Local Utilities + * \{ */ -/* BLI_path_normalize */ -#ifndef _WIN32 -TEST(path_util, Clean) +static void str_replace_char_with_relative_exception(char *str, char src, char dst) { - /* "/./" -> "/" */ - { - char path[FILE_MAX] = "/a/./b/./c/./"; - BLI_path_normalize(nullptr, path); - EXPECT_STREQ("/a/b/c/", path); + /* Always keep "//" or more leading slashes (special meaning). */ + if (src == '/') { + if (str[0] == '/' && str[1] == '/') { + str += 2; + while (*str == '/') { + str++; + } + } } + BLI_str_replace_char(str, src, dst); +} - { - char path[FILE_MAX] = "/./././"; - BLI_path_normalize(nullptr, path); - EXPECT_STREQ("/", path); +static char *str_replace_char_strdup(const char *str, char src, char dst) +{ + if (str == nullptr) { + return nullptr; } + char *str_dupe = strdup(str); + BLI_str_replace_char(str_dupe, src, dst); + return str_dupe; +} - { - char path[FILE_MAX] = "/a/./././b/"; - BLI_path_normalize(nullptr, path); - EXPECT_STREQ("/a/b/", path); - } +/** \} */ - /* "//" -> "/" */ - { - char path[FILE_MAX] = "a////"; - BLI_path_normalize(nullptr, path); - EXPECT_STREQ("a/", path); - } +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_normalize + * \{ */ - if (false) /* FIXME */ - { - char path[FILE_MAX] = "./a////"; - BLI_path_normalize(nullptr, path); - EXPECT_STREQ("./a/", path); - } +#define NORMALIZE_WITH_BASEDIR(input, input_base, output) \ + { \ + char path[FILE_MAX] = input; \ + const char *input_base_test = input_base; \ + if (SEP == '\\') { \ + str_replace_char_with_relative_exception(path, '/', '\\'); \ + input_base_test = str_replace_char_strdup(input_base_test, '/', '\\'); \ + } \ + BLI_path_normalize(input_base_test, path); \ + if (SEP == '\\') { \ + BLI_str_replace_char(path, '\\', '/'); \ + if (input_base_test) { \ + free((void *)input_base_test); \ + } \ + } \ + EXPECT_STREQ(output, path); \ + } \ + ((void)0) - /* "foo/bar/../" -> "foo/" */ - { - char path[FILE_MAX] = "/a/b/c/../../../"; - BLI_path_normalize(nullptr, path); - EXPECT_STREQ("/", path); - } +#define NORMALIZE(input, output) NORMALIZE_WITH_BASEDIR(input, nullptr, output) - { - char path[FILE_MAX] = "/a/../a/b/../b/c/../c/"; - BLI_path_normalize(nullptr, path); - EXPECT_STREQ("/a/b/c/", path); - } +/* #BLI_path_normalize: "/./" -> "/" */ +TEST(path_util, Clean_Dot) +{ + NORMALIZE("/./", "/"); + NORMALIZE("/a/./b/./c/./", "/a/b/c/"); + NORMALIZE("/./././", "/"); + NORMALIZE("/a/./././b/", "/a/b/"); +} +/* #BLI_path_normalize: complex "/./" -> "/", "//" -> "/", "./path/../" -> "./". */ +TEST(path_util, Clean_Complex) +{ + NORMALIZE("/a/./b/./c/./.././.././", "/a/"); + NORMALIZE("/a//.//b//.//c//.//..//.//..//.//", "/a/"); +} +/* #BLI_path_normalize: "//" -> "/" */ +TEST(path_util, Clean_DoubleSlash) +{ + NORMALIZE("//", "//"); /* Exception, double forward slash. */ + NORMALIZE(".//", "./"); + NORMALIZE("a////", "a/"); + NORMALIZE("./a////", "./a/"); +} +/* #BLI_path_normalize: "foo/bar/../" -> "foo/" */ +TEST(path_util, Clean_Parent) +{ + NORMALIZE("/a/b/c/../../../", "/"); + NORMALIZE("/a/../a/b/../b/c/../c/", "/a/b/c/"); + NORMALIZE_WITH_BASEDIR("//../", "/a/b/c/", "/a/b/"); +} - { - char path[FILE_MAX] = "//../"; - BLI_path_normalize("/a/b/c/", path); - EXPECT_STREQ("/a/b/", path); - } +#undef NORMALIZE_WITH_BASEDIR +#undef NORMALIZE + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_parent_dir + * \{ */ + +#define PARENT_DIR(input, output) \ + { \ + char path[FILE_MAX] = input; \ + if (SEP == '\\') { \ + BLI_str_replace_char(path, '/', '\\'); \ + } \ + BLI_path_parent_dir(path); \ + if (SEP == '\\') { \ + BLI_str_replace_char(path, '\\', '/'); \ + } \ + EXPECT_STREQ(output, path); \ + } \ + ((void)0) + +TEST(path_util, ParentDir_Simple) +{ + PARENT_DIR("/a/b/", "/a/"); + PARENT_DIR("/a/b", "/a/"); + PARENT_DIR("/a", "/"); } -#endif + +TEST(path_util, ParentDir_NOP) +{ + PARENT_DIR("/", "/"); + PARENT_DIR("", ""); + PARENT_DIR(".", "."); + PARENT_DIR("./", "./"); + PARENT_DIR(".//", ".//"); + PARENT_DIR("./.", "./."); +} + +TEST(path_util, ParentDir_TrailingPeriod) +{ + /* Ensure trailing dots aren't confused with parent path. */ + PARENT_DIR("/.../.../.../", "/.../.../"); + PARENT_DIR("/.../.../...", "/.../.../"); + + PARENT_DIR("/a../b../c../", "/a../b../"); + PARENT_DIR("/a../b../c..", "/a../b../"); + + PARENT_DIR("/a./b./c./", "/a./b./"); + PARENT_DIR("/a./b./c.", "/a./b./"); +} + +TEST(path_util, ParentDir_Complex) +{ + PARENT_DIR("./a/", "./"); + PARENT_DIR("./a", "./"); + PARENT_DIR("../a/", "../"); + PARENT_DIR("../a", "../"); +} + +#undef PARENT_DIR + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_name_at_index + * \{ */ #define AT_INDEX(str_input, index_input, str_expect) \ { \ char path[] = str_input; \ + /* Test input assumes forward slash, support back-slash on WIN32. */ \ + if (SEP == '\\') { \ + BLI_str_replace_char(path, '/', '\\'); \ + } \ const char *expect = str_expect; \ int index_output, len_output; \ const bool ret = BLI_path_name_at_index(path, index_input, &index_output, &len_output); \ @@ -87,7 +184,6 @@ TEST(path_util, Clean) } \ ((void)0) -/* BLI_path_name_at_index */ TEST(path_util, NameAtIndex_Single) { AT_INDEX("/a", 0, "a"); @@ -166,21 +262,21 @@ TEST(path_util, NameAtIndex_MiscNeg) TEST(path_util, NameAtIndex_MiscComplex) { AT_INDEX("how//now/brown/cow", 0, "how"); - AT_INDEX("//how///now\\/brown/cow", 1, "now"); - AT_INDEX("/how/now\\//brown\\/cow", 2, "brown"); - AT_INDEX("/how/now/brown/cow//\\", 3, "cow"); - AT_INDEX("/how/now/brown/\\cow", 4, nullptr); - AT_INDEX("how/now/brown/\\cow\\", 4, nullptr); + AT_INDEX("//how///now//brown/cow", 1, "now"); + AT_INDEX("/how/now///brown//cow", 2, "brown"); + AT_INDEX("/how/now/brown/cow///", 3, "cow"); + AT_INDEX("/how/now/brown//cow", 4, nullptr); + AT_INDEX("how/now/brown//cow/", 4, nullptr); } TEST(path_util, NameAtIndex_MiscComplexNeg) { AT_INDEX("how//now/brown/cow", -4, "how"); - AT_INDEX("//how///now\\/brown/cow", -3, "now"); - AT_INDEX("/how/now\\//brown\\/cow", -2, "brown"); - AT_INDEX("/how/now/brown/cow//\\", -1, "cow"); - AT_INDEX("/how/now/brown/\\cow", -5, nullptr); - AT_INDEX("how/now/brown/\\cow\\", -5, nullptr); + AT_INDEX("//how///now//brown/cow", -3, "now"); + AT_INDEX("/how/now///brown//cow", -2, "brown"); + AT_INDEX("/how/now/brown/cow///", -1, "cow"); + AT_INDEX("/how/now/brown//cow", -5, nullptr); + AT_INDEX("how/now/brown//cow/", -5, nullptr); } TEST(path_util, NameAtIndex_NoneComplex) @@ -201,21 +297,59 @@ TEST(path_util, NameAtIndex_NoneComplexNeg) #undef AT_INDEX -#define JOIN(str_expect, out_size, ...) \ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_join + * \{ */ + +/* For systems with `/` path separator (non WIN32). */ +#define JOIN_FORWARD_SLASH(str_expect, out_size, ...) \ { \ const char *expect = str_expect; \ char result[(out_size) + 1024]; \ - /* check we don't write past the last byte */ \ + /* Check we don't write past the last byte. */ \ result[out_size] = '\0'; \ BLI_path_join(result, out_size, __VA_ARGS__); \ - /* simplify expected string */ \ + EXPECT_STREQ(result, expect); \ + EXPECT_EQ(result[out_size], '\0'); \ + } \ + ((void)0) + +/* For systems with `\` path separator (WIN32). + * Perform additional manipulation to behave as if input arguments used `\` separators. + * Needed since #BLI_path_join uses native slashes. */ +#define JOIN_BACK_SLASH(str_expect, out_size, ...) \ + { \ + const char *expect = str_expect; \ + char result[(out_size) + 1024]; \ + const char *input_forward_slash[] = {__VA_ARGS__}; \ + char *input_back_slash[ARRAY_SIZE(input_forward_slash)] = {nullptr}; \ + for (int i = 0; i < ARRAY_SIZE(input_forward_slash); i++) { \ + input_back_slash[i] = strdup(input_forward_slash[i]); \ + BLI_str_replace_char(input_back_slash[i], '/', '\\'); \ + } \ + /* Check we don't write past the last byte. */ \ + result[out_size] = '\0'; \ + BLI_path_join_array(result, \ + out_size, \ + const_cast<const char **>(input_back_slash), \ + ARRAY_SIZE(input_back_slash)); \ BLI_str_replace_char(result, '\\', '/'); \ EXPECT_STREQ(result, expect); \ EXPECT_EQ(result[out_size], '\0'); \ + for (int i = 0; i < ARRAY_SIZE(input_forward_slash); i++) { \ + free(input_back_slash[i]); \ + } \ } \ ((void)0) -/* BLI_path_join */ +#ifdef WIN32 +# define JOIN JOIN_BACK_SLASH +#else +# define JOIN JOIN_FORWARD_SLASH +#endif + TEST(path_util, JoinNop) { JOIN("", 100, ""); @@ -293,9 +427,9 @@ TEST(path_util, JoinTruncateLong) TEST(path_util, JoinComplex) { - JOIN("/a/b/c/d/e/f/g/", 100, "/", "\\a/b", "//////c/d", "", "e\\\\", "f", "g//"); - JOIN("/aa/bb/cc/dd/ee/ff/gg/", 100, "/", "\\aa/bb", "//////cc/dd", "", "ee\\\\", "ff", "gg//"); - JOIN("1/2/3/", 100, "1", "////////", "", "2", "3\\"); + JOIN("/a/b/c/d/e/f/g/", 100, "/", "a/b", "//////c/d", "", "e", "f", "g//"); + JOIN("/aa/bb/cc/dd/ee/ff/gg/", 100, "/", "aa/bb", "//////cc/dd", "", "ee", "ff", "gg//"); + JOIN("1/2/3/", 100, "1", "////////", "", "2", "3///"); } TEST(path_util, JoinRelativePrefix) @@ -306,8 +440,15 @@ TEST(path_util, JoinRelativePrefix) } #undef JOIN +#undef JOIN_BACK_SLASH +#undef JOIN_FORWARD_SLASH + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_frame + * \{ */ -/* BLI_path_frame */ TEST(path_util, Frame) { bool ret; @@ -384,7 +525,12 @@ TEST(path_util, Frame) } } -/* BLI_split_dirfile */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_split_dirfile + * \{ */ + TEST(path_util, SplitDirfile) { { @@ -440,6 +586,12 @@ TEST(path_util, SplitDirfile) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_frame_strip + * \{ */ + #define PATH_FRAME_STRIP(input_path, expect_path, expect_ext) \ { \ char path[FILE_MAX]; \ @@ -451,7 +603,6 @@ TEST(path_util, SplitDirfile) } \ ((void)0) -/* BLI_path_frame_strip */ TEST(path_util, PathFrameStrip) { PATH_FRAME_STRIP("", "", ""); @@ -463,6 +614,12 @@ TEST(path_util, PathFrameStrip) } #undef PATH_FRAME_STRIP +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_extension_check + * \{ */ + #define PATH_EXTENSION_CHECK(input_path, input_ext, expect_ext) \ { \ const bool ret = BLI_path_extension_check(input_path, input_ext); \ @@ -475,7 +632,6 @@ TEST(path_util, PathFrameStrip) } \ ((void)0) -/* BLI_path_extension_check */ TEST(path_util, PathExtensionCheck) { PATH_EXTENSION_CHECK("a/b/c.exe", ".exe", ".exe"); @@ -501,6 +657,12 @@ TEST(path_util, PathExtensionCheck) } #undef PATH_EXTENSION_CHECK +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_frame_check_chars + * \{ */ + #define PATH_FRAME_CHECK_CHARS(input_path, expect_hasChars) \ { \ const bool ret = BLI_path_frame_check_chars(input_path); \ @@ -513,7 +675,6 @@ TEST(path_util, PathExtensionCheck) } \ ((void)0) -/* BLI_path_frame_check_chars */ TEST(path_util, PathFrameCheckChars) { PATH_FRAME_CHECK_CHARS("a#", true); @@ -533,6 +694,12 @@ TEST(path_util, PathFrameCheckChars) } #undef PATH_FRAME_CHECK_CHARS +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_frame_range + * \{ */ + #define PATH_FRAME_RANGE(input_path, sta, end, digits, expect_outpath) \ { \ char path[FILE_MAX]; \ @@ -549,7 +716,6 @@ TEST(path_util, PathFrameCheckChars) } \ ((void)0) -/* BLI_path_frame_range */ TEST(path_util, PathFrameRange) { int dummy = -1; @@ -565,6 +731,12 @@ TEST(path_util, PathFrameRange) } #undef PATH_FRAME_RANGE +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_frame_get + * \{ */ + #define PATH_FRAME_GET(input_path, expect_frame, expect_numdigits, expect_pathisvalid) \ { \ char path[FILE_MAX]; \ @@ -582,7 +754,6 @@ TEST(path_util, PathFrameRange) } \ ((void)0) -/* BLI_path_frame_get */ TEST(path_util, PathFrameGet) { PATH_FRAME_GET("001.avi", 1, 3, true); @@ -594,7 +765,12 @@ TEST(path_util, PathFrameGet) } #undef PATH_FRAME_GET -/* BLI_path_extension */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_extension + * \{ */ + TEST(path_util, PathExtension) { EXPECT_EQ(nullptr, BLI_path_extension("some.def/file")); @@ -608,62 +784,88 @@ TEST(path_util, PathExtension) EXPECT_STREQ(".001", BLI_path_extension("Text.001")); } -/* BLI_path_rel. */ -#ifndef _WIN32 +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_rel + * \{ */ -# define PATH_REL(abs_path, ref_path, rel_path) \ - { \ - char path[FILE_MAX]; \ - BLI_strncpy(path, abs_path, sizeof(path)); \ - BLI_path_rel(path, ref_path); \ - EXPECT_STREQ(rel_path, path); \ +#define PATH_REL(abs_path, ref_path, rel_path) \ + { \ + char path[FILE_MAX]; \ + const char *ref_path_test = ref_path; \ + BLI_strncpy(path, abs_path, sizeof(path)); \ + if (SEP == '\\') { \ + BLI_str_replace_char(path, '/', '\\'); \ + ref_path_test = str_replace_char_strdup(ref_path_test, '/', '\\'); \ } \ - void(0) + BLI_path_rel(path, ref_path_test); \ + if (SEP == '\\') { \ + BLI_str_replace_char(path, '\\', '/'); \ + free((void *)ref_path_test); \ + } \ + EXPECT_STREQ(rel_path, path); \ + } \ + void(0) -TEST(path_util, PathRelPath) +#ifdef WIN32 +# define ABS_PREFIX "C:" +#else +# define ABS_PREFIX "" +#endif + +TEST(path_util, PathRelPath_Simple) { - PATH_REL("/foo/bar/blender.blend", "/foo/bar/", "//blender.blend"); - PATH_REL("/foo/bar/blender.blend", "/foo/bar", "//bar/blender.blend"); + PATH_REL(ABS_PREFIX "/foo/bar/blender.blend", ABS_PREFIX "/foo/bar/", "//blender.blend"); +} - /* Check for potential buffer overflows. */ - { - char abs_path_in[FILE_MAX]; - abs_path_in[0] = '/'; - for (int i = 1; i < FILE_MAX - 1; i++) { - abs_path_in[i] = 'A'; - } - abs_path_in[FILE_MAX - 1] = '\0'; - char abs_path_out[FILE_MAX]; - abs_path_out[0] = '/'; - abs_path_out[1] = '/'; - for (int i = 2; i < FILE_MAX - 1; i++) { - abs_path_out[i] = 'A'; - } - abs_path_out[FILE_MAX - 1] = '\0'; - PATH_REL(abs_path_in, "/", abs_path_out); - - const char *ref_path_in = "/foo/bar/"; - const size_t ref_path_in_len = strlen(ref_path_in); - strcpy(abs_path_in, ref_path_in); - for (int i = ref_path_in_len; i < FILE_MAX - 1; i++) { - abs_path_in[i] = 'A'; - } - abs_path_in[FILE_MAX - 1] = '\0'; - abs_path_out[0] = '/'; - abs_path_out[1] = '/'; - for (int i = 2; i < FILE_MAX - (int(ref_path_in_len) - 1); i++) { - abs_path_out[i] = 'A'; - } - abs_path_out[FILE_MAX - (ref_path_in_len - 1)] = '\0'; - PATH_REL(abs_path_in, ref_path_in, abs_path_out); +TEST(path_util, PathRelPath_SimpleSubdir) +{ + PATH_REL(ABS_PREFIX "/foo/bar/blender.blend", ABS_PREFIX "/foo/bar", "//bar/blender.blend"); +} + +TEST(path_util, PathRelPath_BufferOverflowRoot) +{ + char abs_path_in[FILE_MAX]; + const char *abs_prefix = ABS_PREFIX "/"; + for (int i = STRNCPY_RLEN(abs_path_in, abs_prefix); i < FILE_MAX - 1; i++) { + abs_path_in[i] = 'A'; + } + abs_path_in[FILE_MAX - 1] = '\0'; + char abs_path_out[FILE_MAX]; + for (int i = STRNCPY_RLEN(abs_path_out, "//"); i < FILE_MAX - 1; i++) { + abs_path_out[i] = 'A'; + } + abs_path_out[FILE_MAX - std::max((strlen(abs_prefix) - 1), size_t(1))] = '\0'; + PATH_REL(abs_path_in, abs_prefix, abs_path_out); +} + +TEST(path_util, PathRelPath_BufferOverflowSubdir) +{ + char abs_path_in[FILE_MAX]; + const char *ref_path_in = ABS_PREFIX "/foo/bar/"; + const size_t ref_path_in_len = strlen(ref_path_in); + for (int i = STRNCPY_RLEN(abs_path_in, ref_path_in); i < FILE_MAX - 1; i++) { + abs_path_in[i] = 'A'; } + abs_path_in[FILE_MAX - 1] = '\0'; + char abs_path_out[FILE_MAX]; + for (int i = STRNCPY_RLEN(abs_path_out, "//"); i < FILE_MAX - (int(ref_path_in_len) - 1); i++) { + abs_path_out[i] = 'A'; + } + abs_path_out[FILE_MAX - std::max((ref_path_in_len - 1), size_t(1))] = '\0'; + PATH_REL(abs_path_in, ref_path_in, abs_path_out); } -# undef PATH_REL +#undef PATH_REL +#undef ABS_PREFIX -#endif +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tests for: #BLI_path_contains + * \{ */ -/* BLI_path_contains */ TEST(path_util, PathContains) { EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path")) << "A path contains itself"; @@ -692,4 +894,6 @@ TEST(path_util, PathContains_Windows_case_insensitive) EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "c:\\SOME\\path\\inside")) << "On Windows path comparison should ignore case"; } -#endif +#endif /* WIN32 */ + +/** \} */ |