1 | //===- Testing/Support/SupportHelpers.h -----------------------------------===// |
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 | #ifndef LLVM_TESTING_SUPPORT_SUPPORTHELPERS_H |
10 | #define LLVM_TESTING_SUPPORT_SUPPORTHELPERS_H |
11 | |
12 | #include "llvm/ADT/SmallString.h" |
13 | #include "llvm/Support/Error.h" |
14 | #include "llvm/Support/FileSystem.h" |
15 | #include "llvm/Support/Path.h" |
16 | #include "llvm/Support/raw_os_ostream.h" |
17 | #include "gmock/gmock-matchers.h" |
18 | #include "gtest/gtest-printers.h" |
19 | |
20 | #include <optional> |
21 | #include <string> |
22 | |
23 | namespace llvm { |
24 | namespace detail { |
25 | struct ErrorHolder { |
26 | std::vector<std::shared_ptr<ErrorInfoBase>> Infos; |
27 | |
28 | bool Success() const { return Infos.empty(); } |
29 | }; |
30 | |
31 | template <typename T> struct ExpectedHolder : public ErrorHolder { |
32 | ExpectedHolder(ErrorHolder Err, Expected<T> &Exp) |
33 | : ErrorHolder(std::move(Err)), Exp(Exp) {} |
34 | |
35 | Expected<T> &Exp; |
36 | }; |
37 | |
38 | inline void PrintTo(const ErrorHolder &Err, std::ostream *Out) { |
39 | raw_os_ostream OS(*Out); |
40 | OS << (Err.Success() ? "succeeded" : "failed" ); |
41 | if (!Err.Success()) { |
42 | const char *Delim = " (" ; |
43 | for (const auto &Info : Err.Infos) { |
44 | OS << Delim; |
45 | Delim = "; " ; |
46 | Info->log(OS); |
47 | } |
48 | OS << ")" ; |
49 | } |
50 | } |
51 | |
52 | template <typename T> |
53 | void PrintTo(const ExpectedHolder<T> &Item, std::ostream *Out) { |
54 | if (Item.Success()) { |
55 | *Out << "succeeded with value " << ::testing::PrintToString(*Item.Exp); |
56 | } else { |
57 | PrintTo(Err: static_cast<const ErrorHolder &>(Item), Out); |
58 | } |
59 | } |
60 | |
61 | template <class InnerMatcher> class ValueIsMatcher { |
62 | public: |
63 | explicit ValueIsMatcher(InnerMatcher ValueMatcher) |
64 | : ValueMatcher(ValueMatcher) {} |
65 | |
66 | template <class T> |
67 | operator ::testing::Matcher<const std::optional<T> &>() const { |
68 | return ::testing::MakeMatcher( |
69 | new Impl<T>(::testing::SafeMatcherCast<T>(ValueMatcher))); |
70 | } |
71 | |
72 | template <class T, class O = std::optional<T>> |
73 | class Impl : public ::testing::MatcherInterface<const O &> { |
74 | public: |
75 | explicit Impl(const ::testing::Matcher<T> &ValueMatcher) |
76 | : ValueMatcher(ValueMatcher) {} |
77 | |
78 | bool MatchAndExplain(const O &Input, |
79 | testing::MatchResultListener *L) const override { |
80 | return Input && ValueMatcher.MatchAndExplain(*Input, L); |
81 | } |
82 | |
83 | void DescribeTo(std::ostream *OS) const override { |
84 | *OS << "has a value that " ; |
85 | ValueMatcher.DescribeTo(OS); |
86 | } |
87 | void DescribeNegationTo(std::ostream *OS) const override { |
88 | *OS << "does not have a value that " ; |
89 | ValueMatcher.DescribeTo(OS); |
90 | } |
91 | |
92 | private: |
93 | testing::Matcher<T> ValueMatcher; |
94 | }; |
95 | |
96 | private: |
97 | InnerMatcher ValueMatcher; |
98 | }; |
99 | } // namespace detail |
100 | |
101 | /// Matches an std::optional<T> with a value that conforms to an inner matcher. |
102 | /// To match std::nullopt you could use Eq(std::nullopt). |
103 | template <class InnerMatcher> |
104 | detail::ValueIsMatcher<InnerMatcher> ValueIs(const InnerMatcher &ValueMatcher) { |
105 | return detail::ValueIsMatcher<InnerMatcher>(ValueMatcher); |
106 | } |
107 | namespace unittest { |
108 | |
109 | SmallString<128> getInputFileDirectory(const char *Argv0); |
110 | |
111 | /// A RAII object that creates a temporary directory upon initialization and |
112 | /// removes it upon destruction. |
113 | class TempDir { |
114 | SmallString<128> Path; |
115 | |
116 | public: |
117 | /// Creates a managed temporary directory. |
118 | /// |
119 | /// @param Name The name of the directory to create. |
120 | /// @param Unique If true, the directory will be created using |
121 | /// llvm::sys::fs::createUniqueDirectory. |
122 | explicit TempDir(StringRef Name, bool Unique = false) { |
123 | std::error_code EC; |
124 | if (Unique) { |
125 | EC = llvm::sys::fs::createUniqueDirectory(Prefix: Name, ResultPath&: Path); |
126 | if (!EC) { |
127 | // Resolve any symlinks in the new directory. |
128 | std::string UnresolvedPath(Path.str()); |
129 | EC = llvm::sys::fs::real_path(path: UnresolvedPath, output&: Path); |
130 | } |
131 | } else { |
132 | Path = Name; |
133 | EC = llvm::sys::fs::create_directory(path: Path); |
134 | } |
135 | if (EC) |
136 | Path.clear(); |
137 | EXPECT_FALSE(EC) << EC.message(); |
138 | } |
139 | |
140 | ~TempDir() { |
141 | if (!Path.empty()) { |
142 | EXPECT_FALSE(llvm::sys::fs::remove_directories(Path.str())); |
143 | } |
144 | } |
145 | |
146 | TempDir(const TempDir &) = delete; |
147 | TempDir &operator=(const TempDir &) = delete; |
148 | |
149 | TempDir(TempDir &&) = default; |
150 | TempDir &operator=(TempDir &&) = default; |
151 | |
152 | /// The path to the temporary directory. |
153 | StringRef path() const { return Path; } |
154 | |
155 | /// The null-terminated C string pointing to the path. |
156 | const char *c_str() { return Path.c_str(); } |
157 | |
158 | /// Creates a new path by appending the argument to the path of the managed |
159 | /// directory using the native path separator. |
160 | SmallString<128> path(StringRef component) const { |
161 | SmallString<128> Result(Path); |
162 | SmallString<128> ComponentToAppend(component); |
163 | llvm::sys::path::native(path&: ComponentToAppend); |
164 | llvm::sys::path::append(path&: Result, a: Twine(ComponentToAppend)); |
165 | return Result; |
166 | } |
167 | }; |
168 | |
169 | /// A RAII object that creates a link upon initialization and |
170 | /// removes it upon destruction. |
171 | /// |
172 | /// The link may be a soft or a hard link, depending on the platform. |
173 | class TempLink { |
174 | SmallString<128> Path; |
175 | |
176 | public: |
177 | /// Creates a managed link at path Link pointing to Target. |
178 | TempLink(StringRef Target, StringRef Link) { |
179 | Path = Link; |
180 | std::error_code EC = sys::fs::create_link(to: Target, from: Link); |
181 | if (EC) |
182 | Path.clear(); |
183 | EXPECT_FALSE(EC); |
184 | } |
185 | ~TempLink() { |
186 | if (!Path.empty()) { |
187 | EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); |
188 | } |
189 | } |
190 | |
191 | TempLink(const TempLink &) = delete; |
192 | TempLink &operator=(const TempLink &) = delete; |
193 | |
194 | TempLink(TempLink &&) = default; |
195 | TempLink &operator=(TempLink &&) = default; |
196 | |
197 | /// The path to the link. |
198 | StringRef path() const { return Path; } |
199 | }; |
200 | |
201 | /// A RAII object that creates a file upon initialization and |
202 | /// removes it upon destruction. |
203 | class TempFile { |
204 | SmallString<128> Path; |
205 | |
206 | public: |
207 | /// Creates a managed file. |
208 | /// |
209 | /// @param Name The name of the file to create. |
210 | /// @param Contents The string to write to the file. |
211 | /// @param Unique If true, the file will be created using |
212 | /// llvm::sys::fs::createTemporaryFile. |
213 | TempFile(StringRef Name, StringRef Suffix = "" , StringRef Contents = "" , |
214 | bool Unique = false) { |
215 | std::error_code EC; |
216 | int fd; |
217 | if (Unique) { |
218 | EC = llvm::sys::fs::createTemporaryFile(Prefix: Name, Suffix, ResultFD&: fd, ResultPath&: Path); |
219 | } else { |
220 | Path = Name; |
221 | if (!Suffix.empty()) { |
222 | Path.append(RHS: "." ); |
223 | Path.append(RHS: Suffix); |
224 | } |
225 | EC = llvm::sys::fs::openFileForWrite(Name: Path, ResultFD&: fd); |
226 | } |
227 | EXPECT_FALSE(EC); |
228 | raw_fd_ostream OS(fd, /*shouldClose*/ true); |
229 | OS << Contents; |
230 | OS.flush(); |
231 | EXPECT_FALSE(OS.error()); |
232 | if (EC || OS.error()) |
233 | Path.clear(); |
234 | } |
235 | ~TempFile() { |
236 | if (!Path.empty()) { |
237 | EXPECT_FALSE(llvm::sys::fs::remove(Path.str())); |
238 | } |
239 | } |
240 | |
241 | TempFile(const TempFile &) = delete; |
242 | TempFile &operator=(const TempFile &) = delete; |
243 | |
244 | TempFile(TempFile &&) = default; |
245 | TempFile &operator=(TempFile &&) = default; |
246 | |
247 | /// The path to the file. |
248 | StringRef path() const { return Path; } |
249 | }; |
250 | |
251 | } // namespace unittest |
252 | } // namespace llvm |
253 | |
254 | #endif |
255 | |