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 "base/basictypes.h"
6#include "base/command_line.h"
7#if defined(OS_MACOSX)
8#include "base/mac/mac_util.h"
9#endif
10#include "base/strings/stringprintf.h"
11#include "base/test/trace_event_analyzer.h"
12#include "base/win/windows_version.h"
13#include "chrome/browser/extensions/extension_apitest.h"
14#include "chrome/browser/extensions/extension_service.h"
15#include "chrome/browser/extensions/tab_helper.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
18#include "chrome/common/chrome_switches.h"
19#include "chrome/common/chrome_version_info.h"
20#include "chrome/test/base/test_launcher_utils.h"
21#include "chrome/test/base/test_switches.h"
22#include "chrome/test/base/tracing.h"
23#include "content/public/common/content_switches.h"
24#include "extensions/common/feature_switch.h"
25#include "extensions/common/features/base_feature_provider.h"
26#include "extensions/common/features/complex_feature.h"
27#include "extensions/common/features/feature.h"
28#include "extensions/common/features/simple_feature.h"
29#include "extensions/common/switches.h"
30#include "extensions/test/extension_test_message_listener.h"
31#include "testing/gtest/include/gtest/gtest.h"
32#include "testing/perf/perf_test.h"
33#include "ui/compositor/compositor_switches.h"
34#include "ui/gl/gl_switches.h"
35
36namespace {
37
38const char kExtensionId[] = "ddchlicdkolnonkihahngkmmmjnjlkkf";
39
40enum TestFlags {
41  kUseGpu              = 1 << 0, // Only execute test if --enable-gpu was given
42                                 // on the command line.  This is required for
43                                 // tests that run on GPU.
44  kForceGpuComposited  = 1 << 1, // Force the test to use the compositor.
45  kDisableVsync        = 1 << 2, // Do not limit framerate to vertical refresh.
46                                 // when on GPU, nor to 60hz when not on GPU.
47  kTestThroughWebRTC   = 1 << 3, // Send captured frames through webrtc
48  kSmallWindow         = 1 << 4, // 1 = 800x600, 0 = 2000x1000
49
50  kScaleQualityMask    = 3 << 5, // two bits select which scaling quality
51  kScaleQualityDefault = 0 << 5, // to use on aura.
52  kScaleQualityFast    = 1 << 5,
53  kScaleQualityGood    = 2 << 5,
54  kScaleQualityBest    = 3 << 5,
55};
56
57class TabCapturePerformanceTest
58    : public ExtensionApiTest,
59      public testing::WithParamInterface<int> {
60 public:
61  TabCapturePerformanceTest() {}
62
63  bool HasFlag(TestFlags flag) const {
64    return (GetParam() & flag) == flag;
65  }
66
67  bool IsGpuAvailable() const {
68    return CommandLine::ForCurrentProcess()->HasSwitch("enable-gpu");
69  }
70
71  std::string ScalingMethod() const {
72    switch (GetParam() & kScaleQualityMask) {
73      case kScaleQualityFast:
74        return "fast";
75      case kScaleQualityGood:
76        return "good";
77      case kScaleQualityBest:
78        return "best";
79      default:
80        return "";
81    }
82  }
83
84  std::string GetSuffixForTestFlags() {
85    std::string suffix;
86    if (HasFlag(kForceGpuComposited))
87      suffix += "_comp";
88    if (HasFlag(kUseGpu))
89      suffix += "_gpu";
90    if (HasFlag(kDisableVsync))
91      suffix += "_novsync";
92    if (HasFlag(kTestThroughWebRTC))
93      suffix += "_webrtc";
94    if (!ScalingMethod().empty())
95      suffix += "_scale" + ScalingMethod();
96    if (HasFlag(kSmallWindow))
97      suffix += "_small";
98    return suffix;
99  }
100
101  virtual void SetUp() OVERRIDE {
102    EnablePixelOutput();
103    ExtensionApiTest::SetUp();
104  }
105
106  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
107    if (!ScalingMethod().empty()) {
108      command_line->AppendSwitchASCII(switches::kTabCaptureUpscaleQuality,
109                                      ScalingMethod());
110      command_line->AppendSwitchASCII(switches::kTabCaptureDownscaleQuality,
111                                      ScalingMethod());
112    }
113
114    // Some of the tests may launch http requests through JSON or AJAX
115    // which causes a security error (cross domain request) when the page
116    // is loaded from the local file system ( file:// ). The following switch
117    // fixes that error.
118    command_line->AppendSwitch(switches::kAllowFileAccessFromFiles);
119
120    if (HasFlag(kSmallWindow)) {
121      command_line->AppendSwitchASCII(switches::kWindowSize, "800,600");
122    } else {
123      command_line->AppendSwitchASCII(switches::kWindowSize, "2000,1500");
124    }
125
126    if (!HasFlag(kUseGpu))
127      command_line->AppendSwitch(switches::kDisableGpu);
128
129    if (HasFlag(kDisableVsync))
130      command_line->AppendSwitch(switches::kDisableGpuVsync);
131
132    command_line->AppendSwitchASCII(
133        extensions::switches::kWhitelistedExtensionID,
134        kExtensionId);
135    ExtensionApiTest::SetUpCommandLine(command_line);
136  }
137
138  bool PrintResults(trace_analyzer::TraceAnalyzer *analyzer,
139                    const std::string& test_name,
140                    const std::string& event_name,
141                    const std::string& unit) {
142    trace_analyzer::TraceEventVector events;
143    trace_analyzer::Query query =
144        trace_analyzer::Query::EventNameIs(event_name) &&
145        (trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN) ||
146         trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_COMPLETE) ||
147         trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
148         trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_FLOW_BEGIN) ||
149         trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_INSTANT));
150    analyzer->FindEvents(query, &events);
151    if (events.size() < 20) {
152      LOG(ERROR) << "Not enough events of type " << event_name << " found ("
153                 << events.size() << ").";
154      return false;
155    }
156
157    // Ignore some events for startup/setup/caching.
158    trace_analyzer::TraceEventVector rate_events(events.begin() + 3,
159                                                 events.end() - 3);
160    trace_analyzer::RateStats stats;
161    if (!GetRateStats(rate_events, &stats, NULL)) {
162      LOG(ERROR) << "GetRateStats failed";
163      return false;
164    }
165    double mean_ms = stats.mean_us / 1000.0;
166    double std_dev_ms = stats.standard_deviation_us / 1000.0;
167    std::string mean_and_error = base::StringPrintf("%f,%f", mean_ms,
168                                                    std_dev_ms);
169    perf_test::PrintResultMeanAndError(test_name,
170                                       GetSuffixForTestFlags(),
171                                       event_name,
172                                       mean_and_error,
173                                       unit,
174                                       true);
175    return true;
176  }
177
178  void RunTest(const std::string& test_name) {
179    if (HasFlag(kUseGpu) && !IsGpuAvailable()) {
180      LOG(WARNING) <<
181          "Test skipped: requires gpu. Pass --enable-gpu on the command "
182          "line if use of GPU is desired.";
183      return;
184    }
185
186    std::string json_events;
187    ASSERT_TRUE(tracing::BeginTracing("gpu,mirroring"));
188    std::string page = "performance.html";
189    page += HasFlag(kTestThroughWebRTC) ? "?WebRTC=1" : "?WebRTC=0";
190    // Ideally we'd like to run a higher capture rate when vsync is disabled,
191    // but libjingle currently doesn't allow that.
192    // page += HasFlag(kDisableVsync) ? "&fps=300" : "&fps=30";
193    page += "&fps=30";
194    ASSERT_TRUE(RunExtensionSubtest("tab_capture", page)) << message_;
195    ASSERT_TRUE(tracing::EndTracing(&json_events));
196    scoped_ptr<trace_analyzer::TraceAnalyzer> analyzer;
197    analyzer.reset(trace_analyzer::TraceAnalyzer::Create(json_events));
198
199    // The printed result will be the average time between frames in the
200    // browser window.
201    bool gpu_frames = PrintResults(
202        analyzer.get(),
203        test_name,
204        "RenderWidget::didCommitAndDrawCompositorFrame",
205        "ms");
206    EXPECT_TRUE(gpu_frames);
207
208    // This prints out the average time between capture events.
209    // As the capture frame rate is capped at 30fps, this score
210    // cannot get any better than (lower) 33.33 ms.
211    EXPECT_TRUE(PrintResults(analyzer.get(),
212                             test_name,
213                             "Capture",
214                             "ms"));
215  }
216};
217
218}  // namespace
219
220IN_PROC_BROWSER_TEST_P(TabCapturePerformanceTest, Performance) {
221  RunTest("TabCapturePerformance");
222}
223
224// Note: First argument is optional and intentionally left blank.
225// (it's a prefix for the generated test cases)
226INSTANTIATE_TEST_CASE_P(
227    ,
228    TabCapturePerformanceTest,
229    testing::Values(
230        0,
231        kUseGpu | kForceGpuComposited,
232        kDisableVsync,
233        kDisableVsync | kUseGpu | kForceGpuComposited,
234        kTestThroughWebRTC,
235        kTestThroughWebRTC | kUseGpu | kForceGpuComposited,
236        kTestThroughWebRTC | kDisableVsync,
237        kTestThroughWebRTC | kDisableVsync | kUseGpu | kForceGpuComposited));
238
239#if defined(USE_AURA)
240// TODO(hubbe):
241// These are temporary tests for the purpose of determining what the
242// appropriate scaling quality is. Once that has been determined,
243// these tests will be removed.
244
245const int kScalingTestBase =
246    kTestThroughWebRTC | kDisableVsync | kUseGpu | kForceGpuComposited;
247
248INSTANTIATE_TEST_CASE_P(
249    ScalingTests,
250    TabCapturePerformanceTest,
251    testing::Values(
252        kScalingTestBase | kScaleQualityFast,
253        kScalingTestBase | kScaleQualityGood,
254        kScalingTestBase | kScaleQualityBest,
255        kScalingTestBase | kScaleQualityFast | kSmallWindow,
256        kScalingTestBase | kScaleQualityGood | kSmallWindow,
257        kScalingTestBase | kScaleQualityBest | kSmallWindow));
258
259#endif  // USE_AURA
260