OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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. |
| 4 |
| 5 #include "components/offline_pages/core/prefetch/generate_page_bundle_request.h" |
| 6 |
| 7 #include "base/test/mock_callback.h" |
| 8 #include "components/offline_pages/core/prefetch/prefetch_request_test_base.h" |
| 9 #include "components/offline_pages/core/prefetch/prefetch_types.h" |
| 10 #include "components/offline_pages/core/prefetch/prefetch_utils.h" |
| 11 #include "components/offline_pages/core/prefetch/proto/offline_pages.pb.h" |
| 12 #include "components/offline_pages/core/prefetch/proto/operation.pb.h" |
| 13 #include "net/http/http_status_code.h" |
| 14 #include "net/url_request/url_request_status.h" |
| 15 #include "testing/gmock/include/gmock/gmock.h" |
| 16 #include "url/gurl.h" |
| 17 |
| 18 using testing::_; |
| 19 using testing::DoAll; |
| 20 using testing::Eq; |
| 21 using testing::SaveArg; |
| 22 |
| 23 namespace offline_pages { |
| 24 |
| 25 namespace { |
| 26 const char kTestURL[] = "http://example.com"; |
| 27 const char kTestURL2[] = "http://example.com/2"; |
| 28 const char kTestURL3[] = "http://example.com/3"; |
| 29 const char kTestURL4[] = "http://example.com/4"; |
| 30 const char kTestUserAgent[] = "Test User Agent"; |
| 31 const char kTestGCMID[] = "Test GCM ID"; |
| 32 const int kTestMaxBundleSize = 100000; |
| 33 const char kTestBodyName[] = "body_name"; |
| 34 const int64_t kTestBodyLength = 12345678LL; |
| 35 } // namespace |
| 36 |
| 37 class OperationBuilder { |
| 38 public: |
| 39 virtual ~OperationBuilder() {} |
| 40 |
| 41 // Builds the opereation with an Any value and returns it in binary serialized |
| 42 // format. |
| 43 virtual std::string BuildFromAny(const std::string& any_type_url, |
| 44 const std::string& any_value) = 0; |
| 45 |
| 46 // Builds the opereation with with an Any value filled with page bundle data |
| 47 // and returns it in binary serialized format. |
| 48 std::string BuildFromPageBundle(const proto::PageBundle& bundle) { |
| 49 std::string bundle_data; |
| 50 EXPECT_TRUE(bundle.SerializeToString(&bundle_data)); |
| 51 return BuildFromAny(kPageBundleTypeURL, bundle_data); |
| 52 } |
| 53 |
| 54 protected: |
| 55 // Helper function to build the operation based on |is_done| that controls |
| 56 // where Any value goes. |
| 57 std::string BuildOperation(bool is_done, |
| 58 const std::string& any_type_url, |
| 59 const std::string& any_value) { |
| 60 proto::Operation operation; |
| 61 operation.set_done(is_done); |
| 62 proto::Any* any = |
| 63 is_done ? operation.mutable_response() : operation.mutable_metadata(); |
| 64 any->set_type_url(any_type_url); |
| 65 any->set_value(any_value); |
| 66 std::string data; |
| 67 EXPECT_TRUE(operation.SerializeToString(&data)); |
| 68 return data; |
| 69 } |
| 70 }; |
| 71 |
| 72 class DoneOperationBuilder : public OperationBuilder { |
| 73 public: |
| 74 ~DoneOperationBuilder() override {} |
| 75 |
| 76 std::string BuildFromAny(const std::string& any_type_url, |
| 77 const std::string& any_value) override { |
| 78 return BuildOperation(true, any_type_url, any_value); |
| 79 } |
| 80 }; |
| 81 |
| 82 class PendingOperationBuilder : public OperationBuilder { |
| 83 public: |
| 84 ~PendingOperationBuilder() override {} |
| 85 |
| 86 std::string BuildFromAny(const std::string& any_type_url, |
| 87 const std::string& any_value) override { |
| 88 return BuildOperation(false, any_type_url, any_value); |
| 89 } |
| 90 }; |
| 91 |
| 92 class GeneratePageBundleRequestTest : public PrefetchRequestTestBase { |
| 93 public: |
| 94 // Sends GeneratePageBundleRequest for single page with |page_url| and |
| 95 // gets back the data in |http_response|. |
| 96 PrefetchRequestStatus GeneratePage(const std::string& page_url, |
| 97 const std::string& http_response); |
| 98 |
| 99 // Sends GeneratePageBundleRequest for multiple pages and gets back the data |
| 100 // in |http_response|. |
| 101 PrefetchRequestStatus GenerateMultiplePages( |
| 102 const std::vector<std::string>& page_urls, |
| 103 const std::string& http_response); |
| 104 |
| 105 const std::vector<RenderPageInfo>& pages() const { return pages_; } |
| 106 |
| 107 private: |
| 108 std::vector<RenderPageInfo> pages_; |
| 109 }; |
| 110 |
| 111 PrefetchRequestStatus GeneratePageBundleRequestTest::GeneratePage( |
| 112 const std::string& page_url, |
| 113 const std::string& http_response) { |
| 114 std::vector<std::string> page_urls; |
| 115 page_urls.push_back(page_url); |
| 116 return GenerateMultiplePages(page_urls, http_response); |
| 117 } |
| 118 |
| 119 PrefetchRequestStatus GeneratePageBundleRequestTest::GenerateMultiplePages( |
| 120 const std::vector<std::string>& page_urls, |
| 121 const std::string& http_response) { |
| 122 base::MockCallback<GeneratePageBundleRequest::FinishedCallback> callback; |
| 123 std::unique_ptr<GeneratePageBundleRequest> fetcher( |
| 124 new GeneratePageBundleRequest(kTestUserAgent, kTestGCMID, |
| 125 kTestMaxBundleSize, page_urls, |
| 126 request_context(), callback.Get())); |
| 127 |
| 128 PrefetchRequestStatus status; |
| 129 pages_.clear(); |
| 130 EXPECT_CALL(callback, Run(_, _)) |
| 131 .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages_))); |
| 132 RespondWithData(http_response); |
| 133 |
| 134 return status; |
| 135 } |
| 136 |
| 137 TEST_F(GeneratePageBundleRequestTest, FailedToParse) { |
| 138 EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, |
| 139 GeneratePage(kTestURL, "Some data")); |
| 140 EXPECT_TRUE(pages().empty()); |
| 141 } |
| 142 |
| 143 TEST_F(GeneratePageBundleRequestTest, NoResultInDoneOperation) { |
| 144 proto::Operation operation; |
| 145 operation.set_done(true); |
| 146 std::string data; |
| 147 EXPECT_TRUE(operation.SerializeToString(&data)); |
| 148 EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, |
| 149 GeneratePage(kTestURL, data)); |
| 150 EXPECT_TRUE(pages().empty()); |
| 151 } |
| 152 |
| 153 TEST_F(GeneratePageBundleRequestTest, ErrorCodeInDoneOperation) { |
| 154 proto::Operation operation; |
| 155 operation.set_done(true); |
| 156 operation.mutable_error()->set_code(1); |
| 157 std::string data; |
| 158 EXPECT_TRUE(operation.SerializeToString(&data)); |
| 159 EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, |
| 160 GeneratePage(kTestURL, data)); |
| 161 EXPECT_TRUE(pages().empty()); |
| 162 } |
| 163 |
| 164 template <typename T> |
| 165 class GeneratePageBundleRequestOperationTest |
| 166 : public GeneratePageBundleRequestTest { |
| 167 public: |
| 168 std::string BuildFromAny(const std::string& any_type_url, |
| 169 const std::string& any_value) { |
| 170 return builder_.BuildFromAny(any_type_url, any_value); |
| 171 } |
| 172 |
| 173 std::string BuildFromPageBundle(const proto::PageBundle& bundle) { |
| 174 return builder_.BuildFromPageBundle(bundle); |
| 175 } |
| 176 |
| 177 private: |
| 178 T builder_; |
| 179 }; |
| 180 |
| 181 typedef testing::Types<DoneOperationBuilder, PendingOperationBuilder> MyTypes; |
| 182 TYPED_TEST_CASE(GeneratePageBundleRequestOperationTest, MyTypes); |
| 183 |
| 184 TYPED_TEST(GeneratePageBundleRequestOperationTest, InvalidTypeUrl) { |
| 185 EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, |
| 186 this->GeneratePage(kTestURL, this->BuildFromAny("foo", ""))); |
| 187 EXPECT_TRUE(this->pages().empty()); |
| 188 } |
| 189 |
| 190 TYPED_TEST(GeneratePageBundleRequestOperationTest, InvalidValue) { |
| 191 EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, |
| 192 this->GeneratePage(kTestURL, |
| 193 this->BuildFromAny(kPageBundleTypeURL, "foo"))); |
| 194 EXPECT_TRUE(this->pages().empty()); |
| 195 } |
| 196 |
| 197 TYPED_TEST(GeneratePageBundleRequestOperationTest, EmptyPageBundle) { |
| 198 proto::PageBundle bundle; |
| 199 EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, |
| 200 this->GeneratePage(kTestURL, this->BuildFromPageBundle(bundle))); |
| 201 EXPECT_TRUE(this->pages().empty()); |
| 202 } |
| 203 |
| 204 TYPED_TEST(GeneratePageBundleRequestOperationTest, EmptyArchive) { |
| 205 proto::PageBundle bundle; |
| 206 bundle.add_archives(); |
| 207 EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, |
| 208 this->GeneratePage(kTestURL, this->BuildFromPageBundle(bundle))); |
| 209 EXPECT_TRUE(this->pages().empty()); |
| 210 } |
| 211 |
| 212 TYPED_TEST(GeneratePageBundleRequestOperationTest, NoPageInfo) { |
| 213 proto::PageBundle bundle; |
| 214 proto::Archive* archive = bundle.add_archives(); |
| 215 archive->set_body_name(kTestBodyName); |
| 216 archive->set_body_length(kTestBodyLength); |
| 217 EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, |
| 218 this->GeneratePage(kTestURL, this->BuildFromPageBundle(bundle))); |
| 219 EXPECT_TRUE(this->pages().empty()); |
| 220 } |
| 221 |
| 222 TYPED_TEST(GeneratePageBundleRequestOperationTest, MissingPageInfoUrl) { |
| 223 proto::PageBundle bundle; |
| 224 proto::Archive* archive = bundle.add_archives(); |
| 225 proto::PageInfo* page_info = archive->add_page_infos(); |
| 226 page_info->set_redirect_url(kTestURL); |
| 227 EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF, |
| 228 this->GeneratePage(kTestURL, this->BuildFromPageBundle(bundle))); |
| 229 EXPECT_TRUE(this->pages().empty()); |
| 230 } |
| 231 |
| 232 TYPED_TEST(GeneratePageBundleRequestOperationTest, SinglePage) { |
| 233 proto::PageBundle bundle; |
| 234 proto::Archive* archive = bundle.add_archives(); |
| 235 archive->set_body_name(kTestBodyName); |
| 236 archive->set_body_length(kTestBodyLength); |
| 237 proto::PageInfo* page_info = archive->add_page_infos(); |
| 238 page_info->set_url(kTestURL); |
| 239 page_info->set_redirect_url(kTestURL2); |
| 240 page_info->mutable_status()->set_code(proto::OK); |
| 241 page_info->set_transformation(proto::NO_TRANSFORMATION); |
| 242 int64_t ms_since_epoch = base::Time::Now().ToJavaTime(); |
| 243 page_info->mutable_render_time()->set_seconds(ms_since_epoch / 1000); |
| 244 page_info->mutable_render_time()->set_nanos((ms_since_epoch % 1000) * |
| 245 1000000); |
| 246 EXPECT_EQ(PrefetchRequestStatus::SUCCESS, |
| 247 this->GeneratePage(kTestURL, this->BuildFromPageBundle(bundle))); |
| 248 ASSERT_EQ(1u, this->pages().size()); |
| 249 EXPECT_EQ(kTestURL, this->pages().back().url); |
| 250 EXPECT_EQ(kTestURL2, this->pages().back().redirect_url); |
| 251 EXPECT_EQ(RenderStatus::RENDERED, this->pages().back().status); |
| 252 EXPECT_EQ(kTestBodyName, this->pages().back().body_name); |
| 253 EXPECT_EQ(kTestBodyLength, this->pages().back().body_length); |
| 254 EXPECT_EQ(ms_since_epoch, this->pages().back().render_time.ToJavaTime()); |
| 255 } |
| 256 |
| 257 TYPED_TEST(GeneratePageBundleRequestOperationTest, MultiplePages) { |
| 258 proto::PageBundle bundle; |
| 259 |
| 260 // Adds a page that is still being rendered. |
| 261 proto::Archive* archive = bundle.add_archives(); |
| 262 proto::PageInfo* page_info = archive->add_page_infos(); |
| 263 page_info->set_url(kTestURL); |
| 264 page_info->mutable_status()->set_code(proto::NOT_FOUND); |
| 265 |
| 266 // Adds a page that failed to render due to bundle size limits. |
| 267 archive = bundle.add_archives(); |
| 268 page_info = archive->add_page_infos(); |
| 269 page_info->set_url(kTestURL2); |
| 270 page_info->mutable_status()->set_code(proto::FAILED_PRECONDITION); |
| 271 |
| 272 // Adds a page that failed to render for any other reason. |
| 273 archive = bundle.add_archives(); |
| 274 page_info = archive->add_page_infos(); |
| 275 page_info->set_url(kTestURL3); |
| 276 page_info->mutable_status()->set_code(proto::UNKNOWN); |
| 277 |
| 278 // Adds a page that was rendered successfully. |
| 279 archive = bundle.add_archives(); |
| 280 archive->set_body_name(kTestBodyName); |
| 281 archive->set_body_length(kTestBodyLength); |
| 282 page_info = archive->add_page_infos(); |
| 283 page_info->set_url(kTestURL4); |
| 284 page_info->mutable_status()->set_code(proto::OK); |
| 285 page_info->set_transformation(proto::NO_TRANSFORMATION); |
| 286 int64_t ms_since_epoch = base::Time::Now().ToJavaTime(); |
| 287 page_info->mutable_render_time()->set_seconds(ms_since_epoch / 1000); |
| 288 page_info->mutable_render_time()->set_nanos((ms_since_epoch % 1000) * |
| 289 1000000); |
| 290 |
| 291 std::vector<std::string> page_urls = {kTestURL, kTestURL2, kTestURL3}; |
| 292 EXPECT_EQ(PrefetchRequestStatus::SUCCESS, |
| 293 this->GenerateMultiplePages(page_urls, |
| 294 this->BuildFromPageBundle(bundle))); |
| 295 ASSERT_EQ(4u, this->pages().size()); |
| 296 EXPECT_EQ(kTestURL, this->pages().at(0).url); |
| 297 EXPECT_EQ(RenderStatus::PENDING, this->pages().at(0).status); |
| 298 EXPECT_EQ(kTestURL2, this->pages().at(1).url); |
| 299 EXPECT_EQ(RenderStatus::EXCEEDED_LIMIT, this->pages().at(1).status); |
| 300 EXPECT_EQ(kTestURL3, this->pages().at(2).url); |
| 301 EXPECT_EQ(RenderStatus::FAILED, this->pages().at(2).status); |
| 302 EXPECT_EQ(kTestURL4, this->pages().at(3).url); |
| 303 EXPECT_EQ(RenderStatus::RENDERED, this->pages().at(3).status); |
| 304 EXPECT_EQ(kTestBodyName, this->pages().at(3).body_name); |
| 305 EXPECT_EQ(kTestBodyLength, this->pages().at(3).body_length); |
| 306 EXPECT_EQ(ms_since_epoch, this->pages().at(3).render_time.ToJavaTime()); |
| 307 } |
| 308 |
| 309 } // namespace offline_pages |
OLD | NEW |