// Copyright (c) 2006-2009 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 "net/url_request/request_tracker.h"
#include "base/compiler_specific.h"
#include "base/format_macros.h"
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
static const int kMaxNumLoadLogEntries = 1;
class TestRequest {
public:
explicit TestRequest(const GURL& url)
: url_(url),
load_log_(new net::LoadLog(kMaxNumLoadLogEntries)),
ALLOW_THIS_IN_INITIALIZER_LIST(request_tracker_node_(this)) {}
~TestRequest() {}
// This method is used in RequestTrackerTest::Basic test.
const GURL& original_url() const { return url_; }
private:
// RequestTracker<T> will access GetRecentRequestInfo() and
// |request_tracker_node_|.
friend class RequestTracker<TestRequest>;
void GetInfoForTracker(
RequestTracker<TestRequest>::RecentRequestInfo* info) const {
info->original_url = url_;
info->load_log = load_log_;
}
const GURL url_;
scoped_refptr<net::LoadLog> load_log_;
RequestTracker<TestRequest>::Node request_tracker_node_;
DISALLOW_COPY_AND_ASSIGN(TestRequest);
};
TEST(RequestTrackerTest, BasicBounded) {
RequestTracker<TestRequest> tracker;
EXPECT_FALSE(tracker.IsUnbounded());
EXPECT_EQ(0u, tracker.GetLiveRequests().size());
EXPECT_EQ(0u, tracker.GetRecentlyDeceased().size());
TestRequest req1(GURL("http://req1"));
TestRequest req2(GURL("http://req2"));
TestRequest req3(GURL("http://req3"));
TestRequest req4(GURL("http://req4"));
TestRequest req5(GURL("http://req5"));
tracker.Add(&req1);
tracker.Add(&req2);
tracker.Add(&req3);
tracker.Add(&req4);
tracker.Add(&req5);
std::vector<TestRequest*> live_reqs = tracker.GetLiveRequests();
ASSERT_EQ(5u, live_reqs.size());
EXPECT_EQ(GURL("http://req1"), live_reqs[0]->original_url());
EXPECT_EQ(GURL("http://req2"), live_reqs[1]->original_url());
EXPECT_EQ(GURL("http://req3"), live_reqs[2]->original_url());
EXPECT_EQ(GURL("http://req4"), live_reqs[3]->original_url());
EXPECT_EQ(GURL("http://req5"), live_reqs[4]->original_url());
tracker.Remove(&req1);
tracker.Remove(&req5);
tracker.Remove(&req3);
ASSERT_EQ(3u, tracker.GetRecentlyDeceased().size());
live_reqs = tracker.GetLiveRequests();
ASSERT_EQ(2u, live_reqs.size());
EXPECT_EQ(GURL("http://req2"), live_reqs[0]->original_url());
EXPECT_EQ(GURL("http://req4"), live_reqs[1]->original_url());
}
TEST(RequestTrackerTest, GraveyardBounded) {
RequestTracker<TestRequest> tracker;
EXPECT_FALSE(tracker.IsUnbounded());
EXPECT_EQ(0u, tracker.GetLiveRequests().size());
EXPECT_EQ(0u, tracker.GetRecentlyDeceased().size());
// Add twice as many requests as will fit in the graveyard.
for (size_t i = 0;
i < RequestTracker<TestRequest>::kMaxGraveyardSize * 2;
++i) {
TestRequest req(GURL(StringPrintf("http://req%" PRIuS, i).c_str()));
tracker.Add(&req);
tracker.Remove(&req);
}
// Check that only the last |kMaxGraveyardSize| requests are in-memory.
RequestTracker<TestRequest>::RecentRequestInfoList recent_reqs =
tracker.GetRecentlyDeceased();
ASSERT_EQ(RequestTracker<TestRequest>::kMaxGraveyardSize, recent_reqs.size());
for (size_t i = 0; i < RequestTracker<TestRequest>::kMaxGraveyardSize; ++i) {
size_t req_number = i + RequestTracker<TestRequest>::kMaxGraveyardSize;
GURL url(StringPrintf("http://req%" PRIuS, req_number).c_str());
EXPECT_EQ(url, recent_reqs[i].original_url);
}
}
TEST(RequestTrackerTest, GraveyardUnbounded) {
RequestTracker<TestRequest> tracker;
EXPECT_FALSE(tracker.IsUnbounded());
EXPECT_EQ(0u, tracker.GetLiveRequests().size());
EXPECT_EQ(0u, tracker.GetRecentlyDeceased().size());
tracker.SetUnbounded(true);
EXPECT_TRUE(tracker.IsUnbounded());
// Add twice as many requests as would fit in the bounded graveyard.
size_t kMaxSize = RequestTracker<TestRequest>::kMaxGraveyardSize * 2;
for (size_t i = 0; i < kMaxSize; ++i) {
TestRequest req(GURL(StringPrintf("http://req%" PRIuS, i).c_str()));
tracker.Add(&req);
tracker.Remove(&req);
}
// Check that all of them got saved.
RequestTracker<TestRequest>::RecentRequestInfoList recent_reqs =
tracker.GetRecentlyDeceased();
ASSERT_EQ(kMaxSize, recent_reqs.size());
for (size_t i = 0; i < kMaxSize; ++i) {
GURL url(StringPrintf("http://req%" PRIuS, i).c_str());
EXPECT_EQ(url, recent_reqs[i].original_url);
}
}
// Check that very long URLs are truncated.
TEST(RequestTrackerTest, GraveyardURLBounded) {
RequestTracker<TestRequest> tracker;
EXPECT_FALSE(tracker.IsUnbounded());
std::string big_url_spec("http://");
big_url_spec.resize(2 * RequestTracker<TestRequest>::kMaxGraveyardURLSize,
'x');
GURL big_url(big_url_spec);
TestRequest req(big_url);
tracker.Add(&req);
tracker.Remove(&req);
ASSERT_EQ(1u, tracker.GetRecentlyDeceased().size());
// The +1 is because GURL canonicalizes with a trailing '/' ... maybe
// we should just save the std::string rather than the GURL.
EXPECT_EQ(RequestTracker<TestRequest>::kMaxGraveyardURLSize + 1,
tracker.GetRecentlyDeceased()[0].original_url.spec().size());
}
// Test the doesn't fail if the URL was invalid. http://crbug.com/21423.
TEST(URLRequestTrackerTest, TrackingInvalidURL) {
RequestTracker<TestRequest> tracker;
EXPECT_FALSE(tracker.IsUnbounded());
EXPECT_EQ(0u, tracker.GetLiveRequests().size());
EXPECT_EQ(0u, tracker.GetRecentlyDeceased().size());
GURL invalid_url("xabc");
EXPECT_FALSE(invalid_url.is_valid());
TestRequest req(invalid_url);
tracker.Add(&req);
tracker.Remove(&req);
// Check that the invalid URL made it into graveyard.
ASSERT_EQ(1u, tracker.GetRecentlyDeceased().size());
EXPECT_FALSE(tracker.GetRecentlyDeceased()[0].original_url.is_valid());
}
bool ShouldRequestBeAddedToGraveyard(const GURL& url) {
return !url.SchemeIs("chrome") && !url.SchemeIs("data");
}
// Check that we can exclude "chrome://" URLs and "data:" URLs from being
// saved into the recent requests list (graveyard), by using a filter.
TEST(RequestTrackerTest, GraveyardCanBeFiltered) {
RequestTracker<TestRequest> tracker;
EXPECT_FALSE(tracker.IsUnbounded());
tracker.SetGraveyardFilter(ShouldRequestBeAddedToGraveyard);
// This will be excluded.
TestRequest req1(GURL("chrome://dontcare"));
tracker.Add(&req1);
tracker.Remove(&req1);
// This will be be added to graveyard.
TestRequest req2(GURL("chrome2://dontcare"));
tracker.Add(&req2);
tracker.Remove(&req2);
// This will be be added to graveyard.
TestRequest req3(GURL("http://foo"));
tracker.Add(&req3);
tracker.Remove(&req3);
// This will be be excluded.
TestRequest req4(GURL("data:sup"));
tracker.Add(&req4);
tracker.Remove(&req4);
ASSERT_EQ(2u, tracker.GetRecentlyDeceased().size());
EXPECT_EQ("chrome2://dontcare/",
tracker.GetRecentlyDeceased()[0].original_url.spec());
EXPECT_EQ("http://foo/",
tracker.GetRecentlyDeceased()[1].original_url.spec());
}
// Convert an unbounded tracker back to being bounded.
TEST(RequestTrackerTest, ConvertUnboundedToBounded) {
RequestTracker<TestRequest> tracker;
EXPECT_FALSE(tracker.IsUnbounded());
EXPECT_EQ(0u, tracker.GetLiveRequests().size());
EXPECT_EQ(0u, tracker.GetRecentlyDeceased().size());
tracker.SetUnbounded(true);
EXPECT_TRUE(tracker.IsUnbounded());
// Add twice as many requests as would fit in the bounded graveyard.
size_t kMaxSize = RequestTracker<TestRequest>::kMaxGraveyardSize * 2;
for (size_t i = 0; i < kMaxSize; ++i) {
TestRequest req(GURL(StringPrintf("http://req%" PRIuS, i).c_str()));
tracker.Add(&req);
tracker.Remove(&req);
}
// Check that all of them got saved.
ASSERT_EQ(kMaxSize, tracker.GetRecentlyDeceased().size());
// Now make the tracker bounded, and add more entries to its graveyard.
tracker.SetUnbounded(false);
kMaxSize = RequestTracker<TestRequest>::kMaxGraveyardSize;
for (size_t i = 0; i < kMaxSize; ++i) {
TestRequest req(GURL(StringPrintf("http://req%" PRIuS, i).c_str()));
tracker.Add(&req);
tracker.Remove(&req);
}
// We should only have kMaxGraveyardSize entries now.
ASSERT_EQ(kMaxSize, tracker.GetRecentlyDeceased().size());
}
} // namespace