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