// Copyright 2016 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 "net/nqe/observation_buffer.h" #include #include #include #include #include #include "base/macros.h" #include "base/test/simple_test_tick_clock.h" #include "base/time/time.h" #include "net/nqe/network_quality_estimator_params.h" #include "net/nqe/network_quality_observation.h" #include "net/nqe/network_quality_observation_source.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace nqe { namespace internal { namespace { // Verify that the buffer size is never exceeded. TEST(NetworkQualityObservationBufferTest, BoundedBuffer) { std::map variation_params; NetworkQualityEstimatorParams params(variation_params); base::SimpleTestTickClock tick_clock; tick_clock.Advance(base::TimeDelta::FromMinutes(1)); ObservationBuffer observation_buffer(¶ms, &tick_clock, 1.0, 1.0); const base::TimeTicks now = base::TimeTicks() + base::TimeDelta::FromSeconds(1); for (int i = 1; i <= 1000; ++i) { observation_buffer.AddObservation( Observation(i, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP)); // The number of entries should be at most the maximum buffer size. EXPECT_GE(300u, observation_buffer.Size()); } } // Verify that the percentiles are monotonically non-decreasing when a weight is // applied. TEST(NetworkQualityObservationBufferTest, GetPercentileWithWeights) { std::map variation_params; NetworkQualityEstimatorParams params(variation_params); base::SimpleTestTickClock tick_clock; tick_clock.Advance(base::TimeDelta::FromMinutes(1)); ObservationBuffer observation_buffer(¶ms, &tick_clock, 0.98, 1.0); const base::TimeTicks now = tick_clock.NowTicks(); for (int i = 1; i <= 100; ++i) { tick_clock.Advance(base::TimeDelta::FromSeconds(1)); observation_buffer.AddObservation( Observation(i, tick_clock.NowTicks(), INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP)); } EXPECT_EQ(100U, observation_buffer.Size()); int32_t result_lowest = INT32_MAX; int32_t result_highest = INT32_MIN; for (int i = 1; i <= 100; ++i) { size_t observations_count = 0; // Verify that i'th percentile is more than i-1'th percentile. absl::optional result_i = observation_buffer.GetPercentile( now, INT32_MIN, i, &observations_count); EXPECT_EQ(100u, observations_count); ASSERT_TRUE(result_i.has_value()); result_lowest = std::min(result_lowest, result_i.value()); result_highest = std::max(result_highest, result_i.value()); absl::optional result_i_1 = observation_buffer.GetPercentile( now, INT32_MIN, i - 1, &observations_count); EXPECT_EQ(100u, observations_count); ASSERT_TRUE(result_i_1.has_value()); EXPECT_LE(result_i_1.value(), result_i.value()); } EXPECT_LT(result_lowest, result_highest); } // Verifies that the percentiles are correctly computed. All observations have // the same timestamp. TEST(NetworkQualityObservationBufferTest, PercentileSameTimestamps) { std::map variation_params; NetworkQualityEstimatorParams params(variation_params); base::SimpleTestTickClock tick_clock; tick_clock.Advance(base::TimeDelta::FromMinutes(1)); ObservationBuffer buffer(¶ms, &tick_clock, 0.5, 1.0); ASSERT_EQ(0u, buffer.Size()); ASSERT_LT(0u, buffer.Capacity()); const base::TimeTicks now = tick_clock.NowTicks(); size_t observations_count = 0; // Percentiles should be unavailable when no observations are available. EXPECT_FALSE( buffer .GetPercentile(base::TimeTicks(), INT32_MIN, 50, &observations_count) .has_value()); EXPECT_EQ(0u, observations_count); // Insert samples from {1,2,3,..., 100}. First insert odd samples, then even // samples. This helps in verifying that the order of samples does not matter. for (int i = 1; i <= 99; i += 2) { buffer.AddObservation(Observation(i, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); EXPECT_TRUE(buffer.GetPercentile(base::TimeTicks(), INT32_MIN, 50, nullptr) .has_value()); ASSERT_EQ(static_cast(i / 2 + 1), buffer.Size()); } for (int i = 2; i <= 100; i += 2) { buffer.AddObservation(Observation(i, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); EXPECT_TRUE(buffer.GetPercentile(base::TimeTicks(), INT32_MIN, 50, nullptr) .has_value()); ASSERT_EQ(static_cast(i / 2 + 50), buffer.Size()); } ASSERT_EQ(100u, buffer.Size()); for (int i = 0; i <= 100; ++i) { // Checks if the difference between actual result and the computed result is // less than 1. This is required because computed percentiles may be // slightly different from what is expected due to floating point // computation errors and integer rounding off errors. absl::optional result = buffer.GetPercentile( base::TimeTicks(), INT32_MIN, i, &observations_count); EXPECT_EQ(100u, observations_count); EXPECT_TRUE(result.has_value()); EXPECT_NEAR(result.value(), i, 1.0); } EXPECT_FALSE( buffer .GetPercentile(now + base::TimeDelta::FromSeconds(1), INT32_MIN, 50, &observations_count) .has_value()); EXPECT_EQ(0u, observations_count); // Percentiles should be unavailable when no observations are available. buffer.Clear(); EXPECT_FALSE( buffer .GetPercentile(base::TimeTicks(), INT32_MIN, 50, &observations_count) .has_value()); EXPECT_EQ(0u, observations_count); } // Verifies that the percentiles are correctly computed. Observations have // different timestamps with half the observations being very old and the rest // of them being very recent. Percentiles should factor in recent observations // much more heavily than older samples. TEST(NetworkQualityObservationBufferTest, PercentileDifferentTimestamps) { std::map variation_params; NetworkQualityEstimatorParams params(variation_params); base::SimpleTestTickClock tick_clock; tick_clock.Advance(base::TimeDelta::FromMinutes(1)); ObservationBuffer buffer(¶ms, &tick_clock, 0.5, 1.0); const base::TimeTicks now = tick_clock.NowTicks(); const base::TimeTicks very_old = now - base::TimeDelta::FromDays(7); size_t observations_count; // Network quality should be unavailable when no observations are available. EXPECT_FALSE( buffer .GetPercentile(base::TimeTicks(), INT32_MIN, 50, &observations_count) .has_value()); EXPECT_EQ(0u, observations_count); // First 50 samples have very old timestamps. for (int i = 1; i <= 50; ++i) { buffer.AddObservation(Observation(i, very_old, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); } // Next 50 (i.e., from 51 to 100) have recent timestamps. for (int i = 51; i <= 100; ++i) { buffer.AddObservation(Observation(i, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); } // Older samples have very little weight. So, all percentiles are >= 51 // (lowest value among recent observations). for (int i = 1; i < 100; ++i) { // Checks if the difference between the two integers is less than 1. This is // required because computed percentiles may be slightly different from // what is expected due to floating point computation errors and integer // rounding off errors. absl::optional result = buffer.GetPercentile(very_old, INT32_MIN, i, &observations_count); EXPECT_TRUE(result.has_value()); EXPECT_NEAR(result.value(), 51 + 0.49 * i, 1); EXPECT_EQ(100u, observations_count); } EXPECT_FALSE(buffer.GetPercentile(now + base::TimeDelta::FromSeconds(1), INT32_MIN, 50, &observations_count)); EXPECT_EQ(0u, observations_count); } // Verifies that the percentiles are correctly computed. All observations have // same timestamp with half the observations taken at low RSSI, and half the // observations with high RSSI. Percentiles should be computed based on the // current RSSI and the RSSI of the observations. TEST(NetworkQualityObservationBufferTest, PercentileDifferentRSSI) { std::map variation_params; NetworkQualityEstimatorParams params(variation_params); base::SimpleTestTickClock tick_clock; tick_clock.Advance(base::TimeDelta::FromMinutes(1)); ObservationBuffer buffer(¶ms, &tick_clock, 1.0, 0.25); const base::TimeTicks now = tick_clock.NowTicks(); int32_t high_rssi = 4; int32_t low_rssi = 0; // Network quality should be unavailable when no observations are available. EXPECT_FALSE(buffer.GetPercentile(base::TimeTicks(), INT32_MIN, 50, nullptr) .has_value()); // First 50 samples have very low RSSI. for (int i = 1; i <= 50; ++i) { buffer.AddObservation( Observation(i, now, low_rssi, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); } // Next 50 (i.e., from 51 to 100) have high RSSI. for (int i = 51; i <= 100; ++i) { buffer.AddObservation(Observation(i, now, high_rssi, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); } // When the current RSSI is |high_rssi|, higher weight should be assigned // to observations that were taken at |high_rssi|. for (int i = 1; i < 100; ++i) { absl::optional result = buffer.GetPercentile(now, high_rssi, i, nullptr); EXPECT_TRUE(result.has_value()); EXPECT_NEAR(result.value(), 51 + 0.49 * i, 2); } // When the current RSSI is |low_rssi|, higher weight should be assigned // to observations that were taken at |low_rssi|. for (int i = 1; i < 100; ++i) { absl::optional result = buffer.GetPercentile(now, low_rssi, i, nullptr); EXPECT_TRUE(result.has_value()); EXPECT_NEAR(result.value(), i / 2, 2); } } // Verifies that the percentiles are correctly computed when some of the // observation sources are disallowed. All observations have the same timestamp. TEST(NetworkQualityObservationBufferTest, RemoveObservations) { std::map variation_params; NetworkQualityEstimatorParams params(variation_params); base::SimpleTestTickClock tick_clock; tick_clock.Advance(base::TimeDelta::FromMinutes(1)); ObservationBuffer buffer(¶ms, &tick_clock, 0.5, 1.0); const base::TimeTicks now = tick_clock.NowTicks(); // Insert samples from {1,2,3,..., 100}. First insert odd samples, then even // samples. This helps in verifying that the order of samples does not matter. for (int i = 1; i <= 99; i += 2) { buffer.AddObservation(Observation(i, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); } EXPECT_EQ(50u, buffer.Size()); // Add samples for TCP and QUIC observations which should not be taken into // account when computing the percentile. for (int i = 1; i <= 99; i += 2) { buffer.AddObservation(Observation(10000, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP)); buffer.AddObservation(Observation(10000, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC)); } EXPECT_EQ(150u, buffer.Size()); for (int i = 2; i <= 100; i += 2) { buffer.AddObservation(Observation(i, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); } EXPECT_EQ(200u, buffer.Size()); bool deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_MAX] = { false}; // Since all entries in |deleted_observation_sources| are set to false, no // observations should be deleted. buffer.RemoveObservationsWithSource(deleted_observation_sources); EXPECT_EQ(200u, buffer.Size()); // 50 TCP and 50 QUIC observations should be deleted. deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_TCP] = true; deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC] = true; buffer.RemoveObservationsWithSource(deleted_observation_sources); EXPECT_EQ(100u, buffer.Size()); for (int i = 0; i <= 100; ++i) { // Checks if the difference between the two integers is less than 1. This is // required because computed percentiles may be slightly different from // what is expected due to floating point computation errors and integer // rounding off errors. absl::optional result = buffer.GetPercentile(base::TimeTicks(), INT32_MIN, i, nullptr); EXPECT_TRUE(result.has_value()); EXPECT_NEAR(result.value(), i, 1); } deleted_observation_sources[NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP] = true; buffer.RemoveObservationsWithSource(deleted_observation_sources); EXPECT_EQ(0u, buffer.Size()); } TEST(NetworkQualityObservationBufferTest, TestGetMedianRTTSince) { std::map variation_params; NetworkQualityEstimatorParams params(variation_params); base::SimpleTestTickClock tick_clock; tick_clock.Advance(base::TimeDelta::FromMinutes(1)); ObservationBuffer buffer(¶ms, &tick_clock, 0.5, 1.0); base::TimeTicks now = tick_clock.NowTicks(); base::TimeTicks old = now - base::TimeDelta::FromMilliseconds(1); ASSERT_NE(old, now); // First sample has very old timestamp. buffer.AddObservation( Observation(1, old, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); buffer.AddObservation(Observation(100, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); const struct { base::TimeTicks start_timestamp; bool expect_network_quality_available; base::TimeDelta expected_url_request_rtt; } tests[] = { {now + base::TimeDelta::FromSeconds(10), false, base::TimeDelta::FromMilliseconds(0)}, {now, true, base::TimeDelta::FromMilliseconds(100)}, {now - base::TimeDelta::FromMicroseconds(500), true, base::TimeDelta::FromMilliseconds(100)}, }; for (const auto& test : tests) { absl::optional url_request_rtt = buffer.GetPercentile(test.start_timestamp, INT32_MIN, 50, nullptr); EXPECT_EQ(test.expect_network_quality_available, url_request_rtt.has_value()); if (test.expect_network_quality_available) { EXPECT_EQ(test.expected_url_request_rtt.InMillisecondsF(), url_request_rtt.value()); } } } } // namespace } // namespace internal } // namespace nqe } // namespace net