• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2021-2024 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import logging
19import os
20from unittest import TestCase
21
22from pathlib import Path
23from typing import Tuple, Dict, List, Any
24
25from jinja2 import Environment, FileSystemLoader, select_autoescape, TemplateSyntaxError
26
27from runner.plugins.ets.utils.metainformation import InvalidMetaException, find_all_metas
28from runner.plugins.ets.utils.test_parameters import load_params
29from runner.utils import iter_files, write_2_file
30
31from runner.plugins.ets.utils.exceptions import InvalidFileFormatException, UnknownTemplateException
32
33from runner.plugins.ets.utils.constants import \
34    SKIP_PREFIX, \
35    VARIABLE_START_STRING, \
36    TEMPLATE_EXTENSION, OUT_EXTENSION
37
38
39_LOGGER = logging.getLogger("runner.plugins.ets.stdlib_templates.stdlib_templates_generator")
40
41
42class StdlibTemplatesGenerator:
43    def __init__(self, template_root_path: Path) -> None:
44        self.__jinja_env = Environment(
45            loader=FileSystemLoader(str(template_root_path)),
46            autoescape=select_autoescape(),
47            variable_start_string=VARIABLE_START_STRING
48        )
49
50    @staticmethod
51    def __deduplicate_key_content(key: str, key_uniq_suffix: int) -> Tuple[str, int]:
52        key_updated = str(key) + "_" + str(key_uniq_suffix).rjust(3, '0')
53        key_uniq_suffix += 1
54        return key_updated, key_uniq_suffix
55
56    def render_and_write_templates(self, root_path: Path, dirpath: Path, outpath: Path) -> List[str]:
57        """
58        Recursively walk the FS, save rendered templates
59        Loads parameters and renders all templates in `dirpath`.
60        Saves the results in `outpath`
61        """
62        generated_test_list = []
63        os.makedirs(outpath, exist_ok=True)
64        params = load_params(dirpath)
65        for name, path, in iter_files(dirpath, allowed_ext=[TEMPLATE_EXTENSION]):
66            name_without_ext, _ = os.path.splitext(name)
67            if name_without_ext.startswith(SKIP_PREFIX):
68                continue
69            template_relative_path = os.path.relpath(path, root_path)
70            rendered_template = self.__render_template(template_relative_path, params)
71            tests = self.__split_into_tests(rendered_template, path)
72            TestCase().assertTrue(len(tests) > 0, "Internal error: there should be tests")
73            for key, test in tests.items():
74                output_filepath = outpath / f"{name_without_ext}_{key}{OUT_EXTENSION}"
75                write_2_file(file_path=output_filepath, content=test)
76                generated_test_list.append(str(output_filepath))
77        return generated_test_list
78
79    def __render_template(self, filepath: str, params: Any = None) -> str:
80        """
81        Renders a single template and returns result as string
82        """
83        if params is None:
84            params = {}
85        try:
86            template = self.__jinja_env.get_template(filepath.replace(os.path.sep, '/'))
87            return template.render(**params)
88        except TemplateSyntaxError as inv_format_exp:
89            message = f"Template Syntax Error: ${inv_format_exp.message}"
90            _LOGGER.critical(message)
91            raise InvalidFileFormatException(message=message, filepath=filepath) from inv_format_exp
92        except Exception as common_exp:
93            message = f"UnknownTemplateException by filepath {filepath}.\n{common_exp}"
94            _LOGGER.critical(message)
95            raise UnknownTemplateException(filepath=filepath, exception=common_exp) from common_exp
96
97    def __split_into_tests(self, text: str, filepath: str) -> Dict[str, str]:
98        """
99        Splits rendered template into multiple tests.
100        'filepath' is needed for exceptions
101        """
102        result = {}
103        key_uniq = 1
104
105        try:
106            meta_info = find_all_metas(text)
107            start_indices = [metainfile[0] for metainfile in meta_info]
108        except InvalidMetaException as inv_m_exp:
109            raise InvalidFileFormatException(
110                filepath=filepath,
111                message=f"Splitting tests fails: {inv_m_exp.message}"
112            ) from inv_m_exp
113        size_indices = len(start_indices)
114        for i in range(1, size_indices):
115            left = start_indices[i - 1]
116            right = start_indices[i]
117            desc = meta_info[i - 1][2]
118            key = desc["desc"]["function"]
119            if key in result:
120                key, key_uniq = self.__deduplicate_key_content(key, key_uniq)
121            result[key] = text[left:right]
122
123        i = size_indices - 1
124        desc = meta_info[i][2]
125        key = desc["desc"]["function"]
126        if key in result:
127            key, key_uniq = self.__deduplicate_key_content(key, key_uniq)
128        result[key] = text[start_indices[-1]:]
129
130        return result
131