123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- /*
- * Copyright 2012-present Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #pragma once
- #include <map>
- #include <string>
- #include <folly/Range.h>
- #include <folly/ScopeGuard.h>
- #include <folly/experimental/io/FsUtil.h>
- namespace folly {
- namespace test {
- /**
- * Temporary file.
- *
- * By default, the file is created in a system-specific location (the value
- * of the TMPDIR environment variable, or /tmp), but you can override that
- * with a different (non-empty) directory passed to the constructor.
- *
- * By default, the file is closed and deleted when the TemporaryFile object
- * is destroyed, but both these behaviors can be overridden with arguments
- * to the constructor.
- */
- class TemporaryFile {
- public:
- enum class Scope {
- PERMANENT,
- UNLINK_IMMEDIATELY,
- UNLINK_ON_DESTRUCTION,
- };
- explicit TemporaryFile(
- StringPiece namePrefix = StringPiece(),
- fs::path dir = fs::path(),
- Scope scope = Scope::UNLINK_ON_DESTRUCTION,
- bool closeOnDestruction = true);
- ~TemporaryFile();
- // Movable, but not copyable
- TemporaryFile(TemporaryFile&& other) noexcept {
- assign(other);
- }
- TemporaryFile& operator=(TemporaryFile&& other) {
- if (this != &other) {
- reset();
- assign(other);
- }
- return *this;
- }
- void close();
- int fd() const {
- return fd_;
- }
- const fs::path& path() const;
- void reset();
- private:
- Scope scope_;
- bool closeOnDestruction_;
- int fd_;
- fs::path path_;
- void assign(TemporaryFile& other) {
- scope_ = other.scope_;
- closeOnDestruction_ = other.closeOnDestruction_;
- fd_ = std::exchange(other.fd_, -1);
- path_ = other.path_;
- }
- };
- /**
- * Temporary directory.
- *
- * By default, the temporary directory is created in a system-specific
- * location (the value of the TMPDIR environment variable, or /tmp), but you
- * can override that with a non-empty directory passed to the constructor.
- *
- * By default, the directory is recursively deleted when the TemporaryDirectory
- * object is destroyed, but that can be overridden with an argument
- * to the constructor.
- */
- class TemporaryDirectory {
- public:
- enum class Scope {
- PERMANENT,
- DELETE_ON_DESTRUCTION,
- };
- explicit TemporaryDirectory(
- StringPiece namePrefix = StringPiece(),
- fs::path dir = fs::path(),
- Scope scope = Scope::DELETE_ON_DESTRUCTION);
- ~TemporaryDirectory();
- // Movable, but not copiable
- TemporaryDirectory(TemporaryDirectory&&) = default;
- TemporaryDirectory& operator=(TemporaryDirectory&&) = default;
- const fs::path& path() const {
- return *path_;
- }
- private:
- Scope scope_;
- std::unique_ptr<fs::path> path_;
- };
- /**
- * Changes into a temporary directory, and deletes it with all its contents
- * upon destruction, also changing back to the original working directory.
- */
- class ChangeToTempDir {
- public:
- ChangeToTempDir();
- ~ChangeToTempDir();
- // Movable, but not copiable
- ChangeToTempDir(ChangeToTempDir&&) = default;
- ChangeToTempDir& operator=(ChangeToTempDir&&) = default;
- const fs::path& path() const {
- return dir_.path();
- }
- private:
- TemporaryDirectory dir_;
- fs::path orig_;
- };
- namespace detail {
- struct SavedState {
- void* previousThreadLocalHandler;
- int previousCrtReportMode;
- };
- SavedState disableInvalidParameters();
- void enableInvalidParameters(SavedState state);
- } // namespace detail
- // Ok, so fun fact: The CRT on windows will actually abort
- // on certain failed parameter validation checks in debug
- // mode rather than simply returning -1 as it does in release
- // mode. We can however, ensure consistent behavior by
- // registering our own thread-local invalid parameter handler
- // for the duration of the call, and just have that handler
- // immediately return. We also have to disable CRT asertion
- // alerts for the duration of the call, otherwise we get
- // the abort-retry-ignore window.
- template <typename Func>
- auto msvcSuppressAbortOnInvalidParams(Func func) -> decltype(func()) {
- auto savedState = detail::disableInvalidParameters();
- SCOPE_EXIT {
- detail::enableInvalidParameters(savedState);
- };
- return func();
- }
- /**
- * Easy PCRE regex matching. Note that pattern must match the ENTIRE target,
- * so use .* at the start and end of the pattern, as appropriate. See
- * http://regex101.com/ for a PCRE simulator.
- */
- #define EXPECT_PCRE_MATCH(pattern_stringpiece, target_stringpiece) \
- EXPECT_PRED2( \
- ::folly::test::detail::hasPCREPatternMatch, \
- pattern_stringpiece, \
- target_stringpiece)
- #define EXPECT_NO_PCRE_MATCH(pattern_stringpiece, target_stringpiece) \
- EXPECT_PRED2( \
- ::folly::test::detail::hasNoPCREPatternMatch, \
- pattern_stringpiece, \
- target_stringpiece)
- namespace detail {
- bool hasPCREPatternMatch(StringPiece pattern, StringPiece target);
- bool hasNoPCREPatternMatch(StringPiece pattern, StringPiece target);
- } // namespace detail
- /**
- * Use these patterns together with CaptureFD and EXPECT_PCRE_MATCH() to
- * test for the presence (or absence) of log lines at a particular level:
- *
- * CaptureFD stderr(2);
- * LOG(INFO) << "All is well";
- * EXPECT_NO_PCRE_MATCH(glogErrOrWarnPattern(), stderr.readIncremental());
- * LOG(ERROR) << "Uh-oh";
- * EXPECT_PCRE_MATCH(glogErrorPattern(), stderr.readIncremental());
- */
- inline std::string glogErrorPattern() {
- return ".*(^|\n)E[0-9].*";
- }
- inline std::string glogWarningPattern() {
- return ".*(^|\n)W[0-9].*";
- }
- // Error OR warning
- inline std::string glogErrOrWarnPattern() {
- return ".*(^|\n)[EW][0-9].*";
- }
- /**
- * Temporarily capture a file descriptor by redirecting it into a file.
- * You can consume its entire output thus far via read(), incrementally
- * via readIncremental(), or via callback using chunk_cob.
- * Great for testing logging (see also glog*Pattern()).
- */
- class CaptureFD {
- private:
- struct NoOpChunkCob {
- void operator()(StringPiece) {}
- };
- public:
- using ChunkCob = std::function<void(folly::StringPiece)>;
- /**
- * chunk_cob is is guaranteed to consume all the captured output. It is
- * invoked on each readIncremental(), and also on FD release to capture
- * as-yet unread lines. Chunks can be empty.
- */
- explicit CaptureFD(int fd, ChunkCob chunk_cob = NoOpChunkCob());
- ~CaptureFD();
- /**
- * Restore the captured FD to its original state. It can be useful to do
- * this before the destructor so that you can read() the captured data and
- * log about it to the formerly captured stderr or stdout.
- */
- void release();
- /**
- * Reads the whole file into a string, but does not remove the redirect.
- */
- std::string read() const;
- /**
- * Read any bytes that were appended to the file since the last
- * readIncremental. Great for testing line-by-line output.
- */
- std::string readIncremental();
- private:
- ChunkCob chunkCob_;
- TemporaryFile file_;
- int fd_;
- int oldFDCopy_; // equal to fd_ after restore()
- off_t readOffset_; // for incremental reading
- };
- } // namespace test
- } // namespace folly
|