EnvVarSaver.
[folly.git] / folly / experimental / TestUtil.h
1 /*
2  * Copyright 2015 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 #ifndef FOLLY_TESTUTIL_H_
18 #define FOLLY_TESTUTIL_H_
19
20 #include <map>
21 #include <string>
22 #include <folly/Range.h>
23 #include <folly/experimental/io/FsUtil.h>
24
25 namespace folly {
26 namespace test {
27
28 /**
29  * Temporary file.
30  *
31  * By default, the file is created in a system-specific location (the value
32  * of the TMPDIR environment variable, or /tmp), but you can override that
33  * with a different (non-empty) directory passed to the constructor.
34  *
35  * By default, the file is closed and deleted when the TemporaryFile object
36  * is destroyed, but both these behaviors can be overridden with arguments
37  * to the constructor.
38  */
39 class TemporaryFile {
40  public:
41   enum class Scope {
42     PERMANENT,
43     UNLINK_IMMEDIATELY,
44     UNLINK_ON_DESTRUCTION
45   };
46   explicit TemporaryFile(StringPiece namePrefix = StringPiece(),
47                          fs::path dir = fs::path(),
48                          Scope scope = Scope::UNLINK_ON_DESTRUCTION,
49                          bool closeOnDestruction = true);
50   ~TemporaryFile();
51
52   // Movable, but not copiable
53   TemporaryFile(TemporaryFile&&) = default;
54   TemporaryFile& operator=(TemporaryFile&&) = default;
55
56   int fd() const { return fd_; }
57   const fs::path& path() const;
58
59  private:
60   Scope scope_;
61   bool closeOnDestruction_;
62   int fd_;
63   fs::path path_;
64 };
65
66 /**
67  * Temporary directory.
68  *
69  * By default, the temporary directory is created in a system-specific
70  * location (the value of the TMPDIR environment variable, or /tmp), but you
71  * can override that with a non-empty directory passed to the constructor.
72  *
73  * By default, the directory is recursively deleted when the TemporaryDirectory
74  * object is destroyed, but that can be overridden with an argument
75  * to the constructor.
76  */
77
78 class TemporaryDirectory {
79  public:
80   enum class Scope {
81     PERMANENT,
82     DELETE_ON_DESTRUCTION
83   };
84   explicit TemporaryDirectory(StringPiece namePrefix = StringPiece(),
85                               fs::path dir = fs::path(),
86                               Scope scope = Scope::DELETE_ON_DESTRUCTION);
87   ~TemporaryDirectory();
88
89   // Movable, but not copiable
90   TemporaryDirectory(TemporaryDirectory&&) = default;
91   TemporaryDirectory& operator=(TemporaryDirectory&&) = default;
92
93   const fs::path& path() const { return path_; }
94
95  private:
96   Scope scope_;
97   fs::path path_;
98 };
99
100 /**
101  * Changes into a temporary directory, and deletes it with all its contents
102  * upon destruction, also changing back to the original working directory.
103  */
104 class ChangeToTempDir {
105 public:
106   ChangeToTempDir();
107   ~ChangeToTempDir();
108
109   // Movable, but not copiable
110   ChangeToTempDir(ChangeToTempDir&&) = default;
111   ChangeToTempDir& operator=(ChangeToTempDir&&) = default;
112
113   const fs::path& path() const { return dir_.path(); }
114
115 private:
116   fs::path initialPath_;
117   TemporaryDirectory dir_;
118 };
119
120 /**
121  * Easy PCRE regex matching. Note that pattern must match the ENTIRE target,
122  * so use .* at the start and end of the pattern, as appropriate.  See
123  * http://regex101.com/ for a PCRE simulator.
124  */
125 #define EXPECT_PCRE_MATCH(pattern_stringpiece, target_stringpiece) \
126   EXPECT_PRED2( \
127     ::folly::test::detail::hasPCREPatternMatch, \
128     pattern_stringpiece, \
129     target_stringpiece \
130   )
131 #define EXPECT_NO_PCRE_MATCH(pattern_stringpiece, target_stringpiece) \
132   EXPECT_PRED2( \
133     ::folly::test::detail::hasNoPCREPatternMatch, \
134     pattern_stringpiece, \
135     target_stringpiece \
136   )
137
138 namespace detail {
139   bool hasPCREPatternMatch(StringPiece pattern, StringPiece target);
140   bool hasNoPCREPatternMatch(StringPiece pattern, StringPiece target);
141 }  // namespace detail
142
143 /**
144  * Use these patterns together with CaptureFD and EXPECT_PCRE_MATCH() to
145  * test for the presence (or absence) of log lines at a particular level:
146  *
147  *   CaptureFD stderr(2);
148  *   LOG(INFO) << "All is well";
149  *   EXPECT_NO_PCRE_MATCH(glogErrOrWarnPattern(), stderr.readIncremental());
150  *   LOG(ERROR) << "Uh-oh";
151  *   EXPECT_PCRE_MATCH(glogErrorPattern(), stderr.readIncremental());
152  */
153 inline std::string glogErrorPattern() { return ".*(^|\n)E[0-9].*"; }
154 inline std::string glogWarningPattern() { return ".*(^|\n)W[0-9].*"; }
155 // Error OR warning
156 inline std::string glogErrOrWarnPattern() { return ".*(^|\n)[EW][0-9].*"; }
157
158 /**
159  * Temporarily capture a file descriptor by redirecting it into a file.
160  * You can consume its output either all-at-once or incrementally.
161  * Great for testing logging (see also glog*Pattern()).
162  */
163 class CaptureFD {
164 public:
165   explicit CaptureFD(int fd);
166   ~CaptureFD();
167
168   /**
169    * Restore the captured FD to its original state. It can be useful to do
170    * this before the destructor so that you can read() the captured data and
171    * log about it to the formerly captured stderr or stdout.
172    */
173   void release();
174
175   /**
176    * Reads the whole file into a string, but does not remove the redirect.
177    */
178   std::string read();
179
180   /**
181    * Read any bytes that were appended to the file since the last
182    * readIncremental.  Great for testing line-by-line output.
183    */
184   std::string readIncremental();
185
186 private:
187   TemporaryFile file_;
188
189   int fd_;
190   int oldFDCopy_;  // equal to fd_ after restore()
191
192   off_t readOffset_;  // for incremental reading
193 };
194
195 class EnvVarSaver {
196 public:
197   EnvVarSaver();
198   ~EnvVarSaver();
199 private:
200   std::map<std::string, std::string> saved_;
201 };
202
203 }  // namespace test
204 }  // namespace folly
205
206 #endif /* FOLLY_TESTUTIL_H_ */