1 // Copyright 2017 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/media/media_engagement_service.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/callback_helpers.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/macros.h"
15 #include "base/memory/ptr_util.h"
16 #include "base/run_loop.h"
17 #include "base/test/scoped_feature_list.h"
18 #include "base/test/simple_test_clock.h"
19 #include "base/test/test_mock_time_task_runner.h"
20 #include "base/values.h"
21 #include "build/build_config.h"
22 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
23 #include "chrome/browser/history/history_service_factory.h"
24 #include "chrome/browser/media/media_engagement_score.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
28 #include "chrome/test/base/testing_profile.h"
29 #include "components/content_settings/core/browser/content_settings_observer.h"
30 #include "components/content_settings/core/browser/host_content_settings_map.h"
31 #include "components/content_settings/core/common/content_settings.h"
32 #include "components/content_settings/core/common/content_settings_types.h"
33 #include "components/history/core/browser/history_database_params.h"
34 #include "components/history/core/browser/history_service.h"
35 #include "components/history/core/test/test_history_database.h"
36 #include "components/prefs/pref_service.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/navigation_entry.h"
39 #include "content/public/browser/page_navigator.h"
40 #include "content/public/browser/web_contents.h"
41 #include "content/public/test/web_contents_tester.h"
42 #include "media/base/media_switches.h"
43 #include "testing/gtest/include/gtest/gtest.h"
44
45 namespace {
46
47 base::FilePath g_temp_history_dir;
48
49 // History is automatically expired after 90 days.
50 base::TimeDelta kHistoryExpirationThreshold = base::TimeDelta::FromDays(90);
51
52 // Waits until a change is observed in media engagement content settings.
53 class MediaEngagementChangeWaiter : public content_settings::Observer {
54 public:
MediaEngagementChangeWaiter(Profile * profile)55 explicit MediaEngagementChangeWaiter(Profile* profile) : profile_(profile) {
56 HostContentSettingsMapFactory::GetForProfile(profile)->AddObserver(this);
57 }
58
~MediaEngagementChangeWaiter()59 ~MediaEngagementChangeWaiter() override {
60 HostContentSettingsMapFactory::GetForProfile(profile_)->RemoveObserver(
61 this);
62 }
63
64 // Overridden from content_settings::Observer:
OnContentSettingChanged(const ContentSettingsPattern & primary_pattern,const ContentSettingsPattern & secondary_pattern,ContentSettingsType content_type)65 void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
66 const ContentSettingsPattern& secondary_pattern,
67 ContentSettingsType content_type) override {
68 if (content_type == ContentSettingsType::MEDIA_ENGAGEMENT)
69 Proceed();
70 }
71
Wait()72 void Wait() { run_loop_.Run(); }
73
74 private:
Proceed()75 void Proceed() { run_loop_.Quit(); }
76
77 Profile* profile_;
78 base::RunLoop run_loop_;
79
80 DISALLOW_COPY_AND_ASSIGN(MediaEngagementChangeWaiter);
81 };
82
GetReferenceTime()83 base::Time GetReferenceTime() {
84 base::Time::Exploded exploded_reference_time;
85 exploded_reference_time.year = 2015;
86 exploded_reference_time.month = 1;
87 exploded_reference_time.day_of_month = 30;
88 exploded_reference_time.day_of_week = 5;
89 exploded_reference_time.hour = 11;
90 exploded_reference_time.minute = 0;
91 exploded_reference_time.second = 0;
92 exploded_reference_time.millisecond = 0;
93
94 base::Time out_time;
95 EXPECT_TRUE(
96 base::Time::FromLocalExploded(exploded_reference_time, &out_time));
97 return out_time;
98 }
99
BuildTestHistoryService(scoped_refptr<base::SequencedTaskRunner> backend_runner,content::BrowserContext * context)100 std::unique_ptr<KeyedService> BuildTestHistoryService(
101 scoped_refptr<base::SequencedTaskRunner> backend_runner,
102 content::BrowserContext* context) {
103 std::unique_ptr<history::HistoryService> service(
104 new history::HistoryService());
105 if (backend_runner)
106 service->set_backend_task_runner_for_testing(std::move(backend_runner));
107 service->Init(history::TestHistoryDatabaseParamsForPath(g_temp_history_dir));
108 return service;
109 }
110
111 // Blocks until the HistoryBackend is completely destroyed, to ensure the
112 // destruction tasks do not interfere with a newer instance of
113 // HistoryService/HistoryBackend.
BlockUntilHistoryBackendDestroyed(Profile * profile)114 void BlockUntilHistoryBackendDestroyed(Profile* profile) {
115 history::HistoryService* history_service =
116 HistoryServiceFactory::GetForProfileWithoutCreating(profile);
117
118 // Nothing to destroy
119 if (!history_service)
120 return;
121
122 base::RunLoop run_loop;
123 history_service->SetOnBackendDestroyTask(run_loop.QuitClosure());
124 HistoryServiceFactory::ShutdownForProfile(profile);
125 run_loop.Run();
126 }
127
128 } // namespace
129
130 class MediaEngagementServiceTest : public ChromeRenderViewHostTestHarness,
131 public testing::WithParamInterface<bool> {
132 public:
SetUp()133 void SetUp() override {
134 mock_time_task_runner_ =
135 base::MakeRefCounted<base::TestMockTimeTaskRunner>();
136
137 if (GetParam()) {
138 scoped_feature_list_.InitWithFeatures(
139 {media::kRecordMediaEngagementScores,
140 media::kMediaEngagementHTTPSOnly},
141 {});
142 } else {
143 scoped_feature_list_.InitWithFeatures(
144 {media::kRecordMediaEngagementScores},
145 {media::kMediaEngagementHTTPSOnly});
146 }
147 ChromeRenderViewHostTestHarness::SetUp();
148
149 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
150 g_temp_history_dir = temp_dir_.GetPath();
151 ConfigureHistoryService(nullptr);
152
153 test_clock_.SetNow(GetReferenceTime());
154 service_ = base::WrapUnique(StartNewMediaEngagementService());
155 }
156
service() const157 MediaEngagementService* service() const { return service_.get(); }
158
StartNewMediaEngagementService()159 MediaEngagementService* StartNewMediaEngagementService() {
160 MediaEngagementService* service =
161 new MediaEngagementService(profile(), &test_clock_);
162 base::RunLoop().RunUntilIdle();
163 return service;
164 }
165
ConfigureHistoryService(scoped_refptr<base::SequencedTaskRunner> backend_runner)166 void ConfigureHistoryService(
167 scoped_refptr<base::SequencedTaskRunner> backend_runner) {
168 HistoryServiceFactory::GetInstance()->SetTestingFactory(
169 profile(), base::BindRepeating(&BuildTestHistoryService,
170 std::move(backend_runner)));
171 }
172
173 // Properly shuts down the HistoryService associated with |profile()| and then
174 // creates new one that will run using the |backend_runner|.
RestartHistoryService(scoped_refptr<base::SequencedTaskRunner> backend_runner)175 void RestartHistoryService(
176 scoped_refptr<base::SequencedTaskRunner> backend_runner) {
177 // Triggers destruction of the existing HistoryService and waits for all
178 // cleanup work to be done.
179 BlockUntilHistoryBackendDestroyed(profile());
180
181 // Force the creation of a new HistoryService that runs its backend on
182 // |backend_runner|.
183 ConfigureHistoryService(std::move(backend_runner));
184 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
185 profile(), ServiceAccessType::IMPLICIT_ACCESS);
186 history->AddObserver(service());
187 }
188
RecordVisitAndPlaybackAndAdvanceClock(const url::Origin & origin)189 void RecordVisitAndPlaybackAndAdvanceClock(const url::Origin& origin) {
190 RecordVisit(origin);
191 AdvanceClock();
192 RecordPlayback(origin);
193 }
194
TearDown()195 void TearDown() override {
196 service_->Shutdown();
197
198 // Tests that run a history service that uses the mock task runner for
199 // backend processing will post tasks there during TearDown. Run them now to
200 // avoid leaks.
201 mock_time_task_runner_->RunUntilIdle();
202 service_.reset();
203
204 ChromeRenderViewHostTestHarness::TearDown();
205 }
206
AdvanceClock()207 void AdvanceClock() {
208 test_clock_.SetNow(Now() + base::TimeDelta::FromHours(1));
209 }
210
RecordVisit(const url::Origin & origin)211 void RecordVisit(const url::Origin& origin) { service_->RecordVisit(origin); }
212
RecordPlayback(const url::Origin & origin)213 void RecordPlayback(const url::Origin& origin) {
214 RecordPlaybackForService(service_.get(), origin);
215 }
216
RecordPlaybackForService(MediaEngagementService * service,const url::Origin & origin)217 void RecordPlaybackForService(MediaEngagementService* service,
218 const url::Origin& origin) {
219 MediaEngagementScore score = service->CreateEngagementScore(origin);
220 score.IncrementMediaPlaybacks();
221 score.set_last_media_playback_time(service->clock()->Now());
222 score.Commit();
223 }
224
ExpectScores(MediaEngagementService * service,const url::Origin & origin,double expected_score,int expected_visits,int expected_media_playbacks,base::Time expected_last_media_playback_time)225 void ExpectScores(MediaEngagementService* service,
226 const url::Origin& origin,
227 double expected_score,
228 int expected_visits,
229 int expected_media_playbacks,
230 base::Time expected_last_media_playback_time) {
231 EXPECT_EQ(service->GetEngagementScore(origin), expected_score);
232 EXPECT_EQ(service->GetScoreMapForTesting()[origin], expected_score);
233
234 MediaEngagementScore score = service->CreateEngagementScore(origin);
235 EXPECT_EQ(expected_visits, score.visits());
236 EXPECT_EQ(expected_media_playbacks, score.media_playbacks());
237 EXPECT_EQ(expected_last_media_playback_time,
238 score.last_media_playback_time());
239 }
240
ExpectScores(const url::Origin & origin,double expected_score,int expected_visits,int expected_media_playbacks,base::Time expected_last_media_playback_time)241 void ExpectScores(const url::Origin& origin,
242 double expected_score,
243 int expected_visits,
244 int expected_media_playbacks,
245 base::Time expected_last_media_playback_time) {
246 ExpectScores(service_.get(), origin, expected_score, expected_visits,
247 expected_media_playbacks, expected_last_media_playback_time);
248 }
249
SetScores(const url::Origin & origin,int visits,int media_playbacks)250 void SetScores(const url::Origin& origin, int visits, int media_playbacks) {
251 MediaEngagementScore score = service_->CreateEngagementScore(origin);
252 score.SetVisits(visits);
253 score.SetMediaPlaybacks(media_playbacks);
254 score.Commit();
255 }
256
SetLastMediaPlaybackTime(const url::Origin & origin,base::Time last_media_playback_time)257 void SetLastMediaPlaybackTime(const url::Origin& origin,
258 base::Time last_media_playback_time) {
259 MediaEngagementScore score = service_->CreateEngagementScore(origin);
260 score.last_media_playback_time_ = last_media_playback_time;
261 score.Commit();
262 }
263
GetActualScore(const url::Origin & origin)264 double GetActualScore(const url::Origin& origin) {
265 return service_->CreateEngagementScore(origin).actual_score();
266 }
267
GetScoreMapForTesting() const268 std::map<url::Origin, double> GetScoreMapForTesting() const {
269 return service_->GetScoreMapForTesting();
270 }
271
ClearDataBetweenTime(base::Time begin,base::Time end)272 void ClearDataBetweenTime(base::Time begin, base::Time end) {
273 service_->ClearDataBetweenTime(begin, end);
274 }
275
Now()276 base::Time Now() { return test_clock_.Now(); }
277
TimeNotSet() const278 base::Time TimeNotSet() const { return base::Time(); }
279
SetNow(base::Time now)280 void SetNow(base::Time now) { test_clock_.SetNow(now); }
281
GetAllScoreDetails() const282 std::vector<media::mojom::MediaEngagementScoreDetailsPtr> GetAllScoreDetails()
283 const {
284 return service_->GetAllScoreDetails();
285 }
286
HasHighEngagement(const url::Origin & origin) const287 bool HasHighEngagement(const url::Origin& origin) const {
288 return service_->HasHighEngagement(origin);
289 }
290
SetSchemaVersion(int version)291 void SetSchemaVersion(int version) { service_->SetSchemaVersion(version); }
292
GetAllStoredScores(const MediaEngagementService * service) const293 std::vector<MediaEngagementScore> GetAllStoredScores(
294 const MediaEngagementService* service) const {
295 return service->GetAllStoredScores();
296 }
297
GetAllStoredScores() const298 std::vector<MediaEngagementScore> GetAllStoredScores() const {
299 return GetAllStoredScores(service_.get());
300 }
301
302 protected:
303 scoped_refptr<base::TestMockTimeTaskRunner> mock_time_task_runner_;
304
305 private:
306 base::ScopedTempDir temp_dir_;
307
308 base::SimpleTestClock test_clock_;
309
310 std::unique_ptr<MediaEngagementService> service_;
311
312 base::test::ScopedFeatureList scoped_feature_list_;
313 };
314
TEST_P(MediaEngagementServiceTest,MojoSerialization)315 TEST_P(MediaEngagementServiceTest, MojoSerialization) {
316 EXPECT_EQ(0u, GetAllScoreDetails().size());
317
318 RecordVisitAndPlaybackAndAdvanceClock(
319 url::Origin::Create(GURL("http://www.example.com")));
320 RecordVisitAndPlaybackAndAdvanceClock(
321 url::Origin::Create(GURL("https://www.example.com")));
322
323 EXPECT_EQ(GetParam() ? 1u : 2u, GetAllScoreDetails().size());
324 }
325
TEST_P(MediaEngagementServiceTest,RestrictedToHTTPAndHTTPS)326 TEST_P(MediaEngagementServiceTest, RestrictedToHTTPAndHTTPS) {
327 std::vector<url::Origin> origins = {
328 url::Origin::Create(GURL("ftp://www.google.com/")),
329 url::Origin::Create(GURL("file://blah")),
330 url::Origin::Create(GURL("chrome://")),
331 url::Origin::Create(GURL("about://config")),
332 url::Origin::Create(GURL("http://example.com")),
333 url::Origin::Create(GURL("https://example.com")),
334 };
335
336 for (const url::Origin& origin : origins) {
337 RecordVisitAndPlaybackAndAdvanceClock(origin);
338
339 if (origin.scheme() == url::kHttpsScheme ||
340 (origin.scheme() == url::kHttpScheme && !GetParam())) {
341 ExpectScores(origin, 0.05, 1, 1, Now());
342 } else {
343 ExpectScores(origin, 0.0, 0, 0, TimeNotSet());
344 }
345 }
346 }
347
TEST_P(MediaEngagementServiceTest,HandleRecordVisitAndPlaybackAndAdvanceClockion)348 TEST_P(MediaEngagementServiceTest,
349 HandleRecordVisitAndPlaybackAndAdvanceClockion) {
350 url::Origin origin1 = url::Origin::Create(GURL("https://www.google.com"));
351 ExpectScores(origin1, 0.0, 0, 0, TimeNotSet());
352 RecordVisitAndPlaybackAndAdvanceClock(origin1);
353 ExpectScores(origin1, 0.05, 1, 1, Now());
354
355 RecordVisit(origin1);
356 ExpectScores(origin1, 0.05, 2, 1, Now());
357
358 RecordPlayback(origin1);
359 ExpectScores(origin1, 0.1, 2, 2, Now());
360 base::Time origin1_time = Now();
361
362 url::Origin origin2 = url::Origin::Create(GURL("https://www.google.co.uk"));
363 RecordVisitAndPlaybackAndAdvanceClock(origin2);
364 ExpectScores(origin2, 0.05, 1, 1, Now());
365 ExpectScores(origin1, 0.1, 2, 2, origin1_time);
366 }
367
TEST_P(MediaEngagementServiceTest,IncognitoEngagementService)368 TEST_P(MediaEngagementServiceTest, IncognitoEngagementService) {
369 url::Origin origin1 = url::Origin::Create(GURL("https://www.google.fr/"));
370 url::Origin origin2 = url::Origin::Create(GURL("https://www.google.com/"));
371 url::Origin origin3 = url::Origin::Create(GURL("https://drive.google.com/"));
372 url::Origin origin4 = url::Origin::Create(GURL("https://maps.google.com/"));
373
374 RecordVisitAndPlaybackAndAdvanceClock(origin1);
375 base::Time origin1_time = Now();
376 RecordVisitAndPlaybackAndAdvanceClock(origin2);
377
378 MediaEngagementService* incognito_service =
379 MediaEngagementService::Get(profile()->GetPrimaryOTRProfile());
380 ExpectScores(incognito_service, origin1, 0.05, 1, 1, origin1_time);
381 ExpectScores(incognito_service, origin2, 0.05, 1, 1, Now());
382 ExpectScores(incognito_service, origin3, 0.0, 0, 0, TimeNotSet());
383
384 incognito_service->RecordVisit(origin3);
385 ExpectScores(incognito_service, origin3, 0.0, 1, 0, TimeNotSet());
386 ExpectScores(origin3, 0.0, 0, 0, TimeNotSet());
387
388 incognito_service->RecordVisit(origin2);
389 ExpectScores(incognito_service, origin2, 0.05, 2, 1, Now());
390 ExpectScores(origin2, 0.05, 1, 1, Now());
391
392 RecordVisitAndPlaybackAndAdvanceClock(origin3);
393 ExpectScores(incognito_service, origin3, 0.0, 1, 0, TimeNotSet());
394 ExpectScores(origin3, 0.05, 1, 1, Now());
395
396 ExpectScores(incognito_service, origin4, 0.0, 0, 0, TimeNotSet());
397 RecordVisitAndPlaybackAndAdvanceClock(origin4);
398 ExpectScores(incognito_service, origin4, 0.05, 1, 1, Now());
399 ExpectScores(origin4, 0.05, 1, 1, Now());
400 }
401
TEST_P(MediaEngagementServiceTest,IncognitoOverrideRegularProfile)402 TEST_P(MediaEngagementServiceTest, IncognitoOverrideRegularProfile) {
403 const url::Origin kOrigin1 = url::Origin::Create(GURL("https://example.org"));
404 const url::Origin kOrigin2 = url::Origin::Create(GURL("https://example.com"));
405
406 SetScores(kOrigin1, MediaEngagementScore::GetScoreMinVisits(), 1);
407 SetScores(kOrigin2, 1, 0);
408
409 ExpectScores(kOrigin1, 0.05, MediaEngagementScore::GetScoreMinVisits(), 1,
410 TimeNotSet());
411 ExpectScores(kOrigin2, 0.0, 1, 0, TimeNotSet());
412
413 MediaEngagementService* incognito_service =
414 MediaEngagementService::Get(profile()->GetPrimaryOTRProfile());
415 ExpectScores(incognito_service, kOrigin1, 0.05,
416 MediaEngagementScore::GetScoreMinVisits(), 1, TimeNotSet());
417 ExpectScores(incognito_service, kOrigin2, 0.0, 1, 0, TimeNotSet());
418
419 // Scores should be the same in incognito and regular profile.
420 {
421 std::vector<std::pair<url::Origin, double>> kExpectedResults = {
422 {kOrigin2, 0.0},
423 {kOrigin1, 0.05},
424 };
425
426 const auto& scores = GetAllStoredScores();
427 const auto& incognito_scores = GetAllStoredScores(incognito_service);
428
429 EXPECT_EQ(kExpectedResults.size(), scores.size());
430 EXPECT_EQ(kExpectedResults.size(), incognito_scores.size());
431
432 for (size_t i = 0; i < scores.size(); ++i) {
433 EXPECT_EQ(kExpectedResults[i].first, scores[i].origin());
434 EXPECT_EQ(kExpectedResults[i].second, scores[i].actual_score());
435
436 EXPECT_EQ(kExpectedResults[i].first, incognito_scores[i].origin());
437 EXPECT_EQ(kExpectedResults[i].second, incognito_scores[i].actual_score());
438 }
439 }
440
441 incognito_service->RecordVisit(kOrigin1);
442 RecordPlaybackForService(incognito_service, kOrigin2);
443
444 // Score shouldn't have changed in regular profile.
445 {
446 std::vector<std::pair<url::Origin, double>> kExpectedResults = {
447 {kOrigin2, 0.0},
448 {kOrigin1, 0.05},
449 };
450
451 const auto& scores = GetAllStoredScores();
452 EXPECT_EQ(kExpectedResults.size(), scores.size());
453
454 for (size_t i = 0; i < scores.size(); ++i) {
455 EXPECT_EQ(kExpectedResults[i].first, scores[i].origin());
456 EXPECT_EQ(kExpectedResults[i].second, scores[i].actual_score());
457 }
458 }
459
460 // Incognito scores should have the same number of entries but have new
461 // values.
462 {
463 std::vector<std::pair<url::Origin, double>> kExpectedResults = {
464 {kOrigin2, 0.05},
465 {kOrigin1, 1.0 / 21.0},
466 };
467
468 const auto& scores = GetAllStoredScores(incognito_service);
469 EXPECT_EQ(kExpectedResults.size(), scores.size());
470
471 for (size_t i = 0; i < scores.size(); ++i) {
472 EXPECT_EQ(kExpectedResults[i].first, scores[i].origin());
473 EXPECT_EQ(kExpectedResults[i].second, scores[i].actual_score());
474 }
475 }
476 }
477
TEST_P(MediaEngagementServiceTest,CleanupOriginsOnHistoryDeletion)478 TEST_P(MediaEngagementServiceTest, CleanupOriginsOnHistoryDeletion) {
479 url::Origin origin1 = url::Origin::Create(GURL("https://www.google.com/"));
480 url::Origin origin2 = url::Origin::Create(GURL("https://drive.google.com/"));
481 url::Origin origin3 = url::Origin::Create(GURL("https://deleted.com/"));
482 url::Origin origin4 = url::Origin::Create(GURL("https://notdeleted.com"));
483
484 GURL url1a = GURL("https://www.google.com/search?q=asdf");
485 GURL url1b = GURL("https://www.google.com/maps/search?q=asdf");
486 GURL url3a = GURL("https://deleted.com/test");
487
488 // origin1 will have a score that is high enough to not return zero
489 // and we will ensure it has the same score. origin2 will have a score
490 // that is zero and will remain zero. origin3 will have a score
491 // and will be cleared. origin4 will have a normal score.
492 SetScores(origin1, MediaEngagementScore::GetScoreMinVisits() + 2, 14);
493 SetScores(origin2, 2, 1);
494 SetScores(origin3, 2, 1);
495 SetScores(origin4, MediaEngagementScore::GetScoreMinVisits(), 10);
496
497 base::Time today = GetReferenceTime();
498 base::Time yesterday = GetReferenceTime() - base::TimeDelta::FromDays(1);
499 base::Time yesterday_afternoon = GetReferenceTime() -
500 base::TimeDelta::FromDays(1) +
501 base::TimeDelta::FromHours(4);
502 base::Time yesterday_week = GetReferenceTime() - base::TimeDelta::FromDays(8);
503 SetNow(today);
504
505 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
506 profile(), ServiceAccessType::IMPLICIT_ACCESS);
507
508 history->AddPage(origin1.GetURL(), yesterday_afternoon,
509 history::SOURCE_BROWSED);
510 history->AddPage(url1a, yesterday_afternoon, history::SOURCE_BROWSED);
511 history->AddPage(url1b, yesterday_week, history::SOURCE_BROWSED);
512 history->AddPage(origin2.GetURL(), yesterday_afternoon,
513 history::SOURCE_BROWSED);
514 history->AddPage(origin3.GetURL(), yesterday_week, history::SOURCE_BROWSED);
515 history->AddPage(url3a, yesterday_afternoon, history::SOURCE_BROWSED);
516
517 // Check that the scores are valid at the beginning.
518 ExpectScores(origin1, 7.0 / 11.0,
519 MediaEngagementScore::GetScoreMinVisits() + 2, 14, TimeNotSet());
520 EXPECT_EQ(14.0 / 22.0, GetActualScore(origin1));
521 ExpectScores(origin2, 0.05, 2, 1, TimeNotSet());
522 EXPECT_EQ(1 / 20.0, GetActualScore(origin2));
523 ExpectScores(origin3, 0.05, 2, 1, TimeNotSet());
524 EXPECT_EQ(1 / 20.0, GetActualScore(origin3));
525 ExpectScores(origin4, 0.5, MediaEngagementScore::GetScoreMinVisits(), 10,
526 TimeNotSet());
527 EXPECT_EQ(0.5, GetActualScore(origin4));
528
529 {
530 MediaEngagementChangeWaiter waiter(profile());
531
532 base::CancelableTaskTracker task_tracker;
533 // Expire origin1, url1a, origin2, and url3a's most recent visit.
534 history->ExpireHistoryBetween(std::set<GURL>(), yesterday, today,
535 /*user_initiated*/ true, base::DoNothing(),
536 &task_tracker);
537 waiter.Wait();
538
539 // origin1 should have a score that is not zero and is the same as the old
540 // score (sometimes it may not match exactly due to rounding). origin2
541 // should have a score that is zero but it's visits and playbacks should
542 // have decreased. origin3 should have had a decrease in the number of
543 // visits. origin4 should have the old score.
544 ExpectScores(origin1, 0.6, MediaEngagementScore::GetScoreMinVisits(), 12,
545 TimeNotSet());
546 EXPECT_EQ(12.0 / 20.0, GetActualScore(origin1));
547 ExpectScores(origin2, 0.0, 1, 0, TimeNotSet());
548 EXPECT_EQ(0, GetActualScore(origin2));
549 ExpectScores(origin3, 0.0, 1, 0, TimeNotSet());
550 ExpectScores(origin4, 0.5, MediaEngagementScore::GetScoreMinVisits(), 10,
551 TimeNotSet());
552 }
553
554 {
555 MediaEngagementChangeWaiter waiter(profile());
556
557 // Expire url1b.
558 std::vector<history::ExpireHistoryArgs> expire_list;
559 history::ExpireHistoryArgs args;
560 args.urls.insert(url1b);
561 args.SetTimeRangeForOneDay(yesterday_week);
562 expire_list.push_back(args);
563
564 base::CancelableTaskTracker task_tracker;
565 history->ExpireHistory(expire_list, base::DoNothing(), &task_tracker);
566 waiter.Wait();
567
568 // origin1's score should have changed but the rest should remain the same.
569 ExpectScores(origin1, 0.55, MediaEngagementScore::GetScoreMinVisits() - 1,
570 11, TimeNotSet());
571 ExpectScores(origin2, 0.0, 1, 0, TimeNotSet());
572 ExpectScores(origin3, 0.0, 1, 0, TimeNotSet());
573 ExpectScores(origin4, 0.5, MediaEngagementScore::GetScoreMinVisits(), 10,
574 TimeNotSet());
575 }
576
577 {
578 MediaEngagementChangeWaiter waiter(profile());
579
580 // Expire origin3.
581 std::vector<history::ExpireHistoryArgs> expire_list;
582 history::ExpireHistoryArgs args;
583 args.urls.insert(origin3.GetURL());
584 args.SetTimeRangeForOneDay(yesterday_week);
585 expire_list.push_back(args);
586
587 base::CancelableTaskTracker task_tracker;
588 history->ExpireHistory(expire_list, base::DoNothing(), &task_tracker);
589 waiter.Wait();
590
591 // origin3's score should be removed but the rest should remain the same.
592 std::map<url::Origin, double> scores = GetScoreMapForTesting();
593 EXPECT_TRUE(scores.find(origin3) == scores.end());
594 ExpectScores(origin1, 0.55, MediaEngagementScore::GetScoreMinVisits() - 1,
595 11, TimeNotSet());
596 ExpectScores(origin2, 0.0, 1, 0, TimeNotSet());
597 ExpectScores(origin3, 0.0, 0, 0, TimeNotSet());
598 ExpectScores(origin4, 0.5, MediaEngagementScore::GetScoreMinVisits(), 10,
599 TimeNotSet());
600 }
601 }
602
603 // The test is flaky: crbug.com/1042417.
604 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID)
605 #define MAYBE_CleanUpDatabaseWhenHistoryIsExpired \
606 DISABLED_CleanUpDatabaseWhenHistoryIsExpired
607 #else
608 #define MAYBE_CleanUpDatabaseWhenHistoryIsExpired \
609 CleanUpDatabaseWhenHistoryIsExpired
610 #endif
TEST_P(MediaEngagementServiceTest,MAYBE_CleanUpDatabaseWhenHistoryIsExpired)611 TEST_P(MediaEngagementServiceTest, MAYBE_CleanUpDatabaseWhenHistoryIsExpired) {
612 // |origin1| will have history that is before the expiry threshold and should
613 // not be deleted. |origin2| will have history either side of the threshold
614 // and should also not be deleted. |origin3| will have history before the
615 // threshold and should be deleted.
616 url::Origin origin1 = url::Origin::Create(GURL("https://www.google.com"));
617 url::Origin origin2 = url::Origin::Create(GURL("https://drive.google.com"));
618 url::Origin origin3 = url::Origin::Create(GURL("https://deleted.com"));
619
620 // Populate test MEI data.
621 SetScores(origin1, 20, 20);
622 SetScores(origin2, 30, 30);
623 SetScores(origin3, 40, 40);
624
625 base::Time today = base::Time::Now();
626 base::Time before_threshold = today - kHistoryExpirationThreshold;
627 SetNow(today);
628
629 // Populate test history records.
630 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
631 profile(), ServiceAccessType::IMPLICIT_ACCESS);
632
633 history->AddPage(origin1.GetURL(), today, history::SOURCE_BROWSED);
634 history->AddPage(origin2.GetURL(), today, history::SOURCE_BROWSED);
635 history->AddPage(origin2.GetURL(), before_threshold, history::SOURCE_BROWSED);
636 history->AddPage(origin3.GetURL(), before_threshold, history::SOURCE_BROWSED);
637
638 // Expire history older than |threshold|.
639 MediaEngagementChangeWaiter waiter(profile());
640 RestartHistoryService(mock_time_task_runner_);
641
642 // From this point profile() is using a new HistoryService that runs on
643 // mock time.
644
645 // Now, fast forward time to ensure that the expiration job is completed. This
646 // will start by triggering the backend initialization. 30 seconds is the
647 // value of kExpirationDelaySec.
648 mock_time_task_runner_->FastForwardBy(base::TimeDelta::FromSeconds(30));
649 waiter.Wait();
650
651 // Check the scores for the test origins.
652 ExpectScores(origin1, 1.0, 20, 20, TimeNotSet());
653 ExpectScores(origin2, 1.0, 30, 30, TimeNotSet());
654 ExpectScores(origin3, 0, 0, 0, TimeNotSet());
655 }
656
TEST_P(MediaEngagementServiceTest,CleanUpDatabaseWhenHistoryIsDeleted)657 TEST_P(MediaEngagementServiceTest, CleanUpDatabaseWhenHistoryIsDeleted) {
658 url::Origin origin1 = url::Origin::Create(GURL("https://www.google.com/"));
659 url::Origin origin2 = url::Origin::Create(GURL("https://drive.google.com/"));
660 url::Origin origin3 = url::Origin::Create(GURL("https://deleted.com/"));
661 url::Origin origin4 = url::Origin::Create(GURL("https://notdeleted.com"));
662
663 GURL url1a = GURL("https://www.google.com/search?q=asdf");
664 GURL url1b = GURL("https://www.google.com/maps/search?q=asdf");
665 GURL url3a = GURL("https://deleted.com/test");
666
667 // origin1 will have a score that is high enough to not return zero
668 // and we will ensure it has the same score. origin2 will have a score
669 // that is zero and will remain zero. origin3 will have a score
670 // and will be cleared. origin4 will have a normal score.
671 SetScores(origin1, MediaEngagementScore::GetScoreMinVisits() + 2, 14);
672 SetScores(origin2, 2, 1);
673 SetScores(origin3, 2, 1);
674 SetScores(origin4, MediaEngagementScore::GetScoreMinVisits(), 10);
675
676 base::Time today = GetReferenceTime();
677 base::Time yesterday_afternoon = GetReferenceTime() -
678 base::TimeDelta::FromDays(1) +
679 base::TimeDelta::FromHours(4);
680 base::Time yesterday_week = GetReferenceTime() - base::TimeDelta::FromDays(8);
681 SetNow(today);
682
683 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
684 profile(), ServiceAccessType::IMPLICIT_ACCESS);
685
686 history->AddPage(origin1.GetURL(), yesterday_afternoon,
687 history::SOURCE_BROWSED);
688 history->AddPage(url1a, yesterday_afternoon, history::SOURCE_BROWSED);
689 history->AddPage(url1b, yesterday_week, history::SOURCE_BROWSED);
690 history->AddPage(origin2.GetURL(), yesterday_afternoon,
691 history::SOURCE_BROWSED);
692 history->AddPage(origin3.GetURL(), yesterday_week, history::SOURCE_BROWSED);
693 history->AddPage(url3a, yesterday_afternoon, history::SOURCE_BROWSED);
694
695 // Check that the scores are valid at the beginning.
696 ExpectScores(origin1, 7.0 / 11.0,
697 MediaEngagementScore::GetScoreMinVisits() + 2, 14, TimeNotSet());
698 EXPECT_EQ(14.0 / 22.0, GetActualScore(origin1));
699 ExpectScores(origin2, 0.05, 2, 1, TimeNotSet());
700 EXPECT_EQ(1 / 20.0, GetActualScore(origin2));
701 ExpectScores(origin3, 0.05, 2, 1, TimeNotSet());
702 EXPECT_EQ(1 / 20.0, GetActualScore(origin3));
703 ExpectScores(origin4, 0.5, MediaEngagementScore::GetScoreMinVisits(), 10,
704 TimeNotSet());
705 EXPECT_EQ(0.5, GetActualScore(origin4));
706
707 {
708 base::RunLoop run_loop;
709 base::CancelableTaskTracker task_tracker;
710 // Clear all history.
711 history->ExpireHistoryBetween(std::set<GURL>(), base::Time(), base::Time(),
712 /*user_initiated*/ true,
713 run_loop.QuitClosure(), &task_tracker);
714 run_loop.Run();
715
716 // origin1 should have a score that is not zero and is the same as the old
717 // score (sometimes it may not match exactly due to rounding). origin2
718 // should have a score that is zero but it's visits and playbacks should
719 // have decreased. origin3 should have had a decrease in the number of
720 // visits. origin4 should have the old score.
721 ExpectScores(origin1, 0.0, 0, 0, TimeNotSet());
722 EXPECT_EQ(0, GetActualScore(origin1));
723 ExpectScores(origin2, 0.0, 0, 0, TimeNotSet());
724 EXPECT_EQ(0, GetActualScore(origin2));
725 ExpectScores(origin3, 0.0, 0, 0, TimeNotSet());
726 ExpectScores(origin4, 0.0, 0, 0, TimeNotSet());
727 }
728 }
729
TEST_P(MediaEngagementServiceTest,HistoryExpirationIsNoOp)730 TEST_P(MediaEngagementServiceTest, HistoryExpirationIsNoOp) {
731 url::Origin origin1 = url::Origin::Create(GURL("https://www.google.com/"));
732 url::Origin origin2 = url::Origin::Create(GURL("https://drive.google.com/"));
733 url::Origin origin3 = url::Origin::Create(GURL("https://deleted.com/"));
734 url::Origin origin4 = url::Origin::Create(GURL("https://notdeleted.com"));
735
736 GURL url1a = GURL("https://www.google.com/search?q=asdf");
737 GURL url1b = GURL("https://www.google.com/maps/search?q=asdf");
738 GURL url3a = GURL("https://deleted.com/test");
739
740 SetScores(origin1, MediaEngagementScore::GetScoreMinVisits() + 2, 14);
741 SetScores(origin2, 2, 1);
742 SetScores(origin3, 2, 1);
743 SetScores(origin4, MediaEngagementScore::GetScoreMinVisits(), 10);
744
745 ExpectScores(origin1, 7.0 / 11.0,
746 MediaEngagementScore::GetScoreMinVisits() + 2, 14, TimeNotSet());
747 EXPECT_EQ(14.0 / 22.0, GetActualScore(origin1));
748 ExpectScores(origin2, 0.05, 2, 1, TimeNotSet());
749 EXPECT_EQ(1 / 20.0, GetActualScore(origin2));
750 ExpectScores(origin3, 0.05, 2, 1, TimeNotSet());
751 EXPECT_EQ(1 / 20.0, GetActualScore(origin3));
752 ExpectScores(origin4, 0.5, MediaEngagementScore::GetScoreMinVisits(), 10,
753 TimeNotSet());
754 EXPECT_EQ(0.5, GetActualScore(origin4));
755
756 {
757 history::HistoryService* history = HistoryServiceFactory::GetForProfile(
758 profile(), ServiceAccessType::IMPLICIT_ACCESS);
759
760 service()->OnURLsDeleted(
761 history, history::DeletionInfo(history::DeletionTimeRange::Invalid(),
762 true, history::URLRows(),
763 std::set<GURL>(), base::nullopt));
764
765 // Same as above, nothing should have changed.
766 ExpectScores(origin1, 7.0 / 11.0,
767 MediaEngagementScore::GetScoreMinVisits() + 2, 14,
768 TimeNotSet());
769 EXPECT_EQ(14.0 / 22.0, GetActualScore(origin1));
770 ExpectScores(origin2, 0.05, 2, 1, TimeNotSet());
771 EXPECT_EQ(1 / 20.0, GetActualScore(origin2));
772 ExpectScores(origin3, 0.05, 2, 1, TimeNotSet());
773 EXPECT_EQ(1 / 20.0, GetActualScore(origin3));
774 ExpectScores(origin4, 0.5, MediaEngagementScore::GetScoreMinVisits(), 10,
775 TimeNotSet());
776 EXPECT_EQ(0.5, GetActualScore(origin4));
777 }
778 }
779
TEST_P(MediaEngagementServiceTest,CleanupDataOnSiteDataCleanup_OutsideBoundary)780 TEST_P(MediaEngagementServiceTest,
781 CleanupDataOnSiteDataCleanup_OutsideBoundary) {
782 url::Origin origin = url::Origin::Create(GURL("https://www.google.com"));
783
784 base::Time today = GetReferenceTime();
785 SetNow(today);
786
787 SetScores(origin, 1, 1);
788 SetLastMediaPlaybackTime(origin, today);
789
790 ClearDataBetweenTime(today - base::TimeDelta::FromDays(2),
791 today - base::TimeDelta::FromDays(1));
792 ExpectScores(origin, 0.05, 1, 1, today);
793 }
794
TEST_P(MediaEngagementServiceTest,CleanupDataOnSiteDataCleanup_WithinBoundary)795 TEST_P(MediaEngagementServiceTest,
796 CleanupDataOnSiteDataCleanup_WithinBoundary) {
797 url::Origin origin1 = url::Origin::Create(GURL("https://www.google.com"));
798 url::Origin origin2 = url::Origin::Create(GURL("https://www.google.co.uk"));
799
800 base::Time today = GetReferenceTime();
801 base::Time yesterday = today - base::TimeDelta::FromDays(1);
802 base::Time two_days_ago = today - base::TimeDelta::FromDays(2);
803 SetNow(today);
804
805 SetScores(origin1, 1, 1);
806 SetScores(origin2, 1, 1);
807 SetLastMediaPlaybackTime(origin1, yesterday);
808 SetLastMediaPlaybackTime(origin2, two_days_ago);
809
810 ClearDataBetweenTime(two_days_ago, yesterday);
811 ExpectScores(origin1, 0, 0, 0, TimeNotSet());
812 ExpectScores(origin2, 0, 0, 0, TimeNotSet());
813 }
814
TEST_P(MediaEngagementServiceTest,CleanupDataOnSiteDataCleanup_NoTimeSet)815 TEST_P(MediaEngagementServiceTest, CleanupDataOnSiteDataCleanup_NoTimeSet) {
816 url::Origin origin = url::Origin::Create(GURL("https://www.google.com"));
817
818 base::Time today = GetReferenceTime();
819
820 SetNow(GetReferenceTime());
821 SetScores(origin, 1, 0);
822
823 ClearDataBetweenTime(today - base::TimeDelta::FromDays(2),
824 today - base::TimeDelta::FromDays(1));
825 ExpectScores(origin, 0.0, 1, 0, TimeNotSet());
826 }
827
TEST_P(MediaEngagementServiceTest,CleanupDataOnSiteDataCleanup_All)828 TEST_P(MediaEngagementServiceTest, CleanupDataOnSiteDataCleanup_All) {
829 url::Origin origin1 = url::Origin::Create(GURL("https://www.google.com"));
830 url::Origin origin2 = url::Origin::Create(GURL("https://www.google.co.uk"));
831
832 base::Time today = GetReferenceTime();
833 base::Time yesterday = today - base::TimeDelta::FromDays(1);
834 base::Time two_days_ago = today - base::TimeDelta::FromDays(2);
835 SetNow(today);
836
837 SetScores(origin1, 1, 1);
838 SetScores(origin2, 1, 1);
839 SetLastMediaPlaybackTime(origin1, yesterday);
840 SetLastMediaPlaybackTime(origin2, two_days_ago);
841
842 ClearDataBetweenTime(base::Time(), base::Time::Max());
843 ExpectScores(origin1, 0, 0, 0, TimeNotSet());
844 ExpectScores(origin2, 0, 0, 0, TimeNotSet());
845 }
846
TEST_P(MediaEngagementServiceTest,HasHighEngagement)847 TEST_P(MediaEngagementServiceTest, HasHighEngagement) {
848 url::Origin origin1 = url::Origin::Create(GURL("https://www.google.com"));
849 url::Origin origin2 = url::Origin::Create(GURL("https://www.google.co.uk"));
850 url::Origin origin3 = url::Origin::Create(GURL("https://www.example.com"));
851
852 SetScores(origin1, 20, 15);
853 SetScores(origin2, 20, 4);
854
855 EXPECT_TRUE(HasHighEngagement(origin1));
856 EXPECT_FALSE(HasHighEngagement(origin2));
857 EXPECT_FALSE(HasHighEngagement(origin3));
858 }
859
TEST_P(MediaEngagementServiceTest,SchemaVersion_Changed)860 TEST_P(MediaEngagementServiceTest, SchemaVersion_Changed) {
861 url::Origin origin = url::Origin::Create(GURL("https://www.google.com"));
862 SetScores(origin, 1, 2);
863
864 SetSchemaVersion(0);
865 std::unique_ptr<MediaEngagementService> new_service =
866 base::WrapUnique<MediaEngagementService>(
867 StartNewMediaEngagementService());
868
869 ExpectScores(new_service.get(), origin, 0.0, 0, 0, TimeNotSet());
870 new_service->Shutdown();
871 }
872
TEST_P(MediaEngagementServiceTest,SchemaVersion_Same)873 TEST_P(MediaEngagementServiceTest, SchemaVersion_Same) {
874 url::Origin origin = url::Origin::Create(GURL("https://www.google.com"));
875 SetScores(origin, 1, 2);
876
877 std::unique_ptr<MediaEngagementService> new_service =
878 base::WrapUnique<MediaEngagementService>(
879 StartNewMediaEngagementService());
880
881 ExpectScores(new_service.get(), origin, 0.1, 1, 2, TimeNotSet());
882 new_service->Shutdown();
883 }
884
885 INSTANTIATE_TEST_SUITE_P(All, MediaEngagementServiceTest, ::testing::Bool());
886
887 class MediaEngagementServiceEnabledTest
888 : public ChromeRenderViewHostTestHarness {};
889
TEST_F(MediaEngagementServiceEnabledTest,IsEnabled)890 TEST_F(MediaEngagementServiceEnabledTest, IsEnabled) {
891 #if defined(OS_ANDROID)
892 // Make sure these flags are disabled on Android
893 EXPECT_FALSE(base::FeatureList::IsEnabled(
894 media::kMediaEngagementBypassAutoplayPolicies));
895 EXPECT_FALSE(
896 base::FeatureList::IsEnabled(media::kPreloadMediaEngagementData));
897 #else
898 EXPECT_TRUE(base::FeatureList::IsEnabled(
899 media::kMediaEngagementBypassAutoplayPolicies));
900 EXPECT_TRUE(base::FeatureList::IsEnabled(media::kPreloadMediaEngagementData));
901 #endif
902 }
903