Don't preload rarely seen large images
[chromium-blink-merge.git] / components / dom_distiller / core / dom_distiller_service_unittest.cc
blob868b3983b91f351d15abb466435ec62128da81c3
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.
5 #include "components/dom_distiller/core/dom_distiller_service.h"
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"
25 using leveldb_proto::test::FakeDB;
26 using testing::Invoke;
27 using testing::Return;
28 using testing::_;
30 namespace dom_distiller {
31 namespace test {
33 namespace {
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));
43 class MockDistillerObserver : public DomDistillerObserver {
44 public:
45 MOCK_METHOD1(ArticleEntriesUpdated, void(const std::vector<ArticleUpdate>&));
46 virtual ~MockDistillerObserver() {}
49 class MockArticleAvailableCallback {
50 public:
51 MOCK_METHOD1(DistillationCompleted, void(bool));
54 DomDistillerService::ArticleAvailableCallback ArticleCallback(
55 MockArticleAvailableCallback* callback) {
56 return base::Bind(&MockArticleAvailableCallback::DistillationCompleted,
57 base::Unretained(callback));
60 void RunDistillerCallback(FakeDistiller* distiller,
61 scoped_ptr<DistilledArticleProto> proto) {
62 distiller->RunDistillerCallback(proto.Pass());
63 base::RunLoop().RunUntilIdle();
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();
73 scoped_ptr<DistilledArticleProto> CreateDefaultArticle() {
74 return CreateArticleWithURL("http://www.example.com/default_article_page1")
75 .Pass();
78 } // namespace
80 class DomDistillerServiceTest : public testing::Test {
81 public:
82 void SetUp() override {
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);
99 void TearDown() override {
100 base::RunLoop().RunUntilIdle();
101 store_ = NULL;
102 distiller_factory_ = NULL;
103 service_.reset();
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_;
116 TEST_F(DomDistillerServiceTest, TestViewEntry) {
117 FakeDistiller* distiller = new FakeDistiller(false);
118 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
119 .WillOnce(Return(distiller));
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());
127 store_->AddEntry(entry);
129 FakeViewRequestDelegate viewer_delegate;
130 scoped_ptr<ViewerHandle> handle = service_->ViewEntry(
131 &viewer_delegate, service_->CreateDefaultDistillerPage(gfx::Size()),
132 entry_id);
134 ASSERT_FALSE(distiller->GetArticleCallback().is_null());
136 scoped_ptr<DistilledArticleProto> proto = CreateDefaultArticle();
137 EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get()));
139 RunDistillerCallback(distiller, proto.Pass());
142 TEST_F(DomDistillerServiceTest, TestViewUrl) {
143 FakeDistiller* distiller = new FakeDistiller(false);
144 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
145 .WillOnce(Return(distiller));
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);
152 ASSERT_FALSE(distiller->GetArticleCallback().is_null());
153 EXPECT_EQ(url, distiller->GetUrl());
155 scoped_ptr<DistilledArticleProto> proto = CreateDefaultArticle();
156 EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get()));
158 RunDistillerCallback(distiller, proto.Pass());
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));
168 FakeViewRequestDelegate viewer_delegate;
169 FakeViewRequestDelegate viewer_delegate2;
171 GURL url("http://www.example.com/p1");
172 GURL url2("http://www.example.com/a/p1");
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);
180 ASSERT_FALSE(distiller->GetArticleCallback().is_null());
181 EXPECT_EQ(url, distiller->GetUrl());
183 scoped_ptr<DistilledArticleProto> proto = CreateDefaultArticle();
184 EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get()));
186 RunDistillerCallback(distiller, proto.Pass());
188 ASSERT_FALSE(distiller2->GetArticleCallback().is_null());
189 EXPECT_EQ(url2, distiller2->GetUrl());
191 scoped_ptr<DistilledArticleProto> proto2 = CreateDefaultArticle();
192 EXPECT_CALL(viewer_delegate2, OnArticleReady(proto2.get()));
194 RunDistillerCallback(distiller2, proto2.Pass());
197 TEST_F(DomDistillerServiceTest, TestViewUrlCancelled) {
198 FakeDistiller* distiller = new FakeDistiller(false);
199 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
200 .WillOnce(Return(distiller));
202 bool distiller_destroyed = false;
203 EXPECT_CALL(*distiller, Die())
204 .WillOnce(testing::Assign(&distiller_destroyed, true));
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);
211 ASSERT_FALSE(distiller->GetArticleCallback().is_null());
212 EXPECT_EQ(url, distiller->GetUrl());
214 EXPECT_CALL(viewer_delegate, OnArticleReady(_)).Times(0);
216 EXPECT_FALSE(distiller_destroyed);
218 handle.reset();
219 base::RunLoop().RunUntilIdle();
220 EXPECT_TRUE(distiller_destroyed);
223 TEST_F(DomDistillerServiceTest, TestViewUrlDoesNotAddEntry) {
224 FakeDistiller* distiller = new FakeDistiller(false);
225 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
226 .WillOnce(Return(distiller));
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);
233 scoped_ptr<DistilledArticleProto> proto = CreateArticleWithURL(url.spec());
234 EXPECT_CALL(viewer_delegate, OnArticleReady(proto.get()));
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());
242 TEST_F(DomDistillerServiceTest, TestAddAndRemoveEntry) {
243 FakeDistiller* distiller = new FakeDistiller(false);
244 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
245 .WillOnce(Return(distiller));
247 GURL url("http://www.example.com/p1");
249 MockArticleAvailableCallback article_cb;
250 EXPECT_CALL(article_cb, DistillationCompleted(true));
252 std::string entry_id =
253 service_->AddToList(url,
254 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(),
255 ArticleCallback(&article_cb));
257 ASSERT_FALSE(distiller->GetArticleCallback().is_null());
258 EXPECT_EQ(url, distiller->GetUrl());
260 scoped_ptr<DistilledArticleProto> proto = CreateArticleWithURL(url.spec());
261 RunDistillerCallback(distiller, proto.Pass());
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());
272 TEST_F(DomDistillerServiceTest, TestCancellation) {
273 FakeDistiller* distiller = new FakeDistiller(false);
274 MockDistillerObserver observer;
275 service_->AddObserver(&observer);
277 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
278 .WillOnce(Return(distiller));
280 MockArticleAvailableCallback article_cb;
281 EXPECT_CALL(article_cb, DistillationCompleted(false));
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));
289 // Remove entry will cause the |article_cb| to be called with false value.
290 service_->RemoveEntry(entry_id);
291 base::RunLoop().RunUntilIdle();
294 TEST_F(DomDistillerServiceTest, TestMultipleObservers) {
295 FakeDistiller* distiller = new FakeDistiller(false);
296 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
297 .WillOnce(Return(distiller));
299 const int kObserverCount = 5;
300 MockDistillerObserver observers[kObserverCount];
301 for (int i = 0; i < kObserverCount; ++i) {
302 service_->AddObserver(&observers[i]);
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);
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);
318 for (int i = 0; i < kObserverCount; ++i) {
319 EXPECT_CALL(observers[i], ArticleEntriesUpdated(
320 util::HasExpectedUpdates(expected_updates)));
323 scoped_ptr<DistilledArticleProto> proto = CreateDefaultArticle();
324 RunDistillerCallback(distiller, proto.Pass());
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)));
335 service_->RemoveEntry(entry_id);
336 base::RunLoop().RunUntilIdle();
339 TEST_F(DomDistillerServiceTest, TestMultipleCallbacks) {
340 FakeDistiller* distiller = new FakeDistiller(false);
341 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
342 .WillOnce(Return(distiller));
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));
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));
363 scoped_ptr<DistilledArticleProto> proto = CreateArticleWithURL(url.spec());
364 RunDistillerCallback(distiller, proto.Pass());
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])));
376 base::RunLoop().RunUntilIdle();
379 TEST_F(DomDistillerServiceTest, TestMultipleCallbacksOnRemove) {
380 FakeDistiller* distiller = new FakeDistiller(false);
381 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
382 .WillOnce(Return(distiller));
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]));
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));
403 service_->RemoveEntry(entry_id);
404 base::RunLoop().RunUntilIdle();
407 TEST_F(DomDistillerServiceTest, TestMultiplePageArticle) {
408 FakeDistiller* distiller = new FakeDistiller(false);
409 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
410 .WillOnce(Return(distiller));
412 const int kPageCount = 8;
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));
420 MockArticleAvailableCallback article_cb;
421 EXPECT_CALL(article_cb, DistillationCompleted(true));
423 std::string entry_id = service_->AddToList(
424 pages_url[0], service_->CreateDefaultDistillerPage(gfx::Size()).Pass(),
425 ArticleCallback(&article_cb));
427 ArticleEntry entry;
428 ASSERT_FALSE(distiller->GetArticleCallback().is_null());
429 EXPECT_EQ(pages_url[0], distiller->GetUrl());
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());
439 RunDistillerCallback(distiller, proto.Pass());
440 EXPECT_TRUE(store_->GetEntryByUrl(pages_url[0], &entry));
442 EXPECT_EQ(kPageCount, entry.pages_size());
443 // An article should have just one entry.
444 EXPECT_EQ(1u, store_->GetEntries().size());
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());
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));
456 service_->RemoveEntry(entry_id);
457 base::RunLoop().RunUntilIdle();
458 EXPECT_EQ(0u, store_->GetEntries().size());
461 TEST_F(DomDistillerServiceTest, TestHasEntry) {
462 FakeDistiller* distiller = new FakeDistiller(false);
463 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
464 .WillOnce(Return(distiller));
466 GURL url("http://www.example.com/p1");
468 MockArticleAvailableCallback article_cb;
469 EXPECT_CALL(article_cb, DistillationCompleted(true));
471 std::string entry_id = service_->AddToList(
472 url,
473 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(),
474 ArticleCallback(&article_cb));
476 ASSERT_FALSE(distiller->GetArticleCallback().is_null());
477 EXPECT_EQ(url, distiller->GetUrl());
479 scoped_ptr<DistilledArticleProto> proto = CreateArticleWithURL(url.spec());
480 RunDistillerCallback(distiller, proto.Pass());
482 // Check that HasEntry returns true for the article just added.
483 EXPECT_TRUE(service_->HasEntry(entry_id));
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));
493 TEST_F(DomDistillerServiceTest, TestGetUrlForOnePageEntry) {
494 FakeDistiller* distiller = new FakeDistiller(false);
495 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
496 .WillOnce(Return(distiller));
498 GURL url("http://www.example.com/p1");
500 MockArticleAvailableCallback article_cb;
501 EXPECT_CALL(article_cb, DistillationCompleted(true));
503 std::string entry_id = service_->AddToList(
504 url,
505 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(),
506 ArticleCallback(&article_cb));
508 ASSERT_FALSE(distiller->GetArticleCallback().is_null());
509 EXPECT_EQ(url, distiller->GetUrl());
511 scoped_ptr<DistilledArticleProto> proto = CreateArticleWithURL(url.spec());
512 RunDistillerCallback(distiller, proto.Pass());
514 // Check if retrieved URL is same as given URL.
515 GURL retrieved_url(service_->GetUrlForEntry(entry_id));
516 EXPECT_EQ(url, retrieved_url);
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));
526 TEST_F(DomDistillerServiceTest, TestGetUrlForMultiPageEntry) {
527 FakeDistiller* distiller = new FakeDistiller(false);
528 EXPECT_CALL(*distiller_factory_, CreateDistillerImpl())
529 .WillOnce(Return(distiller));
531 const int kPageCount = 8;
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));
539 MockArticleAvailableCallback article_cb;
540 EXPECT_CALL(article_cb, DistillationCompleted(true));
542 std::string entry_id = service_->AddToList(
543 pages_url[0],
544 service_->CreateDefaultDistillerPage(gfx::Size()).Pass(),
545 ArticleCallback(&article_cb));
547 ArticleEntry entry;
548 ASSERT_FALSE(distiller->GetArticleCallback().is_null());
549 EXPECT_EQ(pages_url[0], distiller->GetUrl());
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());
559 RunDistillerCallback(distiller, proto.Pass());
560 EXPECT_TRUE(store_->GetEntryByUrl(pages_url[0], &entry));
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);
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));
573 } // namespace test
574 } // namespace dom_distiller