1#!/usr/bin/python2 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 18from __future__ import print_function 19 20import argparse 21import json 22import os 23import sys 24 25 26def main(): 27 # pylint: disable=missing-docstring 28 29 # The config file is written by TastTest._run_test in tast_unittest.py and 30 # consists of a dict from command names (e.g. 'list', 'run', etc.) to 31 # command definition dicts (see TastCommand in tast_unittest.py). 32 path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 33 'config.json') 34 with open(path) as f: 35 cfg = json.load(f) 36 37 args = parse_args() 38 cmd = cfg.get(args.command) 39 if not cmd: 40 raise RuntimeError('Unexpected command "%s"' % args.command) 41 42 for arg in cmd.get('required_args', []): 43 name, expected_value = arg.split('=', 1) 44 # argparse puts the repeated "pattern" args into a list of lists 45 # instead of a single list. Pull the args back out in this case. 46 val = getattr(args, name) 47 if isinstance(val, list) and len(val) == 1 and isinstance(val[0], list): 48 val = val[0] 49 actual_value = str(val) 50 if actual_value != expected_value: 51 raise RuntimeError('Got arg %s with value "%s"; want "%s"' % 52 (name, actual_value, expected_value)) 53 54 if cmd.get('stdout'): 55 sys.stdout.write(cmd['stdout']) 56 if cmd.get('stderr'): 57 sys.stderr.write(cmd['stderr']) 58 59 if cmd.get('files_to_write'): 60 for path, data in cmd['files_to_write'].iteritems(): 61 dirname = os.path.dirname(path) 62 if not os.path.exists(dirname): 63 os.makedirs(dirname, 0o0755) 64 with open(path, 'w') as f: 65 f.write(data) 66 67 sys.exit(cmd.get('status')) 68 69 70def parse_args(): 71 """Parses command-line arguments. 72 73 @returns: argparse.Namespace object containing parsed attributes. 74 """ 75 def to_bool(v): 76 """Converts a boolean flag's value to a Python bool value.""" 77 if v == 'true' or v == '': 78 return True 79 if v == 'false': 80 return False 81 raise argparse.ArgumentTypeError('"true" or "false" expected') 82 83 parser = argparse.ArgumentParser() 84 parser.add_argument('-logtime', type=to_bool, default=False, nargs='?') 85 parser.add_argument('-verbose', type=to_bool, default=False, nargs='?') 86 parser.add_argument('-version', action='version', version='1.0') 87 88 subparsers = parser.add_subparsers(dest='command') 89 90 def add_common_args(subparser): 91 """Adds arguments shared between tast's 'list' and 'run' subcommands.""" 92 subparser.add_argument('-build', type=to_bool, default=True, nargs='?') 93 subparser.add_argument('-buildbundle', default='cros') 94 subparser.add_argument('-checkbuilddeps', type=to_bool, default=True, 95 nargs='?') 96 subparser.add_argument('-downloadprivatebundles', type=to_bool, 97 default=False, nargs='?') 98 subparser.add_argument('-devservers') 99 subparser.add_argument('-remotebundledir') 100 subparser.add_argument('-remotedatadir') 101 subparser.add_argument('-remoterunner') 102 subparser.add_argument('-sshretries') 103 subparser.add_argument('-downloaddata') 104 subparser.add_argument('target') 105 subparser.add_argument('patterns', action='append', nargs='*') 106 107 list_parser = subparsers.add_parser('list') 108 add_common_args(list_parser) 109 list_parser.add_argument('-json', type=to_bool, default=False, nargs='?') 110 111 run_parser = subparsers.add_parser('run') 112 add_common_args(run_parser) 113 run_parser.add_argument('-resultsdir') 114 run_parser.add_argument('-waituntilready') 115 run_parser.add_argument('-timeout') 116 run_parser.add_argument('-continueafterfailure', type=to_bool, 117 default=False, nargs='?') 118 run_parser.add_argument('-var', action='append', default=[]) 119 run_parser.add_argument('-defaultvarsdir') 120 run_parser.add_argument('-varsfile', action='append', default=[]) 121 run_parser.add_argument('-buildartifactsurl') 122 123 return parser.parse_args() 124 125 126if __name__ == '__main__': 127 main() 128