While relatively harmless, BLI_path_parent_dir wasn't returning failure when passed in "./" which wouldn't succeed. Instead of adding a ".." and normalizing, normalize the path and remove the last directly. This is simpler than inspecting the resulting path to see if normalize removed it or not. Add additional tests which failed previously.
900 lines
25 KiB
C++
900 lines
25 KiB
C++
/* SPDX-License-Identifier: Apache-2.0 */
|
|
|
|
#include "testing/testing.h"
|
|
|
|
#include "IMB_imbuf.h"
|
|
|
|
#include "BLI_fileops.h"
|
|
#include "BLI_path_util.h"
|
|
#include "BLI_string.h"
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Local Utilities
|
|
* \{ */
|
|
|
|
static void str_replace_char_with_relative_exception(char *str, char src, char dst)
|
|
{
|
|
/* 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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Tests for: #BLI_path_normalize
|
|
* \{ */
|
|
|
|
#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)
|
|
|
|
#define NORMALIZE(input, output) NORMALIZE_WITH_BASEDIR(input, nullptr, output)
|
|
|
|
/* #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/");
|
|
}
|
|
|
|
#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", "/");
|
|
}
|
|
|
|
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); \
|
|
if (expect == NULL) { \
|
|
EXPECT_FALSE(ret); \
|
|
} \
|
|
else { \
|
|
EXPECT_TRUE(ret); \
|
|
EXPECT_EQ(strlen(expect), len_output); \
|
|
path[index_output + len_output] = '\0'; \
|
|
EXPECT_STREQ(&path[index_output], expect); \
|
|
} \
|
|
} \
|
|
((void)0)
|
|
|
|
TEST(path_util, NameAtIndex_Single)
|
|
{
|
|
AT_INDEX("/a", 0, "a");
|
|
AT_INDEX("/a/", 0, "a");
|
|
AT_INDEX("a/", 0, "a");
|
|
AT_INDEX("//a//", 0, "a");
|
|
AT_INDEX("a/b", 0, "a");
|
|
|
|
AT_INDEX("/a", 1, nullptr);
|
|
AT_INDEX("/a/", 1, nullptr);
|
|
AT_INDEX("a/", 1, nullptr);
|
|
AT_INDEX("//a//", 1, nullptr);
|
|
}
|
|
TEST(path_util, NameAtIndex_SingleNeg)
|
|
{
|
|
AT_INDEX("/a", -1, "a");
|
|
AT_INDEX("/a/", -1, "a");
|
|
AT_INDEX("a/", -1, "a");
|
|
AT_INDEX("//a//", -1, "a");
|
|
AT_INDEX("a/b", -1, "b");
|
|
|
|
AT_INDEX("/a", -2, nullptr);
|
|
AT_INDEX("/a/", -2, nullptr);
|
|
AT_INDEX("a/", -2, nullptr);
|
|
AT_INDEX("//a//", -2, nullptr);
|
|
}
|
|
|
|
TEST(path_util, NameAtIndex_Double)
|
|
{
|
|
AT_INDEX("/ab", 0, "ab");
|
|
AT_INDEX("/ab/", 0, "ab");
|
|
AT_INDEX("ab/", 0, "ab");
|
|
AT_INDEX("//ab//", 0, "ab");
|
|
AT_INDEX("ab/c", 0, "ab");
|
|
|
|
AT_INDEX("/ab", 1, nullptr);
|
|
AT_INDEX("/ab/", 1, nullptr);
|
|
AT_INDEX("ab/", 1, nullptr);
|
|
AT_INDEX("//ab//", 1, nullptr);
|
|
}
|
|
|
|
TEST(path_util, NameAtIndex_DoublNeg)
|
|
{
|
|
AT_INDEX("/ab", -1, "ab");
|
|
AT_INDEX("/ab/", -1, "ab");
|
|
AT_INDEX("ab/", -1, "ab");
|
|
AT_INDEX("//ab//", -1, "ab");
|
|
AT_INDEX("ab/c", -1, "c");
|
|
|
|
AT_INDEX("/ab", -2, nullptr);
|
|
AT_INDEX("/ab/", -2, nullptr);
|
|
AT_INDEX("ab/", -2, nullptr);
|
|
AT_INDEX("//ab//", -2, nullptr);
|
|
}
|
|
|
|
TEST(path_util, NameAtIndex_Misc)
|
|
{
|
|
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);
|
|
}
|
|
|
|
TEST(path_util, NameAtIndex_MiscNeg)
|
|
{
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
TEST(path_util, NameAtIndex_NoneComplex)
|
|
{
|
|
AT_INDEX("", 0, nullptr);
|
|
AT_INDEX("/", 0, nullptr);
|
|
AT_INDEX("//", 0, nullptr);
|
|
AT_INDEX("///", 0, nullptr);
|
|
}
|
|
|
|
TEST(path_util, NameAtIndex_NoneComplexNeg)
|
|
{
|
|
AT_INDEX("", -1, nullptr);
|
|
AT_INDEX("/", -1, nullptr);
|
|
AT_INDEX("//", -1, nullptr);
|
|
AT_INDEX("///", -1, nullptr);
|
|
}
|
|
|
|
#undef AT_INDEX
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \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. */ \
|
|
result[out_size] = '\0'; \
|
|
BLI_path_join(result, out_size, __VA_ARGS__); \
|
|
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)
|
|
|
|
#ifdef WIN32
|
|
# define JOIN JOIN_BACK_SLASH
|
|
#else
|
|
# define JOIN JOIN_FORWARD_SLASH
|
|
#endif
|
|
|
|
TEST(path_util, JoinNop)
|
|
{
|
|
JOIN("", 100, "");
|
|
JOIN("", 100, "", "");
|
|
JOIN("", 100, "", "", "");
|
|
JOIN("/", 100, "/", "", "");
|
|
JOIN("/", 100, "/", "/");
|
|
JOIN("/", 100, "/", "", "/");
|
|
JOIN("/", 100, "/", "", "/", "");
|
|
}
|
|
|
|
TEST(path_util, JoinSingle)
|
|
{
|
|
JOIN("test", 100, "test");
|
|
JOIN("", 100, "");
|
|
JOIN("a", 100, "a");
|
|
JOIN("/a", 100, "/a");
|
|
JOIN("a/", 100, "a/");
|
|
JOIN("/a/", 100, "/a/");
|
|
JOIN("/a/", 100, "/a//");
|
|
JOIN("//a/", 100, "//a//");
|
|
}
|
|
|
|
TEST(path_util, JoinTriple)
|
|
{
|
|
JOIN("/a/b/c", 100, "/a", "b", "c");
|
|
JOIN("/a/b/c", 100, "/a/", "/b/", "/c");
|
|
JOIN("/a/b/c", 100, "/a/b/", "/c");
|
|
JOIN("/a/b/c", 100, "/a/b/c");
|
|
JOIN("/a/b/c", 100, "/", "a/b/c");
|
|
|
|
JOIN("/a/b/c/", 100, "/a/", "/b/", "/c/");
|
|
JOIN("/a/b/c/", 100, "/a/b/c/");
|
|
JOIN("/a/b/c/", 100, "/a/b/", "/c/");
|
|
JOIN("/a/b/c/", 100, "/a/b/c", "/");
|
|
JOIN("/a/b/c/", 100, "/", "a/b/c", "/");
|
|
}
|
|
|
|
TEST(path_util, JoinTruncateShort)
|
|
{
|
|
JOIN("", 1, "/");
|
|
JOIN("/", 2, "/");
|
|
JOIN("a", 2, "", "aa");
|
|
JOIN("a", 2, "", "a/");
|
|
JOIN("a/b", 4, "a", "bc");
|
|
JOIN("ab/", 4, "ab", "c");
|
|
JOIN("/a/", 4, "/a", "b");
|
|
JOIN("/a/", 4, "/a/", "b/");
|
|
JOIN("/a/", 4, "/a", "/b/");
|
|
JOIN("/a/", 4, "/", "a/b/");
|
|
JOIN("//a", 4, "//", "a/b/");
|
|
|
|
JOIN("/a/b", 5, "/a", "b", "c");
|
|
}
|
|
|
|
TEST(path_util, JoinTruncateLong)
|
|
{
|
|
JOIN("", 1, "//", "//longer", "path");
|
|
JOIN("/", 2, "//", "//longer", "path");
|
|
JOIN("//", 3, "//", "//longer", "path");
|
|
JOIN("//l", 4, "//", "//longer", "path");
|
|
/* snip */
|
|
JOIN("//longe", 8, "//", "//longer", "path");
|
|
JOIN("//longer", 9, "//", "//longer", "path");
|
|
JOIN("//longer/", 10, "//", "//longer", "path");
|
|
JOIN("//longer/p", 11, "//", "//longer", "path");
|
|
JOIN("//longer/pa", 12, "//", "//longer", "path");
|
|
JOIN("//longer/pat", 13, "//", "//longer", "path");
|
|
JOIN("//longer/path", 14, "//", "//longer", "path"); /* not truncated. */
|
|
JOIN("//longer/path", 14, "//", "//longer", "path/");
|
|
JOIN("//longer/path/", 15, "//", "//longer", "path/"); /* not truncated. */
|
|
JOIN("//longer/path/", 15, "//", "//longer", "path/", "trunc");
|
|
JOIN("//longer/path/t", 16, "//", "//longer", "path/", "trunc");
|
|
}
|
|
|
|
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///");
|
|
}
|
|
|
|
TEST(path_util, JoinRelativePrefix)
|
|
{
|
|
JOIN("//a/b/c", 100, "//a", "b", "c");
|
|
JOIN("//a/b/c", 100, "//", "//a//", "//b//", "//c");
|
|
JOIN("//a/b/c", 100, "//", "//", "a", "//", "b", "//", "c");
|
|
}
|
|
|
|
#undef JOIN
|
|
#undef JOIN_BACK_SLASH
|
|
#undef JOIN_FORWARD_SLASH
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Tests for: #BLI_path_frame
|
|
* \{ */
|
|
|
|
TEST(path_util, Frame)
|
|
{
|
|
bool ret;
|
|
|
|
{
|
|
char path[FILE_MAX] = "";
|
|
ret = BLI_path_frame(path, 123, 1);
|
|
EXPECT_TRUE(ret);
|
|
EXPECT_STREQ("123", path);
|
|
}
|
|
|
|
{
|
|
char path[FILE_MAX] = "";
|
|
ret = BLI_path_frame(path, 123, 12);
|
|
EXPECT_TRUE(ret);
|
|
EXPECT_STREQ("000000000123", path);
|
|
}
|
|
|
|
{
|
|
char path[FILE_MAX] = "test_";
|
|
ret = BLI_path_frame(path, 123, 1);
|
|
EXPECT_TRUE(ret);
|
|
EXPECT_STREQ("test_123", path);
|
|
}
|
|
|
|
{
|
|
char path[FILE_MAX] = "test_";
|
|
ret = BLI_path_frame(path, 1, 12);
|
|
EXPECT_TRUE(ret);
|
|
EXPECT_STREQ("test_000000000001", path);
|
|
}
|
|
|
|
{
|
|
char path[FILE_MAX] = "test_############";
|
|
ret = BLI_path_frame(path, 1, 0);
|
|
EXPECT_TRUE(ret);
|
|
EXPECT_STREQ("test_000000000001", path);
|
|
}
|
|
|
|
{
|
|
char path[FILE_MAX] = "test_#_#_middle";
|
|
ret = BLI_path_frame(path, 123, 0);
|
|
EXPECT_TRUE(ret);
|
|
EXPECT_STREQ("test_#_123_middle", path);
|
|
}
|
|
|
|
/* intentionally fail */
|
|
{
|
|
char path[FILE_MAX] = "";
|
|
ret = BLI_path_frame(path, 123, 0);
|
|
EXPECT_FALSE(ret);
|
|
EXPECT_STREQ("", path);
|
|
}
|
|
|
|
{
|
|
char path[FILE_MAX] = "test_middle";
|
|
ret = BLI_path_frame(path, 123, 0);
|
|
EXPECT_FALSE(ret);
|
|
EXPECT_STREQ("test_middle", path);
|
|
}
|
|
|
|
/* negative frame numbers */
|
|
{
|
|
char path[FILE_MAX] = "test_####";
|
|
ret = BLI_path_frame(path, -1, 4);
|
|
EXPECT_TRUE(ret);
|
|
EXPECT_STREQ("test_-0001", path);
|
|
}
|
|
{
|
|
char path[FILE_MAX] = "test_####";
|
|
ret = BLI_path_frame(path, -100, 4);
|
|
EXPECT_TRUE(ret);
|
|
EXPECT_STREQ("test_-0100", path);
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Tests for: #BLI_split_dirfile
|
|
* \{ */
|
|
|
|
TEST(path_util, SplitDirfile)
|
|
{
|
|
{
|
|
const char *path = "";
|
|
char dir[FILE_MAX], file[FILE_MAX];
|
|
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
|
|
EXPECT_STREQ("", dir);
|
|
EXPECT_STREQ("", file);
|
|
}
|
|
|
|
{
|
|
const char *path = "/";
|
|
char dir[FILE_MAX], file[FILE_MAX];
|
|
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
|
|
EXPECT_STREQ("/", dir);
|
|
EXPECT_STREQ("", file);
|
|
}
|
|
|
|
{
|
|
const char *path = "fileonly";
|
|
char dir[FILE_MAX], file[FILE_MAX];
|
|
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
|
|
EXPECT_STREQ("", dir);
|
|
EXPECT_STREQ("fileonly", file);
|
|
}
|
|
|
|
{
|
|
const char *path = "dironly/";
|
|
char dir[FILE_MAX], file[FILE_MAX];
|
|
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
|
|
EXPECT_STREQ("dironly/", dir);
|
|
EXPECT_STREQ("", file);
|
|
}
|
|
|
|
{
|
|
const char *path = "/a/b";
|
|
char dir[FILE_MAX], file[FILE_MAX];
|
|
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
|
|
EXPECT_STREQ("/a/", dir);
|
|
EXPECT_STREQ("b", file);
|
|
}
|
|
|
|
{
|
|
const char *path = "/dirtoobig/filetoobig";
|
|
char dir[5], file[5];
|
|
BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file));
|
|
EXPECT_STREQ("/dir", dir);
|
|
EXPECT_STREQ("file", file);
|
|
|
|
BLI_split_dirfile(path, dir, file, 1, 1);
|
|
EXPECT_STREQ("", dir);
|
|
EXPECT_STREQ("", file);
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Tests for: #BLI_path_frame_strip
|
|
* \{ */
|
|
|
|
#define PATH_FRAME_STRIP(input_path, expect_path, expect_ext) \
|
|
{ \
|
|
char path[FILE_MAX]; \
|
|
char ext[FILE_MAX]; \
|
|
BLI_strncpy(path, (input_path), FILE_MAX); \
|
|
BLI_path_frame_strip(path, ext); \
|
|
EXPECT_STREQ(path, expect_path); \
|
|
EXPECT_STREQ(ext, expect_ext); \
|
|
} \
|
|
((void)0)
|
|
|
|
TEST(path_util, PathFrameStrip)
|
|
{
|
|
PATH_FRAME_STRIP("", "", "");
|
|
PATH_FRAME_STRIP("nonum.abc", "nonum", ".abc");
|
|
PATH_FRAME_STRIP("fileonly.001.abc", "fileonly.###", ".abc");
|
|
PATH_FRAME_STRIP("/abspath/to/somefile.001.abc", "/abspath/to/somefile.###", ".abc");
|
|
PATH_FRAME_STRIP("/ext/longer/somefile.001.alembic", "/ext/longer/somefile.###", ".alembic");
|
|
PATH_FRAME_STRIP("/ext/shorter/somefile.123001.abc", "/ext/shorter/somefile.######", ".abc");
|
|
}
|
|
#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); \
|
|
if (STREQ(input_ext, expect_ext)) { \
|
|
EXPECT_TRUE(ret); \
|
|
} \
|
|
else { \
|
|
EXPECT_FALSE(ret); \
|
|
} \
|
|
} \
|
|
((void)0)
|
|
|
|
TEST(path_util, PathExtensionCheck)
|
|
{
|
|
PATH_EXTENSION_CHECK("a/b/c.exe", ".exe", ".exe");
|
|
PATH_EXTENSION_CHECK("correct/path/to/file.h", ".h", ".h");
|
|
PATH_EXTENSION_CHECK("correct/path/to/file.BLEND", ".BLEND", ".BLEND");
|
|
PATH_EXTENSION_CHECK("../tricky/path/to/file.h", ".h", ".h");
|
|
PATH_EXTENSION_CHECK("../dirty//../path\\to/file.h", ".h", ".h");
|
|
PATH_EXTENSION_CHECK("a/b/c.veryveryverylonglonglongextension",
|
|
".veryveryverylonglonglongextension",
|
|
".veryveryverylonglonglongextension");
|
|
PATH_EXTENSION_CHECK("filename.PNG", "pnG", "pnG");
|
|
PATH_EXTENSION_CHECK("a/b/c.h.exe", ".exe", ".exe");
|
|
PATH_EXTENSION_CHECK("a/b/c.h.exe", "exe", "exe");
|
|
PATH_EXTENSION_CHECK("a/b/c.exe", "c.exe", "c.exe");
|
|
PATH_EXTENSION_CHECK("a/b/noext", "noext", "noext");
|
|
|
|
PATH_EXTENSION_CHECK("a/b/c.exe", ".png", ".exe");
|
|
PATH_EXTENSION_CHECK("a/b/c.exe", "c.png", ".exe");
|
|
PATH_EXTENSION_CHECK("a/b/s.l", "l.s", "s.l");
|
|
PATH_EXTENSION_CHECK(".hiddenfolder", "", ".hiddenfolder");
|
|
PATH_EXTENSION_CHECK("../dirty//../path\\to/actual.h.file.ext", ".h", ".ext");
|
|
PATH_EXTENSION_CHECK("..\\dirty//../path//to/.hiddenfile.JPEG", ".hiddenfile", ".JPEG");
|
|
}
|
|
#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); \
|
|
if (expect_hasChars) { \
|
|
EXPECT_TRUE(ret); \
|
|
} \
|
|
else { \
|
|
EXPECT_FALSE(ret); \
|
|
} \
|
|
} \
|
|
((void)0)
|
|
|
|
TEST(path_util, PathFrameCheckChars)
|
|
{
|
|
PATH_FRAME_CHECK_CHARS("a#", true);
|
|
PATH_FRAME_CHECK_CHARS("aaaaa#", true);
|
|
PATH_FRAME_CHECK_CHARS("#aaaaa", true);
|
|
PATH_FRAME_CHECK_CHARS("a##.###", true);
|
|
PATH_FRAME_CHECK_CHARS("####.abc#", true);
|
|
PATH_FRAME_CHECK_CHARS("path/to/chars/a#", true);
|
|
PATH_FRAME_CHECK_CHARS("path/to/chars/123#123.exe", true);
|
|
|
|
PATH_FRAME_CHECK_CHARS("&", false);
|
|
PATH_FRAME_CHECK_CHARS("\35", false);
|
|
PATH_FRAME_CHECK_CHARS("path#/to#/chars#/$.h", false);
|
|
PATH_FRAME_CHECK_CHARS("path#/to#/chars#/nochars.h", false);
|
|
PATH_FRAME_CHECK_CHARS("..\\dirty\\path#/..//to#\\chars#/nochars.h", false);
|
|
PATH_FRAME_CHECK_CHARS("..\\dirty\\path#/..//to#/chars#\\nochars.h", false);
|
|
}
|
|
#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]; \
|
|
bool ret; \
|
|
BLI_strncpy(path, input_path, FILE_MAX); \
|
|
ret = BLI_path_frame_range(path, sta, end, digits); \
|
|
if (expect_outpath == NULL) { \
|
|
EXPECT_FALSE(ret); \
|
|
} \
|
|
else { \
|
|
EXPECT_TRUE(ret); \
|
|
EXPECT_STREQ(path, expect_outpath); \
|
|
} \
|
|
} \
|
|
((void)0)
|
|
|
|
TEST(path_util, PathFrameRange)
|
|
{
|
|
int dummy = -1;
|
|
PATH_FRAME_RANGE("#", 1, 2, dummy, "1-2");
|
|
PATH_FRAME_RANGE("##", 1, 2, dummy, "01-02");
|
|
PATH_FRAME_RANGE("##", 1000, 2000, dummy, "1000-2000");
|
|
PATH_FRAME_RANGE("###", 100, 200, dummy, "100-200");
|
|
PATH_FRAME_RANGE("###", 8, 9, dummy, "008-009");
|
|
|
|
PATH_FRAME_RANGE("", 100, 200, 1, "100-200");
|
|
PATH_FRAME_RANGE("", 123, 321, 4, "0123-0321");
|
|
PATH_FRAME_RANGE("", 1, 0, 20, "00000000000000000001-00000000000000000000");
|
|
}
|
|
#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]; \
|
|
int out_frame = -1, out_numdigits = -1; \
|
|
BLI_strncpy(path, input_path, FILE_MAX); \
|
|
const bool ret = BLI_path_frame_get(path, &out_frame, &out_numdigits); \
|
|
if (expect_pathisvalid) { \
|
|
EXPECT_TRUE(ret); \
|
|
} \
|
|
else { \
|
|
EXPECT_FALSE(ret); \
|
|
} \
|
|
EXPECT_EQ(out_frame, expect_frame); \
|
|
EXPECT_EQ(out_numdigits, expect_numdigits); \
|
|
} \
|
|
((void)0)
|
|
|
|
TEST(path_util, PathFrameGet)
|
|
{
|
|
PATH_FRAME_GET("001.avi", 1, 3, true);
|
|
PATH_FRAME_GET("0000299.ext", 299, 7, true);
|
|
PATH_FRAME_GET("path/to/frame_2810.dummy_quite_long_extension", 2810, 4, true);
|
|
PATH_FRAME_GET("notframe_7_frame00018.bla", 18, 5, true);
|
|
|
|
PATH_FRAME_GET("", -1, -1, false);
|
|
}
|
|
#undef PATH_FRAME_GET
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Tests for: #BLI_path_extension
|
|
* \{ */
|
|
|
|
TEST(path_util, PathExtension)
|
|
{
|
|
EXPECT_EQ(nullptr, BLI_path_extension("some.def/file"));
|
|
EXPECT_EQ(nullptr, BLI_path_extension("Text"));
|
|
EXPECT_EQ(nullptr, BLI_path_extension("Text…001"));
|
|
|
|
EXPECT_STREQ(".", BLI_path_extension("some/file."));
|
|
EXPECT_STREQ(".gz", BLI_path_extension("some/file.tar.gz"));
|
|
EXPECT_STREQ(".abc", BLI_path_extension("some.def/file.abc"));
|
|
EXPECT_STREQ(".abc", BLI_path_extension("C:\\some.def\\file.abc"));
|
|
EXPECT_STREQ(".001", BLI_path_extension("Text.001"));
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Tests for: #BLI_path_rel
|
|
* \{ */
|
|
|
|
#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, '/', '\\'); \
|
|
} \
|
|
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)
|
|
|
|
#ifdef WIN32
|
|
# define ABS_PREFIX "C:"
|
|
#else
|
|
# define ABS_PREFIX ""
|
|
#endif
|
|
|
|
TEST(path_util, PathRelPath_Simple)
|
|
{
|
|
PATH_REL(ABS_PREFIX "/foo/bar/blender.blend", ABS_PREFIX "/foo/bar/", "//blender.blend");
|
|
}
|
|
|
|
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 ABS_PREFIX
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Tests for: #BLI_path_contains
|
|
* \{ */
|
|
|
|
TEST(path_util, PathContains)
|
|
{
|
|
EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path")) << "A path contains itself";
|
|
EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/inside"))
|
|
<< "A path contains its subdirectory";
|
|
EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/../path/inside"))
|
|
<< "Paths should be normalized";
|
|
EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "C:\\some\\path\\inside"))
|
|
<< "Windows paths should be supported as well";
|
|
|
|
EXPECT_FALSE(BLI_path_contains("C:\\some\\path", "C:\\some\\other\\path"))
|
|
<< "Windows paths should be supported as well";
|
|
EXPECT_FALSE(BLI_path_contains("/some/path", "/"))
|
|
<< "Root directory not be contained in a subdirectory";
|
|
EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path/../outside"))
|
|
<< "Paths should be normalized";
|
|
EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path_library"))
|
|
<< "Just sharing a suffix is not enough, path semantics should be followed";
|
|
EXPECT_FALSE(BLI_path_contains("/some/path", "./contents"))
|
|
<< "Relative paths are not supported";
|
|
}
|
|
|
|
#ifdef WIN32
|
|
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 /* WIN32 */
|
|
|
|
/** \} */
|