verdnatura-chat/ios/Pods/Flipper-Folly/folly/experimental/TestUtil.h

269 lines
7.6 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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