Coverage for pyEDAA/Reports/CodeCoverage/UCIS/ToggleBits.py: 77%

122 statements  

« prev     ^ index     » next       coverage.py v7.17.3, created at 2026-11-14 02:26 +0000

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

2# _____ ____ _ _ ____ _ # 

3# _ __ _ _| ____| _ \ / \ / \ | _ \ ___ _ __ ___ _ __| |_ ___ # 

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

5# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| _ < __/ |_) | (_) | | | |_\__ \ # 

6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_| \_\___| .__/ \___/|_| \__|___/ # 

7# |_| |___/ |_| # 

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

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

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

14# Copyright 2026-2026 Electronic Design Automation Abstraction (EDA²) # 

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""" 

32The bits of the toggle coverage of the UCIS XML interchange format: a signal's ``<toggleBit>`` elements and their 

33transitions. 

34""" 

35from __future__ import annotations 

36 

37from typing import TYPE_CHECKING, Iterable, Optional as Nullable, Self 

38 

39from lxml.etree import _Element 

40from pyTooling.Common import getFullyQualifiedName 

41from pyTooling.Decorators import export, readonly 

42from pyTooling.MetaClasses import ExtendedType 

43 

44from pyEDAA.Reports.CodeCoverage.UCIS.Bins import Bin 

45from pyEDAA.Reports.CodeCoverage.UCIS.Elements import Base, ObjectAttributesMixin, UserAttribute 

46 

47if TYPE_CHECKING: 47 ↛ 48line 47 didn't jump to line 48 because the condition on line 47 was never true

48 from pyEDAA.Reports.CodeCoverage.UCIS.Toggles import ToggleObject 

49 

50 

51# A class with a property named like a class - ``Bin`` - can't name that class in the annotation of a field: the class 

52# body's namespace, where annotations are evaluated, binds the name to the property. 

53_Bin = Bin 

54 

55 

56@export 

57class ToggleBit(Base, ObjectAttributesMixin): 

58 """ 

59 A ``<toggleBit>`` of a toggle object: a bit of the signal, its indices and its transitions. 

60 """ 

61 

62 _parent: Nullable[ToggleObject] #: The toggle object the bit belongs to. 

63 _name: str #: Name of the bit, e.g. ``ff1[2]``. 

64 _key: str #: UCIS key of the bit. 

65 _indices: list[int] #: The indices of the bit, one per dimension. 

66 _toggles: list[Toggle] #: The transitions of the bit. 

67 

68 def __init__( 

69 self, 

70 name: str, 

71 key: str, 

72 indices: Nullable[Iterable[int]] = None, 

73 alias: Nullable[str] = None, 

74 excluded: bool = False, 

75 excludedReason: Nullable[str] = None, 

76 weight: int = 1, 

77 userAttributes: Nullable[Iterable[UserAttribute]] = None, 

78 *, 

79 parent: Nullable[ToggleObject] = None 

80 ) -> None: 

81 """ 

82 Initialize the toggle bit, and append it to the bits of its toggle object. 

83 

84 Its transitions are added by creating them with this bit as their parent. 

85 

86 :param name: Name of the bit, e.g. ``ff1[2]``. 

87 :param key: UCIS key of the bit. 

88 :param indices: Optional, the indices of the bit, one per dimension. Default: ``None``. 

89 :param alias: Optional, an alias of the bit's name. Default: ``None``. 

90 :param excluded: Optional, whether the bit is excluded from the coverage. Default: ``False``. 

91 :param excludedReason: Optional, why the bit is excluded. Default: ``None``. 

92 :param weight: Optional, weight of the bit in computing the coverage. Default: ``1``. 

93 :param userAttributes: Optional, the user-defined attributes. Default: ``None``. 

94 :param parent: Optional, the toggle object the bit belongs to. Default: ``None``. 

95 :raises ValueError: If parameter ``name`` is ``None``. 

96 :raises TypeError: If parameter ``name`` isn't of type :class:`str`. 

97 :raises ValueError: If parameter ``key`` is ``None``. 

98 :raises TypeError: If parameter ``key`` isn't of type :class:`str`. 

99 :raises TypeError: If parameter ``indices`` isn't iterable. 

100 :raises TypeError: If parameter ``indices`` contains an element not of type :class:`int`. 

101 :raises ValueError: If parameter ``indices`` contains a negative index. 

102 :raises TypeError: If parameter ``alias`` isn't of type :class:`str`. 

103 :raises ValueError: If parameter ``excluded`` is ``None``. 

104 :raises TypeError: If parameter ``excluded`` isn't of type :class:`bool`. 

105 :raises TypeError: If parameter ``excludedReason`` isn't of type :class:`str`. 

106 :raises ValueError: If parameter ``weight`` is ``None``. 

107 :raises TypeError: If parameter ``weight`` isn't of type :class:`int`. 

108 :raises ValueError: If parameter ``weight`` is negative. 

109 :raises TypeError: If parameter ``userAttributes`` isn't iterable. 

110 :raises TypeError: If parameter ``userAttributes`` contains an element not of type 

111 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.UserAttribute`. 

112 :raises TypeError: If parameter ``parent`` isn't of type 

113 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Toggles.ToggleObject`. 

114 """ 

115 from pyEDAA.Reports.CodeCoverage.UCIS.Toggles import ToggleObject 

116 

117 super().__init__(userAttributes) 

118 ObjectAttributesMixin.__init__(self, alias, excluded, excludedReason, weight) 

119 

120 for parameterName, value in ( 

121 ("name", name), 

122 ("key", key) 

123 ): 

124 if value is None: 124 ↛ 125line 124 didn't jump to line 125 because the condition on line 124 was never true

125 raise ValueError(f"Parameter '{parameterName}' is None.") 

126 elif not isinstance(value, str): 126 ↛ 127line 126 didn't jump to line 127 because the condition on line 126 was never true

127 ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.") 

128 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") 

129 raise ex 

130 

131 if parent is not None and not isinstance(parent, ToggleObject): 131 ↛ 132line 131 didn't jump to line 132 because the condition on line 131 was never true

132 ex = TypeError("Parameter 'parent' is not of type 'ToggleObject'.") 

133 ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.") 

134 raise ex 

135 

136 self._parent = parent 

137 self._name = name 

138 self._key = key 

139 self._indices = indexList = [] 

140 self._toggles = [] 

141 

142 if indices is not None: 142 ↛ 160line 142 didn't jump to line 160 because the condition on line 142 was always true

143 if not isinstance(indices, Iterable): 143 ↛ 144line 143 didn't jump to line 144 because the condition on line 143 was never true

144 ex = TypeError("Parameter 'indices' is not iterable.") 

145 ex.add_note(f"Got type '{getFullyQualifiedName(indices)}'.") 

146 raise ex 

147 

148 for index in indices: 

149 if not isinstance(index, int) or isinstance(index, bool): 149 ↛ 150line 149 didn't jump to line 150 because the condition on line 149 was never true

150 ex = TypeError("Parameter 'indices' contains an element not of type 'int'.") 

151 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.") 

152 raise ex 

153 elif index < 0: 

154 ex = ValueError("Parameter 'indices' contains a negative index.") 

155 ex.add_note(f"Got value '{index}'.") 

156 raise ex 

157 

158 indexList.append(index) 

159 

160 if parent is not None: 160 ↛ exitline 160 didn't return from function '__init__' because the condition on line 160 was always true

161 parent._bits.append(self) 

162 

163 @classmethod 

164 def Parse(cls, element: _Element, *, parent: Nullable[ToggleObject] = None) -> Self: 

165 """ 

166 Parse a toggle bit and its transitions from its ``<toggleBit>`` element. 

167 

168 :param element: The ``<toggleBit>`` element. 

169 :param parent: Optional, the toggle object the bit belongs to. Default: ``None``. 

170 :returns: The toggle bit. 

171 :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states. 

172 """ 

173 toggleBit = cls( 

174 element.attrib["name"], 

175 element.attrib["key"], 

176 [int(indexElement.text) for indexElement in element.iterfind("{*}index")], 

177 element.attrib.get("alias"), 

178 cls._ParseBoolean(element.attrib.get("excluded", "false")), 

179 element.attrib.get("excludedReason"), 

180 int(element.attrib.get("weight", "1")), 

181 cls._ParseUserAttributes(element), 

182 parent=parent 

183 ) 

184 

185 for toggleElement in element.iterfind("{*}toggle"): 

186 Toggle.Parse(toggleElement, parent=toggleBit) 

187 

188 return toggleBit 

189 

190 @readonly 

191 def Parent(self) -> Nullable[ToggleObject]: 

192 """ 

193 Read-only property to access the toggle object the bit belongs to (:attr:`_parent`). 

194 

195 :returns: The toggle object; ``None``, if the bit belongs to none. 

196 """ 

197 return self._parent 

198 

199 @readonly 

200 def Name(self) -> str: 

201 """ 

202 Read-only property to access the name of the bit (:attr:`_name`). 

203 

204 :returns: The name, e.g. ``ff1[2]``. 

205 """ 

206 return self._name 

207 

208 @readonly 

209 def Key(self) -> str: 

210 """ 

211 Read-only property to access the UCIS key of the bit (:attr:`_key`). 

212 

213 :returns: The key, as stated. 

214 """ 

215 return self._key 

216 

217 @readonly 

218 def Indices(self) -> list[int]: 

219 """ 

220 Read-only property to access the indices of the bit (:attr:`_indices`). 

221 

222 :returns: The indices, one per dimension; empty, if the report states none. 

223 """ 

224 return self._indices 

225 

226 @readonly 

227 def Toggles(self) -> list[Toggle]: 

228 """ 

229 Read-only property to access the transitions of the bit (:attr:`_toggles`). 

230 

231 :returns: The transitions, in the order the report lists them. 

232 """ 

233 return self._toggles 

234 

235 

236@export 

237class Toggle(metaclass=ExtendedType, slots=True): 

238 """ 

239 A ``<toggle>`` of a toggle bit: a transition from one value to another, and its bin - how often it happened. 

240 """ 

241 

242 _parent: Nullable[ToggleBit] #: The toggle bit the transition belongs to. 

243 _from: str #: The value before the transition, e.g. ``0``. 

244 _to: str #: The value after the transition, e.g. ``1``. 

245 _bin: _Bin #: How often the transition happened. 

246 

247 def __init__(self, fromValue: str, toValue: str, coverBin: Bin, *, parent: Nullable[ToggleBit] = None) -> None: 

248 """ 

249 Initialize the transition, become its bin's parent, and append it to the transitions of its toggle bit. 

250 

251 :param fromValue: The value before the transition, e.g. ``0``. 

252 :param toValue: The value after the transition, e.g. ``1``. 

253 :param coverBin: How often the transition happened. 

254 :param parent: Optional, the toggle bit the transition belongs to. Default: ``None``. 

255 :raises ValueError: If parameter ``fromValue`` is ``None``. 

256 :raises TypeError: If parameter ``fromValue`` isn't of type :class:`str`. 

257 :raises ValueError: If parameter ``toValue`` is ``None``. 

258 :raises TypeError: If parameter ``toValue`` isn't of type :class:`str`. 

259 :raises ValueError: If parameter ``coverBin`` is ``None``. 

260 :raises TypeError: If parameter ``coverBin`` isn't of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Bins.Bin`. 

261 :raises TypeError: If parameter ``parent`` isn't of type :class:`ToggleBit`. 

262 """ 

263 for parameterName, value in ( 

264 ("fromValue", fromValue), 

265 ("toValue", toValue) 

266 ): 

267 if value is None: 267 ↛ 268line 267 didn't jump to line 268 because the condition on line 267 was never true

268 raise ValueError(f"Parameter '{parameterName}' is None.") 

269 elif not isinstance(value, str): 269 ↛ 270line 269 didn't jump to line 270 because the condition on line 269 was never true

270 ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.") 

271 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") 

272 raise ex 

273 

274 if coverBin is None: 274 ↛ 275line 274 didn't jump to line 275 because the condition on line 274 was never true

275 raise ValueError("Parameter 'coverBin' is None.") 

276 elif not isinstance(coverBin, Bin): 276 ↛ 277line 276 didn't jump to line 277 because the condition on line 276 was never true

277 ex = TypeError("Parameter 'coverBin' is not of type 'Bin'.") 

278 ex.add_note(f"Got type '{getFullyQualifiedName(coverBin)}'.") 

279 raise ex 

280 

281 if parent is not None and not isinstance(parent, ToggleBit): 

282 ex = TypeError("Parameter 'parent' is not of type 'ToggleBit'.") 

283 ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.") 

284 raise ex 

285 

286 self._parent = parent 

287 self._from = fromValue 

288 self._to = toValue 

289 self._bin = coverBin 

290 coverBin._parent = self 

291 

292 if parent is not None: 292 ↛ exitline 292 didn't return from function '__init__' because the condition on line 292 was always true

293 parent._toggles.append(self) 

294 

295 @classmethod 

296 def Parse(cls, element: _Element, *, parent: Nullable[ToggleBit] = None) -> Self: 

297 """ 

298 Parse a transition and its bin from its ``<toggle>`` element. 

299 

300 :param element: The ``<toggle>`` element. 

301 :param parent: Optional, the toggle bit the transition belongs to. Default: ``None``. 

302 :returns: The transition. 

303 :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states. 

304 """ 

305 return cls(element.attrib["from"], element.attrib["to"], Bin.Parse(element.find("{*}bin")), parent=parent) 

306 

307 @readonly 

308 def Parent(self) -> Nullable[ToggleBit]: 

309 """ 

310 Read-only property to access the toggle bit the transition belongs to (:attr:`_parent`). 

311 

312 :returns: The toggle bit; ``None``, if the transition belongs to none. 

313 """ 

314 return self._parent 

315 

316 @readonly 

317 def From(self) -> str: 

318 """ 

319 Read-only property to access the value before the transition (:attr:`_from`). 

320 

321 :returns: The value, as stated, e.g. ``0``. 

322 """ 

323 return self._from 

324 

325 @readonly 

326 def To(self) -> str: 

327 """ 

328 Read-only property to access the value after the transition (:attr:`_to`). 

329 

330 :returns: The value, as stated, e.g. ``1``. 

331 """ 

332 return self._to 

333 

334 @readonly 

335 def Bin(self) -> Bin: 

336 """ 

337 Read-only property to access the transition's bin (:attr:`_bin`). 

338 

339 :returns: The bin, stating how often the transition happened. 

340 """ 

341 return self._bin