summaryrefslogtreecommitdiffstats
path: root/memory/build/test/TestMozJemallocUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'memory/build/test/TestMozJemallocUtils.cpp')
-rw-r--r--memory/build/test/TestMozJemallocUtils.cpp152
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;
+}