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