1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9// UNSUPPORTED: c++03, c++11, c++14
10// UNSUPPORTED: availability-filesystem-missing
11
12// <filesystem>
13
14// class path
15
16// path lexically_normal() const;
17
18#include <filesystem>
19#include <string>
20
21#include "../../path_helper.h"
22#include "count_new.h"
23#include "test_macros.h"
24#include "assert_macros.h"
25#include "concat_macros.h"
26namespace fs = std::filesystem;
27
28int main(int, char**) {
29 // clang-format off
30 struct {
31 std::string input;
32 std::string expect;
33 } TestCases[] = {
34 {.input: "", .expect: ""},
35 {.input: "/a/b/c", .expect: "/a/b/c"},
36 {.input: "/a/b//c", .expect: "/a/b/c"},
37 {.input: "foo/./bar/..", .expect: "foo/"},
38 {.input: "foo/.///bar/../", .expect: "foo/"},
39 {.input: "/a/b/", .expect: "/a/b/"},
40 {.input: "a/b", .expect: "a/b"},
41 {.input: "a/b/.", .expect: "a/b/"},
42 {.input: "a/b/./", .expect: "a/b/"},
43 {.input: "a/..", .expect: "."},
44 {.input: ".", .expect: "."},
45 {.input: "./", .expect: "."},
46 {.input: "./.", .expect: "."},
47 {.input: "./..", .expect: ".."},
48 {.input: "..", .expect: ".."},
49 {.input: "../..", .expect: "../.."},
50 {.input: "/../", .expect: "/"},
51 {.input: "/../..", .expect: "/"},
52 {.input: "/../../", .expect: "/"},
53 {.input: "..", .expect: ".."},
54 {.input: "../", .expect: ".."},
55 {.input: "/a/b/c/../", .expect: "/a/b/"},
56 {.input: "/a/b/./", .expect: "/a/b/"},
57 {.input: "/a/b/c/../d", .expect: "/a/b/d"},
58 {.input: "/a/b/c/../d/", .expect: "/a/b/d/"},
59#ifdef _WIN32
60 {"//a/", "//a/"},
61 {"//a/b/", "//a/b/"},
62 {"//a/b/.", "//a/b/"},
63 {"//a/..", "//a/"},
64#else
65 {.input: "//a/", .expect: "/a/"},
66 {.input: "//a/b/", .expect: "/a/b/"},
67 {.input: "//a/b/.", .expect: "/a/b/"},
68 {.input: "//a/..", .expect: "/"},
69#endif
70 ///===---------------------------------------------------------------===//
71 /// Tests specifically for the clauses under [fs.path.generic]p6
72 ///===---------------------------------------------------------------===//
73 // p1: If the path is empty, stop.
74 {.input: "", .expect: ""},
75 // p2: Replace each slash character in the root-name with a preferred
76 // separator.
77 {.input: "NO_ROOT_NAME_ON_LINUX", .expect: "NO_ROOT_NAME_ON_LINUX"},
78 // p3: Replace each directory-separator with a preferred-separator.
79 // [ Note: The generic pathname grammar ([fs.path.generic]) defines
80 // directory-separator as one or more slashes and preferred-separators.
81 // - end note ]
82 {.input: "/", .expect: "/"},
83 {.input: "//", .expect: "/"},
84 {.input: "///", .expect: "/"},
85 {.input: "a/b", .expect: "a/b"},
86 {.input: "a//b", .expect: "a/b"},
87 {.input: "a///b", .expect: "a/b"},
88 {.input: "a/b/", .expect: "a/b/"},
89 {.input: "a/b//", .expect: "a/b/"},
90 {.input: "a/b///", .expect: "a/b/"},
91 {.input: "///a////b//////", .expect: "/a/b/"},
92 // p4: Remove each dot filename and any immediately following directory
93 // separators
94 {.input: "foo/.", .expect: "foo/"},
95 {.input: "foo/./bar/.", .expect: "foo/bar/"},
96 {.input: "./foo/././bar/./", .expect: "foo/bar/"},
97 {.input: ".///foo//.////./bar/.///", .expect: "foo/bar/"},
98 // p5: As long as any appear, remove a non-dot-dot filename immediately
99 // followed by a directory-separator and a dot-dot filename, along with
100 // any immediately following directory separator.
101 {.input: "foo/..", .expect: "."},
102 {.input: "foo/../", .expect: "."},
103 {.input: "foo/bar/..", .expect: "foo/"},
104 {.input: "foo/bar/../", .expect: "foo/"},
105 {.input: "foo/bar/../..", .expect: "."},
106 {.input: "foo/bar/../../", .expect: "."},
107 {.input: "foo/bar/baz/../..", .expect: "foo/"},
108 {.input: "foo/bar/baz/../../", .expect: "foo/"},
109 {.input: "foo/bar/./..", .expect: "foo/"},
110 {.input: "foo/bar/./../", .expect: "foo/"},
111 // p6: If there is a root-directory, remove all dot-dot filenames and any
112 // directory-separators immediately following them. [ Note: These dot-dot
113 // filenames attempt to refer to nonexistent parent directories. - end note ]
114 {.input: "/..", .expect: "/"},
115 {.input: "/../", .expect: "/"},
116 {.input: "/foo/../..", .expect: "/"},
117 {.input: "/../foo", .expect: "/foo"},
118 {.input: "/../foo/../..", .expect: "/"},
119 // p7: If the last filename is dot-dot, remove any trailing
120 // directory-separator.
121 {.input: "../", .expect: ".."},
122 {.input: "../../", .expect: "../.."},
123 {.input: "foo/../bar/../..///", .expect: ".."},
124 {.input: "foo/../bar/..//..///../", .expect: "../.."},
125 // p8: If the path is empty, add a dot
126 {.input: ".", .expect: "."},
127 {.input: "./", .expect: "."},
128 {.input: "foo/..", .expect: "."}
129 };
130 // clang-format on
131 for (auto& TC : TestCases) {
132 fs::path p(TC.input);
133 const fs::path output = p.lexically_normal();
134 fs::path expect(TC.expect);
135 expect.make_preferred();
136
137 TEST_REQUIRE(
138 PathEq(LHS: output, RHS: expect),
139 TEST_WRITE_CONCATENATED("Input: ", TC.input, "\nExpected: ", expect.string(), "\nOutput: ", output.string()));
140 }
141 return 0;
142}
143

source code of libcxx/test/std/input.output/filesystems/class.path/path.member/path.gen/lexically_normal.pass.cpp