blob: f3d9de48076582493123d8a0335958d7b54e7b53 [file] [log] [blame]
// 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/precache/core/fetcher_pool.h"
#include <algorithm>
#include <array>
#include <functional>
#include <list>
#include <memory>
#include <string>
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "net/http/http_status_code.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_status.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace precache {
namespace {
using net::FakeURLFetcher;
using net::HTTP_OK;
using net::URLFetcher;
using net::URLRequestStatus;
using ::testing::_;
using ::testing::Invoke;
class MockURLFetcherDelegate : public net::URLFetcherDelegate {
public:
MockURLFetcherDelegate() {};
virtual ~MockURLFetcherDelegate() {};
MOCK_METHOD1(OnURLFetchComplete, void(const URLFetcher*));
MOCK_METHOD4(OnURLFetchDownloadProgress,
void(const URLFetcher* source,
int64_t current,
int64_t total,
int64_t current_network_bytes));
MOCK_METHOD3(OnURLFetchUploadProgress,
void(const URLFetcher* source, int64_t current, int64_t total));
};
TEST(FetcherPoolTest, AddDelete) {
// It also tests IsAvailable.
base::MessageLoop loop;
MockURLFetcherDelegate delegate;
std::unique_ptr<URLFetcher> url_fetcher(
new FakeURLFetcher(GURL("http://a.com"), &delegate, "irrelevant", HTTP_OK,
URLRequestStatus::SUCCESS));
URLFetcher* url_fetcher_ptr = url_fetcher.get();
FetcherPool<URLFetcher> pool(1);
EXPECT_TRUE(pool.IsAvailable());
EXPECT_TRUE(pool.IsEmpty());
pool.Add(std::move(url_fetcher));
url_fetcher_ptr->Start();
EXPECT_FALSE(pool.IsAvailable());
EXPECT_FALSE(pool.IsEmpty());
EXPECT_CALL(delegate, OnURLFetchComplete(url_fetcher_ptr));
base::RunLoop().RunUntilIdle();
pool.Delete(*url_fetcher_ptr);
EXPECT_TRUE(pool.IsEmpty());
EXPECT_TRUE(pool.IsAvailable());
}
TEST(FetcherPoolTest, Delete) {
const size_t kSize = 42;
base::MessageLoop loop;
MockURLFetcherDelegate delegate;
std::unique_ptr<URLFetcher> url_fetcher(
new FakeURLFetcher(GURL("http://a.com"), &delegate, "irrelevant", HTTP_OK,
URLRequestStatus::SUCCESS));
URLFetcher* url_fetcher_ptr = url_fetcher.get();
FetcherPool<URLFetcher> pool(kSize);
pool.Add(std::move(url_fetcher));
url_fetcher_ptr->Start();
pool.Delete(*url_fetcher_ptr);
EXPECT_TRUE(pool.IsEmpty());
EXPECT_CALL(delegate, OnURLFetchComplete(_)).Times(0);
base::RunLoop().RunUntilIdle();
}
TEST(FetcherPoolTest, ParallelURLFetchers) {
// It also tests IsEmpty.
const size_t kSize = 42;
base::MessageLoop loop;
MockURLFetcherDelegate delegate;
FetcherPool<URLFetcher> pool(kSize);
std::string urls[] = {"http://a.com", "http://b.com", "http://c.com"};
// To make sure that nothing slip through while setting the expectations.
EXPECT_CALL(delegate, OnURLFetchComplete(_)).Times(0);
int num_requests_in_flight = 0;
for (const auto& url : urls) {
std::unique_ptr<URLFetcher> url_fetcher(
new FakeURLFetcher(GURL(url), &delegate, "irrelevant", HTTP_OK,
URLRequestStatus::SUCCESS));
num_requests_in_flight++;
url_fetcher->Start();
pool.Add(std::move(url_fetcher));
EXPECT_FALSE(pool.IsEmpty());
EXPECT_TRUE(pool.IsAvailable());
}
EXPECT_CALL(delegate, OnURLFetchComplete(_))
.Times(3)
.WillRepeatedly(Invoke([&pool](const URLFetcher* fetcher) {
EXPECT_TRUE(fetcher);
pool.Delete(*fetcher);
}));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(pool.IsEmpty());
EXPECT_TRUE(pool.IsAvailable());
}
TEST(FetcherPoolTest, DeleteAll) {
const size_t kSize = 42;
base::MessageLoop loop;
MockURLFetcherDelegate delegate;
FetcherPool<URLFetcher> pool(kSize);
std::string urls[] = {"http://a.com", "http://b.com", "http://c.com"};
EXPECT_CALL(delegate, OnURLFetchComplete(_)).Times(0);
for (const auto& url : urls) {
std::unique_ptr<URLFetcher> url_fetcher(
new FakeURLFetcher(GURL(url), &delegate, "irrelevant", HTTP_OK,
URLRequestStatus::SUCCESS));
url_fetcher->Start();
pool.Add(std::move(url_fetcher));
}
pool.DeleteAll();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(pool.IsEmpty());
EXPECT_TRUE(pool.IsAvailable());
}
#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
TEST(FetcherPoolTest, AddTooManyURLFetchers) {
MockURLFetcherDelegate delegate;
FetcherPool<URLFetcher> pool(0);
std::unique_ptr<URLFetcher> url_fetcher(
new FakeURLFetcher(GURL("http://queso.es"), &delegate, "irrelevant",
HTTP_OK, URLRequestStatus::SUCCESS));
EXPECT_DEBUG_DEATH(pool.Add(std::move(url_fetcher)),
"FetcherPool size exceeded");
}
TEST(FetcherPoolTest, AddNullURLFetcher) {
FetcherPool<URLFetcher> pool(1);
std::unique_ptr<URLFetcher> null_ptr;
EXPECT_DEBUG_DEATH(pool.Add(std::move(null_ptr)), "cannot be null");
}
TEST(FetcherPoolTest, DeleteUnregisteredURLFetcher) {
MockURLFetcherDelegate delegate;
FetcherPool<URLFetcher> pool(1);
FakeURLFetcher url_fetcher(GURL("http://queso.es"), &delegate, "irrelevant",
HTTP_OK, URLRequestStatus::SUCCESS);
EXPECT_DEBUG_DEATH(pool.Delete(url_fetcher),
"doesn't contain the given element");
}
#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
TEST(FetcherPoolTest, ExampleUsage) {
base::MessageLoop loop;
FetcherPool<URLFetcher> pool(2);
MockURLFetcherDelegate delegate;
std::list<GURL> pending_urls{
{GURL("http://a.com"), GURL("http://b.com"), GURL("http://c.com")}};
std::function<void()> start_next_batch = [&pending_urls, &pool, &delegate]() {
while (!pending_urls.empty() && pool.IsAvailable()) {
// Called CreateAndStartUrlFetcher in the documentation.
std::unique_ptr<URLFetcher> fetcher(
new FakeURLFetcher(GURL(pending_urls.front()), &delegate,
"irrelevant", HTTP_OK, URLRequestStatus::SUCCESS));
fetcher->Start();
pending_urls.pop_front();
pool.Add(std::move(fetcher));
}
};
EXPECT_CALL(delegate, OnURLFetchComplete(_)).Times(0); // 3 and no more.
EXPECT_CALL(delegate, OnURLFetchComplete(_))
.Times(pending_urls.size())
.WillRepeatedly(
Invoke([&pool, &start_next_batch](const URLFetcher* fetcher) {
EXPECT_TRUE(fetcher);
pool.Delete(*fetcher);
start_next_batch();
}));
start_next_batch();
EXPECT_FALSE(pool.IsEmpty());
EXPECT_FALSE(pool.IsAvailable());
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(pool.IsEmpty());
EXPECT_TRUE(pool.IsAvailable());
}
} // namespace
} // namespace precache