| // Copyright 2018 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 <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "chrome/browser/download/download_prefs.h" |
| #include "chrome/browser/metrics/subprocess_metrics_provider.h" |
| #include "chrome/browser/predictors/loading_predictor_config.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/web_contents_observer.h" |
| #include "content/public/common/resource_load_info.mojom.h" |
| #include "content/public/common/resource_type.h" |
| #include "content/public/test/download_test_observer.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "net/base/filename_util.h" |
| #include "net/base/net_errors.h" |
| #include "net/http/http_response_info.h" |
| #include "net/test/embedded_test_server/controllable_http_response.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/window_open_disposition.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| namespace { |
| |
| // Used for the main frame, in subresource tests. |
| constexpr char kUninterestingMainFramePath[] = |
| "/uninteresting/main_frame/path/"; |
| |
| // Used for the "interesting" request individual tests focus on. |
| constexpr char kInterestingPath[] = "/interesting/path/"; |
| |
| enum class RequestType { |
| kMainFrame, |
| kSubFrame, |
| kImage, |
| kScript, |
| }; |
| |
| enum class HeadersReceived { |
| kHeadersReceived, |
| kNoHeadersReceived, |
| }; |
| |
| enum class NetworkAccessed { |
| kNetworkAccessed, |
| kNoNetworkAccessed, |
| }; |
| |
| // Utility class to wait until the main resource load is complete. This is to |
| // make sure, in the cancel tests, the main resource is fully loaded before the |
| // navigation is cancelled, to ensure the main frame load histograms are in a |
| // consistent state, and can be checked at the end of each test. To avoid races, |
| // create this class before starting a navigation. |
| class WaitForMainFrameResourceObserver : public content::WebContentsObserver { |
| public: |
| explicit WaitForMainFrameResourceObserver(WebContents* web_contents) |
| : content::WebContentsObserver(web_contents) {} |
| ~WaitForMainFrameResourceObserver() override {} |
| |
| // content::WebContentsObserver implementation: |
| void ResourceLoadComplete( |
| RenderFrameHost* render_frame_host, |
| const content::GlobalRequestID& request_id, |
| const content::mojom::ResourceLoadInfo& resource_load_info) override { |
| EXPECT_EQ(RESOURCE_TYPE_MAIN_FRAME, resource_load_info.resource_type); |
| EXPECT_EQ(net::OK, resource_load_info.net_error); |
| run_loop_.Quit(); |
| } |
| |
| void Wait() { run_loop_.Run(); } |
| |
| private: |
| base::RunLoop run_loop_; |
| |
| DISALLOW_COPY_AND_ASSIGN(WaitForMainFrameResourceObserver); |
| }; |
| |
| // This test fixture tests code in content/. The fixture itself is in chrome/ |
| // because SubprocessMetricsProvider is a chrome-only test class. |
| class NetworkRequestMetricsBrowserTest |
| : public InProcessBrowserTest, |
| public testing::WithParamInterface<RequestType> { |
| public: |
| NetworkRequestMetricsBrowserTest() { |
| scoped_feature_list_.InitAndDisableFeature( |
| predictors::kSpeculativePreconnectFeature); |
| } |
| ~NetworkRequestMetricsBrowserTest() override {} |
| |
| // ContentBrowserTest implementation: |
| void SetUpOnMainThread() override { |
| uninteresting_main_frame_response_ = |
| std::make_unique<net::test_server::ControllableHttpResponse>( |
| embedded_test_server(), kUninterestingMainFramePath); |
| interesting_http_response_ = |
| std::make_unique<net::test_server::ControllableHttpResponse>( |
| embedded_test_server(), kInterestingPath); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Need to make this after test setup, to make sure the initial about:blank |
| // load is not counted. |
| histograms_ = std::make_unique<base::HistogramTester>(); |
| } |
| |
| // For non-RequestType::kMainFrame tests, returns the contents of the main |
| // frame, based on the RequestType. |
| std::string GetMainFrameContents(const std::string subresource_path) { |
| switch (GetParam()) { |
| case RequestType::kSubFrame: |
| return base::StringPrintf("<iframe src='%s'></iframe>", |
| subresource_path.c_str()); |
| case RequestType::kImage: |
| return base::StringPrintf("<img src='%s'>", subresource_path.c_str()); |
| break; |
| case RequestType::kScript: |
| return base::StringPrintf("<script src='%s'></script>", |
| subresource_path.c_str()); |
| break; |
| case RequestType::kMainFrame: |
| NOTREACHED(); |
| } |
| return std::string(); |
| } |
| |
| void StartNavigatingAndWaitForRequest() { |
| GURL interesting_url = embedded_test_server()->GetURL(kInterestingPath); |
| if (GetParam() == RequestType::kMainFrame) { |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), interesting_url, WindowOpenDisposition::CURRENT_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| } else { |
| WaitForMainFrameResourceObserver wait_for_main_frame_resource_observer( |
| active_web_contents()); |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), |
| embedded_test_server()->GetURL(kUninterestingMainFramePath), |
| WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); |
| uninteresting_main_frame_response_->WaitForRequest(); |
| uninteresting_main_frame_response_->Send( |
| "HTTP/1.1 200 Peachy\r\n" |
| "Content-Type: text/html\r\n" |
| "\r\n"); |
| uninteresting_main_frame_response_->Send( |
| GetMainFrameContents(interesting_url.spec())); |
| uninteresting_main_frame_response_->Done(); |
| wait_for_main_frame_resource_observer.Wait(); |
| } |
| |
| interesting_http_response_->WaitForRequest(); |
| } |
| |
| net::test_server::ControllableHttpResponse* interesting_http_response() { |
| return interesting_http_response_.get(); |
| } |
| |
| // Checks all relevant histograms. |expected_net_error| is the expected result |
| // of the RequestType specified by the test parameter. |
| void CheckHistograms(int expected_net_error, |
| HeadersReceived headers_received, |
| NetworkAccessed network_accessed) { |
| // Some metrics may come from the renderer. This call ensures that those |
| // metrics are available. |
| SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| |
| if (GetParam() == RequestType::kMainFrame) { |
| histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0); |
| |
| histograms_->ExpectUniqueSample("Net.ErrorCodesForMainFrame4", |
| -expected_net_error, 1); |
| |
| if (headers_received == HeadersReceived::kHeadersReceived) { |
| histograms_->ExpectUniqueSample( |
| "Net.ConnectionInfo.MainFrame", |
| net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, 1); |
| } else { |
| histograms_->ExpectTotalCount("Net.ConnectionInfo.MainFrame", 0); |
| } |
| |
| // Favicon may or may not have been loaded. |
| EXPECT_GE( |
| 1u, |
| histograms_->GetAllSamples("Net.ErrorCodesForSubresources3").size()); |
| EXPECT_GE( |
| 1u, |
| histograms_->GetAllSamples("Net.ConnectionInfo.SubResource").size()); |
| |
| return; |
| } |
| |
| // If not testing the main frame, there should also be just one result for |
| // the main frame. |
| histograms_->ExpectUniqueSample("Net.ErrorCodesForMainFrame4", -net::OK, 1); |
| |
| // Some fuzziness here because of the favicon. It should typically succeed, |
| // but allow it to have been aborted, too, since the test server won't |
| // return a valid icon. |
| std::vector<base::Bucket> buckets = |
| histograms_->GetAllSamples("Net.ErrorCodesForSubresources3"); |
| bool found_expected_load = false; |
| bool found_favicon_load = false; |
| for (auto& bucket : buckets) { |
| if (!found_expected_load && bucket.min == -expected_net_error) { |
| found_expected_load = true; |
| bucket.count--; |
| } |
| if (!found_favicon_load && bucket.count > 0 && |
| (bucket.min == -net::OK || bucket.min == -net::ERR_ABORTED)) { |
| found_favicon_load = true; |
| bucket.count--; |
| } |
| EXPECT_EQ(0, bucket.count) |
| << "Found unexpected load with result: " << bucket.min; |
| } |
| EXPECT_TRUE(found_expected_load); |
| |
| if (GetParam() != RequestType::kImage) { |
| histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0); |
| } else { |
| histograms_->ExpectUniqueSample("Net.ErrorCodesForImages2", |
| -expected_net_error, 1); |
| } |
| |
| // A subresource load requires a main frame load, which is only logged for |
| // network URLs. |
| if (network_accessed == NetworkAccessed::kNetworkAccessed) { |
| histograms_->ExpectUniqueSample( |
| "Net.ConnectionInfo.MainFrame", |
| net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, 1); |
| if (headers_received == HeadersReceived::kHeadersReceived) { |
| // Favicon request may or may not have received a response. |
| size_t subresources = |
| histograms_->GetAllSamples("Net.ConnectionInfo.SubResource").size(); |
| EXPECT_LE(1u, subresources); |
| EXPECT_GE(2u, subresources); |
| } else { |
| histograms_->ExpectTotalCount("Net.ConnectionInfo.SubResource", 0); |
| } |
| } else { |
| histograms_->ExpectTotalCount("Net.ConnectionInfo.MainFrame", 0); |
| histograms_->ExpectTotalCount("Net.ConnectionInfo.SubResource", 0); |
| } |
| } |
| |
| // Checks all relevant histograms in the case a new main frame navigation |
| // interrupted the first one. The request identified by GetParam() is expected |
| // to fail with net::ERR_ABORTED. |
| void CheckHistogramsAfterMainFrameInterruption() { |
| // Some metrics may come from the renderer. This call ensures that those |
| // metrics are available. |
| SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| |
| if (GetParam() == RequestType::kMainFrame) { |
| // Can't check Net.ErrorCodesForSubresources3, due to the favicon, which |
| // Chrome may or may not have attempted to load. |
| histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0); |
| |
| histograms_->ExpectTotalCount("Net.ErrorCodesForMainFrame4", 2); |
| EXPECT_EQ(1, histograms_->GetBucketCount("Net.ErrorCodesForMainFrame4", |
| -net::ERR_ABORTED)); |
| EXPECT_EQ(1, histograms_->GetBucketCount("Net.ErrorCodesForMainFrame4", |
| -net::OK)); |
| return; |
| } |
| |
| histograms_->ExpectUniqueSample("Net.ErrorCodesForMainFrame4", -net::OK, 2); |
| |
| // Some fuzziness here because of the favicon. It should typically succeed, |
| // but allow it to have been aborted, too, since the test server won't |
| // return a valid icon. |
| std::vector<base::Bucket> buckets = |
| histograms_->GetAllSamples("Net.ErrorCodesForSubresources3"); |
| bool found_expected_load = false; |
| int found_favicon_loads = 0; |
| for (auto& bucket : buckets) { |
| if (!found_expected_load && bucket.min == -net::ERR_ABORTED) { |
| found_expected_load = true; |
| bucket.count--; |
| } |
| // Allow up to two favicon loads, one for the original page load, that was |
| // interrupted by a new load, and one for the new page load. |
| if (found_favicon_loads < 2 && bucket.count > 0 && |
| (bucket.min == -net::OK || bucket.min == -net::ERR_ABORTED)) { |
| found_favicon_loads++; |
| bucket.count--; |
| } |
| EXPECT_EQ(0, bucket.count) |
| << "Found unexpected load with result: " << bucket.min; |
| } |
| EXPECT_TRUE(found_expected_load); |
| |
| if (GetParam() != RequestType::kImage) { |
| histograms_->ExpectTotalCount("Net.ErrorCodesForImages2", 0); |
| } else { |
| histograms_->ExpectUniqueSample("Net.ErrorCodesForImages2", |
| -net::ERR_ABORTED, 1); |
| } |
| } |
| |
| // Send headers and a partial body to |interesting_http_response_|. Doesn't |
| // terminate the response, so the socket can either be closed, or the request |
| // aborted. |
| void SendHeadersPartialBody() { |
| // Sending a body that's too short will result in an error after all the |
| // bytes are read. |
| interesting_http_response_->Send("HTTP/1.1 200 Peachy\r\n"); |
| // Send a MIME type to avoid MIME sniffing (Shouldn't matter, but it's one |
| // less thing to worry about). |
| if (GetParam() == RequestType::kImage) { |
| interesting_http_response_->Send("Content-Type: image/png\r\n"); |
| } else if (GetParam() == RequestType::kScript) { |
| interesting_http_response_->Send("Content-Type: text/css\r\n"); |
| } else { |
| interesting_http_response_->Send("Content-Type: text/html\r\n"); |
| } |
| interesting_http_response_->Send("Content-Length: 50\r\n\r\n"); |
| // This is the first byte of the PNG header, to avoid any chance of the |
| // request being cancelled for not looking like a PNG. |
| interesting_http_response_->Send("\x89"); |
| } |
| |
| content::WebContents* active_web_contents() { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| base::HistogramTester* histograms() { return histograms_.get(); } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| std::unique_ptr<net::test_server::ControllableHttpResponse> |
| uninteresting_main_frame_response_; |
| std::unique_ptr<net::test_server::ControllableHttpResponse> |
| interesting_http_response_; |
| std::unique_ptr<base::HistogramTester> histograms_; |
| }; |
| |
| // Testing before headers / during body is most interesting in the frame case, |
| // as it checks the before and after commit case, which with browser-side |
| // navigations / PlzNavigate, follow very different paths. |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, |
| NetErrorBeforeHeaders) { |
| TestNavigationObserver navigation_observer(active_web_contents(), 1); |
| StartNavigatingAndWaitForRequest(); |
| // Not sending any body will result in failing with ERR_EMPTY_RESPONSE, |
| // without receiving any headers so the load won't be committed until the |
| // error page is seen. |
| interesting_http_response()->Done(); |
| navigation_observer.Wait(); |
| |
| CheckHistograms(net::ERR_EMPTY_RESPONSE, HeadersReceived::kNoHeadersReceived, |
| NetworkAccessed::kNetworkAccessed); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, NetErrorDuringBody) { |
| TestNavigationObserver navigation_observer(active_web_contents(), 1); |
| StartNavigatingAndWaitForRequest(); |
| SendHeadersPartialBody(); |
| interesting_http_response()->Done(); |
| navigation_observer.Wait(); |
| |
| CheckHistograms(net::ERR_CONTENT_LENGTH_MISMATCH, |
| HeadersReceived::kHeadersReceived, |
| NetworkAccessed::kNetworkAccessed); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, CancelBeforeHeaders) { |
| TestNavigationObserver navigation_observer(active_web_contents(), 1); |
| StartNavigatingAndWaitForRequest(); |
| active_web_contents()->Stop(); |
| navigation_observer.Wait(); |
| |
| CheckHistograms(net::ERR_ABORTED, HeadersReceived::kNoHeadersReceived, |
| NetworkAccessed::kNetworkAccessed); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, CancelDuringBody) { |
| TestNavigationObserver navigation_observer(active_web_contents(), 1); |
| StartNavigatingAndWaitForRequest(); |
| SendHeadersPartialBody(); |
| |
| // Unfortunately, there's no way to ensure that the body has partially been |
| // received, so can only wait and hope. If the partial body hasn't been |
| // recieved by the time Stop() is called, the test should still pass, however. |
| base::RunLoop run_loop; |
| base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(1)); |
| run_loop.Run(); |
| |
| active_web_contents()->Stop(); |
| navigation_observer.Wait(); |
| |
| CheckHistograms(net::ERR_ABORTED, HeadersReceived::kHeadersReceived, |
| NetworkAccessed::kNetworkAccessed); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, |
| InterruptedBeforeHeaders) { |
| StartNavigatingAndWaitForRequest(); |
| |
| TestNavigationObserver navigation_observer(active_web_contents(), 1); |
| // Can't use ui_test_utils::NavigateToURLWithDisposition(), as it will wait |
| // for the current load to stop, rather than interrupting it. |
| browser()->OpenURL(OpenURLParams( |
| embedded_test_server()->GetURL("/echo"), Referrer(), |
| WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, |
| false /* is_renderer_initiated */)); |
| navigation_observer.Wait(); |
| |
| CheckHistogramsAfterMainFrameInterruption(); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, |
| InterruptedCancelDuringBody) { |
| StartNavigatingAndWaitForRequest(); |
| SendHeadersPartialBody(); |
| |
| // Unfortunately, there's no way to ensure that the body has partially been |
| // received, so can only wait and hope. If the partial body hasn't been |
| // recieved by the time Stop() is called, the test should still pass, however. |
| base::RunLoop run_loop; |
| base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(1)); |
| run_loop.Run(); |
| |
| TestNavigationObserver navigation_observer(active_web_contents(), 1); |
| // Can't use ui_test_utils::NavigateToURLWithDisposition(), as it will wait |
| // for the current load to stop, rather than interrupting it. |
| browser()->OpenURL(OpenURLParams( |
| embedded_test_server()->GetURL("/echo"), Referrer(), |
| WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, |
| false /* is_renderer_initiated */)); |
| navigation_observer.Wait(); |
| |
| CheckHistogramsAfterMainFrameInterruption(); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, SuccessWithBody) { |
| TestNavigationObserver navigation_observer(active_web_contents(), 1); |
| StartNavigatingAndWaitForRequest(); |
| interesting_http_response()->Send("HTTP/1.1 200 Peachy\r\n\r\n"); |
| interesting_http_response()->Send("Grapefruit"); |
| interesting_http_response()->Done(); |
| navigation_observer.Wait(); |
| |
| CheckHistograms(net::OK, HeadersReceived::kHeadersReceived, |
| NetworkAccessed::kNetworkAccessed); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, SuccessWithEmptyBody) { |
| TestNavigationObserver navigation_observer(active_web_contents(), 1); |
| StartNavigatingAndWaitForRequest(); |
| interesting_http_response()->Send("HTTP/1.1 200 Peachy\r\n"); |
| interesting_http_response()->Send("Content-Length: 0\r\n\r\n"); |
| interesting_http_response()->Done(); |
| navigation_observer.Wait(); |
| |
| CheckHistograms(net::OK, HeadersReceived::kHeadersReceived, |
| NetworkAccessed::kNetworkAccessed); |
| } |
| |
| // Downloads should not be logged (Either as successes or failures). |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, Download) { |
| // Only frames can be converted to downloads. |
| if (GetParam() != RequestType::kMainFrame && |
| GetParam() != RequestType::kSubFrame) { |
| return; |
| } |
| |
| browser()->profile()->GetPrefs()->SetInteger( |
| prefs::kDownloadRestrictions, |
| static_cast<int>(DownloadPrefs::DownloadRestriction::ALL_FILES)); |
| browser()->profile()->GetPrefs()->SetBoolean(prefs::kPromptForDownload, |
| false); |
| |
| // Need this to wait for the download to be fully cancelled to avoid a |
| // confirmation prompt on quit. |
| DownloadTestObserverTerminal download_test_observer_terminal( |
| BrowserContext::GetDownloadManager(browser()->profile()), 1, |
| DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_IGNORE); |
| |
| TestNavigationObserver navigation_observer(active_web_contents(), 1); |
| StartNavigatingAndWaitForRequest(); |
| interesting_http_response()->Send("HTTP/1.1 200 Peachy\r\n"); |
| interesting_http_response()->Send( |
| "Content-Type: binary/octet-stream\r\n\r\n"); |
| interesting_http_response()->Send("\x01"); |
| interesting_http_response()->Done(); |
| navigation_observer.Wait(); |
| |
| download_test_observer_terminal.WaitForFinished(); |
| |
| // Some metrics may come from the renderer. This call ensures that those |
| // metrics are available. |
| SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| |
| if (GetParam() == RequestType::kMainFrame) { |
| histograms()->ExpectTotalCount("Net.ErrorCodesForImages2", 0); |
| histograms()->ExpectTotalCount("Net.ErrorCodesForMainFrame4", 0); |
| histograms()->ExpectTotalCount("Net.ConnectionInfo.MainFrame", 0); |
| // Favicon may or may not have been loaded. |
| EXPECT_GE( |
| 1u, |
| histograms()->GetAllSamples("Net.ErrorCodesForSubresources3").size()); |
| EXPECT_GE( |
| 1u, |
| histograms()->GetAllSamples("Net.ConnectionInfo.SubResource").size()); |
| |
| return; |
| } |
| |
| // If not testing the main frame, there should also be just one result for |
| // the main frame. |
| histograms()->ExpectUniqueSample("Net.ErrorCodesForMainFrame4", -net::OK, 1); |
| |
| // Some fuzziness here because of the favicon. It should typically succeed, |
| // but allow it to have been aborted, too, since the test server won't |
| // return a valid icon. |
| std::vector<base::Bucket> buckets = |
| histograms()->GetAllSamples("Net.ErrorCodesForSubresources3"); |
| bool found_favicon_load = false; |
| for (auto& bucket : buckets) { |
| if (!found_favicon_load && bucket.count > 0 && |
| (bucket.min == -net::OK || bucket.min == -net::ERR_ABORTED)) { |
| found_favicon_load = true; |
| bucket.count--; |
| } |
| EXPECT_EQ(0, bucket.count) |
| << "Found unexpected load with result: " << bucket.min; |
| } |
| |
| histograms()->ExpectUniqueSample( |
| "Net.ConnectionInfo.MainFrame", |
| net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, 1); |
| // Favicon request may or may not have received a response. |
| size_t subresources = |
| histograms()->GetAllSamples("Net.ConnectionInfo.SubResource").size(); |
| EXPECT_LE(0u, subresources); |
| EXPECT_GE(1u, subresources); |
| } |
| |
| // A few tests for file:// URLs, so that URLs not handled by the network service |
| // itself have some coverage. |
| |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, FileURLError) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::ScopedTempDir temp_dir_; |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| |
| base::FilePath main_frame_path = temp_dir_.GetPath().AppendASCII("main.html"); |
| if (GetParam() != RequestType::kMainFrame) { |
| std::string main_frame_data = GetMainFrameContents("subresource"); |
| ASSERT_EQ(static_cast<int>(main_frame_data.length()), |
| base::WriteFile(main_frame_path, main_frame_data.c_str(), |
| main_frame_data.length())); |
| } |
| |
| ui_test_utils::NavigateToURL(browser(), |
| net::FilePathToFileURL(main_frame_path)); |
| CheckHistograms(net::ERR_FILE_NOT_FOUND, HeadersReceived::kNoHeadersReceived, |
| NetworkAccessed::kNoNetworkAccessed); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(NetworkRequestMetricsBrowserTest, FileURLSuccess) { |
| const char kSubresourcePath[] = "subresource"; |
| |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::ScopedTempDir temp_dir_; |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| |
| base::FilePath main_frame_path = temp_dir_.GetPath().AppendASCII("main.html"); |
| std::string main_frame_data = "foo"; |
| if (GetParam() != RequestType::kMainFrame) |
| main_frame_data = GetMainFrameContents(kSubresourcePath); |
| ASSERT_EQ(static_cast<int>(main_frame_data.length()), |
| base::WriteFile(main_frame_path, main_frame_data.c_str(), |
| main_frame_data.length())); |
| if (GetParam() != RequestType::kMainFrame) { |
| std::string subresource_data = "foo"; |
| ASSERT_EQ( |
| static_cast<int>(subresource_data.length()), |
| base::WriteFile(temp_dir_.GetPath().AppendASCII(kSubresourcePath), |
| subresource_data.c_str(), subresource_data.length())); |
| } |
| |
| ui_test_utils::NavigateToURL(browser(), |
| net::FilePathToFileURL(main_frame_path)); |
| CheckHistograms(net::OK, HeadersReceived::kNoHeadersReceived, |
| NetworkAccessed::kNoNetworkAccessed); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(, |
| NetworkRequestMetricsBrowserTest, |
| testing::Values(RequestType::kMainFrame, |
| RequestType::kSubFrame, |
| RequestType::kImage, |
| RequestType::kScript)); |
| |
| } // namespace |
| } // namespace content |