maestro-framework 0.1.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.
- maestro_framework-0.1.0/.gitignore +46 -0
- maestro_framework-0.1.0/PKG-INFO +99 -0
- maestro_framework-0.1.0/README.md +53 -0
- maestro_framework-0.1.0/pyproject.toml +101 -0
- maestro_framework-0.1.0/src/maestro_core/__init__.py +43 -0
- maestro_framework-0.1.0/src/maestro_core/cli.py +180 -0
- maestro_framework-0.1.0/src/maestro_core/config/__init__.py +32 -0
- maestro_framework-0.1.0/src/maestro_core/config/devices.py +152 -0
- maestro_framework-0.1.0/src/maestro_core/config/loader.py +111 -0
- maestro_framework-0.1.0/src/maestro_core/config/models.py +86 -0
- maestro_framework-0.1.0/src/maestro_core/config/settings.py +60 -0
- maestro_framework-0.1.0/src/maestro_core/detect/__init__.py +31 -0
- maestro_framework-0.1.0/src/maestro_core/detect/adb_detector.py +67 -0
- maestro_framework-0.1.0/src/maestro_core/detect/base.py +57 -0
- maestro_framework-0.1.0/src/maestro_core/detect/camera_detector.py +72 -0
- maestro_framework-0.1.0/src/maestro_core/detect/registry.py +64 -0
- maestro_framework-0.1.0/src/maestro_core/detect/serial_detector.py +42 -0
- maestro_framework-0.1.0/src/maestro_core/detect/ssh_detector.py +83 -0
- maestro_framework-0.1.0/src/maestro_core/drivers/__init__.py +59 -0
- maestro_framework-0.1.0/src/maestro_core/drivers/_cli.py +61 -0
- maestro_framework-0.1.0/src/maestro_core/drivers/adb_turboadb.py +138 -0
- maestro_framework-0.1.0/src/maestro_core/drivers/base.py +333 -0
- maestro_framework-0.1.0/src/maestro_core/drivers/dlt.py +116 -0
- maestro_framework-0.1.0/src/maestro_core/drivers/fake.py +228 -0
- maestro_framework-0.1.0/src/maestro_core/drivers/power.py +92 -0
- maestro_framework-0.1.0/src/maestro_core/drivers/registry.py +69 -0
- maestro_framework-0.1.0/src/maestro_core/drivers/ssh_turbossh.py +113 -0
- maestro_framework-0.1.0/src/maestro_core/drivers/webcam.py +96 -0
- maestro_framework-0.1.0/src/maestro_core/endurance/__init__.py +20 -0
- maestro_framework-0.1.0/src/maestro_core/endurance/health.py +72 -0
- maestro_framework-0.1.0/src/maestro_core/endurance/loop.py +97 -0
- maestro_framework-0.1.0/src/maestro_core/endurance/rollup.py +65 -0
- maestro_framework-0.1.0/src/maestro_core/endurance/stop.py +97 -0
- maestro_framework-0.1.0/src/maestro_core/engine/__init__.py +30 -0
- maestro_framework-0.1.0/src/maestro_core/engine/_pytest_scenario.py +29 -0
- maestro_framework-0.1.0/src/maestro_core/engine/expect.py +53 -0
- maestro_framework-0.1.0/src/maestro_core/engine/plugin.py +112 -0
- maestro_framework-0.1.0/src/maestro_core/engine/runner.py +435 -0
- maestro_framework-0.1.0/src/maestro_core/errors.py +63 -0
- maestro_framework-0.1.0/src/maestro_core/plugins.py +88 -0
- maestro_framework-0.1.0/src/maestro_core/proc.py +154 -0
- maestro_framework-0.1.0/src/maestro_core/py.typed +0 -0
- maestro_framework-0.1.0/src/maestro_core/reporting/__init__.py +45 -0
- maestro_framework-0.1.0/src/maestro_core/reporting/junit.py +108 -0
- maestro_framework-0.1.0/src/maestro_core/reporting/records.py +146 -0
- maestro_framework-0.1.0/src/maestro_core/reporting/reporter.py +86 -0
- maestro_framework-0.1.0/src/maestro_core/reporting/sink.py +124 -0
- maestro_framework-0.1.0/src/maestro_core/scenario/__init__.py +84 -0
- maestro_framework-0.1.0/src/maestro_core/scenario/graph.py +318 -0
- maestro_framework-0.1.0/src/maestro_core/scenario/io.py +102 -0
- maestro_framework-0.1.0/src/maestro_core/scenario/models.py +276 -0
- maestro_framework-0.1.0/src/maestro_core/scenario/schema.py +41 -0
- maestro_framework-0.1.0/src/maestro_core/scenario/validation.py +247 -0
- maestro_framework-0.1.0/src/maestro_core/steps/__init__.py +55 -0
- maestro_framework-0.1.0/src/maestro_core/steps/builtins.py +270 -0
- maestro_framework-0.1.0/src/maestro_core/steps/context.py +125 -0
- maestro_framework-0.1.0/src/maestro_core/steps/discovery.py +90 -0
- maestro_framework-0.1.0/src/maestro_core/steps/registry.py +245 -0
- maestro_framework-0.1.0/src/maestro_core/steps/script.py +128 -0
- maestro_framework-0.1.0/src/maestro_core/timeutil.py +87 -0
- maestro_framework-0.1.0/src/maestro_server/__init__.py +16 -0
- maestro_framework-0.1.0/src/maestro_server/api/__init__.py +19 -0
- maestro_framework-0.1.0/src/maestro_server/api/benches.py +88 -0
- maestro_framework-0.1.0/src/maestro_server/api/crud_router.py +179 -0
- maestro_framework-0.1.0/src/maestro_server/api/deps.py +42 -0
- maestro_framework-0.1.0/src/maestro_server/api/misc.py +132 -0
- maestro_framework-0.1.0/src/maestro_server/api/resources.py +331 -0
- maestro_framework-0.1.0/src/maestro_server/api/runs.py +252 -0
- maestro_framework-0.1.0/src/maestro_server/auth.py +61 -0
- maestro_framework-0.1.0/src/maestro_server/cli.py +46 -0
- maestro_framework-0.1.0/src/maestro_server/db/__init__.py +48 -0
- maestro_framework-0.1.0/src/maestro_server/db/base.py +53 -0
- maestro_framework-0.1.0/src/maestro_server/db/models.py +249 -0
- maestro_framework-0.1.0/src/maestro_server/db/session.py +52 -0
- maestro_framework-0.1.0/src/maestro_server/integrations/__init__.py +14 -0
- maestro_framework-0.1.0/src/maestro_server/integrations/base.py +38 -0
- maestro_framework-0.1.0/src/maestro_server/integrations/dispatch.py +43 -0
- maestro_framework-0.1.0/src/maestro_server/integrations/email.py +78 -0
- maestro_framework-0.1.0/src/maestro_server/integrations/jira_xray.py +57 -0
- maestro_framework-0.1.0/src/maestro_server/main.py +124 -0
- maestro_framework-0.1.0/src/maestro_server/scheduler.py +183 -0
- maestro_framework-0.1.0/src/maestro_server/schemas.py +520 -0
- maestro_framework-0.1.0/src/maestro_server/services/__init__.py +14 -0
- maestro_framework-0.1.0/src/maestro_server/services/bench.py +40 -0
- maestro_framework-0.1.0/src/maestro_server/services/crud.py +125 -0
- maestro_framework-0.1.0/src/maestro_server/services/detection.py +102 -0
- maestro_framework-0.1.0/src/maestro_server/services/run_trigger.py +137 -0
- maestro_framework-0.1.0/src/maestro_server/services/runs.py +300 -0
- maestro_framework-0.1.0/src/maestro_server/settings.py +75 -0
- maestro_framework-0.1.0/src/maestro_server/web/assets/index-BQIDZnuk.js +275 -0
- maestro_framework-0.1.0/src/maestro_server/web/assets/index-Cee9fdIV.css +1 -0
- maestro_framework-0.1.0/src/maestro_server/web/favicon.svg +15 -0
- maestro_framework-0.1.0/src/maestro_server/web/index.html +15 -0
- maestro_framework-0.1.0/src/maestro_tools/__init__.py +17 -0
- maestro_framework-0.1.0/src/maestro_tools/cli.py +82 -0
- maestro_framework-0.1.0/src/maestro_tools/generator.py +34 -0
- maestro_framework-0.1.0/src/maestro_tools/invoke.py +110 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# MAESTRO — Modular Automotive Embedded Suite for Test Runtime & Operations
|
|
2
|
+
# Author: Naveen Daniel Kennedy
|
|
3
|
+
|
|
4
|
+
# Python
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*.egg-info/
|
|
8
|
+
*.egg
|
|
9
|
+
.eggs/
|
|
10
|
+
build/
|
|
11
|
+
dist/
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
htmlcov/
|
|
19
|
+
*.spec.bak
|
|
20
|
+
|
|
21
|
+
# Test / run artifacts
|
|
22
|
+
allure-results/
|
|
23
|
+
allure-report/
|
|
24
|
+
.maestro/
|
|
25
|
+
runs/
|
|
26
|
+
*.dlt
|
|
27
|
+
*.junit.xml
|
|
28
|
+
|
|
29
|
+
# Node / UI
|
|
30
|
+
node_modules/
|
|
31
|
+
apps/ui/dist/
|
|
32
|
+
packages/maestro-server/src/maestro_server/web/
|
|
33
|
+
*.tsbuildinfo
|
|
34
|
+
|
|
35
|
+
# Local config & secrets (never commit secrets)
|
|
36
|
+
.env
|
|
37
|
+
.env.*
|
|
38
|
+
!.env.example
|
|
39
|
+
config/benches/*.local.toml
|
|
40
|
+
|
|
41
|
+
# OS / editor
|
|
42
|
+
.DS_Store
|
|
43
|
+
Thumbs.db
|
|
44
|
+
.idea/
|
|
45
|
+
.vscode/
|
|
46
|
+
*.swp
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: maestro-framework
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MAESTRO — Modular Automotive Embedded System Test, Reporting & Orchestration: an offline-capable platform for automotive Hardware-in-the-Loop (HIL) and embedded device testing (visual designer + pytest engine + server + web UI + mini-tools).
|
|
5
|
+
Project-URL: Homepage, https://github.com/NVNKENNEDY/maestro
|
|
6
|
+
Project-URL: Source, https://github.com/NVNKENNEDY/maestro
|
|
7
|
+
Author-email: Naveen Daniel Kennedy <nvnkennedy@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: adb,android,automotive,embedded,endurance,fastapi,hil,pytest,qnx,ssh,test-automation
|
|
10
|
+
Classifier: Framework :: FastAPI
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Topic :: Software Development :: Testing
|
|
17
|
+
Classifier: Topic :: System :: Hardware
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Requires-Dist: adbutils<3,>=2.12
|
|
20
|
+
Requires-Dist: alembic<2,>=1.13
|
|
21
|
+
Requires-Dist: allure-pytest<3,>=2.16
|
|
22
|
+
Requires-Dist: apscheduler<4,>=3.11
|
|
23
|
+
Requires-Dist: email-validator>=2.1
|
|
24
|
+
Requires-Dist: fastapi<1,>=0.136
|
|
25
|
+
Requires-Dist: httpx<1,>=0.28
|
|
26
|
+
Requires-Dist: jinja2<4,>=3.1
|
|
27
|
+
Requires-Dist: paramiko<6,>=5.0
|
|
28
|
+
Requires-Dist: pydantic-settings<3,>=2.14
|
|
29
|
+
Requires-Dist: pydantic<3,>=2.13
|
|
30
|
+
Requires-Dist: pyjwt<3,>=2.9
|
|
31
|
+
Requires-Dist: pyserial<4,>=3.5
|
|
32
|
+
Requires-Dist: pytest-check<3,>=2.8
|
|
33
|
+
Requires-Dist: pytest-rerunfailures<17,>=16
|
|
34
|
+
Requires-Dist: pytest-xdist<4,>=3.8
|
|
35
|
+
Requires-Dist: pytest<10,>=9.1
|
|
36
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
37
|
+
Requires-Dist: sqlalchemy<3,>=2.0
|
|
38
|
+
Requires-Dist: tomli-w<2,>=1.2
|
|
39
|
+
Requires-Dist: uvicorn[standard]<1,>=0.49
|
|
40
|
+
Provides-Extra: hardware
|
|
41
|
+
Requires-Dist: opencv-python<5,>=4.10; extra == 'hardware'
|
|
42
|
+
Requires-Dist: pygrabber<1,>=0.2; extra == 'hardware'
|
|
43
|
+
Provides-Extra: reportportal
|
|
44
|
+
Requires-Dist: pytest-reportportal<6,>=5.4; extra == 'reportportal'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# MAESTRO
|
|
48
|
+
|
|
49
|
+
**Modular Automotive Embedded System Test, Reporting & Orchestration.**
|
|
50
|
+
|
|
51
|
+
*MAESTRO* is an open-source, offline-capable test-automation platform for
|
|
52
|
+
automotive Hardware-in-the-Loop (HIL) and embedded device testing. Like a maestro
|
|
53
|
+
leading an orchestra, it conducts your devices, scenarios, and endurance cycles
|
|
54
|
+
from one visual canvas — fitting for automotive infotainment, where the head unit
|
|
55
|
+
conducts the vehicle's audio, video, and connectivity.
|
|
56
|
+
|
|
57
|
+
Design test cases visually on a canvas (no code), run them on real or fake
|
|
58
|
+
devices through a `pytest` engine, and get detailed step-level reports with inline
|
|
59
|
+
attachments and first-class endurance/stability cycles. Results can be emailed and
|
|
60
|
+
pushed to Jira/Xray.
|
|
61
|
+
|
|
62
|
+
## Install
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install maestro-framework
|
|
66
|
+
# optional: real webcam capture (OpenCV / DirectShow)
|
|
67
|
+
pip install "maestro-framework[hardware]"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Run
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
maestro
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This starts the API and serves the bundled web UI, then opens your browser.
|
|
77
|
+
|
|
78
|
+
## Headless (no server / no UI)
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
maestro-core run scenario.json --bench bench.toml
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Mini-tools (drive a device without the framework)
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
maestro-tool list
|
|
88
|
+
maestro-tool invoke turbossh --command "uname -a" --set host=10.0.0.5 --set user=root
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
MAESTRO calls your existing device tools (`turboadb`, `turbossh`, power/DLT
|
|
92
|
+
scripts) through thin adapters — keep them on `PATH`. A hardware-free demo bench of
|
|
93
|
+
fake drivers is included so everything runs end to end with no hardware.
|
|
94
|
+
|
|
95
|
+
> Installs as `maestro-framework`; the Python modules are `maestro_core`,
|
|
96
|
+
> `maestro_server`, and `maestro_tools`, and the console commands are `maestro`,
|
|
97
|
+
> `maestro-core`, and `maestro-tool`.
|
|
98
|
+
|
|
99
|
+
License: MIT © 2026 Naveen Daniel Kennedy
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# MAESTRO
|
|
2
|
+
|
|
3
|
+
**Modular Automotive Embedded System Test, Reporting & Orchestration.**
|
|
4
|
+
|
|
5
|
+
*MAESTRO* is an open-source, offline-capable test-automation platform for
|
|
6
|
+
automotive Hardware-in-the-Loop (HIL) and embedded device testing. Like a maestro
|
|
7
|
+
leading an orchestra, it conducts your devices, scenarios, and endurance cycles
|
|
8
|
+
from one visual canvas — fitting for automotive infotainment, where the head unit
|
|
9
|
+
conducts the vehicle's audio, video, and connectivity.
|
|
10
|
+
|
|
11
|
+
Design test cases visually on a canvas (no code), run them on real or fake
|
|
12
|
+
devices through a `pytest` engine, and get detailed step-level reports with inline
|
|
13
|
+
attachments and first-class endurance/stability cycles. Results can be emailed and
|
|
14
|
+
pushed to Jira/Xray.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install maestro-framework
|
|
20
|
+
# optional: real webcam capture (OpenCV / DirectShow)
|
|
21
|
+
pip install "maestro-framework[hardware]"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Run
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
maestro
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This starts the API and serves the bundled web UI, then opens your browser.
|
|
31
|
+
|
|
32
|
+
## Headless (no server / no UI)
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
maestro-core run scenario.json --bench bench.toml
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Mini-tools (drive a device without the framework)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
maestro-tool list
|
|
42
|
+
maestro-tool invoke turbossh --command "uname -a" --set host=10.0.0.5 --set user=root
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
MAESTRO calls your existing device tools (`turboadb`, `turbossh`, power/DLT
|
|
46
|
+
scripts) through thin adapters — keep them on `PATH`. A hardware-free demo bench of
|
|
47
|
+
fake drivers is included so everything runs end to end with no hardware.
|
|
48
|
+
|
|
49
|
+
> Installs as `maestro-framework`; the Python modules are `maestro_core`,
|
|
50
|
+
> `maestro_server`, and `maestro_tools`, and the console commands are `maestro`,
|
|
51
|
+
> `maestro-core`, and `maestro-tool`.
|
|
52
|
+
|
|
53
|
+
License: MIT © 2026 Naveen Daniel Kennedy
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Single-distribution packaging for PyPI: bundles maestro_core + maestro_tools +
|
|
2
|
+
# maestro_server (and the built UI) into one wheel, published as "maestro-framework"
|
|
3
|
+
# (the bare "maestro"/"maestro-core" names are taken on PyPI; the console scripts
|
|
4
|
+
# are still maestro/maestro-core/maestro-tool).
|
|
5
|
+
#
|
|
6
|
+
# Build with scripts/build_pypi.(sh|ps1), which assembles src/ and runs build.
|
|
7
|
+
#
|
|
8
|
+
# Author: Naveen Daniel Kennedy
|
|
9
|
+
|
|
10
|
+
[build-system]
|
|
11
|
+
requires = ["hatchling>=1.25"]
|
|
12
|
+
build-backend = "hatchling.build"
|
|
13
|
+
|
|
14
|
+
[project]
|
|
15
|
+
name = "maestro-framework"
|
|
16
|
+
version = "0.1.0"
|
|
17
|
+
description = "MAESTRO — Modular Automotive Embedded System Test, Reporting & Orchestration: an offline-capable platform for automotive Hardware-in-the-Loop (HIL) and embedded device testing (visual designer + pytest engine + server + web UI + mini-tools)."
|
|
18
|
+
readme = "README.md"
|
|
19
|
+
requires-python = ">=3.11"
|
|
20
|
+
license = "MIT"
|
|
21
|
+
authors = [{ name = "Naveen Daniel Kennedy", email = "nvnkennedy@gmail.com" }]
|
|
22
|
+
keywords = ["automotive", "hil", "embedded", "test-automation", "pytest", "endurance", "qnx", "android", "adb", "ssh", "fastapi"]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"License :: OSI Approved :: MIT License",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
"Framework :: FastAPI",
|
|
29
|
+
"Topic :: Software Development :: Testing",
|
|
30
|
+
"Topic :: System :: Hardware",
|
|
31
|
+
"Intended Audience :: Developers",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
dependencies = [
|
|
35
|
+
"pydantic>=2.13,<3",
|
|
36
|
+
"pydantic-settings>=2.14,<3",
|
|
37
|
+
"allure-pytest>=2.16,<3",
|
|
38
|
+
"pytest>=9.1,<10",
|
|
39
|
+
"pytest-xdist>=3.8,<4",
|
|
40
|
+
"pytest-rerunfailures>=16,<17",
|
|
41
|
+
"pytest-check>=2.8,<3",
|
|
42
|
+
"tomli-w>=1.2,<2",
|
|
43
|
+
"Jinja2>=3.1,<4",
|
|
44
|
+
"paramiko>=5.0,<6",
|
|
45
|
+
"pyserial>=3.5,<4",
|
|
46
|
+
"adbutils>=2.12,<3",
|
|
47
|
+
"fastapi>=0.136,<1",
|
|
48
|
+
"uvicorn[standard]>=0.49,<1",
|
|
49
|
+
"SQLAlchemy>=2.0,<3",
|
|
50
|
+
"alembic>=1.13,<2",
|
|
51
|
+
"APScheduler>=3.11,<4",
|
|
52
|
+
"httpx>=0.28,<1",
|
|
53
|
+
"PyJWT>=2.9,<3",
|
|
54
|
+
"python-multipart>=0.0.9",
|
|
55
|
+
"email-validator>=2.1",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[project.optional-dependencies]
|
|
59
|
+
hardware = ["opencv-python>=4.10,<5", "pygrabber>=0.2,<1"]
|
|
60
|
+
reportportal = ["pytest-reportportal>=5.4,<6"]
|
|
61
|
+
|
|
62
|
+
[project.urls]
|
|
63
|
+
Homepage = "https://github.com/NVNKENNEDY/maestro"
|
|
64
|
+
Source = "https://github.com/NVNKENNEDY/maestro"
|
|
65
|
+
|
|
66
|
+
[project.scripts]
|
|
67
|
+
maestro = "maestro_server.cli:main"
|
|
68
|
+
maestro-core = "maestro_core.cli:main"
|
|
69
|
+
maestro-tool = "maestro_tools.cli:main"
|
|
70
|
+
|
|
71
|
+
[project.entry-points."maestro.drivers"]
|
|
72
|
+
turboadb = "maestro_core.drivers.adb_turboadb:TurboADBDriver"
|
|
73
|
+
turbossh = "maestro_core.drivers.ssh_turbossh:TurboSSHDriver"
|
|
74
|
+
power = "maestro_core.drivers.power:PowerScriptDriver"
|
|
75
|
+
dlt = "maestro_core.drivers.dlt:DLTDriver"
|
|
76
|
+
webcam = "maestro_core.drivers.webcam:WebcamDriver"
|
|
77
|
+
fake_device = "maestro_core.drivers.fake:FakeDeviceController"
|
|
78
|
+
fake_power = "maestro_core.drivers.fake:FakePowerController"
|
|
79
|
+
fake_log = "maestro_core.drivers.fake:FakeLogController"
|
|
80
|
+
fake_camera = "maestro_core.drivers.fake:FakeCameraController"
|
|
81
|
+
|
|
82
|
+
[project.entry-points."maestro.steps"]
|
|
83
|
+
builtins = "maestro_core.steps.builtins"
|
|
84
|
+
|
|
85
|
+
[project.entry-points."maestro.detectors"]
|
|
86
|
+
serial = "maestro_core.detect.serial_detector:SerialDetector"
|
|
87
|
+
adb = "maestro_core.detect.adb_detector:ADBDetector"
|
|
88
|
+
camera = "maestro_core.detect.camera_detector:CameraDetector"
|
|
89
|
+
ssh = "maestro_core.detect.ssh_detector:SSHDetector"
|
|
90
|
+
|
|
91
|
+
[project.entry-points."maestro.publishers"]
|
|
92
|
+
email = "maestro_server.integrations.email:EmailPublisher"
|
|
93
|
+
jiraxray = "maestro_server.integrations.jira_xray:JiraXrayPublisher"
|
|
94
|
+
|
|
95
|
+
[tool.hatch.build.targets.wheel]
|
|
96
|
+
packages = ["src/maestro_core", "src/maestro_tools", "src/maestro_server"]
|
|
97
|
+
artifacts = ["src/maestro_server/web/**"]
|
|
98
|
+
|
|
99
|
+
[tool.hatch.build.targets.sdist]
|
|
100
|
+
include = ["src", "README.md"]
|
|
101
|
+
artifacts = ["src/maestro_server/web/**"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""MAESTRO core — the headless heart of the platform.
|
|
2
|
+
|
|
3
|
+
MAESTRO (Modular Automotive Embedded System Test, Reporting & Orchestration) is an
|
|
4
|
+
offline-capable test-automation platform for automotive Hardware-in-the-Loop
|
|
5
|
+
(HIL) and embedded device testing.
|
|
6
|
+
|
|
7
|
+
``maestro_core`` contains everything needed to execute a test *scenario* with no
|
|
8
|
+
server and no UI:
|
|
9
|
+
|
|
10
|
+
* :mod:`maestro_core.scenario` — the versioned JSON scenario model (the single
|
|
11
|
+
source of truth authored on the canvas) plus validation and JSON-Schema export.
|
|
12
|
+
* :mod:`maestro_core.steps` — the step registry, the per-step execution context,
|
|
13
|
+
the built-in step library, and the generic script-wrapping adapter.
|
|
14
|
+
* :mod:`maestro_core.drivers` — thin adapters that shell out to the user's own
|
|
15
|
+
device tools (turboadb, turbossh, power scripts, DLT, webcam), plus fully
|
|
16
|
+
functional fake drivers for hardware-free testing.
|
|
17
|
+
* :mod:`maestro_core.detect` — device detectors (serial, adb, camera, ssh).
|
|
18
|
+
* :mod:`maestro_core.engine` — the pytest plugin and small generic runner that
|
|
19
|
+
turn a scenario into executed, reported tests.
|
|
20
|
+
* :mod:`maestro_core.endurance` — the long-running cycle loop and stop conditions.
|
|
21
|
+
* :mod:`maestro_core.reporting` — the attachment pipeline and Allure/JUnit hooks.
|
|
22
|
+
* :mod:`maestro_core.config` — layered TOML configuration loading.
|
|
23
|
+
|
|
24
|
+
Author:
|
|
25
|
+
Naveen Daniel Kennedy <nvnkennedy@gmail.com>
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
#: Semantic version of the MAESTRO core package (single source of truth; the
|
|
31
|
+
#: build backend reads this string to stamp the wheel).
|
|
32
|
+
__version__ = "0.1.0"
|
|
33
|
+
|
|
34
|
+
#: Human-readable product name, backronym, and tagline used across the UI, the
|
|
35
|
+
#: About page, and the docs. MAESTRO conducts the test bench the way a maestro
|
|
36
|
+
#: leads an orchestra — keeping devices, scenarios, and endurance cycles in time —
|
|
37
|
+
#: which is fitting for automotive infotainment, where the head unit conducts the
|
|
38
|
+
#: vehicle's audio, video, and connectivity.
|
|
39
|
+
PRODUCT_NAME = "MAESTRO"
|
|
40
|
+
PRODUCT_BACKRONYM = "Modular Automotive Embedded System Test, Reporting & Orchestration"
|
|
41
|
+
PRODUCT_TAGLINE = "Automotive embedded test orchestration"
|
|
42
|
+
|
|
43
|
+
__all__ = ["PRODUCT_BACKRONYM", "PRODUCT_NAME", "PRODUCT_TAGLINE", "__version__"]
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Headless command-line interface for ``maestro-core``.
|
|
2
|
+
|
|
3
|
+
Run, validate, and inspect scenarios with no server and no UI:
|
|
4
|
+
|
|
5
|
+
* ``maestro-core run <scenario.json> --bench <bench.toml>`` — execute a scenario.
|
|
6
|
+
* ``maestro-core validate <scenario.json>`` — report validation issues.
|
|
7
|
+
* ``maestro-core schema [--out file]`` — export the scenario JSON-Schema.
|
|
8
|
+
* ``maestro-core steps`` — list every registered step.
|
|
9
|
+
* ``maestro-core detect [--host H]`` — run device detectors and print proposals.
|
|
10
|
+
|
|
11
|
+
Author:
|
|
12
|
+
Naveen Daniel Kennedy <nvnkennedy@gmail.com>
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import json
|
|
19
|
+
import sys
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
from maestro_core import PRODUCT_BACKRONYM, PRODUCT_NAME, __version__
|
|
24
|
+
from maestro_core.config import build_devices, load_bench_toml
|
|
25
|
+
from maestro_core.detect import run_detection
|
|
26
|
+
from maestro_core.engine import RunOptions, run_scenario
|
|
27
|
+
from maestro_core.engine.plugin import ScenarioCase, run_via_pytest
|
|
28
|
+
from maestro_core.reporting import write_junit, write_run_record
|
|
29
|
+
from maestro_core.scenario import (
|
|
30
|
+
load_scenario_file,
|
|
31
|
+
parse_scenario,
|
|
32
|
+
scenario_json_schema,
|
|
33
|
+
validate_scenario,
|
|
34
|
+
)
|
|
35
|
+
from maestro_core.steps import all_steps, known_step_names, load_all_steps
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
39
|
+
"""Construct the argument parser for the CLI."""
|
|
40
|
+
parser = argparse.ArgumentParser(
|
|
41
|
+
prog="maestro-core", description=f"{PRODUCT_NAME} — {PRODUCT_BACKRONYM}"
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument("-V", "--version", action="version", version=f"maestro-core {__version__}")
|
|
44
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
45
|
+
|
|
46
|
+
run = sub.add_parser("run", help="run a scenario on a bench")
|
|
47
|
+
run.add_argument("scenario", help="path to the scenario JSON")
|
|
48
|
+
run.add_argument("--bench", required=True, help="path to the bench TOML")
|
|
49
|
+
run.add_argument("--out", default=None, help="output directory for this run")
|
|
50
|
+
run.add_argument("--mode", choices=["serial", "parallel", "step"], default="serial")
|
|
51
|
+
run.add_argument("--user-steps", default=None, help="directory of drop-in step scripts")
|
|
52
|
+
run.add_argument(
|
|
53
|
+
"--pytest", action="store_true", help="execute through pytest (writes an Allure report)"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
validate = sub.add_parser("validate", help="validate a scenario")
|
|
57
|
+
validate.add_argument("scenario", help="path to the scenario JSON")
|
|
58
|
+
|
|
59
|
+
schema = sub.add_parser("schema", help="export the scenario JSON-Schema")
|
|
60
|
+
schema.add_argument("--out", default=None, help="write the schema here instead of stdout")
|
|
61
|
+
|
|
62
|
+
sub.add_parser("steps", help="list registered steps")
|
|
63
|
+
|
|
64
|
+
detect = sub.add_parser("detect", help="run device detectors")
|
|
65
|
+
detect.add_argument("--host", default=None, help="ssh host to probe")
|
|
66
|
+
detect.add_argument("--port", type=int, default=22, help="ssh port to probe")
|
|
67
|
+
return parser
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _cmd_run(args: argparse.Namespace) -> int:
|
|
71
|
+
"""Execute the ``run`` subcommand."""
|
|
72
|
+
load_all_steps(args.user_steps)
|
|
73
|
+
scenario = load_scenario_file(args.scenario, validate=True, known_steps=known_step_names())
|
|
74
|
+
bench = load_bench_toml(args.bench)
|
|
75
|
+
stamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
|
76
|
+
out = (
|
|
77
|
+
Path(args.out)
|
|
78
|
+
if args.out
|
|
79
|
+
else Path(".maestro") / "runs" / f"{scenario.metadata.id}-{stamp}"
|
|
80
|
+
)
|
|
81
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
|
|
83
|
+
devices = build_devices(bench, output_dir=out / "artifacts")
|
|
84
|
+
options = RunOptions(bench=bench.name, mode=args.mode, user_steps_dir=args.user_steps)
|
|
85
|
+
|
|
86
|
+
if args.pytest:
|
|
87
|
+
case = ScenarioCase(
|
|
88
|
+
id=f"{scenario.metadata.id}[{bench.name}]",
|
|
89
|
+
scenario=scenario,
|
|
90
|
+
devices=devices,
|
|
91
|
+
run_dir=out,
|
|
92
|
+
options=options,
|
|
93
|
+
)
|
|
94
|
+
results = run_via_pytest([case], out)
|
|
95
|
+
run = results.get(case.id)
|
|
96
|
+
if run is None:
|
|
97
|
+
print("error: the scenario did not run under pytest", file=sys.stderr)
|
|
98
|
+
return 2
|
|
99
|
+
else:
|
|
100
|
+
run = run_scenario(scenario, devices, out, options=options)
|
|
101
|
+
|
|
102
|
+
write_run_record(run, out / "run.json")
|
|
103
|
+
write_junit(run, out / "junit.xml")
|
|
104
|
+
|
|
105
|
+
print(f"{run.scenario_id} on {run.bench}: {run.status.upper()}")
|
|
106
|
+
print(f" steps: {run.summary.get('passed', 0)} passed, {run.summary.get('failed', 0)} failed")
|
|
107
|
+
if run.is_endurance:
|
|
108
|
+
for group_id, rollup in run.summary.get("endurance", {}).items():
|
|
109
|
+
print(
|
|
110
|
+
f" endurance[{group_id}]: {rollup['passed']}/{rollup['completed_cycles']} cycles "
|
|
111
|
+
f"passed, first failure: {rollup['first_failure_cycle']}"
|
|
112
|
+
)
|
|
113
|
+
print(f" output: {out}")
|
|
114
|
+
return 0 if run.status == "passed" else 1
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _cmd_validate(args: argparse.Namespace) -> int:
|
|
118
|
+
"""Execute the ``validate`` subcommand."""
|
|
119
|
+
load_all_steps(None)
|
|
120
|
+
scenario = parse_scenario(Path(args.scenario).read_text(encoding="utf-8"))
|
|
121
|
+
issues = validate_scenario(scenario, known_steps=known_step_names())
|
|
122
|
+
if not issues:
|
|
123
|
+
print("valid: no issues found")
|
|
124
|
+
return 0
|
|
125
|
+
for issue in issues:
|
|
126
|
+
where = f" @{issue.location}" if issue.location else ""
|
|
127
|
+
print(f"[{issue.severity}] {issue.code}{where}: {issue.message}")
|
|
128
|
+
return 0 if all(i.severity != "error" for i in issues) else 1
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _cmd_schema(args: argparse.Namespace) -> int:
|
|
132
|
+
"""Execute the ``schema`` subcommand."""
|
|
133
|
+
text = json.dumps(scenario_json_schema(), indent=2)
|
|
134
|
+
if args.out:
|
|
135
|
+
Path(args.out).write_text(text + "\n", encoding="utf-8")
|
|
136
|
+
print(f"wrote schema to {args.out}")
|
|
137
|
+
else:
|
|
138
|
+
print(text)
|
|
139
|
+
return 0
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _cmd_steps(_: argparse.Namespace) -> int:
|
|
143
|
+
"""Execute the ``steps`` subcommand."""
|
|
144
|
+
load_all_steps(None)
|
|
145
|
+
for name, spec in sorted(all_steps().items()):
|
|
146
|
+
params = ", ".join(f"{p.name}:{p.kind}{'' if p.required else '?'}" for p in spec.params)
|
|
147
|
+
suffix = " [free-form]" if spec.freeform else ""
|
|
148
|
+
print(f"{name} ({spec.category}){suffix}")
|
|
149
|
+
if params:
|
|
150
|
+
print(f" {params}")
|
|
151
|
+
return 0
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _cmd_detect(args: argparse.Namespace) -> int:
|
|
155
|
+
"""Execute the ``detect`` subcommand."""
|
|
156
|
+
proposals, errors = run_detection(host=args.host, port=args.port)
|
|
157
|
+
output = {
|
|
158
|
+
"proposals": {name: [p.model_dump() for p in items] for name, items in proposals.items()},
|
|
159
|
+
"errors": errors,
|
|
160
|
+
}
|
|
161
|
+
print(json.dumps(output, indent=2))
|
|
162
|
+
return 0
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def main(argv: list[str] | None = None) -> int:
|
|
166
|
+
"""CLI entry point. Returns a process exit code."""
|
|
167
|
+
parser = _build_parser()
|
|
168
|
+
args = parser.parse_args(argv)
|
|
169
|
+
handlers = {
|
|
170
|
+
"run": _cmd_run,
|
|
171
|
+
"validate": _cmd_validate,
|
|
172
|
+
"schema": _cmd_schema,
|
|
173
|
+
"steps": _cmd_steps,
|
|
174
|
+
"detect": _cmd_detect,
|
|
175
|
+
}
|
|
176
|
+
return handlers[args.command](args)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
if __name__ == "__main__":
|
|
180
|
+
sys.exit(main())
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""MAESTRO configuration — typed settings, layered TOML, and the bench bridge.
|
|
2
|
+
|
|
3
|
+
Author:
|
|
4
|
+
Naveen Daniel Kennedy <nvnkennedy@gmail.com>
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from maestro_core.config.devices import build_devices, driver_name_for
|
|
10
|
+
from maestro_core.config.loader import (
|
|
11
|
+
deep_merge,
|
|
12
|
+
load_bench_toml,
|
|
13
|
+
load_layered,
|
|
14
|
+
load_toml,
|
|
15
|
+
write_bench_toml,
|
|
16
|
+
)
|
|
17
|
+
from maestro_core.config.models import BenchConfig, DeviceConfig
|
|
18
|
+
from maestro_core.config.settings import MaestroSettings, get_settings
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"BenchConfig",
|
|
22
|
+
"DeviceConfig",
|
|
23
|
+
"MaestroSettings",
|
|
24
|
+
"build_devices",
|
|
25
|
+
"deep_merge",
|
|
26
|
+
"driver_name_for",
|
|
27
|
+
"get_settings",
|
|
28
|
+
"load_bench_toml",
|
|
29
|
+
"load_layered",
|
|
30
|
+
"load_toml",
|
|
31
|
+
"write_bench_toml",
|
|
32
|
+
]
|