summaryrefslogtreecommitdiff
path: root/chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc')
-rw-r--r--chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc399
1 files changed, 399 insertions, 0 deletions
diff --git a/chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc b/chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
new file mode 100644
index 00000000000..22d676d84fc
--- /dev/null
+++ b/chromium/components/offline_pages/core/prefetch/prefetch_request_operation_response_unittest.cc
@@ -0,0 +1,399 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/mock_callback.h"
+#include "components/offline_pages/core/prefetch/generate_page_bundle_request.h"
+#include "components/offline_pages/core/prefetch/get_operation_request.h"
+#include "components/offline_pages/core/prefetch/prefetch_proto_utils.h"
+#include "components/offline_pages/core/prefetch/prefetch_request_test_base.h"
+#include "components/offline_pages/core/prefetch/prefetch_types.h"
+#include "components/offline_pages/core/prefetch/proto/offline_pages.pb.h"
+#include "components/offline_pages/core/prefetch/proto/operation.pb.h"
+#include "net/http/http_status_code.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "url/gurl.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::Eq;
+using testing::SaveArg;
+
+namespace offline_pages {
+
+namespace {
+const char kTestURL[] = "http://example.com";
+const char kTestURL2[] = "http://example.com/2";
+const char kTestURL3[] = "http://example.com/3";
+const char kTestURL4[] = "http://example.com/4";
+const char kTestUserAgent[] = "Test User Agent";
+const char kTestGCMID[] = "Test GCM ID";
+const int kTestMaxBundleSize = 100000;
+const char kTestBodyName[] = "body_name";
+const int64_t kTestBodyLength = 12345678LL;
+const char kTestMethodName[] = "Test name";
+const char kErrorMessage[] = "Invalid parameter";
+} // namespace
+
+// Builds the request for GeneratePageBundleRequest / GetOperationRequest.
+class RequestBuilder {
+ public:
+ RequestBuilder() {}
+ virtual ~RequestBuilder() {}
+
+ virtual void CreateRequest(
+ net::URLRequestContextGetter* request_context_getter,
+ const PrefetchRequestFinishedCallback& callback) = 0;
+};
+
+class GeneratePageBundleRequestBuilder : public RequestBuilder {
+ public:
+ void CreateRequest(net::URLRequestContextGetter* request_context_getter,
+ const PrefetchRequestFinishedCallback& callback) override {
+ std::vector<std::string> pages = {kTestURL, kTestURL2};
+ fetcher_.reset(new GeneratePageBundleRequest(
+ kTestUserAgent, kTestGCMID, kTestMaxBundleSize, pages,
+ request_context_getter, callback));
+ }
+
+ private:
+ std::unique_ptr<GeneratePageBundleRequest> fetcher_;
+};
+
+class GetOperationRequestBuilder : public RequestBuilder {
+ public:
+ void CreateRequest(net::URLRequestContextGetter* request_context_getter,
+ const PrefetchRequestFinishedCallback& callback) override {
+ fetcher_.reset(new GetOperationRequest(kTestMethodName,
+ request_context_getter, callback));
+ }
+
+ private:
+ std::unique_ptr<GetOperationRequest> fetcher_;
+};
+
+// Builds the response that returns a pending/done operation.
+class OperationBuilder {
+ public:
+ virtual ~OperationBuilder() {}
+
+ // Builds the opereation with an Any value and returns it in binary serialized
+ // format. Notes that Any value could be set in either 'metadata' or
+ // 'result.response' field depending on the state of the operation, pending or
+ // done.
+ virtual std::string BuildFromAny(const std::string& any_type_url,
+ const std::string& any_value) = 0;
+
+ // Builds the operation with an error value and returns it in binary
+ // serialized format. Notes that the error is only respected for done
+ // operation.
+ virtual std::string BuildFromError(int error_code,
+ const std::string& error_message) = 0;
+
+ protected:
+ // Helper function to build the operation based on |is_done| that controls
+ // where Any value goes.
+ std::string BuildOperation(bool is_done,
+ int error_code,
+ const std::string& error_message,
+ const std::string& any_type_url,
+ const std::string& any_value) {
+ proto::Operation operation;
+ operation.set_done(is_done);
+ if (error_code != proto::OK) {
+ operation.mutable_error()->set_code(error_code);
+ operation.mutable_error()->set_message(error_message);
+ }
+ if (!any_type_url.empty()) {
+ proto::Any* any =
+ is_done ? operation.mutable_response() : operation.mutable_metadata();
+ any->set_type_url(any_type_url);
+ any->set_value(any_value);
+ }
+ std::string data;
+ EXPECT_TRUE(operation.SerializeToString(&data));
+ return data;
+ }
+};
+
+class DoneOperationBuilder : public OperationBuilder {
+ public:
+ ~DoneOperationBuilder() override {}
+
+ std::string BuildFromAny(const std::string& any_type_url,
+ const std::string& any_value) override {
+ return BuildOperation(true, 0, std::string(), any_type_url, any_value);
+ }
+
+ std::string BuildFromError(int error_code,
+ const std::string& error_message) override {
+ return BuildOperation(true, error_code, error_message, std::string(),
+ std::string());
+ }
+};
+
+class PendingOperationBuilder : public OperationBuilder {
+ public:
+ ~PendingOperationBuilder() override {}
+
+ std::string BuildFromAny(const std::string& any_type_url,
+ const std::string& any_value) override {
+ return BuildOperation(false, 0, std::string(), any_type_url, any_value);
+ }
+
+ std::string BuildFromError(int error_code,
+ const std::string& error_message) override {
+ return BuildOperation(false, error_code, error_message, std::string(),
+ std::string());
+ }
+};
+
+// Combines both RequestBuilder and OperationBuilder in order to feed to
+// PrefetchRequestOperationResponseTest.
+class PrefetchRequestOperationResponseTestBuilder {
+ public:
+ PrefetchRequestOperationResponseTestBuilder() {}
+ virtual ~PrefetchRequestOperationResponseTestBuilder() {}
+
+ void CreateRequest(net::URLRequestContextGetter* request_context_getter,
+ const PrefetchRequestFinishedCallback& callback) {
+ request_builder_->CreateRequest(request_context_getter, callback);
+ }
+
+ std::string BuildFromAny(const std::string& any_type_url,
+ const std::string& any_value) {
+ return operation_builder_->BuildFromAny(any_type_url, any_value);
+ }
+
+ std::string BuildFromError(int error_code, const std::string& error_message) {
+ return operation_builder_->BuildFromError(error_code, error_message);
+ }
+
+ protected:
+ std::unique_ptr<RequestBuilder> request_builder_;
+ std::unique_ptr<OperationBuilder> operation_builder_;
+};
+
+class GeneratePageBundleRequestDoneOperationBuilder
+ : public PrefetchRequestOperationResponseTestBuilder {
+ public:
+ GeneratePageBundleRequestDoneOperationBuilder() {
+ request_builder_.reset(new GeneratePageBundleRequestBuilder);
+ operation_builder_.reset(new DoneOperationBuilder);
+ }
+};
+
+class GeneratePageBundleRequestPendingOperationBuilder
+ : public PrefetchRequestOperationResponseTestBuilder {
+ public:
+ GeneratePageBundleRequestPendingOperationBuilder() {
+ request_builder_.reset(new GeneratePageBundleRequestBuilder);
+ operation_builder_.reset(new PendingOperationBuilder);
+ }
+};
+
+class GetOperationRequestDoneOperationBuilder
+ : public PrefetchRequestOperationResponseTestBuilder {
+ public:
+ GetOperationRequestDoneOperationBuilder() {
+ request_builder_.reset(new GetOperationRequestBuilder);
+ operation_builder_.reset(new DoneOperationBuilder);
+ }
+};
+
+class GetOperationRequestPendingOperationBuilder
+ : public PrefetchRequestOperationResponseTestBuilder {
+ public:
+ GetOperationRequestPendingOperationBuilder() {
+ request_builder_.reset(new GetOperationRequestBuilder);
+ operation_builder_.reset(new PendingOperationBuilder);
+ }
+};
+
+template <typename T>
+class PrefetchRequestOperationResponseTest : public PrefetchRequestTestBase {
+ public:
+ PrefetchRequestStatus SendWithErrorResponse(
+ int error_code,
+ const std::string& error_message) {
+ return SendWithResponse(builder_.BuildFromError(error_code, error_message));
+ }
+
+ PrefetchRequestStatus SendWithAnyResponse(const std::string& any_type_url,
+ const std::string& any_value) {
+ return SendWithResponse(builder_.BuildFromAny(any_type_url, any_value));
+ }
+
+ PrefetchRequestStatus SendWithPageBundleResponse(
+ const proto::PageBundle& bundle) {
+ std::string bundle_data;
+ EXPECT_TRUE(bundle.SerializeToString(&bundle_data));
+ return SendWithResponse(
+ builder_.BuildFromAny(kPageBundleTypeURL, bundle_data));
+ }
+
+ const std::vector<RenderPageInfo>& pages() const { return pages_; }
+
+ private:
+ PrefetchRequestStatus SendWithResponse(const std::string& response_data) {
+ base::MockCallback<PrefetchRequestFinishedCallback> callback;
+ builder_.CreateRequest(request_context(), callback.Get());
+
+ PrefetchRequestStatus status;
+ pages_.clear();
+ EXPECT_CALL(callback, Run(_, _))
+ .WillOnce(DoAll(SaveArg<0>(&status), SaveArg<1>(&pages_)));
+ RespondWithData(response_data);
+ return status;
+ }
+
+ T builder_;
+ std::vector<RenderPageInfo> pages_;
+};
+
+// Lists all scenarios we want to test.
+typedef testing::Types<GeneratePageBundleRequestDoneOperationBuilder,
+ GeneratePageBundleRequestPendingOperationBuilder,
+ GetOperationRequestDoneOperationBuilder,
+ GetOperationRequestPendingOperationBuilder>
+ MyTypes;
+TYPED_TEST_CASE(PrefetchRequestOperationResponseTest, MyTypes);
+
+TYPED_TEST(PrefetchRequestOperationResponseTest, EmptyOperation) {
+ EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
+ // No error is set for OK. Thus this will cause the operation
+ // being filled with only done flag.
+ this->SendWithErrorResponse(proto::OK, ""));
+ EXPECT_TRUE(this->pages().empty());
+}
+
+TYPED_TEST(PrefetchRequestOperationResponseTest, ErrorValue) {
+ EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
+ this->SendWithErrorResponse(proto::UNKNOWN, kErrorMessage));
+ EXPECT_TRUE(this->pages().empty());
+}
+
+TYPED_TEST(PrefetchRequestOperationResponseTest, InvalidTypeUrl) {
+ EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
+ this->SendWithAnyResponse("foo", ""));
+ EXPECT_TRUE(this->pages().empty());
+}
+
+TYPED_TEST(PrefetchRequestOperationResponseTest, InvalidValue) {
+ EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
+ this->SendWithAnyResponse(kPageBundleTypeURL, "foo"));
+ EXPECT_TRUE(this->pages().empty());
+}
+
+TYPED_TEST(PrefetchRequestOperationResponseTest, EmptyPageBundle) {
+ proto::PageBundle bundle;
+ EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
+ this->SendWithPageBundleResponse(bundle));
+ EXPECT_TRUE(this->pages().empty());
+}
+
+TYPED_TEST(PrefetchRequestOperationResponseTest, EmptyArchive) {
+ proto::PageBundle bundle;
+ bundle.add_archives();
+ EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
+ this->SendWithPageBundleResponse(bundle));
+ EXPECT_TRUE(this->pages().empty());
+}
+
+TYPED_TEST(PrefetchRequestOperationResponseTest, NoPageInfo) {
+ proto::PageBundle bundle;
+ proto::Archive* archive = bundle.add_archives();
+ archive->set_body_name(kTestBodyName);
+ archive->set_body_length(kTestBodyLength);
+ EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
+ this->SendWithPageBundleResponse(bundle));
+ EXPECT_TRUE(this->pages().empty());
+}
+
+TYPED_TEST(PrefetchRequestOperationResponseTest, MissingPageInfoUrl) {
+ proto::PageBundle bundle;
+ proto::Archive* archive = bundle.add_archives();
+ proto::PageInfo* page_info = archive->add_page_infos();
+ page_info->set_redirect_url(kTestURL);
+ EXPECT_EQ(PrefetchRequestStatus::SHOULD_RETRY_WITH_BACKOFF,
+ this->SendWithPageBundleResponse(bundle));
+ EXPECT_TRUE(this->pages().empty());
+}
+
+TYPED_TEST(PrefetchRequestOperationResponseTest, SinglePage) {
+ proto::PageBundle bundle;
+ proto::Archive* archive = bundle.add_archives();
+ archive->set_body_name(kTestBodyName);
+ archive->set_body_length(kTestBodyLength);
+ proto::PageInfo* page_info = archive->add_page_infos();
+ page_info->set_url(kTestURL);
+ page_info->set_redirect_url(kTestURL2);
+ page_info->mutable_status()->set_code(proto::OK);
+ page_info->set_transformation(proto::NO_TRANSFORMATION);
+ int64_t ms_since_epoch = base::Time::Now().ToJavaTime();
+ page_info->mutable_render_time()->set_seconds(ms_since_epoch / 1000);
+ page_info->mutable_render_time()->set_nanos((ms_since_epoch % 1000) *
+ 1000000);
+ EXPECT_EQ(PrefetchRequestStatus::SUCCESS,
+ this->SendWithPageBundleResponse(bundle));
+ ASSERT_EQ(1u, this->pages().size());
+ EXPECT_EQ(kTestURL, this->pages().back().url);
+ EXPECT_EQ(kTestURL2, this->pages().back().redirect_url);
+ EXPECT_EQ(RenderStatus::RENDERED, this->pages().back().status);
+ EXPECT_EQ(kTestBodyName, this->pages().back().body_name);
+ EXPECT_EQ(kTestBodyLength, this->pages().back().body_length);
+ EXPECT_EQ(ms_since_epoch, this->pages().back().render_time.ToJavaTime());
+}
+
+TYPED_TEST(PrefetchRequestOperationResponseTest, MultiplePages) {
+ proto::PageBundle bundle;
+
+ // Adds a page that is still being rendered.
+ proto::Archive* archive = bundle.add_archives();
+ proto::PageInfo* page_info = archive->add_page_infos();
+ page_info->set_url(kTestURL);
+ page_info->mutable_status()->set_code(proto::NOT_FOUND);
+
+ // Adds a page that failed to render due to bundle size limits.
+ archive = bundle.add_archives();
+ page_info = archive->add_page_infos();
+ page_info->set_url(kTestURL2);
+ page_info->mutable_status()->set_code(proto::FAILED_PRECONDITION);
+
+ // Adds a page that failed to render for any other reason.
+ archive = bundle.add_archives();
+ page_info = archive->add_page_infos();
+ page_info->set_url(kTestURL3);
+ page_info->mutable_status()->set_code(proto::UNKNOWN);
+
+ // Adds a page that was rendered successfully.
+ archive = bundle.add_archives();
+ archive->set_body_name(kTestBodyName);
+ archive->set_body_length(kTestBodyLength);
+ page_info = archive->add_page_infos();
+ page_info->set_url(kTestURL4);
+ page_info->mutable_status()->set_code(proto::OK);
+ page_info->set_transformation(proto::NO_TRANSFORMATION);
+ int64_t ms_since_epoch = base::Time::Now().ToJavaTime();
+ page_info->mutable_render_time()->set_seconds(ms_since_epoch / 1000);
+ page_info->mutable_render_time()->set_nanos((ms_since_epoch % 1000) *
+ 1000000);
+
+ EXPECT_EQ(PrefetchRequestStatus::SUCCESS,
+ this->SendWithPageBundleResponse(bundle));
+ ASSERT_EQ(4u, this->pages().size());
+ EXPECT_EQ(kTestURL, this->pages().at(0).url);
+ EXPECT_EQ(RenderStatus::PENDING, this->pages().at(0).status);
+ EXPECT_EQ(kTestURL2, this->pages().at(1).url);
+ EXPECT_EQ(RenderStatus::EXCEEDED_LIMIT, this->pages().at(1).status);
+ EXPECT_EQ(kTestURL3, this->pages().at(2).url);
+ EXPECT_EQ(RenderStatus::FAILED, this->pages().at(2).status);
+ EXPECT_EQ(kTestURL4, this->pages().at(3).url);
+ EXPECT_EQ(RenderStatus::RENDERED, this->pages().at(3).status);
+ EXPECT_EQ(kTestBodyName, this->pages().at(3).body_name);
+ EXPECT_EQ(kTestBodyLength, this->pages().at(3).body_length);
+ EXPECT_EQ(ms_since_epoch, this->pages().at(3).render_time.ToJavaTime());
+}
+
+} // namespace offline_pages