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