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 "base/at_exit.h" 6 #include "base/bind.h" 7 #include "base/message_loop/message_loop.h" 8 #include "mojo/application_manager/application_loader.h" 9 #include "mojo/application_manager/application_manager.h" 10 #include "mojo/application_manager/background_shell_application_loader.h" 11 #include "mojo/application_manager/test.mojom.h" 12 #include "mojo/public/cpp/application/application_connection.h" 13 #include "mojo/public/cpp/application/application_delegate.h" 14 #include "mojo/public/cpp/application/application_impl.h" 15 #include "mojo/public/cpp/application/interface_factory.h" 16 #include "mojo/public/interfaces/application/service_provider.mojom.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 19 namespace mojo { 20 namespace { 21 22 const char kTestURLString[] = "test:testService"; 23 const char kTestAURLString[] = "test:TestA"; 24 const char kTestBURLString[] = "test:TestB"; 25 26 struct TestContext { 27 TestContext() : num_impls(0), num_loader_deletes(0) {} 28 std::string last_test_string; 29 int num_impls; 30 int num_loader_deletes; 31 }; 32 33 class QuitMessageLoopErrorHandler : public ErrorHandler { 34 public: 35 QuitMessageLoopErrorHandler() {} 36 virtual ~QuitMessageLoopErrorHandler() {} 37 38 // |ErrorHandler| implementation: 39 virtual void OnConnectionError() OVERRIDE { 40 base::MessageLoop::current()->QuitWhenIdle(); 41 } 42 43 private: 44 DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler); 45 }; 46 47 class TestServiceImpl : public InterfaceImpl<TestService> { 48 public: 49 explicit TestServiceImpl(TestContext* context) : context_(context) { 50 ++context_->num_impls; 51 } 52 53 virtual ~TestServiceImpl() { --context_->num_impls; } 54 55 virtual void OnConnectionError() OVERRIDE { 56 if (!base::MessageLoop::current()->is_running()) 57 return; 58 base::MessageLoop::current()->Quit(); 59 } 60 61 // TestService implementation: 62 virtual void Test(const String& test_string) OVERRIDE { 63 context_->last_test_string = test_string; 64 client()->AckTest(); 65 } 66 67 private: 68 TestContext* context_; 69 }; 70 71 class TestClientImpl : public TestClient { 72 public: 73 explicit TestClientImpl(TestServicePtr service) 74 : service_(service.Pass()), quit_after_ack_(false) { 75 service_.set_client(this); 76 } 77 78 virtual ~TestClientImpl() { service_.reset(); } 79 80 virtual void AckTest() OVERRIDE { 81 if (quit_after_ack_) 82 base::MessageLoop::current()->Quit(); 83 } 84 85 void Test(std::string test_string) { 86 quit_after_ack_ = true; 87 service_->Test(test_string); 88 } 89 90 private: 91 TestServicePtr service_; 92 bool quit_after_ack_; 93 DISALLOW_COPY_AND_ASSIGN(TestClientImpl); 94 }; 95 96 class TestApplicationLoader : public ApplicationLoader, 97 public ApplicationDelegate, 98 public InterfaceFactory<TestService> { 99 public: 100 TestApplicationLoader() : context_(NULL), num_loads_(0) {} 101 102 virtual ~TestApplicationLoader() { 103 if (context_) 104 ++context_->num_loader_deletes; 105 test_app_.reset(NULL); 106 } 107 108 void set_context(TestContext* context) { context_ = context; } 109 int num_loads() const { return num_loads_; } 110 std::vector<std::string> GetArgs() { 111 return test_app_->args().To<std::vector<std::string> >(); 112 } 113 114 private: 115 // ApplicationLoader implementation. 116 virtual void Load(ApplicationManager* manager, 117 const GURL& url, 118 scoped_refptr<LoadCallbacks> callbacks) OVERRIDE { 119 ++num_loads_; 120 test_app_.reset( 121 new ApplicationImpl(this, callbacks->RegisterApplication().Pass())); 122 } 123 124 virtual void OnApplicationError(ApplicationManager* manager, 125 const GURL& url) OVERRIDE {} 126 127 // ApplicationDelegate implementation. 128 virtual bool ConfigureIncomingConnection( 129 ApplicationConnection* connection) OVERRIDE { 130 connection->AddService(this); 131 return true; 132 } 133 134 // InterfaceFactory implementation. 135 virtual void Create(ApplicationConnection* connection, 136 InterfaceRequest<TestService> request) OVERRIDE { 137 BindToRequest(new TestServiceImpl(context_), &request); 138 } 139 140 scoped_ptr<ApplicationImpl> test_app_; 141 TestContext* context_; 142 int num_loads_; 143 DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader); 144 }; 145 146 class TesterContext { 147 public: 148 explicit TesterContext(base::MessageLoop* loop) 149 : num_b_calls_(0), 150 num_c_calls_(0), 151 num_a_deletes_(0), 152 num_b_deletes_(0), 153 num_c_deletes_(0), 154 tester_called_quit_(false), 155 a_called_quit_(false), 156 loop_(loop) {} 157 158 void IncrementNumBCalls() { 159 base::AutoLock lock(lock_); 160 num_b_calls_++; 161 } 162 163 void IncrementNumCCalls() { 164 base::AutoLock lock(lock_); 165 num_c_calls_++; 166 } 167 168 void IncrementNumADeletes() { 169 base::AutoLock lock(lock_); 170 num_a_deletes_++; 171 } 172 173 void IncrementNumBDeletes() { 174 base::AutoLock lock(lock_); 175 num_b_deletes_++; 176 } 177 178 void IncrementNumCDeletes() { 179 base::AutoLock lock(lock_); 180 num_c_deletes_++; 181 } 182 183 void set_tester_called_quit() { 184 base::AutoLock lock(lock_); 185 tester_called_quit_ = true; 186 } 187 188 void set_a_called_quit() { 189 base::AutoLock lock(lock_); 190 a_called_quit_ = true; 191 } 192 193 int num_b_calls() { 194 base::AutoLock lock(lock_); 195 return num_b_calls_; 196 } 197 int num_c_calls() { 198 base::AutoLock lock(lock_); 199 return num_c_calls_; 200 } 201 int num_a_deletes() { 202 base::AutoLock lock(lock_); 203 return num_a_deletes_; 204 } 205 int num_b_deletes() { 206 base::AutoLock lock(lock_); 207 return num_b_deletes_; 208 } 209 int num_c_deletes() { 210 base::AutoLock lock(lock_); 211 return num_c_deletes_; 212 } 213 bool tester_called_quit() { 214 base::AutoLock lock(lock_); 215 return tester_called_quit_; 216 } 217 bool a_called_quit() { 218 base::AutoLock lock(lock_); 219 return a_called_quit_; 220 } 221 222 void QuitSoon() { 223 loop_->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); 224 } 225 226 private: 227 // lock_ protects all members except for loop_ which must be unchanged for the 228 // lifetime of this class. 229 base::Lock lock_; 230 int num_b_calls_; 231 int num_c_calls_; 232 int num_a_deletes_; 233 int num_b_deletes_; 234 int num_c_deletes_; 235 bool tester_called_quit_; 236 bool a_called_quit_; 237 238 base::MessageLoop* loop_; 239 }; 240 241 // Used to test that the requestor url will be correctly passed. 242 class TestAImpl : public InterfaceImpl<TestA> { 243 public: 244 TestAImpl(ApplicationConnection* connection, TesterContext* test_context) 245 : test_context_(test_context) { 246 connection->ConnectToApplication(kTestBURLString)->ConnectToService(&b_); 247 } 248 virtual ~TestAImpl() { 249 test_context_->IncrementNumADeletes(); 250 if (base::MessageLoop::current()->is_running()) 251 Quit(); 252 } 253 254 private: 255 virtual void CallB() OVERRIDE { 256 b_->B(base::Bind(&TestAImpl::Quit, base::Unretained(this))); 257 } 258 259 virtual void CallCFromB() OVERRIDE { 260 b_->CallC(base::Bind(&TestAImpl::Quit, base::Unretained(this))); 261 } 262 263 void Quit() { 264 base::MessageLoop::current()->Quit(); 265 test_context_->set_a_called_quit(); 266 test_context_->QuitSoon(); 267 } 268 269 TesterContext* test_context_; 270 TestBPtr b_; 271 }; 272 273 class TestBImpl : public InterfaceImpl<TestB> { 274 public: 275 TestBImpl(ApplicationConnection* connection, TesterContext* test_context) 276 : test_context_(test_context) { 277 connection->ConnectToService(&c_); 278 } 279 280 virtual ~TestBImpl() { 281 test_context_->IncrementNumBDeletes(); 282 if (base::MessageLoop::current()->is_running()) 283 base::MessageLoop::current()->Quit(); 284 test_context_->QuitSoon(); 285 } 286 287 private: 288 virtual void B(const mojo::Callback<void()>& callback) OVERRIDE { 289 test_context_->IncrementNumBCalls(); 290 callback.Run(); 291 } 292 293 virtual void CallC(const mojo::Callback<void()>& callback) OVERRIDE { 294 test_context_->IncrementNumBCalls(); 295 c_->C(callback); 296 } 297 298 TesterContext* test_context_; 299 TestCPtr c_; 300 }; 301 302 class TestCImpl : public InterfaceImpl<TestC> { 303 public: 304 TestCImpl(ApplicationConnection* connection, TesterContext* test_context) 305 : test_context_(test_context) {} 306 307 virtual ~TestCImpl() { test_context_->IncrementNumCDeletes(); } 308 309 private: 310 virtual void C(const mojo::Callback<void()>& callback) OVERRIDE { 311 test_context_->IncrementNumCCalls(); 312 callback.Run(); 313 } 314 TesterContext* test_context_; 315 }; 316 317 class Tester : public ApplicationDelegate, 318 public ApplicationLoader, 319 public InterfaceFactory<TestA>, 320 public InterfaceFactory<TestB>, 321 public InterfaceFactory<TestC> { 322 public: 323 Tester(TesterContext* context, const std::string& requestor_url) 324 : context_(context), requestor_url_(requestor_url) {} 325 virtual ~Tester() {} 326 327 private: 328 virtual void Load(ApplicationManager* manager, 329 const GURL& url, 330 scoped_refptr<LoadCallbacks> callbacks) OVERRIDE { 331 app_.reset( 332 new ApplicationImpl(this, callbacks->RegisterApplication().Pass())); 333 } 334 335 virtual void OnApplicationError(ApplicationManager* manager, 336 const GURL& url) OVERRIDE {} 337 338 virtual bool ConfigureIncomingConnection( 339 ApplicationConnection* connection) OVERRIDE { 340 if (!requestor_url_.empty() && 341 requestor_url_ != connection->GetRemoteApplicationURL()) { 342 context_->set_tester_called_quit(); 343 context_->QuitSoon(); 344 base::MessageLoop::current()->Quit(); 345 return false; 346 } 347 // If we're coming from A, then add B, otherwise A. 348 if (connection->GetRemoteApplicationURL() == kTestAURLString) 349 connection->AddService<TestB>(this); 350 else 351 connection->AddService<TestA>(this); 352 return true; 353 } 354 355 virtual bool ConfigureOutgoingConnection( 356 ApplicationConnection* connection) OVERRIDE { 357 // If we're connecting to B, then add C. 358 if (connection->GetRemoteApplicationURL() == kTestBURLString) 359 connection->AddService<TestC>(this); 360 return true; 361 } 362 363 virtual void Create(ApplicationConnection* connection, 364 InterfaceRequest<TestA> request) OVERRIDE { 365 BindToRequest(new TestAImpl(connection, context_), &request); 366 } 367 368 virtual void Create(ApplicationConnection* connection, 369 InterfaceRequest<TestB> request) OVERRIDE { 370 BindToRequest(new TestBImpl(connection, context_), &request); 371 } 372 373 virtual void Create(ApplicationConnection* connection, 374 InterfaceRequest<TestC> request) OVERRIDE { 375 BindToRequest(new TestCImpl(connection, context_), &request); 376 } 377 378 TesterContext* context_; 379 scoped_ptr<ApplicationImpl> app_; 380 std::string requestor_url_; 381 }; 382 383 class TestServiceInterceptor : public ApplicationManager::Interceptor { 384 public: 385 TestServiceInterceptor() : call_count_(0) {} 386 387 virtual ServiceProviderPtr OnConnectToClient( 388 const GURL& url, 389 ServiceProviderPtr service_provider) OVERRIDE { 390 ++call_count_; 391 url_ = url; 392 return service_provider.Pass(); 393 } 394 395 std::string url_spec() const { 396 if (!url_.is_valid()) 397 return "invalid url"; 398 return url_.spec(); 399 } 400 401 int call_count() const { return call_count_; } 402 403 private: 404 int call_count_; 405 GURL url_; 406 DISALLOW_COPY_AND_ASSIGN(TestServiceInterceptor); 407 }; 408 409 } // namespace 410 411 class ApplicationManagerTest : public testing::Test { 412 public: 413 ApplicationManagerTest() : tester_context_(&loop_) {} 414 415 virtual ~ApplicationManagerTest() {} 416 417 virtual void SetUp() OVERRIDE { 418 application_manager_.reset(new ApplicationManager); 419 TestApplicationLoader* default_loader = new TestApplicationLoader; 420 default_loader->set_context(&context_); 421 application_manager_->set_default_loader( 422 scoped_ptr<ApplicationLoader>(default_loader)); 423 424 TestServicePtr service_proxy; 425 application_manager_->ConnectToService(GURL(kTestURLString), 426 &service_proxy); 427 test_client_.reset(new TestClientImpl(service_proxy.Pass())); 428 } 429 430 virtual void TearDown() OVERRIDE { 431 test_client_.reset(NULL); 432 application_manager_.reset(NULL); 433 } 434 435 scoped_ptr<BackgroundShellApplicationLoader> MakeLoader( 436 const std::string& requestor_url) { 437 scoped_ptr<ApplicationLoader> real_loader( 438 new Tester(&tester_context_, requestor_url)); 439 scoped_ptr<BackgroundShellApplicationLoader> loader( 440 new BackgroundShellApplicationLoader(real_loader.Pass(), 441 std::string(), 442 base::MessageLoop::TYPE_DEFAULT)); 443 return loader.Pass(); 444 } 445 446 void AddLoaderForURL(const GURL& url, const std::string& requestor_url) { 447 application_manager_->SetLoaderForURL( 448 MakeLoader(requestor_url).PassAs<ApplicationLoader>(), url); 449 } 450 451 bool HasFactoryForTestURL() { 452 ApplicationManager::TestAPI manager_test_api(application_manager_.get()); 453 return manager_test_api.HasFactoryForURL(GURL(kTestURLString)); 454 } 455 456 protected: 457 base::ShadowingAtExitManager at_exit_; 458 TesterContext tester_context_; 459 TestContext context_; 460 base::MessageLoop loop_; 461 scoped_ptr<TestClientImpl> test_client_; 462 scoped_ptr<ApplicationManager> application_manager_; 463 DISALLOW_COPY_AND_ASSIGN(ApplicationManagerTest); 464 }; 465 466 TEST_F(ApplicationManagerTest, Basic) { 467 test_client_->Test("test"); 468 loop_.Run(); 469 EXPECT_EQ(std::string("test"), context_.last_test_string); 470 } 471 472 // Confirm that no arguments are sent to an application by default. 473 TEST_F(ApplicationManagerTest, NoArgs) { 474 ApplicationManager am; 475 GURL test_url("test:test"); 476 TestContext context; 477 TestApplicationLoader* loader = new TestApplicationLoader; 478 loader->set_context(&context); 479 am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url); 480 TestServicePtr test_service; 481 am.ConnectToService(test_url, &test_service); 482 TestClientImpl test_client(test_service.Pass()); 483 test_client.Test("test"); 484 loop_.Run(); 485 std::vector<std::string> app_args = loader->GetArgs(); 486 EXPECT_EQ(0U, app_args.size()); 487 } 488 489 // Confirm that arguments are sent to an application. 490 TEST_F(ApplicationManagerTest, Args) { 491 ApplicationManager am; 492 GURL test_url("test:test"); 493 std::vector<std::string> args; 494 args.push_back("test_arg1"); 495 args.push_back("test_arg2"); 496 am.SetArgsForURL(args, test_url); 497 TestContext context; 498 TestApplicationLoader* loader = new TestApplicationLoader; 499 loader->set_context(&context); 500 am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(loader), test_url); 501 TestServicePtr test_service; 502 am.ConnectToService(test_url, &test_service); 503 TestClientImpl test_client(test_service.Pass()); 504 test_client.Test("test"); 505 loop_.Run(); 506 std::vector<std::string> app_args = loader->GetArgs(); 507 ASSERT_EQ(args.size(), app_args.size()); 508 EXPECT_EQ(args[0], app_args[0]); 509 EXPECT_EQ(args[1], app_args[1]); 510 } 511 512 TEST_F(ApplicationManagerTest, ClientError) { 513 test_client_->Test("test"); 514 EXPECT_TRUE(HasFactoryForTestURL()); 515 loop_.Run(); 516 EXPECT_EQ(1, context_.num_impls); 517 test_client_.reset(NULL); 518 loop_.Run(); 519 EXPECT_EQ(0, context_.num_impls); 520 EXPECT_TRUE(HasFactoryForTestURL()); 521 } 522 523 TEST_F(ApplicationManagerTest, Deletes) { 524 { 525 ApplicationManager am; 526 TestApplicationLoader* default_loader = new TestApplicationLoader; 527 default_loader->set_context(&context_); 528 TestApplicationLoader* url_loader1 = new TestApplicationLoader; 529 TestApplicationLoader* url_loader2 = new TestApplicationLoader; 530 url_loader1->set_context(&context_); 531 url_loader2->set_context(&context_); 532 TestApplicationLoader* scheme_loader1 = new TestApplicationLoader; 533 TestApplicationLoader* scheme_loader2 = new TestApplicationLoader; 534 scheme_loader1->set_context(&context_); 535 scheme_loader2->set_context(&context_); 536 am.set_default_loader(scoped_ptr<ApplicationLoader>(default_loader)); 537 am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader1), 538 GURL("test:test1")); 539 am.SetLoaderForURL(scoped_ptr<ApplicationLoader>(url_loader2), 540 GURL("test:test1")); 541 am.SetLoaderForScheme(scoped_ptr<ApplicationLoader>(scheme_loader1), 542 "test"); 543 am.SetLoaderForScheme(scoped_ptr<ApplicationLoader>(scheme_loader2), 544 "test"); 545 } 546 EXPECT_EQ(5, context_.num_loader_deletes); 547 } 548 549 // Confirm that both urls and schemes can have their loaders explicitly set. 550 TEST_F(ApplicationManagerTest, SetLoaders) { 551 TestApplicationLoader* default_loader = new TestApplicationLoader; 552 TestApplicationLoader* url_loader = new TestApplicationLoader; 553 TestApplicationLoader* scheme_loader = new TestApplicationLoader; 554 application_manager_->set_default_loader( 555 scoped_ptr<ApplicationLoader>(default_loader)); 556 application_manager_->SetLoaderForURL( 557 scoped_ptr<ApplicationLoader>(url_loader), GURL("test:test1")); 558 application_manager_->SetLoaderForScheme( 559 scoped_ptr<ApplicationLoader>(scheme_loader), "test"); 560 561 // test::test1 should go to url_loader. 562 TestServicePtr test_service; 563 application_manager_->ConnectToService(GURL("test:test1"), &test_service); 564 EXPECT_EQ(1, url_loader->num_loads()); 565 EXPECT_EQ(0, scheme_loader->num_loads()); 566 EXPECT_EQ(0, default_loader->num_loads()); 567 568 // test::test2 should go to scheme loader. 569 application_manager_->ConnectToService(GURL("test:test2"), &test_service); 570 EXPECT_EQ(1, url_loader->num_loads()); 571 EXPECT_EQ(1, scheme_loader->num_loads()); 572 EXPECT_EQ(0, default_loader->num_loads()); 573 574 // http::test1 should go to default loader. 575 application_manager_->ConnectToService(GURL("http:test1"), &test_service); 576 EXPECT_EQ(1, url_loader->num_loads()); 577 EXPECT_EQ(1, scheme_loader->num_loads()); 578 EXPECT_EQ(1, default_loader->num_loads()); 579 } 580 581 // Confirm that the url of a service is correctly passed to another service that 582 // it loads. 583 TEST_F(ApplicationManagerTest, ACallB) { 584 // Any url can load a. 585 AddLoaderForURL(GURL(kTestAURLString), std::string()); 586 587 // Only a can load b. 588 AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); 589 590 TestAPtr a; 591 application_manager_->ConnectToService(GURL(kTestAURLString), &a); 592 a->CallB(); 593 loop_.Run(); 594 EXPECT_EQ(1, tester_context_.num_b_calls()); 595 EXPECT_TRUE(tester_context_.a_called_quit()); 596 } 597 598 // A calls B which calls C. 599 TEST_F(ApplicationManagerTest, BCallC) { 600 // Any url can load a. 601 AddLoaderForURL(GURL(kTestAURLString), std::string()); 602 603 // Only a can load b. 604 AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); 605 606 TestAPtr a; 607 application_manager_->ConnectToService(GURL(kTestAURLString), &a); 608 a->CallCFromB(); 609 loop_.Run(); 610 611 EXPECT_EQ(1, tester_context_.num_b_calls()); 612 EXPECT_EQ(1, tester_context_.num_c_calls()); 613 EXPECT_TRUE(tester_context_.a_called_quit()); 614 } 615 616 // Confirm that a service impl will be deleted if the app that connected to 617 // it goes away. 618 TEST_F(ApplicationManagerTest, BDeleted) { 619 AddLoaderForURL(GURL(kTestAURLString), std::string()); 620 AddLoaderForURL(GURL(kTestBURLString), std::string()); 621 622 TestAPtr a; 623 application_manager_->ConnectToService(GURL(kTestAURLString), &a); 624 625 a->CallB(); 626 loop_.Run(); 627 628 // Kills the a app. 629 application_manager_->SetLoaderForURL(scoped_ptr<ApplicationLoader>(), 630 GURL(kTestAURLString)); 631 loop_.Run(); 632 633 EXPECT_EQ(1, tester_context_.num_b_deletes()); 634 } 635 636 // Confirm that the url of a service is correctly passed to another service that 637 // it loads, and that it can be rejected. 638 TEST_F(ApplicationManagerTest, ANoLoadB) { 639 // Any url can load a. 640 AddLoaderForURL(GURL(kTestAURLString), std::string()); 641 642 // Only c can load b, so this will fail. 643 AddLoaderForURL(GURL(kTestBURLString), "test:TestC"); 644 645 TestAPtr a; 646 application_manager_->ConnectToService(GURL(kTestAURLString), &a); 647 a->CallB(); 648 loop_.Run(); 649 EXPECT_EQ(0, tester_context_.num_b_calls()); 650 651 EXPECT_FALSE(tester_context_.a_called_quit()); 652 EXPECT_TRUE(tester_context_.tester_called_quit()); 653 } 654 655 TEST_F(ApplicationManagerTest, NoServiceNoLoad) { 656 AddLoaderForURL(GURL(kTestAURLString), std::string()); 657 658 // There is no TestC service implementation registered with 659 // ApplicationManager, so this cannot succeed (but also shouldn't crash). 660 TestCPtr c; 661 application_manager_->ConnectToService(GURL(kTestAURLString), &c); 662 QuitMessageLoopErrorHandler quitter; 663 c.set_error_handler(&quitter); 664 665 loop_.Run(); 666 EXPECT_TRUE(c.encountered_error()); 667 } 668 669 TEST_F(ApplicationManagerTest, Interceptor) { 670 TestServiceInterceptor interceptor; 671 TestApplicationLoader* default_loader = new TestApplicationLoader; 672 application_manager_->set_default_loader( 673 scoped_ptr<ApplicationLoader>(default_loader)); 674 application_manager_->SetInterceptor(&interceptor); 675 676 std::string url("test:test3"); 677 TestServicePtr test_service; 678 application_manager_->ConnectToService(GURL(url), &test_service); 679 680 EXPECT_EQ(1, interceptor.call_count()); 681 EXPECT_EQ(url, interceptor.url_spec()); 682 EXPECT_EQ(1, default_loader->num_loads()); 683 } 684 685 } // namespace mojo 686