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/Optional.h" 13 #include "llvm/ADT/SmallString.h" 14 #include "llvm/Support/Error.h" 15 #include "llvm/Support/FileSystem.h" 16 #include "llvm/Support/Path.h" 17 #include "llvm/Support/raw_os_ostream.h" 18 #include "gmock/gmock-matchers.h" 19 #include "gtest/gtest-printers.h" 20 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(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 llvm::Optional<T> &>() const { 68 return ::testing::MakeMatcher( 69 new Impl<T>(::testing::SafeMatcherCast<T>(ValueMatcher))); 70 } 71 72 template <class T> 73 class Impl : public ::testing::MatcherInterface<const llvm::Optional<T> &> { 74 public: 75 explicit Impl(const ::testing::Matcher<T> &ValueMatcher) 76 : ValueMatcher(ValueMatcher) {} 77 78 bool MatchAndExplain(const llvm::Optional<T> &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 llvm::Optional<T> with a value that conforms to an inner matcher. 102 /// To match llvm::None you could use Eq(llvm::None). 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(Name, 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(UnresolvedPath, Path); 130 } 131 } else { 132 Path = Name; 133 EC = llvm::sys::fs::create_directory(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(ComponentToAppend); 164 llvm::sys::path::append(Result, 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(Target, 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(Name, Suffix, fd, Path); 219 } else { 220 Path = Name; 221 if (!Suffix.empty()) { 222 Path.append("."); 223 Path.append(Suffix); 224 } 225 EC = llvm::sys::fs::openFileForWrite(Path, 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