xref: /openbmc/openbmc/poky/meta/lib/oeqa/core/context.py (revision fc113ead)
1c342db35SBrad Bishop## Copyright (C) 2016 Intel Corporation
2c342db35SBrad Bishop#
3c342db35SBrad Bishop# SPDX-License-Identifier: MIT
4c342db35SBrad Bishop#
5eb8dc403SDave Cobbley
6eb8dc403SDave Cobbleyimport os
7eb8dc403SDave Cobbleyimport sys
8eb8dc403SDave Cobbleyimport json
9eb8dc403SDave Cobbleyimport time
10eb8dc403SDave Cobbleyimport logging
11eb8dc403SDave Cobbleyimport collections
1215ae2509SBrad Bishopimport unittest
13eb8dc403SDave Cobbley
14eb8dc403SDave Cobbleyfrom oeqa.core.loader import OETestLoader
15eb8dc403SDave Cobbleyfrom oeqa.core.runner import OETestRunner
16eb8dc403SDave Cobbleyfrom oeqa.core.exception import OEQAMissingManifest, OEQATestNotFound
17eb8dc403SDave Cobbley
18eb8dc403SDave Cobbleyclass OETestContext(object):
19eb8dc403SDave Cobbley    loaderClass = OETestLoader
20eb8dc403SDave Cobbley    runnerClass = OETestRunner
21eb8dc403SDave Cobbley
22eb8dc403SDave Cobbley    files_dir = os.path.abspath(os.path.join(os.path.dirname(
23eb8dc403SDave Cobbley        os.path.abspath(__file__)), "../files"))
24eb8dc403SDave Cobbley
25eb8dc403SDave Cobbley    def __init__(self, td=None, logger=None):
26eb8dc403SDave Cobbley        if not type(td) is dict:
27eb8dc403SDave Cobbley            raise TypeError("td isn't dictionary type")
28eb8dc403SDave Cobbley
29eb8dc403SDave Cobbley        self.td = td
30eb8dc403SDave Cobbley        self.logger = logger
31eb8dc403SDave Cobbley        self._registry = {}
32eb8dc403SDave Cobbley        self._registry['cases'] = collections.OrderedDict()
33eb8dc403SDave Cobbley
346ce62a20SAndrew Geissler        self.results = unittest.TestResult()
356ce62a20SAndrew Geissler        unittest.registerResult(self.results)
366ce62a20SAndrew Geissler
37eb8dc403SDave Cobbley    def _read_modules_from_manifest(self, manifest):
38eb8dc403SDave Cobbley        if not os.path.exists(manifest):
39eb8dc403SDave Cobbley            raise OEQAMissingManifest("Manifest does not exist on %s" % manifest)
40eb8dc403SDave Cobbley
41eb8dc403SDave Cobbley        modules = []
42eb8dc403SDave Cobbley        for line in open(manifest).readlines():
43eb8dc403SDave Cobbley            line = line.strip()
44eb8dc403SDave Cobbley            if line and not line.startswith("#"):
45eb8dc403SDave Cobbley                modules.append(line)
46eb8dc403SDave Cobbley
47eb8dc403SDave Cobbley        return modules
48eb8dc403SDave Cobbley
49eb8dc403SDave Cobbley    def skipTests(self, skips):
50eb8dc403SDave Cobbley        if not skips:
51eb8dc403SDave Cobbley            return
5215ae2509SBrad Bishop        def skipfuncgen(skipmsg):
5315ae2509SBrad Bishop            def func():
5415ae2509SBrad Bishop                raise unittest.SkipTest(skipmsg)
5515ae2509SBrad Bishop            return func
56c8f47128SBrad Bishop        class_ids = {}
57eb8dc403SDave Cobbley        for test in self.suites:
58c8f47128SBrad Bishop            if test.__class__ not in class_ids:
59c8f47128SBrad Bishop                class_ids[test.__class__] = '.'.join(test.id().split('.')[:-1])
60eb8dc403SDave Cobbley            for skip in skips:
61c8f47128SBrad Bishop                if (test.id()+'.').startswith(skip+'.'):
6215ae2509SBrad Bishop                    setattr(test, 'setUp', skipfuncgen('Skip by the command line argument "%s"' % skip))
63c8f47128SBrad Bishop        for tclass in class_ids:
64c8f47128SBrad Bishop            cid = class_ids[tclass]
65c8f47128SBrad Bishop            for skip in skips:
66c8f47128SBrad Bishop                if (cid + '.').startswith(skip + '.'):
67c8f47128SBrad Bishop                    setattr(tclass, 'setUpHooker', skipfuncgen('Skip by the command line argument "%s"' % skip))
68eb8dc403SDave Cobbley
69eb8dc403SDave Cobbley    def loadTests(self, module_paths, modules=[], tests=[],
7079641f25SBrad Bishop            modules_manifest="", modules_required=[], **kwargs):
71eb8dc403SDave Cobbley        if modules_manifest:
72eb8dc403SDave Cobbley            modules = self._read_modules_from_manifest(modules_manifest)
73eb8dc403SDave Cobbley
74eb8dc403SDave Cobbley        self.loader = self.loaderClass(self, module_paths, modules, tests,
7579641f25SBrad Bishop                modules_required, **kwargs)
76eb8dc403SDave Cobbley        self.suites = self.loader.discover()
77eb8dc403SDave Cobbley
7882c905dcSAndrew Geissler    def prepareSuite(self, suites, processes):
7982c905dcSAndrew Geissler        return suites
8082c905dcSAndrew Geissler
811a4b7ee2SBrad Bishop    def runTests(self, processes=None, skips=[]):
82eb8dc403SDave Cobbley        self.runner = self.runnerClass(self, descriptions=False, verbosity=2)
83eb8dc403SDave Cobbley
84*fc113eadSAndrew Geissler        # Dynamically skip those tests specified though arguments
85eb8dc403SDave Cobbley        self.skipTests(skips)
86eb8dc403SDave Cobbley
87eb8dc403SDave Cobbley        self._run_start_time = time.time()
886ce62a20SAndrew Geissler        self._run_end_time = self._run_start_time
8982c905dcSAndrew Geissler        if not processes:
901a4b7ee2SBrad Bishop            self.runner.buffer = True
9182c905dcSAndrew Geissler        result = self.runner.run(self.prepareSuite(self.suites, processes))
92eb8dc403SDave Cobbley        self._run_end_time = time.time()
93eb8dc403SDave Cobbley
94eb8dc403SDave Cobbley        return result
95eb8dc403SDave Cobbley
96eb8dc403SDave Cobbley    def listTests(self, display_type):
97eb8dc403SDave Cobbley        self.runner = self.runnerClass(self, verbosity=2)
98eb8dc403SDave Cobbley        return self.runner.list_tests(self.suites, display_type)
99eb8dc403SDave Cobbley
100eb8dc403SDave Cobbleyclass OETestContextExecutor(object):
101eb8dc403SDave Cobbley    _context_class = OETestContext
102eb8dc403SDave Cobbley    _script_executor = 'oe-test'
103eb8dc403SDave Cobbley
104eb8dc403SDave Cobbley    name = 'core'
105eb8dc403SDave Cobbley    help = 'core test component example'
106eb8dc403SDave Cobbley    description = 'executes core test suite example'
10782c905dcSAndrew Geissler    datetime = time.strftime("%Y%m%d%H%M%S")
108eb8dc403SDave Cobbley
109eb8dc403SDave Cobbley    default_cases = [os.path.join(os.path.abspath(os.path.dirname(__file__)),
110eb8dc403SDave Cobbley            'cases/example')]
111eb8dc403SDave Cobbley    default_test_data = os.path.join(default_cases[0], 'data.json')
112eb8dc403SDave Cobbley    default_tests = None
11382c905dcSAndrew Geissler    default_json_result_dir = None
114eb8dc403SDave Cobbley
115eb8dc403SDave Cobbley    def register_commands(self, logger, subparsers):
116eb8dc403SDave Cobbley        self.parser = subparsers.add_parser(self.name, help=self.help,
117eb8dc403SDave Cobbley                description=self.description, group='components')
118eb8dc403SDave Cobbley
11982c905dcSAndrew Geissler        self.default_output_log = '%s-results-%s.log' % (self.name, self.datetime)
120eb8dc403SDave Cobbley        self.parser.add_argument('--output-log', action='store',
121eb8dc403SDave Cobbley                default=self.default_output_log,
122eb8dc403SDave Cobbley                help="results output log, default: %s" % self.default_output_log)
123eb8dc403SDave Cobbley
12482c905dcSAndrew Geissler        self.parser.add_argument('--json-result-dir', action='store',
12582c905dcSAndrew Geissler                default=self.default_json_result_dir,
12682c905dcSAndrew Geissler                help="json result output dir, default: %s" % self.default_json_result_dir)
12782c905dcSAndrew Geissler
128eb8dc403SDave Cobbley        group = self.parser.add_mutually_exclusive_group()
129eb8dc403SDave Cobbley        group.add_argument('--run-tests', action='store', nargs='+',
130eb8dc403SDave Cobbley                default=self.default_tests,
131eb8dc403SDave Cobbley                help="tests to run in <module>[.<class>[.<name>]]")
132eb8dc403SDave Cobbley        group.add_argument('--list-tests', action='store',
133eb8dc403SDave Cobbley                choices=('module', 'class', 'name'),
134eb8dc403SDave Cobbley                help="lists available tests")
135eb8dc403SDave Cobbley
136eb8dc403SDave Cobbley        if self.default_test_data:
137eb8dc403SDave Cobbley            self.parser.add_argument('--test-data-file', action='store',
138eb8dc403SDave Cobbley                    default=self.default_test_data,
139eb8dc403SDave Cobbley                    help="data file to load, default: %s" % self.default_test_data)
140eb8dc403SDave Cobbley        else:
141eb8dc403SDave Cobbley            self.parser.add_argument('--test-data-file', action='store',
142eb8dc403SDave Cobbley                    help="data file to load")
143eb8dc403SDave Cobbley
144eb8dc403SDave Cobbley        if self.default_cases:
145eb8dc403SDave Cobbley            self.parser.add_argument('CASES_PATHS', action='store',
146eb8dc403SDave Cobbley                    default=self.default_cases, nargs='*',
147eb8dc403SDave Cobbley                    help="paths to directories with test cases, default: %s"\
148eb8dc403SDave Cobbley                            % self.default_cases)
149eb8dc403SDave Cobbley        else:
150eb8dc403SDave Cobbley            self.parser.add_argument('CASES_PATHS', action='store',
151eb8dc403SDave Cobbley                    nargs='+', help="paths to directories with test cases")
152eb8dc403SDave Cobbley
153eb8dc403SDave Cobbley        self.parser.set_defaults(func=self.run)
154eb8dc403SDave Cobbley
155eb8dc403SDave Cobbley    def _setup_logger(self, logger, args):
156eb8dc403SDave Cobbley        formatter = logging.Formatter('%(asctime)s - ' + self.name + \
157eb8dc403SDave Cobbley                ' - %(levelname)s - %(message)s')
158eb8dc403SDave Cobbley        sh = logger.handlers[0]
159eb8dc403SDave Cobbley        sh.setFormatter(formatter)
160eb8dc403SDave Cobbley        fh = logging.FileHandler(args.output_log)
161eb8dc403SDave Cobbley        fh.setFormatter(formatter)
162eb8dc403SDave Cobbley        logger.addHandler(fh)
1634ed12e16SAndrew Geissler        if getattr(args, 'verbose', False):
1644ed12e16SAndrew Geissler            logger.setLevel('DEBUG')
165eb8dc403SDave Cobbley
166eb8dc403SDave Cobbley        return logger
167eb8dc403SDave Cobbley
168eb8dc403SDave Cobbley    def _process_args(self, logger, args):
169eb8dc403SDave Cobbley        self.tc_kwargs = {}
170eb8dc403SDave Cobbley        self.tc_kwargs['init'] = {}
171eb8dc403SDave Cobbley        self.tc_kwargs['load'] = {}
172eb8dc403SDave Cobbley        self.tc_kwargs['list'] = {}
173eb8dc403SDave Cobbley        self.tc_kwargs['run']  = {}
174eb8dc403SDave Cobbley
175eb8dc403SDave Cobbley        self.tc_kwargs['init']['logger'] = self._setup_logger(logger, args)
176eb8dc403SDave Cobbley        if args.test_data_file:
177eb8dc403SDave Cobbley            self.tc_kwargs['init']['td'] = json.load(
178eb8dc403SDave Cobbley                    open(args.test_data_file, "r"))
179eb8dc403SDave Cobbley        else:
180eb8dc403SDave Cobbley            self.tc_kwargs['init']['td'] = {}
181eb8dc403SDave Cobbley
182eb8dc403SDave Cobbley        if args.run_tests:
183eb8dc403SDave Cobbley            self.tc_kwargs['load']['modules'] = args.run_tests
184eb8dc403SDave Cobbley            self.tc_kwargs['load']['modules_required'] = args.run_tests
185eb8dc403SDave Cobbley        else:
186eb8dc403SDave Cobbley            self.tc_kwargs['load']['modules'] = []
187eb8dc403SDave Cobbley
188eb8dc403SDave Cobbley        self.tc_kwargs['run']['skips'] = []
189eb8dc403SDave Cobbley
190eb8dc403SDave Cobbley        self.module_paths = args.CASES_PATHS
191eb8dc403SDave Cobbley
19282c905dcSAndrew Geissler    def _get_json_result_dir(self, args):
19382c905dcSAndrew Geissler        return args.json_result_dir
19482c905dcSAndrew Geissler
19582c905dcSAndrew Geissler    def _get_configuration(self):
19682c905dcSAndrew Geissler        td = self.tc_kwargs['init']['td']
19782c905dcSAndrew Geissler        configuration = {'TEST_TYPE': self.name,
19882c905dcSAndrew Geissler                        'MACHINE': td.get("MACHINE"),
19982c905dcSAndrew Geissler                        'DISTRO': td.get("DISTRO"),
20082c905dcSAndrew Geissler                        'IMAGE_BASENAME': td.get("IMAGE_BASENAME"),
20182c905dcSAndrew Geissler                        'DATETIME': td.get("DATETIME")}
20282c905dcSAndrew Geissler        return configuration
20382c905dcSAndrew Geissler
20482c905dcSAndrew Geissler    def _get_result_id(self, configuration):
20582c905dcSAndrew Geissler        return '%s_%s_%s_%s' % (configuration['TEST_TYPE'], configuration['IMAGE_BASENAME'],
20682c905dcSAndrew Geissler                                configuration['MACHINE'], self.datetime)
20782c905dcSAndrew Geissler
208eb8dc403SDave Cobbley    def _pre_run(self):
209eb8dc403SDave Cobbley        pass
210eb8dc403SDave Cobbley
211eb8dc403SDave Cobbley    def run(self, logger, args):
212eb8dc403SDave Cobbley        self._process_args(logger, args)
213eb8dc403SDave Cobbley
214eb8dc403SDave Cobbley        self.tc = self._context_class(**self.tc_kwargs['init'])
215eb8dc403SDave Cobbley        try:
216eb8dc403SDave Cobbley            self.tc.loadTests(self.module_paths, **self.tc_kwargs['load'])
217eb8dc403SDave Cobbley        except OEQATestNotFound as ex:
218eb8dc403SDave Cobbley            logger.error(ex)
219eb8dc403SDave Cobbley            sys.exit(1)
220eb8dc403SDave Cobbley
221eb8dc403SDave Cobbley        if args.list_tests:
222eb8dc403SDave Cobbley            rc = self.tc.listTests(args.list_tests, **self.tc_kwargs['list'])
223eb8dc403SDave Cobbley        else:
224eb8dc403SDave Cobbley            self._pre_run()
225eb8dc403SDave Cobbley            rc = self.tc.runTests(**self.tc_kwargs['run'])
22682c905dcSAndrew Geissler
22782c905dcSAndrew Geissler            json_result_dir = self._get_json_result_dir(args)
22882c905dcSAndrew Geissler            if json_result_dir:
22982c905dcSAndrew Geissler                configuration = self._get_configuration()
23082c905dcSAndrew Geissler                rc.logDetails(json_result_dir,
23182c905dcSAndrew Geissler                              configuration,
23282c905dcSAndrew Geissler                              self._get_result_id(configuration))
23382c905dcSAndrew Geissler            else:
234eb8dc403SDave Cobbley                rc.logDetails()
23582c905dcSAndrew Geissler
236eb8dc403SDave Cobbley            rc.logSummary(self.name)
237eb8dc403SDave Cobbley
238eb8dc403SDave Cobbley        output_link = os.path.join(os.path.dirname(args.output_log),
239eb8dc403SDave Cobbley                "%s-results.log" % self.name)
240eb8dc403SDave Cobbley        if os.path.exists(output_link):
241eb8dc403SDave Cobbley            os.remove(output_link)
242eb8dc403SDave Cobbley        os.symlink(args.output_log, output_link)
243eb8dc403SDave Cobbley
244eb8dc403SDave Cobbley        return rc
245eb8dc403SDave Cobbley
246eb8dc403SDave Cobbley_executor_class = OETestContextExecutor
247