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/strings/string_number_conversions.h"
16 #include "base/values.h"
17 #include "components/dom_distiller/core/article_distillation_update.h"
18 #include "components/dom_distiller/core/distiller.h"
19 #include "components/dom_distiller/core/distiller_page.h"
20 #include "components/dom_distiller/core/fake_distiller_page.h"
21 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
22 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
23 #include "net/url_request/url_request_context_getter.h"
24 #include "testing/gmock/include/gmock/gmock.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "third_party/dom_distiller_js/dom_distiller.pb.h"
27 #include "third_party/dom_distiller_js/dom_distiller_json_converter.h"
31 using ::testing::Invoke
;
32 using ::testing::Return
;
35 using dom_distiller::proto::DomDistillerOptions
;
36 using dom_distiller::proto::DomDistillerResult
;
37 using dom_distiller::proto::DomDistillerResult_ContentImage
;
38 using dom_distiller::proto::TimingEntry
;
41 const char kTitle
[] = "Title";
42 const char kContent
[] = "Content";
43 const char kURL
[] = "http://a.com/";
44 const size_t kTotalImages
= 3;
45 const char* kImageURLs
[kTotalImages
] = {"http://a.com/img1.jpg",
46 "http://a.com/img2.jpg",
47 "./bad_url_should_fail"};
48 const char* kImageData
[kTotalImages
] = {"abcde", "12345", "VWXYZ"};
49 const char kDebugLog
[] = "Debug Log";
51 const string
GetImageName(int page_num
, int image_num
) {
52 return base::IntToString(page_num
) + "_" + base::IntToString(image_num
);
55 scoped_ptr
<base::Value
> CreateDistilledValueReturnedFromJS(
57 const string
& content
,
58 const vector
<int>& image_indices
,
59 const string
& next_page_url
,
60 const string
& prev_page_url
= "") {
61 DomDistillerResult result
;
62 result
.set_title(title
);
63 result
.mutable_distilled_content()->set_html(content
);
64 result
.mutable_pagination_info()->set_next_page(next_page_url
);
65 result
.mutable_pagination_info()->set_prev_page(prev_page_url
);
67 for (size_t i
= 0; i
< image_indices
.size(); ++i
) {
68 DomDistillerResult_ContentImage
* curr_image
= result
.add_content_images();
69 curr_image
->set_url(kImageURLs
[image_indices
[i
]]);
72 return dom_distiller::proto::json::DomDistillerResult::WriteToValue(result
);
75 // Return the sequence in which Distiller will distill pages.
76 // Note: ignores any delays due to fetching images etc.
77 vector
<int> GetPagesInSequence(int start_page_num
, int num_pages
) {
78 // Distiller prefers distilling past pages first. E.g. when distillation
79 // starts on page 2 then pages are distilled in the order: 2, 1, 0, 3, 4.
80 vector
<int> page_nums
;
81 for (int page
= start_page_num
; page
>= 0; --page
)
82 page_nums
.push_back(page
);
83 for (int page
= start_page_num
+ 1; page
< num_pages
; ++page
)
84 page_nums
.push_back(page
);
88 struct MultipageDistillerData
{
90 MultipageDistillerData() {}
91 ~MultipageDistillerData() {}
92 vector
<string
> page_urls
;
93 vector
<string
> content
;
94 vector
<vector
<int> > image_ids
;
95 // The Javascript values returned by mock distiller.
96 ScopedVector
<base::Value
> distilled_values
;
99 DISALLOW_COPY_AND_ASSIGN(MultipageDistillerData
);
102 void VerifyIncrementalUpdatesMatch(
103 const MultipageDistillerData
* distiller_data
,
104 int num_pages_in_article
,
105 const vector
<dom_distiller::ArticleDistillationUpdate
>& incremental_updates
,
106 int start_page_num
) {
107 vector
<int> page_seq
=
108 GetPagesInSequence(start_page_num
, num_pages_in_article
);
109 // Updates should contain a list of pages. Pages in an update should be in
110 // the correct ascending page order regardless of |start_page_num|.
111 // E.g. if distillation starts at page 2 of a 3 page article, the updates
112 // will be [[2], [1, 2], [1, 2, 3]]. This example assumes that image fetches
113 // do not delay distillation of a page. There can be scenarios when image
114 // fetch delays distillation of a page (E.g. 1 is delayed due to image
115 // fetches so updates can be in this order [[2], [2,3], [1,2,3]].
116 for (size_t update_count
= 0; update_count
< incremental_updates
.size();
118 const dom_distiller::ArticleDistillationUpdate
& update
=
119 incremental_updates
[update_count
];
120 EXPECT_EQ(update_count
+ 1, update
.GetPagesSize());
122 vector
<int> expected_page_nums_in_update(
123 page_seq
.begin(), page_seq
.begin() + update
.GetPagesSize());
124 std::sort(expected_page_nums_in_update
.begin(),
125 expected_page_nums_in_update
.end());
127 // If we already got the first page then there is no previous page.
128 EXPECT_EQ((expected_page_nums_in_update
[0] != 0), update
.HasPrevPage());
130 // if we already got the last page then there is no next page.
132 (*expected_page_nums_in_update
.rbegin()) != num_pages_in_article
- 1,
133 update
.HasNextPage());
134 for (size_t j
= 0; j
< update
.GetPagesSize(); ++j
) {
135 int actual_page_num
= expected_page_nums_in_update
[j
];
136 EXPECT_EQ(distiller_data
->page_urls
[actual_page_num
],
137 update
.GetDistilledPage(j
).url());
138 EXPECT_EQ(distiller_data
->content
[actual_page_num
],
139 update
.GetDistilledPage(j
).html());
144 string
GenerateNextPageUrl(const std::string
& url_prefix
, size_t page_num
,
146 return page_num
+ 1 < pages_size
?
147 url_prefix
+ base::IntToString(page_num
+ 1) : "";
150 string
GeneratePrevPageUrl(const std::string
& url_prefix
, size_t page_num
) {
151 return page_num
> 0 ? url_prefix
+ base::IntToString(page_num
- 1) : "";
154 scoped_ptr
<MultipageDistillerData
> CreateMultipageDistillerDataWithoutImages(
156 scoped_ptr
<MultipageDistillerData
> result(new MultipageDistillerData());
157 string url_prefix
= kURL
;
158 for (size_t page_num
= 0; page_num
< pages_size
; ++page_num
) {
159 result
->page_urls
.push_back(url_prefix
+ base::IntToString(page_num
));
160 result
->content
.push_back("Content for page:" +
161 base::IntToString(page_num
));
162 result
->image_ids
.push_back(vector
<int>());
163 string next_page_url
=
164 GenerateNextPageUrl(url_prefix
, page_num
, pages_size
);
165 string prev_page_url
=
166 GeneratePrevPageUrl(url_prefix
, page_num
);
167 scoped_ptr
<base::Value
> distilled_value
=
168 CreateDistilledValueReturnedFromJS(kTitle
,
169 result
->content
[page_num
],
170 result
->image_ids
[page_num
],
173 result
->distilled_values
.push_back(distilled_value
.release());
175 return result
.Pass();
178 void VerifyArticleProtoMatchesMultipageData(
179 const dom_distiller::DistilledArticleProto
* article_proto
,
180 const MultipageDistillerData
* distiller_data
,
181 size_t distilled_pages_size
,
182 size_t total_pages_size
) {
183 ASSERT_EQ(distilled_pages_size
,
184 static_cast<size_t>(article_proto
->pages_size()));
185 EXPECT_EQ(kTitle
, article_proto
->title());
186 std::string url_prefix
= kURL
;
187 for (size_t page_num
= 0; page_num
< distilled_pages_size
; ++page_num
) {
188 const dom_distiller::DistilledPageProto
& page
=
189 article_proto
->pages(page_num
);
190 EXPECT_EQ(distiller_data
->content
[page_num
], page
.html());
191 EXPECT_EQ(distiller_data
->page_urls
[page_num
], page
.url());
192 EXPECT_EQ(distiller_data
->image_ids
[page_num
].size(),
193 static_cast<size_t>(page
.image_size()));
194 const vector
<int>& image_ids_for_page
= distiller_data
->image_ids
[page_num
];
195 for (size_t img_num
= 0; img_num
< image_ids_for_page
.size(); ++img_num
) {
196 EXPECT_EQ(kImageData
[image_ids_for_page
[img_num
]],
197 page
.image(img_num
).data());
198 EXPECT_EQ(GetImageName(page_num
+ 1, img_num
),
199 page
.image(img_num
).name());
201 std::string expected_next_page_url
=
202 GenerateNextPageUrl(url_prefix
, page_num
, total_pages_size
);
203 std::string expected_prev_page_url
=
204 GeneratePrevPageUrl(url_prefix
, page_num
);
205 EXPECT_EQ(expected_next_page_url
, page
.pagination_info().next_page());
206 EXPECT_EQ(expected_prev_page_url
, page
.pagination_info().prev_page());
207 EXPECT_FALSE(page
.pagination_info().has_canonical_page());
213 namespace dom_distiller
{
215 using test::MockDistillerPage
;
216 using test::MockDistillerPageFactory
;
218 class TestDistillerURLFetcher
: public DistillerURLFetcher
{
220 explicit TestDistillerURLFetcher(bool delay_fetch
)
221 : DistillerURLFetcher(NULL
), delay_fetch_(delay_fetch
) {
222 responses_
[kImageURLs
[0]] = string(kImageData
[0]);
223 responses_
[kImageURLs
[1]] = string(kImageData
[1]);
226 void FetchURL(const string
& url
,
227 const URLFetcherCallback
& callback
) override
{
228 ASSERT_FALSE(callback
.is_null());
230 callback_
= callback
;
236 void PostCallbackTask() {
237 ASSERT_TRUE(base::MessageLoop::current());
238 ASSERT_FALSE(callback_
.is_null());
239 base::MessageLoop::current()->PostTask(
240 FROM_HERE
, base::Bind(callback_
, responses_
[url_
]));
244 std::map
<string
, string
> responses_
;
246 URLFetcherCallback callback_
;
250 class TestDistillerURLFetcherFactory
: public DistillerURLFetcherFactory
{
252 TestDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL
) {}
254 ~TestDistillerURLFetcherFactory() override
{}
255 DistillerURLFetcher
* CreateDistillerURLFetcher() const override
{
256 return new TestDistillerURLFetcher(false);
260 class MockDistillerURLFetcherFactory
: public DistillerURLFetcherFactory
{
262 MockDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL
) {}
263 virtual ~MockDistillerURLFetcherFactory() {}
265 MOCK_CONST_METHOD0(CreateDistillerURLFetcher
, DistillerURLFetcher
*());
268 class DistillerTest
: public testing::Test
{
270 ~DistillerTest() override
{}
272 void OnDistillArticleDone(scoped_ptr
<DistilledArticleProto
> proto
) {
273 article_proto_
= proto
.Pass();
276 void OnDistillArticleUpdate(const ArticleDistillationUpdate
& article_update
) {
277 in_sequence_updates_
.push_back(article_update
);
280 void DistillPage(const std::string
& url
,
281 scoped_ptr
<DistillerPage
> distiller_page
) {
282 distiller_
->DistillPage(GURL(url
),
283 distiller_page
.Pass(),
284 base::Bind(&DistillerTest::OnDistillArticleDone
,
285 base::Unretained(this)),
286 base::Bind(&DistillerTest::OnDistillArticleUpdate
,
287 base::Unretained(this)));
291 scoped_ptr
<DistillerImpl
> distiller_
;
292 scoped_ptr
<DistilledArticleProto
> article_proto_
;
293 std::vector
<ArticleDistillationUpdate
> in_sequence_updates_
;
294 MockDistillerPageFactory page_factory_
;
295 TestDistillerURLFetcherFactory url_fetcher_factory_
;
298 ACTION_P3(DistillerPageOnDistillationDone
, distiller_page
, url
, result
) {
299 distiller_page
->OnDistillationDone(url
, result
);
302 scoped_ptr
<DistillerPage
> CreateMockDistillerPage(const base::Value
* result
,
304 MockDistillerPage
* distiller_page
= new MockDistillerPage();
305 EXPECT_CALL(*distiller_page
, DistillPageImpl(url
, _
))
306 .WillOnce(DistillerPageOnDistillationDone(distiller_page
, url
, result
));
307 return scoped_ptr
<DistillerPage
>(distiller_page
).Pass();
310 scoped_ptr
<DistillerPage
> CreateMockDistillerPageWithPendingJSCallback(
311 MockDistillerPage
** distiller_page_ptr
,
313 MockDistillerPage
* distiller_page
= new MockDistillerPage();
314 *distiller_page_ptr
= distiller_page
;
315 EXPECT_CALL(*distiller_page
, DistillPageImpl(url
, _
));
316 return scoped_ptr
<DistillerPage
>(distiller_page
).Pass();
319 scoped_ptr
<DistillerPage
> CreateMockDistillerPages(
320 MultipageDistillerData
* distiller_data
,
322 int start_page_num
) {
323 MockDistillerPage
* distiller_page
= new MockDistillerPage();
325 testing::InSequence s
;
326 vector
<int> page_nums
= GetPagesInSequence(start_page_num
, pages_size
);
327 for (size_t page_num
= 0; page_num
< pages_size
; ++page_num
) {
328 int page
= page_nums
[page_num
];
329 GURL url
= GURL(distiller_data
->page_urls
[page
]);
330 EXPECT_CALL(*distiller_page
, DistillPageImpl(url
, _
))
331 .WillOnce(DistillerPageOnDistillationDone(
332 distiller_page
, url
, distiller_data
->distilled_values
[page
]));
335 return scoped_ptr
<DistillerPage
>(distiller_page
).Pass();
338 TEST_F(DistillerTest
, DistillPage
) {
339 base::MessageLoopForUI loop
;
340 scoped_ptr
<base::Value
> result
=
341 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, vector
<int>(), "");
343 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
344 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
345 base::MessageLoop::current()->RunUntilIdle();
346 EXPECT_EQ(kTitle
, article_proto_
->title());
347 ASSERT_EQ(article_proto_
->pages_size(), 1);
348 const DistilledPageProto
& first_page
= article_proto_
->pages(0);
349 EXPECT_EQ(kContent
, first_page
.html());
350 EXPECT_EQ(kURL
, first_page
.url());
353 TEST_F(DistillerTest
, DistillPageWithDebugInfo
) {
354 base::MessageLoopForUI loop
;
355 DomDistillerResult dd_result
;
356 dd_result
.mutable_debug_info()->set_log(kDebugLog
);
357 scoped_ptr
<base::Value
> result
=
358 dom_distiller::proto::json::DomDistillerResult::WriteToValue(dd_result
);
360 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
361 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
362 base::MessageLoop::current()->RunUntilIdle();
363 const DistilledPageProto
& first_page
= article_proto_
->pages(0);
364 EXPECT_EQ(kDebugLog
, first_page
.debug_info().log());
367 void SetTimingEntry(TimingEntry
* entry
, const std::string
& name
, double time
) {
368 entry
->set_name(name
);
369 entry
->set_time(time
);
372 TEST_F(DistillerTest
, DistillPageWithTimingInfo
) {
373 base::MessageLoopForUI loop
;
374 DomDistillerResult dd_result
;
375 dd_result
.mutable_timing_info()->set_total_time(1.0);
376 dd_result
.mutable_timing_info()->set_markup_parsing_time(2.0);
377 dd_result
.mutable_timing_info()->set_document_construction_time(3.0);
378 dd_result
.mutable_timing_info()->set_article_processing_time(4.0);
379 dd_result
.mutable_timing_info()->set_formatting_time(5.0);
381 dd_result
.mutable_timing_info()->add_other_times(), "time0", 6.0);
383 dd_result
.mutable_timing_info()->add_other_times(), "time1", 7.0);
384 scoped_ptr
<base::Value
> result
=
385 dom_distiller::proto::json::DomDistillerResult::WriteToValue(dd_result
);
387 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
388 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
389 base::MessageLoop::current()->RunUntilIdle();
390 const DistilledPageProto
& first_page
= article_proto_
->pages(0);
391 std::map
<std::string
, double> timings
;
392 for (int i
= 0; i
< first_page
.timing_info_size(); ++i
) {
393 DistilledPageProto::TimingInfo timing
= first_page
.timing_info(i
);
394 timings
[timing
.name()] = timing
.time();
396 EXPECT_EQ(7u, timings
.size());
397 EXPECT_EQ(1.0, timings
["total"]);
398 EXPECT_EQ(2.0, timings
["markup_parsing"]);
399 EXPECT_EQ(3.0, timings
["document_construction"]);
400 EXPECT_EQ(4.0, timings
["article_processing"]);
401 EXPECT_EQ(5.0, timings
["formatting"]);
402 EXPECT_EQ(6.0, timings
["time0"]);
403 EXPECT_EQ(7.0, timings
["time1"]);
406 TEST_F(DistillerTest
, DistillPageWithImages
) {
407 base::MessageLoopForUI loop
;
408 vector
<int> image_indices
;
409 image_indices
.push_back(0);
410 image_indices
.push_back(1);
411 image_indices
.push_back(2);
412 scoped_ptr
<base::Value
> result
=
413 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, image_indices
, "");
415 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
416 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
417 base::MessageLoop::current()->RunUntilIdle();
418 EXPECT_EQ(kTitle
, article_proto_
->title());
419 ASSERT_EQ(article_proto_
->pages_size(), 1);
420 const DistilledPageProto
& first_page
= article_proto_
->pages(0);
421 EXPECT_EQ(kContent
, first_page
.html());
422 EXPECT_EQ(kURL
, first_page
.url());
423 ASSERT_EQ(2, first_page
.image_size());
424 EXPECT_EQ(kImageData
[0], first_page
.image(0).data());
425 EXPECT_EQ(kImageURLs
[0], first_page
.image(0).url());
426 EXPECT_EQ(GetImageName(1, 0), first_page
.image(0).name());
427 EXPECT_EQ(kImageData
[1], first_page
.image(1).data());
428 EXPECT_EQ(kImageURLs
[1], first_page
.image(1).url());
429 EXPECT_EQ(GetImageName(1, 1), first_page
.image(1).name());
432 TEST_F(DistillerTest
, DistillMultiplePages
) {
433 base::MessageLoopForUI loop
;
434 const size_t kNumPages
= 8;
435 scoped_ptr
<MultipageDistillerData
> distiller_data
=
436 CreateMultipageDistillerDataWithoutImages(kNumPages
);
439 int next_image_number
= 0;
440 for (size_t page_num
= 0; page_num
< kNumPages
; ++page_num
) {
441 // Each page has different number of images.
442 size_t tot_images
= (page_num
+ kTotalImages
) % (kTotalImages
+ 1);
443 vector
<int> image_indices
;
444 for (size_t img_num
= 0; img_num
< tot_images
; img_num
++) {
445 image_indices
.push_back(next_image_number
);
446 next_image_number
= (next_image_number
+ 1) % kTotalImages
;
448 distiller_data
->image_ids
.push_back(image_indices
);
452 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
454 distiller_data
->page_urls
[0],
455 CreateMockDistillerPages(distiller_data
.get(), kNumPages
, 0).Pass());
456 base::MessageLoop::current()->RunUntilIdle();
457 VerifyArticleProtoMatchesMultipageData(
458 article_proto_
.get(), distiller_data
.get(), kNumPages
, kNumPages
);
461 TEST_F(DistillerTest
, DistillLinkLoop
) {
462 base::MessageLoopForUI loop
;
463 // Create a loop, the next page is same as the current page. This could
464 // happen if javascript misparses a next page link.
465 scoped_ptr
<base::Value
> result
=
466 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, vector
<int>(), kURL
);
468 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
469 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
470 base::MessageLoop::current()->RunUntilIdle();
471 EXPECT_EQ(kTitle
, article_proto_
->title());
472 EXPECT_EQ(article_proto_
->pages_size(), 1);
475 TEST_F(DistillerTest
, CheckMaxPageLimitExtraPage
) {
476 base::MessageLoopForUI loop
;
477 const size_t kMaxPagesInArticle
= 10;
478 scoped_ptr
<MultipageDistillerData
> distiller_data
=
479 CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle
);
481 // Note: Next page url of the last page of article is set. So distiller will
482 // try to do kMaxPagesInArticle + 1 calls if the max article limit does not
484 scoped_ptr
<base::Value
> last_page_data
=
485 CreateDistilledValueReturnedFromJS(
487 distiller_data
->content
[kMaxPagesInArticle
- 1],
490 distiller_data
->page_urls
[kMaxPagesInArticle
- 2]);
492 distiller_data
->distilled_values
.pop_back();
493 distiller_data
->distilled_values
.push_back(last_page_data
.release());
496 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
498 distiller_
->SetMaxNumPagesInArticle(kMaxPagesInArticle
);
500 DistillPage(distiller_data
->page_urls
[0],
501 CreateMockDistillerPages(
502 distiller_data
.get(), kMaxPagesInArticle
, 0).Pass());
503 base::MessageLoop::current()->RunUntilIdle();
504 EXPECT_EQ(kTitle
, article_proto_
->title());
505 EXPECT_EQ(kMaxPagesInArticle
,
506 static_cast<size_t>(article_proto_
->pages_size()));
509 TEST_F(DistillerTest
, CheckMaxPageLimitExactLimit
) {
510 base::MessageLoopForUI loop
;
511 const size_t kMaxPagesInArticle
= 10;
512 scoped_ptr
<MultipageDistillerData
> distiller_data
=
513 CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle
);
516 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
518 // Check if distilling an article with exactly the page limit works.
519 distiller_
->SetMaxNumPagesInArticle(kMaxPagesInArticle
);
521 DistillPage(distiller_data
->page_urls
[0],
522 CreateMockDistillerPages(
523 distiller_data
.get(), kMaxPagesInArticle
, 0).Pass());
524 base::MessageLoop::current()->RunUntilIdle();
525 EXPECT_EQ(kTitle
, article_proto_
->title());
526 EXPECT_EQ(kMaxPagesInArticle
,
527 static_cast<size_t>(article_proto_
->pages_size()));
530 TEST_F(DistillerTest
, SinglePageDistillationFailure
) {
531 base::MessageLoopForUI loop
;
532 // To simulate failure return a null value.
533 scoped_ptr
<base::Value
> nullValue(base::Value::CreateNullValue());
535 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
537 CreateMockDistillerPage(nullValue
.get(), GURL(kURL
)).Pass());
538 base::MessageLoop::current()->RunUntilIdle();
539 EXPECT_EQ("", article_proto_
->title());
540 EXPECT_EQ(0, article_proto_
->pages_size());
543 TEST_F(DistillerTest
, MultiplePagesDistillationFailure
) {
544 base::MessageLoopForUI loop
;
545 const size_t kNumPages
= 8;
546 scoped_ptr
<MultipageDistillerData
> distiller_data
=
547 CreateMultipageDistillerDataWithoutImages(kNumPages
);
549 // The page number of the failed page.
550 size_t failed_page_num
= 3;
551 // reset distilled data of the failed page.
552 distiller_data
->distilled_values
.erase(
553 distiller_data
->distilled_values
.begin() + failed_page_num
);
554 distiller_data
->distilled_values
.insert(
555 distiller_data
->distilled_values
.begin() + failed_page_num
,
556 base::Value::CreateNullValue());
557 // Expect only calls till the failed page number.
559 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
560 DistillPage(distiller_data
->page_urls
[0],
561 CreateMockDistillerPages(
562 distiller_data
.get(), failed_page_num
+ 1, 0).Pass());
563 base::MessageLoop::current()->RunUntilIdle();
564 EXPECT_EQ(kTitle
, article_proto_
->title());
565 VerifyArticleProtoMatchesMultipageData(
566 article_proto_
.get(), distiller_data
.get(), failed_page_num
, kNumPages
);
569 TEST_F(DistillerTest
, DistillPreviousPage
) {
570 base::MessageLoopForUI loop
;
571 const size_t kNumPages
= 8;
573 // The page number of the article on which distillation starts.
574 int start_page_num
= 3;
575 scoped_ptr
<MultipageDistillerData
> distiller_data
=
576 CreateMultipageDistillerDataWithoutImages(kNumPages
);
579 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
580 DistillPage(distiller_data
->page_urls
[start_page_num
],
581 CreateMockDistillerPages(
582 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
583 base::MessageLoop::current()->RunUntilIdle();
584 VerifyArticleProtoMatchesMultipageData(
585 article_proto_
.get(), distiller_data
.get(), kNumPages
, kNumPages
);
588 TEST_F(DistillerTest
, IncrementalUpdates
) {
589 base::MessageLoopForUI loop
;
590 const size_t kNumPages
= 8;
592 // The page number of the article on which distillation starts.
593 int start_page_num
= 3;
594 scoped_ptr
<MultipageDistillerData
> distiller_data
=
595 CreateMultipageDistillerDataWithoutImages(kNumPages
);
598 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
599 DistillPage(distiller_data
->page_urls
[start_page_num
],
600 CreateMockDistillerPages(
601 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
602 base::MessageLoop::current()->RunUntilIdle();
603 EXPECT_EQ(kTitle
, article_proto_
->title());
604 ASSERT_EQ(kNumPages
, static_cast<size_t>(article_proto_
->pages_size()));
605 EXPECT_EQ(kNumPages
, in_sequence_updates_
.size());
607 VerifyIncrementalUpdatesMatch(
608 distiller_data
.get(), kNumPages
, in_sequence_updates_
, start_page_num
);
611 TEST_F(DistillerTest
, IncrementalUpdatesDoNotDeleteFinalArticle
) {
612 base::MessageLoopForUI loop
;
613 const size_t kNumPages
= 8;
614 int start_page_num
= 3;
615 scoped_ptr
<MultipageDistillerData
> distiller_data
=
616 CreateMultipageDistillerDataWithoutImages(kNumPages
);
619 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
620 DistillPage(distiller_data
->page_urls
[start_page_num
],
621 CreateMockDistillerPages(
622 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
623 base::MessageLoop::current()->RunUntilIdle();
624 EXPECT_EQ(kNumPages
, in_sequence_updates_
.size());
626 in_sequence_updates_
.clear();
628 // Should still be able to access article and pages.
629 VerifyArticleProtoMatchesMultipageData(
630 article_proto_
.get(), distiller_data
.get(), kNumPages
, kNumPages
);
633 TEST_F(DistillerTest
, DeletingArticleDoesNotInterfereWithUpdates
) {
634 base::MessageLoopForUI loop
;
635 const size_t kNumPages
= 8;
636 scoped_ptr
<MultipageDistillerData
> distiller_data
=
637 CreateMultipageDistillerDataWithoutImages(kNumPages
);
638 // The page number of the article on which distillation starts.
639 int start_page_num
= 3;
642 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
643 DistillPage(distiller_data
->page_urls
[start_page_num
],
644 CreateMockDistillerPages(
645 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
646 base::MessageLoop::current()->RunUntilIdle();
647 EXPECT_EQ(kNumPages
, in_sequence_updates_
.size());
648 EXPECT_EQ(kTitle
, article_proto_
->title());
649 ASSERT_EQ(kNumPages
, static_cast<size_t>(article_proto_
->pages_size()));
651 // Delete the article.
652 article_proto_
.reset();
653 VerifyIncrementalUpdatesMatch(
654 distiller_data
.get(), kNumPages
, in_sequence_updates_
, start_page_num
);
657 TEST_F(DistillerTest
, CancelWithDelayedImageFetchCallback
) {
658 base::MessageLoopForUI loop
;
659 vector
<int> image_indices
;
660 image_indices
.push_back(0);
661 scoped_ptr
<base::Value
> distilled_value
=
662 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, image_indices
, "");
663 TestDistillerURLFetcher
* delayed_fetcher
= new TestDistillerURLFetcher(true);
664 MockDistillerURLFetcherFactory mock_url_fetcher_factory
;
665 EXPECT_CALL(mock_url_fetcher_factory
, CreateDistillerURLFetcher())
666 .WillOnce(Return(delayed_fetcher
));
668 new DistillerImpl(mock_url_fetcher_factory
, DomDistillerOptions()));
670 kURL
, CreateMockDistillerPage(distilled_value
.get(), GURL(kURL
)).Pass());
671 base::MessageLoop::current()->RunUntilIdle();
673 // Post callback from the url fetcher and then delete the distiller.
674 delayed_fetcher
->PostCallbackTask();
677 base::MessageLoop::current()->RunUntilIdle();
680 TEST_F(DistillerTest
, CancelWithDelayedJSCallback
) {
681 base::MessageLoopForUI loop
;
682 scoped_ptr
<base::Value
> distilled_value
=
683 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, vector
<int>(), "");
684 MockDistillerPage
* distiller_page
= NULL
;
686 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
688 CreateMockDistillerPageWithPendingJSCallback(&distiller_page
,
690 base::MessageLoop::current()->RunUntilIdle();
692 ASSERT_TRUE(distiller_page
);
693 // Post the task to execute javascript and then delete the distiller.
694 distiller_page
->OnDistillationDone(GURL(kURL
), distilled_value
.get());
697 base::MessageLoop::current()->RunUntilIdle();
700 } // namespace dom_distiller