
This also removed the code from content, which doesn't belong there because it's for a feature in Chrome. In order to implement this in a unified way, I needed to create a public interface around ChildThread. A lot of the change is updating callers of ChildThread to use ChildThreadImpl instead. BUG=448473 Review URL: https://codereview.chromium.org/897563002 Cr-Commit-Position: refs/heads/master@{#314367}
251 lines
8.2 KiB
C++
251 lines
8.2 KiB
C++
// 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/callback.h"
|
|
#include "base/command_line.h"
|
|
#include "base/memory/discardable_memory.h"
|
|
#include "base/memory/scoped_vector.h"
|
|
#include "base/thread_task_runner_handle.h"
|
|
#include "content/common/resource_messages.h"
|
|
#include "content/common/websocket_messages.h"
|
|
#include "content/public/browser/content_browser_client.h"
|
|
#include "content/public/common/content_client.h"
|
|
#include "content/public/common/content_switches.h"
|
|
#include "content/public/renderer/content_renderer_client.h"
|
|
#include "content/renderer/render_process_impl.h"
|
|
#include "content/renderer/render_thread_impl.h"
|
|
#include "content/test/mock_render_process.h"
|
|
#include "content/test/render_thread_impl_browser_test_ipc_helper.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
// IPC messages for testing ----------------------------------------------------
|
|
|
|
#define IPC_MESSAGE_IMPL
|
|
#include "ipc/ipc_message_macros.h"
|
|
|
|
#undef IPC_MESSAGE_START
|
|
#define IPC_MESSAGE_START TestMsgStart
|
|
IPC_MESSAGE_CONTROL0(TestMsg_QuitRunLoop)
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// These tests leak memory, this macro disables the test when under the
|
|
// LeakSanitizer.
|
|
#ifdef LEAK_SANITIZER
|
|
#define WILL_LEAK(NAME) DISABLED_##NAME
|
|
#else
|
|
#define WILL_LEAK(NAME) NAME
|
|
#endif
|
|
|
|
namespace content {
|
|
namespace {
|
|
|
|
// FIXME: It would be great if there was a reusable mock SingleThreadTaskRunner
|
|
class TestTaskCounter : public base::SingleThreadTaskRunner {
|
|
public:
|
|
TestTaskCounter() : count_(0) {}
|
|
|
|
// SingleThreadTaskRunner implementation.
|
|
bool PostDelayedTask(const tracked_objects::Location&,
|
|
const base::Closure&,
|
|
base::TimeDelta) override {
|
|
base::AutoLock auto_lock(lock_);
|
|
count_++;
|
|
return true;
|
|
}
|
|
|
|
bool PostNonNestableDelayedTask(const tracked_objects::Location&,
|
|
const base::Closure&,
|
|
base::TimeDelta) override {
|
|
base::AutoLock auto_lock(lock_);
|
|
count_++;
|
|
return true;
|
|
}
|
|
|
|
bool RunsTasksOnCurrentThread() const override { return true; }
|
|
|
|
int NumTasksPosted() const {
|
|
base::AutoLock auto_lock(lock_);
|
|
return count_;
|
|
}
|
|
|
|
private:
|
|
~TestTaskCounter() override {}
|
|
|
|
mutable base::Lock lock_;
|
|
int count_;
|
|
};
|
|
|
|
#if defined(COMPILER_MSVC)
|
|
// See explanation for other RenderViewHostImpl which is the same issue.
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 4250)
|
|
#endif
|
|
|
|
class RenderThreadImplForTest : public RenderThreadImpl {
|
|
public:
|
|
RenderThreadImplForTest(const std::string& channel_id,
|
|
scoped_refptr<TestTaskCounter> test_task_counter)
|
|
: RenderThreadImpl(channel_id), test_task_counter_(test_task_counter) {}
|
|
|
|
~RenderThreadImplForTest() override {}
|
|
|
|
void SetResourceDispatchTaskQueue(
|
|
const scoped_refptr<base::SingleThreadTaskRunner>&) override {
|
|
// Use our TestTaskCounter instead.
|
|
RenderThreadImpl::SetResourceDispatchTaskQueue(test_task_counter_);
|
|
}
|
|
|
|
using ChildThreadImpl::OnMessageReceived;
|
|
|
|
private:
|
|
scoped_refptr<TestTaskCounter> test_task_counter_;
|
|
};
|
|
|
|
#if defined(COMPILER_MSVC)
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
void QuitTask(base::MessageLoop* message_loop) {
|
|
message_loop->QuitWhenIdle();
|
|
}
|
|
|
|
class QuitOnTestMsgFilter : public IPC::MessageFilter {
|
|
public:
|
|
explicit QuitOnTestMsgFilter(base::MessageLoop* message_loop)
|
|
: message_loop_(message_loop) {}
|
|
|
|
// IPC::MessageFilter overrides:
|
|
bool OnMessageReceived(const IPC::Message& message) override {
|
|
message_loop_->PostTask(FROM_HERE, base::Bind(&QuitTask, message_loop_));
|
|
return true;
|
|
}
|
|
|
|
bool GetSupportedMessageClasses(
|
|
std::vector<uint32>* supported_message_classes) const override {
|
|
supported_message_classes->push_back(TestMsgStart);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
~QuitOnTestMsgFilter() override {}
|
|
|
|
base::MessageLoop* message_loop_;
|
|
};
|
|
|
|
class RenderThreadImplBrowserTest : public testing::Test {
|
|
public:
|
|
void SetUp() override {
|
|
content_client_.reset(new ContentClient());
|
|
content_browser_client_.reset(new ContentBrowserClient());
|
|
content_renderer_client_.reset(new ContentRendererClient());
|
|
SetContentClient(content_client_.get());
|
|
SetBrowserClientForTesting(content_browser_client_.get());
|
|
SetRendererClientForTesting(content_renderer_client_.get());
|
|
|
|
test_helper_.reset(new RenderThreadImplBrowserIPCTestHelper());
|
|
|
|
mock_process_.reset(new MockRenderProcess);
|
|
test_task_counter_ = make_scoped_refptr(new TestTaskCounter());
|
|
|
|
// RenderThreadImpl expects the browser to pass these flags.
|
|
base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
|
|
base::CommandLine::StringVector old_argv = cmd->argv();
|
|
|
|
cmd->AppendSwitchASCII(switches::kNumRasterThreads, "1");
|
|
thread_ = new RenderThreadImplForTest(test_helper_->GetChannelId(),
|
|
test_task_counter_);
|
|
cmd->InitFromArgv(old_argv);
|
|
|
|
thread_->EnsureWebKitInitialized();
|
|
|
|
test_msg_filter_ = make_scoped_refptr(
|
|
new QuitOnTestMsgFilter(test_helper_->GetMessageLoop()));
|
|
thread_->AddFilter(test_msg_filter_.get());
|
|
}
|
|
|
|
scoped_refptr<TestTaskCounter> test_task_counter_;
|
|
scoped_ptr<ContentClient> content_client_;
|
|
scoped_ptr<ContentBrowserClient> content_browser_client_;
|
|
scoped_ptr<ContentRendererClient> content_renderer_client_;
|
|
scoped_ptr<RenderThreadImplBrowserIPCTestHelper> test_helper_;
|
|
scoped_ptr<MockRenderProcess> mock_process_;
|
|
scoped_refptr<QuitOnTestMsgFilter> test_msg_filter_;
|
|
RenderThreadImplForTest* thread_; // Owned by mock_process_.
|
|
std::string channel_id_;
|
|
};
|
|
|
|
void CheckRenderThreadInputHandlerManager(RenderThreadImpl* thread) {
|
|
ASSERT_TRUE(thread->input_handler_manager());
|
|
}
|
|
|
|
// Check that InputHandlerManager outlives compositor thread because it uses
|
|
// raw pointers to post tasks.
|
|
// Disabled under LeakSanitizer due to memory leaks. http://crbug.com/348994
|
|
TEST_F(RenderThreadImplBrowserTest,
|
|
WILL_LEAK(InputHandlerManagerDestroyedAfterCompositorThread)) {
|
|
ASSERT_TRUE(thread_->input_handler_manager());
|
|
|
|
thread_->compositor_message_loop_proxy()->PostTask(
|
|
FROM_HERE, base::Bind(&CheckRenderThreadInputHandlerManager, thread_));
|
|
}
|
|
|
|
// Checks that emulated discardable memory is discarded when the last widget
|
|
// is hidden.
|
|
// Disabled under LeakSanitizer due to memory leaks.
|
|
TEST_F(RenderThreadImplBrowserTest,
|
|
WILL_LEAK(EmulatedDiscardableMemoryDiscardedWhenWidgetsHidden)) {
|
|
thread_->WidgetCreated();
|
|
|
|
// Allocate 128MB of discardable memory.
|
|
ScopedVector<base::DiscardableMemory> discardable_memory;
|
|
for (int i = 0; i < 32; ++i) {
|
|
discardable_memory.push_back(
|
|
base::DiscardableMemory::CreateLockedMemoryWithType(
|
|
base::DISCARDABLE_MEMORY_TYPE_EMULATED, 4 * 1024 * 1024).release());
|
|
ASSERT_TRUE(discardable_memory.back());
|
|
discardable_memory.back()->Unlock();
|
|
}
|
|
|
|
// Hide all widgets.
|
|
thread_->WidgetHidden();
|
|
|
|
// Count how much memory is left, should be at most one block.
|
|
int blocks_left = 0;
|
|
for (auto iter = discardable_memory.begin(); iter != discardable_memory.end();
|
|
++iter) {
|
|
if ((*iter)->Lock() == base::DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS)
|
|
++blocks_left;
|
|
}
|
|
EXPECT_LE(blocks_left, 1);
|
|
|
|
thread_->WidgetDestroyed();
|
|
}
|
|
|
|
// Disabled under LeakSanitizer due to memory leaks.
|
|
TEST_F(RenderThreadImplBrowserTest,
|
|
WILL_LEAK(ResourceDispatchIPCTasksGoThroughScheduler)) {
|
|
test_helper_->Sender()->Send(new ResourceHostMsg_FollowRedirect(0));
|
|
test_helper_->Sender()->Send(new TestMsg_QuitRunLoop());
|
|
|
|
test_helper_->GetMessageLoop()->Run();
|
|
EXPECT_EQ(1, test_task_counter_->NumTasksPosted());
|
|
}
|
|
|
|
// Disabled under LeakSanitizer due to memory leaks.
|
|
TEST_F(RenderThreadImplBrowserTest,
|
|
WILL_LEAK(NonResourceDispatchIPCTasksDontGoThroughScheduler)) {
|
|
// NOTE other than not being a resource message, the actual message is
|
|
// unimportant.
|
|
test_helper_->Sender()->Send(new WebSocketMsg_NotifyFailure(1, ""));
|
|
test_helper_->Sender()->Send(new TestMsg_QuitRunLoop());
|
|
|
|
test_helper_->GetMessageLoop()->Run();
|
|
|
|
EXPECT_EQ(0, test_task_counter_->NumTasksPosted());
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace content
|