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.
Files changed (97) hide show
  1. maestro_framework-0.1.0/.gitignore +46 -0
  2. maestro_framework-0.1.0/PKG-INFO +99 -0
  3. maestro_framework-0.1.0/README.md +53 -0
  4. maestro_framework-0.1.0/pyproject.toml +101 -0
  5. maestro_framework-0.1.0/src/maestro_core/__init__.py +43 -0
  6. maestro_framework-0.1.0/src/maestro_core/cli.py +180 -0
  7. maestro_framework-0.1.0/src/maestro_core/config/__init__.py +32 -0
  8. maestro_framework-0.1.0/src/maestro_core/config/devices.py +152 -0
  9. maestro_framework-0.1.0/src/maestro_core/config/loader.py +111 -0
  10. maestro_framework-0.1.0/src/maestro_core/config/models.py +86 -0
  11. maestro_framework-0.1.0/src/maestro_core/config/settings.py +60 -0
  12. maestro_framework-0.1.0/src/maestro_core/detect/__init__.py +31 -0
  13. maestro_framework-0.1.0/src/maestro_core/detect/adb_detector.py +67 -0
  14. maestro_framework-0.1.0/src/maestro_core/detect/base.py +57 -0
  15. maestro_framework-0.1.0/src/maestro_core/detect/camera_detector.py +72 -0
  16. maestro_framework-0.1.0/src/maestro_core/detect/registry.py +64 -0
  17. maestro_framework-0.1.0/src/maestro_core/detect/serial_detector.py +42 -0
  18. maestro_framework-0.1.0/src/maestro_core/detect/ssh_detector.py +83 -0
  19. maestro_framework-0.1.0/src/maestro_core/drivers/__init__.py +59 -0
  20. maestro_framework-0.1.0/src/maestro_core/drivers/_cli.py +61 -0
  21. maestro_framework-0.1.0/src/maestro_core/drivers/adb_turboadb.py +138 -0
  22. maestro_framework-0.1.0/src/maestro_core/drivers/base.py +333 -0
  23. maestro_framework-0.1.0/src/maestro_core/drivers/dlt.py +116 -0
  24. maestro_framework-0.1.0/src/maestro_core/drivers/fake.py +228 -0
  25. maestro_framework-0.1.0/src/maestro_core/drivers/power.py +92 -0
  26. maestro_framework-0.1.0/src/maestro_core/drivers/registry.py +69 -0
  27. maestro_framework-0.1.0/src/maestro_core/drivers/ssh_turbossh.py +113 -0
  28. maestro_framework-0.1.0/src/maestro_core/drivers/webcam.py +96 -0
  29. maestro_framework-0.1.0/src/maestro_core/endurance/__init__.py +20 -0
  30. maestro_framework-0.1.0/src/maestro_core/endurance/health.py +72 -0
  31. maestro_framework-0.1.0/src/maestro_core/endurance/loop.py +97 -0
  32. maestro_framework-0.1.0/src/maestro_core/endurance/rollup.py +65 -0
  33. maestro_framework-0.1.0/src/maestro_core/endurance/stop.py +97 -0
  34. maestro_framework-0.1.0/src/maestro_core/engine/__init__.py +30 -0
  35. maestro_framework-0.1.0/src/maestro_core/engine/_pytest_scenario.py +29 -0
  36. maestro_framework-0.1.0/src/maestro_core/engine/expect.py +53 -0
  37. maestro_framework-0.1.0/src/maestro_core/engine/plugin.py +112 -0
  38. maestro_framework-0.1.0/src/maestro_core/engine/runner.py +435 -0
  39. maestro_framework-0.1.0/src/maestro_core/errors.py +63 -0
  40. maestro_framework-0.1.0/src/maestro_core/plugins.py +88 -0
  41. maestro_framework-0.1.0/src/maestro_core/proc.py +154 -0
  42. maestro_framework-0.1.0/src/maestro_core/py.typed +0 -0
  43. maestro_framework-0.1.0/src/maestro_core/reporting/__init__.py +45 -0
  44. maestro_framework-0.1.0/src/maestro_core/reporting/junit.py +108 -0
  45. maestro_framework-0.1.0/src/maestro_core/reporting/records.py +146 -0
  46. maestro_framework-0.1.0/src/maestro_core/reporting/reporter.py +86 -0
  47. maestro_framework-0.1.0/src/maestro_core/reporting/sink.py +124 -0
  48. maestro_framework-0.1.0/src/maestro_core/scenario/__init__.py +84 -0
  49. maestro_framework-0.1.0/src/maestro_core/scenario/graph.py +318 -0
  50. maestro_framework-0.1.0/src/maestro_core/scenario/io.py +102 -0
  51. maestro_framework-0.1.0/src/maestro_core/scenario/models.py +276 -0
  52. maestro_framework-0.1.0/src/maestro_core/scenario/schema.py +41 -0
  53. maestro_framework-0.1.0/src/maestro_core/scenario/validation.py +247 -0
  54. maestro_framework-0.1.0/src/maestro_core/steps/__init__.py +55 -0
  55. maestro_framework-0.1.0/src/maestro_core/steps/builtins.py +270 -0
  56. maestro_framework-0.1.0/src/maestro_core/steps/context.py +125 -0
  57. maestro_framework-0.1.0/src/maestro_core/steps/discovery.py +90 -0
  58. maestro_framework-0.1.0/src/maestro_core/steps/registry.py +245 -0
  59. maestro_framework-0.1.0/src/maestro_core/steps/script.py +128 -0
  60. maestro_framework-0.1.0/src/maestro_core/timeutil.py +87 -0
  61. maestro_framework-0.1.0/src/maestro_server/__init__.py +16 -0
  62. maestro_framework-0.1.0/src/maestro_server/api/__init__.py +19 -0
  63. maestro_framework-0.1.0/src/maestro_server/api/benches.py +88 -0
  64. maestro_framework-0.1.0/src/maestro_server/api/crud_router.py +179 -0
  65. maestro_framework-0.1.0/src/maestro_server/api/deps.py +42 -0
  66. maestro_framework-0.1.0/src/maestro_server/api/misc.py +132 -0
  67. maestro_framework-0.1.0/src/maestro_server/api/resources.py +331 -0
  68. maestro_framework-0.1.0/src/maestro_server/api/runs.py +252 -0
  69. maestro_framework-0.1.0/src/maestro_server/auth.py +61 -0
  70. maestro_framework-0.1.0/src/maestro_server/cli.py +46 -0
  71. maestro_framework-0.1.0/src/maestro_server/db/__init__.py +48 -0
  72. maestro_framework-0.1.0/src/maestro_server/db/base.py +53 -0
  73. maestro_framework-0.1.0/src/maestro_server/db/models.py +249 -0
  74. maestro_framework-0.1.0/src/maestro_server/db/session.py +52 -0
  75. maestro_framework-0.1.0/src/maestro_server/integrations/__init__.py +14 -0
  76. maestro_framework-0.1.0/src/maestro_server/integrations/base.py +38 -0
  77. maestro_framework-0.1.0/src/maestro_server/integrations/dispatch.py +43 -0
  78. maestro_framework-0.1.0/src/maestro_server/integrations/email.py +78 -0
  79. maestro_framework-0.1.0/src/maestro_server/integrations/jira_xray.py +57 -0
  80. maestro_framework-0.1.0/src/maestro_server/main.py +124 -0
  81. maestro_framework-0.1.0/src/maestro_server/scheduler.py +183 -0
  82. maestro_framework-0.1.0/src/maestro_server/schemas.py +520 -0
  83. maestro_framework-0.1.0/src/maestro_server/services/__init__.py +14 -0
  84. maestro_framework-0.1.0/src/maestro_server/services/bench.py +40 -0
  85. maestro_framework-0.1.0/src/maestro_server/services/crud.py +125 -0
  86. maestro_framework-0.1.0/src/maestro_server/services/detection.py +102 -0
  87. maestro_framework-0.1.0/src/maestro_server/services/run_trigger.py +137 -0
  88. maestro_framework-0.1.0/src/maestro_server/services/runs.py +300 -0
  89. maestro_framework-0.1.0/src/maestro_server/settings.py +75 -0
  90. maestro_framework-0.1.0/src/maestro_server/web/assets/index-BQIDZnuk.js +275 -0
  91. maestro_framework-0.1.0/src/maestro_server/web/assets/index-Cee9fdIV.css +1 -0
  92. maestro_framework-0.1.0/src/maestro_server/web/favicon.svg +15 -0
  93. maestro_framework-0.1.0/src/maestro_server/web/index.html +15 -0
  94. maestro_framework-0.1.0/src/maestro_tools/__init__.py +17 -0
  95. maestro_framework-0.1.0/src/maestro_tools/cli.py +82 -0
  96. maestro_framework-0.1.0/src/maestro_tools/generator.py +34 -0
  97. 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
+ ]