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/proto/distilled_article.pb.h"
21 #include "components/dom_distiller/core/proto/distilled_page.pb.h"
22 #include "net/url_request/url_request_context_getter.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
28 using ::testing::Invoke;
29 using ::testing::Return;
33 const char kTitle[] = "Title";
34 const char kContent[] = "Content";
35 const char kURL[] = "http://a.com/";
36 const size_t kTotalImages = 2;
37 const char* kImageURLs[kTotalImages] = {"http://a.com/img1.jpg",
38 "http://a.com/img2.jpg"};
39 const char* kImageData[kTotalImages] = {"abcde", "12345"};
41 const string GetImageName(int page_num, int image_num) {
42 return base::IntToString(page_num) + "_" + base::IntToString(image_num);
45 scoped_ptr<base::ListValue> CreateDistilledValueReturnedFromJS(
47 const string& content,
48 const vector<int>& image_indices,
49 const string& next_page_url,
50 const string& prev_page_url = "") {
51 scoped_ptr<base::ListValue> list(new base::ListValue());
53 list->AppendString(title);
54 list->AppendString(content);
55 list->AppendString(next_page_url);
56 list->AppendString(prev_page_url);
57 for (size_t i = 0; i < image_indices.size(); ++i) {
58 list->AppendString(kImageURLs[image_indices[i]]);
63 // Return the sequence in which Distiller will distill pages.
64 // Note: ignores any delays due to fetching images etc.
65 vector<int> GetPagesInSequence(int start_page_num, int num_pages) {
66 // Distiller prefers distilling past pages first. E.g. when distillation
67 // starts on page 2 then pages are distilled in the order: 2, 1, 0, 3, 4.
68 vector<int> page_nums;
69 for (int page = start_page_num; page >= 0; --page)
70 page_nums.push_back(page);
71 for (int page = start_page_num + 1; page < num_pages; ++page)
72 page_nums.push_back(page);
76 struct MultipageDistillerData {
78 MultipageDistillerData() {}
79 ~MultipageDistillerData() {}
80 vector<string> page_urls;
81 vector<string> content;
82 vector<vector<int> > image_ids;
83 // The Javascript values returned by mock distiller.
84 ScopedVector<base::Value> distilled_values;
87 DISALLOW_COPY_AND_ASSIGN(MultipageDistillerData);
90 void VerifyIncrementalUpdatesMatch(
91 const MultipageDistillerData* distiller_data,
92 int num_pages_in_article,
93 const vector<dom_distiller::ArticleDistillationUpdate>& incremental_updates,
95 vector<int> page_seq =
96 GetPagesInSequence(start_page_num, num_pages_in_article);
97 // Updates should contain a list of pages. Pages in an update should be in
98 // the correct ascending page order regardless of |start_page_num|.
99 // E.g. if distillation starts at page 2 of a 3 page article, the updates
100 // will be [[2], [1, 2], [1, 2, 3]]. This example assumes that image fetches
101 // do not delay distillation of a page. There can be scenarios when image
102 // fetch delays distillation of a page (E.g. 1 is delayed due to image
103 // fetches so updates can be in this order [[2], [2,3], [1,2,3]].
104 for (size_t update_count = 0; update_count < incremental_updates.size();
106 const dom_distiller::ArticleDistillationUpdate& update =
107 incremental_updates[update_count];
108 EXPECT_EQ(update_count + 1, update.GetPagesSize());
110 vector<int> expected_page_nums_in_update(
111 page_seq.begin(), page_seq.begin() + update.GetPagesSize());
112 std::sort(expected_page_nums_in_update.begin(),
113 expected_page_nums_in_update.end());
115 // If we already got the first page then there is no previous page.
116 EXPECT_EQ((expected_page_nums_in_update[0] != 0), update.HasPrevPage());
118 // if we already got the last page then there is no next page.
120 (*expected_page_nums_in_update.rbegin()) != num_pages_in_article - 1,
121 update.HasNextPage());
122 for (size_t j = 0; j < update.GetPagesSize(); ++j) {
123 int actual_page_num = expected_page_nums_in_update[j];
124 EXPECT_EQ(distiller_data->page_urls[actual_page_num],
125 update.GetDistilledPage(j).url());
126 EXPECT_EQ(distiller_data->content[actual_page_num],
127 update.GetDistilledPage(j).html());
132 scoped_ptr<MultipageDistillerData> CreateMultipageDistillerDataWithoutImages(
134 scoped_ptr<MultipageDistillerData> result(new MultipageDistillerData());
135 string url_prefix = "http://a.com/";
136 for (size_t page_num = 0; page_num < pages_size; ++page_num) {
137 result->page_urls.push_back(url_prefix + base::IntToString(page_num));
138 result->content.push_back("Content for page:" +
139 base::IntToString(page_num));
140 result->image_ids.push_back(vector<int>());
141 string next_page_url = (page_num + 1 < pages_size)
142 ? url_prefix + base::IntToString(page_num + 1)
144 string prev_page_url =
145 (page_num > 0) ? result->page_urls[page_num - 1] : "";
146 scoped_ptr<base::ListValue> distilled_value =
147 CreateDistilledValueReturnedFromJS(kTitle,
148 result->content[page_num],
149 result->image_ids[page_num],
152 result->distilled_values.push_back(distilled_value.release());
154 return result.Pass();
157 void VerifyArticleProtoMatchesMultipageData(
158 const dom_distiller::DistilledArticleProto* article_proto,
159 const MultipageDistillerData* distiller_data,
161 EXPECT_EQ(pages_size, static_cast<size_t>(article_proto->pages_size()));
162 EXPECT_EQ(kTitle, article_proto->title());
163 for (size_t page_num = 0; page_num < pages_size; ++page_num) {
164 const dom_distiller::DistilledPageProto& page =
165 article_proto->pages(page_num);
166 EXPECT_EQ(distiller_data->content[page_num], page.html());
167 EXPECT_EQ(distiller_data->page_urls[page_num], page.url());
168 EXPECT_EQ(distiller_data->image_ids[page_num].size(),
169 static_cast<size_t>(page.image_size()));
170 const vector<int>& image_ids_for_page = distiller_data->image_ids[page_num];
171 for (size_t img_num = 0; img_num < image_ids_for_page.size(); ++img_num) {
172 EXPECT_EQ(kImageData[image_ids_for_page[img_num]],
173 page.image(img_num).data());
174 EXPECT_EQ(GetImageName(page_num + 1, img_num),
175 page.image(img_num).name());
182 namespace dom_distiller {
184 class TestDistillerURLFetcher : public DistillerURLFetcher {
186 explicit TestDistillerURLFetcher(bool delay_fetch)
187 : DistillerURLFetcher(NULL), delay_fetch_(delay_fetch) {
188 responses_[kImageURLs[0]] = string(kImageData[0]);
189 responses_[kImageURLs[1]] = string(kImageData[1]);
192 virtual void FetchURL(const string& url,
193 const URLFetcherCallback& callback) OVERRIDE {
194 DCHECK(callback_.is_null());
196 callback_ = callback;
202 void PostCallbackTask() {
203 ASSERT_TRUE(base::MessageLoop::current());
204 base::MessageLoop::current()->PostTask(
205 FROM_HERE, base::Bind(callback_, responses_[url_]));
209 std::map<string, string> responses_;
211 URLFetcherCallback callback_;
215 class TestDistillerURLFetcherFactory : public DistillerURLFetcherFactory {
217 TestDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL) {}
219 virtual ~TestDistillerURLFetcherFactory() {}
220 virtual DistillerURLFetcher* CreateDistillerURLFetcher() const OVERRIDE {
221 return new TestDistillerURLFetcher(false);
225 class MockDistillerURLFetcherFactory : public DistillerURLFetcherFactory {
227 explicit MockDistillerURLFetcherFactory()
228 : DistillerURLFetcherFactory(NULL) {}
229 virtual ~MockDistillerURLFetcherFactory() {}
231 MOCK_CONST_METHOD0(CreateDistillerURLFetcher, DistillerURLFetcher*());
234 class MockDistillerPage : public DistillerPage {
236 MOCK_METHOD0(InitImpl, void());
237 MOCK_METHOD1(LoadURLImpl, void(const GURL& gurl));
238 MOCK_METHOD1(ExecuteJavaScriptImpl, void(const string& script));
240 explicit MockDistillerPage(
241 const base::WeakPtr<DistillerPage::Delegate>& delegate)
242 : DistillerPage(delegate) {}
245 class MockDistillerPageFactory : public DistillerPageFactory {
248 CreateDistillerPageMock,
249 DistillerPage*(const base::WeakPtr<DistillerPage::Delegate>& delegate));
251 virtual scoped_ptr<DistillerPage> CreateDistillerPage(
252 const base::WeakPtr<DistillerPage::Delegate>& delegate) const OVERRIDE {
253 return scoped_ptr<DistillerPage>(CreateDistillerPageMock(delegate));
257 class DistillerTest : public testing::Test {
259 virtual ~DistillerTest() {}
260 void OnDistillArticleDone(scoped_ptr<DistilledArticleProto> proto) {
261 article_proto_ = proto.Pass();
264 void OnDistillArticleUpdate(const ArticleDistillationUpdate& article_update) {
265 in_sequence_updates_.push_back(article_update);
268 void DistillPage(const std::string& url) {
269 distiller_->DistillPage(GURL(url),
270 base::Bind(&DistillerTest::OnDistillArticleDone,
271 base::Unretained(this)),
272 base::Bind(&DistillerTest::OnDistillArticleUpdate,
273 base::Unretained(this)));
277 scoped_ptr<DistillerImpl> distiller_;
278 scoped_ptr<DistilledArticleProto> article_proto_;
279 std::vector<ArticleDistillationUpdate> in_sequence_updates_;
280 MockDistillerPageFactory page_factory_;
281 TestDistillerURLFetcherFactory url_fetcher_factory_;
284 ACTION_P3(DistillerPageOnExecuteJavaScriptDone, distiller_page, url, list) {
285 distiller_page->OnExecuteJavaScriptDone(url, list);
288 ACTION_P2(CreateMockDistillerPage, list, kurl) {
289 const base::WeakPtr<DistillerPage::Delegate>& delegate = arg0;
290 MockDistillerPage* distiller_page = new MockDistillerPage(delegate);
291 EXPECT_CALL(*distiller_page, InitImpl());
292 EXPECT_CALL(*distiller_page, LoadURLImpl(kurl))
293 .WillOnce(testing::InvokeWithoutArgs(distiller_page,
294 &DistillerPage::OnLoadURLDone));
295 EXPECT_CALL(*distiller_page, ExecuteJavaScriptImpl(_)).WillOnce(
296 DistillerPageOnExecuteJavaScriptDone(distiller_page, kurl, list));
297 return distiller_page;
300 ACTION_P2(CreateMockDistillerPageWithPendingJSCallback,
303 const base::WeakPtr<DistillerPage::Delegate>& delegate = arg0;
304 MockDistillerPage* distiller_page = new MockDistillerPage(delegate);
305 *distiller_page_ptr = distiller_page;
306 EXPECT_CALL(*distiller_page, InitImpl());
307 EXPECT_CALL(*distiller_page, LoadURLImpl(kurl))
308 .WillOnce(testing::InvokeWithoutArgs(distiller_page,
309 &DistillerPage::OnLoadURLDone));
310 EXPECT_CALL(*distiller_page, ExecuteJavaScriptImpl(_));
311 return distiller_page;
314 ACTION_P3(CreateMockDistillerPages,
318 const base::WeakPtr<DistillerPage::Delegate>& delegate = arg0;
319 MockDistillerPage* distiller_page = new MockDistillerPage(delegate);
320 EXPECT_CALL(*distiller_page, InitImpl());
322 testing::InSequence s;
323 vector<int> page_nums = GetPagesInSequence(start_page_num, pages_size);
324 for (size_t page_num = 0; page_num < pages_size; ++page_num) {
325 int page = page_nums[page_num];
326 GURL url = GURL(distiller_data->page_urls[page]);
327 EXPECT_CALL(*distiller_page, LoadURLImpl(url))
328 .WillOnce(testing::InvokeWithoutArgs(distiller_page,
329 &DistillerPage::OnLoadURLDone));
330 EXPECT_CALL(*distiller_page, ExecuteJavaScriptImpl(_))
331 .WillOnce(DistillerPageOnExecuteJavaScriptDone(
332 distiller_page, url, distiller_data->distilled_values[page]));
335 return distiller_page;
338 TEST_F(DistillerTest, DistillPage) {
339 base::MessageLoopForUI loop;
340 scoped_ptr<base::ListValue> list =
341 CreateDistilledValueReturnedFromJS(kTitle, kContent, vector<int>(), "");
342 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
343 .WillOnce(CreateMockDistillerPage(list.get(), GURL(kURL)));
344 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
347 base::MessageLoop::current()->RunUntilIdle();
348 EXPECT_EQ(kTitle, article_proto_->title());
349 EXPECT_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, DistillPageWithImages) {
356 base::MessageLoopForUI loop;
357 vector<int> image_indices;
358 image_indices.push_back(0);
359 image_indices.push_back(1);
360 scoped_ptr<base::ListValue> list =
361 CreateDistilledValueReturnedFromJS(kTitle, kContent, image_indices, "");
362 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
363 .WillOnce(CreateMockDistillerPage(list.get(), GURL(kURL)));
364 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
367 base::MessageLoop::current()->RunUntilIdle();
368 EXPECT_EQ(kTitle, article_proto_->title());
369 EXPECT_EQ(article_proto_->pages_size(), 1);
370 const DistilledPageProto& first_page = article_proto_->pages(0);
371 EXPECT_EQ(kContent, first_page.html());
372 EXPECT_EQ(kURL, first_page.url());
373 EXPECT_EQ(2, first_page.image_size());
374 EXPECT_EQ(kImageData[0], first_page.image(0).data());
375 EXPECT_EQ(GetImageName(1, 0), first_page.image(0).name());
376 EXPECT_EQ(kImageData[1], first_page.image(1).data());
377 EXPECT_EQ(GetImageName(1, 1), first_page.image(1).name());
380 TEST_F(DistillerTest, DistillMultiplePages) {
381 base::MessageLoopForUI loop;
382 const size_t kNumPages = 8;
383 scoped_ptr<MultipageDistillerData> distiller_data =
384 CreateMultipageDistillerDataWithoutImages(kNumPages);
387 int next_image_number = 0;
388 for (size_t page_num = 0; page_num < kNumPages; ++page_num) {
389 // Each page has different number of images.
390 size_t tot_images = (page_num + kTotalImages) % (kTotalImages + 1);
391 vector<int> image_indices;
392 for (size_t img_num = 0; img_num < tot_images; img_num++) {
393 image_indices.push_back(next_image_number);
394 next_image_number = (next_image_number + 1) % kTotalImages;
396 distiller_data->image_ids.push_back(image_indices);
399 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
400 .WillOnce(CreateMockDistillerPages(distiller_data.get(), kNumPages, 0));
402 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
404 DistillPage(distiller_data->page_urls[0]);
405 base::MessageLoop::current()->RunUntilIdle();
406 VerifyArticleProtoMatchesMultipageData(
407 article_proto_.get(), distiller_data.get(), kNumPages);
410 TEST_F(DistillerTest, DistillLinkLoop) {
411 base::MessageLoopForUI loop;
412 // Create a loop, the next page is same as the current page. This could
413 // happen if javascript misparses a next page link.
414 scoped_ptr<base::ListValue> list =
415 CreateDistilledValueReturnedFromJS(kTitle, kContent, vector<int>(), kURL);
416 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
417 .WillOnce(CreateMockDistillerPage(list.get(), GURL(kURL)));
418 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
421 base::MessageLoop::current()->RunUntilIdle();
422 EXPECT_EQ(kTitle, article_proto_->title());
423 EXPECT_EQ(article_proto_->pages_size(), 1);
426 TEST_F(DistillerTest, CheckMaxPageLimitExtraPage) {
427 base::MessageLoopForUI loop;
428 const size_t kMaxPagesInArticle = 10;
429 scoped_ptr<MultipageDistillerData> distiller_data =
430 CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle);
432 // Note: Next page url of the last page of article is set. So distiller will
433 // try to do kMaxPagesInArticle + 1 calls if the max article limit does not
435 scoped_ptr<base::ListValue> last_page_data =
436 CreateDistilledValueReturnedFromJS(
438 distiller_data->content[kMaxPagesInArticle - 1],
441 distiller_data->page_urls[kMaxPagesInArticle - 2]);
443 distiller_data->distilled_values.pop_back();
444 distiller_data->distilled_values.push_back(last_page_data.release());
446 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_)).WillOnce(
447 CreateMockDistillerPages(distiller_data.get(), kMaxPagesInArticle, 0));
448 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
450 distiller_->SetMaxNumPagesInArticle(kMaxPagesInArticle);
453 DistillPage(distiller_data->page_urls[0]);
454 base::MessageLoop::current()->RunUntilIdle();
455 EXPECT_EQ(kTitle, article_proto_->title());
456 EXPECT_EQ(kMaxPagesInArticle,
457 static_cast<size_t>(article_proto_->pages_size()));
460 TEST_F(DistillerTest, CheckMaxPageLimitExactLimit) {
461 base::MessageLoopForUI loop;
462 const size_t kMaxPagesInArticle = 10;
463 scoped_ptr<MultipageDistillerData> distiller_data =
464 CreateMultipageDistillerDataWithoutImages(kMaxPagesInArticle);
466 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_)).WillOnce(
467 CreateMockDistillerPages(distiller_data.get(), kMaxPagesInArticle, 0));
468 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
470 // Check if distilling an article with exactly the page limit works.
471 distiller_->SetMaxNumPagesInArticle(kMaxPagesInArticle);
475 DistillPage(distiller_data->page_urls[0]);
476 base::MessageLoop::current()->RunUntilIdle();
477 EXPECT_EQ(kTitle, article_proto_->title());
478 EXPECT_EQ(kMaxPagesInArticle,
479 static_cast<size_t>(article_proto_->pages_size()));
482 TEST_F(DistillerTest, SinglePageDistillationFailure) {
483 base::MessageLoopForUI loop;
484 // To simulate failure return a null value.
485 scoped_ptr<base::Value> nullValue(base::Value::CreateNullValue());
486 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
487 .WillOnce(CreateMockDistillerPage(nullValue.get(), GURL(kURL)));
488 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
491 base::MessageLoop::current()->RunUntilIdle();
492 EXPECT_EQ("", article_proto_->title());
493 EXPECT_EQ(0, article_proto_->pages_size());
496 TEST_F(DistillerTest, MultiplePagesDistillationFailure) {
497 base::MessageLoopForUI loop;
498 const size_t kNumPages = 8;
499 scoped_ptr<MultipageDistillerData> distiller_data =
500 CreateMultipageDistillerDataWithoutImages(kNumPages);
502 // The page number of the failed page.
503 size_t failed_page_num = 3;
504 // reset distilled data of the failed page.
505 distiller_data->distilled_values.erase(
506 distiller_data->distilled_values.begin() + failed_page_num);
507 distiller_data->distilled_values.insert(
508 distiller_data->distilled_values.begin() + failed_page_num,
509 base::Value::CreateNullValue());
510 // Expect only calls till the failed page number.
511 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_)).WillOnce(
512 CreateMockDistillerPages(distiller_data.get(), failed_page_num + 1, 0));
514 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
516 DistillPage(distiller_data->page_urls[0]);
517 base::MessageLoop::current()->RunUntilIdle();
518 EXPECT_EQ(kTitle, article_proto_->title());
519 VerifyArticleProtoMatchesMultipageData(
520 article_proto_.get(), distiller_data.get(), failed_page_num);
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);
532 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
533 .WillOnce(CreateMockDistillerPages(
534 distiller_data.get(), kNumPages, start_page_num));
536 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
538 DistillPage(distiller_data->page_urls[start_page_num]);
539 base::MessageLoop::current()->RunUntilIdle();
540 VerifyArticleProtoMatchesMultipageData(
541 article_proto_.get(), distiller_data.get(), kNumPages);
544 TEST_F(DistillerTest, IncrementalUpdates) {
545 base::MessageLoopForUI loop;
546 const size_t kNumPages = 8;
548 // The page number of the article on which distillation starts.
549 int start_page_num = 3;
550 scoped_ptr<MultipageDistillerData> distiller_data =
551 CreateMultipageDistillerDataWithoutImages(kNumPages);
553 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
554 .WillOnce(CreateMockDistillerPages(
555 distiller_data.get(), kNumPages, start_page_num));
557 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
559 DistillPage(distiller_data->page_urls[start_page_num]);
560 base::MessageLoop::current()->RunUntilIdle();
561 EXPECT_EQ(kTitle, article_proto_->title());
562 EXPECT_EQ(kNumPages, static_cast<size_t>(article_proto_->pages_size()));
563 EXPECT_EQ(kNumPages, in_sequence_updates_.size());
565 VerifyIncrementalUpdatesMatch(
566 distiller_data.get(), kNumPages, in_sequence_updates_, start_page_num);
569 TEST_F(DistillerTest, IncrementalUpdatesDoNotDeleteFinalArticle) {
570 base::MessageLoopForUI loop;
571 const size_t kNumPages = 8;
572 int start_page_num = 3;
573 scoped_ptr<MultipageDistillerData> distiller_data =
574 CreateMultipageDistillerDataWithoutImages(kNumPages);
576 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
577 .WillOnce(CreateMockDistillerPages(
578 distiller_data.get(), kNumPages, start_page_num));
580 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
582 DistillPage(distiller_data->page_urls[start_page_num]);
583 base::MessageLoop::current()->RunUntilIdle();
584 EXPECT_EQ(kNumPages, in_sequence_updates_.size());
586 in_sequence_updates_.clear();
588 // Should still be able to access article and pages.
589 VerifyArticleProtoMatchesMultipageData(
590 article_proto_.get(), distiller_data.get(), kNumPages);
593 TEST_F(DistillerTest, DeletingArticleDoesNotInterfereWithUpdates) {
594 base::MessageLoopForUI loop;
595 const size_t kNumPages = 8;
596 scoped_ptr<MultipageDistillerData> distiller_data =
597 CreateMultipageDistillerDataWithoutImages(kNumPages);
598 // The page number of the article on which distillation starts.
599 int start_page_num = 3;
601 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
602 .WillOnce(CreateMockDistillerPages(
603 distiller_data.get(), kNumPages, start_page_num));
605 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
607 DistillPage(distiller_data->page_urls[start_page_num]);
608 base::MessageLoop::current()->RunUntilIdle();
609 EXPECT_EQ(kNumPages, in_sequence_updates_.size());
610 EXPECT_EQ(kTitle, article_proto_->title());
611 EXPECT_EQ(kNumPages, static_cast<size_t>(article_proto_->pages_size()));
613 // Delete the article.
614 article_proto_.reset();
615 VerifyIncrementalUpdatesMatch(
616 distiller_data.get(), kNumPages, in_sequence_updates_, start_page_num);
619 TEST_F(DistillerTest, CancelWithDelayedImageFetchCallback) {
620 base::MessageLoopForUI loop;
621 vector<int> image_indices;
622 image_indices.push_back(0);
623 scoped_ptr<base::ListValue> distilled_value =
624 CreateDistilledValueReturnedFromJS(kTitle, kContent, image_indices, "");
625 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
626 .WillOnce(CreateMockDistillerPage(distilled_value.get(), GURL(kURL)));
628 TestDistillerURLFetcher* delayed_fetcher = new TestDistillerURLFetcher(true);
629 MockDistillerURLFetcherFactory url_fetcher_factory;
630 EXPECT_CALL(url_fetcher_factory, CreateDistillerURLFetcher())
631 .WillOnce(Return(delayed_fetcher));
632 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory));
635 base::MessageLoop::current()->RunUntilIdle();
637 // Post callback from the url fetcher and then delete the distiller.
638 delayed_fetcher->PostCallbackTask();
641 base::MessageLoop::current()->RunUntilIdle();
644 TEST_F(DistillerTest, CancelWithDelayedJSCallback) {
645 base::MessageLoopForUI loop;
646 scoped_ptr<base::ListValue> distilled_value =
647 CreateDistilledValueReturnedFromJS(kTitle, kContent, vector<int>(), "");
648 MockDistillerPage* distiller_page = NULL;
649 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_))
650 .WillOnce(CreateMockDistillerPageWithPendingJSCallback(&distiller_page,
652 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_));
655 base::MessageLoop::current()->RunUntilIdle();
657 ASSERT_TRUE(distiller_page);
658 // Post the task to execute javascript and then delete the distiller.
659 distiller_page->OnExecuteJavaScriptDone(GURL(kURL), distilled_value.get());
662 base::MessageLoop::current()->RunUntilIdle();
665 } // namespace dom_distiller