| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <map> |
| #include <memory> |
| |
| #include "base/check.h" |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/simple_test_clock.h" |
| #include "base/test/task_environment.h" |
| #include "components/leveldb_proto/testing/fake_db.h" |
| #include "media/base/media_switches.h" |
| #include "media/base/test_data_util.h" |
| #include "media/base/video_codecs.h" |
| #include "media/capabilities/webrtc_video_stats.pb.h" |
| #include "media/capabilities/webrtc_video_stats_db_impl.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gfx/geometry/size.h" |
| |
| using leveldb_proto::test::FakeDB; |
| using testing::_; |
| using testing::Eq; |
| using testing::Pointee; |
| |
| namespace media { |
| |
| class WebrtcVideoStatsDBImplTest : public ::testing::Test { |
| public: |
| using VideoDescKey = WebrtcVideoStatsDB::VideoDescKey; |
| using VideoStats = WebrtcVideoStatsDB::VideoStats; |
| using VideoStatsEntry = WebrtcVideoStatsDB::VideoStatsEntry; |
| |
| WebrtcVideoStatsDBImplTest() |
| : kDecodeStatsKeyVp9( |
| VideoDescKey::MakeBucketedKey(/*is_decode_stats=*/true, |
| VP9PROFILE_PROFILE3, |
| /*hardware_accelerated=*/false, |
| 1280 * 720)), |
| kDecodeStatsKeyVp9Hw( |
| VideoDescKey::MakeBucketedKey(/*is_decode_stats=*/true, |
| VP9PROFILE_PROFILE3, |
| /*hardware_accelerated=*/true, |
| 1280 * 720)), |
| kDecodeStatsKeyVp9FullHd( |
| VideoDescKey::MakeBucketedKey(/*is_decode_stats=*/true, |
| VP9PROFILE_PROFILE3, |
| /*hardware_accelerated=*/false, |
| 1920 * 1080)), |
| kDecodeStatsKeyVp94K( |
| VideoDescKey::MakeBucketedKey(/*is_decode_stats=*/true, |
| VP9PROFILE_PROFILE3, |
| /*hardware_accelerated=*/false, |
| 3840 * 2160)), |
| kDecodeStatsKeyH264( |
| VideoDescKey::MakeBucketedKey(/*is_decode_stats=*/true, |
| H264PROFILE_MIN, |
| /*hardware_accelerated=*/false, |
| 1920 * 1080)), |
| kEncodeStatsKeyVp9( |
| VideoDescKey::MakeBucketedKey(/*is_decode_stats=*/false, |
| VP9PROFILE_PROFILE3, |
| /*hardware_accelerated=*/false, |
| 1280 * 720)) { |
| // Fake DB simply wraps a std::map with the LevelDB interface. We own the |
| // map and will delete it in TearDown(). |
| fake_db_map_ = |
| std::make_unique<FakeDB<WebrtcVideoStatsEntryProto>::EntryMap>(); |
| // `stats_db_` will own this pointer, but we hold a reference to control |
| // its behavior. |
| fake_db_ = new FakeDB<WebrtcVideoStatsEntryProto>(fake_db_map_.get()); |
| |
| // Wrap the fake proto DB with our interface. |
| stats_db_ = base::WrapUnique(new WebrtcVideoStatsDBImpl( |
| std::unique_ptr<FakeDB<WebrtcVideoStatsEntryProto>>(fake_db_))); |
| } |
| |
| WebrtcVideoStatsDBImplTest(const WebrtcVideoStatsDBImplTest&) = delete; |
| WebrtcVideoStatsDBImplTest& operator=(const WebrtcVideoStatsDBImplTest&) = |
| delete; |
| |
| ~WebrtcVideoStatsDBImplTest() override { |
| // Tests should always complete any pending operations |
| VerifyNoPendingOps(); |
| } |
| |
| void VerifyOnePendingOp(std::string op_name) { |
| EXPECT_EQ(stats_db_->pending_operations_.get_pending_ops_for_test().size(), |
| 1u); |
| PendingOperations::PendingOperation* pending_op = |
| stats_db_->pending_operations_.get_pending_ops_for_test() |
| .begin() |
| ->second.get(); |
| EXPECT_EQ(pending_op->uma_str_, op_name); |
| } |
| |
| void VerifyNoPendingOps() { |
| EXPECT_TRUE( |
| stats_db_->pending_operations_.get_pending_ops_for_test().empty()); |
| } |
| |
| base::TimeDelta GetMaxTimeToKeepStats() { |
| return WebrtcVideoStatsDBImpl::GetMaxTimeToKeepStats(); |
| } |
| |
| int GetMaxEntriesPerConfig() { |
| return WebrtcVideoStatsDBImpl::GetMaxEntriesPerConfig(); |
| } |
| |
| void SetDBClock(base::Clock* clock) { |
| stats_db_->set_wall_clock_for_test(clock); |
| } |
| |
| void InitializeDB() { |
| stats_db_->Initialize(base::BindOnce( |
| &WebrtcVideoStatsDBImplTest::OnInitialize, base::Unretained(this))); |
| EXPECT_CALL(*this, OnInitialize(true)); |
| fake_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kOK); |
| testing::Mock::VerifyAndClearExpectations(this); |
| } |
| |
| void AppendStats(const VideoDescKey& key, const VideoStats& entry) { |
| EXPECT_CALL(*this, MockAppendVideoStatsCb(true)); |
| stats_db_->AppendVideoStats( |
| key, entry, |
| base::BindOnce(&WebrtcVideoStatsDBImplTest::MockAppendVideoStatsCb, |
| base::Unretained(this))); |
| VerifyOnePendingOp("Read"); |
| fake_db_->GetCallback(true); |
| VerifyOnePendingOp("Write"); |
| fake_db_->UpdateCallback(true); |
| testing::Mock::VerifyAndClearExpectations(this); |
| } |
| |
| void VerifyReadStats(const VideoDescKey& key, |
| const VideoStatsEntry& expected) { |
| EXPECT_CALL(*this, |
| MockGetVideoStatsCb(true, absl::make_optional(expected))); |
| stats_db_->GetVideoStats( |
| key, base::BindOnce(&WebrtcVideoStatsDBImplTest::MockGetVideoStatsCb, |
| base::Unretained(this))); |
| VerifyOnePendingOp("Read"); |
| fake_db_->GetCallback(true); |
| testing::Mock::VerifyAndClearExpectations(this); |
| } |
| |
| void VerifyReadStatsCollection( |
| const VideoDescKey& key, |
| const WebrtcVideoStatsDB::VideoStatsCollection& expected) { |
| EXPECT_CALL(*this, MockGetVideoStatsCollectionCb( |
| true, absl::make_optional(expected))); |
| stats_db_->GetVideoStatsCollection( |
| key, base::BindOnce( |
| &WebrtcVideoStatsDBImplTest::MockGetVideoStatsCollectionCb, |
| base::Unretained(this))); |
| VerifyOnePendingOp("Read"); |
| fake_db_->LoadCallback(true); |
| testing::Mock::VerifyAndClearExpectations(this); |
| } |
| |
| void VerifyEmptyStats(const VideoDescKey& key) { |
| EXPECT_CALL(*this, |
| MockGetVideoStatsCb(true, absl::optional<VideoStatsEntry>())); |
| stats_db_->GetVideoStats( |
| key, base::BindOnce(&WebrtcVideoStatsDBImplTest::MockGetVideoStatsCb, |
| base::Unretained(this))); |
| VerifyOnePendingOp("Read"); |
| fake_db_->GetCallback(true); |
| testing::Mock::VerifyAndClearExpectations(this); |
| } |
| |
| void AppendToProtoDB(const VideoDescKey& key, |
| const WebrtcVideoStatsEntryProto* const proto) { |
| base::RunLoop run_loop; |
| base::OnceCallback<void(bool)> update_done_cb = base::BindOnce( |
| [](base::RunLoop* run_loop, bool success) { |
| ASSERT_TRUE(success); |
| run_loop->Quit(); |
| }, |
| Unretained(&run_loop)); |
| |
| using DBType = leveldb_proto::ProtoDatabase<WebrtcVideoStatsEntryProto>; |
| std::unique_ptr<DBType::KeyEntryVector> entries = |
| std::make_unique<DBType::KeyEntryVector>(); |
| entries->emplace_back(key.Serialize(), *proto); |
| |
| fake_db_->UpdateEntries(std::move(entries), |
| std::make_unique<leveldb_proto::KeyVector>(), |
| std::move(update_done_cb)); |
| |
| fake_db_->UpdateCallback(true); |
| run_loop.Run(); |
| } |
| |
| MOCK_METHOD1(OnInitialize, void(bool success)); |
| |
| MOCK_METHOD2(MockGetVideoStatsCb, |
| void(bool success, absl::optional<VideoStatsEntry> entry)); |
| |
| MOCK_METHOD2(MockGetVideoStatsCollectionCb, |
| void(bool success, |
| absl::optional<WebrtcVideoStatsDB::VideoStatsCollection> |
| collection)); |
| |
| MOCK_METHOD1(MockAppendVideoStatsCb, void(bool success)); |
| |
| MOCK_METHOD0(MockClearStatsCb, void()); |
| |
| protected: |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| |
| const VideoDescKey kDecodeStatsKeyVp9; |
| const VideoDescKey kDecodeStatsKeyVp9Hw; |
| const VideoDescKey kDecodeStatsKeyVp9FullHd; |
| const VideoDescKey kDecodeStatsKeyVp94K; |
| const VideoDescKey kDecodeStatsKeyH264; |
| const VideoDescKey kEncodeStatsKeyVp9; |
| |
| // See documentation in SetUp() |
| std::unique_ptr<FakeDB<WebrtcVideoStatsEntryProto>::EntryMap> fake_db_map_; |
| raw_ptr<FakeDB<WebrtcVideoStatsEntryProto>> fake_db_; |
| std::unique_ptr<WebrtcVideoStatsDBImpl> stats_db_; |
| }; |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, InitializeFailed) { |
| stats_db_->Initialize(base::BindOnce( |
| &WebrtcVideoStatsDBImplTest::OnInitialize, base::Unretained(this))); |
| EXPECT_CALL(*this, OnInitialize(false)); |
| fake_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kError); |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, InitializeTimedOut) { |
| // Queue up an Initialize. |
| stats_db_->Initialize(base::BindOnce( |
| &WebrtcVideoStatsDBImplTest::OnInitialize, base::Unretained(this))); |
| VerifyOnePendingOp("Initialize"); |
| |
| // Move time forward enough to trigger timeout. |
| EXPECT_CALL(*this, OnInitialize(_)).Times(0); |
| task_environment_.FastForwardBy(base::Seconds(100)); |
| task_environment_.RunUntilIdle(); |
| |
| // Verify we didn't get an init callback and task is no longer considered |
| // pending (because it timed out). |
| testing::Mock::VerifyAndClearExpectations(this); |
| VerifyNoPendingOps(); |
| |
| // Verify callback still works if init completes very late. |
| EXPECT_CALL(*this, OnInitialize(false)); |
| fake_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kError); |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, ReadExpectingNothing) { |
| InitializeDB(); |
| VerifyEmptyStats(kDecodeStatsKeyVp9); |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, WriteReadAndClear) { |
| InitializeDB(); |
| |
| // Set test clock. |
| base::SimpleTestClock clock; |
| SetDBClock(&clock); |
| clock.SetNow(base::Time::Now()); |
| |
| // Append and read back some VP9 stats. |
| VideoStats stats1(clock.Now().ToJsTimeIgnoringNull(), 240, 6, 7.2); |
| VideoStatsEntry entry{stats1}; |
| AppendStats(kDecodeStatsKeyVp9, stats1); |
| VerifyReadStats(kDecodeStatsKeyVp9, entry); |
| |
| // Reading with the wrong key (different codec) should still return nothing. |
| VerifyEmptyStats(kDecodeStatsKeyH264); |
| VerifyEmptyStats(kEncodeStatsKeyVp9); |
| |
| // Appending new VP9 stats. |
| clock.Advance(base::Hours(1)); |
| VideoStats stats2(clock.Now().ToJsTimeIgnoringNull(), 1000, 14, 6.8); |
| |
| AppendStats(kDecodeStatsKeyVp9, stats2); |
| VideoStatsEntry aggregate_entry{stats2, stats1}; |
| VerifyReadStats(kDecodeStatsKeyVp9, aggregate_entry); |
| |
| // Clear all stats from the DB. |
| EXPECT_CALL(*this, MockClearStatsCb); |
| stats_db_->ClearStats(base::BindOnce( |
| &WebrtcVideoStatsDBImplTest::MockClearStatsCb, base::Unretained(this))); |
| VerifyOnePendingOp("Clear"); |
| fake_db_->UpdateCallback(true); |
| |
| // Database is now empty. Expect null entry. |
| VerifyEmptyStats(kDecodeStatsKeyVp9); |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, ExpiredStatsAreNotReturned) { |
| InitializeDB(); |
| |
| // Set test clock. |
| base::SimpleTestClock clock; |
| SetDBClock(&clock); |
| clock.SetNow(base::Time::Now()); |
| |
| // Append and read back some VP9 stats. |
| VideoStats stats1(clock.Now().ToJsTimeIgnoringNull(), 240, 6, 7.2); |
| VideoStatsEntry entry{stats1}; |
| AppendStats(kDecodeStatsKeyVp9, stats1); |
| VerifyReadStats(kDecodeStatsKeyVp9, entry); |
| |
| // Appending new VP9 stats. |
| clock.Advance(base::Days(2)); |
| VideoStats stats2(clock.Now().ToJsTimeIgnoringNull(), 1000, 14, 6.8); |
| clock.SetNow(base::Time::FromJsTime(stats2.timestamp)); |
| |
| AppendStats(kDecodeStatsKeyVp9, stats2); |
| VideoStatsEntry aggregate_entry{stats2, stats1}; |
| VerifyReadStats(kDecodeStatsKeyVp9, aggregate_entry); |
| |
| // Set the clock to a date so that the first entry is expired. |
| clock.SetNow(base::Time::FromJsTime(stats1.timestamp) + base::Days(1) + |
| GetMaxTimeToKeepStats()); |
| VideoStatsEntry nonexpired_entry{stats2}; |
| VerifyReadStats(kDecodeStatsKeyVp9, nonexpired_entry); |
| |
| // Set the clock so that all data have expired. |
| clock.SetNow(base::Time::FromJsTime(stats2.timestamp) + base::Days(1) + |
| GetMaxTimeToKeepStats()); |
| |
| // All stats are expired. Expect null entry. |
| VerifyEmptyStats(kDecodeStatsKeyVp9); |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, ConfigureExpireDays) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| std::unique_ptr<base::FieldTrialList> field_trial_list; |
| |
| base::TimeDelta previous_max_days_to_keep_stats = GetMaxTimeToKeepStats(); |
| constexpr int kNewMaxDaysToKeepStats = 4; |
| ASSERT_LT(base::Days(kNewMaxDaysToKeepStats), |
| previous_max_days_to_keep_stats); |
| |
| // Override field trial. |
| base::FieldTrialParams params; |
| params["db_days_to_keep_stats"] = |
| base::NumberToString(kNewMaxDaysToKeepStats); |
| scoped_feature_list.InitAndEnableFeatureWithParameters( |
| media::kWebrtcMediaCapabilitiesParameters, params); |
| EXPECT_EQ(base::Days(kNewMaxDaysToKeepStats), GetMaxTimeToKeepStats()); |
| |
| InitializeDB(); |
| |
| // Inject a test clock and initialize with the current time. |
| base::SimpleTestClock clock; |
| SetDBClock(&clock); |
| clock.SetNow(base::Time::Now()); |
| |
| // Append and verify read-back. |
| VideoStats stats1(clock.Now().ToJsTimeIgnoringNull(), 240, 6, 7.2); |
| VideoStatsEntry entry{stats1}; |
| AppendStats(kDecodeStatsKeyVp9, stats1); |
| VerifyReadStats(kDecodeStatsKeyVp9, entry); |
| |
| // Some simple math to avoid troubles of integer division. |
| int half_days_to_keep_stats = kNewMaxDaysToKeepStats / 2; |
| int remaining_days_to_keep_stats = |
| kNewMaxDaysToKeepStats - half_days_to_keep_stats; |
| |
| // Advance time half way through grace period. Verify stats not expired. |
| clock.Advance(base::Days(half_days_to_keep_stats)); |
| VerifyReadStats(kDecodeStatsKeyVp9, entry); |
| |
| // Advance time 1 day beyond grace period, verify stats are expired. |
| clock.Advance(base::Days(remaining_days_to_keep_stats + 1)); |
| VerifyEmptyStats(kDecodeStatsKeyVp9); |
| |
| // Advance the clock 100 extra days. Verify stats still expired. |
| clock.Advance(base::Days(100)); |
| VerifyEmptyStats(kDecodeStatsKeyVp9); |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, NewStatsReplaceOldStats) { |
| InitializeDB(); |
| |
| // Inject a test clock and initialize with the current time. |
| base::SimpleTestClock clock; |
| SetDBClock(&clock); |
| clock.SetNow(base::Time::Now()); |
| |
| // Append and verify read-back. |
| constexpr int kNumberOfStatsToAdd = 30; |
| EXPECT_GT(kNumberOfStatsToAdd, GetMaxEntriesPerConfig()); |
| VideoStatsEntry entry; |
| for (int i = 0; i < kNumberOfStatsToAdd; ++i) { |
| VideoStats stats(clock.Now().ToJsTimeIgnoringNull(), 240 + i, 6, |
| 7.2 + i % 3); |
| AppendStats(kDecodeStatsKeyVp9, stats); |
| // Start popping the last stats entry if the number of entries has reached |
| // the limit. |
| if (i >= GetMaxEntriesPerConfig()) { |
| entry.pop_back(); |
| } |
| entry.insert(entry.begin(), stats); |
| VerifyReadStats(kDecodeStatsKeyVp9, entry); |
| clock.Advance(base::Days(1)); |
| } |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, ConfigureMaxEntriesPerConfig) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| std::unique_ptr<base::FieldTrialList> field_trial_list; |
| |
| int previous_max_entries_per_config = GetMaxEntriesPerConfig(); |
| constexpr int kNewMaxEntriesPerConfig = 3; |
| ASSERT_LT(kNewMaxEntriesPerConfig, previous_max_entries_per_config); |
| |
| // Override field trial. |
| base::FieldTrialParams params; |
| params["db_max_entries_per_cpnfig"] = |
| base::NumberToString(kNewMaxEntriesPerConfig); |
| scoped_feature_list.InitAndEnableFeatureWithParameters( |
| media::kWebrtcMediaCapabilitiesParameters, params); |
| EXPECT_EQ(kNewMaxEntriesPerConfig, GetMaxEntriesPerConfig()); |
| |
| InitializeDB(); |
| |
| // Inject a test clock and initialize with the current time. |
| base::SimpleTestClock clock; |
| SetDBClock(&clock); |
| clock.SetNow(base::Time::Now()); |
| |
| // Append and verify read-back. |
| constexpr int kNumberOfStatsToAdd = 30; |
| EXPECT_GT(kNumberOfStatsToAdd, GetMaxEntriesPerConfig()); |
| VideoStatsEntry entry; |
| for (int i = 0; i < kNumberOfStatsToAdd; ++i) { |
| VideoStats stats(clock.Now().ToJsTimeIgnoringNull(), 240 + i, 6, |
| 7.2 + i % 3); |
| AppendStats(kDecodeStatsKeyVp9, stats); |
| // Start popping the last stats entry if the number of entries has reached |
| // the limit. |
| if (i >= GetMaxEntriesPerConfig()) { |
| entry.pop_back(); |
| } |
| entry.insert(entry.begin(), stats); |
| VerifyReadStats(kDecodeStatsKeyVp9, entry); |
| clock.Advance(base::Days(1)); |
| } |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, OutOfOrderTimestampClearsOldStats) { |
| InitializeDB(); |
| |
| // Inject a test clock and initialize with the current time. |
| base::SimpleTestClock clock; |
| SetDBClock(&clock); |
| clock.SetNow(base::Time::Now()); |
| |
| // Append and verify read-back. |
| constexpr int kNumberOfStatsToAdd = 5; |
| VideoStatsEntry entry; |
| for (int i = 0; i < kNumberOfStatsToAdd; ++i) { |
| VideoStats stats(clock.Now().ToJsTimeIgnoringNull(), 240 + i, 6, |
| 7.2 + i % 3); |
| AppendStats(kDecodeStatsKeyVp9, stats); |
| entry.insert(entry.begin(), stats); |
| VerifyReadStats(kDecodeStatsKeyVp9, entry); |
| clock.Advance(base::Days(1)); |
| } |
| |
| // Go back in time and add a new stats entry. |
| clock.Advance(-base::Days(20)); |
| VideoStats stats(clock.Now().ToJsTimeIgnoringNull(), 123, 5, 11.2); |
| AppendStats(kDecodeStatsKeyVp9, stats); |
| // Only the last appended stats should be in the database now. |
| entry = {stats}; |
| VerifyReadStats(kDecodeStatsKeyVp9, entry); |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, FailedWrite) { |
| InitializeDB(); |
| |
| // Expect the callback to indicate success = false when the write fails. |
| EXPECT_CALL(*this, MockAppendVideoStatsCb(false)); |
| |
| // Append stats, but fail the internal DB update. |
| stats_db_->AppendVideoStats( |
| kDecodeStatsKeyVp9, VideoStats(1234, 240, 6, 7.2), |
| base::BindOnce(&WebrtcVideoStatsDBImplTest::MockAppendVideoStatsCb, |
| base::Unretained(this))); |
| fake_db_->GetCallback(true); |
| fake_db_->UpdateCallback(false); |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, DiscardCorruptedDBData) { |
| InitializeDB(); |
| |
| // Inject a test clock and initialize with the current time. |
| base::SimpleTestClock clock; |
| SetDBClock(&clock); |
| clock.SetNow(base::Time::Now()); |
| |
| // Start with a proto that represents a valid uncorrupted and unexpired entry. |
| WebrtcVideoStatsEntryProto valid_proto; |
| WebrtcVideoStatsProto* valid_entry = valid_proto.add_stats(); |
| valid_entry->set_timestamp(clock.Now().ToJsTimeIgnoringNull()); |
| valid_entry->set_frames_processed(300); |
| valid_entry->set_key_frames_processed(8); |
| valid_entry->set_p99_processing_time_ms(11.3); |
| |
| // Append it and read it back without issue. |
| AppendToProtoDB(kEncodeStatsKeyVp9, &valid_proto); |
| VerifyReadStats( |
| kEncodeStatsKeyVp9, |
| {VideoStats{valid_entry->timestamp(), valid_entry->frames_processed(), |
| valid_entry->key_frames_processed(), |
| valid_entry->p99_processing_time_ms()}}); |
| |
| WebrtcVideoStatsEntryProto invalid_proto; |
| WebrtcVideoStatsProto* invalid_entry = invalid_proto.add_stats(); |
| // Invalid because number of frames processed is too low Verify |
| // you can't read it back (filtered for corruption). |
| *invalid_entry = *valid_entry; |
| invalid_entry->set_frames_processed(30); |
| AppendToProtoDB(kDecodeStatsKeyVp9, &invalid_proto); |
| VerifyEmptyStats(kDecodeStatsKeyVp9); |
| |
| // Invalid because number of frames processed is too high. Verify |
| // you can't read it back (filtered for corruption). |
| *invalid_entry = *valid_entry; |
| invalid_entry->set_frames_processed(1000000); |
| AppendToProtoDB(kDecodeStatsKeyVp9, &invalid_proto); |
| VerifyEmptyStats(kDecodeStatsKeyVp9); |
| |
| // Invalid because number of key frames is higher than number of frames |
| // processed. Verify you can't read it back (filtered for corruption). |
| *invalid_entry = *valid_entry; |
| invalid_entry->set_key_frames_processed(valid_entry->frames_processed() + 1); |
| AppendToProtoDB(kDecodeStatsKeyVp9, &invalid_proto); |
| VerifyEmptyStats(kDecodeStatsKeyVp9); |
| |
| // Invalid processing time. Verify |
| // you can't read it back (filtered for corruption). |
| *invalid_entry = *valid_entry; |
| invalid_entry->set_p99_processing_time_ms(-1.0); |
| AppendToProtoDB(kDecodeStatsKeyVp9, &invalid_proto); |
| VerifyEmptyStats(kDecodeStatsKeyVp9); |
| |
| *invalid_entry = *valid_entry; |
| invalid_entry->set_p99_processing_time_ms(20000.0); |
| AppendToProtoDB(kDecodeStatsKeyVp9, &invalid_proto); |
| VerifyEmptyStats(kDecodeStatsKeyVp9); |
| } |
| |
| TEST_F(WebrtcVideoStatsDBImplTest, WriteAndReadCollection) { |
| InitializeDB(); |
| |
| // Set test clock. |
| base::SimpleTestClock clock; |
| SetDBClock(&clock); |
| clock.SetNow(base::Time::Now()); |
| |
| // Append stats for multiple resolutions. |
| VideoStats stats1(clock.Now().ToJsTimeIgnoringNull(), 240, 6, 7.2); |
| VideoStatsEntry entry1{stats1}; |
| AppendStats(kDecodeStatsKeyVp9, stats1); |
| |
| VideoStats stats2(clock.Now().ToJsTimeIgnoringNull(), 360, 7, 9.2); |
| VideoStatsEntry entry2{stats2}; |
| AppendStats(kDecodeStatsKeyVp9FullHd, stats2); |
| |
| VideoStats stats3(clock.Now().ToJsTimeIgnoringNull(), 480, 11, 13.3); |
| VideoStatsEntry entry3{stats3}; |
| AppendStats(kDecodeStatsKeyVp94K, stats3); |
| |
| // Add elements that should not be returned. |
| VideoStats stats4(clock.Now().ToJsTimeIgnoringNull(), 490, 13, 15.3); |
| AppendStats(kEncodeStatsKeyVp9, stats4); |
| AppendStats(kDecodeStatsKeyVp9Hw, stats4); |
| |
| // Created the expected collection that should be returned. |
| WebrtcVideoStatsDB::VideoStatsCollection expected; |
| expected.insert({kDecodeStatsKeyVp9.pixels, entry1}); |
| expected.insert({kDecodeStatsKeyVp9FullHd.pixels, entry2}); |
| expected.insert({kDecodeStatsKeyVp94K.pixels, entry3}); |
| |
| // The same collection is returned for all keys that are associated to the |
| // collection. |
| VerifyReadStatsCollection(kDecodeStatsKeyVp9, expected); |
| VerifyReadStatsCollection(kDecodeStatsKeyVp9FullHd, expected); |
| VerifyReadStatsCollection(kDecodeStatsKeyVp94K, expected); |
| } |
| |
| } // namespace media |