plptools
Loading...
Searching...
No Matches
pathutilstests.cc
Go to the documentation of this file.
1/*
2 * This file is part of plptools.
3 *
4 * Copyright (c) 2026 Jason Morley <hello@jbmorley.co.uk>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 *
19 */
20#include "config.h"
21
22#include <stdlib.h>
23#include <stdio.h>
24
25#include "doctest.h"
26#include "pathutils.h"
27
28using namespace pathutils;
29
30TEST_CASE("pathutils::epoc_basename") {
31 CHECK(epoc_basename("C:\\Random") == "Random");
32 CHECK(epoc_basename("C:\\Documents\\foo.txt") == "foo.txt");
33
34 // Legacy behavior (might be a bug).
35 CHECK(epoc_basename("C:\\Random\\") == "");
36 CHECK(epoc_basename("hello") == "hello");
37 CHECK(epoc_basename("") == "");
38}
39
40TEST_CASE("pathutils::epoc_dirname") {
41 SUBCASE("C:\\Random") {
42 std::string result;
43 result = epoc_dirname("C:\\Random");
44 CHECK(result == "C:\\");
45 }
46 SUBCASE("C:\\Random\\") {
47 std::string result;
48 result = epoc_dirname("C:\\Random\\");
49 CHECK(result == "C:\\");
50 }
51 SUBCASE("C:\\Documents\\foo.txt") {
52 std::string result;
53 result = epoc_dirname("C:\\Documents\\foo.txt");
54 CHECK(result == "C:\\Documents");
55 }
56 SUBCASE("C:\\") {
57 std::string result;
58 result = epoc_dirname("C:\\");
59 CHECK(result == "C:");
60 }
61
62 // Legacy behavior (might be a bug).
63 SUBCASE("C:") {
64 std::string result;
65 result = epoc_dirname("C:");
66 CHECK(result == "C");
67 }
68}
69
70TEST_CASE("pathutils::is_absolute") {
71
72 CHECK(is_absolute("/", PathFormat::kPOSIX) == true);
73 CHECK(is_absolute("\\", PathFormat::kWindows) == false);
74
75 CHECK(is_absolute("C:\\", PathFormat::kWindows) == true);
76 CHECK(is_absolute("C:\\", PathFormat::kPOSIX) == false);
77
78 CHECK(is_absolute("C:", PathFormat::kWindows) == false);
79 CHECK(is_absolute("C:", PathFormat::kPOSIX) == false);
80 CHECK(is_absolute("C:foo", PathFormat::kWindows) == false);
81 CHECK(is_absolute("C:foo", PathFormat::kPOSIX) == false);
82
83 CHECK(is_absolute("", PathFormat::kWindows) == false);
84 CHECK(is_absolute("", PathFormat::kPOSIX) == false);
85
86 CHECK(is_absolute("foo", PathFormat::kWindows) == false);
87 CHECK(is_absolute("foo", PathFormat::kPOSIX) == false);
88 CHECK(is_absolute("foo\\bar", PathFormat::kWindows) == false);
89 CHECK(is_absolute("foo/bar", PathFormat::kPOSIX) == false);
90
91 CHECK(is_absolute("\\C:\\", PathFormat::kWindows) == false);
92 CHECK(is_absolute("/C:/", PathFormat::kPOSIX) == true);
93}
94
95TEST_CASE("pathutils::appending_components") {
96 CHECK(appending_components("C:\\", {"Documents"}, PathFormat::kWindows) == "C:\\Documents");
97 CHECK(appending_components("C:\\", {"Documents"}, PathFormat::kWindows) == "C:\\Documents");
98 CHECK(appending_components("C:foo\\bar\\", {"baz"}, PathFormat::kWindows) == "C:foo\\bar\\baz");
99}
100
101TEST_CASE("pathutils::split") {
102 CHECK(split("", PathFormat::kWindows) == std::vector<std::string>({}));
103 CHECK(split(".", PathFormat::kWindows) == std::vector<std::string>({"."}));
104 CHECK(split(".\\", PathFormat::kWindows) == std::vector<std::string>({".", ""}));
105 CHECK(split("one\\two\\three", PathFormat::kWindows) == std::vector<std::string>({"one", "two", "three"}));
106 CHECK(split("one\\two\\three\\", PathFormat::kWindows) == std::vector<std::string>({"one", "two", "three", ""}));
107 CHECK(split("one\\two\\\\three\\", PathFormat::kWindows) == std::vector<std::string>({"one", "two", "three", ""}));
108 CHECK(split("C:\\one\\two\\\\three\\", PathFormat::kWindows) == std::vector<std::string>({"C:", "\\", "one", "two", "three", ""}));
109 CHECK(split("\\one\\two\\\\three\\", PathFormat::kWindows) == std::vector<std::string>({"\\", "one", "two", "three", ""}));
110 CHECK(split("C:\\one\\two\\\\three\\", PathFormat::kWindows) == std::vector<std::string>({"C:", "\\", "one", "two", "three", ""}));
111 CHECK(split("C:foo\\bar\\", PathFormat::kWindows) == std::vector<std::string>({"C:", "foo", "bar", ""}));
112 CHECK(split("C:\\", PathFormat::kWindows) == std::vector<std::string>({"C:", "\\"}));
113
114 CHECK(split("", PathFormat::kPOSIX) == std::vector<std::string>({}));
115 CHECK(split(".", PathFormat::kPOSIX) == std::vector<std::string>({"."}));
116 CHECK(split("./", PathFormat::kPOSIX) == std::vector<std::string>({".", ""}));
117 CHECK(split("one/two/three", PathFormat::kPOSIX) == std::vector<std::string>({"one", "two", "three"}));
118 CHECK(split("one/two/three/", PathFormat::kPOSIX) == std::vector<std::string>({"one", "two", "three", ""}));
119 CHECK(split("/two//three/", PathFormat::kPOSIX) == std::vector<std::string>({"/", "two", "three", ""}));
120 CHECK(split("/one/two/three/", PathFormat::kPOSIX) == std::vector<std::string>({"/", "one", "two", "three", ""}));
121 CHECK(split("/", PathFormat::kPOSIX) == std::vector<std::string>({"/"}));
122}
123
124TEST_CASE("pathutils::join") {
125 CHECK(join({}, PathFormat::kPOSIX) == "");
126 CHECK(join({"."}, PathFormat::kPOSIX) == ".");
127 CHECK(join({".", ""}, PathFormat::kPOSIX) == "./");
128 CHECK(join({""}, PathFormat::kPOSIX) == "./");
129 CHECK(join({"hello"}, PathFormat::kPOSIX) == "hello");
130 CHECK(join({"hello", "world"}, PathFormat::kPOSIX) == "hello/world");
131 CHECK(join({"/hello", "world"}, PathFormat::kPOSIX) == "/hello/world");
132 CHECK(join({"/hello/", "world"}, PathFormat::kPOSIX) == "/hello/world");
133 CHECK(join({"/", "hello", "world"}, PathFormat::kPOSIX) == "/hello/world");
134 CHECK(join({"..", ".."}, PathFormat::kPOSIX) == "../..");
135
136 CHECK(join({}, PathFormat::kWindows) == "");
137 CHECK(join({"."}, PathFormat::kWindows) == ".");
138 CHECK(join({".", ""}, PathFormat::kWindows) == ".\\");
139 CHECK(join({"C:", ""}, PathFormat::kWindows) == "C:.\\");
140 CHECK(join({"C:", ".", ""}, PathFormat::kWindows) == "C:.\\");
141 CHECK(join({"hello", "world"}, PathFormat::kWindows) == "hello\\world");
142 CHECK(join({"C:\\hello", "world"}, PathFormat::kWindows) == "C:\\hello\\world");
143 CHECK(join({"C:\\hello\\", "world"}, PathFormat::kWindows) == "C:\\hello\\world");
144 CHECK(join({"C:\\", "hello", "world"}, PathFormat::kWindows) == "C:\\hello\\world");
145 CHECK(join({"C:", "hello", "world"}, PathFormat::kWindows) == "C:hello\\world");
146 CHECK(join({"C:", "\\", "hello", "world"}, PathFormat::kWindows) == "C:\\hello\\world");
147 CHECK(join({"..", ".."}, PathFormat::kWindows) == "..\\..");
148
149 // Trailing directory marker.
150 CHECK(join({""}, PathFormat::kWindows) == ".\\");
151 CHECK(join({"\\", ""}, PathFormat::kWindows) == "\\");
152 CHECK(join({"C:", ""}, PathFormat::kWindows) == "C:.\\");
153 CHECK(join({"C:", "\\", ""}, PathFormat::kWindows) == "C:\\");
154 CHECK(join({"C:", "\\", "mnt\\", ""}, PathFormat::kWindows) == "C:\\mnt\\");
155 CHECK(join({"C:", "\\", "mnt", ""}, PathFormat::kWindows) == "C:\\mnt\\");
156 CHECK(join({""}, PathFormat::kPOSIX) == "./");
157 CHECK(join({"/", ""}, PathFormat::kPOSIX) == "/");
158 CHECK(join({"/", "mnt", ""}, PathFormat::kPOSIX) == "/mnt/");
159 CHECK(join({"/", "mnt/", ""}, PathFormat::kPOSIX) == "/mnt/");
160}
161
162TEST_CASE("pathutils::resolve_path") {
163
164 // Posix.
165
166 CHECK(resolve_path("/foo/bar/baz", "", PathFormat::kPOSIX) == "/foo/bar/baz");
167 CHECK(resolve_path("/foo/bar/baz", "/one/two", PathFormat::kPOSIX) == "/foo/bar/baz");
168
169 CHECK(resolve_path("", "/foo/bar/", PathFormat::kPOSIX) == "/foo/bar/");
170
171 CHECK(resolve_path("baz", "/foo/bar", PathFormat::kPOSIX) == "/foo/bar/baz");
172 CHECK(resolve_path("../baz", "/foo/bar", PathFormat::kPOSIX) == "/foo/baz");
173 CHECK(resolve_path("../..", "/foo", PathFormat::kPOSIX) == "/..");
174 CHECK(resolve_path("../../..", "/foo", PathFormat::kPOSIX) == "/../..");
175
176 CHECK(resolve_path("../../..", "foo", PathFormat::kPOSIX) == "../..");
177 CHECK(resolve_path("../../bar", "foo", PathFormat::kPOSIX) == "../bar");
178 CHECK(resolve_path("..", "bob", PathFormat::kPOSIX) == ".");
179 CHECK(resolve_path("../foo/../bar/../baz", "bob", PathFormat::kPOSIX) == "baz");
180
181 CHECK(resolve_path("", "", PathFormat::kPOSIX) == ".");
182 CHECK(resolve_path("./", "", PathFormat::kPOSIX) == "./");
183 CHECK(resolve_path("../", "foo", PathFormat::kPOSIX) == "./");
184
185 // Windows.
186
187 CHECK(resolve_path("C:\\foo\\bar\\baz", "", PathFormat::kWindows) == "C:\\foo\\bar\\baz");
188 CHECK(resolve_path("C:\\foo\\bar\\baz", "C:\\one\\two", PathFormat::kWindows) == "C:\\foo\\bar\\baz");
189
190 CHECK(resolve_path("baz", "C:\\foo\\bar", PathFormat::kWindows) == "C:\\foo\\bar\\baz");
191 CHECK(resolve_path("..\\baz", "D:\\foo\\bar", PathFormat::kWindows) == "D:\\foo\\baz");
192 CHECK(resolve_path("..\\..", "C:\\foo", PathFormat::kWindows) == "C:\\..");
193 CHECK(resolve_path("..\\..\\..", "C:\\foo", PathFormat::kWindows) == "C:\\..\\..");
194
195 CHECK(resolve_path("baz", "\\foo\\bar", PathFormat::kWindows) == "\\foo\\bar\\baz");
196 CHECK(resolve_path("..\\baz", "\\foo\\bar", PathFormat::kWindows) == "\\foo\\baz");
197 CHECK(resolve_path("..\\..", "\\foo", PathFormat::kWindows) == "\\..");
198 CHECK(resolve_path("..\\..\\..", "\\foo", PathFormat::kWindows) == "\\..\\..");
199
200 CHECK(resolve_path("baz", "\\foo\\bar\\", PathFormat::kWindows) == "\\foo\\bar\\baz");
201 CHECK(resolve_path("..\\baz", "\\foo\\bar\\", PathFormat::kWindows) == "\\foo\\baz");
202 CHECK(resolve_path("..\\..", "\\foo\\", PathFormat::kWindows) == "\\..");
203 CHECK(resolve_path("..\\..\\..", "\\foo\\", PathFormat::kWindows) == "\\..\\..");
204
205 CHECK(resolve_path("baz", "C:foo\\bar", PathFormat::kWindows) == "C:foo\\bar\\baz");
206 CHECK(resolve_path("..\\baz", "C:foo\\bar", PathFormat::kWindows) == "C:foo\\baz");
207 CHECK(resolve_path("..\\..", "C:foo", PathFormat::kWindows) == "C:..");
208 CHECK(resolve_path("..\\..\\..", "C:foo", PathFormat::kWindows) == "C:..\\..");
209
210 CHECK(resolve_path("..\\..\\..", "foo", PathFormat::kWindows) == "..\\..");
211 CHECK(resolve_path("..\\..\\bar", "foo", PathFormat::kWindows) == "..\\bar");
212 CHECK(resolve_path("..", "bob", PathFormat::kWindows) == ".");
213
214 CHECK(resolve_path("", "", PathFormat::kWindows) == ".");
215 CHECK(resolve_path(".\\", "", PathFormat::kWindows) == ".\\");
216 CHECK(resolve_path("..\\", "", PathFormat::kWindows) == "..\\");
217 CHECK(resolve_path("..\\", "foo", PathFormat::kWindows) == ".\\");
218
219 CHECK(resolve_path("C:\\foo\\bar", "D:\\baz", PathFormat::kWindows) == "C:\\foo\\bar");
220 CHECK(resolve_path("C:", "D:", PathFormat::kWindows) == "C:");
221 CHECK(resolve_path("\\foo\\bar", "D:\\baz", PathFormat::kWindows) == "D:\\foo\\bar");
222 CHECK(resolve_path("\\foo\\bar", "D:", PathFormat::kWindows) == "D:\\foo\\bar");
223 CHECK(resolve_path("C:\\foo\\bar", "D:\\baz", PathFormat::kWindows) == "C:\\foo\\bar");
224 CHECK(resolve_path("C:\\baz", "D:", PathFormat::kWindows) == "C:\\baz");
225}
#define SUBCASE(name)
Definition: doctest.h:2946
#define CHECK(...)
Definition: doctest.h:2970
#define TEST_CASE(name)
Definition: doctest.h:2937
Conveniences for working with paths.
Definition: pathutils.h:36
char * epoc_dirname(const char *path)
Compute parent directory of an EPOC directory.
Definition: pathutils.cc:91
std::vector< std::string > split(const std::string &path, const PathFormat format)
Split a path, path, into its components, using the path separator, separator.
Definition: pathutils.cc:142
std::string appending_components(const std::string &path, const std::vector< std::string > &components, const PathFormat format)
Convenience wrapper for join that returns a new path resulting from appending path components,...
Definition: pathutils.cc:215
std::string resolve_path(const std::string &path, const std::string &startingPath, const PathFormat format)
Returns a path by resolving a relative or absolute path against a starting path.
Definition: pathutils.cc:262
std::string epoc_basename(std::string path)
Returns the last path component of an EPOC path.
Definition: pathutils.cc:83
std::string join(const std::vector< std::string > &components, const PathFormat format)
Return a new path by joining the path components, components, with path separator,...
Definition: pathutils.cc:179
static bool is_absolute(const std::vector< std::string > &components, const pathutils::PathFormat format)
Definition: pathutils.cc:43