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