cocotb 2.0.0rc2__cp311-cp311-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cocotb might be problematic. Click here for more details.

Files changed (115) hide show
  1. cocotb/_ANSI.py +65 -0
  2. cocotb/__init__.py +125 -0
  3. cocotb/_base_triggers.py +515 -0
  4. cocotb/_bridge.py +186 -0
  5. cocotb/_decorators.py +515 -0
  6. cocotb/_deprecation.py +36 -0
  7. cocotb/_exceptions.py +7 -0
  8. cocotb/_extended_awaitables.py +419 -0
  9. cocotb/_gpi_triggers.py +385 -0
  10. cocotb/_init.py +301 -0
  11. cocotb/_outcomes.py +54 -0
  12. cocotb/_profiling.py +46 -0
  13. cocotb/_py_compat.py +148 -0
  14. cocotb/_scheduler.py +448 -0
  15. cocotb/_test.py +248 -0
  16. cocotb/_test_factory.py +312 -0
  17. cocotb/_test_functions.py +42 -0
  18. cocotb/_typing.py +7 -0
  19. cocotb/_utils.py +274 -0
  20. cocotb/_version.py +4 -0
  21. cocotb/_xunit_reporter.py +66 -0
  22. cocotb/clock.py +419 -0
  23. cocotb/debug.py +24 -0
  24. cocotb/handle.py +1752 -0
  25. cocotb/libs/libcocotb.so +0 -0
  26. cocotb/libs/libcocotbfli_modelsim.so +0 -0
  27. cocotb/libs/libcocotbutils.so +0 -0
  28. cocotb/libs/libcocotbvhpi_aldec.so +0 -0
  29. cocotb/libs/libcocotbvhpi_ius.so +0 -0
  30. cocotb/libs/libcocotbvhpi_modelsim.so +0 -0
  31. cocotb/libs/libcocotbvhpi_nvc.so +0 -0
  32. cocotb/libs/libcocotbvpi_aldec.so +0 -0
  33. cocotb/libs/libcocotbvpi_dsim.so +0 -0
  34. cocotb/libs/libcocotbvpi_ghdl.so +0 -0
  35. cocotb/libs/libcocotbvpi_icarus.vpl +0 -0
  36. cocotb/libs/libcocotbvpi_ius.so +0 -0
  37. cocotb/libs/libcocotbvpi_modelsim.so +0 -0
  38. cocotb/libs/libcocotbvpi_vcs.so +0 -0
  39. cocotb/libs/libcocotbvpi_verilator.so +0 -0
  40. cocotb/libs/libembed.so +0 -0
  41. cocotb/libs/libgpi.so +0 -0
  42. cocotb/libs/libgpilog.so +0 -0
  43. cocotb/libs/libpygpilog.so +0 -0
  44. cocotb/logging.py +424 -0
  45. cocotb/py.typed +0 -0
  46. cocotb/queue.py +225 -0
  47. cocotb/regression.py +896 -0
  48. cocotb/result.py +38 -0
  49. cocotb/share/def/.gitignore +2 -0
  50. cocotb/share/def/README.md +4 -0
  51. cocotb/share/def/aldec.def +61 -0
  52. cocotb/share/def/ghdl.def +43 -0
  53. cocotb/share/def/icarus.def +43 -0
  54. cocotb/share/def/modelsim.def +138 -0
  55. cocotb/share/include/cocotb_utils.h +70 -0
  56. cocotb/share/include/embed.h +33 -0
  57. cocotb/share/include/exports.h +20 -0
  58. cocotb/share/include/gpi.h +459 -0
  59. cocotb/share/include/gpi_logging.h +291 -0
  60. cocotb/share/include/py_gpi_logging.h +33 -0
  61. cocotb/share/include/vhpi_user_ext.h +26 -0
  62. cocotb/share/include/vpi_user_ext.h +33 -0
  63. cocotb/share/lib/verilator/verilator.cpp +209 -0
  64. cocotb/simtime.py +230 -0
  65. cocotb/simulator.cpython-311-darwin.so +0 -0
  66. cocotb/simulator.pyi +107 -0
  67. cocotb/task.py +590 -0
  68. cocotb/triggers.py +67 -0
  69. cocotb/types/__init__.py +31 -0
  70. cocotb/types/_abstract_array.py +151 -0
  71. cocotb/types/_array.py +295 -0
  72. cocotb/types/_indexing.py +17 -0
  73. cocotb/types/_logic.py +333 -0
  74. cocotb/types/_logic_array.py +868 -0
  75. cocotb/types/_range.py +197 -0
  76. cocotb/types/_resolve.py +76 -0
  77. cocotb/utils.py +110 -0
  78. cocotb-2.0.0rc2.dist-info/METADATA +60 -0
  79. cocotb-2.0.0rc2.dist-info/RECORD +115 -0
  80. cocotb-2.0.0rc2.dist-info/WHEEL +5 -0
  81. cocotb-2.0.0rc2.dist-info/entry_points.txt +2 -0
  82. cocotb-2.0.0rc2.dist-info/licenses/LICENSE +29 -0
  83. cocotb-2.0.0rc2.dist-info/top_level.txt +23 -0
  84. cocotb_tools/__init__.py +0 -0
  85. cocotb_tools/_coverage.py +33 -0
  86. cocotb_tools/_vendor/__init__.py +3 -0
  87. cocotb_tools/_vendor/distutils_version.py +346 -0
  88. cocotb_tools/check_results.py +65 -0
  89. cocotb_tools/combine_results.py +152 -0
  90. cocotb_tools/config.py +241 -0
  91. cocotb_tools/ipython_support.py +99 -0
  92. cocotb_tools/makefiles/Makefile.deprecations +27 -0
  93. cocotb_tools/makefiles/Makefile.inc +198 -0
  94. cocotb_tools/makefiles/Makefile.sim +96 -0
  95. cocotb_tools/makefiles/simulators/Makefile.activehdl +72 -0
  96. cocotb_tools/makefiles/simulators/Makefile.cvc +61 -0
  97. cocotb_tools/makefiles/simulators/Makefile.dsim +39 -0
  98. cocotb_tools/makefiles/simulators/Makefile.ghdl +84 -0
  99. cocotb_tools/makefiles/simulators/Makefile.icarus +80 -0
  100. cocotb_tools/makefiles/simulators/Makefile.ius +93 -0
  101. cocotb_tools/makefiles/simulators/Makefile.modelsim +9 -0
  102. cocotb_tools/makefiles/simulators/Makefile.nvc +60 -0
  103. cocotb_tools/makefiles/simulators/Makefile.questa +29 -0
  104. cocotb_tools/makefiles/simulators/Makefile.questa-compat +143 -0
  105. cocotb_tools/makefiles/simulators/Makefile.questa-qisqrun +149 -0
  106. cocotb_tools/makefiles/simulators/Makefile.riviera +144 -0
  107. cocotb_tools/makefiles/simulators/Makefile.vcs +65 -0
  108. cocotb_tools/makefiles/simulators/Makefile.verilator +79 -0
  109. cocotb_tools/makefiles/simulators/Makefile.xcelium +104 -0
  110. cocotb_tools/py.typed +0 -0
  111. cocotb_tools/runner.py +1868 -0
  112. cocotb_tools/sim_versions.py +140 -0
  113. pygpi/__init__.py +0 -0
  114. pygpi/entry.py +42 -0
  115. pygpi/py.typed +0 -0
@@ -0,0 +1,346 @@
1
+ # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2
+ # 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Python Software Foundation;
3
+ # All Rights Reserved
4
+ # Copyright cocotb contributors
5
+ # Licensed under the Revised BSD License, see LICENSE for details.
6
+ # SPDX-License-Identifier: BSD-3-Clause
7
+
8
+ #
9
+ # distutils/version.py
10
+ #
11
+ # Implements multiple version numbering conventions for the
12
+ # Python Module Distribution Utilities.
13
+ #
14
+ # $Id$
15
+ #
16
+
17
+ """Provides classes to represent module version numbers (one class for
18
+ each style of version numbering). There are currently two such classes
19
+ implemented: StrictVersion and LooseVersion.
20
+ Every version number class implements the following interface:
21
+ * the 'parse' method takes a string and parses it to some internal
22
+ representation; if the string is an invalid version number,
23
+ 'parse' raises a ValueError exception
24
+ * the class constructor takes an optional string argument which,
25
+ if supplied, is passed to 'parse'
26
+ * __str__ reconstructs the string that was passed to 'parse' (or
27
+ an equivalent string -- ie. one that will generate an equivalent
28
+ version number instance)
29
+ * __repr__ generates Python code to recreate the version number instance
30
+ * _cmp compares the current instance with either another instance
31
+ of the same class or a string (which will be parsed to an instance
32
+ of the same class, thus must follow the same rules)
33
+ """
34
+
35
+ import re
36
+
37
+ class Version:
38
+ """Abstract base class for version numbering classes. Just provides
39
+ constructor (__init__) and reproducer (__repr__), because those
40
+ seem to be the same for all version numbering classes; and route
41
+ rich comparisons to _cmp.
42
+ """
43
+
44
+ def __init__ (self, vstring=None):
45
+ if vstring:
46
+ self.parse(vstring)
47
+
48
+ def __repr__ (self):
49
+ return "%s ('%s')" % (self.__class__.__name__, str(self))
50
+
51
+ def __eq__(self, other):
52
+ c = self._cmp(other)
53
+ if c is NotImplemented:
54
+ return c
55
+ return c == 0
56
+
57
+ def __lt__(self, other):
58
+ c = self._cmp(other)
59
+ if c is NotImplemented:
60
+ return c
61
+ return c < 0
62
+
63
+ def __le__(self, other):
64
+ c = self._cmp(other)
65
+ if c is NotImplemented:
66
+ return c
67
+ return c <= 0
68
+
69
+ def __gt__(self, other):
70
+ c = self._cmp(other)
71
+ if c is NotImplemented:
72
+ return c
73
+ return c > 0
74
+
75
+ def __ge__(self, other):
76
+ c = self._cmp(other)
77
+ if c is NotImplemented:
78
+ return c
79
+ return c >= 0
80
+
81
+
82
+ # Interface for version-number classes -- must be implemented
83
+ # by the following classes (the concrete ones -- Version should
84
+ # be treated as an abstract class).
85
+ # __init__ (string) - create and take same action as 'parse'
86
+ # (string parameter is optional)
87
+ # parse (string) - convert a string representation to whatever
88
+ # internal representation is appropriate for
89
+ # this style of version numbering
90
+ # __str__ (self) - convert back to a string; should be very similar
91
+ # (if not identical to) the string supplied to parse
92
+ # __repr__ (self) - generate Python code to recreate
93
+ # the instance
94
+ # _cmp (self, other) - compare two version numbers ('other' may
95
+ # be an unparsed version string, or another
96
+ # instance of your version class)
97
+
98
+
99
+ class StrictVersion (Version):
100
+
101
+ """Version numbering for anal retentives and software idealists.
102
+ Implements the standard interface for version number classes as
103
+ described above. A version number consists of two or three
104
+ dot-separated numeric components, with an optional "pre-release" tag
105
+ on the end. The pre-release tag consists of the letter 'a' or 'b'
106
+ followed by a number. If the numeric components of two version
107
+ numbers are equal, then one with a pre-release tag will always
108
+ be deemed earlier (lesser) than one without.
109
+ The following are valid version numbers (shown in the order that
110
+ would be obtained by sorting according to the supplied cmp function):
111
+ 0.4 0.4.0 (these two are equivalent)
112
+ 0.4.1
113
+ 0.5a1
114
+ 0.5b3
115
+ 0.5
116
+ 0.9.6
117
+ 1.0
118
+ 1.0.4a3
119
+ 1.0.4b1
120
+ 1.0.4
121
+ The following are examples of invalid version numbers:
122
+ 1
123
+ 2.7.2.2
124
+ 1.3.a4
125
+ 1.3pl1
126
+ 1.3c4
127
+ The rationale for this version numbering system will be explained
128
+ in the distutils documentation.
129
+ """
130
+
131
+ version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
132
+ re.VERBOSE | re.ASCII)
133
+
134
+
135
+ def parse (self, vstring):
136
+ match = self.version_re.match(vstring)
137
+ if not match:
138
+ raise ValueError("invalid version number '%s'" % vstring)
139
+
140
+ (major, minor, patch, prerelease, prerelease_num) = \
141
+ match.group(1, 2, 4, 5, 6)
142
+
143
+ if patch:
144
+ self.version = tuple(map(int, [major, minor, patch]))
145
+ else:
146
+ self.version = tuple(map(int, [major, minor])) + (0,)
147
+
148
+ if prerelease:
149
+ self.prerelease = (prerelease[0], int(prerelease_num))
150
+ else:
151
+ self.prerelease = None
152
+
153
+
154
+ def __str__ (self):
155
+
156
+ if self.version[2] == 0:
157
+ vstring = '.'.join(map(str, self.version[0:2]))
158
+ else:
159
+ vstring = '.'.join(map(str, self.version))
160
+
161
+ if self.prerelease:
162
+ vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
163
+
164
+ return vstring
165
+
166
+
167
+ def _cmp (self, other):
168
+ if isinstance(other, str):
169
+ other = StrictVersion(other)
170
+ elif not isinstance(other, StrictVersion):
171
+ return NotImplemented
172
+
173
+ if self.version != other.version:
174
+ # numeric versions don't match
175
+ # prerelease stuff doesn't matter
176
+ if self.version < other.version:
177
+ return -1
178
+ else:
179
+ return 1
180
+
181
+ # have to compare prerelease
182
+ # case 1: neither has prerelease; they're equal
183
+ # case 2: self has prerelease, other doesn't; other is greater
184
+ # case 3: self doesn't have prerelease, other does: self is greater
185
+ # case 4: both have prerelease: must compare them!
186
+
187
+ if (not self.prerelease and not other.prerelease):
188
+ return 0
189
+ elif (self.prerelease and not other.prerelease):
190
+ return -1
191
+ elif (not self.prerelease and other.prerelease):
192
+ return 1
193
+ elif (self.prerelease and other.prerelease):
194
+ if self.prerelease == other.prerelease:
195
+ return 0
196
+ elif self.prerelease < other.prerelease:
197
+ return -1
198
+ else:
199
+ return 1
200
+ else:
201
+ assert False, "never get here"
202
+
203
+ # end class StrictVersion
204
+
205
+
206
+ # The rules according to Greg Stein:
207
+ # 1) a version number has 1 or more numbers separated by a period or by
208
+ # sequences of letters. If only periods, then these are compared
209
+ # left-to-right to determine an ordering.
210
+ # 2) sequences of letters are part of the tuple for comparison and are
211
+ # compared lexicographically
212
+ # 3) recognize the numeric components may have leading zeroes
213
+ #
214
+ # The LooseVersion class below implements these rules: a version number
215
+ # string is split up into a tuple of integer and string components, and
216
+ # comparison is a simple tuple comparison. This means that version
217
+ # numbers behave in a predictable and obvious way, but a way that might
218
+ # not necessarily be how people *want* version numbers to behave. There
219
+ # wouldn't be a problem if people could stick to purely numeric version
220
+ # numbers: just split on period and compare the numbers as tuples.
221
+ # However, people insist on putting letters into their version numbers;
222
+ # the most common purpose seems to be:
223
+ # - indicating a "pre-release" version
224
+ # ('alpha', 'beta', 'a', 'b', 'pre', 'p')
225
+ # - indicating a post-release patch ('p', 'pl', 'patch')
226
+ # but of course this can't cover all version number schemes, and there's
227
+ # no way to know what a programmer means without asking him.
228
+ #
229
+ # The problem is what to do with letters (and other non-numeric
230
+ # characters) in a version number. The current implementation does the
231
+ # obvious and predictable thing: keep them as strings and compare
232
+ # lexically within a tuple comparison. This has the desired effect if
233
+ # an appended letter sequence implies something "post-release":
234
+ # eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
235
+ #
236
+ # However, if letters in a version number imply a pre-release version,
237
+ # the "obvious" thing isn't correct. Eg. you would expect that
238
+ # "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
239
+ # implemented here, this just isn't so.
240
+ #
241
+ # Two possible solutions come to mind. The first is to tie the
242
+ # comparison algorithm to a particular set of semantic rules, as has
243
+ # been done in the StrictVersion class above. This works great as long
244
+ # as everyone can go along with bondage and discipline. Hopefully a
245
+ # (large) subset of Python module programmers will agree that the
246
+ # particular flavour of bondage and discipline provided by StrictVersion
247
+ # provides enough benefit to be worth using, and will submit their
248
+ # version numbering scheme to its domination. The free-thinking
249
+ # anarchists in the lot will never give in, though, and something needs
250
+ # to be done to accommodate them.
251
+ #
252
+ # Perhaps a "moderately strict" version class could be implemented that
253
+ # lets almost anything slide (syntactically), and makes some heuristic
254
+ # assumptions about non-digits in version number strings. This could
255
+ # sink into special-case-hell, though; if I was as talented and
256
+ # idiosyncratic as Larry Wall, I'd go ahead and implement a class that
257
+ # somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
258
+ # just as happy dealing with things like "2g6" and "1.13++". I don't
259
+ # think I'm smart enough to do it right though.
260
+ #
261
+ # In any case, I've coded the test suite for this module (see
262
+ # ../test/test_version.py) specifically to fail on things like comparing
263
+ # "1.2a2" and "1.2". That's not because the *code* is doing anything
264
+ # wrong, it's because the simple, obvious design doesn't match my
265
+ # complicated, hairy expectations for real-world version numbers. It
266
+ # would be a snap to fix the test suite to say, "Yep, LooseVersion does
267
+ # the Right Thing" (ie. the code matches the conception). But I'd rather
268
+ # have a conception that matches common notions about version numbers.
269
+
270
+ class LooseVersion (Version):
271
+
272
+ """Version numbering for anarchists and software realists.
273
+ Implements the standard interface for version number classes as
274
+ described above. A version number consists of a series of numbers,
275
+ separated by either periods or strings of letters. When comparing
276
+ version numbers, the numeric components will be compared
277
+ numerically, and the alphabetic components lexically. The following
278
+ are all valid version numbers, in no particular order:
279
+ 1.5.1
280
+ 1.5.2b2
281
+ 161
282
+ 3.10a
283
+ 8.02
284
+ 3.4j
285
+ 1996.07.12
286
+ 3.2.pl0
287
+ 3.1.1.6
288
+ 2g6
289
+ 11g
290
+ 0.960923
291
+ 2.2beta29
292
+ 1.13++
293
+ 5.5.kw
294
+ 2.0b1pl0
295
+ In fact, there is no such thing as an invalid version number under
296
+ this scheme; the rules for comparison are simple and predictable,
297
+ but may not always give the results you want (for some definition
298
+ of "want").
299
+ """
300
+
301
+ component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
302
+
303
+ def __init__ (self, vstring=None):
304
+ if vstring:
305
+ self.parse(vstring)
306
+
307
+
308
+ def parse (self, vstring):
309
+ # I've given up on thinking I can reconstruct the version string
310
+ # from the parsed tuple -- so I just store the string here for
311
+ # use by __str__
312
+ self.vstring = vstring
313
+ components = [x for x in self.component_re.split(vstring)
314
+ if x and x != '.']
315
+ for i, obj in enumerate(components):
316
+ try:
317
+ components[i] = int(obj)
318
+ except ValueError:
319
+ pass
320
+
321
+ self.version = components
322
+
323
+
324
+ def __str__ (self):
325
+ return self.vstring
326
+
327
+
328
+ def __repr__ (self):
329
+ return "LooseVersion ('%s')" % str(self)
330
+
331
+
332
+ def _cmp (self, other):
333
+ if isinstance(other, str):
334
+ other = LooseVersion(other)
335
+ elif not isinstance(other, LooseVersion):
336
+ return NotImplemented
337
+
338
+ if self.version == other.version:
339
+ return 0
340
+ if self.version < other.version:
341
+ return -1
342
+ if self.version > other.version:
343
+ return 1
344
+
345
+
346
+ # end class LooseVersion
@@ -0,0 +1,65 @@
1
+ # Copyright cocotb contributors
2
+ # Licensed under the Revised BSD License, see LICENSE for details.
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+ """Checks if a JUnit results file exists and whether there was failing tests."""
5
+
6
+ import argparse
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Tuple
10
+ from xml.etree import ElementTree
11
+
12
+
13
+ def get_results(results_xml_file: Path) -> Tuple[int, int]:
14
+ """Return number of tests and fails in *results_xml_file*.
15
+
16
+ Returns:
17
+ Tuple of number of tests and number of fails.
18
+
19
+ Raises:
20
+ RuntimeError: If *results_xml_file* is non-existent.
21
+ """
22
+
23
+ __tracebackhide__ = True # Hide the traceback when using PyTest.
24
+
25
+ if not results_xml_file.is_file():
26
+ raise RuntimeError(
27
+ f"ERROR: Simulation terminated abnormally. Results file {results_xml_file} not found."
28
+ )
29
+
30
+ num_tests = 0
31
+ num_failed = 0
32
+
33
+ tree = ElementTree.parse(results_xml_file)
34
+ for ts in tree.iter("testsuite"):
35
+ for tc in ts.iter("testcase"):
36
+ num_tests += 1
37
+ for _ in tc.iter("failure"):
38
+ num_failed += 1
39
+
40
+ return (num_tests, num_failed)
41
+
42
+
43
+ def _get_parser() -> argparse.ArgumentParser:
44
+ """Return the cmdline parser"""
45
+ parser = argparse.ArgumentParser(description=__doc__)
46
+ parser.add_argument(
47
+ "results_file", help="Path to XML file holding JUnit test results.", type=Path
48
+ )
49
+ return parser
50
+
51
+
52
+ def main() -> int:
53
+ parser = _get_parser()
54
+ args = parser.parse_args()
55
+
56
+ try:
57
+ (_, num_failed) = get_results(args.results_file)
58
+ except RuntimeError:
59
+ return 1
60
+ return num_failed
61
+
62
+
63
+ if __name__ == "__main__":
64
+ rc = main()
65
+ sys.exit(rc)
@@ -0,0 +1,152 @@
1
+ # Copyright cocotb contributors
2
+ # Licensed under the Revised BSD License, see LICENSE for details.
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+ #!/usr/bin/env python
5
+ """
6
+ Simple script to combine JUnit test results into a single XML file.
7
+ """
8
+
9
+ import argparse
10
+ import os
11
+ import re
12
+ import sys
13
+ from pathlib import Path
14
+ from typing import Iterable, Pattern
15
+ from xml.etree import ElementTree as ET
16
+
17
+
18
+ def _find_all(name: Pattern, path: Path) -> Iterable[Path]:
19
+ for obj in path.iterdir():
20
+ if obj.is_file() and re.match(name, obj.name):
21
+ yield obj
22
+ elif obj.is_dir():
23
+ yield from _find_all(name, obj)
24
+
25
+
26
+ def _get_parser() -> argparse.ArgumentParser:
27
+ """Return the cmdline parser"""
28
+ parser = argparse.ArgumentParser(
29
+ description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter
30
+ )
31
+ parser.add_argument(
32
+ "directories",
33
+ nargs="*",
34
+ type=lambda args: [Path(arg) for arg in args],
35
+ default=[Path()],
36
+ help="Directories to search for input files.",
37
+ )
38
+ parser.add_argument(
39
+ "-i",
40
+ "--input-filename",
41
+ default=r"results.*\.xml",
42
+ help="A regular expression to match input filenames.",
43
+ )
44
+ parser.add_argument(
45
+ "-o",
46
+ "--output-file",
47
+ default="combined_results.xml",
48
+ help="Path of output XML file.",
49
+ )
50
+ parser.add_argument(
51
+ "--output-testsuites-name",
52
+ default="results",
53
+ help="Name of 'testsuites' element in output XML file.",
54
+ )
55
+ parser.add_argument(
56
+ "--verbose",
57
+ action="store_true",
58
+ help="Enables verbose output.",
59
+ )
60
+ parser.add_argument(
61
+ "--repo-root",
62
+ type=Path,
63
+ help="Specify root of cocotb repo the regression is run from (CI only).",
64
+ )
65
+ return parser
66
+
67
+
68
+ def main() -> int:
69
+ parser = _get_parser()
70
+ args = parser.parse_args()
71
+ rc = 0
72
+
73
+ result = ET.Element("testsuites", name=args.output_testsuites_name)
74
+
75
+ input_pattern = re.compile(args.input_filename)
76
+
77
+ for directory in args.directories:
78
+ if args.verbose:
79
+ print(f"Searching in {directory} for results.xml files.")
80
+ for fname in _find_all(input_pattern, directory):
81
+ if args.verbose:
82
+ print(f"Reading file {fname}.")
83
+ tree = ET.parse(fname)
84
+ for ts in tree.iter("testsuite"):
85
+ if args.verbose:
86
+ print(
87
+ "Testsuite name: {!r}, package: {!r}".format(
88
+ ts.get("name"), ts.get("package")
89
+ )
90
+ )
91
+ for existing in result:
92
+ if (existing.get("name") == ts.get("name")) and (
93
+ existing.get("package") == ts.get("package")
94
+ ):
95
+ if args.verbose:
96
+ print(
97
+ "Testsuite already exists in combined results. Extending it."
98
+ )
99
+ existing.extend(list(ts))
100
+ break
101
+ else:
102
+ if args.verbose:
103
+ print(
104
+ "Testsuite does not already exist in combined results. Adding it."
105
+ )
106
+ result.append(ts)
107
+
108
+ testsuite_count = 0
109
+ testcase_count = 0
110
+ for testsuite in result.iter("testsuite"):
111
+ testsuite_count += 1
112
+ for testcase in testsuite.iter("testcase"):
113
+ testcase_count += 1
114
+ for _ in testcase.iter("failure"):
115
+ rc = 1
116
+ print(
117
+ "Failure in testsuite: '{}' classname: '{}' testcase: '{}' with parameters '{}'".format(
118
+ testsuite.get("name"),
119
+ testcase.get("classname"),
120
+ testcase.get("name"),
121
+ testsuite.get("package"),
122
+ )
123
+ )
124
+ if (
125
+ os.getenv("GITHUB_ACTIONS") is not None
126
+ and args.repo_root is not None
127
+ ):
128
+ # Get test file relative to root of repo
129
+ file = testcase.get("file")
130
+ # if this file was output by cocotb, it has this attribute
131
+ assert file is not None
132
+ relative_file = Path(file).relative_to(args.repo_root)
133
+ print(
134
+ "::error file={},line={}::Test {}:{} failed".format(
135
+ relative_file,
136
+ testcase.get("lineno"),
137
+ testcase.get("classname"),
138
+ testcase.get("name"),
139
+ )
140
+ )
141
+
142
+ print(f"Ran a total of {testsuite_count} TestSuites and {testcase_count} TestCases")
143
+
144
+ if args.verbose:
145
+ print(f"Writing combined results to {args.output_file}")
146
+ ET.ElementTree(result).write(args.output_file, encoding="UTF-8")
147
+ return rc
148
+
149
+
150
+ if __name__ == "__main__":
151
+ rc = main()
152
+ sys.exit(rc)