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

Unified 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, 10 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
« 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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: mojo/shell/tests/lifecycle/lifecycle_unittest.cc
diff --git a/mojo/shell/tests/lifecycle/lifecycle_unittest.cc b/mojo/shell/tests/lifecycle/lifecycle_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5b9fd0ac95215d8f30546e894692d46521887b24
--- /dev/null
+++ b/mojo/shell/tests/lifecycle/lifecycle_unittest.cc
@@ -0,0 +1,388 @@
+// Copyright 2016 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 "base/bind.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "mojo/shell/public/cpp/shell_test.h"
+#include "mojo/shell/public/interfaces/application_manager.mojom.h"
+#include "mojo/shell/tests/lifecycle/lifecycle_unittest.mojom.h"
+
+namespace mojo {
+namespace shell {
+namespace {
+
+const char kTestAppName[] = "mojo:lifecycle_unittest_app";
+const char kTestPackageName[] = "mojo:lifecycle_unittest_package";
+const char kTestPackageAppNameA[] = "mojo:lifecycle_unittest_package_app_a";
+const char kTestPackageAppNameB[] = "mojo:lifecycle_unittest_package_app_b";
+const char kTestName[] = "mojo:lifecycle_unittest";
+
+void QuitLoop(base::RunLoop* loop) {
+ loop->Quit();
+}
+
+void DecrementCountAndQuitWhenZero(base::RunLoop* loop, size_t* count) {
+ if (!--(*count))
+ loop->Quit();
+}
+
+struct Instance {
+ Instance() : id(mojom::Connector::kInvalidApplicationID), pid(0) {}
+ Instance(const std::string& name, const std::string& qualifier, uint32_t id,
+ uint32_t pid)
+ : name(name), qualifier(qualifier), id(id), pid(pid) {}
+
+ std::string name;
+ std::string qualifier;
+ uint32_t id;
+ uint32_t pid;
+};
+
+class InstanceState : public mojom::InstanceListener {
+ public:
+ InstanceState(mojom::InstanceListenerRequest request, base::RunLoop* loop)
+ : binding_(this, std::move(request)), loop_(loop) {}
+ ~InstanceState() override {}
+
+ bool HasInstanceForName(const std::string& name) const {
+ return instances_.find(name) != instances_.end();
+ }
+ size_t GetNewInstanceCount() const {
+ return instances_.size() - initial_instances_.size();
+ }
+ void WaitForInstanceDestruction(base::RunLoop* loop) {
+ DCHECK(!destruction_loop_);
+ destruction_loop_ = loop;
+ // First of all check to see if we should be spinning this loop at all -
+ // the app(s) we're waiting on quitting may already have quit.
+ TryToQuitDestructionLoop();
+ }
+
+ private:
+ // mojom::InstanceListener:
+ void SetExistingInstances(Array<mojom::InstanceInfoPtr> instances) override {
+ for (const auto& instance : instances) {
+ Instance i(instance->name, instance->qualifier, instance->id,
+ instance->pid);
+ initial_instances_[i.name] = i;
+ instances_[i.name] = i;
+ }
+ loop_->Quit();
+ }
+ void InstanceCreated(mojom::InstanceInfoPtr instance) override {
+ instances_[instance->name] =
+ Instance(instance->name, instance->qualifier, instance->id,
+ instance->pid);
+ }
+ void InstanceDestroyed(uint32_t id) override {
+ for (auto it = instances_.begin(); it != instances_.end(); ++it) {
+ if (it->second.id == id) {
+ instances_.erase(it);
+ break;
+ }
+ }
+ TryToQuitDestructionLoop();
+ }
+ void InstancePIDAvailable(uint32_t id, uint32_t pid) override {
+ for (auto& instance : instances_) {
+ if (instance.second.id == id) {
+ instance.second.pid = pid;
+ break;
+ }
+ }
+ }
+
+ void TryToQuitDestructionLoop() {
+ if (!GetNewInstanceCount() && destruction_loop_) {
+ destruction_loop_->Quit();
+ destruction_loop_ = nullptr;
+ }
+ }
+
+ // All currently running instances.
+ std::map<std::string, Instance> instances_;
+ // The initial set of instances.
+ std::map<std::string, Instance> initial_instances_;
+
+ Binding<mojom::InstanceListener> binding_;
+ base::RunLoop* loop_;
+
+ // Set when the client wants to wait for this object to track the destruction
+ // of an instance before proceeding.
+ base::RunLoop* destruction_loop_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(InstanceState);
+};
+
+}
+
+class LifecycleTest : public mojo::test::ShellTest {
+ public:
+ LifecycleTest() : ShellTest(kTestName) {}
+ ~LifecycleTest() override {}
+
+ protected:
+ // mojo::test::ShellTest:
+ void SetUp() override {
+ mojo::test::ShellTest::SetUp();
+ InitPackage();
+ instances_ = TrackInstances();
+ }
+ void TearDown() override {
+ instances_.reset();
+ mojo::test::ShellTest::TearDown();
+ }
+
+ bool CanRunCrashTest() {
+ return !base::CommandLine::ForCurrentProcess()->HasSwitch("single-process");
+ }
+
+ void InitPackage() {
+ test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestPackageName);
+ base::RunLoop loop;
+ lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
+ lifecycle->GracefulQuit();
+ loop.Run();
+ }
+
+ test::mojom::LifecycleControlPtr ConnectTo(const std::string& name) {
+ test::mojom::LifecycleControlPtr lifecycle;
+ connector()->ConnectToInterface(name, &lifecycle);
+ PingPong(lifecycle.get());
+ return lifecycle;
+ }
+
+ void PingPong(test::mojom::LifecycleControl* lifecycle) {
+ base::RunLoop loop;
+ lifecycle->Ping(base::Bind(&QuitLoop, &loop));
+ loop.Run();
+ }
+
+ InstanceState* instances() { return instances_.get(); }
+
+ void WaitForInstanceDestruction() {
+ base::RunLoop loop;
+ instances()->WaitForInstanceDestruction(&loop);
+ loop.Run();
+ }
+
+ private:
+ scoped_ptr<InstanceState> TrackInstances() {
+ mojom::ApplicationManagerPtr am;
+ connector()->ConnectToInterface("mojo:shell", &am);
+ mojom::InstanceListenerPtr listener;
+ base::RunLoop loop;
+ InstanceState* state = new InstanceState(GetProxy(&listener), &loop);
+ am->AddInstanceListener(std::move(listener));
+ loop.Run();
+ return make_scoped_ptr(state);
+ }
+
+ scoped_ptr<InstanceState> instances_;
+
+ DISALLOW_COPY_AND_ASSIGN(LifecycleTest);
+};
+
+TEST_F(LifecycleTest, Standalone_GracefulQuit) {
+ test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestAppName);
+
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestAppName));
+ EXPECT_EQ(1u, instances()->GetNewInstanceCount());
+
+ base::RunLoop loop;
+ lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
+ lifecycle->GracefulQuit();
+ loop.Run();
+
+ WaitForInstanceDestruction();
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestAppName));
+ EXPECT_EQ(0u, instances()->GetNewInstanceCount());
+}
+
+TEST_F(LifecycleTest, Standalone_Crash) {
+ if (!CanRunCrashTest()) {
+ LOG(INFO) << "Skipping Standalone_Crash test in --single-process mode.";
+ return;
+ }
+
+ test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestAppName);
+
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestAppName));
+ EXPECT_EQ(1u, instances()->GetNewInstanceCount());
+
+ base::RunLoop loop;
+ lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
+ lifecycle->Crash();
+ loop.Run();
+
+ WaitForInstanceDestruction();
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestAppName));
+ EXPECT_EQ(0u, instances()->GetNewInstanceCount());
+}
+
+TEST_F(LifecycleTest, Standalone_CloseShellConnection) {
+ test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestAppName);
+
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestAppName));
+ EXPECT_EQ(1u, instances()->GetNewInstanceCount());
+
+ base::RunLoop loop;
+ lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
+ lifecycle->CloseShellConnection();
+
+ WaitForInstanceDestruction();
+
+ // |lifecycle| pipe should still be valid.
+ PingPong(lifecycle.get());
+}
+
+TEST_F(LifecycleTest, PackagedApp_GracefulQuit) {
+ test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestPackageAppNameA);
+
+ // There should be two new instances - one for the app and one for the package
+ // that vended it.
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameA));
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageName));
+ EXPECT_EQ(2u, instances()->GetNewInstanceCount());
+
+ base::RunLoop loop;
+ lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
+ lifecycle->GracefulQuit();
+ loop.Run();
+
+ WaitForInstanceDestruction();
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageName));
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestAppName));
+ EXPECT_EQ(0u, instances()->GetNewInstanceCount());
+}
+
+TEST_F(LifecycleTest, PackagedApp_Crash) {
+ if (!CanRunCrashTest()) {
+ LOG(INFO) << "Skipping Standalone_Crash test in --single-process mode.";
+ return;
+ }
+
+ test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestPackageAppNameA);
+
+ // There should be two new instances - one for the app and one for the package
+ // that vended it.
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameA));
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageName));
+ EXPECT_EQ(2u, instances()->GetNewInstanceCount());
+
+ base::RunLoop loop;
+ lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
+ lifecycle->Crash();
+ loop.Run();
+
+ WaitForInstanceDestruction();
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageName));
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameA));
+ EXPECT_EQ(0u, instances()->GetNewInstanceCount());
+}
+
+TEST_F(LifecycleTest, PackagedApp_CloseShellConnection) {
+ test::mojom::LifecycleControlPtr lifecycle = ConnectTo(kTestPackageAppNameA);
+
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameA));
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageName));
+ EXPECT_EQ(2u, instances()->GetNewInstanceCount());
+
+ base::RunLoop loop;
+ lifecycle.set_connection_error_handler(base::Bind(&QuitLoop, &loop));
+ lifecycle->CloseShellConnection();
+
+ WaitForInstanceDestruction();
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameA));
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageName));
+ EXPECT_EQ(0u, instances()->GetNewInstanceCount());
+
+ // |lifecycle| pipe should still be valid.
+ PingPong(lifecycle.get());
+}
+
+
+// When a single package provides multiple apps out of one process, crashing one
+// app crashes all.
+TEST_F(LifecycleTest, PackagedApp_CrashCrashesOtherProvidedApp) {
+ if (!CanRunCrashTest()) {
+ LOG(INFO) << "Skipping Standalone_Crash test in --single-process mode.";
+ return;
+ }
+
+ test::mojom::LifecycleControlPtr lifecycle_a =
+ ConnectTo(kTestPackageAppNameA);
+ test::mojom::LifecycleControlPtr lifecycle_b =
+ ConnectTo(kTestPackageAppNameB);
+ test::mojom::LifecycleControlPtr lifecycle_package =
+ ConnectTo(kTestPackageName);
+
+ // There should be three instances, one for each packaged app and the package
+ // itself.
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameA));
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameB));
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageName));
+ size_t instance_count = instances()->GetNewInstanceCount();
+ EXPECT_EQ(3u, instance_count);
+
+ base::RunLoop loop;
+ lifecycle_a.set_connection_error_handler(
+ base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
+ lifecycle_b.set_connection_error_handler(
+ base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
+ lifecycle_package.set_connection_error_handler(
+ base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
+
+ // Now crash one of the packaged apps.
+ lifecycle_a->Crash();
+ loop.Run();
+
+ WaitForInstanceDestruction();
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageName));
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameA));
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameB));
+ EXPECT_EQ(0u, instances()->GetNewInstanceCount());
+}
+
+// When a single package provides multiple apps out of one process, crashing one
+// app crashes all.
+TEST_F(LifecycleTest, PackagedApp_GracefulQuitPackageQuitsAll) {
+ test::mojom::LifecycleControlPtr lifecycle_a =
+ ConnectTo(kTestPackageAppNameA);
+ test::mojom::LifecycleControlPtr lifecycle_b =
+ ConnectTo(kTestPackageAppNameB);
+ test::mojom::LifecycleControlPtr lifecycle_package =
+ ConnectTo(kTestPackageName);
+
+ // There should be three instances, one for each packaged app and the package
+ // itself.
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameA));
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageAppNameB));
+ EXPECT_TRUE(instances()->HasInstanceForName(kTestPackageName));
+ size_t instance_count = instances()->GetNewInstanceCount();
+ EXPECT_EQ(3u, instance_count);
+
+ base::RunLoop loop;
+ lifecycle_a.set_connection_error_handler(
+ base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
+ lifecycle_b.set_connection_error_handler(
+ base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
+ lifecycle_package.set_connection_error_handler(
+ base::Bind(&DecrementCountAndQuitWhenZero, &loop, &instance_count));
+
+ // Now quit the package. All the packaged apps should close.
+ lifecycle_package->GracefulQuit();
+ loop.Run();
+
+ WaitForInstanceDestruction();
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageName));
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameA));
+ EXPECT_FALSE(instances()->HasInstanceForName(kTestPackageAppNameB));
+ EXPECT_EQ(0u, instances()->GetNewInstanceCount());
+}
+
+} // namespace shell
+} // namespace mojo
« 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