| 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..27bfc1bc38272470d1e2dffbdc63d2e95d4cbacf
|
| --- /dev/null
|
| +++ b/base/memory/discardable_memory_provider_unittest.cc
|
| @@ -0,0 +1,288 @@
|
| +// Copyright 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) {
|
| + // We set a provider here for two reasons:
|
| + // 1. It ensures that one test cannot affect the next, and
|
| + // 2. Since the provider listens for pressure notifications on the thread
|
| + // it was created on, if we create it on the test thread, we can run
|
| + // the test thread's message loop until idle when we want to process
|
| + // on of these notifications.
|
| + DiscardableMemoryProvider::SetInstanceForTest(provider_.get());
|
| + }
|
| +
|
| + 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
|
|
|