diff options
Diffstat (limited to 'memory/build/test/TestMozJemallocUtils.cpp')
-rw-r--r-- | memory/build/test/TestMozJemallocUtils.cpp | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/memory/build/test/TestMozJemallocUtils.cpp b/memory/build/test/TestMozJemallocUtils.cpp new file mode 100644 index 0000000000..06a1a3b5a5 --- /dev/null +++ b/memory/build/test/TestMozJemallocUtils.cpp @@ -0,0 +1,152 @@ +/* -*- 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 <algorithm> +#include <iostream> +#include <optional> +#include <sstream> +#include <type_traits> + +#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 <typename T> +std::ostream& operator<<(std::ostream& o, std::optional<T> 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 <typename X, typename Y, size_t Xn, size_t Yn> +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<std::remove_cv_t<decltype(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 <typename T> +struct MockAlloc { + size_t count; + T value; + + std::optional<T> 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<int>{.count = i, .value = 5}); + STATIC_ASSERT_VALUE_IS_OF_TYPE(ret, std::optional<int>); + + if (i < 10) { + EXPECT_EQ(ret, std::optional<int>(5)); + } else { + EXPECT_EQ(ret, std::nullopt); + } + size_t const expectedCalls = std::min<size_t>(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<int> value{42}; + auto const ret = stall.StallAndRetry(sleep, [&]() { return value; }); + + STATIC_ASSERT_VALUE_IS_OF_TYPE(ret, std::optional<int>); + 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<int>{.count = 0, .value = 105}); + + STATIC_ASSERT_VALUE_IS_OF_TYPE(ret, std::optional<int>); + EXPECT_EQ(ret, std::optional(105)); + EXPECT_EQ(sleep, (MockSleep{.calls = 1, .sum = 50})); + } + + return TESTS_FAILED ? 1 : 0; +} |