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

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

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

Powered by Google App Engine
This is Rietveld 408576698