1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/history/visit_filter.h" 6 7 #include <math.h> 8 9 #include "base/logging.h" 10 #include "base/time/time.h" 11 #include "chrome/browser/history/history_types.h" 12 #include "testing/gtest/include/gtest/gtest.h" 13 14 namespace { 15 16 // So the tests won't go into the other day +/- several hours, return midday of 17 // today. 18 base::Time GetClosestMidday() { 19 return base::Time::Now().LocalMidnight() + base::TimeDelta::FromHours(12); 20 } 21 22 } // namespace 23 24 namespace history { 25 26 class VisitFilterTest : public testing::Test { 27 public: 28 VisitFilterTest(); 29 30 protected: 31 virtual void SetUp(); 32 virtual void TearDown(); 33 }; 34 35 VisitFilterTest::VisitFilterTest() { 36 } 37 38 void VisitFilterTest::SetUp() { 39 } 40 41 void VisitFilterTest::TearDown() { 42 } 43 44 TEST_F(VisitFilterTest, CheckFilters) { 45 base::Time t(GetClosestMidday()); 46 base::TimeDelta two_hours(base::TimeDelta::FromHours(2)); 47 VisitFilter f; 48 f.set_max_results(21U); 49 f.SetFilterTime(t); 50 f.SetFilterWidth(two_hours); 51 EXPECT_EQ(21U, f.times().size()); 52 for (size_t i = 0; i < f.times().size(); ++i) { 53 base::Time t_interval(t); 54 t_interval -= base::TimeDelta::FromDays(i); 55 EXPECT_EQ(t_interval - two_hours, f.times()[i].first) << 56 "Fails at index:" << i; 57 EXPECT_EQ(t_interval + two_hours, f.times()[i].second) << 58 "Fails at index:" << i; 59 } 60 base::Time::Exploded et; 61 t.LocalExplode(&et); 62 f.SetDayOfTheWeekFilter(et.day_of_week); 63 // 3 weeks in 21 days. 64 ASSERT_EQ(3U, f.times().size()); 65 for (size_t i = 1; i < f.times().size(); ++i) { 66 base::Time t_interval(t); 67 t_interval -= base::TimeDelta::FromDays(i); 68 EXPECT_EQ(f.times()[i].first + base::TimeDelta::FromDays(7), 69 f.times()[i - 1].first) << 70 "Fails at index:" << i; 71 EXPECT_EQ(f.times()[i].second + base::TimeDelta::FromDays(7), 72 f.times()[i - 1].second) << 73 "Fails at index:" << i; 74 EXPECT_EQ(two_hours * 2, 75 f.times()[i].second - f.times()[i].first) << 76 "Fails at index:" << i; 77 } 78 } 79 80 TEST_F(VisitFilterTest, GetTimesInRange) { 81 base::Time::Exploded et = { 2011, 7, 0, 19, 22, 15, 11, 0 }; 82 base::Time t(base::Time::FromLocalExploded(et)); 83 base::TimeDelta two_hours(base::TimeDelta::FromHours(2)); 84 VisitFilter::TimeVector times; 85 VisitFilter::GetTimesInRange(t - two_hours, t + two_hours, 10U, ×); 86 EXPECT_GT(11U, times.size()); 87 for (size_t i = 0; i < times.size(); ++i) { 88 base::Time t_interval(t); 89 t_interval -= base::TimeDelta::FromDays(i); 90 EXPECT_EQ(t_interval - two_hours, times[i].first) << "Fails at index:" << i; 91 EXPECT_EQ(t_interval + two_hours, times[i].second) << 92 "Fails at index:" << i; 93 } 94 } 95 96 TEST_F(VisitFilterTest, GetTimesOnTheDayOfTheWeek) { 97 base::Time t(GetClosestMidday()); 98 VisitFilter::TimeVector times; 99 base::Time::Exploded et; 100 t.LocalExplode(&et); 101 VisitFilter::GetTimesOnTheDayOfTheWeek(et.day_of_week, t, 10U, ×); 102 EXPECT_GT(11U, times.size()); 103 et.hour = 0; 104 et.minute = 0; 105 et.second = 0; 106 et.millisecond = 0; 107 for (size_t i = 0; i < times.size(); ++i) { 108 base::Time t_interval(base::Time::FromLocalExploded(et)); 109 t_interval -= base::TimeDelta::FromDays(7 * i); 110 EXPECT_EQ(t_interval, times[i].first) << "Fails at index:" << i; 111 EXPECT_EQ(t_interval + base::TimeDelta::FromDays(1), times[i].second) << 112 "Fails at index:" << i; 113 } 114 } 115 116 TEST_F(VisitFilterTest, GetTimesOnTheSameDayType) { 117 base::Time::Exploded et = { 2011, 7, 0, 19, 22, 15, 11, 0 }; 118 base::Time t(base::Time::FromLocalExploded(et)); 119 VisitFilter::TimeVector times; 120 t.LocalExplode(&et); 121 VisitFilter::GetTimesOnTheSameDayType(et.day_of_week, t, 10U, ×); 122 EXPECT_GT(11U, times.size()); 123 et.hour = 0; 124 et.minute = 0; 125 et.second = 0; 126 et.millisecond = 0; 127 base::Time t_start(base::Time::FromLocalExploded(et)); 128 base::TimeDelta t_length; 129 if (et.day_of_week == 0 || et.day_of_week == 6) { 130 // Sunday and Saturday. 131 t_length = base::TimeDelta::FromDays(2); 132 if (et.day_of_week == 0) 133 t_start -= base::TimeDelta::FromDays(1); 134 } else { 135 t_length = base::TimeDelta::FromDays(5); 136 if (et.day_of_week != 1) 137 t_start -= base::TimeDelta::FromDays(et.day_of_week - 1); 138 } 139 for (size_t i = 0; i < times.size(); ++i) { 140 base::Time t_interval(t_start); 141 t_interval -= base::TimeDelta::FromDays(7 * i); 142 EXPECT_EQ(t_interval, times[i].first) << "Fails at index:" << i; 143 EXPECT_EQ(t_interval + t_length, times[i].second) << "Fails at index:" << i; 144 } 145 } 146 147 TEST_F(VisitFilterTest, UniteTimeVectors) { 148 base::Time t(base::Time::Now()); 149 base::TimeDelta one_hour(base::TimeDelta::FromHours(1)); 150 base::TimeDelta one_day(base::TimeDelta::FromDays(1)); 151 VisitFilter::TimeVector times1; 152 times1.push_back(std::make_pair(t - one_hour, t + one_hour)); 153 times1.push_back(std::make_pair(t - one_hour - one_day, 154 t + one_hour - one_day)); 155 times1.push_back(std::make_pair(t - one_hour - one_day * 2, 156 t + one_hour - one_day * 2)); 157 times1.push_back(std::make_pair(t - one_hour - one_day * 3, 158 t + one_hour - one_day * 3)); 159 160 VisitFilter::TimeVector times2; 161 // Should lie completely within times1[0]. 162 times2.push_back(std::make_pair(t - one_hour / 2, t + one_hour / 2)); 163 // Should lie just before times1[1]. 164 times2.push_back(std::make_pair(t + one_hour * 2 - one_day, 165 t + one_hour * 3 - one_day)); 166 // Should intersect with times1. 167 times2.push_back(std::make_pair(t - one_day * 2, 168 t + one_hour * 2 - one_day * 2)); 169 times2.push_back(std::make_pair(t - one_hour * 2 - one_day * 3, 170 t - one_day * 3)); 171 172 VisitFilter::TimeVector result; 173 EXPECT_TRUE(VisitFilter::UniteTimeVectors(times1, times2, &result)); 174 ASSERT_EQ(5U, result.size()); 175 EXPECT_EQ(t - one_hour, result[0].first); 176 EXPECT_EQ(t + one_hour, result[0].second); 177 EXPECT_EQ(t + one_hour * 2 - one_day, result[1].first); 178 EXPECT_EQ(t + one_hour * 3 - one_day, result[1].second); 179 EXPECT_EQ(t - one_hour - one_day, result[2].first); 180 EXPECT_EQ(t + one_hour - one_day, result[2].second); 181 EXPECT_EQ(t - one_hour - one_day * 2, result[3].first); 182 EXPECT_EQ(t + one_hour * 2 - one_day * 2, result[3].second); 183 EXPECT_EQ(t - one_hour * 2 - one_day * 3, result[4].first); 184 EXPECT_EQ(t + one_hour - one_day * 3, result[4].second); 185 186 EXPECT_FALSE(VisitFilter::UniteTimeVectors(VisitFilter::TimeVector(), 187 VisitFilter::TimeVector(), 188 &result)); 189 EXPECT_TRUE(result.empty()); 190 } 191 192 TEST_F(VisitFilterTest, IntersectTimeVectors) { 193 base::Time t(base::Time::Now()); 194 base::TimeDelta one_hour(base::TimeDelta::FromHours(1)); 195 base::TimeDelta one_day(base::TimeDelta::FromDays(1)); 196 VisitFilter::TimeVector times1; 197 times1.push_back(std::make_pair(t - one_hour, t + one_hour)); 198 199 VisitFilter::TimeVector times2; 200 // Should lie just before times1[0]. 201 times2.push_back(std::make_pair(t + one_hour * 2, 202 t + one_hour * 3)); 203 204 VisitFilter::TimeVector result; 205 EXPECT_FALSE(VisitFilter::IntersectTimeVectors(times1, times2, &result)); 206 EXPECT_TRUE(result.empty()); 207 208 times1.push_back(std::make_pair(t - one_hour - one_day, 209 t + one_hour - one_day)); 210 times1.push_back(std::make_pair(t - one_hour - one_day * 2, 211 t + one_hour - one_day * 2)); 212 times1.push_back(std::make_pair(t - one_hour - one_day * 3, 213 t + one_hour - one_day * 3)); 214 215 // Should lie completely within times1[1]. 216 times2.push_back(std::make_pair(t - one_hour / 2 - one_day, 217 t + one_hour / 2 - one_day)); 218 // Should intersect with times1. 219 times2.push_back(std::make_pair(t - one_day * 2, 220 t + one_hour * 2 - one_day * 2)); 221 times2.push_back(std::make_pair(t - one_hour * 2 - one_day * 3, 222 t - one_day * 3)); 223 224 EXPECT_TRUE(VisitFilter::IntersectTimeVectors(times1, times2, &result)); 225 ASSERT_EQ(3U, result.size()); 226 EXPECT_EQ(t - one_hour / 2 - one_day, result[0].first); 227 EXPECT_EQ(t + one_hour / 2 - one_day, result[0].second); 228 EXPECT_EQ(t - one_day * 2, result[1].first); 229 EXPECT_EQ(t + one_hour - one_day * 2, result[1].second); 230 EXPECT_EQ(t - one_hour - one_day * 3, result[2].first); 231 EXPECT_EQ(t - one_day * 3, result[2].second); 232 233 // Check that touching ranges do not intersect. 234 times1.clear(); 235 times1.push_back(std::make_pair(t - one_hour, t)); 236 times2.clear(); 237 times2.push_back(std::make_pair(t, t + one_hour)); 238 EXPECT_FALSE(VisitFilter::IntersectTimeVectors(times1, times2, &result)); 239 EXPECT_TRUE(result.empty()); 240 } 241 242 TEST_F(VisitFilterTest, GetVisitScore) { 243 base::Time filter_time; 244 ASSERT_TRUE(base::Time::FromString("Tue, 24 Apr 2012, 12:00:00", 245 &filter_time)); 246 VisitFilter filter; 247 VisitRow visit; 248 249 filter.set_sorting_order(VisitFilter::ORDER_BY_RECENCY); 250 filter.SetFilterTime(filter_time); 251 filter.SetFilterWidth(base::TimeDelta::FromHours(1)); 252 253 double one_week_one_hour_staleness = pow(2, -(24.0 * 7.0 + 1.0) / 254 (24.0 * 7.0)); 255 256 // No decay on current visit. 257 visit.visit_time = filter_time; 258 EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); 259 // Half score after a week. 260 visit.visit_time = filter_time - base::TimeDelta::FromDays(7); 261 EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit)); 262 // Future visits should be treated as current. 263 visit.visit_time = filter_time + base::TimeDelta::FromDays(1); 264 EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); 265 266 filter.set_sorting_order(VisitFilter::ORDER_BY_VISIT_COUNT); 267 // Every visit should score 1 with this filter. 268 visit.visit_time = filter_time; 269 EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); 270 visit.visit_time = filter_time - base::TimeDelta::FromDays(7); 271 EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); 272 visit.visit_time = filter_time + base::TimeDelta::FromDays(7); 273 EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); 274 275 filter.set_sorting_order(VisitFilter::ORDER_BY_TIME_LINEAR); 276 visit.visit_time = filter_time; 277 EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); 278 // Half the filter width forward in time should get half the score for the 279 // time difference, but no staleness decay. 280 visit.visit_time = filter_time + base::TimeDelta::FromMinutes(30); 281 EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit)); 282 // One week back in time gets full time difference score, but a staleness 283 // factor of 0.5 284 visit.visit_time = filter_time - base::TimeDelta::FromDays(7); 285 EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit)); 286 // One week plus half a filter width should have it's score halved before 287 // the staleness factor. 288 filter.SetFilterWidth(base::TimeDelta::FromHours(2)); 289 visit.visit_time = filter_time - base::TimeDelta::FromDays(7) - 290 base::TimeDelta::FromHours(1); 291 EXPECT_DOUBLE_EQ(0.5 * one_week_one_hour_staleness, 292 filter.GetVisitScore(visit)); 293 filter.SetFilterWidth(base::TimeDelta::FromHours(1)); 294 295 filter.set_sorting_order(VisitFilter::ORDER_BY_TIME_GAUSSIAN); 296 visit.visit_time = filter_time; 297 EXPECT_DOUBLE_EQ(1.0, filter.GetVisitScore(visit)); 298 // Going forward in time to test the normal distribution function. 299 visit.visit_time = filter_time + base::TimeDelta::FromHours(1); 300 EXPECT_DOUBLE_EQ(exp(-0.5), filter.GetVisitScore(visit)); 301 visit.visit_time = filter_time + base::TimeDelta::FromMinutes(30); 302 EXPECT_DOUBLE_EQ(exp(-0.125), filter.GetVisitScore(visit)); 303 // One week back in time gets full time difference score, but a staleness 304 // factor of 0.5 305 visit.visit_time = filter_time - base::TimeDelta::FromDays(7); 306 EXPECT_DOUBLE_EQ(0.5, filter.GetVisitScore(visit)); 307 // One standard deviation of decay, plus the staleness factor. 308 visit.visit_time = filter_time - base::TimeDelta::FromDays(7) - 309 base::TimeDelta::FromHours(1); 310 EXPECT_DOUBLE_EQ(exp(-0.5) * one_week_one_hour_staleness, 311 filter.GetVisitScore(visit)); 312 } 313 314 } // namespace history 315