blob: 0f4a804c91d08b0755a916466f093164131963d7 [file] [log] [blame]
// Copyright 2018 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 "services/network/http_cache_data_counter.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/base/cache_type.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/http/http_network_session.h"
#include "net/http/http_server_properties_manager.h"
#include "net/http/http_transaction_factory.h"
#include "net/url_request/url_request_context.h"
#include "services/network/network_context.h"
#include "services/network/network_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
namespace {
struct CacheTestEntry {
const char* url;
const char* date;
int size;
};
constexpr CacheTestEntry kCacheEntries[] = {
{"http://www.google.com", "15 Jun 1975", 1024},
{"https://www.google.com", "15 Jun 1985", 2048},
{"http://www.wikipedia.com", "15 Jun 1995", 4096},
{"https://www.wikipedia.com", "15 Jun 2005", 8192},
{"http://localhost:1234/mysite", "15 Jun 2015", 16384},
{"https://localhost:1234/mysite", "15 Jun 2016", 32768},
{"http://localhost:3456/yoursite", "15 Jun 2017", 65536},
{"https://localhost:3456/yoursite", "15 Jun 2018", 512}};
mojom::NetworkContextParamsPtr CreateContextParams() {
mojom::NetworkContextParamsPtr params = mojom::NetworkContextParams::New();
// Use a fixed proxy config, to avoid dependencies on local network
// configuration.
params->initial_proxy_config = net::ProxyConfigWithAnnotation::CreateDirect();
return params;
}
class HttpCacheDataCounterTest : public testing::Test {
public:
HttpCacheDataCounterTest()
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::IO),
network_service_(NetworkService::CreateForTesting()) {}
~HttpCacheDataCounterTest() override = default;
void SetUp() override {
ASSERT_TRUE(cache_dir_.CreateUniqueTempDir());
InitNetworkContext();
net::HttpCache* cache = network_context_->url_request_context()
->http_transaction_factory()
->GetCache();
ASSERT_TRUE(cache);
{
net::TestCompletionCallback callback;
int rv = cache->GetBackend(&backend_, callback.callback());
ASSERT_EQ(net::OK, callback.GetResult(rv));
ASSERT_TRUE(backend_);
}
// Create some entries in the cache.
for (const CacheTestEntry& test_entry : kCacheEntries) {
disk_cache::Entry* entry = nullptr;
net::TestCompletionCallback create_entry_callback;
int rv = backend_->CreateEntry(test_entry.url, net::HIGHEST, &entry,
create_entry_callback.callback());
ASSERT_EQ(net::OK, create_entry_callback.GetResult(rv));
ASSERT_TRUE(entry);
auto io_buf = base::MakeRefCounted<net::IOBuffer>(test_entry.size);
std::fill(io_buf->data(), io_buf->data() + test_entry.size, 0);
net::TestCompletionCallback write_data_callback;
rv = entry->WriteData(1, 0, io_buf.get(), test_entry.size,
write_data_callback.callback(), true);
ASSERT_EQ(static_cast<int>(test_entry.size),
write_data_callback.GetResult(rv));
base::Time time;
ASSERT_TRUE(base::Time::FromString(test_entry.date, &time));
entry->SetLastUsedTimeForTest(time);
entry->Close();
scoped_task_environment_.RunUntilIdle();
}
}
void ExpectClose(int actual, int expected) {
int margin = std::max(256, expected / 5);
int expected_min = expected - margin;
int expected_max = expected + margin;
EXPECT_LE(expected_min, actual);
EXPECT_LE(actual, expected_max);
EXPECT_GE(actual, 0);
}
int SizeBetween(int first, int last) {
int size = 0;
for (int pos = first; pos < last; ++pos)
size += kCacheEntries[pos].size;
return size;
}
int SizeAll() { return SizeBetween(0, base::size(kCacheEntries)); }
static std::pair<bool, int64_t> CountBetween(NetworkContext* network_context,
base::Time start_time,
base::Time end_time) {
base::RunLoop run_loop;
std::pair<bool, int64_t> result(false, net::ERR_FAILED);
std::unique_ptr<HttpCacheDataCounter> own_counter =
HttpCacheDataCounter::CreateAndStart(
network_context->url_request_context(), start_time, end_time,
base::BindLambdaForTesting([&](HttpCacheDataCounter* counter,
bool upper_bound,
int64_t size_or_error) {
EXPECT_EQ(counter, own_counter.get());
result = std::make_pair(upper_bound, size_or_error);
run_loop.Quit();
}));
run_loop.Run();
return result;
}
void TestCountBetween(int start_index, int end_index) {
DCHECK_LE(0, start_index);
DCHECK_LT(start_index, static_cast<int>(base::size(kCacheEntries)));
DCHECK_LE(0, end_index);
DCHECK_LT(end_index, static_cast<int>(base::size(kCacheEntries)));
base::Time start_time;
ASSERT_TRUE(
base::Time::FromString(kCacheEntries[start_index].date, &start_time));
base::Time end_time;
ASSERT_TRUE(
base::Time::FromString(kCacheEntries[end_index].date, &end_time));
// The upper bound is "exclusive" but appropriximately so; make it clearly
// exclusive.
end_time -= base::TimeDelta::FromDays(1);
auto result = CountBetween(network_context_.get(), start_time, end_time);
ASSERT_GE(result.second, 0);
if (result.first) { // upper bound
ExpectClose(result.second, SizeAll());
} else {
ExpectClose(result.second, SizeBetween(start_index, end_index));
}
}
protected:
void InitNetworkContext() {
mojom::NetworkContextParamsPtr context_params = CreateContextParams();
context_params->http_cache_enabled = true;
context_params->http_cache_path = cache_dir_.GetPath();
network_context_ = std::make_unique<NetworkContext>(
network_service_.get(), mojo::MakeRequest(&network_context_ptr_),
std::move(context_params));
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
base::ScopedTempDir cache_dir_;
std::unique_ptr<NetworkService> network_service_;
std::unique_ptr<NetworkContext> network_context_;
// Stores the NetworkContextPtr of the most recently created NetworkContext.
mojom::NetworkContextPtr network_context_ptr_;
disk_cache::Backend* backend_ = nullptr;
};
TEST_F(HttpCacheDataCounterTest, Basic) {
auto result =
CountBetween(network_context_.get(), base::Time(), base::Time::Max());
EXPECT_EQ(false, result.first);
ExpectClose(result.second, SizeAll());
// Backwards interval to make sure we don't hit DCHECKs.
TestCountBetween(1, 0);
// Technically backwards, too.
TestCountBetween(0, 0);
// Empty interval.
TestCountBetween(0, 1);
// Some non-empty ones.
TestCountBetween(0, 3);
TestCountBetween(2, 5);
}
// Return the sensible thing (0 bytes used) when there is no cache.
TEST(HttpCacheDataCounterTestNoCache, BeSensible) {
base::test::ScopedTaskEnvironment scoped_task_environment(
base::test::ScopedTaskEnvironment::MainThreadType::IO);
std::unique_ptr<NetworkService> network_service(
NetworkService::CreateForTesting());
std::unique_ptr<NetworkContext> network_context;
mojom::NetworkContextPtr network_context_ptr;
mojom::NetworkContextParamsPtr context_params = CreateContextParams();
context_params->http_cache_enabled = false;
network_context = std::make_unique<NetworkContext>(
network_service.get(), mojo::MakeRequest(&network_context_ptr),
std::move(context_params));
auto result = HttpCacheDataCounterTest::CountBetween(
network_context.get(), base::Time(), base::Time::Max());
EXPECT_EQ(false, result.first);
EXPECT_EQ(0, result.second);
}
} // namespace
} // namespace network