1 // Copyright 2013 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 "components/dom_distiller/core/dom_distiller_service.h" 6 7 #include "base/bind.h" 8 #include "base/callback.h" 9 #include "base/containers/hash_tables.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/run_loop.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "components/dom_distiller/core/article_entry.h" 14 #include "components/dom_distiller/core/distilled_page_prefs.h" 15 #include "components/dom_distiller/core/dom_distiller_model.h" 16 #include "components/dom_distiller/core/dom_distiller_store.h" 17 #include "components/dom_distiller/core/dom_distiller_test_util.h" 18 #include "components/dom_distiller/core/fake_distiller.h" 19 #include "components/dom_distiller/core/fake_distiller_page.h" 20 #include "components/dom_distiller/core/task_tracker.h" 21 #include "components/leveldb_proto/testing/fake_db.h" 22 #include "testing/gmock/include/gmock/gmock.h" 23 #include "testing/gtest/include/gtest/gtest.h" 24 25 using leveldb_proto::test::FakeDB; 26 using testing::Invoke; 27 using testing::Return; 28 using testing::_; 29 30 namespace dom_distiller { 31 namespace test { 32 33 namespace { 34 35 class FakeViewRequestDelegate : public ViewRequestDelegate { 36 public: 37 virtual ~FakeViewRequestDelegate() {} 38 MOCK_METHOD1(OnArticleReady, void(const DistilledArticleProto* proto)); 39 MOCK_METHOD1(OnArticleUpdated, 40 void(ArticleDistillationUpdate article_update)); 41 }; 42 43 class MockDistillerObserver : public DomDistillerObserver { 44 public: 45 MOCK_METHOD1(ArticleEntriesUpdated, void(const std::vector<ArticleUpdate>&)); 46 virtual ~MockDistillerObserver() {} 47 }; 48 49 class MockArticleAvailableCallback { 50 public: 51 MOCK_METHOD1(DistillationCompleted, void(bool)); 52 }; 53 54 DomDistillerService::ArticleAvailableCallback ArticleCallback( 55 MockArticleAvailableCallback* callback) { 56 return base::Bind(&MockArticleAvailableCallback::DistillationCompleted, 57 base::Unretained(callback)); 58 } 59 60 void RunDistillerCallback(FakeDistiller* distiller, 61 scoped_ptr<DistilledArticleProto> proto) { 62 distiller->RunDistillerCallback(proto.Pass()); 63 base::RunLoop().RunUntilIdle(); 64 } 65 66 scoped_ptr<DistilledArticleProto> CreateArticleWithURL(const std::string& url) { 67 scoped_ptr<DistilledArticleProto> proto(new DistilledArticleProto); 68 DistilledPageProto* page = proto->add_pages(); 69 page->set_url(url); 70 return proto.Pass(); 71 } 72 73 scoped_ptr<DistilledArticleProto> CreateDefaultArticle() { 74 return CreateArticleWithURL("http://www.example.com/default_article_page1") 75 .Pass(); 76 } 77 78 } // namespace 79 80 class DomDistillerServiceTest : public testing::Test { 81 public: 82 virtual void SetUp() { 83 main_loop_.reset(new base::MessageLoop()); 84 FakeDB<ArticleEntry>* fake_db = new FakeDB<ArticleEntry>(&db_model_); 85 FakeDB<ArticleEntry>::EntryMap store_model; 86 store_ = 87 test::util::CreateStoreWithFakeDB(fake_db, store_model); 88 distiller_factory_ = new MockDistillerFactory(); 89 distiller_page_factory_ = new MockDistillerPageFactory(); 90 service_.reset(new DomDistillerService( 91 scoped_ptr<DomDistillerStoreInterface>(store_), 92 scoped_ptr<DistillerFactory>(distiller_factory_), 93 scoped_ptr<DistillerPageFactory>(distiller_page_factory_), 94 scoped_ptr<DistilledPagePrefs>())); 95 fake_db->InitCallback(true); 96 fake_db->LoadCallback(true); 97 } 98 99 virtual void TearDown() { 100 base::RunLoop().RunUntilIdle(); 101 store_ = NULL; 102 distiller_factory_ = NULL; 103 service_.reset(); 104 } 105 106 protected: 107 // store is owned by service_. 108 DomDistillerStoreInterface* store_; 109 MockDistillerFactory* distiller_factory_; 110 MockDistillerPageFactory* distiller_page_factory_; 111 scoped_ptr<DomDistillerService> service_; 112 scoped_ptr<base::MessageLoop> main_loop_; 113 FakeDB<ArticleEntry>::EntryMap db_model_; 114 }; 115 116 TEST_F(DomDistillerServiceTest, TestViewEntry) { 117 FakeDistiller* distiller = new FakeDistiller(false); 118 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 119 .WillOnce(Return(distiller)); 120 121 GURL url("http://www.example.com/p1"); 122 std::string entry_id("id0"); 123 ArticleEntry entry; 124 entry.set_entry_id(entry_id); 125 entry.add_pages()->set_url(url.spec()); 126 127 store_->AddEntry(entry); 128 129 FakeViewRequestDelegate viewer_delegate; 130 scoped_ptr<ViewerHandle> handle = service_->ViewEntry( 131 &viewer_delegate, service_->CreateDefaultDistillerPage(gfx::Size()), 132 entry_id); 133 134 ASSERT_FALSE(distiller->GetArticleCallback().is_null()); 135 136 scoped_ptr<DistilledArticleProto> proto = CreateDefaultArticle(); 137 EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get())); 138 139 RunDistillerCallback(distiller, proto.Pass()); 140 } 141 142 TEST_F(DomDistillerServiceTest, TestViewUrl) { 143 FakeDistiller* distiller = new FakeDistiller(false); 144 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 145 .WillOnce(Return(distiller)); 146 147 FakeViewRequestDelegate viewer_delegate; 148 GURL url("http://www.example.com/p1"); 149 scoped_ptr<ViewerHandle> handle = service_->ViewUrl( 150 &viewer_delegate, service_->CreateDefaultDistillerPage(gfx::Size()), url); 151 152 ASSERT_FALSE(distiller->GetArticleCallback().is_null()); 153 EXPECT_EQ(url, distiller->GetUrl()); 154 155 scoped_ptr<DistilledArticleProto> proto = CreateDefaultArticle(); 156 EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get())); 157 158 RunDistillerCallback(distiller, proto.Pass()); 159 } 160 161 TEST_F(DomDistillerServiceTest, TestMultipleViewUrl) { 162 FakeDistiller* distiller = new FakeDistiller(false); 163 FakeDistiller* distiller2 = new FakeDistiller(false); 164 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 165 .WillOnce(Return(distiller)) 166 .WillOnce(Return(distiller2)); 167 168 FakeViewRequestDelegate viewer_delegate; 169 FakeViewRequestDelegate viewer_delegate2; 170 171 GURL url("http://www.example.com/p1"); 172 GURL url2("http://www.example.com/a/p1"); 173 174 scoped_ptr<ViewerHandle> handle = service_->ViewUrl( 175 &viewer_delegate, service_->CreateDefaultDistillerPage(gfx::Size()), url); 176 scoped_ptr<ViewerHandle> handle2 = service_->ViewUrl( 177 &viewer_delegate2, service_->CreateDefaultDistillerPage(gfx::Size()), 178 url2); 179 180 ASSERT_FALSE(distiller->GetArticleCallback().is_null()); 181 EXPECT_EQ(url, distiller->GetUrl()); 182 183 scoped_ptr<DistilledArticleProto> proto = CreateDefaultArticle(); 184 EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get())); 185 186 RunDistillerCallback(distiller, proto.Pass()); 187 188 ASSERT_FALSE(distiller2->GetArticleCallback().is_null()); 189 EXPECT_EQ(url2, distiller2->GetUrl()); 190 191 scoped_ptr<DistilledArticleProto> proto2 = CreateDefaultArticle(); 192 EXPECT_CALL(viewer_delegate2, OnArticleReady(proto2.get())); 193 194 RunDistillerCallback(distiller2, proto2.Pass()); 195 } 196 197 TEST_F(DomDistillerServiceTest, TestViewUrlCancelled) { 198 FakeDistiller* distiller = new FakeDistiller(false); 199 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 200 .WillOnce(Return(distiller)); 201 202 bool distiller_destroyed = false; 203 EXPECT_CALL(*distiller, Die()) 204 .WillOnce(testing::Assign(&distiller_destroyed, true)); 205 206 FakeViewRequestDelegate viewer_delegate; 207 GURL url("http://www.example.com/p1"); 208 scoped_ptr<ViewerHandle> handle = service_->ViewUrl( 209 &viewer_delegate, service_->CreateDefaultDistillerPage(gfx::Size()), url); 210 211 ASSERT_FALSE(distiller->GetArticleCallback().is_null()); 212 EXPECT_EQ(url, distiller->GetUrl()); 213 214 EXPECT_CALL(viewer_delegate, OnArticleReady(_)).Times(0); 215 216 EXPECT_FALSE(distiller_destroyed); 217 218 handle.reset(); 219 base::RunLoop().RunUntilIdle(); 220 EXPECT_TRUE(distiller_destroyed); 221 } 222 223 TEST_F(DomDistillerServiceTest, TestViewUrlDoesNotAddEntry) { 224 FakeDistiller* distiller = new FakeDistiller(false); 225 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 226 .WillOnce(Return(distiller)); 227 228 FakeViewRequestDelegate viewer_delegate; 229 GURL url("http://www.example.com/p1"); 230 scoped_ptr<ViewerHandle> handle = service_->ViewUrl( 231 &viewer_delegate, service_->CreateDefaultDistillerPage(gfx::Size()), url); 232 233 scoped_ptr<DistilledArticleProto> proto = CreateArticleWithURL(url.spec()); 234 EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get())); 235 236 RunDistillerCallback(distiller, proto.Pass()); 237 base::RunLoop().RunUntilIdle(); 238 // The entry should not be added to the store. 239 EXPECT_EQ(0u, store_->GetEntries().size()); 240 } 241 242 TEST_F(DomDistillerServiceTest, TestAddAndRemoveEntry) { 243 FakeDistiller* distiller = new FakeDistiller(false); 244 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 245 .WillOnce(Return(distiller)); 246 247 GURL url("http://www.example.com/p1"); 248 249 MockArticleAvailableCallback article_cb; 250 EXPECT_CALL(article_cb, DistillationCompleted(true)); 251 252 std::string entry_id = 253 service_->AddToList(url, 254 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 255 ArticleCallback(&article_cb)); 256 257 ASSERT_FALSE(distiller->GetArticleCallback().is_null()); 258 EXPECT_EQ(url, distiller->GetUrl()); 259 260 scoped_ptr<DistilledArticleProto> proto = CreateArticleWithURL(url.spec()); 261 RunDistillerCallback(distiller, proto.Pass()); 262 263 ArticleEntry entry; 264 EXPECT_TRUE(store_->GetEntryByUrl(url, &entry)); 265 EXPECT_EQ(entry.entry_id(), entry_id); 266 EXPECT_EQ(1u, store_->GetEntries().size()); 267 service_->RemoveEntry(entry_id); 268 base::RunLoop().RunUntilIdle(); 269 EXPECT_EQ(0u, store_->GetEntries().size()); 270 } 271 272 TEST_F(DomDistillerServiceTest, TestCancellation) { 273 FakeDistiller* distiller = new FakeDistiller(false); 274 MockDistillerObserver observer; 275 service_->AddObserver(&observer); 276 277 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 278 .WillOnce(Return(distiller)); 279 280 MockArticleAvailableCallback article_cb; 281 EXPECT_CALL(article_cb, DistillationCompleted(false)); 282 283 GURL url("http://www.example.com/p1"); 284 std::string entry_id = 285 service_->AddToList(url, 286 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 287 ArticleCallback(&article_cb)); 288 289 // Remove entry will cause the |article_cb| to be called with false value. 290 service_->RemoveEntry(entry_id); 291 base::RunLoop().RunUntilIdle(); 292 } 293 294 TEST_F(DomDistillerServiceTest, TestMultipleObservers) { 295 FakeDistiller* distiller = new FakeDistiller(false); 296 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 297 .WillOnce(Return(distiller)); 298 299 const int kObserverCount = 5; 300 MockDistillerObserver observers[kObserverCount]; 301 for (int i = 0; i < kObserverCount; ++i) { 302 service_->AddObserver(&observers[i]); 303 } 304 305 DomDistillerService::ArticleAvailableCallback article_cb; 306 GURL url("http://www.example.com/p1"); 307 std::string entry_id = service_->AddToList( 308 url, service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 309 article_cb); 310 311 // Distillation should notify all observers that article is added. 312 std::vector<DomDistillerObserver::ArticleUpdate> expected_updates; 313 DomDistillerObserver::ArticleUpdate update; 314 update.entry_id = entry_id; 315 update.update_type = DomDistillerObserver::ArticleUpdate::ADD; 316 expected_updates.push_back(update); 317 318 for (int i = 0; i < kObserverCount; ++i) { 319 EXPECT_CALL(observers[i], ArticleEntriesUpdated( 320 util::HasExpectedUpdates(expected_updates))); 321 } 322 323 scoped_ptr<DistilledArticleProto> proto = CreateDefaultArticle(); 324 RunDistillerCallback(distiller, proto.Pass()); 325 326 // Remove should notify all observers that article is removed. 327 update.update_type = DomDistillerObserver::ArticleUpdate::REMOVE; 328 expected_updates.clear(); 329 expected_updates.push_back(update); 330 for (int i = 0; i < kObserverCount; ++i) { 331 EXPECT_CALL(observers[i], ArticleEntriesUpdated( 332 util::HasExpectedUpdates(expected_updates))); 333 } 334 335 service_->RemoveEntry(entry_id); 336 base::RunLoop().RunUntilIdle(); 337 } 338 339 TEST_F(DomDistillerServiceTest, TestMultipleCallbacks) { 340 FakeDistiller* distiller = new FakeDistiller(false); 341 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 342 .WillOnce(Return(distiller)); 343 344 const int kClientsCount = 5; 345 MockArticleAvailableCallback article_cb[kClientsCount]; 346 // Adding a URL and then distilling calls all clients. 347 GURL url("http://www.example.com/p1"); 348 const std::string entry_id = 349 service_->AddToList(url, 350 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 351 ArticleCallback(&article_cb[0])); 352 EXPECT_CALL(article_cb[0], DistillationCompleted(true)); 353 354 for (int i = 1; i < kClientsCount; ++i) { 355 EXPECT_EQ(entry_id, 356 service_->AddToList( 357 url, 358 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 359 ArticleCallback(&article_cb[i]))); 360 EXPECT_CALL(article_cb[i], DistillationCompleted(true)); 361 } 362 363 scoped_ptr<DistilledArticleProto> proto = CreateArticleWithURL(url.spec()); 364 RunDistillerCallback(distiller, proto.Pass()); 365 366 // Add the same url again, all callbacks should be called with true. 367 for (int i = 0; i < kClientsCount; ++i) { 368 EXPECT_CALL(article_cb[i], DistillationCompleted(true)); 369 EXPECT_EQ(entry_id, 370 service_->AddToList( 371 url, 372 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 373 ArticleCallback(&article_cb[i]))); 374 } 375 376 base::RunLoop().RunUntilIdle(); 377 } 378 379 TEST_F(DomDistillerServiceTest, TestMultipleCallbacksOnRemove) { 380 FakeDistiller* distiller = new FakeDistiller(false); 381 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 382 .WillOnce(Return(distiller)); 383 384 const int kClientsCount = 5; 385 MockArticleAvailableCallback article_cb[kClientsCount]; 386 // Adding a URL and remove the entry before distillation. Callback should be 387 // called with false. 388 GURL url("http://www.example.com/p1"); 389 const std::string entry_id = 390 service_->AddToList(url, 391 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 392 ArticleCallback(&article_cb[0])); 393 394 EXPECT_CALL(article_cb[0], DistillationCompleted(false)); 395 for (int i = 1; i < kClientsCount; ++i) { 396 EXPECT_EQ(entry_id, 397 service_->AddToList( 398 url, service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 399 ArticleCallback(&article_cb[i]))); 400 EXPECT_CALL(article_cb[i], DistillationCompleted(false)); 401 } 402 403 service_->RemoveEntry(entry_id); 404 base::RunLoop().RunUntilIdle(); 405 } 406 407 TEST_F(DomDistillerServiceTest, TestMultiplePageArticle) { 408 FakeDistiller* distiller = new FakeDistiller(false); 409 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 410 .WillOnce(Return(distiller)); 411 412 const int kPageCount = 8; 413 414 std::string base_url("http://www.example.com/p"); 415 GURL pages_url[kPageCount]; 416 for (int page_num = 0; page_num < kPageCount; ++page_num) { 417 pages_url[page_num] = GURL(base_url + base::IntToString(page_num)); 418 } 419 420 MockArticleAvailableCallback article_cb; 421 EXPECT_CALL(article_cb, DistillationCompleted(true)); 422 423 std::string entry_id = service_->AddToList( 424 pages_url[0], service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 425 ArticleCallback(&article_cb)); 426 427 ArticleEntry entry; 428 ASSERT_FALSE(distiller->GetArticleCallback().is_null()); 429 EXPECT_EQ(pages_url[0], distiller->GetUrl()); 430 431 // Create the article with pages to pass to the distiller. 432 scoped_ptr<DistilledArticleProto> proto = 433 CreateArticleWithURL(pages_url[0].spec()); 434 for (int page_num = 1; page_num < kPageCount; ++page_num) { 435 DistilledPageProto* distilled_page = proto->add_pages(); 436 distilled_page->set_url(pages_url[page_num].spec()); 437 } 438 439 RunDistillerCallback(distiller, proto.Pass()); 440 EXPECT_TRUE(store_->GetEntryByUrl(pages_url[0], &entry)); 441 442 EXPECT_EQ(kPageCount, entry.pages_size()); 443 // An article should have just one entry. 444 EXPECT_EQ(1u, store_->GetEntries().size()); 445 446 // All pages should have correct urls. 447 for (int page_num = 0; page_num < kPageCount; ++page_num) { 448 EXPECT_EQ(pages_url[page_num].spec(), entry.pages(page_num).url()); 449 } 450 451 // Should be able to query article using any of the pages url. 452 for (int page_num = 0; page_num < kPageCount; ++page_num) { 453 EXPECT_TRUE(store_->GetEntryByUrl(pages_url[page_num], &entry)); 454 } 455 456 service_->RemoveEntry(entry_id); 457 base::RunLoop().RunUntilIdle(); 458 EXPECT_EQ(0u, store_->GetEntries().size()); 459 } 460 461 TEST_F(DomDistillerServiceTest, TestHasEntry) { 462 FakeDistiller* distiller = new FakeDistiller(false); 463 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 464 .WillOnce(Return(distiller)); 465 466 GURL url("http://www.example.com/p1"); 467 468 MockArticleAvailableCallback article_cb; 469 EXPECT_CALL(article_cb, DistillationCompleted(true)); 470 471 std::string entry_id = service_->AddToList( 472 url, 473 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 474 ArticleCallback(&article_cb)); 475 476 ASSERT_FALSE(distiller->GetArticleCallback().is_null()); 477 EXPECT_EQ(url, distiller->GetUrl()); 478 479 scoped_ptr<DistilledArticleProto> proto = CreateArticleWithURL(url.spec()); 480 RunDistillerCallback(distiller, proto.Pass()); 481 482 // Check that HasEntry returns true for the article just added. 483 EXPECT_TRUE(service_->HasEntry(entry_id)); 484 485 // Remove article and check that there is no longer an entry for the given 486 // entry id. 487 service_->RemoveEntry(entry_id); 488 base::RunLoop().RunUntilIdle(); 489 EXPECT_EQ(0u, store_->GetEntries().size()); 490 EXPECT_FALSE(service_->HasEntry(entry_id)); 491 } 492 493 TEST_F(DomDistillerServiceTest, TestGetUrlForOnePageEntry) { 494 FakeDistiller* distiller = new FakeDistiller(false); 495 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 496 .WillOnce(Return(distiller)); 497 498 GURL url("http://www.example.com/p1"); 499 500 MockArticleAvailableCallback article_cb; 501 EXPECT_CALL(article_cb, DistillationCompleted(true)); 502 503 std::string entry_id = service_->AddToList( 504 url, 505 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 506 ArticleCallback(&article_cb)); 507 508 ASSERT_FALSE(distiller->GetArticleCallback().is_null()); 509 EXPECT_EQ(url, distiller->GetUrl()); 510 511 scoped_ptr<DistilledArticleProto> proto = CreateArticleWithURL(url.spec()); 512 RunDistillerCallback(distiller, proto.Pass()); 513 514 // Check if retrieved URL is same as given URL. 515 GURL retrieved_url(service_->GetUrlForEntry(entry_id)); 516 EXPECT_EQ(url, retrieved_url); 517 518 // Remove article and check that there is no longer an entry for the given 519 // entry id. 520 service_->RemoveEntry(entry_id); 521 base::RunLoop().RunUntilIdle(); 522 EXPECT_EQ(0u, store_->GetEntries().size()); 523 EXPECT_EQ("", service_->GetUrlForEntry(entry_id)); 524 } 525 526 TEST_F(DomDistillerServiceTest, TestGetUrlForMultiPageEntry) { 527 FakeDistiller* distiller = new FakeDistiller(false); 528 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) 529 .WillOnce(Return(distiller)); 530 531 const int kPageCount = 8; 532 533 std::string base_url("http://www.example.com/p"); 534 GURL pages_url[kPageCount]; 535 for (int page_num = 0; page_num < kPageCount; ++page_num) { 536 pages_url[page_num] = GURL(base_url + base::IntToString(page_num)); 537 } 538 539 MockArticleAvailableCallback article_cb; 540 EXPECT_CALL(article_cb, DistillationCompleted(true)); 541 542 std::string entry_id = service_->AddToList( 543 pages_url[0], 544 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), 545 ArticleCallback(&article_cb)); 546 547 ArticleEntry entry; 548 ASSERT_FALSE(distiller->GetArticleCallback().is_null()); 549 EXPECT_EQ(pages_url[0], distiller->GetUrl()); 550 551 // Create the article with pages to pass to the distiller. 552 scoped_ptr<DistilledArticleProto> proto = 553 CreateArticleWithURL(pages_url[0].spec()); 554 for (int page_num = 1; page_num < kPageCount; ++page_num) { 555 DistilledPageProto* distilled_page = proto->add_pages(); 556 distilled_page->set_url(pages_url[page_num].spec()); 557 } 558 559 RunDistillerCallback(distiller, proto.Pass()); 560 EXPECT_TRUE(store_->GetEntryByUrl(pages_url[0], &entry)); 561 562 // Check if retrieved URL is same as given URL for the first page. 563 GURL retrieved_url(service_->GetUrlForEntry(entry_id)); 564 EXPECT_EQ(pages_url[0], retrieved_url); 565 566 // Remove the article and check that no URL can be retrieved for the entry. 567 service_->RemoveEntry(entry_id); 568 base::RunLoop().RunUntilIdle(); 569 EXPECT_EQ(0u, store_->GetEntries().size()); 570 EXPECT_EQ("", service_->GetUrlForEntry(entry_id)); 571 } 572 573 } // namespace test 574 } // namespace dom_distiller 575