jolt 0.9.76__py3-none-any.whl → 0.9.429__py3-none-any.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.
Files changed (201) hide show
  1. jolt/__init__.py +88 -7
  2. jolt/__main__.py +9 -1
  3. jolt/bin/fstree-darwin-x86_64 +0 -0
  4. jolt/bin/fstree-linux-x86_64 +0 -0
  5. jolt/cache.py +839 -367
  6. jolt/chroot.py +156 -0
  7. jolt/cli.py +362 -143
  8. jolt/common_pb2.py +63 -0
  9. jolt/common_pb2_grpc.py +4 -0
  10. jolt/config.py +99 -42
  11. jolt/error.py +19 -4
  12. jolt/expires.py +2 -2
  13. jolt/filesystem.py +8 -6
  14. jolt/graph.py +705 -117
  15. jolt/hooks.py +63 -1
  16. jolt/influence.py +129 -6
  17. jolt/loader.py +369 -121
  18. jolt/log.py +225 -63
  19. jolt/manifest.py +28 -38
  20. jolt/options.py +35 -10
  21. jolt/pkgs/abseil.py +42 -0
  22. jolt/pkgs/asio.py +25 -0
  23. jolt/pkgs/autoconf.py +41 -0
  24. jolt/pkgs/automake.py +41 -0
  25. jolt/pkgs/b2.py +31 -0
  26. jolt/pkgs/boost.py +111 -0
  27. jolt/pkgs/boringssl.py +32 -0
  28. jolt/pkgs/busybox.py +39 -0
  29. jolt/pkgs/bzip2.py +43 -0
  30. jolt/pkgs/cares.py +29 -0
  31. jolt/pkgs/catch2.py +36 -0
  32. jolt/pkgs/cbindgen.py +17 -0
  33. jolt/pkgs/cista.py +19 -0
  34. jolt/pkgs/clang.py +44 -0
  35. jolt/pkgs/cli11.py +23 -0
  36. jolt/pkgs/cmake.py +48 -0
  37. jolt/pkgs/cpython.py +196 -0
  38. jolt/pkgs/crun.py +29 -0
  39. jolt/pkgs/curl.py +38 -0
  40. jolt/pkgs/dbus.py +18 -0
  41. jolt/pkgs/double_conversion.py +24 -0
  42. jolt/pkgs/fastfloat.py +21 -0
  43. jolt/pkgs/ffmpeg.py +28 -0
  44. jolt/pkgs/flatbuffers.py +29 -0
  45. jolt/pkgs/fmt.py +27 -0
  46. jolt/pkgs/fstree.py +20 -0
  47. jolt/pkgs/gflags.py +18 -0
  48. jolt/pkgs/glib.py +18 -0
  49. jolt/pkgs/glog.py +25 -0
  50. jolt/pkgs/glslang.py +21 -0
  51. jolt/pkgs/golang.py +16 -11
  52. jolt/pkgs/googlebenchmark.py +18 -0
  53. jolt/pkgs/googletest.py +46 -0
  54. jolt/pkgs/gperf.py +15 -0
  55. jolt/pkgs/grpc.py +73 -0
  56. jolt/pkgs/hdf5.py +19 -0
  57. jolt/pkgs/help2man.py +14 -0
  58. jolt/pkgs/inja.py +28 -0
  59. jolt/pkgs/jsoncpp.py +31 -0
  60. jolt/pkgs/libarchive.py +43 -0
  61. jolt/pkgs/libcap.py +44 -0
  62. jolt/pkgs/libdrm.py +44 -0
  63. jolt/pkgs/libedit.py +42 -0
  64. jolt/pkgs/libevent.py +31 -0
  65. jolt/pkgs/libexpat.py +27 -0
  66. jolt/pkgs/libfastjson.py +21 -0
  67. jolt/pkgs/libffi.py +16 -0
  68. jolt/pkgs/libglvnd.py +30 -0
  69. jolt/pkgs/libogg.py +28 -0
  70. jolt/pkgs/libpciaccess.py +18 -0
  71. jolt/pkgs/libseccomp.py +21 -0
  72. jolt/pkgs/libtirpc.py +24 -0
  73. jolt/pkgs/libtool.py +42 -0
  74. jolt/pkgs/libunwind.py +35 -0
  75. jolt/pkgs/libva.py +18 -0
  76. jolt/pkgs/libvorbis.py +33 -0
  77. jolt/pkgs/libxml2.py +35 -0
  78. jolt/pkgs/libxslt.py +17 -0
  79. jolt/pkgs/libyajl.py +16 -0
  80. jolt/pkgs/llvm.py +81 -0
  81. jolt/pkgs/lua.py +54 -0
  82. jolt/pkgs/lz4.py +26 -0
  83. jolt/pkgs/m4.py +14 -0
  84. jolt/pkgs/make.py +17 -0
  85. jolt/pkgs/mesa.py +81 -0
  86. jolt/pkgs/meson.py +17 -0
  87. jolt/pkgs/mstch.py +28 -0
  88. jolt/pkgs/mysql.py +60 -0
  89. jolt/pkgs/nasm.py +49 -0
  90. jolt/pkgs/ncurses.py +30 -0
  91. jolt/pkgs/ng_log.py +25 -0
  92. jolt/pkgs/ninja.py +45 -0
  93. jolt/pkgs/nlohmann_json.py +25 -0
  94. jolt/pkgs/nodejs.py +19 -11
  95. jolt/pkgs/opencv.py +24 -0
  96. jolt/pkgs/openjdk.py +26 -0
  97. jolt/pkgs/openssl.py +103 -0
  98. jolt/pkgs/paho.py +76 -0
  99. jolt/pkgs/patchelf.py +16 -0
  100. jolt/pkgs/perl.py +42 -0
  101. jolt/pkgs/pkgconfig.py +64 -0
  102. jolt/pkgs/poco.py +39 -0
  103. jolt/pkgs/protobuf.py +77 -0
  104. jolt/pkgs/pugixml.py +27 -0
  105. jolt/pkgs/python.py +19 -0
  106. jolt/pkgs/qt.py +35 -0
  107. jolt/pkgs/rapidjson.py +26 -0
  108. jolt/pkgs/rapidyaml.py +28 -0
  109. jolt/pkgs/re2.py +30 -0
  110. jolt/pkgs/re2c.py +17 -0
  111. jolt/pkgs/readline.py +15 -0
  112. jolt/pkgs/rust.py +41 -0
  113. jolt/pkgs/sdl.py +28 -0
  114. jolt/pkgs/simdjson.py +27 -0
  115. jolt/pkgs/soci.py +46 -0
  116. jolt/pkgs/spdlog.py +29 -0
  117. jolt/pkgs/spirv_llvm.py +21 -0
  118. jolt/pkgs/spirv_tools.py +24 -0
  119. jolt/pkgs/sqlite.py +83 -0
  120. jolt/pkgs/ssl.py +12 -0
  121. jolt/pkgs/texinfo.py +15 -0
  122. jolt/pkgs/tomlplusplus.py +22 -0
  123. jolt/pkgs/wayland.py +26 -0
  124. jolt/pkgs/x11.py +58 -0
  125. jolt/pkgs/xerces_c.py +20 -0
  126. jolt/pkgs/xorg.py +360 -0
  127. jolt/pkgs/xz.py +29 -0
  128. jolt/pkgs/yamlcpp.py +30 -0
  129. jolt/pkgs/zeromq.py +47 -0
  130. jolt/pkgs/zlib.py +69 -0
  131. jolt/pkgs/zstd.py +33 -0
  132. jolt/plugins/alias.py +3 -0
  133. jolt/plugins/allure.py +5 -2
  134. jolt/plugins/autotools.py +66 -0
  135. jolt/plugins/cache.py +133 -0
  136. jolt/plugins/cmake.py +74 -6
  137. jolt/plugins/conan.py +238 -0
  138. jolt/plugins/cxx.py +698 -0
  139. jolt/plugins/cxxinfo.py +7 -0
  140. jolt/plugins/dashboard.py +1 -1
  141. jolt/plugins/docker.py +91 -23
  142. jolt/plugins/email.py +5 -2
  143. jolt/plugins/email.xslt +144 -101
  144. jolt/plugins/environ.py +11 -0
  145. jolt/plugins/fetch.py +141 -0
  146. jolt/plugins/gdb.py +44 -21
  147. jolt/plugins/gerrit.py +1 -14
  148. jolt/plugins/git.py +316 -101
  149. jolt/plugins/googletest.py +522 -1
  150. jolt/plugins/http.py +36 -38
  151. jolt/plugins/libtool.py +63 -0
  152. jolt/plugins/linux.py +990 -0
  153. jolt/plugins/logstash.py +4 -4
  154. jolt/plugins/meson.py +61 -0
  155. jolt/plugins/ninja-compdb.py +107 -31
  156. jolt/plugins/ninja.py +929 -134
  157. jolt/plugins/paths.py +11 -1
  158. jolt/plugins/pkgconfig.py +219 -0
  159. jolt/plugins/podman.py +148 -91
  160. jolt/plugins/python.py +137 -0
  161. jolt/plugins/remote_execution/__init__.py +0 -0
  162. jolt/plugins/remote_execution/administration_pb2.py +46 -0
  163. jolt/plugins/remote_execution/administration_pb2_grpc.py +170 -0
  164. jolt/plugins/remote_execution/log_pb2.py +32 -0
  165. jolt/plugins/remote_execution/log_pb2_grpc.py +68 -0
  166. jolt/plugins/remote_execution/scheduler_pb2.py +41 -0
  167. jolt/plugins/remote_execution/scheduler_pb2_grpc.py +141 -0
  168. jolt/plugins/remote_execution/worker_pb2.py +38 -0
  169. jolt/plugins/remote_execution/worker_pb2_grpc.py +112 -0
  170. jolt/plugins/report.py +12 -2
  171. jolt/plugins/rust.py +25 -0
  172. jolt/plugins/scheduler.py +710 -0
  173. jolt/plugins/selfdeploy/setup.py +9 -4
  174. jolt/plugins/selfdeploy.py +138 -88
  175. jolt/plugins/strings.py +35 -22
  176. jolt/plugins/symlinks.py +26 -11
  177. jolt/plugins/telemetry.py +5 -2
  178. jolt/plugins/timeline.py +13 -3
  179. jolt/plugins/volume.py +46 -48
  180. jolt/scheduler.py +591 -191
  181. jolt/tasks.py +1783 -245
  182. jolt/templates/export.sh.template +12 -6
  183. jolt/templates/timeline.html.template +44 -47
  184. jolt/timer.py +22 -0
  185. jolt/tools.py +749 -302
  186. jolt/utils.py +245 -18
  187. jolt/version.py +1 -1
  188. jolt/version_utils.py +2 -2
  189. jolt/xmldom.py +12 -2
  190. {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/METADATA +98 -38
  191. jolt-0.9.429.dist-info/RECORD +207 -0
  192. {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/WHEEL +1 -1
  193. jolt/plugins/amqp.py +0 -834
  194. jolt/plugins/debian.py +0 -338
  195. jolt/plugins/ftp.py +0 -181
  196. jolt/plugins/ninja-cache.py +0 -64
  197. jolt/plugins/ninjacli.py +0 -271
  198. jolt/plugins/repo.py +0 -253
  199. jolt-0.9.76.dist-info/RECORD +0 -79
  200. {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/entry_points.txt +0 -0
  201. {jolt-0.9.76.dist-info → jolt-0.9.429.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,528 @@
1
+ """
2
+ This module provides the :class:`GTestRunner` task base class, a derivation
3
+ of :class:`jolt.tasks.Runner` for running Google Test applications.
4
+ Several parameters can be assigned or overidden to control the behavior.
5
+
6
+ Example:
7
+
8
+ .. literalinclude:: ../examples/googletest/runner.jolt
9
+ :language: python
10
+ :caption: examples/googletest/runner.jolt
11
+
12
+ The module also provides a set of task decorators for use with standalone
13
+ task classes. They set different ``GTEST_*`` environment variables to control
14
+ the behavior of Google Test executables in the same manner as the parameters in
15
+ :class:`GTestRunner` do.
16
+
17
+
18
+ Example:
19
+
20
+ .. code-block:: python
21
+
22
+ from jolt import Task
23
+ from jolt.plugins import googletest
24
+
25
+ # ....
26
+
27
+ @googletest.fail_fast(default=True)
28
+ @googletest.repeat(default=10)
29
+ @googletest.filter()
30
+ @googletest.junit_report()
31
+ class TestRunner(Task):
32
+ requires = ["test"]
33
+
34
+ def run(self, deps, tools):
35
+ tools.run("unittest")
36
+
37
+ .. code-block:: bash
38
+
39
+ $ jolt build testrunner:repeat=1,filter=TestModule.*
40
+
41
+ """
42
+
43
+ from functools import wraps
44
+ from os import path
45
+ import json
46
+
47
+ from jolt import BooleanParameter, IntParameter, Parameter, Runner
48
+ from jolt import influence
49
+ from jolt.error import raise_task_error, raise_task_error_if
1
50
  from jolt.plugins import junit
2
- from jolt.utils import deprecated
51
+ from jolt.utils import deprecated, ignore_exception
3
52
 
4
53
 
5
54
  @deprecated
6
55
  def import_failures(xml, report):
7
56
  return junit.import_junit_report(xml, report, errors=True, failures=True)
57
+
58
+
59
+ def break_on_failure(default: bool = False, param: bool = True, attr: str = "break_on_failure"):
60
+ """
61
+ Task class decorator controlling the GTEST_BREAK_ON_FAILURE environment variable.
62
+
63
+ When the variable is set, test applications failures trigger breakpoints. On Linux,
64
+ this typically results in a core dump that can be loaded into the debugger.
65
+
66
+ The value is taken from the parameter ``break_on_failure`` which is created
67
+ by default. If no parameter is created, the value is instead taken from the class
68
+ attribute with the same name. The name of the paramter/attribute can be changed.
69
+
70
+ Args:
71
+ default (boolean): Default value of the parameter and env. variable.
72
+ Default: False.
73
+ param (boolean): Create a task parameter. Default: True.
74
+ attr (str): Name of parameter or class attribute.
75
+
76
+ """
77
+
78
+ def decorator(cls):
79
+ _old_run = cls.run
80
+
81
+ @wraps(cls.run)
82
+ def run(self, deps, tools):
83
+ with tools.environ(GTEST_BREAK_ON_FAILURE=str(int(bool(getattr(self, attr, default))))):
84
+ _old_run(self, deps, tools)
85
+
86
+ if param:
87
+ break_on_failure_param = BooleanParameter(default, help="Trigger breakpoint on failures.")
88
+ break_on_failure_param.__set_name__(cls, attr)
89
+ setattr(cls, attr, break_on_failure_param)
90
+
91
+ cls.run = run
92
+ return influence.source("googletest.break_on_failure", break_on_failure)(cls)
93
+
94
+ return decorator
95
+
96
+
97
+ def brief(default: bool = False, param: bool = True, attr: str = "brief"):
98
+ """
99
+ Task class decorator controlling the GTEST_BRIEF environment variable.
100
+
101
+ When the variable is set, only failed test-cases are logged.
102
+
103
+ The value is taken from the parameter ``brief`` which is created
104
+ by default. If no parameter is created, the value is instead taken from the class
105
+ attribute with the same name. The name of the paramter/attribute can be changed.
106
+
107
+ GoogleTest version >= 1.11 is required.
108
+
109
+ Args:
110
+ default (boolean): Default value of the parameter and env. variable.
111
+ Default: False.
112
+ param (boolean): Create a task parameter. Default: True.
113
+ attr (str): Name of parameter or class attribute.
114
+
115
+ """
116
+
117
+ def decorator(cls):
118
+ _old_run = cls.run
119
+
120
+ @wraps(cls.run)
121
+ def run(self, deps, tools):
122
+ with tools.environ(GTEST_BRIEF=str(int(bool(getattr(self, attr, default))))):
123
+ _old_run(self, deps, tools)
124
+
125
+ if param:
126
+ brief_param = BooleanParameter(default, help="Only print failed test-cases.")
127
+ brief_param.__set_name__(cls, attr)
128
+ setattr(cls, attr, brief_param)
129
+
130
+ cls.run = run
131
+ return influence.source("googletest.brief", brief)(cls)
132
+
133
+ return decorator
134
+
135
+
136
+ def disabled(default: bool = False, param: bool = True, attr: str = "disabled"):
137
+ """
138
+ Task class decorator controlling the GTEST_ALSO_RUN_DISABLED_TESTS environment variable.
139
+
140
+ When the variable is set, disabled test-cases are also run.
141
+
142
+ The value is taken from the parameter ``disabled`` which is created
143
+ by default. If no parameter is created, the value is instead taken from the class
144
+ attribute with the same name. The name of the paramter/attribute can be changed.
145
+
146
+ Args:
147
+ default (boolean): Default value of the parameter and env. variable.
148
+ Default: False.
149
+ param (boolean): Create a task parameter. Default: True.
150
+ attr (str): Name of parameter or class attribute.
151
+
152
+ """
153
+
154
+ def decorator(cls):
155
+ _old_run = cls.run
156
+
157
+ @wraps(cls.run)
158
+ def run(self, deps, tools):
159
+ with tools.environ(GTEST_ALSO_RUN_DISABLED_TESTS=str(int(bool(getattr(self, attr, default))))):
160
+ _old_run(self, deps, tools)
161
+
162
+ if param:
163
+ disabled_param = BooleanParameter(default, help="Also run disabled test-cases.")
164
+ disabled_param.__set_name__(cls, attr)
165
+ setattr(cls, attr, disabled_param)
166
+
167
+ cls.run = run
168
+ return influence.source("googletest.disabled", disabled)(cls)
169
+
170
+ return decorator
171
+
172
+
173
+ def fail_fast(default: bool = False, param: bool = True, attr: str = "fail_fast"):
174
+ """
175
+ Task class decorator controlling the GTEST_FAIL_FAST environment variable.
176
+
177
+ When the variable is set, test applications will abort when the first failure is found.
178
+
179
+ The value is taken from the parameter ``fail_fast`` which is created
180
+ by default. If no parameter is created, the value is instead taken from the class
181
+ attribute with the same name. The name of the paramter/attribute can be changed.
182
+
183
+ Args:
184
+ default (boolean): Default value of the parameter and env. variable.
185
+ Default: False.
186
+ param (boolean): Create a task parameter. Default: True.
187
+ attr (str): Name of parameter or class attribute.
188
+
189
+ """
190
+
191
+ def decorator(cls):
192
+ _old_run = cls.run
193
+
194
+ @wraps(cls.run)
195
+ def run(self, deps, tools):
196
+ with tools.environ(GTEST_FAIL_FAST=str(int(bool(getattr(self, attr, default))))):
197
+ _old_run(self, deps, tools)
198
+
199
+ if param:
200
+ fail_fast_param = BooleanParameter(default, help="Stop when the first failure is found.")
201
+ fail_fast_param.__set_name__(cls, attr)
202
+ setattr(cls, attr, fail_fast_param)
203
+
204
+ cls.run = run
205
+ return influence.source("googletest.fail_fast", fail_fast)(cls)
206
+
207
+ return decorator
208
+
209
+
210
+ def filter(default: str = "*", param: bool = True, attr: str = "filter"):
211
+ """
212
+ Task class decorator controlling the GTEST_FILTER environment variable.
213
+
214
+ The variable instructs test applications to only run test-cases that matches
215
+ a wildcard filter string (default: ``*``).
216
+
217
+ The value is taken from the parameter ``filter`` which is created
218
+ by default. If no parameter is created, the value is instead taken from the class
219
+ attribute with the same name. The name of the paramter/attribute can be changed.
220
+
221
+ Args:
222
+ default (boolean): Default value of the parameter and env. variable.
223
+ Default: False.
224
+ param (boolean): Create a task parameter. Default: True.
225
+ attr (str): Name of parameter or class attribute.
226
+
227
+ """
228
+
229
+ def decorator(cls):
230
+ _old_run = cls.run
231
+
232
+ @wraps(cls.run)
233
+ def run(self, deps, tools):
234
+ with tools.environ(GTEST_FILTER=str(getattr(self, attr, default))):
235
+ _old_run(self, deps, tools)
236
+
237
+ if param:
238
+ filter_param = Parameter(default, help="Test-case filter")
239
+ filter_param.__set_name__(cls, attr)
240
+ setattr(cls, attr, filter_param)
241
+
242
+ cls.run = run
243
+ return influence.source("googletest.filter", filter)(cls)
244
+
245
+ return decorator
246
+
247
+
248
+ def junit_report():
249
+ """
250
+ Decorator enabling JUnit test reporting in Google Test applications.
251
+
252
+ The decorator sets the GTEST_OUTPUT environment, thereby instructing
253
+ test applications to write a JUnit report file upon test completion.
254
+ The task publishes the report in the task artifact under report/junit/.
255
+
256
+ Any errors found in the report are parsed and attached to the task.
257
+ They are included in email reports if the email plugin is enabled.
258
+ """
259
+
260
+ def decorator(cls):
261
+ _old_run = cls.run
262
+ _old_publish = cls.publish
263
+
264
+ @wraps(cls.run)
265
+ def run(self, deps, tools):
266
+ raise_task_error_if(
267
+ tools.getenv("GTEST_OUTPUT"),
268
+ self,
269
+ "GoogleTest output already enabled, cannot proceed with JUnit report setup")
270
+ gtestdir = tools.builddir("gtest-junit-report")
271
+ gtestreport = path.join(gtestdir, "report.xml")
272
+ with tools.environ(GTEST_OUTPUT="xml:" + gtestreport):
273
+ try:
274
+ _old_run(self, deps, tools)
275
+ finally:
276
+ with ignore_exception(), self.report() as report:
277
+ junit.import_junit_report(gtestreport, report)
278
+
279
+ @wraps(cls.publish)
280
+ def publish(self, artifact, tools):
281
+ _old_publish(self, artifact, tools)
282
+ gtestdir = tools.builddir("gtest-junit-report")
283
+ with tools.cwd(gtestdir):
284
+ artifact.collect("*", "report/junit/")
285
+
286
+ cls.run = run
287
+ cls.publish = publish
288
+ return influence.source("googletest.junit_report", junit_report)(cls)
289
+
290
+ return decorator
291
+
292
+
293
+ def json_report():
294
+ """
295
+ Decorator enabling JSON test reporting in Google Test applications.
296
+
297
+ The decorator sets the GTEST_OUTPUT environment, thereby instructing
298
+ test applications to write a JSON report file upon test completion.
299
+ The task publishes the report in the task artifact under report/json/.
300
+
301
+ Any errors found in the report are parsed and attached to the task.
302
+ They are included in email reports if the email plugin is enabled.
303
+ """
304
+
305
+ def decorator(cls):
306
+ _old_run = cls.run
307
+ _old_publish = cls.publish
308
+
309
+ @wraps(cls.run)
310
+ def run(self, deps, tools):
311
+ raise_task_error_if(
312
+ tools.getenv("GTEST_OUTPUT"),
313
+ self,
314
+ "GoogleTest output already enabled, cannot proceed with JSON report setup")
315
+ gtestdir = tools.builddir("gtest-json-report")
316
+ gtestreport = path.join(gtestdir, "report.json")
317
+ with tools.environ(GTEST_OUTPUT="json:" + gtestreport):
318
+ try:
319
+ _old_run(self, deps, tools)
320
+ finally:
321
+ with ignore_exception(), self.report() as report:
322
+ with open(gtestreport) as fp:
323
+ json_report = json.load(fp)
324
+ for testsuite in json_report["testsuites"]:
325
+ for testcase in testsuite["testsuite"]:
326
+ failures = testcase.get("failures", [])
327
+ for failure in failures:
328
+ name = f"{testsuite['name']}.{testcase['name']}"
329
+ message = failure["failure"]
330
+ report.add_error("Test Failed", name, message)
331
+
332
+ @wraps(cls.publish)
333
+ def publish(self, artifact, tools):
334
+ _old_publish(self, artifact, tools)
335
+ gtestdir = tools.builddir("gtest-json-report")
336
+ with tools.cwd(gtestdir):
337
+ artifact.collect("*", "report/json/")
338
+
339
+ cls.run = run
340
+ cls.publish = publish
341
+ return influence.source("googletest.json_report", json_report)(cls)
342
+
343
+ return decorator
344
+
345
+
346
+ def repeat(default: int = 1, param: bool = True, attr: str = "repeat"):
347
+ """
348
+ Task class decorator controlling the GTEST_REPEAT environment variable.
349
+
350
+ The variable instructs test applications to repeat test-cases the specified
351
+ number of times.
352
+
353
+ The value is taken from the parameter ``repeat`` which is created
354
+ by default. If no parameter is created, the value is instead taken from the class
355
+ attribute with the same name. The name of the paramter/attribute can be changed.
356
+
357
+ Args:
358
+ default (boolean): Default value of the parameter and env. variable.
359
+ Default: False.
360
+ param (boolean): Create a task parameter. Default: True.
361
+ attr (str): Name of parameter or class attribute.
362
+
363
+ """
364
+
365
+ def decorator(cls):
366
+ _old_run = cls.run
367
+
368
+ @wraps(cls.run)
369
+ def run(self, deps, tools):
370
+ try:
371
+ repeat = int(getattr(self, attr, default))
372
+ except ValueError:
373
+ raise_task_error(self, f"Value assigned to '{attr}' is not an integer")
374
+ with tools.environ(GTEST_REPEAT=str(repeat)):
375
+ _old_run(self, deps, tools)
376
+
377
+ if param:
378
+ repeat_param = IntParameter(default, help="Test-case repetitions.")
379
+ repeat_param.__set_name__(cls, attr)
380
+ setattr(cls, attr, repeat_param)
381
+
382
+ cls.run = run
383
+ return influence.source("googletest.repeat", repeat)(cls)
384
+
385
+ return decorator
386
+
387
+
388
+ def seed(default: int = 0, param: bool = True, attr: str = "seed"):
389
+ """
390
+ Task class decorator controlling the GTEST_RANDOM_SEED environment variable.
391
+
392
+ The variable sets an initial value for the random number generator used to
393
+ shuffle test-cases. In order to reproduce ordering issues, a user may assign
394
+ a specific seed to get the same test-case order. The default value is 0
395
+ which causes Google Test to use time as the seed.
396
+
397
+ The value is taken from the parameter ``seed`` which is created
398
+ by default. If no parameter is created, the value is instead taken from the class
399
+ attribute with the same name. The name of the paramter/attribute can be changed.
400
+
401
+ Args:
402
+ default (boolean): Default value of the parameter and env. variable.
403
+ Default: False.
404
+ param (boolean): Create a task parameter. Default: True.
405
+ attr (str): Name of parameter or class attribute.
406
+
407
+ """
408
+
409
+ def decorator(cls):
410
+ _old_run = cls.run
411
+
412
+ @wraps(cls.run)
413
+ def run(self, deps, tools):
414
+ try:
415
+ seed = int(getattr(self, attr, default))
416
+ except ValueError:
417
+ raise_task_error(self, f"Value assigned to '{attr}' is not an integer")
418
+ with tools.environ(GTEST_RANDOM_SEED=str(seed)):
419
+ _old_run(self, deps, tools)
420
+
421
+ if param:
422
+ seed_param = IntParameter(default, min=0, max=99999, help="Random number generator seed.")
423
+ seed_param.__set_name__(cls, attr)
424
+ setattr(cls, attr, seed_param)
425
+
426
+ cls.run = run
427
+ return influence.source("googletest.seed", seed)(cls)
428
+
429
+ return decorator
430
+
431
+
432
+ def shuffle(default: bool = False, param: bool = True, attr: str = "shuffle"):
433
+ """
434
+ Task class decorator controlling the GTEST_SHUFFLE environment variable.
435
+
436
+ When set, the test application runs its test-cases in random order.
437
+ In order to reproduce test-case ordering issues, a user may assign a specific
438
+ random number generator seed to get the same test-case execution order.
439
+ See :func:`seed`.
440
+
441
+ The value is taken from the parameter ``shuffle`` which is created
442
+ by default. If no parameter is created, the value is instead taken from the class
443
+ attribute with the same name. The name of the paramter/attribute can be changed.
444
+
445
+ Args:
446
+ default (boolean): Default value of the parameter and env. variable.
447
+ Default: False.
448
+ param (boolean): Create a task parameter. Default: True.
449
+ attr (str): Name of parameter or class attribute.
450
+
451
+ """
452
+
453
+ def decorator(cls):
454
+ _old_run = cls.run
455
+
456
+ @wraps(cls.run)
457
+ def run(self, deps, tools):
458
+ with tools.environ(GTEST_SHUFFLE=str(int(bool(getattr(self, attr, default))))):
459
+ _old_run(self, deps, tools)
460
+
461
+ if param:
462
+ shuffle_param = BooleanParameter(default, help="Randomize test-case execution order.")
463
+ shuffle_param.__set_name__(cls, attr)
464
+ setattr(cls, attr, shuffle_param)
465
+
466
+ cls.run = run
467
+ return influence.source("googletest.shuffle", shuffle)(cls)
468
+
469
+ return decorator
470
+
471
+
472
+ @break_on_failure()
473
+ @brief()
474
+ @disabled()
475
+ @fail_fast()
476
+ @filter()
477
+ @junit_report()
478
+ @repeat()
479
+ @seed()
480
+ @shuffle()
481
+ class GTestRunner(Runner):
482
+ """
483
+ Abstract task base class that runs Google Test applications.
484
+
485
+ Test executables are consumed from requirement artifacts. The
486
+ artifacts must export metadata indicating the name of the binary,
487
+ using ``artifact.strings.executable``.
488
+
489
+ A number of parameters can be assigned from the command line
490
+ to control the behavior of the task. Parameters can also be
491
+ overridden using regular Python class attributes in subclasses.
492
+
493
+ A JUnit test report is generated and published in the task artifact.
494
+ The results of the report are also imported into Jolt and distributed
495
+ with report emails if the email plugin has been enabled.
496
+
497
+ Example:
498
+
499
+ .. literalinclude:: ../examples/googletest/runner.jolt
500
+ :language: python
501
+ :caption: examples/googletest/runner.jolt
502
+ """
503
+
504
+ abstract = True
505
+
506
+ break_on_failure = BooleanParameter(False, help="Trigger breakpoint on failures.")
507
+ """ Trigger breakpoint on test-case failure. """
508
+
509
+ brief = BooleanParameter(False, help="Only print failed test-cases.")
510
+ """ Only print failed test-cases. """
511
+
512
+ disabled = BooleanParameter(False, help="Also run disabled test-cases.")
513
+ """ Also run disabled test-cases. """
514
+
515
+ fail_fast = BooleanParameter(False, help="Stop when the first failure is found.")
516
+ """ Stop when the first failure is found. """
517
+
518
+ filter = Parameter("*", help="Test-case filter.")
519
+ """ Test-case filter. """
520
+
521
+ repeat = IntParameter(1, help="Test-case repetitions.")
522
+ """ Test-case repetitions. """
523
+
524
+ seed = IntParameter(0, min=0, max=99999, help="Random number generator seed.")
525
+ """ Random number generator seed. """
526
+
527
+ shuffle = BooleanParameter(True, help="Randomize test-case execution order.")
528
+ """ Randomize test-case execution order. """
jolt/plugins/http.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from requests.auth import HTTPBasicAuth
2
2
  from requests.exceptions import ConnectTimeout, RequestException
3
+ from urllib.parse import urlparse, urlunparse
3
4
  import keyring
4
5
  import getpass
5
6
 
@@ -9,6 +10,7 @@ from jolt import cache
9
10
  from jolt import log
10
11
  from jolt import config
11
12
  from jolt import filesystem as fs
13
+ from jolt import tools
12
14
  from jolt.error import raise_error_if, JoltError
13
15
 
14
16
 
@@ -21,10 +23,9 @@ class Http(cache.StorageProvider):
21
23
  def __init__(self, cache):
22
24
  super(Http, self).__init__()
23
25
  self._cache = cache
24
- self._uri = config.get(NAME, "uri")
26
+ self._uri = config.get(NAME, "uri", "http://cache")
27
+ self._uri = self._uri.rstrip("/")
25
28
  raise_error_if(not self._uri, "HTTP URI not configured")
26
- if self._uri[-1] != "/":
27
- self._uri += "/"
28
29
  self._upload = config.getboolean(NAME, "upload", True)
29
30
  self._download = config.getboolean(NAME, "download", True)
30
31
  self._disabled = False
@@ -49,65 +50,62 @@ class Http(cache.StorageProvider):
49
50
 
50
51
  return HTTPBasicAuth(username, password)
51
52
 
52
- def _get_url(self, node, artifact):
53
- return "{uri}/{name}/{file}".format(
53
+ def _get_url(self, artifact):
54
+ return artifact.tools.expand(
55
+ "{uri}/{name}/{file}",
54
56
  uri=self._uri,
55
- name=node.name,
57
+ name=artifact.task.name,
56
58
  file=fs.path.basename(artifact.get_archive_path()))
57
59
 
58
60
  @utils.retried.on_exception((RequestException, JoltError))
59
- def download(self, node, force=False):
61
+ def download(self, artifact, force=False):
60
62
  if self._disabled:
61
63
  return False
62
64
  if not self._download and not force:
63
65
  return False
64
- with self._cache.get_artifact(node) as artifact:
65
- url = self._get_url(node, artifact)
66
- if node.tools.download(url, artifact.get_archive_path(), exceptions=False, timeout=TIMEOUT):
67
- return True
68
- return False
66
+ url = self._get_url(artifact)
67
+ return artifact.tools.download(
68
+ url,
69
+ artifact.get_archive_path(),
70
+ exceptions=False,
71
+ timeout=TIMEOUT)
69
72
 
70
73
  def download_enabled(self):
71
74
  return not self._disabled and self._download
72
75
 
73
76
  @utils.retried.on_exception((RequestException))
74
- def upload(self, node, force=False):
77
+ def upload(self, artifact, force=False):
75
78
  if self._disabled:
76
79
  return True
77
80
  if not self._upload and not force:
78
81
  return True
79
- with self._cache.get_artifact(node) as artifact:
80
- url = self._get_url(node, artifact)
81
- archive = artifact.get_archive()
82
- return node.tools.upload(
83
- archive, url,
84
- exceptions=False,
85
- auth=self._get_auth(),
86
- timeout=TIMEOUT)
87
- return False
82
+ url = self._get_url(artifact)
83
+ archive = artifact.get_archive()
84
+ return artifact.tools.upload(
85
+ archive, url,
86
+ exceptions=False,
87
+ auth=self._get_auth(),
88
+ timeout=TIMEOUT)
88
89
 
89
90
  def upload_enabled(self):
90
91
  return not self._disabled and self._upload
91
92
 
92
93
  @utils.retried.on_exception((RequestException))
93
- def location(self, node):
94
+ def location(self, artifact):
94
95
  if self._disabled:
95
96
  return False
96
- with self._cache.get_artifact(node) as artifact:
97
- from requests.api import head
98
-
99
- url = self._get_url(node, artifact)
100
- try:
101
- response = head(url, stream=True, timeout=TIMEOUT_HEAD)
102
- except ConnectTimeout:
103
- self._disabled = True
104
- log.warning("[HTTP] failed to establish server connection, disabled")
105
- return False
106
-
107
- log.debug("[HTTP] Head: {0}", url)
108
- log.debug("[HTTP] Response: {0}", response.status_code)
109
- return url if response.status_code == 200 else ''
110
- return False
97
+
98
+ url = self._get_url(artifact)
99
+ try:
100
+ response = tools.http_session.head(url, stream=True, timeout=TIMEOUT_HEAD, auth=self._get_auth())
101
+ except ConnectTimeout:
102
+ self._disabled = True
103
+ log.warning("[HTTP] failed to establish server connection, disabled")
104
+ return False
105
+
106
+ log.debug("[HTTP] Head ({}): {}", response.status_code, url)
107
+ url = urlunparse(urlparse(url))
108
+ return url if response.status_code == 200 else ''
111
109
 
112
110
 
113
111
  @cache.RegisterStorage