| // 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 <cmath> |
| #include <limits> |
| #include <map> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/metrics/histogram_samples.h" |
| #include "base/optional.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/test/metrics/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_response_headers.h" |
| #include "net/http/http_response_info.h" |
| #include "net/http/http_status_code.h" |
| #include "net/log/test_net_log.h" |
| #include "net/nqe/effective_connection_type.h" |
| #include "net/nqe/effective_connection_type_observer.h" |
| #include "net/nqe/network_quality_estimator_test_util.h" |
| #include "net/nqe/network_quality_observation.h" |
| #include "net/nqe/network_quality_observation_source.h" |
| #include "net/nqe/observation_buffer.h" |
| #include "net/nqe/rtt_throughput_estimates_observer.h" |
| #include "net/socket/socket_performance_watcher.h" |
| #include "net/socket/socket_performance_watcher_factory.h" |
| #include "net/test/test_with_scoped_task_environment.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "starboard/types.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| |
| // Verifies that the number of samples in the bucket with minimum value |
| // |bucket_min| in |histogram| are at least |expected_min_count_samples|. |
| void ExpectBucketCountAtLeast(base::HistogramTester* histogram_tester, |
| const std::string& histogram, |
| int32_t bucket_min, |
| int32_t expected_min_count_samples) { |
| std::vector<base::Bucket> buckets = |
| histogram_tester->GetAllSamples(histogram); |
| int actual_count_samples = 0; |
| for (const auto& bucket : buckets) { |
| if (bucket.min == bucket_min) |
| actual_count_samples += bucket.count; |
| } |
| EXPECT_LE(expected_min_count_samples, actual_count_samples) |
| << " histogram=" << histogram << " bucket_min=" << bucket_min |
| << " expected_min_count_samples=" << expected_min_count_samples; |
| } |
| |
| } // namespace |
| |
| namespace net { |
| |
| namespace { |
| |
| class TestEffectiveConnectionTypeObserver |
| : public EffectiveConnectionTypeObserver { |
| public: |
| std::vector<EffectiveConnectionType>& effective_connection_types() { |
| return effective_connection_types_; |
| } |
| |
| // EffectiveConnectionTypeObserver implementation: |
| void OnEffectiveConnectionTypeChanged(EffectiveConnectionType type) override { |
| effective_connection_types_.push_back(type); |
| } |
| |
| private: |
| std::vector<EffectiveConnectionType> effective_connection_types_; |
| }; |
| |
| class TestRTTAndThroughputEstimatesObserver |
| : public RTTAndThroughputEstimatesObserver { |
| public: |
| TestRTTAndThroughputEstimatesObserver() |
| : http_rtt_(nqe::internal::InvalidRTT()), |
| transport_rtt_(nqe::internal::InvalidRTT()), |
| downstream_throughput_kbps_(nqe::internal::INVALID_RTT_THROUGHPUT), |
| notifications_received_(0) {} |
| |
| // RTTAndThroughputEstimatesObserver implementation: |
| void OnRTTOrThroughputEstimatesComputed( |
| base::TimeDelta http_rtt, |
| base::TimeDelta transport_rtt, |
| int32_t downstream_throughput_kbps) override { |
| http_rtt_ = http_rtt; |
| transport_rtt_ = transport_rtt; |
| downstream_throughput_kbps_ = downstream_throughput_kbps; |
| notifications_received_++; |
| } |
| |
| int notifications_received() const { return notifications_received_; } |
| |
| base::TimeDelta http_rtt() const { return http_rtt_; } |
| base::TimeDelta transport_rtt() const { return transport_rtt_; } |
| int32_t downstream_throughput_kbps() const { |
| return downstream_throughput_kbps_; |
| } |
| |
| private: |
| base::TimeDelta http_rtt_; |
| base::TimeDelta transport_rtt_; |
| int32_t downstream_throughput_kbps_; |
| int notifications_received_; |
| }; |
| |
| 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)); |
| } |
| |
| // Returns the last received RTT observation that has source set to |source|. |
| base::TimeDelta last_rtt(NetworkQualityObservationSource source) { |
| for (auto i = observations_.rbegin(); i != observations_.rend(); ++i) { |
| Observation observation = *i; |
| if (observation.source == source) |
| return base::TimeDelta::FromMilliseconds(observation.rtt_ms); |
| } |
| return nqe::internal::InvalidRTT(); |
| } |
| |
| 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 |
| |
| using NetworkQualityEstimatorTest = TestWithScopedTaskEnvironment; |
| |
| TEST_F(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; |
| variation_params["throughput_min_requests_in_flight"] = "1"; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "test"); |
| histogram_tester.ExpectUniqueSample("NQE.CachedNetworkQualityAvailable", |
| false, 2); |
| |
| base::TimeDelta rtt; |
| int32_t kbps; |
| EXPECT_FALSE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_FALSE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &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, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| |
| // Pump message loop to allow estimator tasks to be processed. |
| base::RunLoop().RunUntilIdle(); |
| |
| // Both RTT and downstream throughput should be updated. |
| base::TimeDelta http_rtt; |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &http_rtt, nullptr)); |
| EXPECT_EQ(http_rtt, estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| EXPECT_EQ(kbps, estimator.GetDownstreamThroughputKbps().value()); |
| base::TimeDelta transport_rtt; |
| EXPECT_FALSE(estimator.GetTransportRTT()); |
| EXPECT_FALSE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &transport_rtt, nullptr)); |
| |
| // Verify the contents of the net log. |
| EXPECT_LE( |
| 2, estimator.GetEntriesCount(NetLogEventType::NETWORK_QUALITY_CHANGED)); |
| EXPECT_EQ(http_rtt.InMilliseconds(), |
| estimator.GetNetLogLastIntegerValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, "http_rtt_ms")); |
| EXPECT_EQ(-1, |
| estimator.GetNetLogLastIntegerValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, "transport_rtt_ms")); |
| EXPECT_EQ(kbps, estimator.GetNetLogLastIntegerValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, |
| "downstream_throughput_kbps")); |
| |
| // Check UMA histograms. |
| histogram_tester.ExpectUniqueSample( |
| "NQE.MainFrame.EffectiveConnectionType", |
| EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_UNKNOWN, 1); |
| histogram_tester.ExpectUniqueSample("NQE.EstimateAvailable.MainFrame.RTT", 0, |
| 1); |
| histogram_tester.ExpectUniqueSample( |
| "NQE.EstimateAvailable.MainFrame.TransportRTT", 0, 1); |
| histogram_tester.ExpectUniqueSample("NQE.EstimateAvailable.MainFrame.Kbps", 0, |
| 1); |
| EXPECT_LE(1u, |
| histogram_tester.GetAllSamples("NQE.RTT.OnECTComputation").size()); |
| EXPECT_LE(1u, |
| histogram_tester.GetAllSamples("NQE.Kbps.OnECTComputation").size()); |
| |
| histogram_tester.ExpectBucketCount( |
| "NQE.RTT.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP, 1); |
| histogram_tester.ExpectBucketCount( |
| "NQE.Kbps.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP, 1); |
| histogram_tester.ExpectTotalCount("NQE.Kbps.RawObservation.Http", 1); |
| |
| std::unique_ptr<URLRequest> request2( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request2->SetLoadFlags(request2->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request2->Start(); |
| test_delegate.RunUntilComplete(); |
| histogram_tester.ExpectTotalCount("NQE.MainFrame.EffectiveConnectionType", 2); |
| histogram_tester.ExpectBucketCount("NQE.EstimateAvailable.MainFrame.RTT", 1, |
| 1); |
| histogram_tester.ExpectUniqueSample( |
| "NQE.EstimateAvailable.MainFrame.TransportRTT", 0, 2); |
| histogram_tester.ExpectBucketCount("NQE.EstimateAvailable.MainFrame.Kbps", 1, |
| 1); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); |
| histogram_tester.ExpectUniqueSample("NQE.CachedNetworkQualityAvailable", |
| false, 3); |
| histogram_tester.ExpectTotalCount("NQE.RatioMedianRTT.WiFi", 0); |
| |
| EXPECT_FALSE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_FALSE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| |
| // Verify that metrics are logged correctly on main-frame requests. |
| histogram_tester.ExpectTotalCount("NQE.MainFrame.RTT.Percentile50", 1); |
| histogram_tester.ExpectTotalCount("NQE.MainFrame.TransportRTT.Percentile50", |
| 0); |
| histogram_tester.ExpectTotalCount("NQE.MainFrame.Kbps.Percentile50", 1); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, std::string()); |
| histogram_tester.ExpectUniqueSample("NQE.CachedNetworkQualityAvailable", |
| false, 4); |
| |
| EXPECT_FALSE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_FALSE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| |
| std::unique_ptr<URLRequest> request3( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request3->SetLoadFlags(request2->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request3->Start(); |
| test_delegate.RunUntilComplete(); |
| histogram_tester.ExpectBucketCount( |
| "NQE.MainFrame.EffectiveConnectionType", |
| EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_UNKNOWN, 2); |
| histogram_tester.ExpectTotalCount("NQE.MainFrame.EffectiveConnectionType", 3); |
| histogram_tester.ExpectBucketCount("NQE.EstimateAvailable.MainFrame.RTT", 0, |
| 2); |
| histogram_tester.ExpectBucketCount("NQE.EstimateAvailable.MainFrame.RTT", 1, |
| 1); |
| histogram_tester.ExpectUniqueSample( |
| "NQE.EstimateAvailable.MainFrame.TransportRTT", 0, 3); |
| histogram_tester.ExpectBucketCount("NQE.EstimateAvailable.MainFrame.Kbps", 0, |
| 2); |
| histogram_tester.ExpectBucketCount("NQE.EstimateAvailable.MainFrame.Kbps", 1, |
| 1); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "test"); |
| histogram_tester.ExpectBucketCount("NQE.CachedNetworkQualityAvailable", false, |
| 4); |
| } |
| |
| // Tests that the network quality estimator writes and reads network quality |
| // from the cache store correctly. |
| TEST_F(NetworkQualityEstimatorTest, Caching) { |
| for (NetworkChangeNotifier::ConnectionType connection_type : |
| {NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, |
| NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET}) { |
| base::HistogramTester histogram_tester; |
| std::map<std::string, std::string> variation_params; |
| variation_params["throughput_min_requests_in_flight"] = "1"; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| const std::string connection_id = |
| connection_type == |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI |
| ? "test" |
| : ""; |
| |
| estimator.SimulateNetworkChange(connection_type, connection_id); |
| histogram_tester.ExpectUniqueSample("NQE.CachedNetworkQualityAvailable", |
| false, 2); |
| |
| base::TimeDelta rtt; |
| int32_t kbps; |
| EXPECT_FALSE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_FALSE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.Init(); |
| |
| // Start two requests so that the network quality is added to cache store at |
| // the beginning of the second request from the network traffic observed |
| // from the first request. |
| for (size_t i = 0; i < 2; ++i) { |
| std::unique_ptr<URLRequest> request( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| } |
| histogram_tester.ExpectUniqueSample("NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP, |
| 2); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // Both RTT and downstream throughput should be updated. |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(rtt, estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| EXPECT_EQ(kbps, estimator.GetDownstreamThroughputKbps().value()); |
| EXPECT_NE(EFFECTIVE_CONNECTION_TYPE_UNKNOWN, |
| estimator.GetEffectiveConnectionType()); |
| EXPECT_FALSE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_FALSE(estimator.GetTransportRTT()); |
| |
| histogram_tester.ExpectBucketCount("NQE.CachedNetworkQualityAvailable", |
| false, 2); |
| |
| // Add the observers before changing the network type. |
| TestEffectiveConnectionTypeObserver observer; |
| estimator.AddEffectiveConnectionTypeObserver(&observer); |
| TestRTTObserver rtt_observer; |
| estimator.AddRTTObserver(&rtt_observer); |
| TestThroughputObserver throughput_observer; |
| estimator.AddThroughputObserver(&throughput_observer); |
| |
| // |observer| should be notified as soon as it is added. |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(1U, observer.effective_connection_types().size()); |
| |
| int num_net_log_entries = |
| estimator.GetEntriesCount(NetLogEventType::NETWORK_QUALITY_CHANGED); |
| EXPECT_LE(2, num_net_log_entries); |
| |
| estimator.SimulateNetworkChange(connection_type, connection_id); |
| histogram_tester.ExpectBucketCount( |
| "NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE, 1); |
| histogram_tester.ExpectBucketCount( |
| "NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE, 1); |
| histogram_tester.ExpectTotalCount("NQE.RTT.ObservationSource", 4); |
| |
| histogram_tester.ExpectBucketCount( |
| "NQE.Kbps.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE, 1); |
| histogram_tester.ExpectTotalCount( |
| "NQE.Kbps.RawObservation.HttpCachedEstimate", 1); |
| |
| // Verify the contents of the net log. |
| EXPECT_LE( |
| 1, estimator.GetEntriesCount(NetLogEventType::NETWORK_QUALITY_CHANGED) - |
| num_net_log_entries); |
| EXPECT_NE(-1, estimator.GetNetLogLastIntegerValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, "http_rtt_ms")); |
| EXPECT_NE( |
| -1, estimator.GetNetLogLastIntegerValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, "transport_rtt_ms")); |
| EXPECT_NE(-1, estimator.GetNetLogLastIntegerValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, |
| "downstream_throughput_kbps")); |
| EXPECT_EQ(GetNameForEffectiveConnectionType( |
| estimator.GetEffectiveConnectionType()), |
| estimator.GetNetLogLastStringValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, |
| "effective_connection_type")); |
| |
| histogram_tester.ExpectBucketCount("NQE.CachedNetworkQualityAvailable", |
| true, 1); |
| histogram_tester.ExpectTotalCount("NQE.CachedNetworkQualityAvailable", 3); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Verify that the cached network quality was read, and observers were |
| // notified. |observer| must be notified once right after it was added, and |
| // once again after the cached network quality was read. |
| EXPECT_LE(2U, observer.effective_connection_types().size()); |
| EXPECT_EQ(estimator.GetEffectiveConnectionType(), |
| observer.effective_connection_types().back()); |
| EXPECT_EQ(2U, rtt_observer.observations().size()); |
| EXPECT_EQ(1U, throughput_observer.observations().size()); |
| } |
| } |
| |
| // Tests that the network quality estimator does not read the network quality |
| // from the cache store when caching is not enabled. |
| TEST_F(NetworkQualityEstimatorTest, CachingDisabled) { |
| base::HistogramTester histogram_tester; |
| std::map<std::string, std::string> variation_params; |
| // Do not set |persistent_cache_reading_enabled| variation param. |
| variation_params["persistent_cache_reading_enabled"] = "false"; |
| variation_params["throughput_min_requests_in_flight"] = "1"; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test"); |
| histogram_tester.ExpectTotalCount("NQE.CachedNetworkQualityAvailable", 0); |
| |
| base::TimeDelta rtt; |
| int32_t kbps; |
| EXPECT_FALSE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_FALSE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.Init(); |
| |
| // Start two requests so that the network quality is added to cache store at |
| // the beginning of the second request from the network traffic observed from |
| // the first request. |
| for (size_t i = 0; i < 2; ++i) { |
| std::unique_ptr<URLRequest> request( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| } |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // Both RTT and downstream throughput should be updated. |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(rtt, estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| EXPECT_EQ(kbps, estimator.GetDownstreamThroughputKbps().value()); |
| EXPECT_NE(EFFECTIVE_CONNECTION_TYPE_UNKNOWN, |
| estimator.GetEffectiveConnectionType()); |
| EXPECT_FALSE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_FALSE(estimator.GetTransportRTT()); |
| |
| histogram_tester.ExpectTotalCount("NQE.CachedNetworkQualityAvailable", 0); |
| |
| // Add the observers before changing the network type. |
| TestRTTObserver rtt_observer; |
| estimator.AddRTTObserver(&rtt_observer); |
| TestThroughputObserver throughput_observer; |
| estimator.AddThroughputObserver(&throughput_observer); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test"); |
| |
| histogram_tester.ExpectTotalCount("NQE.CachedNetworkQualityAvailable", 0); |
| base::RunLoop().RunUntilIdle(); |
| |
| // Verify that the cached network quality was read, and observers were |
| // notified. |observer| must be notified once right after it was added, and |
| // once again after the cached network quality was read. |
| EXPECT_EQ(0U, rtt_observer.observations().size()); |
| EXPECT_EQ(0U, throughput_observer.observations().size()); |
| } |
| |
| TEST_F(NetworkQualityEstimatorTest, QuicObservations) { |
| base::HistogramTester histogram_tester; |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| estimator.OnUpdatedTransportRTTAvailable( |
| SocketPerformanceWatcherFactory::PROTOCOL_TCP, |
| base::TimeDelta::FromMilliseconds(10), base::nullopt); |
| estimator.OnUpdatedTransportRTTAvailable( |
| SocketPerformanceWatcherFactory::PROTOCOL_QUIC, |
| base::TimeDelta::FromMilliseconds(10), base::nullopt); |
| histogram_tester.ExpectBucketCount("NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TCP, 1); |
| histogram_tester.ExpectBucketCount( |
| "NQE.RTT.ObservationSource", NETWORK_QUALITY_OBSERVATION_SOURCE_QUIC, 1); |
| histogram_tester.ExpectTotalCount("NQE.EndToEndRTT.OnECTComputation", 1); |
| histogram_tester.ExpectTotalCount("NQE.RTT.ObservationSource", 2); |
| } |
| |
| TEST_F(NetworkQualityEstimatorTest, StoreObservations) { |
| std::map<std::string, std::string> variation_params; |
| variation_params["throughput_min_requests_in_flight"] = "1"; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| base::TimeDelta rtt; |
| int32_t kbps; |
| EXPECT_FALSE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_FALSE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.Init(); |
| |
| const size_t kMaxObservations = 10; |
| for (size_t i = 0; i < kMaxObservations; ++i) { |
| std::unique_ptr<URLRequest> request( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| |
| // Pump the message loop to process estimator tasks. |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_TRUE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| } |
| |
| // Verify that the stored observations are cleared on network change. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-2"); |
| EXPECT_FALSE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_FALSE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| } |
| |
| // This test notifies NetworkQualityEstimator of received data. Next, |
| // throughput and RTT percentiles are checked for correctness by doing simple |
| // verifications. |
| TEST_F(NetworkQualityEstimatorTest, ComputedPercentiles) { |
| std::map<std::string, std::string> variation_params; |
| variation_params["throughput_min_requests_in_flight"] = "1"; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| EXPECT_EQ(nqe::internal::InvalidRTT(), |
| estimator.GetRTTEstimateInternal( |
| base::TimeTicks(), nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| 100, nullptr)); |
| EXPECT_EQ(nqe::internal::INVALID_RTT_THROUGHPUT, |
| estimator.GetDownlinkThroughputKbpsEstimateInternal( |
| base::TimeTicks(), 100)); |
| |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.Init(); |
| |
| for (size_t i = 0; i < 10U; ++i) { |
| std::unique_ptr<URLRequest> request( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| } |
| |
| // 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( |
| base::TimeTicks(), nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| i, nullptr), |
| 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( |
| base::TimeTicks(), nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| i, nullptr), |
| estimator.GetRTTEstimateInternal( |
| base::TimeTicks(), nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| i - 1, nullptr)); |
| } |
| } |
| } |
| |
| // Verifies that the observers receive the notifications when default estimates |
| // are added to the observations. |
| TEST_F(NetworkQualityEstimatorTest, DefaultObservations) { |
| base::HistogramTester histogram_tester; |
| |
| TestEffectiveConnectionTypeObserver effective_connection_type_observer; |
| TestRTTAndThroughputEstimatesObserver rtt_throughput_estimates_observer; |
| TestRTTObserver rtt_observer; |
| TestThroughputObserver throughput_observer; |
| std::map<std::string, std::string> variation_params; |
| TestNetworkQualityEstimator estimator(variation_params, false, false, |
| std::make_unique<BoundTestNetLog>()); |
| |
| // Default observations should be added when constructing the |estimator|. |
| histogram_tester.ExpectBucketCount( |
| "NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM, 1); |
| histogram_tester.ExpectBucketCount( |
| "NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM, 1); |
| histogram_tester.ExpectBucketCount( |
| "NQE.Kbps.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM, 1); |
| histogram_tester.ExpectTotalCount("NQE.Kbps.RawObservation.HttpPlatform", 1); |
| histogram_tester.ExpectTotalCount("NQE.RTT.ObservationSource", 2); |
| histogram_tester.ExpectTotalCount("NQE.RTT.RawObservation.HttpPlatform", 1); |
| histogram_tester.ExpectTotalCount("NQE.RTT.RawObservation.TransportPlatform", |
| 1); |
| histogram_tester.ExpectTotalCount("NQE.Kbps.ObservationSource", 1); |
| |
| // Default observations should be added on connection change. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "unknown-1"); |
| histogram_tester.ExpectBucketCount( |
| "NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM, 2); |
| histogram_tester.ExpectBucketCount( |
| "NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM, 2); |
| histogram_tester.ExpectBucketCount( |
| "NQE.Kbps.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM, 2); |
| histogram_tester.ExpectTotalCount("NQE.Kbps.RawObservation.HttpPlatform", 2); |
| histogram_tester.ExpectTotalCount("NQE.RTT.ObservationSource", 4); |
| histogram_tester.ExpectTotalCount("NQE.RTT.RawObservation.HttpPlatform", 2); |
| histogram_tester.ExpectTotalCount("NQE.RTT.RawObservation.TransportPlatform", |
| 2); |
| histogram_tester.ExpectTotalCount("NQE.Kbps.ObservationSource", 2); |
| |
| base::TimeDelta rtt; |
| int32_t kbps; |
| |
| // Default estimates should be available. |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(115), rtt); |
| EXPECT_EQ(rtt, estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(55), rtt); |
| EXPECT_EQ(rtt, estimator.GetTransportRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| EXPECT_EQ(1961, kbps); |
| EXPECT_EQ(kbps, estimator.GetDownstreamThroughputKbps().value()); |
| |
| estimator.AddEffectiveConnectionTypeObserver( |
| &effective_connection_type_observer); |
| estimator.AddRTTAndThroughputEstimatesObserver( |
| &rtt_throughput_estimates_observer); |
| estimator.AddRTTObserver(&rtt_observer); |
| estimator.AddThroughputObserver(&throughput_observer); |
| |
| // Simulate network change to 3G. Default estimates should be available. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-3"); |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| // Taken from network_quality_estimator_params.cc. |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(273), rtt); |
| EXPECT_EQ(rtt, estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(209), rtt); |
| EXPECT_EQ(rtt, estimator.GetTransportRTT()); |
| EXPECT_TRUE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| EXPECT_EQ(749, kbps); |
| EXPECT_EQ(kbps, estimator.GetDownstreamThroughputKbps().value()); |
| |
| EXPECT_NE(EFFECTIVE_CONNECTION_TYPE_UNKNOWN, |
| estimator.GetEffectiveConnectionType()); |
| EXPECT_EQ( |
| 1U, |
| effective_connection_type_observer.effective_connection_types().size()); |
| EXPECT_NE( |
| EFFECTIVE_CONNECTION_TYPE_UNKNOWN, |
| effective_connection_type_observer.effective_connection_types().front()); |
| |
| // Verify the contents of the net log. |
| EXPECT_LE( |
| 3, estimator.GetEntriesCount(NetLogEventType::NETWORK_QUALITY_CHANGED)); |
| EXPECT_NE( |
| GetNameForEffectiveConnectionType(EFFECTIVE_CONNECTION_TYPE_UNKNOWN), |
| estimator.GetNetLogLastStringValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, |
| "effective_connection_type")); |
| |
| EXPECT_EQ(4, rtt_throughput_estimates_observer.notifications_received()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(273), |
| rtt_throughput_estimates_observer.http_rtt()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(209), |
| rtt_throughput_estimates_observer.transport_rtt()); |
| EXPECT_EQ(749, |
| rtt_throughput_estimates_observer.downstream_throughput_kbps()); |
| |
| EXPECT_EQ(2U, rtt_observer.observations().size()); |
| EXPECT_EQ(273, rtt_observer.observations().at(0).rtt_ms); |
| EXPECT_EQ(NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM, |
| rtt_observer.observations().at(0).source); |
| EXPECT_EQ(209, rtt_observer.observations().at(1).rtt_ms); |
| EXPECT_EQ(NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_TRANSPORT_FROM_PLATFORM, |
| rtt_observer.observations().at(1).source); |
| |
| EXPECT_EQ(1U, throughput_observer.observations().size()); |
| EXPECT_EQ(749, throughput_observer.observations().at(0).throughput_kbps); |
| EXPECT_EQ(NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM, |
| throughput_observer.observations().at(0).source); |
| } |
| |
| // Verifies that the default observations are added to the set of observations. |
| // If default observations are overridden using field trial parameters, verify |
| // that the overriding values are used. |
| TEST_F(NetworkQualityEstimatorTest, DefaultObservationsOverridden) { |
| 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"; |
| |
| variation_params["Unknown.DefaultMedianTransportRTTMsec"] = "500"; |
| variation_params["WiFi.DefaultMedianTransportRTTMsec"] = "1000"; |
| // Negative variation value should not be used. |
| variation_params["2G.DefaultMedianTransportRTTMsec"] = "-5"; |
| |
| TestNetworkQualityEstimator estimator(variation_params, false, false, |
| std::make_unique<BoundTestNetLog>()); |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "unknown-1"); |
| |
| base::TimeDelta rtt; |
| int32_t kbps; |
| |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1000), rtt); |
| EXPECT_EQ(rtt, estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(500), rtt); |
| EXPECT_EQ(rtt, estimator.GetTransportRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| EXPECT_EQ(100, kbps); |
| EXPECT_EQ(kbps, estimator.GetDownstreamThroughputKbps().value()); |
| |
| // Simulate network change to Wi-Fi. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(2000), rtt); |
| EXPECT_EQ(rtt, estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1000), rtt); |
| EXPECT_EQ(rtt, estimator.GetTransportRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| EXPECT_EQ(200, kbps); |
| EXPECT_EQ(kbps, estimator.GetDownstreamThroughputKbps().value()); |
| |
| // Simulate network change to 2G. Only the Kbps default estimate should be |
| // available. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-2"); |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| // Taken from network_quality_estimator_params.cc. |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1726), rtt); |
| EXPECT_EQ(rtt, estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1531), rtt); |
| EXPECT_EQ(rtt, estimator.GetTransportRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| EXPECT_EQ(300, kbps); |
| EXPECT_EQ(kbps, estimator.GetDownstreamThroughputKbps().value()); |
| |
| // Simulate network change to 3G. Default estimates should be available. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_3G, "test-3"); |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(273), rtt); |
| EXPECT_EQ(rtt, estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(209), rtt); |
| EXPECT_EQ(rtt, estimator.GetTransportRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| EXPECT_EQ(749, kbps); |
| EXPECT_EQ(kbps, estimator.GetDownstreamThroughputKbps().value()); |
| } |
| |
| // Tests that |GetEffectiveConnectionType| returns |
| // EFFECTIVE_CONNECTION_TYPE_OFFLINE when the device is currently offline. |
| TEST_F(NetworkQualityEstimatorTest, Offline) { |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| const struct { |
| NetworkChangeNotifier::ConnectionType connection_type; |
| EffectiveConnectionType expected_connection_type; |
| } tests[] = { |
| {NetworkChangeNotifier::CONNECTION_2G, EFFECTIVE_CONNECTION_TYPE_UNKNOWN}, |
| {NetworkChangeNotifier::CONNECTION_NONE, |
| EFFECTIVE_CONNECTION_TYPE_OFFLINE}, |
| {NetworkChangeNotifier::CONNECTION_3G, EFFECTIVE_CONNECTION_TYPE_UNKNOWN}, |
| }; |
| |
| for (const auto& test : tests) { |
| estimator.SimulateNetworkChange(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_F(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"; |
| |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| // Simulate the connection type as Wi-Fi so that GetEffectiveConnectionType |
| // does not return Offline if the device is offline. |
| estimator.SimulateNetworkChange(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test"); |
| |
| const struct { |
| int32_t rtt_msec; |
| EffectiveConnectionType expected_conn_type; |
| } tests[] = { |
| {5000, EFFECTIVE_CONNECTION_TYPE_OFFLINE}, |
| {4000, EFFECTIVE_CONNECTION_TYPE_OFFLINE}, |
| {3000, EFFECTIVE_CONNECTION_TYPE_SLOW_2G}, |
| {2000, EFFECTIVE_CONNECTION_TYPE_SLOW_2G}, |
| {1500, EFFECTIVE_CONNECTION_TYPE_2G}, |
| {1000, EFFECTIVE_CONNECTION_TYPE_2G}, |
| {700, EFFECTIVE_CONNECTION_TYPE_3G}, |
| {500, EFFECTIVE_CONNECTION_TYPE_3G}, |
| {400, EFFECTIVE_CONNECTION_TYPE_4G}, |
| {300, EFFECTIVE_CONNECTION_TYPE_4G}, |
| {200, EFFECTIVE_CONNECTION_TYPE_4G}, |
| {100, EFFECTIVE_CONNECTION_TYPE_4G}, |
| {20, EFFECTIVE_CONNECTION_TYPE_4G}, |
| }; |
| |
| for (const auto& test : tests) { |
| estimator.set_recent_http_rtt( |
| base::TimeDelta::FromMilliseconds(test.rtt_msec)); |
| estimator.set_start_time_null_downlink_throughput_kbps(INT32_MAX); |
| estimator.set_recent_downlink_throughput_kbps(INT32_MAX); |
| estimator.SetStartTimeNullHttpRtt( |
| base::TimeDelta::FromMilliseconds(test.rtt_msec)); |
| EXPECT_EQ(test.expected_conn_type, estimator.GetEffectiveConnectionType()); |
| } |
| } |
| |
| // Tests that default HTTP RTT thresholds for different effective |
| // connection types are correctly set. |
| TEST_F(NetworkQualityEstimatorTest, DefaultHttpRTTBasedThresholds) { |
| const struct { |
| bool override_defaults_using_variation_params; |
| int32_t http_rtt_msec; |
| EffectiveConnectionType expected_conn_type; |
| } tests[] = { |
| // When the variation params do not override connection thresholds, |
| // default values should be used. |
| {false, 5000, EFFECTIVE_CONNECTION_TYPE_SLOW_2G}, |
| {false, 4000, EFFECTIVE_CONNECTION_TYPE_SLOW_2G}, |
| {false, 3000, EFFECTIVE_CONNECTION_TYPE_SLOW_2G}, |
| {false, 2000, EFFECTIVE_CONNECTION_TYPE_2G}, |
| {false, 1500, EFFECTIVE_CONNECTION_TYPE_2G}, |
| {false, 1000, EFFECTIVE_CONNECTION_TYPE_3G}, |
| {false, 100, EFFECTIVE_CONNECTION_TYPE_4G}, |
| {false, 20, EFFECTIVE_CONNECTION_TYPE_4G}, |
| // Override default thresholds using variation params. |
| {true, 5000, EFFECTIVE_CONNECTION_TYPE_OFFLINE}, |
| {true, 4000, EFFECTIVE_CONNECTION_TYPE_OFFLINE}, |
| {true, 3000, EFFECTIVE_CONNECTION_TYPE_SLOW_2G}, |
| {true, 2000, EFFECTIVE_CONNECTION_TYPE_SLOW_2G}, |
| {true, 1500, EFFECTIVE_CONNECTION_TYPE_2G}, |
| {true, 1000, EFFECTIVE_CONNECTION_TYPE_2G}, |
| {true, 20, EFFECTIVE_CONNECTION_TYPE_4G}, |
| }; |
| |
| for (const auto& test : tests) { |
| std::map<std::string, std::string> variation_params; |
| if (test.override_defaults_using_variation_params) { |
| variation_params["Offline.ThresholdMedianHttpRTTMsec"] = "4000"; |
| variation_params["Slow2G.ThresholdMedianHttpRTTMsec"] = "2000"; |
| variation_params["2G.ThresholdMedianHttpRTTMsec"] = "1000"; |
| } |
| |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| // Simulate the connection type as Wi-Fi so that GetEffectiveConnectionType |
| // does not return Offline if the device is offline. |
| estimator.SimulateNetworkChange(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test"); |
| |
| estimator.SetStartTimeNullHttpRtt( |
| base::TimeDelta::FromMilliseconds(test.http_rtt_msec)); |
| estimator.set_recent_http_rtt( |
| base::TimeDelta::FromMilliseconds(test.http_rtt_msec)); |
| estimator.set_start_time_null_downlink_throughput_kbps(INT32_MAX); |
| estimator.set_recent_downlink_throughput_kbps(INT32_MAX); |
| EXPECT_EQ(test.expected_conn_type, estimator.GetEffectiveConnectionType()); |
| } |
| } |
| |
| // Tests that |GetEffectiveConnectionType| returns correct connection type when |
| // both HTTP RTT and throughput thresholds are specified in the variation |
| // params. |
| TEST_F(NetworkQualityEstimatorTest, ObtainThresholdsHttpRTTandThroughput) { |
| 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["Offline.ThresholdMedianKbps"] = "10"; |
| variation_params["Slow2G.ThresholdMedianKbps"] = "100"; |
| variation_params["2G.ThresholdMedianKbps"] = "300"; |
| variation_params["3G.ThresholdMedianKbps"] = "500"; |
| |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| // Simulate the connection type as Wi-Fi so that GetEffectiveConnectionType |
| // does not return Offline if the device is offline. |
| estimator.SimulateNetworkChange(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test"); |
| |
| const struct { |
| int32_t rtt_msec; |
| int32_t downlink_throughput_kbps; |
| EffectiveConnectionType expected_conn_type; |
| } tests[] = { |
| // Set RTT to a very low value to observe the effect of throughput. |
| // Throughput is the bottleneck. |
| {1, 5, EFFECTIVE_CONNECTION_TYPE_OFFLINE}, |
| {1, 10, EFFECTIVE_CONNECTION_TYPE_OFFLINE}, |
| {1, 50, EFFECTIVE_CONNECTION_TYPE_SLOW_2G}, |
| {1, 100, EFFECTIVE_CONNECTION_TYPE_SLOW_2G}, |
| {1, 150, EFFECTIVE_CONNECTION_TYPE_2G}, |
| {1, 300, EFFECTIVE_CONNECTION_TYPE_2G}, |
| {1, 400, EFFECTIVE_CONNECTION_TYPE_3G}, |
| {1, 500, EFFECTIVE_CONNECTION_TYPE_3G}, |
| {1, 700, EFFECTIVE_CONNECTION_TYPE_4G}, |
| {1, 1000, EFFECTIVE_CONNECTION_TYPE_4G}, |
| {1, 1500, EFFECTIVE_CONNECTION_TYPE_4G}, |
| {1, 2500, EFFECTIVE_CONNECTION_TYPE_4G}, |
| // Set both RTT and throughput. RTT is the bottleneck. |
| {3000, 25000, EFFECTIVE_CONNECTION_TYPE_SLOW_2G}, |
| {700, 25000, EFFECTIVE_CONNECTION_TYPE_3G}, |
| }; |
| |
| for (const auto& test : tests) { |
| estimator.SetStartTimeNullHttpRtt( |
| base::TimeDelta::FromMilliseconds(test.rtt_msec)); |
| estimator.set_recent_http_rtt( |
| base::TimeDelta::FromMilliseconds(test.rtt_msec)); |
| estimator.set_start_time_null_downlink_throughput_kbps( |
| test.downlink_throughput_kbps); |
| estimator.set_recent_downlink_throughput_kbps( |
| test.downlink_throughput_kbps); |
| // Run one main frame request to force recomputation of effective connection |
| // type. |
| estimator.RunOneRequest(); |
| EXPECT_EQ(test.expected_conn_type, estimator.GetEffectiveConnectionType()); |
| } |
| } |
| |
| TEST_F(NetworkQualityEstimatorTest, TestGetMetricsSince) { |
| std::map<std::string, std::string> variation_params; |
| |
| const base::TimeDelta rtt_threshold_3g = |
| base::TimeDelta::FromMilliseconds(30); |
| const base::TimeDelta rtt_threshold_4g = base::TimeDelta::FromMilliseconds(1); |
| |
| variation_params["3G.ThresholdMedianHttpRTTMsec"] = |
| base::IntToString(rtt_threshold_3g.InMilliseconds()); |
| variation_params["HalfLifeSeconds"] = "300000"; |
| variation_params["add_default_platform_observations"] = "false"; |
| |
| TestNetworkQualityEstimator estimator(variation_params); |
| base::TimeTicks now = base::TimeTicks::Now(); |
| base::TimeTicks old = now - base::TimeDelta::FromMilliseconds(1); |
| ASSERT_NE(old, now); |
| |
| estimator.SimulateNetworkChange( |
| 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_3g); |
| DCHECK_LT(old_tcp_rtt, rtt_threshold_3g); |
| |
| // First sample has very old timestamp. |
| for (size_t i = 0; i < 2; ++i) { |
| estimator.http_downstream_throughput_kbps_observations_.AddObservation( |
| NetworkQualityEstimator::Observation( |
| old_downlink_kbps, old, INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); |
| estimator.rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP] |
| .AddObservation(NetworkQualityEstimator::Observation( |
| old_url_rtt.InMilliseconds(), old, INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); |
| estimator |
| .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .AddObservation(NetworkQualityEstimator::Observation( |
| old_tcp_rtt.InMilliseconds(), old, INT32_MIN, |
| 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_3g); |
| DCHECK_GT(new_tcp_rtt, rtt_threshold_3g); |
| DCHECK_GT(new_url_rtt, rtt_threshold_4g); |
| DCHECK_GT(new_tcp_rtt, rtt_threshold_4g); |
| |
| estimator.http_downstream_throughput_kbps_observations_.AddObservation( |
| NetworkQualityEstimator::Observation( |
| new_downlink_kbps, now, INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); |
| estimator.rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP] |
| .AddObservation(NetworkQualityEstimator::Observation( |
| new_url_rtt.InMilliseconds(), now, INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); |
| estimator.rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .AddObservation(NetworkQualityEstimator::Observation( |
| new_tcp_rtt.InMilliseconds(), now, INT32_MIN, |
| 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; |
| EffectiveConnectionType expected_effective_connection_type; |
| } tests[] = { |
| {now + base::TimeDelta::FromSeconds(10), false, |
| base::TimeDelta::FromMilliseconds(0), |
| base::TimeDelta::FromMilliseconds(0), 0, EFFECTIVE_CONNECTION_TYPE_4G}, |
| {now, true, new_url_rtt, new_tcp_rtt, new_downlink_kbps, |
| EFFECTIVE_CONNECTION_TYPE_3G}, |
| {old - base::TimeDelta::FromMicroseconds(500), true, old_url_rtt, |
| old_tcp_rtt, old_downlink_kbps, EFFECTIVE_CONNECTION_TYPE_4G}, |
| |
| }; |
| 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.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| test.start_timestamp, &http_rtt, nullptr)); |
| EXPECT_EQ( |
| test.expect_network_quality_available, |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| test.start_timestamp, &transport_rtt, nullptr)); |
| EXPECT_EQ(test.expect_network_quality_available, |
| estimator.GetRecentDownlinkThroughputKbps( |
| 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)); |
| } |
| } |
| } |
| |
| // Tests if the throughput observation is taken correctly when local and network |
| // requests do not overlap. |
| TEST_F(NetworkQualityEstimatorTest, TestThroughputNoRequestOverlap) { |
| base::HistogramTester histogram_tester; |
| std::map<std::string, std::string> variation_params; |
| variation_params["throughput_min_requests_in_flight"] = "1"; |
| variation_params["add_default_platform_observations"] = "false"; |
| |
| static const struct { |
| bool allow_small_localhost_requests; |
| } tests[] = { |
| { |
| false, |
| }, |
| { |
| true, |
| }, |
| }; |
| |
| for (const auto& test : tests) { |
| TestNetworkQualityEstimator estimator(variation_params, |
| test.allow_small_localhost_requests, |
| test.allow_small_localhost_requests, |
| std::make_unique<BoundTestNetLog>()); |
| |
| base::TimeDelta rtt; |
| EXPECT_FALSE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| int32_t kbps; |
| EXPECT_FALSE( |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &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, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| |
| // Pump message loop to allow estimator tasks to be processed. |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(test.allow_small_localhost_requests, |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ( |
| test.allow_small_localhost_requests, |
| estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), &kbps)); |
| } |
| } |
| |
| #if defined(OS_IOS) |
| // Flaky on iOS: crbug.com/672917. |
| #define MAYBE_TestEffectiveConnectionTypeObserver \ |
| DISABLED_TestEffectiveConnectionTypeObserver |
| #else |
| #define MAYBE_TestEffectiveConnectionTypeObserver \ |
| TestEffectiveConnectionTypeObserver |
| #endif |
| |
| // Tests that the effective connection type is computed at the specified |
| // interval, and that the observers are notified of any change. |
| TEST_F(NetworkQualityEstimatorTest, MAYBE_TestEffectiveConnectionTypeObserver) { |
| base::HistogramTester histogram_tester; |
| base::SimpleTestTickClock tick_clock; |
| |
| TestEffectiveConnectionTypeObserver observer; |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| estimator.AddEffectiveConnectionTypeObserver(&observer); |
| // |observer| may be notified as soon as it is added. Run the loop to so that |
| // the notification to |observer| is finished. |
| base::RunLoop().RunUntilIdle(); |
| estimator.SetTickClockForTesting(&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.SetStartTimeNullHttpRtt(base::TimeDelta::FromMilliseconds(1500)); |
| estimator.set_start_time_null_downlink_throughput_kbps(100000); |
| |
| tick_clock.Advance(base::TimeDelta::FromMinutes(60)); |
| |
| std::unique_ptr<URLRequest> request( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| EXPECT_EQ(1U, observer.effective_connection_types().size()); |
| EXPECT_LE( |
| 1, estimator.GetEntriesCount(NetLogEventType::NETWORK_QUALITY_CHANGED)); |
| |
| // Verify the contents of the net log. |
| EXPECT_EQ(GetNameForEffectiveConnectionType(EFFECTIVE_CONNECTION_TYPE_2G), |
| estimator.GetNetLogLastStringValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, |
| "effective_connection_type")); |
| EXPECT_EQ(1500, estimator.GetNetLogLastIntegerValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, "http_rtt_ms")); |
| EXPECT_EQ(-1, |
| estimator.GetNetLogLastIntegerValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, "transport_rtt_ms")); |
| EXPECT_EQ(100000, estimator.GetNetLogLastIntegerValue( |
| NetLogEventType::NETWORK_QUALITY_CHANGED, |
| "downstream_throughput_kbps")); |
| |
| histogram_tester.ExpectUniqueSample("NQE.MainFrame.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, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request2->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request2->Start(); |
| test_delegate.RunUntilComplete(); |
| EXPECT_EQ(1U, observer.effective_connection_types().size()); |
| |
| // Change in connection type should send out notification to the observers. |
| estimator.SetStartTimeNullHttpRtt(base::TimeDelta::FromMilliseconds(500)); |
| estimator.SimulateNetworkChange(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test"); |
| EXPECT_EQ(3U, 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.SetStartTimeNullHttpRtt(base::TimeDelta::FromMilliseconds(100)); |
| EXPECT_EQ(4U, observer.effective_connection_types().size()); |
| |
| TestEffectiveConnectionTypeObserver observer_2; |
| estimator.AddEffectiveConnectionTypeObserver(&observer_2); |
| EXPECT_EQ(0U, observer_2.effective_connection_types().size()); |
| base::RunLoop().RunUntilIdle(); |
| // |observer_2| must be notified as soon as it is added. |
| EXPECT_EQ(1U, observer_2.effective_connection_types().size()); |
| |
| // |observer_3| should not be notified since it unregisters before the |
| // message loop is run. |
| TestEffectiveConnectionTypeObserver observer_3; |
| estimator.AddEffectiveConnectionTypeObserver(&observer_3); |
| EXPECT_EQ(0U, observer_3.effective_connection_types().size()); |
| estimator.RemoveEffectiveConnectionTypeObserver(&observer_3); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(0U, observer_3.effective_connection_types().size()); |
| } |
| |
| // Tests that the transport RTT is used for computing the HTTP RTT. |
| TEST_F(NetworkQualityEstimatorTest, TestTransportRttUsedForHttpRttComputation) { |
| const struct { |
| base::TimeDelta http_rtt; |
| base::TimeDelta transport_rtt; |
| base::TimeDelta expected_http_rtt; |
| EffectiveConnectionType expected_type; |
| } tests[] = { |
| { |
| base::TimeDelta::FromMilliseconds(200), |
| base::TimeDelta::FromMilliseconds(100), |
| base::TimeDelta::FromMilliseconds(200), EFFECTIVE_CONNECTION_TYPE_4G, |
| }, |
| { |
| base::TimeDelta::FromMilliseconds(100), |
| base::TimeDelta::FromMilliseconds(200), |
| base::TimeDelta::FromMilliseconds(200), EFFECTIVE_CONNECTION_TYPE_4G, |
| }, |
| { |
| base::TimeDelta::FromMilliseconds(100), |
| base::TimeDelta::FromMilliseconds(4000), |
| base::TimeDelta::FromMilliseconds(4000), |
| EFFECTIVE_CONNECTION_TYPE_SLOW_2G, |
| }, |
| }; |
| |
| for (const auto& test : tests) { |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| base::SimpleTestTickClock tick_clock; |
| tick_clock.Advance(base::TimeDelta::FromSeconds(1)); |
| estimator.SetTickClockForTesting(&tick_clock); |
| |
| estimator.SetStartTimeNullHttpRtt(test.http_rtt); |
| estimator.SetStartTimeNullTransportRtt(test.transport_rtt); |
| |
| // Minimum number of transport RTT samples that should be present before |
| // transport RTT estimate can be used to clamp the HTTP RTT. |
| estimator.SetTransportRTTAtastECTSampleCount( |
| estimator.params()->http_rtt_transport_rtt_min_count()); |
| |
| // Add one observation to ensure ECT is not computed for each request. |
| estimator.AddAndNotifyObserversOfRTT(NetworkQualityEstimator::Observation( |
| test.http_rtt.InMilliseconds(), tick_clock.NowTicks(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); |
| |
| EXPECT_EQ(test.expected_http_rtt, estimator.GetHttpRTT()); |
| EXPECT_EQ(test.transport_rtt, estimator.GetTransportRTT()); |
| EXPECT_EQ(test.expected_type, estimator.GetEffectiveConnectionType()); |
| } |
| } |
| |
| // Tests that the end to end RTT is used for computing the lower bound for HTTP |
| // RTT. |
| TEST_F(NetworkQualityEstimatorTest, TestEndToEndRttUsedForHttpRttComputation) { |
| const struct { |
| base::TimeDelta http_rtt; |
| base::TimeDelta end_to_end_rtt; |
| bool is_end_to_end_rtt_sample_count_enough; |
| base::TimeDelta expected_http_rtt; |
| EffectiveConnectionType expected_type; |
| } tests[] = { |
| { |
| base::TimeDelta::FromMilliseconds(200), |
| base::TimeDelta::FromMilliseconds(100), true, |
| base::TimeDelta::FromMilliseconds(200), EFFECTIVE_CONNECTION_TYPE_4G, |
| }, |
| { |
| // |http_rtt| is lower than |end_to_end_rtt|. The HTTP RTT estimate |
| // should be set to |end_to_end_rtt|. |
| base::TimeDelta::FromMilliseconds(100), |
| base::TimeDelta::FromMilliseconds(200), true, |
| base::TimeDelta::FromMilliseconds(200), EFFECTIVE_CONNECTION_TYPE_4G, |
| }, |
| { |
| // Not enough samples. End to End RTT should not be used. |
| base::TimeDelta::FromMilliseconds(100), |
| base::TimeDelta::FromMilliseconds(200), false, |
| base::TimeDelta::FromMilliseconds(100), EFFECTIVE_CONNECTION_TYPE_4G, |
| }, |
| { |
| base::TimeDelta::FromMilliseconds(100), |
| base::TimeDelta::FromMilliseconds(4000), true, |
| base::TimeDelta::FromMilliseconds(4000), |
| EFFECTIVE_CONNECTION_TYPE_SLOW_2G, |
| }, |
| }; |
| |
| for (const auto& test : tests) { |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| variation_params["use_end_to_end_rtt"] = "true"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| base::SimpleTestTickClock tick_clock; |
| tick_clock.Advance(base::TimeDelta::FromSeconds(1)); |
| estimator.SetTickClockForTesting(&tick_clock); |
| |
| estimator.SetStartTimeNullHttpRtt(test.http_rtt); |
| estimator.set_start_time_null_end_to_end_rtt(test.end_to_end_rtt); |
| |
| // Minimum number of end to end RTT samples that should be present before |
| // transport RTT estimate can be used to clamp the HTTP RTT. |
| if (test.is_end_to_end_rtt_sample_count_enough) { |
| estimator.set_start_time_null_end_to_end_rtt_observation_count( |
| estimator.params()->http_rtt_transport_rtt_min_count()); |
| } else { |
| estimator.set_start_time_null_end_to_end_rtt_observation_count( |
| estimator.params()->http_rtt_transport_rtt_min_count() - 1); |
| } |
| |
| // Ensure ECT is recomputed. |
| estimator.RunOneRequest(); |
| |
| EXPECT_EQ(test.expected_http_rtt, estimator.GetHttpRTT().value()); |
| EXPECT_EQ(test.expected_type, estimator.GetEffectiveConnectionType()); |
| } |
| } |
| |
| // Tests that the network quality is computed at the specified interval, and |
| // that the network quality observers are notified of any change. |
| TEST_F(NetworkQualityEstimatorTest, TestRTTAndThroughputEstimatesObserver) { |
| base::HistogramTester histogram_tester; |
| base::SimpleTestTickClock tick_clock; |
| |
| TestRTTAndThroughputEstimatesObserver observer; |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| estimator.AddRTTAndThroughputEstimatesObserver(&observer); |
| estimator.SetTickClockForTesting(&tick_clock); |
| |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.Init(); |
| |
| EXPECT_EQ(nqe::internal::InvalidRTT(), observer.http_rtt()); |
| EXPECT_EQ(nqe::internal::InvalidRTT(), observer.transport_rtt()); |
| EXPECT_EQ(nqe::internal::INVALID_RTT_THROUGHPUT, |
| observer.downstream_throughput_kbps()); |
| int notifications_received = observer.notifications_received(); |
| EXPECT_EQ(0, notifications_received); |
| |
| base::TimeDelta http_rtt(base::TimeDelta::FromMilliseconds(100)); |
| base::TimeDelta transport_rtt(base::TimeDelta::FromMilliseconds(200)); |
| int32_t downstream_throughput_kbps(300); |
| estimator.SetStartTimeNullHttpRtt(http_rtt); |
| estimator.SetStartTimeNullTransportRtt(transport_rtt); |
| estimator.set_start_time_null_downlink_throughput_kbps( |
| downstream_throughput_kbps); |
| tick_clock.Advance(base::TimeDelta::FromMinutes(60)); |
| |
| std::unique_ptr<URLRequest> request( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| EXPECT_EQ(http_rtt, observer.http_rtt()); |
| EXPECT_EQ(transport_rtt, observer.transport_rtt()); |
| EXPECT_EQ(downstream_throughput_kbps, observer.downstream_throughput_kbps()); |
| EXPECT_LE(1, observer.notifications_received() - notifications_received); |
| notifications_received = observer.notifications_received(); |
| |
| // The next request should not trigger recomputation of RTT or throughput |
| // since there has been no change in the clock. |
| std::unique_ptr<URLRequest> request2( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request2->Start(); |
| test_delegate.RunUntilComplete(); |
| EXPECT_LE(1, observer.notifications_received() - notifications_received); |
| notifications_received = observer.notifications_received(); |
| |
| // A change in the connection type should send out notification to the |
| // observers. |
| estimator.SimulateNetworkChange(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test"); |
| EXPECT_EQ(http_rtt, observer.http_rtt()); |
| EXPECT_EQ(transport_rtt, observer.transport_rtt()); |
| EXPECT_EQ(downstream_throughput_kbps, observer.downstream_throughput_kbps()); |
| EXPECT_LE(1, observer.notifications_received() - notifications_received); |
| notifications_received = observer.notifications_received(); |
| |
| // 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.SetStartTimeNullHttpRtt(base::TimeDelta::FromMilliseconds(10000)); |
| estimator.SetStartTimeNullHttpRtt(base::TimeDelta::FromMilliseconds(1)); |
| EXPECT_EQ(2, observer.notifications_received() - notifications_received); |
| |
| TestRTTAndThroughputEstimatesObserver observer_2; |
| estimator.AddRTTAndThroughputEstimatesObserver(&observer_2); |
| EXPECT_EQ(nqe::internal::InvalidRTT(), observer_2.http_rtt()); |
| EXPECT_EQ(nqe::internal::InvalidRTT(), observer_2.transport_rtt()); |
| EXPECT_EQ(nqe::internal::INVALID_RTT_THROUGHPUT, |
| observer_2.downstream_throughput_kbps()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_NE(nqe::internal::InvalidRTT(), observer_2.http_rtt()); |
| EXPECT_NE(nqe::internal::InvalidRTT(), observer_2.transport_rtt()); |
| EXPECT_NE(nqe::internal::INVALID_RTT_THROUGHPUT, |
| observer_2.downstream_throughput_kbps()); |
| |
| // |observer_3| should not be notified because it is unregisters before the |
| // message loop is run. |
| TestRTTAndThroughputEstimatesObserver observer_3; |
| estimator.AddRTTAndThroughputEstimatesObserver(&observer_3); |
| EXPECT_EQ(nqe::internal::InvalidRTT(), observer_3.http_rtt()); |
| EXPECT_EQ(nqe::internal::InvalidRTT(), observer_3.transport_rtt()); |
| EXPECT_EQ(nqe::internal::INVALID_RTT_THROUGHPUT, |
| observer_3.downstream_throughput_kbps()); |
| estimator.RemoveRTTAndThroughputEstimatesObserver(&observer_3); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(nqe::internal::InvalidRTT(), observer_3.http_rtt()); |
| EXPECT_EQ(nqe::internal::InvalidRTT(), observer_3.transport_rtt()); |
| EXPECT_EQ(nqe::internal::INVALID_RTT_THROUGHPUT, |
| observer_3.downstream_throughput_kbps()); |
| } |
| |
| // Tests that the effective connection type is computed on every RTT |
| // observation if the last computed effective connection type was unknown. |
| TEST_F(NetworkQualityEstimatorTest, UnknownEffectiveConnectionType) { |
| base::SimpleTestTickClock tick_clock; |
| |
| TestEffectiveConnectionTypeObserver observer; |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| estimator.SetTickClockForTesting(&tick_clock); |
| estimator.AddEffectiveConnectionTypeObserver(&observer); |
| tick_clock.Advance(base::TimeDelta::FromMinutes(60)); |
| |
| size_t expected_effective_connection_type_notifications = 0; |
| estimator.set_recent_effective_connection_type( |
| EFFECTIVE_CONNECTION_TYPE_UNKNOWN); |
| // Run one main frame request to force recomputation of effective connection |
| // type. |
| estimator.RunOneRequest(); |
| estimator.SimulateNetworkChange(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test"); |
| |
| NetworkQualityEstimator::Observation rtt_observation( |
| 5000, tick_clock.NowTicks(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP); |
| |
| for (size_t i = 0; i < 10; ++i) { |
| estimator.AddAndNotifyObserversOfRTT(rtt_observation); |
| EXPECT_EQ(expected_effective_connection_type_notifications, |
| observer.effective_connection_types().size()); |
| } |
| estimator.set_recent_effective_connection_type( |
| EFFECTIVE_CONNECTION_TYPE_SLOW_2G); |
| // Even though there are 10 RTT samples already available, the addition of one |
| // more RTT sample should trigger recomputation of the effective connection |
| // type since the last computed effective connection type was unknown. |
| estimator.AddAndNotifyObserversOfRTT(NetworkQualityEstimator::Observation( |
| 5000, tick_clock.NowTicks(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); |
| ++expected_effective_connection_type_notifications; |
| EXPECT_EQ(expected_effective_connection_type_notifications, |
| observer.effective_connection_types().size()); |
| } |
| |
| // Tests that the effective connection type is computed regularly depending |
| // on the number of RTT and bandwidth samples. |
| TEST_F(NetworkQualityEstimatorTest, |
| AdaptiveRecomputationEffectiveConnectionType) { |
| base::HistogramTester histogram_tester; |
| base::SimpleTestTickClock tick_clock; |
| |
| TestEffectiveConnectionTypeObserver observer; |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| estimator.SetTickClockForTesting(&tick_clock); |
| estimator.SimulateNetworkChange(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test"); |
| estimator.AddEffectiveConnectionTypeObserver(&observer); |
| // |observer| may be notified as soon as it is added. Run the loop to so that |
| // the notification to |observer| is finished. |
| base::RunLoop().RunUntilIdle(); |
| |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.Init(); |
| |
| EXPECT_EQ(0U, observer.effective_connection_types().size()); |
| |
| estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_2G); |
| tick_clock.Advance(base::TimeDelta::FromMinutes(60)); |
| |
| std::unique_ptr<URLRequest> request( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| EXPECT_EQ(1U, observer.effective_connection_types().size()); |
| histogram_tester.ExpectUniqueSample("NQE.MainFrame.EffectiveConnectionType", |
| EFFECTIVE_CONNECTION_TYPE_2G, 1); |
| histogram_tester.ExpectUniqueSample("NQE.EstimateAvailable.MainFrame.RTT", 0, |
| 1); |
| histogram_tester.ExpectUniqueSample( |
| "NQE.EstimateAvailable.MainFrame.TransportRTT", 0, 1); |
| histogram_tester.ExpectUniqueSample("NQE.EstimateAvailable.MainFrame.Kbps", 0, |
| 1); |
| EXPECT_LE(1u, |
| histogram_tester |
| .GetAllSamples("NQE.EffectiveConnectionType.OnECTComputation") |
| .size()); |
| |
| size_t expected_effective_connection_type_notifications = 1; |
| EXPECT_EQ(expected_effective_connection_type_notifications, |
| observer.effective_connection_types().size()); |
| |
| EXPECT_EQ( |
| expected_effective_connection_type_notifications, |
| (estimator.rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP] |
| .Size() + |
| estimator |
| .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .Size())); |
| |
| // Increase the number of RTT observations. Every time the number of RTT |
| // observations is more than doubled, effective connection type must be |
| // recomputed and notified to observers. |
| for (size_t repetition = 0; repetition < 2; ++repetition) { |
| // Change the effective connection type so that the observers are |
| // notified when the effective connection type is recomputed. |
| if (repetition % 2 == 0) { |
| estimator.set_recent_effective_connection_type( |
| EFFECTIVE_CONNECTION_TYPE_SLOW_2G); |
| } else { |
| estimator.set_recent_effective_connection_type( |
| EFFECTIVE_CONNECTION_TYPE_3G); |
| } |
| size_t rtt_observations_count = |
| (estimator |
| .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP] |
| .Size() + |
| estimator |
| .rtt_ms_observations_ |
| [nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .Size()) * |
| 0.5; |
| // Increase the number of RTT observations to more than twice the number |
| // of current observations. This should trigger recomputation of |
| // effective connection type. |
| for (size_t i = 0; i < rtt_observations_count + 1; ++i) { |
| estimator.AddAndNotifyObserversOfRTT(NetworkQualityEstimator::Observation( |
| 5000, tick_clock.NowTicks(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); |
| |
| if (i == rtt_observations_count) { |
| // Effective connection type must be recomputed since the number of RTT |
| // samples are now more than twice the number of RTT samples that were |
| // available when effective connection type was last computed. |
| ++expected_effective_connection_type_notifications; |
| } |
| EXPECT_EQ(expected_effective_connection_type_notifications, |
| observer.effective_connection_types().size()); |
| } |
| } |
| } |
| |
| TEST_F(NetworkQualityEstimatorTest, TestRttThroughputObservers) { |
| base::HistogramTester histogram_tester; |
| TestRTTObserver rtt_observer; |
| TestThroughputObserver throughput_observer; |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["throughput_min_requests_in_flight"] = "1"; |
| variation_params["add_default_platform_observations"] = "false"; |
| 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, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| |
| std::unique_ptr<URLRequest> request2( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request2->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request2->Start(); |
| test_delegate.RunUntilComplete(); |
| |
| // Pump message loop to allow estimator tasks to be processed. |
| base::RunLoop().RunUntilIdle(); |
| |
| // Both RTT and downstream throughput should be updated. |
| base::TimeDelta rtt; |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| |
| int32_t throughput; |
| EXPECT_TRUE(estimator.GetRecentDownlinkThroughputKbps(base::TimeTicks(), |
| &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_HTTP, 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_HTTP, observation.source); |
| } |
| |
| EXPECT_FALSE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| |
| // 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)); |
| |
| // Use a public IP address so that the socket watcher runs the RTT callback. |
| IPAddressList ip_list; |
| IPAddress ip_address; |
| ASSERT_TRUE(ip_address.AssignFromIPLiteral("157.0.0.1")); |
| ip_list.push_back(ip_address); |
| AddressList address_list = |
| AddressList::CreateFromIPAddressList(ip_list, "canonical.example.com"); |
| |
| std::unique_ptr<SocketPerformanceWatcher> tcp_watcher = |
| estimator.GetSocketPerformanceWatcherFactory() |
| ->CreateSocketPerformanceWatcher( |
| SocketPerformanceWatcherFactory::PROTOCOL_TCP, address_list); |
| |
| std::unique_ptr<SocketPerformanceWatcher> quic_watcher = |
| estimator.GetSocketPerformanceWatcherFactory() |
| ->CreateSocketPerformanceWatcher( |
| SocketPerformanceWatcherFactory::PROTOCOL_QUIC, address_list); |
| |
| tcp_watcher->OnUpdatedRTTAvailable(tcp_rtt); |
| // First RTT sample from QUIC connections is dropped, but the second RTT |
| // notification should not be dropped. |
| quic_watcher->OnUpdatedRTTAvailable(quic_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.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| |
| EXPECT_EQ(quic_rtt, estimator.end_to_end_rtt_.value()); |
| EXPECT_LT( |
| 0u, estimator.end_to_end_rtt_observation_count_at_last_ect_computation_); |
| const std::vector<base::Bucket> end_to_end_rtt_samples = |
| histogram_tester.GetAllSamples("NQE.EndToEndRTT.OnECTComputation"); |
| EXPECT_FALSE(end_to_end_rtt_samples.empty()); |
| for (const auto& bucket : end_to_end_rtt_samples) |
| EXPECT_EQ(quic_rtt.InMilliseconds(), bucket.min); |
| } |
| |
| TEST_F(NetworkQualityEstimatorTest, TestGlobalSocketWatcherThrottle) { |
| base::SimpleTestTickClock tick_clock; |
| tick_clock.Advance(base::TimeDelta::FromSeconds(1)); |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| estimator.SetTickClockForTesting(&tick_clock); |
| |
| TestRTTObserver rtt_observer; |
| estimator.AddRTTObserver(&rtt_observer); |
| |
| const base::TimeDelta tcp_rtt(base::TimeDelta::FromMilliseconds(1)); |
| |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.Init(); |
| |
| // Use a public IP address so that the socket watcher runs the RTT callback. |
| IPAddressList ip_list; |
| IPAddress ip_address; |
| ASSERT_TRUE(ip_address.AssignFromIPLiteral("157.0.0.1")); |
| ip_list.push_back(ip_address); |
| AddressList address_list = |
| AddressList::CreateFromIPAddressList(ip_list, "canonical.example.com"); |
| std::unique_ptr<SocketPerformanceWatcher> tcp_watcher = |
| estimator.GetSocketPerformanceWatcherFactory() |
| ->CreateSocketPerformanceWatcher( |
| SocketPerformanceWatcherFactory::PROTOCOL_TCP, address_list); |
| |
| EXPECT_EQ(0U, rtt_observer.observations().size()); |
| EXPECT_TRUE(tcp_watcher->ShouldNotifyUpdatedRTT()); |
| std::unique_ptr<URLRequest> request( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| EXPECT_EQ(1U, rtt_observer.observations().size()); |
| EXPECT_TRUE(tcp_watcher->ShouldNotifyUpdatedRTT()); |
| |
| tcp_watcher->OnUpdatedRTTAvailable(tcp_rtt); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(tcp_watcher->ShouldNotifyUpdatedRTT()); |
| EXPECT_EQ(2U, rtt_observer.observations().size()); |
| // Advancing the clock should make it possible to notify new RTT |
| // notifications. |
| tick_clock.Advance( |
| estimator.params()->socket_watchers_min_notification_interval()); |
| EXPECT_TRUE(tcp_watcher->ShouldNotifyUpdatedRTT()); |
| |
| EXPECT_EQ(tcp_rtt.InMilliseconds(), rtt_observer.observations().at(1).rtt_ms); |
| base::TimeDelta rtt; |
| EXPECT_TRUE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| } |
| |
| // TestTCPSocketRTT requires kernel support for tcp_info struct, and so it is |
| // enabled only on certain platforms. |
| #if defined(TCP_INFO) || (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || \ |
| defined(OS_ANDROID) |
| #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_F(NetworkQualityEstimatorTest, MAYBE_TestTCPSocketRTT) { |
| base::SimpleTestTickClock tick_clock; |
| tick_clock.Advance(base::TimeDelta::FromSeconds(1)); |
| |
| base::HistogramTester histogram_tester; |
| TestRTTObserver rtt_observer; |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["persistent_cache_reading_enabled"] = "true"; |
| variation_params["throughput_min_requests_in_flight"] = "1"; |
| TestNetworkQualityEstimator estimator(variation_params, true, true, |
| std::make_unique<BoundTestNetLog>()); |
| estimator.SetTickClockForTesting(&tick_clock); |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test"); |
| |
| estimator.AddRTTObserver(&rtt_observer); |
| // |observer| may be notified as soon as it is added. Run the loop to so that |
| // the notification to |observer| is finished. |
| base::RunLoop().RunUntilIdle(); |
| |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| |
| std::unique_ptr<HttpNetworkSession::Context> session_context( |
| new HttpNetworkSession::Context); |
| // |estimator| should be notified of TCP RTT observations. |
| session_context->socket_performance_watcher_factory = |
| estimator.GetSocketPerformanceWatcherFactory(); |
| context.set_http_network_session_context(std::move(session_context)); |
| context.Init(); |
| |
| EXPECT_EQ(0U, rtt_observer.observations().size()); |
| base::TimeDelta rtt; |
| EXPECT_TRUE(estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(rtt, estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(rtt, estimator.GetTransportRTT().value()); |
| |
| // 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, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| tick_clock.Advance( |
| estimator.params()->socket_watchers_min_notification_interval()); |
| |
| test_delegate.RunUntilComplete(); |
| |
| 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.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_HTTP, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_NE(nqe::internal::InvalidRTT(), estimator.GetHttpRTT().value()); |
| EXPECT_TRUE( |
| estimator.GetRecentRTT(nqe::internal::OBSERVATION_CATEGORY_TRANSPORT, |
| base::TimeTicks(), &rtt, nullptr)); |
| EXPECT_EQ(rtt, estimator.GetTransportRTT().value()); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); |
| |
| // Verify that metrics are logged correctly on main-frame requests. |
| histogram_tester.ExpectTotalCount("NQE.MainFrame.TransportRTT.Percentile50", |
| num_requests); |
| histogram_tester.ExpectUniqueSample("NQE.EstimateAvailable.MainFrame.RTT", 1, |
| num_requests); |
| histogram_tester.ExpectUniqueSample( |
| "NQE.EstimateAvailable.MainFrame.TransportRTT", 1, num_requests); |
| histogram_tester.ExpectUniqueSample("NQE.EstimateAvailable.MainFrame.Kbps", 1, |
| num_requests); |
| |
| histogram_tester.ExpectTotalCount("NQE.MainFrame.EffectiveConnectionType", |
| num_requests); |
| histogram_tester.ExpectBucketCount("NQE.MainFrame.EffectiveConnectionType", |
| EFFECTIVE_CONNECTION_TYPE_UNKNOWN, 0); |
| ExpectBucketCountAtLeast(&histogram_tester, "NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TCP, 1); |
| ExpectBucketCountAtLeast(&histogram_tester, "NQE.Kbps.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP, 1); |
| EXPECT_LE(1u, |
| histogram_tester |
| .GetAllSamples("NQE.EffectiveConnectionType.OnECTComputation") |
| .size()); |
| EXPECT_LE(1u, |
| histogram_tester.GetAllSamples("NQE.TransportRTT.OnECTComputation") |
| .size()); |
| EXPECT_LE(1u, |
| histogram_tester.GetAllSamples("NQE.RTT.OnECTComputation").size()); |
| |
| histogram_tester.ExpectBucketCount( |
| "NQE.Kbps.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE, 0); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test"); |
| histogram_tester.ExpectBucketCount( |
| "NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE, 1); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); |
| histogram_tester.ExpectBucketCount( |
| "NQE.RTT.ObservationSource", |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE, 2); |
| } |
| |
| #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_F(NetworkQualityEstimatorTest, MAYBE_RecordAccuracy) { |
| const int expected_rtt_msec = 500; |
| const int expected_downstream_throughput_kbps = 2000; |
| |
| const base::TimeDelta accuracy_recording_delays[] = { |
| base::TimeDelta::FromSeconds(0), base::TimeDelta::FromSeconds(1), |
| }; |
| |
| const struct { |
| base::TimeDelta rtt; |
| base::TimeDelta recent_rtt; |
| int32_t downstream_throughput_kbps; |
| int32_t recent_downstream_throughput_kbps; |
| EffectiveConnectionType effective_connection_type; |
| EffectiveConnectionType recent_effective_connection_type; |
| } tests[] = { |
| {base::TimeDelta::FromMilliseconds(expected_rtt_msec), |
| base::TimeDelta::FromMilliseconds(expected_rtt_msec), |
| expected_downstream_throughput_kbps, expected_downstream_throughput_kbps, |
| EFFECTIVE_CONNECTION_TYPE_3G, EFFECTIVE_CONNECTION_TYPE_3G}, |
| { |
| base::TimeDelta::FromMilliseconds(expected_rtt_msec + 1000), |
| base::TimeDelta::FromMilliseconds(expected_rtt_msec), |
| expected_downstream_throughput_kbps - 1, |
| expected_downstream_throughput_kbps, EFFECTIVE_CONNECTION_TYPE_2G, |
| EFFECTIVE_CONNECTION_TYPE_3G, |
| }, |
| { |
| base::TimeDelta::FromMilliseconds(expected_rtt_msec - 400), |
| base::TimeDelta::FromMilliseconds(expected_rtt_msec), |
| expected_downstream_throughput_kbps + 1, |
| expected_downstream_throughput_kbps, EFFECTIVE_CONNECTION_TYPE_4G, |
| EFFECTIVE_CONNECTION_TYPE_3G, |
| }, |
| }; |
| |
| for (const auto& accuracy_recording_delay : accuracy_recording_delays) { |
| for (const auto& test : tests) { |
| base::SimpleTestTickClock tick_clock; |
| tick_clock.Advance(base::TimeDelta::FromSeconds(1)); |
| |
| std::map<std::string, std::string> variation_params; |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| estimator.SetTickClockForTesting(&tick_clock); |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); |
| tick_clock.Advance(base::TimeDelta::FromSeconds(1)); |
| |
| 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.SetStartTimeNullHttpRtt(test.rtt); |
| estimator.set_recent_http_rtt(test.recent_rtt); |
| estimator.set_rtt_estimate_internal(test.recent_rtt); |
| estimator.SetStartTimeNullTransportRtt(test.rtt); |
| estimator.set_recent_transport_rtt(test.recent_rtt); |
| estimator.set_start_time_null_downlink_throughput_kbps( |
| test.downstream_throughput_kbps); |
| estimator.set_recent_downlink_throughput_kbps( |
| test.recent_downstream_throughput_kbps); |
| |
| 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, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| |
| if (accuracy_recording_delay != base::TimeDelta()) { |
| tick_clock.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 rtt_diff = std::abs(test.rtt.InMilliseconds() - |
| test.recent_rtt.InMilliseconds()); |
| const int kbps_diff = std::abs(test.downstream_throughput_kbps - |
| test.recent_downstream_throughput_kbps); |
| const int ect_diff = std::abs(test.effective_connection_type - |
| test.recent_effective_connection_type); |
| |
| const std::string rtt_sign_suffix_with_zero_samples = |
| test.rtt.InMilliseconds() - test.recent_rtt.InMilliseconds() >= 0 |
| ? "Negative" |
| : "Positive"; |
| const std::string kbps_sign_suffix_with_zero_samples = |
| test.downstream_throughput_kbps - |
| test.recent_downstream_throughput_kbps >= |
| 0 |
| ? "Negative" |
| : "Positive"; |
| |
| const std::string rtt_sign_suffix_with_one_sample = |
| rtt_sign_suffix_with_zero_samples == "Positive" ? "Negative" |
| : "Positive"; |
| const std::string ect_sign_suffix_with_zero_samples = |
| test.rtt.InMilliseconds() - test.recent_rtt.InMilliseconds() > 0 |
| ? "Positive" |
| : "Negative"; |
| |
| const std::string kbps_sign_suffix_with_one_sample = |
| kbps_sign_suffix_with_zero_samples == "Positive" ? "Negative" |
| : "Positive"; |
| const std::string ect_sign_suffix_with_one_sample = |
| ect_sign_suffix_with_zero_samples == "Positive" ? "Negative" |
| : "Positive"; |
| const std::string interval_value = |
| base::IntToString(accuracy_recording_delay.InSeconds()); |
| |
| histogram_tester.ExpectUniqueSample( |
| "NQE.Accuracy.DownstreamThroughputKbps.EstimatedObservedDiff." + |
| kbps_sign_suffix_with_one_sample + "." + interval_value + |
| ".1260_2540", |
| kbps_diff, 1); |
| histogram_tester.ExpectTotalCount( |
| "NQE.Accuracy.DownstreamThroughputKbps.EstimatedObservedDiff." + |
| kbps_sign_suffix_with_zero_samples + "." + interval_value + |
| ".1260_2540", |
| 0); |
| |
| histogram_tester.ExpectUniqueSample( |
| "NQE.Accuracy.EffectiveConnectionType.EstimatedObservedDiff." + |
| ect_sign_suffix_with_one_sample + "." + interval_value + ".3G", |
| ect_diff, 1); |
| histogram_tester.ExpectTotalCount( |
| "NQE.Accuracy.EffectiveConnectionType.EstimatedObservedDiff." + |
| ect_sign_suffix_with_zero_samples + "." + interval_value + ".3G", |
| 0); |
| |
| histogram_tester.ExpectUniqueSample( |
| "NQE.Accuracy.HttpRTT.EstimatedObservedDiff." + |
| rtt_sign_suffix_with_one_sample + "." + interval_value + |
| ".300_620", |
| rtt_diff, 1); |
| histogram_tester.ExpectTotalCount( |
| "NQE.Accuracy.HttpRTT.EstimatedObservedDiff." + |
| rtt_sign_suffix_with_zero_samples + "." + interval_value + |
| ".300_620", |
| 0); |
| |
| histogram_tester.ExpectUniqueSample( |
| "NQE.Accuracy.TransportRTT.EstimatedObservedDiff." + |
| rtt_sign_suffix_with_one_sample + "." + interval_value + |
| ".300_620", |
| rtt_diff, 1); |
| histogram_tester.ExpectTotalCount( |
| "NQE.Accuracy.TransportRTT.EstimatedObservedDiff." + |
| rtt_sign_suffix_with_zero_samples + "." + interval_value + |
| ".300_620", |
| 0); |
| } |
| } |
| } |
| |
| TEST_F(NetworkQualityEstimatorTest, TestRecordNetworkIDAvailability) { |
| base::HistogramTester histogram_tester; |
| TestNetworkQualityEstimator estimator; |
| |
| // The NetworkID is recorded as available on Wi-Fi connection. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); |
| histogram_tester.ExpectUniqueSample("NQE.NetworkIdAvailable", 1, 1); |
| |
| // The histogram is not recorded on an unknown connection. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, ""); |
| histogram_tester.ExpectTotalCount("NQE.NetworkIdAvailable", 1); |
| |
| // The NetworkID is recorded as not being available on a Wi-Fi connection |
| // with an empty SSID. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, ""); |
| histogram_tester.ExpectBucketCount("NQE.NetworkIdAvailable", 0, 1); |
| histogram_tester.ExpectTotalCount("NQE.NetworkIdAvailable", 2); |
| |
| // The NetworkID is recorded as being available on a Wi-Fi connection. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, "test-1"); |
| histogram_tester.ExpectBucketCount("NQE.NetworkIdAvailable", 1, 2); |
| histogram_tester.ExpectTotalCount("NQE.NetworkIdAvailable", 3); |
| |
| // The NetworkID is recorded as being available on a cellular connection. |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test-1"); |
| histogram_tester.ExpectBucketCount("NQE.NetworkIdAvailable", 1, 3); |
| histogram_tester.ExpectTotalCount("NQE.NetworkIdAvailable", 4); |
| } |
| |
| class TestNetworkQualitiesCacheObserver |
| : public nqe::internal::NetworkQualityStore::NetworkQualitiesCacheObserver { |
| public: |
| TestNetworkQualitiesCacheObserver() |
| : network_id_(net::NetworkChangeNotifier::CONNECTION_UNKNOWN, |
| std::string(), |
| INT32_MIN), |
| notification_received_(0) {} |
| ~TestNetworkQualitiesCacheObserver() override = default; |
| |
| void OnChangeInCachedNetworkQuality( |
| const nqe::internal::NetworkID& network_id, |
| const nqe::internal::CachedNetworkQuality& cached_network_quality) |
| override { |
| network_id_ = network_id; |
| notification_received_++; |
| } |
| |
| size_t get_notification_received_and_reset() { |
| size_t notification_received = notification_received_; |
| notification_received_ = 0; |
| return notification_received; |
| } |
| |
| nqe::internal::NetworkID network_id() const { return network_id_; } |
| |
| private: |
| nqe::internal::NetworkID network_id_; |
| size_t notification_received_; |
| DISALLOW_COPY_AND_ASSIGN(TestNetworkQualitiesCacheObserver); |
| }; |
| |
| TEST_F(NetworkQualityEstimatorTest, CacheObserver) { |
| TestNetworkQualitiesCacheObserver observer; |
| TestNetworkQualityEstimator estimator; |
| |
| // Add |observer| as a persistent caching observer. |
| estimator.AddNetworkQualitiesCacheObserver(&observer); |
| |
| estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_3G); |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "test3g"); |
| estimator.RunOneRequest(); |
| EXPECT_EQ(4u, observer.get_notification_received_and_reset()); |
| EXPECT_EQ("test3g", observer.network_id().id); |
| |
| estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_2G); |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test2g"); |
| // One notification should be received for the previous network |
| // ("test3g") right before the connection change event. The second |
| // notification should be received for the second network ("test2g"). |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(2u, observer.get_notification_received_and_reset()); |
| estimator.RunOneRequest(); |
| EXPECT_EQ("test2g", observer.network_id().id); |
| |
| estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_4G); |
| // Start multiple requests, but there should be only one notification |
| // received, since the effective connection type does not change. |
| estimator.RunOneRequest(); |
| estimator.RunOneRequest(); |
| estimator.RunOneRequest(); |
| EXPECT_EQ(1u, observer.get_notification_received_and_reset()); |
| |
| estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_2G); |
| estimator.RunOneRequest(); |
| EXPECT_EQ(1u, observer.get_notification_received_and_reset()); |
| |
| // Remove |observer|, and it should not receive any notifications. |
| estimator.RemoveNetworkQualitiesCacheObserver(&observer); |
| estimator.set_recent_effective_connection_type(EFFECTIVE_CONNECTION_TYPE_3G); |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_2G, "test2g"); |
| EXPECT_EQ(0u, observer.get_notification_received_and_reset()); |
| estimator.RunOneRequest(); |
| EXPECT_EQ(0u, observer.get_notification_received_and_reset()); |
| } |
| |
| // Tests that the value of the effective connection type can be forced through |
| // field trial parameters. |
| TEST_F(NetworkQualityEstimatorTest, |
| ForceEffectiveConnectionTypeThroughFieldTrial) { |
| for (int i = 0; i < EFFECTIVE_CONNECTION_TYPE_LAST; ++i) { |
| EffectiveConnectionType ect_type = static_cast<EffectiveConnectionType>(i); |
| std::map<std::string, std::string> variation_params; |
| variation_params[kForceEffectiveConnectionType] = |
| GetNameForEffectiveConnectionType( |
| static_cast<EffectiveConnectionType>(i)); |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| TestEffectiveConnectionTypeObserver ect_observer; |
| estimator.AddEffectiveConnectionTypeObserver(&ect_observer); |
| TestRTTAndThroughputEstimatesObserver rtt_throughput_observer; |
| estimator.AddRTTAndThroughputEstimatesObserver(&rtt_throughput_observer); |
| // |observer| may be notified as soon as it is added. Run the loop to so |
| // that the notification to |observer| is finished. |
| base::RunLoop().RunUntilIdle(); |
| |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.Init(); |
| |
| if (ect_type == EFFECTIVE_CONNECTION_TYPE_UNKNOWN) { |
| EXPECT_EQ(0U, ect_observer.effective_connection_types().size()); |
| } else { |
| EXPECT_EQ(1U, ect_observer.effective_connection_types().size()); |
| } |
| |
| std::unique_ptr<URLRequest> request( |
| context.CreateRequest(estimator.GetEchoURL(), DEFAULT_PRIORITY, |
| &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| request->SetLoadFlags(request->load_flags() | LOAD_MAIN_FRAME_DEPRECATED); |
| request->Start(); |
| test_delegate.RunUntilComplete(); |
| |
| // Pump message loop to allow estimator tasks to be processed. |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(i, estimator.GetEffectiveConnectionType()); |
| |
| size_t expected_count = |
| ect_type == EFFECTIVE_CONNECTION_TYPE_UNKNOWN ? 0 : 1; |
| ASSERT_EQ(expected_count, ect_observer.effective_connection_types().size()); |
| if (expected_count == 1) { |
| EffectiveConnectionType last_notified_type = |
| ect_observer.effective_connection_types().at( |
| ect_observer.effective_connection_types().size() - 1); |
| EXPECT_EQ(i, last_notified_type); |
| |
| if (ect_type == EFFECTIVE_CONNECTION_TYPE_UNKNOWN || |
| ect_type == EFFECTIVE_CONNECTION_TYPE_OFFLINE) { |
| EXPECT_EQ(nqe::internal::InvalidRTT(), |
| rtt_throughput_observer.http_rtt()); |
| EXPECT_EQ(nqe::internal::InvalidRTT(), |
| rtt_throughput_observer.transport_rtt()); |
| EXPECT_EQ(nqe::internal::INVALID_RTT_THROUGHPUT, |
| rtt_throughput_observer.downstream_throughput_kbps()); |
| } else { |
| EXPECT_EQ(estimator.params_->TypicalNetworkQuality(ect_type).http_rtt(), |
| rtt_throughput_observer.http_rtt()); |
| EXPECT_EQ( |
| estimator.params_->TypicalNetworkQuality(ect_type).transport_rtt(), |
| rtt_throughput_observer.transport_rtt()); |
| EXPECT_EQ(estimator.params_->TypicalNetworkQuality(ect_type) |
| .downstream_throughput_kbps(), |
| rtt_throughput_observer.downstream_throughput_kbps()); |
| } |
| } |
| } |
| } |
| |
| // Tests that the value of the effective connection type can be forced after |
| // network quality estimator has been initialized. |
| TEST_F(NetworkQualityEstimatorTest, SimulateNetworkQualityChangeForTesting) { |
| for (int i = 0; i < EFFECTIVE_CONNECTION_TYPE_LAST; ++i) { |
| EffectiveConnectionType ect_type = static_cast<EffectiveConnectionType>(i); |
| TestNetworkQualityEstimator estimator; |
| |
| TestEffectiveConnectionTypeObserver ect_observer; |
| estimator.AddEffectiveConnectionTypeObserver(&ect_observer); |
| |
| // |observer| may be notified as soon as it is added. Run the loop to so |
| // that the notification to |observer| is finished. |
| base::RunLoop().RunUntilIdle(); |
| |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.Init(); |
| estimator.SimulateNetworkQualityChangeForTesting(ect_type); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_EQ(ect_type, ect_observer.effective_connection_types().back()); |
| } |
| } |
| |
| // Test that the typical network qualities are set correctly. |
| TEST_F(NetworkQualityEstimatorTest, TypicalNetworkQualities) { |
| TestNetworkQualityEstimator estimator; |
| TestDelegate test_delegate; |
| TestURLRequestContext context(true); |
| context.set_network_quality_estimator(&estimator); |
| context.Init(); |
| |
| for (size_t effective_connection_type = EFFECTIVE_CONNECTION_TYPE_SLOW_2G; |
| effective_connection_type <= EFFECTIVE_CONNECTION_TYPE_4G; |
| ++effective_connection_type) { |
| // Set the RTT and throughput values to the typical values for |
| // |effective_connection_type|. The effective connection type should be |
| // computed as |effective_connection_type|. |
| estimator.SetStartTimeNullHttpRtt( |
| estimator.params_ |
| ->TypicalNetworkQuality( |
| static_cast<EffectiveConnectionType>(effective_connection_type)) |
| .http_rtt()); |
| estimator.set_start_time_null_downlink_throughput_kbps(INT32_MAX); |
| estimator.SetStartTimeNullTransportRtt( |
| estimator.params_ |
| ->TypicalNetworkQuality( |
| static_cast<EffectiveConnectionType>(effective_connection_type)) |
| .transport_rtt()); |
| |
| EXPECT_EQ(effective_connection_type, |
| static_cast<size_t>(estimator.GetEffectiveConnectionType())); |
| } |
| } |
| |
| // Verify that the cached network qualities from the prefs are correctly used. |
| TEST_F(NetworkQualityEstimatorTest, OnPrefsRead) { |
| base::HistogramTester histogram_tester; |
| |
| // Construct the read prefs. |
| std::map<nqe::internal::NetworkID, nqe::internal::CachedNetworkQuality> |
| read_prefs; |
| read_prefs[nqe::internal::NetworkID(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test_ect_2g", INT32_MIN)] = |
| nqe::internal::CachedNetworkQuality(EFFECTIVE_CONNECTION_TYPE_2G); |
| read_prefs[nqe::internal::NetworkID(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test_ect_slow_2g", INT32_MIN)] = |
| nqe::internal::CachedNetworkQuality(EFFECTIVE_CONNECTION_TYPE_SLOW_2G); |
| read_prefs[nqe::internal::NetworkID(NetworkChangeNotifier::CONNECTION_4G, |
| "test_ect_4g", INT32_MIN)] = |
| nqe::internal::CachedNetworkQuality(EFFECTIVE_CONNECTION_TYPE_4G); |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["persistent_cache_reading_enabled"] = "true"; |
| variation_params["add_default_platform_observations"] = "false"; |
| // Disable default platform values so that the effect of cached estimates |
| // at the time of startup can be studied in isolation. |
| TestNetworkQualityEstimator estimator(variation_params, true, true, |
| std::make_unique<BoundTestNetLog>()); |
| |
| // Add observers. |
| TestRTTObserver rtt_observer; |
| TestThroughputObserver throughput_observer; |
| TestRTTAndThroughputEstimatesObserver rtt_throughput_observer; |
| TestEffectiveConnectionTypeObserver effective_connection_type_observer; |
| estimator.AddRTTObserver(&rtt_observer); |
| estimator.AddThroughputObserver(&throughput_observer); |
| estimator.AddRTTAndThroughputEstimatesObserver(&rtt_throughput_observer); |
| estimator.AddEffectiveConnectionTypeObserver( |
| &effective_connection_type_observer); |
| |
| std::string network_name("test_ect_2g"); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, network_name); |
| EXPECT_EQ(0u, rtt_observer.observations().size()); |
| EXPECT_EQ(0u, throughput_observer.observations().size()); |
| EXPECT_LE(0, rtt_throughput_observer.notifications_received()); |
| |
| // Simulate reading of prefs. |
| estimator.OnPrefsRead(read_prefs); |
| histogram_tester.ExpectUniqueSample("NQE.Prefs.ReadSize", read_prefs.size(), |
| 1); |
| |
| // Taken from network_quality_estimator_params.cc. |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1800), |
| rtt_observer.last_rtt( |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1500), |
| rtt_observer.last_rtt( |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE)); |
| EXPECT_EQ(1u, throughput_observer.observations().size()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1800), |
| rtt_throughput_observer.http_rtt()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1500), |
| rtt_throughput_observer.transport_rtt()); |
| EXPECT_EQ(75, rtt_throughput_observer.downstream_throughput_kbps()); |
| EXPECT_LE( |
| 1u, |
| effective_connection_type_observer.effective_connection_types().size()); |
| // Compare the ECT stored in prefs with the observer's last entry. |
| EXPECT_EQ( |
| read_prefs[nqe::internal::NetworkID( |
| NetworkChangeNotifier::CONNECTION_WIFI, network_name, |
| INT32_MIN)] |
| .effective_connection_type(), |
| effective_connection_type_observer.effective_connection_types().back()); |
| |
| // Change to a different connection type. |
| network_name = "test_ect_slow_2g"; |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, network_name); |
| |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(3600), |
| rtt_observer.last_rtt( |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(3000), |
| rtt_observer.last_rtt( |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE)); |
| EXPECT_EQ(2U, throughput_observer.observations().size()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(3600), |
| rtt_throughput_observer.http_rtt()); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(3000), |
| rtt_throughput_observer.transport_rtt()); |
| EXPECT_EQ(40, rtt_throughput_observer.downstream_throughput_kbps()); |
| EXPECT_LE( |
| 2u, |
| effective_connection_type_observer.effective_connection_types().size()); |
| // Compare with the last entry. |
| EXPECT_EQ( |
| read_prefs[nqe::internal::NetworkID( |
| NetworkChangeNotifier::CONNECTION_WIFI, network_name, |
| INT32_MIN)] |
| .effective_connection_type(), |
| effective_connection_type_observer.effective_connection_types().back()); |
| |
| // Cleanup. |
| estimator.RemoveRTTObserver(&rtt_observer); |
| estimator.RemoveThroughputObserver(&throughput_observer); |
| estimator.RemoveRTTAndThroughputEstimatesObserver(&rtt_throughput_observer); |
| estimator.RemoveEffectiveConnectionTypeObserver( |
| &effective_connection_type_observer); |
| } |
| |
| // Verify that the cached network qualities from the prefs are not used if the |
| // reading of the network quality prefs is not enabled.. |
| TEST_F(NetworkQualityEstimatorTest, OnPrefsReadWithReadingDisabled) { |
| base::HistogramTester histogram_tester; |
| |
| // Construct the read prefs. |
| std::map<nqe::internal::NetworkID, nqe::internal::CachedNetworkQuality> |
| read_prefs; |
| read_prefs[nqe::internal::NetworkID(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test_ect_2g", INT32_MIN)] = |
| nqe::internal::CachedNetworkQuality(EFFECTIVE_CONNECTION_TYPE_2G); |
| read_prefs[nqe::internal::NetworkID(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test_ect_slow_2g", INT32_MIN)] = |
| nqe::internal::CachedNetworkQuality(EFFECTIVE_CONNECTION_TYPE_SLOW_2G); |
| read_prefs[nqe::internal::NetworkID(NetworkChangeNotifier::CONNECTION_4G, |
| "test_ect_4g", INT32_MIN)] = |
| nqe::internal::CachedNetworkQuality(EFFECTIVE_CONNECTION_TYPE_4G); |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["persistent_cache_reading_enabled"] = "false"; |
| variation_params["add_default_platform_observations"] = "false"; |
| |
| // Disable default platform values so that the effect of cached estimates |
| // at the time of startup can be studied in isolation. |
| TestNetworkQualityEstimator estimator(variation_params, true, true, |
| std::make_unique<BoundTestNetLog>()); |
| |
| // Add observers. |
| TestRTTObserver rtt_observer; |
| TestThroughputObserver throughput_observer; |
| TestRTTAndThroughputEstimatesObserver rtt_throughput_observer; |
| TestEffectiveConnectionTypeObserver effective_connection_type_observer; |
| estimator.AddRTTObserver(&rtt_observer); |
| estimator.AddThroughputObserver(&throughput_observer); |
| estimator.AddRTTAndThroughputEstimatesObserver(&rtt_throughput_observer); |
| estimator.AddEffectiveConnectionTypeObserver( |
| &effective_connection_type_observer); |
| |
| std::string network_name("test_ect_2g"); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, network_name); |
| EXPECT_EQ(0u, rtt_observer.observations().size()); |
| EXPECT_EQ(0u, throughput_observer.observations().size()); |
| EXPECT_LE(0, rtt_throughput_observer.notifications_received()); |
| |
| // Simulate reading of prefs. |
| estimator.OnPrefsRead(read_prefs); |
| histogram_tester.ExpectUniqueSample("NQE.Prefs.ReadSize", read_prefs.size(), |
| 1); |
| |
| // Force read the network quality store from the store to verify that store |
| // gets populated even if reading of prefs is not enabled. |
| nqe::internal::CachedNetworkQuality cached_network_quality; |
| EXPECT_TRUE(estimator.network_quality_store_->GetById( |
| nqe::internal::NetworkID(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test_ect_2g", INT32_MIN), |
| &cached_network_quality)); |
| EXPECT_EQ(EFFECTIVE_CONNECTION_TYPE_2G, |
| cached_network_quality.effective_connection_type()); |
| |
| // Taken from network_quality_estimator_params.cc. |
| EXPECT_EQ(nqe::internal::InvalidRTT(), |
| rtt_observer.last_rtt( |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE)); |
| EXPECT_EQ(nqe::internal::InvalidRTT(), |
| rtt_observer.last_rtt( |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE)); |
| EXPECT_EQ(0u, throughput_observer.observations().size()); |
| |
| EXPECT_EQ( |
| 0u, |
| effective_connection_type_observer.effective_connection_types().size()); |
| |
| // Change to a different connection type. |
| network_name = "test_ect_slow_2g"; |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, network_name); |
| |
| EXPECT_EQ(nqe::internal::InvalidRTT(), |
| rtt_observer.last_rtt( |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE)); |
| EXPECT_EQ(nqe::internal::InvalidRTT(), |
| rtt_observer.last_rtt( |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE)); |
| EXPECT_EQ(0U, throughput_observer.observations().size()); |
| |
| // Cleanup. |
| estimator.RemoveRTTObserver(&rtt_observer); |
| estimator.RemoveThroughputObserver(&throughput_observer); |
| estimator.RemoveRTTAndThroughputEstimatesObserver(&rtt_throughput_observer); |
| estimator.RemoveEffectiveConnectionTypeObserver( |
| &effective_connection_type_observer); |
| } |
| |
| // Tests that |ComputeBandwidthDelayProduct| calculates the |
| // BDP correctly and records histogram data. |
| TEST_F(NetworkQualityEstimatorTest, TestBDPComputation) { |
| TestNetworkQualityEstimator estimator; |
| base::HistogramTester histogram_tester; |
| base::TimeTicks now = base::TimeTicks::Now(); |
| for (int i = 1; i <= std::pow(2, 10); i *= 2) { |
| estimator |
| .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .AddObservation(NetworkQualityEstimator::Observation( |
| i, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP)); |
| } |
| for (int i = 1; i <= std::pow(3, 10); i *= 3) { |
| estimator.http_downstream_throughput_kbps_observations_.AddObservation( |
| NetworkQualityEstimator::Observation( |
| i, now, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); |
| } |
| estimator.RunOneRequest(); |
| |
| // Histograms must contain at least one entry each. |
| EXPECT_GE( |
| 1u, histogram_tester |
| .GetAllSamples("NQE.BDPComputationTransportRTT.OnECTComputation") |
| .size()); |
| EXPECT_GE(1u, histogram_tester |
| .GetAllSamples("NQE.BDPComputationKbps.OnECTComputation") |
| .size()); |
| EXPECT_GE( |
| 1u, |
| histogram_tester.GetAllSamples("NQE.BDPKbits.OnECTComputation").size()); |
| |
| EXPECT_TRUE(estimator.GetBandwidthDelayProductKbits().has_value()); |
| EXPECT_EQ(estimator.GetBandwidthDelayProductKbits().value(), |
| (int32_t)(std::pow(2, 2) * std::pow(3, 8) / 1000)); |
| } |
| |
| TEST_F(NetworkQualityEstimatorTest, |
| TestComputeIncreaseInTransportRTTFullHostsOverlap) { |
| base::SimpleTestTickClock tick_clock; |
| tick_clock.Advance(base::TimeDelta::FromMinutes(1)); |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| estimator.SetTickClockForTesting(&tick_clock); |
| |
| base::TimeTicks now = tick_clock.NowTicks(); |
| base::TimeTicks recent = now - base::TimeDelta::FromMilliseconds(2500); |
| base::TimeTicks historical = now - base::TimeDelta::FromSeconds(20); |
| |
| // Add historical observations. The 0 percentile for |host| is |10 * host| |
| // ms. |
| for (int host = 1; host <= 3; ++host) { |
| for (int rtt = 10 * host; rtt <= 10 * host + 20; ++rtt) { |
| estimator |
| .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .AddObservation(NetworkQualityEstimator::Observation( |
| rtt, historical, INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TCP, |
| static_cast<uint64_t>(host))); |
| } |
| } |
| |
| // Add recent observations. The 50 percentile for |host| is |10 * host + 10| |
| // ms. The difference between them is expected to be 10 ms. |
| for (int host = 1; host <= 3; ++host) { |
| for (int rtt = 10 * host + 5; rtt <= 10 * host + 15; ++rtt) { |
| estimator |
| .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .AddObservation(NetworkQualityEstimator::Observation( |
| rtt, recent, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP, |
| static_cast<uint64_t>(host))); |
| } |
| } |
| |
| EXPECT_EQ(10, estimator.ComputeIncreaseInTransportRTTForTests().value_or(0)); |
| } |
| |
| TEST_F(NetworkQualityEstimatorTest, |
| TestComputeIncreaseInTransportRTTPartialHostsOverlap) { |
| base::SimpleTestTickClock tick_clock; |
| tick_clock.Advance(base::TimeDelta::FromMinutes(1)); |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| estimator.SetTickClockForTesting(&tick_clock); |
| |
| base::TimeTicks now = tick_clock.NowTicks(); |
| base::TimeTicks recent = now - base::TimeDelta::FromMilliseconds(2500); |
| base::TimeTicks historical = now - base::TimeDelta::FromSeconds(20); |
| |
| // Add historical observations for hosts 1 and 2 with minimum RTT as |
| // |10 * host|. |
| for (int host = 1; host <= 2; ++host) { |
| for (int rtt = 10 * host; rtt <= 10 * host + 20; ++rtt) { |
| estimator.AddAndNotifyObserversOfRTT(NetworkQualityEstimator::Observation( |
| rtt, historical, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP, |
| static_cast<uint64_t>(host))); |
| } |
| } |
| |
| // Add recent observations, with median RTT as |10 + host| over the |
| // historical minimum for hosts 2 and 3. |
| for (int host = 2; host <= 3; ++host) { |
| for (int rtt = 11 * host + 5; rtt <= 11 * host + 15; ++rtt) { |
| estimator.AddAndNotifyObserversOfRTT(NetworkQualityEstimator::Observation( |
| rtt, recent, INT32_MIN, NETWORK_QUALITY_OBSERVATION_SOURCE_TCP, |
| static_cast<uint64_t>(host))); |
| } |
| } |
| |
| // Only host 2 should have contributed to the calculation. Hence, the median |
| // should be |10 + 2 = 12|. |
| EXPECT_EQ(12, estimator.ComputeIncreaseInTransportRTTForTests().value_or(0)); |
| } |
| |
| // Verifies that when the cached network qualities from the prefs are available, |
| // then estimates from the platform or the external estimate provider are not |
| // used. |
| TEST_F(NetworkQualityEstimatorTest, |
| ObservationDiscardedIfCachedEstimateAvailable) { |
| base::HistogramTester histogram_tester; |
| |
| // Construct the read prefs. |
| std::map<nqe::internal::NetworkID, nqe::internal::CachedNetworkQuality> |
| read_prefs; |
| read_prefs[nqe::internal::NetworkID(NetworkChangeNotifier::CONNECTION_WIFI, |
| "test_2g", INT32_MIN)] = |
| nqe::internal::CachedNetworkQuality(EFFECTIVE_CONNECTION_TYPE_2G); |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["persistent_cache_reading_enabled"] = "true"; |
| variation_params["add_default_platform_observations"] = "false"; |
| // Disable default platform values so that the effect of cached estimates |
| // at the time of startup can be studied in isolation. |
| TestNetworkQualityEstimator estimator(variation_params, true, true, |
| std::make_unique<BoundTestNetLog>()); |
| |
| // Add observers. |
| TestRTTObserver rtt_observer; |
| TestThroughputObserver throughput_observer; |
| estimator.AddRTTObserver(&rtt_observer); |
| estimator.AddThroughputObserver(&throughput_observer); |
| |
| std::string network_name("test_2g"); |
| |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, network_name); |
| EXPECT_EQ(0u, rtt_observer.observations().size()); |
| EXPECT_EQ(0u, throughput_observer.observations().size()); |
| EXPECT_EQ( |
| 0u, |
| estimator |
| .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .Size()); |
| EXPECT_EQ(0u, estimator.http_downstream_throughput_kbps_observations_.Size()); |
| |
| // Simulate reading of prefs. |
| estimator.OnPrefsRead(read_prefs); |
| histogram_tester.ExpectUniqueSample("NQE.Prefs.ReadSize", read_prefs.size(), |
| 1); |
| |
| // Taken from network_quality_estimator_params.cc. |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1800), |
| rtt_observer.last_rtt( |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_CACHED_ESTIMATE)); |
| EXPECT_EQ(base::TimeDelta::FromMilliseconds(1500), |
| rtt_observer.last_rtt( |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TRANSPORT_CACHED_ESTIMATE)); |
| EXPECT_EQ(2u, rtt_observer.observations().size()); |
| |
| // RTT observation with source |
| // DEPRECATED_NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_EXTERNAL_ESTIMATE should |
| // be removed from |estimator.rtt_ms_observations_| when a cached estimate is |
| // received. |
| EXPECT_EQ( |
| 1u, |
| estimator.rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP] |
| .Size()); |
| EXPECT_EQ( |
| 1u, |
| estimator |
| .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .Size()); |
| |
| // When a cached estimate is available, RTT observations from the external |
| // estimate provider and platform must be discarded. |
| estimator.AddAndNotifyObserversOfRTT(nqe::internal::Observation( |
| 1, base::TimeTicks::Now(), INT32_MIN, |
| DEPRECATED_NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_EXTERNAL_ESTIMATE)); |
| estimator.AddAndNotifyObserversOfRTT(nqe::internal::Observation( |
| 1, base::TimeTicks::Now(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM)); |
| EXPECT_EQ(3u, rtt_observer.observations().size()); |
| EXPECT_EQ( |
| 2u, |
| estimator.rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP] |
| .Size()); |
| EXPECT_EQ( |
| 1u, |
| estimator |
| .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .Size()); |
| estimator.AddAndNotifyObserversOfRTT( |
| nqe::internal::Observation(1, base::TimeTicks::Now(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); |
| EXPECT_EQ(4u, rtt_observer.observations().size()); |
| EXPECT_EQ( |
| 3u, |
| estimator.rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_HTTP] |
| .Size()); |
| EXPECT_EQ( |
| 1u, |
| estimator |
| .rtt_ms_observations_[nqe::internal::OBSERVATION_CATEGORY_TRANSPORT] |
| .Size()); |
| |
| // When a cached estimate is available, throughput observations from the |
| // external estimate provider and platform must be discarded. |
| EXPECT_EQ(1u, throughput_observer.observations().size()); |
| // Throughput observation with source |
| // DEPRECATED_NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_EXTERNAL_ESTIMATE should |
| // be removed from |estimator.downstream_throughput_kbps_observations_| when a |
| // cached estimate is received. |
| EXPECT_EQ(1u, estimator.http_downstream_throughput_kbps_observations_.Size()); |
| estimator.AddAndNotifyObserversOfThroughput(nqe::internal::Observation( |
| 1, base::TimeTicks::Now(), INT32_MIN, |
| DEPRECATED_NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP_EXTERNAL_ESTIMATE)); |
| estimator.AddAndNotifyObserversOfThroughput(nqe::internal::Observation( |
| 1, base::TimeTicks::Now(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_DEFAULT_HTTP_FROM_PLATFORM)); |
| EXPECT_EQ(2u, throughput_observer.observations().size()); |
| EXPECT_EQ(2u, estimator.http_downstream_throughput_kbps_observations_.Size()); |
| estimator.AddAndNotifyObserversOfThroughput( |
| nqe::internal::Observation(1, base::TimeTicks::Now(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP)); |
| EXPECT_EQ(3u, throughput_observer.observations().size()); |
| EXPECT_EQ(3u, estimator.http_downstream_throughput_kbps_observations_.Size()); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // Tests that the ECT is computed when more than N RTT samples have been |
| // received. |
| TEST_F(NetworkQualityEstimatorTest, MaybeComputeECTAfterNSamples) { |
| base::SimpleTestTickClock tick_clock; |
| tick_clock.Advance(base::TimeDelta::FromMinutes(1)); |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| TestNetworkQualityEstimator estimator(variation_params); |
| estimator.DisableOfflineCheckForTesting(true); |
| base::RunLoop().RunUntilIdle(); |
| estimator.SetTickClockForTesting(&tick_clock); |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "test"); |
| tick_clock.Advance(base::TimeDelta::FromMinutes(1)); |
| |
| const base::TimeDelta rtt = base::TimeDelta::FromSeconds(1); |
| uint64_t host = 1u; |
| |
| // Fill the observation buffer so that ECT computations are not triggered due |
| // to observation buffer's size increasing to 1.5x. |
| for (size_t i = 0; i < estimator.params()->observation_buffer_size(); ++i) { |
| estimator.AddAndNotifyObserversOfRTT(NetworkQualityEstimator::Observation( |
| rtt.InMilliseconds(), tick_clock.NowTicks(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP, host)); |
| } |
| EXPECT_EQ(rtt, estimator.GetHttpRTT().value()); |
| tick_clock.Advance(base::TimeDelta::FromMinutes(60)); |
| |
| const base::TimeDelta rtt_new = base::TimeDelta::FromSeconds(3); |
| for (size_t i = 0; |
| i < estimator.params()->count_new_observations_received_compute_ect(); |
| ++i) { |
| estimator.AddAndNotifyObserversOfRTT(NetworkQualityEstimator::Observation( |
| rtt_new.InMilliseconds(), tick_clock.NowTicks(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_HTTP, host)); |
| } |
| EXPECT_EQ(rtt_new, estimator.GetHttpRTT().value()); |
| } |
| |
| // Tests that the hanging request is correctly detected. |
| TEST_F(NetworkQualityEstimatorTest, HangingRequestUsingHttpOnly) { |
| base::HistogramTester histogram_tester; |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| variation_params["hanging_request_http_rtt_upper_bound_http_rtt_multiplier"] = |
| "6"; |
| variation_params["hanging_request_upper_bound_min_http_rtt_msec"] = "500"; |
| |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| // 500 msec. |
| const int32_t hanging_request_threshold = |
| estimator.params() |
| ->hanging_request_upper_bound_min_http_rtt() |
| .InMilliseconds(); |
| |
| estimator.SetStartTimeNullHttpRtt(base::TimeDelta::FromMilliseconds(5)); |
| base::RunLoop().RunUntilIdle(); |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "test"); |
| |
| const struct { |
| base::TimeDelta observed_http_rtt; |
| std::string histogram_name; |
| } tests[] = { |
| {base::TimeDelta::FromMilliseconds(10), |
| "NQE.RTT.NotAHangingRequest.HttpRTT"}, |
| {base::TimeDelta::FromMilliseconds(100), |
| "NQE.RTT.NotAHangingRequest.MinHttpBound"}, |
| {base::TimeDelta::FromMilliseconds(hanging_request_threshold - 1), |
| "NQE.RTT.NotAHangingRequest.MinHttpBound"}, |
| {base::TimeDelta::FromMilliseconds(hanging_request_threshold + 1), |
| "NQE.RTT.HangingRequest"}, |
| {base::TimeDelta::FromMilliseconds(1000), "NQE.RTT.HangingRequest"}, |
| }; |
| |
| for (const auto& test : tests) { |
| EXPECT_EQ( |
| test.observed_http_rtt.InMilliseconds() >= hanging_request_threshold, |
| estimator.IsHangingRequest(test.observed_http_rtt)); |
| histogram_tester.ExpectBucketCount( |
| test.histogram_name, test.observed_http_rtt.InMilliseconds(), 1); |
| } |
| |
| // Verify total sample count in all histograms. |
| histogram_tester.ExpectTotalCount("NQE.RTT.NotAHangingRequest.TransportRTT", |
| 0); |
| histogram_tester.ExpectTotalCount("NQE.RTT.NotAHangingRequest.HttpRTT", 1); |
| histogram_tester.ExpectTotalCount("NQE.RTT.NotAHangingRequest.MinHttpBound", |
| 2); |
| histogram_tester.ExpectTotalCount("NQE.RTT.HangingRequest", 2); |
| } |
| |
| // Tests that the hanging request is correctly detected using end-to-end RTT. |
| TEST_F(NetworkQualityEstimatorTest, HangingRequestEndToEndUsingHttpOnly) { |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| variation_params["hanging_request_http_rtt_upper_bound_http_rtt_multiplier"] = |
| "6"; |
| variation_params["hanging_request_upper_bound_min_http_rtt_msec"] = "500"; |
| variation_params["use_end_to_end_rtt"] = "true"; |
| |
| int end_to_end_rtt_milliseconds = 1000; |
| int hanging_request_http_rtt_upper_bound_transport_rtt_multiplier = 8; |
| |
| TestNetworkQualityEstimator estimator(variation_params); |
| estimator.SetStartTimeNullHttpRtt(base::TimeDelta::FromMilliseconds(10)); |
| |
| base::RunLoop().RunUntilIdle(); |
| estimator.set_start_time_null_end_to_end_rtt( |
| base::TimeDelta::FromMilliseconds(end_to_end_rtt_milliseconds)); |
| estimator.SimulateNetworkChange( |
| NetworkChangeNotifier::ConnectionType::CONNECTION_UNKNOWN, "test"); |
| |
| const struct { |
| base::TimeDelta observed_http_rtt; |
| bool is_end_to_end_rtt_sample_count_enough; |
| bool expect_hanging_request; |
| std::string histogram_name; |
| } tests[] = { |
| {base::TimeDelta::FromMilliseconds(10), true, false, |
| "NQE.RTT.NotAHangingRequest.EndToEndRTT"}, |
| {base::TimeDelta::FromMilliseconds(10), false, false, |
| "NQE.RTT.NotAHangingRequest.HttpRTT"}, |
| {base::TimeDelta::FromMilliseconds(100), true, false, |
| "NQE.RTT.NotAHangingRequest.EndToEndRTT"}, |
| // |observed_http_rtt| is not large enough. Request is expected to be |
| // classified as not hanging. |
| {base::TimeDelta::FromMilliseconds( |
| (end_to_end_rtt_milliseconds * |
| hanging_request_http_rtt_upper_bound_transport_rtt_multiplier) - |
| 1), |
| true, false, "NQE.RTT.NotAHangingRequest.EndToEndRTT"}, |
| // |observed_http_rtt| is large. Request is expected to be classified as |
| // hanging. |
| {base::TimeDelta::FromMilliseconds( |
| (end_to_end_rtt_milliseconds * |
| hanging_request_http_rtt_upper_bound_transport_rtt_multiplier) + |
| 1), |
| true, true, "NQE.RTT.HangingRequest"}, |
| // Not enough end-to-end RTT samples. Request is expected to be classified |
| // as hanging. |
| {base::TimeDelta::FromMilliseconds( |
| end_to_end_rtt_milliseconds * |
| hanging_request_http_rtt_upper_bound_transport_rtt_multiplier - |
| 1), |
| false, true, "NQE.RTT.HangingRequest"}, |
| }; |
| |
| for (const auto& test : tests) { |
| base::HistogramTester histogram_tester; |
| if (test.is_end_to_end_rtt_sample_count_enough) { |
| estimator.set_start_time_null_end_to_end_rtt_observation_count( |
| estimator.params()->http_rtt_transport_rtt_min_count()); |
| } else { |
| estimator.set_start_time_null_end_to_end_rtt_observation_count( |
| estimator.params()->http_rtt_transport_rtt_min_count() - 1); |
| } |
| EXPECT_EQ(test.expect_hanging_request, |
| estimator.IsHangingRequest(test.observed_http_rtt)); |
| histogram_tester.ExpectBucketCount( |
| test.histogram_name, test.observed_http_rtt.InMilliseconds(), 1); |
| } |
| } |
| |
| TEST_F(NetworkQualityEstimatorTest, HangingRequestUsingTransportAndHttpOnly) { |
| base::HistogramTester histogram_tester; |
| |
| std::map<std::string, std::string> variation_params; |
| variation_params["add_default_platform_observations"] = "false"; |
| variation_params |
| ["hanging_request_http_rtt_upper_bound_transport_rtt_multiplier"] = "8"; |
| variation_params["hanging_request_http_rtt_upper_bound_http_rtt_multiplier"] = |
| "6"; |
| variation_params["hanging_request_upper_bound_min_http_rtt_msec"] = "500"; |
| |
| const base::TimeDelta transport_rtt = base::TimeDelta::FromMilliseconds(100); |
| |
| TestNetworkQualityEstimator estimator(variation_params); |
| |
| // 800 msec. |
| const int32_t hanging_request_threshold = |
| transport_rtt.InMilliseconds() * |
| estimator.params() |
| ->hanging_request_http_rtt_upper_bound_transport_rtt_multiplier(); |
| |
| estimator.DisableOfflineCheckForTesting(true); |
| estimator.SetStartTimeNullHttpRtt(base::TimeDelta::FromMilliseconds(5)); |
| |
| for (size_t i = 0; i < 100; ++i) { |
| // Throw enough transport RTT samples so that transport RTT estimate is |
| // recomputed. |
| estimator.AddAndNotifyObserversOfRTT(NetworkQualityEstimator::Observation( |
| transport_rtt.InMilliseconds(), base::TimeTicks::Now(), INT32_MIN, |
| NETWORK_QUALITY_OBSERVATION_SOURCE_TCP, 0)); |
| } |
| |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(transport_rtt, estimator.GetTransportRTT()); |
| |
| const struct { |
| base::TimeDelta observed_http_rtt; |
| std::string histogram_name; |
| } tests[] = { |
| {base::TimeDelta::FromMilliseconds(100), |
| "NQE.RTT.NotAHangingRequest.TransportRTT"}, |
| {base::TimeDelta::FromMilliseconds(500), |
| "NQE.RTT.NotAHangingRequest.TransportRTT"}, |
| {base::TimeDelta::FromMilliseconds(hanging_request_threshold - 1), |
| "NQE.RTT.NotAHangingRequest.TransportRTT"}, |
| {base::TimeDelta::FromMilliseconds(hanging_request_threshold + 1), |
| "NQE.RTT.HangingRequest"}, |
| {base::TimeDelta::FromMilliseconds(1000), "NQE.RTT.HangingRequest"}, |
| }; |
| |
| for (const auto& test : tests) { |
| EXPECT_EQ( |
| test.observed_http_rtt.InMilliseconds() >= hanging_request_threshold, |
| estimator.IsHangingRequest(test.observed_http_rtt)); |
| histogram_tester.ExpectBucketCount( |
| test.histogram_name, test.observed_http_rtt.InMilliseconds(), 1); |
| } |
| |
| // Verify total sample count in all histograms. |
| histogram_tester.ExpectTotalCount("NQE.RTT.NotAHangingRequest.TransportRTT", |
| 3); |
| histogram_tester.ExpectTotalCount("NQE.RTT.NotAHangingRequest.HttpRTT", 0); |
| histogram_tester.ExpectTotalCount("NQE.RTT.NotAHangingRequest.MinHttpBound", |
| 0); |
| histogram_tester.ExpectTotalCount("NQE.RTT.HangingRequest", 2); |
| } |
| |
| } // namespace net |