site_data_recorder_unittest.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. // Copyright 2020 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 "components/performance_manager/public/decorators/site_data_recorder.h"
  5. #include <memory>
  6. #include "base/location.h"
  7. #include "base/memory/raw_ptr.h"
  8. #include "base/run_loop.h"
  9. #include "base/test/bind.h"
  10. #include "base/threading/sequence_bound.h"
  11. #include "components/performance_manager/graph/page_node_impl.h"
  12. #include "components/performance_manager/persistence/site_data/site_data_cache.h"
  13. #include "components/performance_manager/persistence/site_data/site_data_cache_factory.h"
  14. #include "components/performance_manager/persistence/site_data/site_data_impl.h"
  15. #include "components/performance_manager/persistence/site_data/site_data_writer.h"
  16. #include "components/performance_manager/persistence/site_data/tab_visibility.h"
  17. #include "components/performance_manager/persistence/site_data/unittest_utils.h"
  18. #include "components/performance_manager/public/graph/graph.h"
  19. #include "components/performance_manager/public/performance_manager.h"
  20. #include "components/performance_manager/test_support/performance_manager_test_harness.h"
  21. #include "content/public/browser/browser_task_traits.h"
  22. #include "content/public/browser/browser_thread.h"
  23. #include "content/public/test/web_contents_tester.h"
  24. #include "testing/gmock/include/gmock/gmock.h"
  25. #include "testing/gtest/include/gtest/gtest.h"
  26. #include "url/origin.h"
  27. namespace performance_manager {
  28. constexpr base::TimeDelta kTitleOrFaviconChangePostLoadGracePeriod =
  29. base::Seconds(20);
  30. constexpr base::TimeDelta kFeatureUsagePostBackgroundGracePeriod =
  31. base::Seconds(10);
  32. // A mock implementation of a SiteDataWriter.
  33. class LenientMockDataWriter : public SiteDataWriter {
  34. public:
  35. LenientMockDataWriter(const url::Origin& origin,
  36. scoped_refptr<internal::SiteDataImpl> impl)
  37. : SiteDataWriter(impl), origin_(origin) {}
  38. ~LenientMockDataWriter() override {
  39. if (on_destroy_indicator_)
  40. *on_destroy_indicator_ = true;
  41. }
  42. LenientMockDataWriter(const LenientMockDataWriter& other) = delete;
  43. LenientMockDataWriter& operator=(const LenientMockDataWriter&) = delete;
  44. MOCK_METHOD1(NotifySiteLoaded, void(TabVisibility));
  45. MOCK_METHOD1(NotifySiteUnloaded, void(TabVisibility));
  46. MOCK_METHOD1(NotifySiteForegrounded, void(bool));
  47. MOCK_METHOD1(NotifySiteBackgrounded, void(bool));
  48. MOCK_METHOD0(NotifyUpdatesFaviconInBackground, void());
  49. MOCK_METHOD0(NotifyUpdatesTitleInBackground, void());
  50. MOCK_METHOD0(NotifyUsesAudioInBackground, void());
  51. MOCK_METHOD3(NotifyLoadTimePerformanceMeasurement,
  52. void(base::TimeDelta, base::TimeDelta, uint64_t));
  53. // Used to record the destruction of this object.
  54. void SetOnDestroyIndicator(bool* on_destroy_indicator) {
  55. if (on_destroy_indicator)
  56. EXPECT_FALSE(*on_destroy_indicator);
  57. on_destroy_indicator_ = on_destroy_indicator;
  58. }
  59. const url::Origin& Origin() const { return origin_; }
  60. private:
  61. raw_ptr<bool> on_destroy_indicator_ = nullptr;
  62. url::Origin origin_;
  63. };
  64. using MockDataWriter = ::testing::StrictMock<LenientMockDataWriter>;
  65. // A data cache that serves MockDataWriter objects.
  66. class MockDataCache : public SiteDataCache {
  67. public:
  68. MockDataCache() = default;
  69. ~MockDataCache() override = default;
  70. MockDataCache(const MockDataCache& other) = delete;
  71. MockDataCache& operator=(const MockDataCache&) = delete;
  72. // SiteDataCache:
  73. std::unique_ptr<SiteDataReader> GetReaderForOrigin(
  74. const url::Origin& origin) override {
  75. return nullptr;
  76. }
  77. std::unique_ptr<SiteDataWriter> GetWriterForOrigin(
  78. const url::Origin& origin) override {
  79. scoped_refptr<internal::SiteDataImpl> fake_impl =
  80. base::WrapRefCounted(new internal::SiteDataImpl(
  81. origin, delegate_.GetWeakPtr(), &data_store_));
  82. return std::make_unique<MockDataWriter>(origin, fake_impl);
  83. }
  84. bool IsRecording() const override { return true; }
  85. int Size() const override { return 0; }
  86. private:
  87. testing::NoopSiteDataStore data_store_;
  88. // The mock delegate used by the SiteDataImpl objects
  89. // created by this class, NiceMock is used to avoid having to set
  90. // expectations in test cases that don't care about this.
  91. ::testing::NiceMock<testing::MockSiteDataImplOnDestroyDelegate> delegate_;
  92. };
  93. void NavigatePageNodeOnUIThread(content::WebContents* contents,
  94. const GURL& url) {
  95. EXPECT_TRUE(contents);
  96. content::WebContentsTester* web_contents_tester =
  97. content::WebContentsTester::For(contents);
  98. EXPECT_TRUE(web_contents_tester);
  99. web_contents_tester->NavigateAndCommit(url);
  100. }
  101. void RunTaskOnPMSequence(base::OnceClosure task) {
  102. base::RunLoop run_loop;
  103. PerformanceManager::CallOnGraph(FROM_HERE, std::move(task));
  104. PerformanceManager::CallOnGraph(FROM_HERE, run_loop.QuitClosure());
  105. run_loop.Run();
  106. }
  107. MockDataWriter* GetMockWriterForPageNode(const PageNode* page_node) {
  108. return static_cast<MockDataWriter*>(
  109. SiteDataRecorder::Data::GetForTesting(page_node)->writer());
  110. }
  111. class SiteDataRecorderTest : public PerformanceManagerTestHarness {
  112. public:
  113. SiteDataRecorderTest()
  114. : PerformanceManagerTestHarness(
  115. content::BrowserTaskEnvironment::TimeSource::MOCK_TIME) {}
  116. ~SiteDataRecorderTest() override = default;
  117. explicit SiteDataRecorderTest(const SiteDataRecorderTest& other) = delete;
  118. SiteDataRecorderTest& operator=(const SiteDataRecorderTest&) = delete;
  119. void SetUp() override {
  120. PerformanceManagerTestHarness::SetUp();
  121. cache_factory_ = base::SequenceBound<SiteDataCacheFactory>(
  122. PerformanceManager::GetTaskRunner());
  123. auto recorder = std::make_unique<SiteDataRecorder>();
  124. recorder_ = recorder.get();
  125. PerformanceManager::PassToGraph(FROM_HERE, std::move(recorder));
  126. auto browser_context_id = GetBrowserContext()->UniqueId();
  127. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  128. SiteDataCacheFactory::GetInstance()->SetCacheForTesting(
  129. browser_context_id, std::make_unique<MockDataCache>());
  130. }));
  131. SetContents(CreateTestWebContents());
  132. base::WeakPtr<PageNode> page_node =
  133. PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents());
  134. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  135. auto* page_node_impl = PageNodeImpl::FromNode(page_node.get());
  136. page_node_impl->SetIsAudible(false);
  137. page_node_impl->SetIsVisible(false);
  138. page_node_impl->SetLoadingState(PageNode::LoadingState::kLoading);
  139. }));
  140. }
  141. void TearDown() override {
  142. DeleteContents();
  143. recorder_ = nullptr;
  144. cache_factory_.SynchronouslyResetForTest();
  145. PerformanceManagerTestHarness::TearDown();
  146. }
  147. const GURL kTestUrl1 = GURL("http://foo.com");
  148. const GURL kTestUrl2 = GURL("http://bar.com");
  149. private:
  150. raw_ptr<SiteDataRecorder> recorder_ = nullptr;
  151. base::SequenceBound<SiteDataCacheFactory> cache_factory_;
  152. };
  153. TEST_F(SiteDataRecorderTest, NavigationEventsBasicTests) {
  154. base::WeakPtr<PageNode> page_node =
  155. PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents());
  156. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  157. EXPECT_TRUE(page_node);
  158. EXPECT_FALSE(
  159. SiteDataRecorder::Data::GetForTesting(page_node.get())->writer());
  160. }));
  161. // Send a navigation event with the |committed| bit set and make sure that a
  162. // writer has been created for this origin.
  163. NavigatePageNodeOnUIThread(web_contents(), kTestUrl1);
  164. MockDataWriter* mock_writer = nullptr;
  165. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  166. mock_writer = GetMockWriterForPageNode(page_node.get());
  167. ASSERT_TRUE(mock_writer);
  168. EXPECT_EQ(url::Origin::Create(kTestUrl1), mock_writer->Origin());
  169. }));
  170. {
  171. // A navigation to the same origin shouldn't cause caused this writer to get
  172. // destroyed.
  173. bool writer_has_been_destroyed = false;
  174. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  175. mock_writer->SetOnDestroyIndicator(&writer_has_been_destroyed);
  176. }));
  177. NavigatePageNodeOnUIThread(web_contents(), kTestUrl1);
  178. RunTaskOnPMSequence(base::BindLambdaForTesting(
  179. [&]() { EXPECT_FALSE(writer_has_been_destroyed); }));
  180. // Navigate to a different origin and make sure that this causes the
  181. // destruction of the writer.
  182. NavigatePageNodeOnUIThread(web_contents(), kTestUrl2);
  183. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  184. EXPECT_TRUE(writer_has_been_destroyed);
  185. mock_writer = GetMockWriterForPageNode(page_node.get());
  186. EXPECT_EQ(url::Origin::Create(kTestUrl2), mock_writer->Origin());
  187. mock_writer->SetOnDestroyIndicator(nullptr);
  188. }));
  189. }
  190. }
  191. // Test that the feature usage events get forwarded to the writer when the tab
  192. // is in background.
  193. TEST_F(SiteDataRecorderTest, FeatureEventsGetForwardedWhenInBackground) {
  194. base::WeakPtr<PageNode> page_node =
  195. PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents());
  196. NavigatePageNodeOnUIThread(web_contents(), kTestUrl1);
  197. MockDataWriter* mock_writer = nullptr;
  198. PageNodeImpl* node_impl = nullptr;
  199. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  200. mock_writer = GetMockWriterForPageNode(page_node.get());
  201. ASSERT_TRUE(mock_writer);
  202. EXPECT_EQ(url::Origin::Create(kTestUrl1), mock_writer->Origin());
  203. node_impl = PageNodeImpl::FromNode(page_node.get());
  204. EXPECT_CALL(*mock_writer, NotifySiteLoaded(TabVisibility::kBackground));
  205. node_impl->SetLoadingState(PageNode::LoadingState::kLoadedIdle);
  206. ::testing::Mock::VerifyAndClear(mock_writer);
  207. EXPECT_CALL(*mock_writer, NotifySiteForegrounded(true));
  208. }));
  209. web_contents()->WasShown();
  210. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  211. ::testing::Mock::VerifyAndClear(mock_writer);
  212. // Ensure that no event gets forwarded if the tab is not in background.
  213. node_impl->OnFaviconUpdated();
  214. ::testing::Mock::VerifyAndClear(mock_writer);
  215. node_impl->OnTitleUpdated();
  216. ::testing::Mock::VerifyAndClear(mock_writer);
  217. node_impl->SetIsAudible(true);
  218. ::testing::Mock::VerifyAndClear(mock_writer);
  219. EXPECT_CALL(*mock_writer, NotifySiteBackgrounded(true));
  220. }));
  221. web_contents()->WasHidden();
  222. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  223. ::testing::Mock::VerifyAndClear(mock_writer);
  224. // Title and Favicon should be ignored during the post-loading grace period.
  225. node_impl->OnFaviconUpdated();
  226. node_impl->OnTitleUpdated();
  227. ::testing::Mock::VerifyAndClear(mock_writer);
  228. }));
  229. task_environment()->FastForwardBy(kTitleOrFaviconChangePostLoadGracePeriod);
  230. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  231. EXPECT_CALL(*mock_writer, NotifyUpdatesFaviconInBackground());
  232. node_impl->OnFaviconUpdated();
  233. ::testing::Mock::VerifyAndClear(mock_writer);
  234. EXPECT_CALL(*mock_writer, NotifyUpdatesTitleInBackground());
  235. node_impl->OnTitleUpdated();
  236. ::testing::Mock::VerifyAndClear(mock_writer);
  237. // Brievly switch the tab to foreground to reset the last backgrounded time.
  238. EXPECT_CALL(*mock_writer, NotifySiteForegrounded(true));
  239. EXPECT_CALL(*mock_writer, NotifySiteBackgrounded(true));
  240. }));
  241. web_contents()->WasShown();
  242. web_contents()->WasHidden();
  243. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  244. ::testing::Mock::VerifyAndClear(mock_writer);
  245. // These events should be ignored during the post-background grace period.
  246. node_impl->SetIsAudible(true);
  247. node_impl->SetIsAudible(false);
  248. node_impl->OnFaviconUpdated();
  249. node_impl->OnTitleUpdated();
  250. ::testing::Mock::VerifyAndClear(mock_writer);
  251. }));
  252. task_environment()->FastForwardBy(kFeatureUsagePostBackgroundGracePeriod);
  253. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  254. EXPECT_CALL(*mock_writer, NotifyUsesAudioInBackground());
  255. EXPECT_CALL(*mock_writer, NotifyUpdatesFaviconInBackground());
  256. EXPECT_CALL(*mock_writer, NotifyUpdatesTitleInBackground());
  257. node_impl->SetIsAudible(true);
  258. node_impl->OnFaviconUpdated();
  259. node_impl->OnTitleUpdated();
  260. ::testing::Mock::VerifyAndClear(mock_writer);
  261. EXPECT_CALL(*mock_writer, NotifySiteUnloaded(TabVisibility::kBackground));
  262. }));
  263. NavigatePageNodeOnUIThread(web_contents(), GURL("about:blank"));
  264. }
  265. TEST_F(SiteDataRecorderTest, FeatureEventsIgnoredWhenLoadingInBackground) {
  266. base::WeakPtr<PageNode> page_node =
  267. PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents());
  268. NavigatePageNodeOnUIThread(web_contents(), kTestUrl1);
  269. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  270. MockDataWriter* mock_writer = GetMockWriterForPageNode(page_node.get());
  271. ASSERT_TRUE(mock_writer);
  272. EXPECT_EQ(url::Origin::Create(kTestUrl1), mock_writer->Origin());
  273. PageNodeImpl* node_impl = PageNodeImpl::FromNode(page_node.get());
  274. ::testing::Mock::VerifyAndClear(mock_writer);
  275. node_impl->OnFaviconUpdated();
  276. ::testing::Mock::VerifyAndClear(mock_writer);
  277. node_impl->OnTitleUpdated();
  278. ::testing::Mock::VerifyAndClear(mock_writer);
  279. node_impl->SetIsAudible(true);
  280. ::testing::Mock::VerifyAndClear(mock_writer);
  281. }));
  282. }
  283. TEST_F(SiteDataRecorderTest, VisibilityEvent) {
  284. base::WeakPtr<PageNode> page_node =
  285. PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents());
  286. NavigatePageNodeOnUIThread(web_contents(), kTestUrl1);
  287. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  288. MockDataWriter* mock_writer = GetMockWriterForPageNode(page_node.get());
  289. PageNodeImpl* node_impl = PageNodeImpl::FromNode(page_node.get());
  290. // Test that the visibility events get forwarded to the writer.
  291. EXPECT_CALL(*mock_writer, NotifySiteForegrounded(false));
  292. node_impl->SetIsVisible(true);
  293. ::testing::Mock::VerifyAndClear(mock_writer);
  294. EXPECT_CALL(*mock_writer, NotifySiteBackgrounded(false));
  295. node_impl->SetIsVisible(false);
  296. ::testing::Mock::VerifyAndClear(mock_writer);
  297. }));
  298. }
  299. TEST_F(SiteDataRecorderTest, LoadEvent) {
  300. base::WeakPtr<PageNode> page_node =
  301. PerformanceManager::GetPrimaryPageNodeForWebContents(web_contents());
  302. NavigatePageNodeOnUIThread(web_contents(), kTestUrl1);
  303. RunTaskOnPMSequence(base::BindLambdaForTesting([&]() {
  304. MockDataWriter* mock_writer = GetMockWriterForPageNode(page_node.get());
  305. PageNodeImpl* node_impl = PageNodeImpl::FromNode(page_node.get());
  306. // Test that the load/unload events get forwarded to the writer.
  307. EXPECT_CALL(*mock_writer, NotifySiteLoaded(TabVisibility::kBackground));
  308. node_impl->SetLoadingState(PageNode::LoadingState::kLoadedIdle);
  309. ::testing::Mock::VerifyAndClear(mock_writer);
  310. EXPECT_CALL(*mock_writer, NotifySiteUnloaded(TabVisibility::kBackground));
  311. node_impl->SetLoadingState(PageNode::LoadingState::kLoading);
  312. ::testing::Mock::VerifyAndClear(mock_writer);
  313. }));
  314. }
  315. } // namespace performance_manager