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.
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
23 Disabled = decorators.Disabled
24 Enabled = decorators.Enabled
27 class InvalidOptionsError(Exception):
28 """Raised for invalid benchmark options."""
32 class BenchmarkMetadata(object):
33 def __init__(self, name):
40 class Benchmark(command_line.Command):
41 """Base class for a Telemetry benchmark.
43 A test packages a PageTest and a PageSet together.
49 name = cls.__module__.split('.')[-1]
50 if hasattr(cls, 'tag'):
52 if hasattr(cls, 'page_set'):
53 name += '.' + cls.page_set.Name()
57 def AddCommandLineArgs(cls, parser):
58 cls.PageTestClass().AddCommandLineArgs(parser)
60 if hasattr(cls, 'AddTestCommandLineArgs'):
61 group = optparse.OptionGroup(parser, '%s test options' % cls.Name())
62 cls.AddTestCommandLineArgs(group)
63 parser.add_option_group(group)
66 def SetArgumentDefaults(cls, parser):
67 cls.PageTestClass().SetArgumentDefaults(parser)
68 default_values = parser.get_default_values()
70 o for o in cls.options if not hasattr(default_values, o)]
72 raise InvalidOptionsError('Invalid benchmark options: %s',
73 ', '.join(invalid_options))
74 parser.set_defaults(**cls.options)
77 def ProcessCommandLineArgs(cls, parser, args):
78 cls.PageTestClass().ProcessCommandLineArgs(parser, args)
80 def CustomizeBrowserOptions(self, options):
81 """Add browser options that are required by this benchmark."""
83 def GetMetadata(self):
84 return BenchmarkMetadata(self.Name())
86 def Run(self, finder_options):
87 """Run this test with the given options."""
88 self.CustomizeBrowserOptions(finder_options.browser_options)
90 pt = self.PageTestClass()()
91 pt.__name__ = self.__class__.__name__
93 if hasattr(self, '_disabled_strings'):
94 pt._disabled_strings = self._disabled_strings
95 if hasattr(self, '_enabled_strings'):
96 pt._enabled_strings = self._enabled_strings
98 expectations = self.CreateExpectations()
99 us = self.CreateUserStorySet(finder_options)
101 self._DownloadGeneratedProfileArchive(finder_options)
103 benchmark_metadata = self.GetMetadata()
104 results = results_options.CreateResults(benchmark_metadata, finder_options)
106 page_runner.Run(pt, us, expectations, finder_options, results)
107 except page_test.TestNotSupportedOnPlatformFailure as failure:
108 logging.warning(str(failure))
110 results.PrintSummary()
111 return len(results.failures)
113 def _DownloadGeneratedProfileArchive(self, options):
114 """Download and extract profile directory archive if one exists."""
115 archive_name = getattr(self, 'generated_profile_archive', None)
117 # If attribute not specified, nothing to do.
121 # If profile dir specified on command line, nothing to do.
122 if options.browser_options.profile_dir:
123 logging.warning("Profile directory specified on command line: %s, this"
124 "overrides the benchmark's default profile directory.",
125 options.browser_options.profile_dir)
128 # Download profile directory from cloud storage.
129 found_browser = browser_finder.FindBrowser(options)
130 test_data_dir = os.path.join(util.GetChromiumSrcDir(), 'tools', 'perf',
131 'generated_profiles',
132 found_browser.target_os)
133 generated_profile_archive_path = os.path.normpath(
134 os.path.join(test_data_dir, archive_name))
137 cloud_storage.GetIfChanged(generated_profile_archive_path,
138 cloud_storage.PUBLIC_BUCKET)
139 except (cloud_storage.CredentialsError,
140 cloud_storage.PermissionError) as e:
141 if os.path.exists(generated_profile_archive_path):
142 # If the profile directory archive exists, assume the user has their
143 # own local copy simply warn.
144 logging.warning('Could not download Profile archive: %s',
145 generated_profile_archive_path)
147 # If the archive profile directory doesn't exist, this is fatal.
148 logging.error('Can not run without required profile archive: %s. '
149 'If you believe you have credentials, follow the '
150 'instructions below.',
151 generated_profile_archive_path)
152 logging.error(str(e))
155 # Unzip profile directory.
156 extracted_profile_dir_path = (
157 os.path.splitext(generated_profile_archive_path)[0])
158 if not os.path.isfile(generated_profile_archive_path):
159 raise Exception("Profile directory archive not downloaded: ",
160 generated_profile_archive_path)
161 with zipfile.ZipFile(generated_profile_archive_path) as f:
163 f.extractall(os.path.dirname(generated_profile_archive_path))
165 # Cleanup any leftovers from unzipping.
166 if os.path.exists(extracted_profile_dir_path):
167 shutil.rmtree(extracted_profile_dir_path)
168 logging.error("Error extracting profile directory zip file: %s", e)
171 # Run with freshly extracted profile directory.
172 logging.info("Using profile archive directory: %s",
173 extracted_profile_dir_path)
174 options.browser_options.profile_dir = extracted_profile_dir_path
177 def PageTestClass(cls):
178 """Get the PageTest for this Benchmark.
180 If the Benchmark has no PageTest, raises NotImplementedError.
182 if not hasattr(cls, 'test'):
183 raise NotImplementedError('This test has no "test" attribute.')
184 if not issubclass(cls.test, page_test.PageTest):
185 raise TypeError('"%s" is not a PageTest.' % cls.test.__name__)
188 def CreatePageSet(self, options): # pylint: disable=W0613
189 """Get the page set this test will run on.
191 By default, it will create a page set from the this test's page_set
192 attribute. Override to generate a custom page set.
194 if not hasattr(self, 'page_set'):
195 raise NotImplementedError('This test has no "page_set" attribute.')
196 if not issubclass(self.page_set, page_set.PageSet):
197 raise TypeError('"%s" is not a PageSet.' % self.page_set.__name__)
198 return self.page_set()
200 def CreateUserStorySet(self, options):
201 return self.CreatePageSet(options)
204 def CreateExpectations(cls):
205 """Get the expectations this test will run with.
207 By default, it will create an empty expectations set. Override to generate
210 return test_expectations.TestExpectations()
213 def AddCommandLineArgs(parser):
214 page_runner.AddCommandLineArgs(parser)
217 def ProcessCommandLineArgs(parser, args):
218 page_runner.ProcessCommandLineArgs(parser, args)