Index: mojo/shell/external_application_listener_unittest.cc |
diff --git a/mojo/shell/external_application_listener_unittest.cc b/mojo/shell/external_application_listener_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b7e2fa4cf9d176d477b3f34dab82be5ce2d2f5d8 |
--- /dev/null |
+++ b/mojo/shell/external_application_listener_unittest.cc |
@@ -0,0 +1,228 @@ |
+// 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 "base/files/file_path.h" |
+#include "base/files/file_util.h" |
+#include "base/files/scoped_temp_dir.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
+#include "base/threading/thread.h" |
+#include "mojo/application_manager/application_manager.h" |
+#include "mojo/common/common_type_converters.h" |
+#include "mojo/public/interfaces/application/application.mojom.h" |
+#include "mojo/public/interfaces/application/service_provider.mojom.h" |
+#include "mojo/public/interfaces/application/shell.mojom.h" |
+#include "mojo/shell/external_application_listener_posix.h" |
+#include "mojo/shell/external_application_registrar.mojom.h" |
+#include "mojo/shell/external_application_registrar_connection.h" |
+#include "net/base/net_errors.h" |
+#include "net/base/test_completion_callback.h" |
+#include "net/socket/unix_domain_client_socket_posix.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "url/gurl.h" |
+ |
+namespace mojo { |
+namespace shell { |
+ |
+class ExternalApplicationListenerTest : public testing::Test { |
+ public: |
+ ExternalApplicationListenerTest() : io_thread_("io thread") {} |
+ virtual ~ExternalApplicationListenerTest() {} |
+ |
+ virtual void SetUp() OVERRIDE { |
+ base::Thread::Options options; |
+ options.message_loop_type = base::MessageLoop::TYPE_IO; |
+ io_thread_.StartWithOptions(options); |
+ |
+ listener_.reset(new ExternalApplicationListenerPosix( |
+ loop_.task_runner(), io_thread_.task_runner())); |
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
+ socket_path_ = temp_dir_.path().Append(FILE_PATH_LITERAL("socket")); |
+ } |
+ |
+ protected: |
+ base::MessageLoop loop_; |
+ base::RunLoop run_loop_; |
+ base::Thread io_thread_; |
+ |
+ base::ScopedTempDir temp_dir_; |
+ ApplicationManager application_manager_; |
+ base::FilePath socket_path_; |
+ scoped_ptr<ExternalApplicationListener> listener_; |
+}; |
+ |
+namespace { |
+class StubShellImpl : public InterfaceImpl<Shell> { |
+ private: |
+ virtual void ConnectToApplication( |
+ const String& requestor_url, |
+ InterfaceRequest<ServiceProvider> in_service_provider) MOJO_OVERRIDE { |
+ ServiceProviderPtr out_service_provider; |
+ out_service_provider.Bind(in_service_provider.PassMessagePipe()); |
+ client()->AcceptConnection(requestor_url, out_service_provider.Pass()); |
+ } |
+}; |
+ |
+void DoLocalRegister(const GURL& app_url, ScopedMessagePipeHandle shell) { |
+ BindToPipe(new StubShellImpl, shell.Pass()); |
+} |
+ |
+void QuitLoopOnConnect(scoped_refptr<base::TaskRunner> loop, |
+ base::Closure quit_callback, |
+ int rv) { |
+ EXPECT_EQ(net::OK, rv); |
+ loop->PostTask(FROM_HERE, quit_callback); |
+} |
+ |
+void ConnectOnIOThread(const base::FilePath& socket_path, |
+ scoped_refptr<base::TaskRunner> to_quit, |
+ base::Closure quit_callback) { |
+ ExternalApplicationRegistrarConnection connection(socket_path); |
+ connection.Connect(base::Bind(&QuitLoopOnConnect, to_quit, quit_callback)); |
+} |
+} // namespace |
+ |
+TEST_F(ExternalApplicationListenerTest, ConnectConnection) { |
+ listener_->ListenInBackground(socket_path_, base::Bind(&DoLocalRegister)); |
+ listener_->WaitForListening(); |
+ io_thread_.task_runner()->PostTask(FROM_HERE, |
+ base::Bind(&ConnectOnIOThread, |
+ socket_path_, |
+ loop_.task_runner(), |
+ run_loop_.QuitClosure())); |
+ run_loop_.Run(); |
+} |
+ |
+namespace { |
+class QuitLoopOnConnectApplicationImpl : public InterfaceImpl<Application> { |
+ public: |
+ QuitLoopOnConnectApplicationImpl(const std::string& url, |
+ scoped_refptr<base::TaskRunner> loop, |
+ base::Closure quit_callback) |
+ : url_(url), to_quit_(loop), quit_callback_(quit_callback) {} |
+ |
+ private: |
+ virtual void Initialize(Array<String> args) MOJO_OVERRIDE {} |
+ |
+ virtual void AcceptConnection(const String& requestor_url, |
+ ServiceProviderPtr p) MOJO_OVERRIDE { |
+ DVLOG(1) << url_ << " accepting connection from " << requestor_url; |
+ to_quit_->PostTask(FROM_HERE, quit_callback_); |
+ } |
+ |
+ const std::string url_; |
+ scoped_refptr<base::TaskRunner> to_quit_; |
+ base::Closure quit_callback_; |
+}; |
+ |
+class FakeExternalApplication { |
+ public: |
+ FakeExternalApplication(const std::string& url) : url_(url) {} |
+ |
+ void ConnectSynchronously(const base::FilePath& socket_path) { |
+ connection_.reset(new ExternalApplicationRegistrarConnection(socket_path)); |
+ net::TestCompletionCallback connect_callback; |
+ connection_->Connect(connect_callback.callback()); |
+ connect_callback.WaitForResult(); |
+ } |
+ |
+ // application_impl is the the actual implementation to be registered. |
+ void Register(scoped_ptr<InterfaceImpl<Application>> application_impl, |
+ base::Closure register_complete_callback) { |
+ connection_->Register(GURL(url_), &ptr_, register_complete_callback); |
+ application_impl_ = application_impl.Pass(); |
+ ptr_.set_client(application_impl_.get()); |
+ } |
+ |
+ void ConnectToAppByUrl(std::string app_url) { |
+ ServiceProviderPtr sp; |
+ ptr_->ConnectToApplication(app_url, Get(&sp)); |
+ } |
+ |
+ const std::string& url() { return url_; } |
+ |
+ private: |
+ const std::string url_; |
+ scoped_ptr<InterfaceImpl<Application>> application_impl_; |
+ ShellPtr ptr_; |
+ |
+ scoped_ptr<ExternalApplicationRegistrarConnection> connection_; |
+}; |
+ |
+void ConnectToApp(FakeExternalApplication* connector, |
+ FakeExternalApplication* connectee) { |
+ connector->ConnectToAppByUrl(connectee->url()); |
+} |
+ |
+void NoOp() { |
+} |
+ |
+void ConnectAndRegisterOnIOThread(const base::FilePath& socket_path, |
+ scoped_refptr<base::TaskRunner> loop, |
+ base::Closure quit_callback, |
+ FakeExternalApplication* connector, |
+ FakeExternalApplication* connectee) { |
+ // Connect the first app to the registrar. |
+ connector->ConnectSynchronously(socket_path); |
+ // connector will use this implementation of the Mojo Application interface |
+ // once registration complete. |
+ scoped_ptr<QuitLoopOnConnectApplicationImpl> connector_app_impl( |
+ new QuitLoopOnConnectApplicationImpl( |
+ connector->url(), loop, quit_callback)); |
+ // Since connectee won't be ready when connector is done registering, pass |
+ // in a do-nothing callback. |
+ connector->Register(connector_app_impl.PassAs<InterfaceImpl<Application>>(), |
+ base::Bind(&NoOp)); |
+ |
+ // Connect the second app to the registrar. |
+ connectee->ConnectSynchronously(socket_path); |
+ scoped_ptr<QuitLoopOnConnectApplicationImpl> connectee_app_impl( |
+ new QuitLoopOnConnectApplicationImpl( |
+ connectee->url(), loop, quit_callback)); |
+ // After connectee is successfully registered, connector should be |
+ // able to connect to is by URL. Pass in a callback to attempt the |
+ // app -> app connection. |
+ connectee->Register(connectee_app_impl.PassAs<InterfaceImpl<Application>>(), |
+ base::Bind(&ConnectToApp, connector, connectee)); |
+} |
+ |
+void DestroyOnIOThread(scoped_ptr<FakeExternalApplication> doomed1, |
+ scoped_ptr<FakeExternalApplication> doomed2) { |
+} |
+} // namespace |
+ |
+// Create two external applications, have them discover and connect to |
+// the registrar, and then have one app connect to the other by URL. |
+TEST_F(ExternalApplicationListenerTest, ConnectTwoExternalApplications) { |
+ listener_->ListenInBackground( |
+ socket_path_, |
+ base::Bind(&ApplicationManager::RegisterExternalApplication, |
+ base::Unretained(&application_manager_))); |
+ listener_->WaitForListening(); |
+ |
+ // Create two external apps. |
+ scoped_ptr<FakeExternalApplication> supersweet_app( |
+ new FakeExternalApplication("http://my.supersweet.app")); |
+ scoped_ptr<FakeExternalApplication> awesome_app( |
+ new FakeExternalApplication("http://my.awesome.app")); |
+ |
+ // Connecting and talking to the registrar has to happen on the IO thread. |
+ io_thread_.task_runner()->PostTask(FROM_HERE, |
+ base::Bind(&ConnectAndRegisterOnIOThread, |
+ socket_path_, |
+ loop_.task_runner(), |
+ run_loop_.QuitClosure(), |
+ supersweet_app.get(), |
+ awesome_app.get())); |
+ run_loop_.Run(); |
+ |
+ // The apps need to be destroyed on the thread where they did socket stuff. |
+ io_thread_.task_runner()->PostTask(FROM_HERE, |
+ base::Bind(&DestroyOnIOThread, |
+ base::Passed(&supersweet_app), |
+ base::Passed(&awesome_app))); |
+} |
+ |
+} // namespace shell |
+} // namespace mojo |