| // 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 "chrome/browser/data_usage/tab_id_annotator.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/sessions/session_tab_helper.h" |
| #include "chrome/test/base/chrome_render_view_host_test_harness.h" |
| #include "components/data_usage/core/data_use.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/resource_request_info.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/previews_state.h" |
| #include "net/base/network_change_notifier.h" |
| #include "net/base/request_priority.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 "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| |
| using content::BrowserThread; |
| using data_usage::DataUse; |
| |
| namespace chrome_browser_data_usage { |
| |
| namespace { |
| |
| class TabIdAnnotatorTest : public ChromeRenderViewHostTestHarness { |
| public: |
| TabIdAnnotatorTest() |
| : ChromeRenderViewHostTestHarness( |
| content::TestBrowserThreadBundle::REAL_IO_THREAD) {} |
| |
| ~TabIdAnnotatorTest() override {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TabIdAnnotatorTest); |
| }; |
| |
| // Synthesizes a DataUse object with the given |tab_id|. |
| std::unique_ptr<DataUse> CreateDataUse(int32_t tab_id, int render_process_id) { |
| auto data_use = std::unique_ptr<DataUse>(new DataUse( |
| GURL("http://foo.com"), base::TimeTicks(), GURL(), tab_id, |
| net::NetworkChangeNotifier::CONNECTION_UNKNOWN, std::string(), 100, 100)); |
| return data_use; |
| } |
| |
| // Expects that |expected| and |actual| are equal. |
| void ExpectDataUse(std::unique_ptr<DataUse> expected, |
| std::unique_ptr<DataUse> actual) { |
| // Single out the |tab_id| for better debug output in failure cases. |
| EXPECT_EQ(expected->tab_id, actual->tab_id); |
| EXPECT_EQ(*expected, *actual); |
| } |
| |
| // Expects that |expected| and |actual| are equal, then quits |ui_run_loop| on |
| // the UI thread. |
| void ExpectDataUseAndQuit(base::RunLoop* ui_run_loop, |
| std::unique_ptr<DataUse> expected, |
| std::unique_ptr<DataUse> actual) { |
| DCHECK(ui_run_loop); |
| ExpectDataUse(std::move(expected), std::move(actual)); |
| |
| // This can't use run_loop->QuitClosure here because that uses WeakPtrs, which |
| // aren't thread safe. |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| base::BindOnce(&base::RunLoop::Quit, base::Unretained(ui_run_loop))); |
| } |
| |
| // Tests that for a sample URLRequest, associated with the given |
| // |render_process_id| and |render_frame_id|, repeatedly annotating DataUse for |
| // that URLRequest yields the |expected_tab_id|. |ui_run_loop| is the RunLoop on |
| // the UI thread that should be quit after all the annotations are done. |
| // Passing in -1 for either or both of |render_process_id| or |render_frame_id| |
| // indicates that the URLRequest should have no associated ResourceRequestInfo. |
| void TestAnnotateOnIOThread(base::RunLoop* ui_run_loop, |
| int render_process_id, |
| int render_frame_id, |
| int32_t expected_tab_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(ui_run_loop); |
| |
| TabIdAnnotator annotator; |
| net::TestURLRequestContext context; |
| net::TestDelegate test_delegate; |
| std::unique_ptr<net::URLRequest> request = |
| context.CreateRequest(GURL("http://foo.com"), net::IDLE, &test_delegate, |
| TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| if (render_process_id != -1 && render_frame_id != -1) { |
| // The only args that matter here for the ResourceRequestInfo are the |
| // |request|, the |render_process_id|, and the |render_frame_id|; arbitrary |
| // values are used for all the other args. |
| content::ResourceRequestInfo::AllocateForTesting( |
| request.get(), content::RESOURCE_TYPE_MAIN_FRAME, nullptr, |
| render_process_id, -1, render_frame_id, true, false, true, true, |
| content::PREVIEWS_OFF); |
| } |
| |
| // An invalid tab ID to check that the annotator always sets the tab ID. -2 is |
| // used because a tab ID of -1 is a valid value that means "no tab was found". |
| const int32_t kInvalidTabId = -2; |
| |
| // Annotate two separate DataUse objects to ensure that repeated annotations |
| // for the same URLRequest work properly. |
| std::unique_ptr<DataUse> first_expected_data_use = |
| CreateDataUse(expected_tab_id, render_process_id); |
| annotator.Annotate( |
| request.get(), CreateDataUse(kInvalidTabId, render_process_id), |
| base::Bind(&ExpectDataUse, base::Passed(&first_expected_data_use))); |
| |
| // Quit the |ui_run_loop| after the second annotation. |
| std::unique_ptr<DataUse> second_expected_data_use = |
| CreateDataUse(expected_tab_id, render_process_id); |
| annotator.Annotate(request.get(), |
| CreateDataUse(kInvalidTabId, render_process_id), |
| base::Bind(&ExpectDataUseAndQuit, ui_run_loop, |
| base::Passed(&second_expected_data_use))); |
| } |
| |
| TEST_F(TabIdAnnotatorTest, AnnotateWithNoRenderFrame) { |
| base::RunLoop ui_run_loop; |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::BindOnce(&TestAnnotateOnIOThread, &ui_run_loop, |
| -1 /* render_process_id */, -1 /* render_frame_id */, |
| -1 /* expected_tab_id */)); |
| ui_run_loop.Run(); |
| } |
| |
| TEST_F(TabIdAnnotatorTest, AnnotateWithRenderFrameAndNoTab) { |
| base::RunLoop ui_run_loop; |
| // |web_contents()| isn't a tab, so it shouldn't have a tab ID. |
| EXPECT_EQ(-1, SessionTabHelper::IdForTab(web_contents())); |
| |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::BindOnce(&TestAnnotateOnIOThread, &ui_run_loop, |
| web_contents()->GetMainFrame()->GetProcess()->GetID(), |
| web_contents()->GetMainFrame()->GetRoutingID(), |
| -1 /* expected_tab_id */)); |
| ui_run_loop.Run(); |
| } |
| |
| TEST_F(TabIdAnnotatorTest, AnnotateWithRenderFrameAndTab) { |
| base::RunLoop ui_run_loop; |
| // Make |web_contents()| into a tab. |
| SessionTabHelper::CreateForWebContents(web_contents()); |
| int32_t expected_tab_id = SessionTabHelper::IdForTab(web_contents()); |
| // |web_contents()| is a tab, so it should have a tab ID. |
| EXPECT_NE(-1, expected_tab_id); |
| |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::BindOnce(&TestAnnotateOnIOThread, &ui_run_loop, |
| web_contents()->GetMainFrame()->GetProcess()->GetID(), |
| web_contents()->GetMainFrame()->GetRoutingID(), |
| expected_tab_id)); |
| ui_run_loop.Run(); |
| } |
| |
| } // namespace |
| |
| } // namespace chrome_browser_data_usage |