/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // This is a cppunittest, rather than a gtest, in order to assert that no // additional DLL needs to be linked in to use the function(s) tested herein. #include #include #include #include #include #include "mozmemory_utils.h" #include "mozilla/Likely.h" static bool TESTS_FAILED = false; // Introduce iostream output operators for std::optional, for convenience's // sake. // // (This is technically undefined behavior per [namespace.std], but it's // unlikely to have any surprising effects when confined to this compilation // unit.) namespace std { template std::ostream& operator<<(std::ostream& o, std::optional const& s) { if (s) { return o << "std::optional{" << s.value() << "}"; } return o << "std::nullopt"; } std::ostream& operator<<(std::ostream& o, std::nullopt_t const& s) { return o << "std::nullopt"; } } // namespace std // EXPECT_EQ // // Assert that two expressions are equal. Print them, and their values, on // failure. (Based on the GTest macro of the same name.) template void AssertEqualImpl_(X&& x, Y&& y, const char* file, size_t line, const char (&xStr)[Xn], const char (&yStr)[Yn], const char* explanation = nullptr) { if (MOZ_LIKELY(x == y)) return; TESTS_FAILED = true; std::stringstream sstr; sstr << file << ':' << line << ": "; if (explanation) sstr << explanation << "\n\t"; sstr << "expected " << xStr << " (" << x << ") == " << yStr << " (" << y << ")\n"; std::cerr << sstr.str() << std::flush; } #define EXPECT_EQ(x, y) \ do { \ AssertEqualImpl_(x, y, __FILE__, __LINE__, #x, #y); \ } while (0) // STATIC_ASSERT_VALUE_IS_OF_TYPE // // Assert that a value `v` is of type `t` (ignoring cv-qualification). #define STATIC_ASSERT_VALUE_IS_OF_TYPE(v, t) \ static_assert(std::is_same_v, t>) // MockSleep // // Mock replacement for ::Sleep that merely logs its calls. struct MockSleep { size_t calls = 0; size_t sum = 0; void operator()(size_t val) { ++calls; sum += val; } bool operator==(MockSleep const& that) const { return calls == that.calls && sum == that.sum; } }; std::ostream& operator<<(std::ostream& o, MockSleep const& s) { return o << "MockSleep { count: " << s.calls << ", sum: " << s.sum << " }"; } // MockAlloc // // Mock memory allocation mechanism. Eventually returns a value. template struct MockAlloc { size_t count; T value; std::optional operator()() { if (!count--) return value; return std::nullopt; } }; int main() { using mozilla::StallSpecs; const StallSpecs stall = {.maxAttempts = 10, .delayMs = 50}; // semantic test: stalls as requested but still yields a value, // up until it doesn't for (size_t i = 0; i < 20; ++i) { MockSleep sleep; auto const ret = stall.StallAndRetry(sleep, MockAlloc{.count = i, .value = 5}); STATIC_ASSERT_VALUE_IS_OF_TYPE(ret, std::optional); if (i < 10) { EXPECT_EQ(ret, std::optional(5)); } else { EXPECT_EQ(ret, std::nullopt); } size_t const expectedCalls = std::min(i + 1, 10); EXPECT_EQ(sleep, (MockSleep{.calls = expectedCalls, .sum = 50 * expectedCalls})); } // syntactic test: inline capturing lambda is accepted for aOperation { MockSleep sleep; std::optional value{42}; auto const ret = stall.StallAndRetry(sleep, [&]() { return value; }); STATIC_ASSERT_VALUE_IS_OF_TYPE(ret, std::optional); EXPECT_EQ(ret, std::optional(42)); EXPECT_EQ(sleep, (MockSleep{.calls = 1, .sum = 50})); } // syntactic test: inline capturing lambda is accepted for aDelayFunc { MockSleep sleep; auto const ret = stall.StallAndRetry([&](size_t time) { sleep(time); }, MockAlloc{.count = 0, .value = 105}); STATIC_ASSERT_VALUE_IS_OF_TYPE(ret, std::optional); EXPECT_EQ(ret, std::optional(105)); EXPECT_EQ(sleep, (MockSleep{.calls = 1, .sum = 50})); } return TESTS_FAILED ? 1 : 0; }