e10b518f7157c28cba20331464bf57ef1198f1fc
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / benchmark.py
1 # Copyright 2014 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 logging
6 import optparse
7 import os
8 import shutil
9 import sys
10 import zipfile
11
12 from telemetry import decorators
13 from telemetry.core import browser_finder
14 from telemetry.core import command_line
15 from telemetry.core import util
16 from telemetry.page import page_runner
17 from telemetry.page import page_set
18 from telemetry.page import page_test
19 from telemetry.page import test_expectations
20 from telemetry.results import results_options
21 from telemetry.util import cloud_storage
22
23 Disabled = decorators.Disabled
24 Enabled = decorators.Enabled
25
26
27 class BenchmarkMetadata(object):
28   def __init__(self, name):
29     self._name = name
30
31   @property
32   def name(self):
33     return self._name
34
35 class Benchmark(command_line.Command):
36   """Base class for a Telemetry benchmark.
37
38   A test packages a PageTest and a PageSet together.
39   """
40   options = {}
41
42   @classmethod
43   def Name(cls):
44     name = cls.__module__.split('.')[-1]
45     if hasattr(cls, 'tag'):
46       name += '.' + cls.tag
47     if hasattr(cls, 'page_set'):
48       name += '.' + cls.page_set.Name()
49     return name
50
51   @classmethod
52   def AddCommandLineArgs(cls, parser):
53     cls.PageTestClass().AddCommandLineArgs(parser)
54
55     if hasattr(cls, 'AddTestCommandLineArgs'):
56       group = optparse.OptionGroup(parser, '%s test options' % cls.Name())
57       cls.AddTestCommandLineArgs(group)
58       parser.add_option_group(group)
59
60   @classmethod
61   def SetArgumentDefaults(cls, parser):
62     cls.PageTestClass().SetArgumentDefaults(parser)
63     parser.set_defaults(**cls.options)
64
65   @classmethod
66   def ProcessCommandLineArgs(cls, parser, args):
67     cls.PageTestClass().ProcessCommandLineArgs(parser, args)
68
69   def CustomizeBrowserOptions(self, options):
70     """Add browser options that are required by this benchmark."""
71
72   def GetMetadata(self):
73     return BenchmarkMetadata(self.Name())
74
75   def Run(self, finder_options):
76     """Run this test with the given options."""
77     self.CustomizeBrowserOptions(finder_options.browser_options)
78
79     pt = self.PageTestClass()()
80     pt.__name__ = self.__class__.__name__
81
82     if hasattr(self, '_disabled_strings'):
83       pt._disabled_strings = self._disabled_strings
84     if hasattr(self, '_enabled_strings'):
85       pt._enabled_strings = self._enabled_strings
86
87     ps = self.CreatePageSet(finder_options)
88     expectations = self.CreateExpectations(ps)
89
90     self._DownloadGeneratedProfileArchive(finder_options)
91
92     results = results_options.CreateResults(self.GetMetadata(), finder_options)
93     try:
94       page_runner.Run(pt, ps, expectations, finder_options, results)
95     except page_test.TestNotSupportedOnPlatformFailure as failure:
96       logging.warning(str(failure))
97
98     results.PrintSummary()
99     return len(results.failures)
100
101   def _DownloadGeneratedProfileArchive(self, options):
102     """Download and extract profile directory archive if one exists."""
103     archive_name = getattr(self, 'generated_profile_archive', None)
104
105     # If attribute not specified, nothing to do.
106     if not archive_name:
107       return
108
109     # If profile dir specified on command line, nothing to do.
110     if options.browser_options.profile_dir:
111       logging.warning("Profile directory specified on command line: %s, this"
112           "overrides the benchmark's default profile directory.",
113           options.browser_options.profile_dir)
114       return
115
116     # Download profile directory from cloud storage.
117     found_browser = browser_finder.FindBrowser(options)
118     test_data_dir = os.path.join(util.GetChromiumSrcDir(), 'tools', 'perf',
119         'generated_profiles',
120         found_browser.target_os)
121     generated_profile_archive_path = os.path.normpath(
122         os.path.join(test_data_dir, archive_name))
123
124     try:
125       cloud_storage.GetIfChanged(generated_profile_archive_path,
126           cloud_storage.PUBLIC_BUCKET)
127     except (cloud_storage.CredentialsError,
128             cloud_storage.PermissionError) as e:
129       if os.path.exists(generated_profile_archive_path):
130         # If the profile directory archive exists, assume the user has their
131         # own local copy simply warn.
132         logging.warning('Could not download Profile archive: %s',
133             generated_profile_archive_path)
134       else:
135         # If the archive profile directory doesn't exist, this is fatal.
136         logging.error('Can not run without required profile archive: %s. '
137                       'If you believe you have credentials, follow the '
138                       'instructions below.',
139                       generated_profile_archive_path)
140         logging.error(str(e))
141         sys.exit(-1)
142
143     # Unzip profile directory.
144     extracted_profile_dir_path = (
145         os.path.splitext(generated_profile_archive_path)[0])
146     if not os.path.isfile(generated_profile_archive_path):
147       raise Exception("Profile directory archive not downloaded: ",
148           generated_profile_archive_path)
149     with zipfile.ZipFile(generated_profile_archive_path) as f:
150       try:
151         f.extractall(os.path.dirname(generated_profile_archive_path))
152       except e:
153         # Cleanup any leftovers from unzipping.
154         if os.path.exists(extracted_profile_dir_path):
155           shutil.rmtree(extracted_profile_dir_path)
156         logging.error("Error extracting profile directory zip file: %s", e)
157         sys.exit(-1)
158
159     # Run with freshly extracted profile directory.
160     logging.info("Using profile archive directory: %s",
161         extracted_profile_dir_path)
162     options.browser_options.profile_dir = extracted_profile_dir_path
163
164   @classmethod
165   def PageTestClass(cls):
166     """Get the PageTest for this Benchmark.
167
168     If the Benchmark has no PageTest, raises NotImplementedError.
169     """
170     if not hasattr(cls, 'test'):
171       raise NotImplementedError('This test has no "test" attribute.')
172     if not issubclass(cls.test, page_test.PageTest):
173       raise TypeError('"%s" is not a PageTest.' % cls.test.__name__)
174     return cls.test
175
176   @classmethod
177   def PageSetClass(cls):
178     """Get the PageSet for this Benchmark.
179
180     If the Benchmark has no PageSet, raises NotImplementedError.
181     """
182     if not hasattr(cls, 'page_set'):
183       raise NotImplementedError('This test has no "page_set" attribute.')
184     if not issubclass(cls.page_set, page_set.PageSet):
185       raise TypeError('"%s" is not a PageSet.' % cls.page_set.__name__)
186     return cls.page_set
187
188   @classmethod
189   def CreatePageSet(cls, options):  # pylint: disable=W0613
190     """Get the page set this test will run on.
191
192     By default, it will create a page set from the file at this test's
193     page_set attribute. Override to generate a custom page set.
194     """
195     return cls.PageSetClass()()
196
197   @classmethod
198   def CreateExpectations(cls, ps):  # pylint: disable=W0613
199     """Get the expectations this test will run with.
200
201     By default, it will create an empty expectations set. Override to generate
202     custom expectations.
203     """
204     return test_expectations.TestExpectations()
205
206
207 def AddCommandLineArgs(parser):
208   page_runner.AddCommandLineArgs(parser)
209
210
211 def ProcessCommandLineArgs(parser, args):
212   page_runner.ProcessCommandLineArgs(parser, args)