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

122 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-21 23:49 +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 TypeError: If parameter ``userAttributes`` isn't iterable. 

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

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

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

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

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

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

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

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

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

105 :raises ValueError: If parameter ``name`` or ``key`` is ``None``. 

106 :raises TypeError: If parameter ``name`` or ``key`` isn't of type :class:`str`. 

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

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

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

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

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

112 """ 

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

114 

115 super().__init__(userAttributes) 

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

117 

118 for parameterName, value in ( 

119 ("name", name), 

120 ("key", key) 

121 ): 

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

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

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

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

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

127 raise ex 

128 

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

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

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

132 raise ex 

133 

134 self._parent = parent 

135 self._name = name 

136 self._key = key 

137 self._indices = [] 

138 self._toggles = [] 

139 

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

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

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

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

144 raise ex 

145 

146 for index in indices: 

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

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

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

150 raise ex 

151 elif index < 0: 

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

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

154 raise ex 

155 

156 self._indices.append(index) 

157 

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

159 parent._bits.append(self) 

160 

161 @classmethod 

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

163 """ 

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

165 

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

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

168 :returns: The toggle bit. 

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

170 """ 

171 toggleBit = cls( 

172 element.attrib["name"], 

173 element.attrib["key"], 

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

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

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

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

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

179 cls._ParseUserAttributes(element), 

180 parent=parent 

181 ) 

182 

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

184 Toggle.Parse(toggleElement, parent=toggleBit) 

185 

186 return toggleBit 

187 

188 @readonly 

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

190 """ 

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

192 

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

194 """ 

195 return self._parent 

196 

197 @readonly 

198 def Name(self) -> str: 

199 """ 

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

201 

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

203 """ 

204 return self._name 

205 

206 @readonly 

207 def Key(self) -> str: 

208 """ 

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

210 

211 :returns: The key, as stated. 

212 """ 

213 return self._key 

214 

215 @readonly 

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

217 """ 

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

219 

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

221 """ 

222 return self._indices 

223 

224 @readonly 

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

226 """ 

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

228 

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

230 """ 

231 return self._toggles 

232 

233 

234@export 

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

236 """ 

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

238 """ 

239 

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

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

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

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

244 

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

246 """ 

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

248 

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

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

251 :param coverBin: How often the transition happened. 

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

253 :raises ValueError: If parameter ``fromValue`` or ``toValue`` is ``None``. 

254 :raises TypeError: If parameter ``fromValue`` or ``toValue`` isn't of type :class:`str`. 

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

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

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

258 """ 

259 for parameterName, value in ( 

260 ("fromValue", fromValue), 

261 ("toValue", toValue) 

262 ): 

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

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

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

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

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

268 raise ex 

269 

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

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

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

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

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

275 raise ex 

276 

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

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

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

280 raise ex 

281 

282 self._parent = parent 

283 self._from = fromValue 

284 self._to = toValue 

285 self._bin = coverBin 

286 coverBin._parent = self 

287 

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

289 parent._toggles.append(self) 

290 

291 @classmethod 

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

293 """ 

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

295 

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

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

298 :returns: The transition. 

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

300 """ 

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

302 

303 @readonly 

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

305 """ 

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

307 

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

309 """ 

310 return self._parent 

311 

312 @readonly 

313 def From(self) -> str: 

314 """ 

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

316 

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

318 """ 

319 return self._from 

320 

321 @readonly 

322 def To(self) -> str: 

323 """ 

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

325 

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

327 """ 

328 return self._to 

329 

330 @readonly 

331 def Bin(self) -> Bin: 

332 """ 

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

334 

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

336 """ 

337 return self._bin