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

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

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

Powered by Google App Engine
This is Rietveld 408576698