Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(185)

Side by Side Diff: ios/chrome/browser/ui/stack_view/stack_view_controller_perftest.mm

Issue 2587023002: Upstream Chrome on iOS source code [8/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2012 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 #import <UIKit/UIKit.h>
6
7 #include "base/callback_helpers.h"
8 #import "base/mac/bind_objc_block.h"
9 #include "base/mac/foundation_util.h"
10 #include "base/strings/sys_string_conversions.h"
11 #import "base/test/ios/wait_util.h"
12 #import "ios/chrome/browser/snapshots/snapshot_manager.h"
13 #import "ios/chrome/browser/tabs/tab.h"
14 #import "ios/chrome/browser/tabs/tab_model.h"
15 #include "ios/chrome/browser/test/perf_test_with_bvc_ios.h"
16 #import "ios/chrome/browser/ui/browser_view_controller.h"
17 #import "ios/chrome/browser/ui/fullscreen_controller.h"
18 #import "ios/chrome/browser/ui/stack_view/stack_view_controller.h"
19 #include "ios/chrome/browser/ui/ui_util.h"
20 #include "ios/web/public/referrer.h"
21 #import "net/base/mac/url_conversions.h"
22
23 // These tests measure the performance of opening the stack view controller on
24 // an iPhone. On an iPad, the tests do not run, as the iPad does not use the
25 // stack view controller.
26
27 // Opening the SVC smoothly and quickly is critical to the user experience, and
28 // any sort of pause or delay is awkward and gives a poor impression.
29 // The target time of opening SVC is less than 250 ms. In order to target
30 // optimizations and ensure that improvements actually lower the time, this test
31 // aims to mimic as closely as possible the application experience.
32
33 // To mimic the full experience, it is necessary to set up a browser view
34 // controller, with a website loaded, and use as much of the generic experience
35 // as possible while culling items such as PrerenderController which may
36 // muddy the performance analysis. In that vein, the tests wait for websites to
37 // load before opening the stack view controller, to avoid slowing down the
38 // animation run loops with networking or javascript calls. This way the tests
39 // provide meaningful and trackable performance numbers. The downside is that it
40 // means the tests do not completely mimic the user experience, as the animation
41 // may in fact take longer if the website is still loading, and the UIWebView
42 // making network calls on the main thread.
43
44 // Testing delegate to receive animation start and end notifications.
45 // It contains a weak pointer to the BrowserViewController so that it can
46 // take a snapshot of the toolbar for the toolbar animation.
47 @interface StackViewControllerPerfTestDelegate
48 : NSObject<TabSwitcherDelegate, StackViewControllerTestDelegate> {
49 @private
50 BOOL showAnimationStarted_;
51 BOOL showAnimationEnded_;
52 BOOL dismissAnimationStarted_;
53 BOOL dismissAnimationEnded_;
54 BOOL preloadCardViewsEnded_;
55 @public
56 BrowserViewController* bvc_; // weak
57 }
58
59 - (void)reinitialize;
60
61 @property(nonatomic, assign) BOOL showAnimationStarted;
62 @property(nonatomic, assign) BOOL showAnimationEnded;
63 @property(nonatomic, assign) BOOL dismissAnimationStarted;
64 @property(nonatomic, assign) BOOL dismissAnimationEnded;
65 @property(nonatomic, assign) BOOL preloadCardViewsEnded;
66
67 @end
68
69 @implementation StackViewControllerPerfTestDelegate
70
71 @synthesize showAnimationStarted = showAnimationStarted_;
72 @synthesize showAnimationEnded = showAnimationEnded_;
73 @synthesize dismissAnimationStarted = dismissAnimationStarted_;
74 @synthesize dismissAnimationEnded = dismissAnimationEnded_;
75 @synthesize preloadCardViewsEnded = preloadCardViewsEnded_;
76
77 - (id)initWithBrowserViewController:(BrowserViewController*)bvc {
78 self = [super init];
79 if (self)
80 bvc_ = bvc;
81 return self;
82 }
83
84 - (void)reinitialize {
85 [self setShowAnimationStarted:NO];
86 [self setShowAnimationEnded:NO];
87 [self setDismissAnimationStarted:NO];
88 [self setDismissAnimationEnded:NO];
89 [self setPreloadCardViewsEnded:NO];
90 }
91
92 - (void)stackViewControllerShowWithSelectedTabAnimationDidStart {
93 [self setShowAnimationStarted:YES];
94 }
95
96 - (void)stackViewControllerShowWithSelectedTabAnimationDidEnd {
97 [self setShowAnimationEnded:YES];
98 }
99
100 - (void)tabSwitcher:(id<TabSwitcher>)tabSwitcher
101 dismissTransitionWillStartWithActiveModel:(TabModel*)tabModel {
102 [self setDismissAnimationStarted:YES];
103 }
104
105 - (void)tabSwitcherDismissTransitionDidEnd:(id<TabSwitcher>)tabSwitcher {
106 [self setDismissAnimationEnded:YES];
107 }
108
109 - (void)stackViewControllerPreloadCardViewsDidEnd {
110 [self setPreloadCardViewsEnded:YES];
111 }
112
113 - (void)tabSwitcherPresentationTransitionDidEnd:(id<TabSwitcher>)tabSwitcher {
114 [self stackViewControllerShowWithSelectedTabAnimationDidEnd];
115 }
116
117 - (id<ToolbarOwner>)tabSwitcherTransitionToolbarOwner {
118 return bvc_;
119 }
120
121 @end
122
123 #pragma mark -
124
125 namespace {
126
127 #define ARRAYSIZE(a) \
128 ((sizeof(a) / sizeof(*(a))) / \
129 static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
130
131 // Use multiple URLs in the test in case the complexity of a website has an
132 // effect on the performance of UIWebView -renderInContext.
133 static const char* url_list[] = {
134 // TODO(crbug.com/546315): Create static websites for these.
135 "https://www.google.com", "https://news.google.com",
136 "https://browsingtest.appspot.com",
137 };
138
139 // The maximum delay of a single spin of the run loop in seconds.
140 // This is very small to ensure that we are spining fast enough.
141 const NSTimeInterval kSpinDelay = 0.01; // seconds
142 // The total maximum delay of all spins of the run loop while
143 // waiting for the stack view to appear and disappear.
144 const NSTimeInterval kTotalSpinDelay = 20; // seconds
145 // The maximum time to wait for a website to load.
146 const NSTimeInterval kMaxPageLoadDelay = 20; // seconds
147 // The time UI gets to catch up after a website has loaded. Give a full second
148 // to make sure the progress bar's finish delay has completed and the toolbar
149 // snapshot taken.
150 const NSTimeInterval kMaxUICatchupDelay = 1.0; // seconds
151
152 class StackViewControllerPerfTest : public PerfTestWithBVC {
153 public:
154 StackViewControllerPerfTest() : PerfTestWithBVC("Stack View") {}
155
156 void SetUp() override {
157 // Opening a StackViewController is done only on iPhones, not on iPads.
158 // This test is meaningless on an iPad.
159 if (IsIPadIdiom())
160 return;
161
162 [FullScreenController setHideOmniboxDelaySeconds:0.0];
163
164 // Base class does most of the setup.
165 PerfTestWithBVC::SetUp();
166
167 current_url_index_ = 0;
168 reuse_svc_ = false;
169
170 // The testing delegate will receive stack view animation notifications.
171 delegate_.reset([[StackViewControllerPerfTestDelegate alloc]
172 initWithBrowserViewController:bvc_]);
173 }
174 void TearDown() override {
175 // Opening a StackViewController is done only on iPhones, not on iPads.
176 // This test is meaningless on an iPad.
177 if (IsIPadIdiom())
178 return;
179
180 PerfTestWithBVC::TearDown();
181 }
182
183 protected:
184 // Stack view controller & delegate.
185 base::scoped_nsobject<StackViewControllerPerfTestDelegate> delegate_;
186 base::scoped_nsobject<StackViewController> view_controller_;
187
188 int current_url_index_;
189 BOOL reuse_svc_;
190
191 // Utility function to print out timing information for testing
192 // with |number_of_tabs| tabs opened.
193 void PrintTestResult(std::string test_description,
194 int number_of_tabs,
195 base::TimeDelta elapsed) {
196 std::stringstream test_name;
197 test_name << test_description << " - " << number_of_tabs << " Tab"
198 << (number_of_tabs == 1 ? "" : "s");
199 LogPerfTiming(test_name.str(), elapsed);
200 }
201
202 // Creates and adds |number_of_tabs| tabs to the tab model. If |same_url|
203 // is true, always uses "www.google.com", otherwise iterates through url_list.
204 void CreateTabs(int number_of_tabs, bool same_url);
205 // Navigates to the next URL in the current tab.
206 void LoadNextURL();
207
208 // Gets the next URL in the list.
209 const GURL NextURL();
210 // Gets "google.com"
211 static const GURL GoogleURL();
212 // Waits for the page to load in the given tab.
213 static void WaitForPageLoad(Tab* tab);
214 // Creates the stack view and adds it to the main window.
215 base::TimeDelta OpenStackView();
216 // Copy of MainController's -showTabSwitcher function. Pulling in all of
217 // MainController is not practical for unit tests, nor necessary.
218 void MainControllerShowTabSwitcher();
219 // Dismisses the stack view and removes it from the main window.
220 base::TimeDelta CloseStackView();
221
222 // Time how long it takes BVC to make a snapshot of the current website.
223 base::TimeDelta TakeSnapshot();
224 };
225
226 void StackViewControllerPerfTest::CreateTabs(int number_of_tabs,
227 bool same_url) {
228 // Create and add the tabs to the tab model.
229 Tab* tab = nil;
230 for (int i = 0; i < number_of_tabs; i++) {
231 tab = [bvc_ addSelectedTabWithURL:(same_url ? GoogleURL() : NextURL())
232 transition:(ui::PAGE_TRANSITION_AUTO_TOPLEVEL)];
233 WaitForPageLoad(tab);
234 }
235 }
236
237 void StackViewControllerPerfTest::LoadNextURL() {
238 [bvc_ loadURL:NextURL()
239 referrer:web::Referrer()
240 transition:(ui::PAGE_TRANSITION_TYPED)
241 rendererInitiated:NO];
242 Tab* tab = [tab_model_ currentTab];
243 WaitForPageLoad(tab);
244 }
245
246 const GURL StackViewControllerPerfTest::NextURL() {
247 current_url_index_++;
248 if (static_cast<unsigned long>(current_url_index_) >= ARRAYSIZE(url_list))
249 current_url_index_ = 0;
250 return GURL(url_list[current_url_index_]);
251 }
252
253 const GURL StackViewControllerPerfTest::GoogleURL() {
254 return GURL("http://www.google.com");
255 }
256
257 void StackViewControllerPerfTest::WaitForPageLoad(Tab* tab) {
258 base::test::ios::WaitUntilCondition(
259 ^bool() {
260 return !tab.webState->IsLoading();
261 },
262 nullptr, base::TimeDelta::FromSecondsD(kMaxPageLoadDelay));
263 base::test::ios::WaitUntilCondition(
264 nil, nullptr, base::TimeDelta::FromSecondsD(kMaxUICatchupDelay));
265 }
266
267 base::TimeDelta StackViewControllerPerfTest::OpenStackView() {
268 return base::test::ios::TimeUntilCondition(
269 ^{
270 [delegate_ reinitialize];
271 MainControllerShowTabSwitcher();
272 },
273 ^bool() {
274 return [delegate_ showAnimationEnded];
275 },
276 nullptr, base::TimeDelta::FromSecondsD(kTotalSpinDelay));
277 }
278
279 void StackViewControllerPerfTest::MainControllerShowTabSwitcher() {
280 // The code for this function is copied from MainController -showTabSwitcher.
281 // Note that if the code there changes, this code should change to match.
282 Tab* currentTab = [[bvc_ tabModel] currentTab];
283
284 // In order to generate the transition between the current browser view
285 // controller and the tab switcher controller it's possible that multiple
286 // screenshots of the same tab are taken. Since taking a screenshot is
287 // expensive we activate snapshot coalescing in the scope of this function
288 // which will cache the first snapshot for the tab and reuse it instead of
289 // regenerating a new one each time.
290 [currentTab setSnapshotCoalescingEnabled:YES];
291 base::ScopedClosureRunner runner(base::BindBlock(^{
292 [currentTab setSnapshotCoalescingEnabled:NO];
293 }));
294
295 if (!view_controller_) {
296 view_controller_.reset([[StackViewController alloc]
297 initWithMainTabModel:tab_model_
298 otrTabModel:otr_tab_model_
299 activeTabModel:tab_model_]);
300 } else {
301 [view_controller_ restoreInternalStateWithMainTabModel:tab_model_
302 otrTabModel:otr_tab_model_
303 activeTabModel:tab_model_];
304 }
305 [view_controller_ setDelegate:delegate_];
306
307 // The only addition to the function for testing.
308 [view_controller_ setTestDelegate:delegate_];
309
310 [bvc_ presentViewController:view_controller_.get()
311 animated:NO
312 completion:^{
313 [view_controller_ showWithSelectedTabAnimation];
314 EXPECT_TRUE([delegate_ showAnimationStarted]);
315 EXPECT_FALSE([delegate_ showAnimationEnded]);
316 }];
317 }
318
319 base::TimeDelta StackViewControllerPerfTest::CloseStackView() {
320 base::Time startTime = base::Time::NowFromSystemTime();
321 // Spin and wait for the dismiss stack view animation to finish.
322 base::test::ios::TimeUntilCondition(
323 ^{
324 [view_controller_ dismissWithSelectedTabAnimation];
325 EXPECT_TRUE([delegate_ dismissAnimationStarted]);
326 EXPECT_FALSE([delegate_ dismissAnimationEnded]);
327 },
328 ^bool() {
329 return [delegate_ dismissAnimationEnded];
330 },
331 nullptr, base::TimeDelta::FromSecondsD(kTotalSpinDelay));
332
333 [view_controller_ dismissViewControllerAnimated:NO completion:nil];
334 if (!reuse_svc_)
335 view_controller_.reset();
336
337 base::TimeDelta closeTime = base::Time::NowFromSystemTime() - startTime;
338
339 // Run the runloop a bit longer to give time for temporary retains that happen
340 // in the OS during view teardown to resolve, so that the view gets its
341 // dismissal callbacks.
342 base::test::ios::WaitUntilCondition(
343 nil, nullptr, base::TimeDelta::FromSecondsD(kSpinDelay));
344
345 return closeTime;
346 }
347
348 base::TimeDelta StackViewControllerPerfTest::TakeSnapshot() {
349 base::Time startTime = base::Time::NowFromSystemTime();
350 UIImage* image = [[tab_model_ currentTab] updateSnapshotWithOverlay:YES
351 visibleFrameOnly:YES];
352 base::TimeDelta elapsed = base::Time::NowFromSystemTime() - startTime;
353 EXPECT_TRUE(image);
354 return elapsed;
355 }
356
357 TEST_F(StackViewControllerPerfTest, WebView_Shapshot) {
358 // Opening a StackViewController is done only on iPhones, not on iPads.
359 // This test is meaningless on an iPad.
360 if (IsIPadIdiom())
361 return;
362 const int kNumTests = 10;
363 base::TimeDelta times[kNumTests];
364 CreateTabs(1, false);
365 for (int i = 0; i < kNumTests; i++) {
366 times[i] = TakeSnapshot();
367 LoadNextURL();
368 }
369
370 base::TimeDelta avg = CalculateAverage(times, kNumTests, NULL, NULL);
371 LogPerfTiming("Snapshot", avg);
372 }
373
374 // TODO(crbug.com/546328): Add back in tests for checking opening & closing
375 // stack view controller with multiple tabs open.
376 TEST_F(StackViewControllerPerfTest, DISABLED_OpenAndCloseStackView_1_Tab) {
377 // Opening a StackViewController is done only on iPhones, not on iPads.
378 // This test is meaningless on an iPad.
379 if (IsIPadIdiom())
380 return;
381 reuse_svc_ = true;
382 const int kNumTests = 10;
383 base::TimeDelta open_times[kNumTests];
384 base::TimeDelta close_times[kNumTests];
385 CreateTabs(1, false);
386 for (int i = 0; i < kNumTests; i++) {
387 open_times[i] = OpenStackView();
388 close_times[i] = CloseStackView();
389 LoadNextURL();
390 }
391
392 base::TimeDelta max_open;
393 base::TimeDelta max_close;
394 // When calculating the average, only take into account the 'warm' tests.
395 // i.e. ignore the 'cold' time.
396 base::TimeDelta open_avg =
397 CalculateAverage(open_times + 1, kNumTests - 1, NULL, &max_open);
398 base::TimeDelta close_avg =
399 CalculateAverage(close_times + 1, kNumTests - 1, NULL, &max_close);
400 LogPerfTiming("Open cold", open_times[0]);
401 LogPerfTiming("Open warm avg", open_avg);
402 LogPerfTiming("Open warm max", max_open);
403 LogPerfTiming("Close cold", close_times[0]);
404 LogPerfTiming("Close cold avg", close_avg);
405 LogPerfTiming("Close cold max", max_close);
406 }
407
408 } // anonymous namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698