| Index: mojo/shell/application_manager/application_manager_unittest.cc | 
| diff --git a/mojo/shell/application_manager/application_manager_unittest.cc b/mojo/shell/application_manager/application_manager_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..ed16d151352500e17a46f3d05af744a5fb1ee13f | 
| --- /dev/null | 
| +++ b/mojo/shell/application_manager/application_manager_unittest.cc | 
| @@ -0,0 +1,829 @@ | 
| +// 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/at_exit.h" | 
| +#include "base/bind.h" | 
| +#include "base/macros.h" | 
| +#include "base/memory/scoped_vector.h" | 
| +#include "base/message_loop/message_loop.h" | 
| +#include "mojo/public/cpp/application/application_connection.h" | 
| +#include "mojo/public/cpp/application/application_delegate.h" | 
| +#include "mojo/public/cpp/application/application_impl.h" | 
| +#include "mojo/public/cpp/application/interface_factory.h" | 
| +#include "mojo/public/cpp/bindings/strong_binding.h" | 
| +#include "mojo/public/interfaces/application/service_provider.mojom.h" | 
| +#include "mojo/shell/application_manager/application_loader.h" | 
| +#include "mojo/shell/application_manager/application_manager.h" | 
| +#include "mojo/shell/application_manager/test.mojom.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| + | 
| +namespace mojo { | 
| +namespace shell { | 
| +namespace { | 
| + | 
| +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(bool* value) { | 
| +  *value = true; | 
| +  base::MessageLoop::current()->QuitWhenIdle(); | 
| +} | 
| + | 
| +class QuitMessageLoopErrorHandler : public ErrorHandler { | 
| + public: | 
| +  QuitMessageLoopErrorHandler() {} | 
| +  ~QuitMessageLoopErrorHandler() override {} | 
| + | 
| +  // |ErrorHandler| implementation: | 
| +  void OnConnectionError() override { | 
| +    base::MessageLoop::current()->QuitWhenIdle(); | 
| +  } | 
| + | 
| + private: | 
| +  DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler); | 
| +}; | 
| + | 
| +class TestServiceImpl : public TestService { | 
| + public: | 
| +  TestServiceImpl(TestContext* context, InterfaceRequest<TestService> request) | 
| +      : context_(context), binding_(this, request.Pass()) { | 
| +    ++context_->num_impls; | 
| +  } | 
| + | 
| +  ~TestServiceImpl() override { | 
| +    --context_->num_impls; | 
| +    if (!base::MessageLoop::current()->is_running()) | 
| +      return; | 
| +    base::MessageLoop::current()->Quit(); | 
| +  } | 
| + | 
| +  // TestService implementation: | 
| +  void Test(const String& test_string, | 
| +            const Callback<void()>& callback) override { | 
| +    context_->last_test_string = test_string; | 
| +    callback.Run(); | 
| +  } | 
| + | 
| + private: | 
| +  TestContext* context_; | 
| +  StrongBinding<TestService> binding_; | 
| +}; | 
| + | 
| +class TestClient { | 
| + public: | 
| +  explicit TestClient(TestServicePtr service) | 
| +      : service_(service.Pass()), quit_after_ack_(false) {} | 
| + | 
| +  void AckTest() { | 
| +    if (quit_after_ack_) | 
| +      base::MessageLoop::current()->Quit(); | 
| +  } | 
| + | 
| +  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 TestApplicationLoader : public ApplicationLoader, | 
| +                              public ApplicationDelegate, | 
| +                              public InterfaceFactory<TestService> { | 
| + public: | 
| +  TestApplicationLoader() : context_(nullptr), num_loads_(0) {} | 
| + | 
| +  ~TestApplicationLoader() override { | 
| +    if (context_) | 
| +      ++context_->num_loader_deletes; | 
| +    test_app_.reset(); | 
| +  } | 
| + | 
| +  void set_context(TestContext* context) { context_ = context; } | 
| +  int num_loads() const { return num_loads_; } | 
| +  const std::vector<std::string>& GetArgs() const { return test_app_->args(); } | 
| + | 
| + private: | 
| +  // ApplicationLoader implementation. | 
| +  void Load(const GURL& url, | 
| +            InterfaceRequest<Application> application_request) override { | 
| +    ++num_loads_; | 
| +    test_app_.reset(new ApplicationImpl(this, application_request.Pass())); | 
| +  } | 
| + | 
| +  // ApplicationDelegate implementation. | 
| +  bool ConfigureIncomingConnection(ApplicationConnection* connection) override { | 
| +    connection->AddService(this); | 
| +    return true; | 
| +  } | 
| + | 
| +  // InterfaceFactory implementation. | 
| +  void Create(ApplicationConnection* connection, | 
| +              InterfaceRequest<TestService> request) override { | 
| +    new TestServiceImpl(context_, request.Pass()); | 
| +  } | 
| + | 
| +  scoped_ptr<ApplicationImpl> test_app_; | 
| +  TestContext* context_; | 
| +  int num_loads_; | 
| +  DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader); | 
| +}; | 
| + | 
| +class ClosingApplicationLoader : public ApplicationLoader { | 
| + private: | 
| +  // ApplicationLoader implementation. | 
| +  void Load(const GURL& url, | 
| +            InterfaceRequest<Application> application_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 url will be correctly passed. | 
| +class TestAImpl : public TestA { | 
| + public: | 
| +  TestAImpl(ApplicationImpl* app_impl, | 
| +            TesterContext* test_context, | 
| +            InterfaceRequest<TestA> request) | 
| +      : test_context_(test_context), binding_(this, request.Pass()) { | 
| +    app_impl->ConnectToApplication(kTestBURLString)->ConnectToService(&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()->Quit(); | 
| +    test_context_->set_a_called_quit(); | 
| +    test_context_->QuitSoon(); | 
| +  } | 
| + | 
| +  TesterContext* test_context_; | 
| +  TestBPtr b_; | 
| +  StrongBinding<TestA> binding_; | 
| +}; | 
| + | 
| +class TestBImpl : public TestB { | 
| + public: | 
| +  TestBImpl(ApplicationConnection* connection, | 
| +            TesterContext* test_context, | 
| +            InterfaceRequest<TestB> request) | 
| +      : test_context_(test_context), binding_(this, request.Pass()) { | 
| +    connection->ConnectToService(&c_); | 
| +  } | 
| + | 
| +  ~TestBImpl() override { | 
| +    test_context_->IncrementNumBDeletes(); | 
| +    if (base::MessageLoop::current()->is_running()) | 
| +      base::MessageLoop::current()->Quit(); | 
| +    test_context_->QuitSoon(); | 
| +  } | 
| + | 
| + private: | 
| +  void B(const Callback<void()>& callback) override { | 
| +    test_context_->IncrementNumBCalls(); | 
| +    callback.Run(); | 
| +  } | 
| + | 
| +  void CallC(const Callback<void()>& callback) override { | 
| +    test_context_->IncrementNumBCalls(); | 
| +    c_->C(callback); | 
| +  } | 
| + | 
| +  TesterContext* test_context_; | 
| +  TestCPtr c_; | 
| +  StrongBinding<TestB> binding_; | 
| +}; | 
| + | 
| +class TestCImpl : public TestC { | 
| + public: | 
| +  TestCImpl(ApplicationConnection* connection, | 
| +            TesterContext* test_context, | 
| +            InterfaceRequest<TestC> request) | 
| +      : test_context_(test_context), binding_(this, request.Pass()) {} | 
| + | 
| +  ~TestCImpl() override { test_context_->IncrementNumCDeletes(); } | 
| + | 
| + private: | 
| +  void C(const Callback<void()>& callback) override { | 
| +    test_context_->IncrementNumCCalls(); | 
| +    callback.Run(); | 
| +  } | 
| + | 
| +  TesterContext* test_context_; | 
| +  StrongBinding<TestC> binding_; | 
| +}; | 
| + | 
| +class Tester : public ApplicationDelegate, | 
| +               public ApplicationLoader, | 
| +               public InterfaceFactory<TestA>, | 
| +               public InterfaceFactory<TestB>, | 
| +               public InterfaceFactory<TestC> { | 
| + public: | 
| +  Tester(TesterContext* context, const std::string& requestor_url) | 
| +      : context_(context), requestor_url_(requestor_url) {} | 
| +  ~Tester() override {} | 
| + | 
| + private: | 
| +  void Load(const GURL& url, | 
| +            InterfaceRequest<Application> application_request) override { | 
| +    app_.reset(new ApplicationImpl(this, application_request.Pass())); | 
| +  } | 
| + | 
| +  bool ConfigureIncomingConnection(ApplicationConnection* connection) override { | 
| +    if (!requestor_url_.empty() && | 
| +        requestor_url_ != connection->GetRemoteApplicationURL()) { | 
| +      context_->set_tester_called_quit(); | 
| +      context_->QuitSoon(); | 
| +      base::MessageLoop::current()->Quit(); | 
| +      return false; | 
| +    } | 
| +    // If we're coming from A, then add B, otherwise A. | 
| +    if (connection->GetRemoteApplicationURL() == kTestAURLString) | 
| +      connection->AddService<TestB>(this); | 
| +    else | 
| +      connection->AddService<TestA>(this); | 
| +    return true; | 
| +  } | 
| + | 
| +  bool ConfigureOutgoingConnection(ApplicationConnection* connection) override { | 
| +    // If we're connecting to B, then add C. | 
| +    if (connection->GetRemoteApplicationURL() == kTestBURLString) | 
| +      connection->AddService<TestC>(this); | 
| +    return true; | 
| +  } | 
| + | 
| +  void Create(ApplicationConnection* connection, | 
| +              InterfaceRequest<TestA> request) override { | 
| +    a_bindings_.push_back(new TestAImpl(app_.get(), context_, request.Pass())); | 
| +  } | 
| + | 
| +  void Create(ApplicationConnection* connection, | 
| +              InterfaceRequest<TestB> request) override { | 
| +    new TestBImpl(connection, context_, request.Pass()); | 
| +  } | 
| + | 
| +  void Create(ApplicationConnection* connection, | 
| +              InterfaceRequest<TestC> request) override { | 
| +    new TestCImpl(connection, context_, request.Pass()); | 
| +  } | 
| + | 
| +  TesterContext* context_; | 
| +  scoped_ptr<ApplicationImpl> app_; | 
| +  std::string requestor_url_; | 
| +  ScopedVector<TestAImpl> a_bindings_; | 
| +}; | 
| + | 
| +class TestDelegate : public ApplicationManager::Delegate { | 
| + public: | 
| +  void AddMapping(const GURL& from, const GURL& to) { mappings_[from] = to; } | 
| + | 
| +  // ApplicationManager::Delegate | 
| +  GURL ResolveMappings(const GURL& url) override { | 
| +    auto it = mappings_.find(url); | 
| +    if (it != mappings_.end()) | 
| +      return it->second; | 
| +    return url; | 
| +  } | 
| + | 
| +  // ApplicationManager::Delegate | 
| +  GURL ResolveURL(const GURL& url) override { | 
| +    GURL mapped_url = ResolveMappings(url); | 
| +    // The shell automatically map mojo URLs. | 
| +    if (mapped_url.scheme() == "mojo") { | 
| +      url::Replacements<char> replacements; | 
| +      replacements.SetScheme("file", url::Component(0, 4)); | 
| +      mapped_url = mapped_url.ReplaceComponents(replacements); | 
| +    } | 
| +    return mapped_url; | 
| +  } | 
| + | 
| + private: | 
| +  std::map<GURL, GURL> mappings_; | 
| +}; | 
| + | 
| +class TestExternal : public ApplicationDelegate { | 
| + public: | 
| +  TestExternal() : configure_incoming_connection_called_(false) {} | 
| + | 
| +  void Initialize(ApplicationImpl* app) override { | 
| +    initialize_args_ = app->args(); | 
| +    base::MessageLoop::current()->Quit(); | 
| +  } | 
| + | 
| +  bool ConfigureIncomingConnection(ApplicationConnection* connection) override { | 
| +    configure_incoming_connection_called_ = true; | 
| +    base::MessageLoop::current()->Quit(); | 
| +    return true; | 
| +  } | 
| + | 
| +  const std::vector<std::string>& initialize_args() const { | 
| +    return initialize_args_; | 
| +  } | 
| + | 
| +  bool configure_incoming_connection_called() const { | 
| +    return configure_incoming_connection_called_; | 
| +  } | 
| + | 
| + private: | 
| +  std::vector<std::string> initialize_args_; | 
| +  bool configure_incoming_connection_called_; | 
| +}; | 
| + | 
| +class ApplicationManagerTest : public testing::Test { | 
| + public: | 
| +  ApplicationManagerTest() : tester_context_(&loop_) {} | 
| + | 
| +  ~ApplicationManagerTest() override {} | 
| + | 
| +  void SetUp() override { | 
| +    application_manager_.reset(new ApplicationManager(&test_delegate_)); | 
| +    test_loader_ = new TestApplicationLoader; | 
| +    test_loader_->set_context(&context_); | 
| +    application_manager_->set_default_loader( | 
| +        scoped_ptr<ApplicationLoader>(test_loader_)); | 
| + | 
| +    TestServicePtr service_proxy; | 
| +    application_manager_->ConnectToService(GURL(kTestURLString), | 
| +                                           &service_proxy); | 
| +    test_client_.reset(new TestClient(service_proxy.Pass())); | 
| +  } | 
| + | 
| +  void TearDown() override { | 
| +    test_client_.reset(); | 
| +    application_manager_.reset(); | 
| +  } | 
| + | 
| +  void AddLoaderForURL(const GURL& url, const std::string& requestor_url) { | 
| +    application_manager_->SetLoaderForURL( | 
| +        make_scoped_ptr(new Tester(&tester_context_, requestor_url)), url); | 
| +  } | 
| + | 
| +  bool HasFactoryForTestURL() { | 
| +    ApplicationManager::TestAPI manager_test_api(application_manager_.get()); | 
| +    return manager_test_api.HasFactoryForURL(GURL(kTestURLString)); | 
| +  } | 
| + | 
| + protected: | 
| +  base::ShadowingAtExitManager at_exit_; | 
| +  TestDelegate test_delegate_; | 
| +  TestApplicationLoader* test_loader_; | 
| +  TesterContext tester_context_; | 
| +  TestContext context_; | 
| +  base::MessageLoop loop_; | 
| +  scoped_ptr<TestClient> test_client_; | 
| +  scoped_ptr<ApplicationManager> application_manager_; | 
| +  DISALLOW_COPY_AND_ASSIGN(ApplicationManagerTest); | 
| +}; | 
| + | 
| +TEST_F(ApplicationManagerTest, Basic) { | 
| +  test_client_->Test("test"); | 
| +  loop_.Run(); | 
| +  EXPECT_EQ(std::string("test"), context_.last_test_string); | 
| +} | 
| + | 
| +// Confirm that no arguments are sent to an application by default. | 
| +TEST_F(ApplicationManagerTest, NoArgs) { | 
| +  ApplicationManager am(&test_delegate_); | 
| +  GURL test_url("test:test"); | 
| +  TestApplicationLoader* loader = new TestApplicationLoader; | 
| +  loader->set_context(&context_); | 
| +  am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url); | 
| +  TestServicePtr test_service; | 
| +  am.ConnectToService(test_url, &test_service); | 
| +  TestClient test_client(test_service.Pass()); | 
| +  test_client.Test("test"); | 
| +  loop_.Run(); | 
| +  std::vector<std::string> app_args = loader->GetArgs(); | 
| +  EXPECT_EQ(0U, app_args.size()); | 
| +} | 
| + | 
| +// Confirm that arguments are sent to an application. | 
| +TEST_F(ApplicationManagerTest, Args) { | 
| +  ApplicationManager am(&test_delegate_); | 
| +  GURL test_url("test:test"); | 
| +  std::vector<std::string> args; | 
| +  args.push_back("test_arg1"); | 
| +  args.push_back("test_arg2"); | 
| +  am.SetArgsForURL(args, test_url); | 
| +  TestApplicationLoader* loader = new TestApplicationLoader; | 
| +  loader->set_context(&context_); | 
| +  am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url); | 
| +  TestServicePtr test_service; | 
| +  am.ConnectToService(test_url, &test_service); | 
| +  TestClient test_client(test_service.Pass()); | 
| +  test_client.Test("test"); | 
| +  loop_.Run(); | 
| +  std::vector<std::string> app_args = loader->GetArgs(); | 
| +  ASSERT_EQ(args.size(), app_args.size()); | 
| +  EXPECT_EQ(args[0], app_args[0]); | 
| +  EXPECT_EQ(args[1], app_args[1]); | 
| +} | 
| + | 
| +// Confirm that arguments are aggregated through mappings. | 
| +TEST_F(ApplicationManagerTest, ArgsAndMapping) { | 
| +  ApplicationManager am(&test_delegate_); | 
| +  GURL test_url("test:test"); | 
| +  GURL test_url2("test:test2"); | 
| +  test_delegate_.AddMapping(test_url, test_url2); | 
| +  std::vector<std::string> args; | 
| +  args.push_back("test_arg1"); | 
| +  args.push_back("test_arg2"); | 
| +  am.SetArgsForURL(args, test_url); | 
| +  std::vector<std::string> args2; | 
| +  args2.push_back("test_arg3"); | 
| +  args2.push_back("test_arg4"); | 
| +  am.SetArgsForURL(args2, test_url2); | 
| +  TestApplicationLoader* loader = new TestApplicationLoader; | 
| +  loader->set_context(&context_); | 
| +  am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url2); | 
| +  { | 
| +    // Connext to the mapped url | 
| +    TestServicePtr test_service; | 
| +    am.ConnectToService(test_url, &test_service); | 
| +    TestClient test_client(test_service.Pass()); | 
| +    test_client.Test("test"); | 
| +    loop_.Run(); | 
| +    std::vector<std::string> app_args = loader->GetArgs(); | 
| +    ASSERT_EQ(args.size() + args2.size(), app_args.size()); | 
| +    EXPECT_EQ(args[0], app_args[0]); | 
| +    EXPECT_EQ(args[1], app_args[1]); | 
| +    EXPECT_EQ(args2[0], app_args[2]); | 
| +    EXPECT_EQ(args2[1], app_args[3]); | 
| +  } | 
| +  { | 
| +    // Connext to the target url | 
| +    TestServicePtr test_service; | 
| +    am.ConnectToService(test_url2, &test_service); | 
| +    TestClient test_client(test_service.Pass()); | 
| +    test_client.Test("test"); | 
| +    loop_.Run(); | 
| +    std::vector<std::string> app_args = loader->GetArgs(); | 
| +    ASSERT_EQ(args.size() + args2.size(), app_args.size()); | 
| +    EXPECT_EQ(args[0], app_args[0]); | 
| +    EXPECT_EQ(args[1], app_args[1]); | 
| +    EXPECT_EQ(args2[0], app_args[2]); | 
| +    EXPECT_EQ(args2[1], app_args[3]); | 
| +  } | 
| +} | 
| + | 
| +TEST_F(ApplicationManagerTest, ClientError) { | 
| +  test_client_->Test("test"); | 
| +  EXPECT_TRUE(HasFactoryForTestURL()); | 
| +  loop_.Run(); | 
| +  EXPECT_EQ(1, context_.num_impls); | 
| +  test_client_.reset(); | 
| +  loop_.Run(); | 
| +  EXPECT_EQ(0, context_.num_impls); | 
| +  EXPECT_TRUE(HasFactoryForTestURL()); | 
| +} | 
| + | 
| +TEST_F(ApplicationManagerTest, Deletes) { | 
| +  { | 
| +    ApplicationManager am(&test_delegate_); | 
| +    TestApplicationLoader* default_loader = new TestApplicationLoader; | 
| +    default_loader->set_context(&context_); | 
| +    TestApplicationLoader* url_loader1 = new TestApplicationLoader; | 
| +    TestApplicationLoader* url_loader2 = new TestApplicationLoader; | 
| +    url_loader1->set_context(&context_); | 
| +    url_loader2->set_context(&context_); | 
| +    TestApplicationLoader* scheme_loader1 = new TestApplicationLoader; | 
| +    TestApplicationLoader* scheme_loader2 = new TestApplicationLoader; | 
| +    scheme_loader1->set_context(&context_); | 
| +    scheme_loader2->set_context(&context_); | 
| +    am.set_default_loader(scoped_ptr<ApplicationLoader>(default_loader)); | 
| +    am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader1), | 
| +                       GURL("test:test1")); | 
| +    am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader2), | 
| +                       GURL("test:test1")); | 
| +    am.SetLoaderForScheme(scoped_ptr<ApplicationLoader>(scheme_loader1), | 
| +                          "test"); | 
| +    am.SetLoaderForScheme(scoped_ptr<ApplicationLoader>(scheme_loader2), | 
| +                          "test"); | 
| +  } | 
| +  EXPECT_EQ(5, context_.num_loader_deletes); | 
| +} | 
| + | 
| +// Confirm that both urls and schemes can have their loaders explicitly set. | 
| +TEST_F(ApplicationManagerTest, SetLoaders) { | 
| +  TestApplicationLoader* default_loader = new TestApplicationLoader; | 
| +  TestApplicationLoader* url_loader = new TestApplicationLoader; | 
| +  TestApplicationLoader* scheme_loader = new TestApplicationLoader; | 
| +  application_manager_->set_default_loader( | 
| +      scoped_ptr<ApplicationLoader>(default_loader)); | 
| +  application_manager_->SetLoaderForURL( | 
| +      scoped_ptr<ApplicationLoader>(url_loader), GURL("test:test1")); | 
| +  application_manager_->SetLoaderForScheme( | 
| +      scoped_ptr<ApplicationLoader>(scheme_loader), "test"); | 
| + | 
| +  // test::test1 should go to url_loader. | 
| +  TestServicePtr test_service; | 
| +  application_manager_->ConnectToService(GURL("test:test1"), &test_service); | 
| +  EXPECT_EQ(1, url_loader->num_loads()); | 
| +  EXPECT_EQ(0, scheme_loader->num_loads()); | 
| +  EXPECT_EQ(0, default_loader->num_loads()); | 
| + | 
| +  // test::test2 should go to scheme loader. | 
| +  application_manager_->ConnectToService(GURL("test:test2"), &test_service); | 
| +  EXPECT_EQ(1, url_loader->num_loads()); | 
| +  EXPECT_EQ(1, scheme_loader->num_loads()); | 
| +  EXPECT_EQ(0, default_loader->num_loads()); | 
| + | 
| +  // http::test1 should go to default loader. | 
| +  application_manager_->ConnectToService(GURL("http:test1"), &test_service); | 
| +  EXPECT_EQ(1, url_loader->num_loads()); | 
| +  EXPECT_EQ(1, scheme_loader->num_loads()); | 
| +  EXPECT_EQ(1, default_loader->num_loads()); | 
| +} | 
| + | 
| +// Confirm that the url of a service is correctly passed to another service that | 
| +// it loads. | 
| +TEST_F(ApplicationManagerTest, ACallB) { | 
| +  // Any url can load a. | 
| +  AddLoaderForURL(GURL(kTestAURLString), std::string()); | 
| + | 
| +  // Only a can load b. | 
| +  AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); | 
| + | 
| +  TestAPtr a; | 
| +  application_manager_->ConnectToService(GURL(kTestAURLString), &a); | 
| +  a->CallB(); | 
| +  loop_.Run(); | 
| +  EXPECT_EQ(1, tester_context_.num_b_calls()); | 
| +  EXPECT_TRUE(tester_context_.a_called_quit()); | 
| +} | 
| + | 
| +// A calls B which calls C. | 
| +TEST_F(ApplicationManagerTest, BCallC) { | 
| +  // Any url can load a. | 
| +  AddLoaderForURL(GURL(kTestAURLString), std::string()); | 
| + | 
| +  // Only a can load b. | 
| +  AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); | 
| + | 
| +  TestAPtr a; | 
| +  application_manager_->ConnectToService(GURL(kTestAURLString), &a); | 
| +  a->CallCFromB(); | 
| +  loop_.Run(); | 
| + | 
| +  EXPECT_EQ(1, tester_context_.num_b_calls()); | 
| +  EXPECT_EQ(1, tester_context_.num_c_calls()); | 
| +  EXPECT_TRUE(tester_context_.a_called_quit()); | 
| +} | 
| + | 
| +// Confirm that a service impl will be deleted if the app that connected to | 
| +// it goes away. | 
| +TEST_F(ApplicationManagerTest, BDeleted) { | 
| +  AddLoaderForURL(GURL(kTestAURLString), std::string()); | 
| +  AddLoaderForURL(GURL(kTestBURLString), std::string()); | 
| + | 
| +  TestAPtr a; | 
| +  application_manager_->ConnectToService(GURL(kTestAURLString), &a); | 
| + | 
| +  a->CallB(); | 
| +  loop_.Run(); | 
| + | 
| +  // Kills the a app. | 
| +  application_manager_->SetLoaderForURL(scoped_ptr<ApplicationLoader>(), | 
| +                                        GURL(kTestAURLString)); | 
| +  loop_.Run(); | 
| + | 
| +  EXPECT_EQ(1, tester_context_.num_b_deletes()); | 
| +} | 
| + | 
| +// Confirm that the url of a service is correctly passed to another service that | 
| +// it loads, and that it can be rejected. | 
| +TEST_F(ApplicationManagerTest, ANoLoadB) { | 
| +  // Any url can load a. | 
| +  AddLoaderForURL(GURL(kTestAURLString), std::string()); | 
| + | 
| +  // Only c can load b, so this will fail. | 
| +  AddLoaderForURL(GURL(kTestBURLString), "test:TestC"); | 
| + | 
| +  TestAPtr a; | 
| +  application_manager_->ConnectToService(GURL(kTestAURLString), &a); | 
| +  a->CallB(); | 
| +  loop_.Run(); | 
| +  EXPECT_EQ(0, tester_context_.num_b_calls()); | 
| + | 
| +  EXPECT_FALSE(tester_context_.a_called_quit()); | 
| +  EXPECT_TRUE(tester_context_.tester_called_quit()); | 
| +} | 
| + | 
| +TEST_F(ApplicationManagerTest, NoServiceNoLoad) { | 
| +  AddLoaderForURL(GURL(kTestAURLString), std::string()); | 
| + | 
| +  // There is no TestC service implementation registered with | 
| +  // ApplicationManager, so this cannot succeed (but also shouldn't crash). | 
| +  TestCPtr c; | 
| +  application_manager_->ConnectToService(GURL(kTestAURLString), &c); | 
| +  QuitMessageLoopErrorHandler quitter; | 
| +  c.set_error_handler(&quitter); | 
| + | 
| +  loop_.Run(); | 
| +  EXPECT_TRUE(c.encountered_error()); | 
| +} | 
| + | 
| +TEST_F(ApplicationManagerTest, MappedURLsShouldNotCauseDuplicateLoad) { | 
| +  test_delegate_.AddMapping(GURL("foo:foo2"), GURL("foo:foo")); | 
| +  // 1 because ApplicationManagerTest connects once at startup. | 
| +  EXPECT_EQ(1, test_loader_->num_loads()); | 
| + | 
| +  TestServicePtr test_service; | 
| +  application_manager_->ConnectToService(GURL("foo:foo"), &test_service); | 
| +  EXPECT_EQ(2, test_loader_->num_loads()); | 
| + | 
| +  TestServicePtr test_service2; | 
| +  application_manager_->ConnectToService(GURL("foo:foo2"), &test_service2); | 
| +  EXPECT_EQ(2, test_loader_->num_loads()); | 
| + | 
| +  TestServicePtr test_service3; | 
| +  application_manager_->ConnectToService(GURL("bar:bar"), &test_service2); | 
| +  EXPECT_EQ(3, test_loader_->num_loads()); | 
| +} | 
| + | 
| +TEST_F(ApplicationManagerTest, MappedURLsShouldWorkWithLoaders) { | 
| +  TestApplicationLoader* custom_loader = new TestApplicationLoader; | 
| +  TestContext context; | 
| +  custom_loader->set_context(&context); | 
| +  application_manager_->SetLoaderForURL(make_scoped_ptr(custom_loader), | 
| +                                        GURL("mojo:foo")); | 
| +  test_delegate_.AddMapping(GURL("mojo:foo2"), GURL("mojo:foo")); | 
| + | 
| +  TestServicePtr test_service; | 
| +  application_manager_->ConnectToService(GURL("mojo:foo2"), &test_service); | 
| +  EXPECT_EQ(1, custom_loader->num_loads()); | 
| +  custom_loader->set_context(nullptr); | 
| +} | 
| + | 
| +TEST_F(ApplicationManagerTest, ExternalApp) { | 
| +  ApplicationPtr application; | 
| +  TestExternal external; | 
| +  std::vector<std::string> args; | 
| +  args.push_back("test"); | 
| +  ApplicationImpl app(&external, GetProxy(&application)); | 
| +  application_manager_->RegisterExternalApplication(GURL("mojo:test"), args, | 
| +                                                    application.Pass()); | 
| +  loop_.Run(); | 
| +  EXPECT_EQ(args, external.initialize_args()); | 
| +  application_manager_->ConnectToServiceByName(GURL("mojo:test"), | 
| +                                               std::string()); | 
| +  loop_.Run(); | 
| +  EXPECT_TRUE(external.configure_incoming_connection_called()); | 
| +}; | 
| + | 
| +TEST_F(ApplicationManagerTest, TestQueryWithLoaders) { | 
| +  TestApplicationLoader* url_loader = new TestApplicationLoader; | 
| +  TestApplicationLoader* scheme_loader = new TestApplicationLoader; | 
| +  application_manager_->SetLoaderForURL( | 
| +      scoped_ptr<ApplicationLoader>(url_loader), GURL("test:test1")); | 
| +  application_manager_->SetLoaderForScheme( | 
| +      scoped_ptr<ApplicationLoader>(scheme_loader), "test"); | 
| + | 
| +  // test::test1 should go to url_loader. | 
| +  TestServicePtr test_service; | 
| +  application_manager_->ConnectToService(GURL("test:test1?foo=bar"), | 
| +                                         &test_service); | 
| +  EXPECT_EQ(1, url_loader->num_loads()); | 
| +  EXPECT_EQ(0, scheme_loader->num_loads()); | 
| + | 
| +  // test::test2 should go to scheme loader. | 
| +  application_manager_->ConnectToService(GURL("test:test2?foo=bar"), | 
| +                                         &test_service); | 
| +  EXPECT_EQ(1, url_loader->num_loads()); | 
| +  EXPECT_EQ(1, scheme_loader->num_loads()); | 
| +} | 
| + | 
| +TEST_F(ApplicationManagerTest, TestEndApplicationClosure) { | 
| +  ClosingApplicationLoader* loader = new ClosingApplicationLoader(); | 
| +  application_manager_->SetLoaderForScheme( | 
| +      scoped_ptr<ApplicationLoader>(loader), "test"); | 
| + | 
| +  bool called = false; | 
| +  application_manager_->ConnectToApplication( | 
| +      GURL("test:test"), GURL(), nullptr, nullptr, | 
| +      base::Bind(&QuitClosure, base::Unretained(&called))); | 
| +  loop_.Run(); | 
| +  EXPECT_TRUE(called); | 
| +} | 
| + | 
| +}  // namespace | 
| +}  // namespace shell | 
| +}  // namespace mojo | 
|  |