Home | History | Annotate | Download | only in shell
      1 // Copyright 2014 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 "mojo/shell/shell_test_base.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/i18n/time_formatting.h"
      9 #include "base/macros.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "mojo/public/cpp/bindings/error_handler.h"
     13 #include "mojo/public/cpp/bindings/interface_ptr.h"
     14 #include "mojo/public/cpp/system/core.h"
     15 #include "mojo/services/test_service/test_request_tracker.mojom.h"
     16 #include "mojo/services/test_service/test_service.mojom.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 #include "url/gurl.h"
     19 
     20 using mojo::test::ServiceReport;
     21 using mojo::test::ServiceReportPtr;
     22 using mojo::test::TestService;
     23 using mojo::test::TestTimeService;
     24 using mojo::test::TestServicePtr;
     25 using mojo::test::TestTimeServicePtr;
     26 using mojo::test::TestTrackedRequestService;
     27 using mojo::test::TestTrackedRequestServicePtr;
     28 
     29 namespace mojo {
     30 namespace shell {
     31 namespace test {
     32 namespace {
     33 
     34 void GetReportCallback(base::MessageLoop* loop,
     35                        std::vector<ServiceReport>* reports_out,
     36                        mojo::Array<ServiceReportPtr> report) {
     37   for (size_t i = 0; i < report.size(); i++)
     38     reports_out->push_back(*report[i]);
     39   loop->QuitWhenIdle();
     40 }
     41 
     42 class ShellTestBaseTest : public ShellTestBase {
     43  public:
     44   // Convenience helpers for use as callbacks in tests.
     45   template <typename T>
     46   base::Callback<void()> SetAndQuit(T* val, T result) {
     47     return base::Bind(&ShellTestBaseTest::SetAndQuitImpl<T>,
     48         base::Unretained(this), val, result);
     49   }
     50   template <typename T>
     51   base::Callback<void(T result)> SetAndQuit(T* val) {
     52       return base::Bind(&ShellTestBaseTest::SetAndQuitImpl<T>,
     53           base::Unretained(this), val);
     54   }
     55   static GURL test_app_url() {
     56     return GURL("mojo:mojo_test_app");
     57   }
     58 
     59   void GetReport(std::vector<ServiceReport>* report) {
     60     ConnectToService(GURL("mojo:mojo_test_request_tracker_app"),
     61                      &request_tracking_);
     62     request_tracking_->GetReport(base::Bind(&GetReportCallback,
     63         base::Unretained(message_loop()),
     64         base::Unretained(report)));
     65     message_loop()->Run();
     66   }
     67 
     68  private:
     69   template<typename T>
     70   void SetAndQuitImpl(T* val, T result) {
     71     *val = result;
     72     message_loop()->QuitWhenIdle();
     73   }
     74   TestTrackedRequestServicePtr request_tracking_;
     75 };
     76 
     77 class QuitMessageLoopErrorHandler : public ErrorHandler {
     78  public:
     79   QuitMessageLoopErrorHandler() {}
     80   virtual ~QuitMessageLoopErrorHandler() {}
     81 
     82   // |ErrorHandler| implementation:
     83   virtual void OnConnectionError() OVERRIDE {
     84     base::MessageLoop::current()->QuitWhenIdle();
     85   }
     86 
     87  private:
     88   DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler);
     89 };
     90 
     91 // Tests that we can connect to a single service within a single app.
     92 TEST_F(ShellTestBaseTest, ConnectBasic) {
     93   InterfacePtr<TestService> service;
     94   ConnectToService(test_app_url(), &service);
     95 
     96   bool was_run = false;
     97   service->Ping(SetAndQuit<bool>(&was_run, true));
     98   message_loop()->Run();
     99   EXPECT_TRUE(was_run);
    100   EXPECT_FALSE(service.encountered_error());
    101 
    102   service.reset();
    103 
    104   // This will run until the test app has actually quit (which it will,
    105   // since we killed the only connection to it).
    106   message_loop()->Run();
    107 }
    108 
    109 // Tests that trying to connect to a service fails properly if the service
    110 // doesn't exist. Implicit in this test is verification that the shell
    111 // terminates if no services are running.
    112 TEST_F(ShellTestBaseTest, ConnectInvalidService) {
    113   InterfacePtr<TestService> test_service;
    114   ConnectToService(GURL("mojo:non_existent_service"), &test_service);
    115 
    116   bool was_run = false;
    117   test_service->Ping(SetAndQuit<bool>(&was_run, true));
    118 
    119   // This will quit because there's nothing running.
    120   message_loop()->Run();
    121   EXPECT_FALSE(was_run);
    122 
    123   // It may have quit before an error was processed.
    124   if (!test_service.encountered_error()) {
    125     QuitMessageLoopErrorHandler quitter;
    126     test_service.set_error_handler(&quitter);
    127     message_loop()->Run();
    128     EXPECT_TRUE(test_service.encountered_error());
    129   }
    130 
    131   test_service.reset();
    132 }
    133 
    134 // Tests that we can connect to a single service within a single app using
    135 // a network based loader instead of local files.
    136 // TODO(tim): Disabled because network service leaks NSS at exit, meaning
    137 // subsequent tests can't init properly.
    138 TEST_F(ShellTestBaseTest, DISABLED_ConnectBasicNetwork) {
    139   InterfacePtr<TestService> service;
    140   ConnectToService(test_app_url(), &service);
    141 
    142   bool was_run = false;
    143   service->Ping(SetAndQuit<bool>(&was_run, true));
    144   message_loop()->Run();
    145   EXPECT_TRUE(was_run);
    146   EXPECT_FALSE(service.encountered_error());
    147 
    148   // Note that use of the network service is implicit in this test.
    149   // Since TestService is not the only service in use, the shell won't auto
    150   // magically exit when TestService is destroyed (unlike ConnectBasic).
    151   // Tearing down the shell context will kill connections. The shell loop will
    152   // exit as soon as no more apps are connected.
    153   // TODO(tim): crbug.com/392685.  Calling this explicitly shouldn't be
    154   // necessary once the shell terminates if the primordial app exits, which
    155   // we could enforce here by resetting |service|.
    156   shell_context()->application_manager()->TerminateShellConnections();
    157   message_loop()->Run();  // Waits for all connections to die.
    158 }
    159 
    160 // Tests that trying to connect to a service over network fails preoprly
    161 // if the service doesn't exist.
    162 // TODO(tim): Disabled because network service leaks NSS at exit, meaning
    163 // subsequent tests can't init properly.
    164 TEST_F(ShellTestBaseTest, DISABLED_ConnectInvalidServiceNetwork) {
    165   InterfacePtr<TestService> test_service;
    166   ConnectToServiceViaNetwork(GURL("mojo:non_existent_service"), &test_service);
    167   QuitMessageLoopErrorHandler quitter;
    168   test_service.set_error_handler(&quitter);
    169   bool was_run = false;
    170   test_service->Ping(SetAndQuit<bool>(&was_run, true));
    171   message_loop()->Run();
    172   EXPECT_TRUE(test_service.encountered_error());
    173 
    174   // TODO(tim): crbug.com/392685.  Calling this explicitly shouldn't be
    175   // necessary once the shell terminates if the primordial app exits, which
    176   // we could enforce here by resetting |service|.
    177   shell_context()->application_manager()->TerminateShellConnections();
    178   message_loop()->Run();  // Waits for all connections to die.
    179 }
    180 
    181 // Similar to ConnectBasic, but causes the app to instantiate multiple
    182 // service implementation objects and verifies the shell can reach both.
    183 TEST_F(ShellTestBaseTest, ConnectMultipleInstancesPerApp) {
    184   {
    185     TestServicePtr service1, service2;
    186     ConnectToService(test_app_url(), &service1);
    187     ConnectToService(test_app_url(), &service2);
    188 
    189     bool was_run1 = false;
    190     bool was_run2 = false;
    191     service1->Ping(SetAndQuit<bool>(&was_run1, true));
    192     message_loop()->Run();
    193     service2->Ping(SetAndQuit<bool>(&was_run2, true));
    194     message_loop()->Run();
    195     EXPECT_TRUE(was_run1);
    196     EXPECT_TRUE(was_run2);
    197     EXPECT_FALSE(service1.encountered_error());
    198     EXPECT_FALSE(service2.encountered_error());
    199   }
    200   message_loop()->Run();
    201 }
    202 
    203 // Tests that service A and service B, both in App 1, can talk to each other
    204 // and parameters are passed around properly.
    205 TEST_F(ShellTestBaseTest, ConnectDifferentServicesInSingleApp) {
    206   // Have a TestService GetPartyTime on a TestTimeService in the same app.
    207   int64 time_message;
    208   TestServicePtr service;
    209   ConnectToService(test_app_url(), &service);
    210   service->ConnectToAppAndGetTime(test_app_url().spec(),
    211                                   SetAndQuit<int64>(&time_message));
    212   message_loop()->Run();
    213 
    214   // Verify by hitting the TimeService directly.
    215   TestTimeServicePtr time_service;
    216   ConnectToService(test_app_url(), &time_service);
    217   int64 party_time;
    218   time_service->GetPartyTime(SetAndQuit<int64>(&party_time));
    219   message_loop()->Run();
    220 
    221   EXPECT_EQ(time_message, party_time);
    222 }
    223 
    224 // Tests that a service A in App 1 can talk to service B in App 2 and
    225 // parameters are passed around properly.
    226 TEST_F(ShellTestBaseTest, ConnectDifferentServicesInDifferentApps) {
    227   int64 time_message;
    228   TestServicePtr service;
    229   ConnectToService(test_app_url(), &service);
    230   service->ConnectToAppAndGetTime("mojo:mojo_test_request_tracker_app",
    231                                   SetAndQuit<int64>(&time_message));
    232   message_loop()->Run();
    233 
    234   // Verify by hitting the TimeService in the request tracker app directly.
    235   TestTimeServicePtr time_service;
    236   ConnectToService(GURL("mojo:mojo_test_request_tracker_app"), &time_service);
    237   int64 party_time;
    238   time_service->GetPartyTime(SetAndQuit<int64>(&party_time));
    239   message_loop()->Run();
    240 
    241   EXPECT_EQ(time_message, party_time);
    242 }
    243 
    244 // Tests that service A in App 1 can be a client of service B in App 2.
    245 TEST_F(ShellTestBaseTest, ConnectServiceAsClientOfSeparateApp) {
    246   TestServicePtr service;
    247   ConnectToService(test_app_url(), &service);
    248   service->StartTrackingRequests(message_loop()->QuitWhenIdleClosure());
    249   service->Ping(mojo::Callback<void()>());
    250   message_loop()->Run();
    251 
    252   for (int i = 0; i < 8; i++)
    253     service->Ping(mojo::Callback<void()>());
    254   service->Ping(message_loop()->QuitWhenIdleClosure());
    255   message_loop()->Run();
    256 
    257   // If everything worked properly, the tracking service should report
    258   // 10 pings to TestService.
    259   std::vector<ServiceReport> reports;
    260   GetReport(&reports);
    261   ASSERT_EQ(1U, reports.size());
    262   EXPECT_EQ(TestService::Name_, reports[0].service_name);
    263   EXPECT_EQ(10U, reports[0].total_requests);
    264 }
    265 
    266 // Connect several services together and use the tracking service to verify
    267 // communication.
    268 TEST_F(ShellTestBaseTest, ConnectManyClientsAndServices) {
    269   TestServicePtr service;
    270   TestTimeServicePtr time_service;
    271 
    272   // Make a request to the TestService and have it contact TimeService in the
    273   // tracking app. Do all this with tracking enabled, meaning both services
    274   // are connected as clients of the TrackedRequestService.
    275   ConnectToService(test_app_url(), &service);
    276   service->StartTrackingRequests(message_loop()->QuitWhenIdleClosure());
    277   message_loop()->Run();
    278   for (int i = 0; i < 5; i++)
    279     service->Ping(mojo::Callback<void()>());
    280   int64 time_result;
    281   service->ConnectToAppAndGetTime("mojo:mojo_test_request_tracker_app",
    282                                   SetAndQuit<int64>(&time_result));
    283   message_loop()->Run();
    284 
    285   // Also make a few requests to the TimeService in the test_app.
    286   ConnectToService(test_app_url(), &time_service);
    287   time_service->StartTrackingRequests(message_loop()->QuitWhenIdleClosure());
    288   time_service->GetPartyTime(mojo::Callback<void(uint64_t)>());
    289   message_loop()->Run();
    290   for (int i = 0; i < 18; i++)
    291     time_service->GetPartyTime(mojo::Callback<void(uint64_t)>());
    292   // Flush the tasks with one more to quit.
    293   int64 party_time = 0;
    294   time_service->GetPartyTime(SetAndQuit<int64>(&party_time));
    295   message_loop()->Run();
    296 
    297   std::vector<ServiceReport> reports;
    298   GetReport(&reports);
    299   ASSERT_EQ(3U, reports.size());
    300   EXPECT_EQ(TestService::Name_, reports[0].service_name);
    301   EXPECT_EQ(6U, reports[0].total_requests);
    302   EXPECT_EQ(TestTimeService::Name_, reports[1].service_name);
    303   EXPECT_EQ(1U, reports[1].total_requests);
    304   EXPECT_EQ(TestTimeService::Name_, reports[2].service_name);
    305   EXPECT_EQ(20U, reports[2].total_requests);
    306 }
    307 
    308 }  // namespace
    309 }  // namespace test
    310 }  // namespace shell
    311 }  // namespace mojo
    312