blob: 87de5f174485ae3b7c27a8ca8be57e374dd8a9de [file] [log] [blame]
// Copyright 2015 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/network_quality_estimator.h"
#include <stddef.h>
#include <stdint.h>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/metrics/histogram_samples.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/histogram_tester.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "net/base/load_flags.h"
#include "net/base/network_change_notifier.h"
#include "net/http/http_status_code.h"
#include "net/nqe/external_estimate_provider.h"
#include "net/nqe/network_quality_observation.h"
#include "net/nqe/network_quality_observation_source.h"
#include "net/nqe/observation_buffer.h"
#include "net/socket/socket_performance_watcher.h"
#include "net/socket/socket_performance_watcher_factory.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace net {
namespace {
// Helps in setting the current network type and id.
class TestNetworkQualityEstimator : public NetworkQualityEstimator {
public:
TestNetworkQualityEstimator(
const std::map<std::string, std::string>& variation_params,
std::unique_ptr<net::ExternalEstimateProvider> external_estimate_provider)
: TestNetworkQualityEstimator(std::move(external_estimate_provider),
variation_params,
true,
true) {}
TestNetworkQualityEstimator(
std::unique_ptr<net::ExternalEstimateProvider> external_estimate_provider,
const std::map<std::string, std::string>& variation_params,
bool allow_local_host_requests_for_tests,
bool allow_smaller_responses_for_tests)
: NetworkQualityEstimator(std::move(external_estimate_provider),
variation_params,
allow_local_host_requests_for_tests,
allow_smaller_responses_for_tests),
effective_connection_type_set_(false),
effective_connection_type_(EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
current_network_type_(NetworkChangeNotifier::CONNECTION_UNKNOWN),
accuracy_recording_intervals_set_(false),
http_rtt_set_(false),
recent_http_rtt_set_(false),
transport_rtt_set_(false),
recent_transport_rtt_set_(false),
downlink_throughput_kbps_set_(false) {
// Set up embedded test server.
embedded_test_server_.ServeFilesFromDirectory(
base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
EXPECT_TRUE(embedded_test_server_.Start());
embedded_test_server_.RegisterRequestHandler(base::Bind(
&TestNetworkQualityEstimator::HandleRequest, base::Unretained(this)));
}
explicit TestNetworkQualityEstimator(
const std::map<std::string, std::string>& variation_params)
: TestNetworkQualityEstimator(
variation_params,
std::unique_ptr<ExternalEstimateProvider>()) {}
~TestNetworkQualityEstimator() override {}
// Overrides the current network type and id.
// Notifies network quality estimator of change in connection.
void SimulateNetworkChangeTo(NetworkChangeNotifier::ConnectionType type,
const std::string& network_id) {
current_network_type_ = type;
current_network_id_ = network_id;
OnConnectionTypeChanged(type);
}
// Called by embedded server when a HTTP request is received.
std::unique_ptr<test_server::HttpResponse> HandleRequest(
const test_server::HttpRequest& request) {
std::unique_ptr<test_server::BasicHttpResponse> http_response(
new test_server::BasicHttpResponse());
http_response->set_code(HTTP_OK);
http_response->set_content("hello");
http_response->set_content_type("text/plain");
return std::move(http_response);
}
// Returns a GURL hosted at embedded test server.
const GURL GetEchoURL() const {
return embedded_test_server_.GetURL("/echo.html");
}
void set_effective_connection_type(EffectiveConnectionType type) {
effective_connection_type_set_ = true;
effective_connection_type_ = type;
}
// Returns the effective connection type that was set using
// |set_effective_connection_type|. If connection type has not been set, then
// the base implementation is called.
EffectiveConnectionType GetEffectiveConnectionType() const override {
if (effective_connection_type_set_)
return effective_connection_type_;
return NetworkQualityEstimator::GetEffectiveConnectionType();
}
void set_http_rtt(const base::TimeDelta& http_rtt) {
http_rtt_set_ = true;
http_rtt_ = http_rtt;
}
// Returns the HTTP RTT that was set using |set_http_rtt|. If the HTTP RTT has
// not been set, then the base implementation is called.
bool GetHttpRTTEstimate(base::TimeDelta* rtt) const override {
if (http_rtt_set_) {
*rtt = http_rtt_;
return true;
}
return NetworkQualityEstimator::GetHttpRTTEstimate(rtt);
}
void set_recent_http_rtt(const base::TimeDelta& recent_http_rtt) {
recent_http_rtt_set_ = true;
recent_http_rtt_ = recent_http_rtt;
}
// Returns the recent HTTP RTT that was set using |set_recent_http_rtt|. If
// the recent HTTP RTT has not been set, then the base implementation is
// called.
bool GetRecentHttpRTTMedian(const base::TimeTicks& start_time,
base::TimeDelta* rtt) const override {
if (recent_http_rtt_set_) {
*rtt = recent_http_rtt_;
return true;
}
return NetworkQualityEstimator::GetRecentHttpRTTMedian(start_time, rtt);
}
void set_transport_rtt(const base::TimeDelta& transport_rtt) {
transport_rtt_set_ = true;
transport_rtt_ = transport_rtt;
}
// Returns the transport RTT that was set using |set_transport_rtt|. If the
// transport RTT has not been set, then the base implementation is called.
bool GetTransportRTTEstimate(base::TimeDelta* rtt) const override {
if (transport_rtt_set_) {
*rtt = transport_rtt_;
return true;
}
return NetworkQualityEstimator::GetTransportRTTEstimate(rtt);
}
void set_recent_transport_rtt(const base::TimeDelta& recent_transport_rtt) {
recent_transport_rtt_set_ = true;
recent_transport_rtt_ = recent_transport_rtt;
}
// Returns the recent transport RTT that was set using
// |set_recent_transport_rtt|. If the recent transport RTT has not been set,
// then the base implementation is called.
bool GetRecentTransportRTTMedian(const base::TimeTicks& start_time,
base::TimeDelta* rtt) const override {
if (recent_transport_rtt_set_) {
*rtt = recent_transport_rtt_;
return true;
}
return NetworkQualityEstimator::GetRecentTransportRTTMedian(start_time,
rtt);
}
void set_downlink_throughput_kbps(int32_t downlink_throughput_kbps) {
downlink_throughput_kbps_set_ = true;
downlink_throughput_kbps_ = downlink_throughput_kbps;
}
// Returns the downlink throughput that was set using
// |set_downlink_throughput_kbps|. If the downlink throughput has not been
// set, then the base implementation is called.
bool GetDownlinkThroughputKbpsEstimate(int32_t* kbps) const override {
if (downlink_throughput_kbps_set_) {
*kbps = downlink_throughput_kbps_;
return true;
}
return NetworkQualityEstimator::GetDownlinkThroughputKbpsEstimate(kbps);
}
// Returns the downlink throughput that was set using
// |set_downlink_throughput_kbps|. If the downlink throughput has not been
// set, then the base implementation is called.
bool GetRecentMedianDownlinkThroughputKbps(const base::TimeTicks& start_time,
int32_t* kbps) const override {
if (downlink_throughput_kbps_set_) {
*kbps = downlink_throughput_kbps_;
return true;
}
return NetworkQualityEstimator::GetRecentMedianDownlinkThroughputKbps(
start_time, kbps);
}
void SetAccuracyRecordingIntervals(
const std::vector<base::TimeDelta>& accuracy_recording_intervals) {
accuracy_recording_intervals_set_ = true;
accuracy_recording_intervals_ = accuracy_recording_intervals;
}
const std::vector<base::TimeDelta>& GetAccuracyRecordingIntervals()
const override {
if (accuracy_recording_intervals_set_)
return accuracy_recording_intervals_;
return NetworkQualityEstimator::GetAccuracyRecordingIntervals();
}
using NetworkQualityEstimator::SetTickClockForTesting;
using NetworkQualityEstimator::ReadCachedNetworkQualityEstimate;
using NetworkQualityEstimator::OnConnectionTypeChanged;
private:
// NetworkQualityEstimator implementation that returns the overridden
// network
// id (instead of invoking platform APIs).
NetworkQualityEstimator::NetworkID GetCurrentNetworkID() const override {
return NetworkQualityEstimator::NetworkID(current_network_type_,
current_network_id_);
}
bool effective_connection_type_set_;
EffectiveConnectionType effective_connection_type_;
NetworkChangeNotifier::ConnectionType current_network_type_;
std::string current_network_id_;
bool accuracy_recording_intervals_set_;
std::vector<base::TimeDelta> accuracy_recording_intervals_;
bool http_rtt_set_;
base::TimeDelta http_rtt_;
bool recent_http_rtt_set_;
base::TimeDelta recent_http_rtt_;
bool transport_rtt_set_;
base::TimeDelta transport_rtt_;
bool recent_transport_rtt_set_;
base::TimeDelta recent_transport_rtt_;
bool downlink_throughput_kbps_set_;
int32_t downlink_throughput_kbps_;
// Embedded server used for testing.
EmbeddedTestServer embedded_test_server_;
DISALLOW_COPY_AND_ASSIGN(TestNetworkQualityEstimator);
};
class TestEffectiveConnectionTypeObserver
: public NetworkQualityEstimator::EffectiveConnectionTypeObserver {
public:
std::vector<NetworkQualityEstimator::EffectiveConnectionType>&
effective_connection_types() {
return effective_connection_types_;
}
// EffectiveConnectionTypeObserver implementation:
void OnEffectiveConnectionTypeChanged(
NetworkQualityEstimator::EffectiveConnectionType type) override {
effective_connection_types_.push_back(type);
}
private:
std::vector<NetworkQualityEstimator::EffectiveConnectionType>
effective_connection_types_;
};
class TestRTTObserver : public NetworkQualityEstimator::RTTObserver {
public:
struct Observation {
Observation(int32_t ms,
const base::TimeTicks& ts,
NetworkQualityObservationSource src)
: rtt_ms(ms), timestamp(ts), source(src) {}
int32_t rtt_ms;
base::TimeTicks timestamp;
NetworkQualityObservationSource source;
};
std::vector<Observation>& observations() { return observations_; }
// RttObserver implementation:
void OnRTTObservation(int32_t rtt_ms,
const base::TimeTicks& timestamp,
NetworkQualityObservationSource source) override {
observations_.push_back(Observation(rtt_ms, timestamp, source));
}
private:
std::vector<Observation> observations_;
};
class TestThroughputObserver
: public NetworkQualityEstimator::ThroughputObserver {
public:
struct Observation {
Observation(int32_t kbps,
const base::TimeTicks& ts,
NetworkQualityObservationSource src)
: throughput_kbps(kbps), timestamp(ts), source(src) {}
int32_t throughput_kbps;
base::TimeTicks timestamp;
NetworkQualityObservationSource source;
};
std::vector<Observation>& observations() { return observations_; }
// ThroughputObserver implementation:
void OnThroughputObservation(
int32_t throughput_kbps,
const base::TimeTicks& timestamp,
NetworkQualityObservationSource source) override {
observations_.push_back(Observation(throughput_kbps, timestamp, source));
}
private:
std::vector<Observation> observations_;
};
} // namespace
TEST(NetworkQualityEstimatorTest, TestKbpsRTTUpdates) {
base::HistogramTester histogram_tester;
// Enable requests to local host to be used for network quality estimation.
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
base::TimeDelta rtt;
int32_t kbps;
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
TestDelegate test_delegate;
TestURLRequestContext context(true);
context.set_network_quality_estimator(&estimator);
context.Init();
std::unique_ptr<URLRequest> request(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
request->Start();
base::RunLoop().Run();
// Both RTT and downstream throughput should be updated.
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
EXPECT_FALSE(estimator.GetTransportRTTEstimate(&rtt));
// Check UMA histograms.
histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 0);
histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 0);
histogram_tester.ExpectUniqueSample(
"NQE.MainFrame.EffectiveConnectionType.Unknown",
NetworkQualityEstimator::EffectiveConnectionType::
EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
1);
std::unique_ptr<URLRequest> request2(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request2->SetLoadFlags(request2->load_flags() | LOAD_MAIN_FRAME);
request2->Start();
base::RunLoop().Run();
histogram_tester.ExpectTotalCount(
"NQE.MainFrame.EffectiveConnectionType.Unknown", 2);
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1");
histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.RatioMedianRTT.WiFi", 0);
histogram_tester.ExpectTotalCount("NQE.RTT.Percentile0.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.RTT.Percentile10.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.RTT.Percentile50.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.RTT.Percentile90.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.RTT.Percentile100.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.TransportRTT.Percentile50.Unknown", 0);
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
// Verify that metrics are logged correctly on main-frame requests.
histogram_tester.ExpectTotalCount("NQE.MainFrame.RTT.Percentile50.Unknown",
1);
histogram_tester.ExpectTotalCount(
"NQE.MainFrame.TransportRTT.Percentile50.Unknown", 0);
histogram_tester.ExpectTotalCount("NQE.MainFrame.Kbps.Percentile50.Unknown",
1);
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, std::string());
histogram_tester.ExpectTotalCount("NQE.PeakKbps.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.FastestRTT.Unknown", 1);
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
std::unique_ptr<URLRequest> request3(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request3->SetLoadFlags(request2->load_flags() | LOAD_MAIN_FRAME);
request3->Start();
base::RunLoop().Run();
histogram_tester.ExpectUniqueSample(
"NQE.MainFrame.EffectiveConnectionType.WiFi",
NetworkQualityEstimator::EffectiveConnectionType::
EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
1);
}
TEST(NetworkQualityEstimatorTest, StoreObservations) {
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
base::TimeDelta rtt;
int32_t kbps;
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
TestDelegate test_delegate;
TestURLRequestContext context(true);
context.set_network_quality_estimator(&estimator);
context.Init();
// Push more observations than the maximum buffer size.
const size_t kMaxObservations = 1000;
for (size_t i = 0; i < kMaxObservations; ++i) {
std::unique_ptr<URLRequest> request(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request->Start();
base::RunLoop().Run();
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
}
// Verify that the stored observations are cleared on network change.
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-2");
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
}
// This test notifies NetworkQualityEstimator of received data. Next,
// throughput and RTT percentiles are checked for correctness by doing simple
// verifications.
TEST(NetworkQualityEstimatorTest, ComputedPercentiles) {
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
std::vector<NetworkQualityObservationSource> disallowed_observation_sources;
disallowed_observation_sources.push_back(
NETWORK_QUALITY_OBSERVATION_SOURCE_TCP);
disallowed_observation_sources.push_back(
NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC);
EXPECT_EQ(nqe::internal::InvalidRTT(),
estimator.GetRTTEstimateInternal(disallowed_observation_sources,
base::TimeTicks(), 100));
EXPECT_EQ(nqe::internal::kInvalidThroughput,
estimator.GetDownlinkThroughputKbpsEstimateInternal(
base::TimeTicks(), 100));
TestDelegate test_delegate;
TestURLRequestContext context(true);
context.set_network_quality_estimator(&estimator);
context.Init();
// Number of observations are more than the maximum buffer size.
for (size_t i = 0; i < 1000U; ++i) {
std::unique_ptr<URLRequest> request(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request->Start();
base::RunLoop().Run();
}
// Verify the percentiles through simple tests.
for (int i = 0; i <= 100; ++i) {
EXPECT_GT(estimator.GetDownlinkThroughputKbpsEstimateInternal(
base::TimeTicks(), i),
0);
EXPECT_LT(estimator.GetRTTEstimateInternal(disallowed_observation_sources,
base::TimeTicks(), i),
base::TimeDelta::Max());
if (i != 0) {
// Throughput percentiles are in decreasing order.
EXPECT_LE(estimator.GetDownlinkThroughputKbpsEstimateInternal(
base::TimeTicks(), i),
estimator.GetDownlinkThroughputKbpsEstimateInternal(
base::TimeTicks(), i - 1));
// RTT percentiles are in increasing order.
EXPECT_GE(estimator.GetRTTEstimateInternal(disallowed_observation_sources,
base::TimeTicks(), i),
estimator.GetRTTEstimateInternal(disallowed_observation_sources,
base::TimeTicks(), i - 1));
}
}
}
TEST(NetworkQualityEstimatorTest, ObtainOperatingParams) {
std::map<std::string, std::string> variation_params;
variation_params["Unknown.DefaultMedianKbps"] = "100";
variation_params["WiFi.DefaultMedianKbps"] = "200";
variation_params["2G.DefaultMedianKbps"] = "300";
variation_params["Unknown.DefaultMedianRTTMsec"] = "1000";
variation_params["WiFi.DefaultMedianRTTMsec"] = "2000";
// Negative variation value should not be used.
variation_params["2G.DefaultMedianRTTMsec"] = "-5";
TestNetworkQualityEstimator estimator(variation_params);
base::TimeDelta rtt;
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
int32_t kbps;
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
EXPECT_EQ(100, kbps);
EXPECT_EQ(base::TimeDelta::FromMilliseconds(1000), rtt);
EXPECT_FALSE(estimator.GetTransportRTTEstimate(&rtt));
// Simulate network change to Wi-Fi.
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1");
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
EXPECT_EQ(200, kbps);
EXPECT_EQ(base::TimeDelta::FromMilliseconds(2000), rtt);
EXPECT_FALSE(estimator.GetTransportRTTEstimate(&rtt));
// Peak network quality should not be affected by the network quality
// estimator field trial.
EXPECT_EQ(nqe::internal::InvalidRTT(),
estimator.peak_network_quality_.http_rtt());
EXPECT_EQ(nqe::internal::kInvalidThroughput,
estimator.peak_network_quality_.downstream_throughput_kbps());
// Simulate network change to 2G. Only the Kbps default estimate should be
// available.
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-2");
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
EXPECT_EQ(300, kbps);
// Simulate network change to 3G. Default estimates should be unavailable.
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-3");
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
}
TEST(NetworkQualityEstimatorTest, ObtainAlgorithmToUseFromParams) {
const struct {
bool set_variation_param;
std::string algorithm;
NetworkQualityEstimator::EffectiveConnectionTypeAlgorithm
expected_algorithm;
} tests[] = {
{false, "", NetworkQualityEstimator::EffectiveConnectionTypeAlgorithm::
HTTP_RTT_AND_DOWNSTREAM_THROUGHOUT},
{true, "", NetworkQualityEstimator::EffectiveConnectionTypeAlgorithm::
HTTP_RTT_AND_DOWNSTREAM_THROUGHOUT},
{true, "HttpRTTAndDownstreamThroughput",
NetworkQualityEstimator::EffectiveConnectionTypeAlgorithm::
HTTP_RTT_AND_DOWNSTREAM_THROUGHOUT},
};
for (const auto& test : tests) {
std::map<std::string, std::string> variation_params;
if (test.set_variation_param)
variation_params["effective_connection_type_algorithm"] = test.algorithm;
TestNetworkQualityEstimator estimator(variation_params);
EXPECT_EQ(test.expected_algorithm,
estimator.effective_connection_type_algorithm_)
<< test.algorithm;
}
}
// Tests that |GetEffectiveConnectionType| returns correct connection type when
// no variation params are specified.
TEST(NetworkQualityEstimatorTest, ObtainThresholdsNone) {
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
// Simulate the connection type as Wi-Fi so that GetEffectiveConnectionType
// does not return Offline if the device is offline.
estimator.SimulateNetworkChangeTo(NetworkChangeNotifier::CONNECTION_WIFI,
"test");
const struct {
int32_t rtt_msec;
NetworkQualityEstimator::EffectiveConnectionType expected_conn_type;
} tests[] = {
{5000, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_BROADBAND},
{20, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_BROADBAND},
};
for (const auto& test : tests) {
estimator.set_http_rtt(base::TimeDelta::FromMilliseconds(test.rtt_msec));
estimator.set_recent_http_rtt(
base::TimeDelta::FromMilliseconds(test.rtt_msec));
estimator.set_downlink_throughput_kbps(INT32_MAX);
EXPECT_EQ(test.expected_conn_type, estimator.GetEffectiveConnectionType());
}
}
// Tests that |GetEffectiveConnectionType| returns
// EFFECTIVE_CONNECTION_TYPE_OFFLINE when the device is currently offline.
TEST(NetworkQualityEstimatorTest, Offline) {
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
const struct {
NetworkChangeNotifier::ConnectionType connection_type;
NetworkQualityEstimator::EffectiveConnectionType expected_connection_type;
} tests[] = {
{NetworkChangeNotifier::CONNECTION_2G,
NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_UNKNOWN},
{NetworkChangeNotifier::CONNECTION_NONE,
NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_OFFLINE},
{NetworkChangeNotifier::CONNECTION_3G,
NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_UNKNOWN},
};
for (const auto& test : tests) {
estimator.SimulateNetworkChangeTo(test.connection_type, "test");
EXPECT_EQ(test.expected_connection_type,
estimator.GetEffectiveConnectionType());
}
}
// Tests that |GetEffectiveConnectionType| returns correct connection type when
// only RTT thresholds are specified in the variation params.
TEST(NetworkQualityEstimatorTest, ObtainThresholdsOnlyRTT) {
std::map<std::string, std::string> variation_params;
variation_params["Offline.ThresholdMedianHttpRTTMsec"] = "4000";
variation_params["Slow2G.ThresholdMedianHttpRTTMsec"] = "2000";
variation_params["2G.ThresholdMedianHttpRTTMsec"] = "1000";
variation_params["3G.ThresholdMedianHttpRTTMsec"] = "500";
variation_params["4G.ThresholdMedianHttpRTTMsec"] = "300";
variation_params["Broadband.ThresholdMedianHttpRTTMsec"] = "100";
TestNetworkQualityEstimator estimator(variation_params);
// Simulate the connection type as Wi-Fi so that GetEffectiveConnectionType
// does not return Offline if the device is offline.
estimator.SimulateNetworkChangeTo(NetworkChangeNotifier::CONNECTION_WIFI,
"test");
const struct {
int32_t rtt_msec;
NetworkQualityEstimator::EffectiveConnectionType expected_conn_type;
} tests[] = {
{5000, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_OFFLINE},
{4000, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_OFFLINE},
{3000, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_SLOW_2G},
{2000, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_SLOW_2G},
{1500, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_2G},
{1000, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_2G},
{700, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_3G},
{500, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_3G},
{400, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_4G},
{300, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_4G},
{200, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_BROADBAND},
{100, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_BROADBAND},
{20, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_BROADBAND},
};
for (const auto& test : tests) {
estimator.set_http_rtt(base::TimeDelta::FromMilliseconds(test.rtt_msec));
estimator.set_recent_http_rtt(
base::TimeDelta::FromMilliseconds(test.rtt_msec));
estimator.set_downlink_throughput_kbps(INT32_MAX);
EXPECT_EQ(test.expected_conn_type, estimator.GetEffectiveConnectionType());
}
}
// Tests that |GetEffectiveConnectionType| returns correct connection type when
// both RTT and throughput thresholds are specified in the variation params.
TEST(NetworkQualityEstimatorTest, ObtainThresholdsRTTandThroughput) {
std::map<std::string, std::string> variation_params;
variation_params["Offline.ThresholdMedianHttpRTTMsec"] = "4000";
variation_params["Slow2G.ThresholdMedianHttpRTTMsec"] = "2000";
variation_params["2G.ThresholdMedianHttpRTTMsec"] = "1000";
variation_params["3G.ThresholdMedianHttpRTTMsec"] = "500";
variation_params["4G.ThresholdMedianHttpRTTMsec"] = "300";
variation_params["Broadband.ThresholdMedianHttpRTTMsec"] = "100";
variation_params["Offline.ThresholdMedianKbps"] = "10";
variation_params["Slow2G.ThresholdMedianKbps"] = "100";
variation_params["2G.ThresholdMedianKbps"] = "300";
variation_params["3G.ThresholdMedianKbps"] = "500";
variation_params["4G.ThresholdMedianKbps"] = "1000";
variation_params["Broadband.ThresholdMedianKbps"] = "2000";
TestNetworkQualityEstimator estimator(variation_params);
// Simulate the connection type as Wi-Fi so that GetEffectiveConnectionType
// does not return Offline if the device is offline.
estimator.SimulateNetworkChangeTo(NetworkChangeNotifier::CONNECTION_WIFI,
"test");
const struct {
int32_t rtt_msec;
int32_t downlink_throughput_kbps;
NetworkQualityEstimator::EffectiveConnectionType expected_conn_type;
} tests[] = {
// Set RTT to a very low value to observe the effect of throughput.
// Throughput is the bottleneck.
{1, 5, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_OFFLINE},
{1, 10, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_OFFLINE},
{1, 50, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_SLOW_2G},
{1, 100, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_SLOW_2G},
{1, 150, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_2G},
{1, 300, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_2G},
{1, 400, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_3G},
{1, 500, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_3G},
{1, 700, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_4G},
{1, 1000, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_4G},
{1, 1500, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_BROADBAND},
{1, 2500, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_BROADBAND},
// Set both RTT and throughput. RTT is the bottleneck.
{3000, 25000, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_SLOW_2G},
{700, 25000, NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_3G},
};
for (const auto& test : tests) {
estimator.set_http_rtt(base::TimeDelta::FromMilliseconds(test.rtt_msec));
estimator.set_recent_http_rtt(
base::TimeDelta::FromMilliseconds(test.rtt_msec));
estimator.set_downlink_throughput_kbps(test.downlink_throughput_kbps);
EXPECT_EQ(test.expected_conn_type, estimator.GetEffectiveConnectionType());
}
}
// Tests if |weight_multiplier_per_second_| is set to correct value for various
// values of half life parameter.
TEST(NetworkQualityEstimatorTest, HalfLifeParam) {
std::map<std::string, std::string> variation_params;
const struct {
std::string description;
std::string variation_params_value;
double expected_weight_multiplier;
} tests[] = {
{"Half life parameter is not set, default value should be used",
std::string(), 0.988},
{"Half life parameter is set to negative, default value should be used",
"-100", 0.988},
{"Half life parameter is set to zero, default value should be used", "0",
0.988},
{"Half life parameter is set correctly", "10", 0.933},
};
for (const auto& test : tests) {
variation_params["HalfLifeSeconds"] = test.variation_params_value;
TestNetworkQualityEstimator estimator(variation_params);
EXPECT_NEAR(test.expected_weight_multiplier,
estimator.weight_multiplier_per_second_, 0.001)
<< test.description;
}
}
// Test if the network estimates are cached when network change notification
// is invoked.
TEST(NetworkQualityEstimatorTest, TestCaching) {
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
size_t expected_cache_size = 0;
EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());
// Cache entry will not be added for (NONE, "").
estimator.downstream_throughput_kbps_observations_.AddObservation(
NetworkQualityEstimator::ThroughputObservation(
1, base::TimeTicks::Now(),
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.rtt_observations_.AddObservation(
NetworkQualityEstimator::RttObservation(
base::TimeDelta::FromMilliseconds(1000), base::TimeTicks::Now(),
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-1");
EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());
// Entry will be added for (2G, "test1").
// Also, set the network quality for (2G, "test1") so that it is stored in
// the cache.
estimator.downstream_throughput_kbps_observations_.AddObservation(
NetworkQualityEstimator::ThroughputObservation(
1, base::TimeTicks::Now(),
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.rtt_observations_.AddObservation(
NetworkQualityEstimator::RttObservation(
base::TimeDelta::FromMilliseconds(1000), base::TimeTicks::Now(),
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-1");
++expected_cache_size;
EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());
// Entry will be added for (3G, "test1").
// Also, set the network quality for (3G, "test1") so that it is stored in
// the cache.
estimator.downstream_throughput_kbps_observations_.AddObservation(
NetworkQualityEstimator::ThroughputObservation(
2, base::TimeTicks::Now(),
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.rtt_observations_.AddObservation(
NetworkQualityEstimator::RttObservation(
base::TimeDelta::FromMilliseconds(500), base::TimeTicks::Now(),
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-2");
++expected_cache_size;
EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());
// Entry will not be added for (3G, "test2").
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-1");
EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());
// Read the network quality for (2G, "test-1").
EXPECT_TRUE(estimator.ReadCachedNetworkQualityEstimate());
base::TimeDelta rtt;
int32_t kbps;
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
EXPECT_EQ(1, kbps);
EXPECT_EQ(base::TimeDelta::FromMilliseconds(1000), rtt);
EXPECT_FALSE(estimator.GetTransportRTTEstimate(&rtt));
// No new entry should be added for (2G, "test-1") since it already exists
// in the cache.
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-1");
EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());
// Read the network quality for (3G, "test-1").
EXPECT_TRUE(estimator.ReadCachedNetworkQualityEstimate());
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
EXPECT_EQ(2, kbps);
EXPECT_EQ(base::TimeDelta::FromMilliseconds(500), rtt);
// No new entry should be added for (3G, "test1") since it already exists
// in the cache.
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-2");
EXPECT_EQ(expected_cache_size, estimator.cached_network_qualities_.size());
// Reading quality of (3G, "test-2") should return false.
EXPECT_FALSE(estimator.ReadCachedNetworkQualityEstimate());
// Reading quality of (2G, "test-3") should return false.
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-3");
EXPECT_FALSE(estimator.ReadCachedNetworkQualityEstimate());
}
// Tests if the cache size remains bounded. Also, ensure that the cache is
// LRU.
TEST(NetworkQualityEstimatorTest, TestLRUCacheMaximumSize) {
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, std::string());
EXPECT_EQ(0U, estimator.cached_network_qualities_.size());
// Add 100 more networks than the maximum size of the cache.
size_t network_count =
NetworkQualityEstimator::kMaximumNetworkQualityCacheSize + 100;
base::TimeTicks update_time_of_network_100;
for (size_t i = 0; i < network_count; ++i) {
estimator.downstream_throughput_kbps_observations_.AddObservation(
NetworkQualityEstimator::ThroughputObservation(
2, base::TimeTicks::Now(),
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.rtt_observations_.AddObservation(
NetworkQualityEstimator::RttObservation(
base::TimeDelta::FromMilliseconds(500), base::TimeTicks::Now(),
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
if (i == 100)
update_time_of_network_100 = base::TimeTicks::Now();
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI,
base::SizeTToString(i));
if (i < NetworkQualityEstimator::kMaximumNetworkQualityCacheSize)
EXPECT_EQ(i, estimator.cached_network_qualities_.size());
EXPECT_LE(estimator.cached_network_qualities_.size(),
static_cast<size_t>(
NetworkQualityEstimator::kMaximumNetworkQualityCacheSize));
}
// One more call so that the last network is also written to cache.
estimator.downstream_throughput_kbps_observations_.AddObservation(
NetworkQualityEstimator::ThroughputObservation(
2, base::TimeTicks::Now(),
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.rtt_observations_.AddObservation(
NetworkQualityEstimator::RttObservation(
base::TimeDelta::FromMilliseconds(500), base::TimeTicks::Now(),
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI,
base::SizeTToString(network_count - 1));
EXPECT_EQ(static_cast<size_t>(
NetworkQualityEstimator::kMaximumNetworkQualityCacheSize),
estimator.cached_network_qualities_.size());
// Test that the cache is LRU by examining its contents. Networks in cache
// must all be newer than the 100th network.
for (NetworkQualityEstimator::CachedNetworkQualities::iterator it =
estimator.cached_network_qualities_.begin();
it != estimator.cached_network_qualities_.end(); ++it) {
EXPECT_GE((it->second).last_update_time_, update_time_of_network_100);
}
}
TEST(NetworkQualityEstimatorTest, TestGetMetricsSince) {
std::map<std::string, std::string> variation_params;
const base::TimeDelta rtt_threshold_4g =
base::TimeDelta::FromMilliseconds(30);
const base::TimeDelta rtt_threshold_broadband =
base::TimeDelta::FromMilliseconds(1);
variation_params["4G.ThresholdMedianHttpRTTMsec"] =
base::IntToString(rtt_threshold_4g.InMilliseconds());
variation_params["Broadband.ThresholdMedianHttpRTTMsec"] =
base::IntToString(rtt_threshold_broadband.InMilliseconds());
variation_params["HalfLifeSeconds"] = "300000";
TestNetworkQualityEstimator estimator(variation_params);
base::TimeTicks now = base::TimeTicks::Now();
base::TimeTicks old = now - base::TimeDelta::FromMilliseconds(1);
ASSERT_NE(old, now);
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test");
const int32_t old_downlink_kbps = 1;
const base::TimeDelta old_url_rtt = base::TimeDelta::FromMilliseconds(1);
const base::TimeDelta old_tcp_rtt = base::TimeDelta::FromMilliseconds(10);
DCHECK_LT(old_url_rtt, rtt_threshold_4g);
DCHECK_LT(old_tcp_rtt, rtt_threshold_4g);
// First sample has very old timestamp.
for (size_t i = 0; i < 2; ++i) {
estimator.downstream_throughput_kbps_observations_.AddObservation(
NetworkQualityEstimator::ThroughputObservation(
old_downlink_kbps, old,
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.rtt_observations_.AddObservation(
NetworkQualityEstimator::RttObservation(
old_url_rtt, old, NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.rtt_observations_.AddObservation(
NetworkQualityEstimator::RttObservation(
old_tcp_rtt, old, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP));
}
const int32_t new_downlink_kbps = 100;
const base::TimeDelta new_url_rtt = base::TimeDelta::FromMilliseconds(100);
const base::TimeDelta new_tcp_rtt = base::TimeDelta::FromMilliseconds(1000);
DCHECK_NE(old_downlink_kbps, new_downlink_kbps);
DCHECK_NE(old_url_rtt, new_url_rtt);
DCHECK_NE(old_tcp_rtt, new_tcp_rtt);
DCHECK_GT(new_url_rtt, rtt_threshold_4g);
DCHECK_GT(new_tcp_rtt, rtt_threshold_4g);
DCHECK_GT(new_url_rtt, rtt_threshold_broadband);
DCHECK_GT(new_tcp_rtt, rtt_threshold_broadband);
estimator.downstream_throughput_kbps_observations_.AddObservation(
NetworkQualityEstimator::ThroughputObservation(
new_downlink_kbps, now,
NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.rtt_observations_.AddObservation(
NetworkQualityEstimator::RttObservation(
new_url_rtt, now, NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST));
estimator.rtt_observations_.AddObservation(
NetworkQualityEstimator::RttObservation(
new_tcp_rtt, now, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP));
const struct {
base::TimeTicks start_timestamp;
bool expect_network_quality_available;
base::TimeDelta expected_http_rtt;
base::TimeDelta expected_transport_rtt;
int32_t expected_downstream_throughput;
NetworkQualityEstimator::EffectiveConnectionType
expected_effective_connection_type;
} tests[] = {
{now + base::TimeDelta::FromSeconds(10), false,
base::TimeDelta::FromMilliseconds(0),
base::TimeDelta::FromMilliseconds(0), 0,
NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_BROADBAND},
{now, true, new_url_rtt, new_tcp_rtt, new_downlink_kbps,
NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_4G},
{old - base::TimeDelta::FromMicroseconds(500), true, old_url_rtt,
old_tcp_rtt, old_downlink_kbps,
NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_BROADBAND},
};
for (const auto& test : tests) {
base::TimeDelta http_rtt;
base::TimeDelta transport_rtt;
int32_t downstream_throughput_kbps;
EXPECT_EQ(
test.expect_network_quality_available,
estimator.GetRecentHttpRTTMedian(test.start_timestamp, &http_rtt));
EXPECT_EQ(test.expect_network_quality_available,
estimator.GetRecentTransportRTTMedian(test.start_timestamp,
&transport_rtt));
EXPECT_EQ(test.expect_network_quality_available,
estimator.GetRecentMedianDownlinkThroughputKbps(
test.start_timestamp, &downstream_throughput_kbps));
if (test.expect_network_quality_available) {
EXPECT_EQ(test.expected_http_rtt, http_rtt);
EXPECT_EQ(test.expected_transport_rtt, transport_rtt);
EXPECT_EQ(test.expected_downstream_throughput,
downstream_throughput_kbps);
EXPECT_EQ(
test.expected_effective_connection_type,
estimator.GetRecentEffectiveConnectionType(test.start_timestamp));
}
}
}
// An external estimate provider that does not have a valid RTT or throughput
// estimate.
class InvalidExternalEstimateProvider : public ExternalEstimateProvider {
public:
InvalidExternalEstimateProvider() : update_count_(0) {}
~InvalidExternalEstimateProvider() override {}
bool GetRTT(base::TimeDelta* rtt) const override {
DCHECK(rtt);
return false;
}
bool GetDownstreamThroughputKbps(
int32_t* downstream_throughput_kbps) const override {
DCHECK(downstream_throughput_kbps);
return false;
}
bool GetUpstreamThroughputKbps(
int32_t* upstream_throughput_kbps) const override {
// NetworkQualityEstimator does not support upstream throughput.
ADD_FAILURE();
return false;
}
bool GetTimeSinceLastUpdate(
base::TimeDelta* time_since_last_update) const override {
NOTREACHED();
return false;
}
void SetUpdatedEstimateDelegate(UpdatedEstimateDelegate* delegate) override {}
void Update() const override { update_count_++; }
size_t update_count() const { return update_count_; }
private:
mutable size_t update_count_;
DISALLOW_COPY_AND_ASSIGN(InvalidExternalEstimateProvider);
};
// Tests if the RTT value from external estimate provider is discarded if the
// external estimate provider is invalid.
TEST(NetworkQualityEstimatorTest, InvalidExternalEstimateProvider) {
base::HistogramTester histogram_tester;
InvalidExternalEstimateProvider* invalid_external_estimate_provider =
new InvalidExternalEstimateProvider();
std::unique_ptr<ExternalEstimateProvider> external_estimate_provider(
invalid_external_estimate_provider);
TestNetworkQualityEstimator estimator(std::map<std::string, std::string>(),
std::move(external_estimate_provider));
estimator.SimulateNetworkChangeTo(net::NetworkChangeNotifier::CONNECTION_WIFI,
"test");
base::TimeDelta rtt;
int32_t kbps;
EXPECT_EQ(1U, invalid_external_estimate_provider->update_count());
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
histogram_tester.ExpectTotalCount("NQE.ExternalEstimateProviderStatus", 2);
histogram_tester.ExpectBucketCount(
"NQE.ExternalEstimateProviderStatus",
1 /* EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE */, 1);
histogram_tester.ExpectBucketCount(
"NQE.ExternalEstimateProviderStatus",
2 /* EXTERNAL_ESTIMATE_PROVIDER_STATUS_QUERIED */, 1);
histogram_tester.ExpectTotalCount("NQE.ExternalEstimateProvider.RTT", 0);
histogram_tester.ExpectTotalCount(
"NQE.ExternalEstimateProvider.DownlinkBandwidth", 0);
}
class TestExternalEstimateProvider : public ExternalEstimateProvider {
public:
TestExternalEstimateProvider(base::TimeDelta rtt,
int32_t downstream_throughput_kbps)
: delegate_(nullptr),
should_notify_delegate_(true),
rtt_(rtt),
downstream_throughput_kbps_(downstream_throughput_kbps),
update_count_(0) {}
~TestExternalEstimateProvider() override {}
bool GetRTT(base::TimeDelta* rtt) const override {
NOTREACHED();
return true;
}
bool GetDownstreamThroughputKbps(
int32_t* downstream_throughput_kbps) const override {
NOTREACHED();
return true;
}
bool GetUpstreamThroughputKbps(
int32_t* upstream_throughput_kbps) const override {
NOTREACHED();
return false;
}
bool GetTimeSinceLastUpdate(
base::TimeDelta* time_since_last_update) const override {
NOTREACHED();
return true;
}
void SetUpdatedEstimateDelegate(UpdatedEstimateDelegate* delegate) override {
delegate_ = delegate;
}
void set_should_notify_delegate(bool should_notify_delegate) {
should_notify_delegate_ = should_notify_delegate;
}
void Update() const override {
update_count_++;
if (!should_notify_delegate_)
return;
delegate_->OnUpdatedEstimateAvailable(rtt_, downstream_throughput_kbps_,
-1);
}
size_t update_count() const { return update_count_; }
private:
UpdatedEstimateDelegate* delegate_;
bool should_notify_delegate_;
// RTT and downstream throughput estimates.
const base::TimeDelta rtt_;
const int32_t downstream_throughput_kbps_;
mutable size_t update_count_;
DISALLOW_COPY_AND_ASSIGN(TestExternalEstimateProvider);
};
// Tests if the external estimate provider is called in the constructor and
// on network change notification.
TEST(NetworkQualityEstimatorTest, TestExternalEstimateProvider) {
base::HistogramTester histogram_tester;
const base::TimeDelta external_estimate_provider_rtt =
base::TimeDelta::FromMilliseconds(1);
const int32_t external_estimate_provider_downstream_throughput = 100;
TestExternalEstimateProvider* test_external_estimate_provider =
new TestExternalEstimateProvider(
external_estimate_provider_rtt,
external_estimate_provider_downstream_throughput);
std::unique_ptr<ExternalEstimateProvider> external_estimate_provider(
test_external_estimate_provider);
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params,
std::move(external_estimate_provider));
estimator.SimulateNetworkChangeTo(net::NetworkChangeNotifier::CONNECTION_WIFI,
"test");
base::TimeDelta rtt;
int32_t kbps;
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_FALSE(estimator.GetTransportRTTEstimate(&rtt));
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
histogram_tester.ExpectTotalCount("NQE.ExternalEstimateProviderStatus", 5);
histogram_tester.ExpectBucketCount(
"NQE.ExternalEstimateProviderStatus",
1 /* EXTERNAL_ESTIMATE_PROVIDER_STATUS_AVAILABLE */, 1);
histogram_tester.ExpectBucketCount(
"NQE.ExternalEstimateProviderStatus",
2 /* EXTERNAL_ESTIMATE_PROVIDER_STATUS_QUERIED */, 1);
histogram_tester.ExpectBucketCount(
"NQE.ExternalEstimateProviderStatus",
4 /* EXTERNAL_ESTIMATE_PROVIDER_STATUS_CALLBACK */, 1);
histogram_tester.ExpectBucketCount(
"NQE.ExternalEstimateProviderStatus",
5 /* EXTERNAL_ESTIMATE_PROVIDER_STATUS_RTT_AVAILABLE */, 1);
histogram_tester.ExpectBucketCount(
"NQE.ExternalEstimateProviderStatus",
6 /* EXTERNAL_ESTIMATE_PROVIDER_STATUS_DOWNLINK_BANDWIDTH_AVAILABLE */,
1);
histogram_tester.ExpectUniqueSample("NQE.ExternalEstimateProvider.RTT", 1, 1);
histogram_tester.ExpectUniqueSample(
"NQE.ExternalEstimateProvider.DownlinkBandwidth", 100, 1);
EXPECT_EQ(1U, test_external_estimate_provider->update_count());
// Change network type to WiFi. Number of queries to External estimate
// provider must increment.
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1");
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
EXPECT_EQ(2U, test_external_estimate_provider->update_count());
test_external_estimate_provider->set_should_notify_delegate(false);
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-2");
EXPECT_EQ(3U, test_external_estimate_provider->update_count());
// Estimates are unavailable because external estimate provider never
// notifies network quality estimator of the updated estimates.
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
}
// Tests if the estimate from the external estimate provider is merged with the
// observations collected from the HTTP requests.
TEST(NetworkQualityEstimatorTest, TestExternalEstimateProviderMergeEstimates) {
const base::TimeDelta external_estimate_provider_rtt =
base::TimeDelta::FromMilliseconds(10 * 1000);
const int32_t external_estimate_provider_downstream_throughput = 100 * 1000;
TestExternalEstimateProvider* test_external_estimate_provider =
new TestExternalEstimateProvider(
external_estimate_provider_rtt,
external_estimate_provider_downstream_throughput);
std::unique_ptr<ExternalEstimateProvider> external_estimate_provider(
test_external_estimate_provider);
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params,
std::move(external_estimate_provider));
estimator.SimulateNetworkChangeTo(net::NetworkChangeNotifier::CONNECTION_WIFI,
"test");
base::TimeDelta rtt;
// Estimate provided by network quality estimator should match the estimate
// provided by external estimate provider.
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_EQ(external_estimate_provider_rtt, rtt);
int32_t kbps;
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
EXPECT_EQ(external_estimate_provider_downstream_throughput, kbps);
TestDelegate test_delegate;
TestURLRequestContext context(true);
context.set_network_quality_estimator(&estimator);
context.Init();
std::unique_ptr<URLRequest> request(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request->Start();
base::RunLoop().Run();
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_NE(external_estimate_provider_rtt, rtt);
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
EXPECT_NE(external_estimate_provider_downstream_throughput, kbps);
}
// Tests if the throughput observation is taken correctly when local and network
// requests do not overlap.
TEST(NetworkQualityEstimatorTest, TestThroughputNoRequestOverlap) {
base::HistogramTester histogram_tester;
std::map<std::string, std::string> variation_params;
static const struct {
bool allow_small_localhost_requests;
} tests[] = {
{
false,
},
{
true,
},
};
for (const auto& test : tests) {
TestNetworkQualityEstimator estimator(
std::unique_ptr<net::ExternalEstimateProvider>(), variation_params,
test.allow_small_localhost_requests,
test.allow_small_localhost_requests);
base::TimeDelta rtt;
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
int32_t kbps;
EXPECT_FALSE(estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
TestDelegate test_delegate;
TestURLRequestContext context(true);
context.set_network_quality_estimator(&estimator);
context.Init();
std::unique_ptr<URLRequest> request(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
request->Start();
base::RunLoop().Run();
EXPECT_EQ(test.allow_small_localhost_requests,
estimator.GetHttpRTTEstimate(&rtt));
EXPECT_EQ(test.allow_small_localhost_requests,
estimator.GetDownlinkThroughputKbpsEstimate(&kbps));
}
}
// Tests that the effective connection type is computed at the specified
// interval, and that the observers are notified of any change.
TEST(NetworkQualityEstimatorTest, TestEffectiveConnectionTypeObserver) {
base::HistogramTester histogram_tester;
std::unique_ptr<base::SimpleTestTickClock> tick_clock(
new base::SimpleTestTickClock());
base::SimpleTestTickClock* tick_clock_ptr = tick_clock.get();
TestEffectiveConnectionTypeObserver observer;
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
estimator.AddEffectiveConnectionTypeObserver(&observer);
estimator.SetTickClockForTesting(std::move(tick_clock));
TestDelegate test_delegate;
TestURLRequestContext context(true);
context.set_network_quality_estimator(&estimator);
context.Init();
EXPECT_EQ(0U, observer.effective_connection_types().size());
estimator.set_effective_connection_type(
NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_2G);
tick_clock_ptr->Advance(base::TimeDelta::FromMinutes(60));
std::unique_ptr<URLRequest> request(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
request->Start();
base::RunLoop().Run();
EXPECT_EQ(1U, observer.effective_connection_types().size());
histogram_tester.ExpectUniqueSample(
"NQE.MainFrame.EffectiveConnectionType.Unknown",
NetworkQualityEstimator::EffectiveConnectionType::
EFFECTIVE_CONNECTION_TYPE_2G,
1);
// Next request should not trigger recomputation of effective connection type
// since there has been no change in the clock.
std::unique_ptr<URLRequest> request2(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request2->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
request2->Start();
base::RunLoop().Run();
EXPECT_EQ(1U, observer.effective_connection_types().size());
// Change in connection type should send out notification to the observers.
estimator.set_effective_connection_type(
NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_3G);
estimator.SimulateNetworkChangeTo(NetworkChangeNotifier::CONNECTION_WIFI,
"test");
EXPECT_EQ(2U, observer.effective_connection_types().size());
// A change in effective connection type does not trigger notification to the
// observers, since it is not accompanied by any new observation or a network
// change event.
estimator.set_effective_connection_type(
NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_3G);
EXPECT_EQ(2U, observer.effective_connection_types().size());
}
TEST(NetworkQualityEstimatorTest, TestRttThroughputObservers) {
TestRTTObserver rtt_observer;
TestThroughputObserver throughput_observer;
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
estimator.AddRTTObserver(&rtt_observer);
estimator.AddThroughputObserver(&throughput_observer);
TestDelegate test_delegate;
TestURLRequestContext context(true);
context.set_network_quality_estimator(&estimator);
context.Init();
EXPECT_EQ(0U, rtt_observer.observations().size());
EXPECT_EQ(0U, throughput_observer.observations().size());
base::TimeTicks then = base::TimeTicks::Now();
std::unique_ptr<URLRequest> request(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
request->Start();
base::RunLoop().Run();
std::unique_ptr<URLRequest> request2(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request2->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
request2->Start();
base::RunLoop().Run();
// Both RTT and downstream throughput should be updated.
base::TimeDelta rtt;
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
int32_t throughput;
EXPECT_TRUE(estimator.GetDownlinkThroughputKbpsEstimate(&throughput));
EXPECT_EQ(2U, rtt_observer.observations().size());
EXPECT_EQ(2U, throughput_observer.observations().size());
for (const auto& observation : rtt_observer.observations()) {
EXPECT_LE(0, observation.rtt_ms);
EXPECT_LE(0, (observation.timestamp - then).InMilliseconds());
EXPECT_EQ(NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST,
observation.source);
}
for (const auto& observation : throughput_observer.observations()) {
EXPECT_LE(0, observation.throughput_kbps);
EXPECT_LE(0, (observation.timestamp - then).InMilliseconds());
EXPECT_EQ(NETWORK_QUALITY_OBSERVATION_SOURCE_URL_REQUEST,
observation.source);
}
EXPECT_FALSE(estimator.GetTransportRTTEstimate(&rtt));
// Verify that observations from TCP and QUIC are passed on to the observers.
base::TimeDelta tcp_rtt(base::TimeDelta::FromMilliseconds(1));
base::TimeDelta quic_rtt(base::TimeDelta::FromMilliseconds(2));
std::unique_ptr<SocketPerformanceWatcher> tcp_watcher =
estimator.GetSocketPerformanceWatcherFactory()
->CreateSocketPerformanceWatcher(
SocketPerformanceWatcherFactory::PROTOCOL_TCP);
std::unique_ptr<SocketPerformanceWatcher> quic_watcher =
estimator.GetSocketPerformanceWatcherFactory()
->CreateSocketPerformanceWatcher(
SocketPerformanceWatcherFactory::PROTOCOL_QUIC);
tcp_watcher->OnUpdatedRTTAvailable(tcp_rtt);
quic_watcher->OnUpdatedRTTAvailable(quic_rtt);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(4U, rtt_observer.observations().size());
EXPECT_EQ(2U, throughput_observer.observations().size());
EXPECT_EQ(tcp_rtt.InMilliseconds(), rtt_observer.observations().at(2).rtt_ms);
EXPECT_EQ(quic_rtt.InMilliseconds(),
rtt_observer.observations().at(3).rtt_ms);
EXPECT_TRUE(estimator.GetTransportRTTEstimate(&rtt));
}
// TestTCPSocketRTT requires kernel support for tcp_info struct, and so it is
// enabled only on certain platforms.
#if defined(TCP_INFO) || defined(OS_LINUX)
#define MAYBE_TestTCPSocketRTT TestTCPSocketRTT
#else
#define MAYBE_TestTCPSocketRTT DISABLED_TestTCPSocketRTT
#endif
// Tests that the TCP socket notifies the Network Quality Estimator of TCP RTTs,
// which in turn notifies registered RTT observers.
TEST(NetworkQualityEstimatorTest, MAYBE_TestTCPSocketRTT) {
base::HistogramTester histogram_tester;
TestRTTObserver rtt_observer;
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
estimator.AddRTTObserver(&rtt_observer);
TestDelegate test_delegate;
TestURLRequestContext context(true);
context.set_network_quality_estimator(&estimator);
std::unique_ptr<HttpNetworkSession::Params> params(
new HttpNetworkSession::Params);
// |estimator| should be notified of TCP RTT observations.
params->socket_performance_watcher_factory =
estimator.GetSocketPerformanceWatcherFactory();
context.set_http_network_session_params(std::move(params));
context.Init();
EXPECT_EQ(0U, rtt_observer.observations().size());
base::TimeDelta rtt;
EXPECT_FALSE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_FALSE(estimator.GetTransportRTTEstimate(&rtt));
// Send two requests. Verify that the completion of each request generates at
// least one TCP RTT observation.
const size_t num_requests = 2;
for (size_t i = 0; i < num_requests; ++i) {
size_t before_count_tcp_rtt_observations = 0;
for (const auto& observation : rtt_observer.observations()) {
if (observation.source == NETWORK_QUALITY_OBSERVATION_SOURCE_TCP)
++before_count_tcp_rtt_observations;
}
std::unique_ptr<URLRequest> request(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
request->Start();
base::RunLoop().Run();
size_t after_count_tcp_rtt_observations = 0;
for (const auto& observation : rtt_observer.observations()) {
if (observation.source == NETWORK_QUALITY_OBSERVATION_SOURCE_TCP)
++after_count_tcp_rtt_observations;
}
// At least one notification should be received per socket performance
// watcher.
EXPECT_LE(1U, after_count_tcp_rtt_observations -
before_count_tcp_rtt_observations)
<< i;
}
EXPECT_TRUE(estimator.GetHttpRTTEstimate(&rtt));
EXPECT_TRUE(estimator.GetTransportRTTEstimate(&rtt));
estimator.SimulateNetworkChangeTo(
NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1");
histogram_tester.ExpectTotalCount("NQE.TransportRTT.Percentile50.Unknown", 1);
histogram_tester.ExpectBucketCount("NQE.TransportRTT.Percentile50.Unknown",
rtt.InMilliseconds(), 1);
histogram_tester.ExpectTotalCount("NQE.TransportRTT.Percentile10.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.TransportRTT.Percentile50.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.TransportRTT.Percentile90.Unknown", 1);
histogram_tester.ExpectTotalCount("NQE.TransportRTT.Percentile100.Unknown",
1);
// Verify that metrics are logged correctly on main-frame requests.
histogram_tester.ExpectTotalCount(
"NQE.MainFrame.TransportRTT.Percentile50.Unknown", num_requests);
histogram_tester.ExpectTotalCount(
"NQE.MainFrame.EffectiveConnectionType.Unknown", num_requests);
histogram_tester.ExpectBucketCount(
"NQE.MainFrame.EffectiveConnectionType.Unknown",
NetworkQualityEstimator::EffectiveConnectionType::
EFFECTIVE_CONNECTION_TYPE_UNKNOWN,
1);
}
#if defined(OS_IOS)
// Flaky on iOS when |accuracy_recording_delay| is non-zero.
#define MAYBE_RecordAccuracy DISABLED_RecordAccuracy
#else
#define MAYBE_RecordAccuracy RecordAccuracy
#endif
// Tests if the NQE accuracy metrics are recorded properly.
TEST(NetworkQualityEstimatorTest, MAYBE_RecordAccuracy) {
const int expected_rtt_msec = 100;
const base::TimeDelta accuracy_recording_delays[] = {
base::TimeDelta::FromSeconds(0), base::TimeDelta::FromSeconds(1),
};
const struct {
base::TimeDelta rtt;
base::TimeDelta recent_rtt;
} tests[] = {
{base::TimeDelta::FromMilliseconds(expected_rtt_msec),
base::TimeDelta::FromMilliseconds(expected_rtt_msec)},
{base::TimeDelta::FromMilliseconds(expected_rtt_msec + 1),
base::TimeDelta::FromMilliseconds(expected_rtt_msec)},
{base::TimeDelta::FromMilliseconds(expected_rtt_msec - 1),
base::TimeDelta::FromMilliseconds(expected_rtt_msec)},
};
for (const auto& accuracy_recording_delay : accuracy_recording_delays) {
for (const auto& test : tests) {
std::unique_ptr<base::SimpleTestTickClock> tick_clock(
new base::SimpleTestTickClock());
base::SimpleTestTickClock* tick_clock_ptr = tick_clock.get();
tick_clock_ptr->Advance(base::TimeDelta::FromSeconds(1));
std::map<std::string, std::string> variation_params;
TestNetworkQualityEstimator estimator(variation_params);
estimator.SetTickClockForTesting(std::move(tick_clock));
std::vector<base::TimeDelta> accuracy_recording_intervals;
accuracy_recording_intervals.push_back(accuracy_recording_delay);
estimator.SetAccuracyRecordingIntervals(accuracy_recording_intervals);
// RTT is higher than threshold. Network is slow.
// Network was predicted to be slow and actually was slow.
estimator.set_http_rtt(test.rtt);
estimator.set_recent_http_rtt(test.recent_rtt);
estimator.set_transport_rtt(test.rtt);
estimator.set_recent_transport_rtt(test.recent_rtt);
base::HistogramTester histogram_tester;
TestDelegate test_delegate;
TestURLRequestContext context(true);
context.set_network_quality_estimator(&estimator);
context.Init();
// Start a main-frame request which should cause network quality estimator
// to record accuracy UMA.
std::unique_ptr<URLRequest> request(context.CreateRequest(
estimator.GetEchoURL(), DEFAULT_PRIORITY, &test_delegate));
request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME);
request->Start();
base::RunLoop().Run();
if (accuracy_recording_delay != base::TimeDelta()) {
tick_clock_ptr->Advance(accuracy_recording_delay);
// Sleep for some time to ensure that the delayed task is posted.
base::PlatformThread::Sleep(accuracy_recording_delay * 2);
base::RunLoop().RunUntilIdle();
}
const int diff = std::abs(test.rtt.InMilliseconds() -
test.recent_rtt.InMilliseconds());
const std::string sign_suffix_with_one_sample =
test.rtt.InMilliseconds() - test.recent_rtt.InMilliseconds() >= 0
? "Positive"
: "Negative";
const std::string sign_suffix_with_zero_samples =
test.rtt.InMilliseconds() - test.recent_rtt.InMilliseconds() >= 0
? "Negative"
: "Positive";
const std::string interval_value =
base::IntToString(accuracy_recording_delay.InSeconds());
histogram_tester.ExpectUniqueSample(
"NQE.Accuracy.HttpRTT.EstimatedObservedDiff." +
sign_suffix_with_one_sample + "." + interval_value + ".60_140",
diff, 1);
histogram_tester.ExpectTotalCount(
"NQE.Accuracy.HttpRTT.EstimatedObservedDiff." +
sign_suffix_with_zero_samples + "." + interval_value + ".60_140",
0);
histogram_tester.ExpectUniqueSample(
"NQE.Accuracy.TransportRTT.EstimatedObservedDiff." +
sign_suffix_with_one_sample + "." + interval_value + ".60_140",
diff, 1);
histogram_tester.ExpectTotalCount(
"NQE.Accuracy.TransportRTT.EstimatedObservedDiff." +
sign_suffix_with_zero_samples + "." + interval_value + ".60_140",
0);
}
}
}
// Tests that the effective connection type is converted correctly to a
// descriptive string name, and vice-versa.
TEST(NetworkQualityEstimatorTest, NameConnectionTypeConversion) {
for (size_t i = 0;
i < NetworkQualityEstimator::EFFECTIVE_CONNECTION_TYPE_LAST; ++i) {
const NetworkQualityEstimator::EffectiveConnectionType
effective_connection_type =
static_cast<NetworkQualityEstimator::EffectiveConnectionType>(i);
std::string connection_type_name =
std::string(NetworkQualityEstimator::GetNameForEffectiveConnectionType(
effective_connection_type));
EXPECT_FALSE(connection_type_name.empty());
EXPECT_EQ(effective_connection_type,
NetworkQualityEstimator::GetEffectiveConnectionTypeForName(
connection_type_name));
}
}
} // namespace net