TestUtil.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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. #include <folly/experimental/TestUtil.h>
  17. #include <sys/stat.h>
  18. #include <sys/types.h>
  19. #include <boost/regex.hpp>
  20. #include <folly/Exception.h>
  21. #include <folly/File.h>
  22. #include <folly/FileUtil.h>
  23. #include <folly/Memory.h>
  24. #include <folly/String.h>
  25. #include <folly/portability/Fcntl.h>
  26. #ifdef _WIN32
  27. #include <crtdbg.h> // @manual
  28. #endif
  29. namespace folly {
  30. namespace test {
  31. namespace {
  32. fs::path generateUniquePath(fs::path path, StringPiece namePrefix) {
  33. if (path.empty()) {
  34. path = fs::temp_directory_path();
  35. }
  36. if (namePrefix.empty()) {
  37. path /= fs::unique_path();
  38. } else {
  39. path /=
  40. fs::unique_path(to<std::string>(namePrefix, ".%%%%-%%%%-%%%%-%%%%"));
  41. }
  42. return path;
  43. }
  44. } // namespace
  45. TemporaryFile::TemporaryFile(
  46. StringPiece namePrefix,
  47. fs::path dir,
  48. Scope scope,
  49. bool closeOnDestruction)
  50. : scope_(scope),
  51. closeOnDestruction_(closeOnDestruction),
  52. fd_(-1),
  53. path_(generateUniquePath(std::move(dir), namePrefix)) {
  54. fd_ = open(path_.string().c_str(), O_RDWR | O_CREAT | O_EXCL, 0666);
  55. checkUnixError(fd_, "open failed");
  56. if (scope_ == Scope::UNLINK_IMMEDIATELY) {
  57. boost::system::error_code ec;
  58. fs::remove(path_, ec);
  59. if (ec) {
  60. LOG(WARNING) << "unlink on construction failed: " << ec;
  61. } else {
  62. path_.clear();
  63. }
  64. }
  65. }
  66. void TemporaryFile::close() {
  67. if (::close(fd_) == -1) {
  68. PLOG(ERROR) << "close failed";
  69. }
  70. fd_ = -1;
  71. }
  72. const fs::path& TemporaryFile::path() const {
  73. CHECK(scope_ != Scope::UNLINK_IMMEDIATELY);
  74. DCHECK(!path_.empty());
  75. return path_;
  76. }
  77. void TemporaryFile::reset() {
  78. if (fd_ != -1 && closeOnDestruction_) {
  79. if (::close(fd_) == -1) {
  80. PLOG(ERROR) << "close failed (fd = " << fd_ << "): ";
  81. }
  82. }
  83. // If we previously failed to unlink() (UNLINK_IMMEDIATELY), we'll
  84. // try again here.
  85. if (scope_ != Scope::PERMANENT && !path_.empty()) {
  86. boost::system::error_code ec;
  87. fs::remove(path_, ec);
  88. if (ec) {
  89. LOG(WARNING) << "unlink on destruction failed: " << ec;
  90. }
  91. }
  92. }
  93. TemporaryFile::~TemporaryFile() {
  94. reset();
  95. }
  96. TemporaryDirectory::TemporaryDirectory(
  97. StringPiece namePrefix,
  98. fs::path dir,
  99. Scope scope)
  100. : scope_(scope),
  101. path_(std::make_unique<fs::path>(
  102. generateUniquePath(std::move(dir), namePrefix))) {
  103. fs::create_directory(path());
  104. }
  105. TemporaryDirectory::~TemporaryDirectory() {
  106. if (scope_ == Scope::DELETE_ON_DESTRUCTION && path_ != nullptr) {
  107. boost::system::error_code ec;
  108. fs::remove_all(path(), ec);
  109. if (ec) {
  110. LOG(WARNING) << "recursive delete on destruction failed: " << ec;
  111. }
  112. }
  113. }
  114. ChangeToTempDir::ChangeToTempDir() {
  115. orig_ = fs::current_path();
  116. fs::current_path(path());
  117. }
  118. ChangeToTempDir::~ChangeToTempDir() {
  119. if (!orig_.empty()) {
  120. fs::current_path(orig_);
  121. }
  122. }
  123. namespace detail {
  124. SavedState disableInvalidParameters() {
  125. #ifdef _WIN32
  126. SavedState ret;
  127. ret.previousThreadLocalHandler =
  128. _set_thread_local_invalid_parameter_handler([](const wchar_t*,
  129. const wchar_t*,
  130. const wchar_t*,
  131. unsigned int,
  132. uintptr_t) {});
  133. ret.previousCrtReportMode = _CrtSetReportMode(_CRT_ASSERT, 0);
  134. return ret;
  135. #else
  136. return SavedState();
  137. #endif
  138. }
  139. #ifdef _WIN32
  140. void enableInvalidParameters(SavedState state) {
  141. _set_thread_local_invalid_parameter_handler(
  142. (_invalid_parameter_handler)state.previousThreadLocalHandler);
  143. _CrtSetReportMode(_CRT_ASSERT, state.previousCrtReportMode);
  144. }
  145. #else
  146. void enableInvalidParameters(SavedState) {}
  147. #endif
  148. bool hasPCREPatternMatch(StringPiece pattern, StringPiece target) {
  149. return boost::regex_match(
  150. target.begin(),
  151. target.end(),
  152. boost::regex(pattern.begin(), pattern.end()));
  153. }
  154. bool hasNoPCREPatternMatch(StringPiece pattern, StringPiece target) {
  155. return !hasPCREPatternMatch(pattern, target);
  156. }
  157. } // namespace detail
  158. CaptureFD::CaptureFD(int fd, ChunkCob chunk_cob)
  159. : chunkCob_(std::move(chunk_cob)), fd_(fd), readOffset_(0) {
  160. oldFDCopy_ = dup(fd_);
  161. PCHECK(oldFDCopy_ != -1) << "Could not copy FD " << fd_;
  162. int file_fd = open(file_.path().string().c_str(), O_WRONLY | O_CREAT, 0600);
  163. PCHECK(dup2(file_fd, fd_) != -1)
  164. << "Could not replace FD " << fd_ << " with " << file_fd;
  165. PCHECK(close(file_fd) != -1) << "Could not close " << file_fd;
  166. }
  167. void CaptureFD::release() {
  168. if (oldFDCopy_ != fd_) {
  169. readIncremental(); // Feed chunkCob_
  170. PCHECK(dup2(oldFDCopy_, fd_) != -1)
  171. << "Could not restore old FD " << oldFDCopy_ << " into " << fd_;
  172. PCHECK(close(oldFDCopy_) != -1) << "Could not close " << oldFDCopy_;
  173. oldFDCopy_ = fd_; // Make this call idempotent
  174. }
  175. }
  176. CaptureFD::~CaptureFD() {
  177. release();
  178. }
  179. std::string CaptureFD::read() const {
  180. std::string contents;
  181. std::string filename = file_.path().string();
  182. PCHECK(folly::readFile(filename.c_str(), contents));
  183. return contents;
  184. }
  185. std::string CaptureFD::readIncremental() {
  186. std::string filename = file_.path().string();
  187. // Yes, I know that I could just keep the file open instead. So sue me.
  188. folly::File f(openNoInt(filename.c_str(), O_RDONLY), true);
  189. auto size = size_t(lseek(f.fd(), 0, SEEK_END) - readOffset_);
  190. std::unique_ptr<char[]> buf(new char[size]);
  191. auto bytes_read = folly::preadFull(f.fd(), buf.get(), size, readOffset_);
  192. PCHECK(ssize_t(size) == bytes_read);
  193. readOffset_ += off_t(size);
  194. chunkCob_(StringPiece(buf.get(), buf.get() + size));
  195. return std::string(buf.get(), size);
  196. }
  197. } // namespace test
  198. } // namespace folly