1// Copyright (c) 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#include "base/command_line.h"
6#include "base/environment.h"
7#include "base/file_util.h"
8#include "base/files/file_path.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/path_service.h"
11#include "base/strings/stringprintf.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/threading/platform_thread.h"
14#include "base/time/time.h"
15#include "chrome/app/chrome_command_ids.h"
16#include "chrome/common/chrome_paths.h"
17#include "chrome/common/chrome_switches.h"
18#include "chrome/common/env_vars.h"
19#include "chrome/test/automation/automation_proxy.h"
20#include "chrome/test/automation/browser_proxy.h"
21#include "chrome/test/automation/tab_proxy.h"
22#include "chrome/test/perf/perf_test.h"
23#include "chrome/test/ui/ui_perf_test.h"
24#include "net/base/net_util.h"
25#include "url/gurl.h"
26
27using base::TimeDelta;
28
29namespace {
30
31// This Automated UI test opens static files in different tabs in a proxy
32// browser. After all the tabs have opened, it switches between tabs, and notes
33// time taken for each switch. It then prints out the times on the console,
34// with the aim that the page cycler parser can interpret these numbers to
35// draw graphs for page cycler Tab Switching Performance.
36class TabSwitchingUITest : public UIPerfTest {
37 public:
38  TabSwitchingUITest() {
39    PathService::Get(base::DIR_SOURCE_ROOT, &path_prefix_);
40    path_prefix_ = path_prefix_.AppendASCII("data");
41    path_prefix_ = path_prefix_.AppendASCII("tab_switching");
42
43    show_window_ = true;
44  }
45
46  virtual void SetUp() {
47    // Set the log_file_name_ path according to the selected browser_directory_.
48    log_file_name_ = browser_directory_.AppendASCII("chrome_debug.log");
49
50    // Set the log file path for the browser test.
51    scoped_ptr<base::Environment> env(base::Environment::Create());
52#if defined(OS_WIN)
53    env->SetVar(env_vars::kLogFileName, WideToUTF8(log_file_name_.value()));
54#else
55    env->SetVar(env_vars::kLogFileName, log_file_name_.value());
56#endif
57
58    // Add the necessary arguments to Chrome's launch command for these tests.
59    AddLaunchArguments();
60
61    // Run the rest of the UITest initialization.
62    UITest::SetUp();
63  }
64
65  static const int kNumCycles = 5;
66
67  void PrintTimings(const char* label, TimeDelta timings[kNumCycles],
68    bool important) {
69      std::string times;
70      for (int i = 0; i < kNumCycles; ++i)
71        base::StringAppendF(&times, "%.2f,", timings[i].InMillisecondsF());
72      perf_test::PrintResultList(
73          "times", std::string(), label, times, "ms", important);
74  }
75
76  void RunTabSwitchingUITest(const char* label, bool important) {
77    // Shut down from window UITest sets up automatically.
78    UITest::TearDown();
79
80    TimeDelta timings[kNumCycles];
81    for (int i = 0; i < kNumCycles; ++i) {
82      // Prepare for this test run.
83      SetUp();
84
85      // Create a browser proxy.
86      browser_proxy_ = automation()->GetBrowserWindow(0);
87
88      // Open all the tabs.
89      int initial_tab_count = 0;
90      ASSERT_TRUE(browser_proxy_->GetTabCount(&initial_tab_count));
91      int new_tab_count = OpenTabs();
92      int tab_count = -1;
93      ASSERT_TRUE(browser_proxy_->GetTabCount(&tab_count));
94      ASSERT_EQ(initial_tab_count + new_tab_count, tab_count);
95
96      // Switch linearly between tabs.
97      ASSERT_TRUE(browser_proxy_->ActivateTab(0));
98      int final_tab_count = 0;
99      ASSERT_TRUE(browser_proxy_->GetTabCount(&final_tab_count));
100      for (int j = initial_tab_count; j < final_tab_count; ++j) {
101        ASSERT_TRUE(browser_proxy_->ActivateTab(j));
102        ASSERT_TRUE(browser_proxy_->WaitForTabToBecomeActive(
103            j, base::TimeDelta::FromSeconds(10)));
104      }
105
106      // Close the browser to force a dump of log.
107      bool application_closed = false;
108      EXPECT_TRUE(CloseBrowser(browser_proxy_.get(), &application_closed));
109
110      // Open the corresponding log file and collect average from the
111      // histogram stats generated for RenderWidgetHost_TabSwitchPaintDuration.
112      bool log_has_been_dumped = false;
113      std::string contents;
114      int max_tries = 20;
115      do {
116        log_has_been_dumped = file_util::ReadFileToString(log_file_name_,
117                                                          &contents);
118        if (!log_has_been_dumped)
119          base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
120      } while (!log_has_been_dumped && max_tries--);
121      ASSERT_TRUE(log_has_been_dumped) << "Failed to read the log file";
122
123      // Parse the contents to get average.
124      int64 average = 0;
125      const std::string average_str("average = ");
126      std::string::size_type pos = contents.find(
127          "Histogram: MPArch.RWH_TabSwitchPaintDuration", 0);
128      std::string::size_type comma_pos;
129      std::string::size_type number_length;
130
131      ASSERT_NE(pos, std::string::npos) <<
132        "Histogram: MPArch.RWH_TabSwitchPaintDuration wasn't found\n" <<
133        contents;
134
135      // Get the average.
136      pos = contents.find(average_str, pos);
137      comma_pos = contents.find(",", pos);
138      pos += average_str.length();
139      number_length = comma_pos - pos;
140      average = atoi(contents.substr(pos, number_length).c_str());
141
142      // Print the average and standard deviation.
143      timings[i] = TimeDelta::FromMilliseconds(average);
144
145      // Clean up from the test run.
146      UITest::TearDown();
147    }
148    PrintTimings(label, timings, important);
149  }
150
151 protected:
152  // Opens new tabs. Returns the number of tabs opened.
153  int OpenTabs() {
154    // Add tabs.
155    static const char* files[] = { "espn.go.com", "bugzilla.mozilla.org",
156                                   "news.cnet.com", "www.amazon.com",
157                                   "kannada.chakradeo.net", "allegro.pl",
158                                   "ml.wikipedia.org", "www.bbc.co.uk",
159                                   "126.com", "www.altavista.com"};
160    int number_of_new_tabs_opened = 0;
161    base::FilePath file_name;
162    for (size_t i = 0; i < arraysize(files); ++i) {
163      file_name = path_prefix_;
164      file_name = file_name.AppendASCII(files[i]);
165      file_name = file_name.AppendASCII("index.html");
166      bool success =
167          browser_proxy_->AppendTab(net::FilePathToFileURL(file_name));
168      EXPECT_TRUE(success);
169      if (success)
170        number_of_new_tabs_opened++;
171    }
172
173    return number_of_new_tabs_opened;
174  }
175
176  base::FilePath path_prefix_;
177  base::FilePath log_file_name_;
178  scoped_refptr<BrowserProxy> browser_proxy_;
179
180 private:
181  void AddLaunchArguments() {
182    launch_arguments_.AppendSwitch(switches::kEnableLogging);
183    launch_arguments_.AppendSwitchASCII(switches::kLoggingLevel, "0");
184  }
185
186  DISALLOW_COPY_AND_ASSIGN(TabSwitchingUITest);
187};
188
189// This is failing, and taking forever to finish when doing so.
190// http://crbug.com/102162
191
192TEST_F(TabSwitchingUITest, DISABLED_TabSwitch) {
193  RunTabSwitchingUITest("t", true);
194}
195
196// Started failing with a webkit roll in r49936. See http://crbug.com/46751
197TEST_F(TabSwitchingUITest, DISABLED_TabSwitchRef) {
198  UseReferenceBuild();
199  RunTabSwitchingUITest("t_ref", true);
200}
201
202}  // namespace
203