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,214 @@
|
|
|
1
|
+
# Stations
|
|
2
|
+
|
|
3
|
+
A **Station** is where you test — a physical bench with instruments. Station configs define what instruments are available and how to connect to them.
|
|
4
|
+
|
|
5
|
+
## Station Configuration
|
|
6
|
+
|
|
7
|
+
Station configs are YAML files in `stations/`:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
# stations/bench_1.yaml
|
|
11
|
+
id: bench_1
|
|
12
|
+
name: "Production Bench 1"
|
|
13
|
+
location: "Lab A"
|
|
14
|
+
|
|
15
|
+
instruments:
|
|
16
|
+
dmm:
|
|
17
|
+
type: dmm
|
|
18
|
+
driver: pymeasure.instruments.keysight.Keysight34461A
|
|
19
|
+
resource: "TCPIP::192.168.1.100::INSTR"
|
|
20
|
+
|
|
21
|
+
psu:
|
|
22
|
+
type: psu
|
|
23
|
+
driver: pymeasure.instruments.keysight.KeysightE36312A
|
|
24
|
+
resource: "GPIB0::5::INSTR"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Instrument Configuration
|
|
28
|
+
|
|
29
|
+
Each instrument has:
|
|
30
|
+
|
|
31
|
+
| Field | Description |
|
|
32
|
+
|-------|-------------|
|
|
33
|
+
| `type` | Instrument type (dmm, scope, psu, eload, etc.) |
|
|
34
|
+
| `driver` | Import path to the driver class (PyMeasure / PyVISA / vendor) |
|
|
35
|
+
| `resource` | VISA address or connection string |
|
|
36
|
+
| `mock_config` | Canned return values used in `--mock-instruments` mode |
|
|
37
|
+
| `mock` | Force mock mode for this instrument |
|
|
38
|
+
|
|
39
|
+
Instruments can be shared across multiple UUT sites in a multi-UUT fixture. TesterKit connects a shared instrument once and lets each UUT's test use it one at a time, so two tests never drive the same instrument at the same moment. Sharing is detected automatically — no extra flags. See [Configuring Stations](../../how-to/configuration/configuring-stations.md#shared-instruments-multi-uut) for details.
|
|
40
|
+
|
|
41
|
+
### Common Instrument Types
|
|
42
|
+
|
|
43
|
+
| Type | Description | Typical Capabilities |
|
|
44
|
+
|------|-------------|---------------------|
|
|
45
|
+
| `dmm` | Digital Multimeter | voltage (DC/AC), current, resistance |
|
|
46
|
+
| `scope` | Oscilloscope | voltage (AC), frequency, time |
|
|
47
|
+
| `psu` | DC Power Supply | voltage output, current output |
|
|
48
|
+
| `eload` | Electronic Load | current sink |
|
|
49
|
+
| `funcgen` | Function Generator | waveform output |
|
|
50
|
+
|
|
51
|
+
## Mock Mode
|
|
52
|
+
|
|
53
|
+
For development without hardware, use `--mock-instruments`:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pytest tests/ --station=stations/bench_1.yaml --mock-instruments --uut-serial=SIM001
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Configure mock values in the station:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
instruments:
|
|
63
|
+
dmm:
|
|
64
|
+
type: dmm
|
|
65
|
+
driver: pymeasure.instruments.keysight.Keysight34461A
|
|
66
|
+
resource: "TCPIP::192.168.1.100::INSTR"
|
|
67
|
+
mock_config:
|
|
68
|
+
measure_dc_voltage: 3.31
|
|
69
|
+
measure_current: 0.1
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### When to Use Mock Mode
|
|
73
|
+
|
|
74
|
+
| Approach | Use Case |
|
|
75
|
+
|----------|----------|
|
|
76
|
+
| `--mock-instruments` flag | Development, CI, station without hardware |
|
|
77
|
+
| Real hardware | Production, calibration |
|
|
78
|
+
|
|
79
|
+
## Station Types and Instances
|
|
80
|
+
|
|
81
|
+
A station can be split into a reusable template and the actual bench that fills it in:
|
|
82
|
+
|
|
83
|
+
### Station Types (Templates)
|
|
84
|
+
|
|
85
|
+
List the instrument roles and drivers a station needs:
|
|
86
|
+
|
|
87
|
+
```yaml
|
|
88
|
+
# stations/types/voltage_tester.yaml
|
|
89
|
+
id: voltage_tester
|
|
90
|
+
description: "Station for voltage testing"
|
|
91
|
+
instruments:
|
|
92
|
+
dmm: {type: dmm, driver: pymeasure.instruments.keysight.Keysight34461A}
|
|
93
|
+
psu: {type: psu, driver: pymeasure.instruments.keysight.KeysightE36312A}
|
|
94
|
+
capabilities: # catalog capability ids
|
|
95
|
+
- keysight.34461a.dc_voltage
|
|
96
|
+
- keysight.e36312a.dc_source
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
One file per station type under `stations/types/`. Every role listed under `instruments:` is required (there is no `required:` field). `capabilities:` is a list of catalog capability id strings — not inline `Capability` dicts.
|
|
100
|
+
|
|
101
|
+
### Station Instances (Real Benches)
|
|
102
|
+
|
|
103
|
+
Concrete stations that implement a type:
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
# stations/bench_1.yaml
|
|
107
|
+
id: bench_1
|
|
108
|
+
name: "Bench 1"
|
|
109
|
+
station_type: voltage_tester
|
|
110
|
+
location: "Lab A, Bench 1"
|
|
111
|
+
|
|
112
|
+
instruments:
|
|
113
|
+
dmm:
|
|
114
|
+
type: dmm
|
|
115
|
+
driver: pymeasure.instruments.keysight.Keysight34461A
|
|
116
|
+
resource: "TCPIP::192.168.1.100::INSTR"
|
|
117
|
+
psu:
|
|
118
|
+
type: psu
|
|
119
|
+
driver: pymeasure.instruments.keysight.KeysightE36312A
|
|
120
|
+
resource: "GPIB0::5::INSTR"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Instrument aliases per test
|
|
124
|
+
|
|
125
|
+
Station configs define the physical instrument inventory. You can override which station instrument a fixture name points to for a specific test or class — without editing the station config — using a per-test override file. See [Writing Tests](../../how-to/execution/writing-tests.md) for details.
|
|
126
|
+
|
|
127
|
+
## Using Stations in Tests
|
|
128
|
+
|
|
129
|
+
### Via pytest
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pytest tests/ --station=bench_1 --uut-serial=SN001
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Via fixtures
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
def test_voltage(dmm, measure):
|
|
139
|
+
"""Each instrument role in the station config is available as a fixture (here: dmm)."""
|
|
140
|
+
measure("voltage", dmm.measure_voltage())
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Via CLI
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
testerkit serve # Start UI
|
|
147
|
+
testerkit runs # List recent runs
|
|
148
|
+
testerkit show <run_id> # Show run details
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Supported Test Phases
|
|
152
|
+
|
|
153
|
+
Stations can optionally declare which test phases they support, via `supported_phases`. This lets you filter stations when selecting one for a run or profile (see [Profiles](../../how-to/execution/profiles.md)):
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
id: bench_1
|
|
157
|
+
name: "Production Bench 1"
|
|
158
|
+
|
|
159
|
+
supported_phases:
|
|
160
|
+
- validation
|
|
161
|
+
- production
|
|
162
|
+
- debug
|
|
163
|
+
|
|
164
|
+
instruments:
|
|
165
|
+
# ...
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Multiple Stations
|
|
169
|
+
|
|
170
|
+
Large test environments may have multiple stations:
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
stations/
|
|
174
|
+
├── _base.yaml # Station type definitions
|
|
175
|
+
├── bench_1.yaml # Production bench 1
|
|
176
|
+
├── bench_2.yaml # Production bench 2
|
|
177
|
+
├── debug_bench.yaml # Debug station
|
|
178
|
+
└── ci_station.yaml # CI (simulated) station
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### CI Station Example
|
|
182
|
+
|
|
183
|
+
```yaml
|
|
184
|
+
# stations/ci_station.yaml
|
|
185
|
+
id: ci_station
|
|
186
|
+
name: "CI Environment"
|
|
187
|
+
description: "For CI/CD with --mock-instruments"
|
|
188
|
+
|
|
189
|
+
instruments:
|
|
190
|
+
dmm:
|
|
191
|
+
type: dmm
|
|
192
|
+
mock: true
|
|
193
|
+
catalog_ref: generic_dmm
|
|
194
|
+
mock_config:
|
|
195
|
+
measure_dc_voltage: 3.3
|
|
196
|
+
measure_current: 0.1
|
|
197
|
+
psu:
|
|
198
|
+
type: psu
|
|
199
|
+
mock: true
|
|
200
|
+
catalog_ref: generic_psu
|
|
201
|
+
mock_config:
|
|
202
|
+
measure_voltage: 5.0
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Run in CI:
|
|
206
|
+
```bash
|
|
207
|
+
pytest tests/ --station=stations/ci_station.yaml --mock-instruments --uut-serial=CI-TEST
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Next Steps
|
|
211
|
+
|
|
212
|
+
- [Capabilities](capabilities.md) — Understanding what stations can do
|
|
213
|
+
- [Fixtures](fixtures.md) — Mapping UUT pins to instruments
|
|
214
|
+
- [Configuration Reference](../../reference/configuration.md) — YAML schema details
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# The TesterKit data stores
|
|
2
|
+
|
|
3
|
+
TesterKit uses four complementary data stores, each optimized for a different access pattern. Together they provide a complete picture of test activity.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
| Store | Data | Format |
|
|
8
|
+
|-------|------|--------|
|
|
9
|
+
| **EventStore** | Typed events (sessions, measurements, diagnostics) — the source of truth | Arrow files (DuckDB-queryable) |
|
|
10
|
+
| **ChannelStore** | Time-series instrument data (waveforms, readings) | Arrow segments |
|
|
11
|
+
| **FileStore** | Captured artifacts — images, video, vendor capture files (`file://`) | files + index |
|
|
12
|
+
| **RunStore** | Flat test results, one row per measurement | Parquet files |
|
|
13
|
+
|
|
14
|
+
## EventStore — Source of Truth
|
|
15
|
+
|
|
16
|
+
The event log captures every significant action as a typed event. It is the canonical record of what happened.
|
|
17
|
+
|
|
18
|
+
- **Write path:** events are buffered and streamed into the DuckDB-backed event log (see [flight-streaming](flight-streaming.md) for the cross-process detail).
|
|
19
|
+
- **Read path:** SQL queries, or direct reads of the Arrow files.
|
|
20
|
+
- **Storage:** `<data_dir>/events/{date}/{session_id}-{pid}.arrow`
|
|
21
|
+
|
|
22
|
+
Each event carries only its own fields — `SessionStarted` carries the session/station/operator metadata once (and never a `run_id`); `RunStarted` carries the run/UUT context; `MeasurementRecorded` carries just the measurement. The flat run rows fill in the shared context when they're written.
|
|
23
|
+
|
|
24
|
+
## ChannelStore — Time-Series Data
|
|
25
|
+
|
|
26
|
+
Instrument reads that produce arrays, waveforms, or high-frequency scalar streams go to the ChannelStore. The event carries a compact reference URI (`channel://scope.ch1.waveform?session=…`) that points at the data, instead of the bulk samples.
|
|
27
|
+
|
|
28
|
+
- **Write path:** samples are buffered and written to rotating Arrow segments (each closed and readable after every flush).
|
|
29
|
+
- **Read path:** query the channel back, with optional downsampling for plotting.
|
|
30
|
+
- **Storage:** `<data_dir>/channels/{date}/{channel_id}_{session_short}.arrow`
|
|
31
|
+
|
|
32
|
+
Each channel gets its own IPC file with a schema inferred from the first write. Segment rotation ensures files are always readable (closed after each flush).
|
|
33
|
+
|
|
34
|
+
## RunStore — Analysis-Ready Results
|
|
35
|
+
|
|
36
|
+
The run store produces analysis-ready parquet files with one row per measurement, every row carrying its full context. This is what you query with DuckDB, Polars, or Spark for yield analysis and SPC.
|
|
37
|
+
|
|
38
|
+
- **Write path:** the runs daemon collects events as a run executes and writes the per-run parquet when the run ends.
|
|
39
|
+
- **Read path:** any Parquet reader (DuckDB, Polars, pandas).
|
|
40
|
+
- **Storage:** `<data_dir>/runs/{date}/{timestamp}_{run_id8}_{serial}.parquet`
|
|
41
|
+
|
|
42
|
+
The parquet files are built from the event stream — if the schema changes, they can be rebuilt from events.
|
|
43
|
+
|
|
44
|
+
### Live streaming + crash safety
|
|
45
|
+
|
|
46
|
+
During execution, the materializer holds row state in-process and flushes to a single per-run parquet at run end. The operator UI subscribes to the in-process event stream for live updates — there is no separate JSONL journal on disk. If the process is killed before the parquet is finalized, the close-time fallback writes whatever rows reached the materializer with `run_outcome = aborted`.
|
|
47
|
+
|
|
48
|
+
## FileStore — Captured Artifacts
|
|
49
|
+
|
|
50
|
+
Bulk artifacts — scope screenshots, camera frames, vendor capture files (`.tdms`, NPZ), any blob — go to the FileStore. Like the ChannelStore, the event carries a compact `file://` reference instead of the bytes.
|
|
51
|
+
|
|
52
|
+
- **Write path:** `observe(name, value)` with an image / `bytes` / `Path`, or `files.write` / `files.stream` directly.
|
|
53
|
+
- **Read path:** fetch the artifact by its `file://` reference.
|
|
54
|
+
- **Storage:** `<data_dir>/files/{date}/{session_id}/{filename}`
|
|
55
|
+
|
|
56
|
+
See [the three verbs](three-verbs.md) for how a test routes a value to the FileStore.
|
|
57
|
+
|
|
58
|
+
## How They Relate
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Events (source of truth)
|
|
62
|
+
├── RunStore — per-run parquet, written when the run ends
|
|
63
|
+
├── ChannelStore — time-series; events carry a channel:// reference
|
|
64
|
+
└── FileStore — artifacts; events carry a file:// reference
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
1. All activity flows through the EventStore — the source of truth.
|
|
68
|
+
2. The runs daemon collects events and writes a per-run parquet when the run ends.
|
|
69
|
+
3. Array/waveform values write to the ChannelStore and artifacts to the FileStore; the event keeps a `channel://` or `file://` reference.
|
|
70
|
+
4. Queries join across stores using `session_id`.
|
|
71
|
+
|
|
72
|
+
## Storage Layout
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
<data_dir>/
|
|
76
|
+
├── events/ # EventStore
|
|
77
|
+
│ └── 2026-03-10/
|
|
78
|
+
│ └── {session_id}-{pid}.arrow
|
|
79
|
+
├── channels/ # ChannelStore
|
|
80
|
+
│ ├── 2026-03-10/
|
|
81
|
+
│ │ ├── dmm.voltage_{session_short}.arrow
|
|
82
|
+
│ │ └── scope.ch1.waveform_{session_short}.arrow
|
|
83
|
+
│ └── _index.duckdb # channel descriptors + query index (single file, disposable)
|
|
84
|
+
├── files/ # FileStore
|
|
85
|
+
│ ├── 2026-03-10/
|
|
86
|
+
│ │ └── {session_id}/
|
|
87
|
+
│ │ └── setup_photo.png
|
|
88
|
+
│ └── _index.duckdb # file catalog + query index (single file, disposable)
|
|
89
|
+
└── runs/ # RunStore (parquet)
|
|
90
|
+
├── 2026-03-10/
|
|
91
|
+
│ └── 20260310T143022Z_a1b2c3d4_SN001.parquet
|
|
92
|
+
└── _index.<fingerprint>.duckdb # content-addressed query index (see below)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Sessions are not a stored entity — they're derived from events at query time.
|
|
96
|
+
|
|
97
|
+
## Where the data dir lives
|
|
98
|
+
|
|
99
|
+
`<data_dir>` defaults to a shared per-user directory so every project on the machine sees the same results pool — `testerkit runs`, `testerkit serve`, and DuckDB queries see everything.
|
|
100
|
+
|
|
101
|
+
Resolution order (first match wins):
|
|
102
|
+
|
|
103
|
+
1. Explicit `--data-dir` argument or `data_dir=` parameter
|
|
104
|
+
2. `data_dir` field in the project's `testerkit.yaml`
|
|
105
|
+
3. `TESTERKIT_HOME` environment variable
|
|
106
|
+
4. `~/.local/share/testerkit/data/` (platform default via `platformdirs`)
|
|
107
|
+
|
|
108
|
+
To isolate a project's results from the shared pool, add to `testerkit.yaml`:
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
name: my-project
|
|
112
|
+
data_dir: data # writes to ./data/ instead of the global pool
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Schema evolution — HARD contract
|
|
116
|
+
|
|
117
|
+
Parquet files are the permanent record. Each testerkit version may add columns; older files simply lack them. The parquet artifact is a **HARD contract**: changes must be additive because written files cannot be retroactively rewritten when a new version ships.
|
|
118
|
+
|
|
119
|
+
Until the 1.0 cut, the following invariants hold:
|
|
120
|
+
|
|
121
|
+
- **New columns only.** Every release may add columns. Existing column names, types, and semantics are stable across 0.x releases.
|
|
122
|
+
- **No removals or type changes** in 0.x. If a column would otherwise be removed or repurposed, it stays in the schema and reads as NULL for newly-written rows; the old meaning is documented as deprecated.
|
|
123
|
+
- **PK stability.** `(run_id, step_path, vector_index)` is the per-step identity in the materialized table; `(run_id, step_path, vector_index, measurement_name, vector_retry)` discriminates measurement rows. These tuples do not change shape in 0.x.
|
|
124
|
+
- **`record_type` discriminator stable.** The at-rest `'run'` / `'step'` / `'vector'` values are part of the wire format and do not change (the daemon also projects a `'measurement'` type at query time).
|
|
125
|
+
- **Read with `union_by_name=true`.** Consumer queries that follow the recommended `read_parquet(..., union_by_name=true)` pattern survive every additive evolution automatically.
|
|
126
|
+
|
|
127
|
+
```sql
|
|
128
|
+
-- DuckDB handles mixed schemas automatically
|
|
129
|
+
SELECT station_id, project_name, run_outcome
|
|
130
|
+
FROM read_parquet('~/.local/share/testerkit/data/runs/**/*.parquet',
|
|
131
|
+
union_by_name=true)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Schema rewrites and column removals are deferred to the 1.0 cut, when a migration story for old files lands.
|
|
135
|
+
|
|
136
|
+
## The DuckDB query index
|
|
137
|
+
|
|
138
|
+
Every store keeps a DuckDB index alongside its data files, to speed up queries like `testerkit runs` and the web UI. The index is always a **disposable cache** — delete it and it rebuilds from the parquet or Arrow files underneath. Deleting an index file never loses data.
|
|
139
|
+
|
|
140
|
+
Two index shapes are in use, depending on the store:
|
|
141
|
+
|
|
142
|
+
**RunStore — content-addressed.** The runs index file name embeds a short fingerprint of the query it serves — the shape of the projection plus the TesterKit version that built it — as `_index.<fingerprint>.duckdb`. A query or schema change never rewrites an existing file in place: it produces a different fingerprint, and the next build writes a *new* file alongside any older ones. Multiple fingerprints can sit in the same `<data_dir>/runs/` directory at once — an older TesterKit version (or another project pinned to an older release) reading the same results keeps opening its own file, untouched by a newer index being built next to it.
|
|
143
|
+
|
|
144
|
+
**ChannelStore and FileStore — a single `_index.duckdb`.** These two stores have not adopted the content-addressed model yet; each keeps one index file that's reused (or, on a schema change, deleted and rebuilt) in place. Same disposable-cache guarantee, no coexisting versions.
|
|
145
|
+
|
|
146
|
+
### Managing runs-index files
|
|
147
|
+
|
|
148
|
+
`testerkit data index` manages the RunStore's index files (called *epochs* — one per fingerprint):
|
|
149
|
+
|
|
150
|
+
```cli
|
|
151
|
+
$ testerkit data index list
|
|
152
|
+
FINGERPRINT SCHEMA BUILT BY SEEN BY RUNS SIZE LAST SEEN
|
|
153
|
+
* a1b2c3d4e5f6 3 0.3.0 0.3.0 1,204 18.2 MB just now
|
|
154
|
+
f0e1d2c3b4a5 2 0.2.4 0.2.4, 0.3.0 1,204 17.9 MB 6 days ago
|
|
155
|
+
|
|
156
|
+
$ testerkit data index build # warm the current epoch
|
|
157
|
+
$ testerkit data index rm f0e1d2c3b4a5 # drop one epoch by its fingerprint prefix
|
|
158
|
+
$ testerkit data index prune # reap epochs no recent version has touched
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- `list` — every epoch present, with its fingerprint, schema version, the TesterKit version that built it, every version that has since read it, row count, size, and last access.
|
|
162
|
+
- `build` — warms the epoch matching the currently installed TesterKit version by ingesting from parquet. `--rebuild` discards it first for a clean rebuild.
|
|
163
|
+
- `rm <fingerprint-prefix>` — deletes one epoch. Refuses to remove the epoch actively serving queries unless `--force`.
|
|
164
|
+
- `prune` — removes epochs no recent TesterKit version has touched, always keeping the current epoch and (by default) the three most-recently-seen others.
|
|
165
|
+
|
|
166
|
+
None of these touch the parquet files. Every command is safe to run at any time — the worst case is that the next query pays the cost of a rebuild.
|
|
167
|
+
|
|
168
|
+
To force a full rebuild of the channels or files index, delete `_index.duckdb` under that store's directory — it rebuilds from the Arrow segments or file catalog on next access.
|
|
169
|
+
|
|
170
|
+
## Mixed versions on one machine
|
|
171
|
+
|
|
172
|
+
When multiple projects use different TesterKit versions but share the global results directory:
|
|
173
|
+
|
|
174
|
+
| Layer | What happens | User impact |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| Parquet files | Each version writes its own schema. Newer files may have more columns. | NULL values for columns that didn't exist when the file was written. |
|
|
177
|
+
| Runs query index | Content-addressed: each TesterKit version reads and writes its own epoch file, keyed by its own fingerprint. Old and new epochs coexist in `<data_dir>/runs/` without contention. | `testerkit data index list` shows every epoch present; nothing to migrate by hand. Run `testerkit data index prune` to reclaim disk from epochs no version uses anymore. |
|
|
178
|
+
| Channels / files query index | A single shared index file per store. | Rare cross-version friction can still surface here; delete `_index.duckdb` in that store's directory to force a rebuild. |
|
|
179
|
+
| Web UI / CLI | Shows whatever the current process's index has. | Some fields may be empty for older runs. |
|
|
180
|
+
|
|
181
|
+
The rule: newer is always a superset. An older TesterKit version reading newer results ignores unknown columns; a newer version reading older results sees NULL for missing columns. No version corrupts or downgrades another's data.
|
|
182
|
+
|
|
183
|
+
## See also
|
|
184
|
+
|
|
185
|
+
**Same topic, other quadrants:**
|
|
186
|
+
|
|
187
|
+
- [Reference → Parquet schema](../../reference/data/parquet-schema.md) — column-level reference for the materialized run rows
|
|
188
|
+
- [Reference → Query API](../../reference/data/query-api.md) — `RunsQuery` / `StepsQuery` / `MeasurementsQuery` — the read path over the run store
|
|
189
|
+
- [Reference → `testerkit data index`](../../reference/cli.md#cli-data-index) — full flag reference for `list` / `build` / `rm` / `prune`
|
|
190
|
+
- [How-to → Querying events](../../how-to/data/querying-events.md), [Querying channels](../../how-to/data/querying-channels.md) — task recipes against each store
|
|
191
|
+
- [How-to → Export results](../../how-to/data/export-results.md) — pulling rows out of the parquet store
|
|
192
|
+
- [Integration → Lakehouse import](../../integration/data/lakehouse-import.md) — pulling TesterKit parquet into your warehouse
|
|
193
|
+
|
|
194
|
+
**Sibling concepts:**
|
|
195
|
+
|
|
196
|
+
- [Event log](event-log.md) — the source-of-truth event stream
|
|
197
|
+
- [Event sourcing](event-sourcing.md) — why the platform is event-sourced
|
|
198
|
+
- [Sessions](sessions.md) — the observation window that keys events
|
|
199
|
+
- [Flight streaming](flight-streaming.md) — the cross-process query model
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# The Event Log
|
|
2
|
+
|
|
3
|
+
The event log is TesterKit's unified record of everything that happens during testing — sessions, instrument connections, measurements, diagnostics, and more.
|
|
4
|
+
|
|
5
|
+
## One stream, in order
|
|
6
|
+
|
|
7
|
+
Every significant action emits a typed event into a single ordered stream. That stream lets you reconstruct what happened during a test, line up instrument reads with measurements, and watch a test live as it runs.
|
|
8
|
+
|
|
9
|
+
## Event Hierarchy
|
|
10
|
+
|
|
11
|
+
Every event carries the same common fields:
|
|
12
|
+
|
|
13
|
+
| Field | Type | Description |
|
|
14
|
+
|-------|------|-------------|
|
|
15
|
+
| `id` | UUID | Unique event identifier |
|
|
16
|
+
| `occurred_at` | datetime | When the event happened |
|
|
17
|
+
| `received_at` | datetime | When the log processed it for storage |
|
|
18
|
+
| `session_id` | UUID | Which session this event belongs to |
|
|
19
|
+
| `run_id` | UUID | Which test run (if applicable) |
|
|
20
|
+
|
|
21
|
+
Each event also carries an `event_type` string (e.g. `test.measurement`) naming which kind of event it is.
|
|
22
|
+
|
|
23
|
+
## Event Categories
|
|
24
|
+
|
|
25
|
+
TesterKit defines events across 12 categories.
|
|
26
|
+
|
|
27
|
+
### Session (2 events)
|
|
28
|
+
| Event | Type String | Description |
|
|
29
|
+
|-------|-------------|-------------|
|
|
30
|
+
| `SessionStarted` | `session.started` | Session-wide metadata: station, operator, fixture |
|
|
31
|
+
| `SessionEnded` | `session.ended` | Session outcome |
|
|
32
|
+
|
|
33
|
+
### Run (3 events)
|
|
34
|
+
| Event | Type String | Description |
|
|
35
|
+
|-------|-------------|-------------|
|
|
36
|
+
| `RunStarted` | `run.started` | Full run context: UUT, part, operator, config snapshots |
|
|
37
|
+
| `RunEnded` | `run.ended` | Run outcome |
|
|
38
|
+
| `RunMaterialized` | `run.materialized` | Emitted after the run's Parquet file is durably written; signals that the run is ready for downstream consumers |
|
|
39
|
+
|
|
40
|
+
### Site (2 events — multi-UUT)
|
|
41
|
+
| Event | Type String | Description |
|
|
42
|
+
|-------|-------------|-------------|
|
|
43
|
+
| `SiteStarted` | `site.started` | A multi-UUT site subprocess begins |
|
|
44
|
+
| `SiteCompleted` | `site.completed` | A multi-UUT site subprocess finishes |
|
|
45
|
+
|
|
46
|
+
### Sync (2 events — multi-UUT)
|
|
47
|
+
| Event | Type String | Description |
|
|
48
|
+
|-------|-------------|-------------|
|
|
49
|
+
| `SyncArrived` | `sync.arrived` | A worker reached a synchronization barrier |
|
|
50
|
+
| `SyncRelease` | `sync.release` | All workers arrived; barrier released |
|
|
51
|
+
|
|
52
|
+
### Route (2 events — signal switching)
|
|
53
|
+
| Event | Type String | Description |
|
|
54
|
+
|-------|-------------|-------------|
|
|
55
|
+
| `RouteClosed` | `route.closed` | Switch route closed (signal connected) |
|
|
56
|
+
| `RouteOpened` | `route.opened` | Switch route opened (signal disconnected) |
|
|
57
|
+
|
|
58
|
+
### Fixture (5 events)
|
|
59
|
+
| Event | Type String | Description |
|
|
60
|
+
|-------|-------------|-------------|
|
|
61
|
+
| `InstrumentConnected` | `fixture.instrument_connected` | Instrument identified and connected |
|
|
62
|
+
| `IdentityVerified` | `fixture.identity_verified` | Expected vs actual instrument identity |
|
|
63
|
+
| `CalibrationWarning` | `fixture.calibration_warning` | Calibration due date approaching |
|
|
64
|
+
| `UutScanned` | `fixture.uut_scanned` | UUT serial barcode scanned |
|
|
65
|
+
| `InstrumentDisconnected` | `fixture.instrument_disconnected` | Instrument released during teardown |
|
|
66
|
+
|
|
67
|
+
### Test (7 events)
|
|
68
|
+
| Event | Type String | Description |
|
|
69
|
+
|-------|-------------|-------------|
|
|
70
|
+
| `StepsDiscovered` | `test.steps_discovered` | Full list of collected test items |
|
|
71
|
+
| `StepStarted` | `test.step_started` | A test step begins execution |
|
|
72
|
+
| `MeasurementRecorded` | `test.measurement` | A single measurement with limits and outcome |
|
|
73
|
+
| `StepEnded` | `test.step_ended` | A test step finishes |
|
|
74
|
+
| `Observation` | `test.observation` | An environmental or contextual reading recorded during a step |
|
|
75
|
+
| `VectorStarted` | `test.vector_started` | A parametric sweep vector begins |
|
|
76
|
+
| `VectorEnded` | `test.vector_ended` | A parametric sweep vector finishes |
|
|
77
|
+
|
|
78
|
+
### Instrument (2 events)
|
|
79
|
+
| Event | Type String | Description |
|
|
80
|
+
|-------|-------------|-------------|
|
|
81
|
+
| `InstrumentSet` | `instrument.set` | Driver set via proxy |
|
|
82
|
+
| `InstrumentConfigure` | `instrument.configure` | Driver configure via proxy |
|
|
83
|
+
|
|
84
|
+
### Diagnostic (2 events)
|
|
85
|
+
| Event | Type String | Description |
|
|
86
|
+
|-------|-------------|-------------|
|
|
87
|
+
| `DiagnosticWarning` | `diagnostic.warning` | Non-fatal warning |
|
|
88
|
+
| `DiagnosticError` | `diagnostic.error` | Error condition |
|
|
89
|
+
|
|
90
|
+
### Channel (3 events)
|
|
91
|
+
| Event | Type String | Description |
|
|
92
|
+
|-------|-------------|-------------|
|
|
93
|
+
| `ChannelStarted` | `channel.started` | A channel received its first sample in this session |
|
|
94
|
+
| `ChannelEnded` | `channel.ended` | A channel was sealed for this session |
|
|
95
|
+
| `ChannelCheckpoint` | `channel.checkpoint` | Liveness + progress marker from an active channel producer |
|
|
96
|
+
|
|
97
|
+
### File (3 events)
|
|
98
|
+
| Event | Type String | Description |
|
|
99
|
+
|-------|-------------|-------------|
|
|
100
|
+
| `FileStarted` | `file.started` | A data stream begins |
|
|
101
|
+
| `FileEnded` | `file.ended` | A data stream ends |
|
|
102
|
+
| `FileCheckpoint` | `file.checkpoint` | Liveness + progress marker from an active file sink |
|
|
103
|
+
|
|
104
|
+
### Dialog (2 events)
|
|
105
|
+
| Event | Type String | Description |
|
|
106
|
+
|-------|-------------|-------------|
|
|
107
|
+
| `DialogOpened` | `dialog.opened` | Operator dialog shown, execution paused |
|
|
108
|
+
| `DialogResponded` | `dialog.responded` | Operator responded to dialog |
|
|
109
|
+
|
|
110
|
+
## Event Timeline
|
|
111
|
+
|
|
112
|
+
A typical test session emits events in this order:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
SessionStarted # Session-wide metadata (station, operator)
|
|
116
|
+
├── RunStarted # Run context (UUT, part, config snapshots)
|
|
117
|
+
├── InstrumentConnected # One per instrument role
|
|
118
|
+
├── IdentityVerified # Optional identity check
|
|
119
|
+
├── StepsDiscovered # Full list of collected test items
|
|
120
|
+
├── StepStarted # First test step
|
|
121
|
+
│ ├── InstrumentSet
|
|
122
|
+
│ └── MeasurementRecorded
|
|
123
|
+
├── StepEnded
|
|
124
|
+
├── StepStarted # Next test step...
|
|
125
|
+
│ └── ...
|
|
126
|
+
├── StepEnded
|
|
127
|
+
├── RunEnded # All steps complete
|
|
128
|
+
├── RunMaterialized # Parquet file durably written
|
|
129
|
+
├── InstrumentDisconnected
|
|
130
|
+
└── SessionEnded # Cleanup complete
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## How the log is written
|
|
134
|
+
|
|
135
|
+
When an event is written:
|
|
136
|
+
|
|
137
|
+
1. **`received_at` is stamped** and the event is buffered for batched writes to an Arrow file (a fast columnar on-disk format)
|
|
138
|
+
2. **On `RunEnded`** the run's Parquet file is written. The same path runs when you replay stored events with `testerkit export`.
|
|
139
|
+
3. **Flush** happens every 50 events (configurable), writing a batch to the Arrow file
|
|
140
|
+
|
|
141
|
+
The set of writers (Parquet, the live UI feed) is built in; adding a new output format is a change to TesterKit itself, not a drop-in plugin.
|
|
142
|
+
|
|
143
|
+
## Storage
|
|
144
|
+
|
|
145
|
+
Events are stored as Arrow files, date-partitioned:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
<data_dir>/events/
|
|
149
|
+
├── 2026-03-10/
|
|
150
|
+
│ ├── {session_id}-{pid}.arrow
|
|
151
|
+
│ └── {session_id}-{pid}_0001.arrow # rotation for large sessions
|
|
152
|
+
└── 2026-03-11/
|
|
153
|
+
└── ...
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Each Arrow file contains index columns (`id`, `event_type`, `occurred_at`, `received_at`, `session_id`, `run_id`) plus a `json` column with the full serialized event for lossless replay.
|
|
157
|
+
|
|
158
|
+
## Dual-Write Pattern
|
|
159
|
+
|
|
160
|
+
Events are also loaded into an in-memory database as they're written, so you can query them with SQL right away:
|
|
161
|
+
|
|
162
|
+
1. **Arrow file** — crash-safe append-only storage
|
|
163
|
+
2. **In-memory DuckDB** — immediate SQL queryability
|
|
164
|
+
|
|
165
|
+
Each batch is loaded into an in-memory SQL database as it's written, so a query sees an event the instant after it's written.
|
|
166
|
+
|
|
167
|
+
## What stays stable across releases
|
|
168
|
+
|
|
169
|
+
Once an event type and its fields exist, they don't change or disappear within 0.x. Only new event types and new optional fields are added. A query or report you write today keeps working as the platform evolves.
|
|
170
|
+
|
|
171
|
+
## See also
|
|
172
|
+
|
|
173
|
+
**Same topic, other quadrants:**
|
|
174
|
+
|
|
175
|
+
- [Reference → Event types](../../reference/data/event-types.md) — generated field reference for every event class
|
|
176
|
+
- [Reference → Parquet schema](../../reference/data/parquet-schema.md) — the materialized projection of these events
|
|
177
|
+
- [How-to → Querying events](../../how-to/data/querying-events.md) — MCP / HTTP / Python recipes for reading the event log
|
|
178
|
+
- [Operator UI → Events](../../reference/operator-ui/events.md) — the browser view of the event log
|
|
179
|
+
- [Tutorial → Step 10: Live monitoring](../../tutorial/10-live-monitoring.md) — first hands-on with events as they happen
|
|
180
|
+
|
|
181
|
+
**Sibling concepts:**
|
|
182
|
+
|
|
183
|
+
- [Event sourcing](event-sourcing.md) — why the platform is event-sourced rather than mutation-based
|
|
184
|
+
- [Data stores](data-stores.md) — how EventStore fits with ChannelStore, FileStore, and RunStore
|
|
185
|
+
- [Sessions](sessions.md) — the observation window the event log keys events by
|