1 // Copyright 2018 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 "third_party/blink/public/common/media/watch_time_component.h"
6 
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "testing/gmock/include/gmock/gmock.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/blink/public/common/media/display_type.h"
12 
13 namespace blink {
14 
15 class WatchTimeInterceptor : public media::mojom::WatchTimeRecorder {
16  public:
17   WatchTimeInterceptor() = default;
18   ~WatchTimeInterceptor() override = default;
19 
20   // media::mojom::WatchTimeRecorder implementation:
21   MOCK_METHOD2(RecordWatchTime, void(media::WatchTimeKey, base::TimeDelta));
22   MOCK_METHOD1(FinalizeWatchTime,
23                void(const std::vector<media::WatchTimeKey>&));
24   MOCK_METHOD1(OnError, void(media::PipelineStatus));
25   MOCK_METHOD1(SetAutoplayInitiated, void(bool));
26   MOCK_METHOD1(OnDurationChanged, void(base::TimeDelta));
27   MOCK_METHOD2(UpdateVideoDecodeStats, void(uint32_t, uint32_t));
28   MOCK_METHOD1(UpdateUnderflowCount, void(int32_t));
29   MOCK_METHOD2(UpdateUnderflowDuration, void(int32_t, base::TimeDelta));
30   MOCK_METHOD1(UpdateSecondaryProperties,
31                void(media::mojom::SecondaryPlaybackPropertiesPtr));
32   MOCK_METHOD1(OnCurrentTimestampChanged, void(base::TimeDelta));
33 };
34 
35 class WatchTimeComponentTest : public testing::Test {
36  public:
37   WatchTimeComponentTest() = default;
38   ~WatchTimeComponentTest() override = default;
39 
40  protected:
41   template <typename T>
CreateComponent(T initial_value,std::vector<media::WatchTimeKey> keys_to_finalize,typename WatchTimeComponent<T>::ValueToKeyCB value_to_key_cb)42   std::unique_ptr<WatchTimeComponent<T>> CreateComponent(
43       T initial_value,
44       std::vector<media::WatchTimeKey> keys_to_finalize,
45       typename WatchTimeComponent<T>::ValueToKeyCB value_to_key_cb) {
46     return std::make_unique<WatchTimeComponent<T>>(
47         initial_value, std::move(keys_to_finalize), std::move(value_to_key_cb),
48         base::BindRepeating(&WatchTimeComponentTest::GetMediaTime,
49                             base::Unretained(this)),
50         &recorder_);
51   }
52 
53   MOCK_METHOD0(GetMediaTime, base::TimeDelta(void));
54 
55   // Usage of StrictMock is intentional here. This ensures all mock method calls
56   // are accounted for in tests.
57   testing::StrictMock<WatchTimeInterceptor> recorder_;
58 
59  private:
60   DISALLOW_COPY_AND_ASSIGN(WatchTimeComponentTest);
61 };
62 
63 // Components should be key agnostic so just choose an arbitrary key for running
64 // most of the tests.
65 constexpr media::WatchTimeKey kTestKey = media::WatchTimeKey::kAudioAll;
66 
67 // This is a test of the standard flow for most components. Most components will
68 // be created, be enabled, start reporting, record watch time, be disabled,
69 // report a finalize, and then record watch time again.
TEST_F(WatchTimeComponentTest,BasicFlow)70 TEST_F(WatchTimeComponentTest, BasicFlow) {
71   auto test_component = CreateComponent<bool>(
72       false, {kTestKey}, WatchTimeComponent<bool>::ValueToKeyCB());
73   EXPECT_FALSE(test_component->current_value_for_testing());
74   EXPECT_FALSE(test_component->NeedsFinalize());
75   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
76 
77   // Simulate flag enabled after construction, but before timer is running; this
78   // should set the current value immediately.
79   test_component->SetCurrentValue(true);
80   EXPECT_TRUE(test_component->current_value_for_testing());
81   EXPECT_FALSE(test_component->NeedsFinalize());
82   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
83 
84   // Notify the start of reporting to set the starting timestamp.
85   const base::TimeDelta kStartTime = base::TimeDelta::FromSeconds(1);
86   test_component->OnReportingStarted(kStartTime);
87   EXPECT_TRUE(test_component->current_value_for_testing());
88   EXPECT_FALSE(test_component->NeedsFinalize());
89   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
90 
91   // Simulate a single recording tick.
92   const base::TimeDelta kWatchTime = base::TimeDelta::FromSeconds(2);
93   EXPECT_CALL(recorder_, RecordWatchTime(kTestKey, kWatchTime - kStartTime));
94   test_component->RecordWatchTime(kWatchTime);
95   EXPECT_TRUE(test_component->current_value_for_testing());
96   EXPECT_FALSE(test_component->NeedsFinalize());
97   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
98 
99   // Simulate the flag being flipped to false while the timer is running; which
100   // should trigger a finalize, but not yet set the current value.
101   const base::TimeDelta kFinalWatchTime = base::TimeDelta::FromSeconds(3);
102   EXPECT_CALL(*this, GetMediaTime()).WillOnce(testing::Return(kFinalWatchTime));
103   test_component->SetPendingValue(false);
104   EXPECT_TRUE(test_component->current_value_for_testing());
105   EXPECT_TRUE(test_component->NeedsFinalize());
106   EXPECT_EQ(test_component->end_timestamp(), kFinalWatchTime);
107 
108   // If record is called again it should use the finalize timestamp instead of
109   // whatever timestamp we provide.
110   EXPECT_CALL(recorder_,
111               RecordWatchTime(kTestKey, kFinalWatchTime - kStartTime));
112   test_component->RecordWatchTime(base::TimeDelta::FromSeconds(1234));
113   EXPECT_TRUE(test_component->current_value_for_testing());
114   EXPECT_TRUE(test_component->NeedsFinalize());
115   EXPECT_EQ(test_component->end_timestamp(), kFinalWatchTime);
116 
117   // Calling it twice or more should not change anything; nor even generate a
118   // report since that time has already been recorded.
119   for (int i = 0; i < 2; ++i) {
120     test_component->RecordWatchTime(base::TimeDelta::FromSeconds(1234 + i));
121     EXPECT_TRUE(test_component->current_value_for_testing());
122     EXPECT_TRUE(test_component->NeedsFinalize());
123     EXPECT_EQ(test_component->end_timestamp(), kFinalWatchTime);
124   }
125 
126   // Trigger finalize which should transition the pending value to the current
127   // value as well as clear the finalize.
128   std::vector<media::WatchTimeKey> finalize_keys;
129   test_component->Finalize(&finalize_keys);
130   EXPECT_FALSE(test_component->current_value_for_testing());
131   EXPECT_FALSE(test_component->NeedsFinalize());
132   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
133   ASSERT_EQ(finalize_keys.size(), 1u);
134   EXPECT_EQ(finalize_keys[0], kTestKey);
135 
136   // The start timestamps should be equal to the previous end timestamp now, so
137   // if we call RecordWatchTime again, the value should be relative.
138   const base::TimeDelta kNewWatchTime = base::TimeDelta::FromSeconds(4);
139   EXPECT_CALL(recorder_,
140               RecordWatchTime(kTestKey, kNewWatchTime - kFinalWatchTime));
141   test_component->RecordWatchTime(kNewWatchTime);
142   EXPECT_FALSE(test_component->current_value_for_testing());
143   EXPECT_FALSE(test_component->NeedsFinalize());
144   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
145 }
146 
TEST_F(WatchTimeComponentTest,SetCurrentValue)147 TEST_F(WatchTimeComponentTest, SetCurrentValue) {
148   auto test_component = CreateComponent<bool>(
149       true, {kTestKey}, WatchTimeComponent<bool>::ValueToKeyCB());
150   EXPECT_TRUE(test_component->current_value_for_testing());
151   EXPECT_FALSE(test_component->NeedsFinalize());
152   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
153 
154   // An update when the timer isn't running should take effect immediately.
155   test_component->SetCurrentValue(false);
156   EXPECT_FALSE(test_component->current_value_for_testing());
157   EXPECT_FALSE(test_component->NeedsFinalize());
158   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
159 
160   test_component->SetCurrentValue(true);
161   EXPECT_TRUE(test_component->current_value_for_testing());
162   EXPECT_FALSE(test_component->NeedsFinalize());
163   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
164 }
165 
TEST_F(WatchTimeComponentTest,RecordDuringFinalizeRespectsCurrentTime)166 TEST_F(WatchTimeComponentTest, RecordDuringFinalizeRespectsCurrentTime) {
167   auto test_component = CreateComponent<bool>(
168       true, {kTestKey}, WatchTimeComponent<bool>::ValueToKeyCB());
169   EXPECT_TRUE(test_component->current_value_for_testing());
170   EXPECT_FALSE(test_component->NeedsFinalize());
171   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
172 
173   // Simulate the flag being flipped to false while the timer is running; which
174   // should trigger a finalize, but not yet set the current value.
175   const base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(3);
176   EXPECT_CALL(*this, GetMediaTime()).WillOnce(testing::Return(kWatchTime1));
177   test_component->SetPendingValue(false);
178   EXPECT_TRUE(test_component->current_value_for_testing());
179   EXPECT_TRUE(test_component->NeedsFinalize());
180   EXPECT_EQ(test_component->end_timestamp(), kWatchTime1);
181 
182   // Now issue a RecordWatchTime() call with a media time before the finalize
183   // time. This can happen when the TimeDelta provided to RecordWatchTime has
184   // been clamped for some reason (e.g., a superseding finalize).
185   const base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(2);
186   EXPECT_CALL(recorder_, RecordWatchTime(kTestKey, kWatchTime2));
187   test_component->RecordWatchTime(kWatchTime2);
188 }
189 
TEST_F(WatchTimeComponentTest,SetPendingValue)190 TEST_F(WatchTimeComponentTest, SetPendingValue) {
191   auto test_component = CreateComponent<bool>(
192       true, {kTestKey}, WatchTimeComponent<bool>::ValueToKeyCB());
193   EXPECT_TRUE(test_component->current_value_for_testing());
194   EXPECT_FALSE(test_component->NeedsFinalize());
195   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
196 
197   // A change when running should trigger a finalize.
198   const base::TimeDelta kFinalWatchTime = base::TimeDelta::FromSeconds(1);
199   EXPECT_CALL(*this, GetMediaTime()).WillOnce(testing::Return(kFinalWatchTime));
200   test_component->SetPendingValue(false);
201   EXPECT_TRUE(test_component->current_value_for_testing());
202   EXPECT_TRUE(test_component->NeedsFinalize());
203   EXPECT_EQ(test_component->end_timestamp(), kFinalWatchTime);
204 
205   // Issuing the same property change again should do nothing since there's a
206   // pending finalize already.
207   test_component->SetPendingValue(false);
208   EXPECT_TRUE(test_component->current_value_for_testing());
209   EXPECT_TRUE(test_component->NeedsFinalize());
210   EXPECT_EQ(test_component->end_timestamp(), kFinalWatchTime);
211 
212   // Changing the value back, should cancel the finalize.
213   test_component->SetPendingValue(true);
214   EXPECT_TRUE(test_component->current_value_for_testing());
215   EXPECT_FALSE(test_component->NeedsFinalize());
216   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
217 }
218 
219 // Tests RecordWatchTime() behavior when a ValueToKeyCB is provided.
TEST_F(WatchTimeComponentTest,WithValueToKeyCB)220 TEST_F(WatchTimeComponentTest, WithValueToKeyCB) {
221   const std::vector<media::WatchTimeKey> finalize_keys = {
222       media::WatchTimeKey::kAudioVideoDisplayInline,
223       media::WatchTimeKey::kAudioVideoDisplayFullscreen,
224       media::WatchTimeKey::kAudioVideoDisplayPictureInPicture};
225   auto test_component = CreateComponent<DisplayType>(
226       DisplayType::kFullscreen, finalize_keys,
227       base::BindRepeating([](DisplayType display_type) {
228         switch (display_type) {
229           case DisplayType::kInline:
230             return media::WatchTimeKey::kAudioVideoDisplayInline;
231           case DisplayType::kFullscreen:
232             return media::WatchTimeKey::kAudioVideoDisplayFullscreen;
233           case DisplayType::kPictureInPicture:
234             return media::WatchTimeKey::kAudioVideoDisplayPictureInPicture;
235         }
236       }));
237   EXPECT_EQ(test_component->current_value_for_testing(),
238             DisplayType::kFullscreen);
239   EXPECT_FALSE(test_component->NeedsFinalize());
240   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
241 
242   // Notify the start of reporting to set the starting timestamp.
243   const base::TimeDelta kStartTime = base::TimeDelta::FromSeconds(1);
244   test_component->OnReportingStarted(kStartTime);
245   EXPECT_EQ(test_component->current_value_for_testing(),
246             DisplayType::kFullscreen);
247   EXPECT_FALSE(test_component->NeedsFinalize());
248   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
249 
250   // Record and verify the key recorded too matches the callback provided.
251   const base::TimeDelta kWatchTime1 = base::TimeDelta::FromSeconds(2);
252   EXPECT_CALL(recorder_,
253               RecordWatchTime(media::WatchTimeKey::kAudioVideoDisplayFullscreen,
254                               kWatchTime1 - kStartTime));
255   test_component->RecordWatchTime(kWatchTime1);
256   EXPECT_EQ(test_component->current_value_for_testing(),
257             DisplayType::kFullscreen);
258   EXPECT_FALSE(test_component->NeedsFinalize());
259   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
260 
261   // Change property while saying the timer isn't running to avoid finalize.
262   const base::TimeDelta kWatchTime2 = base::TimeDelta::FromSeconds(3);
263   test_component->SetCurrentValue(DisplayType::kInline);
264   EXPECT_CALL(recorder_,
265               RecordWatchTime(media::WatchTimeKey::kAudioVideoDisplayInline,
266                               kWatchTime2 - kStartTime));
267   test_component->RecordWatchTime(kWatchTime2);
268   EXPECT_EQ(test_component->current_value_for_testing(), DisplayType::kInline);
269   EXPECT_FALSE(test_component->NeedsFinalize());
270   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
271 
272   // Cycle through all three properties...
273   const base::TimeDelta kWatchTime3 = base::TimeDelta::FromSeconds(4);
274   test_component->SetCurrentValue(DisplayType::kPictureInPicture);
275   EXPECT_CALL(
276       recorder_,
277       RecordWatchTime(media::WatchTimeKey::kAudioVideoDisplayPictureInPicture,
278                       kWatchTime3 - kStartTime));
279   test_component->RecordWatchTime(kWatchTime3);
280   EXPECT_EQ(test_component->current_value_for_testing(),
281             DisplayType::kPictureInPicture);
282   EXPECT_FALSE(test_component->NeedsFinalize());
283   EXPECT_EQ(test_component->end_timestamp(), media::kNoTimestamp);
284 
285   // Verify finalize sends all three keys.
286   std::vector<media::WatchTimeKey> actual_finalize_keys;
287   const base::TimeDelta kFinalWatchTime = base::TimeDelta::FromSeconds(5);
288   EXPECT_CALL(*this, GetMediaTime()).WillOnce(testing::Return(kFinalWatchTime));
289   test_component->SetPendingValue(DisplayType::kFullscreen);
290   test_component->Finalize(&actual_finalize_keys);
291   ASSERT_EQ(actual_finalize_keys.size(), finalize_keys.size());
292   for (size_t i = 0; i < finalize_keys.size(); ++i)
293     EXPECT_EQ(actual_finalize_keys[i], finalize_keys[i]);
294 }
295 
296 // Unlike WatchTimeReporter, WatchTimeComponents have no automatic finalization
297 // so creating and destroying one without calls, should do nothing.
TEST_F(WatchTimeComponentTest,NoAutomaticFinalize)298 TEST_F(WatchTimeComponentTest, NoAutomaticFinalize) {
299   auto test_component = CreateComponent<bool>(
300       false, {kTestKey}, WatchTimeComponent<bool>::ValueToKeyCB());
301 }
302 
303 }  // namespace blink
304