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

Unified Diff: services/shell/tests/loader_unittest.cc

Issue 1899583002: Revert of Remove shell::Loader (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « services/shell/tests/BUILD.gn ('k') | services/shell/tests/placeholder_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: services/shell/tests/loader_unittest.cc
diff --git a/services/shell/tests/loader_unittest.cc b/services/shell/tests/loader_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..757514a2932f5f09c40543b5acee0bdd180c7e1d
--- /dev/null
+++ b/services/shell/tests/loader_unittest.cc
@@ -0,0 +1,560 @@
+// Copyright 2014 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 <memory>
+#include <utility>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/catalog/catalog.h"
+#include "services/catalog/store.h"
+#include "services/shell/connect_util.h"
+#include "services/shell/loader.h"
+#include "services/shell/public/cpp/connector.h"
+#include "services/shell/public/cpp/interface_factory.h"
+#include "services/shell/public/cpp/shell_client.h"
+#include "services/shell/public/cpp/shell_connection.h"
+#include "services/shell/public/interfaces/interface_provider.mojom.h"
+#include "services/shell/shell.h"
+#include "services/shell/tests/test.mojom.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace shell {
+namespace test {
+
+const char kTestURLString[] = "test:testService";
+const char kTestAURLString[] = "test:TestA";
+const char kTestBURLString[] = "test:TestB";
+
+struct TestContext {
+ TestContext() : num_impls(0), num_loader_deletes(0) {}
+ std::string last_test_string;
+ int num_impls;
+ int num_loader_deletes;
+};
+
+void QuitClosure(const Identity& expected,
+ bool* value,
+ const Identity& actual) {
+ if (expected == actual) {
+ *value = true;
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+}
+
+class TestServiceImpl : public TestService {
+ public:
+ TestServiceImpl(TestContext* context, TestServiceRequest request)
+ : context_(context), binding_(this, std::move(request)) {
+ ++context_->num_impls;
+ }
+
+ ~TestServiceImpl() override {
+ --context_->num_impls;
+ if (!base::MessageLoop::current()->is_running())
+ return;
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+
+ // TestService implementation:
+ void Test(const mojo::String& test_string,
+ const mojo::Closure& callback) override {
+ context_->last_test_string = test_string;
+ callback.Run();
+ }
+
+ private:
+ TestContext* context_;
+ mojo::StrongBinding<TestService> binding_;
+};
+
+class TestClient {
+ public:
+ explicit TestClient(TestServicePtr service)
+ : service_(std::move(service)), quit_after_ack_(false) {}
+
+ void AckTest() {
+ if (quit_after_ack_)
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+
+ void Test(const std::string& test_string) {
+ quit_after_ack_ = true;
+ service_->Test(test_string,
+ base::Bind(&TestClient::AckTest, base::Unretained(this)));
+ }
+
+ private:
+ TestServicePtr service_;
+ bool quit_after_ack_;
+ DISALLOW_COPY_AND_ASSIGN(TestClient);
+};
+
+class TestLoader : public Loader,
+ public ShellClient,
+ public InterfaceFactory<TestService> {
+ public:
+ explicit TestLoader(TestContext* context)
+ : context_(context), num_loads_(0) {}
+
+ ~TestLoader() override {
+ ++context_->num_loader_deletes;
+ shell_connection_.reset();
+ }
+
+ int num_loads() const { return num_loads_; }
+ const std::string& last_requestor_name() const {
+ return last_requestor_name_;
+ }
+
+ private:
+ // Loader implementation.
+ void Load(const std::string& name,
+ mojom::ShellClientRequest request) override {
+ ++num_loads_;
+ shell_connection_.reset(new ShellConnection(this, std::move(request)));
+ }
+
+ // ShellClient implementation.
+ bool AcceptConnection(Connection* connection) override {
+ connection->AddInterface<TestService>(this);
+ last_requestor_name_ = connection->GetRemoteIdentity().name();
+ return true;
+ }
+
+ // InterfaceFactory<TestService> implementation.
+ void Create(Connection* connection, TestServiceRequest request) override {
+ new TestServiceImpl(context_, std::move(request));
+ }
+
+ std::unique_ptr<ShellConnection> shell_connection_;
+ TestContext* context_;
+ int num_loads_;
+ std::string last_requestor_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestLoader);
+};
+
+class ClosingLoader : public Loader {
+ private:
+ // Loader implementation.
+ void Load(const std::string& name,
+ mojom::ShellClientRequest request) override {
+ }
+};
+
+class TesterContext {
+ public:
+ explicit TesterContext(base::MessageLoop* loop)
+ : num_b_calls_(0),
+ num_c_calls_(0),
+ num_a_deletes_(0),
+ num_b_deletes_(0),
+ num_c_deletes_(0),
+ tester_called_quit_(false),
+ a_called_quit_(false),
+ loop_(loop) {}
+
+ void IncrementNumBCalls() {
+ base::AutoLock lock(lock_);
+ num_b_calls_++;
+ }
+
+ void IncrementNumCCalls() {
+ base::AutoLock lock(lock_);
+ num_c_calls_++;
+ }
+
+ void IncrementNumADeletes() {
+ base::AutoLock lock(lock_);
+ num_a_deletes_++;
+ }
+
+ void IncrementNumBDeletes() {
+ base::AutoLock lock(lock_);
+ num_b_deletes_++;
+ }
+
+ void IncrementNumCDeletes() {
+ base::AutoLock lock(lock_);
+ num_c_deletes_++;
+ }
+
+ void set_tester_called_quit() {
+ base::AutoLock lock(lock_);
+ tester_called_quit_ = true;
+ }
+
+ void set_a_called_quit() {
+ base::AutoLock lock(lock_);
+ a_called_quit_ = true;
+ }
+
+ int num_b_calls() {
+ base::AutoLock lock(lock_);
+ return num_b_calls_;
+ }
+ int num_c_calls() {
+ base::AutoLock lock(lock_);
+ return num_c_calls_;
+ }
+ int num_a_deletes() {
+ base::AutoLock lock(lock_);
+ return num_a_deletes_;
+ }
+ int num_b_deletes() {
+ base::AutoLock lock(lock_);
+ return num_b_deletes_;
+ }
+ int num_c_deletes() {
+ base::AutoLock lock(lock_);
+ return num_c_deletes_;
+ }
+ bool tester_called_quit() {
+ base::AutoLock lock(lock_);
+ return tester_called_quit_;
+ }
+ bool a_called_quit() {
+ base::AutoLock lock(lock_);
+ return a_called_quit_;
+ }
+
+ void QuitSoon() {
+ loop_->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+ }
+
+ private:
+ // lock_ protects all members except for loop_ which must be unchanged for the
+ // lifetime of this class.
+ base::Lock lock_;
+ int num_b_calls_;
+ int num_c_calls_;
+ int num_a_deletes_;
+ int num_b_deletes_;
+ int num_c_deletes_;
+ bool tester_called_quit_;
+ bool a_called_quit_;
+
+ base::MessageLoop* loop_;
+};
+
+// Used to test that the requestor name will be correctly passed.
+class TestAImpl : public TestA {
+ public:
+ TestAImpl(Connector* connector,
+ TesterContext* test_context,
+ TestARequest request,
+ InterfaceFactory<TestC>* factory)
+ : test_context_(test_context), binding_(this, std::move(request)) {
+ connection_ = connector->Connect(kTestBURLString);
+ connection_->AddInterface<TestC>(factory);
+ connection_->GetInterface(&b_);
+ }
+
+ ~TestAImpl() override {
+ test_context_->IncrementNumADeletes();
+ if (base::MessageLoop::current()->is_running())
+ Quit();
+ }
+
+ private:
+ void CallB() override {
+ b_->B(base::Bind(&TestAImpl::Quit, base::Unretained(this)));
+ }
+
+ void CallCFromB() override {
+ b_->CallC(base::Bind(&TestAImpl::Quit, base::Unretained(this)));
+ }
+
+ void Quit() {
+ base::MessageLoop::current()->QuitWhenIdle();
+ test_context_->set_a_called_quit();
+ test_context_->QuitSoon();
+ }
+
+ std::unique_ptr<Connection> connection_;
+ TesterContext* test_context_;
+ TestBPtr b_;
+ mojo::StrongBinding<TestA> binding_;
+};
+
+class TestBImpl : public TestB {
+ public:
+ TestBImpl(Connection* connection,
+ TesterContext* test_context,
+ TestBRequest request)
+ : test_context_(test_context), binding_(this, std::move(request)) {
+ connection->GetInterface(&c_);
+ }
+
+ ~TestBImpl() override {
+ test_context_->IncrementNumBDeletes();
+ if (base::MessageLoop::current()->is_running())
+ base::MessageLoop::current()->QuitWhenIdle();
+ test_context_->QuitSoon();
+ }
+
+ private:
+ void B(const mojo::Closure& callback) override {
+ test_context_->IncrementNumBCalls();
+ callback.Run();
+ }
+
+ void CallC(const mojo::Closure& callback) override {
+ test_context_->IncrementNumBCalls();
+ c_->C(callback);
+ }
+
+ TesterContext* test_context_;
+ TestCPtr c_;
+ mojo::StrongBinding<TestB> binding_;
+};
+
+class TestCImpl : public TestC {
+ public:
+ TestCImpl(Connection* connection,
+ TesterContext* test_context,
+ TestCRequest request)
+ : test_context_(test_context), binding_(this, std::move(request)) {}
+
+ ~TestCImpl() override { test_context_->IncrementNumCDeletes(); }
+
+ private:
+ void C(const mojo::Closure& callback) override {
+ test_context_->IncrementNumCCalls();
+ callback.Run();
+ }
+
+ TesterContext* test_context_;
+ mojo::StrongBinding<TestC> binding_;
+};
+
+class Tester : public ShellClient,
+ public Loader,
+ public InterfaceFactory<TestA>,
+ public InterfaceFactory<TestB>,
+ public InterfaceFactory<TestC> {
+ public:
+ Tester(TesterContext* context, const std::string& requestor_name)
+ : context_(context), requestor_name_(requestor_name) {}
+ ~Tester() override {}
+
+ private:
+ void Load(const std::string& name,
+ mojom::ShellClientRequest request) override {
+ app_.reset(new ShellConnection(this, std::move(request)));
+ }
+
+ bool AcceptConnection(Connection* connection) override {
+ if (!requestor_name_.empty() &&
+ requestor_name_ != connection->GetRemoteIdentity().name()) {
+ context_->set_tester_called_quit();
+ context_->QuitSoon();
+ base::MessageLoop::current()->QuitWhenIdle();
+ return false;
+ }
+ // If we're coming from A, then add B, otherwise A.
+ if (connection->GetRemoteIdentity().name() == kTestAURLString)
+ connection->AddInterface<TestB>(this);
+ else
+ connection->AddInterface<TestA>(this);
+ return true;
+ }
+
+ void Create(Connection* connection, TestARequest request) override {
+ a_bindings_.push_back(
+ new TestAImpl(app_->connector(), context_, std::move(request), this));
+ }
+
+ void Create(Connection* connection, TestBRequest request) override {
+ new TestBImpl(connection, context_, std::move(request));
+ }
+
+ void Create(Connection* connection, TestCRequest request) override {
+ new TestCImpl(connection, context_, std::move(request));
+ }
+
+ TesterContext* context_;
+ std::unique_ptr<ShellConnection> app_;
+ std::string requestor_name_;
+ ScopedVector<TestAImpl> a_bindings_;
+};
+
+void OnConnect(base::RunLoop* loop,
+ mojom::ConnectResult result,
+ const mojo::String& user_id,
+ uint32_t instance_id) {
+ loop->Quit();
+}
+
+class LoaderTest : public testing::Test {
+ public:
+ LoaderTest() : tester_context_(&loop_) {}
+ ~LoaderTest() override {}
+
+ void SetUp() override {
+ blocking_pool_ = new base::SequencedWorkerPool(3, "blocking_pool");
+ catalog_.reset(
+ new catalog::Catalog(blocking_pool_.get(), nullptr, nullptr));
+ shell_.reset(new Shell(nullptr, catalog_->TakeShellClient()));
+ test_loader_ = new TestLoader(&context_);
+ shell_->set_default_loader(std::unique_ptr<Loader>(test_loader_));
+
+ TestServicePtr service_proxy;
+ ConnectToInterface(kTestURLString, &service_proxy);
+ test_client_.reset(new TestClient(std::move(service_proxy)));
+ }
+
+ void TearDown() override {
+ test_client_.reset();
+ shell_.reset();
+ blocking_pool_->Shutdown();
+ }
+
+ void AddLoaderForName(const std::string& name,
+ const std::string& requestor_name) {
+ shell_->SetLoaderForName(
+ base::WrapUnique(new Tester(&tester_context_, requestor_name)), name);
+ }
+
+ bool HasRunningInstanceForName(const std::string& name) {
+ Shell::TestAPI test_api(shell_.get());
+ return test_api.HasRunningInstanceForName(name);
+ }
+
+ protected:
+ template <typename Interface>
+ void ConnectToInterface(const std::string& name,
+ mojo::InterfacePtr<Interface>* ptr) {
+ base::RunLoop loop;
+ mojom::InterfaceProviderPtr remote_interfaces;
+ std::unique_ptr<ConnectParams> params(new ConnectParams);
+ params->set_source(CreateShellIdentity());
+ params->set_target(Identity(name, mojom::kRootUserID));
+ params->set_remote_interfaces(mojo::GetProxy(&remote_interfaces));
+ params->set_connect_callback(
+ base::Bind(&OnConnect, base::Unretained(&loop)));
+ shell_->Connect(std::move(params));
+ loop.Run();
+
+ GetInterface(remote_interfaces.get(), ptr);
+ }
+
+ base::ShadowingAtExitManager at_exit_;
+ TestLoader* test_loader_;
+ TesterContext tester_context_;
+ TestContext context_;
+ base::MessageLoop loop_;
+ std::unique_ptr<TestClient> test_client_;
+ std::unique_ptr<catalog::Catalog> catalog_;
+ scoped_refptr<base::SequencedWorkerPool> blocking_pool_;
+ std::unique_ptr<Shell> shell_;
+
+ DISALLOW_COPY_AND_ASSIGN(LoaderTest);
+};
+
+TEST_F(LoaderTest, Basic) {
+ test_client_->Test("test");
+ loop_.Run();
+ EXPECT_EQ(std::string("test"), context_.last_test_string);
+}
+
+TEST_F(LoaderTest, ClientError) {
+ test_client_->Test("test");
+ EXPECT_TRUE(HasRunningInstanceForName(kTestURLString));
+ loop_.Run();
+ EXPECT_EQ(1, context_.num_impls);
+ test_client_.reset();
+ loop_.Run();
+ EXPECT_EQ(0, context_.num_impls);
+ EXPECT_TRUE(HasRunningInstanceForName(kTestURLString));
+}
+
+TEST_F(LoaderTest, Deletes) {
+ {
+ catalog::Catalog catalog(blocking_pool_.get(), nullptr, nullptr);
+ Shell shell(nullptr, catalog.TakeShellClient());
+ TestLoader* default_loader = new TestLoader(&context_);
+ TestLoader* name_loader1 = new TestLoader(&context_);
+ TestLoader* name_loader2 = new TestLoader(&context_);
+ shell.set_default_loader(std::unique_ptr<Loader>(default_loader));
+ shell.SetLoaderForName(std::unique_ptr<Loader>(name_loader1), "test:test1");
+ shell.SetLoaderForName(std::unique_ptr<Loader>(name_loader2), "test:test1");
+ }
+ EXPECT_EQ(3, context_.num_loader_deletes);
+}
+
+// Test for SetLoaderForName() & set_default_loader().
+TEST_F(LoaderTest, SetLoaders) {
+ TestLoader* default_loader = new TestLoader(&context_);
+ TestLoader* name_loader = new TestLoader(&context_);
+ shell_->set_default_loader(std::unique_ptr<Loader>(default_loader));
+ shell_->SetLoaderForName(std::unique_ptr<Loader>(name_loader), "test:test1");
+
+ // test::test1 should go to name_loader.
+ TestServicePtr test_service;
+ ConnectToInterface("test:test1", &test_service);
+ EXPECT_EQ(1, name_loader->num_loads());
+ EXPECT_EQ(0, default_loader->num_loads());
+
+ // http::test1 should go to default loader.
+ ConnectToInterface("http:test1", &test_service);
+ EXPECT_EQ(1, name_loader->num_loads());
+ EXPECT_EQ(1, default_loader->num_loads());
+}
+
+TEST_F(LoaderTest, NoServiceNoLoad) {
+ AddLoaderForName(kTestAURLString, std::string());
+
+ // There is no TestC service implementation registered with the Shell, so this
+ // cannot succeed (but also shouldn't crash).
+ TestCPtr c;
+ ConnectToInterface(kTestAURLString, &c);
+ c.set_connection_error_handler(
+ []() { base::MessageLoop::current()->QuitWhenIdle(); });
+
+ loop_.Run();
+ EXPECT_TRUE(c.encountered_error());
+}
+
+TEST_F(LoaderTest, TestEndApplicationClosure) {
+ ClosingLoader* loader = new ClosingLoader();
+ shell_->SetLoaderForName(std::unique_ptr<Loader>(loader), "test:test");
+
+ bool called = false;
+ std::unique_ptr<ConnectParams> params(new ConnectParams);
+ params->set_source(CreateShellIdentity());
+ params->set_target(Identity("test:test", mojom::kRootUserID));
+ shell_->SetInstanceQuitCallback(
+ base::Bind(&QuitClosure, params->target(), &called));
+ shell_->Connect(std::move(params));
+ loop_.Run();
+ EXPECT_TRUE(called);
+}
+
+TEST_F(LoaderTest, SameIdentityShouldNotCauseDuplicateLoad) {
+ // 1 because LoaderTest connects once at startup.
+ EXPECT_EQ(1, test_loader_->num_loads());
+
+ TestServicePtr test_service;
+ ConnectToInterface("test:foo", &test_service);
+ EXPECT_EQ(2, test_loader_->num_loads());
+
+ // Exactly the same name as above.
+ ConnectToInterface("test:foo", &test_service);
+ EXPECT_EQ(2, test_loader_->num_loads());
+
+ // A different identity because the domain is different.
+ ConnectToInterface("test:bar", &test_service);
+ EXPECT_EQ(3, test_loader_->num_loads());
+}
+
+} // namespace test
+} // namespace shell
« no previous file with comments | « services/shell/tests/BUILD.gn ('k') | services/shell/tests/placeholder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698