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.
5 #include "media/filters/hls_manifest_demuxer_engine.h"
6 #include "media/filters/manifest_demuxer.h"
12 #include "base/memory/scoped_refptr.h"
13 #include "base/run_loop.h"
14 #include "base/test/gmock_callback_support.h"
15 #include "base/test/task_environment.h"
16 #include "media/base/mock_media_log.h"
17 #include "media/base/pipeline_status.h"
18 #include "media/base/test_helpers.h"
19 #include "media/filters/hls_data_source_provider.h"
20 #include "media/filters/hls_test_helpers.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
26 const std::string kInvalidMediaPlaylist =
30 const std::string kShortMediaPlaylist =
32 "#EXT-X-TARGETDURATION:10\n"
35 "http://media.example.com/first.ts\n"
38 const std::string kSimpleMediaPlaylist =
40 "#EXT-X-TARGETDURATION:10\n"
43 "http://media.example.com/first.ts\n"
45 "http://media.example.com/second.ts\n"
47 "http://media.example.com/third.ts\n"
50 const std::string kSimpleLiveMediaPlaylist =
52 "#EXT-X-TARGETDURATION:10\n"
54 "#EXT-X-MEDIA-SEQUENCE:18698597\n"
56 "http://media.example.com/first.ts\n"
58 "http://media.example.com/second.ts\n"
60 "http://media.example.com/third.ts\n";
62 const std::string kSingleInfoMediaPlaylist =
64 "#EXT-X-TARGETDURATION:10\n"
67 "http://media.example.com/only.ts\n";
69 const std::string kUnsupportedCodecs =
71 "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"vvc1.00.00\"\n"
72 "http://example.com/audio-only.m3u8\n"
73 "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"sheet.music\"\n"
74 "http://example.com/audio-only.m3u8\n"
75 "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"av02.00.00\"\n"
76 "http://example.com/audio-only.m3u8\n";
78 const std::string kSimpleMultivariantPlaylist =
80 "#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1000000\n"
81 "http://example.com/low.m3u8\n"
82 "#EXT-X-STREAM-INF:BANDWIDTH=2560000,AVERAGE-BANDWIDTH=2000000\n"
83 "http://example.com/mid.m3u8\n"
84 "#EXT-X-STREAM-INF:BANDWIDTH=7680000,AVERAGE-BANDWIDTH=6000000\n"
85 "http://example.com/hi.m3u8\n"
86 "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.5\"\n"
87 "http://example.com/audio-only.m3u8\n";
89 const std::string kMultivariantPlaylistWithAlts =
91 "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Eng\",DEFAULT=YES,"
92 "AUTOSELECT=YES,LANGUAGE=\"en\",URI=\"eng-audio.m3u8\"\n"
93 "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Ger\",DEFAULT=NO,"
94 "AUTOSELECT=YES,LANGUAGE=\"en\",URI=\"ger-audio.m3u8\"\n"
95 "#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"aac\",NAME=\"Com\",DEFAULT=NO,"
96 "AUTOSELECT=NO,LANGUAGE=\"en\",URI=\"eng-comments.m3u8\"\n"
97 "#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"avc1.420000\",AUDIO=\"aac\"\n"
98 "low/video-only.m3u8\n"
99 "#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS=\"avc1.420000\",AUDIO=\"aac\"\n"
100 "mid/video-only.m3u8\n"
101 "#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS=\"avc1.420000\",AUDIO=\"aac\"\n"
102 "hi/video-only.m3u8\n"
103 "#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS=\"mp4a.40.05\",AUDIO=\"aac\"\n"
104 "main/english-audio.m3u8\n";
106 using ::base::test::RunOnceCallback;
107 using ::base::test::RunOnceClosure;
109 using testing::AtLeast;
110 using testing::ByMove;
111 using testing::DoAll;
113 using testing::Invoke;
114 using testing::NiceMock;
115 using testing::NotNull;
117 using testing::Return;
118 using testing::SaveArg;
119 using testing::SetArgPointee;
120 using testing::StrictMock;
125 std::string(negation ? "isn't" : "is") + " within " +
126 testing::PrintToString(Radius) + " of " +
127 testing::PrintToString(Target)) {
128 return (arg - Target <= Radius) || (Target - arg <= Radius);
132 class FakeHlsDataSourceProvider : public HlsDataSourceProvider {
134 raw_ptr<HlsDataSourceProvider> mock_;
137 FakeHlsDataSourceProvider(HlsDataSourceProvider* mock) : mock_(mock) {}
139 void ReadFromUrl(GURL url,
140 absl::optional<hls::types::ByteRange> range,
141 HlsDataSourceProvider::ReadCb request) override {
142 mock_->ReadFromUrl(url, range, std::move(request));
145 void ReadFromExistingStream(std::unique_ptr<HlsDataSourceStream> stream,
146 HlsDataSourceProvider::ReadCb cb) override {
147 CHECK(!stream->CanReadMore());
148 std::move(cb).Run(std::move(stream));
151 void AbortPendingReads(base::OnceClosure callback) override {
152 mock_->AbortPendingReads(std::move(callback));
156 class HlsManifestDemuxerEngineTest : public testing::Test {
158 std::unique_ptr<MediaLog> media_log_;
159 std::unique_ptr<MockManifestDemuxerEngineHost> mock_mdeh_;
160 std::unique_ptr<MockHlsDataSourceProvider> mock_dsp_;
161 base::test::TaskEnvironment task_environment_;
162 std::unique_ptr<HlsManifestDemuxerEngine> engine_;
163 std::unique_ptr<MockCodecDetector> mock_detector_;
165 MOCK_METHOD(void, MockInitComplete, (PipelineStatus status), ());
167 template <typename T>
168 void BindUrlToDataSource(std::string url, std::string value) {
169 EXPECT_CALL(*mock_dsp_, ReadFromUrl(GURL(url), _, _))
171 .WillOnce(RunOnceCallback<2>(T::CreateStream(value)));
175 HlsManifestDemuxerEngineTest()
176 : media_log_(std::make_unique<NiceMock<media::MockMediaLog>>()),
177 mock_mdeh_(std::make_unique<NiceMock<MockManifestDemuxerEngineHost>>()),
178 mock_dsp_(std::make_unique<StrictMock<MockHlsDataSourceProvider>>()) {
179 ON_CALL(*mock_mdeh_, AddRole(_, _, _)).WillByDefault(Return(true));
180 ON_CALL(*mock_mdeh_, GetBufferedRanges(_))
181 .WillByDefault(Return(Ranges<base::TimeDelta>()));
183 EXPECT_CALL(*mock_dsp_, ReadFromExistingStream(_, _)).Times(0);
185 base::SequenceBound<FakeHlsDataSourceProvider> dsp(
186 task_environment_.GetMainThreadTaskRunner(), mock_dsp_.get());
188 engine_ = std::make_unique<HlsManifestDemuxerEngine>(
189 std::move(dsp), base::SingleThreadTaskRunner::GetCurrentDefault(),
190 GURL("http://media.example.com/manifest.m3u8"), media_log_.get());
192 mock_detector_ = std::make_unique<StrictMock<MockCodecDetector>>();
195 void InitializeEngine() {
198 base::BindOnce(&HlsManifestDemuxerEngineTest::MockInitComplete,
199 base::Unretained(this)));
202 void InitializeEngineWithMockDetector() {
203 engine_->InitializeWithMockCodecDetectorForTesting(
205 base::BindOnce(&HlsManifestDemuxerEngineTest::MockInitComplete,
206 base::Unretained(this)),
207 std::move(mock_detector_));
210 ~HlsManifestDemuxerEngineTest() override {
212 base::RunLoop().RunUntilIdle();
216 TEST_F(HlsManifestDemuxerEngineTest, TestInitFailure) {
217 BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
218 "http://media.example.com/manifest.m3u8", kInvalidMediaPlaylist);
219 EXPECT_CALL(*mock_mdeh_,
220 OnError(HasStatusCode(DEMUXER_ERROR_COULD_NOT_PARSE)));
221 EXPECT_CALL(*this, MockInitComplete(_)).Times(0);
223 task_environment_.RunUntilIdle();
224 ASSERT_TRUE(engine_->IsSeekable());
227 TEST_F(HlsManifestDemuxerEngineTest, TestSimpleConfigAddsOnePrimaryRole) {
228 EXPECT_CALL(*mock_mdeh_, SetSequenceMode(base::StringPiece("primary"), true));
229 EXPECT_CALL(*mock_mdeh_, SetDuration(21.021));
230 EXPECT_CALL(*mock_mdeh_, AddRole(base::StringPiece("primary"), "video/mp2t",
231 "avc1.420000, mp4a.40.05"));
232 EXPECT_CALL(*mock_mdeh_, RemoveRole(base::StringPiece("primary")));
233 BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
234 "http://media.example.com/manifest.m3u8", kSimpleMediaPlaylist);
235 BindUrlToDataSource<FileHlsDataSourceStreamFactory>(
236 "http://media.example.com/first.ts", "bear-1280x720-hls.ts");
237 EXPECT_CALL(*this, MockInitComplete(HasStatusCode(PIPELINE_OK)));
239 task_environment_.RunUntilIdle();
240 ASSERT_TRUE(engine_->IsSeekable());
243 TEST_F(HlsManifestDemuxerEngineTest, TestSimpleLiveConfigAddsOnePrimaryRole) {
244 EXPECT_CALL(*mock_mdeh_, SetSequenceMode(base::StringPiece("primary"), true));
245 EXPECT_CALL(*mock_mdeh_, AddRole(base::StringPiece("primary"), "video/mp2t",
246 "avc1.420000, mp4a.40.05"));
247 EXPECT_CALL(*mock_mdeh_, RemoveRole(base::StringPiece("primary")));
248 BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
249 "http://media.example.com/manifest.m3u8", kSimpleLiveMediaPlaylist);
250 BindUrlToDataSource<FileHlsDataSourceStreamFactory>(
251 "http://media.example.com/first.ts", "bear-1280x720-hls.ts");
252 EXPECT_CALL(*this, MockInitComplete(HasStatusCode(PIPELINE_OK)));
254 task_environment_.RunUntilIdle();
255 ASSERT_FALSE(engine_->IsSeekable());
258 TEST_F(HlsManifestDemuxerEngineTest, TestMultivariantPlaylistNoAlternates) {
259 EXPECT_CALL(*mock_mdeh_, SetSequenceMode(base::StringPiece("primary"), true));
260 EXPECT_CALL(*mock_mdeh_, SetDuration(21.021));
261 EXPECT_CALL(*mock_mdeh_, AddRole(base::StringPiece("primary"), "video/mp2t",
262 "avc1.420000, mp4a.40.05"));
263 BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
264 "http://media.example.com/manifest.m3u8", kSimpleMultivariantPlaylist);
265 BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
266 "http://example.com/hi.m3u8", kSimpleMediaPlaylist);
267 BindUrlToDataSource<FileHlsDataSourceStreamFactory>(
268 "http://media.example.com/first.ts", "bear-1280x720-hls.ts");
269 EXPECT_CALL(*this, MockInitComplete(HasStatusCode(PIPELINE_OK)));
271 task_environment_.RunUntilIdle();
274 TEST_F(HlsManifestDemuxerEngineTest, TestMultivariantPlaylistWithAlternates) {
275 EXPECT_CALL(*mock_mdeh_,
276 SetSequenceMode(base::StringPiece("audio-override"), true));
277 EXPECT_CALL(*mock_mdeh_, SetSequenceMode(base::StringPiece("primary"), true));
278 EXPECT_CALL(*mock_mdeh_, SetDuration(21.021));
279 EXPECT_CALL(*mock_mdeh_, AddRole(base::StringPiece("audio-override"),
280 "video/mp2t", "avc1.420000"));
281 EXPECT_CALL(*mock_mdeh_, AddRole(base::StringPiece("primary"), "video/mp2t",
284 // URL queries in order:
285 // - manifest.m3u8: root manifest
286 // - eng-audio.m3u8: audio override rendition playlist
287 // - only.ts: check the container/codecs for the audio override rendition
288 // - video-only.m3u8: primary rendition
289 // - first.ts: check container/codecs for the primary rendition
290 BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
291 "http://media.example.com/manifest.m3u8", kMultivariantPlaylistWithAlts);
292 BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
293 "http://media.example.com/eng-audio.m3u8", kSingleInfoMediaPlaylist);
294 BindUrlToDataSource<FileHlsDataSourceStreamFactory>(
295 "http://media.example.com/only.ts", "bear-1280x720-aac_he.ts");
296 BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
297 "http://media.example.com/hi/video-only.m3u8", kSimpleMediaPlaylist);
298 BindUrlToDataSource<FileHlsDataSourceStreamFactory>(
299 "http://media.example.com/first.ts", "bear-1280x720-hls.ts");
300 EXPECT_CALL(*this, MockInitComplete(HasStatusCode(PIPELINE_OK)));
302 task_environment_.RunUntilIdle();
305 TEST_F(HlsManifestDemuxerEngineTest, TestMultivariantWithNoSupportedCodecs) {
306 EXPECT_CALL(*mock_mdeh_, AddRole(_, _, _)).Times(0);
307 EXPECT_CALL(*mock_mdeh_, SetSequenceMode(_, _)).Times(0);
308 BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
309 "http://media.example.com/manifest.m3u8", kUnsupportedCodecs);
310 EXPECT_CALL(*mock_mdeh_,
311 OnError(HasStatusCode(DEMUXER_ERROR_COULD_NOT_PARSE)));
313 task_environment_.RunUntilIdle();
316 TEST_F(HlsManifestDemuxerEngineTest, TestAsyncSeek) {
317 auto rendition = std::make_unique<StrictMock<MockHlsRendition>>();
318 EXPECT_CALL(*rendition, GetDuration()).WillOnce(Return(base::Seconds(30)));
319 auto* rendition_ptr = rendition.get();
320 engine_->AddRenditionForTesting(std::move(rendition));
321 // Set up rendition state and run, expecting no other callbacks.
322 task_environment_.RunUntilIdle();
324 // When seeking, indicate that we do not need to load more buffers.
325 EXPECT_CALL(*rendition_ptr, StartWaitingForSeek());
326 engine_->StartWaitingForSeek();
327 task_environment_.RunUntilIdle();
329 EXPECT_CALL(*rendition_ptr, Seek(_))
330 .WillOnce(Return(ManifestDemuxer::SeekState::kIsReady));
331 EXPECT_CALL(*mock_dsp_, AbortPendingReads(_)).WillOnce(RunOnceClosure<0>());
332 engine_->Seek(base::Seconds(10),
333 base::BindOnce([](ManifestDemuxer::SeekResponse resp) {
334 ASSERT_TRUE(resp.has_value());
335 ASSERT_EQ(std::move(resp).value(),
336 ManifestDemuxer::SeekState::kIsReady);
338 task_environment_.RunUntilIdle();
340 // Destruction should call stop.
341 EXPECT_CALL(*rendition_ptr, Stop());
342 task_environment_.RunUntilIdle();
345 TEST_F(HlsManifestDemuxerEngineTest, TestMultiRenditionCheckState) {
346 auto rendition1 = std::make_unique<MockHlsRendition>();
347 auto rendition2 = std::make_unique<MockHlsRendition>();
348 EXPECT_CALL(*rendition1, GetDuration()).WillOnce(Return(absl::nullopt));
349 EXPECT_CALL(*rendition2, GetDuration()).WillOnce(Return(absl::nullopt));
351 auto* rend1 = rendition1.get();
352 auto* rend2 = rendition2.get();
353 engine_->AddRenditionForTesting(std::move(rendition1));
355 // While there is only one rendition, the response from |OnTimeUpdate| is
356 // whatever that rendition wants.
357 EXPECT_CALL(*rend1, CheckState(_, _, _))
358 .WillOnce(RunOnceCallback<2>(base::Seconds(7)));
359 engine_->OnTimeUpdate(base::Seconds(0), 0.0,
360 base::BindOnce([](base::TimeDelta r) {
361 ASSERT_EQ(r, base::Seconds(7));
364 EXPECT_CALL(*rend1, CheckState(_, _, _))
365 .WillOnce(RunOnceCallback<2>(kNoTimestamp));
366 engine_->OnTimeUpdate(
367 base::Seconds(0), 0.0,
368 base::BindOnce([](base::TimeDelta r) { ASSERT_EQ(r, kNoTimestamp); }));
370 // After adding the second rendition, the response from OnTimeUpdate is now
371 // the lesser of (rend1.response - (calc time of rend2)) and
373 engine_->AddRenditionForTesting(std::move(rendition2));
375 // Both renditions request time, so pick the lesser.
376 EXPECT_CALL(*rend1, CheckState(_, _, _))
377 .WillOnce(RunOnceCallback<2>(base::Seconds(7)));
378 EXPECT_CALL(*rend2, CheckState(_, _, _))
379 .WillOnce(RunOnceCallback<2>(base::Seconds(3)));
380 engine_->OnTimeUpdate(
381 base::Seconds(0), 0.0, base::BindOnce([](base::TimeDelta r) {
382 EXPECT_THAT(r, CloseTo(base::Seconds(3), base::Milliseconds(1)));
385 // When one rendition provides kNoTimestamp and another does not, use the
386 // non-kNoTimestamp value.
387 EXPECT_CALL(*rend1, CheckState(_, _, _))
388 .WillOnce(RunOnceCallback<2>(kNoTimestamp));
389 EXPECT_CALL(*rend2, CheckState(_, _, _))
390 .WillOnce(RunOnceCallback<2>(base::Seconds(3)));
391 engine_->OnTimeUpdate(
392 base::Seconds(0), 0.0, base::BindOnce([](base::TimeDelta r) {
393 EXPECT_THAT(r, CloseTo(base::Seconds(3), base::Milliseconds(1)));
396 EXPECT_CALL(*rend1, CheckState(_, _, _))
397 .WillOnce(RunOnceCallback<2>(base::Seconds(7)));
398 EXPECT_CALL(*rend2, CheckState(_, _, _))
399 .WillOnce(RunOnceCallback<2>(kNoTimestamp));
400 engine_->OnTimeUpdate(
401 base::Seconds(0), 0.0, base::BindOnce([](base::TimeDelta r) {
402 EXPECT_THAT(r, CloseTo(base::Seconds(7), base::Milliseconds(1)));
406 TEST_F(HlsManifestDemuxerEngineTest, SeekAfterErrorFails) {
407 BindUrlToDataSource<StringHlsDataSourceStreamFactory>(
408 "http://media.example.com/manifest.m3u8", kInvalidMediaPlaylist);
409 EXPECT_CALL(*mock_mdeh_,
410 OnError(HasStatusCode(DEMUXER_ERROR_COULD_NOT_PARSE)));
411 EXPECT_CALL(*this, MockInitComplete(_)).Times(0);
413 task_environment_.RunUntilIdle();
415 // When one of the renditions surfaces an error, ManifestDemuxer will request
416 // that the engine stop. Mimic that here.
418 task_environment_.RunUntilIdle();
420 // Now if we try to seek, the response should be an instant aborted error.
421 engine_->Seek(base::Seconds(10),
422 base::BindOnce([](ManifestDemuxer::SeekResponse resp) {
423 ASSERT_FALSE(resp.has_value());
424 ASSERT_EQ(std::move(resp).error(), PIPELINE_ERROR_ABORT);
426 task_environment_.RunUntilIdle();
429 TEST_F(HlsManifestDemuxerEngineTest, TestEndOfStreamAfterAllFetched) {
430 // All the expectations set during the initialization process.
431 EXPECT_CALL(*mock_mdeh_, SetSequenceMode(base::StringPiece("primary"), true));
432 EXPECT_CALL(*mock_mdeh_, AddRole(base::StringPiece("primary"), "video/mp2t",
433 "avc1.420000, mp4a.40.05"));
434 EXPECT_CALL(*mock_mdeh_, SetDuration(9.009));
435 HlsCodecDetector::ContainerAndCodecs mock_response = {
436 "video/mp2t", "avc1.420000, mp4a.40.05"};
437 EXPECT_CALL(*mock_detector_, DetermineContainerAndCodec(_, _))
438 .WillOnce(RunOnceCallback<1>(mock_response));
439 EXPECT_CALL(*this, MockInitComplete(HasStatusCode(PIPELINE_OK)));
441 // We can't use `BindUrlToDataSource` here, since it can't re-create streams
442 // like we need it to. The network requests are in order:
443 // - manifest.m3u8 - main manifest
444 // - first.ts - request for the first few bytes to do codec detection
445 // - first.ts - request for chunks of data to add to ChunkDemuxer
446 EXPECT_CALL(*mock_dsp_,
447 ReadFromUrl(GURL("http://media.example.com/manifest.m3u8"), _, _))
448 .WillOnce(RunOnceCallback<2>(
449 StringHlsDataSourceStreamFactory::CreateStream(kShortMediaPlaylist)));
450 EXPECT_CALL(*mock_dsp_,
451 ReadFromUrl(GURL("http://media.example.com/first.ts"), _, _))
453 RunOnceCallback<2>(StringHlsDataSourceStreamFactory::CreateStream(
454 "hey, this isn't a bitstream!")))
456 RunOnceCallback<2>(StringHlsDataSourceStreamFactory::CreateStream(
457 "do I look like a video to you?")));
459 // `GetBufferedRanges` gets called many times during this process:
460 // - HlsVodRendition::CheckState (1) => empty ranges, nothing loaded.
461 // - HlsVodRendition::OnSegmentData (1) => populated by AppendAndParseData
462 // - HlsVodRendition::CheckState (2) => still has data
463 Ranges<base::TimeDelta> populated_ranges;
464 populated_ranges.Add(base::Seconds(0), base::Seconds(5));
465 EXPECT_CALL(*mock_mdeh_, GetBufferedRanges(_))
466 .WillOnce(Return(Ranges<base::TimeDelta>()))
467 .WillOnce(Return(populated_ranges))
468 .WillOnce(Return(populated_ranges));
470 // The first call to `OnTimeUpdate` should trigger the append function,
471 // and our data was 30 characters long.
472 EXPECT_CALL(*mock_mdeh_,
473 AppendAndParseData("primary", base::Seconds(0), _, _, _, 30))
474 .WillOnce(Return(true));
476 // Finally, and EndOfStream call happens:
477 EXPECT_CALL(*mock_mdeh_, SetEndOfStream());
479 // And then teardown:
480 EXPECT_CALL(*mock_mdeh_, RemoveRole(base::StringPiece("primary")));
482 // Setup with a mock codec detector - this will set all the roles, duration,
483 // modes, and also make a request for the manifest and the first segment.
484 InitializeEngineWithMockDetector();
485 task_environment_.RunUntilIdle();
487 // For the first state check, there should be empty ranges, which triggers
488 // `HlsVodRendition::FetchNext`, which should request the data from first.ts
489 // add its content, and then return.
490 engine_->OnTimeUpdate(base::Seconds(0), 1.0, base::DoNothing());
491 task_environment_.RunUntilIdle();
493 // For the second state check, there are no more segments, no pending segment,
494 // and there are loaded ranges, so HlsVodRendition will report an EndOfStream.
495 engine_->OnTimeUpdate(base::Seconds(6), 1.0, base::DoNothing());
496 task_environment_.RunUntilIdle();
498 // Expectations on teardown.
499 ASSERT_TRUE(engine_->IsSeekable());
500 task_environment_.RunUntilIdle();