summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortbansal <tbansal@chromium.org>2015-06-24 13:34:22 -0700
committerCommit bot <commit-bot@chromium.org>2015-06-24 20:35:11 +0000
commit11af83139c09b9a3fe17fabc8f32642527f0536f (patch)
tree39f8b0143317576331283a28a44fc55e3e92e3a1
parent160f331818cd9ac48ad9d769f23603dc18027927 (diff)
downloadchromium_src-11af83139c09b9a3fe17fabc8f32642527f0536f.zip
chromium_src-11af83139c09b9a3fe17fabc8f32642527f0536f.tar.gz
chromium_src-11af83139c09b9a3fe17fabc8f32642527f0536f.tar.bz2
Compute time weighted median network quality estimate.
Compute median network quality estimate from the stored observations. This is done by adding a general function GetEstimate(int percentile). Observations are assigned a weight based on their timestamp. Recent observations are given higher weight. BUG=472681 Review URL: https://codereview.chromium.org/1183203002 Cr-Commit-Position: refs/heads/master@{#335990}
-rw-r--r--components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc7
-rw-r--r--components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc16
-rw-r--r--net/base/network_quality.cc13
-rw-r--r--net/base/network_quality.h9
-rw-r--r--net/base/network_quality_estimator.cc102
-rw-r--r--net/base/network_quality_estimator.h69
-rw-r--r--net/base/network_quality_estimator_unittest.cc140
7 files changed, 339 insertions, 17 deletions
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
index 73f6521..853221f 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config.cc
@@ -399,9 +399,10 @@ bool DataReductionProxyConfig::IsNetworkQualityProhibitivelySlow(
network_quality_last_updated_ = base::TimeTicks::Now();
- // TODO(tbansal): Set |network_prohibitively_slow| based on medians.
- net::NetworkQuality network_quality =
- network_quality_estimator->GetPeakEstimate();
+ net::NetworkQuality network_quality;
+
+ if (!network_quality_estimator->GetEstimate(&network_quality))
+ return false;
// Network is prohibitvely slow if either the downlink bandwidth is too low
// or the RTT is too high.
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
index d9f84f4..dbdc85c 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_unittest.cc
@@ -1218,25 +1218,29 @@ TEST_F(DataReductionProxyConfigTest, LoFiStatusTransition) {
}
}
-// Overrides net::NetworkQualityEstimator::GetPeakEstimate() for testing
-// purposes.
+// Overrides net::NetworkQualityEstimator for testing.
class TestNetworkQualityEstimator : public net::NetworkQualityEstimator {
public:
TestNetworkQualityEstimator() : rtt_(base::TimeDelta()), kbps_(0) {}
~TestNetworkQualityEstimator() override {}
- net::NetworkQuality GetPeakEstimate() const override {
- return net::NetworkQuality(rtt_, kbps_);
+ bool GetEstimate(net::NetworkQuality* median) const override {
+ // |median| must not be null.
+ *median = net::NetworkQuality(rtt_, kbps_);
+ return true;
}
void SetRtt(base::TimeDelta rtt) { rtt_ = rtt; }
- void SetKbps(uint64_t kbps) { kbps_ = kbps; }
+ void SetKbps(int32_t kbps) { kbps_ = kbps; }
private:
+ // RTT of the network.
base::TimeDelta rtt_;
- uint64_t kbps_;
+
+ // Downstream throughput of the network.
+ int32_t kbps_;
};
TEST_F(DataReductionProxyConfigTest, AutoLoFiParams) {
diff --git a/net/base/network_quality.cc b/net/base/network_quality.cc
index a1a1ee0..90f282d 100644
--- a/net/base/network_quality.cc
+++ b/net/base/network_quality.cc
@@ -8,6 +8,9 @@
namespace net {
+NetworkQuality::NetworkQuality() : NetworkQuality(base::TimeDelta::Max(), 0) {
+}
+
NetworkQuality::NetworkQuality(const base::TimeDelta& rtt,
int32_t downstream_throughput_kbps)
: rtt_(rtt), downstream_throughput_kbps_(downstream_throughput_kbps) {
@@ -15,7 +18,17 @@ NetworkQuality::NetworkQuality(const base::TimeDelta& rtt,
DCHECK_GE(downstream_throughput_kbps_, 0);
}
+NetworkQuality::NetworkQuality(const NetworkQuality& other)
+ : NetworkQuality(other.rtt_, other.downstream_throughput_kbps_) {
+}
+
NetworkQuality::~NetworkQuality() {
}
+NetworkQuality& NetworkQuality::operator=(const NetworkQuality& other) {
+ rtt_ = other.rtt_;
+ downstream_throughput_kbps_ = other.downstream_throughput_kbps_;
+ return *this;
+}
+
} // namespace net
diff --git a/net/base/network_quality.h b/net/base/network_quality.h
index 328fb70..315616f 100644
--- a/net/base/network_quality.h
+++ b/net/base/network_quality.h
@@ -16,13 +16,16 @@ namespace net {
// estimated by the NetworkQualityEstimator.
class NET_EXPORT_PRIVATE NetworkQuality {
public:
+ NetworkQuality();
// |rtt| is the estimate of the round trip time.
// |downstream_throughput_kbps| is the estimate of the downstream throughput.
NetworkQuality(const base::TimeDelta& rtt,
int32_t downstream_throughput_kbps);
-
+ NetworkQuality(const NetworkQuality& other);
~NetworkQuality();
+ NetworkQuality& operator=(const NetworkQuality& other);
+
// Returns the estimate of the round trip time.
const base::TimeDelta& rtt() const { return rtt_; }
@@ -34,10 +37,10 @@ class NET_EXPORT_PRIVATE NetworkQuality {
private:
// Estimated round trip time.
- const base::TimeDelta rtt_;
+ base::TimeDelta rtt_;
// Estimated downstream throughput in Kbps.
- const int32_t downstream_throughput_kbps_;
+ int32_t downstream_throughput_kbps_;
};
} // namespace net
diff --git a/net/base/network_quality_estimator.cc b/net/base/network_quality_estimator.cc
index d181d2c..9131f8f 100644
--- a/net/base/network_quality_estimator.cc
+++ b/net/base/network_quality_estimator.cc
@@ -4,8 +4,12 @@
#include "net/base/network_quality_estimator.h"
+#include <float.h>
+#include <algorithm>
+#include <cmath>
#include <limits>
#include <string>
+#include <vector>
#include "base/logging.h"
#include "base/metrics/histogram.h"
@@ -20,6 +24,13 @@ namespace {
// Maximum number of observations that can be held in the ObservationBuffer.
const size_t kMaximumObservationsBufferSize = 500;
+// Half life (in seconds) for computing time weighted percentiles.
+// Every |kHalfLifeSeconds|, the weight of all observations reduces by half.
+// Lowering the half life would reduce the weight of older values faster.
+// TODO(tbansal): |kHalfLifeSeconds| should be configured through field trial
+// in the NetworkQualityEstimator constructor.
+const int kHalfLifeSeconds = 60;
+
} // namespace
namespace net {
@@ -39,6 +50,7 @@ NetworkQualityEstimator::NetworkQualityEstimator(
peak_kbps_since_last_connection_change_(0) {
static_assert(kMinRequestDurationMicroseconds > 0,
"Minimum request duration must be > 0");
+ static_assert(kHalfLifeSeconds > 0, "Half life duration must be > 0");
NetworkChangeNotifier::AddConnectionTypeObserver(this);
}
@@ -88,7 +100,6 @@ void NetworkQualityEstimator::NotifyDataReceived(
// headers were received.
base::TimeDelta observed_rtt = headers_received_time - request_start_time;
DCHECK_GE(observed_rtt, base::TimeDelta());
-
if (observed_rtt < fastest_rtt_since_last_connection_change_)
fastest_rtt_since_last_connection_change_ = observed_rtt;
@@ -111,7 +122,7 @@ void NetworkQualityEstimator::NotifyDataReceived(
since_request_start.InSecondsF();
DCHECK_GE(kbps_f, 0.0);
- // Check overflow errors. This may happen if the kbpsF is more than
+ // Check overflow errors. This may happen if the kbps_f is more than
// 2 * 10^9 (= 2000 Gbps).
if (kbps_f >= std::numeric_limits<int32_t>::max())
kbps_f = std::numeric_limits<int32_t>::max() - 1;
@@ -230,6 +241,15 @@ NetworkQuality NetworkQualityEstimator::GetPeakEstimate() const {
peak_kbps_since_last_connection_change_);
}
+bool NetworkQualityEstimator::GetEstimate(NetworkQuality* median) const {
+ if (kbps_observations_.Size() == 0 || rtt_msec_observations_.Size() == 0) {
+ *median = NetworkQuality();
+ return false;
+ }
+ *median = GetEstimate(50);
+ return true;
+}
+
size_t NetworkQualityEstimator::GetMaximumObservationBufferSizeForTests()
const {
return kMaximumObservationsBufferSize;
@@ -251,9 +271,12 @@ NetworkQualityEstimator::Observation::Observation(int32_t value,
NetworkQualityEstimator::Observation::~Observation() {
}
-NetworkQualityEstimator::ObservationBuffer::ObservationBuffer() {
+NetworkQualityEstimator::ObservationBuffer::ObservationBuffer()
+ : weight_multiplier_per_second_(exp(log(0.5) / kHalfLifeSeconds)) {
static_assert(kMaximumObservationsBufferSize > 0U,
"Minimum size of observation buffer must be > 0");
+ DCHECK_GE(weight_multiplier_per_second_, 0.0);
+ DCHECK_LE(weight_multiplier_per_second_, 1.0);
}
NetworkQualityEstimator::ObservationBuffer::~ObservationBuffer() {
@@ -279,4 +302,77 @@ void NetworkQualityEstimator::ObservationBuffer::Clear() {
DCHECK(observations_.empty());
}
+NetworkQuality NetworkQualityEstimator::GetEstimate(int percentile) const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_GE(percentile, 0);
+ DCHECK_LE(percentile, 100);
+ DCHECK_GT(kbps_observations_.Size(), 0U);
+ DCHECK_GT(rtt_msec_observations_.Size(), 0U);
+
+ // RTT observations are sorted by duration from shortest to longest, thus
+ // a higher percentile RTT will have a longer RTT than a lower percentile.
+ // Throughput observations are sorted by kbps from slowest to fastest,
+ // thus a higher percentile throughput will be faster than a lower one.
+ return NetworkQuality(base::TimeDelta::FromMilliseconds(
+ rtt_msec_observations_.GetPercentile(percentile)),
+ kbps_observations_.GetPercentile(100 - percentile));
+}
+
+void NetworkQualityEstimator::ObservationBuffer::ComputeWeightedObservations(
+ std::vector<WeightedObservation>& weighted_observations,
+ double* total_weight) const {
+ weighted_observations.clear();
+ double total_weight_observations = 0.0;
+ base::TimeTicks now = base::TimeTicks::Now();
+
+ for (const auto& observation : observations_) {
+ base::TimeDelta time_since_sample_taken = now - observation.timestamp;
+ double weight =
+ pow(weight_multiplier_per_second_, time_since_sample_taken.InSeconds());
+ weight = std::max(DBL_MIN, std::min(1.0, weight));
+
+ weighted_observations.push_back(
+ WeightedObservation(observation.value, weight));
+ total_weight_observations += weight;
+ }
+
+ // Sort the samples by value in ascending order.
+ std::sort(weighted_observations.begin(), weighted_observations.end());
+ *total_weight = total_weight_observations;
+}
+
+int32_t NetworkQualityEstimator::ObservationBuffer::GetPercentile(
+ int percentile) const {
+ DCHECK(!observations_.empty());
+
+ // Stores WeightedObservation in increasing order of value.
+ std::vector<WeightedObservation> weighted_observations;
+
+ // Total weight of all observations in |weighted_observations|.
+ double total_weight = 0.0;
+
+ ComputeWeightedObservations(weighted_observations, &total_weight);
+ DCHECK(!weighted_observations.empty());
+ DCHECK_GT(total_weight, 0.0);
+ DCHECK_EQ(observations_.size(), weighted_observations.size());
+
+ double desired_weight = percentile / 100.0 * total_weight;
+
+ double cumulative_weight_seen_so_far = 0.0;
+ for (const auto& weighted_observation : weighted_observations) {
+ cumulative_weight_seen_so_far += weighted_observation.weight;
+
+ // TODO(tbansal): Consider interpolating between observations.
+ if (cumulative_weight_seen_so_far >= desired_weight)
+ return weighted_observation.value;
+ }
+
+ // Computation may reach here due to floating point errors. This may happen
+ // if |percentile| was 100 (or close to 100), and |desired_weight| was
+ // slightly larger than |total_weight| (due to floating point errors).
+ // In this case, we return the highest |value| among all observations.
+ // This is same as value of the last observation in the sorted vector.
+ return weighted_observations.at(weighted_observations.size() - 1).value;
+}
+
} // namespace net
diff --git a/net/base/network_quality_estimator.h b/net/base/network_quality_estimator.h
index 6f3a467..025ce2c 100644
--- a/net/base/network_quality_estimator.h
+++ b/net/base/network_quality_estimator.h
@@ -41,6 +41,14 @@ class NET_EXPORT_PRIVATE NetworkQualityEstimator
// Virtualized for testing.
virtual NetworkQuality GetPeakEstimate() const;
+ // Sets |median| to the estimate of median network quality. The estimated
+ // quality is computed using a weighted median algorithm that assigns higher
+ // weight to the recent observations. |median| must not be nullptr. Returns
+ // true only if an estimate of the network quality is available (enough
+ // observations must be available to make an estimate). Virtualized for
+ // testing.
+ virtual bool GetEstimate(NetworkQuality* median) const;
+
// Notifies NetworkQualityEstimator that a response has been received.
// |cumulative_prefilter_bytes_read| is the count of the bytes received prior
// to applying filters (e.g. decompression, SDCH) from request creation time
@@ -57,6 +65,11 @@ class NET_EXPORT_PRIVATE NetworkQualityEstimator
TestPeakKbpsFastestRTTUpdates);
FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, TestAddObservation);
FRIEND_TEST_ALL_PREFIXES(URLRequestTestHTTP, NetworkQualityEstimator);
+ FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest,
+ PercentileSameTimestamps);
+ FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest,
+ PercentileDifferentTimestamps);
+ FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, ComputedPercentiles);
// Records the round trip time or throughput observation, along with the time
// the observation was made.
@@ -72,6 +85,32 @@ class NET_EXPORT_PRIVATE NetworkQualityEstimator
const base::TimeTicks timestamp;
};
+ // Holds an observation and its weight.
+ struct WeightedObservation {
+ WeightedObservation(int32_t value, double weight)
+ : value(value), weight(weight) {}
+ WeightedObservation(const WeightedObservation& other)
+ : WeightedObservation(other.value, other.weight) {}
+
+ WeightedObservation& operator=(const WeightedObservation& other) {
+ value = other.value;
+ weight = other.weight;
+ return *this;
+ }
+
+ // Required for sorting the samples in the ascending order of values.
+ bool operator<(const WeightedObservation& other) const {
+ return (value < other.value);
+ }
+
+ // Value of the sample.
+ int32_t value;
+
+ // Weight of the sample. This is computed based on how much time has passed
+ // since the sample was taken.
+ double weight;
+ };
+
// Stores observations sorted by time.
class ObservationBuffer {
public:
@@ -89,13 +128,31 @@ class NET_EXPORT_PRIVATE NetworkQualityEstimator
// Clears the observations stored in this buffer.
void Clear();
+ // Returns the |percentile| value of the observations in this buffer.
+ int32_t GetPercentile(int percentile) const;
+
private:
FRIEND_TEST_ALL_PREFIXES(NetworkQualityEstimatorTest, StoreObservations);
+ // Computes the weighted observations and stores them in
+ // |weighted_observations| sorted by ascending |WeightedObservation.value|.
+ // Sets |total_weight| to the total weight of all observations. Should be
+ // called only when there is at least one observation in the buffer.
+ void ComputeWeightedObservations(
+ std::vector<WeightedObservation>& weighted_observations,
+ double* total_weight) const;
+
// Holds observations sorted by time, with the oldest observation at the
// front of the queue.
std::deque<Observation> observations_;
+ // The factor by which the weight of an observation reduces every second.
+ // For example, if an observation is 6 seconds old, its weight would be:
+ // weight_multiplier_per_second_ ^ 6
+ // Calculated from |kHalfLifeSeconds| by solving the following equation:
+ // weight_multiplier_per_second_ ^ kHalfLifeSeconds = 0.5
+ const double weight_multiplier_per_second_;
+
DISALLOW_COPY_AND_ASSIGN(ObservationBuffer);
};
@@ -133,6 +190,14 @@ class NET_EXPORT_PRIVATE NetworkQualityEstimator
void OnConnectionTypeChanged(
NetworkChangeNotifier::ConnectionType type) override;
+ // Returns an estimate of network quality at the specified |percentile|.
+ // |percentile| must be between 0 and 100 (both inclusive) with higher
+ // percentiles indicating less performant networks. For example, if
+ // |percentile| is 90, then the network is expected to be faster than the
+ // returned estimate with 0.9 probability. Similarly, network is expected to
+ // be slower than the returned estimate with 0.1 probability.
+ NetworkQuality GetEstimate(int percentile) const;
+
// Determines if the requests to local host can be used in estimating the
// network quality. Set to true only for tests.
const bool allow_localhost_requests_;
@@ -159,10 +224,10 @@ class NET_EXPORT_PRIVATE NetworkQualityEstimator
// 2) The transfer time includes at least one RTT while no bytes are read.
int32_t peak_kbps_since_last_connection_change_;
- // Buffer that holds Kbps observations.
+ // Buffer that holds Kbps observations sorted by timestamp.
ObservationBuffer kbps_observations_;
- // Buffer that holds RTT (in milliseconds) observations.
+ // Buffer that holds RTT (in milliseconds) observations sorted by timestamp.
ObservationBuffer rtt_msec_observations_;
base::ThreadChecker thread_checker_;
diff --git a/net/base/network_quality_estimator_unittest.cc b/net/base/network_quality_estimator_unittest.cc
index d07c5b6..c1955ec4 100644
--- a/net/base/network_quality_estimator_unittest.cc
+++ b/net/base/network_quality_estimator_unittest.cc
@@ -120,4 +120,144 @@ TEST(NetworkQualityEstimatorTest, StoreObservations) {
}
}
+// http://crbug.com/504058
+#if !defined(OS_WIN)
+// Verifies that the percentiles are correctly computed. All observations have
+// the same timestamp. Kbps percentiles must be in decreasing order. RTT
+// percentiles must be in increasing order.
+TEST(NetworkQualityEstimatorTest, PercentileSameTimestamps) {
+ NetworkQualityEstimator estimator;
+ base::TimeTicks now = base::TimeTicks::Now();
+
+ // Network quality should be unavailable when no observations are available.
+ NetworkQuality network_quality;
+ EXPECT_FALSE(estimator.GetEstimate(&network_quality));
+
+ // 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) {
+ estimator.kbps_observations_.AddObservation(
+ NetworkQualityEstimator::Observation(i, now));
+ estimator.rtt_msec_observations_.AddObservation(
+ NetworkQualityEstimator::Observation(i, now));
+ EXPECT_TRUE(estimator.GetEstimate(&network_quality));
+ }
+
+ for (int i = 2; i <= 100; i += 2) {
+ estimator.kbps_observations_.AddObservation(
+ NetworkQualityEstimator::Observation(i, now));
+ estimator.rtt_msec_observations_.AddObservation(
+ NetworkQualityEstimator::Observation(i, now));
+ EXPECT_TRUE(estimator.GetEstimate(&network_quality));
+ }
+
+ 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.
+ EXPECT_NEAR(estimator.GetEstimate(i).downstream_throughput_kbps(), 100 - i,
+ 1);
+ EXPECT_NEAR(estimator.GetEstimate(i).rtt().InMilliseconds(), i, 1);
+ }
+
+ EXPECT_TRUE(estimator.GetEstimate(&network_quality));
+ // |network_quality| should be equal to the 50 percentile value.
+ EXPECT_EQ(estimator.GetEstimate(50).downstream_throughput_kbps(),
+ network_quality.downstream_throughput_kbps());
+ EXPECT_EQ(estimator.GetEstimate(50).rtt(), network_quality.rtt());
+}
+
+// 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. Kbps percentiles must be in decreasing
+// order. RTT percentiles must be in increasing order.
+TEST(NetworkQualityEstimatorTest, PercentileDifferentTimestamps) {
+ NetworkQualityEstimator estimator;
+ base::TimeTicks now = base::TimeTicks::Now();
+ base::TimeTicks very_old = base::TimeTicks::UnixEpoch();
+
+ // First 50 samples have very old timestamp.
+ for (int i = 1; i <= 50; ++i) {
+ estimator.kbps_observations_.AddObservation(
+ NetworkQualityEstimator::Observation(i, very_old));
+ estimator.rtt_msec_observations_.AddObservation(
+ NetworkQualityEstimator::Observation(i, very_old));
+ }
+
+ // Next 50 (i.e., from 51 to 100) have recent timestamp.
+ for (int i = 51; i <= 100; ++i) {
+ estimator.kbps_observations_.AddObservation(
+ NetworkQualityEstimator::Observation(i, now));
+ estimator.rtt_msec_observations_.AddObservation(
+ NetworkQualityEstimator::Observation(i, now));
+ }
+
+ // 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.
+ EXPECT_NEAR(estimator.GetEstimate(i).downstream_throughput_kbps(),
+ 51 + 0.49 * (100 - i), 1);
+ EXPECT_NEAR(estimator.GetEstimate(i).rtt().InMilliseconds(), 51 + 0.49 * i,
+ 1);
+ }
+}
+#endif // !defined(OS_WIN)
+
+// This test notifies NetworkQualityEstimator of received data. Next,
+// throughput and RTT percentiles are checked for correctness by doing simple
+// verifications.
+TEST(NetworkQualityEstimatorTest, ComputedPercentiles) {
+ net::test_server::EmbeddedTestServer embedded_test_server;
+ embedded_test_server.ServeFilesFromDirectory(
+ base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
+ ASSERT_TRUE(embedded_test_server.InitializeAndWaitUntilReady());
+
+ NetworkQualityEstimator estimator(true, true);
+ NetworkQuality network_quality = estimator.GetPeakEstimate();
+ EXPECT_EQ(network_quality.rtt(), base::TimeDelta::Max());
+ EXPECT_EQ(network_quality.downstream_throughput_kbps(), 0);
+
+ TestDelegate test_delegate;
+ TestURLRequestContext context(false);
+
+ uint64 min_transfer_size_in_bytes =
+ NetworkQualityEstimator::kMinTransferSizeInBytes;
+
+ // Number of observations are more than the maximum buffer size.
+ for (size_t i = 0;
+ i < estimator.GetMaximumObservationBufferSizeForTests() + 100U; ++i) {
+ scoped_ptr<URLRequest> request(
+ context.CreateRequest(embedded_test_server.GetURL("/echo.html"),
+ DEFAULT_PRIORITY, &test_delegate));
+ request->Start();
+ base::RunLoop().Run();
+
+ // Use different number of bytes to create variation.
+ estimator.NotifyDataReceived(*request, min_transfer_size_in_bytes + i * 100,
+ min_transfer_size_in_bytes + i * 100);
+ }
+
+ // Verify the percentiles through simple tests.
+ for (int i = 0; i <= 100; ++i) {
+ EXPECT_GT(estimator.GetEstimate(i).downstream_throughput_kbps(), 0);
+ EXPECT_LT(estimator.GetEstimate(i).rtt(), base::TimeDelta::Max());
+
+ if (i != 0) {
+ // Throughput percentiles are in decreasing order.
+ EXPECT_LE(estimator.GetEstimate(i).downstream_throughput_kbps(),
+ estimator.GetEstimate(i - 1).downstream_throughput_kbps());
+
+ // RTT percentiles are in increasing order.
+ EXPECT_GE(estimator.GetEstimate(i).rtt(),
+ estimator.GetEstimate(i - 1).rtt());
+ }
+ }
+}
+
} // namespace net