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 14using testing::Return; 15using testing::_; 16 17namespace dom_distiller { 18namespace test { 19 20class 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 29class 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 39class 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 52class MockSaveCallback { 53 public: 54 MOCK_METHOD3(Save, 55 void(const ArticleEntry&, const DistilledArticleProto*, bool)); 56}; 57 58class 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 84TEST_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 93TEST_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 103TEST_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 121TEST_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 140TEST_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 162TEST_F(DomDistillerTaskTrackerTest, 163 TestSaveCallbackCalledOnDistillationComplete) { 164 MockDistillerFactory distiller_factory; 165 FakeDistiller* distiller = new FakeDistiller(true); 166 EXPECT_CALL(distiller_factory, CreateDistillerImpl()) 167 .WillOnce(Return(distiller)); 168 TestCancelCallback cancel_callback; 169 TaskTracker task_tracker( 170 GetDefaultEntry(), cancel_callback.GetCallback(), NULL); 171 172 MockSaveCallback save_callback; 173 task_tracker.AddSaveCallback( 174 base::Bind(&MockSaveCallback::Save, base::Unretained(&save_callback))); 175 base::RunLoop().RunUntilIdle(); 176 177 EXPECT_CALL(save_callback, Save(_, _, _)); 178 179 task_tracker.StartDistiller(&distiller_factory, 180 scoped_ptr<DistillerPage>().Pass()); 181 base::RunLoop().RunUntilIdle(); 182 183 EXPECT_TRUE(cancel_callback.Cancelled()); 184} 185 186DistilledArticleProto CreateDistilledArticleForEntry( 187 const ArticleEntry& entry) { 188 DistilledArticleProto article; 189 for (int i = 0; i < entry.pages_size(); ++i) { 190 DistilledPageProto* page = article.add_pages(); 191 page->set_url(entry.pages(i).url()); 192 page->set_html("<div>" + entry.pages(i).url() + "</div>"); 193 } 194 return article; 195} 196 197TEST_F(DomDistillerTaskTrackerTest, TestBlobFetcher) { 198 ArticleEntry entry_with_blob = GetDefaultEntry(); 199 DistilledArticleProto stored_distilled_article = 200 CreateDistilledArticleForEntry(entry_with_blob); 201 InMemoryContentStore content_store(kDefaultMaxNumCachedEntries); 202 content_store.InjectContent(entry_with_blob, stored_distilled_article); 203 TestCancelCallback cancel_callback; 204 205 TaskTracker task_tracker( 206 entry_with_blob, cancel_callback.GetCallback(), &content_store); 207 208 FakeViewRequestDelegate viewer_delegate; 209 scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate)); 210 base::RunLoop().RunUntilIdle(); 211 212 const DistilledArticleProto* distilled_article; 213 214 EXPECT_CALL(viewer_delegate, OnArticleReady(_)) 215 .WillOnce(testing::SaveArg<0>(&distilled_article)); 216 217 task_tracker.StartBlobFetcher(); 218 base::RunLoop().RunUntilIdle(); 219 220 EXPECT_EQ(stored_distilled_article.SerializeAsString(), 221 distilled_article->SerializeAsString()); 222 223 EXPECT_FALSE(cancel_callback.Cancelled()); 224} 225 226TEST_F(DomDistillerTaskTrackerTest, TestBlobFetcherFinishesFirst) { 227 MockDistillerFactory distiller_factory; 228 FakeDistiller* distiller = new FakeDistiller(false); 229 EXPECT_CALL(distiller_factory, CreateDistillerImpl()) 230 .WillOnce(Return(distiller)); 231 232 ArticleEntry entry_with_blob = GetDefaultEntry(); 233 DistilledArticleProto stored_distilled_article = 234 CreateDistilledArticleForEntry(entry_with_blob); 235 InMemoryContentStore content_store(kDefaultMaxNumCachedEntries); 236 content_store.InjectContent(entry_with_blob, stored_distilled_article); 237 TestCancelCallback cancel_callback; 238 TaskTracker task_tracker( 239 entry_with_blob, cancel_callback.GetCallback(), &content_store); 240 241 FakeViewRequestDelegate viewer_delegate; 242 scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate)); 243 base::RunLoop().RunUntilIdle(); 244 245 DistilledArticleProto distilled_article; 246 247 EXPECT_CALL(viewer_delegate, OnArticleReady(_)) 248 .WillOnce(testing::SaveArgPointee<0>(&distilled_article)); 249 bool distiller_destroyed = false; 250 EXPECT_CALL(*distiller, Die()) 251 .WillOnce(testing::Assign(&distiller_destroyed, true)); 252 253 task_tracker.StartDistiller(&distiller_factory, 254 scoped_ptr<DistillerPage>().Pass()); 255 task_tracker.StartBlobFetcher(); 256 base::RunLoop().RunUntilIdle(); 257 258 testing::Mock::VerifyAndClearExpectations(&viewer_delegate); 259 EXPECT_EQ(stored_distilled_article.SerializeAsString(), 260 distilled_article.SerializeAsString()); 261 262 EXPECT_TRUE(distiller_destroyed); 263 EXPECT_FALSE(cancel_callback.Cancelled()); 264 base::RunLoop().RunUntilIdle(); 265} 266 267TEST_F(DomDistillerTaskTrackerTest, TestBlobFetcherWithoutBlob) { 268 MockDistillerFactory distiller_factory; 269 FakeDistiller* distiller = new FakeDistiller(false); 270 EXPECT_CALL(distiller_factory, CreateDistillerImpl()) 271 .WillOnce(Return(distiller)); 272 273 ArticleEntry entry(GetDefaultEntry()); 274 InMemoryContentStore content_store(kDefaultMaxNumCachedEntries); 275 scoped_ptr<DistilledArticleProto> distilled_article( 276 new DistilledArticleProto(CreateDistilledArticleForEntry(entry))); 277 278 TestCancelCallback cancel_callback; 279 TaskTracker task_tracker( 280 GetDefaultEntry(), cancel_callback.GetCallback(), &content_store); 281 282 FakeViewRequestDelegate viewer_delegate; 283 scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate)); 284 base::RunLoop().RunUntilIdle(); 285 286 task_tracker.StartBlobFetcher(); 287 task_tracker.StartDistiller(&distiller_factory, 288 scoped_ptr<DistillerPage>().Pass()); 289 290 // OnArticleReady shouldn't be called until distillation finishes (i.e. the 291 // blob fetcher shouldn't return distilled content). 292 EXPECT_CALL(viewer_delegate, OnArticleReady(_)).Times(0); 293 base::RunLoop().RunUntilIdle(); 294 295 EXPECT_CALL(viewer_delegate, OnArticleReady(_)); 296 distiller->RunDistillerCallback(distilled_article.Pass()); 297 base::RunLoop().RunUntilIdle(); 298 299 EXPECT_FALSE(cancel_callback.Cancelled()); 300} 301 302TEST_F(DomDistillerTaskTrackerTest, TestDistillerFailsFirst) { 303 MockDistillerFactory distiller_factory; 304 FakeDistiller* distiller = new FakeDistiller(false); 305 EXPECT_CALL(distiller_factory, CreateDistillerImpl()) 306 .WillOnce(Return(distiller)); 307 308 ArticleEntry entry(GetDefaultEntry()); 309 MockContentStore content_store; 310 311 TestCancelCallback cancel_callback; 312 TaskTracker task_tracker( 313 GetDefaultEntry(), cancel_callback.GetCallback(), &content_store); 314 315 FakeViewRequestDelegate viewer_delegate; 316 scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate)); 317 318 DistilledContentStore::LoadCallback content_store_load_callback; 319 EXPECT_CALL(content_store, LoadContent(_, _)) 320 .WillOnce(testing::SaveArg<1>(&content_store_load_callback)); 321 322 task_tracker.StartDistiller(&distiller_factory, 323 scoped_ptr<DistillerPage>().Pass()); 324 task_tracker.StartBlobFetcher(); 325 326 EXPECT_CALL(viewer_delegate, OnArticleReady(_)).Times(0); 327 distiller->RunDistillerCallback( 328 scoped_ptr<DistilledArticleProto>(new DistilledArticleProto)); 329 base::RunLoop().RunUntilIdle(); 330 331 EXPECT_CALL(viewer_delegate, OnArticleReady(_)); 332 content_store_load_callback.Run( 333 true, 334 scoped_ptr<DistilledArticleProto>( 335 new DistilledArticleProto(CreateDistilledArticleForEntry(entry)))); 336 base::RunLoop().RunUntilIdle(); 337 338 EXPECT_FALSE(cancel_callback.Cancelled()); 339} 340 341TEST_F(DomDistillerTaskTrackerTest, ContentIsSaved) { 342 MockDistillerFactory distiller_factory; 343 FakeDistiller* distiller = new FakeDistiller(false); 344 EXPECT_CALL(distiller_factory, CreateDistillerImpl()) 345 .WillOnce(Return(distiller)); 346 347 ArticleEntry entry(GetDefaultEntry()); 348 DistilledArticleProto distilled_article = 349 CreateDistilledArticleForEntry(entry); 350 351 MockContentStore content_store; 352 TestCancelCallback cancel_callback; 353 TaskTracker task_tracker( 354 GetDefaultEntry(), cancel_callback.GetCallback(), &content_store); 355 356 FakeViewRequestDelegate viewer_delegate; 357 scoped_ptr<ViewerHandle> handle(task_tracker.AddViewer(&viewer_delegate)); 358 359 DistilledArticleProto stored_distilled_article; 360 DistilledContentStore::LoadCallback content_store_load_callback; 361 EXPECT_CALL(content_store, SaveContent(_, _, _)) 362 .WillOnce(testing::SaveArg<1>(&stored_distilled_article)); 363 364 task_tracker.StartDistiller(&distiller_factory, 365 scoped_ptr<DistillerPage>().Pass()); 366 367 EXPECT_CALL(viewer_delegate, OnArticleReady(_)); 368 distiller->RunDistillerCallback(scoped_ptr<DistilledArticleProto>( 369 new DistilledArticleProto(distilled_article))); 370 base::RunLoop().RunUntilIdle(); 371 372 ASSERT_EQ(stored_distilled_article.SerializeAsString(), 373 distilled_article.SerializeAsString()); 374 EXPECT_FALSE(cancel_callback.Cancelled()); 375} 376 377} // namespace test 378} // namespace dom_distiller 379