Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 1 | # test case management tool - manual execution from testopia test cases |
| 2 | # |
| 3 | # Copyright (c) 2018, Intel Corporation. |
| 4 | # |
| 5 | # This program is free software; you can redistribute it and/or modify it |
| 6 | # under the terms and conditions of the GNU General Public License, |
| 7 | # version 2, as published by the Free Software Foundation. |
| 8 | # |
| 9 | # This program is distributed in the hope it will be useful, but WITHOUT |
| 10 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 12 | # more details. |
| 13 | # |
| 14 | import argparse |
| 15 | import json |
| 16 | import os |
| 17 | import sys |
| 18 | import datetime |
| 19 | import re |
| 20 | from oeqa.core.runner import OETestResultJSONHelper |
| 21 | |
| 22 | |
| 23 | def load_json_file(file): |
| 24 | with open(file, "r") as f: |
| 25 | return json.load(f) |
| 26 | |
Brad Bishop | d89cb5f | 2019-04-10 09:02:41 -0400 | [diff] [blame^] | 27 | def write_json_file(f, json_data): |
| 28 | os.makedirs(os.path.dirname(f), exist_ok=True) |
| 29 | with open(f, 'w') as filedata: |
| 30 | filedata.write(json.dumps(json_data, sort_keys=True, indent=4)) |
| 31 | |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 32 | class ManualTestRunner(object): |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 33 | |
| 34 | def _get_testcases(self, file): |
| 35 | self.jdata = load_json_file(file) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 36 | self.test_module = self.jdata[0]['test']['@alias'].split('.', 2)[0] |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 37 | |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 38 | def _get_input(self, config): |
| 39 | while True: |
| 40 | output = input('{} = '.format(config)) |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 41 | if re.match('^[a-z0-9-.]+$', output): |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 42 | break |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 43 | print('Only lowercase alphanumeric, hyphen and dot are allowed. Please try again') |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 44 | return output |
| 45 | |
Brad Bishop | d89cb5f | 2019-04-10 09:02:41 -0400 | [diff] [blame^] | 46 | def _get_available_config_options(self, config_options, test_module, target_config): |
| 47 | avail_config_options = None |
| 48 | if test_module in config_options: |
| 49 | avail_config_options = config_options[test_module].get(target_config) |
| 50 | return avail_config_options |
| 51 | |
| 52 | def _choose_config_option(self, options): |
| 53 | while True: |
| 54 | output = input('{} = '.format('Option index number')) |
| 55 | if output in options: |
| 56 | break |
| 57 | print('Only integer index inputs from above available configuration options are allowed. Please try again.') |
| 58 | return options[output] |
| 59 | |
| 60 | def _create_config(self, config_options): |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 61 | from oeqa.utils.metadata import get_layers |
| 62 | from oeqa.utils.commands import get_bb_var |
| 63 | from resulttool.resultutils import store_map |
| 64 | |
| 65 | layers = get_layers(get_bb_var('BBLAYERS')) |
| 66 | self.configuration = {} |
| 67 | self.configuration['LAYERS'] = layers |
| 68 | current_datetime = datetime.datetime.now() |
| 69 | self.starttime = current_datetime.strftime('%Y%m%d%H%M%S') |
| 70 | self.configuration['STARTTIME'] = self.starttime |
| 71 | self.configuration['TEST_TYPE'] = 'manual' |
| 72 | self.configuration['TEST_MODULE'] = self.test_module |
| 73 | |
| 74 | extra_config = set(store_map['manual']) - set(self.configuration) |
| 75 | for config in sorted(extra_config): |
Brad Bishop | d89cb5f | 2019-04-10 09:02:41 -0400 | [diff] [blame^] | 76 | avail_config_options = self._get_available_config_options(config_options, self.test_module, config) |
| 77 | if avail_config_options: |
| 78 | print('---------------------------------------------') |
| 79 | print('These are available configuration #%s options:' % config) |
| 80 | print('---------------------------------------------') |
| 81 | for option, _ in sorted(avail_config_options.items(), key=lambda x: int(x[0])): |
| 82 | print('%s: %s' % (option, avail_config_options[option])) |
| 83 | print('Please select configuration option, enter the integer index number.') |
| 84 | value_conf = self._choose_config_option(avail_config_options) |
| 85 | print('---------------------------------------------\n') |
| 86 | else: |
| 87 | print('---------------------------------------------') |
| 88 | print('This is configuration #%s. Please provide configuration value(use "None" if not applicable).' % config) |
| 89 | print('---------------------------------------------') |
| 90 | value_conf = self._get_input('Configuration Value') |
| 91 | print('---------------------------------------------\n') |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 92 | self.configuration[config] = value_conf |
| 93 | |
| 94 | def _create_result_id(self): |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 95 | self.result_id = 'manual_%s_%s' % (self.test_module, self.starttime) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 96 | |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 97 | def _execute_test_steps(self, test): |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 98 | test_result = {} |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 99 | print('------------------------------------------------------------------------') |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 100 | print('Executing test case: %s' % test['test']['@alias']) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 101 | print('------------------------------------------------------------------------') |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 102 | print('You have total %s test steps to be executed.' % len(test['test']['execution'])) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 103 | print('------------------------------------------------------------------------\n') |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 104 | for step, _ in sorted(test['test']['execution'].items(), key=lambda x: int(x[0])): |
| 105 | print('Step %s: %s' % (step, test['test']['execution'][step]['action'])) |
| 106 | expected_output = test['test']['execution'][step]['expected_results'] |
| 107 | if expected_output: |
| 108 | print('Expected output: %s' % expected_output) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 109 | while True: |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 110 | done = input('\nPlease provide test results: (P)assed/(F)ailed/(B)locked/(S)kipped? \n').lower() |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 111 | result_types = {'p':'PASSED', |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 112 | 'f':'FAILED', |
| 113 | 'b':'BLOCKED', |
| 114 | 's':'SKIPPED'} |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 115 | if done in result_types: |
| 116 | for r in result_types: |
| 117 | if done == r: |
| 118 | res = result_types[r] |
| 119 | if res == 'FAILED': |
| 120 | log_input = input('\nPlease enter the error and the description of the log: (Ex:log:211 Error Bitbake)\n') |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 121 | test_result.update({test['test']['@alias']: {'status': '%s' % res, 'log': '%s' % log_input}}) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 122 | else: |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 123 | test_result.update({test['test']['@alias']: {'status': '%s' % res}}) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 124 | break |
| 125 | print('Invalid input!') |
| 126 | return test_result |
| 127 | |
| 128 | def _create_write_dir(self): |
| 129 | basepath = os.environ['BUILDDIR'] |
| 130 | self.write_dir = basepath + '/tmp/log/manual/' |
| 131 | |
Brad Bishop | d89cb5f | 2019-04-10 09:02:41 -0400 | [diff] [blame^] | 132 | def run_test(self, file, config_options_file): |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 133 | self._get_testcases(file) |
Brad Bishop | d89cb5f | 2019-04-10 09:02:41 -0400 | [diff] [blame^] | 134 | config_options = {} |
| 135 | if config_options_file: |
| 136 | config_options = load_json_file(config_options_file) |
| 137 | self._create_config(config_options) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 138 | self._create_result_id() |
| 139 | self._create_write_dir() |
| 140 | test_results = {} |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 141 | print('\nTotal number of test cases in this test suite: %s\n' % len(self.jdata)) |
| 142 | for t in self.jdata: |
| 143 | test_result = self._execute_test_steps(t) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 144 | test_results.update(test_result) |
| 145 | return self.configuration, self.result_id, self.write_dir, test_results |
| 146 | |
Brad Bishop | d89cb5f | 2019-04-10 09:02:41 -0400 | [diff] [blame^] | 147 | def _get_true_false_input(self, input_message): |
| 148 | yes_list = ['Y', 'YES'] |
| 149 | no_list = ['N', 'NO'] |
| 150 | while True: |
| 151 | more_config_option = input(input_message).upper() |
| 152 | if more_config_option in yes_list or more_config_option in no_list: |
| 153 | break |
| 154 | print('Invalid input!') |
| 155 | if more_config_option in no_list: |
| 156 | return False |
| 157 | return True |
| 158 | |
| 159 | def make_config_option_file(self, logger, manual_case_file, config_options_file): |
| 160 | config_options = {} |
| 161 | if config_options_file: |
| 162 | config_options = load_json_file(config_options_file) |
| 163 | new_test_module = os.path.basename(manual_case_file).split('.')[0] |
| 164 | print('Creating configuration options file for test module: %s' % new_test_module) |
| 165 | new_config_options = {} |
| 166 | |
| 167 | while True: |
| 168 | config_name = input('\nPlease provide test configuration to create:\n').upper() |
| 169 | new_config_options[config_name] = {} |
| 170 | while True: |
| 171 | config_value = self._get_input('Configuration possible option value') |
| 172 | config_option_index = len(new_config_options[config_name]) + 1 |
| 173 | new_config_options[config_name][config_option_index] = config_value |
| 174 | more_config_option = self._get_true_false_input('\nIs there more configuration option input: (Y)es/(N)o\n') |
| 175 | if not more_config_option: |
| 176 | break |
| 177 | more_config = self._get_true_false_input('\nIs there more configuration to create: (Y)es/(N)o\n') |
| 178 | if not more_config: |
| 179 | break |
| 180 | |
| 181 | if new_config_options: |
| 182 | config_options[new_test_module] = new_config_options |
| 183 | if not config_options_file: |
| 184 | self._create_write_dir() |
| 185 | config_options_file = os.path.join(self.write_dir, 'manual_config_options.json') |
| 186 | write_json_file(config_options_file, config_options) |
| 187 | logger.info('Configuration option file created at %s' % config_options_file) |
| 188 | |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 189 | def manualexecution(args, logger): |
| 190 | testrunner = ManualTestRunner() |
Brad Bishop | d89cb5f | 2019-04-10 09:02:41 -0400 | [diff] [blame^] | 191 | if args.make_config_options_file: |
| 192 | testrunner.make_config_option_file(logger, args.file, args.config_options_file) |
| 193 | return 0 |
| 194 | get_configuration, get_result_id, get_write_dir, get_test_results = testrunner.run_test(args.file, args.config_options_file) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 195 | resultjsonhelper = OETestResultJSONHelper() |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 196 | resultjsonhelper.dump_testresult_file(get_write_dir, get_configuration, get_result_id, get_test_results) |
Brad Bishop | 40320b1 | 2019-03-26 16:08:25 -0400 | [diff] [blame] | 197 | return 0 |
| 198 | |
| 199 | def register_commands(subparsers): |
| 200 | """Register subcommands from this plugin""" |
| 201 | parser_build = subparsers.add_parser('manualexecution', help='helper script for results populating during manual test execution.', |
| 202 | description='helper script for results populating during manual test execution. You can find manual test case JSON file in meta/lib/oeqa/manual/', |
| 203 | group='manualexecution') |
| 204 | parser_build.set_defaults(func=manualexecution) |
| 205 | parser_build.add_argument('file', help='specify path to manual test case JSON file.Note: Please use \"\" to encapsulate the file path.') |
Brad Bishop | d89cb5f | 2019-04-10 09:02:41 -0400 | [diff] [blame^] | 206 | parser_build.add_argument('-c', '--config-options-file', default='', |
| 207 | help='the config options file to import and used as available configuration option selection or make config option file') |
| 208 | parser_build.add_argument('-m', '--make-config-options-file', action='store_true', |
| 209 | help='make the configuration options file based on provided inputs') |