1#!/usr/bin/python 2# Copyright 2018 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Fake implementation of tast executable used by tast_unittest.py. 7 8In unit tests, this file is executed instead of the real tast executable by the 9tast.py server test. It: 10 11- reads a config.json file installed alongside this script, 12- parses command-line arguments passed by tast.py, 13- checks that the arguments included all required args specified by the config 14 file for the given command (e.g. 'list', 'run' etc.), and 15- performs actions specified by the config file. 16""" 17 18import argparse 19import json 20import os 21import sys 22 23 24def main(): 25 # pylint: disable=missing-docstring 26 27 # The config file is written by TastTest._run_test in tast_unittest.py and 28 # consists of a dict from command names (e.g. 'list', 'run', etc.) to 29 # command definition dicts (see TastCommand in tast_unittest.py). 30 path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 31 'config.json') 32 with open(path) as f: 33 cfg = json.load(f) 34 35 args = parse_args() 36 cmd = cfg.get(args.command) 37 if not cmd: 38 raise RuntimeError('Unexpected command "%s"' % args.command) 39 40 for arg in cmd.get('required_args', []): 41 name, expected_value = arg.split('=') 42 # argparse puts the repeated "pattern" args into a list of lists 43 # instead of a single list. Pull the args back out in this case. 44 val = getattr(args, name) 45 if isinstance(val, list) and len(val) == 1 and isinstance(val[0], list): 46 val = val[0] 47 actual_value = str(val) 48 if actual_value != expected_value: 49 raise RuntimeError('Got arg %s with value "%s"; want "%s"' % 50 (name, actual_value, expected_value)) 51 52 if cmd.get('stdout'): 53 sys.stdout.write(cmd['stdout']) 54 if cmd.get('stderr'): 55 sys.stderr.write(cmd['stderr']) 56 57 if cmd.get('files_to_write'): 58 for path, data in cmd['files_to_write'].iteritems(): 59 dirname = os.path.dirname(path) 60 if not os.path.exists(dirname): 61 os.makedirs(dirname, 0755) 62 with open(path, 'w') as f: 63 f.write(data) 64 65 sys.exit(cmd.get('status')) 66 67 68def parse_args(): 69 """Parses command-line arguments. 70 71 @returns: argparse.Namespace object containing parsed attributes. 72 """ 73 def to_bool(v): 74 """Converts a boolean flag's value to a Python bool value.""" 75 if v == 'true' or v == '': 76 return True 77 if v == 'false': 78 return False 79 raise argparse.ArgumentTypeError('"true" or "false" expected') 80 81 parser = argparse.ArgumentParser() 82 parser.add_argument('-logtime', type=to_bool, default=False, nargs='?') 83 parser.add_argument('-verbose', type=to_bool, default=False, nargs='?') 84 parser.add_argument('-version', action='version', version='1.0') 85 86 subparsers = parser.add_subparsers(dest='command') 87 88 def add_common_args(subparser): 89 """Adds arguments shared between tast's 'list' and 'run' subcommands.""" 90 subparser.add_argument('-build', type=to_bool, default=True, nargs='?') 91 subparser.add_argument('-downloadprivatebundles', type=to_bool, 92 default=False, nargs='?') 93 subparser.add_argument('-devservers') 94 subparser.add_argument('-remotebundledir') 95 subparser.add_argument('-remotedatadir') 96 subparser.add_argument('-remoterunner') 97 subparser.add_argument('-sshretries') 98 subparser.add_argument('target') 99 subparser.add_argument('patterns', action='append', nargs='*') 100 101 list_parser = subparsers.add_parser('list') 102 add_common_args(list_parser) 103 list_parser.add_argument('-json', type=to_bool, default=False, nargs='?') 104 105 run_parser = subparsers.add_parser('run') 106 add_common_args(run_parser) 107 run_parser.add_argument('-resultsdir') 108 run_parser.add_argument('-waituntilready') 109 run_parser.add_argument('-timeout') 110 111 return parser.parse_args() 112 113 114if __name__ == '__main__': 115 main() 116