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