Coverage for sphinx_reports/DataModel/CodeCoverage.py: 71%

152 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-09 22:12 +0000

1# ==================================================================================================================== # 

2# _ _ _ # 

3# ___ _ __ | |__ (_)_ __ __ __ _ __ ___ _ __ ___ _ __| |_ ___ # 

4# / __| '_ \| '_ \| | '_ \\ \/ /____| '__/ _ \ '_ \ / _ \| '__| __/ __| # 

5# \__ \ |_) | | | | | | | |> <_____| | | __/ |_) | (_) | | | |_\__ \ # 

6# |___/ .__/|_| |_|_|_| |_/_/\_\ |_| \___| .__/ \___/|_| \__|___/ # 

7# |_| |_| # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

13# ==================================================================================================================== # 

14# Copyright 2023-2025 Patrick Lehmann - Bötzingen, Germany # 

15# # 

16# Licensed under the Apache License, Version 2.0 (the "License"); # 

17# you may not use this file except in compliance with the License. # 

18# You may obtain a copy of the License at # 

19# # 

20# http://www.apache.org/licenses/LICENSE-2.0 # 

21# # 

22# Unless required by applicable law or agreed to in writing, software # 

23# distributed under the License is distributed on an "AS IS" BASIS, # 

24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

25# See the License for the specific language governing permissions and # 

26# limitations under the License. # 

27# # 

28# SPDX-License-Identifier: Apache-2.0 # 

29# ==================================================================================================================== # 

30# 

31""" 

32**Abstract documentation coverage data model for Python code.** 

33""" 

34from pathlib import Path 

35from typing import Optional as Nullable, Dict, Union, Generic, TypeVar 

36 

37from pyTooling.Decorators import export, readonly 

38from pyEDAA.Reports.DocumentationCoverage.Python import PackageCoverage 

39 

40 

41_ParentType = TypeVar("_ParentType", bound="Base") 

42 

43 

44@export 

45class Base(Generic[_ParentType]): 

46 _name: str 

47 _parent: Nullable[_ParentType] 

48 

49 def __init__(self, name: str, parent: Nullable[_ParentType] = None) -> None: 

50 self._name = name 

51 self._parent = parent 

52 

53 @readonly 

54 def Name(self) -> str: 

55 return self._name 

56 

57 @readonly 

58 def Parent(self) -> Nullable[_ParentType]: 

59 return self._parent 

60 

61 

62@export 

63class Coverage(Base[_ParentType], Generic[_ParentType]): 

64 _file: Path 

65 

66 _totalStatements: int 

67 _excludedStatements: int 

68 _coveredStatements: int 

69 _missingStatements: int 

70 _totalBranches: int 

71 _partialBranches: int 

72 

73 _coverage: float 

74 

75 def __init__(self, name: str, file: Path, parent: Nullable[_ParentType] = None) -> None: 

76 super().__init__(name, parent) 

77 self._file = file 

78 

79 self._totalStatements = 0 

80 self._excludedStatements = 0 

81 self._coveredStatements = 0 

82 self._missingStatements = 0 

83 

84 self._totalBranches = 0 

85 self._coveredBranches = 0 

86 self._partialBranches = 0 

87 self._missingBranches = 0 

88 

89 self._coverage = -1.0 

90 

91 @readonly 

92 def File(self) -> Path: 

93 return self._file 

94 

95 @readonly 

96 def TotalStatements(self) -> int: 

97 return self._totalStatements 

98 

99 @readonly 

100 def ExcludedStatements(self) -> int: 

101 return self._excludedStatements 

102 

103 @readonly 

104 def CoveredStatements(self) -> int: 

105 return self._coveredStatements 

106 

107 @readonly 

108 def MissingStatements(self) -> int: 

109 return self._missingStatements 

110 

111 @readonly 

112 def StatementCoverage(self) -> float: 

113 if self._totalStatements <= 0: 

114 return 0.0 

115 

116 return self._coveredStatements / self._totalStatements 

117 

118 @readonly 

119 def TotalBranches(self) -> int: 

120 return self._totalBranches 

121 

122 @readonly 

123 def CoveredBranches(self) -> int: 

124 return self._coveredBranches 

125 

126 @readonly 

127 def PartialBranches(self) -> int: 

128 return self._partialBranches 

129 

130 @readonly 

131 def MissingBranches(self) -> int: 

132 return self._missingBranches 

133 

134 @readonly 

135 def BranchCoverage(self) -> float: 

136 if self._totalBranches <= 0: 

137 return 0.0 

138 

139 return (self._coveredBranches + self._partialBranches) / self._totalBranches 

140 

141 @readonly 

142 def Coverage(self) -> float: 

143 return self._coverage 

144 

145 

146@export 

147class ModuleCoverage(Coverage["PackageCoverage"]): 

148 def __init__(self, name: str, file: Path, parent: Nullable["PackageCoverage"] = None) -> None: 

149 super().__init__(name, file, parent) 

150 

151 if parent is not None: 151 ↛ 152line 151 didn't jump to line 152 because the condition on line 151 was never true

152 parent._modules[name] = self 

153 

154 

155@export 

156class PackageCoverage(Coverage["PackageCoverage"]): 

157 _modules: Dict[str, ModuleCoverage] 

158 _packages: Dict[str, "PackageCoverage"] 

159 

160 def __init__(self, name: str, file: Path, parent: Nullable["PackageCoverage"] = None) -> None: 

161 super().__init__(name, file, parent) 

162 

163 if parent is not None: 163 ↛ 164line 163 didn't jump to line 164 because the condition on line 163 was never true

164 parent._packages[name] = self 

165 

166 self._modules = {} 

167 self._packages = {} 

168 

169 @readonly 

170 def FileCount(self) -> int: 

171 return self.TotalModuleCount 

172 

173 @readonly 

174 def PackageCount(self) -> int: 

175 return len(self._packages) 

176 

177 @readonly 

178 def ModuleCount(self) -> int: 

179 return 1 + len(self._modules) 

180 

181 @readonly 

182 def TotalPackageCount(self) -> int: 

183 return 1 + sum(p.TotalPackageCount for p in self._packages.values()) 

184 

185 @readonly 

186 def TotalModuleCount(self) -> int: 

187 return 1 + sum(p.TotalModuleCount for p in self._packages.values()) + len(self._modules) 

188 

189 @readonly 

190 def Packages(self) -> Dict[str, "PackageCoverage"]: 

191 return self._packages 

192 

193 @readonly 

194 def Modules(self) -> Dict[str, ModuleCoverage]: 

195 return self._modules 

196 

197 @readonly 

198 def AggregatedTotalStatements(self) -> int: 

199 return ( 

200 self._totalStatements + 

201 sum(p.AggregatedTotalStatements for p in self._packages.values()) + 

202 sum(m._totalStatements for m in self._modules.values()) 

203 ) 

204 

205 @readonly 

206 def AggregatedExcludedStatements(self) -> int: 

207 return ( 

208 self._excludedStatements + 

209 sum(p.AggregatedExcludedStatements for p in self._packages.values()) + 

210 sum(m._excludedStatements for m in self._modules.values()) 

211 ) 

212 

213 @readonly 

214 def AggregatedCoveredStatements(self) -> int: 

215 return ( 

216 self._coveredStatements + 

217 sum(p.AggregatedCoveredStatements for p in self._packages.values()) + 

218 sum(m._coveredStatements for m in self._modules.values()) 

219 ) 

220 

221 @readonly 

222 def AggregatedMissingStatements(self) -> int: 

223 return ( 

224 self._missingStatements + 

225 sum(p.AggregatedMissingStatements for p in self._packages.values()) + 

226 sum(m._missingStatements for m in self._modules.values()) 

227 ) 

228 

229 @readonly 

230 def AggregatedStatementCoverage(self) -> float: 

231 return self.AggregatedCoveredStatements / self.AggregatedTotalStatements 

232 

233 @readonly 

234 def AggregatedTotalBranches(self) -> int: 

235 return ( 

236 self._totalBranches + 

237 sum(p.AggregatedTotalBranches for p in self._packages.values()) + 

238 sum(m._totalBranches for m in self._modules.values()) 

239 ) 

240 

241 @readonly 

242 def AggregatedCoveredBranches(self) -> int: 

243 return ( 

244 self._coveredBranches + 

245 sum(p.AggregatedCoveredBranches for p in self._packages.values()) + 

246 sum(m._coveredBranches for m in self._modules.values()) 

247 ) 

248 

249 @readonly 

250 def AggregatedPartialBranches(self) -> int: 

251 return ( 

252 self._partialBranches + 

253 sum(p.AggregatedPartialBranches for p in self._packages.values()) + 

254 sum(m._partialBranches for m in self._modules.values()) 

255 ) 

256 

257 @readonly 

258 def AggregatedMissingBranches(self) -> int: 

259 return ( 

260 self._missingBranches + 

261 sum(p.AggregatedMissingBranches for p in self._packages.values()) + 

262 sum(m._missingBranches for m in self._modules.values()) 

263 ) 

264 

265 @readonly 

266 def AggregatedBranchCoverage(self) -> float: 

267 return (self.AggregatedCoveredBranches + self.AggregatedPartialBranches) / self.AggregatedTotalBranches 

268 

269 def __getitem__(self, key: str) -> Union["PackageCoverage", ModuleCoverage]: 

270 try: 

271 return self._modules[key] 

272 except KeyError: 

273 return self._packages[key]