testerkit 0.4.0__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.
- testerkit/__init__.py +68 -0
- testerkit/_docs/_assets/operator-ui/channels/table.png +0 -0
- testerkit/_docs/_assets/operator-ui/dashboard/runs.png +0 -0
- testerkit/_docs/_assets/operator-ui/dashboard/stations.png +0 -0
- testerkit/_docs/_assets/operator-ui/events/filters.png +0 -0
- testerkit/_docs/_assets/operator-ui/events/table.png +0 -0
- testerkit/_docs/_assets/operator-ui/explore/chart.png +0 -0
- testerkit/_docs/_assets/operator-ui/explore/filters.png +0 -0
- testerkit/_docs/_assets/operator-ui/explore/plot-controls.png +0 -0
- testerkit/_docs/_assets/operator-ui/files/table.png +0 -0
- testerkit/_docs/_assets/operator-ui/launch/form.png +0 -0
- testerkit/_docs/_assets/operator-ui/metrics/filters.png +0 -0
- testerkit/_docs/_assets/operator-ui/metrics/pareto.png +0 -0
- testerkit/_docs/_assets/operator-ui/metrics/ppk.png +0 -0
- testerkit/_docs/_assets/operator-ui/metrics/yield.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/detail-header.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/detail-measurements.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/detail-overview.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/detail-steps.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/stats.png +0 -0
- testerkit/_docs/_assets/operator-ui/results/table.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/designer.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/docs.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/fixtures.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/instruments.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/parts.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/profiles.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/stations.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/tests.png +0 -0
- testerkit/_docs/_assets/operator-ui/tour/uuts.png +0 -0
- testerkit/_docs/concepts/configuration/capabilities.md +447 -0
- testerkit/_docs/concepts/configuration/fixtures.md +335 -0
- testerkit/_docs/concepts/configuration/index.md +14 -0
- testerkit/_docs/concepts/configuration/parts.md +276 -0
- testerkit/_docs/concepts/configuration/stations.md +214 -0
- testerkit/_docs/concepts/data/data-stores.md +199 -0
- testerkit/_docs/concepts/data/event-log.md +185 -0
- testerkit/_docs/concepts/data/event-sourcing.md +76 -0
- testerkit/_docs/concepts/data/flight-streaming.md +58 -0
- testerkit/_docs/concepts/data/index.md +17 -0
- testerkit/_docs/concepts/data/sessions.md +78 -0
- testerkit/_docs/concepts/data/three-verbs.md +260 -0
- testerkit/_docs/concepts/execution/index.md +13 -0
- testerkit/_docs/concepts/execution/outcomes.md +199 -0
- testerkit/_docs/concepts/execution/step-hierarchy.md +170 -0
- testerkit/_docs/concepts/execution/step-manifest.md +115 -0
- testerkit/_docs/concepts/index.md +40 -0
- testerkit/_docs/concepts/overview/ai-integration.md +57 -0
- testerkit/_docs/concepts/overview/architecture.md +357 -0
- testerkit/_docs/concepts/overview/index.md +14 -0
- testerkit/_docs/concepts/overview/platform-vs-framework.md +147 -0
- testerkit/_docs/concepts/overview/pytest.md +53 -0
- testerkit/_docs/concepts/overview/tiers.md +46 -0
- testerkit/_docs/how-to/catalog/datasheet-to-test.md +169 -0
- testerkit/_docs/how-to/catalog/index.md +11 -0
- testerkit/_docs/how-to/configuration/configuring-stations.md +364 -0
- testerkit/_docs/how-to/configuration/custom-drivers.md +484 -0
- testerkit/_docs/how-to/configuration/index.md +13 -0
- testerkit/_docs/how-to/configuration/mock-mode.md +342 -0
- testerkit/_docs/how-to/data/benchmarking.md +120 -0
- testerkit/_docs/how-to/data/capture-an-artifact.md +84 -0
- testerkit/_docs/how-to/data/capture-waveform.md +110 -0
- testerkit/_docs/how-to/data/choosing-a-channel-verb.md +108 -0
- testerkit/_docs/how-to/data/compare-runs.md +120 -0
- testerkit/_docs/how-to/data/export-results.md +104 -0
- testerkit/_docs/how-to/data/find-flaky-tests.md +108 -0
- testerkit/_docs/how-to/data/grafana-dashboards.md +148 -0
- testerkit/_docs/how-to/data/index.md +23 -0
- testerkit/_docs/how-to/data/mcp-debug-failures.md +125 -0
- testerkit/_docs/how-to/data/mcp-query-runs.md +120 -0
- testerkit/_docs/how-to/data/querying-channels.md +117 -0
- testerkit/_docs/how-to/data/querying-events.md +85 -0
- testerkit/_docs/how-to/data/stream-live-channel.md +134 -0
- testerkit/_docs/how-to/execution/custom-operator-ui.md +136 -0
- testerkit/_docs/how-to/execution/index.md +21 -0
- testerkit/_docs/how-to/execution/limits.md +193 -0
- testerkit/_docs/how-to/execution/managing-sessions.md +100 -0
- testerkit/_docs/how-to/execution/multi-uut-testing.md +211 -0
- testerkit/_docs/how-to/execution/operator-prompts.md +151 -0
- testerkit/_docs/how-to/execution/profiles.md +304 -0
- testerkit/_docs/how-to/execution/spec-driven-testing.md +169 -0
- testerkit/_docs/how-to/execution/test-context.md +212 -0
- testerkit/_docs/how-to/execution/traceability.md +252 -0
- testerkit/_docs/how-to/execution/vector-expansion.md +394 -0
- testerkit/_docs/how-to/execution/writing-tests.md +363 -0
- testerkit/_docs/how-to/index.md +59 -0
- testerkit/_docs/how-to/overview/index.md +11 -0
- testerkit/_docs/how-to/overview/mcp-integration.md +168 -0
- testerkit/_docs/how-to/overview/operator-ui-tour.md +234 -0
- testerkit/_docs/integration/data/grafana.md +162 -0
- testerkit/_docs/integration/data/index.md +15 -0
- testerkit/_docs/integration/data/lakehouse-import.md +226 -0
- testerkit/_docs/integration/data/logging.md +131 -0
- testerkit/_docs/integration/data/results-api.md +51 -0
- testerkit/_docs/integration/index.md +20 -0
- testerkit/_docs/integration/runtime/harness.md +281 -0
- testerkit/_docs/integration/runtime/index.md +13 -0
- testerkit/_docs/integration/runtime/instruments.md +284 -0
- testerkit/_docs/integration/runtime/pytest-existing.md +315 -0
- testerkit/_docs/reference/catalog/cookbook.md +389 -0
- testerkit/_docs/reference/catalog/index.md +11 -0
- testerkit/_docs/reference/catalog/schema.md +375 -0
- testerkit/_docs/reference/cli.md +660 -0
- testerkit/_docs/reference/configuration.md +406 -0
- testerkit/_docs/reference/data/channels-schema.md +145 -0
- testerkit/_docs/reference/data/event-types.md +554 -0
- testerkit/_docs/reference/data/events-schema.md +120 -0
- testerkit/_docs/reference/data/files-schema.md +141 -0
- testerkit/_docs/reference/data/index.md +18 -0
- testerkit/_docs/reference/data/models.md +2229 -0
- testerkit/_docs/reference/data/outputs.md +82 -0
- testerkit/_docs/reference/data/parquet-schema.md +529 -0
- testerkit/_docs/reference/data/performance-limits.md +224 -0
- testerkit/_docs/reference/data/query-api.md +247 -0
- testerkit/_docs/reference/index.md +69 -0
- testerkit/_docs/reference/operator-ui/channels/detail.md +82 -0
- testerkit/_docs/reference/operator-ui/channels/list.md +70 -0
- testerkit/_docs/reference/operator-ui/dashboard.md +87 -0
- testerkit/_docs/reference/operator-ui/designer.md +167 -0
- testerkit/_docs/reference/operator-ui/events.md +89 -0
- testerkit/_docs/reference/operator-ui/files.md +74 -0
- testerkit/_docs/reference/operator-ui/fixtures.md +100 -0
- testerkit/_docs/reference/operator-ui/instruments.md +126 -0
- testerkit/_docs/reference/operator-ui/launch.md +79 -0
- testerkit/_docs/reference/operator-ui/live.md +102 -0
- testerkit/_docs/reference/operator-ui/measurements.md +159 -0
- testerkit/_docs/reference/operator-ui/metrics.md +217 -0
- testerkit/_docs/reference/operator-ui/parts.md +98 -0
- testerkit/_docs/reference/operator-ui/profiles.md +103 -0
- testerkit/_docs/reference/operator-ui/results/detail.md +214 -0
- testerkit/_docs/reference/operator-ui/results/list.md +106 -0
- testerkit/_docs/reference/operator-ui/stations.md +98 -0
- testerkit/_docs/reference/operator-ui/tests.md +173 -0
- testerkit/_docs/reference/operator-ui/uuts.md +67 -0
- testerkit/_docs/reference/overview/index.md +11 -0
- testerkit/_docs/reference/overview/pytest-native.md +142 -0
- testerkit/_docs/reference/overview/skills.md +177 -0
- testerkit/_docs/reference/pytest/fixtures.md +342 -0
- testerkit/_docs/reference/pytest/index.md +12 -0
- testerkit/_docs/reference/pytest/markers.md +257 -0
- testerkit/_docs/reference/runtime/api.md +246 -0
- testerkit/_docs/reference/runtime/client.md +280 -0
- testerkit/_docs/reference/runtime/connect.md +140 -0
- testerkit/_docs/reference/runtime/index.md +13 -0
- testerkit/_docs/tutorial/01-first-test.md +113 -0
- testerkit/_docs/tutorial/02-mock-instruments.md +88 -0
- testerkit/_docs/tutorial/03-fixtures.md +169 -0
- testerkit/_docs/tutorial/04-limits.md +139 -0
- testerkit/_docs/tutorial/05-configuration.md +194 -0
- testerkit/_docs/tutorial/06-specifications.md +273 -0
- testerkit/_docs/tutorial/07-real-instruments.md +245 -0
- testerkit/_docs/tutorial/08-capabilities.md +223 -0
- testerkit/_docs/tutorial/09-production.md +374 -0
- testerkit/_docs/tutorial/10-live-monitoring.md +156 -0
- testerkit/_docs/tutorial/11-waveforms-and-evidence.md +169 -0
- testerkit/_docs/tutorial/12-continuous-monitoring.md +145 -0
- testerkit/_docs/tutorial/13-parallel-testing.md +233 -0
- testerkit/_docs/tutorial/index.md +61 -0
- testerkit/_docs/tutorial/quickstart.md +282 -0
- testerkit/analysis/__init__.py +9 -0
- testerkit/analysis/_common.py +24 -0
- testerkit/analysis/measurement_facets.py +524 -0
- testerkit/analysis/measurements_query.py +1458 -0
- testerkit/analysis/metrics.py +389 -0
- testerkit/analysis/runs_query.py +434 -0
- testerkit/analysis/steps_query.py +413 -0
- testerkit/api/__init__.py +6 -0
- testerkit/api/_mime.py +71 -0
- testerkit/api/app.py +1171 -0
- testerkit/api/dialogs/__init__.py +36 -0
- testerkit/api/dialogs/manager.py +617 -0
- testerkit/api/dialogs/models.py +79 -0
- testerkit/api/models.py +80 -0
- testerkit/api/responses.py +205 -0
- testerkit/api/runner.py +222 -0
- testerkit/api/schemas.py +311 -0
- testerkit/benchmark/__init__.py +34 -0
- testerkit/benchmark/concurrency.py +248 -0
- testerkit/benchmark/core.py +232 -0
- testerkit/benchmark/runner.py +628 -0
- testerkit/benchmark/scenario.py +313 -0
- testerkit/benchmark/system.py +191 -0
- testerkit/benchmark/workloads.py +507 -0
- testerkit/catalog/__init__.py +6 -0
- testerkit/catalog/generic/generic_dmm.yaml +61 -0
- testerkit/catalog/generic/generic_eload.yaml +31 -0
- testerkit/catalog/generic/generic_fgen.yaml +56 -0
- testerkit/catalog/generic/generic_oscilloscope.yaml +45 -0
- testerkit/catalog/generic/generic_psu.yaml +77 -0
- testerkit/catalog/generic/generic_smu.yaml +73 -0
- testerkit/channels.py +538 -0
- testerkit/cli/__init__.py +32 -0
- testerkit/cli/_common.py +113 -0
- testerkit/cli/_time.py +179 -0
- testerkit/cli/benchmark_cmd.py +56 -0
- testerkit/cli/catalog_cmd.py +39 -0
- testerkit/cli/daemon.py +157 -0
- testerkit/cli/data_cmd.py +1040 -0
- testerkit/cli/discover_cmd.py +94 -0
- testerkit/cli/docs_cmd.py +100 -0
- testerkit/cli/instrument.py +172 -0
- testerkit/cli/mcp_cmd.py +37 -0
- testerkit/cli/metrics.py +366 -0
- testerkit/cli/project.py +312 -0
- testerkit/cli/root.py +14 -0
- testerkit/cli/runs.py +412 -0
- testerkit/cli/schema_cmd.py +87 -0
- testerkit/cli/serve_cmd.py +50 -0
- testerkit/cli/setup_cmd.py +604 -0
- testerkit/cli/station.py +294 -0
- testerkit/cli/validate.py +112 -0
- testerkit/client.py +463 -0
- testerkit/connect.py +635 -0
- testerkit/data/__init__.py +8 -0
- testerkit/data/_accumulator_pool.py +455 -0
- testerkit/data/_atomic.py +59 -0
- testerkit/data/_collection_indices.py +67 -0
- testerkit/data/_daemon_lifecycle.py +535 -0
- testerkit/data/_duckdb_daemon.py +695 -0
- testerkit/data/_duckdb_flight_server.py +813 -0
- testerkit/data/_event_filters.py +15 -0
- testerkit/data/_event_reader.py +101 -0
- testerkit/data/_flight_errors.py +199 -0
- testerkit/data/_flight_query.py +202 -0
- testerkit/data/_flight_retry.py +148 -0
- testerkit/data/_flight_subscribe.py +67 -0
- testerkit/data/_index_epoch.py +342 -0
- testerkit/data/_ipc_writer.py +185 -0
- testerkit/data/_json_safe.py +63 -0
- testerkit/data/_process.py +18 -0
- testerkit/data/_push_relay.py +124 -0
- testerkit/data/_runs_duckdb_daemon.py +2704 -0
- testerkit/data/_session_reaper.py +110 -0
- testerkit/data/_sql_helpers.py +30 -0
- testerkit/data/_store.py +24 -0
- testerkit/data/backends/__init__.py +7 -0
- testerkit/data/backends/_event_accumulator.py +957 -0
- testerkit/data/backends/_row_helpers.py +1172 -0
- testerkit/data/backends/parquet.py +1143 -0
- testerkit/data/backends/protocol.py +35 -0
- testerkit/data/channels/__init__.py +8 -0
- testerkit/data/channels/_flight_daemon.py +75 -0
- testerkit/data/channels/client.py +242 -0
- testerkit/data/channels/flight_manager.py +88 -0
- testerkit/data/channels/index.py +664 -0
- testerkit/data/channels/models.py +418 -0
- testerkit/data/channels/server.py +162 -0
- testerkit/data/channels/store.py +1481 -0
- testerkit/data/data_dir.py +61 -0
- testerkit/data/duckdb_manager.py +79 -0
- testerkit/data/event_log.py +384 -0
- testerkit/data/event_store.py +768 -0
- testerkit/data/events.py +1025 -0
- testerkit/data/exporters/__init__.py +6 -0
- testerkit/data/exporters/_helpers.py +40 -0
- testerkit/data/exporters/csv_exporter.py +168 -0
- testerkit/data/exporters/hdf5.py +234 -0
- testerkit/data/exporters/json_exporter.py +229 -0
- testerkit/data/exporters/mdf4.py +201 -0
- testerkit/data/exporters/stdf.py +255 -0
- testerkit/data/exporters/tdms.py +292 -0
- testerkit/data/files/__init__.py +71 -0
- testerkit/data/files/_backend.py +207 -0
- testerkit/data/files/_catalog_daemon.py +84 -0
- testerkit/data/files/catalog.py +218 -0
- testerkit/data/files/catalog_manager.py +325 -0
- testerkit/data/files/models.py +60 -0
- testerkit/data/files/serializers.py +414 -0
- testerkit/data/files/store.py +463 -0
- testerkit/data/files/streaming.py +783 -0
- testerkit/data/models.py +604 -0
- testerkit/data/ref.py +152 -0
- testerkit/data/retention.py +297 -0
- testerkit/data/run_store.py +367 -0
- testerkit/data/runs_duckdb_manager.py +103 -0
- testerkit/data/schema_dispatch.py +191 -0
- testerkit/data/schema_migrate.py +76 -0
- testerkit/data/schema_versions.py +85 -0
- testerkit/data/schemas.py +288 -0
- testerkit/data/subscribers/__init__.py +48 -0
- testerkit/data/subscribers/_base.py +20 -0
- testerkit/data/subscribers/_output_file.py +25 -0
- testerkit/data/subscribers/replay.py +55 -0
- testerkit/environment.py +84 -0
- testerkit/execution/__init__.py +11 -0
- testerkit/execution/_git.py +190 -0
- testerkit/execution/_state.py +740 -0
- testerkit/execution/accessors.py +54 -0
- testerkit/execution/audit.py +51 -0
- testerkit/execution/cascade.py +85 -0
- testerkit/execution/connections.py +491 -0
- testerkit/execution/harness.py +1931 -0
- testerkit/execution/instrument_events.py +42 -0
- testerkit/execution/limits.py +154 -0
- testerkit/execution/metadata.py +102 -0
- testerkit/execution/mocks.py +63 -0
- testerkit/execution/profiles.py +616 -0
- testerkit/execution/run_scope.py +1331 -0
- testerkit/execution/session_scope.py +196 -0
- testerkit/execution/sidecar.py +280 -0
- testerkit/execution/site_runner.py +705 -0
- testerkit/execution/sites.py +183 -0
- testerkit/execution/sync.py +294 -0
- testerkit/execution/uut_provider.py +231 -0
- testerkit/execution/vectors.py +205 -0
- testerkit/execution/verify.py +396 -0
- testerkit/expand.py +75 -0
- testerkit/files.py +235 -0
- testerkit/fixtures/__init__.py +6 -0
- testerkit/fixtures/manager.py +357 -0
- testerkit/grafana/__init__.py +1 -0
- testerkit/grafana/bootstrap.py +61 -0
- testerkit/grafana/cli.py +314 -0
- testerkit/grafana/dashboards/asset_utilization.json +92 -0
- testerkit/grafana/dashboards/channel_explorer.json +77 -0
- testerkit/grafana/dashboards/event_log.json +92 -0
- testerkit/grafana/dashboards/failure_pareto.json +104 -0
- testerkit/grafana/dashboards/measurement_distribution.json +97 -0
- testerkit/grafana/dashboards/measurement_trend.json +161 -0
- testerkit/grafana/dashboards/station_comparison.json +95 -0
- testerkit/grafana/dashboards/test_duration.json +104 -0
- testerkit/grafana/dashboards/unit_traceability.json +73 -0
- testerkit/grafana/dashboards/yield_overview.json +189 -0
- testerkit/grafana/provisioning/dashboards.yaml.j2 +12 -0
- testerkit/grafana/provisioning/datasources.yaml.j2 +17 -0
- testerkit/grafana/server.py +282 -0
- testerkit/init.py +724 -0
- testerkit/instruments/__init__.py +10 -0
- testerkit/instruments/base.py +82 -0
- testerkit/instruments/discovery/__init__.py +62 -0
- testerkit/instruments/discovery/_base.py +174 -0
- testerkit/instruments/discovery/lxi.py +152 -0
- testerkit/instruments/discovery/ni.py +95 -0
- testerkit/instruments/discovery/serial.py +73 -0
- testerkit/instruments/discovery/visa.py +98 -0
- testerkit/instruments/lifecycle.py +213 -0
- testerkit/instruments/loader.py +79 -0
- testerkit/instruments/locks.py +178 -0
- testerkit/instruments/mocks.py +230 -0
- testerkit/instruments/observer.py +270 -0
- testerkit/instruments/observers/__init__.py +36 -0
- testerkit/instruments/observers/_base.py +68 -0
- testerkit/instruments/observers/daqmx.py +70 -0
- testerkit/instruments/observers/descriptor.py +69 -0
- testerkit/instruments/observers/generic.py +92 -0
- testerkit/instruments/observers/lantz.py +31 -0
- testerkit/instruments/observers/modbus.py +96 -0
- testerkit/instruments/observers/motion.py +68 -0
- testerkit/instruments/observers/ni_modular.py +73 -0
- testerkit/instruments/observers/ophyd.py +80 -0
- testerkit/instruments/observers/pymeasure.py +120 -0
- testerkit/instruments/observers/qcodes.py +66 -0
- testerkit/instruments/observers/scpi.py +88 -0
- testerkit/instruments/observers/tektronix.py +57 -0
- testerkit/instruments/observers/visa.py +103 -0
- testerkit/instruments/pool.py +459 -0
- testerkit/instruments/proxy.py +59 -0
- testerkit/instruments/route_manager.py +329 -0
- testerkit/instruments/routed_proxy.py +88 -0
- testerkit/instruments/server.py +508 -0
- testerkit/instruments/switch.py +43 -0
- testerkit/instruments/visa.py +294 -0
- testerkit/matching/__init__.py +6 -0
- testerkit/matching/service.py +969 -0
- testerkit/mcp/__init__.py +6 -0
- testerkit/mcp/server.py +681 -0
- testerkit/mcp/tools.py +1868 -0
- testerkit/models/__init__.py +18 -0
- testerkit/models/capability.py +598 -0
- testerkit/models/catalog.py +76 -0
- testerkit/models/data_options.py +153 -0
- testerkit/models/enums.py +334 -0
- testerkit/models/instrument.py +152 -0
- testerkit/models/instrument_asset.py +25 -0
- testerkit/models/part.py +271 -0
- testerkit/models/part_manifest.py +96 -0
- testerkit/models/project.py +114 -0
- testerkit/models/station.py +140 -0
- testerkit/models/test_config.py +748 -0
- testerkit/ontology/__init__.py +39 -0
- testerkit/ontology/schema.py +188 -0
- testerkit/ontology/testerkit.yaml +1420 -0
- testerkit/parts/__init__.py +8 -0
- testerkit/parts/context.py +181 -0
- testerkit/parts/folder.py +251 -0
- testerkit/parts/loader.py +27 -0
- testerkit/prompts/__init__.py +17 -0
- testerkit/prompts/core.py +133 -0
- testerkit/pytest_plugin/__init__.py +1287 -0
- testerkit/pytest_plugin/autouse.py +369 -0
- testerkit/pytest_plugin/helpers.py +354 -0
- testerkit/pytest_plugin/hooks.py +1982 -0
- testerkit/pytest_plugin/markers.py +203 -0
- testerkit/pytest_plugin/retry.py +37 -0
- testerkit/pytest_plugin/sweeps.py +117 -0
- testerkit/queries.py +78 -0
- testerkit/reports/__init__.py +6 -0
- testerkit/reports/core.py +492 -0
- testerkit/reports/datasheet.py +831 -0
- testerkit/reports/templates/datasheet.html +383 -0
- testerkit/reports/templates/default.html +153 -0
- testerkit/sbom.py +142 -0
- testerkit/schema_export.py +101 -0
- testerkit/signals.py +63 -0
- testerkit/skills/README.md +58 -0
- testerkit/skills/templates/project-instructions.md +125 -0
- testerkit/skills/testerkit-analysis/SKILL.md +87 -0
- testerkit/skills/testerkit-capture/SKILL.md +114 -0
- testerkit/skills/testerkit-data/SKILL.md +138 -0
- testerkit/skills/testerkit-datasheets/SKILL.md +100 -0
- testerkit/skills/testerkit-datasheets/agents/scaffold-writer.md +115 -0
- testerkit/skills/testerkit-datasheets/agents/section-extractor.md +98 -0
- testerkit/skills/testerkit-datasheets/agents/section-reviewer.md +153 -0
- testerkit/skills/testerkit-datasheets/agents/section-splitter.md +89 -0
- testerkit/skills/testerkit-datasheets/agents/section-writer.md +226 -0
- testerkit/skills/testerkit-datasheets/references/catalog-pipeline.md +271 -0
- testerkit/skills/testerkit-datasheets/references/process-queue.md +42 -0
- testerkit/skills/testerkit-datasheets/references/scaffold.md +156 -0
- testerkit/skills/testerkit-datasheets/references/test-pipeline.md +312 -0
- testerkit/skills/testerkit-debug/SKILL.md +127 -0
- testerkit/skills/testerkit-interactive/SKILL.md +188 -0
- testerkit/skills/testerkit-interactive/references/live-ui-patterns.md +102 -0
- testerkit/skills/testerkit-mocks/SKILL.md +128 -0
- testerkit/skills/testerkit-parts/SKILL.md +126 -0
- testerkit/skills/testerkit-profiles/SKILL.md +139 -0
- testerkit/skills/testerkit-sites/SKILL.md +140 -0
- testerkit/skills/testerkit-stations/SKILL.md +146 -0
- testerkit/skills/testerkit-tests/SKILL.md +136 -0
- testerkit/store.py +1639 -0
- testerkit/ui/__init__.py +71 -0
- testerkit/ui/_asgi.py +162 -0
- testerkit/ui/app.py +32 -0
- testerkit/ui/components/__init__.py +13 -0
- testerkit/ui/components/artifact_viewer.py +250 -0
- testerkit/ui/components/channel_values.py +159 -0
- testerkit/ui/components/event_timeline.py +222 -0
- testerkit/ui/components/execution_gantt.py +210 -0
- testerkit/ui/components/file_streams.py +147 -0
- testerkit/ui/components/instrument_activity.py +146 -0
- testerkit/ui/components/session_table.py +118 -0
- testerkit/ui/pages/__init__.py +25 -0
- testerkit/ui/pages/channels/__init__.py +4 -0
- testerkit/ui/pages/channels/detail.py +932 -0
- testerkit/ui/pages/channels/list.py +515 -0
- testerkit/ui/pages/dashboard.py +236 -0
- testerkit/ui/pages/designer/__init__.py +5 -0
- testerkit/ui/pages/designer/graph.py +635 -0
- testerkit/ui/pages/designer/matching.py +663 -0
- testerkit/ui/pages/designer/page.py +593 -0
- testerkit/ui/pages/designer/properties.py +591 -0
- testerkit/ui/pages/designer/state.py +468 -0
- testerkit/ui/pages/docs/__init__.py +6 -0
- testerkit/ui/pages/docs/index.py +95 -0
- testerkit/ui/pages/docs/page.py +698 -0
- testerkit/ui/pages/events/__init__.py +3 -0
- testerkit/ui/pages/events/list.py +263 -0
- testerkit/ui/pages/explore.py +1108 -0
- testerkit/ui/pages/files/__init__.py +4 -0
- testerkit/ui/pages/files/detail.py +426 -0
- testerkit/ui/pages/files/list.py +447 -0
- testerkit/ui/pages/fixtures/__init__.py +8 -0
- testerkit/ui/pages/fixtures/detail.py +280 -0
- testerkit/ui/pages/fixtures/edit.py +370 -0
- testerkit/ui/pages/fixtures/list.py +180 -0
- testerkit/ui/pages/fixtures/new.py +195 -0
- testerkit/ui/pages/instruments/__init__.py +4 -0
- testerkit/ui/pages/instruments/detail.py +320 -0
- testerkit/ui/pages/instruments/edit.py +482 -0
- testerkit/ui/pages/instruments/list.py +249 -0
- testerkit/ui/pages/instruments/new.py +200 -0
- testerkit/ui/pages/launch.py +261 -0
- testerkit/ui/pages/live.py +124 -0
- testerkit/ui/pages/metrics_page.py +1229 -0
- testerkit/ui/pages/parts/__init__.py +11 -0
- testerkit/ui/pages/parts/detail.py +202 -0
- testerkit/ui/pages/parts/edit.py +310 -0
- testerkit/ui/pages/parts/list.py +160 -0
- testerkit/ui/pages/parts/new.py +170 -0
- testerkit/ui/pages/parts/requirements.py +153 -0
- testerkit/ui/pages/parts/stations.py +199 -0
- testerkit/ui/pages/profiles/__init__.py +4 -0
- testerkit/ui/pages/profiles/detail.py +113 -0
- testerkit/ui/pages/profiles/list.py +86 -0
- testerkit/ui/pages/results/__init__.py +4 -0
- testerkit/ui/pages/results/detail.py +631 -0
- testerkit/ui/pages/results/list.py +230 -0
- testerkit/ui/pages/stations/__init__.py +4 -0
- testerkit/ui/pages/stations/detail.py +256 -0
- testerkit/ui/pages/stations/edit.py +273 -0
- testerkit/ui/pages/stations/list.py +180 -0
- testerkit/ui/pages/stations/new.py +311 -0
- testerkit/ui/pages/tests/__init__.py +4 -0
- testerkit/ui/pages/tests/detail.py +152 -0
- testerkit/ui/pages/tests/list.py +194 -0
- testerkit/ui/pages/uuts/__init__.py +4 -0
- testerkit/ui/pages/uuts/list.py +97 -0
- testerkit/ui/shared/__init__.py +8 -0
- testerkit/ui/shared/components.py +1826 -0
- testerkit/ui/shared/dialogs.py +105 -0
- testerkit/ui/shared/event_binding.py +194 -0
- testerkit/ui/shared/layout.py +314 -0
- testerkit/ui/shared/services.py +1719 -0
- testerkit/ui/shared/timestamps.py +21 -0
- testerkit/ui/static/branding/favicon-16.png +0 -0
- testerkit/ui/static/branding/favicon-32.png +0 -0
- testerkit/ui/static/branding/favicon-48.png +0 -0
- testerkit/ui/static/branding/testerkit-icon-256.png +0 -0
- testerkit/ui/static/branding/testerkit-mark.svg +1 -0
- testerkit/ui/static/branding/testerkit-wordmark.svg +1 -0
- testerkit/ui/static/global.css +465 -0
- testerkit/utils/__init__.py +17 -0
- testerkit/utils/enum_meta.py +800 -0
- testerkit/utils/paths.py +75 -0
- testerkit/utils/ranges.py +257 -0
- testerkit/validation.py +82 -0
- testerkit/verbs.py +142 -0
- testerkit-0.4.0.dist-info/METADATA +250 -0
- testerkit-0.4.0.dist-info/RECORD +521 -0
- testerkit-0.4.0.dist-info/WHEEL +4 -0
- testerkit-0.4.0.dist-info/entry_points.txt +5 -0
- testerkit-0.4.0.dist-info/licenses/LICENSE +191 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Step 13: Parallel Testing
|
|
2
|
+
|
|
3
|
+
**Goal:** Scale one bench from one UUT to two, tested at the same time, with zero changes to test code.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- [Step 7: Real Instruments](07-real-instruments.md) — station YAML, instrument channels
|
|
8
|
+
- [Step 9: Production Ready](09-production.md) — fixture YAML, pin-to-instrument mapping, sidecar limits
|
|
9
|
+
|
|
10
|
+
## The scenario
|
|
11
|
+
|
|
12
|
+
One bench, two UUT positions, tested in parallel. Each position is a **site** — the same parallel-position concept STDF calls `SITE_NUM` and NI TestStand calls a "test socket." A fixture that declares two or more sites turns a bare `pytest` invocation into a parallel run: no `--fixture` flag, no test rewrite, no per-UUT script.
|
|
13
|
+
|
|
14
|
+
A site's identity is its **0-based position in the fixture's `sites:` list** — `site_index`. It's never a field you set; it's just where the site sits in the list. `site_index 0` gets an optional `name:` label — a human-readable tag like `left` that has no bearing on execution order.
|
|
15
|
+
|
|
16
|
+
## Growing a fixture to two sites
|
|
17
|
+
|
|
18
|
+
A single-UUT fixture wires `connections:` directly. A multi-UUT fixture replaces that with `sites:` — an ordered list, one entry per UUT position, each with its own `connections:`:
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
# fixtures/dual_site_bench.yaml
|
|
22
|
+
id: dual_site_bench
|
|
23
|
+
name: Dual-Site Bench Fixture
|
|
24
|
+
description: >-
|
|
25
|
+
Two-UUT parallel fixture — left/right sites share the bench's psu
|
|
26
|
+
and dmm but land on different instrument channels.
|
|
27
|
+
part_id: dual_rail_uut
|
|
28
|
+
station_types: [bench]
|
|
29
|
+
sites:
|
|
30
|
+
- name: left
|
|
31
|
+
connections:
|
|
32
|
+
vout:
|
|
33
|
+
name: vout
|
|
34
|
+
instrument: dmm
|
|
35
|
+
instrument_channel: "1"
|
|
36
|
+
instrument_terminal: hi
|
|
37
|
+
uut_pin: TP_VOUT
|
|
38
|
+
net: VOUT_3V3
|
|
39
|
+
vin:
|
|
40
|
+
name: vin
|
|
41
|
+
instrument: psu
|
|
42
|
+
instrument_channel: "1"
|
|
43
|
+
instrument_terminal: hi
|
|
44
|
+
uut_pin: TP_VIN
|
|
45
|
+
net: VIN_5V
|
|
46
|
+
- name: right
|
|
47
|
+
connections:
|
|
48
|
+
vout:
|
|
49
|
+
name: vout
|
|
50
|
+
instrument: dmm
|
|
51
|
+
instrument_channel: "2"
|
|
52
|
+
instrument_terminal: hi
|
|
53
|
+
uut_pin: TP_VOUT
|
|
54
|
+
net: VOUT_3V3
|
|
55
|
+
vin:
|
|
56
|
+
name: vin
|
|
57
|
+
instrument: psu
|
|
58
|
+
instrument_channel: "2"
|
|
59
|
+
instrument_terminal: hi
|
|
60
|
+
uut_pin: TP_VIN
|
|
61
|
+
net: VIN_5V
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`left` is `site_index 0`, `right` is `site_index 1` — list position, not an authored field. Both sites route to the same instrument roles (`dmm`, `psu`) on the station, but each lands on its own `instrument_channel` — `left` on channel `"1"`, `right` on channel `"2"`. That's the whole trick: one physical DMM, one physical PSU, two UUT positions, no collision.
|
|
65
|
+
|
|
66
|
+
Two or more sites makes this fixture **multi-site**, which is what fans a bare `pytest` invocation into parallel execution. One site is just the single-UUT case you already know — `connections:` directly on the fixture is shorthand for one unnamed site. For the full field-by-field schema, see [Fixture YAML reference](../reference/configuration.md#fixture-yaml); for the design rationale behind sites and shared instruments, see [Fixtures → Multi-UUT scaling](../concepts/configuration/fixtures.md#multi-uut-scaling-sites-shared-instruments-switching).
|
|
67
|
+
|
|
68
|
+
## The station and the test don't change
|
|
69
|
+
|
|
70
|
+
The station YAML declares `psu` and `dmm` exactly as a single-UUT bench would — it has no idea there are two sites:
|
|
71
|
+
|
|
72
|
+
```yaml
|
|
73
|
+
# stations/bench_dual.yaml
|
|
74
|
+
id: bench_dual
|
|
75
|
+
name: Dual-Site Bench
|
|
76
|
+
station_type: bench
|
|
77
|
+
instruments:
|
|
78
|
+
psu:
|
|
79
|
+
type: psu
|
|
80
|
+
driver: drivers.PSU
|
|
81
|
+
resource: TCPIP::192.168.1.101::INSTR
|
|
82
|
+
mock_config:
|
|
83
|
+
set_voltage: 5.0
|
|
84
|
+
set_current: 0.5
|
|
85
|
+
measure_voltage: 5.0
|
|
86
|
+
measure_current: 0.042
|
|
87
|
+
dmm:
|
|
88
|
+
type: dmm
|
|
89
|
+
driver: drivers.DMM
|
|
90
|
+
resource: TCPIP::192.168.1.102::INSTR
|
|
91
|
+
mock_config:
|
|
92
|
+
measure_dc_voltage: 3.3
|
|
93
|
+
measure_dc_current: 0.042
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Neither does the test:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
# tests/test_dual_rail.py
|
|
100
|
+
|
|
101
|
+
def test_vout_within_spec(verify, psu, dmm, context) -> None:
|
|
102
|
+
"""5 V PSU input -> ~3.3 V DMM readback, on this site's connections."""
|
|
103
|
+
psu.set_voltage(5.0)
|
|
104
|
+
psu.set_current(0.5)
|
|
105
|
+
for _ in context.connections:
|
|
106
|
+
verify("vout", dmm.measure_dc_voltage())
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
# tests/test_dual_rail.yaml — sidecar
|
|
111
|
+
tests:
|
|
112
|
+
test_vout_within_spec:
|
|
113
|
+
connections: [vout]
|
|
114
|
+
limits:
|
|
115
|
+
vout: {low: 3.2, high: 3.4, unit: V}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`context.connections` iterates the connections the sidecar's `connections: [vout]` list resolved for *this site* — see the [`connections` fixture reference](../reference/pytest/fixtures.md#connections-function). The test never names `left`, `right`, a channel, or an index; it just reads `dmm` and `psu` through its own site's wiring. The site-specific channel routing lives entirely in the fixture YAML from the previous section — that's what let the station and test files stay unmodified while the fixture grew from one site to two.
|
|
119
|
+
|
|
120
|
+
## Launching a parallel run
|
|
121
|
+
|
|
122
|
+
`pytest.ini` assigns a serial to each named site by default, so a bare `pytest` is already a complete parallel run:
|
|
123
|
+
|
|
124
|
+
```ini
|
|
125
|
+
# pytest.ini
|
|
126
|
+
[pytest]
|
|
127
|
+
addopts = --mock-instruments --uut-serials left=SN-A,right=SN-B
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```cli
|
|
131
|
+
cd examples/12-parallel-sites
|
|
132
|
+
uv run pytest -q
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
[site:0] . [100%]
|
|
137
|
+
[site:0] 1 passed in 0.43s
|
|
138
|
+
[site:1] . [100%]
|
|
139
|
+
[site:1] 1 passed in 0.43s
|
|
140
|
+
|
|
141
|
+
============================================================
|
|
142
|
+
Multi-UUT Results
|
|
143
|
+
============================================================
|
|
144
|
+
site[0]: PASS 1 passed in 0.43s
|
|
145
|
+
site[1]: PASS 1 passed in 0.43s
|
|
146
|
+
============================================================
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
A fixture with 2+ sites is what triggers this: the parent process (the **orchestrator**) spawns one **worker** subprocess per site, each running the full test session against its own site's connections. `[site:N]` prefixes each worker's stdout as it streams back — the two lines interleave as the workers run concurrently, so `[site:1]` sometimes reports before `[site:0]`; the order isn't guaranteed. Below the per-site output, the `Multi-UUT Results` block is the orchestrator's own summary once both workers exit.
|
|
150
|
+
|
|
151
|
+
You'll also see pytest print `no tests ran` from the orchestrator process itself — it collected zero tests directly (both workers did the real collecting), so this line is harmless.
|
|
152
|
+
|
|
153
|
+
## Reading the result
|
|
154
|
+
|
|
155
|
+
Two workers means two independent runs, each with its own serial:
|
|
156
|
+
|
|
157
|
+
```cli
|
|
158
|
+
uv run testerkit runs --limit 5
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
Run ID Started UUT Serial Project Station Outcome
|
|
163
|
+
----------------------------------------------------------------------------------------------------------
|
|
164
|
+
437bdc0e 2026-07-01T18:00:00-0600 SN-B parallel-sites-example bench_dual passed
|
|
165
|
+
7fa161df 2026-07-01T18:00:00-0600 SN-A parallel-sites-example bench_dual passed
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Site 0 (`left`) ran as `SN-A`, site 1 (`right`) ran as `SN-B`. Each run's parquet carries the `site_index` / `site_name` it was frozen with at start — `SN-A` recorded `site_index=0, site_name="left"`, `SN-B` recorded `site_index=1, site_name="right"`. That freeze point matters: rename a site in the fixture YAML next month and this run's rows still read the name that was active when it ran. See [Multi-UUT testing → Parquet Data](../how-to/execution/multi-uut-testing.md#parquet-data) for the DuckDB query shape over those columns.
|
|
169
|
+
|
|
170
|
+
Give it a few seconds before querying — ingest lands a beat after the summary prints. If `testerkit runs` comes back empty right after the run, re-run the command.
|
|
171
|
+
|
|
172
|
+
## Experimenting with serial assignment
|
|
173
|
+
|
|
174
|
+
`pytest.ini`'s `addopts` is a default, not a lock — any `--uut-serials` / `--site` / `--uut-serial` on the command line overrides it for that invocation.
|
|
175
|
+
|
|
176
|
+
**Positional** — one serial per site, in fixture list order (`left` gets the first):
|
|
177
|
+
|
|
178
|
+
```cli
|
|
179
|
+
uv run pytest -q --uut-serials SN1,SN2
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
[site:0] . [100%]
|
|
184
|
+
[site:0] 1 passed in 0.42s
|
|
185
|
+
[site:1] . [100%]
|
|
186
|
+
[site:1] 1 passed in 0.42s
|
|
187
|
+
|
|
188
|
+
============================================================
|
|
189
|
+
Multi-UUT Results
|
|
190
|
+
============================================================
|
|
191
|
+
site[0]: PASS 1 passed in 0.42s
|
|
192
|
+
site[1]: PASS 1 passed in 0.42s
|
|
193
|
+
============================================================
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Indexed** (`site_index=serial`, any order) and **named** (`site_name=serial`) produce the same 2-worker run — only how the serial maps to a site differs:
|
|
197
|
+
|
|
198
|
+
```cli
|
|
199
|
+
uv run pytest -q --uut-serials 0=SN1,1=SN2
|
|
200
|
+
uv run pytest -q --uut-serials left=SN1,right=SN2
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Single-lane** — target one site, single-process, no orchestrator:
|
|
204
|
+
|
|
205
|
+
```cli
|
|
206
|
+
uv run pytest -q --site left --uut-serial SN1
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
. [100%]
|
|
211
|
+
1 passed in 0.43s
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
No `[site:N]` prefix, no `Multi-UUT Results` banner — this ran in the current process. It still records exactly as `left` would inside a parallel run (`site_index=0, site_name="left"`), which makes it the right tool for debugging one UUT position without waiting on its neighbor. `--site` always runs single-process, even against a multi-site fixture; pair it with `--uut-serial` (singular), not `--uut-serials`.
|
|
215
|
+
|
|
216
|
+
## Why the shared instrument doesn't collide
|
|
217
|
+
|
|
218
|
+
`psu` and `dmm` are one physical box each in this example, shared by both sites. TesterKit serializes calls to a shared instrument so `left` and `right` never talk to it at the same instant — you didn't write a lock anywhere in `test_dual_rail.py` for that to be true. Mock instruments are the one exception: under `--mock-instruments` each site gets its own mock state, so a fault injected on one site's `dmm` never leaks into the other's.
|
|
219
|
+
|
|
220
|
+
For sync points, per-site environment variables, and debugging a hung or failing site, see [Multi-UUT testing](../how-to/execution/multi-uut-testing.md) — the full reference this chapter walked through.
|
|
221
|
+
|
|
222
|
+
## You've completed the tutorial
|
|
223
|
+
|
|
224
|
+
You've taken a suite from a bare `conftest.py` with one mock fixture through production traceability, live monitoring, and now parallel execution across multiple UUT positions on one bench. The full worked example for this step lives in [`examples/12-parallel-sites`](https://github.com/pragmatest-dev/testerkit/tree/main/examples/12-parallel-sites) — clone it, wipe its `data/` directory, and re-run any of the commands above against real output of your own.
|
|
225
|
+
|
|
226
|
+
## Next Steps
|
|
227
|
+
|
|
228
|
+
- [Multi-UUT testing](../how-to/execution/multi-uut-testing.md) — the full recipe: sync points, environment variables, debugging failures
|
|
229
|
+
- [Fixtures → Multi-UUT scaling](../concepts/configuration/fixtures.md#multi-uut-scaling-sites-shared-instruments-switching) — the design behind sites, shared instruments, and switched routing
|
|
230
|
+
- [Configuration reference](../reference/configuration.md#fixture-yaml) — fixture YAML field-by-field
|
|
231
|
+
- [CLI reference](../reference/cli.md) — every `testerkit` command
|
|
232
|
+
|
|
233
|
+
← [Step 12: Continuous Monitoring](12-continuous-monitoring.md) | [Tutorial index](index.md)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Tutorials
|
|
2
|
+
|
|
3
|
+
Two independent ways to learn TesterKit — pick the one that fits how you want to start. They are not a sequence: the Quick Start is **not** "step 0" of the step-by-step path.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
`testerkit init quick_start --starter` scaffolds a complete project — part spec, station, fixture, a test, and config — that passes `pytest` immediately with mock instruments. Best when you want to see the whole thing working first, then read the walkthrough to understand each piece.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install testerkit
|
|
11
|
+
testerkit init quick_start --starter && cd quick_start
|
|
12
|
+
pytest
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
[Quick Start walkthrough →](quickstart.md)
|
|
16
|
+
|
|
17
|
+
## Step-by-step tutorial
|
|
18
|
+
|
|
19
|
+
Start from nothing — a bare `conftest.py` with one mock fixture — and add a single concept per step, up to a production-ready, fully traceable suite with live monitoring. Best when you want to learn each piece as you introduce it; each step builds on the previous one.
|
|
20
|
+
|
|
21
|
+
| Step | Goal | What You'll Learn |
|
|
22
|
+
|------|------|-------------------|
|
|
23
|
+
| [1. First Test](01-first-test.md) | Run something | pytest, project structure |
|
|
24
|
+
| [2. Running Without Hardware](02-mock-instruments.md) | Use mock mode | `--mock-instruments`, `mock_config` (station-config block of canned return values used when `--mock-instruments` is on) |
|
|
25
|
+
| [3. pytest-native tests](03-fixtures.md) | Log measurements | the core per-test [TesterKit fixtures](../reference/overview/pytest-native.md) — `context` is the ambient run/UUT/station/vector state (always available; carries sweep params when present, observations always), `verify` records the measurement row AND raises on FAIL, `measure` records the row without raising (plus ~17 other public fixtures the plugin exposes — see [testerkit-fixtures](../reference/pytest/fixtures.md)) |
|
|
26
|
+
| [4. Add Limits](04-limits.md) | Pass/fail criteria | [Limit](../reference/data/models.md), Measurement, [Outcome](../reference/data/models.md#enum-outcome) |
|
|
27
|
+
| [5. Test Configuration](05-configuration.md) | Configure in YAML | [sidecar](05-configuration.md) (YAML next to a test file carrying vectors and limits) |
|
|
28
|
+
| [6. Part Specifications](06-specifications.md) | Define [parts](../concepts/configuration/parts.md) | parts/*.yaml, [characteristics](../concepts/configuration/capabilities.md) |
|
|
29
|
+
| [7. Real Instruments](07-real-instruments.md) | Talk to hardware | [stations/*.yaml](../concepts/configuration/stations.md), VISA, simulation |
|
|
30
|
+
| [8. Capability Matching](08-capabilities.md) | Find compatible [stations](../concepts/configuration/stations.md) | [direction flip, matching API](../concepts/configuration/capabilities.md) |
|
|
31
|
+
| [9. Production Ready](09-production.md) | Full [traceability](../how-to/execution/traceability.md) | [fixtures](../concepts/configuration/fixtures.md), test classes, sidecar |
|
|
32
|
+
| [10. Live Monitoring](10-live-monitoring.md) | Monitor in real time | [sessions](../concepts/data/sessions.md), [events](../concepts/data/event-log.md), [channels](../concepts/data/data-stores.md) |
|
|
33
|
+
| [11. Waveforms and Evidence](11-waveforms-and-evidence.md) | Capture a scope waveform and judge derived scalars | [three verbs](../concepts/data/three-verbs.md), `observe`, ChannelStore |
|
|
34
|
+
| [12. Continuous Monitoring](12-continuous-monitoring.md) | Stream live DMM data from an interactive session into the operator UI | [three verbs](../concepts/data/three-verbs.md), `channels.stream`, interactive `connect` |
|
|
35
|
+
| [13. Parallel Testing](13-parallel-testing.md) | Run one bench against two UUTs at once | multi-UUT [sites](../concepts/configuration/fixtures.md#multi-uut-scaling-sites-shared-instruments-switching), `site_index` / `site_name`, `--uut-serials`, the orchestrator/worker split |
|
|
36
|
+
|
|
37
|
+
## Prerequisites
|
|
38
|
+
|
|
39
|
+
- Python 3.11 or later
|
|
40
|
+
- Basic familiarity with pytest
|
|
41
|
+
- TesterKit installed (`pip install testerkit`)
|
|
42
|
+
|
|
43
|
+
## Time Commitment
|
|
44
|
+
|
|
45
|
+
Each step takes 10-15 minutes. You can complete the entire tutorial in about 2 hours, or work through it over several sessions.
|
|
46
|
+
|
|
47
|
+
## What You'll Build
|
|
48
|
+
|
|
49
|
+
By the end of this tutorial, you'll have:
|
|
50
|
+
|
|
51
|
+
1. A working test suite for a voltage converter
|
|
52
|
+
2. YAML-based configuration for limits and test parameters
|
|
53
|
+
3. Tests that run with real instruments OR in simulation mode
|
|
54
|
+
4. Automatic station matching based on required capabilities
|
|
55
|
+
5. Full traceability from requirements to results
|
|
56
|
+
6. Live monitoring of test sessions with event queries
|
|
57
|
+
|
|
58
|
+
## Ready?
|
|
59
|
+
|
|
60
|
+
- See it working now → [Quick Start](quickstart.md)
|
|
61
|
+
- Learn it from the ground up → [Step 1: First Test](01-first-test.md)
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# Quick Start
|
|
2
|
+
|
|
3
|
+
Install TesterKit, scaffold a starter project, and run a passing test. Requires Python 3.11+.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# 1. Install TesterKit
|
|
7
|
+
pip install testerkit
|
|
8
|
+
|
|
9
|
+
# 2. Create a starter project
|
|
10
|
+
testerkit init quick_start --starter
|
|
11
|
+
cd quick_start
|
|
12
|
+
|
|
13
|
+
# 3. Run the test
|
|
14
|
+
pytest
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The starter ships a single test (`test_output_voltage`). Run it and pytest reports `1 passed` — the measurement was taken against a mock instrument, checked against its limit, and recorded.
|
|
18
|
+
|
|
19
|
+
> **Explore without hardware.** [](https://codespaces.new/pragmatest-dev/testerkit-starter) opens a browser sandbox to try TesterKit with no install — mock-instrument tests, the operator UI, analytics, and AI integration. Real instrument control (PyVISA/serial to a bench) needs a local install, so the sandbox stops there.
|
|
20
|
+
|
|
21
|
+
> **Concepts cheat-sheet.** This quick start runs a complete TesterKit project, so it touches nearly every TesterKit concept at once — most for the first time. Each term below links forward to the tutorial step that introduces it properly:
|
|
22
|
+
>
|
|
23
|
+
> - **Part spec** — `parts/*.yaml`. Describes the device under test. → [Step 6](06-specifications.md), [concepts/parts](../concepts/configuration/parts.md)
|
|
24
|
+
> - **Station YAML** — `stations/*.yaml`. Declares the bench's instruments. → [Step 7](07-real-instruments.md), [concepts/stations](../concepts/configuration/stations.md)
|
|
25
|
+
> - **Sidecar YAML** — `tests/test_<module>.yaml`. Carries limits, sweeps, mocks for tests in that module. → [Step 5](05-configuration.md)
|
|
26
|
+
> - **`verify` / `measure` / `context` fixtures** — three of the fixtures TesterKit contributes. → [Step 3](03-fixtures.md), [reference/testerkit-fixtures](../reference/pytest/fixtures.md)
|
|
27
|
+
> - **`@pytest.mark.testerkit_limits`** — one of the seven TesterKit markers; pins a limit at the top of a test. → [Step 4](04-limits.md), [reference/testerkit-markers](../reference/pytest/markers.md)
|
|
28
|
+
> - **`mock_config`** — Per-instrument return values for mock mode. → [Step 2](02-mock-instruments.md), [how-to/mock-mode](../how-to/configuration/mock-mode.md)
|
|
29
|
+
> - **Characteristics, bands, accuracy, `when:`** — Part-spec vocabulary. → [Step 6](06-specifications.md), [reference/catalog-schema](../reference/catalog/schema.md)
|
|
30
|
+
> - **Capability matching** — How TesterKit pairs a part to a station. → [Step 8](08-capabilities.md), [concepts/capabilities](../concepts/configuration/capabilities.md)
|
|
31
|
+
> - **MCP** — Model Context Protocol; how AI agents drive TesterKit. → [how-to/mcp-integration](../how-to/overview/mcp-integration.md)
|
|
32
|
+
|
|
33
|
+
## Project Structure
|
|
34
|
+
|
|
35
|
+
TesterKit projects follow a standard folder structure. The UI is driven by these folders.
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
quick_start/
|
|
39
|
+
├── testerkit.yaml # Project config (data_dir, default station, mock mode)
|
|
40
|
+
├── parts/ # WHAT you're testing
|
|
41
|
+
│ └── example_part.yaml # Part specification
|
|
42
|
+
├── stations/ # WHERE you test
|
|
43
|
+
│ └── starter_station.yaml # Instruments + addresses
|
|
44
|
+
├── fixtures/ # HOW parts are connected to instruments
|
|
45
|
+
│ └── example_fixture.yaml # Pin-to-channel mappings
|
|
46
|
+
├── instruments/ # Instrument asset records (identity, calibration)
|
|
47
|
+
│ ├── psu.yaml
|
|
48
|
+
│ └── dmm.yaml
|
|
49
|
+
├── tests/ # Test code + sidecar config
|
|
50
|
+
│ ├── conftest.py # Custom fixtures (optional — roles auto-register)
|
|
51
|
+
│ ├── test_example.py # Test functions
|
|
52
|
+
│ └── test_example.yaml # Sidecar (limits, sweeps, mocks)
|
|
53
|
+
├── data/ # Output (gitignored)
|
|
54
|
+
└── pyproject.toml
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Understanding the Starter Project
|
|
58
|
+
|
|
59
|
+
When you run `testerkit init quick_start --starter`, it generates all of these files. Here's what each one does:
|
|
60
|
+
|
|
61
|
+
### Part Spec (`parts/example_part.yaml`)
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
# parts/example_part.yaml
|
|
65
|
+
id: "example_part"
|
|
66
|
+
name: "Example Part"
|
|
67
|
+
description: "Auto-generated example part specification"
|
|
68
|
+
|
|
69
|
+
pins:
|
|
70
|
+
TP_VOUT: {name: "TP1", net: "VOUT_3V3", description: "Output voltage test point"}
|
|
71
|
+
|
|
72
|
+
characteristics:
|
|
73
|
+
output_voltage:
|
|
74
|
+
function: "dc_voltage"
|
|
75
|
+
direction: "output"
|
|
76
|
+
unit: "V"
|
|
77
|
+
pin: "TP_VOUT"
|
|
78
|
+
bands:
|
|
79
|
+
- value: 3.3
|
|
80
|
+
accuracy: {pct_reading: 2.0}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Station Config (`stations/starter_station.yaml`)
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
# stations/starter_station.yaml
|
|
87
|
+
id: "starter_station"
|
|
88
|
+
name: "Starter Station"
|
|
89
|
+
description: "Auto-generated starter station with mock instruments"
|
|
90
|
+
|
|
91
|
+
instruments:
|
|
92
|
+
psu:
|
|
93
|
+
type: "psu"
|
|
94
|
+
resource: "TCPIP::192.168.1.100::INSTR"
|
|
95
|
+
mock: true # Start with mocks, switch to real hardware later
|
|
96
|
+
mock_config: {set_voltage: null, enable_output: null, measure_voltage: 5.0, measure_current: 0.25}
|
|
97
|
+
|
|
98
|
+
dmm:
|
|
99
|
+
type: "dmm"
|
|
100
|
+
resource: "TCPIP::192.168.1.101::INSTR"
|
|
101
|
+
mock: true
|
|
102
|
+
mock_config: {measure_dc_voltage: 3.3}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
For real hardware, just remove `mock: true`. TesterKit uses [PyVISA](https://pyvisa.readthedocs.io/) directly:
|
|
106
|
+
|
|
107
|
+
```yaml
|
|
108
|
+
# stations/bench_1.yaml - Real hardware
|
|
109
|
+
id: bench_1
|
|
110
|
+
name: "Test Bench 1"
|
|
111
|
+
|
|
112
|
+
instruments:
|
|
113
|
+
dmm:
|
|
114
|
+
type: dmm
|
|
115
|
+
resource: "TCPIP::192.168.1.100::INSTR"
|
|
116
|
+
# No mock: true → uses PyVISA, fixture has .query()/.write()
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or use [PyMeasure](https://pymeasure.readthedocs.io/) for high-level drivers (100+ instruments):
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
instruments:
|
|
123
|
+
dmm:
|
|
124
|
+
type: dmm
|
|
125
|
+
driver: pymeasure.instruments.keysight.Keysight34461A
|
|
126
|
+
resource: "TCPIP::192.168.1.100::INSTR"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Test Code (`tests/test_example.py`)
|
|
130
|
+
|
|
131
|
+
Tests are **plain pytest** — no decorator, no base class. TesterKit's pytest integration contributes [fixtures](../reference/pytest/fixtures.md) (the per-test `context` / `verify` / `measure`, plus `pins`, `instruments`, per-role auto-fixtures from the station YAML, etc.) and [seven markers](../reference/pytest/markers.md). For how TesterKit tests use pytest's own collection / fixture / marker mechanisms see [pytest-native reference](../reference/overview/pytest-native.md).
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
# tests/test_example.py
|
|
135
|
+
def test_output_voltage(context, psu, dmm, verify) -> None:
|
|
136
|
+
"""Verify output voltage is within spec."""
|
|
137
|
+
vin = context.get_param("vin", 5.0)
|
|
138
|
+
psu.set_voltage(vin)
|
|
139
|
+
psu.enable_output()
|
|
140
|
+
# verify() resolves the limit from the sidecar / part YAML,
|
|
141
|
+
# records the measurement, and raises on fail.
|
|
142
|
+
verify("output_voltage", float(dmm.measure_dc_voltage()))
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
For measurements that don't come from the part spec, use `measure(name, value, limit={"low": ..., "high": ..., "unit": "V"})` with inline limits or a sidecar `test_<module>.yaml`.
|
|
146
|
+
|
|
147
|
+
### Sidecar (`tests/test_example.yaml`)
|
|
148
|
+
|
|
149
|
+
Sidecar YAML carries limits, sweeps, and mocks alongside the test file. A top-level key applies to every test in the module; per-test overrides go under `tests:`. The starter ships a per-test limit:
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
# tests/test_example.yaml
|
|
153
|
+
tests:
|
|
154
|
+
test_output_voltage:
|
|
155
|
+
limits:
|
|
156
|
+
output_voltage:
|
|
157
|
+
low: 3.234
|
|
158
|
+
high: 3.366
|
|
159
|
+
unit: V
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Sweeps and mocks live here too — e.g. a module-level `sweeps: [{vin: [5.0]}]` to parametrize, or `mocks: [{target: dmm.measure_dc_voltage, return_value: 3.31}]` (the starter instead sets mock returns in the station's `mock_config`).
|
|
163
|
+
|
|
164
|
+
### Running Tests
|
|
165
|
+
|
|
166
|
+
The starter's `pyproject.toml` bakes the station, mock mode, and a UUT serial into `addopts` (and `testerkit.yaml` sets the same defaults), so the everyday command is just:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
pytest
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
That expands to the explicit form below — useful when you want to override a default or run from outside the project:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Mock-instrument run (the starter's default)
|
|
176
|
+
pytest tests/ --station=starter_station --mock-instruments --uut-serial=STARTER001 -v
|
|
177
|
+
|
|
178
|
+
# With real hardware (drop --mock-instruments; use a real serial)
|
|
179
|
+
pytest tests/ --station=starter_station --uut-serial=SN001 -v
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
> **On `--uut-serial` for early articles:** if your first UUT doesn't have
|
|
183
|
+
> a real serial yet (engineering build, breadboard, dev unit), call it
|
|
184
|
+
> whatever you like — `bob`, `proto-1`, `dev`. The serial is just the
|
|
185
|
+
> identifier the run record will be filed under. Best practice once you
|
|
186
|
+
> have real units is to use the value that uniquely identifies what is
|
|
187
|
+
> being tested and measured (printed serial, scanned barcode, lot+sequence).
|
|
188
|
+
|
|
189
|
+
## The Pattern
|
|
190
|
+
|
|
191
|
+
Every TesterKit test follows this pattern:
|
|
192
|
+
|
|
193
|
+
1. **GET CONDITIONS** from `context.get_param(...)` (not hardcoded)
|
|
194
|
+
2. **SET UP** stimulus (PSU voltage, load current)
|
|
195
|
+
3. **MEASURE** the result
|
|
196
|
+
4. **CHECK** with `verify(name, value)` or `measure(name, value, ...)` — never `assert 3.0 <= v <= 3.6`
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
def test_something(context, psu, dmm, verify):
|
|
200
|
+
vin = context.get_param("vin", 5.0) # GET from context
|
|
201
|
+
psu.set_voltage(vin) # SET UP
|
|
202
|
+
psu.enable_output()
|
|
203
|
+
verify("output_voltage", # MEASURE + CHECK + RECORD
|
|
204
|
+
dmm.measure_dc_voltage())
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**No hardcoded values in code.** Conditions come from `context` (populated by native `@pytest.mark.parametrize` or sidecar YAML). Limits come from the part spec, an inline `@pytest.mark.testerkit_limits` decorator, or the sidecar's `limits:` field — never inline asserts.
|
|
208
|
+
|
|
209
|
+
For the full reference — markers, sidecar YAML, `context.changed()`, mocks, retries — see the [Writing Tests guide](../how-to/execution/writing-tests.md).
|
|
210
|
+
|
|
211
|
+
## View Results
|
|
212
|
+
|
|
213
|
+
### CLI
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
testerkit runs # List recent runs
|
|
217
|
+
testerkit show <run_id> # Show run details
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Operator UI
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
testerkit serve
|
|
224
|
+
# Open http://localhost:8000
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Programmatic
|
|
228
|
+
|
|
229
|
+
Each run writes one parquet at `data/runs/{date}/*.parquet`. Measurements are
|
|
230
|
+
nested under the vector rows (`record_type = 'vector'`), so read them with a
|
|
231
|
+
DuckDB `UNNEST`:
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
import duckdb
|
|
235
|
+
|
|
236
|
+
rows = duckdb.sql("""
|
|
237
|
+
SELECT run_id, m.name, m.value, m.unit, m.outcome
|
|
238
|
+
FROM read_parquet('data/runs/**/*.parquet', union_by_name=true),
|
|
239
|
+
UNNEST(measurements) AS t(m)
|
|
240
|
+
WHERE record_type = 'vector'
|
|
241
|
+
""").fetchall()
|
|
242
|
+
for row in rows:
|
|
243
|
+
print(row)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
For cross-run analytics (yield, Ppk, Pareto) use the higher-level
|
|
247
|
+
[`MeasurementsQuery`](../reference/data/query-api.md) API instead of reading
|
|
248
|
+
parquet directly.
|
|
249
|
+
|
|
250
|
+
## Key Folders
|
|
251
|
+
|
|
252
|
+
| Folder | Purpose | UI Page |
|
|
253
|
+
|--------|---------|---------|
|
|
254
|
+
| `parts/` | Part specs (what you're testing) | /parts |
|
|
255
|
+
| `stations/` | Station configs (instruments + addresses) | /stations |
|
|
256
|
+
| `fixtures/` | Pin-to-instrument mappings | /fixtures |
|
|
257
|
+
| `instruments/` | Instrument asset records (identity, calibration) | /instruments |
|
|
258
|
+
| `tests/` | Test code + sidecar config | - |
|
|
259
|
+
| `data/` | Parquet + event log output (gitignored) | /runs |
|
|
260
|
+
|
|
261
|
+
## Optional: Set Up AI Assistance
|
|
262
|
+
|
|
263
|
+
If you use an AI coding tool, TesterKit can register its [MCP (Model Context Protocol)](../how-to/overview/mcp-integration.md) server and generate project instructions so your AI understands TesterKit:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
testerkit setup claude-code # Claude Code
|
|
267
|
+
testerkit setup claude-desktop # Claude Desktop
|
|
268
|
+
testerkit setup copilot # GitHub Copilot (VS Code + CLI)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Next: Connect Real Hardware
|
|
272
|
+
|
|
273
|
+
When you're ready to move from mocks to real instruments, see [Step 7: Real Instruments](07-real-instruments.md). It covers station configuration, `testerkit discover`, driver wiring, and common troubleshooting.
|
|
274
|
+
|
|
275
|
+
## Next Steps
|
|
276
|
+
|
|
277
|
+
You've seen the whole loop. From here:
|
|
278
|
+
|
|
279
|
+
- **Learn it from the ground up** → the [step-by-step tutorial](index.md) builds a project from nothing, one concept at a time. It's a separate path from this complete-project quick start, not a continuation of it.
|
|
280
|
+
- [Core Concepts](../concepts/) — Understand parts, stations, and capabilities
|
|
281
|
+
- [Writing Tests](../how-to/execution/writing-tests.md) — Patterns and best practices
|
|
282
|
+
- [Configuration Reference](../reference/configuration.md) — YAML schema details
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Built-in analysis: yield, capability, Pareto, trend, and test time metrics.
|
|
2
|
+
|
|
3
|
+
Import directly from the submodule that owns the type:
|
|
4
|
+
|
|
5
|
+
from testerkit.analysis.measurements_query import MeasurementsQuery
|
|
6
|
+
from testerkit.analysis.runs_query import RunsQuery
|
|
7
|
+
from testerkit.analysis.steps_query import StepsQuery
|
|
8
|
+
from testerkit.analysis.metrics import calculate_ppk, pareto_analysis
|
|
9
|
+
"""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Shared utilities for the analysis module."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
from testerkit.data.models import ensure_utc
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_datetime(value: str | datetime | None) -> datetime | None:
|
|
11
|
+
"""Parse a datetime string or pass through a datetime object.
|
|
12
|
+
|
|
13
|
+
Handles ISO format strings and the common "Z" UTC suffix. Returns a
|
|
14
|
+
UTC-aware datetime (naive inputs are assumed UTC, the server's storage
|
|
15
|
+
convention), or None on failure (malformed strings, None input).
|
|
16
|
+
"""
|
|
17
|
+
if value is None:
|
|
18
|
+
return None
|
|
19
|
+
if isinstance(value, datetime):
|
|
20
|
+
return ensure_utc(value)
|
|
21
|
+
try:
|
|
22
|
+
return ensure_utc(datetime.fromisoformat(value.replace("Z", "+00:00")))
|
|
23
|
+
except ValueError:
|
|
24
|
+
return None
|