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
def main(config, args=None):
results_arg_parser = results_processor.ArgumentParser(
legacy_formats=command_line.LEGACY_OUTPUT_FORMATS)
options = command_line.ParseArgs(
environment=config, args=args,
results_arg_parser=results_processor.ArgumentParser())
environment=config, args=args, results_arg_parser=results_arg_parser)
results_processor.ProcessOptions(options)
run_return_code = command_line.RunCommand(options)
process_return_code = results_processor.ProcessResults(options)
......
......@@ -5,3 +5,4 @@
from core.results_processor.processor import ArgumentParser
from core.results_processor.processor import ProcessOptions
from core.results_processor.processor import ProcessResults
from core.results_processor.processor import main
......@@ -12,47 +12,45 @@ import argparse
import datetime
import os
import re
from telemetry import command_line
from telemetry.core import util
import sys
from py_utils import cloud_storage
SUPPORTED_FORMATS = {
'none': NotImplemented
'none': NotImplemented,
}
def ArgumentParser():
def ArgumentParser(standalone=False, legacy_formats=None):
"""Create an ArgumentParser defining options required by the processor."""
all_output_formats = sorted(
set(SUPPORTED_FORMATS).union(command_line.LEGACY_OUTPUT_FORMATS))
parser = argparse.ArgumentParser(add_help=False)
group = parser.add_argument_group(title='Result processor options')
set(SUPPORTED_FORMATS).union(legacy_formats or ()))
parser, group = _CreateTopLevelParser(standalone)
group.add_argument(
'--output-format', action='append', dest='output_formats',
metavar='FORMAT', choices=all_output_formats,
help=' '.join([
metavar='FORMAT', choices=all_output_formats, required=standalone,
help=Sentences(
'Output format to produce.',
'May be used multiple times to produce multiple outputs.',
'Avaliable formats: %s.' % ', '.join(all_output_formats),
'Defaults to: html.']))
'' if standalone else 'Defaults to: html.'))
group.add_argument(
'--output-dir', default=util.GetBaseDir(), metavar='DIR_PATH',
help=' '.join([
'Path to a directory where to write final results.',
'Default: %(default)s.']))
'--intermediate-dir', metavar='DIR_PATH', required=standalone,
help=Sentences(
'Path to a directory where intermediate results are stored.',
'' if standalone else 'If not provided, the default is to create a '
'new directory within "{output_dir}/artifacts/".'))
group.add_argument(
'--intermediate-dir', metavar='DIR_PATH',
help=' '.join([
'Path to a directory where to store intermediate results.',
'If not provided, the default is to create a new directory',
'within "{output_dir}/artifacts/".']))
'--output-dir', default=_DefaultOutputDir(), metavar='DIR_PATH',
help=Sentences(
'Path to a directory where to write final results.',
'Default: %(default)s.'))
group.add_argument(
'--reset-results', action='store_true',
help='Remove any previous files in the output directory. The default '
'is to append to existing results.')
help=Sentences(
'Overwrite any previous output files in the output directory.',
'The default is to append to existing results.'))
group.add_argument(
'--results-label', metavar='LABEL',
help='Label to identify the results generated by this run.')
......@@ -61,11 +59,11 @@ def ArgumentParser():
help='Upload generated artifacts to cloud storage.')
group.add_argument(
'--upload-bucket', default='output', metavar='BUCKET',
help=' '.join([
help=Sentences(
'Storage bucket to use for uploading artifacts.',
'Supported values are: %s; or a valid cloud storage bucket name.'
% ', '.join(cloud_storage.BUCKET_ALIAS_NAMES),
'Defaults to: %(default)s.']))
% ', '.join(sorted(cloud_storage.BUCKET_ALIASES)),
'Defaults to: %(default)s.'))
group.set_defaults(legacy_output_formats=[])
return parser
......@@ -141,3 +139,41 @@ def ProcessResults(options):
return 0
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):
class ProcessOptionsTestCase(unittest.TestCase):
def setUp(self):
self.legacy_formats = []
self.standalone = False
# Mock os module within results_processor so path manipulations do not
# depend on the file system of the test environment.
mock_os = mock.patch(module('os')).start()
......@@ -35,14 +38,15 @@ class ProcessOptionsTestCase(unittest.TestCase):
mock_os.path.dirname.side_effect = posixpath.dirname
mock_os.path.join.side_effect = posixpath.join
telemetry_util = mock.patch(module('util')).start()
telemetry_util.GetBaseDir.return_value = '/path/to/output_dir'
mock.patch(module('_DefaultOutputDir'),
return_value='/path/to/output_dir').start()
def tearDown(self):
mock.patch.stopall()
def ParseArgs(self, args):
parser = processor.ArgumentParser()
parser = processor.ArgumentParser(
standalone=self.standalone, legacy_formats=self.legacy_formats)
options = parser.parse_args(args)
processor.ProcessOptions(options)
return options
......@@ -112,6 +116,7 @@ class TestProcessOptions(ProcessOptionsTestCase):
self.assertEqual(options.upload_bucket, 'some-special-bucket')
def testDefaultOutputFormat(self):
self.legacy_formats = ['html']
options = self.ParseArgs([])
self.assertEqual(options.output_formats, [])
self.assertEqual(options.legacy_output_formats, ['html'])
......@@ -121,10 +126,32 @@ class TestProcessOptions(ProcessOptionsTestCase):
self.ParseArgs(['--output-format', 'unknown'])
@mock.patch.dict(module('SUPPORTED_FORMATS'), {'new-format': None})
@mock.patch(module('command_line'))
def testOutputFormatsSplit(self, telemetry_cli):
telemetry_cli.LEGACY_OUTPUT_FORMATS = ['old-format']
def testOutputFormatsSplit(self):
self.legacy_formats = ['old-format']
options = self.ParseArgs(
['--output-format', 'new-format', '--output-format', 'old-format'])
self.assertEqual(options.output_formats, ['new-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