diff options
Diffstat (limited to 'chromium/media/capabilities/in_memory_video_decode_stats_db_unittest.cc')
-rw-r--r-- | chromium/media/capabilities/in_memory_video_decode_stats_db_unittest.cc | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/chromium/media/capabilities/in_memory_video_decode_stats_db_unittest.cc b/chromium/media/capabilities/in_memory_video_decode_stats_db_unittest.cc new file mode 100644 index 00000000000..6f6264bf490 --- /dev/null +++ b/chromium/media/capabilities/in_memory_video_decode_stats_db_unittest.cc @@ -0,0 +1,391 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/logging.h" +#include "base/memory/ptr_util.h" +#include "base/test/scoped_task_environment.h" +#include "media/capabilities/in_memory_video_decode_stats_db_impl.h" +#include "media/capabilities/video_decode_stats_db_provider.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Eq; +using testing::Pointee; +using testing::IsNull; + +namespace media { + +static VideoDecodeStatsDB::VideoDescKey kTestKey() { + return VideoDecodeStatsDB::VideoDescKey::MakeBucketedKey( + VP9PROFILE_PROFILE3, gfx::Size(1024, 768), 60); +} + +static VideoDecodeStatsDB::DecodeStatsEntry kEmtpyEntry() { + return VideoDecodeStatsDB::DecodeStatsEntry(0, 0, 0); +} + +class MockSeedDB : public VideoDecodeStatsDB { + public: + MockSeedDB() = default; + ~MockSeedDB() override = default; + + MOCK_METHOD1(Initialize, void(InitializeCB init_cb)); + MOCK_METHOD3(AppendDecodeStats, + void(const VideoDescKey& key, + const DecodeStatsEntry& entry, + AppendDecodeStatsCB append_done_cb)); + MOCK_METHOD2(GetDecodeStats, + void(const VideoDescKey& key, GetDecodeStatsCB get_stats_cb)); + MOCK_METHOD1(DestroyStats, void(base::OnceClosure destroy_done_cb)); +}; + +class MockDBProvider : public VideoDecodeStatsDBProvider { + public: + MockDBProvider() = default; + ~MockDBProvider() override = default; + + MOCK_METHOD1(GetVideoDecodeStatsDB, void(GetCB get_db_b)); +}; + +template <bool WithSeedDB> +class InMemoryDBTestBase : public testing::Test { + public: + InMemoryDBTestBase() + : seed_db_(WithSeedDB ? new MockSeedDB() : nullptr), + db_provider_(WithSeedDB ? new MockDBProvider() : nullptr), + in_memory_db_(new InMemoryVideoDecodeStatsDBImpl(db_provider_.get())) { + // Setup MockDBProvider to provide the seed DB. No need to initialize the + // DB here since it too is a Mock. + if (db_provider_) { + using GetCB = VideoDecodeStatsDBProvider::GetCB; + ON_CALL(*db_provider_, GetVideoDecodeStatsDB(_)) + .WillByDefault([&](GetCB cb) { std::move(cb).Run(seed_db_.get()); }); + } + + // The InMemoryDB should NEVER modify the seed DB. + if (seed_db_) { + EXPECT_CALL(*seed_db_, AppendDecodeStats(_, _, _)).Times(0); + EXPECT_CALL(*seed_db_, DestroyStats(_)).Times(0); + } + } + + void InitializeEmptyDB() { + if (seed_db_) + EXPECT_CALL(*db_provider_, GetVideoDecodeStatsDB(_)); + + EXPECT_CALL(*this, InitializeCB(true)); + + in_memory_db_->Initialize(base::BindOnce(&InMemoryDBTestBase::InitializeCB, + base::Unretained(this))); + scoped_task_environment_.RunUntilIdle(); + } + + MOCK_METHOD1(InitializeCB, void(bool success)); + MOCK_METHOD1(AppendDecodeStatsCB, void(bool success)); + MOCK_METHOD2( + GetDecodeStatsCB, + void(bool success, + std::unique_ptr<VideoDecodeStatsDB::DecodeStatsEntry> entry)); + MOCK_METHOD0(DestroyStatsCB, void()); + + protected: + using VideoDescKey = media::VideoDecodeStatsDB::VideoDescKey; + using DecodeStatsEntry = media::VideoDecodeStatsDB::DecodeStatsEntry; + + base::test::ScopedTaskEnvironment scoped_task_environment_; + std::unique_ptr<MockSeedDB> seed_db_; + std::unique_ptr<MockDBProvider> db_provider_; + std::unique_ptr<InMemoryVideoDecodeStatsDBImpl> in_memory_db_; +}; + +// Specialization for tests that have/lack a seed DB. Some tests only make sense +// with seed DB, so we separate them. +class SeededInMemoryDBTest : public InMemoryDBTestBase<true> {}; +class SeedlessInMemoryDBTest : public InMemoryDBTestBase<false> {}; + +TEST_F(SeedlessInMemoryDBTest, ReadExpectingEmpty) { + InitializeEmptyDB(); + + // Database is empty, seed DB is empty => expect empty stats entry. + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(kEmtpyEntry())))); + + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); +} + +TEST_F(SeededInMemoryDBTest, ReadExpectingEmpty) { + InitializeEmptyDB(); + + // Make seed DB return null (empty) for this request. + EXPECT_CALL(*seed_db_, GetDecodeStats(Eq(kTestKey()), _)) + .WillOnce([](const auto& key, auto get_cb) { + std::move(get_cb).Run(true, nullptr); + }); + + // Database is empty, seed DB is empty => expect empty stats entry. + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(kEmtpyEntry())))); + + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); +} + +TEST_F(SeededInMemoryDBTest, ReadExpectingSeedData) { + InitializeEmptyDB(); + + // Setup seed DB to return an entry for the test key. + DecodeStatsEntry seed_entry(1000, 2, 10); + + EXPECT_CALL(*seed_db_, GetDecodeStats(Eq(kTestKey()), _)) + .WillOnce([&](const auto& key, auto get_cb) { + std::move(get_cb).Run(true, + std::make_unique<DecodeStatsEntry>(seed_entry)); + }); + + // Seed DB has a an entry for the test key. Expect it! + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(seed_entry)))); + + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); + ::testing::Mock::VerifyAndClear(this); + + // Verify a second GetDecodeStats() call with the same key does not trigger a + // second call to the seed DB (we cache it). + EXPECT_CALL(*seed_db_, GetDecodeStats(_, _)).Times(0); + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(seed_entry)))); + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); +} + +TEST_F(SeededInMemoryDBTest, AppendReadAndDestroy) { + const DecodeStatsEntry seed_entry(1000, 2, 10); + const DecodeStatsEntry double_seed_entry(2000, 4, 20); + const DecodeStatsEntry triple_seed_entry(3000, 6, 30); + + InitializeEmptyDB(); + + // Setup seed DB to always return an entry for the test key. + ON_CALL(*seed_db_, GetDecodeStats(Eq(kTestKey()), _)) + .WillByDefault([&](const auto& key, auto get_cb) { + std::move(get_cb).Run(true, + std::make_unique<DecodeStatsEntry>(seed_entry)); + }); + + // First append should trigger a request for the same key from the seed DB. + // Simulate a successful read providing seed_entry for that key. + EXPECT_CALL(*seed_db_, GetDecodeStats(Eq(kTestKey()), _)); + + // Append the same seed entry, doubling the stats for this key. + EXPECT_CALL(*this, AppendDecodeStatsCB(true)); + in_memory_db_->AppendDecodeStats( + kTestKey(), seed_entry, + base::BindOnce(&InMemoryDBTestBase::AppendDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); + ::testing::Mock::VerifyAndClear(this); + + // Seed DB should not be queried again for this key. + EXPECT_CALL(*seed_db_, GetDecodeStats(Eq(kTestKey()), _)).Times(0); + + // Now verify that the stats were doubled by the append above. + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(double_seed_entry)))); + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); + ::testing::Mock::VerifyAndClear(this); + + // Append the same seed entry again to triple the stats. Additional appends + // should not trigger queries the seed DB for this key. + EXPECT_CALL(*seed_db_, GetDecodeStats(Eq(kTestKey()), _)).Times(0); + in_memory_db_->AppendDecodeStats( + kTestKey(), seed_entry, + base::BindOnce(&InMemoryDBTestBase::AppendDecodeStatsCB, + base::Unretained(this))); + + // Verify we have 3x the stats. + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(triple_seed_entry)))); + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + // Now destroy the in-memory stats... + EXPECT_CALL(*this, DestroyStatsCB()); + in_memory_db_->DestroyStats(base::BindOnce( + &InMemoryDBTestBase::DestroyStatsCB, base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); + ::testing::Mock::VerifyAndClear(this); + + // With in-memory stats now gone, GetDecodeStats(kTestKey()) should again + // trigger a call to the seed DB and return the un-doubled seed stats. + EXPECT_CALL(*seed_db_, GetDecodeStats(Eq(kTestKey()), _)); + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(seed_entry)))); + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); +} + +TEST_F(SeedlessInMemoryDBTest, AppendReadAndDestroy) { + const DecodeStatsEntry entry(50, 1, 5); + const DecodeStatsEntry double_entry(100, 2, 10); + + InitializeEmptyDB(); + + // Expect successful append to the empty seedless DB. + EXPECT_CALL(*this, AppendDecodeStatsCB(true)); + in_memory_db_->AppendDecodeStats( + kTestKey(), entry, + base::BindOnce(&InMemoryDBTestBase::AppendDecodeStatsCB, + base::Unretained(this))); + + // Verify stats can be read back. + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(entry)))); + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); + ::testing::Mock::VerifyAndClear(this); + + // Append same stats again to test summation. + EXPECT_CALL(*this, AppendDecodeStatsCB(true)); + in_memory_db_->AppendDecodeStats( + kTestKey(), entry, + base::BindOnce(&InMemoryDBTestBase::AppendDecodeStatsCB, + base::Unretained(this))); + + // Verify doubled stats can be read back. + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(double_entry)))); + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); + ::testing::Mock::VerifyAndClear(this); + + // Now destroy the in-memory stats... + EXPECT_CALL(*this, DestroyStatsCB()); + in_memory_db_->DestroyStats(base::BindOnce( + &InMemoryDBTestBase::DestroyStatsCB, base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); + ::testing::Mock::VerifyAndClear(this); + + // Verify DB now empty for this key. + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(kEmtpyEntry())))); + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); +} + +TEST_F(SeededInMemoryDBTest, ProvidedNullSeedDB) { + // DB provider may provide a null seed DB if it encounters some error. + EXPECT_CALL(*db_provider_, GetVideoDecodeStatsDB(_)) + .WillOnce([](auto get_db_cb) { std::move(get_db_cb).Run(nullptr); }); + + // Failing to obtain the seed DB is not a show stopper. The in-memory DB + // should simply carry on in a seedless fashion. + EXPECT_CALL(*this, InitializeCB(true)); + in_memory_db_->Initialize(base::BindOnce(&InMemoryDBTestBase::InitializeCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); + ::testing::Mock::VerifyAndClear(this); + + // Writes still succeed. + EXPECT_CALL(*this, AppendDecodeStatsCB(true)); + const DecodeStatsEntry entry(50, 1, 5); + in_memory_db_->AppendDecodeStats( + kTestKey(), entry, + base::BindOnce(&InMemoryDBTestBase::AppendDecodeStatsCB, + base::Unretained(this))); + + // Reads should still succeed. + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(entry)))); + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); +} + +TEST_F(SeededInMemoryDBTest, SeedReadFailureOnGettingStats) { + // Everything seems fine at initialization... + InitializeEmptyDB(); + + // But seed DB will repeatedly fail to provide stats. + ON_CALL(*seed_db_, GetDecodeStats(_, _)) + .WillByDefault([](const auto& key, auto get_cb) { + std::move(get_cb).Run(false, nullptr); + }); + + // Reading the in-memory will still try to read the seed DB, and the read + // callback will simply report that the DB is empty for this key. + EXPECT_CALL(*seed_db_, GetDecodeStats(Eq(kTestKey()), _)); + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(kEmtpyEntry())))); + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); +} + +TEST_F(SeededInMemoryDBTest, SeedReadFailureOnAppendingingStats) { + // Everything seems fine at initialization... + InitializeEmptyDB(); + + // But seed DB will repeatedly fail to provide stats. + ON_CALL(*seed_db_, GetDecodeStats(_, _)) + .WillByDefault([](const auto& key, auto get_cb) { + std::move(get_cb).Run(false, nullptr); + }); + + // Appending to the in-memory will still try to read the seed DB, and the + // append will proceed successfully as if the seed DB were empty. + EXPECT_CALL(*seed_db_, GetDecodeStats(Eq(kTestKey()), _)); + EXPECT_CALL(*this, AppendDecodeStatsCB(true)); + const DecodeStatsEntry entry(50, 1, 5); + in_memory_db_->AppendDecodeStats( + kTestKey(), entry, + base::BindOnce(&InMemoryDBTestBase::AppendDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); + ::testing::Mock::VerifyAndClear(this); + + // Reading the appended data works without issue and does not trigger new + // queries to the seed DB. + EXPECT_CALL(*seed_db_, GetDecodeStats(Eq(kTestKey()), _)).Times(0); + EXPECT_CALL(*this, GetDecodeStatsCB(true, Pointee(Eq(entry)))); + in_memory_db_->GetDecodeStats( + kTestKey(), base::BindOnce(&InMemoryDBTestBase::GetDecodeStatsCB, + base::Unretained(this))); + + scoped_task_environment_.RunUntilIdle(); +} + +} // namespace media |