blob: 6b1ac99d1faa77523ff8b6171dcabefbe1d49378 [file] [log] [blame]
// Copyright 2015 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 "extensions/browser/load_monitoring_extension_host_queue.h"
#include <stddef.h>
#include <limits>
#include <vector>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "extensions/browser/deferred_start_render_host.h"
#include "extensions/browser/extensions_test.h"
#include "extensions/browser/serial_extension_host_queue.h"
namespace extensions {
namespace {
class StubDeferredStartRenderHost : public DeferredStartRenderHost {
public:
// Returns true if this host is being observed by |observer|.
bool IsObservedBy(DeferredStartRenderHostObserver* observer) {
return observers_.count(observer) > 0;
}
private:
// DeferredStartRenderHost:
void AddDeferredStartRenderHostObserver(
DeferredStartRenderHostObserver* observer) override {
observers_.insert(observer);
}
void RemoveDeferredStartRenderHostObserver(
DeferredStartRenderHostObserver* observer) override {
observers_.erase(observer);
}
void CreateRenderViewNow() override {}
std::set<DeferredStartRenderHostObserver*> observers_;
};
const size_t g_invalid_size_t = std::numeric_limits<size_t>::max();
} // namespace
class LoadMonitoringExtensionHostQueueTest : public ExtensionsTest {
public:
LoadMonitoringExtensionHostQueueTest()
: finished_(false),
// Arbitrary choice of an invalid size_t.
num_queued_(g_invalid_size_t),
num_loaded_(g_invalid_size_t),
max_awaiting_loading_(g_invalid_size_t),
max_active_loading_(g_invalid_size_t) {}
void SetUp() override {
queue_.reset(new LoadMonitoringExtensionHostQueue(
// Use a SerialExtensionHostQueue because it's simple.
std::unique_ptr<ExtensionHostQueue>(new SerialExtensionHostQueue()),
base::TimeDelta(), // no delay, easier to test
base::Bind(&LoadMonitoringExtensionHostQueueTest::Finished,
base::Unretained(this))));
}
protected:
// Creates a new DeferredStartRenderHost. Ownership is held by this class,
// not passed to caller.
StubDeferredStartRenderHost* CreateHost() {
stubs_.push_back(base::MakeUnique<StubDeferredStartRenderHost>());
return stubs_.back().get();
}
// Our single LoadMonitoringExtensionHostQueue instance.
LoadMonitoringExtensionHostQueue* queue() { return queue_.get(); }
// Returns true if the queue has finished monitoring.
bool finished() const { return finished_; }
// These are available after the queue has finished (in which case finished()
// will return true).
size_t num_queued() { return num_queued_; }
size_t num_loaded() { return num_loaded_; }
size_t max_awaiting_loading() { return max_awaiting_loading_; }
size_t max_active_loading() { return max_active_loading_; }
private:
// Callback when queue has finished monitoring.
void Finished(size_t num_queued,
size_t num_loaded,
size_t max_awaiting_loading,
size_t max_active_loading) {
CHECK(!finished_);
finished_ = true;
num_queued_ = num_queued;
num_loaded_ = num_loaded;
max_awaiting_loading_ = max_awaiting_loading;
max_active_loading_ = max_active_loading;
}
content::TestBrowserThreadBundle thread_bundle_;
std::unique_ptr<LoadMonitoringExtensionHostQueue> queue_;
std::vector<std::unique_ptr<StubDeferredStartRenderHost>> stubs_;
// Set after the queue has finished monitoring.
bool finished_;
size_t num_queued_;
size_t num_loaded_;
size_t max_awaiting_loading_;
size_t max_active_loading_;
};
// Tests that if monitoring is never started, nor any hosts added, nothing is
// recorded.
TEST_F(LoadMonitoringExtensionHostQueueTest, NeverStarted) {
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(finished());
}
// Tests that if monitoring has started but no hosts added, it's recorded as 0.
TEST_F(LoadMonitoringExtensionHostQueueTest, NoHosts) {
queue()->StartMonitoring();
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(finished());
EXPECT_EQ(0u, num_queued());
EXPECT_EQ(0u, num_loaded());
EXPECT_EQ(0u, max_awaiting_loading());
EXPECT_EQ(0u, max_active_loading());
}
// Tests that adding a host starts monitoring.
TEST_F(LoadMonitoringExtensionHostQueueTest, AddOneHost) {
queue()->Add(CreateHost());
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(finished());
EXPECT_EQ(1u, num_queued());
EXPECT_EQ(0u, num_loaded());
EXPECT_EQ(1u, max_awaiting_loading());
EXPECT_EQ(0u, max_active_loading());
}
// Tests that a host added and removed is still recorded, but not as a load
// finished.
TEST_F(LoadMonitoringExtensionHostQueueTest, AddAndRemoveOneHost) {
DeferredStartRenderHost* host = CreateHost();
queue()->Add(host);
queue()->Remove(host);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(finished());
EXPECT_EQ(1u, num_queued());
EXPECT_EQ(0u, num_loaded());
EXPECT_EQ(1u, max_awaiting_loading());
EXPECT_EQ(0u, max_active_loading());
}
// Tests adding and starting a single host.
TEST_F(LoadMonitoringExtensionHostQueueTest, AddAndStartOneHost) {
DeferredStartRenderHost* host = CreateHost();
queue()->Add(host);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(finished());
EXPECT_EQ(1u, num_queued());
EXPECT_EQ(0u, num_loaded());
EXPECT_EQ(1u, max_awaiting_loading());
EXPECT_EQ(1u, max_active_loading());
}
// Tests adding and destroying a single host without starting it.
TEST_F(LoadMonitoringExtensionHostQueueTest, AddAndDestroyOneHost) {
DeferredStartRenderHost* host = CreateHost();
queue()->Add(host);
queue()->OnDeferredStartRenderHostDestroyed(host);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(finished());
EXPECT_EQ(1u, num_queued());
EXPECT_EQ(0u, num_loaded());
EXPECT_EQ(1u, max_awaiting_loading());
EXPECT_EQ(0u, max_active_loading());
}
// Tests adding, starting, and stopping a single host.
TEST_F(LoadMonitoringExtensionHostQueueTest, AddAndStartAndStopOneHost) {
DeferredStartRenderHost* host = CreateHost();
queue()->Add(host);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host);
queue()->OnDeferredStartRenderHostDidStopFirstLoad(host);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(finished());
EXPECT_EQ(1u, num_queued());
EXPECT_EQ(1u, num_loaded());
EXPECT_EQ(1u, max_awaiting_loading());
EXPECT_EQ(1u, max_active_loading());
}
// Tests adding, starting, and stopping a single host - twice.
TEST_F(LoadMonitoringExtensionHostQueueTest, AddAndStartAndStopOneHostTwice) {
DeferredStartRenderHost* host = CreateHost();
queue()->Add(host);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host);
queue()->OnDeferredStartRenderHostDidStopFirstLoad(host);
// Re-starting loading should also be recorded fine (e.g. navigations could
// trigger this).
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host);
queue()->OnDeferredStartRenderHostDidStopFirstLoad(host);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(finished());
EXPECT_EQ(1u, num_queued());
EXPECT_EQ(2u, num_loaded()); // 2 loaded this time, because we ran twice
EXPECT_EQ(1u, max_awaiting_loading());
EXPECT_EQ(1u, max_active_loading());
}
// Tests adding, starting, and destroying (i.e. an implicit stop) a single host.
TEST_F(LoadMonitoringExtensionHostQueueTest, AddAndStartAndDestroyOneHost) {
DeferredStartRenderHost* host = CreateHost();
queue()->Add(host);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host);
queue()->OnDeferredStartRenderHostDestroyed(host);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(finished());
EXPECT_EQ(1u, num_queued());
EXPECT_EQ(1u, num_loaded());
EXPECT_EQ(1u, max_awaiting_loading());
EXPECT_EQ(1u, max_active_loading());
}
// Tests adding, starting, and removing a single host.
TEST_F(LoadMonitoringExtensionHostQueueTest, AddAndStartAndRemoveOneHost) {
DeferredStartRenderHost* host = CreateHost();
queue()->Add(host);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host);
queue()->Remove(host);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(finished());
EXPECT_EQ(1u, num_queued());
EXPECT_EQ(0u, num_loaded());
EXPECT_EQ(1u, max_awaiting_loading());
EXPECT_EQ(1u, max_active_loading());
}
// Tests monitoring a sequence of hosts.
TEST_F(LoadMonitoringExtensionHostQueueTest, Sequence) {
// Scenario:
//
// 6 hosts will be added, only 5 will start loading, with a maximum of 4 in
// the queue and 3 loading at any time. Only 2 will finish.
DeferredStartRenderHost* host1 = CreateHost();
DeferredStartRenderHost* host2 = CreateHost();
DeferredStartRenderHost* host3 = CreateHost();
DeferredStartRenderHost* host4 = CreateHost();
DeferredStartRenderHost* host5 = CreateHost();
DeferredStartRenderHost* host6 = CreateHost();
queue()->Add(host1);
queue()->Add(host2);
queue()->Add(host3);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host1);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host2);
queue()->OnDeferredStartRenderHostDidStopFirstLoad(host1);
queue()->Add(host4);
queue()->Add(host5);
queue()->Add(host6);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host3);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host4);
queue()->OnDeferredStartRenderHostDidStopFirstLoad(host4);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host5);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(finished());
EXPECT_EQ(6u, num_queued());
EXPECT_EQ(2u, num_loaded());
EXPECT_EQ(4u, max_awaiting_loading());
EXPECT_EQ(3u, max_active_loading());
// Complete a realistic sequence by stopping and/or destroying all hosts.
queue()->OnDeferredStartRenderHostDestroyed(host1);
queue()->OnDeferredStartRenderHostDidStopFirstLoad(host2);
queue()->OnDeferredStartRenderHostDestroyed(host2);
queue()->OnDeferredStartRenderHostDidStopFirstLoad(host3);
queue()->OnDeferredStartRenderHostDestroyed(host3);
queue()->OnDeferredStartRenderHostDestroyed(host4);
queue()->OnDeferredStartRenderHostDestroyed(host5); // never stopped
queue()->OnDeferredStartRenderHostDestroyed(host6); // never started/stopped
}
// Tests that the queue is observing Hosts from adding them through to being
// removed - that the load sequence itself is irrelevant.
//
// This is an unfortunate implementation-style test, but it used to be a bug
// and difficult to catch outside of a proper test framework - one in which we
// don't have to trigger events by hand.
TEST_F(LoadMonitoringExtensionHostQueueTest, ObserverLifetime) {
StubDeferredStartRenderHost* host1 = CreateHost();
StubDeferredStartRenderHost* host2 = CreateHost();
StubDeferredStartRenderHost* host3 = CreateHost();
StubDeferredStartRenderHost* host4 = CreateHost();
EXPECT_FALSE(host1->IsObservedBy(queue()));
EXPECT_FALSE(host2->IsObservedBy(queue()));
EXPECT_FALSE(host3->IsObservedBy(queue()));
EXPECT_FALSE(host4->IsObservedBy(queue()));
queue()->Add(host1);
queue()->Add(host2);
queue()->Add(host3);
queue()->Add(host4);
EXPECT_TRUE(host1->IsObservedBy(queue()));
EXPECT_TRUE(host2->IsObservedBy(queue()));
EXPECT_TRUE(host3->IsObservedBy(queue()));
EXPECT_TRUE(host4->IsObservedBy(queue()));
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host1);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host2);
queue()->OnDeferredStartRenderHostDidStartFirstLoad(host3);
// host4 will test that we Remove before Starting - so don't start.
EXPECT_TRUE(host1->IsObservedBy(queue()));
EXPECT_TRUE(host2->IsObservedBy(queue()));
EXPECT_TRUE(host3->IsObservedBy(queue()));
EXPECT_TRUE(host4->IsObservedBy(queue()));
queue()->OnDeferredStartRenderHostDidStopFirstLoad(host1);
queue()->OnDeferredStartRenderHostDestroyed(host2);
EXPECT_TRUE(host1->IsObservedBy(queue()));
EXPECT_TRUE(host2->IsObservedBy(queue()));
queue()->Remove(host1);
queue()->Remove(host2);
queue()->Remove(host3);
queue()->Remove(host4);
EXPECT_FALSE(host1->IsObservedBy(queue()));
EXPECT_FALSE(host2->IsObservedBy(queue()));
EXPECT_FALSE(host3->IsObservedBy(queue()));
EXPECT_FALSE(host4->IsObservedBy(queue()));
}
} // namespace extensions