1 // Copyright 2014 The Android Open Source Project 2 // 3 // This software is licensed under the terms of the GNU General Public 4 // License version 2, as published by the Free Software Foundation, and 5 // may be copied, distributed, and modified under those terms. 6 // 7 // This program is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 // GNU General Public License for more details. 11 12 #include "android/base/files/PathUtils.h" 13 14 #include "android/base/containers/StringVector.h" 15 #include "android/base/String.h" 16 17 #include <gtest/gtest.h> 18 19 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) 20 21 namespace android { 22 namespace base { 23 24 static const int kHostTypeCount = PathUtils::kHostTypeCount; 25 26 TEST(PathUtils, isDirSeparator) { 27 static const struct { 28 int ch; 29 bool expected[kHostTypeCount]; 30 } kData[] = { 31 { '/', { true, true }}, 32 { '\\', { false, true }}, 33 { '$', { false, false }}, 34 { ':', { false, false }}, 35 { ';', { false, false }}, 36 }; 37 38 for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { 39 int ch = kData[n].ch; 40 EXPECT_EQ(kData[n].expected[kHostPosix], 41 PathUtils::isDirSeparator(ch, kHostPosix)) 42 << "Testing '" << ch << "'"; 43 EXPECT_EQ(kData[n].expected[kHostWin32], 44 PathUtils::isDirSeparator(ch, kHostWin32)) 45 << "Testing '" << ch << "'"; 46 EXPECT_EQ(kData[n].expected[kHostType], 47 PathUtils::isDirSeparator(ch)) 48 << "Testing '" << ch << "'"; 49 } 50 } 51 52 TEST(PathUtils, isPathSeparator) { 53 static const struct { 54 int ch; 55 bool expected[kHostTypeCount]; 56 } kData[] = { 57 { ':', { true, false }}, 58 { ';', { false, true }}, 59 { '/', { false, false }}, 60 { '\\', { false, false }}, 61 { '$', { false, false }}, 62 }; 63 64 for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { 65 int ch = kData[n].ch; 66 EXPECT_EQ(kData[n].expected[kHostPosix], 67 PathUtils::isPathSeparator(ch, kHostPosix)) 68 << "Testing '" << ch << "'"; 69 EXPECT_EQ(kData[n].expected[kHostWin32], 70 PathUtils::isPathSeparator(ch, kHostWin32)) 71 << "Testing '" << ch << "'"; 72 EXPECT_EQ(kData[n].expected[kHostType], 73 PathUtils::isPathSeparator(ch)) 74 << "Testing '" << ch << "'"; 75 } 76 } 77 78 TEST(PathUtils, rootPrefixSize) { 79 static const struct { 80 const char* path; 81 size_t prefixSize[kHostTypeCount]; 82 } kData[] = { 83 { NULL, { 0u, 0u} }, 84 { "", { 0u, 0u } }, 85 { "foo", { 0u, 0u } }, 86 { "foo/bar", { 0u, 0u } }, 87 { "/foo", { 1u, 1u } }, 88 { "//foo", { 1u, 5u } }, 89 { "//foo/bar", { 1u, 6u } }, 90 { "c:", { 0u, 2u } }, 91 { "c:foo", { 0u, 2u } }, 92 { "c/foo", { 0u, 0u } }, 93 { "c:/foo", { 0u, 3u } }, 94 { "c:\\", { 0u, 3u } }, 95 { "c:\\\\", { 0u, 3u } }, 96 { "1:/foo", { 0u, 0u } }, 97 { "\\", { 0u, 1u } }, 98 { "\\foo", { 0u, 1u } }, 99 { "\\foo\\bar", { 0u, 1u } }, 100 { "\\\\foo", { 0u, 5u } }, 101 { "\\\\foo\\", { 0u, 6u } }, 102 { "\\\\foo\\\\bar", { 0u, 6u } }, 103 }; 104 for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { 105 const char* path = kData[n].path; 106 EXPECT_EQ(kData[n].prefixSize[kHostPosix], 107 PathUtils::rootPrefixSize(path, kHostPosix)) 108 << "Testing '" << (path ? path : "<NULL>") << "'"; 109 EXPECT_EQ(kData[n].prefixSize[kHostWin32], 110 PathUtils::rootPrefixSize(path, kHostWin32)) 111 << "Testing '" << (path ? path : "<NULL>") << "'"; 112 EXPECT_EQ(kData[n].prefixSize[kHostType], 113 PathUtils::rootPrefixSize(path)) 114 << "Testing '" << (path ? path : "<NULL>") << "'"; 115 } 116 } 117 118 TEST(PathUtils, isAbsolute) { 119 static const struct { 120 const char* path; 121 bool expected[kHostTypeCount]; 122 } kData[] = { 123 { "foo", { false, false } }, 124 { "/foo", { true, true } }, 125 { "\\foo", { false, true } }, 126 { "/foo/bar", { true, true } }, 127 { "\\foo\\bar", { false, true } }, 128 { "C:foo", { false, false } }, 129 { "C:/foo", { false, true } }, 130 { "C:\\foo", { false, true } }, 131 { "//server", { true, false } }, 132 { "//server/path", { true, true } }, 133 }; 134 for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { 135 const char* path = kData[n].path; 136 EXPECT_EQ(kData[n].expected[kHostPosix], 137 PathUtils::isAbsolute(path, kHostPosix)) 138 << "Testing '" << (path ? path : "<NULL>") << "'"; 139 EXPECT_EQ(kData[n].expected[kHostWin32], 140 PathUtils::isAbsolute(path, kHostWin32)) 141 << "Testing '" << (path ? path : "<NULL>") << "'"; 142 EXPECT_EQ(kData[n].expected[kHostType], 143 PathUtils::isAbsolute(path)) 144 << "Testing '" << (path ? path : "<NULL>") << "'"; 145 } 146 } 147 148 static const int kMaxComponents = 10; 149 150 typedef const char* ComponentList[kMaxComponents]; 151 152 static void checkComponents(const ComponentList& expected, 153 const StringVector& components, 154 const char* hostType, 155 const char* path) { 156 size_t m; 157 for (m = 0; m < components.size(); ++m) { 158 if (!expected[m]) 159 break; 160 const char* component = expected[m]; 161 EXPECT_STREQ(component, components[m].c_str()) 162 << hostType << " component #" << (m + 1) << " in " << path; 163 } 164 EXPECT_EQ(m, components.size()) 165 << hostType << " component #" << (m + 1) << " in " << path; 166 } 167 168 TEST(PathUtils, decompose) { 169 static const struct { 170 const char* path; 171 const ComponentList components[kHostTypeCount]; 172 } kData[] = { 173 { "", { { NULL }, { NULL } } }, 174 { "foo", { 175 { "foo", NULL }, 176 { "foo", NULL } } }, 177 { "foo/", { 178 { "foo", NULL }, 179 { "foo", NULL } } }, 180 { "foo/bar", { 181 { "foo", "bar", NULL }, 182 { "foo", "bar", NULL } } }, 183 { "foo//bar/zoo", { 184 { "foo", "bar", "zoo", NULL }, 185 { "foo", "bar", "zoo", NULL } } }, 186 { "\\foo\\bar\\", { 187 { "\\foo\\bar\\", NULL }, 188 { "\\", "foo", "bar", NULL } } }, 189 { "C:foo\\bar", { 190 { "C:foo\\bar", NULL }, 191 { "C:", "foo", "bar", NULL } } }, 192 { "C:/foo", { 193 { "C:", "foo", NULL }, 194 { "C:/", "foo", NULL } } }, 195 { "/foo", { 196 { "/", "foo", NULL }, 197 { "/", "foo", NULL } } }, 198 { "\\foo", { 199 { "\\foo", NULL }, 200 { "\\", "foo", NULL } } }, 201 }; 202 for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { 203 const char* path = kData[n].path; 204 checkComponents(kData[n].components[kHostPosix], 205 PathUtils::decompose(path, kHostPosix), 206 "posix", 207 path); 208 209 checkComponents(kData[n].components[kHostWin32], 210 PathUtils::decompose(path, kHostWin32), 211 "win32", 212 path); 213 214 checkComponents(kData[n].components[kHostType], 215 PathUtils::decompose(path), 216 "host", 217 path); 218 } 219 } 220 221 static StringVector componentListToVector( 222 const ComponentList& input) { 223 StringVector result; 224 for (size_t i = 0; input[i]; ++i) 225 result.push_back(input[i]); 226 return result; 227 } 228 229 TEST(PathUtils, recompose) { 230 static const struct { 231 const ComponentList input; 232 const char* path[kHostTypeCount]; 233 } kData[] = { 234 { { NULL }, { "", "" } }, 235 { { ".", NULL }, { ".", "." } }, 236 { { "..", NULL }, { "..", ".." } }, 237 { { "/", NULL }, { "/", "/" } }, 238 { { "/", "foo", NULL }, { "/foo", "/foo" } }, 239 { { "\\", "foo", NULL }, { "\\/foo", "\\foo" } }, 240 { { "foo", NULL }, { "foo", "foo" } }, 241 { { "foo", "bar", NULL }, { "foo/bar", "foo\\bar" } }, 242 { { ".", "foo", "..", NULL }, { "./foo/..", ".\\foo\\.." } }, 243 { { "C:", "foo", NULL }, { "C:/foo", "C:foo" } }, 244 }; 245 for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { 246 StringVector components = componentListToVector(kData[n].input); 247 EXPECT_STREQ(kData[n].path[kHostPosix], 248 PathUtils::recompose(components, kHostPosix).c_str()); 249 EXPECT_STREQ(kData[n].path[kHostWin32], 250 PathUtils::recompose(components, kHostWin32).c_str()); 251 EXPECT_STREQ(kData[n].path[kHostType], 252 PathUtils::recompose(components).c_str()); 253 } 254 } 255 256 257 // Convert a vector of strings |components| into a file path, using 258 // |separator| as the directory separator. 259 static String componentsToPath( 260 const ComponentList& components, 261 char separator) { 262 String result; 263 for (size_t n = 0; components[n]; ++n) { 264 if (n) 265 result += separator; 266 result += components[n]; 267 } 268 return result; 269 } 270 271 static String stringVectorToPath( 272 const StringVector& input, 273 char separator) { 274 String result; 275 for (size_t n = 0; n < input.size(); ++n) { 276 if (n) 277 result += separator; 278 result += input[n]; 279 } 280 return result; 281 } 282 283 TEST(PathUtils, simplifyComponents) { 284 static const struct { 285 const ComponentList input; 286 const ComponentList expected; 287 } kData[] = { 288 { { NULL }, { ".", NULL } }, 289 { { ".", NULL }, { ".", NULL } }, 290 { { "..", NULL }, { "..", NULL } }, 291 { { "foo", NULL }, { "foo", NULL } }, 292 { { "foo", ".", NULL }, { "foo", NULL } }, 293 { { "foo", "bar", NULL }, { "foo", "bar", NULL } }, 294 { { ".", "foo", ".", "bar", ".", NULL }, { "foo", "bar", NULL } }, 295 { { "foo", "..", "bar", NULL }, { "bar", NULL } }, 296 { { ".", "..", "foo", "bar", NULL }, { "..", "foo", "bar", NULL } }, 297 { { "..", "foo", "..", "bar", NULL }, { "..", "bar", NULL } }, 298 { { "foo", "..", "..", "bar", NULL }, { "..", "bar", NULL } }, 299 }; 300 for (size_t n = 0; n < ARRAY_SIZE(kData); ++n) { 301 const ComponentList& input = kData[n].input; 302 String inputPath = componentsToPath(input, '!'); 303 String expectedPath = componentsToPath(kData[n].expected, '!'); 304 StringVector components = componentListToVector(input); 305 PathUtils::simplifyComponents(&components); 306 String path = stringVectorToPath(components, '!'); 307 308 EXPECT_STREQ(expectedPath.c_str(), path.c_str()) 309 << "When simplifying " << inputPath.c_str(); 310 } 311 }; 312 313 } // namespace android 314 } // namespace base 315