| // Copyright 2018 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 "chromecast/browser/lru_renderer_cache.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/memory/ptr_util.h" |
| #include "chromecast/browser/renderer_prelauncher.h" |
| #include "content/public/browser/site_instance.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| #define EXPECT_CREATE_AND_PRELAUNCH(ptr, url) \ |
| ptr = new MockPrelauncher(&browser_context_, url); \ |
| EXPECT_CALL(*ptr, Prelaunch()); \ |
| EXPECT_CALL(factory_, Create(&browser_context_, url)) \ |
| .WillOnce(Return(ByMove(std::unique_ptr<MockPrelauncher>(ptr)))); |
| |
| #define EXPECT_EVICTION(ptr) EXPECT_CALL(*ptr, Destroy()); |
| |
| using ::testing::_; |
| using ::testing::ByMove; |
| using ::testing::Expectation; |
| using ::testing::Mock; |
| using ::testing::Return; |
| using ::testing::StrictMock; |
| |
| namespace chromecast { |
| |
| namespace { |
| |
| const GURL kUrl1("https://www.one.com"); |
| const GURL kUrl2("https://www.two.com"); |
| const GURL kUrl3("https://www.three.com"); |
| |
| } // namespace |
| |
| class MockPrelauncher : public RendererPrelauncher { |
| public: |
| MockPrelauncher(content::BrowserContext* browser_context, |
| const GURL& page_url) |
| : RendererPrelauncher(browser_context, |
| page_url) {} |
| virtual ~MockPrelauncher() { Destroy(); } |
| |
| MOCK_METHOD0(Prelaunch, void()); |
| MOCK_METHOD0(Destroy, void()); |
| }; |
| |
| class MockFactory : public RendererPrelauncherFactory { |
| public: |
| MOCK_METHOD2(Create, |
| std::unique_ptr<RendererPrelauncher>( |
| content::BrowserContext* browser_context, |
| const GURL& page_url)); |
| }; |
| |
| class LRURendererCacheTest : public testing::Test { |
| protected: |
| void SetUp() override {} |
| |
| void SetFactory() { |
| DCHECK(lru_cache_); |
| lru_cache_->SetFactoryForTesting(&factory_); |
| } |
| |
| content::TestBrowserThreadBundle threads_; |
| content::TestBrowserContext browser_context_; |
| MockFactory factory_; |
| std::unique_ptr<LRURendererCache> lru_cache_; |
| }; |
| |
| TEST_F(LRURendererCacheTest, SimpleTakeAndRelease) { |
| lru_cache_ = std::make_unique<LRURendererCache>(&browser_context_, 1); |
| SetFactory(); |
| MockPrelauncher* p1; |
| std::unique_ptr<RendererPrelauncher> taken; |
| |
| // Don't return a prelauncher the first time, since the cache is empty. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl1); |
| ASSERT_FALSE(taken); |
| // Cache: [] |
| // In-use: [ 1 ] |
| |
| // Releasing the prelauncher will cache it and prelaunch for later use. |
| EXPECT_CREATE_AND_PRELAUNCH(p1, kUrl1); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl1); |
| threads_.RunUntilIdle(); |
| // Cache: [ 1 ] |
| // In-use: [] |
| |
| // Get the cached prelauncher. |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl1); |
| ASSERT_TRUE(taken); |
| ASSERT_TRUE(taken->IsForURL(kUrl1)); |
| // Cache: [ ] |
| // In-use: [ 1 ] |
| |
| // Return the prelauncher again, it should be cached the same as before. |
| EXPECT_CREATE_AND_PRELAUNCH(p1, kUrl1); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl1); |
| threads_.RunUntilIdle(); |
| // Cache: [ 1 ] |
| // In-use: [] |
| } |
| |
| TEST_F(LRURendererCacheTest, SimpleCacheEviction) { |
| lru_cache_ = std::make_unique<LRURendererCache>(&browser_context_, 1); |
| SetFactory(); |
| MockPrelauncher* p1; |
| std::unique_ptr<RendererPrelauncher> taken; |
| |
| // Fill the cache. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl1); |
| ASSERT_FALSE(taken); |
| EXPECT_CREATE_AND_PRELAUNCH(p1, kUrl1); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl1); |
| threads_.RunUntilIdle(); |
| // Cache: [ 1 ] |
| // In-use: [] |
| |
| // Taking a different prelauncher destroys the cached one. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| EXPECT_EVICTION(p1); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl2); |
| ASSERT_FALSE(taken); |
| // Cache: [ ] |
| // In-use: [ 2 ] |
| } |
| |
| TEST_F(LRURendererCacheTest, CapacityOne) { |
| lru_cache_ = std::make_unique<LRURendererCache>(&browser_context_, 1); |
| SetFactory(); |
| MockPrelauncher* p1; |
| MockPrelauncher* p2; |
| std::unique_ptr<RendererPrelauncher> taken; |
| |
| // Don't return a prelauncher the first time, since the cache is empty. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl1); |
| ASSERT_FALSE(taken); |
| // Cache: [] |
| // In-use: [ 1 ] |
| |
| // Releasing the prelauncher will cache it and prelaunch for later use. |
| EXPECT_CREATE_AND_PRELAUNCH(p1, kUrl1); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl1); |
| threads_.RunUntilIdle(); |
| // Cache: [ 1 ] |
| // In-use: [] |
| |
| // Get the cached prelauncher. |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl1); |
| ASSERT_TRUE(taken); |
| ASSERT_TRUE(taken->IsForURL(kUrl1)); |
| // Cache: [ ] |
| // In-use: [ 1 ] |
| |
| // Return the prelauncher again, it should be cached the same as before. |
| EXPECT_CREATE_AND_PRELAUNCH(p1, kUrl1); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl1); |
| threads_.RunUntilIdle(); |
| // Cache: [ 1 ] |
| // In-use: [] |
| |
| // Getting the prelauncher for a non-cached URL will return nullptr. The cache |
| // will evict 1 to stay below the renderer limit. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| EXPECT_EVICTION(p1); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl2); |
| ASSERT_FALSE(taken); |
| // Cache: [ ] |
| // In-use: [ 2 ] |
| |
| // Return prelauncher 2, it should be cached. |
| EXPECT_CREATE_AND_PRELAUNCH(p2, kUrl2); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl2); |
| threads_.RunUntilIdle(); |
| // Cache: [ 2 ] |
| // In-use: [ ] |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl2); |
| ASSERT_TRUE(taken); |
| ASSERT_TRUE(taken->IsForURL(kUrl2)); |
| // Cache: [ ] |
| // In-use: [ 2 ] |
| |
| // Return prelauncher 2 once more, it will be cached. |
| EXPECT_CREATE_AND_PRELAUNCH(p2, kUrl2); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl2); |
| threads_.RunUntilIdle(); |
| // Cache: [ 2 ] |
| // In-use: [ ] |
| |
| // Prelauncher 1 was evicted when 2 was cached. Taking 1 will evict 2. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| EXPECT_EVICTION(p2); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl1); |
| ASSERT_FALSE(taken); |
| // Cache: [ ] |
| // In-use: [ 1 ] |
| |
| // Prelauncher 2 was evicted when 1 was taken. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl2); |
| ASSERT_FALSE(taken); |
| // Cache: [ ] |
| // In-use: [ 1, 2 ] |
| |
| // Returning one of the two in-use pages to the cache won't actually cache it, |
| // since there's still exactly 1 renderer in-use. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl2); |
| threads_.RunUntilIdle(); |
| // Cache: [ ] |
| // In-use: [ 1 ] |
| } |
| |
| TEST_F(LRURendererCacheTest, CapacityTwo) { |
| lru_cache_ = std::make_unique<LRURendererCache>(&browser_context_, 2); |
| SetFactory(); |
| MockPrelauncher* p1; |
| MockPrelauncher* p2; |
| std::unique_ptr<RendererPrelauncher> taken; |
| |
| // Take three renderers. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl1); |
| ASSERT_FALSE(taken); |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl2); |
| ASSERT_FALSE(taken); |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl3); |
| ASSERT_FALSE(taken); |
| // Cache: [] |
| // In-use: [ 1, 2, 3 ] |
| |
| // Don't cache renderer 3 since there are still 2 in use. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl3); |
| threads_.RunUntilIdle(); |
| // In-use: [ 1, 2 ] |
| |
| // Fill the cache with remaining 2 renderers. |
| EXPECT_CREATE_AND_PRELAUNCH(p2, kUrl2); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl2); |
| threads_.RunUntilIdle(); |
| EXPECT_CREATE_AND_PRELAUNCH(p1, kUrl1); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl1); |
| threads_.RunUntilIdle(); |
| // Cache: [ 1, 2 ] |
| // In-use: [ ] |
| |
| // Cache hit for renderer 1. |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl1); |
| ASSERT_TRUE(taken); |
| ASSERT_TRUE(taken->IsForURL(kUrl1)); |
| // Cache: [ 2 ] |
| // In-use: [ 1 ] |
| |
| // Return renderer 1. |
| EXPECT_CREATE_AND_PRELAUNCH(p1, kUrl1); |
| lru_cache_->ReleaseRendererPrelauncher(kUrl1); |
| threads_.RunUntilIdle(); |
| // Cache: [ 1, 2 ] |
| // In-use: [ ] |
| |
| // Evict the least-recently cached renderer (2). |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| EXPECT_EVICTION(p2); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl3); |
| ASSERT_FALSE(taken); |
| // Cache: [ 1 ] |
| // In-use: [ 3 ] |
| |
| // Getting renderer 2 will fail since it's no long cached. This will evict |
| // renderer 1. |
| EXPECT_CALL(factory_, Create(_, _)).Times(0); |
| EXPECT_EVICTION(p1); |
| taken = lru_cache_->TakeRendererPrelauncher(kUrl2); |
| ASSERT_FALSE(taken); |
| // Cache: [ ] |
| // In-use: [ 2, 3 ] |
| } |
| |
| } // namespace chromecast |