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
25using leveldb_proto::test::FakeDB;
26using testing::Invoke;
27using testing::Return;
28using testing::_;
29
30namespace dom_distiller {
31namespace test {
32
33namespace {
34
35class 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
43class MockDistillerObserver : public DomDistillerObserver {
44 public:
45  MOCK_METHOD1(ArticleEntriesUpdated, void(const std::vector<ArticleUpdate>&));
46  virtual ~MockDistillerObserver() {}
47};
48
49class MockArticleAvailableCallback {
50 public:
51  MOCK_METHOD1(DistillationCompleted, void(bool));
52};
53
54DomDistillerService::ArticleAvailableCallback ArticleCallback(
55    MockArticleAvailableCallback* callback) {
56  return base::Bind(&MockArticleAvailableCallback::DistillationCompleted,
57                    base::Unretained(callback));
58}
59
60void RunDistillerCallback(FakeDistiller* distiller,
61                          scoped_ptr<DistilledArticleProto> proto) {
62  distiller->RunDistillerCallback(proto.Pass());
63  base::RunLoop().RunUntilIdle();
64}
65
66scoped_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
73scoped_ptr<DistilledArticleProto> CreateDefaultArticle() {
74  return CreateArticleWithURL("http://www.example.com/default_article_page1")
75      .Pass();
76}
77
78}  // namespace
79
80class 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
116TEST_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
142TEST_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
161TEST_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
197TEST_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
223TEST_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
242TEST_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
272TEST_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
294TEST_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
339TEST_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
379TEST_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
407TEST_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
461TEST_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
493TEST_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
526TEST_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