throughput_analyzer_unittest.cc 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/nqe/throughput_analyzer.h"
  5. #include <stdint.h>
  6. #include <map>
  7. #include <memory>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11. #include "base/bind.h"
  12. #include "base/callback_helpers.h"
  13. #include "base/containers/circular_deque.h"
  14. #include "base/location.h"
  15. #include "base/run_loop.h"
  16. #include "base/strings/string_number_conversions.h"
  17. #include "base/task/single_thread_task_runner.h"
  18. #include "base/test/metrics/histogram_tester.h"
  19. #include "base/test/scoped_feature_list.h"
  20. #include "base/test/simple_test_tick_clock.h"
  21. #include "base/test/test_timeouts.h"
  22. #include "base/threading/platform_thread.h"
  23. #include "base/threading/thread_task_runner_handle.h"
  24. #include "base/time/default_tick_clock.h"
  25. #include "base/time/time.h"
  26. #include "build/build_config.h"
  27. #include "net/base/features.h"
  28. #include "net/base/isolation_info.h"
  29. #include "net/base/schemeful_site.h"
  30. #include "net/dns/mock_host_resolver.h"
  31. #include "net/nqe/network_quality_estimator.h"
  32. #include "net/nqe/network_quality_estimator_params.h"
  33. #include "net/nqe/network_quality_estimator_test_util.h"
  34. #include "net/nqe/network_quality_estimator_util.h"
  35. #include "net/test/test_with_task_environment.h"
  36. #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
  37. #include "net/url_request/url_request.h"
  38. #include "net/url_request/url_request_context.h"
  39. #include "net/url_request/url_request_context_builder.h"
  40. #include "net/url_request/url_request_test_util.h"
  41. #include "testing/gtest/include/gtest/gtest.h"
  42. #include "url/gurl.h"
  43. namespace net::nqe {
  44. namespace {
  45. // Creates a mock resolver mapping example.com to a public IP address.
  46. std::unique_ptr<HostResolver> CreateMockHostResolver() {
  47. auto host_resolver = std::make_unique<MockCachingHostResolver>(
  48. /*cache_invalidation_num=*/0,
  49. /*default_result=*/ERR_NAME_NOT_RESOLVED);
  50. // local.com resolves to a private IP address.
  51. host_resolver->rules()->AddRule("local.com", "127.0.0.1");
  52. host_resolver->LoadIntoCache(HostPortPair("local.com", 80),
  53. NetworkIsolationKey(), absl::nullopt);
  54. // Hosts not listed here (e.g., "example.com") are treated as external. See
  55. // ThroughputAnalyzerTest.PrivateHost below.
  56. return host_resolver;
  57. }
  58. class TestThroughputAnalyzer : public internal::ThroughputAnalyzer {
  59. public:
  60. TestThroughputAnalyzer(NetworkQualityEstimator* network_quality_estimator,
  61. NetworkQualityEstimatorParams* params,
  62. const base::TickClock* tick_clock)
  63. : internal::ThroughputAnalyzer(
  64. network_quality_estimator,
  65. params,
  66. base::ThreadTaskRunnerHandle::Get(),
  67. base::BindRepeating(
  68. &TestThroughputAnalyzer::OnNewThroughputObservationAvailable,
  69. base::Unretained(this)),
  70. tick_clock,
  71. NetLogWithSource::Make(NetLogSourceType::NONE)) {}
  72. TestThroughputAnalyzer(const TestThroughputAnalyzer&) = delete;
  73. TestThroughputAnalyzer& operator=(const TestThroughputAnalyzer&) = delete;
  74. ~TestThroughputAnalyzer() override = default;
  75. int32_t throughput_observations_received() const {
  76. return throughput_observations_received_;
  77. }
  78. void OnNewThroughputObservationAvailable(int32_t downstream_kbps) {
  79. throughput_observations_received_++;
  80. }
  81. int64_t GetBitsReceived() const override { return bits_received_; }
  82. void IncrementBitsReceived(int64_t additional_bits_received) {
  83. bits_received_ += additional_bits_received;
  84. }
  85. using internal::ThroughputAnalyzer::CountActiveInFlightRequests;
  86. using internal::ThroughputAnalyzer::
  87. disable_throughput_measurements_for_testing;
  88. using internal::ThroughputAnalyzer::EraseHangingRequests;
  89. using internal::ThroughputAnalyzer::IsHangingWindow;
  90. private:
  91. int throughput_observations_received_ = 0;
  92. int64_t bits_received_ = 0;
  93. };
  94. using ThroughputAnalyzerTest = TestWithTaskEnvironment;
  95. TEST_F(ThroughputAnalyzerTest, PrivateHost) {
  96. auto host_resolver = CreateMockHostResolver();
  97. EXPECT_FALSE(nqe::internal::IsPrivateHostForTesting(
  98. host_resolver.get(), HostPortPair("example.com", 80),
  99. NetworkIsolationKey()));
  100. EXPECT_TRUE(nqe::internal::IsPrivateHostForTesting(
  101. host_resolver.get(), HostPortPair("local.com", 80),
  102. NetworkIsolationKey()));
  103. }
  104. #if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID)
  105. // Flaky on iOS: crbug.com/672917.
  106. // Flaky on Android: crbug.com/1223950.
  107. #define MAYBE_MaximumRequests DISABLED_MaximumRequests
  108. #else
  109. #define MAYBE_MaximumRequests MaximumRequests
  110. #endif
  111. TEST_F(ThroughputAnalyzerTest, MAYBE_MaximumRequests) {
  112. const struct TestCase {
  113. GURL url;
  114. bool is_local;
  115. } kTestCases[] = {
  116. {GURL("http://127.0.0.1/test.html"), true /* is_local */},
  117. {GURL("http://example.com/test.html"), false /* is_local */},
  118. {GURL("http://local.com/test.html"), true /* is_local */},
  119. };
  120. for (const auto& test_case : kTestCases) {
  121. const base::TickClock* tick_clock = base::DefaultTickClock::GetInstance();
  122. TestNetworkQualityEstimator network_quality_estimator;
  123. std::map<std::string, std::string> variation_params;
  124. NetworkQualityEstimatorParams params(variation_params);
  125. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  126. &params, tick_clock);
  127. TestDelegate test_delegate;
  128. auto context_builder = CreateTestURLRequestContextBuilder();
  129. context_builder->set_host_resolver(CreateMockHostResolver());
  130. auto context = context_builder->Build();
  131. ASSERT_FALSE(
  132. throughput_analyzer.disable_throughput_measurements_for_testing());
  133. base::circular_deque<std::unique_ptr<URLRequest>> requests;
  134. // Start more requests than the maximum number of requests that can be held
  135. // in the memory.
  136. EXPECT_EQ(test_case.is_local,
  137. nqe::internal::IsPrivateHostForTesting(
  138. context->host_resolver(),
  139. HostPortPair::FromURL(test_case.url), NetworkIsolationKey()));
  140. for (size_t i = 0; i < 1000; ++i) {
  141. std::unique_ptr<URLRequest> request(
  142. context->CreateRequest(test_case.url, DEFAULT_PRIORITY,
  143. &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
  144. throughput_analyzer.NotifyStartTransaction(*(request.get()));
  145. requests.push_back(std::move(request));
  146. }
  147. // Too many local requests should cause the |throughput_analyzer| to disable
  148. // throughput measurements.
  149. EXPECT_NE(test_case.is_local,
  150. throughput_analyzer.IsCurrentlyTrackingThroughput());
  151. }
  152. }
  153. #if BUILDFLAG(IS_IOS)
  154. // Flaky on iOS: crbug.com/672917.
  155. #define MAYBE_MaximumRequestsWithNetworkIsolationKey \
  156. DISABLED_MaximumRequestsWithNetworkIsolationKey
  157. #else
  158. #define MAYBE_MaximumRequestsWithNetworkIsolationKey \
  159. MaximumRequestsWithNetworkIsolationKey
  160. #endif
  161. // Make sure that the NetworkIsolationKey is respected when resolving a host
  162. // from the cache.
  163. TEST_F(ThroughputAnalyzerTest, MAYBE_MaximumRequestsWithNetworkIsolationKey) {
  164. const SchemefulSite kSite(GURL("https://foo.test/"));
  165. const net::NetworkIsolationKey kNetworkIsolationKey(kSite, kSite);
  166. const GURL kUrl = GURL("http://foo.test/test.html");
  167. base::test::ScopedFeatureList feature_list;
  168. feature_list.InitAndEnableFeature(
  169. features::kSplitHostCacheByNetworkIsolationKey);
  170. for (bool use_network_isolation_key : {false, true}) {
  171. const base::TickClock* tick_clock = base::DefaultTickClock::GetInstance();
  172. TestNetworkQualityEstimator network_quality_estimator;
  173. std::map<std::string, std::string> variation_params;
  174. NetworkQualityEstimatorParams params(variation_params);
  175. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  176. &params, tick_clock);
  177. TestDelegate test_delegate;
  178. auto context_builder = CreateTestURLRequestContextBuilder();
  179. auto mock_host_resolver = std::make_unique<MockCachingHostResolver>();
  180. // Add an entry to the host cache mapping kUrl to non-local IP when using an
  181. // empty NetworkIsolationKey.
  182. mock_host_resolver->rules()->AddRule(kUrl.host(), "1.2.3.4");
  183. mock_host_resolver->LoadIntoCache(HostPortPair::FromURL(kUrl),
  184. NetworkIsolationKey(), absl::nullopt);
  185. // Add an entry to the host cache mapping kUrl to local IP when using
  186. // kNetworkIsolationKey.
  187. mock_host_resolver->rules()->ClearRules();
  188. mock_host_resolver->rules()->AddRule(kUrl.host(), "127.0.0.1");
  189. mock_host_resolver->LoadIntoCache(HostPortPair::FromURL(kUrl),
  190. kNetworkIsolationKey, absl::nullopt);
  191. context_builder->set_host_resolver(std::move(mock_host_resolver));
  192. auto context = context_builder->Build();
  193. ASSERT_FALSE(
  194. throughput_analyzer.disable_throughput_measurements_for_testing());
  195. base::circular_deque<std::unique_ptr<URLRequest>> requests;
  196. // Start more requests than the maximum number of requests that can be held
  197. // in the memory.
  198. EXPECT_EQ(use_network_isolation_key,
  199. nqe::internal::IsPrivateHostForTesting(
  200. context->host_resolver(), HostPortPair::FromURL(kUrl),
  201. use_network_isolation_key ? kNetworkIsolationKey
  202. : NetworkIsolationKey()));
  203. for (size_t i = 0; i < 1000; ++i) {
  204. std::unique_ptr<URLRequest> request(
  205. context->CreateRequest(kUrl, DEFAULT_PRIORITY, &test_delegate,
  206. TRAFFIC_ANNOTATION_FOR_TESTS));
  207. if (use_network_isolation_key)
  208. request->set_isolation_info(IsolationInfo::CreatePartial(
  209. IsolationInfo::RequestType::kOther, kNetworkIsolationKey));
  210. throughput_analyzer.NotifyStartTransaction(*(request.get()));
  211. requests.push_back(std::move(request));
  212. }
  213. // Too many local requests should cause the |throughput_analyzer| to disable
  214. // throughput measurements.
  215. EXPECT_NE(use_network_isolation_key,
  216. throughput_analyzer.IsCurrentlyTrackingThroughput());
  217. }
  218. }
  219. // Tests that the throughput observation is taken only if there are sufficient
  220. // number of requests in-flight.
  221. TEST_F(ThroughputAnalyzerTest, TestMinRequestsForThroughputSample) {
  222. const base::TickClock* tick_clock = base::DefaultTickClock::GetInstance();
  223. TestNetworkQualityEstimator network_quality_estimator;
  224. std::map<std::string, std::string> variation_params;
  225. variation_params["throughput_hanging_requests_cwnd_size_multiplier"] = "-1";
  226. NetworkQualityEstimatorParams params(variation_params);
  227. // Set HTTP RTT to a large value so that the throughput observation window
  228. // is not detected as hanging. In practice, this would be provided by
  229. // |network_quality_estimator| based on the recent observations.
  230. network_quality_estimator.SetStartTimeNullHttpRtt(base::Seconds(100));
  231. for (size_t num_requests = 1;
  232. num_requests <= params.throughput_min_requests_in_flight() + 1;
  233. ++num_requests) {
  234. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  235. &params, tick_clock);
  236. auto context_builder = CreateTestURLRequestContextBuilder();
  237. context_builder->set_host_resolver(CreateMockHostResolver());
  238. auto context = context_builder->Build();
  239. std::vector<std::unique_ptr<URLRequest>> requests_not_local;
  240. std::vector<TestDelegate> not_local_test_delegates(num_requests);
  241. for (auto& delegate : not_local_test_delegates) {
  242. // We don't care about completion, except for the first one (see below).
  243. delegate.set_on_complete(base::DoNothing());
  244. std::unique_ptr<URLRequest> request_not_local(context->CreateRequest(
  245. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &delegate,
  246. TRAFFIC_ANNOTATION_FOR_TESTS));
  247. request_not_local->Start();
  248. requests_not_local.push_back(std::move(request_not_local));
  249. }
  250. not_local_test_delegates[0].RunUntilComplete();
  251. EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
  252. for (const auto& request : requests_not_local) {
  253. throughput_analyzer.NotifyStartTransaction(*request);
  254. }
  255. // Increment the bytes received count to emulate the bytes received for
  256. // |request_local| and |requests_not_local|.
  257. throughput_analyzer.IncrementBitsReceived(100 * 1000 * 8);
  258. for (const auto& request : requests_not_local) {
  259. throughput_analyzer.NotifyRequestCompleted(*request);
  260. }
  261. base::RunLoop().RunUntilIdle();
  262. int expected_throughput_observations =
  263. num_requests >= params.throughput_min_requests_in_flight() ? 1 : 0;
  264. EXPECT_EQ(expected_throughput_observations,
  265. throughput_analyzer.throughput_observations_received());
  266. }
  267. }
  268. // Tests that the hanging requests are dropped from the |requests_|, and
  269. // throughput observation window is ended.
  270. TEST_F(ThroughputAnalyzerTest, TestHangingRequests) {
  271. static const struct {
  272. int hanging_request_duration_http_rtt_multiplier;
  273. base::TimeDelta http_rtt;
  274. base::TimeDelta requests_hang_duration;
  275. bool expect_throughput_observation;
  276. } tests[] = {
  277. {
  278. // |requests_hang_duration| is less than 5 times the HTTP RTT.
  279. // Requests should not be marked as hanging.
  280. 5,
  281. base::Milliseconds(1000),
  282. base::Milliseconds(3000),
  283. true,
  284. },
  285. {
  286. // |requests_hang_duration| is more than 5 times the HTTP RTT.
  287. // Requests should be marked as hanging.
  288. 5,
  289. base::Milliseconds(200),
  290. base::Milliseconds(3000),
  291. false,
  292. },
  293. {
  294. // |requests_hang_duration| is less than
  295. // |hanging_request_min_duration_msec|. Requests should not be marked
  296. // as hanging.
  297. 1,
  298. base::Milliseconds(100),
  299. base::Milliseconds(100),
  300. true,
  301. },
  302. {
  303. // |requests_hang_duration| is more than
  304. // |hanging_request_min_duration_msec|. Requests should be marked as
  305. // hanging.
  306. 1,
  307. base::Milliseconds(2000),
  308. base::Milliseconds(3100),
  309. false,
  310. },
  311. {
  312. // |requests_hang_duration| is less than 5 times the HTTP RTT.
  313. // Requests should not be marked as hanging.
  314. 5,
  315. base::Seconds(2),
  316. base::Seconds(1),
  317. true,
  318. },
  319. {
  320. // HTTP RTT is unavailable. Requests should not be marked as hanging.
  321. 5,
  322. base::Seconds(-1),
  323. base::Seconds(-1),
  324. true,
  325. },
  326. };
  327. for (const auto& test : tests) {
  328. base::HistogramTester histogram_tester;
  329. const base::TickClock* tick_clock = base::DefaultTickClock::GetInstance();
  330. TestNetworkQualityEstimator network_quality_estimator;
  331. if (test.http_rtt >= base::TimeDelta())
  332. network_quality_estimator.SetStartTimeNullHttpRtt(test.http_rtt);
  333. std::map<std::string, std::string> variation_params;
  334. // Set the transport RTT multiplier to a large value so that the hanging
  335. // request decision is made only on the basis of the HTTP RTT.
  336. variation_params
  337. ["hanging_request_http_rtt_upper_bound_transport_rtt_multiplier"] =
  338. "10000";
  339. variation_params["throughput_hanging_requests_cwnd_size_multiplier"] = "-1";
  340. variation_params["hanging_request_duration_http_rtt_multiplier"] =
  341. base::NumberToString(test.hanging_request_duration_http_rtt_multiplier);
  342. NetworkQualityEstimatorParams params(variation_params);
  343. const size_t num_requests = params.throughput_min_requests_in_flight();
  344. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  345. &params, tick_clock);
  346. auto context_builder = CreateTestURLRequestContextBuilder();
  347. context_builder->set_host_resolver(CreateMockHostResolver());
  348. auto context = context_builder->Build();
  349. std::vector<std::unique_ptr<URLRequest>> requests_not_local;
  350. std::vector<TestDelegate> not_local_test_delegates(num_requests);
  351. for (size_t i = 0; i < num_requests; ++i) {
  352. // We don't care about completion, except for the first one (see below).
  353. not_local_test_delegates[i].set_on_complete(base::DoNothing());
  354. std::unique_ptr<URLRequest> request_not_local(context->CreateRequest(
  355. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY,
  356. &not_local_test_delegates[i], TRAFFIC_ANNOTATION_FOR_TESTS));
  357. request_not_local->Start();
  358. requests_not_local.push_back(std::move(request_not_local));
  359. }
  360. not_local_test_delegates[0].RunUntilComplete();
  361. EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
  362. for (size_t i = 0; i < num_requests; ++i) {
  363. throughput_analyzer.NotifyStartTransaction(*requests_not_local.at(i));
  364. }
  365. // Increment the bytes received count to emulate the bytes received for
  366. // |request_local| and |requests_not_local|.
  367. throughput_analyzer.IncrementBitsReceived(100 * 1000 * 8);
  368. // Mark in-flight requests as hanging requests (if specified in the test
  369. // params).
  370. if (test.requests_hang_duration >= base::TimeDelta())
  371. base::PlatformThread::Sleep(test.requests_hang_duration);
  372. EXPECT_EQ(num_requests, throughput_analyzer.CountActiveInFlightRequests());
  373. for (size_t i = 0; i < num_requests; ++i) {
  374. throughput_analyzer.NotifyRequestCompleted(*requests_not_local.at(i));
  375. if (!test.expect_throughput_observation) {
  376. // All in-flight requests should be marked as hanging, and thus should
  377. // be deleted from the set of in-flight requests.
  378. EXPECT_EQ(0u, throughput_analyzer.CountActiveInFlightRequests());
  379. } else {
  380. // One request should be deleted at one time.
  381. EXPECT_EQ(requests_not_local.size() - i - 1,
  382. throughput_analyzer.CountActiveInFlightRequests());
  383. }
  384. }
  385. base::RunLoop().RunUntilIdle();
  386. EXPECT_EQ(test.expect_throughput_observation,
  387. throughput_analyzer.throughput_observations_received() > 0);
  388. }
  389. }
  390. // Tests that the check for hanging requests is done at most once per second.
  391. TEST_F(ThroughputAnalyzerTest, TestHangingRequestsCheckedOnlyPeriodically) {
  392. base::SimpleTestTickClock tick_clock;
  393. TestNetworkQualityEstimator network_quality_estimator;
  394. network_quality_estimator.SetStartTimeNullHttpRtt(base::Seconds(1));
  395. std::map<std::string, std::string> variation_params;
  396. variation_params["hanging_request_duration_http_rtt_multiplier"] = "5";
  397. variation_params["hanging_request_min_duration_msec"] = "2000";
  398. NetworkQualityEstimatorParams params(variation_params);
  399. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  400. &params, &tick_clock);
  401. TestDelegate test_delegate;
  402. auto context_builder = CreateTestURLRequestContextBuilder();
  403. context_builder->set_host_resolver(CreateMockHostResolver());
  404. auto context = context_builder->Build();
  405. std::vector<std::unique_ptr<URLRequest>> requests_not_local;
  406. for (size_t i = 0; i < 2; ++i) {
  407. std::unique_ptr<URLRequest> request_not_local(context->CreateRequest(
  408. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
  409. TRAFFIC_ANNOTATION_FOR_TESTS));
  410. request_not_local->Start();
  411. requests_not_local.push_back(std::move(request_not_local));
  412. }
  413. std::unique_ptr<URLRequest> some_other_request(context->CreateRequest(
  414. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
  415. TRAFFIC_ANNOTATION_FOR_TESTS));
  416. test_delegate.RunUntilComplete();
  417. // First request starts at t=1. The second request starts at t=2. The first
  418. // request would be marked as hanging at t=6, and the second request at t=7
  419. // seconds.
  420. for (size_t i = 0; i < 2; ++i) {
  421. tick_clock.Advance(base::Milliseconds(1000));
  422. throughput_analyzer.NotifyStartTransaction(*requests_not_local.at(i));
  423. }
  424. EXPECT_EQ(2u, throughput_analyzer.CountActiveInFlightRequests());
  425. tick_clock.Advance(base::Milliseconds(3500));
  426. // Current time is t = 5.5 seconds.
  427. throughput_analyzer.EraseHangingRequests(*some_other_request);
  428. EXPECT_EQ(2u, throughput_analyzer.CountActiveInFlightRequests());
  429. tick_clock.Advance(base::Milliseconds(1000));
  430. // Current time is t = 6.5 seconds. One request should be marked as hanging.
  431. throughput_analyzer.EraseHangingRequests(*some_other_request);
  432. EXPECT_EQ(1u, throughput_analyzer.CountActiveInFlightRequests());
  433. // Current time is t = 6.5 seconds. Calling NotifyBytesRead again should not
  434. // run the hanging request checker since the last check was at t=6.5 seconds.
  435. throughput_analyzer.EraseHangingRequests(*some_other_request);
  436. EXPECT_EQ(1u, throughput_analyzer.CountActiveInFlightRequests());
  437. tick_clock.Advance(base::Milliseconds(600));
  438. // Current time is t = 7.1 seconds. Calling NotifyBytesRead again should not
  439. // run the hanging request checker since the last check was at t=6.5 seconds
  440. // (less than 1 second ago).
  441. throughput_analyzer.EraseHangingRequests(*some_other_request);
  442. EXPECT_EQ(1u, throughput_analyzer.CountActiveInFlightRequests());
  443. tick_clock.Advance(base::Milliseconds(400));
  444. // Current time is t = 7.5 seconds. Calling NotifyBytesRead again should run
  445. // the hanging request checker since the last check was at t=6.5 seconds (at
  446. // least 1 second ago).
  447. throughput_analyzer.EraseHangingRequests(*some_other_request);
  448. EXPECT_EQ(0u, throughput_analyzer.CountActiveInFlightRequests());
  449. }
  450. // Tests that the last received time for a request is updated when data is
  451. // received for that request.
  452. TEST_F(ThroughputAnalyzerTest, TestLastReceivedTimeIsUpdated) {
  453. base::SimpleTestTickClock tick_clock;
  454. TestNetworkQualityEstimator network_quality_estimator;
  455. network_quality_estimator.SetStartTimeNullHttpRtt(base::Seconds(1));
  456. std::map<std::string, std::string> variation_params;
  457. variation_params["hanging_request_duration_http_rtt_multiplier"] = "5";
  458. variation_params["hanging_request_min_duration_msec"] = "2000";
  459. NetworkQualityEstimatorParams params(variation_params);
  460. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  461. &params, &tick_clock);
  462. TestDelegate test_delegate;
  463. auto context_builder = CreateTestURLRequestContextBuilder();
  464. context_builder->set_host_resolver(CreateMockHostResolver());
  465. auto context = context_builder->Build();
  466. std::unique_ptr<URLRequest> request_not_local(context->CreateRequest(
  467. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
  468. TRAFFIC_ANNOTATION_FOR_TESTS));
  469. request_not_local->Start();
  470. test_delegate.RunUntilComplete();
  471. std::unique_ptr<URLRequest> some_other_request(context->CreateRequest(
  472. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
  473. TRAFFIC_ANNOTATION_FOR_TESTS));
  474. // Start time for the request is t=0 second. The request will be marked as
  475. // hanging at t=5 seconds.
  476. throughput_analyzer.NotifyStartTransaction(*request_not_local);
  477. tick_clock.Advance(base::Milliseconds(4000));
  478. // Current time is t=4.0 seconds.
  479. throughput_analyzer.EraseHangingRequests(*some_other_request);
  480. EXPECT_EQ(1u, throughput_analyzer.CountActiveInFlightRequests());
  481. // The request will be marked as hanging at t=9 seconds.
  482. throughput_analyzer.NotifyBytesRead(*request_not_local);
  483. tick_clock.Advance(base::Milliseconds(4000));
  484. // Current time is t=8 seconds.
  485. throughput_analyzer.EraseHangingRequests(*some_other_request);
  486. EXPECT_EQ(1u, throughput_analyzer.CountActiveInFlightRequests());
  487. tick_clock.Advance(base::Milliseconds(2000));
  488. // Current time is t=10 seconds.
  489. throughput_analyzer.EraseHangingRequests(*some_other_request);
  490. EXPECT_EQ(0u, throughput_analyzer.CountActiveInFlightRequests());
  491. }
  492. // Test that a request that has been hanging for a long time is deleted
  493. // immediately when EraseHangingRequests is called even if the last hanging
  494. // request check was done recently.
  495. TEST_F(ThroughputAnalyzerTest, TestRequestDeletedImmediately) {
  496. base::SimpleTestTickClock tick_clock;
  497. TestNetworkQualityEstimator network_quality_estimator;
  498. network_quality_estimator.SetStartTimeNullHttpRtt(base::Seconds(1));
  499. std::map<std::string, std::string> variation_params;
  500. variation_params["hanging_request_duration_http_rtt_multiplier"] = "2";
  501. NetworkQualityEstimatorParams params(variation_params);
  502. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  503. &params, &tick_clock);
  504. TestDelegate test_delegate;
  505. auto context_builder = CreateTestURLRequestContextBuilder();
  506. context_builder->set_host_resolver(CreateMockHostResolver());
  507. auto context = context_builder->Build();
  508. std::unique_ptr<URLRequest> request_not_local(context->CreateRequest(
  509. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
  510. TRAFFIC_ANNOTATION_FOR_TESTS));
  511. request_not_local->Start();
  512. test_delegate.RunUntilComplete();
  513. // Start time for the request is t=0 second. The request will be marked as
  514. // hanging at t=2 seconds.
  515. throughput_analyzer.NotifyStartTransaction(*request_not_local);
  516. EXPECT_EQ(1u, throughput_analyzer.CountActiveInFlightRequests());
  517. tick_clock.Advance(base::Milliseconds(2900));
  518. // Current time is t=2.9 seconds.
  519. throughput_analyzer.EraseHangingRequests(*request_not_local);
  520. EXPECT_EQ(1u, throughput_analyzer.CountActiveInFlightRequests());
  521. // |request_not_local| should be deleted since it has been idle for 2.4
  522. // seconds.
  523. tick_clock.Advance(base::Milliseconds(500));
  524. throughput_analyzer.NotifyBytesRead(*request_not_local);
  525. EXPECT_EQ(0u, throughput_analyzer.CountActiveInFlightRequests());
  526. }
  527. #if BUILDFLAG(IS_IOS)
  528. // Flaky on iOS: crbug.com/672917.
  529. #define MAYBE_TestThroughputWithMultipleRequestsOverlap \
  530. DISABLED_TestThroughputWithMultipleRequestsOverlap
  531. #else
  532. #define MAYBE_TestThroughputWithMultipleRequestsOverlap \
  533. TestThroughputWithMultipleRequestsOverlap
  534. #endif
  535. // Tests if the throughput observation is taken correctly when local and network
  536. // requests overlap.
  537. TEST_F(ThroughputAnalyzerTest,
  538. MAYBE_TestThroughputWithMultipleRequestsOverlap) {
  539. static const struct {
  540. bool start_local_request;
  541. bool local_request_completes_first;
  542. bool expect_throughput_observation;
  543. } tests[] = {
  544. {
  545. false, false, true,
  546. },
  547. {
  548. true, false, false,
  549. },
  550. {
  551. true, true, true,
  552. },
  553. };
  554. for (const auto& test : tests) {
  555. const base::TickClock* tick_clock = base::DefaultTickClock::GetInstance();
  556. TestNetworkQualityEstimator network_quality_estimator;
  557. // Localhost requests are not allowed for estimation purposes.
  558. std::map<std::string, std::string> variation_params;
  559. variation_params["throughput_hanging_requests_cwnd_size_multiplier"] = "-1";
  560. NetworkQualityEstimatorParams params(variation_params);
  561. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  562. &params, tick_clock);
  563. TestDelegate local_delegate;
  564. local_delegate.set_on_complete(base::DoNothing());
  565. auto context_builder = CreateTestURLRequestContextBuilder();
  566. context_builder->set_host_resolver(CreateMockHostResolver());
  567. auto context = context_builder->Build();
  568. std::unique_ptr<URLRequest> request_local;
  569. std::vector<std::unique_ptr<URLRequest>> requests_not_local;
  570. std::vector<TestDelegate> not_local_test_delegates(
  571. params.throughput_min_requests_in_flight());
  572. for (size_t i = 0; i < params.throughput_min_requests_in_flight(); ++i) {
  573. // We don't care about completion, except for the first one (see below).
  574. not_local_test_delegates[i].set_on_complete(base::DoNothing());
  575. std::unique_ptr<URLRequest> request_not_local(context->CreateRequest(
  576. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY,
  577. &not_local_test_delegates[i], TRAFFIC_ANNOTATION_FOR_TESTS));
  578. request_not_local->Start();
  579. requests_not_local.push_back(std::move(request_not_local));
  580. }
  581. if (test.start_local_request) {
  582. request_local = context->CreateRequest(GURL("http://127.0.0.1/echo.html"),
  583. DEFAULT_PRIORITY, &local_delegate,
  584. TRAFFIC_ANNOTATION_FOR_TESTS);
  585. request_local->Start();
  586. }
  587. // Wait until the first not-local request completes.
  588. not_local_test_delegates[0].RunUntilComplete();
  589. EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
  590. // If |test.start_local_request| is true, then |request_local| starts
  591. // before |request_not_local|, and ends after |request_not_local|. Thus,
  592. // network quality estimator should not get a chance to record throughput
  593. // observation from |request_not_local| because of ongoing local request
  594. // at all times.
  595. if (test.start_local_request)
  596. throughput_analyzer.NotifyStartTransaction(*request_local);
  597. for (const auto& request : requests_not_local) {
  598. throughput_analyzer.NotifyStartTransaction(*request);
  599. }
  600. if (test.local_request_completes_first) {
  601. ASSERT_TRUE(test.start_local_request);
  602. throughput_analyzer.NotifyRequestCompleted(*request_local);
  603. }
  604. // Increment the bytes received count to emulate the bytes received for
  605. // |request_local| and |requests_not_local|.
  606. throughput_analyzer.IncrementBitsReceived(100 * 1000 * 8);
  607. for (const auto& request : requests_not_local) {
  608. throughput_analyzer.NotifyRequestCompleted(*request);
  609. }
  610. if (test.start_local_request && !test.local_request_completes_first)
  611. throughput_analyzer.NotifyRequestCompleted(*request_local);
  612. // Pump the message loop to let analyzer tasks get processed.
  613. base::RunLoop().RunUntilIdle();
  614. int expected_throughput_observations =
  615. test.expect_throughput_observation ? 1 : 0;
  616. EXPECT_EQ(expected_throughput_observations,
  617. throughput_analyzer.throughput_observations_received());
  618. }
  619. }
  620. // Tests if the throughput observation is taken correctly when two network
  621. // requests overlap.
  622. TEST_F(ThroughputAnalyzerTest, TestThroughputWithNetworkRequestsOverlap) {
  623. static const struct {
  624. size_t throughput_min_requests_in_flight;
  625. size_t number_requests_in_flight;
  626. int64_t increment_bits;
  627. bool expect_throughput_observation;
  628. } tests[] = {
  629. {
  630. 1, 2, 100 * 1000 * 8, true,
  631. },
  632. {
  633. 3, 1, 100 * 1000 * 8, false,
  634. },
  635. {
  636. 3, 2, 100 * 1000 * 8, false,
  637. },
  638. {
  639. 3, 3, 100 * 1000 * 8, true,
  640. },
  641. {
  642. 3, 4, 100 * 1000 * 8, true,
  643. },
  644. {
  645. 1, 2, 1, false,
  646. },
  647. };
  648. for (const auto& test : tests) {
  649. const base::TickClock* tick_clock = base::DefaultTickClock::GetInstance();
  650. TestNetworkQualityEstimator network_quality_estimator;
  651. // Localhost requests are not allowed for estimation purposes.
  652. std::map<std::string, std::string> variation_params;
  653. variation_params["throughput_min_requests_in_flight"] =
  654. base::NumberToString(test.throughput_min_requests_in_flight);
  655. variation_params["throughput_hanging_requests_cwnd_size_multiplier"] = "-1";
  656. NetworkQualityEstimatorParams params(variation_params);
  657. // Set HTTP RTT to a large value so that the throughput observation window
  658. // is not detected as hanging. In practice, this would be provided by
  659. // |network_quality_estimator| based on the recent observations.
  660. network_quality_estimator.SetStartTimeNullHttpRtt(base::Seconds(100));
  661. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  662. &params, tick_clock);
  663. auto context_builder = CreateTestURLRequestContextBuilder();
  664. context_builder->set_host_resolver(CreateMockHostResolver());
  665. auto context = context_builder->Build();
  666. EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
  667. std::vector<std::unique_ptr<URLRequest>> requests_in_flight;
  668. std::vector<TestDelegate> in_flight_test_delegates(
  669. test.number_requests_in_flight);
  670. for (size_t i = 0; i < test.number_requests_in_flight; ++i) {
  671. // We don't care about completion, except for the first one (see below).
  672. in_flight_test_delegates[i].set_on_complete(base::DoNothing());
  673. std::unique_ptr<URLRequest> request_network_1 = context->CreateRequest(
  674. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY,
  675. &in_flight_test_delegates[i], TRAFFIC_ANNOTATION_FOR_TESTS);
  676. requests_in_flight.push_back(std::move(request_network_1));
  677. requests_in_flight.back()->Start();
  678. }
  679. in_flight_test_delegates[0].RunUntilComplete();
  680. EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
  681. for (size_t i = 0; i < test.number_requests_in_flight; ++i) {
  682. URLRequest* request = requests_in_flight.at(i).get();
  683. throughput_analyzer.NotifyStartTransaction(*request);
  684. }
  685. // Increment the bytes received count to emulate the bytes received for
  686. // |request_network_1| and |request_network_2|.
  687. throughput_analyzer.IncrementBitsReceived(test.increment_bits);
  688. for (size_t i = 0; i < test.number_requests_in_flight; ++i) {
  689. URLRequest* request = requests_in_flight.at(i).get();
  690. throughput_analyzer.NotifyRequestCompleted(*request);
  691. }
  692. base::RunLoop().RunUntilIdle();
  693. // Only one observation should be taken since two requests overlap.
  694. if (test.expect_throughput_observation) {
  695. EXPECT_EQ(1, throughput_analyzer.throughput_observations_received());
  696. } else {
  697. EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
  698. }
  699. }
  700. }
  701. // Tests if the throughput observation is taken correctly when the start and end
  702. // of network requests overlap, and the minimum number of in flight requests
  703. // when taking an observation is more than 1.
  704. TEST_F(ThroughputAnalyzerTest, TestThroughputWithMultipleNetworkRequests) {
  705. const base::test::ScopedRunLoopTimeout increased_run_timeout(
  706. FROM_HERE, TestTimeouts::action_max_timeout());
  707. const base::TickClock* tick_clock = base::DefaultTickClock::GetInstance();
  708. TestNetworkQualityEstimator network_quality_estimator;
  709. std::map<std::string, std::string> variation_params;
  710. variation_params["throughput_min_requests_in_flight"] = "3";
  711. variation_params["throughput_hanging_requests_cwnd_size_multiplier"] = "-1";
  712. NetworkQualityEstimatorParams params(variation_params);
  713. // Set HTTP RTT to a large value so that the throughput observation window
  714. // is not detected as hanging. In practice, this would be provided by
  715. // |network_quality_estimator| based on the recent observations.
  716. network_quality_estimator.SetStartTimeNullHttpRtt(base::Seconds(100));
  717. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  718. &params, tick_clock);
  719. TestDelegate test_delegate;
  720. auto context_builder = CreateTestURLRequestContextBuilder();
  721. context_builder->set_host_resolver(CreateMockHostResolver());
  722. auto context = context_builder->Build();
  723. EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
  724. std::unique_ptr<URLRequest> request_1 = context->CreateRequest(
  725. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
  726. TRAFFIC_ANNOTATION_FOR_TESTS);
  727. std::unique_ptr<URLRequest> request_2 = context->CreateRequest(
  728. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
  729. TRAFFIC_ANNOTATION_FOR_TESTS);
  730. std::unique_ptr<URLRequest> request_3 = context->CreateRequest(
  731. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
  732. TRAFFIC_ANNOTATION_FOR_TESTS);
  733. std::unique_ptr<URLRequest> request_4 = context->CreateRequest(
  734. GURL("http://example.com/echo.html"), DEFAULT_PRIORITY, &test_delegate,
  735. TRAFFIC_ANNOTATION_FOR_TESTS);
  736. request_1->Start();
  737. request_2->Start();
  738. request_3->Start();
  739. request_4->Start();
  740. // We dispatched four requests, so wait for four completions.
  741. for (int i = 0; i < 4; ++i)
  742. test_delegate.RunUntilComplete();
  743. EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
  744. throughput_analyzer.NotifyStartTransaction(*(request_1.get()));
  745. throughput_analyzer.NotifyStartTransaction(*(request_2.get()));
  746. const size_t increment_bits = 100 * 1000 * 8;
  747. // Increment the bytes received count to emulate the bytes received for
  748. // |request_1| and |request_2|.
  749. throughput_analyzer.IncrementBitsReceived(increment_bits);
  750. throughput_analyzer.NotifyRequestCompleted(*(request_1.get()));
  751. base::RunLoop().RunUntilIdle();
  752. // No observation should be taken since only 1 request is in flight.
  753. EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
  754. throughput_analyzer.NotifyStartTransaction(*(request_3.get()));
  755. throughput_analyzer.NotifyStartTransaction(*(request_4.get()));
  756. EXPECT_EQ(0, throughput_analyzer.throughput_observations_received());
  757. // 3 requests are in flight which is at least as many as the minimum number of
  758. // in flight requests required. An observation should be taken.
  759. throughput_analyzer.IncrementBitsReceived(increment_bits);
  760. // Only one observation should be taken since two requests overlap.
  761. throughput_analyzer.NotifyRequestCompleted(*(request_2.get()));
  762. base::RunLoop().RunUntilIdle();
  763. EXPECT_EQ(1, throughput_analyzer.throughput_observations_received());
  764. throughput_analyzer.NotifyRequestCompleted(*(request_3.get()));
  765. throughput_analyzer.NotifyRequestCompleted(*(request_4.get()));
  766. EXPECT_EQ(1, throughput_analyzer.throughput_observations_received());
  767. }
  768. TEST_F(ThroughputAnalyzerTest, TestHangingWindow) {
  769. static constexpr size_t kCwndSizeKilobytes = 10 * 1.5;
  770. static constexpr size_t kCwndSizeBits = kCwndSizeKilobytes * 1000 * 8;
  771. base::SimpleTestTickClock tick_clock;
  772. TestNetworkQualityEstimator network_quality_estimator;
  773. int64_t http_rtt_msec = 1000;
  774. network_quality_estimator.SetStartTimeNullHttpRtt(
  775. base::Milliseconds(http_rtt_msec));
  776. std::map<std::string, std::string> variation_params;
  777. variation_params["throughput_hanging_requests_cwnd_size_multiplier"] = "1";
  778. NetworkQualityEstimatorParams params(variation_params);
  779. TestThroughputAnalyzer throughput_analyzer(&network_quality_estimator,
  780. &params, &tick_clock);
  781. const struct {
  782. size_t bits_received;
  783. base::TimeDelta window_duration;
  784. bool expected_hanging;
  785. } tests[] = {
  786. {100, base::Milliseconds(http_rtt_msec), true},
  787. {kCwndSizeBits - 1, base::Milliseconds(http_rtt_msec), true},
  788. {kCwndSizeBits + 1, base::Milliseconds(http_rtt_msec), false},
  789. {2 * (kCwndSizeBits - 1), base::Milliseconds(http_rtt_msec * 2), true},
  790. {2 * (kCwndSizeBits + 1), base::Milliseconds(http_rtt_msec * 2), false},
  791. {kCwndSizeBits / 2 - 1, base::Milliseconds(http_rtt_msec / 2), true},
  792. {kCwndSizeBits / 2 + 1, base::Milliseconds(http_rtt_msec / 2), false},
  793. };
  794. for (const auto& test : tests) {
  795. base::HistogramTester histogram_tester;
  796. double kbps = test.bits_received / test.window_duration.InMillisecondsF();
  797. EXPECT_EQ(test.expected_hanging,
  798. throughput_analyzer.IsHangingWindow(test.bits_received,
  799. test.window_duration, kbps));
  800. if (test.expected_hanging) {
  801. histogram_tester.ExpectUniqueSample("NQE.ThroughputObservation.Hanging",
  802. kbps, 1);
  803. histogram_tester.ExpectTotalCount("NQE.ThroughputObservation.NotHanging",
  804. 0);
  805. } else {
  806. histogram_tester.ExpectTotalCount("NQE.ThroughputObservation.Hanging", 0);
  807. histogram_tester.ExpectUniqueSample(
  808. "NQE.ThroughputObservation.NotHanging", kbps, 1);
  809. }
  810. }
  811. }
  812. } // namespace
  813. } // namespace net::nqe