Home | History | Annotate | Download | only in core
      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/task_tracker.h"
      6 
      7 #include "base/run_loop.h"
      8 #include "components/dom_distiller/core/article_distillation_update.h"
      9 #include "components/dom_distiller/core/article_entry.h"
     10 #include "components/dom_distiller/core/distilled_content_store.h"
     11 #include "components/dom_distiller/core/fake_distiller.h"
     12 #include "testing/gtest/include/gtest/gtest.h"
     13 
     14 using testing::Return;
     15 using testing::_;
     16 
     17 namespace dom_distiller {
     18 namespace test {
     19 
     20 class FakeViewRequestDelegate : public ViewRequestDelegate {
     21  public:
     22   virtual ~FakeViewRequestDelegate() {}
     23   MOCK_METHOD1(OnArticleReady,
     24                void(const DistilledArticleProto* article_proto));
     25   MOCK_METHOD1(OnArticleUpdated,
     26                void(ArticleDistillationUpdate article_update));
     27 };
     28 
     29 class MockContentStore : public DistilledContentStore {
     30  public:
     31   MOCK_METHOD2(LoadContent,
     32                void(const ArticleEntry& entry, LoadCallback callback));
     33   MOCK_METHOD3(SaveContent,
     34                void(const ArticleEntry& entry,
     35                     const DistilledArticleProto& proto,
     36                     SaveCallback callback));
     37 };
     38 
     39 class TestCancelCallback {
     40  public:
     41   TestCancelCallback() : cancelled_(false) {}
     42   TaskTracker::CancelCallback GetCallback() {
     43     return base::Bind(&TestCancelCallback::Cancel, base::Unretained(this));
     44   }
     45   void Cancel(TaskTracker*) { cancelled_ = true; }
     46   bool Cancelled() { return cancelled_; }
     47 
     48  private:
     49   bool cancelled_;
     50 };
     51 
     52 class MockSaveCallback {
     53  public:
     54   MOCK_METHOD3(Save,
     55                void(const ArticleEntry&, const DistilledArticleProto*, bool));
     56 };
     57 
     58 class DomDistillerTaskTrackerTest : public testing::Test {
     59  public:
     60   virtual void SetUp() OVERRIDE {
     61     message_loop_.reset(new base::MessageLoop());
     62     entry_id_ = "id0";
     63     page_0_url_ = GURL("http://www.example.com/1");
     64     page_1_url_ = GURL("http://www.example.com/2");
     65   }
     66 
     67   ArticleEntry GetDefaultEntry() {
     68     ArticleEntry entry;
     69     entry.set_entry_id(entry_id_);
     70     ArticleEntryPage* page0 = entry.add_pages();
     71     ArticleEntryPage* page1 = entry.add_pages();
     72     page0->set_url(page_0_url_.spec());
     73     page1->set_url(page_1_url_.spec());
     74     return entry;
     75   }
     76 
     77  protected:
     78   scoped_ptr<base::MessageLoop> message_loop_;
     79   std::string entry_id_;
     80   GURL page_0_url_;
     81   GURL page_1_url_;
     82 };
     83 
     84 TEST_F(DomDistillerTaskTrackerTest, TestHasEntryId) {
     85   MockDistillerFactory distiller_factory;
     86   TestCancelCallback cancel_callback;
     87   TaskTracker task_tracker(
     88       GetDefaultEntry(), cancel_callback.GetCallback(), NULL);
     89   EXPECT_TRUE(task_tracker.HasEntryId(entry_id_));
     90   EXPECT_FALSE(task_tracker.HasEntryId("other_id"));
     91 }
     92 
     93 TEST_F(DomDistillerTaskTrackerTest, TestHasUrl) {
     94   MockDistillerFactory distiller_factory;
     95   TestCancelCallback cancel_callback;
     96   TaskTracker task_tracker(
     97       GetDefaultEntry(), cancel_callback.GetCallback(), NULL);
     98   EXPECT_TRUE(task_tracker.HasUrl(page_0_url_));
     99   EXPECT_TRUE(task_tracker.HasUrl(page_1_url_));
    100   EXPECT_FALSE(task_tracker.HasUrl(GURL("http://other.url/")));
    101 }
    102 
    103 TEST_F(DomDistillerTaskTrackerTest, TestViewerCancelled) {
    104   MockDistillerFactory distiller_factory;
    105   TestCancelCallback cancel_callback;
    106   TaskTracker task_tracker(
    107       GetDefaultEntry(), cancel_callback.GetCallback(), NULL);
    108 
    109   FakeViewRequestDelegate viewer_delegate;
    110   FakeViewRequestDelegate viewer_delegate2;
    111   scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate));
    112   scoped_ptr<ViewerHandle> handle2(task_tracker.AddViewer(&viewer_delegate2));
    113 
    114   EXPECT_FALSE(cancel_callback.Cancelled());
    115   handle.reset();
    116   EXPECT_FALSE(cancel_callback.Cancelled());
    117   handle2.reset();
    118   EXPECT_TRUE(cancel_callback.Cancelled());
    119 }
    120 
    121 TEST_F(DomDistillerTaskTrackerTest, TestViewerCancelledWithSaveRequest) {
    122   MockDistillerFactory distiller_factory;
    123   TestCancelCallback cancel_callback;
    124   TaskTracker task_tracker(
    125       GetDefaultEntry(), cancel_callback.GetCallback(), NULL);
    126 
    127   FakeViewRequestDelegate viewer_delegate;
    128   scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate));
    129   EXPECT_FALSE(cancel_callback.Cancelled());
    130 
    131   MockSaveCallback save_callback;
    132   task_tracker.AddSaveCallback(
    133       base::Bind(&MockSaveCallback::Save, base::Unretained(&save_callback)));
    134   handle.reset();
    135 
    136   // Since there is a pending save request, the task shouldn't be cancelled.
    137   EXPECT_FALSE(cancel_callback.Cancelled());
    138 }
    139 
    140 TEST_F(DomDistillerTaskTrackerTest, TestViewerNotifiedOnDistillationComplete) {
    141   MockDistillerFactory distiller_factory;
    142   FakeDistiller* distiller = new FakeDistiller(true);
    143   EXPECT_CALL(distiller_factory, CreateDistillerImpl())
    144       .WillOnce(Return(distiller));
    145   TestCancelCallback cancel_callback;
    146   TaskTracker task_tracker(
    147       GetDefaultEntry(), cancel_callback.GetCallback(), NULL);
    148 
    149   FakeViewRequestDelegate viewer_delegate;
    150   scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate));
    151   base::RunLoop().RunUntilIdle();
    152 
    153   EXPECT_CALL(viewer_delegate, OnArticleReady(_));
    154 
    155   task_tracker.StartDistiller(&distiller_factory,
    156                               scoped_ptr<DistillerPage>().Pass());
    157   base::RunLoop().RunUntilIdle();
    158 
    159   EXPECT_FALSE(cancel_callback.Cancelled());
    160 }
    161 
    162 TEST_F(DomDistillerTaskTrackerTest, TestDistillerFails) {
    163   MockDistillerFactory distiller_factory;
    164   FakeDistiller* distiller = new FakeDistiller(false);
    165   EXPECT_CALL(distiller_factory, CreateDistillerImpl())
    166       .WillOnce(Return(distiller));
    167 
    168   TestCancelCallback cancel_callback;
    169   TaskTracker task_tracker(
    170       GetDefaultEntry(), cancel_callback.GetCallback(), NULL);
    171 
    172   FakeViewRequestDelegate viewer_delegate;
    173   scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate));
    174   base::RunLoop().RunUntilIdle();
    175 
    176   EXPECT_CALL(viewer_delegate, OnArticleReady(_));
    177 
    178   task_tracker.StartDistiller(&distiller_factory,
    179                               scoped_ptr<DistillerPage>().Pass());
    180   distiller->RunDistillerCallback(
    181       scoped_ptr<DistilledArticleProto>(new DistilledArticleProto));
    182   base::RunLoop().RunUntilIdle();
    183 
    184   EXPECT_FALSE(cancel_callback.Cancelled());
    185 }
    186 
    187 TEST_F(DomDistillerTaskTrackerTest,
    188        TestSaveCallbackCalledOnDistillationComplete) {
    189   MockDistillerFactory distiller_factory;
    190   FakeDistiller* distiller = new FakeDistiller(true);
    191   EXPECT_CALL(distiller_factory, CreateDistillerImpl())
    192       .WillOnce(Return(distiller));
    193   TestCancelCallback cancel_callback;
    194   TaskTracker task_tracker(
    195       GetDefaultEntry(), cancel_callback.GetCallback(), NULL);
    196 
    197   MockSaveCallback save_callback;
    198   task_tracker.AddSaveCallback(
    199       base::Bind(&MockSaveCallback::Save, base::Unretained(&save_callback)));
    200   base::RunLoop().RunUntilIdle();
    201 
    202   EXPECT_CALL(save_callback, Save(_, _, _));
    203 
    204   task_tracker.StartDistiller(&distiller_factory,
    205                               scoped_ptr<DistillerPage>().Pass());
    206   base::RunLoop().RunUntilIdle();
    207 
    208   EXPECT_TRUE(cancel_callback.Cancelled());
    209 }
    210 
    211 DistilledArticleProto CreateDistilledArticleForEntry(
    212     const ArticleEntry& entry) {
    213   DistilledArticleProto article;
    214   for (int i = 0; i < entry.pages_size(); ++i) {
    215     DistilledPageProto* page = article.add_pages();
    216     page->set_url(entry.pages(i).url());
    217     page->set_html("<div>" + entry.pages(i).url() + "</div>");
    218   }
    219   return article;
    220 }
    221 
    222 TEST_F(DomDistillerTaskTrackerTest, TestBlobFetcher) {
    223   ArticleEntry entry_with_blob = GetDefaultEntry();
    224   DistilledArticleProto stored_distilled_article =
    225       CreateDistilledArticleForEntry(entry_with_blob);
    226   InMemoryContentStore content_store(kDefaultMaxNumCachedEntries);
    227   content_store.InjectContent(entry_with_blob, stored_distilled_article);
    228   TestCancelCallback cancel_callback;
    229 
    230   TaskTracker task_tracker(
    231       entry_with_blob, cancel_callback.GetCallback(), &content_store);
    232 
    233   FakeViewRequestDelegate viewer_delegate;
    234   scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate));
    235   base::RunLoop().RunUntilIdle();
    236 
    237   const DistilledArticleProto* distilled_article;
    238 
    239   EXPECT_CALL(viewer_delegate, OnArticleReady(_))
    240       .WillOnce(testing::SaveArg<0>(&distilled_article));
    241 
    242   task_tracker.StartBlobFetcher();
    243   base::RunLoop().RunUntilIdle();
    244 
    245   EXPECT_EQ(stored_distilled_article.SerializeAsString(),
    246             distilled_article->SerializeAsString());
    247 
    248   EXPECT_FALSE(cancel_callback.Cancelled());
    249 }
    250 
    251 TEST_F(DomDistillerTaskTrackerTest, TestBlobFetcherFinishesFirst) {
    252   MockDistillerFactory distiller_factory;
    253   FakeDistiller* distiller = new FakeDistiller(false);
    254   EXPECT_CALL(distiller_factory, CreateDistillerImpl())
    255       .WillOnce(Return(distiller));
    256 
    257   ArticleEntry entry_with_blob = GetDefaultEntry();
    258   DistilledArticleProto stored_distilled_article =
    259       CreateDistilledArticleForEntry(entry_with_blob);
    260   InMemoryContentStore content_store(kDefaultMaxNumCachedEntries);
    261   content_store.InjectContent(entry_with_blob, stored_distilled_article);
    262   TestCancelCallback cancel_callback;
    263   TaskTracker task_tracker(
    264       entry_with_blob, cancel_callback.GetCallback(), &content_store);
    265 
    266   FakeViewRequestDelegate viewer_delegate;
    267   scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate));
    268   base::RunLoop().RunUntilIdle();
    269 
    270   DistilledArticleProto distilled_article;
    271 
    272   EXPECT_CALL(viewer_delegate, OnArticleReady(_))
    273       .WillOnce(testing::SaveArgPointee<0>(&distilled_article));
    274   bool distiller_destroyed = false;
    275   EXPECT_CALL(*distiller, Die())
    276       .WillOnce(testing::Assign(&distiller_destroyed, true));
    277 
    278   task_tracker.StartDistiller(&distiller_factory,
    279                               scoped_ptr<DistillerPage>().Pass());
    280   task_tracker.StartBlobFetcher();
    281   base::RunLoop().RunUntilIdle();
    282 
    283   testing::Mock::VerifyAndClearExpectations(&viewer_delegate);
    284   EXPECT_EQ(stored_distilled_article.SerializeAsString(),
    285             distilled_article.SerializeAsString());
    286 
    287   EXPECT_TRUE(distiller_destroyed);
    288   EXPECT_FALSE(cancel_callback.Cancelled());
    289   base::RunLoop().RunUntilIdle();
    290 }
    291 
    292 TEST_F(DomDistillerTaskTrackerTest, TestBlobFetcherWithoutBlob) {
    293   MockDistillerFactory distiller_factory;
    294   FakeDistiller* distiller = new FakeDistiller(false);
    295   EXPECT_CALL(distiller_factory, CreateDistillerImpl())
    296       .WillOnce(Return(distiller));
    297 
    298   ArticleEntry entry(GetDefaultEntry());
    299   InMemoryContentStore content_store(kDefaultMaxNumCachedEntries);
    300   scoped_ptr<DistilledArticleProto> distilled_article(
    301       new DistilledArticleProto(CreateDistilledArticleForEntry(entry)));
    302 
    303   TestCancelCallback cancel_callback;
    304   TaskTracker task_tracker(
    305       GetDefaultEntry(), cancel_callback.GetCallback(), &content_store);
    306 
    307   FakeViewRequestDelegate viewer_delegate;
    308   scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate));
    309   base::RunLoop().RunUntilIdle();
    310 
    311   task_tracker.StartBlobFetcher();
    312   task_tracker.StartDistiller(&distiller_factory,
    313                               scoped_ptr<DistillerPage>().Pass());
    314 
    315   // OnArticleReady shouldn't be called until distillation finishes (i.e. the
    316   // blob fetcher shouldn't return distilled content).
    317   EXPECT_CALL(viewer_delegate, OnArticleReady(_)).Times(0);
    318   base::RunLoop().RunUntilIdle();
    319 
    320   EXPECT_CALL(viewer_delegate, OnArticleReady(_));
    321   distiller->RunDistillerCallback(distilled_article.Pass());
    322   base::RunLoop().RunUntilIdle();
    323 
    324   EXPECT_FALSE(cancel_callback.Cancelled());
    325 }
    326 
    327 TEST_F(DomDistillerTaskTrackerTest, TestDistillerFailsFirst) {
    328   MockDistillerFactory distiller_factory;
    329   FakeDistiller* distiller = new FakeDistiller(false);
    330   EXPECT_CALL(distiller_factory, CreateDistillerImpl())
    331       .WillOnce(Return(distiller));
    332 
    333   ArticleEntry entry(GetDefaultEntry());
    334   MockContentStore content_store;
    335 
    336   TestCancelCallback cancel_callback;
    337   TaskTracker task_tracker(
    338       GetDefaultEntry(), cancel_callback.GetCallback(), &content_store);
    339 
    340   FakeViewRequestDelegate viewer_delegate;
    341   scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate));
    342 
    343   DistilledContentStore::LoadCallback content_store_load_callback;
    344   EXPECT_CALL(content_store, LoadContent(_, _))
    345       .WillOnce(testing::SaveArg<1>(&content_store_load_callback));
    346 
    347   task_tracker.StartDistiller(&distiller_factory,
    348                               scoped_ptr<DistillerPage>().Pass());
    349   task_tracker.StartBlobFetcher();
    350 
    351   EXPECT_CALL(viewer_delegate, OnArticleReady(_)).Times(0);
    352   distiller->RunDistillerCallback(
    353       scoped_ptr<DistilledArticleProto>(new DistilledArticleProto));
    354   base::RunLoop().RunUntilIdle();
    355 
    356   EXPECT_CALL(viewer_delegate, OnArticleReady(_));
    357   content_store_load_callback.Run(
    358       true,
    359       scoped_ptr<DistilledArticleProto>(
    360           new DistilledArticleProto(CreateDistilledArticleForEntry(entry))));
    361   base::RunLoop().RunUntilIdle();
    362 
    363   EXPECT_FALSE(cancel_callback.Cancelled());
    364 }
    365 
    366 TEST_F(DomDistillerTaskTrackerTest, ContentIsSaved) {
    367   MockDistillerFactory distiller_factory;
    368   FakeDistiller* distiller = new FakeDistiller(false);
    369   EXPECT_CALL(distiller_factory, CreateDistillerImpl())
    370       .WillOnce(Return(distiller));
    371 
    372   ArticleEntry entry(GetDefaultEntry());
    373   DistilledArticleProto distilled_article =
    374       CreateDistilledArticleForEntry(entry);
    375 
    376   MockContentStore content_store;
    377   TestCancelCallback cancel_callback;
    378   TaskTracker task_tracker(
    379       GetDefaultEntry(), cancel_callback.GetCallback(), &content_store);
    380 
    381   FakeViewRequestDelegate viewer_delegate;
    382   scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate));
    383 
    384   DistilledArticleProto stored_distilled_article;
    385   DistilledContentStore::LoadCallback content_store_load_callback;
    386   EXPECT_CALL(content_store, SaveContent(_, _, _))
    387       .WillOnce(testing::SaveArg<1>(&stored_distilled_article));
    388 
    389   task_tracker.StartDistiller(&distiller_factory,
    390                               scoped_ptr<DistillerPage>().Pass());
    391 
    392   EXPECT_CALL(viewer_delegate, OnArticleReady(_));
    393   distiller->RunDistillerCallback(scoped_ptr<DistilledArticleProto>(
    394       new DistilledArticleProto(distilled_article)));
    395   base::RunLoop().RunUntilIdle();
    396 
    397   ASSERT_EQ(stored_distilled_article.SerializeAsString(),
    398             distilled_article.SerializeAsString());
    399   EXPECT_FALSE(cancel_callback.Cancelled());
    400 }
    401 
    402 }  // namespace test
    403 }  // namespace dom_distiller
    404