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 re 20from dataclasses import dataclass, field 21from enum import Enum 22from pathlib import Path 23from typing import Optional, Any, List 24 25import yaml 26 27METADATA_PATTERN = re.compile(r"(?<=/\*---)(.*?)(?=---\*/)", flags=re.DOTALL) 28DOTS_WHITESPACES_PATTERN = r"(\.\w+)*" 29PACKAGE_PATTERN = re.compile(f"\\n\\s*package[\\t\\f\\v ]+(?P<package_name>\\w+{DOTS_WHITESPACES_PATTERN})\\b") 30SPEC_CHAPTER_PATTERN = re.compile(r"^\d{1,2}\.{0,1}\d{0,3}\.{0,1}\d{0,3}\.{0,1}\d{0,3}\.{0,1}\d{0,3}$") 31_LOGGER = logging.getLogger("runner.plugins.ets.ets_templates.test_metadata") 32 33 34class Tags: 35 class EtsTag(Enum): 36 COMPILE_ONLY = "compile-only" 37 NO_WARMUP = "no-warmup" 38 NOT_A_TEST = "not-a-test" 39 NEGATIVE = "negative" 40 41 def __init__(self, tags: Optional[List[str]] = None) -> None: 42 self.__compile_only = Tags.__contains(Tags.EtsTag.COMPILE_ONLY.value, tags) 43 self.__negative = Tags.__contains(Tags.EtsTag.NEGATIVE.value, tags) 44 self.__not_a_test = Tags.__contains(Tags.EtsTag.NOT_A_TEST.value, tags) 45 self.__no_warmup = Tags.__contains(Tags.EtsTag.NO_WARMUP.value, tags) 46 47 @property 48 def compile_only(self) -> bool: 49 return self.__compile_only 50 51 @property 52 def negative(self) -> bool: 53 return self.__negative 54 55 @property 56 def not_a_test(self) -> bool: 57 return self.__not_a_test 58 59 @property 60 def no_warmup(self) -> bool: 61 return self.__no_warmup 62 63 @staticmethod 64 def __contains(tag: str, tags: Optional[List[str]]) -> bool: 65 return tag in tags if tags is not None else False 66 67 68@dataclass 69class TestMetadata: 70 tags: Tags 71 desc: Optional[str] = None 72 files: Optional[List[str]] = None 73 assertion: Optional[str] = None 74 params: Optional[Any] = None 75 name: Optional[str] = None 76 package: Optional[str] = None 77 ark_options: List[str] = field(default_factory=list) 78 timeout: Optional[int] = None 79 spec: Optional[str] = None 80 81 def __post_init__(self) -> None: 82 if self.spec is None: 83 return 84 self.spec = str(self.spec) 85 if not re.match(SPEC_CHAPTER_PATTERN, self.spec): 86 error_message = f"Incorrect format of specification chapter number : {self.spec}" 87 _LOGGER.error(error_message) 88 89 90def get_metadata(path: Path) -> TestMetadata: 91 data = Path.read_text(path) 92 yaml_text = "\n".join(re.findall(METADATA_PATTERN, data)) 93 metadata = yaml.safe_load(yaml_text) 94 if metadata is None: 95 metadata = {} 96 metadata['tags'] = Tags(metadata.get('tags')) 97 metadata['assertion'] = metadata.get('assert') 98 if 'assert' in metadata: 99 del metadata['assert'] 100 if isinstance(type(metadata.get('ark_options')), str): 101 metadata['ark_options'] = [metadata['ark_options']] 102 metadata['package'] = metadata.get("package") 103 if metadata['package'] is None: 104 metadata['package'] = get_package_statement(path) 105 return TestMetadata(**metadata) 106 107 108def get_package_statement(path: Path) -> Optional[str]: 109 data = Path.read_text(path) 110 match = PACKAGE_PATTERN.search(data) 111 if match is None: 112 return None 113 return stmt if (stmt := match.group("package_name")) is not None else None 114