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
21namespace android {
22namespace base {
23
24static const int kHostTypeCount = PathUtils::kHostTypeCount;
25
26TEST(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
52TEST(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
78TEST(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
118TEST(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
148static const int kMaxComponents = 10;
149
150typedef const char* ComponentList[kMaxComponents];
151
152static 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
168TEST(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
221static 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
229TEST(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.
259static 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
271static 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
283TEST(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