OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 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 |
| 5 #include <memory> |
| 6 |
| 7 #include "base/mac/scoped_nsobject.h" |
| 8 #include "base/message_loop/message_loop.h" |
| 9 #include "base/metrics/histogram_samples.h" |
| 10 #include "base/stl_util.h" |
| 11 #include "base/test/histogram_tester.h" |
| 12 #import "ios/chrome/browser/metrics/previous_session_info.h" |
| 13 #include "ios/chrome/browser/metrics/tab_usage_recorder.h" |
| 14 #import "ios/chrome/browser/metrics/tab_usage_recorder_delegate.h" |
| 15 #import "ios/chrome/browser/tabs/tab.h" |
| 16 #include "ios/testing/ocmock_complex_type_helper.h" |
| 17 #include "ios/web/public/test/test_web_thread.h" |
| 18 #import "ios/web/web_state/ui/crw_web_controller.h" |
| 19 #include "testing/gtest/include/gtest/gtest.h" |
| 20 #include "testing/platform_test.h" |
| 21 #include "third_party/ocmock/ocmock_extensions.h" |
| 22 |
| 23 @interface TURTestTabMock : OCMockComplexTypeHelper { |
| 24 GURL _url; |
| 25 } |
| 26 |
| 27 @property(nonatomic, assign) const GURL& url; |
| 28 @end |
| 29 |
| 30 @implementation TURTestTabMock |
| 31 - (const GURL&)url { |
| 32 return _url; |
| 33 } |
| 34 - (void)setUrl:(const GURL&)url { |
| 35 _url = url; |
| 36 } |
| 37 @end |
| 38 |
| 39 // A mock TabUsageRecorderDelegate which allows the unit tests to control |
| 40 // the count of live tabs returned from the |liveTabsCount| delegate method. |
| 41 @interface MockTabUsageRecorderDelegate : NSObject<TabUsageRecorderDelegate> { |
| 42 NSUInteger _tabCount; |
| 43 } |
| 44 |
| 45 // Sets the live tab count returned from the |liveTabsCount| delegate method. |
| 46 - (void)setLiveTabsCount:(NSUInteger)count; |
| 47 |
| 48 @end |
| 49 |
| 50 @implementation MockTabUsageRecorderDelegate |
| 51 |
| 52 - (void)setLiveTabsCount:(NSUInteger)count { |
| 53 _tabCount = count; |
| 54 } |
| 55 |
| 56 - (NSUInteger)liveTabsCount { |
| 57 return _tabCount; |
| 58 } |
| 59 |
| 60 @end |
| 61 |
| 62 namespace { |
| 63 |
| 64 // The number of alive tabs at a renderer termination used by unit test. |
| 65 const NSUInteger kAliveTabsCountAtRendererTermination = 2U; |
| 66 |
| 67 // The number of timestamps added to the renderer termination timestamp list |
| 68 // that are not counted in the RecentlyAliveTabs metric. |
| 69 const int kExpiredTimesAddedCount = 2; |
| 70 |
| 71 class TabUsageRecorderForTesting : public TabUsageRecorder { |
| 72 public: |
| 73 TabUsageRecorderForTesting(MockTabUsageRecorderDelegate* delegate) |
| 74 : TabUsageRecorder(delegate) {} |
| 75 // For testing only. |
| 76 base::TimeTicks RestoreStartTime() const { return restore_start_time_; } |
| 77 |
| 78 // Adds |time| to the deque keeping track of renderer termination |
| 79 // timestamps. |
| 80 void AddTimeToDeque(base::TimeTicks time) { |
| 81 termination_timestamps_.push_back(time); |
| 82 } |
| 83 }; |
| 84 |
| 85 class TabUsageRecorderTest : public PlatformTest { |
| 86 protected: |
| 87 void SetUp() override { |
| 88 loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_DEFAULT)); |
| 89 ui_thread_.reset(new web::TestWebThread(web::WebThread::UI, loop_.get())); |
| 90 histogram_tester_.reset(new base::HistogramTester()); |
| 91 // Set the delegate to nil to allow the relevant unit tests direct access to |
| 92 // the mock delegate. |
| 93 tab_usage_recorder_.reset(new TabUsageRecorderForTesting(nil)); |
| 94 webUrl_ = GURL("http://www.chromium.org"); |
| 95 nativeUrl_ = GURL("chrome://version"); |
| 96 } |
| 97 |
| 98 id MockTab(bool inMemory) { |
| 99 id tab_mock = [[TURTestTabMock alloc] |
| 100 initWithRepresentedObject:[OCMockObject mockForClass:[Tab class]]]; |
| 101 id web_controller_mock = |
| 102 [OCMockObject mockForClass:[CRWWebController class]]; |
| 103 [[[tab_mock stub] andReturn:web_controller_mock] webController]; |
| 104 [[[tab_mock stub] andReturnBool:false] isPrerenderTab]; |
| 105 [tab_mock setUrl:webUrl_]; |
| 106 [[[web_controller_mock stub] andReturnBool:inMemory] isViewAlive]; |
| 107 [[web_controller_mock stub] removeObserver:OCMOCK_ANY]; |
| 108 return [tab_mock autorelease]; |
| 109 } |
| 110 |
| 111 GURL webUrl_; |
| 112 GURL nativeUrl_; |
| 113 std::unique_ptr<base::MessageLoop> loop_; |
| 114 std::unique_ptr<web::TestWebThread> ui_thread_; |
| 115 std::unique_ptr<base::HistogramTester> histogram_tester_; |
| 116 std::unique_ptr<TabUsageRecorderForTesting> tab_usage_recorder_; |
| 117 }; |
| 118 |
| 119 TEST_F(TabUsageRecorderTest, SwitchBetweenInMemoryTabs) { |
| 120 id tab_mock_a = MockTab(true); |
| 121 id tab_mock_b = MockTab(true); |
| 122 |
| 123 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 124 histogram_tester_->ExpectUniqueSample(kSelectedTabHistogramName, |
| 125 TabUsageRecorder::IN_MEMORY, 1); |
| 126 } |
| 127 |
| 128 TEST_F(TabUsageRecorderTest, SwitchToEvictedTab) { |
| 129 id tab_mock_a = MockTab(true); |
| 130 id tab_mock_b = MockTab(false); |
| 131 |
| 132 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 133 histogram_tester_->ExpectUniqueSample(kSelectedTabHistogramName, |
| 134 TabUsageRecorder::EVICTED, 1); |
| 135 } |
| 136 |
| 137 TEST_F(TabUsageRecorderTest, SwitchFromEvictedTab) { |
| 138 id tab_mock_a = MockTab(false); |
| 139 id tab_mock_b = MockTab(true); |
| 140 |
| 141 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 142 histogram_tester_->ExpectUniqueSample(kSelectedTabHistogramName, |
| 143 TabUsageRecorder::IN_MEMORY, 1); |
| 144 } |
| 145 |
| 146 TEST_F(TabUsageRecorderTest, SwitchBetweenEvictedTabs) { |
| 147 id tab_mock_a = MockTab(false); |
| 148 id tab_mock_b = MockTab(false); |
| 149 |
| 150 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 151 histogram_tester_->ExpectUniqueSample(kSelectedTabHistogramName, |
| 152 TabUsageRecorder::EVICTED, 1); |
| 153 } |
| 154 |
| 155 TEST_F(TabUsageRecorderTest, CountPageLoadsBeforeEvictedTab) { |
| 156 id tab_mock_a = MockTab(true); |
| 157 id tab_mock_b = MockTab(false); |
| 158 |
| 159 // Call reload an arbitrary number of times. |
| 160 const int kNumReloads = 4; |
| 161 for (int i = 0; i < kNumReloads; i++) { |
| 162 tab_usage_recorder_->RecordPageLoadStart(tab_mock_a); |
| 163 } |
| 164 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 165 histogram_tester_->ExpectUniqueSample(kPageLoadsBeforeEvictedTabSelected, |
| 166 kNumReloads, 1); |
| 167 } |
| 168 |
| 169 TEST_F(TabUsageRecorderTest, CountNativePageLoadsBeforeEvictedTab) { |
| 170 id tab_mock_a = MockTab(true); |
| 171 id tab_mock_b = MockTab(false); |
| 172 [tab_mock_a setUrl:nativeUrl_]; |
| 173 [tab_mock_b setUrl:nativeUrl_]; |
| 174 |
| 175 // Call reload an arbitrary number of times. |
| 176 const int kNumReloads = 4; |
| 177 for (int i = 0; i < kNumReloads; i++) { |
| 178 tab_usage_recorder_->RecordPageLoadStart(tab_mock_a); |
| 179 } |
| 180 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 181 histogram_tester_->ExpectTotalCount(kPageLoadsBeforeEvictedTabSelected, 0); |
| 182 } |
| 183 |
| 184 TEST_F(TabUsageRecorderTest, TestColdStartTabs) { |
| 185 id tab_mock_a = MockTab(false); |
| 186 id tab_mock_b = MockTab(false); |
| 187 id tab_mock_c = MockTab(false); |
| 188 // Set A and B as cold-start evicted tabs. Leave C just evicted. |
| 189 NSMutableArray* cold_start_tabs = [NSMutableArray array]; |
| 190 [cold_start_tabs addObject:tab_mock_a]; |
| 191 [cold_start_tabs addObject:tab_mock_b]; |
| 192 tab_usage_recorder_->InitialRestoredTabs(tab_mock_a, cold_start_tabs); |
| 193 |
| 194 // Switch from A (cold start evicted) to B (cold start evicted). |
| 195 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 196 // Switch from B (cold start evicted) to C (evicted). |
| 197 tab_usage_recorder_->RecordTabSwitched(tab_mock_b, tab_mock_c); |
| 198 histogram_tester_->ExpectTotalCount(kSelectedTabHistogramName, 2); |
| 199 histogram_tester_->ExpectBucketCount( |
| 200 kSelectedTabHistogramName, TabUsageRecorder::EVICTED_DUE_TO_COLD_START, |
| 201 1); |
| 202 histogram_tester_->ExpectBucketCount(kSelectedTabHistogramName, |
| 203 TabUsageRecorder::EVICTED, 1); |
| 204 } |
| 205 |
| 206 TEST_F(TabUsageRecorderTest, TestSwitchedModeTabs) { |
| 207 id tab_mock_a = MockTab(false); |
| 208 id tab_mock_b = MockTab(false); |
| 209 id tab_mock_c = MockTab(false); |
| 210 NSMutableArray* switch_to_incognito_tabs = [NSMutableArray array]; |
| 211 [switch_to_incognito_tabs addObject:tab_mock_a]; |
| 212 [switch_to_incognito_tabs addObject:tab_mock_b]; |
| 213 tab_usage_recorder_->RecordPrimaryTabModelChange(false, nil); |
| 214 |
| 215 // Switch from A (incognito evicted) to B (incognito evicted). |
| 216 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 217 // Switch from B (incognito evicted) to C (evicted). |
| 218 tab_usage_recorder_->RecordTabSwitched(tab_mock_b, tab_mock_c); |
| 219 histogram_tester_->ExpectTotalCount(kSelectedTabHistogramName, 2); |
| 220 histogram_tester_->ExpectBucketCount( |
| 221 kSelectedTabHistogramName, TabUsageRecorder::EVICTED_DUE_TO_INCOGNITO, 0); |
| 222 histogram_tester_->ExpectBucketCount(kSelectedTabHistogramName, |
| 223 TabUsageRecorder::EVICTED, 2); |
| 224 } |
| 225 |
| 226 TEST_F(TabUsageRecorderTest, TestEvictedTabReloadTime) { |
| 227 id tab_mock_a = MockTab(true); |
| 228 id tab_mock_b = MockTab(false); |
| 229 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 230 tab_usage_recorder_->RecordPageLoadStart(tab_mock_b); |
| 231 tab_usage_recorder_->RecordPageLoadDone(tab_mock_b, true); |
| 232 histogram_tester_->ExpectTotalCount(kEvictedTabReloadTime, 1); |
| 233 } |
| 234 |
| 235 TEST_F(TabUsageRecorderTest, TestEvictedTabReloadSuccess) { |
| 236 id tab_mock_a = MockTab(true); |
| 237 id tab_mock_b = MockTab(false); |
| 238 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 239 tab_usage_recorder_->RecordPageLoadStart(tab_mock_b); |
| 240 tab_usage_recorder_->RecordPageLoadDone(tab_mock_b, true); |
| 241 histogram_tester_->ExpectUniqueSample(kEvictedTabReloadSuccessRate, |
| 242 TabUsageRecorder::LOAD_SUCCESS, 1); |
| 243 } |
| 244 |
| 245 TEST_F(TabUsageRecorderTest, TestEvictedTabReloadFailure) { |
| 246 id tab_mock_a = MockTab(true); |
| 247 id tab_mock_b = MockTab(false); |
| 248 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 249 tab_usage_recorder_->RecordPageLoadStart(tab_mock_b); |
| 250 tab_usage_recorder_->RecordPageLoadDone(tab_mock_b, false); |
| 251 histogram_tester_->ExpectUniqueSample(kEvictedTabReloadSuccessRate, |
| 252 TabUsageRecorder::LOAD_FAILURE, 1); |
| 253 } |
| 254 |
| 255 TEST_F(TabUsageRecorderTest, TestUserWaitedForEvictedTabLoad) { |
| 256 id tab_mock_a = MockTab(true); |
| 257 id tab_mock_b = MockTab(false); |
| 258 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 259 tab_usage_recorder_->RecordPageLoadStart(tab_mock_b); |
| 260 tab_usage_recorder_->RecordPageLoadDone(tab_mock_b, true); |
| 261 tab_usage_recorder_->RecordTabSwitched(tab_mock_b, tab_mock_a); |
| 262 histogram_tester_->ExpectUniqueSample(kDidUserWaitForEvictedTabReload, |
| 263 TabUsageRecorder::USER_WAITED, 1); |
| 264 } |
| 265 |
| 266 TEST_F(TabUsageRecorderTest, TestUserDidNotWaitForEvictedTabLoad) { |
| 267 id tab_mock_a = MockTab(true); |
| 268 id tab_mock_b = MockTab(false); |
| 269 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 270 tab_usage_recorder_->RecordPageLoadStart(tab_mock_b); |
| 271 tab_usage_recorder_->RecordTabSwitched(tab_mock_b, tab_mock_a); |
| 272 histogram_tester_->ExpectUniqueSample(kDidUserWaitForEvictedTabReload, |
| 273 TabUsageRecorder::USER_DID_NOT_WAIT, 1); |
| 274 } |
| 275 |
| 276 TEST_F(TabUsageRecorderTest, TestUserBackgroundedDuringEvictedTabLoad) { |
| 277 id tab_mock_a = MockTab(true); |
| 278 id tab_mock_b = MockTab(false); |
| 279 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 280 tab_usage_recorder_->RecordPageLoadStart(tab_mock_b); |
| 281 tab_usage_recorder_->AppDidEnterBackground(); |
| 282 histogram_tester_->ExpectUniqueSample(kDidUserWaitForEvictedTabReload, |
| 283 TabUsageRecorder::USER_LEFT_CHROME, 1); |
| 284 } |
| 285 |
| 286 TEST_F(TabUsageRecorderTest, TestTimeBetweenRestores) { |
| 287 id tab_mock_a = MockTab(false); |
| 288 id tab_mock_b = MockTab(false); |
| 289 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 290 // Should record the time since launch until this page load begins. |
| 291 tab_usage_recorder_->RecordPageLoadStart(tab_mock_b); |
| 292 tab_usage_recorder_->RecordTabSwitched(tab_mock_b, tab_mock_a); |
| 293 // Should record the time since previous restore until this restore. |
| 294 tab_usage_recorder_->RecordPageLoadStart(tab_mock_a); |
| 295 histogram_tester_->ExpectTotalCount(kTimeBetweenRestores, 2); |
| 296 } |
| 297 |
| 298 TEST_F(TabUsageRecorderTest, TestTimeAfterLastRestore) { |
| 299 id tab_mock_a = MockTab(false); |
| 300 id tab_mock_b = MockTab(false); |
| 301 // Should record time since launch until background. |
| 302 tab_usage_recorder_->AppDidEnterBackground(); |
| 303 tab_usage_recorder_->AppWillEnterForeground(); |
| 304 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 305 // Should record nothing. |
| 306 tab_usage_recorder_->RecordPageLoadStart(tab_mock_b); |
| 307 histogram_tester_->ExpectTotalCount(kTimeAfterLastRestore, 1); |
| 308 } |
| 309 |
| 310 // Verifies that metrics are recorded correctly when a renderer terminates. |
| 311 TEST_F(TabUsageRecorderTest, RendererTerminated) { |
| 312 Tab* terminated_tab = MockTab(false); |
| 313 |
| 314 // Set up the delegate to return |kAliveTabsCountAtRenderTermination|. |
| 315 base::scoped_nsobject<MockTabUsageRecorderDelegate> delegate( |
| 316 [[MockTabUsageRecorderDelegate alloc] init]); |
| 317 [delegate setLiveTabsCount:kAliveTabsCountAtRendererTermination]; |
| 318 tab_usage_recorder_->SetDelegate(delegate); |
| 319 |
| 320 base::TimeTicks now = base::TimeTicks::Now(); |
| 321 |
| 322 // Add |kExpiredTimesAddedCount| expired timestamps and one recent timestamp |
| 323 // to the termination timestamp list. |
| 324 for (int seconds = kExpiredTimesAddedCount; seconds > 0; seconds--) { |
| 325 int expired_time_delta = kSecondsBeforeRendererTermination + seconds; |
| 326 tab_usage_recorder_->AddTimeToDeque( |
| 327 now - base::TimeDelta::FromSeconds(expired_time_delta)); |
| 328 } |
| 329 base::TimeTicks recent_time = |
| 330 now - base::TimeDelta::FromSeconds(kSecondsBeforeRendererTermination / 2); |
| 331 tab_usage_recorder_->AddTimeToDeque(recent_time); |
| 332 |
| 333 tab_usage_recorder_->RendererTerminated(terminated_tab, false); |
| 334 |
| 335 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| 336 BOOL saw_memory_warning = |
| 337 [defaults boolForKey:previous_session_info_constants:: |
| 338 kDidSeeMemoryWarningShortlyBeforeTerminating]; |
| 339 histogram_tester_->ExpectUniqueSample(kRendererTerminationSawMemoryWarning, |
| 340 saw_memory_warning, 1); |
| 341 histogram_tester_->ExpectUniqueSample(kRendererTerminationAliveRenderers, |
| 342 kAliveTabsCountAtRendererTermination, |
| 343 1); |
| 344 // Tests that the logged count of recently alive renderers is equal to the |
| 345 // live count at termination plus the recent termination and the |
| 346 // renderer terminated just now. |
| 347 histogram_tester_->ExpectUniqueSample( |
| 348 kRendererTerminationRecentlyAliveRenderers, |
| 349 kAliveTabsCountAtRendererTermination + 2, 1); |
| 350 } |
| 351 |
| 352 // Verifies that metrics are recorded correctly when a renderer terminated tab |
| 353 // is switched to and reloaded. |
| 354 TEST_F(TabUsageRecorderTest, SwitchToRendererTerminatedTab) { |
| 355 id tab_mock_a = MockTab(true); |
| 356 id tab_mock_b = MockTab(false); |
| 357 |
| 358 tab_usage_recorder_->RendererTerminated(tab_mock_b, false); |
| 359 tab_usage_recorder_->RecordTabSwitched(tab_mock_a, tab_mock_b); |
| 360 |
| 361 histogram_tester_->ExpectUniqueSample( |
| 362 kSelectedTabHistogramName, |
| 363 TabUsageRecorder::EVICTED_DUE_TO_RENDERER_TERMINATION, 1); |
| 364 } |
| 365 |
| 366 } // namespace |
OLD | NEW |