Commit 52274f02 authored by Juan Antonio Navarro Perez's avatar Juan Antonio Navarro Perez Committed by Commit Bot

[Telemetry] Provide standalone results_processor script

The script can be used independently of Telemetry to process a
directory with intermediate results.

Also add tests for options parsing/processing in both regular
and standalone modes.

Bug: 981349
Change-Id: Ic27e3ac538d56f45a5352b7bf866fcf725a49918
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1760306
Commit-Queue: Juan Antonio Navarro Pérez <perezju@chromium.org>
Reviewed-by: default avatarCaleb Rouleau <crouleau@chromium.org>
Cr-Commit-Position: refs/heads/master@{#691570}
parent ed172d31
...@@ -15,9 +15,10 @@ from telemetry import command_line ...@@ -15,9 +15,10 @@ from telemetry import command_line
def main(config, args=None): def main(config, args=None):
results_arg_parser = results_processor.ArgumentParser(
legacy_formats=command_line.LEGACY_OUTPUT_FORMATS)
options = command_line.ParseArgs( options = command_line.ParseArgs(
environment=config, args=args, environment=config, args=args, results_arg_parser=results_arg_parser)
results_arg_parser=results_processor.ArgumentParser())
results_processor.ProcessOptions(options) results_processor.ProcessOptions(options)
run_return_code = command_line.RunCommand(options) run_return_code = command_line.RunCommand(options)
process_return_code = results_processor.ProcessResults(options) process_return_code = results_processor.ProcessResults(options)
......
...@@ -5,3 +5,4 @@ ...@@ -5,3 +5,4 @@
from core.results_processor.processor import ArgumentParser from core.results_processor.processor import ArgumentParser
from core.results_processor.processor import ProcessOptions from core.results_processor.processor import ProcessOptions
from core.results_processor.processor import ProcessResults from core.results_processor.processor import ProcessResults
from core.results_processor.processor import main
...@@ -12,47 +12,45 @@ import argparse ...@@ -12,47 +12,45 @@ import argparse
import datetime import datetime
import os import os
import re import re
import sys
from telemetry import command_line
from telemetry.core import util
from py_utils import cloud_storage from py_utils import cloud_storage
SUPPORTED_FORMATS = { SUPPORTED_FORMATS = {
'none': NotImplemented 'none': NotImplemented,
} }
def ArgumentParser(): def ArgumentParser(standalone=False, legacy_formats=None):
"""Create an ArgumentParser defining options required by the processor.""" """Create an ArgumentParser defining options required by the processor."""
all_output_formats = sorted( all_output_formats = sorted(
set(SUPPORTED_FORMATS).union(command_line.LEGACY_OUTPUT_FORMATS)) set(SUPPORTED_FORMATS).union(legacy_formats or ()))
parser = argparse.ArgumentParser(add_help=False) parser, group = _CreateTopLevelParser(standalone)
group = parser.add_argument_group(title='Result processor options')
group.add_argument( group.add_argument(
'--output-format', action='append', dest='output_formats', '--output-format', action='append', dest='output_formats',
metavar='FORMAT', choices=all_output_formats, metavar='FORMAT', choices=all_output_formats, required=standalone,
help=' '.join([ help=Sentences(
'Output format to produce.', 'Output format to produce.',
'May be used multiple times to produce multiple outputs.', 'May be used multiple times to produce multiple outputs.',
'Avaliable formats: %s.' % ', '.join(all_output_formats), 'Avaliable formats: %s.' % ', '.join(all_output_formats),
'Defaults to: html.'])) '' if standalone else 'Defaults to: html.'))
group.add_argument( group.add_argument(
'--output-dir', default=util.GetBaseDir(), metavar='DIR_PATH', '--intermediate-dir', metavar='DIR_PATH', required=standalone,
help=' '.join([ help=Sentences(
'Path to a directory where to write final results.', 'Path to a directory where intermediate results are stored.',
'Default: %(default)s.'])) '' if standalone else 'If not provided, the default is to create a '
'new directory within "{output_dir}/artifacts/".'))
group.add_argument( group.add_argument(
'--intermediate-dir', metavar='DIR_PATH', '--output-dir', default=_DefaultOutputDir(), metavar='DIR_PATH',
help=' '.join([ help=Sentences(
'Path to a directory where to store intermediate results.', 'Path to a directory where to write final results.',
'If not provided, the default is to create a new directory', 'Default: %(default)s.'))
'within "{output_dir}/artifacts/".']))
group.add_argument( group.add_argument(
'--reset-results', action='store_true', '--reset-results', action='store_true',
help='Remove any previous files in the output directory. The default ' help=Sentences(
'is to append to existing results.') 'Overwrite any previous output files in the output directory.',
'The default is to append to existing results.'))
group.add_argument( group.add_argument(
'--results-label', metavar='LABEL', '--results-label', metavar='LABEL',
help='Label to identify the results generated by this run.') help='Label to identify the results generated by this run.')
...@@ -61,11 +59,11 @@ def ArgumentParser(): ...@@ -61,11 +59,11 @@ def ArgumentParser():
help='Upload generated artifacts to cloud storage.') help='Upload generated artifacts to cloud storage.')
group.add_argument( group.add_argument(
'--upload-bucket', default='output', metavar='BUCKET', '--upload-bucket', default='output', metavar='BUCKET',
help=' '.join([ help=Sentences(
'Storage bucket to use for uploading artifacts.', 'Storage bucket to use for uploading artifacts.',
'Supported values are: %s; or a valid cloud storage bucket name.' 'Supported values are: %s; or a valid cloud storage bucket name.'
% ', '.join(cloud_storage.BUCKET_ALIAS_NAMES), % ', '.join(sorted(cloud_storage.BUCKET_ALIASES)),
'Defaults to: %(default)s.'])) 'Defaults to: %(default)s.'))
group.set_defaults(legacy_output_formats=[]) group.set_defaults(legacy_output_formats=[])
return parser return parser
...@@ -141,3 +139,41 @@ def ProcessResults(options): ...@@ -141,3 +139,41 @@ def ProcessResults(options):
return 0 return 0
raise NotImplementedError(options.output_formats) raise NotImplementedError(options.output_formats)
def _CreateTopLevelParser(standalone):
"""Create top level parser, and group for result options."""
if standalone:
parser = argparse.ArgumentParser(
description='Standalone command line interface to results_processor.')
# In standalone mode, both the parser and group are the same thing.
return parser, parser
else:
parser = argparse.ArgumentParser(add_help=False)
group = parser.add_argument_group(title='Result processor options')
return parser, group
def _DefaultOutputDir():
"""Default output directory.
Points to the directory of the benchmark runner script, if found, or the
current working directory otherwise.
"""
main_module = sys.modules['__main__']
if hasattr(main_module, '__file__'):
return os.path.abspath(os.path.dirname(main_module.__file__))
else:
return os.getcwd()
def Sentences(*args):
return ' '.join(s for s in args if s)
def main(args=None):
"""Entry point for the standalone version of the results_processor script."""
parser = ArgumentParser(standalone=True)
options = parser.parse_args(args)
ProcessOptions(options)
return ProcessResults(options)
...@@ -19,6 +19,9 @@ def module(symbol): ...@@ -19,6 +19,9 @@ def module(symbol):
class ProcessOptionsTestCase(unittest.TestCase): class ProcessOptionsTestCase(unittest.TestCase):
def setUp(self): def setUp(self):
self.legacy_formats = []
self.standalone = False
# Mock os module within results_processor so path manipulations do not # Mock os module within results_processor so path manipulations do not
# depend on the file system of the test environment. # depend on the file system of the test environment.
mock_os = mock.patch(module('os')).start() mock_os = mock.patch(module('os')).start()
...@@ -35,14 +38,15 @@ class ProcessOptionsTestCase(unittest.TestCase): ...@@ -35,14 +38,15 @@ class ProcessOptionsTestCase(unittest.TestCase):
mock_os.path.dirname.side_effect = posixpath.dirname mock_os.path.dirname.side_effect = posixpath.dirname
mock_os.path.join.side_effect = posixpath.join mock_os.path.join.side_effect = posixpath.join
telemetry_util = mock.patch(module('util')).start() mock.patch(module('_DefaultOutputDir'),
telemetry_util.GetBaseDir.return_value = '/path/to/output_dir' return_value='/path/to/output_dir').start()
def tearDown(self): def tearDown(self):
mock.patch.stopall() mock.patch.stopall()
def ParseArgs(self, args): def ParseArgs(self, args):
parser = processor.ArgumentParser() parser = processor.ArgumentParser(
standalone=self.standalone, legacy_formats=self.legacy_formats)
options = parser.parse_args(args) options = parser.parse_args(args)
processor.ProcessOptions(options) processor.ProcessOptions(options)
return options return options
...@@ -112,6 +116,7 @@ class TestProcessOptions(ProcessOptionsTestCase): ...@@ -112,6 +116,7 @@ class TestProcessOptions(ProcessOptionsTestCase):
self.assertEqual(options.upload_bucket, 'some-special-bucket') self.assertEqual(options.upload_bucket, 'some-special-bucket')
def testDefaultOutputFormat(self): def testDefaultOutputFormat(self):
self.legacy_formats = ['html']
options = self.ParseArgs([]) options = self.ParseArgs([])
self.assertEqual(options.output_formats, []) self.assertEqual(options.output_formats, [])
self.assertEqual(options.legacy_output_formats, ['html']) self.assertEqual(options.legacy_output_formats, ['html'])
...@@ -121,10 +126,32 @@ class TestProcessOptions(ProcessOptionsTestCase): ...@@ -121,10 +126,32 @@ class TestProcessOptions(ProcessOptionsTestCase):
self.ParseArgs(['--output-format', 'unknown']) self.ParseArgs(['--output-format', 'unknown'])
@mock.patch.dict(module('SUPPORTED_FORMATS'), {'new-format': None}) @mock.patch.dict(module('SUPPORTED_FORMATS'), {'new-format': None})
@mock.patch(module('command_line')) def testOutputFormatsSplit(self):
def testOutputFormatsSplit(self, telemetry_cli): self.legacy_formats = ['old-format']
telemetry_cli.LEGACY_OUTPUT_FORMATS = ['old-format']
options = self.ParseArgs( options = self.ParseArgs(
['--output-format', 'new-format', '--output-format', 'old-format']) ['--output-format', 'new-format', '--output-format', 'old-format'])
self.assertEqual(options.output_formats, ['new-format']) self.assertEqual(options.output_formats, ['new-format'])
self.assertEqual(options.legacy_output_formats, ['old-format']) self.assertEqual(options.legacy_output_formats, ['old-format'])
class StandaloneTestProcessOptions(ProcessOptionsTestCase):
def setUp(self):
super(StandaloneTestProcessOptions, self).setUp()
self.standalone = True
def testOutputFormatRequired(self):
with self.assertRaises(SystemExit):
self.ParseArgs([])
@mock.patch.dict(module('SUPPORTED_FORMATS'), {'new-format': None})
def testIntermediateDirRequired(self):
with self.assertRaises(SystemExit):
self.ParseArgs(['--output-format', 'new-format'])
@mock.patch.dict(module('SUPPORTED_FORMATS'), {'new-format': None})
def testSuccessful(self):
options = self.ParseArgs(
['--output-format', 'new-format', '--intermediate-dir', 'some_dir'])
self.assertEqual(options.output_formats, ['new-format'])
self.assertEqual(options.intermediate_dir, '/path/to/curdir/some_dir')
self.assertEqual(options.output_dir, '/path/to/output_dir')
#!/usr/bin/env vpython
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import sys
from core import path_util
path_util.AddPyUtilsToPath()
from core import results_processor
if __name__ == '__main__':
sys.exit(results_processor.main())
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment