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