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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/renderer_host/render_process_host_impl.h"
6
7 #include "base/command_line.h"
8 #include "base/process_util.h"
9 #include "base/run_loop.h"
10 #include "content/browser/child_process_launcher.h"
11 #include "content/browser/loader/resource_dispatcher_host_impl.h"
12 #include "content/browser/site_instance_impl.h"
13 #include "content/browser/storage_partition_impl.h"
14 #include "content/browser/webui/web_ui_controller_factory_registry.h"
15 #include "content/common/child_process_messages.h"
16 #include "content/common/view_messages.h"
17 #include "content/public/browser/render_process_host_factory.h"
18 #include "content/public/common/sandboxed_process_launcher_delegate.h"
19 #include "content/public/test/test_browser_context.h"
20 #include "content/public/test/test_browser_thread.h"
21 #include "content/test/test_content_browser_client.h"
22 #include "content/test/test_render_view_host_factory.h"
23 #include "content/test/test_web_contents.h"
24 #include "ipc/ipc_switches.h"
25 #include "ipc/ipc_test_sink.h"
26 #include "net/url_request/url_request_test_util.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 namespace content {
30 namespace {
31
32 class FakeChildProcessLauncher;
33
34 struct LaunchedChildProcess {
35 // Becomes NULL after the FakeChildProcessLauncher is destroyed.
36 FakeChildProcessLauncher* launcher_;
37 // Collects messages sent to the child process.
38 IPC::TestSink messages_;
39 };
40
41 class FakeChildProcessLauncher : public ChildProcessLauncher {
42 public:
43 FakeChildProcessLauncher(LaunchedChildProcess* info,
44 const IPC::ChannelHandle& channel,
45 Client* client)
46 : info_(info),
47 client_(client),
48 starting_(true),
49 termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION),
50 channel_(new IPC::ChannelProxy(
51 channel,
52 IPC::Channel::MODE_CLIENT,
53 &info->messages_,
54 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))),
55 weak_this_factory_(this) {
56 info_->launcher_ = this;
57
58 // Automatically start the child process so the RenderProcessHostImpl sends
59 // messages to it. To mimic real child processes, don't start instantly.
60 base::MessageLoopProxy::current()->PostTask(
61 FROM_HERE,
62 base::Bind(&FakeChildProcessLauncher::Start,
63 weak_this_factory_.GetWeakPtr()));
64 }
65
66 virtual ~FakeChildProcessLauncher() { info_->launcher_ = NULL; }
67
68 void Send(IPC::Message* msg) {
69 EXPECT_TRUE(channel_->Send(msg));
70 }
71
72 // Implementation of ChildProcessLauncher.
73 virtual bool IsStarting() OVERRIDE { return starting_; }
74 virtual base::ProcessHandle GetHandle() OVERRIDE {
75 return base::Process::Current().handle();
76 }
77 virtual base::TerminationStatus GetChildTerminationStatus(bool known_dead,
78 int* exit_code)
79 OVERRIDE {
80 return termination_status_;
81 }
82 virtual void SetProcessBackgrounded(bool background) OVERRIDE {}
83 virtual void SetTerminateChildOnShutdown(bool terminate_on_shutdown)
84 OVERRIDE {}
85
86 private:
87 void Start() {
88 starting_ = false;
89 client_->OnProcessLaunched();
90 }
91
92 LaunchedChildProcess* info_;
93 Client* client_;
94 bool starting_;
95 base::TerminationStatus termination_status_;
96 scoped_ptr<IPC::ChannelProxy> channel_;
97 base::WeakPtrFactory<FakeChildProcessLauncher> weak_this_factory_;
98 };
99
100 scoped_ptr<ChildProcessLauncher> NewFakeChildProcessLauncher(
101 ScopedVector<LaunchedChildProcess>* child_processes,
102 #if defined(OS_WIN)
103 SandboxedProcessLauncherDelegate* delegate,
104 #elif defined(OS_POSIX)
105 bool use_zygote,
106 const base::EnvironmentVector& environ,
107 int ipcfd,
108 #endif
109 CommandLine* cmd_line,
110 int child_process_id,
111 ChildProcessLauncher::Client* client) {
112 #if defined(OS_WIN)
113 delete delegate;
114 #endif
115 scoped_ptr<CommandLine> cmd_line_deleter(cmd_line);
116 child_processes->push_back(new LaunchedChildProcess());
117 std::string channel_id =
118 cmd_line->GetSwitchValueASCII(switches::kProcessChannelID);
119 return scoped_ptr<ChildProcessLauncher>(new FakeChildProcessLauncher(
120 child_processes->back(),
121 IPC::ChannelHandle(channel_id
122 #if defined(OS_POSIX)
123 ,
124 base::FileDescriptor(ipcfd, false)
125 #endif
126 ),
127 client));
128 }
129
130 class RPHIBrowserContext : public TestBrowserContext {
131 private:
132 virtual net::URLRequestContextGetter* GetRequestContextForRenderProcess(
133 int renderer_child_id) OVERRIDE {
134 return GetRequestContext();
135 }
136 };
137
138 class RPHIBrowserClient : public TestContentBrowserClient {
139 public:
140 RPHIBrowserClient()
141 : request_context_getter_(new net::TestURLRequestContextGetter(
142 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))) {}
143
144 virtual net::URLRequestContextGetter* CreateRequestContext(
145 BrowserContext* browser_context,
146 ProtocolHandlerMap* protocol_handlers) OVERRIDE {
147 return request_context_getter_.get();
148 }
149
150 private:
151 scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
152 };
153
154 class FakeChildProcessRPHFactory : public RenderProcessHostFactory {
155 public:
156 FakeChildProcessRPHFactory(
157 ScopedVector<LaunchedChildProcess>* child_processes,
158 SiteInstance* site_instance)
159 : child_processes_(child_processes), site_instance_(site_instance) {
160 SiteInstanceImpl::set_render_process_host_factory(this);
161 }
162 virtual ~FakeChildProcessRPHFactory() {
163 SiteInstanceImpl::set_render_process_host_factory(NULL);
164 }
165
166 virtual RenderProcessHost* CreateRenderProcessHost(
167 BrowserContext* browser_context) const OVERRIDE {
168 RenderProcessHostImpl* host =
169 new RenderProcessHostImpl(browser_context,
170 static_cast<StoragePartitionImpl*>(
171 TestBrowserContext::GetStoragePartition(
172 browser_context, site_instance_)),
173 false,
174 false);
175 host->SetChildProcessLauncherFactory(
176 base::Bind(&NewFakeChildProcessLauncher, child_processes_));
177 return host;
178 }
179
180 private:
181 ScopedVector<LaunchedChildProcess>* child_processes_;
182 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
183 };
184
185 class RenderProcessHostImplTest : public testing::Test {
186 public:
187 RenderProcessHostImplTest()
188 : ui_thread_(BrowserThread::UI, &message_loop_),
189 webkit_thread_(BrowserThread::WEBKIT_DEPRECATED, &message_loop_),
190 file_user_blocking_thread_(BrowserThread::FILE_USER_BLOCKING,
191 &message_loop_),
192 io_thread_(BrowserThread::IO, &message_loop_),
193 old_browser_client_(SetBrowserClientForTesting(&test_browser_client_)) {
194 }
195
196 virtual ~RenderProcessHostImplTest() {
197 base::RunLoop().RunUntilIdle();
198 SetBrowserClientForTesting(old_browser_client_);
199 }
200
201 protected:
202 ScopedVector<LaunchedChildProcess> child_processes_;
203 base::MessageLoopForIO message_loop_;
204 TestBrowserThread ui_thread_;
205 TestBrowserThread webkit_thread_;
206 TestBrowserThread file_user_blocking_thread_;
207 TestBrowserThread io_thread_;
208
209 RPHIBrowserContext browser_context_;
210 RPHIBrowserClient test_browser_client_;
211 ContentBrowserClient* old_browser_client_;
212 ResourceDispatcherHostImpl resource_dispatcher_host_;
213
214 DISALLOW_COPY_AND_ASSIGN(RenderProcessHostImplTest);
215 };
216
217 // http://crbug.com/87176
218 TEST_F(RenderProcessHostImplTest,
219 RejectShutdownFromChildProcessWithOneActiveView) {
220 const GURL kUrl("http://example.com");
221
222 scoped_refptr<SiteInstance> site(
223 SiteInstance::CreateForURL(&browser_context_, kUrl));
224 FakeChildProcessRPHFactory rph_factory(&child_processes_, site.get());
225
226 WebContents::CreateParams params(&browser_context_, site.get());
227 scoped_ptr<WebContentsImpl> tab(
228 WebContentsImpl::CreateWithOpener(params, NULL));
229 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
230 base::RunLoop().RunUntilIdle();
231 ASSERT_EQ(1U, child_processes_.size());
232
233 child_processes_[0]->messages_.ClearMessages();
234
235 // Double-check that the loading tab isn't pending.
236 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.
237 tab->GetRenderManagerForTesting()->pending_render_view_host());
238 EXPECT_EQ(1,
239 static_cast<RenderProcessHostImpl*>(
240 tab->GetSiteInstance()->GetProcess())->GetActiveViewCount());
241
242 // Sometimes the renderer process's ShutdownRequest (corresponding to the
243 // 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
244 // after the browser process decides to re-use the renderer for a new purpose.
245 // This test makes sure the browser doesn't let the renderer die in that case.
246 child_processes_[0]->launcher_
247 ->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
248 base::RunLoop().RunUntilIdle();
249 EXPECT_EQ(NULL,
Charlie Reis 2013/06/05 19:07:10 EXPECT_FALSE?
Jeffrey Yasskin 2013/06/05 21:23:27 Done.
250 child_processes_[0]->messages_.GetFirstMessageMatching(
251 ChildProcessMsg_Shutdown::ID))
252 << "Host sent a message confirming that the renderer process should shut "
253 << "down even though it was still being used.";
254 }
255
256 } // namespace
257 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698