TestUtil.h 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * Copyright 2012-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #pragma once
  17. #include <map>
  18. #include <string>
  19. #include <folly/Range.h>
  20. #include <folly/ScopeGuard.h>
  21. #include <folly/experimental/io/FsUtil.h>
  22. namespace folly {
  23. namespace test {
  24. /**
  25. * Temporary file.
  26. *
  27. * By default, the file is created in a system-specific location (the value
  28. * of the TMPDIR environment variable, or /tmp), but you can override that
  29. * with a different (non-empty) directory passed to the constructor.
  30. *
  31. * By default, the file is closed and deleted when the TemporaryFile object
  32. * is destroyed, but both these behaviors can be overridden with arguments
  33. * to the constructor.
  34. */
  35. class TemporaryFile {
  36. public:
  37. enum class Scope {
  38. PERMANENT,
  39. UNLINK_IMMEDIATELY,
  40. UNLINK_ON_DESTRUCTION,
  41. };
  42. explicit TemporaryFile(
  43. StringPiece namePrefix = StringPiece(),
  44. fs::path dir = fs::path(),
  45. Scope scope = Scope::UNLINK_ON_DESTRUCTION,
  46. bool closeOnDestruction = true);
  47. ~TemporaryFile();
  48. // Movable, but not copyable
  49. TemporaryFile(TemporaryFile&& other) noexcept {
  50. assign(other);
  51. }
  52. TemporaryFile& operator=(TemporaryFile&& other) {
  53. if (this != &other) {
  54. reset();
  55. assign(other);
  56. }
  57. return *this;
  58. }
  59. void close();
  60. int fd() const {
  61. return fd_;
  62. }
  63. const fs::path& path() const;
  64. void reset();
  65. private:
  66. Scope scope_;
  67. bool closeOnDestruction_;
  68. int fd_;
  69. fs::path path_;
  70. void assign(TemporaryFile& other) {
  71. scope_ = other.scope_;
  72. closeOnDestruction_ = other.closeOnDestruction_;
  73. fd_ = std::exchange(other.fd_, -1);
  74. path_ = other.path_;
  75. }
  76. };
  77. /**
  78. * Temporary directory.
  79. *
  80. * By default, the temporary directory is created in a system-specific
  81. * location (the value of the TMPDIR environment variable, or /tmp), but you
  82. * can override that with a non-empty directory passed to the constructor.
  83. *
  84. * By default, the directory is recursively deleted when the TemporaryDirectory
  85. * object is destroyed, but that can be overridden with an argument
  86. * to the constructor.
  87. */
  88. class TemporaryDirectory {
  89. public:
  90. enum class Scope {
  91. PERMANENT,
  92. DELETE_ON_DESTRUCTION,
  93. };
  94. explicit TemporaryDirectory(
  95. StringPiece namePrefix = StringPiece(),
  96. fs::path dir = fs::path(),
  97. Scope scope = Scope::DELETE_ON_DESTRUCTION);
  98. ~TemporaryDirectory();
  99. // Movable, but not copiable
  100. TemporaryDirectory(TemporaryDirectory&&) = default;
  101. TemporaryDirectory& operator=(TemporaryDirectory&&) = default;
  102. const fs::path& path() const {
  103. return *path_;
  104. }
  105. private:
  106. Scope scope_;
  107. std::unique_ptr<fs::path> path_;
  108. };
  109. /**
  110. * Changes into a temporary directory, and deletes it with all its contents
  111. * upon destruction, also changing back to the original working directory.
  112. */
  113. class ChangeToTempDir {
  114. public:
  115. ChangeToTempDir();
  116. ~ChangeToTempDir();
  117. // Movable, but not copiable
  118. ChangeToTempDir(ChangeToTempDir&&) = default;
  119. ChangeToTempDir& operator=(ChangeToTempDir&&) = default;
  120. const fs::path& path() const {
  121. return dir_.path();
  122. }
  123. private:
  124. TemporaryDirectory dir_;
  125. fs::path orig_;
  126. };
  127. namespace detail {
  128. struct SavedState {
  129. void* previousThreadLocalHandler;
  130. int previousCrtReportMode;
  131. };
  132. SavedState disableInvalidParameters();
  133. void enableInvalidParameters(SavedState state);
  134. } // namespace detail
  135. // Ok, so fun fact: The CRT on windows will actually abort
  136. // on certain failed parameter validation checks in debug
  137. // mode rather than simply returning -1 as it does in release
  138. // mode. We can however, ensure consistent behavior by
  139. // registering our own thread-local invalid parameter handler
  140. // for the duration of the call, and just have that handler
  141. // immediately return. We also have to disable CRT asertion
  142. // alerts for the duration of the call, otherwise we get
  143. // the abort-retry-ignore window.
  144. template <typename Func>
  145. auto msvcSuppressAbortOnInvalidParams(Func func) -> decltype(func()) {
  146. auto savedState = detail::disableInvalidParameters();
  147. SCOPE_EXIT {
  148. detail::enableInvalidParameters(savedState);
  149. };
  150. return func();
  151. }
  152. /**
  153. * Easy PCRE regex matching. Note that pattern must match the ENTIRE target,
  154. * so use .* at the start and end of the pattern, as appropriate. See
  155. * http://regex101.com/ for a PCRE simulator.
  156. */
  157. #define EXPECT_PCRE_MATCH(pattern_stringpiece, target_stringpiece) \
  158. EXPECT_PRED2( \
  159. ::folly::test::detail::hasPCREPatternMatch, \
  160. pattern_stringpiece, \
  161. target_stringpiece)
  162. #define EXPECT_NO_PCRE_MATCH(pattern_stringpiece, target_stringpiece) \
  163. EXPECT_PRED2( \
  164. ::folly::test::detail::hasNoPCREPatternMatch, \
  165. pattern_stringpiece, \
  166. target_stringpiece)
  167. namespace detail {
  168. bool hasPCREPatternMatch(StringPiece pattern, StringPiece target);
  169. bool hasNoPCREPatternMatch(StringPiece pattern, StringPiece target);
  170. } // namespace detail
  171. /**
  172. * Use these patterns together with CaptureFD and EXPECT_PCRE_MATCH() to
  173. * test for the presence (or absence) of log lines at a particular level:
  174. *
  175. * CaptureFD stderr(2);
  176. * LOG(INFO) << "All is well";
  177. * EXPECT_NO_PCRE_MATCH(glogErrOrWarnPattern(), stderr.readIncremental());
  178. * LOG(ERROR) << "Uh-oh";
  179. * EXPECT_PCRE_MATCH(glogErrorPattern(), stderr.readIncremental());
  180. */
  181. inline std::string glogErrorPattern() {
  182. return ".*(^|\n)E[0-9].*";
  183. }
  184. inline std::string glogWarningPattern() {
  185. return ".*(^|\n)W[0-9].*";
  186. }
  187. // Error OR warning
  188. inline std::string glogErrOrWarnPattern() {
  189. return ".*(^|\n)[EW][0-9].*";
  190. }
  191. /**
  192. * Temporarily capture a file descriptor by redirecting it into a file.
  193. * You can consume its entire output thus far via read(), incrementally
  194. * via readIncremental(), or via callback using chunk_cob.
  195. * Great for testing logging (see also glog*Pattern()).
  196. */
  197. class CaptureFD {
  198. private:
  199. struct NoOpChunkCob {
  200. void operator()(StringPiece) {}
  201. };
  202. public:
  203. using ChunkCob = std::function<void(folly::StringPiece)>;
  204. /**
  205. * chunk_cob is is guaranteed to consume all the captured output. It is
  206. * invoked on each readIncremental(), and also on FD release to capture
  207. * as-yet unread lines. Chunks can be empty.
  208. */
  209. explicit CaptureFD(int fd, ChunkCob chunk_cob = NoOpChunkCob());
  210. ~CaptureFD();
  211. /**
  212. * Restore the captured FD to its original state. It can be useful to do
  213. * this before the destructor so that you can read() the captured data and
  214. * log about it to the formerly captured stderr or stdout.
  215. */
  216. void release();
  217. /**
  218. * Reads the whole file into a string, but does not remove the redirect.
  219. */
  220. std::string read() const;
  221. /**
  222. * Read any bytes that were appended to the file since the last
  223. * readIncremental. Great for testing line-by-line output.
  224. */
  225. std::string readIncremental();
  226. private:
  227. ChunkCob chunkCob_;
  228. TemporaryFile file_;
  229. int fd_;
  230. int oldFDCopy_; // equal to fd_ after restore()
  231. off_t readOffset_; // for incremental reading
  232. };
  233. } // namespace test
  234. } // namespace folly