Coverage for pyEDAA/Reports/CodeCoverage/UCIS/HistoryNodes.py: 76%

230 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 history nodes of the UCIS XML interchange format: the tests, which measured the coverage, and the merges of their 

33coverage, with the tool, the date and how the test ran. 

34""" 

35from __future__ import annotations 

36 

37from datetime import datetime 

38from decimal import Decimal 

39from pathlib import Path 

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

41 

42from lxml.etree import _Element 

43from pyTooling.Common import getFullyQualifiedName 

44from pyTooling.Decorators import export, readonly 

45 

46from pyEDAA.Reports.CodeCoverage.UCIS.Elements import Base, UserAttribute 

47 

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

49 from pyEDAA.Reports.CodeCoverage.UCIS import Report 

50 

51 

52@export 

53class HistoryNode(Base): 

54 """ 

55 A ``<historyNodes>`` of the report: a test, which measured coverage, or a merge of coverage, its tool, its date and - 

56 if the report says - how the test ran. 

57 

58 A history node names the node it belongs to - e.g. the merge, which merged it - by the ID of that node. 

59 """ 

60 

61 _parent: Nullable[Report] #: The report the history node belongs to. 

62 _historyNodeID: int #: ID of the history node. 

63 _logicalName: str #: Name of the history node, e.g. of the test. 

64 _testStatus: bool #: Whether the test passed. 

65 _date: datetime #: When the test ran. 

66 _toolCategory: str #: Kind of the tool, e.g. ``simulator``. 

67 _ucisVersion: str #: Version of the UCIS, in which the tool measured the coverage. 

68 _vendorID: str #: ID of the tool's vendor. 

69 _vendorTool: str #: Name of the tool. 

70 _vendorToolVersion: str #: Version of the tool. 

71 _parentID: Nullable[int] #: ID of the history node this one belongs to. 

72 _physicalName: Nullable[Path] #: Path of the coverage database the history node wrote. 

73 _kind: Nullable[str] #: Kind of the history node, e.g. a test or a merge. 

74 _simulationTime: Nullable[float] #: How long the test ran, in simulated time. 

75 _timeUnit: Nullable[str] #: Unit of the simulated time. 

76 _runDirectory: Nullable[Path] #: The directory the test ran in. 

77 _cpuTime: Nullable[float] #: How long the test ran, in CPU time. 

78 _seed: Nullable[str] #: The seed of the test's random numbers. 

79 _command: Nullable[str] #: The command, which ran the test. 

80 _arguments: Nullable[str] #: The command line arguments of the test. 

81 _compulsory: Nullable[str] #: Whether the test must run. 

82 _userName: Nullable[str] #: Name of the user, who ran the test. 

83 _cost: Nullable[Decimal] #: Cost of the test. 

84 _sameTests: Nullable[int] #: ID of a test this one is equivalent to. 

85 _comment: Nullable[str] #: A comment. 

86 

87 def __init__( 

88 self, 

89 historyNodeID: int, 

90 logicalName: str, 

91 testStatus: bool, 

92 date: datetime, 

93 toolCategory: str, 

94 ucisVersion: str, 

95 vendorID: str, 

96 vendorTool: str, 

97 vendorToolVersion: str, 

98 parentID: Nullable[int] = None, 

99 physicalName: Nullable[Path] = None, 

100 kind: Nullable[str] = None, 

101 simulationTime: Nullable[float] = None, 

102 timeUnit: Nullable[str] = None, 

103 runDirectory: Nullable[Path] = None, 

104 cpuTime: Nullable[float] = None, 

105 seed: Nullable[str] = None, 

106 command: Nullable[str] = None, 

107 arguments: Nullable[str] = None, 

108 compulsory: Nullable[str] = None, 

109 userName: Nullable[str] = None, 

110 cost: Nullable[Decimal] = None, 

111 sameTests: Nullable[int] = None, 

112 comment: Nullable[str] = None, 

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

114 *, 

115 parent: Nullable[Report] = None 

116 ) -> None: 

117 """ 

118 Initialize the history node, and add it to the history nodes of its report. 

119 

120 :param historyNodeID: ID of the history node. 

121 :param logicalName: Name of the history node, e.g. of the test. 

122 :param testStatus: Whether the test passed. 

123 :param date: When the test ran. 

124 :param toolCategory: Kind of the tool, e.g. ``simulator``. 

125 :param ucisVersion: Version of the UCIS, in which the tool measured the coverage, e.g. ``1.0``. 

126 :param vendorID: ID of the tool's vendor. 

127 :param vendorTool: Name of the tool. 

128 :param vendorToolVersion: Version of the tool. 

129 :param parentID: Optional, ID of the history node this one belongs to. Default: ``None``. 

130 :param physicalName: Optional, path of the coverage database the history node wrote. Default: ``None``. 

131 :param kind: Optional, kind of the history node, e.g. a test or a merge. Default: ``None``. 

132 :param simulationTime: Optional, how long the test ran, in simulated time. Default: ``None``. 

133 :param timeUnit: Optional, unit of the simulated time, e.g. ``ns``. Default: ``None``. 

134 :param runDirectory: Optional, the directory the test ran in. Default: ``None``. 

135 :param cpuTime: Optional, how long the test ran, in CPU time. Default: ``None``. 

136 :param seed: Optional, the seed of the test's random numbers. Default: ``None``. 

137 :param command: Optional, the command, which ran the test. Default: ``None``. 

138 :param arguments: Optional, the command line arguments of the test. Default: ``None``. 

139 :param compulsory: Optional, whether the test must run. Default: ``None``. 

140 :param userName: Optional, name of the user, who ran the test. Default: ``None``. 

141 :param cost: Optional, cost of the test. Default: ``None``. 

142 :param sameTests: Optional, ID of a test this one is equivalent to. Default: ``None``. 

143 :param comment: Optional, a comment. Default: ``None``. 

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

145 :param parent: Optional, the report the history node belongs to; the history node is added to its 

146 history nodes. Default: ``None``. 

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

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

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

150 :raises ValueError: If parameter ``historyNodeID`` is ``None``. 

151 :raises TypeError: If parameter ``historyNodeID`` isn't of type :class:`int`. 

152 :raises ValueError: If parameter ``historyNodeID`` is negative. 

153 :raises ValueError: If parameter ``testStatus`` is ``None``. 

154 :raises TypeError: If parameter ``testStatus`` isn't of type :class:`bool`. 

155 :raises ValueError: If parameter ``date`` is ``None``. 

156 :raises TypeError: If parameter ``date`` isn't of type :class:`~datetime.datetime`. 

157 :raises ValueError: If parameter ``logicalName``, ``toolCategory``, ``ucisVersion``, ``vendorID``, 

158 ``vendorTool`` or ``vendorToolVersion`` is ``None``. 

159 :raises TypeError: If parameter ``logicalName``, ``toolCategory``, ``ucisVersion``, ``vendorID``, 

160 ``vendorTool`` or ``vendorToolVersion`` isn't of type :class:`str`. 

161 :raises TypeError: If parameter ``parentID`` isn't of type :class:`int`. 

162 :raises ValueError: If parameter ``parentID`` is negative. 

163 :raises TypeError: If parameter ``sameTests`` isn't of type :class:`int`. 

164 :raises ValueError: If parameter ``sameTests`` is negative. 

165 :raises TypeError: If parameter ``physicalName`` isn't of type :class:`~pathlib.Path`. 

166 :raises TypeError: If parameter ``runDirectory`` isn't of type :class:`~pathlib.Path`. 

167 :raises TypeError: If parameter ``simulationTime`` isn't of type :class:`float`. 

168 :raises TypeError: If parameter ``cpuTime`` isn't of type :class:`float`. 

169 :raises TypeError: If parameter ``kind``, ``timeUnit``, ``seed``, ``command``, ``arguments``, 

170 ``compulsory``, ``userName`` or ``comment`` isn't of type :class:`str`. 

171 :raises TypeError: If parameter ``cost`` isn't of type :class:`~decimal.Decimal`. 

172 :raises TypeError: If parameter ``parent`` isn't of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Report`. 

173 """ 

174 from pyEDAA.Reports.CodeCoverage.UCIS import Report 

175 

176 super().__init__(userAttributes) 

177 

178 if historyNodeID is None: 178 ↛ 179line 178 didn't jump to line 179 because the condition on line 178 was never true

179 raise ValueError("Parameter 'historyNodeID' is None.") 

180 elif not isinstance(historyNodeID, int) or isinstance(historyNodeID, bool): 180 ↛ 181line 180 didn't jump to line 181 because the condition on line 180 was never true

181 ex = TypeError("Parameter 'historyNodeID' is not of type 'int'.") 

182 ex.add_note(f"Got type '{getFullyQualifiedName(historyNodeID)}'.") 

183 raise ex 

184 elif historyNodeID < 0: 184 ↛ 185line 184 didn't jump to line 185 because the condition on line 184 was never true

185 ex = ValueError("Parameter 'historyNodeID' is negative.") 

186 ex.add_note(f"Got value '{historyNodeID}'.") 

187 raise ex 

188 

189 if testStatus is None: 189 ↛ 190line 189 didn't jump to line 190 because the condition on line 189 was never true

190 raise ValueError("Parameter 'testStatus' is None.") 

191 elif not isinstance(testStatus, bool): 191 ↛ 192line 191 didn't jump to line 192 because the condition on line 191 was never true

192 ex = TypeError("Parameter 'testStatus' is not of type 'bool'.") 

193 ex.add_note(f"Got type '{getFullyQualifiedName(testStatus)}'.") 

194 raise ex 

195 

196 if date is None: 196 ↛ 197line 196 didn't jump to line 197 because the condition on line 196 was never true

197 raise ValueError("Parameter 'date' is None.") 

198 elif not isinstance(date, datetime): 

199 ex = TypeError("Parameter 'date' is not of type 'datetime'.") 

200 ex.add_note(f"Got type '{getFullyQualifiedName(date)}'.") 

201 raise ex 

202 

203 for name, value in ( 

204 ("logicalName", logicalName), 

205 ("toolCategory", toolCategory), 

206 ("ucisVersion", ucisVersion), 

207 ("vendorID", vendorID), 

208 ("vendorTool", vendorTool), 

209 ("vendorToolVersion", vendorToolVersion) 

210 ): 

211 if value is None: 

212 raise ValueError(f"Parameter '{name}' is None.") 

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

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

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

216 raise ex 

217 

218 if parentID is not None: 

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

220 ex = TypeError("Parameter 'parentID' is not of type 'int'.") 

221 ex.add_note(f"Got type '{getFullyQualifiedName(parentID)}'.") 

222 raise ex 

223 elif parentID < 0: 223 ↛ 228line 223 didn't jump to line 228 because the condition on line 223 was always true

224 ex = ValueError("Parameter 'parentID' is negative.") 

225 ex.add_note(f"Got value '{parentID}'.") 

226 raise ex 

227 

228 if sameTests is not None: 228 ↛ 229line 228 didn't jump to line 229 because the condition on line 228 was never true

229 if not isinstance(sameTests, int) or isinstance(sameTests, bool): 

230 ex = TypeError("Parameter 'sameTests' is not of type 'int'.") 

231 ex.add_note(f"Got type '{getFullyQualifiedName(sameTests)}'.") 

232 raise ex 

233 elif sameTests < 0: 

234 ex = ValueError("Parameter 'sameTests' is negative.") 

235 ex.add_note(f"Got value '{sameTests}'.") 

236 raise ex 

237 

238 if physicalName is not None and not isinstance(physicalName, Path): 238 ↛ 239line 238 didn't jump to line 239 because the condition on line 238 was never true

239 ex = TypeError("Parameter 'physicalName' is not of type 'Path'.") 

240 ex.add_note(f"Got type '{getFullyQualifiedName(physicalName)}'.") 

241 raise ex 

242 

243 if runDirectory is not None and not isinstance(runDirectory, Path): 243 ↛ 244line 243 didn't jump to line 244 because the condition on line 243 was never true

244 ex = TypeError("Parameter 'runDirectory' is not of type 'Path'.") 

245 ex.add_note(f"Got type '{getFullyQualifiedName(runDirectory)}'.") 

246 raise ex 

247 

248 if simulationTime is not None and not isinstance(simulationTime, float): 

249 ex = TypeError("Parameter 'simulationTime' is not of type 'float'.") 

250 ex.add_note(f"Got type '{getFullyQualifiedName(simulationTime)}'.") 

251 raise ex 

252 

253 if cpuTime is not None and not isinstance(cpuTime, float): 253 ↛ 254line 253 didn't jump to line 254 because the condition on line 253 was never true

254 ex = TypeError("Parameter 'cpuTime' is not of type 'float'.") 

255 ex.add_note(f"Got type '{getFullyQualifiedName(cpuTime)}'.") 

256 raise ex 

257 

258 for name, value in ( 

259 ("kind", kind), 

260 ("timeUnit", timeUnit), 

261 ("seed", seed), 

262 ("command", command), 

263 ("arguments", arguments), 

264 ("compulsory", compulsory), 

265 ("userName", userName), 

266 ("comment", comment) 

267 ): 

268 if value is not None and not isinstance(value, str): 268 ↛ 269line 268 didn't jump to line 269 because the condition on line 268 was never true

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

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

271 raise ex 

272 

273 if cost is not None and not isinstance(cost, Decimal): 273 ↛ 274line 273 didn't jump to line 274 because the condition on line 273 was never true

274 ex = TypeError("Parameter 'cost' is not of type 'Decimal'.") 

275 ex.add_note(f"Got type '{getFullyQualifiedName(cost)}'.") 

276 raise ex 

277 

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

279 ex = TypeError("Parameter 'parent' is not of type 'Report'.") 

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

281 raise ex 

282 

283 self._parent = parent 

284 self._historyNodeID = historyNodeID 

285 self._logicalName = logicalName 

286 self._testStatus = testStatus 

287 self._date = date 

288 self._toolCategory = toolCategory 

289 self._ucisVersion = ucisVersion 

290 self._vendorID = vendorID 

291 self._vendorTool = vendorTool 

292 self._vendorToolVersion = vendorToolVersion 

293 self._parentID = parentID 

294 self._physicalName = physicalName 

295 self._kind = kind 

296 self._simulationTime = simulationTime 

297 self._timeUnit = timeUnit 

298 self._runDirectory = runDirectory 

299 self._cpuTime = cpuTime 

300 self._seed = seed 

301 self._command = command 

302 self._arguments = arguments 

303 self._compulsory = compulsory 

304 self._userName = userName 

305 self._cost = cost 

306 self._sameTests = sameTests 

307 self._comment = comment 

308 

309 if parent is not None: 

310 parent._historyNodes[historyNodeID] = self 

311 

312 @classmethod 

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

314 """ 

315 Parse a history node from its ``<historyNodes>`` element. 

316 

317 :param element: The ``<historyNodes>`` element. 

318 :param parent: Optional, the report the history node belongs to. Default: ``None``. 

319 :returns: The history node. 

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

321 """ 

322 attributes = element.attrib 

323 parentID = attributes.get("parentId") 

324 physicalName = attributes.get("physicalName") 

325 simulationTime = attributes.get("simtime") 

326 runDirectory = attributes.get("runCwd") 

327 cpuTime = attributes.get("cpuTime") 

328 cost = attributes.get("cost") 

329 sameTests = attributes.get("sameTests") 

330 

331 return cls( 

332 int(attributes["historyNodeId"]), 

333 attributes["logicalName"], 

334 cls._ParseBoolean(attributes["testStatus"]), 

335 datetime.fromisoformat(attributes["date"]), 

336 attributes["toolCategory"], 

337 attributes["ucisVersion"], 

338 attributes["vendorId"], 

339 attributes["vendorTool"], 

340 attributes["vendorToolVersion"], 

341 None if parentID is None else int(parentID), 

342 None if physicalName is None else Path(physicalName), 

343 attributes.get("kind"), 

344 None if simulationTime is None else float(simulationTime), 

345 attributes.get("timeunit"), 

346 None if runDirectory is None else Path(runDirectory), 

347 None if cpuTime is None else float(cpuTime), 

348 attributes.get("seed"), 

349 attributes.get("cmd"), 

350 attributes.get("args"), 

351 attributes.get("compulsory"), 

352 attributes.get("userName"), 

353 None if cost is None else Decimal(cost), 

354 None if sameTests is None else int(sameTests), 

355 attributes.get("comment"), 

356 cls._ParseUserAttributes(element), 

357 parent=parent 

358 ) 

359 

360 @readonly 

361 def Parent(self) -> Nullable[Report]: 

362 """ 

363 Read-only property to access the report the history node belongs to (:attr:`_parent`). 

364 

365 :returns: The report; ``None``, if the history node belongs to none. 

366 """ 

367 return self._parent 

368 

369 @readonly 

370 def HistoryNodeID(self) -> int: 

371 """ 

372 Read-only property to access the ID of the history node (:attr:`_historyNodeID`). 

373 

374 :returns: The ID, by which bins name the tests, which covered them. 

375 """ 

376 return self._historyNodeID 

377 

378 @readonly 

379 def LogicalName(self) -> str: 

380 """ 

381 Read-only property to access the name of the history node (:attr:`_logicalName`). 

382 

383 :returns: The name, e.g. of the test. 

384 """ 

385 return self._logicalName 

386 

387 @readonly 

388 def TestStatus(self) -> bool: 

389 """ 

390 Read-only property to access whether the test passed (:attr:`_testStatus`). 

391 

392 :returns: ``True``, if the test passed. 

393 """ 

394 return self._testStatus 

395 

396 @readonly 

397 def Date(self) -> datetime: 

398 """ 

399 Read-only property to access when the test ran (:attr:`_date`). 

400 

401 :returns: The date and time. 

402 """ 

403 return self._date 

404 

405 @readonly 

406 def ToolCategory(self) -> str: 

407 """ 

408 Read-only property to access the kind of the tool (:attr:`_toolCategory`). 

409 

410 :returns: The kind, as stated. 

411 """ 

412 return self._toolCategory 

413 

414 @readonly 

415 def UCISVersion(self) -> str: 

416 """ 

417 Read-only property to access the UCIS version, in which the tool measured the coverage (:attr:`_ucisVersion`). 

418 

419 :returns: The version, as stated. 

420 """ 

421 return self._ucisVersion 

422 

423 @readonly 

424 def VendorID(self) -> str: 

425 """ 

426 Read-only property to access the ID of the tool's vendor (:attr:`_vendorID`). 

427 

428 :returns: The ID, as stated. 

429 """ 

430 return self._vendorID 

431 

432 @readonly 

433 def VendorTool(self) -> str: 

434 """ 

435 Read-only property to access the name of the tool (:attr:`_vendorTool`). 

436 

437 :returns: The name, as stated. 

438 """ 

439 return self._vendorTool 

440 

441 @readonly 

442 def VendorToolVersion(self) -> str: 

443 """ 

444 Read-only property to access the version of the tool (:attr:`_vendorToolVersion`). 

445 

446 :returns: The version, as stated. 

447 """ 

448 return self._vendorToolVersion 

449 

450 @readonly 

451 def ParentID(self) -> Nullable[int]: 

452 """ 

453 Read-only property to access the ID of the history node this one belongs to (:attr:`_parentID`). 

454 

455 :returns: The ID, e.g. of the merge, which merged this one; ``None``, if the report states none. 

456 """ 

457 return self._parentID 

458 

459 @readonly 

460 def PhysicalName(self) -> Nullable[Path]: 

461 """ 

462 Read-only property to access the path of the coverage database the history node wrote (:attr:`_physicalName`). 

463 

464 :returns: The path, as stated; ``None``, if the report states none. 

465 """ 

466 return self._physicalName 

467 

468 @readonly 

469 def Kind(self) -> Nullable[str]: 

470 """ 

471 Read-only property to access the kind of the history node (:attr:`_kind`). 

472 

473 :returns: The kind, as stated; ``None``, if the report states none. 

474 """ 

475 return self._kind 

476 

477 @readonly 

478 def SimulationTime(self) -> Nullable[float]: 

479 """ 

480 Read-only property to access how long the test ran in simulated time (:attr:`_simulationTime`). 

481 

482 :returns: The time, in :attr:`TimeUnit`; ``None``, if the report states none. 

483 """ 

484 return self._simulationTime 

485 

486 @readonly 

487 def TimeUnit(self) -> Nullable[str]: 

488 """ 

489 Read-only property to access the unit of the simulated time (:attr:`_timeUnit`). 

490 

491 :returns: The unit, e.g. ``ns``; ``None``, if the report states none. 

492 """ 

493 return self._timeUnit 

494 

495 @readonly 

496 def RunDirectory(self) -> Nullable[Path]: 

497 """ 

498 Read-only property to access the directory the test ran in (:attr:`_runDirectory`). 

499 

500 It is a path on the machine the coverage was measured on, so it may not exist where the report is read. 

501 

502 :returns: The directory; ``None``, if the report states none. 

503 """ 

504 return self._runDirectory 

505 

506 @readonly 

507 def CPUTime(self) -> Nullable[float]: 

508 """ 

509 Read-only property to access how long the test ran in CPU time (:attr:`_cpuTime`). 

510 

511 :returns: The time; ``None``, if the report states none. 

512 """ 

513 return self._cpuTime 

514 

515 @readonly 

516 def Seed(self) -> Nullable[str]: 

517 """ 

518 Read-only property to access the seed of the test's random numbers (:attr:`_seed`). 

519 

520 :returns: The seed; ``None``, if the report states none. 

521 """ 

522 return self._seed 

523 

524 @readonly 

525 def Command(self) -> Nullable[str]: 

526 """ 

527 Read-only property to access the command, which ran the test (:attr:`_command`). 

528 

529 :returns: The command; ``None``, if the report states none. 

530 """ 

531 return self._command 

532 

533 @readonly 

534 def Arguments(self) -> Nullable[str]: 

535 """ 

536 Read-only property to access the command line arguments of the test (:attr:`_arguments`). 

537 

538 :returns: The arguments; ``None``, if the report states none. 

539 """ 

540 return self._arguments 

541 

542 @readonly 

543 def Compulsory(self) -> Nullable[str]: 

544 """ 

545 Read-only property to access whether the test must run (:attr:`_compulsory`). 

546 

547 :returns: The value, as stated; ``None``, if the report states none. 

548 """ 

549 return self._compulsory 

550 

551 @readonly 

552 def UserName(self) -> Nullable[str]: 

553 """ 

554 Read-only property to access the name of the user, who ran the test (:attr:`_userName`). 

555 

556 :returns: The name; ``None``, if the report states none. 

557 """ 

558 return self._userName 

559 

560 @readonly 

561 def Cost(self) -> Nullable[Decimal]: 

562 """ 

563 Read-only property to access the cost of the test (:attr:`_cost`). 

564 

565 :returns: The cost; ``None``, if the report states none. 

566 """ 

567 return self._cost 

568 

569 @readonly 

570 def SameTests(self) -> Nullable[int]: 

571 """ 

572 Read-only property to access the ID of a test this one is equivalent to (:attr:`_sameTests`). 

573 

574 :returns: The ID; ``None``, if the report states none. 

575 """ 

576 return self._sameTests 

577 

578 @readonly 

579 def Comment(self) -> Nullable[str]: 

580 """ 

581 Read-only property to access the comment (:attr:`_comment`). 

582 

583 :returns: The comment; ``None``, if the report states none. 

584 """ 

585 return self._comment