// Copyright 2014 The Android Open Source Project // // This software is licensed under the terms of the GNU General Public // License version 2, as published by the Free Software Foundation, and // may be copied, distributed, and modified under those terms. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. #include "android/base/files/PathUtils.h" #include "android/base/containers/StringVector.h" #include "android/base/String.h" #include <gtest/gtest.h> #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) namespace android { namespace base { static const int kHostTypeCount = PathUtils::kHostTypeCount; TEST(PathUtils, isDirSeparator) { static const struct { int ch; bool expected[kHostTypeCount]; } kData[] = { { '/', { true, true }}, { '\\', { false, true }}, { '$', { false, false }}, { ':', { false, false }}, { ';', { false, false }}, }; for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { int ch = kData[n].ch; EXPECT_EQ(kData[n].expected[kHostPosix], PathUtils::isDirSeparator(ch, kHostPosix)) << "Testing '" << ch << "'"; EXPECT_EQ(kData[n].expected[kHostWin32], PathUtils::isDirSeparator(ch, kHostWin32)) << "Testing '" << ch << "'"; EXPECT_EQ(kData[n].expected[kHostType], PathUtils::isDirSeparator(ch)) << "Testing '" << ch << "'"; } } TEST(PathUtils, isPathSeparator) { static const struct { int ch; bool expected[kHostTypeCount]; } kData[] = { { ':', { true, false }}, { ';', { false, true }}, { '/', { false, false }}, { '\\', { false, false }}, { '$', { false, false }}, }; for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { int ch = kData[n].ch; EXPECT_EQ(kData[n].expected[kHostPosix], PathUtils::isPathSeparator(ch, kHostPosix)) << "Testing '" << ch << "'"; EXPECT_EQ(kData[n].expected[kHostWin32], PathUtils::isPathSeparator(ch, kHostWin32)) << "Testing '" << ch << "'"; EXPECT_EQ(kData[n].expected[kHostType], PathUtils::isPathSeparator(ch)) << "Testing '" << ch << "'"; } } TEST(PathUtils, rootPrefixSize) { static const struct { const char* path; size_t prefixSize[kHostTypeCount]; } kData[] = { { NULL, { 0u, 0u} }, { "", { 0u, 0u } }, { "foo", { 0u, 0u } }, { "foo/bar", { 0u, 0u } }, { "/foo", { 1u, 1u } }, { "//foo", { 1u, 5u } }, { "//foo/bar", { 1u, 6u } }, { "c:", { 0u, 2u } }, { "c:foo", { 0u, 2u } }, { "c/foo", { 0u, 0u } }, { "c:/foo", { 0u, 3u } }, { "c:\\", { 0u, 3u } }, { "c:\\\\", { 0u, 3u } }, { "1:/foo", { 0u, 0u } }, { "\\", { 0u, 1u } }, { "\\foo", { 0u, 1u } }, { "\\foo\\bar", { 0u, 1u } }, { "\\\\foo", { 0u, 5u } }, { "\\\\foo\\", { 0u, 6u } }, { "\\\\foo\\\\bar", { 0u, 6u } }, }; for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { const char* path = kData[n].path; EXPECT_EQ(kData[n].prefixSize[kHostPosix], PathUtils::rootPrefixSize(path, kHostPosix)) << "Testing '" << (path ? path : "<NULL>") << "'"; EXPECT_EQ(kData[n].prefixSize[kHostWin32], PathUtils::rootPrefixSize(path, kHostWin32)) << "Testing '" << (path ? path : "<NULL>") << "'"; EXPECT_EQ(kData[n].prefixSize[kHostType], PathUtils::rootPrefixSize(path)) << "Testing '" << (path ? path : "<NULL>") << "'"; } } TEST(PathUtils, isAbsolute) { static const struct { const char* path; bool expected[kHostTypeCount]; } kData[] = { { "foo", { false, false } }, { "/foo", { true, true } }, { "\\foo", { false, true } }, { "/foo/bar", { true, true } }, { "\\foo\\bar", { false, true } }, { "C:foo", { false, false } }, { "C:/foo", { false, true } }, { "C:\\foo", { false, true } }, { "//server", { true, false } }, { "//server/path", { true, true } }, }; for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { const char* path = kData[n].path; EXPECT_EQ(kData[n].expected[kHostPosix], PathUtils::isAbsolute(path, kHostPosix)) << "Testing '" << (path ? path : "<NULL>") << "'"; EXPECT_EQ(kData[n].expected[kHostWin32], PathUtils::isAbsolute(path, kHostWin32)) << "Testing '" << (path ? path : "<NULL>") << "'"; EXPECT_EQ(kData[n].expected[kHostType], PathUtils::isAbsolute(path)) << "Testing '" << (path ? path : "<NULL>") << "'"; } } static const int kMaxComponents = 10; typedef const char* ComponentList[kMaxComponents]; static void checkComponents(const ComponentList& expected, const StringVector& components, const char* hostType, const char* path) { size_t m; for (m = 0; m < components.size(); ++m) { if (!expected[m]) break; const char* component = expected[m]; EXPECT_STREQ(component, components[m].c_str()) << hostType << " component #" << (m + 1) << " in " << path; } EXPECT_EQ(m, components.size()) << hostType << " component #" << (m + 1) << " in " << path; } TEST(PathUtils, decompose) { static const struct { const char* path; const ComponentList components[kHostTypeCount]; } kData[] = { { "", { { NULL }, { NULL } } }, { "foo", { { "foo", NULL }, { "foo", NULL } } }, { "foo/", { { "foo", NULL }, { "foo", NULL } } }, { "foo/bar", { { "foo", "bar", NULL }, { "foo", "bar", NULL } } }, { "foo//bar/zoo", { { "foo", "bar", "zoo", NULL }, { "foo", "bar", "zoo", NULL } } }, { "\\foo\\bar\\", { { "\\foo\\bar\\", NULL }, { "\\", "foo", "bar", NULL } } }, { "C:foo\\bar", { { "C:foo\\bar", NULL }, { "C:", "foo", "bar", NULL } } }, { "C:/foo", { { "C:", "foo", NULL }, { "C:/", "foo", NULL } } }, { "/foo", { { "/", "foo", NULL }, { "/", "foo", NULL } } }, { "\\foo", { { "\\foo", NULL }, { "\\", "foo", NULL } } }, }; for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { const char* path = kData[n].path; checkComponents(kData[n].components[kHostPosix], PathUtils::decompose(path, kHostPosix), "posix", path); checkComponents(kData[n].components[kHostWin32], PathUtils::decompose(path, kHostWin32), "win32", path); checkComponents(kData[n].components[kHostType], PathUtils::decompose(path), "host", path); } } static StringVector componentListToVector( const ComponentList& input) { StringVector result; for (size_t i = 0; input[i]; ++i) result.push_back(input[i]); return result; } TEST(PathUtils, recompose) { static const struct { const ComponentList input; const char* path[kHostTypeCount]; } kData[] = { { { NULL }, { "", "" } }, { { ".", NULL }, { ".", "." } }, { { "..", NULL }, { "..", ".." } }, { { "/", NULL }, { "/", "/" } }, { { "/", "foo", NULL }, { "/foo", "/foo" } }, { { "\\", "foo", NULL }, { "\\/foo", "\\foo" } }, { { "foo", NULL }, { "foo", "foo" } }, { { "foo", "bar", NULL }, { "foo/bar", "foo\\bar" } }, { { ".", "foo", "..", NULL }, { "./foo/..", ".\\foo\\.." } }, { { "C:", "foo", NULL }, { "C:/foo", "C:foo" } }, }; for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { StringVector components = componentListToVector(kData[n].input); EXPECT_STREQ(kData[n].path[kHostPosix], PathUtils::recompose(components, kHostPosix).c_str()); EXPECT_STREQ(kData[n].path[kHostWin32], PathUtils::recompose(components, kHostWin32).c_str()); EXPECT_STREQ(kData[n].path[kHostType], PathUtils::recompose(components).c_str()); } } // Convert a vector of strings |components| into a file path, using // |separator| as the directory separator. static String componentsToPath( const ComponentList& components, char separator) { String result; for (size_t n = 0; components[n]; ++n) { if (n) result += separator; result += components[n]; } return result; } static String stringVectorToPath( const StringVector& input, char separator) { String result; for (size_t n = 0; n < input.size(); ++n) { if (n) result += separator; result += input[n]; } return result; } TEST(PathUtils, simplifyComponents) { static const struct { const ComponentList input; const ComponentList expected; } kData[] = { { { NULL }, { ".", NULL } }, { { ".", NULL }, { ".", NULL } }, { { "..", NULL }, { "..", NULL } }, { { "foo", NULL }, { "foo", NULL } }, { { "foo", ".", NULL }, { "foo", NULL } }, { { "foo", "bar", NULL }, { "foo", "bar", NULL } }, { { ".", "foo", ".", "bar", ".", NULL }, { "foo", "bar", NULL } }, { { "foo", "..", "bar", NULL }, { "bar", NULL } }, { { ".", "..", "foo", "bar", NULL }, { "..", "foo", "bar", NULL } }, { { "..", "foo", "..", "bar", NULL }, { "..", "bar", NULL } }, { { "foo", "..", "..", "bar", NULL }, { "..", "bar", NULL } }, }; for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { const ComponentList& input = kData[n].input; String inputPath = componentsToPath(input, '!'); String expectedPath = componentsToPath(kData[n].expected, '!'); StringVector components = componentListToVector(input); PathUtils::simplifyComponents(&components); String path = stringVectorToPath(components, '!'); EXPECT_STREQ(expectedPath.c_str(), path.c_str()) << "When simplifying " << inputPath.c_str(); } }; } // namespace android } // namespace base