Index: base/memory/discardable_memory_provider_unittest.cc |
diff --git a/base/memory/discardable_memory_provider_unittest.cc b/base/memory/discardable_memory_provider_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6e3dab695ea44f5fae4abc66c801ccd354a3dfe0 |
--- /dev/null |
+++ b/base/memory/discardable_memory_provider_unittest.cc |
@@ -0,0 +1,282 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/memory/discardable_memory_provider.h" |
+ |
+#include "base/memory/discardable_memory.h" |
+#include "base/message_loop.h" |
+#include "base/run_loop.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace base { |
+ |
+class DiscardableMemoryProviderTest : public testing::Test { |
+ public: |
+ DiscardableMemoryProviderTest() |
+ : message_loop_(MessageLoop::TYPE_IO), |
+ provider_(new DiscardableMemoryProvider) { |
+ DiscardableMemoryProvider::SetInstanceForTest(provider_.get()); |
Avi (use Gerrit)
2013/06/18 00:00:46
I'm lost; why are we keeping our own provider here
|
+ } |
+ |
+ virtual ~DiscardableMemoryProviderTest() { |
+ DiscardableMemoryProvider::SetInstanceForTest(NULL); |
+ } |
+ |
+ protected: |
+ void Register(DiscardableMemory* discardable) { |
+ DiscardableMemoryProvider::GetInstance()->Register(discardable); |
+ } |
+ |
+ const DiscardableMemoryProvider::AllocationMap& allocations() const { |
+ return DiscardableMemoryProvider::GetInstance()->allocations_; |
+ } |
+ |
+ size_t bytes_allocated() const { |
+ return DiscardableMemoryProvider::GetInstance()->bytes_allocated_; |
+ } |
+ |
+ void* Memory(const DiscardableMemory& discardable) const { |
+ return discardable.memory_; |
+ } |
+ |
+ void SetDiscardableMemoryLimit(size_t bytes) { |
+ DiscardableMemoryProvider::GetInstance()-> |
+ SetDiscardableMemoryLimit(bytes); |
+ } |
+ |
+ void SetBytesToReclaimUnderModeratePressure(size_t bytes) { |
+ DiscardableMemoryProvider::GetInstance()-> |
+ SetBytesToReclaimUnderModeratePressure(bytes); |
+ } |
+ |
+ private: |
+ MessageLoop message_loop_; |
+ scoped_ptr<DiscardableMemoryProvider> provider_; |
+}; |
+ |
+TEST_F(DiscardableMemoryProviderTest, Register) { |
+ DiscardableMemory discardable; |
+ Register(&discardable); |
+ ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
+ ASSERT_EQ(0u, bytes_allocated()); |
+} |
+ |
+TEST_F(DiscardableMemoryProviderTest, InitializeAndLock) { |
+ DiscardableMemory discardable; |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
+ size_t size = 1024; |
+ ASSERT_TRUE(discardable.InitializeAndLock(size)); |
+ ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
+ ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
+ ASSERT_EQ(1024u, bytes_allocated()); |
+ ASSERT_TRUE(discardable.is_locked()); |
+} |
+ |
+TEST_F(DiscardableMemoryProviderTest, InitializeAndLockZeroSize) { |
+ DiscardableMemory discardable; |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
+ size_t size = 0; |
+ ASSERT_FALSE(discardable.InitializeAndLock(size)); |
+ ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
+ ASSERT_EQ(0u, bytes_allocated()); |
+ ASSERT_FALSE(discardable.is_locked()); |
+} |
+ |
+TEST_F(DiscardableMemoryProviderTest, LockBeforeInitialization) { |
+ DiscardableMemory discardable; |
+ ASSERT_EQ(DISCARDABLE_MEMORY_FAILED, discardable.Lock()); |
+} |
+ |
+TEST_F(DiscardableMemoryProviderTest, LockAfterUnlock) { |
+ DiscardableMemory discardable; |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
+ size_t size = 1024; |
+ ASSERT_TRUE(discardable.InitializeAndLock(size)); |
+ ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
+ ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
+ ASSERT_EQ(1024u, bytes_allocated()); |
+ ASSERT_TRUE(discardable.is_locked()); |
+ |
+ // Now to unlock so we can lock later. |
+ discardable.Unlock(); |
+ ASSERT_FALSE(discardable.is_locked()); |
+ |
+ ASSERT_EQ(DISCARDABLE_MEMORY_SUCCESS, discardable.Lock()); |
+ ASSERT_TRUE(discardable.is_locked()); |
+} |
+ |
+TEST_F(DiscardableMemoryProviderTest, LockAfterPurge) { |
+ DiscardableMemory discardable; |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
+ size_t size = 1024; |
+ ASSERT_TRUE(discardable.InitializeAndLock(size)); |
+ ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
+ ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
+ ASSERT_EQ(1024u, bytes_allocated()); |
+ ASSERT_TRUE(discardable.is_locked()); |
+ |
+ // Now to unlock so we can lock later. |
+ discardable.Unlock(); |
+ ASSERT_FALSE(discardable.is_locked()); |
+ |
+ // Force the system to purge. |
+ MemoryPressureListener::NotifyMemoryPressure( |
+ MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); |
+ |
+ // Required because ObserverListThreadSafe notifies via PostTask. |
+ RunLoop().RunUntilIdle(); |
+ |
+ ASSERT_EQ(DISCARDABLE_MEMORY_PURGED, discardable.Lock()); |
+ ASSERT_TRUE(discardable.is_locked()); |
+} |
+ |
+TEST_F(DiscardableMemoryProviderTest, LockAfterPurgeAndCannotReallocate) { |
+ DiscardableMemory discardable; |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
+ size_t size = 1024; |
+ ASSERT_TRUE(discardable.InitializeAndLock(size)); |
+ ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
+ ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
+ ASSERT_EQ(1024u, bytes_allocated()); |
+ ASSERT_TRUE(discardable.is_locked()); |
+ |
+ // Now to unlock so we can lock later. |
+ discardable.Unlock(); |
+ ASSERT_FALSE(discardable.is_locked()); |
+ |
+ // Force the system to purge. |
+ MemoryPressureListener::NotifyMemoryPressure( |
+ MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); |
+ |
+ // Required because ObserverListThreadSafe notifies via PostTask. |
+ RunLoop().RunUntilIdle(); |
+ |
+ // Set max allowed allocation to 1 byte. This will make reallocation fail. |
+ SetDiscardableMemoryLimit(1); |
+ |
+ ASSERT_EQ(DISCARDABLE_MEMORY_FAILED, discardable.Lock()); |
+ ASSERT_FALSE(discardable.is_locked()); |
+} |
+ |
+#define DiscardableMemoryProviderPermutionTest(name, d0, d1, d2, pressure) \ |
+TEST_F(DiscardableMemoryProviderTest, name##_##d0##_##d1##_##d2) { \ |
+ DiscardableMemory discardables[3]; \ |
+ for (int i = 0; i < 3; ++i) { \ |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardables[i])); \ |
+ ASSERT_TRUE(discardables[i].InitializeAndLock(1024)); \ |
+ discardables[i].Unlock(); \ |
+ ASSERT_NE(static_cast<void*>(NULL), Memory(discardables[i])); \ |
+ } \ |
+ int ordering[] = { d0, d1, d2 }; \ |
+ for (int i = 0; i < 3; ++i) { \ |
+ int current_index = ordering[i]; \ |
+ ASSERT_NE(DISCARDABLE_MEMORY_FAILED, discardables[current_index].Lock()); \ |
+ if (i > 0) \ |
+ discardables[current_index].Unlock(); \ |
+ } \ |
+ SetBytesToReclaimUnderModeratePressure(1024); \ |
+ if (pressure) { \ |
+ MemoryPressureListener::NotifyMemoryPressure( \ |
+ MemoryPressureListener::MEMORY_PRESSURE_MODERATE); \ |
+ RunLoop().RunUntilIdle(); \ |
+ } else { \ |
+ SetDiscardableMemoryLimit(2048); \ |
+ } \ |
+ for (int i = 0; i < 3; ++i) { \ |
+ if (i == 1) \ |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardables[ordering[i]])); \ |
+ else \ |
+ ASSERT_NE(static_cast<void*>(NULL), Memory(discardables[ordering[i]])); \ |
+ } \ |
+} |
+ |
+#define DiscardableMemoryProviderPermutions(name, pressure) \ |
+DiscardableMemoryProviderPermutionTest(name, 0, 1, 2, pressure); \ |
+DiscardableMemoryProviderPermutionTest(name, 0, 2, 1, pressure); \ |
+DiscardableMemoryProviderPermutionTest(name, 1, 0, 2, pressure); \ |
+DiscardableMemoryProviderPermutionTest(name, 1, 2, 0, pressure); \ |
+DiscardableMemoryProviderPermutionTest(name, 2, 0, 1, pressure); \ |
+DiscardableMemoryProviderPermutionTest(name, 2, 1, 0, pressure); |
+ |
+DiscardableMemoryProviderPermutions(LRUDiscardedModeratePressure, true); |
+DiscardableMemoryProviderPermutions(LRUDiscardedExceedLimit, false); |
+ |
+TEST_F(DiscardableMemoryProviderTest, CriticalPressureFreesAllUnlocked) { |
+ DiscardableMemory discardables[3]; |
+ for (int i = 0; i < 3; ++i) { |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardables[i])); |
+ ASSERT_TRUE(discardables[i].InitializeAndLock(1024)); |
+ discardables[i].Unlock(); |
+ ASSERT_NE(static_cast<void*>(NULL), Memory(discardables[i])); |
+ } |
+ |
+ for (int i = 0; i < 3; ++i) { |
+ ASSERT_NE(DISCARDABLE_MEMORY_FAILED, discardables[i].Lock()); |
+ if (i > 0) |
+ discardables[i].Unlock(); |
+ } |
+ |
+ MemoryPressureListener::NotifyMemoryPressure( |
+ MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); |
+ RunLoop().RunUntilIdle(); |
+ |
+ for (int i = 0; i < 3; ++i) { |
+ if (discardables[i].is_locked()) |
+ ASSERT_NE(static_cast<void*>(NULL), Memory(discardables[i])); |
+ else |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardables[i])); |
+ } |
+} |
+ |
+TEST_F(DiscardableMemoryProviderTest, NormalDestruction) { |
+ { |
+ DiscardableMemory discardable; |
+ size_t size = 1024; |
+ ASSERT_TRUE(discardable.InitializeAndLock(size)); |
+ ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
+ ASSERT_EQ(1024u, bytes_allocated()); |
+ } |
+ ASSERT_TRUE(allocations().empty()); |
+ ASSERT_EQ(0u, bytes_allocated()); |
+} |
+ |
+TEST_F(DiscardableMemoryProviderTest, DestructionWhileLocked) { |
+ { |
+ DiscardableMemory discardable; |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
+ size_t size = 1024; |
+ ASSERT_TRUE(discardable.InitializeAndLock(size)); |
+ ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
+ ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
+ ASSERT_EQ(1024u, bytes_allocated()); |
+ ASSERT_TRUE(discardable.is_locked()); |
+ } |
+ // Should have ignored the "locked" status and freed the discardable memory. |
+ ASSERT_TRUE(allocations().empty()); |
+ ASSERT_EQ(0u, bytes_allocated()); |
+} |
+ |
+TEST_F(DiscardableMemoryProviderTest, MemoryBeforeLock) { |
+ DiscardableMemory discardable; |
+ // We *must* die if we are asked to vend a pointer to unlocked memory. |
+ EXPECT_DEATH(discardable.Memory(), ".*Check failed.*"); |
+} |
+ |
+TEST_F(DiscardableMemoryProviderTest, MemoryAfterUnlock) { |
+ DiscardableMemory discardable; |
+ ASSERT_EQ(static_cast<void*>(NULL), Memory(discardable)); |
+ size_t size = 1024; |
+ ASSERT_TRUE(discardable.InitializeAndLock(size)); |
+ ASSERT_NE(allocations().end(), allocations().Peek(&discardable)); |
+ ASSERT_NE(static_cast<void*>(NULL), Memory(discardable)); |
+ ASSERT_EQ(1024u, bytes_allocated()); |
+ ASSERT_TRUE(discardable.is_locked()); |
+ discardable.Unlock(); |
+ ASSERT_FALSE(discardable.is_locked()); |
+ // We *must* die if we are asked to vend a pointer to unlocked memory. |
+ EXPECT_DEATH(discardable.Memory(), ".*Check failed.*"); |
+} |
+ |
+} // namespace base |