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