blob: 215e1faedc27d072f0db025cdbc15c217c258e9e [file] [log] [blame]
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Helper script to parse autotest control files for data gathering."""
import argparse
import pathlib
import re
import actions
import cf_parse
import filters
# ChromeOS src/ dir relative to this file.
DEFAULT_SRC_DIR = pathlib.Path(__file__).joinpath('../../../../..').resolve()
def modify_control_files(src_dir, actions_list, filters_list, dry_run=True,
public=True, private=True, client=True, server=True):
"""Apply the given actions to control files if they pass the given filters.
Args:
src_dir: Relative path to the chromium.org src/ directory.
actions_list: List of action functions applied to a ControlFile object.
filters_list: List of filter functions applied to a ControlFile object.
dry_run: True if no files should be actually modified, just printed.
public: True if public tests should be searched.
private: True if private tests should be searched.
client: True if client tests should be searched.
server: True if server tests should be searched.
"""
# Places to look for control files, relative to src_dir.
PUBLIC_CLIENT_DIR = 'third_party/autotest/files/client/site_tests/'
PUBLIC_SERVER_DIR = 'third_party/autotest/files/server/site_tests/'
PRIVATE_CLIENT_DIR = 'third_party/autotest-private/client/site_tests/'
autotest_dirs = []
if public:
if client:
autotest_dirs.append(PUBLIC_CLIENT_DIR)
if server:
autotest_dirs.append(PUBLIC_SERVER_DIR)
if private and client:
autotest_dirs.append(PRIVATE_CLIENT_DIR)
for tests_dir in [pathlib.Path(src_dir, d) for d in autotest_dirs]:
for cf_path in tests_dir.glob('*/control*'):
cf = cf_parse.ControlFile(cf_path)
if not cf.is_valid:
continue
# Skip this control file if it doesn't match all the given filters.
if not all(filter_func(cf) for filter_func in filters_list):
continue
# Apply the given actions to this control file.
modified = False
for action_func in actions_list:
modified = action_func(cf) or modified
cf.update_contents()
if dry_run:
print(f'Will modify {cf_path}:')
print(cf.contents)
else:
print(f'Editing {cf_path}')
with open(cf.path, 'w', encoding='utf-8') as f:
f.write(cf.contents)
def _set_up_args():
"""Define CLI arguments."""
parser = argparse.ArgumentParser()
parser.add_argument('--src_dir', default=DEFAULT_SRC_DIR,
help=('Path to the top-level chromiumos src/ directory '
'in your repo. Defaults to the path relative to '
'this file\'s location.'))
parser.add_argument('--write_out', action='store_true',
help=('Write out changes to your local repo. Please '
'do a dry run first!'))
public_private_group = parser.add_mutually_exclusive_group()
public_private_group.add_argument(
'--public_only', action='store_true',
help='Limit filtering to only public autotest tests.')
public_private_group.add_argument(
'--private_only', action='store_true',
help='Limit filtering to only autotest-private tests.')
client_server_group = parser.add_mutually_exclusive_group()
client_server_group.add_argument(
'--client_only', action='store_true',
help='Limit filtering to only client tests.')
client_server_group.add_argument(
'--server_only', action='store_true',
help='Limit filtering to only server tests.')
action_group = parser.add_argument_group(
'actions', 'Actions which are performed on a control file.')
action_group.add_argument(
'--remove_contacts', required=False, action='append',
metavar='EMAIL,EMAIL,...',
help=('Remove the given (comma or newline-separated) '
'email addresses from Contacts.'))
action_group.add_argument(
'--append_contacts', required=False, action='append',
metavar='EMAIL',
help=('Add (or move) the given email addresses to the END of the '
'contacts list.'))
action_group.add_argument(
'--prepend_contacts', required=False, action='append',
metavar='EMAIL',
help=('Add (or move) the given email addresses to the START of the '
'contacts list.'))
filter_group = parser.add_argument_group(
'filters', 'Filters to specify which control files are touched.')
filter_group.add_argument(
'--test_names', required=False, action='append',
help=('Action: Modify only the given (comma or newline-separated) '
'test ids (i.e. inlcuding \'tauto.\' prefix).'))
return parser.parse_args()
def _split_list_input(string_input):
"""Split the given string into comma or newline-separated values."""
return [elt.strip() for elt in re.split(r',\s*|\n\s*', string_input)]
def _get_actions(args):
"""Given the arguments to the CLI, return a list of actions requested."""
output = []
if args.remove_contacts:
for elt in args.remove_contacts:
output.append(actions.remove_contacts(_split_list_input(elt)))
if args.append_contacts:
for elt in args.append_contact:
output.append(actions.append_contacts(_split_list_input(elt)))
if args.prepend_contacts:
for elt in args.prepend_contact:
output.append(actions.prepend_contacts(_split_list_input(elt)))
return output
def _get_filters(args):
"""Given the arguments to the CLI, return a list of filters requested."""
output = []
if args.test_names:
for elt in args.test_names:
tests = [t.strip() for t in re.split(r',\s*|\n\s*', elt)]
output.append(filters.test_list(tests))
return output
def main():
args = _set_up_args()
print(args)
modify_control_files(
args.src_dir, _get_actions(args), _get_filters(args),
dry_run=not args.write_out,
public=not args.private_only, private=not args.public_only,
client=not args.server_only, server=not args.client_only)
if __name__ == '__main__':
main()