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
TEST(PathUtils,isDirSeparator)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
TEST(PathUtils,isPathSeparator)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
TEST(PathUtils,rootPrefixSize)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
TEST(PathUtils,isAbsolute)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
checkComponents(const ComponentList & expected,const StringVector & components,const char * hostType,const char * path)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
TEST(PathUtils,decompose)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
componentListToVector(const ComponentList & input)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
TEST(PathUtils,recompose)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.
componentsToPath(const ComponentList & components,char 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
stringVectorToPath(const StringVector & input,char separator)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
TEST(PathUtils,simplifyComponents)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