Export of internal Abseil changes.

--
441d1aa02483cdc510eb2fef012b31384fd8e3a6 by Eric Fiselier <ericwf@google.com>:

Fix str_format with non-POSIX libc implementations.

PiperOrigin-RevId: 218441122

--
da6190130e74222af6eb161a5593364341370370 by Jon Cohen <cohenjon@google.com>:

Refactor ExceptionSafetyTester::Test in order to remove the levels of indirection related to unpacking tuples.

PiperOrigin-RevId: 218403355
GitOrigin-RevId: 441d1aa02483cdc510eb2fef012b31384fd8e3a6
Change-Id: I6f6b978eb96fe261e8ee41ecdce185e5356a601d
This commit is contained in:
Abseil Team 2018-10-23 18:09:41 -07:00 committed by Ashley Hedberg
parent 8efc526087
commit 94c298e2a0
5 changed files with 154 additions and 136 deletions

View file

@ -770,6 +770,18 @@ TEST(ExceptionCheckTest, ModifyingChecker) {
.Test(invoker));
}
TEST(ExceptionSafetyTesterTest, ResetsCountdown) {
auto test =
testing::MakeExceptionSafetyTester()
.WithInitialValue(ThrowingValue<>())
.WithContracts([](ThrowingValue<>*) { return AssertionSuccess(); })
.WithOperation([](ThrowingValue<>*) {});
ASSERT_TRUE(test.Test());
// If the countdown isn't reset because there were no exceptions thrown, then
// this will fail with a termination from an unhandled exception
EXPECT_TRUE(test.Test());
}
struct NonCopyable : public NonNegative {
NonCopyable(const NonCopyable&) = delete;
NonCopyable() : NonNegative{0} {}

View file

@ -23,6 +23,10 @@ exceptions_internal::NoThrowTag nothrow_ctor;
exceptions_internal::StrongGuaranteeTagType strong_guarantee;
exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester() {
return {};
}
namespace exceptions_internal {
int countdown = -1;

View file

@ -190,70 +190,6 @@ class TrackedObject {
~TrackedObject() noexcept { ConstructorTracker::ObjectDestructed(this); }
};
template <typename Factory, typename Operation, typename Contract>
absl::optional<testing::AssertionResult> TestSingleContractAtCountdownImpl(
const Factory& factory, const Operation& operation, int count,
const Contract& contract) {
auto t_ptr = factory();
absl::optional<testing::AssertionResult> current_res;
SetCountdown(count);
try {
operation(t_ptr.get());
} catch (const exceptions_internal::TestException& e) {
current_res.emplace(contract(t_ptr.get()));
if (!current_res.value()) {
*current_res << e.what() << " failed contract check";
}
}
UnsetCountdown();
return current_res;
}
template <typename Factory, typename Operation>
absl::optional<testing::AssertionResult> TestSingleContractAtCountdownImpl(
const Factory& factory, const Operation& operation, int count,
StrongGuaranteeTagType) {
using TPtr = typename decltype(factory())::pointer;
auto t_is_strong = [&](TPtr t) { return *t == *factory(); };
return TestSingleContractAtCountdownImpl(factory, operation, count,
t_is_strong);
}
template <typename Factory, typename Operation, typename Contract>
int TestSingleContractAtCountdown(
const Factory& factory, const Operation& operation, int count,
const Contract& contract,
absl::optional<testing::AssertionResult>* reduced_res) {
// If reduced_res is empty, it means the current call to
// TestSingleContractAtCountdown(...) is the first test being run so we do
// want to run it. Alternatively, if it's not empty (meaning a previous test
// has run) we want to check if it passed. If the previous test did pass, we
// want to contine running tests so we do want to run the current one. If it
// failed, we want to short circuit so as not to overwrite the AssertionResult
// output. If that's the case, we do not run the current test and instead we
// simply return.
if (!reduced_res->has_value() || reduced_res->value()) {
*reduced_res =
TestSingleContractAtCountdownImpl(factory, operation, count, contract);
}
return 0;
}
template <typename Factory, typename Operation, typename... Contracts>
inline absl::optional<testing::AssertionResult> TestAllContractsAtCountdown(
const Factory& factory, const Operation& operation, int count,
const Contracts&... contracts) {
absl::optional<testing::AssertionResult> reduced_res;
// Run each checker, short circuiting after the first failure
int dummy[] = {
0, (TestSingleContractAtCountdown(factory, operation, count, contracts,
&reduced_res))...};
static_cast<void>(dummy);
return reduced_res;
}
} // namespace exceptions_internal
extern exceptions_internal::NoThrowTag nothrow_ctor;
@ -871,7 +807,7 @@ testing::AssertionResult TestNothrowOp(const Operation& operation) {
namespace exceptions_internal {
// Dummy struct for ExceptionSafetyTester<> partial state.
// Dummy struct for ExceptionSafetyTestBuilder<> partial state.
struct UninitializedT {};
template <typename T>
@ -893,20 +829,97 @@ using EnableIfTestable = typename absl::enable_if_t<
template <typename Factory = UninitializedT,
typename Operation = UninitializedT, typename... Contracts>
class ExceptionSafetyTester;
class ExceptionSafetyTestBuilder;
} // namespace exceptions_internal
exceptions_internal::ExceptionSafetyTester<> MakeExceptionSafetyTester();
/*
* Constructs an empty ExceptionSafetyTestBuilder. All
* ExceptionSafetyTestBuilder objects are immutable and all With[thing] mutation
* methods return new instances of ExceptionSafetyTestBuilder.
*
* In order to test a T for exception safety, a factory for that T, a testable
* operation, and at least one contract callback returning an assertion
* result must be applied using the respective methods.
*/
exceptions_internal::ExceptionSafetyTestBuilder<> MakeExceptionSafetyTester();
namespace exceptions_internal {
template <typename T>
struct IsUniquePtr : std::false_type {};
template <typename T, typename D>
struct IsUniquePtr<std::unique_ptr<T, D>> : std::true_type {};
template <typename Factory>
struct FactoryPtrTypeHelper {
using type = decltype(std::declval<const Factory&>()());
static_assert(IsUniquePtr<type>::value, "Factories must return a unique_ptr");
};
template <typename Factory>
using FactoryPtrType = typename FactoryPtrTypeHelper<Factory>::type;
template <typename Factory>
using FactoryElementType = typename FactoryPtrType<Factory>::element_type;
template <typename T>
class ExceptionSafetyTest {
using Factory = std::function<std::unique_ptr<T>()>;
using Operation = std::function<void(T*)>;
using Contract = std::function<AssertionResult(T*)>;
public:
template <typename... Contracts>
explicit ExceptionSafetyTest(const Factory& f, const Operation& op,
const Contracts&... contracts)
: factory_(f), operation_(op), contracts_{WrapContract(contracts)...} {}
AssertionResult Test() const {
for (int count = 0;; ++count) {
exceptions_internal::ConstructorTracker ct(count);
for (const auto& contract : contracts_) {
auto t_ptr = factory_();
try {
SetCountdown(count);
operation_(t_ptr.get());
// Unset for the case that the operation throws no exceptions, which
// would leave the countdown set and break the *next* exception safety
// test after this one.
UnsetCountdown();
return AssertionSuccess();
} catch (const exceptions_internal::TestException& e) {
if (!contract(t_ptr.get())) {
return AssertionFailure() << e.what() << " failed contract check";
}
}
}
}
}
private:
template <typename ContractFn>
Contract WrapContract(const ContractFn& contract) {
return [contract](T* t_ptr) { return AssertionResult(contract(t_ptr)); };
}
Contract WrapContract(StrongGuaranteeTagType) {
return [this](T* t_ptr) { return AssertionResult(*factory_() == *t_ptr); };
}
Factory factory_;
Operation operation_;
std::vector<Contract> contracts_;
};
/*
* Builds a tester object that tests if performing a operation on a T follows
* exception safety guarantees. Verification is done via contract assertion
* callbacks applied to T instances post-throw.
*
* Template parameters for ExceptionSafetyTester:
* Template parameters for ExceptionSafetyTestBuilder:
*
* - Factory: The factory object (passed in via tester.WithFactory(...) or
* tester.WithInitialValue(...)) must be invocable with the signature
@ -933,13 +946,13 @@ namespace exceptions_internal {
* please.
*/
template <typename Factory, typename Operation, typename... Contracts>
class ExceptionSafetyTester {
class ExceptionSafetyTestBuilder {
public:
/*
* Returns a new ExceptionSafetyTester with an included T factory based on the
* provided T instance. The existing factory will not be included in the newly
* created tester instance. The created factory returns a new T instance by
* copy-constructing the provided const T& t.
* Returns a new ExceptionSafetyTestBuilder with an included T factory based
* on the provided T instance. The existing factory will not be included in
* the newly created tester instance. The created factory returns a new T
* instance by copy-constructing the provided const T& t.
*
* Preconditions for tester.WithInitialValue(const T& t):
*
@ -948,41 +961,41 @@ class ExceptionSafetyTester {
* tester.WithFactory(...).
*/
template <typename T>
ExceptionSafetyTester<DefaultFactory<T>, Operation, Contracts...>
ExceptionSafetyTestBuilder<DefaultFactory<T>, Operation, Contracts...>
WithInitialValue(const T& t) const {
return WithFactory(DefaultFactory<T>(t));
}
/*
* Returns a new ExceptionSafetyTester with the provided T factory included.
* The existing factory will not be included in the newly-created tester
* instance. This method is intended for use with types lacking a copy
* Returns a new ExceptionSafetyTestBuilder with the provided T factory
* included. The existing factory will not be included in the newly-created
* tester instance. This method is intended for use with types lacking a copy
* constructor. Types that can be copy-constructed should instead use the
* method tester.WithInitialValue(...).
*/
template <typename NewFactory>
ExceptionSafetyTester<absl::decay_t<NewFactory>, Operation, Contracts...>
ExceptionSafetyTestBuilder<absl::decay_t<NewFactory>, Operation, Contracts...>
WithFactory(const NewFactory& new_factory) const {
return {new_factory, operation_, contracts_};
}
/*
* Returns a new ExceptionSafetyTester with the provided testable operation
* included. The existing operation will not be included in the newly created
* tester.
* Returns a new ExceptionSafetyTestBuilder with the provided testable
* operation included. The existing operation will not be included in the
* newly created tester.
*/
template <typename NewOperation>
ExceptionSafetyTester<Factory, absl::decay_t<NewOperation>, Contracts...>
ExceptionSafetyTestBuilder<Factory, absl::decay_t<NewOperation>, Contracts...>
WithOperation(const NewOperation& new_operation) const {
return {factory_, new_operation, contracts_};
}
/*
* Returns a new ExceptionSafetyTester with the provided MoreContracts...
* Returns a new ExceptionSafetyTestBuilder with the provided MoreContracts...
* combined with the Contracts... that were already included in the instance
* on which the method was called. Contracts... cannot be removed or replaced
* once added to an ExceptionSafetyTester instance. A fresh object must be
* created in order to get an empty Contracts... list.
* once added to an ExceptionSafetyTestBuilder instance. A fresh object must
* be created in order to get an empty Contracts... list.
*
* In addition to passing in custom contract assertion callbacks, this method
* accepts `testing::strong_guarantee` as an argument which checks T instances
@ -991,8 +1004,8 @@ class ExceptionSafetyTester {
* properly rolled back.
*/
template <typename... MoreContracts>
ExceptionSafetyTester<Factory, Operation, Contracts...,
absl::decay_t<MoreContracts>...>
ExceptionSafetyTestBuilder<Factory, Operation, Contracts...,
absl::decay_t<MoreContracts>...>
WithContracts(const MoreContracts&... more_contracts) const {
return {
factory_, operation_,
@ -1039,48 +1052,27 @@ class ExceptionSafetyTester {
typename LazyOperation = Operation,
typename = EnableIfTestable<sizeof...(Contracts), Factory, LazyOperation>>
testing::AssertionResult Test() const {
return TestImpl(operation_, absl::index_sequence_for<Contracts...>());
return Test(operation_);
}
private:
template <typename, typename, typename...>
friend class ExceptionSafetyTester;
friend class ExceptionSafetyTestBuilder;
friend ExceptionSafetyTester<> testing::MakeExceptionSafetyTester();
friend ExceptionSafetyTestBuilder<> testing::MakeExceptionSafetyTester();
ExceptionSafetyTester() {}
ExceptionSafetyTestBuilder() {}
ExceptionSafetyTester(const Factory& f, const Operation& o,
const std::tuple<Contracts...>& i)
ExceptionSafetyTestBuilder(const Factory& f, const Operation& o,
const std::tuple<Contracts...>& i)
: factory_(f), operation_(o), contracts_(i) {}
template <typename SelectedOperation, size_t... Indices>
testing::AssertionResult TestImpl(const SelectedOperation& selected_operation,
testing::AssertionResult TestImpl(SelectedOperation selected_operation,
absl::index_sequence<Indices...>) const {
// Starting from 0 and counting upwards until one of the exit conditions is
// hit...
for (int count = 0;; ++count) {
exceptions_internal::ConstructorTracker ct(count);
// Run the full exception safety test algorithm for the current countdown
auto reduced_res =
TestAllContractsAtCountdown(factory_, selected_operation, count,
std::get<Indices>(contracts_)...);
// If there is no value in the optional, no contracts were run because no
// exception was thrown. This means that the test is complete and the loop
// can exit successfully.
if (!reduced_res.has_value()) {
return testing::AssertionSuccess();
}
// If the optional is not empty and the value is falsy, an contract check
// failed so the test must exit to propegate the failure.
if (!reduced_res.value()) {
return reduced_res.value();
}
// If the optional is not empty and the value is not falsy, it means
// exceptions were thrown but the contracts passed so the test must
// continue to run.
}
return ExceptionSafetyTest<FactoryElementType<Factory>>(
factory_, selected_operation, std::get<Indices>(contracts_)...)
.Test();
}
Factory factory_;
@ -1090,20 +1082,6 @@ class ExceptionSafetyTester {
} // namespace exceptions_internal
/*
* Constructs an empty ExceptionSafetyTester. All ExceptionSafetyTester
* objects are immutable and all With[thing] mutation methods return new
* instances of ExceptionSafetyTester.
*
* In order to test a T for exception safety, a factory for that T, a testable
* operation, and at least one contract callback returning an assertion
* result must be applied using the respective methods.
*/
inline exceptions_internal::ExceptionSafetyTester<>
MakeExceptionSafetyTester() {
return {};
}
} // namespace testing
#endif // ABSL_BASE_INTERNAL_EXCEPTION_SAFETY_TESTING_H_

View file

@ -20,6 +20,16 @@
namespace absl {
namespace str_format_internal {
namespace {
struct ClearErrnoGuard {
ClearErrnoGuard() : old_value(errno) { errno = 0; }
~ClearErrnoGuard() {
if (!errno) errno = old_value;
}
int old_value;
};
} // namespace
void BufferRawSink::Write(string_view v) {
size_t to_write = std::min(v.size(), size_);
std::memcpy(buffer_, v.data(), to_write);
@ -30,14 +40,27 @@ void BufferRawSink::Write(string_view v) {
void FILERawSink::Write(string_view v) {
while (!v.empty() && !error_) {
// Reset errno to zero in case the libc implementation doesn't set errno
// when a failure occurs.
ClearErrnoGuard guard;
if (size_t result = std::fwrite(v.data(), 1, v.size(), output_)) {
// Some progress was made.
count_ += result;
v.remove_prefix(result);
} else {
// Some error occurred.
if (errno != EINTR) {
if (errno == EINTR) {
continue;
} else if (errno) {
error_ = errno;
} else if (std::ferror(output_)) {
// Non-POSIX compliant libc implementations may not set errno, so we
// have check the streams error indicator.
error_ = EBADF;
} else {
// We're likely on a non-POSIX system that encountered EINTR but had no
// way of reporting it.
continue;
}
}
}

View file

@ -242,6 +242,7 @@ class TempFile {
std::string ReadFile() {
std::fseek(file_, 0, SEEK_END);
int size = std::ftell(file_);
EXPECT_GT(size, 0);
std::rewind(file_);
std::string str(2 * size, ' ');
int read_bytes = std::fread(&str[0], 1, str.size(), file_);
@ -270,7 +271,7 @@ TEST_F(FormatEntryPointTest, FPrintFError) {
EXPECT_EQ(errno, EBADF);
}
#if __GNUC__
#if __GLIBC__
TEST_F(FormatEntryPointTest, FprintfTooLarge) {
std::FILE* f = std::fopen("/dev/null", "w");
int width = 2000000000;
@ -297,7 +298,7 @@ TEST_F(FormatEntryPointTest, PrintF) {
EXPECT_EQ(result, 30);
EXPECT_EQ(tmp.ReadFile(), "STRING: ABC NUMBER: -000000019");
}
#endif // __GNUC__
#endif // __GLIBC__
TEST_F(FormatEntryPointTest, SNPrintF) {
char buffer[16];