Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(639)

Unified Diff: content/browser/renderer_host/render_process_host_impl_unittest.cc

Issue 16267002: Re-fix http://crbug.com/87176, and add a test. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Much shorter test Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/render_process_host_impl_unittest.cc
diff --git a/content/browser/renderer_host/render_process_host_impl_unittest.cc b/content/browser/renderer_host/render_process_host_impl_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5e22b71c5dce538a7ce941596974c3921054e83d
--- /dev/null
+++ b/content/browser/renderer_host/render_process_host_impl_unittest.cc
@@ -0,0 +1,257 @@
+// 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 "content/browser/renderer_host/render_process_host_impl.h"
+
+#include "base/command_line.h"
+#include "base/process_util.h"
+#include "base/run_loop.h"
+#include "content/browser/child_process_launcher.h"
+#include "content/browser/loader/resource_dispatcher_host_impl.h"
+#include "content/browser/site_instance_impl.h"
+#include "content/browser/storage_partition_impl.h"
+#include "content/browser/webui/web_ui_controller_factory_registry.h"
+#include "content/common/child_process_messages.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/render_process_host_factory.h"
+#include "content/public/common/sandboxed_process_launcher_delegate.h"
+#include "content/public/test/test_browser_context.h"
+#include "content/public/test/test_browser_thread.h"
+#include "content/test/test_content_browser_client.h"
+#include "content/test/test_render_view_host_factory.h"
+#include "content/test/test_web_contents.h"
+#include "ipc/ipc_switches.h"
+#include "ipc/ipc_test_sink.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+namespace {
+
+class FakeChildProcessLauncher;
+
+struct LaunchedChildProcess {
+ // Becomes NULL after the FakeChildProcessLauncher is destroyed.
+ FakeChildProcessLauncher* launcher_;
+ // Collects messages sent to the child process.
+ IPC::TestSink messages_;
+};
+
+class FakeChildProcessLauncher : public ChildProcessLauncher {
+ public:
+ FakeChildProcessLauncher(LaunchedChildProcess* info,
+ const IPC::ChannelHandle& channel,
+ Client* client)
+ : info_(info),
+ client_(client),
+ starting_(true),
+ termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
+ channel_(new IPC::ChannelProxy(
+ channel,
+ IPC::Channel::MODE_CLIENT,
+ &info->messages_,
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))),
+ weak_this_factory_(this) {
+ info_->launcher_ = this;
+
+ // Automatically start the child process so the RenderProcessHostImpl sends
+ // messages to it. To mimic real child processes, don't start instantly.
+ base::MessageLoopProxy::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeChildProcessLauncher::Start,
+ weak_this_factory_.GetWeakPtr()));
+ }
+
+ virtual ~FakeChildProcessLauncher() { info_->launcher_ = NULL; }
+
+ void Send(IPC::Message* msg) {
+ EXPECT_TRUE(channel_->Send(msg));
+ }
+
+ // Implementation of ChildProcessLauncher.
+ virtual bool IsStarting() OVERRIDE { return starting_; }
+ virtual base::ProcessHandle GetHandle() OVERRIDE {
+ return base::Process::Current().handle();
+ }
+ virtual base::TerminationStatus GetChildTerminationStatus(bool known_dead,
+ int* exit_code)
+ OVERRIDE {
+ return termination_status_;
+ }
+ virtual void SetProcessBackgrounded(bool background) OVERRIDE {}
+ virtual void SetTerminateChildOnShutdown(bool terminate_on_shutdown)
+ OVERRIDE {}
+
+ private:
+ void Start() {
+ starting_ = false;
+ client_->OnProcessLaunched();
+ }
+
+ LaunchedChildProcess* info_;
+ Client* client_;
+ bool starting_;
+ base::TerminationStatus termination_status_;
+ scoped_ptr<IPC::ChannelProxy> channel_;
+ base::WeakPtrFactory<FakeChildProcessLauncher> weak_this_factory_;
+};
+
+scoped_ptr<ChildProcessLauncher> NewFakeChildProcessLauncher(
+ ScopedVector<LaunchedChildProcess>* child_processes,
+#if defined(OS_WIN)
+ SandboxedProcessLauncherDelegate* delegate,
+#elif defined(OS_POSIX)
+ bool use_zygote,
+ const base::EnvironmentVector& environ,
+ int ipcfd,
+#endif
+ CommandLine* cmd_line,
+ int child_process_id,
+ ChildProcessLauncher::Client* client) {
+#if defined(OS_WIN)
+ delete delegate;
+#endif
+ scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
+ child_processes->push_back(new LaunchedChildProcess());
+ std::string channel_id =
+ cmd_line->GetSwitchValueASCII(switches::kProcessChannelID);
+ return scoped_ptr<ChildProcessLauncher>(new FakeChildProcessLauncher(
+ child_processes->back(),
+ IPC::ChannelHandle(channel_id
+#if defined(OS_POSIX)
+ ,
+ base::FileDescriptor(ipcfd, false)
+#endif
+ ),
+ client));
+}
+
+class RPHIBrowserContext : public TestBrowserContext {
+ private:
+ virtual net::URLRequestContextGetter* GetRequestContextForRenderProcess(
+ int renderer_child_id) OVERRIDE {
+ return GetRequestContext();
+ }
+};
+
+class RPHIBrowserClient : public TestContentBrowserClient {
+ public:
+ RPHIBrowserClient()
+ : request_context_getter_(new net::TestURLRequestContextGetter(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))) {}
+
+ virtual net::URLRequestContextGetter* CreateRequestContext(
+ BrowserContext* browser_context,
+ ProtocolHandlerMap* protocol_handlers) OVERRIDE {
+ return request_context_getter_.get();
+ }
+
+ private:
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
+};
+
+class FakeChildProcessRPHFactory : public RenderProcessHostFactory {
+ public:
+ FakeChildProcessRPHFactory(
+ ScopedVector<LaunchedChildProcess>* child_processes,
+ SiteInstance* site_instance)
+ : child_processes_(child_processes), site_instance_(site_instance) {
+ SiteInstanceImpl::set_render_process_host_factory(this);
+ }
+ virtual ~FakeChildProcessRPHFactory() {
+ SiteInstanceImpl::set_render_process_host_factory(NULL);
+ }
+
+ virtual RenderProcessHost* CreateRenderProcessHost(
+ BrowserContext* browser_context) const OVERRIDE {
+ RenderProcessHostImpl* host =
+ new RenderProcessHostImpl(browser_context,
+ static_cast<StoragePartitionImpl*>(
+ TestBrowserContext::GetStoragePartition(
+ browser_context, site_instance_)),
+ false,
+ false);
+ host->SetChildProcessLauncherFactory(
+ base::Bind(&NewFakeChildProcessLauncher, child_processes_));
+ return host;
+ }
+
+ private:
+ ScopedVector<LaunchedChildProcess>* child_processes_;
+ SiteInstance* site_instance_;
Charlie Reis 2013/06/05 19:07:10 It's not clear why this needs a SiteInstance, sinc
Jeffrey Yasskin 2013/06/05 21:23:27 Sure, done. Another possible refactoring would be
Charlie Reis 2013/06/05 21:54:09 Good point! That seems like a better solution, so
Jeffrey Yasskin 2013/06/05 22:53:20 I've put that in https://codereview.chromium.org/1
+};
+
+class RenderProcessHostImplTest : public testing::Test {
+ public:
+ RenderProcessHostImplTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ webkit_thread_(BrowserThread::WEBKIT_DEPRECATED, &message_loop_),
+ file_user_blocking_thread_(BrowserThread::FILE_USER_BLOCKING,
+ &message_loop_),
+ io_thread_(BrowserThread::IO, &message_loop_),
+ old_browser_client_(SetBrowserClientForTesting(&test_browser_client_)) {
+ }
+
+ virtual ~RenderProcessHostImplTest() {
+ base::RunLoop().RunUntilIdle();
+ SetBrowserClientForTesting(old_browser_client_);
+ }
+
+ protected:
+ ScopedVector<LaunchedChildProcess> child_processes_;
+ base::MessageLoopForIO message_loop_;
+ TestBrowserThread ui_thread_;
+ TestBrowserThread webkit_thread_;
+ TestBrowserThread file_user_blocking_thread_;
+ TestBrowserThread io_thread_;
+
+ RPHIBrowserContext browser_context_;
+ RPHIBrowserClient test_browser_client_;
+ ContentBrowserClient* old_browser_client_;
+ ResourceDispatcherHostImpl resource_dispatcher_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderProcessHostImplTest);
+};
+
+// http://crbug.com/87176
+TEST_F(RenderProcessHostImplTest,
+ RejectShutdownFromChildProcessWithOneActiveView) {
+ const GURL kUrl("http://example.com");
+
+ scoped_refptr<SiteInstance> site(
+ SiteInstance::CreateForURL(&browser_context_, kUrl));
+ FakeChildProcessRPHFactory rph_factory(&child_processes_, site.get());
+
+ WebContents::CreateParams params(&browser_context_, site.get());
+ scoped_ptr<WebContentsImpl> tab(
+ WebContentsImpl::CreateWithOpener(params, NULL));
+ tab->GetController().LoadURL(kUrl, Referrer(), PAGE_TRANSITION_TYPED, "");
Charlie Reis 2013/06/05 19:07:10 How does example.com commit? Am I missing a part
Jeffrey Yasskin 2013/06/05 21:23:27 No, AFAIK, there's no hard-coded response for it,
Charlie Reis 2013/06/05 21:54:09 Well, we usually use a TestWebContents and call Te
Jeffrey Yasskin 2013/06/05 22:53:20 I started this test with a TestWebContents, but Te
+ base::RunLoop().RunUntilIdle();
+ ASSERT_EQ(1U, child_processes_.size());
+
+ child_processes_[0]->messages_.ClearMessages();
+
+ // Double-check that the loading tab isn't pending.
+ EXPECT_EQ(NULL,
Charlie Reis 2013/06/05 19:07:10 Any reason not to use EXPECT_FALSE? That seems to
Jeffrey Yasskin 2013/06/05 21:23:27 No particular reason. Done.
+ tab->GetRenderManagerForTesting()->pending_render_view_host());
+ EXPECT_EQ(1,
+ static_cast<RenderProcessHostImpl*>(
+ tab->GetSiteInstance()->GetProcess())->GetActiveViewCount());
+
+ // Sometimes the renderer process's ShutdownRequest (corresponding to the
+ // ViewMsg_WasSwappedOut from a previous navigation) doesn't arrive until
Charlie Reis 2013/06/05 19:07:10 Interesting. I like that we don't have to simulat
+ // after the browser process decides to re-use the renderer for a new purpose.
+ // This test makes sure the browser doesn't let the renderer die in that case.
+ child_processes_[0]->launcher_
+ ->Send(new ChildProcessHostMsg_ShutdownRequest());
Charlie Reis 2013/06/05 19:07:10 nit: -> on previous line
Jeffrey Yasskin 2013/06/05 21:23:27 Done. Since clang-format puts the -> at the beginn
Charlie Reis 2013/06/05 21:54:09 Thanks. Not sure if there's an official rule on t
+ base::RunLoop().RunUntilIdle();
+ EXPECT_EQ(NULL,
Charlie Reis 2013/06/05 19:07:10 EXPECT_FALSE?
Jeffrey Yasskin 2013/06/05 21:23:27 Done.
+ child_processes_[0]->messages_.GetFirstMessageMatching(
+ ChildProcessMsg_Shutdown::ID))
+ << "Host sent a message confirming that the renderer process should shut "
+ << "down even though it was still being used.";
+}
+
+} // namespace
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698