From 3795907d5e3ebe816bae402450493f6f983e277e Mon Sep 17 00:00:00 2001 From: Tibor Frank Date: Mon, 24 Feb 2020 14:49:00 +0100 Subject: PAL: Process local xml file Change-Id: I2d60b2c6616de6d6b2e1d742407ac392471883aa Signed-off-by: Tibor Frank (cherry picked from commit 0c3e94ed3f70c9dcd41e55ba2c043dd134aa7333) --- resources/tools/presentation/input_data_parser.py | 129 ++++++++++++++++++++-- 1 file changed, 119 insertions(+), 10 deletions(-) (limited to 'resources/tools/presentation/input_data_parser.py') diff --git a/resources/tools/presentation/input_data_parser.py b/resources/tools/presentation/input_data_parser.py index c43d46014e..e14d8d4d66 100644 --- a/resources/tools/presentation/input_data_parser.py +++ b/resources/tools/presentation/input_data_parser.py @@ -25,7 +25,8 @@ import resource import logging from collections import OrderedDict -from os import remove +from os import remove, walk, listdir +from os.path import isfile, isdir, join from datetime import datetime as dt from datetime import timedelta from json import loads @@ -41,6 +42,7 @@ from robot import errors from resources.libraries.python import jumpavg from input_data_files import download_and_unzip_data_file +from pal_errors import PresentationError # Separator used in file names @@ -1304,7 +1306,6 @@ class ExecutionChecker(ResultVisitor): :type msg: Message :returns: Nothing. """ - if self._msg_type: self.parse_msg[self._msg_type](msg) @@ -1366,7 +1367,6 @@ class InputData: :returns: Metadata :rtype: pandas.Series """ - return self.data[job][build][u"metadata"] def suites(self, job, build): @@ -1379,7 +1379,6 @@ class InputData: :returns: Suites. :rtype: pandas.Series """ - return self.data[job][str(build)][u"suites"] def tests(self, job, build): @@ -1392,7 +1391,6 @@ class InputData: :returns: Tests. :rtype: pandas.Series """ - return self.data[job][build][u"tests"] def _parse_tests(self, job, build, log): @@ -1576,6 +1574,122 @@ class InputData: logging.info(u"Done.") + def process_local_file(self, local_file, job=u"local", build_nr=1, + replace=True): + """Process local XML file given as a command-line parameter. + + :param local_file: The file to process. + :param job: Job name. + :param build_nr: Build number. + :param replace: If True, the information about jobs and builds is + replaced by the new one, otherwise the new jobs and builds are + added. + :type local_file: str + :type job: str + :type build_nr: int + :type replace: bool + :raises: PresentationError in an error occurs. + """ + if not isfile(local_file): + raise PresentationError(f"The file {local_file} does not exist.") + + build = { + u"build": build_nr, + u"status": u"failed", + u"file-name": local_file + } + if replace: + self._cfg.builds = dict() + self._cfg.add_build(job, build) + + logging.info(f"Processing {job}: {build_nr:2d}: {local_file}") + data = self._parse_tests(job, build, list()) + if data is None: + raise PresentationError( + f"Error occurred while parsing the file {local_file}" + ) + + build_data = pd.Series({ + u"metadata": pd.Series( + list(data[u"metadata"].values()), + index=list(data[u"metadata"].keys()) + ), + u"suites": pd.Series( + list(data[u"suites"].values()), + index=list(data[u"suites"].keys()) + ), + u"tests": pd.Series( + list(data[u"tests"].values()), + index=list(data[u"tests"].keys()) + ) + }) + + if self._input_data.get(job, None) is None: + self._input_data[job] = pd.Series() + self._input_data[job][str(build_nr)] = build_data + + self._cfg.set_input_state(job, build_nr, u"processed") + + def process_local_directory(self, local_dir, replace=True): + """Process local directory with XML file(s). The directory is processed + as a 'job' and the XML files in in as builds. + If the given directory contains only sub-directories, these + sub-directories processed as jobs and corresponding XML files as builds + of their job. + + :param local_dir: Local directory to process. + :param replace: If True, the information about jobs and builds is + replaced by the new one, otherwise the new jobs and builds are + added. + :type local_dir: str + :type replace: bool + """ + if not isdir(local_dir): + raise PresentationError( + f"The directory {local_dir} does not exist." + ) + + # Check if the given directory includes only files, or only directories + _, dirnames, filenames = next(walk(local_dir)) + + if filenames and not dirnames: + filenames.sort() + # local_builds: + # key: dir (job) name, value: list of file names (builds) + local_builds = { + local_dir: [join(local_dir, name) for name in filenames] + } + + elif dirnames and not filenames: + dirnames.sort() + # local_builds: + # key: dir (job) name, value: list of file names (builds) + local_builds = dict() + for dirname in dirnames: + builds = [ + join(local_dir, dirname, name) + for name in listdir(join(local_dir, dirname)) + if isfile(join(local_dir, dirname, name)) + ] + if builds: + local_builds[dirname] = sorted(builds) + + elif not filenames and not dirnames: + raise PresentationError(f"The directory {local_dir} is empty.") + else: + raise PresentationError( + f"The directory {local_dir} can include only files or only " + f"directories, not both.\nThe directory {local_dir} includes " + f"file(s):\n{filenames}\nand directories:\n{dirnames}" + ) + + if replace: + self._cfg.builds = dict() + + for job, files in local_builds.items(): + for idx, local_file in enumerate(files): + self.process_local_file(local_file, job, idx + 1, replace=False) + @staticmethod def _end_of_tag(tag_filter, start=0, closer=u"'"): """Return the index of character in the string which is the end of tag. @@ -1589,7 +1703,6 @@ class InputData: :returns: The index of the tag closer. :rtype: int """ - try: idx_opener = tag_filter.index(closer, start) return tag_filter.index(closer, idx_opener + 1) @@ -1605,7 +1718,6 @@ class InputData: :returns: Conditional statement which can be evaluated. :rtype: str """ - index = 0 while True: index = InputData._end_of_tag(tag_filter, index) @@ -1619,7 +1731,6 @@ class InputData: """Filter required data from the given jobs and builds. The output data structure is: - - job 1 - build 1 - test (or suite) 1 ID: @@ -1722,7 +1833,6 @@ class InputData: """Filter required data from the given jobs and builds. The output data structure is: - - job 1 - build 1 - test (or suite) 1 ID: @@ -1836,7 +1946,6 @@ class InputData: for item in builds.values: for item_id, item_data in item.items(): merged_data[item_id] = item_data - return merged_data def print_all_oper_data(self): -- cgit 1.2.3-korg