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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/stack_view/stack_view_controller_perftest.mm
diff --git a/ios/chrome/browser/ui/stack_view/stack_view_controller_perftest.mm b/ios/chrome/browser/ui/stack_view/stack_view_controller_perftest.mm
new file mode 100644
index 0000000000000000000000000000000000000000..fded70ef1de00b2bc70af497e3b3fa78262ad07c
--- /dev/null
+++ b/ios/chrome/browser/ui/stack_view/stack_view_controller_perftest.mm
@@ -0,0 +1,408 @@
+// Copyright 2012 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.
+
+#import <UIKit/UIKit.h>
+
+#include "base/callback_helpers.h"
+#import "base/mac/bind_objc_block.h"
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+#import "base/test/ios/wait_util.h"
+#import "ios/chrome/browser/snapshots/snapshot_manager.h"
+#import "ios/chrome/browser/tabs/tab.h"
+#import "ios/chrome/browser/tabs/tab_model.h"
+#include "ios/chrome/browser/test/perf_test_with_bvc_ios.h"
+#import "ios/chrome/browser/ui/browser_view_controller.h"
+#import "ios/chrome/browser/ui/fullscreen_controller.h"
+#import "ios/chrome/browser/ui/stack_view/stack_view_controller.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#include "ios/web/public/referrer.h"
+#import "net/base/mac/url_conversions.h"
+
+// These tests measure the performance of opening the stack view controller on
+// an iPhone. On an iPad, the tests do not run, as the iPad does not use the
+// stack view controller.
+
+// Opening the SVC smoothly and quickly is critical to the user experience, and
+// any sort of pause or delay is awkward and gives a poor impression.
+// The target time of opening SVC is less than 250 ms. In order to target
+// optimizations and ensure that improvements actually lower the time, this test
+// aims to mimic as closely as possible the application experience.
+
+// To mimic the full experience, it is necessary to set up a browser view
+// controller, with a website loaded, and use as much of the generic experience
+// as possible while culling items such as PrerenderController which may
+// muddy the performance analysis. In that vein, the tests wait for websites to
+// load before opening the stack view controller, to avoid slowing down the
+// animation run loops with networking or javascript calls. This way the tests
+// provide meaningful and trackable performance numbers. The downside is that it
+// means the tests do not completely mimic the user experience, as the animation
+// may in fact take longer if the website is still loading, and the UIWebView
+// making network calls on the main thread.
+
+// Testing delegate to receive animation start and end notifications.
+// It contains a weak pointer to the BrowserViewController so that it can
+// take a snapshot of the toolbar for the toolbar animation.
+@interface StackViewControllerPerfTestDelegate
+ : NSObject<TabSwitcherDelegate, StackViewControllerTestDelegate> {
+ @private
+ BOOL showAnimationStarted_;
+ BOOL showAnimationEnded_;
+ BOOL dismissAnimationStarted_;
+ BOOL dismissAnimationEnded_;
+ BOOL preloadCardViewsEnded_;
+ @public
+ BrowserViewController* bvc_; // weak
+}
+
+- (void)reinitialize;
+
+@property(nonatomic, assign) BOOL showAnimationStarted;
+@property(nonatomic, assign) BOOL showAnimationEnded;
+@property(nonatomic, assign) BOOL dismissAnimationStarted;
+@property(nonatomic, assign) BOOL dismissAnimationEnded;
+@property(nonatomic, assign) BOOL preloadCardViewsEnded;
+
+@end
+
+@implementation StackViewControllerPerfTestDelegate
+
+@synthesize showAnimationStarted = showAnimationStarted_;
+@synthesize showAnimationEnded = showAnimationEnded_;
+@synthesize dismissAnimationStarted = dismissAnimationStarted_;
+@synthesize dismissAnimationEnded = dismissAnimationEnded_;
+@synthesize preloadCardViewsEnded = preloadCardViewsEnded_;
+
+- (id)initWithBrowserViewController:(BrowserViewController*)bvc {
+ self = [super init];
+ if (self)
+ bvc_ = bvc;
+ return self;
+}
+
+- (void)reinitialize {
+ [self setShowAnimationStarted:NO];
+ [self setShowAnimationEnded:NO];
+ [self setDismissAnimationStarted:NO];
+ [self setDismissAnimationEnded:NO];
+ [self setPreloadCardViewsEnded:NO];
+}
+
+- (void)stackViewControllerShowWithSelectedTabAnimationDidStart {
+ [self setShowAnimationStarted:YES];
+}
+
+- (void)stackViewControllerShowWithSelectedTabAnimationDidEnd {
+ [self setShowAnimationEnded:YES];
+}
+
+- (void)tabSwitcher:(id<TabSwitcher>)tabSwitcher
+ dismissTransitionWillStartWithActiveModel:(TabModel*)tabModel {
+ [self setDismissAnimationStarted:YES];
+}
+
+- (void)tabSwitcherDismissTransitionDidEnd:(id<TabSwitcher>)tabSwitcher {
+ [self setDismissAnimationEnded:YES];
+}
+
+- (void)stackViewControllerPreloadCardViewsDidEnd {
+ [self setPreloadCardViewsEnded:YES];
+}
+
+- (void)tabSwitcherPresentationTransitionDidEnd:(id<TabSwitcher>)tabSwitcher {
+ [self stackViewControllerShowWithSelectedTabAnimationDidEnd];
+}
+
+- (id<ToolbarOwner>)tabSwitcherTransitionToolbarOwner {
+ return bvc_;
+}
+
+@end
+
+#pragma mark -
+
+namespace {
+
+#define ARRAYSIZE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+// Use multiple URLs in the test in case the complexity of a website has an
+// effect on the performance of UIWebView -renderInContext.
+static const char* url_list[] = {
+ // TODO(crbug.com/546315): Create static websites for these.
+ "https://www.google.com", "https://news.google.com",
+ "https://browsingtest.appspot.com",
+};
+
+// The maximum delay of a single spin of the run loop in seconds.
+// This is very small to ensure that we are spining fast enough.
+const NSTimeInterval kSpinDelay = 0.01; // seconds
+// The total maximum delay of all spins of the run loop while
+// waiting for the stack view to appear and disappear.
+const NSTimeInterval kTotalSpinDelay = 20; // seconds
+// The maximum time to wait for a website to load.
+const NSTimeInterval kMaxPageLoadDelay = 20; // seconds
+// The time UI gets to catch up after a website has loaded. Give a full second
+// to make sure the progress bar's finish delay has completed and the toolbar
+// snapshot taken.
+const NSTimeInterval kMaxUICatchupDelay = 1.0; // seconds
+
+class StackViewControllerPerfTest : public PerfTestWithBVC {
+ public:
+ StackViewControllerPerfTest() : PerfTestWithBVC("Stack View") {}
+
+ void SetUp() override {
+ // Opening a StackViewController is done only on iPhones, not on iPads.
+ // This test is meaningless on an iPad.
+ if (IsIPadIdiom())
+ return;
+
+ [FullScreenController setHideOmniboxDelaySeconds:0.0];
+
+ // Base class does most of the setup.
+ PerfTestWithBVC::SetUp();
+
+ current_url_index_ = 0;
+ reuse_svc_ = false;
+
+ // The testing delegate will receive stack view animation notifications.
+ delegate_.reset([[StackViewControllerPerfTestDelegate alloc]
+ initWithBrowserViewController:bvc_]);
+ }
+ void TearDown() override {
+ // Opening a StackViewController is done only on iPhones, not on iPads.
+ // This test is meaningless on an iPad.
+ if (IsIPadIdiom())
+ return;
+
+ PerfTestWithBVC::TearDown();
+ }
+
+ protected:
+ // Stack view controller & delegate.
+ base::scoped_nsobject<StackViewControllerPerfTestDelegate> delegate_;
+ base::scoped_nsobject<StackViewController> view_controller_;
+
+ int current_url_index_;
+ BOOL reuse_svc_;
+
+ // Utility function to print out timing information for testing
+ // with |number_of_tabs| tabs opened.
+ void PrintTestResult(std::string test_description,
+ int number_of_tabs,
+ base::TimeDelta elapsed) {
+ std::stringstream test_name;
+ test_name << test_description << " - " << number_of_tabs << " Tab"
+ << (number_of_tabs == 1 ? "" : "s");
+ LogPerfTiming(test_name.str(), elapsed);
+ }
+
+ // Creates and adds |number_of_tabs| tabs to the tab model. If |same_url|
+ // is true, always uses "www.google.com", otherwise iterates through url_list.
+ void CreateTabs(int number_of_tabs, bool same_url);
+ // Navigates to the next URL in the current tab.
+ void LoadNextURL();
+
+ // Gets the next URL in the list.
+ const GURL NextURL();
+ // Gets "google.com"
+ static const GURL GoogleURL();
+ // Waits for the page to load in the given tab.
+ static void WaitForPageLoad(Tab* tab);
+ // Creates the stack view and adds it to the main window.
+ base::TimeDelta OpenStackView();
+ // Copy of MainController's -showTabSwitcher function. Pulling in all of
+ // MainController is not practical for unit tests, nor necessary.
+ void MainControllerShowTabSwitcher();
+ // Dismisses the stack view and removes it from the main window.
+ base::TimeDelta CloseStackView();
+
+ // Time how long it takes BVC to make a snapshot of the current website.
+ base::TimeDelta TakeSnapshot();
+};
+
+void StackViewControllerPerfTest::CreateTabs(int number_of_tabs,
+ bool same_url) {
+ // Create and add the tabs to the tab model.
+ Tab* tab = nil;
+ for (int i = 0; i < number_of_tabs; i++) {
+ tab = [bvc_ addSelectedTabWithURL:(same_url ? GoogleURL() : NextURL())
+ transition:(ui::PAGE_TRANSITION_AUTO_TOPLEVEL)];
+ WaitForPageLoad(tab);
+ }
+}
+
+void StackViewControllerPerfTest::LoadNextURL() {
+ [bvc_ loadURL:NextURL()
+ referrer:web::Referrer()
+ transition:(ui::PAGE_TRANSITION_TYPED)
+ rendererInitiated:NO];
+ Tab* tab = [tab_model_ currentTab];
+ WaitForPageLoad(tab);
+}
+
+const GURL StackViewControllerPerfTest::NextURL() {
+ current_url_index_++;
+ if (static_cast<unsigned long>(current_url_index_) >= ARRAYSIZE(url_list))
+ current_url_index_ = 0;
+ return GURL(url_list[current_url_index_]);
+}
+
+const GURL StackViewControllerPerfTest::GoogleURL() {
+ return GURL("http://www.google.com");
+}
+
+void StackViewControllerPerfTest::WaitForPageLoad(Tab* tab) {
+ base::test::ios::WaitUntilCondition(
+ ^bool() {
+ return !tab.webState->IsLoading();
+ },
+ nullptr, base::TimeDelta::FromSecondsD(kMaxPageLoadDelay));
+ base::test::ios::WaitUntilCondition(
+ nil, nullptr, base::TimeDelta::FromSecondsD(kMaxUICatchupDelay));
+}
+
+base::TimeDelta StackViewControllerPerfTest::OpenStackView() {
+ return base::test::ios::TimeUntilCondition(
+ ^{
+ [delegate_ reinitialize];
+ MainControllerShowTabSwitcher();
+ },
+ ^bool() {
+ return [delegate_ showAnimationEnded];
+ },
+ nullptr, base::TimeDelta::FromSecondsD(kTotalSpinDelay));
+}
+
+void StackViewControllerPerfTest::MainControllerShowTabSwitcher() {
+ // The code for this function is copied from MainController -showTabSwitcher.
+ // Note that if the code there changes, this code should change to match.
+ Tab* currentTab = [[bvc_ tabModel] currentTab];
+
+ // In order to generate the transition between the current browser view
+ // controller and the tab switcher controller it's possible that multiple
+ // screenshots of the same tab are taken. Since taking a screenshot is
+ // expensive we activate snapshot coalescing in the scope of this function
+ // which will cache the first snapshot for the tab and reuse it instead of
+ // regenerating a new one each time.
+ [currentTab setSnapshotCoalescingEnabled:YES];
+ base::ScopedClosureRunner runner(base::BindBlock(^{
+ [currentTab setSnapshotCoalescingEnabled:NO];
+ }));
+
+ if (!view_controller_) {
+ view_controller_.reset([[StackViewController alloc]
+ initWithMainTabModel:tab_model_
+ otrTabModel:otr_tab_model_
+ activeTabModel:tab_model_]);
+ } else {
+ [view_controller_ restoreInternalStateWithMainTabModel:tab_model_
+ otrTabModel:otr_tab_model_
+ activeTabModel:tab_model_];
+ }
+ [view_controller_ setDelegate:delegate_];
+
+ // The only addition to the function for testing.
+ [view_controller_ setTestDelegate:delegate_];
+
+ [bvc_ presentViewController:view_controller_.get()
+ animated:NO
+ completion:^{
+ [view_controller_ showWithSelectedTabAnimation];
+ EXPECT_TRUE([delegate_ showAnimationStarted]);
+ EXPECT_FALSE([delegate_ showAnimationEnded]);
+ }];
+}
+
+base::TimeDelta StackViewControllerPerfTest::CloseStackView() {
+ base::Time startTime = base::Time::NowFromSystemTime();
+ // Spin and wait for the dismiss stack view animation to finish.
+ base::test::ios::TimeUntilCondition(
+ ^{
+ [view_controller_ dismissWithSelectedTabAnimation];
+ EXPECT_TRUE([delegate_ dismissAnimationStarted]);
+ EXPECT_FALSE([delegate_ dismissAnimationEnded]);
+ },
+ ^bool() {
+ return [delegate_ dismissAnimationEnded];
+ },
+ nullptr, base::TimeDelta::FromSecondsD(kTotalSpinDelay));
+
+ [view_controller_ dismissViewControllerAnimated:NO completion:nil];
+ if (!reuse_svc_)
+ view_controller_.reset();
+
+ base::TimeDelta closeTime = base::Time::NowFromSystemTime() - startTime;
+
+ // Run the runloop a bit longer to give time for temporary retains that happen
+ // in the OS during view teardown to resolve, so that the view gets its
+ // dismissal callbacks.
+ base::test::ios::WaitUntilCondition(
+ nil, nullptr, base::TimeDelta::FromSecondsD(kSpinDelay));
+
+ return closeTime;
+}
+
+base::TimeDelta StackViewControllerPerfTest::TakeSnapshot() {
+ base::Time startTime = base::Time::NowFromSystemTime();
+ UIImage* image = [[tab_model_ currentTab] updateSnapshotWithOverlay:YES
+ visibleFrameOnly:YES];
+ base::TimeDelta elapsed = base::Time::NowFromSystemTime() - startTime;
+ EXPECT_TRUE(image);
+ return elapsed;
+}
+
+TEST_F(StackViewControllerPerfTest, WebView_Shapshot) {
+ // Opening a StackViewController is done only on iPhones, not on iPads.
+ // This test is meaningless on an iPad.
+ if (IsIPadIdiom())
+ return;
+ const int kNumTests = 10;
+ base::TimeDelta times[kNumTests];
+ CreateTabs(1, false);
+ for (int i = 0; i < kNumTests; i++) {
+ times[i] = TakeSnapshot();
+ LoadNextURL();
+ }
+
+ base::TimeDelta avg = CalculateAverage(times, kNumTests, NULL, NULL);
+ LogPerfTiming("Snapshot", avg);
+}
+
+// TODO(crbug.com/546328): Add back in tests for checking opening & closing
+// stack view controller with multiple tabs open.
+TEST_F(StackViewControllerPerfTest, DISABLED_OpenAndCloseStackView_1_Tab) {
+ // Opening a StackViewController is done only on iPhones, not on iPads.
+ // This test is meaningless on an iPad.
+ if (IsIPadIdiom())
+ return;
+ reuse_svc_ = true;
+ const int kNumTests = 10;
+ base::TimeDelta open_times[kNumTests];
+ base::TimeDelta close_times[kNumTests];
+ CreateTabs(1, false);
+ for (int i = 0; i < kNumTests; i++) {
+ open_times[i] = OpenStackView();
+ close_times[i] = CloseStackView();
+ LoadNextURL();
+ }
+
+ base::TimeDelta max_open;
+ base::TimeDelta max_close;
+ // When calculating the average, only take into account the 'warm' tests.
+ // i.e. ignore the 'cold' time.
+ base::TimeDelta open_avg =
+ CalculateAverage(open_times + 1, kNumTests - 1, NULL, &max_open);
+ base::TimeDelta close_avg =
+ CalculateAverage(close_times + 1, kNumTests - 1, NULL, &max_close);
+ LogPerfTiming("Open cold", open_times[0]);
+ LogPerfTiming("Open warm avg", open_avg);
+ LogPerfTiming("Open warm max", max_open);
+ LogPerfTiming("Close cold", close_times[0]);
+ LogPerfTiming("Close cold avg", close_avg);
+ LogPerfTiming("Close cold max", max_close);
+}
+
+} // anonymous namespace

Powered by Google App Engine
This is Rietveld 408576698