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;
39 const char kTitle[] = "Title";
40 const char kContent[] = "Content";
41 const char kURL[] = "http://a.com/";
42 const size_t kTotalImages = 2;
43 const char* kImageURLs[kTotalImages] = {"http://a.com/img1.jpg",
44 "http://a.com/img2.jpg"};
45 const char* kImageData[kTotalImages] = {"abcde", "12345"};
46 const char kDebugLog[] = "Debug Log";
48 const string GetImageName(int page_num, int image_num) {
49 return base::IntToString(page_num) + "_" + base::IntToString(image_num);
52 scoped_ptr<base::Value> CreateDistilledValueReturnedFromJS(
54 const string& content,
55 const vector<int>& image_indices,
56 const string& next_page_url,
57 const string& prev_page_url = "") {
58 DomDistillerResult result;
59 result.set_title(title);
60 result.mutable_distilled_content()->set_html(content);
61 result.mutable_pagination_info()->set_next_page(next_page_url);
62 result.mutable_pagination_info()->set_prev_page(prev_page_url);
64 for (size_t i = 0; i < image_indices.size(); ++i) {
65 result.add_image_urls(kImageURLs[image_indices[i]]);
68 return dom_distiller::proto::json::DomDistillerResult::WriteToValue(result);
71 // Return the sequence in which Distiller will distill pages.
72 // Note: ignores any delays due to fetching images etc.
73 vector<int> GetPagesInSequence(int start_page_num, int num_pages) {
74 // Distiller prefers distilling past pages first. E.g. when distillation
75 // starts on page 2 then pages are distilled in the order: 2, 1, 0, 3, 4.
76 vector<int> page_nums;
77 for (int page = start_page_num; page >= 0; --page)
78 page_nums.push_back(page);
79 for (int page = start_page_num + 1; page < num_pages; ++page)
80 page_nums.push_back(page);
84 struct MultipageDistillerData {
86 MultipageDistillerData() {}
87 ~MultipageDistillerData() {}
88 vector<string> page_urls;
89 vector<string> content;
90 vector<vector<int> > image_ids;
91 // The Javascript values returned by mock distiller.
92 ScopedVector<base::Value> distilled_values;
95 DISALLOW_COPY_AND_ASSIGN(MultipageDistillerData);
98 void VerifyIncrementalUpdatesMatch(
99 const MultipageDistillerData* distiller_data,
100 int num_pages_in_article,
101 const vector<dom_distiller::ArticleDistillationUpdate>& incremental_updates,
102 int start_page_num) {
103 vector<int> page_seq =
104 GetPagesInSequence(start_page_num, num_pages_in_article);
105 // Updates should contain a list of pages. Pages in an update should be in
106 // the correct ascending page order regardless of |start_page_num|.
107 // E.g. if distillation starts at page 2 of a 3 page article, the updates
108 // will be [[2], [1, 2], [1, 2, 3]]. This example assumes that image fetches
109 // do not delay distillation of a page. There can be scenarios when image
110 // fetch delays distillation of a page (E.g. 1 is delayed due to image
111 // fetches so updates can be in this order [[2], [2,3], [1,2,3]].
112 for (size_t update_count = 0; update_count < incremental_updates.size();
114 const dom_distiller::ArticleDistillationUpdate& update =
115 incremental_updates[update_count];
116 EXPECT_EQ(update_count + 1, update.GetPagesSize());
118 vector<int> expected_page_nums_in_update(
119 page_seq.begin(), page_seq.begin() + update.GetPagesSize());
120 std::sort(expected_page_nums_in_update.begin(),
121 expected_page_nums_in_update.end());
123 // If we already got the first page then there is no previous page.
124 EXPECT_EQ((expected_page_nums_in_update[0] != 0), update.HasPrevPage());
126 // if we already got the last page then there is no next page.
128 (*expected_page_nums_in_update.rbegin()) != num_pages_in_article - 1,
129 update.HasNextPage());
130 for (size_t j = 0; j < update.GetPagesSize(); ++j) {
131 int actual_page_num = expected_page_nums_in_update[j];
132 EXPECT_EQ(distiller_data->page_urls[actual_page_num],
133 update.GetDistilledPage(j).url());
134 EXPECT_EQ(distiller_data->content[actual_page_num],
135 update.GetDistilledPage(j).html());
140 string GenerateNextPageUrl(const std::string& url_prefix, size_t page_num,
142 return page_num + 1 < pages_size ?
143 url_prefix + base::IntToString(page_num + 1) : "";
146 string GeneratePrevPageUrl(const std::string& url_prefix, size_t page_num) {
147 return page_num > 0 ? url_prefix + base::IntToString(page_num - 1) : "";
150 scoped_ptr<MultipageDistillerData> CreateMultipageDistillerDataWithoutImages(
152 scoped_ptr<MultipageDistillerData> result(new MultipageDistillerData());
153 string url_prefix = kURL;
154 for (size_t page_num = 0; page_num < pages_size; ++page_num) {
155 result->page_urls.push_back(url_prefix + base::IntToString(page_num));
156 result->content.push_back("Content for page:" +
157 base::IntToString(page_num));
158 result->image_ids.push_back(vector<int>());
159 string next_page_url =
160 GenerateNextPageUrl(url_prefix, page_num, pages_size);
161 string prev_page_url =
162 GeneratePrevPageUrl(url_prefix, page_num);
163 scoped_ptr<base::Value> distilled_value =
164 CreateDistilledValueReturnedFromJS(kTitle,
165 result->content[page_num],
166 result->image_ids[page_num],
169 result->distilled_values.push_back(distilled_value.release());
171 return result.Pass();
174 void VerifyArticleProtoMatchesMultipageData(
175 const dom_distiller::DistilledArticleProto* article_proto,
176 const MultipageDistillerData* distiller_data,
177 size_t distilled_pages_size,
178 size_t total_pages_size) {
179 ASSERT_EQ(distilled_pages_size,
180 static_cast<size_t>(article_proto->pages_size()));
181 EXPECT_EQ(kTitle, article_proto->title());
182 std::string url_prefix = kURL;
183 for (size_t page_num = 0; page_num < distilled_pages_size; ++page_num) {
184 const dom_distiller::DistilledPageProto& page =
185 article_proto->pages(page_num);
186 EXPECT_EQ(distiller_data->content[page_num], page.html());
187 EXPECT_EQ(distiller_data->page_urls[page_num], page.url());
188 EXPECT_EQ(distiller_data->image_ids[page_num].size(),
189 static_cast<size_t>(page.image_size()));
190 const vector<int>& image_ids_for_page = distiller_data->image_ids[page_num];
191 for (size_t img_num = 0; img_num < image_ids_for_page.size(); ++img_num) {
192 EXPECT_EQ(kImageData[image_ids_for_page[img_num]],
193 page.image(img_num).data());
194 EXPECT_EQ(GetImageName(page_num + 1, img_num),
195 page.image(img_num).name());
197 std::string expected_next_page_url =
198 GenerateNextPageUrl(url_prefix, page_num, total_pages_size);
199 std::string expected_prev_page_url =
200 GeneratePrevPageUrl(url_prefix, page_num);
201 EXPECT_EQ(expected_next_page_url, page.pagination_info().next_page());
202 EXPECT_EQ(expected_prev_page_url, page.pagination_info().prev_page());
203 EXPECT_FALSE(page.pagination_info().has_canonical_page());
209 namespace dom_distiller {
211 using test::MockDistillerPage;
212 using test::MockDistillerPageFactory;
214 class TestDistillerURLFetcher : public DistillerURLFetcher {
216 explicit TestDistillerURLFetcher(bool delay_fetch)
217 : DistillerURLFetcher(NULL), delay_fetch_(delay_fetch) {
218 responses_[kImageURLs[0]] = string(kImageData[0]);
219 responses_[kImageURLs[1]] = string(kImageData[1]);
222 void FetchURL(const string& url,
223 const URLFetcherCallback& callback) override {
224 ASSERT_FALSE(callback.is_null());
226 callback_ = callback;
232 void PostCallbackTask() {
233 ASSERT_TRUE(base::MessageLoop::current());
234 ASSERT_FALSE(callback_.is_null());
235 base::MessageLoop::current()->PostTask(
236 FROM_HERE, base::Bind(callback_, responses_[url_]));
240 std::map<string, string> responses_;
242 URLFetcherCallback callback_;
246 class TestDistillerURLFetcherFactory : public DistillerURLFetcherFactory {
248 TestDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL) {}
250 ~TestDistillerURLFetcherFactory() override {}
251 DistillerURLFetcher* CreateDistillerURLFetcher() const override {
252 return new TestDistillerURLFetcher(false);
256 class MockDistillerURLFetcherFactory : public DistillerURLFetcherFactory {
258 MockDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL) {}
259 virtual ~MockDistillerURLFetcherFactory() {}
261 MOCK_CONST_METHOD0(CreateDistillerURLFetcher, DistillerURLFetcher*());
264 class DistillerTest : public testing::Test {
266 ~DistillerTest() override {}
268 void OnDistillArticleDone(scoped_ptr<DistilledArticleProto> proto) {
269 article_proto_ = proto.Pass();
272 void OnDistillArticleUpdate(const ArticleDistillationUpdate& article_update) {
273 in_sequence_updates_.push_back(article_update);
276 void DistillPage(const std::string& url,
277 scoped_ptr<DistillerPage> distiller_page) {
278 distiller_->DistillPage(GURL(url),
279 distiller_page.Pass(),
280 base::Bind(&DistillerTest::OnDistillArticleDone,
281 base::Unretained(this)),
282 base::Bind(&DistillerTest::OnDistillArticleUpdate,
283 base::Unretained(this)));
287 scoped_ptr<DistillerImpl> distiller_;
288 scoped_ptr<DistilledArticleProto> article_proto_;
289 std::vector<ArticleDistillationUpdate> in_sequence_updates_;
290 MockDistillerPageFactory page_factory_;
291 TestDistillerURLFetcherFactory url_fetcher_factory_;
294 ACTION_P3(DistillerPageOnDistillationDone, distiller_page, url, result) {
295 distiller_page->OnDistillationDone(url, result);
298 scoped_ptr<DistillerPage> CreateMockDistillerPage(const base::Value* result,
300 MockDistillerPage* distiller_page = new MockDistillerPage();
301 EXPECT_CALL(*distiller_page, DistillPageImpl(url, _))
302 .WillOnce(DistillerPageOnDistillationDone(distiller_page, url, result));
303 return scoped_ptr<DistillerPage>(distiller_page).Pass();
306 scoped_ptr<DistillerPage> CreateMockDistillerPageWithPendingJSCallback(
307 MockDistillerPage** distiller_page_ptr,
309 MockDistillerPage* distiller_page = new MockDistillerPage();
310 *distiller_page_ptr = distiller_page;
311 EXPECT_CALL(*distiller_page, DistillPageImpl(url, _));
312 return scoped_ptr<DistillerPage>(distiller_page).Pass();
315 scoped_ptr<DistillerPage> CreateMockDistillerPages(
316 MultipageDistillerData* distiller_data,
318 int start_page_num) {
319 MockDistillerPage* distiller_page = new MockDistillerPage();
321 testing::InSequence s;
322 vector<int> page_nums = GetPagesInSequence(start_page_num, pages_size);
323 for (size_t page_num = 0; page_num < pages_size; ++page_num) {
324 int page = page_nums[page_num];
325 GURL url = GURL(distiller_data->page_urls[page]);
326 EXPECT_CALL(*distiller_page, DistillPageImpl(url, _))
327 .WillOnce(DistillerPageOnDistillationDone(
328 distiller_page, url, distiller_data->distilled_values[page]));
331 return scoped_ptr<DistillerPage>(distiller_page).Pass();
334 TEST_F(DistillerTest, DistillPage) {
335 base::MessageLoopForUI loop;
336 scoped_ptr<base::Value> result =
337 CreateDistilledValueReturnedFromJS(kTitle, kContent, vector<int>(), "");
339 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
340 DistillPage(kURL, CreateMockDistillerPage(result.get(), GURL(kURL)).Pass());
341 base::MessageLoop::current()->RunUntilIdle();
342 EXPECT_EQ(kTitle, article_proto_->title());
343 ASSERT_EQ(article_proto_->pages_size(), 1);
344 const DistilledPageProto& first_page = article_proto_->pages(0);
345 EXPECT_EQ(kContent, first_page.html());
346 EXPECT_EQ(kURL, first_page.url());
349 TEST_F(DistillerTest, DistillPageWithDebugInfo) {
350 base::MessageLoopForUI loop;
351 DomDistillerResult dd_result;
352 dd_result.mutable_debug_info()->set_log(kDebugLog);
353 scoped_ptr<base::Value> result =
354 dom_distiller::proto::json::DomDistillerResult::WriteToValue(dd_result);
356 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
357 DistillPage(kURL, CreateMockDistillerPage(result.get(), GURL(kURL)).Pass());
358 base::MessageLoop::current()->RunUntilIdle();
359 const DistilledPageProto& first_page = article_proto_->pages(0);
360 EXPECT_EQ(kDebugLog, first_page.debug_info().log());
363 TEST_F(DistillerTest, DistillPageWithImages) {
364 base::MessageLoopForUI loop;
365 vector<int> image_indices;
366 image_indices.push_back(0);
367 image_indices.push_back(1);
368 scoped_ptr<base::Value> result =
369 CreateDistilledValueReturnedFromJS(kTitle, kContent, image_indices, "");
371 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
372 DistillPage(kURL, CreateMockDistillerPage(result.get(), GURL(kURL)).Pass());
373 base::MessageLoop::current()->RunUntilIdle();
374 EXPECT_EQ(kTitle, article_proto_->title());
375 ASSERT_EQ(article_proto_->pages_size(), 1);
376 const DistilledPageProto& first_page = article_proto_->pages(0);
377 EXPECT_EQ(kContent, first_page.html());
378 EXPECT_EQ(kURL, first_page.url());
379 ASSERT_EQ(2, first_page.image_size());
380 EXPECT_EQ(kImageData[0], first_page.image(0).data());
381 EXPECT_EQ(GetImageName(1, 0), first_page.image(0).name());
382 EXPECT_EQ(kImageData[1], first_page.image(1).data());
383 EXPECT_EQ(GetImageName(1, 1), first_page.image(1).name());
386 TEST_F(DistillerTest, DistillMultiplePages) {
387 base::MessageLoopForUI loop;
388 const size_t kNumPages = 8;
389 scoped_ptr<MultipageDistillerData> distiller_data =
390 CreateMultipageDistillerDataWithoutImages(kNumPages);
393 int next_image_number = 0;
394 for (size_t page_num = 0; page_num < kNumPages; ++page_num) {
395 // Each page has different number of images.
396 size_t tot_images = (page_num + kTotalImages) % (kTotalImages + 1);
397 vector<int> image_indices;
398 for (size_t img_num = 0; img_num < tot_images; img_num++) {
399 image_indices.push_back(next_image_number);
400 next_image_number = (next_image_number + 1) % kTotalImages;
402 distiller_data->image_ids.push_back(image_indices);
406 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
408 distiller_data->page_urls[0],
409 CreateMockDistillerPages(distiller_data.get(), kNumPages, 0).Pass());
410 base::MessageLoop::current()->RunUntilIdle();
411 VerifyArticleProtoMatchesMultipageData(
412 article_proto_.get(), distiller_data.get(), kNumPages, kNumPages);
415 TEST_F(DistillerTest, DistillLinkLoop) {
416 base::MessageLoopForUI loop;
417 // Create a loop, the next page is same as the current page. This could
418 // happen if javascript misparses a next page link.
419 scoped_ptr<base::Value> result =
420 CreateDistilledValueReturnedFromJS(kTitle, kContent, vector<int>(), kURL);
422 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
423 DistillPage(kURL, CreateMockDistillerPage(result.get(), GURL(kURL)).Pass());
424 base::MessageLoop::current()->RunUntilIdle();
425 EXPECT_EQ(kTitle, article_proto_->title());
426 EXPECT_EQ(article_proto_->pages_size(), 1);
429 TEST_F(DistillerTest, CheckMaxPageLimitExtraPage) {
430 base::MessageLoopForUI loop;
431 const size_t kMaxPagesInArticle = 10;
432 scoped_ptr<MultipageDistillerData> distiller_data =
433 CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle);
435 // Note: Next page url of the last page of article is set. So distiller will
436 // try to do kMaxPagesInArticle + 1 calls if the max article limit does not
438 scoped_ptr<base::Value> last_page_data =
439 CreateDistilledValueReturnedFromJS(
441 distiller_data->content[kMaxPagesInArticle - 1],
444 distiller_data->page_urls[kMaxPagesInArticle - 2]);
446 distiller_data->distilled_values.pop_back();
447 distiller_data->distilled_values.push_back(last_page_data.release());
450 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
452 distiller_->SetMaxNumPagesInArticle(kMaxPagesInArticle);
454 DistillPage(distiller_data->page_urls[0],
455 CreateMockDistillerPages(
456 distiller_data.get(), kMaxPagesInArticle, 0).Pass());
457 base::MessageLoop::current()->RunUntilIdle();
458 EXPECT_EQ(kTitle, article_proto_->title());
459 EXPECT_EQ(kMaxPagesInArticle,
460 static_cast<size_t>(article_proto_->pages_size()));
463 TEST_F(DistillerTest, CheckMaxPageLimitExactLimit) {
464 base::MessageLoopForUI loop;
465 const size_t kMaxPagesInArticle = 10;
466 scoped_ptr<MultipageDistillerData> distiller_data =
467 CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle);
470 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
472 // Check if distilling an article with exactly the page limit works.
473 distiller_->SetMaxNumPagesInArticle(kMaxPagesInArticle);
475 DistillPage(distiller_data->page_urls[0],
476 CreateMockDistillerPages(
477 distiller_data.get(), kMaxPagesInArticle, 0).Pass());
478 base::MessageLoop::current()->RunUntilIdle();
479 EXPECT_EQ(kTitle, article_proto_->title());
480 EXPECT_EQ(kMaxPagesInArticle,
481 static_cast<size_t>(article_proto_->pages_size()));
484 TEST_F(DistillerTest, SinglePageDistillationFailure) {
485 base::MessageLoopForUI loop;
486 // To simulate failure return a null value.
487 scoped_ptr<base::Value> nullValue(base::Value::CreateNullValue());
489 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
491 CreateMockDistillerPage(nullValue.get(), GURL(kURL)).Pass());
492 base::MessageLoop::current()->RunUntilIdle();
493 EXPECT_EQ("", article_proto_->title());
494 EXPECT_EQ(0, article_proto_->pages_size());
497 TEST_F(DistillerTest, MultiplePagesDistillationFailure) {
498 base::MessageLoopForUI loop;
499 const size_t kNumPages = 8;
500 scoped_ptr<MultipageDistillerData> distiller_data =
501 CreateMultipageDistillerDataWithoutImages(kNumPages);
503 // The page number of the failed page.
504 size_t failed_page_num = 3;
505 // reset distilled data of the failed page.
506 distiller_data->distilled_values.erase(
507 distiller_data->distilled_values.begin() + failed_page_num);
508 distiller_data->distilled_values.insert(
509 distiller_data->distilled_values.begin() + failed_page_num,
510 base::Value::CreateNullValue());
511 // Expect only calls till the failed page number.
513 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
514 DistillPage(distiller_data->page_urls[0],
515 CreateMockDistillerPages(
516 distiller_data.get(), failed_page_num + 1, 0).Pass());
517 base::MessageLoop::current()->RunUntilIdle();
518 EXPECT_EQ(kTitle, article_proto_->title());
519 VerifyArticleProtoMatchesMultipageData(
520 article_proto_.get(), distiller_data.get(), failed_page_num, kNumPages);
523 TEST_F(DistillerTest, DistillPreviousPage) {
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 VerifyArticleProtoMatchesMultipageData(
539 article_proto_.get(), distiller_data.get(), kNumPages, kNumPages);
542 TEST_F(DistillerTest, IncrementalUpdates) {
543 base::MessageLoopForUI loop;
544 const size_t kNumPages = 8;
546 // The page number of the article on which distillation starts.
547 int start_page_num = 3;
548 scoped_ptr<MultipageDistillerData> distiller_data =
549 CreateMultipageDistillerDataWithoutImages(kNumPages);
552 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
553 DistillPage(distiller_data->page_urls[start_page_num],
554 CreateMockDistillerPages(
555 distiller_data.get(), kNumPages, start_page_num).Pass());
556 base::MessageLoop::current()->RunUntilIdle();
557 EXPECT_EQ(kTitle, article_proto_->title());
558 ASSERT_EQ(kNumPages, static_cast<size_t>(article_proto_->pages_size()));
559 EXPECT_EQ(kNumPages, in_sequence_updates_.size());
561 VerifyIncrementalUpdatesMatch(
562 distiller_data.get(), kNumPages, in_sequence_updates_, start_page_num);
565 TEST_F(DistillerTest, IncrementalUpdatesDoNotDeleteFinalArticle) {
566 base::MessageLoopForUI loop;
567 const size_t kNumPages = 8;
568 int start_page_num = 3;
569 scoped_ptr<MultipageDistillerData> distiller_data =
570 CreateMultipageDistillerDataWithoutImages(kNumPages);
573 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
574 DistillPage(distiller_data->page_urls[start_page_num],
575 CreateMockDistillerPages(
576 distiller_data.get(), kNumPages, start_page_num).Pass());
577 base::MessageLoop::current()->RunUntilIdle();
578 EXPECT_EQ(kNumPages, in_sequence_updates_.size());
580 in_sequence_updates_.clear();
582 // Should still be able to access article and pages.
583 VerifyArticleProtoMatchesMultipageData(
584 article_proto_.get(), distiller_data.get(), kNumPages, kNumPages);
587 TEST_F(DistillerTest, DeletingArticleDoesNotInterfereWithUpdates) {
588 base::MessageLoopForUI loop;
589 const size_t kNumPages = 8;
590 scoped_ptr<MultipageDistillerData> distiller_data =
591 CreateMultipageDistillerDataWithoutImages(kNumPages);
592 // The page number of the article on which distillation starts.
593 int start_page_num = 3;
596 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
597 DistillPage(distiller_data->page_urls[start_page_num],
598 CreateMockDistillerPages(
599 distiller_data.get(), kNumPages, start_page_num).Pass());
600 base::MessageLoop::current()->RunUntilIdle();
601 EXPECT_EQ(kNumPages, in_sequence_updates_.size());
602 EXPECT_EQ(kTitle, article_proto_->title());
603 ASSERT_EQ(kNumPages, static_cast<size_t>(article_proto_->pages_size()));
605 // Delete the article.
606 article_proto_.reset();
607 VerifyIncrementalUpdatesMatch(
608 distiller_data.get(), kNumPages, in_sequence_updates_, start_page_num);
611 TEST_F(DistillerTest, CancelWithDelayedImageFetchCallback) {
612 base::MessageLoopForUI loop;
613 vector<int> image_indices;
614 image_indices.push_back(0);
615 scoped_ptr<base::Value> distilled_value =
616 CreateDistilledValueReturnedFromJS(kTitle, kContent, image_indices, "");
617 TestDistillerURLFetcher* delayed_fetcher = new TestDistillerURLFetcher(true);
618 MockDistillerURLFetcherFactory mock_url_fetcher_factory;
619 EXPECT_CALL(mock_url_fetcher_factory, CreateDistillerURLFetcher())
620 .WillOnce(Return(delayed_fetcher));
622 new DistillerImpl(mock_url_fetcher_factory, DomDistillerOptions()));
624 kURL, CreateMockDistillerPage(distilled_value.get(), GURL(kURL)).Pass());
625 base::MessageLoop::current()->RunUntilIdle();
627 // Post callback from the url fetcher and then delete the distiller.
628 delayed_fetcher->PostCallbackTask();
631 base::MessageLoop::current()->RunUntilIdle();
634 TEST_F(DistillerTest, CancelWithDelayedJSCallback) {
635 base::MessageLoopForUI loop;
636 scoped_ptr<base::Value> distilled_value =
637 CreateDistilledValueReturnedFromJS(kTitle, kContent, vector<int>(), "");
638 MockDistillerPage* distiller_page = NULL;
640 new DistillerImpl(url_fetcher_factory_, DomDistillerOptions()));
642 CreateMockDistillerPageWithPendingJSCallback(&distiller_page,
644 base::MessageLoop::current()->RunUntilIdle();
646 ASSERT_TRUE(distiller_page);
647 // Post the task to execute javascript and then delete the distiller.
648 distiller_page->OnDistillationDone(GURL(kURL), distilled_value.get());
651 base::MessageLoop::current()->RunUntilIdle();
654 } // namespace dom_distiller