1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4# Copyright (c) 2021-2025 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 17import threading 18import platform 19from pathlib import Path 20from typing import Optional, Tuple 21from runner.code_coverage.coverage_dir import CoverageDir 22from runner.code_coverage.llvm_cov_tool import LlvmCovTool 23from runner.code_coverage.lcov_tool import LcovTool 24from runner.code_coverage.cmd_executor import CmdExecutor, LinuxCmdExecutor 25 26 27class CoverageManager: 28 _instance: Optional['CoverageManager'] = None 29 _lock: threading.Lock = threading.Lock() 30 _initialized: bool = False 31 32 def __new__( 33 cls, 34 build_dir_path: Optional[Path] = None, 35 coverage_dir: Optional[CoverageDir] = None 36 ) -> 'CoverageManager': 37 if cls._instance is not None: 38 return cls._instance 39 with cls._lock: 40 build_dir_path, coverage_dir = cls._validate_parameters(build_dir_path, coverage_dir) 41 if cls._instance is None: 42 instance = super().__new__(cls) 43 instance._initialize(build_dir_path, coverage_dir) 44 cls._instance = instance 45 return cls._instance 46 47 @property 48 def llvm_cov_tool(self) -> LlvmCovTool: 49 return self.llvm_cov 50 51 @property 52 def lcov_tool(self) -> LcovTool: 53 return self.lcov 54 55 @staticmethod 56 def _get_cmd_executor() -> CmdExecutor: 57 system = platform.system() 58 if system == "Linux": 59 return LinuxCmdExecutor() 60 raise NotImplementedError(f"Unsupported OS: {system}") 61 62 @classmethod 63 def _validate_parameters( 64 cls, 65 build_dir_path: Optional[Path] = None, 66 coverage_dir: Optional[CoverageDir] = None 67 ) -> Tuple[Path, CoverageDir]: 68 if build_dir_path is None: 69 raise ValueError("build_dir_path is not initialized") 70 if not build_dir_path.parts or str(build_dir_path) == ".": 71 raise ValueError("build_dir_path must not be '.' or empty") 72 if not build_dir_path.exists(): 73 raise FileNotFoundError(f"Directory build_dir_path={build_dir_path} not found") 74 75 if coverage_dir is None: 76 raise ValueError("coverage_dir is not initialized") 77 if not coverage_dir.root.exists(): 78 raise FileNotFoundError(f"Directory coverage_dir.root={coverage_dir.root} not found") 79 80 return build_dir_path, coverage_dir 81 82 def _initialize(self, build_dir_path: Path, coverage_dir: CoverageDir) -> None: 83 if self._initialized: 84 return 85 self.cmd_executor = self._get_cmd_executor() 86 self.llvm_cov = LlvmCovTool(build_dir_path, coverage_dir, self.cmd_executor) 87 self.lcov = LcovTool(build_dir_path, coverage_dir, self.cmd_executor) 88 self._initialized = True 89