blob: f100ad79bdb3c6b4df21235a07252a571cd75ba7 [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 "chrome/browser/history/visit_filter.h"
#include <math.h>
#include "base/logging.h"
#include "base/time/time.h"
#include "chrome/browser/history/history_types.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// So the tests won't go into the other day +/- several hours, return midday of
// today.
base::Time GetClosestMidday() {
return base::Time::Now().LocalMidnight() + base::TimeDelta::FromHours(12);
}
} // namespace
namespace history {
class VisitFilterTest : public testing::Test {
public:
VisitFilterTest();
protected:
virtual void SetUp();
virtual void TearDown();
};
VisitFilterTest::VisitFilterTest() {
}
void VisitFilterTest::SetUp() {
}
void VisitFilterTest::TearDown() {
}
TEST_F(VisitFilterTest, CheckFilters) {
base::Time t(GetClosestMidday());
base::TimeDelta two_hours(base::TimeDelta::FromHours(2));
VisitFilter f;
f.set_max_results(21U);
f.SetFilterTime(t);
f.SetFilterWidth(two_hours);
EXPECT_EQ(21U, f.times().size());
for (size_t i = 0; i < f.times().size(); ++i) {
base::Time t_interval(t);
t_interval -= base::TimeDelta::FromDays(i);
EXPECT_EQ(t_interval - two_hours, f.times()[i].first) <<
"Fails at index:" << i;
EXPECT_EQ(t_interval + two_hours, f.times()[i].second) <<
"Fails at index:" << i;
}
base::Time::Exploded et;
t.LocalExplode(&et);
f.SetDayOfTheWeekFilter(et.day_of_week);
// 3 weeks in 21 days.
ASSERT_EQ(3U, f.times().size());
for (size_t i = 1; i < f.times().size(); ++i) {
base::Time t_interval(t);
t_interval -= base::TimeDelta::FromDays(i);
EXPECT_EQ(f.times()[i].first + base::TimeDelta::FromDays(7),
f.times()[i - 1].first) <<
"Fails at index:" << i;
EXPECT_EQ(f.times()[i].second + base::TimeDelta::FromDays(7),
f.times()[i - 1].second) <<
"Fails at index:" << i;
EXPECT_EQ(two_hours * 2,
f.times()[i].second - f.times()[i].first) <<
"Fails at index:" << i;
}
}
TEST_F(VisitFilterTest, GetTimesInRange) {
base::Time::Exploded et = { 2011, 7, 0, 19, 22, 15, 11, 0 };
base::Time t(base::Time::FromLocalExploded(et));
base::TimeDelta two_hours(base::TimeDelta::FromHours(2));
VisitFilter::TimeVector times;
VisitFilter::GetTimesInRange(t - two_hours, t + two_hours, 10U, &times);
EXPECT_GT(11U, times.size());
for (size_t i = 0; i < times.size(); ++i) {
base::Time t_interval(t);
t_interval -= base::TimeDelta::FromDays(i);
EXPECT_EQ(t_interval - two_hours, times[i].first) << "Fails at index:" << i;
EXPECT_EQ(t_interval + two_hours, times[i].second) <<
"Fails at index:" << i;
}
}
TEST_F(VisitFilterTest, GetTimesOnTheDayOfTheWeek) {
base::Time t(GetClosestMidday());
VisitFilter::TimeVector times;
base::Time::Exploded et;
t.LocalExplode(&et);
VisitFilter::GetTimesOnTheDayOfTheWeek(et.day_of_week, t, 10U, &times);
EXPECT_GT(11U, times.size());
et.hour = 0;
et.minute = 0;
et.second = 0;
et.millisecond = 0;
for (size_t i = 0; i < times.size(); ++i) {
base::Time t_interval(base::Time::FromLocalExploded(et));
t_interval -= base::TimeDelta::FromDays(7 * i);
EXPECT_EQ(t_interval, times[i].first) << "Fails at index:" << i;
EXPECT_EQ(t_interval + base::TimeDelta::FromDays(1), times[i].second) <<
"Fails at index:" << i;
}
}
TEST_F(VisitFilterTest, GetTimesOnTheSameDayType) {
base::Time::Exploded et = { 2011, 7, 0, 19, 22, 15, 11, 0 };
base::Time t(base::Time::FromLocalExploded(et));
VisitFilter::TimeVector times;
t.LocalExplode(&et);
VisitFilter::GetTimesOnTheSameDayType(et.day_of_week, t, 10U, &times);
EXPECT_GT(11U, times.size());
et.hour = 0;
et.minute = 0;
et.second = 0;
et.millisecond = 0;
base::Time t_start(base::Time::FromLocalExploded(et));
base::TimeDelta t_length;
if (et.day_of_week == 0 || et.day_of_week == 6) {
// Sunday and Saturday.
t_length = base::TimeDelta::FromDays(2);
if (et.day_of_week == 0)
t_start -= base::TimeDelta::FromDays(1);
} else {
t_length = base::TimeDelta::FromDays(5);
if (et.day_of_week != 1)
t_start -= base::TimeDelta::FromDays(et.day_of_week - 1);
}
for (size_t i = 0; i < times.size(); ++i) {
base::Time t_interval(t_start);
t_interval -= base::TimeDelta::FromDays(7 * i);
EXPECT_EQ(t_interval, times[i].first) << "Fails at index:" << i;
EXPECT_EQ(t_interval + t_length, times[i].second) << "Fails at index:" << i;
}
}
TEST_F(VisitFilterTest, UniteTimeVectors) {
base::Time t(base::Time::Now());
base::TimeDelta one_hour(base::TimeDelta::FromHours(1));
base::TimeDelta one_day(base::TimeDelta::FromDays(1));
VisitFilter::TimeVector times1;
times1.push_back(std::make_pair(t - one_hour, t + one_hour));
times1.push_back(std::make_pair(t - one_hour - one_day,
t + one_hour - one_day));
times1.push_back(std::make_pair(t - one_hour - one_day * 2,
t + one_hour - one_day * 2));
times1.push_back(std::make_pair(t - one_hour - one_day * 3,
t + one_hour - one_day * 3));
VisitFilter::TimeVector times2;
// Should lie completely within times1[0].
times2.push_back(std::make_pair(t - one_hour / 2, t + one_hour / 2));
// Should lie just before times1[1].
times2.push_back(std::make_pair(t + one_hour * 2 - one_day,
t + one_hour * 3 - one_day));
// Should intersect with times1.
times2.push_back(std::make_pair(t - one_day * 2,
t + one_hour * 2 - one_day * 2));
times2.push_back(std::make_pair(t - one_hour * 2 - one_day * 3,
t - one_day * 3));
VisitFilter::TimeVector result;
EXPECT_TRUE(VisitFilter::UniteTimeVectors(times1, times2, &result));
ASSERT_EQ(5U, result.size());
EXPECT_EQ(t - one_hour, result[0].first);
EXPECT_EQ(t + one_hour, result[0].second);
EXPECT_EQ(t + one_hour * 2 - one_day, result[1].first);
EXPECT_EQ(t + one_hour * 3 - one_day, result[1].second);
EXPECT_EQ(t - one_hour - one_day, result[2].first);
EXPECT_EQ(t + one_hour - one_day, result[2].second);
EXPECT_EQ(t - one_hour - one_day * 2, result[3].first);
EXPECT_EQ(t + one_hour * 2 - one_day * 2, result[3].second);
EXPECT_EQ(t - one_hour * 2 - one_day * 3, result[4].first);
EXPECT_EQ(t + one_hour - one_day * 3, result[4].second);
EXPECT_FALSE(VisitFilter::UniteTimeVectors(VisitFilter::TimeVector(),
VisitFilter::TimeVector(),
&result));
EXPECT_TRUE(result.empty());
}
TEST_F(VisitFilterTest, IntersectTimeVectors) {
base::Time t(base::Time::Now());
base::TimeDelta one_hour(base::TimeDelta::FromHours(1));
base::TimeDelta one_day(base::TimeDelta::FromDays(1));
VisitFilter::TimeVector times1;
times1.push_back(std::make_pair(t - one_hour, t + one_hour));
VisitFilter::TimeVector times2;
// Should lie just before times1[0].
times2.push_back(std::make_pair(t + one_hour * 2,
t + one_hour * 3));
VisitFilter::TimeVector result;
EXPECT_FALSE(VisitFilter::IntersectTimeVectors(times1, times2, &result));
EXPECT_TRUE(result.empty());
times1.push_back(std::make_pair(t - one_hour - one_day,
t + one_hour - one_day));
times1.push_back(std::make_pair(t - one_hour - one_day * 2,
t + one_hour - one_day * 2));
times1.push_back(std::make_pair(t - one_hour - one_day * 3,
t + one_hour - one_day * 3));
// Should lie completely within times1[1].
times2.push_back(std::make_pair(t - one_hour / 2 - one_day,
t + one_hour / 2 - one_day));
// Should intersect with times1.
times2.push_back(std::make_pair(t - one_day * 2,
t + one_hour * 2 - one_day * 2));
times2.push_back(std::make_pair(t - one_hour * 2 - one_day * 3,
t - one_day * 3));
EXPECT_TRUE(VisitFilter::IntersectTimeVectors(times1, times2, &result));
ASSERT_EQ(3U, result.size());
EXPECT_EQ(t - one_hour / 2 - one_day, result[0].first);
EXPECT_EQ(t + one_hour / 2 - one_day, result[0].second);
EXPECT_EQ(t - one_day * 2, result[1].first);
EXPECT_EQ(t + one_hour - one_day * 2, result[1].second);
EXPECT_EQ(t - one_hour - one_day * 3, result[2].first);
EXPECT_EQ(t - one_day * 3, result[2].second);
// Check that touching ranges do not intersect.
times1.clear();
times1.push_back(std::make_pair(t - one_hour, t));
times2.clear();
times2.push_back(std::make_pair(t, t + one_hour));
EXPECT_FALSE(VisitFilter::IntersectTimeVectors(times1, times2, &result));
EXPECT_TRUE(result.empty());
}
TEST_F(VisitFilterTest, GetVisitScore) {
base::Time filter_time;
ASSERT_TRUE(base::Time::FromString("Tue, 24 Apr 2012, 12:00:00",
&filter_time));
VisitFilter filter;
VisitRow visit;
filter.set_sorting_order(VisitFilter::ORDER_BY_RECENCY);
filter.SetFilterTime(filter_time);
filter.SetFilterWidth(base::TimeDelta::FromHours(1));
double one_week_one_hour_staleness = pow(2, -(24.0 * 7.0 + 1.0) /
(24.0 * 7.0));
// No decay on current visit.
visit.visit_time = filter_time;
EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit));
// Half score after a week.
visit.visit_time = filter_time - base::TimeDelta::FromDays(7);
EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit));
// Future visits should be treated as current.
visit.visit_time = filter_time + base::TimeDelta::FromDays(1);
EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit));
filter.set_sorting_order(VisitFilter::ORDER_BY_VISIT_COUNT);
// Every visit should score 1 with this filter.
visit.visit_time = filter_time;
EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit));
visit.visit_time = filter_time - base::TimeDelta::FromDays(7);
EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit));
visit.visit_time = filter_time + base::TimeDelta::FromDays(7);
EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit));
filter.set_sorting_order(VisitFilter::ORDER_BY_TIME_LINEAR);
visit.visit_time = filter_time;
EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit));
// Half the filter width forward in time should get half the score for the
// time difference, but no staleness decay.
visit.visit_time = filter_time + base::TimeDelta::FromMinutes(30);
EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit));
// One week back in time gets full time difference score, but a staleness
// factor of 0.5
visit.visit_time = filter_time - base::TimeDelta::FromDays(7);
EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit));
// One week plus half a filter width should have it's score halved before
// the staleness factor.
filter.SetFilterWidth(base::TimeDelta::FromHours(2));
visit.visit_time = filter_time - base::TimeDelta::FromDays(7) -
base::TimeDelta::FromHours(1);
EXPECT_DOUBLE_EQ(0.5 * one_week_one_hour_staleness,
filter.GetVisitScore(visit));
filter.SetFilterWidth(base::TimeDelta::FromHours(1));
filter.set_sorting_order(VisitFilter::ORDER_BY_TIME_GAUSSIAN);
visit.visit_time = filter_time;
EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit));
// Going forward in time to test the normal distribution function.
visit.visit_time = filter_time + base::TimeDelta::FromHours(1);
EXPECT_DOUBLE_EQ(exp(-0.5), filter.GetVisitScore(visit));
visit.visit_time = filter_time + base::TimeDelta::FromMinutes(30);
EXPECT_DOUBLE_EQ(exp(-0.125), filter.GetVisitScore(visit));
// One week back in time gets full time difference score, but a staleness
// factor of 0.5
visit.visit_time = filter_time - base::TimeDelta::FromDays(7);
EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit));
// One standard deviation of decay, plus the staleness factor.
visit.visit_time = filter_time - base::TimeDelta::FromDays(7) -
base::TimeDelta::FromHours(1);
EXPECT_DOUBLE_EQ(exp(-0.5) * one_week_one_hour_staleness,
filter.GetVisitScore(visit));
}
} // namespace history