artefacts-cli 0.7.1__py3-none-any.whl → 0.7.2__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.
- artefacts/cli/app.py +60 -29
- artefacts/cli/ros2.py +98 -14
- artefacts/cli/utils.py +15 -1
- artefacts/cli/utils_ros.py +21 -15
- artefacts/cli/version.py +2 -2
- {artefacts_cli-0.7.1.dist-info → artefacts_cli-0.7.2.dist-info}/METADATA +1 -1
- {artefacts_cli-0.7.1.dist-info → artefacts_cli-0.7.2.dist-info}/RECORD +10 -10
- {artefacts_cli-0.7.1.dist-info → artefacts_cli-0.7.2.dist-info}/WHEEL +1 -1
- {artefacts_cli-0.7.1.dist-info → artefacts_cli-0.7.2.dist-info}/entry_points.txt +0 -0
- {artefacts_cli-0.7.1.dist-info → artefacts_cli-0.7.2.dist-info}/top_level.txt +0 -0
artefacts/cli/app.py
CHANGED
@@ -309,8 +309,8 @@ def run(
|
|
309
309
|
api_conf = APIConf(project_id, jobname)
|
310
310
|
click.echo(f"[{jobname}] Starting tests")
|
311
311
|
if jobname not in warpconfig["jobs"]:
|
312
|
-
click.
|
313
|
-
raise click.
|
312
|
+
click.secho(f"[{jobname}] Error: Job name not defined", err=True, bold=True)
|
313
|
+
raise click.Abort()
|
314
314
|
jobconf = warpconfig["jobs"][jobname]
|
315
315
|
job_type = jobconf.get("type", "test")
|
316
316
|
if job_type not in ["test"]:
|
@@ -358,10 +358,12 @@ def run(
|
|
358
358
|
first,
|
359
359
|
)
|
360
360
|
except AuthenticationError:
|
361
|
-
click.
|
362
|
-
f"[{jobname}] Unable to authenticate (Stage: Job initialisation), please check your project name and API key"
|
361
|
+
click.secho(
|
362
|
+
f"[{jobname}] Unable to authenticate (Stage: Job initialisation), please check your project name and API key",
|
363
|
+
err=True,
|
364
|
+
bold=True,
|
363
365
|
)
|
364
|
-
raise click.
|
366
|
+
raise click.Abort()
|
365
367
|
|
366
368
|
job_success = True
|
367
369
|
for scenario_n, scenario in enumerate(scenarios):
|
@@ -371,18 +373,29 @@ def run(
|
|
371
373
|
try:
|
372
374
|
run = warpjob.new_run(scenario)
|
373
375
|
except AuthenticationError:
|
374
|
-
click.
|
375
|
-
f"[{jobname}] Unable to authenticate (Stage: Job run), please check your project name and API key"
|
376
|
+
click.secho(
|
377
|
+
f"[{jobname}] Unable to authenticate (Stage: Job run), please check your project name and API key",
|
378
|
+
err=True,
|
379
|
+
bold=True,
|
376
380
|
)
|
377
|
-
raise click.
|
381
|
+
raise click.Abort()
|
378
382
|
if framework is not None and framework.startswith("ros2:"):
|
379
383
|
from artefacts.cli.ros2 import run_ros2_tests
|
384
|
+
from artefacts.cli.utils_ros import get_TestSuite_error_result
|
380
385
|
|
381
386
|
if "ros_testfile" not in run.params:
|
382
|
-
click.
|
383
|
-
f"[{jobname}] Test launch file not specified for ros2 project"
|
387
|
+
click.secho(
|
388
|
+
f"[{jobname}] Test launch file not specified for ros2 project",
|
389
|
+
err=True,
|
390
|
+
bold=True,
|
391
|
+
)
|
392
|
+
result = get_TestSuite_error_result(
|
393
|
+
scenario["name"],
|
394
|
+
"launch_test file not specified error",
|
395
|
+
f"Please specify a `ros_testfile` in the artefacts.yaml scenario configuration.",
|
384
396
|
)
|
385
|
-
|
397
|
+
run.log_tests_results([result], False)
|
398
|
+
run.stop()
|
386
399
|
if dryrun:
|
387
400
|
click.echo(f"[{jobname}] Performing dry run")
|
388
401
|
results, success = {}, True
|
@@ -393,26 +406,34 @@ def run(
|
|
393
406
|
warpjob.stop()
|
394
407
|
warpjob.log_tests_result(False)
|
395
408
|
click.secho(e, bold=True, err=True)
|
396
|
-
click.
|
397
|
-
|
409
|
+
click.secho(
|
410
|
+
f"[{jobname}] artefacts failed to execute the tests",
|
411
|
+
err=True,
|
412
|
+
bold=True,
|
413
|
+
)
|
414
|
+
raise click.Abort()
|
398
415
|
if success is None:
|
399
416
|
run.stop()
|
400
417
|
warpjob.stop()
|
401
418
|
warpjob.log_tests_result(job_success)
|
402
|
-
click.
|
403
|
-
f"[{jobname}] Not able to execute tests. Make sure that ROS2 is sourced and that your launch file syntax is correct."
|
419
|
+
click.secho(
|
420
|
+
f"[{jobname}] Not able to execute tests. Make sure that ROS2 is sourced and that your launch file syntax is correct.",
|
421
|
+
err=True,
|
422
|
+
bold=True,
|
404
423
|
)
|
405
|
-
raise click.
|
424
|
+
raise click.Abort()
|
406
425
|
if not success:
|
407
426
|
job_success = False
|
408
427
|
elif framework is not None and framework.startswith("ros1:"):
|
409
428
|
from artefacts.cli.ros1 import run_ros1_tests
|
410
429
|
|
411
430
|
if "ros_testfile" not in run.params:
|
412
|
-
click.
|
413
|
-
f"[{jobname}] Test launch file not specified for ros1 project"
|
431
|
+
click.secho(
|
432
|
+
f"[{jobname}] Test launch file not specified for ros1 project",
|
433
|
+
err=True,
|
434
|
+
bold=True,
|
414
435
|
)
|
415
|
-
raise click.
|
436
|
+
raise click.Abort()
|
416
437
|
if dryrun:
|
417
438
|
click.echo(f"[{jobname}] Performing dry run")
|
418
439
|
results, success = {}, True
|
@@ -424,8 +445,12 @@ def run(
|
|
424
445
|
from artefacts.cli.other import run_other_tests
|
425
446
|
|
426
447
|
if "run" not in run.params:
|
427
|
-
click.
|
428
|
-
|
448
|
+
click.secho(
|
449
|
+
f"[{jobname}] run command not specified for scenario",
|
450
|
+
err=True,
|
451
|
+
bold=True,
|
452
|
+
)
|
453
|
+
raise click.Abort()
|
429
454
|
if dryrun:
|
430
455
|
click.echo(f"[{jobname}] Performing dry run")
|
431
456
|
results, success = {}, True
|
@@ -562,17 +587,23 @@ def run_remote(config, description, jobname, skip_validation=False):
|
|
562
587
|
f"Missing access! Please make sure your API key is added at {dashboard_url}/settings"
|
563
588
|
)
|
564
589
|
|
565
|
-
if
|
566
|
-
upload_urls_response.status_code == 401
|
567
|
-
and result["message"] == "no linked repository"
|
568
|
-
):
|
590
|
+
if upload_urls_response.status_code == 402:
|
569
591
|
raise click.ClickException(
|
570
|
-
f"
|
592
|
+
f"Billing issue, please go to {dashboard_url}/settings to correct: {result['error']}"
|
571
593
|
)
|
572
594
|
|
573
|
-
|
574
|
-
|
575
|
-
|
595
|
+
if "message" in result:
|
596
|
+
raise click.ClickException(
|
597
|
+
f"Error getting project info: {result['message']}"
|
598
|
+
)
|
599
|
+
elif "error" in result:
|
600
|
+
raise click.ClickException(
|
601
|
+
f"Error getting project info: {result['error']}"
|
602
|
+
)
|
603
|
+
else:
|
604
|
+
raise click.ClickException(
|
605
|
+
f"Error getting project info: {upload_urls_response.status_code} {upload_urls_response.reason}. Response text: {upload_urls_response.text}."
|
606
|
+
)
|
576
607
|
|
577
608
|
upload_urls = upload_urls_response.json()["upload_urls"]
|
578
609
|
url = ""
|
artefacts/cli/ros2.py
CHANGED
@@ -4,10 +4,58 @@ import os
|
|
4
4
|
import shutil
|
5
5
|
|
6
6
|
from .utils import run_and_save_logs
|
7
|
-
from .utils_ros import parse_tests_results
|
7
|
+
from .utils_ros import parse_tests_results, get_TestSuite_error_result
|
8
8
|
from .parameters import TMP_SCENARIO_PARAMS_YAML
|
9
9
|
|
10
10
|
|
11
|
+
# custom exceptions raised when trying to run ros2 tests
|
12
|
+
class Launch_test_CmdNotFoundError(FileNotFoundError):
|
13
|
+
pass
|
14
|
+
|
15
|
+
|
16
|
+
class LaunchTestFileNotFoundError(FileNotFoundError):
|
17
|
+
pass
|
18
|
+
|
19
|
+
|
20
|
+
class BadLaunchTestFileError(Exception):
|
21
|
+
pass
|
22
|
+
|
23
|
+
|
24
|
+
def ros2_run_and_save_logs(
|
25
|
+
args, output_path, shell=False, executable=None, env=None, cwd=None
|
26
|
+
):
|
27
|
+
try:
|
28
|
+
return_code, stdout, stderr = run_and_save_logs(
|
29
|
+
args,
|
30
|
+
output_path,
|
31
|
+
shell=shell,
|
32
|
+
executable=executable,
|
33
|
+
env=env,
|
34
|
+
cwd=cwd,
|
35
|
+
with_output=True,
|
36
|
+
)
|
37
|
+
except FileNotFoundError:
|
38
|
+
raise Launch_test_CmdNotFoundError(
|
39
|
+
f"Running {args} failed. Please check that `launch_test` is installed and in the path."
|
40
|
+
)
|
41
|
+
if return_code == 2:
|
42
|
+
# check the proc stderr for `launch_test: error: Test file '[filename]' does not exist`
|
43
|
+
|
44
|
+
if "does not exist" in stderr:
|
45
|
+
raise LaunchTestFileNotFoundError(
|
46
|
+
f"Running {args} failed. Please check that the launch file exists."
|
47
|
+
)
|
48
|
+
if "launch_test: error: " in stderr:
|
49
|
+
# example errors:
|
50
|
+
# "has no attribute 'generate_test_description'"
|
51
|
+
# "error: name 'xxx' is not defined"
|
52
|
+
raise BadLaunchTestFileError(
|
53
|
+
f"Running {args} failed. Check that the launch_test file syntax is correct."
|
54
|
+
)
|
55
|
+
|
56
|
+
return return_code
|
57
|
+
|
58
|
+
|
11
59
|
def generate_scenario_parameter_output(params: dict, param_file: str):
|
12
60
|
"""
|
13
61
|
Store `params` in `param_file` and convert to ros2 param file nested format,
|
@@ -57,19 +105,54 @@ def run_ros2_tests(run):
|
|
57
105
|
# Main: test execution
|
58
106
|
# shell=True required to support command list items that are strings with spaces
|
59
107
|
# (this way, scenario["ros_testfile"] can be either a path to the launch file or '<package_name> <launch_name>')
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
108
|
+
try:
|
109
|
+
return_code = ros2_run_and_save_logs(
|
110
|
+
" ".join(command),
|
111
|
+
shell=True,
|
112
|
+
executable="/bin/bash",
|
113
|
+
env={
|
114
|
+
**os.environ,
|
115
|
+
**{
|
116
|
+
"ROS_LOG_DIR": ros_log_dir,
|
117
|
+
"ARTEFACTS_SCENARIO_PARAMS_FILE": TMP_SCENARIO_PARAMS_YAML,
|
118
|
+
},
|
69
119
|
},
|
70
|
-
|
71
|
-
|
72
|
-
|
120
|
+
output_path=os.path.join(run.output_path, "test_process_log.txt"),
|
121
|
+
)
|
122
|
+
except Launch_test_CmdNotFoundError as e:
|
123
|
+
# raise Exception(
|
124
|
+
# f"Running {scenario['ros_testfile']} failed. Please check that the launch file exists."
|
125
|
+
# )
|
126
|
+
# closes the run properly and mark as errored
|
127
|
+
# dict matching junit xml format for test execution error
|
128
|
+
result = get_TestSuite_error_result(
|
129
|
+
scenario["ros_testfile"],
|
130
|
+
"launch_test command not found",
|
131
|
+
"Please check that launch_test is installed and in the path. Exception: {e}",
|
132
|
+
)
|
133
|
+
results = [result]
|
134
|
+
run.log_tests_results(results, False)
|
135
|
+
return results, False
|
136
|
+
except LaunchTestFileNotFoundError as e:
|
137
|
+
result = get_TestSuite_error_result(
|
138
|
+
scenario["ros_testfile"],
|
139
|
+
"launch_test file not found",
|
140
|
+
f"Please check that the `ros_testfile` config is correct. Exception: {e}",
|
141
|
+
)
|
142
|
+
results = [result]
|
143
|
+
run.log_tests_results(results, False)
|
144
|
+
return results, False
|
145
|
+
except BadLaunchTestFileError as e:
|
146
|
+
launch_test_file = scenario["ros_testfile"]
|
147
|
+
result = get_TestSuite_error_result(
|
148
|
+
scenario["ros_testfile"],
|
149
|
+
"launch_test file syntax error",
|
150
|
+
f"Please check that the file specified in `ros_testfile` config is a valid ros_test file. You may be able to identify issues by doing `launch_test {launch_test_file}`. Exception: {e}",
|
151
|
+
)
|
152
|
+
results = [result]
|
153
|
+
run.log_tests_results(results, False)
|
154
|
+
return results, False
|
155
|
+
|
73
156
|
if return_code == 2:
|
74
157
|
raise Exception(
|
75
158
|
f"Running {scenario['ros_testfile']} failed. Please check that the launch file exists."
|
@@ -82,10 +165,11 @@ def run_ros2_tests(run):
|
|
82
165
|
|
83
166
|
# parse xml generated by launch_test
|
84
167
|
results, success = parse_tests_results(test_result_file_path)
|
168
|
+
# upload logs anyway to help user debug
|
169
|
+
run.log_artifacts(run.output_path)
|
85
170
|
if success is None:
|
86
171
|
run.log_tests_results(results, False)
|
87
172
|
return results, success
|
88
|
-
run.log_artifacts(run.output_path)
|
89
173
|
|
90
174
|
# upload any additional files in the folders specified by the user in artefacts.yaml
|
91
175
|
for output in scenario.get("output_dirs", []):
|
artefacts/cli/utils.py
CHANGED
@@ -12,7 +12,13 @@ from artefacts.cli import WarpRun
|
|
12
12
|
|
13
13
|
|
14
14
|
def run_and_save_logs(
|
15
|
-
args,
|
15
|
+
args,
|
16
|
+
output_path,
|
17
|
+
shell=False,
|
18
|
+
executable=None,
|
19
|
+
env=None,
|
20
|
+
cwd=None,
|
21
|
+
with_output=False,
|
16
22
|
):
|
17
23
|
"""
|
18
24
|
Run a command and save stdout and stderr to a file in output_path
|
@@ -20,6 +26,7 @@ def run_and_save_logs(
|
|
20
26
|
Note: explicitly list used named params instead of using **kwargs to avoid typing issue: https://github.com/microsoft/pyright/issues/455#issuecomment-780076232
|
21
27
|
"""
|
22
28
|
output_file = open(output_path, "wb")
|
29
|
+
|
23
30
|
proc = subprocess.Popen(
|
24
31
|
args,
|
25
32
|
stdout=subprocess.PIPE, # Capture stdout
|
@@ -30,17 +37,24 @@ def run_and_save_logs(
|
|
30
37
|
cwd=cwd,
|
31
38
|
)
|
32
39
|
# write test-process stdout and stderr into file and stdout
|
40
|
+
stderr_content = ""
|
41
|
+
stdout_content = ""
|
33
42
|
if proc.stdout:
|
34
43
|
for line in proc.stdout:
|
35
44
|
decoded_line = line.decode()
|
36
45
|
sys.stdout.write(decoded_line)
|
37
46
|
output_file.write(line)
|
47
|
+
stdout_content += decoded_line
|
38
48
|
if proc.stderr:
|
49
|
+
output_file.write("[STDERR]\n".encode())
|
39
50
|
for line in proc.stderr:
|
40
51
|
decoded_line = line.decode()
|
41
52
|
sys.stderr.write(decoded_line)
|
42
53
|
output_file.write(line)
|
54
|
+
stderr_content += decoded_line
|
43
55
|
proc.wait()
|
56
|
+
if with_output:
|
57
|
+
return proc.returncode, stdout_content, stderr_content
|
44
58
|
return proc.returncode
|
45
59
|
|
46
60
|
|
artefacts/cli/utils_ros.py
CHANGED
@@ -6,6 +6,22 @@ class FailureElement(Element):
|
|
6
6
|
message = Attr()
|
7
7
|
|
8
8
|
|
9
|
+
def get_TestSuite_error_result(test_suite_name, name, error_msg):
|
10
|
+
return {
|
11
|
+
"suite": test_suite_name,
|
12
|
+
"errors": 1,
|
13
|
+
"failures": 0,
|
14
|
+
"tests": 1,
|
15
|
+
"details": [
|
16
|
+
{
|
17
|
+
"name": name,
|
18
|
+
"failure_message": error_msg,
|
19
|
+
"result": "failure",
|
20
|
+
}
|
21
|
+
],
|
22
|
+
}
|
23
|
+
|
24
|
+
|
9
25
|
def parse_tests_results(file):
|
10
26
|
def parse_suite(suite):
|
11
27
|
nonlocal success, results
|
@@ -50,19 +66,9 @@ def parse_tests_results(file):
|
|
50
66
|
except Exception as e:
|
51
67
|
print(f"[Exception in parse_tests_results] {e}")
|
52
68
|
print("Test result xml could not be loaded, marking success as False")
|
53
|
-
results =
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
"tests": 1,
|
59
|
-
"details": [
|
60
|
-
{
|
61
|
-
"name": "Error parsing XML test results",
|
62
|
-
"failure_message": f"The test may have timed out. Exception: {e}",
|
63
|
-
"result": "failure",
|
64
|
-
}
|
65
|
-
],
|
66
|
-
}
|
67
|
-
]
|
69
|
+
results = get_TestSuite_error_result(
|
70
|
+
"unittest.suite.TestSuite",
|
71
|
+
"Error parsing XML test results",
|
72
|
+
f"The test may have timed out. Exception: {e}",
|
73
|
+
)
|
68
74
|
return results, None
|
artefacts/cli/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: artefacts_cli
|
3
|
-
Version: 0.7.
|
3
|
+
Version: 0.7.2
|
4
4
|
Author-email: FD <fabian@artefacts.com>, AGC <alejandro@artefacts.com>, TN <tomo@artefacts.com>, EP <eric@artefacts.com>
|
5
5
|
Project-URL: Homepage, https://github.com/art-e-fact/artefacts-client
|
6
6
|
Project-URL: Bug Tracker, https://github.com/art-e-fact/artefacts-client/issues
|
@@ -1,6 +1,6 @@
|
|
1
1
|
artefacts/__init__.py,sha256=VLmogtpRQeJjQjAORV8ClSJ5qF-57Hxx3apvgy9H1zk,76
|
2
2
|
artefacts/cli/__init__.py,sha256=pt8OK66hMeQUxT9iLcvzYIIjFGrPS63ecWo8hS0T2qQ,11980
|
3
|
-
artefacts/cli/app.py,sha256=
|
3
|
+
artefacts/cli/app.py,sha256=lTdfJsNCdUh8A0V-MvvYUNoB1rgHAMpXxInqPgPeC4g,24241
|
4
4
|
artefacts/cli/app_containers.py,sha256=AOTiXjyJzYZn6JqfW26N3ehGQWcEVQ5mEmKDdp9a6tQ,5636
|
5
5
|
artefacts/cli/bagparser.py,sha256=FE_QaztC9pg4hQzTjGSdyve6mzZbHJbyqa3wqvZSbxE,3702
|
6
6
|
artefacts/cli/constants.py,sha256=bvsVDwqkAc49IZN7j6k6IL6EG87bECHd_VINtKJqbv8,320
|
@@ -9,16 +9,16 @@ artefacts/cli/logger.py,sha256=MP8WDImHA3BKVsn55BMWtGP5-aCmXl5ViVPtIo3jKk4,242
|
|
9
9
|
artefacts/cli/other.py,sha256=7NvzlspvG0zF7sryR-QznwdLupXLln1BKWxHB9VuEcc,1160
|
10
10
|
artefacts/cli/parameters.py,sha256=msf2aG-tmw0ahxwrPpB2W6KqdMj5A-nw9DPG9flkHTg,788
|
11
11
|
artefacts/cli/ros1.py,sha256=rKepZckAuy5O_qraF2CW5GiTmTZHar7LRD4pvESy6T0,9622
|
12
|
-
artefacts/cli/ros2.py,sha256=
|
13
|
-
artefacts/cli/utils.py,sha256=
|
14
|
-
artefacts/cli/utils_ros.py,sha256=
|
15
|
-
artefacts/cli/version.py,sha256=
|
12
|
+
artefacts/cli/ros2.py,sha256=RGAK228jjbWzcJW-ONGmkAl5QGvHC39SMij9GUthUTY,7587
|
13
|
+
artefacts/cli/utils.py,sha256=oy56o3N361srwhIvbMxwSPg8I_-tC7xcWtTSINTF2rE,4125
|
14
|
+
artefacts/cli/utils_ros.py,sha256=ucJrIMLcTh26ioduj3xiozgxqXZghkyTMHWI9BsHNjI,2156
|
15
|
+
artefacts/cli/version.py,sha256=d8Q6A3AJbbiXbr7TTxrk_DMbAQYVhMa2jrNwZsIl2LM,511
|
16
16
|
artefacts/cli/containers/__init__.py,sha256=K0efkJXNCqXH-qYBqhCE_8zVUCHbVmeuKH-y_fE8s4M,2254
|
17
17
|
artefacts/cli/containers/docker.py,sha256=R0yA-aIZCyYWN7gzim_Dhn1owpKI9ekMu6qbz5URYbQ,4311
|
18
18
|
artefacts/cli/containers/utils.py,sha256=bILX0uvazUJq7hoqKk4ztRzI_ZerYs04XQdKdx1ltjk,2002
|
19
19
|
artefacts/wrappers/artefacts_ros1_meta.launch,sha256=9tN7_0xLH8jW27KYFerhF3NuWDx2dED3ks_qoGVZAPw,1412
|
20
|
-
artefacts_cli-0.7.
|
21
|
-
artefacts_cli-0.7.
|
22
|
-
artefacts_cli-0.7.
|
23
|
-
artefacts_cli-0.7.
|
24
|
-
artefacts_cli-0.7.
|
20
|
+
artefacts_cli-0.7.2.dist-info/METADATA,sha256=BLLhtZXxpR49NRGgSHH9qVu4bImVgt5KJLFbHZsKxFk,3183
|
21
|
+
artefacts_cli-0.7.2.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
|
22
|
+
artefacts_cli-0.7.2.dist-info/entry_points.txt,sha256=nlTXRzilNjccbi53FgaRWCQPkG-pv61HRkaCkrKjlec,58
|
23
|
+
artefacts_cli-0.7.2.dist-info/top_level.txt,sha256=FdaMV1C9m36MWa-2Stm5xVODv7hss_nRYNwR83j_7ow,10
|
24
|
+
artefacts_cli-0.7.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|