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/task_tracker.h"
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"
14 using testing::Return
;
17 namespace dom_distiller
{
20 class FakeViewRequestDelegate
: public ViewRequestDelegate
{
22 virtual ~FakeViewRequestDelegate() {}
23 MOCK_METHOD1(OnArticleReady
,
24 void(const DistilledArticleProto
* article_proto
));
25 MOCK_METHOD1(OnArticleUpdated
,
26 void(ArticleDistillationUpdate article_update
));
29 class MockContentStore
: public DistilledContentStore
{
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
));
39 class TestCancelCallback
{
41 TestCancelCallback() : cancelled_(false) {}
42 TaskTracker::CancelCallback
GetCallback() {
43 return base::Bind(&TestCancelCallback::Cancel
, base::Unretained(this));
45 void Cancel(TaskTracker
*) { cancelled_
= true; }
46 bool Cancelled() { return cancelled_
; }
52 class MockSaveCallback
{
55 void(const ArticleEntry
&, const DistilledArticleProto
*, bool));
58 class DomDistillerTaskTrackerTest
: public testing::Test
{
60 void SetUp() override
{
61 message_loop_
.reset(new base::MessageLoop());
63 page_0_url_
= GURL("http://www.example.com/1");
64 page_1_url_
= GURL("http://www.example.com/2");
67 ArticleEntry
GetDefaultEntry() {
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());
78 scoped_ptr
<base::MessageLoop
> message_loop_
;
79 std::string entry_id_
;
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"));
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/")));
103 TEST_F(DomDistillerTaskTrackerTest
, TestViewerCancelled
) {
104 MockDistillerFactory distiller_factory
;
105 TestCancelCallback cancel_callback
;
106 TaskTracker
task_tracker(
107 GetDefaultEntry(), cancel_callback
.GetCallback(), NULL
);
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
));
114 EXPECT_FALSE(cancel_callback
.Cancelled());
116 EXPECT_FALSE(cancel_callback
.Cancelled());
118 EXPECT_TRUE(cancel_callback
.Cancelled());
121 TEST_F(DomDistillerTaskTrackerTest
, TestViewerCancelledWithSaveRequest
) {
122 MockDistillerFactory distiller_factory
;
123 TestCancelCallback cancel_callback
;
124 TaskTracker
task_tracker(
125 GetDefaultEntry(), cancel_callback
.GetCallback(), NULL
);
127 FakeViewRequestDelegate viewer_delegate
;
128 scoped_ptr
<ViewerHandle
> handle(task_tracker
.AddViewer(&viewer_delegate
));
129 EXPECT_FALSE(cancel_callback
.Cancelled());
131 MockSaveCallback save_callback
;
132 task_tracker
.AddSaveCallback(
133 base::Bind(&MockSaveCallback::Save
, base::Unretained(&save_callback
)));
136 // Since there is a pending save request, the task shouldn't be cancelled.
137 EXPECT_FALSE(cancel_callback
.Cancelled());
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
);
149 FakeViewRequestDelegate viewer_delegate
;
150 scoped_ptr
<ViewerHandle
> handle(task_tracker
.AddViewer(&viewer_delegate
));
151 base::RunLoop().RunUntilIdle();
153 EXPECT_CALL(viewer_delegate
, OnArticleReady(_
));
155 task_tracker
.StartDistiller(&distiller_factory
,
156 scoped_ptr
<DistillerPage
>().Pass());
157 base::RunLoop().RunUntilIdle();
159 EXPECT_FALSE(cancel_callback
.Cancelled());
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
));
168 TestCancelCallback cancel_callback
;
169 TaskTracker
task_tracker(
170 GetDefaultEntry(), cancel_callback
.GetCallback(), NULL
);
172 FakeViewRequestDelegate viewer_delegate
;
173 scoped_ptr
<ViewerHandle
> handle(task_tracker
.AddViewer(&viewer_delegate
));
174 base::RunLoop().RunUntilIdle();
176 EXPECT_CALL(viewer_delegate
, OnArticleReady(_
));
178 task_tracker
.StartDistiller(&distiller_factory
,
179 scoped_ptr
<DistillerPage
>().Pass());
180 distiller
->RunDistillerCallback(
181 scoped_ptr
<DistilledArticleProto
>(new DistilledArticleProto
));
182 base::RunLoop().RunUntilIdle();
184 EXPECT_FALSE(cancel_callback
.Cancelled());
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
);
197 MockSaveCallback save_callback
;
198 task_tracker
.AddSaveCallback(
199 base::Bind(&MockSaveCallback::Save
, base::Unretained(&save_callback
)));
200 base::RunLoop().RunUntilIdle();
202 EXPECT_CALL(save_callback
, Save(_
, _
, _
));
204 task_tracker
.StartDistiller(&distiller_factory
,
205 scoped_ptr
<DistillerPage
>().Pass());
206 base::RunLoop().RunUntilIdle();
208 EXPECT_TRUE(cancel_callback
.Cancelled());
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>");
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
;
230 TaskTracker
task_tracker(
231 entry_with_blob
, cancel_callback
.GetCallback(), &content_store
);
233 FakeViewRequestDelegate viewer_delegate
;
234 scoped_ptr
<ViewerHandle
> handle(task_tracker
.AddViewer(&viewer_delegate
));
235 base::RunLoop().RunUntilIdle();
237 const DistilledArticleProto
* distilled_article
;
239 EXPECT_CALL(viewer_delegate
, OnArticleReady(_
))
240 .WillOnce(testing::SaveArg
<0>(&distilled_article
));
242 task_tracker
.StartBlobFetcher();
243 base::RunLoop().RunUntilIdle();
245 EXPECT_EQ(stored_distilled_article
.SerializeAsString(),
246 distilled_article
->SerializeAsString());
248 EXPECT_FALSE(cancel_callback
.Cancelled());
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
));
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
);
266 FakeViewRequestDelegate viewer_delegate
;
267 scoped_ptr
<ViewerHandle
> handle(task_tracker
.AddViewer(&viewer_delegate
));
268 base::RunLoop().RunUntilIdle();
270 DistilledArticleProto distilled_article
;
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));
278 task_tracker
.StartDistiller(&distiller_factory
,
279 scoped_ptr
<DistillerPage
>().Pass());
280 task_tracker
.StartBlobFetcher();
281 base::RunLoop().RunUntilIdle();
283 testing::Mock::VerifyAndClearExpectations(&viewer_delegate
);
284 EXPECT_EQ(stored_distilled_article
.SerializeAsString(),
285 distilled_article
.SerializeAsString());
287 EXPECT_TRUE(distiller_destroyed
);
288 EXPECT_FALSE(cancel_callback
.Cancelled());
289 base::RunLoop().RunUntilIdle();
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
));
298 ArticleEntry
entry(GetDefaultEntry());
299 InMemoryContentStore
content_store(kDefaultMaxNumCachedEntries
);
300 scoped_ptr
<DistilledArticleProto
> distilled_article(
301 new DistilledArticleProto(CreateDistilledArticleForEntry(entry
)));
303 TestCancelCallback cancel_callback
;
304 TaskTracker
task_tracker(
305 GetDefaultEntry(), cancel_callback
.GetCallback(), &content_store
);
307 FakeViewRequestDelegate viewer_delegate
;
308 scoped_ptr
<ViewerHandle
> handle(task_tracker
.AddViewer(&viewer_delegate
));
309 base::RunLoop().RunUntilIdle();
311 task_tracker
.StartBlobFetcher();
312 task_tracker
.StartDistiller(&distiller_factory
,
313 scoped_ptr
<DistillerPage
>().Pass());
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();
320 EXPECT_CALL(viewer_delegate
, OnArticleReady(_
));
321 distiller
->RunDistillerCallback(distilled_article
.Pass());
322 base::RunLoop().RunUntilIdle();
324 EXPECT_FALSE(cancel_callback
.Cancelled());
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
));
333 ArticleEntry
entry(GetDefaultEntry());
334 MockContentStore content_store
;
336 TestCancelCallback cancel_callback
;
337 TaskTracker
task_tracker(
338 GetDefaultEntry(), cancel_callback
.GetCallback(), &content_store
);
340 FakeViewRequestDelegate viewer_delegate
;
341 scoped_ptr
<ViewerHandle
> handle(task_tracker
.AddViewer(&viewer_delegate
));
343 DistilledContentStore::LoadCallback content_store_load_callback
;
344 EXPECT_CALL(content_store
, LoadContent(_
, _
))
345 .WillOnce(testing::SaveArg
<1>(&content_store_load_callback
));
347 task_tracker
.StartDistiller(&distiller_factory
,
348 scoped_ptr
<DistillerPage
>().Pass());
349 task_tracker
.StartBlobFetcher();
351 EXPECT_CALL(viewer_delegate
, OnArticleReady(_
)).Times(0);
352 distiller
->RunDistillerCallback(
353 scoped_ptr
<DistilledArticleProto
>(new DistilledArticleProto
));
354 base::RunLoop().RunUntilIdle();
356 EXPECT_CALL(viewer_delegate
, OnArticleReady(_
));
357 content_store_load_callback
.Run(
359 scoped_ptr
<DistilledArticleProto
>(
360 new DistilledArticleProto(CreateDistilledArticleForEntry(entry
))));
361 base::RunLoop().RunUntilIdle();
363 EXPECT_FALSE(cancel_callback
.Cancelled());
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
));
372 ArticleEntry
entry(GetDefaultEntry());
373 DistilledArticleProto distilled_article
=
374 CreateDistilledArticleForEntry(entry
);
376 MockContentStore content_store
;
377 TestCancelCallback cancel_callback
;
378 TaskTracker
task_tracker(
379 GetDefaultEntry(), cancel_callback
.GetCallback(), &content_store
);
381 FakeViewRequestDelegate viewer_delegate
;
382 scoped_ptr
<ViewerHandle
> handle(task_tracker
.AddViewer(&viewer_delegate
));
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
));
389 task_tracker
.StartDistiller(&distiller_factory
,
390 scoped_ptr
<DistillerPage
>().Pass());
392 EXPECT_CALL(viewer_delegate
, OnArticleReady(_
));
393 distiller
->RunDistillerCallback(scoped_ptr
<DistilledArticleProto
>(
394 new DistilledArticleProto(distilled_article
)));
395 base::RunLoop().RunUntilIdle();
397 ASSERT_EQ(stored_distilled_article
.SerializeAsString(),
398 distilled_article
.SerializeAsString());
399 EXPECT_FALSE(cancel_callback
.Cancelled());
403 } // namespace dom_distiller