xref: /aosp_15_r20/external/bazel-skylib/tests/paths_tests.bzl (revision bcb5dc7965af6ee42bf2f21341a2ec00233a8c8a)
1# Copyright 2017 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Unit tests for paths.bzl."""
16
17load("//lib:paths.bzl", "paths")
18load("//lib:unittest.bzl", "asserts", "unittest")
19
20def _basename_test(ctx):
21    """Unit tests for paths.basename."""
22    env = unittest.begin(ctx)
23
24    # Verify some degenerate cases.
25    asserts.equals(env, "", paths.basename(""))
26    asserts.equals(env, "", paths.basename("/"))
27    asserts.equals(env, "bar", paths.basename("foo///bar"))
28
29    # Verify some realistic cases.
30    asserts.equals(env, "foo", paths.basename("foo"))
31    asserts.equals(env, "foo", paths.basename("/foo"))
32    asserts.equals(env, "foo", paths.basename("bar/foo"))
33    asserts.equals(env, "foo", paths.basename("/bar/foo"))
34
35    # Verify that we correctly duplicate Python's os.path.basename behavior,
36    # where a trailing slash means the basename is empty.
37    asserts.equals(env, "", paths.basename("foo/"))
38    asserts.equals(env, "", paths.basename("/foo/"))
39
40    return unittest.end(env)
41
42basename_test = unittest.make(_basename_test)
43
44def _dirname_test(ctx):
45    """Unit tests for paths.dirname."""
46    env = unittest.begin(ctx)
47
48    # Verify some degenerate cases.
49    asserts.equals(env, "", paths.dirname(""))
50    asserts.equals(env, "/", paths.dirname("/"))
51    asserts.equals(env, "foo", paths.dirname("foo///bar"))
52
53    # Verify some realistic cases.
54    asserts.equals(env, "", paths.dirname("foo"))
55    asserts.equals(env, "/", paths.dirname("/foo"))
56    asserts.equals(env, "bar", paths.dirname("bar/foo"))
57    asserts.equals(env, "/bar", paths.dirname("/bar/foo"))
58
59    # Verify that we correctly duplicate Python's os.path.dirname behavior,
60    # where a trailing slash means the dirname is the same as the original
61    # path (without the trailing slash).
62    asserts.equals(env, "foo", paths.dirname("foo/"))
63    asserts.equals(env, "/foo", paths.dirname("/foo/"))
64
65    return unittest.end(env)
66
67dirname_test = unittest.make(_dirname_test)
68
69def _is_absolute_test(ctx):
70    """Unit tests for paths.is_absolute."""
71    env = unittest.begin(ctx)
72
73    # Try a degenerate case.
74    asserts.false(env, paths.is_absolute(""))
75
76    # Try some relative paths.
77    asserts.false(env, paths.is_absolute("foo"))
78    asserts.false(env, paths.is_absolute("foo/"))
79    asserts.false(env, paths.is_absolute("foo/bar"))
80
81    # Try some Linux absolute paths.
82    asserts.true(env, paths.is_absolute("/"))
83    asserts.true(env, paths.is_absolute("/foo"))
84    asserts.true(env, paths.is_absolute("/foo/"))
85    asserts.true(env, paths.is_absolute("/foo/bar"))
86
87    # Try some Windows absolute paths.
88    asserts.true(env, paths.is_absolute("D:\\"))
89    asserts.true(env, paths.is_absolute("C:\\"))
90    asserts.true(env, paths.is_absolute("C:\\foo"))
91    asserts.true(env, paths.is_absolute("C:\\foo\\bar"))
92
93    return unittest.end(env)
94
95is_absolute_test = unittest.make(_is_absolute_test)
96
97def _join_test(ctx):
98    """Unit tests for paths.join."""
99    env = unittest.begin(ctx)
100
101    # Try a degenerate case.
102    asserts.equals(env, "", paths.join(""))
103
104    # Try some basic paths.
105    asserts.equals(env, "foo", paths.join("foo"))
106    asserts.equals(env, "foo/bar", paths.join("foo", "bar"))
107    asserts.equals(env, "foo/bar/baz", paths.join("foo", "bar", "baz"))
108
109    # Make sure an initially absolute path stays absolute.
110    asserts.equals(env, "/foo", paths.join("/foo"))
111    asserts.equals(env, "/foo/bar", paths.join("/foo", "bar"))
112
113    # Make sure an absolute path later in the list resets the result.
114    asserts.equals(env, "/baz", paths.join("foo", "bar", "/baz"))
115    asserts.equals(env, "/baz", paths.join("foo", "/bar", "/baz"))
116    asserts.equals(env, "/bar/baz", paths.join("foo", "/bar", "baz"))
117    asserts.equals(env, "/bar", paths.join("/foo", "/bar"))
118
119    # Make sure a leading empty segment doesn't make it absolute.
120    asserts.equals(env, "foo", paths.join("", "foo"))
121
122    # Try some trailing slash scenarios.
123    asserts.equals(env, "foo/", paths.join("foo", ""))
124    asserts.equals(env, "foo/", paths.join("foo/"))
125    asserts.equals(env, "foo/", paths.join("foo/", ""))
126    asserts.equals(env, "foo//", paths.join("foo//", ""))
127    asserts.equals(env, "foo//", paths.join("foo//"))
128    asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz", ""))
129    asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz/"))
130    asserts.equals(env, "foo/bar/baz/", paths.join("foo/", "bar/", "baz/", ""))
131
132    # Make sure that adjacent empty segments don't add extra path separators.
133    asserts.equals(env, "foo/", paths.join("foo", "", ""))
134    asserts.equals(env, "foo", paths.join("", "", "foo"))
135    asserts.equals(env, "foo/bar", paths.join("foo", "", "", "bar"))
136
137    return unittest.end(env)
138
139join_test = unittest.make(_join_test)
140
141def _normalize_test(ctx):
142    """Unit tests for paths.normalize."""
143    env = unittest.begin(ctx)
144
145    # Try the most basic case.
146    asserts.equals(env, ".", paths.normalize(""))
147
148    # Try some basic adjacent-slash removal.
149    asserts.equals(env, "foo/bar", paths.normalize("foo//bar"))
150    asserts.equals(env, "foo/bar", paths.normalize("foo////bar"))
151
152    # Try some "." removal.
153    asserts.equals(env, "foo/bar", paths.normalize("foo/./bar"))
154    asserts.equals(env, "foo/bar", paths.normalize("./foo/bar"))
155    asserts.equals(env, "foo/bar", paths.normalize("foo/bar/."))
156    asserts.equals(env, "/", paths.normalize("/."))
157
158    # Try some ".." removal.
159    asserts.equals(env, "bar", paths.normalize("foo/../bar"))
160    asserts.equals(env, "foo", paths.normalize("foo/bar/.."))
161    asserts.equals(env, ".", paths.normalize("foo/.."))
162    asserts.equals(env, ".", paths.normalize("foo/bar/../.."))
163    asserts.equals(env, "..", paths.normalize("foo/../.."))
164    asserts.equals(env, "/", paths.normalize("/foo/../.."))
165    asserts.equals(env, "../../c", paths.normalize("a/b/../../../../c/d/.."))
166
167    # Make sure one or two initial slashes are preserved, but three or more are
168    # collapsed to a single slash.
169    asserts.equals(env, "/foo", paths.normalize("/foo"))
170    asserts.equals(env, "//foo", paths.normalize("//foo"))
171    asserts.equals(env, "/foo", paths.normalize("///foo"))
172
173    # Trailing slashes should be removed unless the entire path is a trailing
174    # slash.
175    asserts.equals(env, "/", paths.normalize("/"))
176    asserts.equals(env, "foo", paths.normalize("foo/"))
177    asserts.equals(env, "foo/bar", paths.normalize("foo/bar/"))
178
179    return unittest.end(env)
180
181normalize_test = unittest.make(_normalize_test)
182
183def _is_normalized_test(ctx):
184    """Unit tests for paths.is_normalized."""
185    env = unittest.begin(ctx)
186
187    # Try the most basic cases.
188    asserts.true(env, paths.is_normalized(""))
189    asserts.false(env, paths.is_normalized("."))
190    asserts.true(env, paths.is_normalized("/"))
191    asserts.true(env, paths.is_normalized("/tmp"))
192    asserts.true(env, paths.is_normalized("tmp"))
193    asserts.true(env, paths.is_normalized("c:/"))
194    asserts.false(env, paths.is_normalized("../a"))
195    asserts.false(env, paths.is_normalized("a/.."))
196
197    # Try some basic adjacent-slash removal.
198    asserts.true(env, paths.is_normalized("foo//bar"))
199    asserts.true(env, paths.is_normalized("foo////bar"))
200
201    # Try some "." removal.
202    asserts.false(env, paths.is_normalized("foo/./bar"))
203    asserts.false(env, paths.is_normalized("./foo/bar"))
204    asserts.false(env, paths.is_normalized("foo/bar/."))
205    asserts.false(env, paths.is_normalized("/."))
206
207    # Try some ".." removal.
208    asserts.false(env, paths.is_normalized("foo/../bar"))
209    asserts.false(env, paths.is_normalized("foo/bar/.."))
210    asserts.false(env, paths.is_normalized("foo/.."))
211    asserts.false(env, paths.is_normalized("foo/bar/../.."))
212    asserts.false(env, paths.is_normalized("foo/../.."))
213    asserts.false(env, paths.is_normalized("/foo/../.."))
214    asserts.false(env, paths.is_normalized("a/b/../../../../c/d/.."))
215
216    # Make sure one or two initial slashes are preserved, but three or more are
217    # collapsed to a single slash.
218    asserts.true(env, paths.is_normalized("/foo"))
219    asserts.true(env, paths.is_normalized("//foo"))
220    asserts.true(env, paths.is_normalized("///foo"))
221
222    # Trailing slashes should be removed unless the entire path is a trailing
223    # slash.
224    asserts.true(env, paths.is_normalized("/"))
225    asserts.true(env, paths.is_normalized("foo/"))
226    asserts.true(env, paths.is_normalized("foo/bar/"))
227
228    return unittest.end(env)
229
230is_normalized_test = unittest.make(_is_normalized_test)
231
232def _relativize_test(ctx):
233    """Unit tests for paths.relativize."""
234    env = unittest.begin(ctx)
235
236    # Make sure that relative-to-current-directory works in all forms.
237    asserts.equals(env, "foo", paths.relativize("foo", ""))
238    asserts.equals(env, "foo", paths.relativize("foo", "."))
239
240    # Try some regular cases.
241    asserts.equals(env, "bar", paths.relativize("foo/bar", "foo"))
242    asserts.equals(env, "baz", paths.relativize("foo/bar/baz", "foo/bar"))
243    asserts.equals(env, "bar/baz", paths.relativize("foo/bar/baz", "foo"))
244
245    # Try a case where a parent directory is normalized away.
246    asserts.equals(env, "baz", paths.relativize("foo/bar/../baz", "foo"))
247
248    # Relative paths work, as long as they share a common start.
249    asserts.equals(env, "file", paths.relativize("../foo/bar/baz/file", "../foo/bar/baz"))
250    asserts.equals(env, "baz/file", paths.relativize("../foo/bar/baz/file", "../foo/bar"))
251
252    # TODO(allevato): Test failure cases, once that is possible.
253
254    return unittest.end(env)
255
256relativize_test = unittest.make(_relativize_test)
257
258def _replace_extension_test(ctx):
259    """Unit tests for paths.replace_extension."""
260    env = unittest.begin(ctx)
261
262    # Try some degenerate cases.
263    asserts.equals(env, ".foo", paths.replace_extension("", ".foo"))
264    asserts.equals(env, "/.foo", paths.replace_extension("/", ".foo"))
265    asserts.equals(env, "foo.bar", paths.replace_extension("foo", ".bar"))
266
267    # Try a directory with an extension and basename that doesn't have one.
268    asserts.equals(
269        env,
270        "foo.bar/baz.quux",
271        paths.replace_extension("foo.bar/baz", ".quux"),
272    )
273
274    # Now try some things with legit extensions.
275    asserts.equals(env, "a.z", paths.replace_extension("a.b", ".z"))
276    asserts.equals(env, "a.b.z", paths.replace_extension("a.b.c", ".z"))
277    asserts.equals(env, "a/b.z", paths.replace_extension("a/b.c", ".z"))
278    asserts.equals(env, "a.b/c.z", paths.replace_extension("a.b/c.d", ".z"))
279    asserts.equals(env, ".a/b.z", paths.replace_extension(".a/b.c", ".z"))
280    asserts.equals(env, ".a.z", paths.replace_extension(".a.b", ".z"))
281
282    # Verify that we don't insert a period on the extension if none is provided.
283    asserts.equals(env, "foobaz", paths.replace_extension("foo.bar", "baz"))
284
285    return unittest.end(env)
286
287replace_extension_test = unittest.make(_replace_extension_test)
288
289def _split_extension_test(ctx):
290    """Unit tests for paths.split_extension."""
291    env = unittest.begin(ctx)
292
293    # Try some degenerate cases.
294    asserts.equals(env, ("", ""), paths.split_extension(""))
295    asserts.equals(env, ("/", ""), paths.split_extension("/"))
296    asserts.equals(env, ("foo", ""), paths.split_extension("foo"))
297
298    # Try some paths whose basenames start with ".".
299    asserts.equals(env, (".", ""), paths.split_extension("."))
300    asserts.equals(env, (".bashrc", ""), paths.split_extension(".bashrc"))
301    asserts.equals(env, ("foo/.bashrc", ""), paths.split_extension("foo/.bashrc"))
302    asserts.equals(
303        env,
304        (".foo/.bashrc", ""),
305        paths.split_extension(".foo/.bashrc"),
306    )
307
308    # Try some directories with extensions with basenames that don't have one.
309    asserts.equals(env, ("foo.bar/baz", ""), paths.split_extension("foo.bar/baz"))
310    asserts.equals(
311        env,
312        ("foo.bar/.bashrc", ""),
313        paths.split_extension("foo.bar/.bashrc"),
314    )
315
316    # Now try some things that will actually get split.
317    asserts.equals(env, ("a", ".b"), paths.split_extension("a.b"))
318    asserts.equals(env, ("a.b", ".c"), paths.split_extension("a.b.c"))
319    asserts.equals(env, ("a/b", ".c"), paths.split_extension("a/b.c"))
320    asserts.equals(env, ("a.b/c", ".d"), paths.split_extension("a.b/c.d"))
321    asserts.equals(env, (".a/b", ".c"), paths.split_extension(".a/b.c"))
322    asserts.equals(env, (".a", ".b"), paths.split_extension(".a.b"))
323
324    return unittest.end(env)
325
326split_extension_test = unittest.make(_split_extension_test)
327
328def _starts_with_test(ctx):
329    """Unit tests for paths.starts_with."""
330    env = unittest.begin(ctx)
331
332    # Make sure that relative-to-current-directory works in all forms.
333    asserts.true(env, paths.starts_with("foo", ""))
334    asserts.false(env, paths.starts_with("foo", "."))
335
336    # Try some regular cases.
337    asserts.true(env, paths.starts_with("foo/bar", "foo"))
338    asserts.false(env, paths.starts_with("foo/bar", "fo"))
339    asserts.true(env, paths.starts_with("foo/bar/baz", "foo/bar"))
340    asserts.true(env, paths.starts_with("foo/bar/baz", "foo"))
341
342    # Try a case where a parent directory is normalized away.
343    asserts.true(env, paths.starts_with("foo/bar/../baz", "foo"))
344
345    # Relative paths work, as long as they share a common start.
346    asserts.true(env, paths.starts_with("../foo/bar/baz/file", "../foo/bar/baz"))
347    asserts.true(env, paths.starts_with("../foo/bar/baz/file", "../foo/bar"))
348
349    return unittest.end(env)
350
351starts_with_test = unittest.make(_starts_with_test)
352
353def paths_test_suite():
354    """Creates the test targets and test suite for paths.bzl tests."""
355    unittest.suite(
356        "paths_tests",
357        basename_test,
358        dirname_test,
359        is_absolute_test,
360        join_test,
361        normalize_test,
362        is_normalized_test,
363        relativize_test,
364        replace_extension_test,
365        split_extension_test,
366        starts_with_test,
367    )
368