manualexecution.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. import copy
  21. from oeqa.core.runner import OETestResultJSONHelper
  22. def load_json_file(f):
  23. with open(f, "r") as filedata:
  24. return json.load(filedata)
  25. def write_json_file(f, json_data):
  26. os.makedirs(os.path.dirname(f), exist_ok=True)
  27. with open(f, 'w') as filedata:
  28. filedata.write(json.dumps(json_data, sort_keys=True, indent=4))
  29. class ManualTestRunner(object):
  30. def _get_test_module(self, case_file):
  31. return os.path.basename(case_file).split('.')[0]
  32. def _get_input(self, config):
  33. while True:
  34. output = input('{} = '.format(config))
  35. if re.match('^[a-z0-9-.]+$', output):
  36. break
  37. print('Only lowercase alphanumeric, hyphen and dot are allowed. Please try again')
  38. return output
  39. def _get_available_config_options(self, config_options, test_module, target_config):
  40. avail_config_options = None
  41. if test_module in config_options:
  42. avail_config_options = config_options[test_module].get(target_config)
  43. return avail_config_options
  44. def _choose_config_option(self, options):
  45. while True:
  46. output = input('{} = '.format('Option index number'))
  47. if output in options:
  48. break
  49. print('Only integer index inputs from above available configuration options are allowed. Please try again.')
  50. return options[output]
  51. def _get_config(self, config_options, test_module):
  52. from oeqa.utils.metadata import get_layers
  53. from oeqa.utils.commands import get_bb_var
  54. from resulttool.resultutils import store_map
  55. layers = get_layers(get_bb_var('BBLAYERS'))
  56. configurations = {}
  57. configurations['LAYERS'] = layers
  58. configurations['STARTTIME'] = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
  59. configurations['TEST_TYPE'] = 'manual'
  60. configurations['TEST_MODULE'] = test_module
  61. extra_config = set(store_map['manual']) - set(configurations)
  62. for config in sorted(extra_config):
  63. avail_config_options = self._get_available_config_options(config_options, test_module, config)
  64. if avail_config_options:
  65. print('---------------------------------------------')
  66. print('These are available configuration #%s options:' % config)
  67. print('---------------------------------------------')
  68. for option, _ in sorted(avail_config_options.items(), key=lambda x: int(x[0])):
  69. print('%s: %s' % (option, avail_config_options[option]))
  70. print('Please select configuration option, enter the integer index number.')
  71. value_conf = self._choose_config_option(avail_config_options)
  72. print('---------------------------------------------\n')
  73. else:
  74. print('---------------------------------------------')
  75. print('This is configuration #%s. Please provide configuration value(use "None" if not applicable).' % config)
  76. print('---------------------------------------------')
  77. value_conf = self._get_input('Configuration Value')
  78. print('---------------------------------------------\n')
  79. configurations[config] = value_conf
  80. return configurations
  81. def _execute_test_steps(self, case):
  82. test_result = {}
  83. print('------------------------------------------------------------------------')
  84. print('Executing test case: %s' % case['test']['@alias'])
  85. print('------------------------------------------------------------------------')
  86. print('You have total %s test steps to be executed.' % len(case['test']['execution']))
  87. print('------------------------------------------------------------------------\n')
  88. for step, _ in sorted(case['test']['execution'].items(), key=lambda x: int(x[0])):
  89. print('Step %s: %s' % (step, case['test']['execution'][step]['action']))
  90. expected_output = case['test']['execution'][step]['expected_results']
  91. if expected_output:
  92. print('Expected output: %s' % expected_output)
  93. while True:
  94. done = input('\nPlease provide test results: (P)assed/(F)ailed/(B)locked/(S)kipped? \n').lower()
  95. result_types = {'p':'PASSED',
  96. 'f':'FAILED',
  97. 'b':'BLOCKED',
  98. 's':'SKIPPED'}
  99. if done in result_types:
  100. for r in result_types:
  101. if done == r:
  102. res = result_types[r]
  103. if res == 'FAILED':
  104. log_input = input('\nPlease enter the error and the description of the log: (Ex:log:211 Error Bitbake)\n')
  105. test_result.update({case['test']['@alias']: {'status': '%s' % res, 'log': '%s' % log_input}})
  106. else:
  107. test_result.update({case['test']['@alias']: {'status': '%s' % res}})
  108. break
  109. print('Invalid input!')
  110. return test_result
  111. def _get_write_dir(self):
  112. return os.environ['BUILDDIR'] + '/tmp/log/manual/'
  113. def run_test(self, case_file, config_options_file, testcase_config_file):
  114. test_module = self._get_test_module(case_file)
  115. cases = load_json_file(case_file)
  116. config_options = {}
  117. if config_options_file:
  118. config_options = load_json_file(config_options_file)
  119. configurations = self._get_config(config_options, test_module)
  120. result_id = 'manual_%s_%s' % (test_module, configurations['STARTTIME'])
  121. test_results = {}
  122. if testcase_config_file:
  123. test_case_config = load_json_file(testcase_config_file)
  124. test_case_to_execute = test_case_config['testcases']
  125. for case in copy.deepcopy(cases) :
  126. if case['test']['@alias'] not in test_case_to_execute:
  127. cases.remove(case)
  128. print('\nTotal number of test cases in this test suite: %s\n' % len(cases))
  129. for c in cases:
  130. test_result = self._execute_test_steps(c)
  131. test_results.update(test_result)
  132. return configurations, result_id, self._get_write_dir(), test_results
  133. def _get_true_false_input(self, input_message):
  134. yes_list = ['Y', 'YES']
  135. no_list = ['N', 'NO']
  136. while True:
  137. more_config_option = input(input_message).upper()
  138. if more_config_option in yes_list or more_config_option in no_list:
  139. break
  140. print('Invalid input!')
  141. if more_config_option in no_list:
  142. return False
  143. return True
  144. def make_config_option_file(self, logger, case_file, config_options_file):
  145. config_options = {}
  146. if config_options_file:
  147. config_options = load_json_file(config_options_file)
  148. new_test_module = self._get_test_module(case_file)
  149. print('Creating configuration options file for test module: %s' % new_test_module)
  150. new_config_options = {}
  151. while True:
  152. config_name = input('\nPlease provide test configuration to create:\n').upper()
  153. new_config_options[config_name] = {}
  154. while True:
  155. config_value = self._get_input('Configuration possible option value')
  156. config_option_index = len(new_config_options[config_name]) + 1
  157. new_config_options[config_name][config_option_index] = config_value
  158. more_config_option = self._get_true_false_input('\nIs there more configuration option input: (Y)es/(N)o\n')
  159. if not more_config_option:
  160. break
  161. more_config = self._get_true_false_input('\nIs there more configuration to create: (Y)es/(N)o\n')
  162. if not more_config:
  163. break
  164. if new_config_options:
  165. config_options[new_test_module] = new_config_options
  166. if not config_options_file:
  167. config_options_file = os.path.join(self._get_write_dir(), 'manual_config_options.json')
  168. write_json_file(config_options_file, config_options)
  169. logger.info('Configuration option file created at %s' % config_options_file)
  170. def manualexecution(args, logger):
  171. testrunner = ManualTestRunner()
  172. if args.make_config_options_file:
  173. testrunner.make_config_option_file(logger, args.file, args.config_options_file)
  174. return 0
  175. configurations, result_id, write_dir, test_results = testrunner.run_test(args.file, args.config_options_file, args.testcase_config_file)
  176. resultjsonhelper = OETestResultJSONHelper()
  177. resultjsonhelper.dump_testresult_file(write_dir, configurations, result_id, test_results)
  178. return 0
  179. def register_commands(subparsers):
  180. """Register subcommands from this plugin"""
  181. parser_build = subparsers.add_parser('manualexecution', help='helper script for results populating during manual test execution.',
  182. description='helper script for results populating during manual test execution. You can find manual test case JSON file in meta/lib/oeqa/manual/',
  183. group='manualexecution')
  184. parser_build.set_defaults(func=manualexecution)
  185. parser_build.add_argument('file', help='specify path to manual test case JSON file.Note: Please use \"\" to encapsulate the file path.')
  186. parser_build.add_argument('-c', '--config-options-file', default='',
  187. help='the config options file to import and used as available configuration option selection or make config option file')
  188. parser_build.add_argument('-m', '--make-config-options-file', action='store_true',
  189. help='make the configuration options file based on provided inputs')
  190. parser_build.add_argument('-t', '--testcase-config-file', default='',
  191. help='the testcase configuration file to enable user to run a selected set of test case')