OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <map> | 5 #include <map> |
| 6 #include <string> |
| 7 #include <vector> |
6 | 8 |
7 #include "base/bind.h" | 9 #include "base/bind.h" |
8 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
9 #include "base/location.h" | 11 #include "base/location.h" |
10 #include "base/memory/scoped_ptr.h" | 12 #include "base/memory/scoped_ptr.h" |
11 #include "base/message_loop/message_loop.h" | 13 #include "base/message_loop/message_loop.h" |
| 14 #include "base/strings/string_number_conversions.h" |
12 #include "base/values.h" | 15 #include "base/values.h" |
13 #include "components/dom_distiller/core/distiller.h" | 16 #include "components/dom_distiller/core/distiller.h" |
14 #include "components/dom_distiller/core/distiller_page.h" | 17 #include "components/dom_distiller/core/distiller_page.h" |
| 18 #include "components/dom_distiller/core/proto/distilled_article.pb.h" |
15 #include "components/dom_distiller/core/proto/distilled_page.pb.h" | 19 #include "components/dom_distiller/core/proto/distilled_page.pb.h" |
16 #include "net/url_request/url_request_context_getter.h" | 20 #include "net/url_request/url_request_context_getter.h" |
17 #include "testing/gmock/include/gmock/gmock.h" | 21 #include "testing/gmock/include/gmock/gmock.h" |
18 #include "testing/gtest/include/gtest/gtest.h" | 22 #include "testing/gtest/include/gtest/gtest.h" |
19 | 23 |
| 24 using std::vector; |
| 25 using std::string; |
20 using::testing::Invoke; | 26 using::testing::Invoke; |
21 using::testing::Return; | 27 using::testing::Return; |
22 using::testing::_; | 28 using::testing::_; |
23 | 29 |
24 namespace { | 30 namespace { |
25 const char kTitle[] = "Title"; | 31 const char kTitle[] = "Title"; |
26 const char kContent[] = "Content"; | 32 const char kContent[] = "Content"; |
27 const char kURL[] = "http://a.com/"; | 33 const char kURL[] = "http://a.com/"; |
28 const char kId0[] = "0"; | 34 const size_t kTotalImages = 2; |
29 const char kId1[] = "1"; | 35 const char* kImageURLs[kTotalImages] = {"http://a.com/img1.jpg", |
30 const char kImageURL0[] = "http://a.com/img1.jpg"; | 36 "http://a.com/img2.jpg"}; |
31 const char kImageURL1[] = "http://a.com/img2.jpg"; | 37 const char* kImageData[kTotalImages] = {"abcde", "12345"}; |
32 const char kImageData0[] = { 'a', 'b', 'c', 'd', 'e', 0 }; | 38 |
33 const char kImageData1[] = { '1', '2', '3', '4', '5', 0 }; | 39 const string GetImageName(int page_num, int image_num) { |
| 40 return base::IntToString(page_num) + "_" + base::IntToString(image_num); |
| 41 } |
| 42 |
| 43 scoped_ptr<base::ListValue> CreateDistilledValueReturnedFromJS( |
| 44 const string& title, |
| 45 const string& content, |
| 46 const vector<int>& image_indices, |
| 47 const string& next_page_url) { |
| 48 scoped_ptr<base::ListValue> list(new base::ListValue()); |
| 49 |
| 50 list->AppendString(title); |
| 51 list->AppendString(content); |
| 52 list->AppendString(next_page_url); |
| 53 for (size_t i = 0; i < image_indices.size(); ++i) { |
| 54 list->AppendString(kImageURLs[image_indices[i]]); |
| 55 } |
| 56 return list.Pass(); |
| 57 } |
| 58 |
34 } // namespace | 59 } // namespace |
35 | 60 |
36 namespace dom_distiller { | 61 namespace dom_distiller { |
37 | 62 |
38 class TestDistillerURLFetcher : public DistillerURLFetcher { | 63 class TestDistillerURLFetcher : public DistillerURLFetcher { |
39 public: | 64 public: |
40 TestDistillerURLFetcher() : DistillerURLFetcher(NULL) { | 65 TestDistillerURLFetcher() : DistillerURLFetcher(NULL) { |
41 responses_[kImageURL0] = std::string(kImageData0); | 66 responses_[kImageURLs[0]] = string(kImageData[0]); |
42 responses_[kImageURL1] = std::string(kImageData1); | 67 responses_[kImageURLs[1]] = string(kImageData[1]); |
43 } | 68 } |
44 | 69 |
45 void CallCallback(std::string url, const URLFetcherCallback& callback) { | 70 void CallCallback(string url, const URLFetcherCallback& callback) { |
46 callback.Run(responses_[url]); | 71 callback.Run(responses_[url]); |
47 } | 72 } |
48 | 73 |
49 virtual void FetchURL(const std::string& url, | 74 virtual void FetchURL(const string& url, |
50 const URLFetcherCallback& callback) OVERRIDE { | 75 const URLFetcherCallback& callback) OVERRIDE { |
51 ASSERT_TRUE(base::MessageLoop::current()); | 76 ASSERT_TRUE(base::MessageLoop::current()); |
52 base::MessageLoop::current()->PostTask( | 77 base::MessageLoop::current()->PostTask( |
53 FROM_HERE, | 78 FROM_HERE, |
54 base::Bind(&TestDistillerURLFetcher::CallCallback, | 79 base::Bind(&TestDistillerURLFetcher::CallCallback, |
55 base::Unretained(this), url, callback)); | 80 base::Unretained(this), url, callback)); |
56 } | 81 } |
57 | 82 |
58 std::map<std::string, std::string> responses_; | 83 std::map<string, string> responses_; |
59 }; | 84 }; |
60 | 85 |
61 | 86 |
62 class TestDistillerURLFetcherFactory : public DistillerURLFetcherFactory { | 87 class TestDistillerURLFetcherFactory : public DistillerURLFetcherFactory { |
63 public: | 88 public: |
64 TestDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL) {} | 89 TestDistillerURLFetcherFactory() : DistillerURLFetcherFactory(NULL) {} |
65 virtual ~TestDistillerURLFetcherFactory() {} | 90 virtual ~TestDistillerURLFetcherFactory() {} |
66 virtual DistillerURLFetcher* CreateDistillerURLFetcher() const OVERRIDE { | 91 virtual DistillerURLFetcher* CreateDistillerURLFetcher() const OVERRIDE { |
67 return new TestDistillerURLFetcher(); | 92 return new TestDistillerURLFetcher(); |
68 } | 93 } |
69 }; | 94 }; |
70 | 95 |
71 | 96 |
72 class MockDistillerPage : public DistillerPage { | 97 class MockDistillerPage : public DistillerPage { |
73 public: | 98 public: |
74 MOCK_METHOD0(InitImpl, void()); | 99 MOCK_METHOD0(InitImpl, void()); |
75 MOCK_METHOD1(LoadURLImpl, void(const GURL& gurl)); | 100 MOCK_METHOD1(LoadURLImpl, void(const GURL& gurl)); |
76 MOCK_METHOD1(ExecuteJavaScriptImpl, void(const std::string& script)); | 101 MOCK_METHOD1(ExecuteJavaScriptImpl, void(const string& script)); |
77 | 102 |
78 explicit MockDistillerPage(DistillerPage::Delegate* delegate) | 103 explicit MockDistillerPage(DistillerPage::Delegate* delegate) |
79 : DistillerPage(delegate) {} | 104 : DistillerPage(delegate) {} |
80 }; | 105 }; |
81 | 106 |
82 | 107 |
83 class MockDistillerPageFactory : public DistillerPageFactory { | 108 class MockDistillerPageFactory : public DistillerPageFactory { |
84 public: | 109 public: |
85 MOCK_CONST_METHOD1( | 110 MOCK_CONST_METHOD1( |
86 CreateDistillerPageMock, | 111 CreateDistillerPageMock, |
87 DistillerPage*(DistillerPage::Delegate* delegate)); | 112 DistillerPage*(DistillerPage::Delegate* delegate)); |
88 | 113 |
89 virtual scoped_ptr<DistillerPage> CreateDistillerPage( | 114 virtual scoped_ptr<DistillerPage> CreateDistillerPage( |
90 DistillerPage::Delegate* delegate) const OVERRIDE { | 115 DistillerPage::Delegate* delegate) const OVERRIDE { |
91 return scoped_ptr<DistillerPage>(CreateDistillerPageMock(delegate)); | 116 return scoped_ptr<DistillerPage>(CreateDistillerPageMock(delegate)); |
92 } | 117 } |
93 }; | 118 }; |
94 | 119 |
95 | 120 |
96 class DistillerTest : public testing::Test { | 121 class DistillerTest : public testing::Test { |
97 public: | 122 public: |
98 virtual ~DistillerTest() {} | 123 virtual ~DistillerTest() {} |
99 void OnDistillPageDone(scoped_ptr<DistilledPageProto> proto) { | 124 void OnDistillPageDone(scoped_ptr<DistilledArticleProto> proto) { |
100 proto_ = proto.Pass(); | 125 article_proto_ = proto.Pass(); |
101 } | 126 } |
102 | 127 |
103 protected: | 128 protected: |
104 scoped_ptr<DistillerImpl> distiller_; | 129 scoped_ptr<DistillerImpl> distiller_; |
105 scoped_ptr<DistilledPageProto> proto_; | 130 scoped_ptr<DistilledArticleProto> article_proto_; |
106 MockDistillerPageFactory page_factory_; | 131 MockDistillerPageFactory page_factory_; |
107 TestDistillerURLFetcherFactory url_fetcher_factory_; | 132 TestDistillerURLFetcherFactory url_fetcher_factory_; |
108 }; | 133 }; |
109 | 134 |
110 ACTION_P2(DistillerPageOnExecuteJavaScriptDone, distiller_page, list) { | 135 ACTION_P3(DistillerPageOnExecuteJavaScriptDone, distiller_page, url, list) { |
111 distiller_page->OnExecuteJavaScriptDone(list); | 136 distiller_page->OnExecuteJavaScriptDone(url, list); |
112 } | 137 } |
113 | 138 |
114 ACTION_P2(CreateMockDistillerPage, list, kurl) { | 139 ACTION_P2(CreateMockDistillerPage, list, kurl) { |
115 DistillerPage::Delegate* delegate = arg0; | 140 DistillerPage::Delegate* delegate = arg0; |
116 MockDistillerPage* distiller_page = new MockDistillerPage(delegate); | 141 MockDistillerPage* distiller_page = new MockDistillerPage(delegate); |
117 EXPECT_CALL(*distiller_page, InitImpl()); | 142 EXPECT_CALL(*distiller_page, InitImpl()); |
118 EXPECT_CALL(*distiller_page, LoadURLImpl(kurl)) | 143 EXPECT_CALL(*distiller_page, LoadURLImpl(kurl)) |
119 .WillOnce(testing::InvokeWithoutArgs(distiller_page, | 144 .WillOnce(testing::InvokeWithoutArgs(distiller_page, |
120 &DistillerPage::OnLoadURLDone)); | 145 &DistillerPage::OnLoadURLDone)); |
121 EXPECT_CALL(*distiller_page, ExecuteJavaScriptImpl(_)) | 146 EXPECT_CALL(*distiller_page, ExecuteJavaScriptImpl(_)).WillOnce( |
122 .WillOnce(DistillerPageOnExecuteJavaScriptDone(distiller_page, list)); | 147 DistillerPageOnExecuteJavaScriptDone(distiller_page, kurl, list)); |
| 148 return distiller_page; |
| 149 } |
| 150 |
| 151 ACTION_P3(CreateMockDistillerPages, lists, kurls, num_pages) { |
| 152 DistillerPage::Delegate* delegate = arg0; |
| 153 MockDistillerPage* distiller_page = new MockDistillerPage(delegate); |
| 154 EXPECT_CALL(*distiller_page, InitImpl()); |
| 155 { |
| 156 testing::InSequence s; |
| 157 |
| 158 for (int page = 0; page < num_pages; ++page) { |
| 159 GURL url = GURL(kurls[page]); |
| 160 EXPECT_CALL(*distiller_page, LoadURLImpl(url)) |
| 161 .WillOnce(testing::InvokeWithoutArgs(distiller_page, |
| 162 &DistillerPage::OnLoadURLDone)); |
| 163 EXPECT_CALL(*distiller_page, ExecuteJavaScriptImpl(_)) |
| 164 .WillOnce(DistillerPageOnExecuteJavaScriptDone( |
| 165 distiller_page, url, lists[page].get())); |
| 166 } |
| 167 } |
123 return distiller_page; | 168 return distiller_page; |
124 } | 169 } |
125 | 170 |
126 TEST_F(DistillerTest, DistillPage) { | 171 TEST_F(DistillerTest, DistillPage) { |
127 base::MessageLoopForUI loop; | 172 base::MessageLoopForUI loop; |
128 scoped_ptr<base::ListValue> list(new base::ListValue()); | 173 scoped_ptr<base::ListValue> list = |
129 list->AppendString(kTitle); | 174 CreateDistilledValueReturnedFromJS(kTitle, kContent, vector<int>(), ""); |
130 list->AppendString(kContent); | 175 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_)) |
131 list->AppendString(kImageURL0); | 176 .WillOnce(CreateMockDistillerPage(list.get(), GURL(kURL))); |
132 list->AppendString(kImageURL1); | 177 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_)); |
| 178 distiller_->Init(); |
| 179 distiller_->DistillPage( |
| 180 GURL(kURL), |
| 181 base::Bind(&DistillerTest::OnDistillPageDone, base::Unretained(this))); |
| 182 base::MessageLoop::current()->RunUntilIdle(); |
| 183 EXPECT_EQ(kTitle, article_proto_->title()); |
| 184 EXPECT_EQ(article_proto_->pages_size(), 1); |
| 185 const DistilledPageProto& first_page = article_proto_->pages(0); |
| 186 EXPECT_EQ(kContent, first_page.html()); |
| 187 EXPECT_EQ(kURL, first_page.url()); |
| 188 } |
| 189 |
| 190 TEST_F(DistillerTest, DistillPageWithImages) { |
| 191 base::MessageLoopForUI loop; |
| 192 vector<int> image_indices; |
| 193 image_indices.push_back(0); |
| 194 image_indices.push_back(1); |
| 195 scoped_ptr<base::ListValue> list = |
| 196 CreateDistilledValueReturnedFromJS(kTitle, kContent, image_indices, ""); |
133 EXPECT_CALL(page_factory_, | 197 EXPECT_CALL(page_factory_, |
134 CreateDistillerPageMock(_)).WillOnce( | 198 CreateDistillerPageMock(_)).WillOnce( |
135 CreateMockDistillerPage(list.get(), GURL(kURL))); | 199 CreateMockDistillerPage(list.get(), GURL(kURL))); |
136 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_)); | 200 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_)); |
137 distiller_->Init(); | 201 distiller_->Init(); |
138 distiller_->DistillPage( | 202 distiller_->DistillPage( |
139 GURL(kURL), | 203 GURL(kURL), |
140 base::Bind(&DistillerTest::OnDistillPageDone, base::Unretained(this))); | 204 base::Bind(&DistillerTest::OnDistillPageDone, base::Unretained(this))); |
141 base::MessageLoop::current()->RunUntilIdle(); | 205 base::MessageLoop::current()->RunUntilIdle(); |
142 EXPECT_EQ(kTitle, proto_->title()); | 206 EXPECT_EQ(kTitle, article_proto_->title()); |
143 EXPECT_EQ(kContent, proto_->html()); | 207 EXPECT_EQ(article_proto_->pages_size(), 1); |
144 EXPECT_EQ(kURL, proto_->url()); | 208 const DistilledPageProto& first_page = article_proto_->pages(0); |
145 EXPECT_EQ(2, proto_->image_size()); | 209 EXPECT_EQ(kContent, first_page.html()); |
146 EXPECT_EQ(kImageData0, proto_->image(0).data()); | 210 EXPECT_EQ(kURL, first_page.url()); |
147 EXPECT_EQ(kId0, proto_->image(0).name()); | 211 EXPECT_EQ(2, first_page.image_size()); |
148 EXPECT_EQ(kImageData1, proto_->image(1).data()); | 212 EXPECT_EQ(kImageData[0], first_page.image(0).data()); |
149 EXPECT_EQ(kId1, proto_->image(1).name()); | 213 EXPECT_EQ(GetImageName(1, 0), first_page.image(0).name()); |
| 214 EXPECT_EQ(kImageData[1], first_page.image(1).data()); |
| 215 EXPECT_EQ(GetImageName(1, 1), first_page.image(1).name()); |
| 216 } |
| 217 |
| 218 TEST_F(DistillerTest, DistillMultiplePages) { |
| 219 base::MessageLoopForUI loop; |
| 220 const int kNumPages = 8; |
| 221 vector<int> image_indices[kNumPages]; |
| 222 string content[kNumPages]; |
| 223 string page_urls[kNumPages]; |
| 224 scoped_ptr<base::ListValue> list[kNumPages]; |
| 225 |
| 226 int next_image_number = 0; |
| 227 |
| 228 for (int page_num = 0; page_num < kNumPages; ++page_num) { |
| 229 // Each page has different number of images. |
| 230 int tot_images = (page_num + kTotalImages) % (kTotalImages + 1); |
| 231 for (int img_num = 0; img_num < tot_images; img_num++) { |
| 232 image_indices[page_num].push_back(next_image_number); |
| 233 next_image_number = (next_image_number + 1) % kTotalImages; |
| 234 } |
| 235 |
| 236 page_urls[page_num] = "http://a.com/" + base::IntToString(page_num); |
| 237 content[page_num] = "Content for page:" + base::IntToString(page_num); |
| 238 } |
| 239 for (int i = 0; i < kNumPages; ++i) { |
| 240 string next_page_url = ""; |
| 241 if (i + 1 < kNumPages) |
| 242 next_page_url = page_urls[i + 1]; |
| 243 |
| 244 list[i] = CreateDistilledValueReturnedFromJS( |
| 245 kTitle, content[i], image_indices[i], next_page_url); |
| 246 } |
| 247 |
| 248 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_)) |
| 249 .WillOnce(CreateMockDistillerPages(list, page_urls, kNumPages)); |
| 250 |
| 251 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_)); |
| 252 distiller_->Init(); |
| 253 distiller_->DistillPage( |
| 254 GURL(page_urls[0]), |
| 255 base::Bind(&DistillerTest::OnDistillPageDone, base::Unretained(this))); |
| 256 base::MessageLoop::current()->RunUntilIdle(); |
| 257 EXPECT_EQ(kTitle, article_proto_->title()); |
| 258 EXPECT_EQ(article_proto_->pages_size(), kNumPages); |
| 259 for (int page_num = 0; page_num < kNumPages; ++page_num) { |
| 260 const DistilledPageProto& page = article_proto_->pages(page_num); |
| 261 EXPECT_EQ(content[page_num], page.html()); |
| 262 EXPECT_EQ(page_urls[page_num], page.url()); |
| 263 EXPECT_EQ(image_indices[page_num].size(), |
| 264 static_cast<size_t>(page.image_size())); |
| 265 for (size_t img_num = 0; img_num < image_indices[page_num].size(); |
| 266 ++img_num) { |
| 267 EXPECT_EQ(kImageData[image_indices[page_num][img_num]], |
| 268 page.image(img_num).data()); |
| 269 EXPECT_EQ(GetImageName(page_num + 1, img_num), |
| 270 page.image(img_num).name()); |
| 271 } |
| 272 } |
| 273 } |
| 274 |
| 275 TEST_F(DistillerTest, DistillLinkLoop) { |
| 276 base::MessageLoopForUI loop; |
| 277 // Create a loop, the next page is same as the current page. This could |
| 278 // happen if javascript misparses a next page link. |
| 279 scoped_ptr<base::ListValue> list = |
| 280 CreateDistilledValueReturnedFromJS(kTitle, kContent, vector<int>(), kURL); |
| 281 EXPECT_CALL(page_factory_, CreateDistillerPageMock(_)) |
| 282 .WillOnce(CreateMockDistillerPage(list.get(), GURL(kURL))); |
| 283 distiller_.reset(new DistillerImpl(page_factory_, url_fetcher_factory_)); |
| 284 distiller_->Init(); |
| 285 distiller_->DistillPage( |
| 286 GURL(kURL), |
| 287 base::Bind(&DistillerTest::OnDistillPageDone, base::Unretained(this))); |
| 288 base::MessageLoop::current()->RunUntilIdle(); |
| 289 EXPECT_EQ(kTitle, article_proto_->title()); |
| 290 EXPECT_EQ(article_proto_->pages_size(), 1); |
150 } | 291 } |
151 | 292 |
152 } // namespace dom_distiller | 293 } // namespace dom_distiller |
OLD | NEW |