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/path_service.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/values.h"
18 #include "components/dom_distiller/core/article_distillation_update.h"
19 #include "components/dom_distiller/core/distiller.h"
20 #include "components/dom_distiller/core/distiller_page.h"
21 #include "components/dom_distiller/core/fake_distiller_page.h"
22 #include "components/dom_distiller/core/proto/distilled_article.pb.h"
23 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
24 #include "net/url_request/url_request_context_getter.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #include "third_party/dom_distiller_js/dom_distiller.pb.h"
28 #include "third_party/dom_distiller_js/dom_distiller_json_converter.h"
29 #include "ui/base/resource/resource_bundle.h"
33 using ::testing::Invoke
;
34 using ::testing::Return
;
37 using dom_distiller::proto::DomDistillerOptions
;
38 using dom_distiller::proto::DomDistillerResult
;
41 const char kTitle
[] = "Title";
42 const char kContent
[] = "Content";
43 const char kURL
[] = "http://a.com/";
44 const size_t kTotalImages
= 2;
45 const char* kImageURLs
[kTotalImages
] = {"http://a.com/img1.jpg",
46 "http://a.com/img2.jpg"};
47 const char* kImageData
[kTotalImages
] = {"abcde", "12345"};
49 const string
GetImageName(int page_num
, int image_num
) {
50 return base::IntToString(page_num
) + "_" + base::IntToString(image_num
);
53 scoped_ptr
<base::Value
> CreateDistilledValueReturnedFromJS(
55 const string
& content
,
56 const vector
<int>& image_indices
,
57 const string
& next_page_url
,
58 const string
& prev_page_url
= "") {
59 DomDistillerResult result
;
60 result
.set_title(title
);
61 result
.mutable_distilled_content()->set_html(content
);
62 result
.mutable_pagination_info()->set_next_page(next_page_url
);
63 result
.mutable_pagination_info()->set_prev_page(prev_page_url
);
65 for (size_t i
= 0; i
< image_indices
.size(); ++i
) {
66 result
.add_image_urls(kImageURLs
[image_indices
[i
]]);
69 return dom_distiller::proto::json::DomDistillerResult::WriteToValue(result
);
72 // Return the sequence in which Distiller will distill pages.
73 // Note: ignores any delays due to fetching images etc.
74 vector
<int> GetPagesInSequence(int start_page_num
, int num_pages
) {
75 // Distiller prefers distilling past pages first. E.g. when distillation
76 // starts on page 2 then pages are distilled in the order: 2, 1, 0, 3, 4.
77 vector
<int> page_nums
;
78 for (int page
= start_page_num
; page
>= 0; --page
)
79 page_nums
.push_back(page
);
80 for (int page
= start_page_num
+ 1; page
< num_pages
; ++page
)
81 page_nums
.push_back(page
);
85 struct MultipageDistillerData
{
87 MultipageDistillerData() {}
88 ~MultipageDistillerData() {}
89 vector
<string
> page_urls
;
90 vector
<string
> content
;
91 vector
<vector
<int> > image_ids
;
92 // The Javascript values returned by mock distiller.
93 ScopedVector
<base::Value
> distilled_values
;
96 DISALLOW_COPY_AND_ASSIGN(MultipageDistillerData
);
99 void VerifyIncrementalUpdatesMatch(
100 const MultipageDistillerData
* distiller_data
,
101 int num_pages_in_article
,
102 const vector
<dom_distiller::ArticleDistillationUpdate
>& incremental_updates
,
103 int start_page_num
) {
104 vector
<int> page_seq
=
105 GetPagesInSequence(start_page_num
, num_pages_in_article
);
106 // Updates should contain a list of pages. Pages in an update should be in
107 // the correct ascending page order regardless of |start_page_num|.
108 // E.g. if distillation starts at page 2 of a 3 page article, the updates
109 // will be [[2], [1, 2], [1, 2, 3]]. This example assumes that image fetches
110 // do not delay distillation of a page. There can be scenarios when image
111 // fetch delays distillation of a page (E.g. 1 is delayed due to image
112 // fetches so updates can be in this order [[2], [2,3], [1,2,3]].
113 for (size_t update_count
= 0; update_count
< incremental_updates
.size();
115 const dom_distiller::ArticleDistillationUpdate
& update
=
116 incremental_updates
[update_count
];
117 EXPECT_EQ(update_count
+ 1, update
.GetPagesSize());
119 vector
<int> expected_page_nums_in_update(
120 page_seq
.begin(), page_seq
.begin() + update
.GetPagesSize());
121 std::sort(expected_page_nums_in_update
.begin(),
122 expected_page_nums_in_update
.end());
124 // If we already got the first page then there is no previous page.
125 EXPECT_EQ((expected_page_nums_in_update
[0] != 0), update
.HasPrevPage());
127 // if we already got the last page then there is no next page.
129 (*expected_page_nums_in_update
.rbegin()) != num_pages_in_article
- 1,
130 update
.HasNextPage());
131 for (size_t j
= 0; j
< update
.GetPagesSize(); ++j
) {
132 int actual_page_num
= expected_page_nums_in_update
[j
];
133 EXPECT_EQ(distiller_data
->page_urls
[actual_page_num
],
134 update
.GetDistilledPage(j
).url());
135 EXPECT_EQ(distiller_data
->content
[actual_page_num
],
136 update
.GetDistilledPage(j
).html());
141 scoped_ptr
<MultipageDistillerData
> CreateMultipageDistillerDataWithoutImages(
143 scoped_ptr
<MultipageDistillerData
> result(new MultipageDistillerData());
144 string url_prefix
= "http://a.com/";
145 for (size_t page_num
= 0; page_num
< pages_size
; ++page_num
) {
146 result
->page_urls
.push_back(url_prefix
+ base::IntToString(page_num
));
147 result
->content
.push_back("Content for page:" +
148 base::IntToString(page_num
));
149 result
->image_ids
.push_back(vector
<int>());
150 string next_page_url
= (page_num
+ 1 < pages_size
)
151 ? url_prefix
+ base::IntToString(page_num
+ 1)
153 string prev_page_url
=
154 (page_num
> 0) ? result
->page_urls
[page_num
- 1] : "";
155 scoped_ptr
<base::Value
> distilled_value
=
156 CreateDistilledValueReturnedFromJS(kTitle
,
157 result
->content
[page_num
],
158 result
->image_ids
[page_num
],
161 result
->distilled_values
.push_back(distilled_value
.release());
163 return result
.Pass();
166 void VerifyArticleProtoMatchesMultipageData(
167 const dom_distiller::DistilledArticleProto
* article_proto
,
168 const MultipageDistillerData
* distiller_data
,
170 ASSERT_EQ(pages_size
, static_cast<size_t>(article_proto
->pages_size()));
171 EXPECT_EQ(kTitle
, article_proto
->title());
172 for (size_t page_num
= 0; page_num
< pages_size
; ++page_num
) {
173 const dom_distiller::DistilledPageProto
& page
=
174 article_proto
->pages(page_num
);
175 EXPECT_EQ(distiller_data
->content
[page_num
], page
.html());
176 EXPECT_EQ(distiller_data
->page_urls
[page_num
], page
.url());
177 EXPECT_EQ(distiller_data
->image_ids
[page_num
].size(),
178 static_cast<size_t>(page
.image_size()));
179 const vector
<int>& image_ids_for_page
= distiller_data
->image_ids
[page_num
];
180 for (size_t img_num
= 0; img_num
< image_ids_for_page
.size(); ++img_num
) {
181 EXPECT_EQ(kImageData
[image_ids_for_page
[img_num
]],
182 page
.image(img_num
).data());
183 EXPECT_EQ(GetImageName(page_num
+ 1, img_num
),
184 page
.image(img_num
).name());
189 void AddComponentsResources() {
190 base::FilePath pak_file
;
191 base::FilePath pak_dir
;
192 PathService::Get(base::DIR_MODULE
, &pak_dir
);
193 pak_file
= pak_dir
.Append(FILE_PATH_LITERAL("components_resources.pak"));
194 ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath(
195 pak_file
, ui::SCALE_FACTOR_NONE
);
200 namespace dom_distiller
{
202 using test::MockDistillerPage
;
203 using test::MockDistillerPageFactory
;
205 class TestDistillerURLFetcher
: public DistillerURLFetcher
{
207 explicit TestDistillerURLFetcher(bool delay_fetch
)
208 : DistillerURLFetcher(NULL
), delay_fetch_(delay_fetch
) {
209 responses_
[kImageURLs
[0]] = string(kImageData
[0]);
210 responses_
[kImageURLs
[1]] = string(kImageData
[1]);
213 virtual void FetchURL(const string
& url
,
214 const URLFetcherCallback
& callback
) OVERRIDE
{
215 ASSERT_FALSE(callback
.is_null());
217 callback_
= callback
;
223 void PostCallbackTask() {
224 ASSERT_TRUE(base::MessageLoop::current());
225 ASSERT_FALSE(callback_
.is_null());
226 base::MessageLoop::current()->PostTask(
227 FROM_HERE
, base::Bind(callback_
, responses_
[url_
]));
231 std::map
<string
, string
> responses_
;
233 URLFetcherCallback callback_
;
237 class TestDistillerURLFetcherFactory
: public DistillerURLFetcherFactory
{
239 TestDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL
) {}
241 virtual ~TestDistillerURLFetcherFactory() {}
242 virtual DistillerURLFetcher
* CreateDistillerURLFetcher() const OVERRIDE
{
243 return new TestDistillerURLFetcher(false);
247 class MockDistillerURLFetcherFactory
: public DistillerURLFetcherFactory
{
249 MockDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL
) {}
250 virtual ~MockDistillerURLFetcherFactory() {}
252 MOCK_CONST_METHOD0(CreateDistillerURLFetcher
, DistillerURLFetcher
*());
255 class DistillerTest
: public testing::Test
{
257 virtual ~DistillerTest() {}
259 virtual void SetUp() OVERRIDE
{
260 AddComponentsResources();
263 void OnDistillArticleDone(scoped_ptr
<DistilledArticleProto
> proto
) {
264 article_proto_
= proto
.Pass();
267 void OnDistillArticleUpdate(const ArticleDistillationUpdate
& article_update
) {
268 in_sequence_updates_
.push_back(article_update
);
271 void DistillPage(const std::string
& url
,
272 scoped_ptr
<DistillerPage
> distiller_page
) {
273 distiller_
->DistillPage(GURL(url
),
274 distiller_page
.Pass(),
275 base::Bind(&DistillerTest::OnDistillArticleDone
,
276 base::Unretained(this)),
277 base::Bind(&DistillerTest::OnDistillArticleUpdate
,
278 base::Unretained(this)));
282 scoped_ptr
<DistillerImpl
> distiller_
;
283 scoped_ptr
<DistilledArticleProto
> article_proto_
;
284 std::vector
<ArticleDistillationUpdate
> in_sequence_updates_
;
285 MockDistillerPageFactory page_factory_
;
286 TestDistillerURLFetcherFactory url_fetcher_factory_
;
289 ACTION_P3(DistillerPageOnDistillationDone
, distiller_page
, url
, result
) {
290 distiller_page
->OnDistillationDone(url
, result
);
293 scoped_ptr
<DistillerPage
> CreateMockDistillerPage(const base::Value
* result
,
295 MockDistillerPage
* distiller_page
= new MockDistillerPage();
296 EXPECT_CALL(*distiller_page
, DistillPageImpl(url
, _
))
297 .WillOnce(DistillerPageOnDistillationDone(distiller_page
, url
, result
));
298 return scoped_ptr
<DistillerPage
>(distiller_page
).Pass();
301 scoped_ptr
<DistillerPage
> CreateMockDistillerPageWithPendingJSCallback(
302 MockDistillerPage
** distiller_page_ptr
,
304 MockDistillerPage
* distiller_page
= new MockDistillerPage();
305 *distiller_page_ptr
= distiller_page
;
306 EXPECT_CALL(*distiller_page
, DistillPageImpl(url
, _
));
307 return scoped_ptr
<DistillerPage
>(distiller_page
).Pass();
310 scoped_ptr
<DistillerPage
> CreateMockDistillerPages(
311 MultipageDistillerData
* distiller_data
,
313 int start_page_num
) {
314 MockDistillerPage
* distiller_page
= new MockDistillerPage();
316 testing::InSequence s
;
317 vector
<int> page_nums
= GetPagesInSequence(start_page_num
, pages_size
);
318 for (size_t page_num
= 0; page_num
< pages_size
; ++page_num
) {
319 int page
= page_nums
[page_num
];
320 GURL url
= GURL(distiller_data
->page_urls
[page
]);
321 EXPECT_CALL(*distiller_page
, DistillPageImpl(url
, _
))
322 .WillOnce(DistillerPageOnDistillationDone(
323 distiller_page
, url
, distiller_data
->distilled_values
[page
]));
326 return scoped_ptr
<DistillerPage
>(distiller_page
).Pass();
329 TEST_F(DistillerTest
, DistillPage
) {
330 base::MessageLoopForUI loop
;
331 scoped_ptr
<base::Value
> result
=
332 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, vector
<int>(), "");
334 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
335 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
336 base::MessageLoop::current()->RunUntilIdle();
337 EXPECT_EQ(kTitle
, article_proto_
->title());
338 ASSERT_EQ(article_proto_
->pages_size(), 1);
339 const DistilledPageProto
& first_page
= article_proto_
->pages(0);
340 EXPECT_EQ(kContent
, first_page
.html());
341 EXPECT_EQ(kURL
, first_page
.url());
344 TEST_F(DistillerTest
, DistillPageWithImages
) {
345 base::MessageLoopForUI loop
;
346 vector
<int> image_indices
;
347 image_indices
.push_back(0);
348 image_indices
.push_back(1);
349 scoped_ptr
<base::Value
> result
=
350 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, image_indices
, "");
352 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
353 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
354 base::MessageLoop::current()->RunUntilIdle();
355 EXPECT_EQ(kTitle
, article_proto_
->title());
356 ASSERT_EQ(article_proto_
->pages_size(), 1);
357 const DistilledPageProto
& first_page
= article_proto_
->pages(0);
358 EXPECT_EQ(kContent
, first_page
.html());
359 EXPECT_EQ(kURL
, first_page
.url());
360 ASSERT_EQ(2, first_page
.image_size());
361 EXPECT_EQ(kImageData
[0], first_page
.image(0).data());
362 EXPECT_EQ(GetImageName(1, 0), first_page
.image(0).name());
363 EXPECT_EQ(kImageData
[1], first_page
.image(1).data());
364 EXPECT_EQ(GetImageName(1, 1), first_page
.image(1).name());
367 TEST_F(DistillerTest
, DistillMultiplePages
) {
368 base::MessageLoopForUI loop
;
369 const size_t kNumPages
= 8;
370 scoped_ptr
<MultipageDistillerData
> distiller_data
=
371 CreateMultipageDistillerDataWithoutImages(kNumPages
);
374 int next_image_number
= 0;
375 for (size_t page_num
= 0; page_num
< kNumPages
; ++page_num
) {
376 // Each page has different number of images.
377 size_t tot_images
= (page_num
+ kTotalImages
) % (kTotalImages
+ 1);
378 vector
<int> image_indices
;
379 for (size_t img_num
= 0; img_num
< tot_images
; img_num
++) {
380 image_indices
.push_back(next_image_number
);
381 next_image_number
= (next_image_number
+ 1) % kTotalImages
;
383 distiller_data
->image_ids
.push_back(image_indices
);
387 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
389 distiller_data
->page_urls
[0],
390 CreateMockDistillerPages(distiller_data
.get(), kNumPages
, 0).Pass());
391 base::MessageLoop::current()->RunUntilIdle();
392 VerifyArticleProtoMatchesMultipageData(
393 article_proto_
.get(), distiller_data
.get(), kNumPages
);
396 TEST_F(DistillerTest
, DistillLinkLoop
) {
397 base::MessageLoopForUI loop
;
398 // Create a loop, the next page is same as the current page. This could
399 // happen if javascript misparses a next page link.
400 scoped_ptr
<base::Value
> result
=
401 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, vector
<int>(), kURL
);
403 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
404 DistillPage(kURL
, CreateMockDistillerPage(result
.get(), GURL(kURL
)).Pass());
405 base::MessageLoop::current()->RunUntilIdle();
406 EXPECT_EQ(kTitle
, article_proto_
->title());
407 EXPECT_EQ(article_proto_
->pages_size(), 1);
410 TEST_F(DistillerTest
, CheckMaxPageLimitExtraPage
) {
411 base::MessageLoopForUI loop
;
412 const size_t kMaxPagesInArticle
= 10;
413 scoped_ptr
<MultipageDistillerData
> distiller_data
=
414 CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle
);
416 // Note: Next page url of the last page of article is set. So distiller will
417 // try to do kMaxPagesInArticle + 1 calls if the max article limit does not
419 scoped_ptr
<base::Value
> last_page_data
=
420 CreateDistilledValueReturnedFromJS(
422 distiller_data
->content
[kMaxPagesInArticle
- 1],
425 distiller_data
->page_urls
[kMaxPagesInArticle
- 2]);
427 distiller_data
->distilled_values
.pop_back();
428 distiller_data
->distilled_values
.push_back(last_page_data
.release());
431 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
433 distiller_
->SetMaxNumPagesInArticle(kMaxPagesInArticle
);
435 DistillPage(distiller_data
->page_urls
[0],
436 CreateMockDistillerPages(
437 distiller_data
.get(), kMaxPagesInArticle
, 0).Pass());
438 base::MessageLoop::current()->RunUntilIdle();
439 EXPECT_EQ(kTitle
, article_proto_
->title());
440 EXPECT_EQ(kMaxPagesInArticle
,
441 static_cast<size_t>(article_proto_
->pages_size()));
444 TEST_F(DistillerTest
, CheckMaxPageLimitExactLimit
) {
445 base::MessageLoopForUI loop
;
446 const size_t kMaxPagesInArticle
= 10;
447 scoped_ptr
<MultipageDistillerData
> distiller_data
=
448 CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle
);
451 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
453 // Check if distilling an article with exactly the page limit works.
454 distiller_
->SetMaxNumPagesInArticle(kMaxPagesInArticle
);
456 DistillPage(distiller_data
->page_urls
[0],
457 CreateMockDistillerPages(
458 distiller_data
.get(), kMaxPagesInArticle
, 0).Pass());
459 base::MessageLoop::current()->RunUntilIdle();
460 EXPECT_EQ(kTitle
, article_proto_
->title());
461 EXPECT_EQ(kMaxPagesInArticle
,
462 static_cast<size_t>(article_proto_
->pages_size()));
465 TEST_F(DistillerTest
, SinglePageDistillationFailure
) {
466 base::MessageLoopForUI loop
;
467 // To simulate failure return a null value.
468 scoped_ptr
<base::Value
> nullValue(base::Value::CreateNullValue());
470 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
472 CreateMockDistillerPage(nullValue
.get(), GURL(kURL
)).Pass());
473 base::MessageLoop::current()->RunUntilIdle();
474 EXPECT_EQ("", article_proto_
->title());
475 EXPECT_EQ(0, article_proto_
->pages_size());
478 TEST_F(DistillerTest
, MultiplePagesDistillationFailure
) {
479 base::MessageLoopForUI loop
;
480 const size_t kNumPages
= 8;
481 scoped_ptr
<MultipageDistillerData
> distiller_data
=
482 CreateMultipageDistillerDataWithoutImages(kNumPages
);
484 // The page number of the failed page.
485 size_t failed_page_num
= 3;
486 // reset distilled data of the failed page.
487 distiller_data
->distilled_values
.erase(
488 distiller_data
->distilled_values
.begin() + failed_page_num
);
489 distiller_data
->distilled_values
.insert(
490 distiller_data
->distilled_values
.begin() + failed_page_num
,
491 base::Value::CreateNullValue());
492 // Expect only calls till the failed page number.
494 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
495 DistillPage(distiller_data
->page_urls
[0],
496 CreateMockDistillerPages(
497 distiller_data
.get(), failed_page_num
+ 1, 0).Pass());
498 base::MessageLoop::current()->RunUntilIdle();
499 EXPECT_EQ(kTitle
, article_proto_
->title());
500 VerifyArticleProtoMatchesMultipageData(
501 article_proto_
.get(), distiller_data
.get(), failed_page_num
);
504 TEST_F(DistillerTest
, DistillPreviousPage
) {
505 base::MessageLoopForUI loop
;
506 const size_t kNumPages
= 8;
508 // The page number of the article on which distillation starts.
509 int start_page_num
= 3;
510 scoped_ptr
<MultipageDistillerData
> distiller_data
=
511 CreateMultipageDistillerDataWithoutImages(kNumPages
);
514 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
515 DistillPage(distiller_data
->page_urls
[start_page_num
],
516 CreateMockDistillerPages(
517 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
518 base::MessageLoop::current()->RunUntilIdle();
519 VerifyArticleProtoMatchesMultipageData(
520 article_proto_
.get(), distiller_data
.get(), kNumPages
);
523 TEST_F(DistillerTest
, IncrementalUpdates
) {
524 base::MessageLoopForUI loop
;
525 const size_t kNumPages
= 8;
527 // The page number of the article on which distillation starts.
528 int start_page_num
= 3;
529 scoped_ptr
<MultipageDistillerData
> distiller_data
=
530 CreateMultipageDistillerDataWithoutImages(kNumPages
);
533 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
534 DistillPage(distiller_data
->page_urls
[start_page_num
],
535 CreateMockDistillerPages(
536 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
537 base::MessageLoop::current()->RunUntilIdle();
538 EXPECT_EQ(kTitle
, article_proto_
->title());
539 ASSERT_EQ(kNumPages
, static_cast<size_t>(article_proto_
->pages_size()));
540 EXPECT_EQ(kNumPages
, in_sequence_updates_
.size());
542 VerifyIncrementalUpdatesMatch(
543 distiller_data
.get(), kNumPages
, in_sequence_updates_
, start_page_num
);
546 TEST_F(DistillerTest
, IncrementalUpdatesDoNotDeleteFinalArticle
) {
547 base::MessageLoopForUI loop
;
548 const size_t kNumPages
= 8;
549 int start_page_num
= 3;
550 scoped_ptr
<MultipageDistillerData
> distiller_data
=
551 CreateMultipageDistillerDataWithoutImages(kNumPages
);
554 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
555 DistillPage(distiller_data
->page_urls
[start_page_num
],
556 CreateMockDistillerPages(
557 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
558 base::MessageLoop::current()->RunUntilIdle();
559 EXPECT_EQ(kNumPages
, in_sequence_updates_
.size());
561 in_sequence_updates_
.clear();
563 // Should still be able to access article and pages.
564 VerifyArticleProtoMatchesMultipageData(
565 article_proto_
.get(), distiller_data
.get(), kNumPages
);
568 TEST_F(DistillerTest
, DeletingArticleDoesNotInterfereWithUpdates
) {
569 base::MessageLoopForUI loop
;
570 const size_t kNumPages
= 8;
571 scoped_ptr
<MultipageDistillerData
> distiller_data
=
572 CreateMultipageDistillerDataWithoutImages(kNumPages
);
573 // The page number of the article on which distillation starts.
574 int start_page_num
= 3;
577 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
578 DistillPage(distiller_data
->page_urls
[start_page_num
],
579 CreateMockDistillerPages(
580 distiller_data
.get(), kNumPages
, start_page_num
).Pass());
581 base::MessageLoop::current()->RunUntilIdle();
582 EXPECT_EQ(kNumPages
, in_sequence_updates_
.size());
583 EXPECT_EQ(kTitle
, article_proto_
->title());
584 ASSERT_EQ(kNumPages
, static_cast<size_t>(article_proto_
->pages_size()));
586 // Delete the article.
587 article_proto_
.reset();
588 VerifyIncrementalUpdatesMatch(
589 distiller_data
.get(), kNumPages
, in_sequence_updates_
, start_page_num
);
592 TEST_F(DistillerTest
, CancelWithDelayedImageFetchCallback
) {
593 base::MessageLoopForUI loop
;
594 vector
<int> image_indices
;
595 image_indices
.push_back(0);
596 scoped_ptr
<base::Value
> distilled_value
=
597 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, image_indices
, "");
598 TestDistillerURLFetcher
* delayed_fetcher
= new TestDistillerURLFetcher(true);
599 MockDistillerURLFetcherFactory mock_url_fetcher_factory
;
600 EXPECT_CALL(mock_url_fetcher_factory
, CreateDistillerURLFetcher())
601 .WillOnce(Return(delayed_fetcher
));
603 new DistillerImpl(mock_url_fetcher_factory
, DomDistillerOptions()));
605 kURL
, CreateMockDistillerPage(distilled_value
.get(), GURL(kURL
)).Pass());
606 base::MessageLoop::current()->RunUntilIdle();
608 // Post callback from the url fetcher and then delete the distiller.
609 delayed_fetcher
->PostCallbackTask();
612 base::MessageLoop::current()->RunUntilIdle();
615 TEST_F(DistillerTest
, CancelWithDelayedJSCallback
) {
616 base::MessageLoopForUI loop
;
617 scoped_ptr
<base::Value
> distilled_value
=
618 CreateDistilledValueReturnedFromJS(kTitle
, kContent
, vector
<int>(), "");
619 MockDistillerPage
* distiller_page
= NULL
;
621 new DistillerImpl(url_fetcher_factory_
, DomDistillerOptions()));
623 CreateMockDistillerPageWithPendingJSCallback(&distiller_page
,
625 base::MessageLoop::current()->RunUntilIdle();
627 ASSERT_TRUE(distiller_page
);
628 // Post the task to execute javascript and then delete the distiller.
629 distiller_page
->OnDistillationDone(GURL(kURL
), distilled_value
.get());
632 base::MessageLoop::current()->RunUntilIdle();
635 } // namespace dom_distiller