blob: c01ce774dc84ae213d3be73cbf67d885c5b3882c [file] [log] [blame]
// Copyright (c) 2012 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 <string>
#include "base/bind.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/stl_util.h"
#include "base/string16.h"
#include "base/time.h"
#include "base/values.h"
#include "chrome/browser/download/download_query.h"
#include "content/public/test/mock_download_item.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::Return;
using ::testing::ReturnRef;
using ::testing::_;
using base::Time;
using base::Value;
using content::DownloadItem;
typedef DownloadQuery::DownloadVector DownloadVector;
namespace {
bool IdNotEqual(int not_id, const DownloadItem& item) {
return item.GetId() != not_id;
}
bool AlwaysReturn(bool result, const DownloadItem& item) {
return result;
}
} // anonymous namespace
class DownloadQueryTest : public testing::Test {
public:
DownloadQueryTest() {}
virtual ~DownloadQueryTest() {}
virtual void TearDown() {
STLDeleteElements(&mocks_);
}
void CreateMocks(int count) {
for (int i = 0; i < count; ++i) {
mocks_.push_back(new content::MockDownloadItem());
}
}
content::MockDownloadItem& mock(int index) { return *mocks_[index]; }
DownloadQuery* query() { return &query_; }
template<typename ValueType> void AddFilter(
DownloadQuery::FilterType name, ValueType value);
void Search() {
query_.Search(mocks_.begin(), mocks_.end(), &results_);
}
DownloadVector* results() { return &results_; }
private:
std::vector<content::MockDownloadItem*> mocks_;
DownloadQuery query_;
DownloadVector results_;
DISALLOW_COPY_AND_ASSIGN(DownloadQueryTest);
};
template<> void DownloadQueryTest::AddFilter(
DownloadQuery::FilterType name, bool cpp_value) {
scoped_ptr<base::Value> value(Value::CreateBooleanValue(cpp_value));
CHECK(query_.AddFilter(name, *value.get()));
}
template<> void DownloadQueryTest::AddFilter(
DownloadQuery::FilterType name, int cpp_value) {
scoped_ptr<base::Value> value(Value::CreateIntegerValue(cpp_value));
CHECK(query_.AddFilter(name, *value.get()));
}
template<> void DownloadQueryTest::AddFilter(
DownloadQuery::FilterType name, const char* cpp_value) {
scoped_ptr<base::Value> value(Value::CreateStringValue(cpp_value));
CHECK(query_.AddFilter(name, *value.get()));
}
template<> void DownloadQueryTest::AddFilter(
DownloadQuery::FilterType name, const char16* cpp_value) {
scoped_ptr<base::Value> value(Value::CreateStringValue(string16(cpp_value)));
CHECK(query_.AddFilter(name, *value.get()));
}
TEST_F(DownloadQueryTest, DownloadQueryEmptyNoItems) {
Search();
EXPECT_EQ(0U, results()->size());
}
TEST_F(DownloadQueryTest, DownloadQueryEmptySomeItems) {
CreateMocks(3);
Search();
EXPECT_EQ(3U, results()->size());
}
TEST_F(DownloadQueryTest, DownloadQueryInvalidFilters) {
scoped_ptr<base::Value> value(Value::CreateIntegerValue(0));
EXPECT_FALSE(query()->AddFilter(
static_cast<DownloadQuery::FilterType>(kint32max),
*value.get()));
}
TEST_F(DownloadQueryTest, DownloadQueryLimit) {
CreateMocks(2);
query()->Limit(1);
Search();
EXPECT_EQ(1U, results()->size());
}
// Syntactic sugar for an expression version of the switch-case statement.
// Cf. Lisp's |case| form.
#define SWITCH2(_index, _col1, _ret1, _default) \
((_index == (_col1)) ? _ret1 : _default)
#define SWITCH3(_index, _col1, _ret1, _col2, _ret2, _default) \
SWITCH2(_index, _col1, _ret1, SWITCH2(_index, _col2, _ret2, _default))
#define SWITCH4(_index, _col1, _ret1, _col2, _ret2, _col3, _ret3, _default) \
SWITCH3(_index, _col1, _ret1, _col2, _ret2, \
SWITCH2(_index, _col3, _ret3, _default))
TEST_F(DownloadQueryTest, DownloadQuery_QueryFilter) {
FilePath match_filename(FILE_PATH_LITERAL("query"));
FilePath fail_filename(FILE_PATH_LITERAL("fail"));
GURL fail_url("http://example.com/fail");
GURL match_url("http://query.com/query");
scoped_ptr<base::Value> query_str(base::Value::CreateStringValue("query"));
static const size_t kNumItems = 4;
CreateMocks(kNumItems);
for (size_t i = 0; i < kNumItems; ++i) {
if (i != 0)
EXPECT_CALL(mock(i), GetId()).WillOnce(Return(i));
EXPECT_CALL(mock(i), GetTargetFilePath())
.WillRepeatedly(ReturnRef((i & 1) ? match_filename : fail_filename));
EXPECT_CALL(mock(i), GetOriginalUrl())
.WillRepeatedly(ReturnRef((i & 2) ? match_url : fail_url));
EXPECT_CALL(mock(i), GetBrowserContext()).WillRepeatedly(Return(
static_cast<content::BrowserContext*>(NULL)));
}
query()->AddFilter(DownloadQuery::FILTER_QUERY, *query_str.get());
Search();
ASSERT_EQ(3U, results()->size());
EXPECT_EQ(1, results()->at(0)->GetId());
EXPECT_EQ(2, results()->at(1)->GetId());
EXPECT_EQ(3, results()->at(2)->GetId());
// TODO(phajdan.jr): Also test these strings:
// "/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd",
// L"/\x4f60\x597d\x4f60\x597d",
// "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD"
}
TEST_F(DownloadQueryTest, DownloadQueryAllFilters) {
// Set up mocks such that only mock(0) matches all filters, and every other
// mock fails a different filter (or two for GREATER/LESS filters).
static const size_t kNumItems = 18;
CreateMocks(kNumItems);
FilePath refail_filename(FILE_PATH_LITERAL("z"));
FilePath fail_filename(FILE_PATH_LITERAL("fail"));
FilePath match_filename(FILE_PATH_LITERAL("match"));
GURL refail_url("http://z.com/");
GURL fail_url("http://example.com/fail");
GURL match_url("http://example.com/match");
// Picture a 2D matrix. The rows are MockDownloadItems and the columns are
// filter types. Every cell contains a value that matches all filters, except
// for the diagonal. Every item matches all the filters except one filter,
// which it fails, except one item, which matches all the filters without
// exception. Each mocked method is used to test (corresponds to) one or more
// filter types (columns). For example, GetTotalBytes() is used to test
// FILTER_TOTAL_BYTES_GREATER, FILTER_TOTAL_BYTES_LESS, and
// FILTER_TOTAL_BYTES, so it uses 3 columns: it returns 1 for row (item) 11,
// it returns 4 for row 12, 3 for 13, and it returns 2 for all other rows
// (items).
for (size_t i = 0; i < kNumItems; ++i) {
EXPECT_CALL(mock(i), GetId()).WillRepeatedly(Return(i));
EXPECT_CALL(mock(i), GetReceivedBytes()).WillRepeatedly(Return(SWITCH2(i,
1, 2,
1)));
EXPECT_CALL(mock(i), GetSafetyState()).WillRepeatedly(Return(SWITCH2(i,
2, DownloadItem::DANGEROUS,
DownloadItem::DANGEROUS_BUT_VALIDATED)));
EXPECT_CALL(mock(i), GetTargetFilePath())
.WillRepeatedly(ReturnRef(SWITCH3(i,
3, refail_filename,
4, fail_filename,
match_filename)));
EXPECT_CALL(mock(i), GetMimeType()).WillRepeatedly(Return(SWITCH2(i,
5, "image",
"text")));
EXPECT_CALL(mock(i), IsPaused()).WillRepeatedly(Return(SWITCH2(i,
6, false,
true)));
EXPECT_CALL(mock(i), GetStartTime()).WillRepeatedly(Return(SWITCH4(i,
7, base::Time::FromTimeT(1),
8, base::Time::FromTimeT(4),
9, base::Time::FromTimeT(3),
base::Time::FromTimeT(2))));
EXPECT_CALL(mock(i), GetTotalBytes()).WillRepeatedly(Return(SWITCH4(i,
10, 1,
11, 4,
12, 3,
2)));
EXPECT_CALL(mock(i), GetOriginalUrl()).WillRepeatedly(ReturnRef(SWITCH3(i,
13, refail_url,
14, fail_url,
match_url)));
// 15 is AddFilter(Bind(IdNotEqual, 15))
EXPECT_CALL(mock(i), GetState()).WillRepeatedly(Return(SWITCH2(i,
16, DownloadItem::CANCELLED,
DownloadItem::IN_PROGRESS)));
EXPECT_CALL(mock(i), GetDangerType()).WillRepeatedly(Return(SWITCH2(i,
17, content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS)));
}
for (size_t i = 0; i < kNumItems; ++i) {
switch (i) {
case 0: break;
case 1: AddFilter(DownloadQuery::FILTER_BYTES_RECEIVED, 1); break;
case 2: AddFilter(DownloadQuery::FILTER_DANGER_ACCEPTED, true);
break;
case 3: AddFilter(DownloadQuery::FILTER_FILENAME_REGEX, "a"); break;
case 4: AddFilter(DownloadQuery::FILTER_FILENAME,
match_filename.value().c_str()); break;
case 5: AddFilter(DownloadQuery::FILTER_MIME, "text"); break;
case 6: AddFilter(DownloadQuery::FILTER_PAUSED, true); break;
case 7: AddFilter(DownloadQuery::FILTER_STARTED_AFTER, 1000); break;
case 8: AddFilter(DownloadQuery::FILTER_STARTED_BEFORE, 4000);
break;
case 9: AddFilter(DownloadQuery::FILTER_START_TIME, 2000); break;
case 10: AddFilter(DownloadQuery::FILTER_TOTAL_BYTES_GREATER, 1);
break;
case 11: AddFilter(DownloadQuery::FILTER_TOTAL_BYTES_LESS, 4);
break;
case 12: AddFilter(DownloadQuery::FILTER_TOTAL_BYTES, 2); break;
case 13: AddFilter(DownloadQuery::FILTER_URL_REGEX, "example");
break;
case 14: AddFilter(DownloadQuery::FILTER_URL,
match_url.spec().c_str()); break;
case 15: CHECK(query()->AddFilter(base::Bind(&IdNotEqual, 15))); break;
case 16: query()->AddFilter(DownloadItem::IN_PROGRESS); break;
case 17: query()->AddFilter(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS);
break;
default: NOTREACHED(); break;
}
Search();
ASSERT_EQ(kNumItems - i, results()->size())
<< "Failing filter: " << i;
if (i > 0) {
ASSERT_EQ(0, results()->at(0)->GetId())
<< "Failing filter: " << i;
for (size_t j = 1; j < kNumItems - i; ++j) {
ASSERT_EQ(static_cast<int32>(j + i), results()->at(j)->GetId())
<< "Failing filter: " << i;
}
}
}
}
TEST_F(DownloadQueryTest, DownloadQuerySortBytesReceived) {
CreateMocks(2);
EXPECT_CALL(mock(0), GetReceivedBytes()).WillRepeatedly(Return(0));
EXPECT_CALL(mock(1), GetReceivedBytes()).WillRepeatedly(Return(1));
query()->AddSorter(
DownloadQuery::SORT_BYTES_RECEIVED, DownloadQuery::DESCENDING);
Search();
EXPECT_EQ(1, results()->at(0)->GetReceivedBytes());
EXPECT_EQ(0, results()->at(1)->GetReceivedBytes());
}
TEST_F(DownloadQueryTest, DownloadQuerySortDanger) {
CreateMocks(2);
EXPECT_CALL(mock(0), GetDangerType()).WillRepeatedly(Return(
content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE));
EXPECT_CALL(mock(1), GetDangerType()).WillRepeatedly(Return(
content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS));
query()->AddSorter(
DownloadQuery::SORT_DANGER, DownloadQuery::ASCENDING);
Search();
EXPECT_EQ(content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
results()->at(0)->GetDangerType());
EXPECT_EQ(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_FILE,
results()->at(1)->GetDangerType());
}
TEST_F(DownloadQueryTest, DownloadQuerySortDangerAccepted) {
CreateMocks(2);
EXPECT_CALL(mock(0), GetSafetyState()).WillRepeatedly(Return(
DownloadItem::DANGEROUS));
EXPECT_CALL(mock(1), GetSafetyState()).WillRepeatedly(Return(
DownloadItem::DANGEROUS_BUT_VALIDATED));
query()->AddSorter(
DownloadQuery::SORT_DANGER_ACCEPTED, DownloadQuery::DESCENDING);
Search();
EXPECT_EQ(DownloadItem::DANGEROUS_BUT_VALIDATED,
results()->at(0)->GetSafetyState());
EXPECT_EQ(DownloadItem::DANGEROUS, results()->at(1)->GetSafetyState());
}
TEST_F(DownloadQueryTest, DownloadQuerySortFilename) {
CreateMocks(2);
FilePath a_path(FILE_PATH_LITERAL("a"));
FilePath b_path(FILE_PATH_LITERAL("b"));
EXPECT_CALL(mock(0), GetTargetFilePath()).WillRepeatedly(ReturnRef(b_path));
EXPECT_CALL(mock(1), GetTargetFilePath()).WillRepeatedly(ReturnRef(a_path));
query()->AddSorter(
DownloadQuery::SORT_FILENAME, DownloadQuery::ASCENDING);
Search();
EXPECT_EQ(a_path, results()->at(0)->GetTargetFilePath());
EXPECT_EQ(b_path, results()->at(1)->GetTargetFilePath());
}
TEST_F(DownloadQueryTest, DownloadQuerySortMime) {
CreateMocks(2);
EXPECT_CALL(mock(0), GetMimeType()).WillRepeatedly(Return("a"));
EXPECT_CALL(mock(1), GetMimeType()).WillRepeatedly(Return("b"));
query()->AddSorter(
DownloadQuery::SORT_MIME, DownloadQuery::DESCENDING);
Search();
EXPECT_EQ("b", results()->at(0)->GetMimeType());
EXPECT_EQ("a", results()->at(1)->GetMimeType());
}
TEST_F(DownloadQueryTest, DownloadQuerySortPaused) {
CreateMocks(2);
EXPECT_CALL(mock(0), IsPaused()).WillRepeatedly(Return(true));
EXPECT_CALL(mock(1), IsPaused()).WillRepeatedly(Return(false));
query()->AddSorter(
DownloadQuery::SORT_PAUSED, DownloadQuery::ASCENDING);
Search();
EXPECT_FALSE(results()->at(0)->IsPaused());
EXPECT_TRUE(results()->at(1)->IsPaused());
}
TEST_F(DownloadQueryTest, DownloadQuerySortStartTime) {
CreateMocks(2);
EXPECT_CALL(mock(0), GetStartTime()).WillRepeatedly(Return(
base::Time::FromTimeT(0)));
EXPECT_CALL(mock(1), GetStartTime()).WillRepeatedly(Return(
base::Time::FromTimeT(1)));
query()->AddSorter(
DownloadQuery::SORT_START_TIME, DownloadQuery::DESCENDING);
Search();
EXPECT_EQ(base::Time::FromTimeT(1), results()->at(0)->GetStartTime());
EXPECT_EQ(base::Time::FromTimeT(0), results()->at(1)->GetStartTime());
}
TEST_F(DownloadQueryTest, DownloadQuerySortState) {
CreateMocks(2);
EXPECT_CALL(mock(0), GetState()).WillRepeatedly(Return(
DownloadItem::IN_PROGRESS));
EXPECT_CALL(mock(1), GetState()).WillRepeatedly(Return(
DownloadItem::COMPLETE));
query()->AddSorter(
DownloadQuery::SORT_STATE, DownloadQuery::ASCENDING);
Search();
EXPECT_EQ(DownloadItem::IN_PROGRESS, results()->at(0)->GetState());
EXPECT_EQ(DownloadItem::COMPLETE, results()->at(1)->GetState());
}
TEST_F(DownloadQueryTest, DownloadQuerySortTotalBytes) {
CreateMocks(2);
EXPECT_CALL(mock(0), GetTotalBytes()).WillRepeatedly(Return(0));
EXPECT_CALL(mock(1), GetTotalBytes()).WillRepeatedly(Return(1));
query()->AddSorter(
DownloadQuery::SORT_TOTAL_BYTES, DownloadQuery::DESCENDING);
Search();
EXPECT_EQ(1, results()->at(0)->GetTotalBytes());
EXPECT_EQ(0, results()->at(1)->GetTotalBytes());
}
TEST_F(DownloadQueryTest, DownloadQuerySortUrl) {
CreateMocks(2);
GURL a_url("http://example.com/a");
GURL b_url("http://example.com/b");
EXPECT_CALL(mock(0), GetOriginalUrl()).WillRepeatedly(ReturnRef(b_url));
EXPECT_CALL(mock(1), GetOriginalUrl()).WillRepeatedly(ReturnRef(a_url));
query()->AddSorter(
DownloadQuery::SORT_URL, DownloadQuery::ASCENDING);
Search();
EXPECT_EQ(a_url, results()->at(0)->GetOriginalUrl());
EXPECT_EQ(b_url, results()->at(1)->GetOriginalUrl());
}
TEST_F(DownloadQueryTest, DownloadQuerySortId) {
CreateMocks(2);
EXPECT_CALL(mock(0), GetReceivedBytes()).WillRepeatedly(Return(0));
EXPECT_CALL(mock(1), GetReceivedBytes()).WillRepeatedly(Return(0));
EXPECT_CALL(mock(0), GetId()).WillRepeatedly(Return(1));
EXPECT_CALL(mock(1), GetId()).WillRepeatedly(Return(0));
query()->AddSorter(
DownloadQuery::SORT_BYTES_RECEIVED, DownloadQuery::DESCENDING);
Search();
EXPECT_EQ(0, results()->at(0)->GetId());
EXPECT_EQ(1, results()->at(1)->GetId());
}
TEST_F(DownloadQueryTest, DownloadQueryFilterPerformance) {
static const int kNumItems = 10000;
static const int kNumFilters = 1000;
CreateMocks(kNumItems);
for (size_t i = 0; i < (kNumFilters - 1); ++i) {
query()->AddFilter(base::Bind(&AlwaysReturn, true));
}
query()->AddFilter(base::Bind(&AlwaysReturn, false));
base::Time start = base::Time::Now();
Search();
base::Time end = base::Time::Now();
double nanos = (end - start).InMillisecondsF() * 1000.0 * 1000.0;
double nanos_per_item = nanos / static_cast<double>(kNumItems);
double nanos_per_item_per_filter = nanos_per_item
/ static_cast<double>(kNumFilters);
std::cout << "Search took " << nanos_per_item_per_filter
<< " nanoseconds per item per filter.\n";
}