cleepcli 1.45.0__tar.gz → 1.46.0__tar.gz
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.
- {cleepcli-1.45.0/cleepcli.egg-info → cleepcli-1.46.0}/PKG-INFO +4 -4
- {cleepcli-1.45.0 → cleepcli-1.46.0}/README.md +3 -3
- {cleepcli-1.45.0 → cleepcli-1.46.0}/bin/ccli +6 -4
- {cleepcli-1.45.0 → cleepcli-1.46.0}/bin/cleep-cli +6 -4
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/console.py +45 -8
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/module.py +2 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/test.py +59 -67
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/version.py +1 -1
- {cleepcli-1.45.0 → cleepcli-1.46.0/cleepcli.egg-info}/PKG-INFO +4 -4
- {cleepcli-1.45.0 → cleepcli-1.46.0}/LICENSE +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/MANIFEST.in +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/__init__.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/check.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/ci.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/cleepapi.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/config.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/docs.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/file.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/git.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/package.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/tools.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli/watch.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli.egg-info/SOURCES.txt +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli.egg-info/dependency_links.txt +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli.egg-info/requires.txt +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/cleepcli.egg-info/top_level.txt +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/pyproject.toml +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/requirements.txt +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/setup.cfg +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/setup.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/tests/test_check.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/tests/test_config.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/tests/test_console.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/tests/test_coverage.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/tests/test_docs_breaking.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/tests/test_module_scaffold.py +0 -0
- {cleepcli-1.45.0 → cleepcli-1.46.0}/tests/test_watch.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cleepcli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.46.0
|
|
4
4
|
Summary: Cleep-cli helps developers to build great Cleep applications from command line.
|
|
5
5
|
Home-page: https://github.com/CleepDevice/cleep-cli
|
|
6
6
|
Author: Tanguy Bonneau
|
|
@@ -42,7 +42,7 @@ This utility helps developers to build Cleep applications providing some useful
|
|
|
42
42
|
- `coreget` to clone or pull official Cleep repository.
|
|
43
43
|
- `coredocs` to generate core documentation in HTML format.
|
|
44
44
|
- `coresync` to synchronize sources from repository to execution folders.
|
|
45
|
-
- `coretests` to perform core tests suite.
|
|
45
|
+
- `coretests` to perform core unit tests suite (`--functional` for real OS binary tests on device / OS migration).
|
|
46
46
|
- `coretestscov` to display last core tests code coverage report.
|
|
47
47
|
- `cpuprof` to run CPU profiler on application execution instance.
|
|
48
48
|
- `memprof` to run memory profiler on application execution instance.
|
|
@@ -52,7 +52,7 @@ This utility helps developers to build Cleep applications providing some useful
|
|
|
52
52
|
- `moddelete` to delete installed files of a module.
|
|
53
53
|
- `moddocs` to generate module documentation.
|
|
54
54
|
- `modsync` to synchronize sources from module folder to execution folders.
|
|
55
|
-
- `modtests` to execute module tests.
|
|
55
|
+
- `modtests` to execute module unit tests (`--functional` for real OS/device tests).
|
|
56
56
|
- `modtestscov` to display last module tests code coverage report.
|
|
57
57
|
- `modbuild` to create application package (.zip)
|
|
58
58
|
- `modcheckback` to check app backend (python files structure and content)
|
|
@@ -142,7 +142,7 @@ The [watchdog](https://pypi.org/project/watchdog/) python library is used to det
|
|
|
142
142
|
|
|
143
143
|
## CI (GitHub Actions)
|
|
144
144
|
|
|
145
|
-
A tag `vX.Y.Z` (or a manual **workflow_dispatch**) builds a **py3** wheel + sdist on `ubuntu-latest` (Python 3.
|
|
145
|
+
A tag `vX.Y.Z` (or a manual **workflow_dispatch**) builds a **py3** wheel + sdist on `ubuntu-latest` (Python 3.12) via `python -m build` and uploads them to PyPI.
|
|
146
146
|
|
|
147
147
|
GitHub **environment** `actions` (Settings → Environments), secret:
|
|
148
148
|
|
|
@@ -6,7 +6,7 @@ This utility helps developers to build Cleep applications providing some useful
|
|
|
6
6
|
- `coreget` to clone or pull official Cleep repository.
|
|
7
7
|
- `coredocs` to generate core documentation in HTML format.
|
|
8
8
|
- `coresync` to synchronize sources from repository to execution folders.
|
|
9
|
-
- `coretests` to perform core tests suite.
|
|
9
|
+
- `coretests` to perform core unit tests suite (`--functional` for real OS binary tests on device / OS migration).
|
|
10
10
|
- `coretestscov` to display last core tests code coverage report.
|
|
11
11
|
- `cpuprof` to run CPU profiler on application execution instance.
|
|
12
12
|
- `memprof` to run memory profiler on application execution instance.
|
|
@@ -16,7 +16,7 @@ This utility helps developers to build Cleep applications providing some useful
|
|
|
16
16
|
- `moddelete` to delete installed files of a module.
|
|
17
17
|
- `moddocs` to generate module documentation.
|
|
18
18
|
- `modsync` to synchronize sources from module folder to execution folders.
|
|
19
|
-
- `modtests` to execute module tests.
|
|
19
|
+
- `modtests` to execute module unit tests (`--functional` for real OS/device tests).
|
|
20
20
|
- `modtestscov` to display last module tests code coverage report.
|
|
21
21
|
- `modbuild` to create application package (.zip)
|
|
22
22
|
- `modcheckback` to check app backend (python files structure and content)
|
|
@@ -106,7 +106,7 @@ The [watchdog](https://pypi.org/project/watchdog/) python library is used to det
|
|
|
106
106
|
|
|
107
107
|
## CI (GitHub Actions)
|
|
108
108
|
|
|
109
|
-
A tag `vX.Y.Z` (or a manual **workflow_dispatch**) builds a **py3** wheel + sdist on `ubuntu-latest` (Python 3.
|
|
109
|
+
A tag `vX.Y.Z` (or a manual **workflow_dispatch**) builds a **py3** wheel + sdist on `ubuntu-latest` (Python 3.12) via `python -m build` and uploads them to PyPI.
|
|
110
110
|
|
|
111
111
|
GitHub **environment** `actions` (Settings → Environments), secret:
|
|
112
112
|
|
|
@@ -114,12 +114,13 @@ def coresync():
|
|
|
114
114
|
@click.option('--xml', is_flag=True, default=False, help='Use xml reporting instead of default one (report).')
|
|
115
115
|
@click.option('--quiet', is_flag=True, default=True, help='Display or not coverage warnings.')
|
|
116
116
|
@click.option('-p', '--pattern', default=None, help='Run tests on specified file pattern only')
|
|
117
|
-
|
|
117
|
+
@click.option('--functional', is_flag=True, help='Run functional tests (real OS binaries) instead of unit tests.')
|
|
118
|
+
def coretests(coverage, output, xml, quiet, pattern, functional):
|
|
118
119
|
"""
|
|
119
120
|
Execute core tests
|
|
120
121
|
"""
|
|
121
122
|
m = Test()
|
|
122
|
-
res = m.core_tests(coverage, output, xml, quiet, pattern)
|
|
123
|
+
res = m.core_tests(coverage, output, xml, quiet, pattern, functional=functional)
|
|
123
124
|
|
|
124
125
|
if not res:
|
|
125
126
|
sys.exit(1)
|
|
@@ -385,7 +386,8 @@ def test():
|
|
|
385
386
|
@click.option('--coverage', is_flag=True, help='Display coverage report.')
|
|
386
387
|
@click.option('--copyto', help='Copy .coverage file to specified dir.')
|
|
387
388
|
@click.option('-p', '--pattern', help='Execute tests matching pattern.')
|
|
388
|
-
|
|
389
|
+
@click.option('--functional', is_flag=True, help='Run functional tests (real OS/device) instead of unit tests.')
|
|
390
|
+
def modtests(ctx, module, coverage, copyto, pattern, functional):
|
|
389
391
|
"""
|
|
390
392
|
Execute module tests
|
|
391
393
|
"""
|
|
@@ -393,7 +395,7 @@ def modtests(ctx, module, coverage, copyto, pattern):
|
|
|
393
395
|
ctx.invoke(modsync, module=module)
|
|
394
396
|
|
|
395
397
|
m = Test()
|
|
396
|
-
res = m.module_tests(module, coverage, copyto, pattern)
|
|
398
|
+
res = m.module_tests(module, coverage, copyto, pattern, functional=functional)
|
|
397
399
|
|
|
398
400
|
if not res:
|
|
399
401
|
sys.exit(1)
|
|
@@ -114,12 +114,13 @@ def coresync():
|
|
|
114
114
|
@click.option('--xml', is_flag=True, default=False, help='Use xml reporting instead of default one (report).')
|
|
115
115
|
@click.option('--quiet', is_flag=True, default=True, help='Display or not coverage warnings.')
|
|
116
116
|
@click.option('-p', '--pattern', default=None, help='Run tests on specified file pattern only')
|
|
117
|
-
|
|
117
|
+
@click.option('--functional', is_flag=True, help='Run functional tests (real OS binaries) instead of unit tests.')
|
|
118
|
+
def coretests(coverage, output, xml, quiet, pattern, functional):
|
|
118
119
|
"""
|
|
119
120
|
Execute core tests
|
|
120
121
|
"""
|
|
121
122
|
m = Test()
|
|
122
|
-
res = m.core_tests(coverage, output, xml, quiet, pattern)
|
|
123
|
+
res = m.core_tests(coverage, output, xml, quiet, pattern, functional=functional)
|
|
123
124
|
|
|
124
125
|
if not res:
|
|
125
126
|
sys.exit(1)
|
|
@@ -385,7 +386,8 @@ def test():
|
|
|
385
386
|
@click.option('--coverage', is_flag=True, help='Display coverage report.')
|
|
386
387
|
@click.option('--copyto', help='Copy .coverage file to specified dir.')
|
|
387
388
|
@click.option('-p', '--pattern', help='Execute tests matching pattern.')
|
|
388
|
-
|
|
389
|
+
@click.option('--functional', is_flag=True, help='Run functional tests (real OS/device) instead of unit tests.')
|
|
390
|
+
def modtests(ctx, module, coverage, copyto, pattern, functional):
|
|
389
391
|
"""
|
|
390
392
|
Execute module tests
|
|
391
393
|
"""
|
|
@@ -393,7 +395,7 @@ def modtests(ctx, module, coverage, copyto, pattern):
|
|
|
393
395
|
ctx.invoke(modsync, module=module)
|
|
394
396
|
|
|
395
397
|
m = Test()
|
|
396
|
-
res = m.module_tests(module, coverage, copyto, pattern)
|
|
398
|
+
res = m.module_tests(module, coverage, copyto, pattern, functional=functional)
|
|
397
399
|
|
|
398
400
|
if not res:
|
|
399
401
|
sys.exit(1)
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import sys
|
|
5
5
|
import subprocess
|
|
6
6
|
import time
|
|
7
|
+
import atexit
|
|
7
8
|
from threading import Timer, Thread
|
|
8
9
|
try: # pragma: no cover
|
|
9
10
|
from Queue import Queue, Empty
|
|
@@ -58,12 +59,40 @@ class EndlessConsole(Thread):
|
|
|
58
59
|
self.__stderr_queue = Queue()
|
|
59
60
|
self.__stdout_thread = None
|
|
60
61
|
self.__stderr_thread = None
|
|
62
|
+
self.__proc = None
|
|
63
|
+
self.__pid = None
|
|
64
|
+
# Child runs in its own session (setsid). Reap it if the CLI exits via
|
|
65
|
+
# Ctrl-C before kill() — otherwise coverage/pytest can orphan and OOM a Pi.
|
|
66
|
+
atexit.register(self.__reap_child)
|
|
61
67
|
|
|
62
68
|
def __del__(self):
|
|
63
69
|
"""
|
|
64
70
|
Destructor
|
|
65
71
|
"""
|
|
66
72
|
self.__stop()
|
|
73
|
+
self.__reap_child()
|
|
74
|
+
|
|
75
|
+
def __reap_child(self):
|
|
76
|
+
"""Kill leftover process group if the subprocess is still running."""
|
|
77
|
+
pid = self.__pid
|
|
78
|
+
proc = self.__proc
|
|
79
|
+
if not pid or pid == 1:
|
|
80
|
+
return
|
|
81
|
+
try:
|
|
82
|
+
if proc is not None and proc.poll() is None:
|
|
83
|
+
self.logger.debug('Reaping leftover process group pid=%s', pid)
|
|
84
|
+
if ON_POSIX:
|
|
85
|
+
os.killpg(os.getpgid(pid), signal.SIGKILL)
|
|
86
|
+
else: # pragma: no cover
|
|
87
|
+
proc.kill()
|
|
88
|
+
self.killed = True
|
|
89
|
+
except Exception: # pragma: no cover
|
|
90
|
+
pass
|
|
91
|
+
finally:
|
|
92
|
+
try:
|
|
93
|
+
atexit.unregister(self.__reap_child)
|
|
94
|
+
except Exception: # pragma: no cover
|
|
95
|
+
pass
|
|
67
96
|
|
|
68
97
|
def __enqueue_output(self, output, queue):
|
|
69
98
|
for line in iter(output.readline, b''):
|
|
@@ -103,6 +132,7 @@ class EndlessConsole(Thread):
|
|
|
103
132
|
self.logger.debug('Process killed manually')
|
|
104
133
|
self.killed = True
|
|
105
134
|
self.__stop()
|
|
135
|
+
self.__reap_child()
|
|
106
136
|
|
|
107
137
|
def __send_stds(self):
|
|
108
138
|
"""
|
|
@@ -156,7 +186,9 @@ class EndlessConsole(Thread):
|
|
|
156
186
|
close_fds=True,
|
|
157
187
|
preexec_fn=os.setsid
|
|
158
188
|
)
|
|
159
|
-
|
|
189
|
+
self.__proc = proc
|
|
190
|
+
self.__pid = proc.pid
|
|
191
|
+
pid = self.__pid
|
|
160
192
|
self.logger.trace('PID=%d' % pid)
|
|
161
193
|
|
|
162
194
|
if self.callback:
|
|
@@ -214,14 +246,19 @@ class EndlessConsole(Thread):
|
|
|
214
246
|
except Exception: # pragma: no cover
|
|
215
247
|
pass
|
|
216
248
|
|
|
217
|
-
#
|
|
218
|
-
if
|
|
249
|
+
# Always reap the process group if still alive. The child is started in its
|
|
250
|
+
# own session (setsid): if the CLI exits on Ctrl-C without kill(), pytest/
|
|
251
|
+
# coverage would otherwise keep running and can OOM a Raspberry Pi.
|
|
252
|
+
if pid != 1:
|
|
219
253
|
try:
|
|
220
|
-
if
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
254
|
+
if proc.poll() is None:
|
|
255
|
+
self.logger.debug('Terminating leftover process group pid=%s', pid)
|
|
256
|
+
if ON_POSIX:
|
|
257
|
+
os.killpg(os.getpgid(pid), signal.SIGKILL)
|
|
258
|
+
else: # pragma: no cover
|
|
259
|
+
proc.kill()
|
|
260
|
+
self.killed = True
|
|
261
|
+
except Exception: # pragma: no cover
|
|
225
262
|
pass
|
|
226
263
|
|
|
227
264
|
# process is over
|
|
@@ -488,6 +488,8 @@ pytest_plugins = ('cleep.libs.tests.fixtures',)
|
|
|
488
488
|
pythonpath = .
|
|
489
489
|
testpaths = tests
|
|
490
490
|
python_files = test_*.py
|
|
491
|
+
markers =
|
|
492
|
+
functional: real OS/device tests (CLEEP_FUNCTIONAL_TESTS / modtests --functional)
|
|
491
493
|
"""
|
|
492
494
|
DOCS_CONF_PY = """# -*- coding: utf-8 -*-
|
|
493
495
|
import os, sys
|
|
@@ -116,11 +116,11 @@ class Test():
|
|
|
116
116
|
self.__stderr.append(stderr)
|
|
117
117
|
|
|
118
118
|
def __console_callback(self, stdout, stderr):
|
|
119
|
-
|
|
119
|
+
# Forward pytest/unittest output as-is so failure tracebacks stay readable in CI
|
|
120
|
+
if stdout:
|
|
120
121
|
self.logger.info(" | " + stdout)
|
|
121
|
-
if stderr and stderr.find('----------------------------------------------------------------------') < 0:
|
|
122
|
-
self.logger.info(" | " + stderr)
|
|
123
122
|
if stderr:
|
|
123
|
+
self.logger.info(" | " + stderr)
|
|
124
124
|
self.__stderr.append(stderr)
|
|
125
125
|
|
|
126
126
|
def __console_end_callback(self, return_code, killed):
|
|
@@ -243,7 +243,7 @@ cd "%(path)s"
|
|
|
243
243
|
|
|
244
244
|
return res
|
|
245
245
|
|
|
246
|
-
def module_tests(self, module_name, display_coverage=False, copy_to=None, pattern=None):
|
|
246
|
+
def module_tests(self, module_name, display_coverage=False, copy_to=None, pattern=None, functional=False):
|
|
247
247
|
"""
|
|
248
248
|
Execute module unit tests and display process output on stdout
|
|
249
249
|
|
|
@@ -252,6 +252,7 @@ cd "%(path)s"
|
|
|
252
252
|
display_coverage (bool): display coverage report (default False)
|
|
253
253
|
copy_to (str): copy coverage file to specified path
|
|
254
254
|
pattern (str): run testcase that match pattern (no coverage)
|
|
255
|
+
functional (bool): run functional tests (real OS / device) instead of unit tests
|
|
255
256
|
|
|
256
257
|
Returns:
|
|
257
258
|
bool: True if process succeed, False otherwise
|
|
@@ -267,11 +268,16 @@ cd "%(path)s"
|
|
|
267
268
|
if module_version is None:
|
|
268
269
|
return False
|
|
269
270
|
|
|
270
|
-
|
|
271
|
+
if functional:
|
|
272
|
+
self.logger.info('Running module functional tests...')
|
|
273
|
+
else:
|
|
274
|
+
self.logger.info('Running unit tests...')
|
|
271
275
|
coverage_file_path = self.__get_coverage_file(module_name, module_version)
|
|
272
276
|
module_root_path = self.__get_module_root_path(module_name)
|
|
273
277
|
if pattern:
|
|
274
|
-
cmd = self.__get_tests_cmd_with_pattern(module_root_path, pattern)
|
|
278
|
+
cmd = self.__get_tests_cmd_with_pattern(module_root_path, pattern, functional=functional)
|
|
279
|
+
elif functional:
|
|
280
|
+
cmd = self.__get_functional_tests_cmd(module_root_path)
|
|
275
281
|
else:
|
|
276
282
|
cmd = self.__get_tests_cmd_with_coverage(module_root_path, coverage_file_path)
|
|
277
283
|
self.logger.debug('Test cmd: %s' % cmd)
|
|
@@ -284,18 +290,22 @@ cd "%(path)s"
|
|
|
284
290
|
time.sleep(0.25)
|
|
285
291
|
|
|
286
292
|
# display coverage report
|
|
287
|
-
if display_coverage:
|
|
293
|
+
if display_coverage and not functional:
|
|
288
294
|
self.logger.debug('Display coverage')
|
|
289
295
|
self.logger.info(self.module_tests_coverage(module_name))
|
|
290
296
|
|
|
291
297
|
# copy coverage file to standart location (in module tests directory) for coverage report tools
|
|
292
|
-
if copy_to:
|
|
298
|
+
if copy_to and not functional:
|
|
293
299
|
if not os.path.isfile(coverage_file_path):
|
|
294
300
|
self.logger.error('Coverage file missing: %s' % coverage_file_path)
|
|
295
301
|
return False
|
|
296
302
|
shutil.copyfile(coverage_file_path, os.path.join(copy_to, '.coverage'))
|
|
297
303
|
|
|
298
304
|
self.logger.debug('Return code: %s' % self.__endless_command_return_code)
|
|
305
|
+
# pytest exit code 5 = no tests collected (app has no @pytest.mark.functional)
|
|
306
|
+
if functional and self.__endless_command_return_code == 5:
|
|
307
|
+
self.logger.info('No functional tests for module "%s" — OK' % module_name)
|
|
308
|
+
return True
|
|
299
309
|
if self.__endless_command_return_code!=0:
|
|
300
310
|
return False
|
|
301
311
|
|
|
@@ -309,16 +319,30 @@ cd "%(path)s"
|
|
|
309
319
|
"""
|
|
310
320
|
return """
|
|
311
321
|
cd "%s"
|
|
312
|
-
PYTHONPATH=. COVERAGE_FILE=%s coverage run --omit="*/lib/python*/*","*/tests/*","test_*" --source=backend --concurrency=thread -m pytest tests
|
|
322
|
+
PYTHONPATH=. COVERAGE_FILE=%s coverage run --omit="*/lib/python*/*","*/tests/*","test_*" --source=backend --concurrency=thread -m pytest --tb=short -ra -m "not functional" tests
|
|
313
323
|
""" % (module_root_path, coverage_file_path)
|
|
314
324
|
|
|
315
|
-
def
|
|
325
|
+
def __get_functional_tests_cmd(self, module_root_path):
|
|
326
|
+
"""
|
|
327
|
+
Return cmdline to run module functional tests only (no coverage).
|
|
328
|
+
"""
|
|
329
|
+
return """
|
|
330
|
+
cd "%s"
|
|
331
|
+
CLEEP_FUNCTIONAL_TESTS=1 PYTHONPATH=. python3 -m pytest -v --tb=short -ra -m functional tests
|
|
332
|
+
""" % module_root_path
|
|
333
|
+
|
|
334
|
+
def __get_tests_cmd_with_pattern(self, module_root_path, pattern, functional=False):
|
|
316
335
|
"""
|
|
317
336
|
Return cmdline to run tests matching specified pattern (no coverage).
|
|
318
337
|
"""
|
|
338
|
+
if functional:
|
|
339
|
+
return """
|
|
340
|
+
cd "%s"
|
|
341
|
+
CLEEP_FUNCTIONAL_TESTS=1 PYTHONPATH=. python3 -m pytest --tb=short -ra -m functional tests -k "%s"
|
|
342
|
+
""" % (module_root_path, pattern)
|
|
319
343
|
return """
|
|
320
344
|
cd "%s"
|
|
321
|
-
PYTHONPATH=. python3 -m pytest tests -k "%s"
|
|
345
|
+
PYTHONPATH=. python3 -m pytest --tb=short -ra -m "not functional" tests -k "%s"
|
|
322
346
|
""" % (module_root_path, pattern)
|
|
323
347
|
|
|
324
348
|
def module_tests_coverage(self, module_name, missing=False, as_json=False, quiet=True):
|
|
@@ -379,57 +403,7 @@ PYTHONPATH=. python3 -m pytest tests -k "%s"
|
|
|
379
403
|
"""
|
|
380
404
|
return config.REPO_DIR
|
|
381
405
|
|
|
382
|
-
def
|
|
383
|
-
"""
|
|
384
|
-
Return all python files on core with their associated tests
|
|
385
|
-
|
|
386
|
-
Args:
|
|
387
|
-
core_path (string): core path
|
|
388
|
-
pattern (str): custom pattern to filter files to process
|
|
389
|
-
|
|
390
|
-
Returns:
|
|
391
|
-
dict: list of files with test file path::
|
|
392
|
-
|
|
393
|
-
{
|
|
394
|
-
python file (string): test file (string)
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
"""
|
|
398
|
-
files = []
|
|
399
|
-
tests_path = self.__get_core_tests_path()
|
|
400
|
-
|
|
401
|
-
self.logger.info('Searching core files...')
|
|
402
|
-
for root, _, filenames in os.walk(core_path):
|
|
403
|
-
for filename in filenames:
|
|
404
|
-
# build paths
|
|
405
|
-
filepath = os.path.join(root, filename)
|
|
406
|
-
core_relative_path = root.replace('%s' % core_path, '')
|
|
407
|
-
core_relative_path = core_relative_path[1:] if core_relative_path.startswith('/') else core_relative_path
|
|
408
|
-
test_filepath = os.path.join(tests_path, core_relative_path, 'test_%s' % filename)
|
|
409
|
-
|
|
410
|
-
# drop useless files
|
|
411
|
-
if not filename.endswith('.py'):
|
|
412
|
-
self.logger.debug(' Drop file "%s": not python file', filepath)
|
|
413
|
-
continue
|
|
414
|
-
if filename.startswith('__init__'):
|
|
415
|
-
self.logger.debug(' Drop file "%s": python init file', filepath)
|
|
416
|
-
continue
|
|
417
|
-
if filename.startswith('test_'):
|
|
418
|
-
self.logger.debug(' Drop file "%s": test file', filepath)
|
|
419
|
-
continue
|
|
420
|
-
if root.endswith('libs/tests'):
|
|
421
|
-
self.logger.debug(' Drop file "%s": test lib', filepath)
|
|
422
|
-
continue
|
|
423
|
-
if pattern and not re.search(pattern, test_filepath) and not re.search(pattern, filepath):
|
|
424
|
-
self.logger.debug(' Drop file "%s": does not match pattern %s', filepath, pattern)
|
|
425
|
-
continue
|
|
426
|
-
|
|
427
|
-
self.logger.debug(' Found "%s" [%s]' % (filepath, test_filepath))
|
|
428
|
-
files.append((filepath, test_filepath))
|
|
429
|
-
|
|
430
|
-
return files
|
|
431
|
-
|
|
432
|
-
def core_tests(self, display_coverage=False, display_test_output=False, xml=False, quiet=True, pattern=None):
|
|
406
|
+
def core_tests(self, display_coverage=False, display_test_output=False, xml=False, quiet=True, pattern=None, functional=False):
|
|
433
407
|
"""
|
|
434
408
|
Execute core unit tests and display process output on stdout
|
|
435
409
|
|
|
@@ -439,6 +413,7 @@ PYTHONPATH=. python3 -m pytest tests -k "%s"
|
|
|
439
413
|
xml (bool): use xml coverage command instead of report command
|
|
440
414
|
quiet (bool): display or not coverage.py warnings
|
|
441
415
|
pattern (str): custom pattern to filter tests (pytest -k)
|
|
416
|
+
functional (bool): run functional tests (real OS binaries) instead of unit tests
|
|
442
417
|
|
|
443
418
|
Returns:
|
|
444
419
|
bool: True if process succeed.
|
|
@@ -451,15 +426,25 @@ PYTHONPATH=. python3 -m pytest tests -k "%s"
|
|
|
451
426
|
self.logger.error('Unable to clear previous tests results')
|
|
452
427
|
return False
|
|
453
428
|
|
|
454
|
-
|
|
429
|
+
if functional:
|
|
430
|
+
self.logger.info('Running core functional tests (pytest, real OS binaries)...')
|
|
431
|
+
marker_opt = '-m functional'
|
|
432
|
+
env_prefix = 'CLEEP_FUNCTIONAL_TESTS=1 '
|
|
433
|
+
else:
|
|
434
|
+
self.logger.info('Running core unit tests (pytest)...')
|
|
435
|
+
marker_opt = '-m "not functional"'
|
|
436
|
+
env_prefix = ''
|
|
455
437
|
pattern_opt = '-k "%s"' % pattern if pattern else ''
|
|
456
|
-
|
|
438
|
+
# Always keep short failure tracebacks (+ failed/error summary) so CI logs are actionable
|
|
439
|
+
verbosity = '-v --tb=short -ra' if display_test_output else '-q --tb=short -ra'
|
|
457
440
|
cmd = """
|
|
458
441
|
cd "%(repo_dir)s"
|
|
459
|
-
|
|
442
|
+
%(env_prefix)sPYTHONPATH=. coverage run --omit="*/lib/python*/*","*/tests/*","*test_*.py" --source=cleep --concurrency=gevent -m pytest %(verbosity)s %(marker_opt)s cleep/tests %(pattern_opt)s
|
|
460
443
|
""" % {
|
|
461
444
|
'repo_dir': repo_dir,
|
|
445
|
+
'env_prefix': env_prefix,
|
|
462
446
|
'verbosity': verbosity,
|
|
447
|
+
'marker_opt': marker_opt,
|
|
463
448
|
'pattern_opt': pattern_opt,
|
|
464
449
|
}
|
|
465
450
|
self.logger.debug('Test cmd: %s' % cmd)
|
|
@@ -469,8 +454,15 @@ PYTHONPATH=. coverage run --omit="*/lib/python*/*","*/tests/*","*test_*.py" --so
|
|
|
469
454
|
c = EndlessConsole(cmd, console_callback, self.__console_end_callback)
|
|
470
455
|
c.start()
|
|
471
456
|
|
|
472
|
-
|
|
473
|
-
|
|
457
|
+
try:
|
|
458
|
+
while self.__endless_command_running:
|
|
459
|
+
time.sleep(0.25)
|
|
460
|
+
except KeyboardInterrupt:
|
|
461
|
+
self.logger.warning('Interrupted — stopping tests...')
|
|
462
|
+
c.kill()
|
|
463
|
+
while self.__endless_command_running:
|
|
464
|
+
time.sleep(0.1)
|
|
465
|
+
raise
|
|
474
466
|
|
|
475
467
|
success = self.__endless_command_return_code == 0
|
|
476
468
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cleepcli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.46.0
|
|
4
4
|
Summary: Cleep-cli helps developers to build great Cleep applications from command line.
|
|
5
5
|
Home-page: https://github.com/CleepDevice/cleep-cli
|
|
6
6
|
Author: Tanguy Bonneau
|
|
@@ -42,7 +42,7 @@ This utility helps developers to build Cleep applications providing some useful
|
|
|
42
42
|
- `coreget` to clone or pull official Cleep repository.
|
|
43
43
|
- `coredocs` to generate core documentation in HTML format.
|
|
44
44
|
- `coresync` to synchronize sources from repository to execution folders.
|
|
45
|
-
- `coretests` to perform core tests suite.
|
|
45
|
+
- `coretests` to perform core unit tests suite (`--functional` for real OS binary tests on device / OS migration).
|
|
46
46
|
- `coretestscov` to display last core tests code coverage report.
|
|
47
47
|
- `cpuprof` to run CPU profiler on application execution instance.
|
|
48
48
|
- `memprof` to run memory profiler on application execution instance.
|
|
@@ -52,7 +52,7 @@ This utility helps developers to build Cleep applications providing some useful
|
|
|
52
52
|
- `moddelete` to delete installed files of a module.
|
|
53
53
|
- `moddocs` to generate module documentation.
|
|
54
54
|
- `modsync` to synchronize sources from module folder to execution folders.
|
|
55
|
-
- `modtests` to execute module tests.
|
|
55
|
+
- `modtests` to execute module unit tests (`--functional` for real OS/device tests).
|
|
56
56
|
- `modtestscov` to display last module tests code coverage report.
|
|
57
57
|
- `modbuild` to create application package (.zip)
|
|
58
58
|
- `modcheckback` to check app backend (python files structure and content)
|
|
@@ -142,7 +142,7 @@ The [watchdog](https://pypi.org/project/watchdog/) python library is used to det
|
|
|
142
142
|
|
|
143
143
|
## CI (GitHub Actions)
|
|
144
144
|
|
|
145
|
-
A tag `vX.Y.Z` (or a manual **workflow_dispatch**) builds a **py3** wheel + sdist on `ubuntu-latest` (Python 3.
|
|
145
|
+
A tag `vX.Y.Z` (or a manual **workflow_dispatch**) builds a **py3** wheel + sdist on `ubuntu-latest` (Python 3.12) via `python -m build` and uploads them to PyPI.
|
|
146
146
|
|
|
147
147
|
GitHub **environment** `actions` (Settings → Environments), secret:
|
|
148
148
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|