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

Side by Side Diff: mojo/shell/tests/lifecycle/lifecycle_unittest.cc

Issue 1877753003: Move mojo\shell to services\shell (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@62scan
Patch Set: . Created 4 years, 8 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
OLDNEW
(Empty)
1 // Copyright 2016 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 "base/bind.h"
6 #include "base/command_line.h"
7 #include "base/macros.h"
8 #include "base/process/process.h"
9 #include "base/run_loop.h"
10 #include "mojo/shell/public/cpp/identity.h"
11 #include "mojo/shell/public/cpp/shell_test.h"
12 #include "mojo/shell/public/interfaces/shell.mojom.h"
13 #include "mojo/shell/tests/lifecycle/lifecycle_unittest.mojom.h"
14 #include "mojo/shell/tests/util.h"
15
16 namespace mojo {
17 namespace shell {
18 namespace {
19
20 const char kTestAppName[] = "mojo:lifecycle_unittest_app";
21 const char kTestParentName[] = "mojo:lifecycle_unittest_parent";
22 const char kTestExeName[] = "exe:lifecycle_unittest_exe";
23 const char kTestPackageName[] = "mojo:lifecycle_unittest_package";
24 const char kTestPackageAppNameA[] = "mojo:lifecycle_unittest_package_app_a";
25 const char kTestPackageAppNameB[] = "mojo:lifecycle_unittest_package_app_b";
26 const char kTestName[] = "mojo:lifecycle_unittest";
27
28 void QuitLoop(base::RunLoop* loop) {
29 loop->Quit();
30 }
31
32 void DecrementCountAndQuitWhenZero(base::RunLoop* loop, size_t* count) {
33 if (!--(*count))
34 loop->Quit();
35 }
36
37 struct Instance {
38 Instance() : id(shell::mojom::kInvalidInstanceID), pid(0) {}
39 Instance(const Identity& identity, uint32_t id, uint32_t pid)
40 : identity(identity), id(id), pid(pid) {}
41
42 Identity identity;
43 uint32_t id;
44 uint32_t pid;
45 };
46
47 class InstanceState : public mojom::InstanceListener {
48 public:
49 InstanceState(mojom::InstanceListenerRequest request, base::RunLoop* loop)
50 : binding_(this, std::move(request)), loop_(loop) {}
51 ~InstanceState() override {}
52
53 bool HasInstanceForName(const std::string& name) const {
54 return instances_.find(name) != instances_.end();
55 }
56 size_t GetNewInstanceCount() const {
57 return instances_.size() - initial_instances_.size();
58 }
59 void WaitForInstanceDestruction(base::RunLoop* loop) {
60 DCHECK(!destruction_loop_);
61 destruction_loop_ = loop;
62 // First of all check to see if we should be spinning this loop at all -
63 // the app(s) we're waiting on quitting may already have quit.
64 TryToQuitDestructionLoop();
65 }
66
67 private:
68 // mojom::InstanceListener:
69 void SetExistingInstances(Array<mojom::InstanceInfoPtr> instances) override {
70 for (const auto& instance : instances) {
71 Instance i(instance->identity.To<Identity>(), instance->id,
72 instance->pid);
73 initial_instances_[i.identity.name()] = i;
74 instances_[i.identity.name()] = i;
75 }
76 loop_->Quit();
77 }
78 void InstanceCreated(mojom::InstanceInfoPtr instance) override {
79 instances_[instance->identity->name] =
80 Instance(instance->identity.To<Identity>(), instance->id,
81 instance->pid);
82 }
83 void InstanceDestroyed(uint32_t id) override {
84 for (auto it = instances_.begin(); it != instances_.end(); ++it) {
85 if (it->second.id == id) {
86 instances_.erase(it);
87 break;
88 }
89 }
90 TryToQuitDestructionLoop();
91 }
92 void InstancePIDAvailable(uint32_t id, uint32_t pid) override {
93 for (auto& instance : instances_) {
94 if (instance.second.id == id) {
95 instance.second.pid = pid;
96 break;
97 }
98 }
99 }
100
101 void TryToQuitDestructionLoop() {
102 if (!GetNewInstanceCount() && destruction_loop_) {
103 destruction_loop_->Quit();
104 destruction_loop_ = nullptr;
105 }
106 }
107
108 // All currently running instances.
109 std::map<std::string, Instance> instances_;
110 // The initial set of instances.
111 std::map<std::string, Instance> initial_instances_;
112
113 Binding<mojom::InstanceListener> binding_;
114 base::RunLoop* loop_;
115
116 // Set when the client wants to wait for this object to track the destruction
117 // of an instance before proceeding.
118 base::RunLoop* destruction_loop_ = nullptr;
119
120 DISALLOW_COPY_AND_ASSIGN(InstanceState);
121 };
122
123 }
124
125 class LifecycleTest : public mojo::test::ShellTest {
126 public:
127 LifecycleTest() : ShellTest(kTestName) {}
128 ~LifecycleTest() override {}
129
130 protected:
131 // mojo::test::ShellTest:
132 void SetUp() override {
133 mojo::test::ShellTest::SetUp();
134 InitPackage();
135 instances_ = TrackInstances();
136 }
137 void TearDown() override {
138 instances_.reset();
139 mojo::test::ShellTest::TearDown();
140 }
141
142 bool CanRunCrashTest() {
143 return !base::CommandLine::ForCurrentProcess()->HasSwitch("single-process");
144 }
145
146 void InitPackage() {
147 test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestPackageName);
148 base::RunLoop loop;
149 lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
150 lifecycle->GracefulQuit();
151 loop.Run();
152 }
153
154 test::mojom::LifecycleControlPtr ConnectTo(const std::string& name) {
155 test::mojom::LifecycleControlPtr lifecycle;
156 connector()->ConnectToInterface(name, &lifecycle);
157 PingPong(lifecycle.get());
158 return lifecycle;
159 }
160
161 base::Process LaunchProcess() {
162 base::Process process;
163 test::LaunchAndConnectToProcess(
164 #if defined(OS_WIN)
165 "lifecycle_unittest_exe.exe",
166 #else
167 "lifecycle_unittest_exe",
168 #endif
169 Identity(kTestExeName, mojom::kInheritUserID),
170 connector(),
171 &process);
172 return process;
173 }
174
175 void PingPong(test::mojom::LifecycleControl* lifecycle) {
176 base::RunLoop loop;
177 lifecycle->Ping(base::Bind(&QuitLoop, &loop));
178 loop.Run();
179 }
180
181 InstanceState* instances() { return instances_.get(); }
182
183 void WaitForInstanceDestruction() {
184 base::RunLoop loop;
185 instances()->WaitForInstanceDestruction(&loop);
186 loop.Run();
187 }
188
189 private:
190 scoped_ptr<InstanceState> TrackInstances() {
191 mojom::ShellPtr shell;
192 connector()->ConnectToInterface("mojo:shell", &shell);
193 mojom::InstanceListenerPtr listener;
194 base::RunLoop loop;
195 InstanceState* state = new InstanceState(GetProxy(&listener), &loop);
196 shell->AddInstanceListener(std::move(listener));
197 loop.Run();
198 return make_scoped_ptr(state);
199 }
200
201 scoped_ptr<InstanceState> instances_;
202
203 DISALLOW_COPY_AND_ASSIGN(LifecycleTest);
204 };
205
206 TEST_F(LifecycleTest, Standalone_GracefulQuit) {
207 test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestAppName);
208
209 EXPECT_TRUE(instances()->HasInstanceForName(kTestAppName));
210 EXPECT_EQ(1u, instances()->GetNewInstanceCount());
211
212 base::RunLoop loop;
213 lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
214 lifecycle->GracefulQuit();
215 loop.Run();
216
217 WaitForInstanceDestruction();
218 EXPECT_FALSE(instances()->HasInstanceForName(kTestAppName));
219 EXPECT_EQ(0u, instances()->GetNewInstanceCount());
220 }
221
222 TEST_F(LifecycleTest, Standalone_Crash) {
223 if (!CanRunCrashTest()) {
224 LOG(INFO) << "Skipping Standalone_Crash test in --single-process mode.";
225 return;
226 }
227
228 test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestAppName);
229
230 EXPECT_TRUE(instances()->HasInstanceForName(kTestAppName));
231 EXPECT_EQ(1u, instances()->GetNewInstanceCount());
232
233 base::RunLoop loop;
234 lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
235 lifecycle->Crash();
236 loop.Run();
237
238 WaitForInstanceDestruction();
239 EXPECT_FALSE(instances()->HasInstanceForName(kTestAppName));
240 EXPECT_EQ(0u, instances()->GetNewInstanceCount());
241 }
242
243 TEST_F(LifecycleTest, Standalone_CloseShellConnection) {
244 test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestAppName);
245
246 EXPECT_TRUE(instances()->HasInstanceForName(kTestAppName));
247 EXPECT_EQ(1u, instances()->GetNewInstanceCount());
248
249 base::RunLoop loop;
250 lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
251 lifecycle->CloseShellConnection();
252
253 WaitForInstanceDestruction();
254
255 // |lifecycle| pipe should still be valid.
256 PingPong(lifecycle.get());
257 }
258
259 TEST_F(LifecycleTest, PackagedApp_GracefulQuit) {
260 test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestPackageAppNameA);
261
262 // There should be two new instances - one for the app and one for the package
263 // that vended it.
264 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameA));
265 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageName));
266 EXPECT_EQ(2u, instances()->GetNewInstanceCount());
267
268 base::RunLoop loop;
269 lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
270 lifecycle->GracefulQuit();
271 loop.Run();
272
273 WaitForInstanceDestruction();
274 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageName));
275 EXPECT_FALSE(instances()->HasInstanceForName(kTestAppName));
276 EXPECT_EQ(0u, instances()->GetNewInstanceCount());
277 }
278
279 TEST_F(LifecycleTest, PackagedApp_Crash) {
280 if (!CanRunCrashTest()) {
281 LOG(INFO) << "Skipping Standalone_Crash test in --single-process mode.";
282 return;
283 }
284
285 test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestPackageAppNameA);
286
287 // There should be two new instances - one for the app and one for the package
288 // that vended it.
289 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameA));
290 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageName));
291 EXPECT_EQ(2u, instances()->GetNewInstanceCount());
292
293 base::RunLoop loop;
294 lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
295 lifecycle->Crash();
296 loop.Run();
297
298 WaitForInstanceDestruction();
299 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageName));
300 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameA));
301 EXPECT_EQ(0u, instances()->GetNewInstanceCount());
302 }
303
304 TEST_F(LifecycleTest, PackagedApp_CloseShellConnection) {
305 test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestPackageAppNameA);
306
307 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameA));
308 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageName));
309 EXPECT_EQ(2u, instances()->GetNewInstanceCount());
310
311 base::RunLoop loop;
312 lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
313 lifecycle->CloseShellConnection();
314
315 WaitForInstanceDestruction();
316 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameA));
317 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageName));
318 EXPECT_EQ(0u, instances()->GetNewInstanceCount());
319
320 // |lifecycle| pipe should still be valid.
321 PingPong(lifecycle.get());
322 }
323
324
325 // When a single package provides multiple apps out of one process, crashing one
326 // app crashes all.
327 TEST_F(LifecycleTest, PackagedApp_CrashCrashesOtherProvidedApp) {
328 if (!CanRunCrashTest()) {
329 LOG(INFO) << "Skipping Standalone_Crash test in --single-process mode.";
330 return;
331 }
332
333 test::mojom::LifecycleControlPtr lifecycle_a =
334 ConnectTo(kTestPackageAppNameA);
335 test::mojom::LifecycleControlPtr lifecycle_b =
336 ConnectTo(kTestPackageAppNameB);
337 test::mojom::LifecycleControlPtr lifecycle_package =
338 ConnectTo(kTestPackageName);
339
340 // There should be three instances, one for each packaged app and the package
341 // itself.
342 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameA));
343 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameB));
344 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageName));
345 size_t instance_count = instances()->GetNewInstanceCount();
346 EXPECT_EQ(3u, instance_count);
347
348 base::RunLoop loop;
349 lifecycle_a.set_connection_error_handler(
350 base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
351 lifecycle_b.set_connection_error_handler(
352 base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
353 lifecycle_package.set_connection_error_handler(
354 base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
355
356 // Now crash one of the packaged apps.
357 lifecycle_a->Crash();
358 loop.Run();
359
360 WaitForInstanceDestruction();
361 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageName));
362 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameA));
363 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameB));
364 EXPECT_EQ(0u, instances()->GetNewInstanceCount());
365 }
366
367 // When a single package provides multiple apps out of one process, crashing one
368 // app crashes all.
369 TEST_F(LifecycleTest, PackagedApp_GracefulQuitPackageQuitsAll) {
370 test::mojom::LifecycleControlPtr lifecycle_a =
371 ConnectTo(kTestPackageAppNameA);
372 test::mojom::LifecycleControlPtr lifecycle_b =
373 ConnectTo(kTestPackageAppNameB);
374 test::mojom::LifecycleControlPtr lifecycle_package =
375 ConnectTo(kTestPackageName);
376
377 // There should be three instances, one for each packaged app and the package
378 // itself.
379 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameA));
380 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameB));
381 EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageName));
382 size_t instance_count = instances()->GetNewInstanceCount();
383 EXPECT_EQ(3u, instance_count);
384
385 base::RunLoop loop;
386 lifecycle_a.set_connection_error_handler(
387 base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
388 lifecycle_b.set_connection_error_handler(
389 base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
390 lifecycle_package.set_connection_error_handler(
391 base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
392
393 // Now quit the package. All the packaged apps should close.
394 lifecycle_package->GracefulQuit();
395 loop.Run();
396
397 WaitForInstanceDestruction();
398 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageName));
399 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameA));
400 EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameB));
401 EXPECT_EQ(0u, instances()->GetNewInstanceCount());
402 }
403
404 TEST_F(LifecycleTest, Exe_GracefulQuit) {
405 base::Process process = LaunchProcess();
406
407 test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestExeName);
408
409 EXPECT_TRUE(instances()->HasInstanceForName(kTestExeName));
410 EXPECT_EQ(1u, instances()->GetNewInstanceCount());
411
412 base::RunLoop loop;
413 lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
414 lifecycle->GracefulQuit();
415 loop.Run();
416
417 WaitForInstanceDestruction();
418 EXPECT_FALSE(instances()->HasInstanceForName(kTestExeName));
419 EXPECT_EQ(0u, instances()->GetNewInstanceCount());
420
421 process.Terminate(9, true);
422 }
423
424 TEST_F(LifecycleTest, Exe_TerminateProcess) {
425 base::Process process = LaunchProcess();
426
427 test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestExeName);
428
429 EXPECT_TRUE(instances()->HasInstanceForName(kTestExeName));
430 EXPECT_EQ(1u, instances()->GetNewInstanceCount());
431
432 base::RunLoop loop;
433 lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
434 process.Terminate(9, true);
435 loop.Run();
436
437 WaitForInstanceDestruction();
438 EXPECT_FALSE(instances()->HasInstanceForName(kTestExeName));
439 EXPECT_EQ(0u, instances()->GetNewInstanceCount());
440 }
441
442 TEST_F(LifecycleTest, ShutdownTree) {
443 // Verifies that Instances are destroyed when their creator is.
444 scoped_ptr<Connection> parent_connection =
445 connector()->Connect(kTestParentName);
446 test::mojom::ParentPtr parent;
447 parent_connection->GetInterface(&parent);
448
449 // This asks kTestParentName to open a connection to kTestAppName and blocks
450 // on a response from a Ping().
451 {
452 base::RunLoop loop;
453 parent->ConnectToChild(base::Bind(&QuitLoop, &loop));
454 loop.Run();
455 }
456
457 // Should now have two new instances (parent and child).
458 EXPECT_EQ(2u, instances()->GetNewInstanceCount());
459 EXPECT_TRUE(instances()->HasInstanceForName(kTestParentName));
460 EXPECT_TRUE(instances()->HasInstanceForName(kTestAppName));
461
462 parent->Quit();
463
464 // Quitting the parent should cascade-quit the child.
465 WaitForInstanceDestruction();
466 EXPECT_EQ(0u, instances()->GetNewInstanceCount());
467 EXPECT_FALSE(instances()->HasInstanceForName(kTestParentName));
468 EXPECT_FALSE(instances()->HasInstanceForName(kTestAppName));
469 }
470
471 } // namespace shell
472 } // namespace mojo
OLDNEW
« no previous file with comments | « mojo/shell/tests/lifecycle/lifecycle_exe.cc ('k') | mojo/shell/tests/lifecycle/lifecycle_unittest.mojom » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698