Chromium Code Reviews| Index: media/cast/logging/stats_event_subscriber_unittest.cc |
| diff --git a/media/cast/logging/stats_event_subscriber_unittest.cc b/media/cast/logging/stats_event_subscriber_unittest.cc |
| index 8f4d0c5271154d83ef98df8acb0beb03a40862f8..3d5f8f141784d82f8cb7a535c2e12d79482beb64 100644 |
| --- a/media/cast/logging/stats_event_subscriber_unittest.cc |
| +++ b/media/cast/logging/stats_event_subscriber_unittest.cc |
| @@ -4,169 +4,401 @@ |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| +#include "base/rand_util.h" |
| #include "base/test/simple_test_tick_clock.h" |
| #include "base/time/tick_clock.h" |
| #include "media/cast/cast_environment.h" |
| #include "media/cast/logging/logging_defines.h" |
| #include "media/cast/logging/stats_event_subscriber.h" |
| +#include "media/cast/test/fake_receiver_time_offset_estimator.h" |
| #include "media/cast/test/fake_single_thread_task_runner.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| +namespace { |
| +const int kReceiverOffsetSecs = 100; |
| +} |
| + |
| namespace media { |
| namespace cast { |
| class StatsEventSubscriberTest : public ::testing::Test { |
| protected: |
| StatsEventSubscriberTest() |
| - : testing_clock_(new base::SimpleTestTickClock()), |
| - task_runner_(new test::FakeSingleThreadTaskRunner(testing_clock_)), |
| + : sender_clock_(new base::SimpleTestTickClock()), |
| + task_runner_(new test::FakeSingleThreadTaskRunner(sender_clock_)), |
| cast_environment_(new CastEnvironment( |
| - scoped_ptr<base::TickClock>(testing_clock_).Pass(), |
| + scoped_ptr<base::TickClock>(sender_clock_).Pass(), |
| task_runner_, |
| task_runner_, |
| - task_runner_)) {} |
| + task_runner_)), |
| + fake_offset_estimator_( |
| + base::TimeDelta::FromSeconds(kReceiverOffsetSecs)) { |
| + receiver_clock_.Advance(base::TimeDelta::FromSeconds(kReceiverOffsetSecs)); |
| + cast_environment_->Logging()->AddRawEventSubscriber( |
| + &fake_offset_estimator_); |
| + } |
| virtual ~StatsEventSubscriberTest() { |
| if (subscriber_.get()) |
| cast_environment_->Logging()->RemoveRawEventSubscriber(subscriber_.get()); |
| + cast_environment_->Logging()->RemoveRawEventSubscriber( |
| + &fake_offset_estimator_); |
| + } |
| + |
| + void AdvanceClocks(base::TimeDelta delta) { |
| + sender_clock_->Advance(delta); |
| + receiver_clock_.Advance(delta); |
| } |
| void Init(EventMediaType event_media_type) { |
| DCHECK(!subscriber_.get()); |
| - subscriber_.reset(new StatsEventSubscriber(event_media_type)); |
| + subscriber_.reset(new StatsEventSubscriber( |
| + event_media_type, cast_environment_->Clock(), &fake_offset_estimator_)); |
| cast_environment_->Logging()->AddRawEventSubscriber(subscriber_.get()); |
| } |
| - base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment. |
| + base::SimpleTestTickClock* sender_clock_; // Owned by CastEnvironment. |
| + base::SimpleTestTickClock receiver_clock_; |
| scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_; |
| scoped_refptr<CastEnvironment> cast_environment_; |
| + test::FakeReceiverTimeOffsetEstimator fake_offset_estimator_; |
| scoped_ptr<StatsEventSubscriber> subscriber_; |
| }; |
| -TEST_F(StatsEventSubscriberTest, FrameStats) { |
| +TEST_F(StatsEventSubscriberTest, EmptyStats) { |
| + Init(VIDEO_EVENT); |
| + |
| + StatsEventSubscriber::StatsMap stats_map; |
| + subscriber_->GetStatsInternal(&stats_map); |
| + |
| + EXPECT_TRUE(stats_map.empty()); |
| +} |
| + |
| +TEST_F(StatsEventSubscriberTest, Capture) { |
| + Init(VIDEO_EVENT); |
| + |
| + uint32 rtp_timestamp = 0; |
| + uint32 frame_id = 0; |
| + int num_frames = 10; |
| + base::TimeTicks start_time = sender_clock_->NowTicks(); |
| + for (int i = 0; i < num_frames; i++) { |
| + cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(), |
| + kVideoFrameCaptured, |
| + rtp_timestamp, |
| + frame_id); |
| + |
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(30)); |
| + rtp_timestamp += 90; |
| + frame_id++; |
| + } |
| + |
| + base::TimeTicks end_time = sender_clock_->NowTicks(); |
| + |
| + StatsEventSubscriber::StatsMap stats_map; |
| + subscriber_->GetStatsInternal(&stats_map); |
| + |
| + StatsEventSubscriber::StatsMap::iterator it = |
| + stats_map.find(StatsEventSubscriber::CAPTURE_FPS); |
| + ASSERT_NE(it, stats_map.end()); |
| + |
| + base::TimeDelta duration = end_time - start_time; |
| + EXPECT_NEAR( |
| + it->second, |
| + static_cast<double>(num_frames) / duration.InMilliseconds() * 1000, |
|
miu
2014/04/18 00:02:03
Use InMillisecondsF(), not InMilliseconds() throug
imcheng
2014/04/18 18:20:27
Done.
|
| + 0.05); |
| +} |
| + |
| +TEST_F(StatsEventSubscriberTest, Encode) { |
| Init(VIDEO_EVENT); |
| + |
| uint32 rtp_timestamp = 0; |
| uint32 frame_id = 0; |
| int num_frames = 10; |
| - int frame_size = 123; |
| - int delay_base_ms = 10; |
| - base::TimeTicks now; |
| + base::TimeTicks start_time = sender_clock_->NowTicks(); |
| + int total_size = 0; |
| for (int i = 0; i < num_frames; i++) { |
| - now = testing_clock_->NowTicks(); |
| - cast_environment_->Logging()->InsertFrameEvent( |
| - now, kVideoFrameReceived, rtp_timestamp, frame_id); |
| - testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30)); |
| + int size = 1000 + base::RandInt(-100, 100); |
| + total_size += size; |
| + cast_environment_->Logging()->InsertFrameEventWithSize( |
| + sender_clock_->NowTicks(), |
| + kVideoFrameEncoded, |
| + rtp_timestamp, |
| + frame_id, |
| + size); |
| + |
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(30)); |
| + rtp_timestamp += 90; |
| + frame_id++; |
| + } |
| + |
| + base::TimeTicks end_time = sender_clock_->NowTicks(); |
| + |
| + StatsEventSubscriber::StatsMap stats_map; |
| + subscriber_->GetStatsInternal(&stats_map); |
| + |
| + StatsEventSubscriber::StatsMap::iterator it = |
| + stats_map.find(StatsEventSubscriber::ENCODE_FPS); |
| + ASSERT_NE(it, stats_map.end()); |
| + |
| + base::TimeDelta duration = end_time - start_time; |
| + EXPECT_NEAR( |
| + it->second, |
| + static_cast<double>(num_frames) / duration.InMilliseconds() * 1000, |
| + 0.05); |
| + |
| + it = stats_map.find(StatsEventSubscriber::ENCODE_KBPS); |
| + ASSERT_NE(it, stats_map.end()); |
| - cast_environment_->Logging()->InsertEncodedFrameEvent( |
| - now, kVideoFrameEncoded, rtp_timestamp, i, frame_size, true); |
| - testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30)); |
| + EXPECT_NEAR(it->second, |
| + static_cast<double>(total_size) / duration.InMilliseconds() * 8, |
| + 0.05); |
| +} |
| + |
| +TEST_F(StatsEventSubscriberTest, Decode) { |
| + Init(VIDEO_EVENT); |
| + |
| + uint32 rtp_timestamp = 0; |
| + uint32 frame_id = 0; |
| + int num_frames = 10; |
| + base::TimeTicks start_time = sender_clock_->NowTicks(); |
| + for (int i = 0; i < num_frames; i++) { |
| + cast_environment_->Logging()->InsertFrameEvent(receiver_clock_.NowTicks(), |
| + kVideoFrameDecoded, |
| + rtp_timestamp, |
| + frame_id); |
| + |
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(30)); |
| + rtp_timestamp += 90; |
| + frame_id++; |
| + } |
| + base::TimeTicks end_time = sender_clock_->NowTicks(); |
| + |
| + StatsEventSubscriber::StatsMap stats_map; |
| + subscriber_->GetStatsInternal(&stats_map); |
| + |
| + StatsEventSubscriber::StatsMap::iterator it = |
| + stats_map.find(StatsEventSubscriber::DECODE_FPS); |
| + ASSERT_NE(it, stats_map.end()); |
| + |
| + base::TimeDelta duration = end_time - start_time; |
| + EXPECT_NEAR( |
| + it->second, |
| + static_cast<double>(num_frames) / duration.InMilliseconds() * 1000, |
| + 0.05); |
| +} |
| + |
| +TEST_F(StatsEventSubscriberTest, PlayoutDelay) { |
| + Init(VIDEO_EVENT); |
| + |
| + uint32 rtp_timestamp = 0; |
| + uint32 frame_id = 0; |
| + int num_frames = 10; |
| + int total_delay_ms = 0; |
| + for (int i = 0; i < num_frames; i++) { |
| + int delay_ms = base::RandInt(-50, 50); |
| + base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_ms); |
| + total_delay_ms += delay_ms; |
| cast_environment_->Logging()->InsertFrameEventWithDelay( |
| - now, |
| + receiver_clock_.NowTicks(), |
| kVideoRenderDelay, |
| rtp_timestamp, |
| - i, |
| - base::TimeDelta::FromMilliseconds(i * delay_base_ms)); |
| - testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30)); |
| + frame_id, |
| + delay); |
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(30)); |
| rtp_timestamp += 90; |
| + frame_id++; |
| } |
| - // Verify stats. |
| - FrameStatsMap frame_stats; |
| - subscriber_->GetFrameStats(&frame_stats); |
| + StatsEventSubscriber::StatsMap stats_map; |
| + subscriber_->GetStatsInternal(&stats_map); |
| - // Size of stats equals the number of events. |
| - EXPECT_EQ(3u, frame_stats.size()); |
| - FrameStatsMap::const_iterator it = frame_stats.find(kVideoFrameReceived); |
| - ASSERT_TRUE(it != frame_stats.end()); |
| - EXPECT_EQ(num_frames, it->second.event_counter); |
| + StatsEventSubscriber::StatsMap::iterator it = |
| + stats_map.find(StatsEventSubscriber::AVG_PLAYOUT_DELAY_MS); |
| + ASSERT_NE(it, stats_map.end()); |
| - it = frame_stats.find(kVideoFrameEncoded); |
| - ASSERT_TRUE(it != frame_stats.end()); |
| + EXPECT_NEAR( |
| + it->second, static_cast<double>(total_delay_ms) / num_frames, 0.05); |
| +} |
| - EXPECT_EQ(num_frames * frame_size, static_cast<int>(it->second.sum_size)); |
| +TEST_F(StatsEventSubscriberTest, E2ELatency) { |
| + Init(VIDEO_EVENT); |
| + |
| + uint32 rtp_timestamp = 0; |
| + uint32 frame_id = 0; |
| + int num_frames = 10; |
| + int total_latency_ms = 0; |
| + for (int i = 0; i < num_frames; i++) { |
| + cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(), |
| + kVideoFrameCaptured, |
| + rtp_timestamp, |
| + frame_id); |
| - it = frame_stats.find(kVideoRenderDelay); |
| - ASSERT_TRUE(it != frame_stats.end()); |
| + int latency_ms = 100 + base::RandInt(-50, 50); |
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(latency_ms)); |
| + |
| + int delay_ms = base::RandInt(-50, 50); |
| + base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_ms); |
| + total_latency_ms += latency_ms + delay_ms; |
| + |
| + cast_environment_->Logging()->InsertFrameEventWithDelay( |
| + receiver_clock_.NowTicks(), |
| + kVideoRenderDelay, |
| + rtp_timestamp, |
| + frame_id, |
| + delay); |
| - EXPECT_EQ(0, it->second.min_delay.InMilliseconds()); |
| - EXPECT_EQ((num_frames - 1) * delay_base_ms, |
| - it->second.max_delay.InMilliseconds()); |
| - EXPECT_EQ((num_frames - 1) * num_frames / 2 * delay_base_ms, |
| - it->second.sum_delay.InMilliseconds()); |
| + rtp_timestamp += 90; |
| + frame_id++; |
| + } |
| + |
| + StatsEventSubscriber::StatsMap stats_map; |
| + subscriber_->GetStatsInternal(&stats_map); |
| + |
| + StatsEventSubscriber::StatsMap::iterator it = |
| + stats_map.find(StatsEventSubscriber::AVG_E2E_LATENCY_MS); |
| + ASSERT_NE(it, stats_map.end()); |
| + |
| + EXPECT_NEAR( |
| + it->second, static_cast<double>(total_latency_ms) / num_frames, 0.05); |
| } |
| -TEST_F(StatsEventSubscriberTest, PacketStats) { |
| - Init(AUDIO_EVENT); |
| +TEST_F(StatsEventSubscriberTest, Packets) { |
| + Init(VIDEO_EVENT); |
| + |
| uint32 rtp_timestamp = 0; |
| - uint32 frame_id = 0; |
| int num_packets = 10; |
| - int packet_size = 123; |
| - base::TimeTicks first_event_time = testing_clock_->NowTicks(); |
| - base::TimeTicks now; |
| + int num_latency_recorded_packets = 0; |
| + base::TimeTicks start_time = sender_clock_->NowTicks(); |
| + int total_size = 0; |
| + int retransmit_total_size = 0; |
| + int total_latency_ms = 0; |
| + int num_packets_sent = 0; |
| + int num_packets_retransmitted = 0; |
| + // Every 2nd packet will be retransmitted once. |
| + // Every 4th packet will be retransmitted twice. |
| + // Every 8th packet will be retransmitted 3 times. |
| for (int i = 0; i < num_packets; i++) { |
| - now = testing_clock_->NowTicks(); |
| - cast_environment_->Logging()->InsertPacketEvent(now, |
| - kAudioPacketSentToPacer, |
| + int size = 1000 + base::RandInt(-100, 100); |
| + total_size += size; |
| + |
| + cast_environment_->Logging()->InsertPacketEvent(sender_clock_->NowTicks(), |
| + kVideoPacketSentToNetwork, |
| rtp_timestamp, |
| - frame_id, |
| 0, |
| - 10, |
| - packet_size); |
| - testing_clock_->Advance(base::TimeDelta::FromMilliseconds(30)); |
| - } |
| + i, |
| + num_packets - 1, |
| + size); |
| + num_packets_sent++; |
| - PacketStatsMap stats_map; |
| - subscriber_->GetPacketStats(&stats_map); |
| + int latency_ms = 20 + base::RandInt(-10, 10); |
| + // Latency is only recorded for packets that aren't retransmitted. |
| + if (i % 2 != 0) { |
| + total_latency_ms += latency_ms; |
| + num_latency_recorded_packets++; |
| + } |
| - // Size of stats equals the number of event types. |
| - EXPECT_EQ(1u, stats_map.size()); |
| - PacketStatsMap::const_iterator it = stats_map.find(kAudioPacketSentToPacer); |
| - ASSERT_NE(stats_map.end(), it); |
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(latency_ms)); |
| - EXPECT_EQ(first_event_time, it->second.first_event_time); |
| - EXPECT_EQ(now, it->second.last_event_time); |
| - EXPECT_EQ(num_packets, it->second.event_counter); |
| - EXPECT_EQ(num_packets * packet_size, static_cast<int>(it->second.sum_size)); |
| -} |
| + base::TimeTicks received_time = receiver_clock_.NowTicks(); |
| -TEST_F(StatsEventSubscriberTest, GenericStats) { |
| - Init(OTHER_EVENT); |
| - int num_generic_events = 10; |
| - int value = 123; |
| - for (int i = 0; i < num_generic_events; i++) { |
| - cast_environment_->Logging()->InsertGenericEvent( |
| - testing_clock_->NowTicks(), kRttMs, value); |
| + // Retransmission 1. |
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(10)); |
| + if (i % 2 == 0) { |
| + cast_environment_->Logging()->InsertPacketEvent( |
| + receiver_clock_.NowTicks(), |
| + kVideoPacketRetransmitted, |
| + rtp_timestamp, |
| + 0, |
| + i, |
| + num_packets - 1, |
| + size); |
| + retransmit_total_size += size; |
| + num_packets_sent++; |
| + num_packets_retransmitted++; |
| + } |
| + |
| + // Retransmission 2. |
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(10)); |
| + if (i % 4 == 0) { |
| + cast_environment_->Logging()->InsertPacketEvent( |
| + receiver_clock_.NowTicks(), |
| + kVideoPacketRetransmitted, |
| + rtp_timestamp, |
| + 0, |
| + i, |
| + num_packets - 1, |
| + size); |
| + retransmit_total_size += size; |
| + num_packets_sent++; |
| + num_packets_retransmitted++; |
| + } |
| + |
| + // Retransmission 3. |
| + AdvanceClocks(base::TimeDelta::FromMilliseconds(10)); |
| + if (i % 8 == 0) { |
| + cast_environment_->Logging()->InsertPacketEvent( |
| + receiver_clock_.NowTicks(), |
| + kVideoPacketRetransmitted, |
| + rtp_timestamp, |
| + 0, |
| + i, |
| + num_packets - 1, |
| + size); |
| + retransmit_total_size += size; |
| + num_packets_sent++; |
| + num_packets_retransmitted++; |
| + } |
| + |
| + cast_environment_->Logging()->InsertPacketEvent(received_time, |
| + kVideoPacketReceived, |
| + rtp_timestamp, |
| + 0, |
| + i, |
| + num_packets - 1, |
| + size); |
| } |
| - GenericStatsMap stats_map; |
| - subscriber_->GetGenericStats(&stats_map); |
| + base::TimeTicks end_time = sender_clock_->NowTicks(); |
| + base::TimeDelta duration = end_time - start_time; |
| - EXPECT_EQ(1u, stats_map.size()); |
| - GenericStatsMap::const_iterator it = stats_map.find(kRttMs); |
| - ASSERT_NE(stats_map.end(), it); |
| + StatsEventSubscriber::StatsMap stats_map; |
| + subscriber_->GetStatsInternal(&stats_map); |
| - EXPECT_EQ(num_generic_events, it->second.event_counter); |
| - EXPECT_EQ(num_generic_events * value, it->second.sum); |
| - EXPECT_EQ(static_cast<uint64>(num_generic_events * value * value), |
| - it->second.sum_squared); |
| - EXPECT_LE(value, it->second.min); |
| - EXPECT_GE(value, it->second.max); |
| -} |
| + // Measure AVG_NETWORK_LATENCY_MS, TRANSMISSION_KBPS, RETRANSMISSION_KBPS, |
| + // and PACKET_LOSS_PERCENTAGE. |
| + StatsEventSubscriber::StatsMap::iterator it = |
| + stats_map.find(StatsEventSubscriber::AVG_NETWORK_LATENCY_MS); |
| + ASSERT_NE(it, stats_map.end()); |
| -TEST_F(StatsEventSubscriberTest, Reset) { |
| - Init(VIDEO_EVENT); |
| - cast_environment_->Logging()->InsertFrameEvent( |
| - testing_clock_->NowTicks(), kVideoFrameReceived, 0, 0); |
| + EXPECT_NEAR( |
| + it->second, |
| + static_cast<double>(total_latency_ms) / num_latency_recorded_packets, |
| + 0.05); |
| + |
| + it = stats_map.find(StatsEventSubscriber::TRANSMISSION_KBPS); |
| + ASSERT_NE(it, stats_map.end()); |
| + |
| + EXPECT_NEAR(it->second, |
| + static_cast<double>(total_size) / duration.InMilliseconds() * 8, |
| + 0.05); |
| + |
| + it = stats_map.find(StatsEventSubscriber::RETRANSMISSION_KBPS); |
| + ASSERT_NE(it, stats_map.end()); |
| + |
| + EXPECT_NEAR(it->second, |
| + static_cast<double>(retransmit_total_size) / |
| + duration.InMilliseconds() * 8, |
| + 0.05); |
| - FrameStatsMap frame_stats; |
| - subscriber_->GetFrameStats(&frame_stats); |
| - EXPECT_EQ(1u, frame_stats.size()); |
| + it = stats_map.find(StatsEventSubscriber::PACKET_LOSS_PERCENTAGE); |
| + ASSERT_NE(it, stats_map.end()); |
| - subscriber_->Reset(); |
| - subscriber_->GetFrameStats(&frame_stats); |
| - EXPECT_TRUE(frame_stats.empty()); |
| + EXPECT_NEAR( |
| + it->second, |
| + static_cast<double>(num_packets_retransmitted) / num_packets_sent * 100, |
| + 0.05); |
| } |
|
miu
2014/04/18 00:02:03
Given the issues of precision found in the .cc fil
imcheng
2014/04/18 18:20:27
I have converted all AdvanceClock args to have sub
|
| } // namespace cast |