summaryrefslogtreecommitdiffstats
path: root/image/test/gtest/TestCopyOnWrite.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'image/test/gtest/TestCopyOnWrite.cpp')
-rw-r--r--image/test/gtest/TestCopyOnWrite.cpp237
1 files changed, 237 insertions, 0 deletions
diff --git a/image/test/gtest/TestCopyOnWrite.cpp b/image/test/gtest/TestCopyOnWrite.cpp
new file mode 100644
index 0000000000..d5ad3e4f6b
--- /dev/null
+++ b/image/test/gtest/TestCopyOnWrite.cpp
@@ -0,0 +1,237 @@
+/* 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/. */
+
+#include "gtest/gtest.h"
+
+#include "CopyOnWrite.h"
+
+using namespace mozilla;
+using namespace mozilla::image;
+
+struct ValueStats {
+ int32_t mCopies = 0;
+ int32_t mFrees = 0;
+ int32_t mCalls = 0;
+ int32_t mConstCalls = 0;
+ int32_t mSerial = 0;
+};
+
+struct Value {
+ NS_INLINE_DECL_REFCOUNTING(Value)
+
+ explicit Value(ValueStats& aStats)
+ : mStats(aStats), mSerial(mStats.mSerial++) {}
+
+ Value(const Value& aOther)
+ : mStats(aOther.mStats), mSerial(mStats.mSerial++) {
+ mStats.mCopies++;
+ }
+
+ void Go() { mStats.mCalls++; }
+ void Go() const { mStats.mConstCalls++; }
+
+ int32_t Serial() const { return mSerial; }
+
+ protected:
+ ~Value() { mStats.mFrees++; }
+
+ private:
+ ValueStats& mStats;
+ int32_t mSerial;
+};
+
+TEST(ImageCopyOnWrite, Read)
+{
+ ValueStats stats;
+
+ {
+ CopyOnWrite<Value> cow(new Value(stats));
+
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_TRUE(cow.CanRead());
+
+ cow.Read([&](const Value* aValue) {
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_EQ(0, aValue->Serial());
+ EXPECT_TRUE(cow.CanRead());
+ EXPECT_TRUE(cow.CanWrite());
+
+ aValue->Go();
+
+ EXPECT_EQ(0, stats.mCalls);
+ EXPECT_EQ(1, stats.mConstCalls);
+ });
+
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_EQ(0, stats.mCalls);
+ EXPECT_EQ(1, stats.mConstCalls);
+ }
+
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(1, stats.mFrees);
+}
+
+TEST(ImageCopyOnWrite, RecursiveRead)
+{
+ ValueStats stats;
+
+ {
+ CopyOnWrite<Value> cow(new Value(stats));
+
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_TRUE(cow.CanRead());
+
+ cow.Read([&](const Value* aValue) {
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_EQ(0, aValue->Serial());
+ EXPECT_TRUE(cow.CanRead());
+ EXPECT_TRUE(cow.CanWrite());
+
+ // Make sure that Read() inside a Read() succeeds.
+ cow.Read(
+ [&](const Value* aValue) {
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_EQ(0, aValue->Serial());
+ EXPECT_TRUE(cow.CanRead());
+ EXPECT_TRUE(cow.CanWrite());
+
+ aValue->Go();
+
+ EXPECT_EQ(0, stats.mCalls);
+ EXPECT_EQ(1, stats.mConstCalls);
+ },
+ []() {
+ // This gets called if we can't read. We shouldn't get here.
+ EXPECT_TRUE(false);
+ });
+ });
+
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_EQ(0, stats.mCalls);
+ EXPECT_EQ(1, stats.mConstCalls);
+ }
+
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(1, stats.mFrees);
+}
+
+TEST(ImageCopyOnWrite, Write)
+{
+ ValueStats stats;
+
+ {
+ CopyOnWrite<Value> cow(new Value(stats));
+
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_TRUE(cow.CanRead());
+ EXPECT_TRUE(cow.CanWrite());
+
+ cow.Write([&](Value* aValue) {
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_EQ(0, aValue->Serial());
+ EXPECT_TRUE(!cow.CanRead());
+ EXPECT_TRUE(!cow.CanWrite());
+
+ aValue->Go();
+
+ EXPECT_EQ(1, stats.mCalls);
+ EXPECT_EQ(0, stats.mConstCalls);
+ });
+
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_EQ(1, stats.mCalls);
+ EXPECT_EQ(0, stats.mConstCalls);
+ }
+
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(1, stats.mFrees);
+}
+
+TEST(ImageCopyOnWrite, WriteRecursive)
+{
+ ValueStats stats;
+
+ {
+ CopyOnWrite<Value> cow(new Value(stats));
+
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_TRUE(cow.CanRead());
+ EXPECT_TRUE(cow.CanWrite());
+
+ cow.Read([&](const Value* aValue) {
+ EXPECT_EQ(0, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_EQ(0, aValue->Serial());
+ EXPECT_TRUE(cow.CanRead());
+ EXPECT_TRUE(cow.CanWrite());
+
+ // Make sure Write() inside a Read() succeeds.
+ cow.Write(
+ [&](Value* aValue) {
+ EXPECT_EQ(1, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_EQ(1, aValue->Serial());
+ EXPECT_TRUE(!cow.CanRead());
+ EXPECT_TRUE(!cow.CanWrite());
+
+ aValue->Go();
+
+ EXPECT_EQ(1, stats.mCalls);
+ EXPECT_EQ(0, stats.mConstCalls);
+
+ // Make sure Read() inside a Write() fails.
+ cow.Read(
+ [](const Value* aValue) {
+ // This gets called if we can read. We shouldn't get here.
+ EXPECT_TRUE(false);
+ },
+ []() {
+ // This gets called if we can't read. We *should* get here.
+ EXPECT_TRUE(true);
+ });
+
+ // Make sure Write() inside a Write() fails.
+ cow.Write(
+ [](Value* aValue) {
+ // This gets called if we can write. We shouldn't get here.
+ EXPECT_TRUE(false);
+ },
+ []() {
+ // This gets called if we can't write. We *should* get here.
+ EXPECT_TRUE(true);
+ });
+ },
+ []() {
+ // This gets called if we can't write. We shouldn't get here.
+ EXPECT_TRUE(false);
+ });
+
+ aValue->Go();
+
+ EXPECT_EQ(1, stats.mCopies);
+ EXPECT_EQ(0, stats.mFrees);
+ EXPECT_EQ(1, stats.mCalls);
+ EXPECT_EQ(1, stats.mConstCalls);
+ });
+
+ EXPECT_EQ(1, stats.mCopies);
+ EXPECT_EQ(1, stats.mFrees);
+ EXPECT_EQ(1, stats.mCalls);
+ EXPECT_EQ(1, stats.mConstCalls);
+ }
+
+ EXPECT_EQ(1, stats.mCopies);
+ EXPECT_EQ(2, stats.mFrees);
+}