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.
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/location.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/thread_task_runner_handle.h"
18 #include "base/values.h"
19 #include "components/dom_distiller/core/article_distillation_update.h"
20 #include "components/dom_distiller/core/distiller.h"
21 #include "components/dom_distiller/core/distiller_page.h"
22 #include "components/dom_distiller/core/fake_distiller_page.h"
23 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
24 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
25 #include "net/url_request/url_request_context_getter.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "third_party/dom_distiller_js/dom_distiller.pb.h"
29 #include "third_party/dom_distiller_js/dom_distiller_json_converter.h"
33 using ::testing::Invoke
;
34 using ::testing::Return
;
37 using dom_distiller::proto::DomDistillerOptions
;
38 using dom_distiller::proto::DomDistillerResult
;
39 using dom_distiller::proto::DomDistillerResult_ContentImage
;
40 using dom_distiller::proto::TimingEntry
;
43 const char kTitle
[] = "Title";
44 const char kContent
[] = "Content";
45 const char kURL
[] = "http://a.com/";
46 const size_t kTotalImages
= 3;
47 const char* kImageURLs
[kTotalImages
] = {"http://a.com/img1.jpg",
48 "http://a.com/img2.jpg",
49 "./bad_url_should_fail"};
50 const char* kImageData
[kTotalImages
] = {"abcde", "12345", "VWXYZ"};
51 const char kDebugLog
[] = "Debug Log";
53 const string
GetImageName(int page_num
, int image_num
) {
54 return base::IntToString(page_num
) + "_" + base::IntToString(image_num
);
57 scoped_ptr
<base::Value
> CreateDistilledValueReturnedFromJS(
59 const string
& content
,
60 const vector
<int>& image_indices
,
61 const string
& next_page_url
,
62 const string
& prev_page_url
= "") {
63 DomDistillerResult result
;
64 result
.set_title(title
);
65 result
.mutable_distilled_content()->set_html(content
);
66 result
.mutable_pagination_info()->set_next_page(next_page_url
);
67 result
.mutable_pagination_info()->set_prev_page(prev_page_url
);
69 for (size_t i
= 0; i
< image_indices
.size(); ++i
) {
70 DomDistillerResult_ContentImage
* curr_image
= result
.add_content_images();
71 curr_image
->set_url(kImageURLs
[image_indices
[i
]]);
74 return dom_distiller::proto::json::DomDistillerResult::WriteToValue(result
);
77 // Return the sequence in which Distiller will distill pages.
78 // Note: ignores any delays due to fetching images etc.
79 vector
<int> GetPagesInSequence(int start_page_num
, int num_pages
) {
80 // Distiller prefers distilling past pages first. E.g. when distillation
81 // starts on page 2 then pages are distilled in the order: 2, 1, 0, 3, 4.
82 vector
<int> page_nums
;
83 for (int page
= start_page_num
; page
>= 0; --page
)
84 page_nums
.push_back(page
);
85 for (int page
= start_page_num
+ 1; page
< num_pages
; ++page
)
86 page_nums
.push_back(page
);
90 struct MultipageDistillerData
{
92 MultipageDistillerData() {}
93 ~MultipageDistillerData() {}
94 vector
<string
> page_urls
;
95 vector
<string
> content
;
96 vector
<vector
<int> > image_ids
;
97 // The Javascript values returned by mock distiller.
98 ScopedVector
<base::Value
> distilled_values
;
101 DISALLOW_COPY_AND_ASSIGN(MultipageDistillerData
);
104 void VerifyIncrementalUpdatesMatch(
105 const MultipageDistillerData
* distiller_data
,
106 int num_pages_in_article
,
107 const vector
<dom_distiller::ArticleDistillationUpdate
>& incremental_updates
,
108 int start_page_num
) {
109 vector
<int> page_seq
=
110 GetPagesInSequence(start_page_num
, num_pages_in_article
);
111 // Updates should contain a list of pages. Pages in an update should be in
112 // the correct ascending page order regardless of |start_page_num|.
113 // E.g. if distillation starts at page 2 of a 3 page article, the updates
114 // will be [[2], [1, 2], [1, 2, 3]]. This example assumes that image fetches
115 // do not delay distillation of a page. There can be scenarios when image
116 // fetch delays distillation of a page (E.g. 1 is delayed due to image
117 // fetches so updates can be in this order [[2], [2,3], [1,2,3]].
118 for (size_t update_count
= 0; update_count
< incremental_updates
.size();
120 const dom_distiller::ArticleDistillationUpdate
& update
=
121 incremental_updates
[update_count
];
122 EXPECT_EQ(update_count
+ 1, update
.GetPagesSize());
124 vector
<int> expected_page_nums_in_update(
125 page_seq
.begin(), page_seq
.begin() + update
.GetPagesSize());
126 std::sort(expected_page_nums_in_update
.begin(),
127 expected_page_nums_in_update
.end());
129 // If we already got the first page then there is no previous page.
130 EXPECT_EQ((expected_page_nums_in_update
[0] != 0), update
.HasPrevPage());
132 // if we already got the last page then there is no next page.
134 (*expected_page_nums_in_update
.rbegin()) != num_pages_in_article
- 1,
135 update
.HasNextPage());
136 for (size_t j
= 0; j
< update
.GetPagesSize(); ++j
) {
137 int actual_page_num
= expected_page_nums_in_update
[j
];
138 EXPECT_EQ(distiller_data
->page_urls
[actual_page_num
],
139 update
.GetDistilledPage(j
).url());
140 EXPECT_EQ(distiller_data
->content
[actual_page_num
],
141 update
.GetDistilledPage(j
).html());
146 string
GenerateNextPageUrl(const std::string
& url_prefix
, size_t page_num
,
148 return page_num
+ 1 < pages_size
?
149 url_prefix
+ base::IntToString(page_num
+ 1) : "";
152 string
GeneratePrevPageUrl(const std::string
& url_prefix
, size_t page_num
) {
153 return page_num
> 0 ? url_prefix
+ base::IntToString(page_num
- 1) : "";
156 scoped_ptr
<MultipageDistillerData
> CreateMultipageDistillerDataWithoutImages(
158 scoped_ptr
<MultipageDistillerData
> result(new MultipageDistillerData());
159 string url_prefix
= kURL
;
160 for (size_t page_num
= 0; page_num
< pages_size
; ++page_num
) {
161 result
->page_urls
.push_back(url_prefix
+ base::IntToString(page_num
));
162 result
->content
.push_back("Content for page:" +
163 base::IntToString(page_num
));
164 result
->image_ids
.push_back(vector
<int>());
165 string next_page_url
=
166 GenerateNextPageUrl(url_prefix
, page_num
, pages_size
);
167 string prev_page_url
=
168 GeneratePrevPageUrl(url_prefix
, page_num
);
169 scoped_ptr
<base::Value
> distilled_value
=
170 CreateDistilledValueReturnedFromJS(kTitle
,
171 result
->content
[page_num
],
172 result
->image_ids
[page_num
],
175 result
->distilled_values
.push_back(distilled_value
.release());
177 return result
.Pass();
180 void VerifyArticleProtoMatchesMultipageData(
181 const dom_distiller::DistilledArticleProto
* article_proto
,
182 const MultipageDistillerData
* distiller_data
,
183 size_t distilled_pages_size
,
184 size_t total_pages_size
) {
185 ASSERT_EQ(distilled_pages_size
,
186 static_cast<size_t>(article_proto
->pages_size()));
187 EXPECT_EQ(kTitle
, article_proto
->title());
188 std::string url_prefix
= kURL
;
189 for (size_t page_num
= 0; page_num
< distilled_pages_size
; ++page_num
) {
190 const dom_distiller::DistilledPageProto
& page
=
191 article_proto
->pages(page_num
);
192 EXPECT_EQ(distiller_data
->content
[page_num
], page
.html());
193 EXPECT_EQ(distiller_data
->page_urls
[page_num
], page
.url());
194 EXPECT_EQ(distiller_data
->image_ids
[page_num
].size(),
195 static_cast<size_t>(page
.image_size()));
196 const vector
<int>& image_ids_for_page
= distiller_data
->image_ids
[page_num
];
197 for (size_t img_num
= 0; img_num
< image_ids_for_page
.size(); ++img_num
) {
198 EXPECT_EQ(kImageData
[image_ids_for_page
[img_num
]],
199 page
.image(img_num
).data());
200 EXPECT_EQ(GetImageName(page_num
+ 1, img_num
),
201 page
.image(img_num
).name());
203 std::string expected_next_page_url
=
204 GenerateNextPageUrl(url_prefix
, page_num
, total_pages_size
);
205 std::string expected_prev_page_url
=
206 GeneratePrevPageUrl(url_prefix
, page_num
);
207 EXPECT_EQ(expected_next_page_url
, page
.pagination_info().next_page());
208 EXPECT_EQ(expected_prev_page_url
, page
.pagination_info().prev_page());
209 EXPECT_FALSE(page
.pagination_info().has_canonical_page());
215 namespace dom_distiller
{
217 using test::MockDistillerPage
;
218 using test::MockDistillerPageFactory
;
220 class TestDistillerURLFetcher
: public DistillerURLFetcher
{
222 explicit TestDistillerURLFetcher(bool delay_fetch
)
223 : DistillerURLFetcher(NULL
), delay_fetch_(delay_fetch
) {
224 responses_
[kImageURLs
[0]] = string(kImageData
[0]);
225 responses_
[kImageURLs
[1]] = string(kImageData
[1]);
228 void FetchURL(const string
& url
,
229 const URLFetcherCallback
& callback
) override
{
230 ASSERT_FALSE(callback
.is_null());
232 callback_
= callback
;
238 void PostCallbackTask() {
239 ASSERT_TRUE(base::MessageLoop::current());
240 ASSERT_FALSE(callback_
.is_null());
241 base::ThreadTaskRunnerHandle::Get()->PostTask(
242 FROM_HERE
, base::Bind(callback_
, responses_
[url_
]));
246 std::map
<string
, string
> responses_
;
248 URLFetcherCallback callback_
;
252 class TestDistillerURLFetcherFactory
: public DistillerURLFetcherFactory
{
254 TestDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL
) {}
256 ~TestDistillerURLFetcherFactory() override
{}
257 DistillerURLFetcher
* CreateDistillerURLFetcher() const override
{
258 return new TestDistillerURLFetcher(false);
262 class MockDistillerURLFetcherFactory
: public DistillerURLFetcherFactory
{
264 MockDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL
) {}
265 virtual ~MockDistillerURLFetcherFactory() {}
267 MOCK_CONST_METHOD0(CreateDistillerURLFetcher
, DistillerURLFetcher
*());
270 class DistillerTest
: public testing::Test
{
272 ~DistillerTest() override
{}
274 void OnDistillArticleDone(scoped_ptr
<DistilledArticleProto
> proto
) {
275 article_proto_
= proto
.Pass();
278 void OnDistillArticleUpdate(const ArticleDistillationUpdate
& article_update
) {
279 in_sequence_updates_
.push_back(article_update
);
282 void DistillPage(const std::string
& url
,
283 scoped_ptr
<DistillerPage
> distiller_page
) {
284 distiller_
->DistillPage(GURL(url
),
285 distiller_page
.Pass(),
286 base::Bind(&DistillerTest::OnDistillArticleDone
,
287 base::Unretained(this)),
288 base::Bind(&DistillerTest::OnDistillArticleUpdate
,
289 base::Unretained(this)));
293 scoped_ptr
<DistillerImpl
> distiller_
;
294 scoped_ptr
<DistilledArticleProto
> article_proto_
;
295 std::vector
<ArticleDistillationUpdate
> in_sequence_updates_
;
296 MockDistillerPageFactory page_factory_
;
297 TestDistillerURLFetcherFactory url_fetcher_factory_
;
300 ACTION_P3(DistillerPageOnDistillationDone
, distiller_page
, url
, result
) {
301 distiller_page
->OnDistillationDone(url
, result
);
304 scoped_ptr
<DistillerPage
> CreateMockDistillerPage(const base::Value
* result
,
306 MockDistillerPage
* distiller_page
= new MockDistillerPage();
307 EXPECT_CALL(*distiller_page
, DistillPageImpl(url
, _
))
308 .WillOnce(DistillerPageOnDistillationDone(distiller_page
, url
, result
));
309 return scoped_ptr
<DistillerPage
>(distiller_page
).Pass();
312 scoped_ptr
<DistillerPage
> CreateMockDistillerPageWithPendingJSCallback(
313 MockDistillerPage
** distiller_page_ptr
,
315 MockDistillerPage
* distiller_page
= new MockDistillerPage();
316 *distiller_page_ptr
= distiller_page
;
317 EXPECT_CALL(*distiller_page
, DistillPageImpl(url
, _
));
318 return scoped_ptr
<DistillerPage
>(distiller_page
).Pass();
321 scoped_ptr
<DistillerPage
> CreateMockDistillerPages(
322 MultipageDistillerData
* distiller_data
,
324 int start_page_num
) {
325 MockDistillerPage
* distiller_page
= new MockDistillerPage();
327 testing::InSequence s
;
328 vector
<int> page_nums
= GetPagesInSequence(start_page_num
, pages_size
);
329 for (size_t page_num
= 0; page_num
< pages_size
; ++page_num
) {
330 int page
= page_nums
[page_num
];
331 GURL url
= GURL(distiller_data
->page_urls
[page
]);
332 EXPECT_CALL(*distiller_page
, DistillPageImpl(url
, _
))
333 .WillOnce(DistillerPageOnDistillationDone(
334 distiller_page
, url
, distiller_data
->distilled_values
[page
]));
337 return scoped_ptr
<DistillerPage
>(distiller_page
).Pass();
340 TEST_F(DistillerTest
, DistillPage
) {
341 base::MessageLoopForUI loop
;
342 scoped_ptr
<base::Value
> result
=
343 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, vector
<int>(), "");
345 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
346 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
347 base::MessageLoop::current()->RunUntilIdle();
348 EXPECT_EQ(kTitle
, article_proto_
->title());
349 ASSERT_EQ(article_proto_
->pages_size(), 1);
350 const DistilledPageProto
& first_page
= article_proto_
->pages(0);
351 EXPECT_EQ(kContent
, first_page
.html());
352 EXPECT_EQ(kURL
, first_page
.url());
355 TEST_F(DistillerTest
, DistillPageWithDebugInfo
) {
356 base::MessageLoopForUI loop
;
357 DomDistillerResult dd_result
;
358 dd_result
.mutable_debug_info()->set_log(kDebugLog
);
359 scoped_ptr
<base::Value
> result
=
360 dom_distiller::proto::json::DomDistillerResult::WriteToValue(dd_result
);
362 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
363 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
364 base::MessageLoop::current()->RunUntilIdle();
365 const DistilledPageProto
& first_page
= article_proto_
->pages(0);
366 EXPECT_EQ(kDebugLog
, first_page
.debug_info().log());
369 void SetTimingEntry(TimingEntry
* entry
, const std::string
& name
, double time
) {
370 entry
->set_name(name
);
371 entry
->set_time(time
);
374 TEST_F(DistillerTest
, DistillPageWithTimingInfo
) {
375 base::MessageLoopForUI loop
;
376 DomDistillerResult dd_result
;
377 dd_result
.mutable_timing_info()->set_total_time(1.0);
378 dd_result
.mutable_timing_info()->set_markup_parsing_time(2.0);
379 dd_result
.mutable_timing_info()->set_document_construction_time(3.0);
380 dd_result
.mutable_timing_info()->set_article_processing_time(4.0);
381 dd_result
.mutable_timing_info()->set_formatting_time(5.0);
383 dd_result
.mutable_timing_info()->add_other_times(), "time0", 6.0);
385 dd_result
.mutable_timing_info()->add_other_times(), "time1", 7.0);
386 scoped_ptr
<base::Value
> result
=
387 dom_distiller::proto::json::DomDistillerResult::WriteToValue(dd_result
);
389 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
390 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
391 base::MessageLoop::current()->RunUntilIdle();
392 const DistilledPageProto
& first_page
= article_proto_
->pages(0);
393 std::map
<std::string
, double> timings
;
394 for (int i
= 0; i
< first_page
.timing_info_size(); ++i
) {
395 DistilledPageProto::TimingInfo timing
= first_page
.timing_info(i
);
396 timings
[timing
.name()] = timing
.time();
398 EXPECT_EQ(7u, timings
.size());
399 EXPECT_EQ(1.0, timings
["total"]);
400 EXPECT_EQ(2.0, timings
["markup_parsing"]);
401 EXPECT_EQ(3.0, timings
["document_construction"]);
402 EXPECT_EQ(4.0, timings
["article_processing"]);
403 EXPECT_EQ(5.0, timings
["formatting"]);
404 EXPECT_EQ(6.0, timings
["time0"]);
405 EXPECT_EQ(7.0, timings
["time1"]);
408 TEST_F(DistillerTest
, DistillPageWithImages
) {
409 base::MessageLoopForUI loop
;
410 vector
<int> image_indices
;
411 image_indices
.push_back(0);
412 image_indices
.push_back(1);
413 image_indices
.push_back(2);
414 scoped_ptr
<base::Value
> result
=
415 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, image_indices
, "");
417 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
418 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
419 base::MessageLoop::current()->RunUntilIdle();
420 EXPECT_EQ(kTitle
, article_proto_
->title());
421 ASSERT_EQ(article_proto_
->pages_size(), 1);
422 const DistilledPageProto
& first_page
= article_proto_
->pages(0);
423 EXPECT_EQ(kContent
, first_page
.html());
424 EXPECT_EQ(kURL
, first_page
.url());
425 ASSERT_EQ(2, first_page
.image_size());
426 EXPECT_EQ(kImageData
[0], first_page
.image(0).data());
427 EXPECT_EQ(kImageURLs
[0], first_page
.image(0).url());
428 EXPECT_EQ(GetImageName(1, 0), first_page
.image(0).name());
429 EXPECT_EQ(kImageData
[1], first_page
.image(1).data());
430 EXPECT_EQ(kImageURLs
[1], first_page
.image(1).url());
431 EXPECT_EQ(GetImageName(1, 1), first_page
.image(1).name());
434 TEST_F(DistillerTest
, DistillMultiplePages
) {
435 base::MessageLoopForUI loop
;
436 const size_t kNumPages
= 8;
437 scoped_ptr
<MultipageDistillerData
> distiller_data
=
438 CreateMultipageDistillerDataWithoutImages(kNumPages
);
441 int next_image_number
= 0;
442 for (size_t page_num
= 0; page_num
< kNumPages
; ++page_num
) {
443 // Each page has different number of images.
444 size_t tot_images
= (page_num
+ kTotalImages
) % (kTotalImages
+ 1);
445 vector
<int> image_indices
;
446 for (size_t img_num
= 0; img_num
< tot_images
; img_num
++) {
447 image_indices
.push_back(next_image_number
);
448 next_image_number
= (next_image_number
+ 1) % kTotalImages
;
450 distiller_data
->image_ids
.push_back(image_indices
);
454 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
456 distiller_data
->page_urls
[0],
457 CreateMockDistillerPages(distiller_data
.get(), kNumPages
, 0).Pass());
458 base::MessageLoop::current()->RunUntilIdle();
459 VerifyArticleProtoMatchesMultipageData(
460 article_proto_
.get(), distiller_data
.get(), kNumPages
, kNumPages
);
463 TEST_F(DistillerTest
, DistillLinkLoop
) {
464 base::MessageLoopForUI loop
;
465 // Create a loop, the next page is same as the current page. This could
466 // happen if javascript misparses a next page link.
467 scoped_ptr
<base::Value
> result
=
468 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, vector
<int>(), kURL
);
470 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
471 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
472 base::MessageLoop::current()->RunUntilIdle();
473 EXPECT_EQ(kTitle
, article_proto_
->title());
474 EXPECT_EQ(article_proto_
->pages_size(), 1);
477 TEST_F(DistillerTest
, CheckMaxPageLimitExtraPage
) {
478 base::MessageLoopForUI loop
;
479 const size_t kMaxPagesInArticle
= 10;
480 scoped_ptr
<MultipageDistillerData
> distiller_data
=
481 CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle
);
483 // Note: Next page url of the last page of article is set. So distiller will
484 // try to do kMaxPagesInArticle + 1 calls if the max article limit does not
486 scoped_ptr
<base::Value
> last_page_data
=
487 CreateDistilledValueReturnedFromJS(
489 distiller_data
->content
[kMaxPagesInArticle
- 1],
492 distiller_data
->page_urls
[kMaxPagesInArticle
- 2]);
494 distiller_data
->distilled_values
.pop_back();
495 distiller_data
->distilled_values
.push_back(last_page_data
.release());
498 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
500 distiller_
->SetMaxNumPagesInArticle(kMaxPagesInArticle
);
502 DistillPage(distiller_data
->page_urls
[0],
503 CreateMockDistillerPages(
504 distiller_data
.get(), kMaxPagesInArticle
, 0).Pass());
505 base::MessageLoop::current()->RunUntilIdle();
506 EXPECT_EQ(kTitle
, article_proto_
->title());
507 EXPECT_EQ(kMaxPagesInArticle
,
508 static_cast<size_t>(article_proto_
->pages_size()));
511 TEST_F(DistillerTest
, CheckMaxPageLimitExactLimit
) {
512 base::MessageLoopForUI loop
;
513 const size_t kMaxPagesInArticle
= 10;
514 scoped_ptr
<MultipageDistillerData
> distiller_data
=
515 CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle
);
518 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
520 // Check if distilling an article with exactly the page limit works.
521 distiller_
->SetMaxNumPagesInArticle(kMaxPagesInArticle
);
523 DistillPage(distiller_data
->page_urls
[0],
524 CreateMockDistillerPages(
525 distiller_data
.get(), kMaxPagesInArticle
, 0).Pass());
526 base::MessageLoop::current()->RunUntilIdle();
527 EXPECT_EQ(kTitle
, article_proto_
->title());
528 EXPECT_EQ(kMaxPagesInArticle
,
529 static_cast<size_t>(article_proto_
->pages_size()));
532 TEST_F(DistillerTest
, SinglePageDistillationFailure
) {
533 base::MessageLoopForUI loop
;
534 // To simulate failure return a null value.
535 scoped_ptr
<base::Value
> null_value
= base::Value::CreateNullValue();
537 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
539 CreateMockDistillerPage(null_value
.get(), GURL(kURL
)).Pass());
540 base::MessageLoop::current()->RunUntilIdle();
541 EXPECT_EQ("", article_proto_
->title());
542 EXPECT_EQ(0, article_proto_
->pages_size());
545 TEST_F(DistillerTest
, MultiplePagesDistillationFailure
) {
546 base::MessageLoopForUI loop
;
547 const size_t kNumPages
= 8;
548 scoped_ptr
<MultipageDistillerData
> distiller_data
=
549 CreateMultipageDistillerDataWithoutImages(kNumPages
);
551 // The page number of the failed page.
552 size_t failed_page_num
= 3;
553 // reset distilled data of the failed page.
554 distiller_data
->distilled_values
.erase(
555 distiller_data
->distilled_values
.begin() + failed_page_num
);
556 distiller_data
->distilled_values
.insert(
557 distiller_data
->distilled_values
.begin() + failed_page_num
,
558 base::Value::CreateNullValue().release());
559 // Expect only calls till the failed page number.
561 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
562 DistillPage(distiller_data
->page_urls
[0],
563 CreateMockDistillerPages(
564 distiller_data
.get(), failed_page_num
+ 1, 0).Pass());
565 base::MessageLoop::current()->RunUntilIdle();
566 EXPECT_EQ(kTitle
, article_proto_
->title());
567 VerifyArticleProtoMatchesMultipageData(
568 article_proto_
.get(), distiller_data
.get(), failed_page_num
, kNumPages
);
571 TEST_F(DistillerTest
, DistillPreviousPage
) {
572 base::MessageLoopForUI loop
;
573 const size_t kNumPages
= 8;
575 // The page number of the article on which distillation starts.
576 int start_page_num
= 3;
577 scoped_ptr
<MultipageDistillerData
> distiller_data
=
578 CreateMultipageDistillerDataWithoutImages(kNumPages
);
581 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
582 DistillPage(distiller_data
->page_urls
[start_page_num
],
583 CreateMockDistillerPages(
584 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
585 base::MessageLoop::current()->RunUntilIdle();
586 VerifyArticleProtoMatchesMultipageData(
587 article_proto_
.get(), distiller_data
.get(), kNumPages
, kNumPages
);
590 TEST_F(DistillerTest
, IncrementalUpdates
) {
591 base::MessageLoopForUI loop
;
592 const size_t kNumPages
= 8;
594 // The page number of the article on which distillation starts.
595 int start_page_num
= 3;
596 scoped_ptr
<MultipageDistillerData
> distiller_data
=
597 CreateMultipageDistillerDataWithoutImages(kNumPages
);
600 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
601 DistillPage(distiller_data
->page_urls
[start_page_num
],
602 CreateMockDistillerPages(
603 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
604 base::MessageLoop::current()->RunUntilIdle();
605 EXPECT_EQ(kTitle
, article_proto_
->title());
606 ASSERT_EQ(kNumPages
, static_cast<size_t>(article_proto_
->pages_size()));
607 EXPECT_EQ(kNumPages
, in_sequence_updates_
.size());
609 VerifyIncrementalUpdatesMatch(
610 distiller_data
.get(), kNumPages
, in_sequence_updates_
, start_page_num
);
613 TEST_F(DistillerTest
, IncrementalUpdatesDoNotDeleteFinalArticle
) {
614 base::MessageLoopForUI loop
;
615 const size_t kNumPages
= 8;
616 int start_page_num
= 3;
617 scoped_ptr
<MultipageDistillerData
> distiller_data
=
618 CreateMultipageDistillerDataWithoutImages(kNumPages
);
621 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
622 DistillPage(distiller_data
->page_urls
[start_page_num
],
623 CreateMockDistillerPages(
624 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
625 base::MessageLoop::current()->RunUntilIdle();
626 EXPECT_EQ(kNumPages
, in_sequence_updates_
.size());
628 in_sequence_updates_
.clear();
630 // Should still be able to access article and pages.
631 VerifyArticleProtoMatchesMultipageData(
632 article_proto_
.get(), distiller_data
.get(), kNumPages
, kNumPages
);
635 TEST_F(DistillerTest
, DeletingArticleDoesNotInterfereWithUpdates
) {
636 base::MessageLoopForUI loop
;
637 const size_t kNumPages
= 8;
638 scoped_ptr
<MultipageDistillerData
> distiller_data
=
639 CreateMultipageDistillerDataWithoutImages(kNumPages
);
640 // The page number of the article on which distillation starts.
641 int start_page_num
= 3;
644 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
645 DistillPage(distiller_data
->page_urls
[start_page_num
],
646 CreateMockDistillerPages(
647 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
648 base::MessageLoop::current()->RunUntilIdle();
649 EXPECT_EQ(kNumPages
, in_sequence_updates_
.size());
650 EXPECT_EQ(kTitle
, article_proto_
->title());
651 ASSERT_EQ(kNumPages
, static_cast<size_t>(article_proto_
->pages_size()));
653 // Delete the article.
654 article_proto_
.reset();
655 VerifyIncrementalUpdatesMatch(
656 distiller_data
.get(), kNumPages
, in_sequence_updates_
, start_page_num
);
659 TEST_F(DistillerTest
, CancelWithDelayedImageFetchCallback
) {
660 base::MessageLoopForUI loop
;
661 vector
<int> image_indices
;
662 image_indices
.push_back(0);
663 scoped_ptr
<base::Value
> distilled_value
=
664 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, image_indices
, "");
665 TestDistillerURLFetcher
* delayed_fetcher
= new TestDistillerURLFetcher(true);
666 MockDistillerURLFetcherFactory mock_url_fetcher_factory
;
667 EXPECT_CALL(mock_url_fetcher_factory
, CreateDistillerURLFetcher())
668 .WillOnce(Return(delayed_fetcher
));
670 new DistillerImpl(mock_url_fetcher_factory
, DomDistillerOptions()));
672 kURL
, CreateMockDistillerPage(distilled_value
.get(), GURL(kURL
)).Pass());
673 base::MessageLoop::current()->RunUntilIdle();
675 // Post callback from the url fetcher and then delete the distiller.
676 delayed_fetcher
->PostCallbackTask();
679 base::MessageLoop::current()->RunUntilIdle();
682 TEST_F(DistillerTest
, CancelWithDelayedJSCallback
) {
683 base::MessageLoopForUI loop
;
684 scoped_ptr
<base::Value
> distilled_value
=
685 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, vector
<int>(), "");
686 MockDistillerPage
* distiller_page
= NULL
;
688 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
690 CreateMockDistillerPageWithPendingJSCallback(&distiller_page
,
692 base::MessageLoop::current()->RunUntilIdle();
694 ASSERT_TRUE(distiller_page
);
695 // Post the task to execute javascript and then delete the distiller.
696 distiller_page
->OnDistillationDone(GURL(kURL
), distilled_value
.get());
699 base::MessageLoop::current()->RunUntilIdle();
702 } // namespace dom_distiller