| // Copyright 2016 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 "components/image_fetcher/core/image_data_fetcher.h" |
| |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/run_loop.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "net/base/load_flags.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/http/http_status_code.h" |
| #include "net/http/http_util.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "net/url_request/url_request_status.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| const char kImageURL[] = "http://www.example.com/image"; |
| const char kURLResponseData[] = "EncodedImageData"; |
| |
| } // namespace |
| |
| namespace image_fetcher { |
| |
| class ImageDataFetcherTest : public testing::Test { |
| public: |
| ImageDataFetcherTest() |
| : shared_factory_( |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| &test_url_loader_factory_)), |
| image_data_fetcher_(shared_factory_) {} |
| ~ImageDataFetcherTest() override {} |
| |
| MOCK_METHOD2(OnImageDataFetched, |
| void(const std::string&, const RequestMetadata&)); |
| |
| MOCK_METHOD2(OnImageDataFetchedFailedRequest, |
| void(const std::string&, const RequestMetadata&)); |
| |
| MOCK_METHOD2(OnImageDataFetchedMultipleRequests, |
| void(const std::string&, const RequestMetadata&)); |
| |
| protected: |
| base::test::ScopedTaskEnvironment task_environment_; |
| |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| scoped_refptr<network::SharedURLLoaderFactory> shared_factory_; |
| |
| ImageDataFetcher image_data_fetcher_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ImageDataFetcherTest); |
| }; |
| |
| TEST_F(ImageDataFetcherTest, FetchImageData) { |
| std::string content = kURLResponseData; |
| |
| image_data_fetcher_.FetchImageData( |
| GURL(kImageURL), |
| base::BindOnce(&ImageDataFetcherTest::OnImageDataFetched, |
| base::Unretained(this)), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| RequestMetadata expected_metadata; |
| expected_metadata.mime_type = std::string("image/png"); |
| expected_metadata.http_response_code = net::HTTP_OK; |
| EXPECT_CALL(*this, OnImageDataFetched(content, expected_metadata)); |
| |
| // Check to make sure the request is pending with proper flags, and |
| // provide a response. |
| int pending_load_flags = 0; |
| EXPECT_TRUE( |
| test_url_loader_factory_.IsPending(kImageURL, &pending_load_flags)); |
| EXPECT_TRUE(pending_load_flags & net::LOAD_DO_NOT_SEND_COOKIES); |
| EXPECT_TRUE(pending_load_flags & net::LOAD_DO_NOT_SAVE_COOKIES); |
| EXPECT_TRUE(pending_load_flags & net::LOAD_DO_NOT_SEND_AUTH_DATA); |
| |
| network::ResourceResponseHead head; |
| std::string raw_header = |
| "HTTP/1.1 200 OK\n" |
| "Content-type: image/png\n\n"; |
| head.headers = new net::HttpResponseHeaders( |
| net::HttpUtil::AssembleRawHeaders(raw_header.c_str(), raw_header.size())); |
| head.mime_type = "image/png"; |
| network::URLLoaderCompletionStatus status; |
| status.decoded_body_length = content.size(); |
| test_url_loader_factory_.AddResponse(GURL(kImageURL), head, content, status); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(ImageDataFetcherTest, FetchImageDataWithCookies) { |
| std::string content = kURLResponseData; |
| |
| image_data_fetcher_.FetchImageData( |
| GURL(kImageURL), |
| base::BindOnce(&ImageDataFetcherTest::OnImageDataFetched, |
| base::Unretained(this)), |
| TRAFFIC_ANNOTATION_FOR_TESTS, true); |
| |
| RequestMetadata expected_metadata; |
| expected_metadata.mime_type = std::string("image/png"); |
| expected_metadata.http_response_code = net::HTTP_OK; |
| EXPECT_CALL(*this, OnImageDataFetched(content, expected_metadata)); |
| |
| // Check to make sure the request is pending with proper flags, and |
| // provide a response. |
| int pending_load_flags = 0; |
| EXPECT_TRUE( |
| test_url_loader_factory_.IsPending(kImageURL, &pending_load_flags)); |
| EXPECT_FALSE(pending_load_flags & net::LOAD_DO_NOT_SEND_COOKIES); |
| EXPECT_FALSE(pending_load_flags & net::LOAD_DO_NOT_SAVE_COOKIES); |
| EXPECT_FALSE(pending_load_flags & net::LOAD_DO_NOT_SEND_AUTH_DATA); |
| |
| network::ResourceResponseHead head; |
| std::string raw_header = |
| "HTTP/1.1 200 OK\n" |
| "Content-type: image/png\n\n"; |
| head.headers = new net::HttpResponseHeaders( |
| net::HttpUtil::AssembleRawHeaders(raw_header.c_str(), raw_header.size())); |
| head.mime_type = "image/png"; |
| network::URLLoaderCompletionStatus status; |
| status.decoded_body_length = content.size(); |
| test_url_loader_factory_.AddResponse(GURL(kImageURL), head, content, status); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(ImageDataFetcherTest, FetchImageData_NotFound) { |
| std::string content = kURLResponseData; |
| |
| image_data_fetcher_.FetchImageData( |
| GURL(kImageURL), |
| base::BindOnce(&ImageDataFetcherTest::OnImageDataFetched, |
| base::Unretained(this)), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| RequestMetadata expected_metadata; |
| expected_metadata.mime_type = std::string("image/png"); |
| expected_metadata.http_response_code = net::HTTP_NOT_FOUND; |
| // For 404, expect an empty result even though correct image data is sent. |
| EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata)); |
| |
| // Check to make sure the request is pending, and provide a response. |
| EXPECT_TRUE(test_url_loader_factory_.IsPending(kImageURL)); |
| |
| network::ResourceResponseHead head; |
| std::string raw_header = |
| "HTTP/1.1 404 Not Found\n" |
| "Content-type: image/png\n\n"; |
| head.headers = new net::HttpResponseHeaders( |
| net::HttpUtil::AssembleRawHeaders(raw_header.c_str(), raw_header.size())); |
| head.mime_type = "image/png"; |
| network::URLLoaderCompletionStatus status; |
| status.decoded_body_length = content.size(); |
| test_url_loader_factory_.AddResponse(GURL(kImageURL), head, content, status); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(ImageDataFetcherTest, FetchImageData_WithContentLocation) { |
| std::string content = kURLResponseData; |
| |
| image_data_fetcher_.FetchImageData( |
| GURL(kImageURL), |
| base::BindOnce(&ImageDataFetcherTest::OnImageDataFetched, |
| base::Unretained(this)), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| RequestMetadata expected_metadata; |
| expected_metadata.mime_type = std::string("image/png"); |
| expected_metadata.http_response_code = net::HTTP_NOT_FOUND; |
| expected_metadata.content_location_header = "http://test-location/image.png"; |
| // For 404, expect an empty result even though correct image data is sent. |
| EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata)); |
| |
| // Check to make sure the request is pending, and provide a response. |
| EXPECT_TRUE(test_url_loader_factory_.IsPending(kImageURL)); |
| |
| network::ResourceResponseHead head; |
| std::string raw_header = |
| "HTTP/1.1 404 Not Found\n" |
| "Content-type: image/png\n" |
| "Content-location: http://test-location/image.png\n\n"; |
| head.headers = new net::HttpResponseHeaders( |
| net::HttpUtil::AssembleRawHeaders(raw_header.c_str(), raw_header.size())); |
| head.mime_type = "image/png"; |
| network::URLLoaderCompletionStatus status; |
| status.decoded_body_length = content.size(); |
| test_url_loader_factory_.AddResponse(GURL(kImageURL), head, content, status); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(ImageDataFetcherTest, FetchImageData_FailedRequest) { |
| image_data_fetcher_.FetchImageData( |
| GURL(kImageURL), |
| base::BindOnce(&ImageDataFetcherTest::OnImageDataFetchedFailedRequest, |
| base::Unretained(this)), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| RequestMetadata expected_metadata; |
| expected_metadata.http_response_code = net::URLFetcher::RESPONSE_CODE_INVALID; |
| EXPECT_CALL( |
| *this, OnImageDataFetchedFailedRequest(std::string(), expected_metadata)); |
| |
| // Check to make sure the request is pending, and provide a response. |
| EXPECT_TRUE(test_url_loader_factory_.IsPending(kImageURL)); |
| |
| network::ResourceResponseHead head; |
| head.mime_type = "image/png"; |
| network::URLLoaderCompletionStatus status; |
| status.error_code = net::ERR_INVALID_URL; |
| test_url_loader_factory_.AddResponse(GURL(kImageURL), head, "", status); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(ImageDataFetcherTest, FetchImageData_MultipleRequests) { |
| EXPECT_CALL(*this, OnImageDataFetchedMultipleRequests(testing::_, testing::_)) |
| .Times(2); |
| |
| image_data_fetcher_.FetchImageData( |
| GURL(kImageURL), |
| base::BindOnce(&ImageDataFetcherTest::OnImageDataFetchedMultipleRequests, |
| base::Unretained(this)), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| image_data_fetcher_.FetchImageData( |
| GURL(kImageURL), |
| base::BindOnce(&ImageDataFetcherTest::OnImageDataFetchedMultipleRequests, |
| base::Unretained(this)), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| // Multiple calls to FetchImageData for the same URL will result in |
| // multiple URLFetchers being created. |
| EXPECT_EQ(2, test_url_loader_factory_.NumPending()); |
| test_url_loader_factory_.AddResponse(kImageURL, ""); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(ImageDataFetcherTest, FetchImageData_CancelFetchIfImageExceedsMaxSize) { |
| const int64_t kMaxDownloadBytes = 1024; |
| std::string oversize_download(kMaxDownloadBytes + 1, '#'); |
| |
| image_data_fetcher_.SetImageDownloadLimit(kMaxDownloadBytes); |
| image_data_fetcher_.FetchImageData( |
| GURL(kImageURL), |
| base::BindOnce(&ImageDataFetcherTest::OnImageDataFetched, |
| base::Unretained(this)), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| // Fetching an oversized image will behave like any other failed request. |
| // There will be exactly one call to OnImageDataFetched containing a response |
| // code that would be impossible for a completed fetch. |
| RequestMetadata expected_metadata; |
| expected_metadata.http_response_code = net::URLFetcher::RESPONSE_CODE_INVALID; |
| EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata)); |
| |
| EXPECT_TRUE(test_url_loader_factory_.IsPending(kImageURL)); |
| test_url_loader_factory_.AddResponse(kImageURL, oversize_download); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| TEST_F(ImageDataFetcherTest, DeleteFromCallback) { |
| // Test to make sure that deleting an ImageDataFetcher from the callback |
| // passed to its FetchImageData() does not crash. |
| auto heap_fetcher = std::make_unique<ImageDataFetcher>(shared_factory_); |
| heap_fetcher->FetchImageData( |
| GURL(kImageURL), |
| base::BindLambdaForTesting( |
| [&](const std::string&, const RequestMetadata&) { |
| heap_fetcher = nullptr; |
| }), |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| test_url_loader_factory_.AddResponse(kImageURL, ""); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| } // namespace image_fetcher |