Home | History | Annotate | Download | only in tab_capture
      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 
     36 namespace {
     37 
     38 const char kExtensionId[] = "ddchlicdkolnonkihahngkmmmjnjlkkf";
     39 
     40 enum 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 
     57 class 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 
    220 IN_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)
    226 INSTANTIATE_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 
    245 const int kScalingTestBase =
    246     kTestThroughWebRTC | kDisableVsync | kUseGpu | kForceGpuComposited;
    247 
    248 INSTANTIATE_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