Revert "[M120 Migration]Fix for crash during chrome exit"
[platform/framework/web/chromium-efl.git] / media / filters / hls_live_rendition_unittest.cc
1 // Copyright 2023 The Chromium Authors
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 "media/filters/hls_live_rendition.h"
6 #include "base/test/gmock_callback_support.h"
7 #include "base/test/task_environment.h"
8 #include "media/base/test_helpers.h"
9 #include "media/filters/hls_test_helpers.h"
10
11 namespace media {
12
13 namespace {
14
15 const std::string kInitialFetchLivePlaylist =
16     "#EXTM3U\n"
17     "#EXT-X-VERSION:3\n"
18     "#EXT-X-TARGETDURATION:2\n"
19     "#EXT-X-MEDIA-SEQUENCE:14551245\n"
20     "#EXTINF:2.00000,\n"
21     "playlist_4500Kb_14551245.ts\n"
22     "#EXTINF:2.00000,\n"
23     "playlist_4500Kb_14551246.ts\n"
24     "#EXTINF:2.00000,\n"
25     "playlist_4500Kb_14551247.ts\n"
26     "#EXTINF:2.00000,\n"
27     "playlist_4500Kb_14551248.ts\n"
28     "#EXTINF:2.00000,\n"
29     "playlist_4500Kb_14551249.ts\n"
30     "#EXTINF:2.00000,\n"
31     "playlist_4500Kb_14551250.ts\n"
32     "#EXTINF:2.00000,\n"
33     "playlist_4500Kb_14551251.ts\n"
34     "#EXTINF:2.00000,\n"
35     "playlist_4500Kb_14551252.ts\n"
36     "#EXTINF:2.00000,\n"
37     "playlist_4500Kb_14551253.ts\n"
38     "#EXTINF:2.00000,\n"
39     "playlist_4500Kb_14551254.ts\n";
40
41 const std::string kSecondFetchLivePlaylist =
42     "#EXTM3U\n"
43     "#EXT-X-VERSION:3\n"
44     "#EXT-X-TARGETDURATION:2\n"
45     "#EXT-X-MEDIA-SEQUENCE:14551249\n"
46     "#EXTINF:2.00000,\n"
47     "playlist_4500Kb_14551249.ts\n"
48     "#EXTINF:2.00000,\n"
49     "playlist_4500Kb_14551250.ts\n"
50     "#EXTINF:2.00000,\n"
51     "playlist_4500Kb_14551251.ts\n"
52     "#EXTINF:2.00000,\n"
53     "playlist_4500Kb_14551252.ts\n"
54     "#EXTINF:2.00000,\n"
55     "playlist_4500Kb_14551253.ts\n"
56     "#EXTINF:2.00000,\n"
57     "playlist_4500Kb_14551254.ts\n"
58     "#EXTINF:2.00000,\n"
59     "playlist_4500Kb_14551255.ts\n"
60     "#EXTINF:2.00000,\n"
61     "playlist_4500Kb_14551256.ts\n"
62     "#EXTINF:2.00000,\n"
63     "playlist_4500Kb_14551257.ts\n"
64     "#EXTINF:2.00000,\n"
65     "playlist_4500Kb_14551258.ts\n";
66
67 }  // namespace
68
69 using testing::_;
70 using testing::Return;
71
72 class HlsLiveRenditionUnittest : public testing::Test {
73  protected:
74   std::unique_ptr<MockManifestDemuxerEngineHost> mock_mdeh_;
75   std::unique_ptr<MockHlsRenditionHost> mock_hrh_;
76   base::test::TaskEnvironment task_environment_{
77       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
78
79   std::unique_ptr<HlsLiveRendition> MakeLiveRendition(
80       GURL uri,
81       base::StringPiece content) {
82     constexpr hls::types::DecimalInteger version = 3;
83     auto parsed = hls::MediaPlaylist::Parse(content, uri, version, nullptr);
84     if (!parsed.has_value()) {
85       LOG(ERROR) << MediaSerialize(std::move(parsed).error());
86       return nullptr;
87     }
88     return std::make_unique<HlsLiveRendition>(mock_mdeh_.get(), mock_hrh_.get(),
89                                               "test", std::move(parsed).value(),
90                                               uri);
91   }
92
93   MOCK_METHOD(void, CheckStateComplete, (base::TimeDelta delay), ());
94
95   ManifestDemuxer::DelayCallback BindCheckState(base::TimeDelta time) {
96     EXPECT_CALL(*this, CheckStateComplete(time));
97     return base::BindOnce(&HlsLiveRenditionUnittest::CheckStateComplete,
98                           base::Unretained(this));
99   }
100
101   ManifestDemuxer::DelayCallback BindCheckStateNoExpect() {
102     return base::BindOnce(&HlsLiveRenditionUnittest::CheckStateComplete,
103                           base::Unretained(this));
104   }
105
106   void RespondToUrl(std::string uri,
107                     std::string content,
108                     bool batching = true) {
109     EXPECT_CALL(*mock_hrh_, ReadFromUrl(GURL(uri), batching, _, _))
110         .WillOnce([content = std::move(content), host = mock_hrh_.get()](
111                       GURL url, bool, absl::optional<hls::types::ByteRange>,
112                       HlsDataSourceProvider::ReadCb cb) {
113           auto stream = StringHlsDataSourceStreamFactory::CreateStream(content);
114           std::move(cb).Run(std::move(stream));
115         });
116   }
117
118  public:
119   HlsLiveRenditionUnittest()
120       : mock_mdeh_(std::make_unique<MockManifestDemuxerEngineHost>()),
121         mock_hrh_(std::make_unique<MockHlsRenditionHost>()) {}
122 };
123
124 TEST_F(HlsLiveRenditionUnittest, TestNonRealTimePlaybackRate) {
125   auto rendition =
126       MakeLiveRendition(GURL("http://example.com"), kInitialFetchLivePlaylist);
127   ASSERT_NE(rendition, nullptr);
128   ASSERT_EQ(rendition->GetDuration(), absl::nullopt);
129
130   // Any rate not 0.0 or 1.0 should error.
131   EXPECT_CALL(*mock_mdeh_, OnError(_));
132   rendition->CheckState(base::Seconds(0), 2.0, BindCheckStateNoExpect());
133   task_environment_.RunUntilIdle();
134
135   // From destructor.
136   EXPECT_CALL(*mock_mdeh_, RemoveRole(_));
137   task_environment_.RunUntilIdle();
138 }
139
140 TEST_F(HlsLiveRenditionUnittest, TestCreateRenditionPaused) {
141   auto rendition =
142       MakeLiveRendition(GURL("http://example.com"), kInitialFetchLivePlaylist);
143   ASSERT_NE(rendition, nullptr);
144   ASSERT_EQ(rendition->GetDuration(), absl::nullopt);
145
146   // CheckState causes the rentidion to:
147   // Check buffered ranges first
148   EXPECT_CALL(*mock_mdeh_, GetBufferedRanges(_));
149   // The first segment will be queried
150   RespondToUrl("http://example.com/playlist_4500Kb_14551245.ts", "tscontent");
151   // Then appended.
152   EXPECT_CALL(*mock_mdeh_, AppendAndParseData(_, _, _, _, _, 9))
153       .WillOnce(Return(true));
154   // CheckState should in this case respond with a delay of zero seconds.
155   rendition->CheckState(base::Seconds(0), 0.0,
156                         BindCheckState(base::Seconds(0)));
157   task_environment_.RunUntilIdle();
158
159   // From destructor.
160   EXPECT_CALL(*mock_mdeh_, RemoveRole(_));
161   task_environment_.RunUntilIdle();
162 }
163
164 TEST_F(HlsLiveRenditionUnittest, TestPausedRenditionHasSomeData) {
165   auto rendition =
166       MakeLiveRendition(GURL("http://example.com"), kInitialFetchLivePlaylist);
167   ASSERT_NE(rendition, nullptr);
168   ASSERT_EQ(rendition->GetDuration(), absl::nullopt);
169
170   // CheckState causes the rentidion to:
171   // Check buffered ranges first. In this case, we've loaded a bunch of content
172   // already, and our loaded ranges are [0 - 8)
173   Ranges<base::TimeDelta> loaded_ranges;
174   loaded_ranges.Add(base::Seconds(0), base::Seconds(8));
175   EXPECT_CALL(*mock_mdeh_, GetBufferedRanges(_))
176       .WillOnce(Return(loaded_ranges));
177   // The next unqueried segment will be queried
178   RespondToUrl("http://example.com/playlist_4500Kb_14551245.ts", "tscontent");
179   // Then appended.
180   EXPECT_CALL(*mock_mdeh_, AppendAndParseData(_, _, _, _, _, 9))
181       .WillOnce(Return(true));
182   // CheckState should in this case respond with a delay of zero seconds.
183   rendition->CheckState(base::Seconds(0), 0.0,
184                         BindCheckState(base::Seconds(0)));
185   task_environment_.RunUntilIdle();
186
187   // From destructor.
188   EXPECT_CALL(*mock_mdeh_, RemoveRole(_));
189   task_environment_.RunUntilIdle();
190 }
191
192 TEST_F(HlsLiveRenditionUnittest, TestPausedRenditionHasEnoughBufferedData) {
193   auto rendition =
194       MakeLiveRendition(GURL("http://example.com"), kInitialFetchLivePlaylist);
195   ASSERT_NE(rendition, nullptr);
196   ASSERT_EQ(rendition->GetDuration(), absl::nullopt);
197
198   // CheckState causes the rentidion to:
199   // Check buffered ranges first. In this case, we've loaded a bunch of content
200   // already, and our loaded ranges are [0 - 12)
201   Ranges<base::TimeDelta> loaded_ranges;
202   loaded_ranges.Add(base::Seconds(0), base::Seconds(12));
203   EXPECT_CALL(*mock_mdeh_, GetBufferedRanges(_))
204       .WillOnce(Return(loaded_ranges));
205   // Old data will try to be removed. Since media time is 0, there is nothing
206   // to do. Then there will be an attempt to fetch a new manifest, which won't
207   // have any work to do either, instead just posting the delay_cb back.
208   // CheckState should in this case respond with a delay of 10 / 1.5 seconds.
209   rendition->CheckState(base::Seconds(0), 0.0,
210                         BindCheckState(base::Seconds(10.0 / 1.5)));
211   task_environment_.RunUntilIdle();
212
213   // From destructor.
214   EXPECT_CALL(*mock_mdeh_, RemoveRole(_));
215   task_environment_.RunUntilIdle();
216 }
217
218 TEST_F(HlsLiveRenditionUnittest, TestRenditionHasEnoughDataFetchNewManifest) {
219   auto rendition =
220       MakeLiveRendition(GURL("http://example.com"), kInitialFetchLivePlaylist);
221   ASSERT_NE(rendition, nullptr);
222   ASSERT_EQ(rendition->GetDuration(), absl::nullopt);
223
224   // CheckState causes the rentidion to:
225   // Check buffered ranges first. In this case, we've loaded a bunch of content
226   // already, and our loaded ranges are [0 - 12)
227   Ranges<base::TimeDelta> loaded_ranges;
228   loaded_ranges.Add(base::Seconds(0), base::Seconds(12));
229   EXPECT_CALL(*mock_mdeh_, GetBufferedRanges(_))
230       .WillOnce(Return(loaded_ranges));
231   // Old data will try to be removed. Since media time is 0, there is nothing
232   // to do. Then there will be an attempt to fetch a new manifest, which will
233   // get an update.
234   task_environment_.FastForwardBy(base::Seconds(23));
235   RespondToUrl("http://example.com", kSecondFetchLivePlaylist, false);
236
237   EXPECT_CALL(*mock_hrh_, ParseMediaPlaylistFromStringSource(_, _, _))
238       .WillOnce([](base::StringPiece source, GURL uri,
239                    hls::types::DecimalInteger version) {
240         return hls::MediaPlaylist::Parse(source, uri, version, nullptr);
241       });
242
243   // CheckState should in this case respond with a delay of 10 / 1.5 seconds.
244   rendition->CheckState(base::Seconds(0), 0.0,
245                         BindCheckState(base::Seconds(10.0 / 1.5)));
246   task_environment_.RunUntilIdle();
247
248   // From destructor.
249   EXPECT_CALL(*mock_mdeh_, RemoveRole(_));
250   task_environment_.RunUntilIdle();
251 }
252
253 TEST_F(HlsLiveRenditionUnittest, TestRenditionHasEnoughDataDeleteOldContent) {
254   auto rendition =
255       MakeLiveRendition(GURL("http://example.com"), kInitialFetchLivePlaylist);
256   ASSERT_NE(rendition, nullptr);
257   ASSERT_EQ(rendition->GetDuration(), absl::nullopt);
258
259   // CheckState causes the rentidion to:
260   // Check buffered ranges first. In this case, we've loaded a bunch of content
261   // already, and our loaded ranges are [0 - 32)
262   Ranges<base::TimeDelta> loaded_ranges;
263   loaded_ranges.Add(base::Seconds(0), base::Seconds(32));
264   EXPECT_CALL(*mock_mdeh_, GetBufferedRanges(_))
265       .WillOnce(Return(loaded_ranges));
266   // Old data will try to be removed. Since media time is 15, there are 10
267   // seconds of old data to delete. There will be no new fetch and parse for
268   // manifest updates.
269   EXPECT_CALL(*mock_mdeh_, Remove(_, base::Seconds(0), base::Seconds(10)));
270   task_environment_.FastForwardBy(base::Seconds(15));
271
272   // CheckState should in this case respond with a delay of 10 / 1.5 seconds.
273   rendition->CheckState(base::Seconds(15), 0.0,
274                         BindCheckState(base::Seconds(10.0 / 1.5)));
275   task_environment_.RunUntilIdle();
276
277   // From destructor.
278   EXPECT_CALL(*mock_mdeh_, RemoveRole(_));
279   task_environment_.RunUntilIdle();
280 }
281
282 TEST_F(HlsLiveRenditionUnittest, TestPauseAndUnpause) {
283   auto rendition =
284       MakeLiveRendition(GURL("http://example.com"), kInitialFetchLivePlaylist);
285   ASSERT_NE(rendition, nullptr);
286   ASSERT_EQ(rendition->GetDuration(), absl::nullopt);
287
288   ON_CALL(*mock_mdeh_, OnError(_)).WillByDefault([](PipelineStatus st) {
289     LOG(ERROR) << MediaSerialize(st);
290   });
291
292   // Load a bunch of data, check state, will set `has_ever_played_`
293   Ranges<base::TimeDelta> loaded_ranges;
294   loaded_ranges.Add(base::Seconds(0), base::Seconds(32));
295   EXPECT_CALL(*mock_mdeh_, GetBufferedRanges(_))
296       .Times(2)
297       .WillRepeatedly(Return(loaded_ranges));
298   rendition->CheckState(base::Seconds(4), 1.0,
299                         BindCheckState(base::Seconds(10 / 1.5)));
300   task_environment_.RunUntilIdle();
301
302   // The pause should remove everything.
303   EXPECT_CALL(*mock_mdeh_, Remove(_, base::Seconds(0), base::Seconds(32)));
304   rendition->CheckState(base::Seconds(4), 0.0, BindCheckState(kNoTimestamp));
305   task_environment_.RunUntilIdle();
306
307   // Restarting playback should requery the manifest, respond with another
308   // event for 0 seconds, expecting to download more
309   Ranges<base::TimeDelta> post_seek_ranges;
310   EXPECT_CALL(*mock_mdeh_, GetBufferedRanges(_))
311       .WillRepeatedly(Return(post_seek_ranges));
312   RespondToUrl("http://example.com", kSecondFetchLivePlaylist, false);
313   EXPECT_CALL(*mock_hrh_, ParseMediaPlaylistFromStringSource(_, _, _))
314       .WillOnce([](base::StringPiece source, GURL uri,
315                    hls::types::DecimalInteger version) {
316         return hls::MediaPlaylist::Parse(source, uri, version, nullptr);
317       });
318   rendition->CheckState(base::Seconds(4), 1.0,
319                         BindCheckState(base::Seconds(0)));
320   task_environment_.RunUntilIdle();
321
322   // It then gets called again (since it was scheduled for zero seconds),
323   // and this time tries to download data.
324   RespondToUrl("http://example.com/playlist_4500Kb_14551249.ts", "tscontent");
325   EXPECT_CALL(*mock_mdeh_, AppendAndParseData(_, _, _, _, _, 9))
326       .WillOnce(Return(true));
327   rendition->CheckState(base::Seconds(4), 1.0,
328                         BindCheckState(base::Seconds(0)));
329   task_environment_.RunUntilIdle();
330
331   // Loading that content creates a buffered range somewhere in the future,
332   // which we then get a request to seek to.
333   post_seek_ranges.Add(base::Seconds(1000), base::Seconds(1032));
334   EXPECT_CALL(*mock_mdeh_, GetBufferedRanges(_))
335       .WillRepeatedly(Return(post_seek_ranges));
336   EXPECT_CALL(*mock_mdeh_, RequestSeek(base::Seconds(1000)));
337   rendition->CheckState(base::Seconds(4), 1.0, BindCheckState(kNoTimestamp));
338
339   // From destructor.
340   EXPECT_CALL(*mock_mdeh_, RemoveRole(_));
341   task_environment_.RunUntilIdle();
342 }
343
344 TEST_F(HlsLiveRenditionUnittest, TestStop) {
345   auto rendition =
346       MakeLiveRendition(GURL("http://example.com"), kInitialFetchLivePlaylist);
347   ASSERT_NE(rendition, nullptr);
348
349   rendition->Stop();
350
351   // Should always be kNoTimestamp after `Stop()` and no network requests.
352   rendition->CheckState(base::Seconds(0), 1.0, BindCheckState(kNoTimestamp));
353 }
354
355 }  // namespace media