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,507 @@
|
|
|
1
|
+
"""Store workloads — the single definition of what we measure.
|
|
2
|
+
|
|
3
|
+
Both ``testerkit benchmark`` and the perf test suite build their cases from
|
|
4
|
+
this module, so user numbers and CI numbers come from identical code.
|
|
5
|
+
|
|
6
|
+
Each operation is swept across a list of ``scales`` (unit of work) and,
|
|
7
|
+
for writes, a list of ``writers`` (concurrency) — every combination is a
|
|
8
|
+
separate :class:`Workload` case, hence a separate row in the report.
|
|
9
|
+
|
|
10
|
+
Every workload measures the SHIPPED path: channel queries route through
|
|
11
|
+
the channels daemon (not a disk glob); file resolves route through the
|
|
12
|
+
catalog daemon (not a date-dir walk). A user's number matches what they
|
|
13
|
+
actually experience.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
from datetime import UTC, datetime
|
|
20
|
+
from typing import NamedTuple
|
|
21
|
+
from uuid import UUID, uuid4
|
|
22
|
+
|
|
23
|
+
from testerkit.benchmark.core import BenchContext, Workload
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _Releaser:
|
|
27
|
+
"""Tiny closeable that runs ``fn`` on teardown (for daemon releases)."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, fn: Callable[[], object]) -> None:
|
|
30
|
+
self._fn = fn
|
|
31
|
+
|
|
32
|
+
def close(self) -> None:
|
|
33
|
+
self._fn()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
# Sample data builders (shared with the perf tests)
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def make_measurement(session_id: UUID, i: int):
|
|
42
|
+
"""A representative MeasurementRecorded event."""
|
|
43
|
+
from testerkit.data.events import MeasurementRecorded
|
|
44
|
+
|
|
45
|
+
return MeasurementRecorded(
|
|
46
|
+
session_id=session_id,
|
|
47
|
+
step_name=f"step_{i % 10}",
|
|
48
|
+
step_index=i % 10,
|
|
49
|
+
measurement_name=f"voltage_{i}",
|
|
50
|
+
value=3.3 + (i % 100) * 0.01,
|
|
51
|
+
unit="V",
|
|
52
|
+
outcome="passed",
|
|
53
|
+
limit_low=3.0,
|
|
54
|
+
limit_high=3.6,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def build_run(seed: int, *, n_steps: int = 10, n_meas: int = 5):
|
|
59
|
+
"""A finalized TestRun with ``n_steps`` steps x ``n_meas`` measurements."""
|
|
60
|
+
from testerkit.data.models import (
|
|
61
|
+
UUT,
|
|
62
|
+
Measurement,
|
|
63
|
+
Outcome,
|
|
64
|
+
TestRun,
|
|
65
|
+
TestStep,
|
|
66
|
+
TestVector,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return TestRun(
|
|
70
|
+
id=uuid4(),
|
|
71
|
+
started_at=datetime(2026, 6, 1, 12, 0, 0, tzinfo=UTC),
|
|
72
|
+
ended_at=datetime(2026, 6, 1, 12, 1, 0, tzinfo=UTC),
|
|
73
|
+
uut=UUT(serial=f"SN-{seed:06d}"),
|
|
74
|
+
outcome=Outcome.PASSED,
|
|
75
|
+
steps=[
|
|
76
|
+
TestStep(
|
|
77
|
+
name=f"step_{s}",
|
|
78
|
+
outcome=Outcome.PASSED,
|
|
79
|
+
started_at=datetime(2026, 6, 1, 12, 0, 0, tzinfo=UTC),
|
|
80
|
+
ended_at=datetime(2026, 6, 1, 12, 0, 30, tzinfo=UTC),
|
|
81
|
+
vectors=[
|
|
82
|
+
TestVector(
|
|
83
|
+
outcome=Outcome.PASSED,
|
|
84
|
+
measurements=[
|
|
85
|
+
Measurement(name=f"m_{m}", value=3.3 + m * 0.01, outcome=Outcome.PASSED)
|
|
86
|
+
for m in range(n_meas)
|
|
87
|
+
],
|
|
88
|
+
)
|
|
89
|
+
],
|
|
90
|
+
)
|
|
91
|
+
for s in range(n_steps)
|
|
92
|
+
],
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
# Events
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _setup_events_emit(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
102
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
103
|
+
from testerkit.data.event_store import EventStore
|
|
104
|
+
|
|
105
|
+
es: EventStore = ctx.track(EventStore(_data_dir=ctx.data_dir)) # type: ignore[assignment]
|
|
106
|
+
|
|
107
|
+
def emit_all() -> None:
|
|
108
|
+
# Fresh session + flush() per call so the timed work is the
|
|
109
|
+
# DURABLE path (written to disk AND acked by the daemon), not
|
|
110
|
+
# an in-memory batch fill.
|
|
111
|
+
sid = uuid4()
|
|
112
|
+
for i in range(scale):
|
|
113
|
+
es.emit(make_measurement(sid, i))
|
|
114
|
+
es.flush()
|
|
115
|
+
|
|
116
|
+
return emit_all
|
|
117
|
+
|
|
118
|
+
return setup
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _setup_events_query(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
122
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
123
|
+
from testerkit.data.event_store import EventStore
|
|
124
|
+
|
|
125
|
+
es: EventStore = ctx.track(EventStore(_data_dir=ctx.data_dir)) # type: ignore[assignment]
|
|
126
|
+
sid = uuid4()
|
|
127
|
+
for i in range(scale):
|
|
128
|
+
es.emit(make_measurement(sid, i))
|
|
129
|
+
|
|
130
|
+
def query_all() -> object:
|
|
131
|
+
return es.events(session_id=sid)
|
|
132
|
+
|
|
133
|
+
return query_all
|
|
134
|
+
|
|
135
|
+
return setup
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# ---------------------------------------------------------------------------
|
|
139
|
+
# Runs (the materialized view of events)
|
|
140
|
+
# ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _setup_runs_save(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
144
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
145
|
+
# Mirror the real finalize path (``save_test_run`` = atomic file
|
|
146
|
+
# write, no synchronous daemon notify).
|
|
147
|
+
from testerkit.data.backends.parquet import ParquetBackend
|
|
148
|
+
|
|
149
|
+
backend = ParquetBackend(data_dir=ctx.data_dir)
|
|
150
|
+
|
|
151
|
+
def save_n() -> None:
|
|
152
|
+
for _ in range(scale):
|
|
153
|
+
backend.save_test_run(build_run(uuid4().int % 1_000_000))
|
|
154
|
+
|
|
155
|
+
return save_n
|
|
156
|
+
|
|
157
|
+
return setup
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _populate_runs(ctx: BenchContext, n: int) -> None:
|
|
161
|
+
from testerkit.data.backends.parquet import ParquetBackend
|
|
162
|
+
from testerkit.data.run_store import RunStore
|
|
163
|
+
|
|
164
|
+
backend = ParquetBackend(data_dir=ctx.data_dir)
|
|
165
|
+
store = RunStore(_data_dir=ctx.data_dir)
|
|
166
|
+
try:
|
|
167
|
+
for i in range(n):
|
|
168
|
+
store.notify_new_run(backend.save_test_run(build_run(i)))
|
|
169
|
+
finally:
|
|
170
|
+
store.close()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _setup_runs_list(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
174
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
175
|
+
from testerkit.analysis.runs_query import RunsQuery
|
|
176
|
+
|
|
177
|
+
_populate_runs(ctx, scale)
|
|
178
|
+
|
|
179
|
+
def list_page() -> object:
|
|
180
|
+
with RunsQuery(_data_dir=ctx.data_dir) as q:
|
|
181
|
+
return q.list_recent(limit=50)
|
|
182
|
+
|
|
183
|
+
return list_page
|
|
184
|
+
|
|
185
|
+
return setup
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _setup_runs_steps(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
189
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
190
|
+
from testerkit.analysis.runs_query import RunsQuery
|
|
191
|
+
from testerkit.analysis.steps_query import StepsQuery
|
|
192
|
+
|
|
193
|
+
_populate_runs(ctx, scale)
|
|
194
|
+
with RunsQuery(_data_dir=ctx.data_dir) as q:
|
|
195
|
+
rows = q.list_recent(limit=1)
|
|
196
|
+
run_id = str(rows[0].run_id) if rows else ""
|
|
197
|
+
|
|
198
|
+
def steps() -> object:
|
|
199
|
+
with StepsQuery(_data_dir=ctx.data_dir) as q:
|
|
200
|
+
return q.list_for_run(run_id)
|
|
201
|
+
|
|
202
|
+
return steps
|
|
203
|
+
|
|
204
|
+
return setup
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# ---------------------------------------------------------------------------
|
|
208
|
+
# Channels — write through a serve producer, query the daemon
|
|
209
|
+
# ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _setup_channels_write(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
213
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
214
|
+
from testerkit.data.channels.store import ChannelStore
|
|
215
|
+
|
|
216
|
+
store = ChannelStore(ctx.data_dir, uuid4(), flush_threshold=100, serve=True)
|
|
217
|
+
store.open()
|
|
218
|
+
ctx.track(store)
|
|
219
|
+
|
|
220
|
+
def write_all() -> None:
|
|
221
|
+
for i in range(scale):
|
|
222
|
+
store.write("sensor.temp", 25.0 + i * 0.01, unit="C")
|
|
223
|
+
|
|
224
|
+
return write_all
|
|
225
|
+
|
|
226
|
+
return setup
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _setup_channels_query(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
230
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
231
|
+
from testerkit.data.channels import flight_manager
|
|
232
|
+
from testerkit.data.channels.client import ChannelClient
|
|
233
|
+
from testerkit.data.channels.store import ChannelStore
|
|
234
|
+
|
|
235
|
+
# Unique channel per case — all channel cases share the temp dir's
|
|
236
|
+
# one daemon/index, so a fixed name would let this query count
|
|
237
|
+
# samples written by other cases (polluting the row count).
|
|
238
|
+
channel_id = f"bench.{uuid4().hex[:8]}"
|
|
239
|
+
producer = ChannelStore(ctx.data_dir, uuid4(), flush_threshold=100, serve=True)
|
|
240
|
+
producer.open()
|
|
241
|
+
ctx.track(producer)
|
|
242
|
+
for i in range(scale):
|
|
243
|
+
producer.write(channel_id, 25.0 + i * 0.01, unit="C")
|
|
244
|
+
|
|
245
|
+
channels_dir = ctx.data_dir / "channels"
|
|
246
|
+
location = flight_manager.acquire(channels_dir)
|
|
247
|
+
client = ChannelClient(location)
|
|
248
|
+
ctx.track(client)
|
|
249
|
+
ctx.track(_Releaser(lambda: flight_manager.release(channels_dir)))
|
|
250
|
+
|
|
251
|
+
def query() -> object:
|
|
252
|
+
return client.query(channel_id)
|
|
253
|
+
|
|
254
|
+
return query
|
|
255
|
+
|
|
256
|
+
return setup
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _setup_channels_stream(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
260
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
261
|
+
import testerkit.channels as channels_mod
|
|
262
|
+
from testerkit.data.channels.store import ChannelStore
|
|
263
|
+
from testerkit.execution._state import set_channel_store
|
|
264
|
+
|
|
265
|
+
store = ChannelStore(ctx.data_dir, uuid4(), flush_threshold=50, serve=True)
|
|
266
|
+
store.open()
|
|
267
|
+
ctx.track(store)
|
|
268
|
+
set_channel_store(store)
|
|
269
|
+
ctx.track(_Releaser(lambda: set_channel_store(None)))
|
|
270
|
+
values = [25.0 + (i % 100) * 0.01 for i in range(scale)]
|
|
271
|
+
|
|
272
|
+
def stream_one() -> None:
|
|
273
|
+
with channels_mod.stream("dmm.voltage") as sink:
|
|
274
|
+
for v in values:
|
|
275
|
+
sink.write(v)
|
|
276
|
+
|
|
277
|
+
return stream_one
|
|
278
|
+
|
|
279
|
+
return setup
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
# ---------------------------------------------------------------------------
|
|
283
|
+
# Files — object store + catalog daemon
|
|
284
|
+
# ---------------------------------------------------------------------------
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _acquire_catalog(ctx: BenchContext):
|
|
288
|
+
from testerkit.data.files.catalog_manager import acquire, release
|
|
289
|
+
|
|
290
|
+
files_dir = ctx.data_dir / "files"
|
|
291
|
+
acquire(files_dir)
|
|
292
|
+
ctx.track(_Releaser(lambda: release(files_dir)))
|
|
293
|
+
return files_dir
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _setup_files_write(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
297
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
298
|
+
from testerkit.data.files.store import FileStore
|
|
299
|
+
|
|
300
|
+
_acquire_catalog(ctx)
|
|
301
|
+
store = FileStore(_data_dir=ctx.data_dir)
|
|
302
|
+
sid = uuid4().hex
|
|
303
|
+
payload = b"x" * (100 * 1024) # 100 KB blobs
|
|
304
|
+
|
|
305
|
+
def write_n() -> None:
|
|
306
|
+
for _ in range(scale):
|
|
307
|
+
store.write(f"blob_{uuid4().hex[:8]}", payload, session_id=sid)
|
|
308
|
+
|
|
309
|
+
return write_n
|
|
310
|
+
|
|
311
|
+
return setup
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _setup_files_resolve(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
315
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
316
|
+
from testerkit.data.files.catalog_manager import resolve_uri
|
|
317
|
+
from testerkit.data.files.store import FileStore
|
|
318
|
+
|
|
319
|
+
files_dir = _acquire_catalog(ctx)
|
|
320
|
+
store = FileStore(_data_dir=ctx.data_dir)
|
|
321
|
+
uri = store.write("resolveme", b"x" * 256, session_id=uuid4().hex)
|
|
322
|
+
|
|
323
|
+
def resolve() -> object:
|
|
324
|
+
return resolve_uri(files_dir, uri)
|
|
325
|
+
|
|
326
|
+
return resolve
|
|
327
|
+
|
|
328
|
+
return setup
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def _setup_files_list(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
332
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
333
|
+
from testerkit.data.files.catalog_manager import list_recent
|
|
334
|
+
from testerkit.data.files.store import FileStore
|
|
335
|
+
|
|
336
|
+
files_dir = _acquire_catalog(ctx)
|
|
337
|
+
store = FileStore(_data_dir=ctx.data_dir)
|
|
338
|
+
sid = uuid4().hex
|
|
339
|
+
for i in range(scale):
|
|
340
|
+
store.write(f"list_{i}_{uuid4().hex[:6]}", b"y" * 128, session_id=sid)
|
|
341
|
+
|
|
342
|
+
def listing() -> object:
|
|
343
|
+
return list_recent(files_dir, 50)
|
|
344
|
+
|
|
345
|
+
return listing
|
|
346
|
+
|
|
347
|
+
return setup
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _setup_files_stream_raw(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
351
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
352
|
+
from testerkit.data.files.store import FileStore
|
|
353
|
+
|
|
354
|
+
store = FileStore(_data_dir=ctx.data_dir)
|
|
355
|
+
sid = uuid4().hex
|
|
356
|
+
chunk = b"a" * (64 * 1024) # 64 KB chunks
|
|
357
|
+
|
|
358
|
+
def stream_one() -> None:
|
|
359
|
+
sink = store.open_stream(name=f"raw_{uuid4().hex[:8]}", format="raw", session_id=sid)
|
|
360
|
+
for _ in range(scale):
|
|
361
|
+
sink.write(chunk)
|
|
362
|
+
sink.close()
|
|
363
|
+
|
|
364
|
+
return stream_one
|
|
365
|
+
|
|
366
|
+
return setup
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _setup_channels_block(scale: int) -> Callable[[BenchContext], Callable[[], object]]:
|
|
370
|
+
"""The high-rate channel path: one array (waveform block) per write —
|
|
371
|
+
one RPC carries ``scale`` points, vs one RPC per scalar in ``channels.write``."""
|
|
372
|
+
|
|
373
|
+
def setup(ctx: BenchContext) -> Callable[[], object]:
|
|
374
|
+
from testerkit.data.channels.store import ChannelStore
|
|
375
|
+
|
|
376
|
+
store = ChannelStore(ctx.data_dir, uuid4(), flush_threshold=100, serve=True)
|
|
377
|
+
store.open()
|
|
378
|
+
ctx.track(store)
|
|
379
|
+
block = [25.0 + (i % 100) * 0.01 for i in range(scale)]
|
|
380
|
+
|
|
381
|
+
def write_block() -> None:
|
|
382
|
+
store.write("scope.ch1", block, sample_interval=1e-6)
|
|
383
|
+
|
|
384
|
+
return write_block
|
|
385
|
+
|
|
386
|
+
return setup
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
# ---------------------------------------------------------------------------
|
|
390
|
+
# Case registry — sweep scales (unit) and writers (concurrency)
|
|
391
|
+
# ---------------------------------------------------------------------------
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
class _Op(NamedTuple):
|
|
395
|
+
store: str
|
|
396
|
+
unit: str
|
|
397
|
+
factory: Callable[[int], Callable[[BenchContext], Callable[[], object]]]
|
|
398
|
+
fast_scales: list[int]
|
|
399
|
+
full_scales: list[int]
|
|
400
|
+
bytes_per_unit: int | None # set for byte-heavy ops → enables bytes/s
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# Write ops include a ``scale=1`` floor anchor so the per-call overhead is
|
|
404
|
+
# measured directly (not extrapolated). bytes_per_unit is set only where a
|
|
405
|
+
# record has a fixed byte size (channels samples, file blobs/chunks) — for
|
|
406
|
+
# those, the report shows bytes/s, which is the real throughput axis.
|
|
407
|
+
# Bytes per record, so every op reports a real byte rate. Events/runs are
|
|
408
|
+
# measured from a serialized sample; the rest are known fixed sizes.
|
|
409
|
+
_EVENT_BYTES = len(make_measurement(uuid4(), 0).model_dump_json().encode())
|
|
410
|
+
_RUN_BYTES = len(build_run(0).model_dump_json().encode())
|
|
411
|
+
_SCALAR_BYTES = 8 # one float sample / point
|
|
412
|
+
_ROW_BYTES = 256 # one summary/metadata row from a query
|
|
413
|
+
|
|
414
|
+
# Each scale is its own row; the smallest (often 1) is the single-call case
|
|
415
|
+
# — its rate is overhead-bound but a valid data point. Call overhead is the
|
|
416
|
+
# line's intercept; per-record is the slope.
|
|
417
|
+
_OPS: dict[str, _Op] = {
|
|
418
|
+
"events.emit": _Op(
|
|
419
|
+
"events", "events", _setup_events_emit, [1, 1000], [1, 100, 1000, 10000], _EVENT_BYTES
|
|
420
|
+
),
|
|
421
|
+
"events.query": _Op(
|
|
422
|
+
"events", "events", _setup_events_query, [100, 1000], [100, 1000, 10000], _EVENT_BYTES
|
|
423
|
+
),
|
|
424
|
+
"channels.write": _Op(
|
|
425
|
+
"channels",
|
|
426
|
+
"samples",
|
|
427
|
+
_setup_channels_write,
|
|
428
|
+
[1, 1000],
|
|
429
|
+
[1, 100, 1000, 10000],
|
|
430
|
+
_SCALAR_BYTES,
|
|
431
|
+
),
|
|
432
|
+
"channels.block": _Op(
|
|
433
|
+
"channels",
|
|
434
|
+
"points",
|
|
435
|
+
_setup_channels_block,
|
|
436
|
+
[1000, 10000],
|
|
437
|
+
[1000, 10000, 100000],
|
|
438
|
+
_SCALAR_BYTES,
|
|
439
|
+
),
|
|
440
|
+
"channels.query": _Op(
|
|
441
|
+
"channels", "samples", _setup_channels_query, [100, 1000], [100, 1000, 10000], _SCALAR_BYTES
|
|
442
|
+
),
|
|
443
|
+
"channels.stream": _Op(
|
|
444
|
+
"channels",
|
|
445
|
+
"samples",
|
|
446
|
+
_setup_channels_stream,
|
|
447
|
+
[1, 1000],
|
|
448
|
+
[1, 100, 1000, 10000],
|
|
449
|
+
_SCALAR_BYTES,
|
|
450
|
+
),
|
|
451
|
+
"runs.save": _Op("runs", "runs", _setup_runs_save, [1, 10], [1, 10, 100], _RUN_BYTES),
|
|
452
|
+
"runs.list": _Op("runs", "runs", _setup_runs_list, [10, 100], [10, 100, 1000], _ROW_BYTES),
|
|
453
|
+
"runs.steps": _Op("runs", "runs", _setup_runs_steps, [10, 100], [10, 100, 1000], _ROW_BYTES),
|
|
454
|
+
"files.write": _Op("files", "artifacts", _setup_files_write, [1, 10], [1, 10, 100], 100 * 1024),
|
|
455
|
+
"files.resolve": _Op("files", "files", _setup_files_resolve, [1], [1], _ROW_BYTES),
|
|
456
|
+
"files.list": _Op("files", "files", _setup_files_list, [10, 100], [10, 100, 1000], _ROW_BYTES),
|
|
457
|
+
"files.stream_raw": _Op(
|
|
458
|
+
"files", "chunks", _setup_files_stream_raw, [1, 64], [1, 64, 256], 64 * 1024
|
|
459
|
+
),
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
# Write ops get a concurrency sweep at one representative scale (which is
|
|
463
|
+
# also present in the 1-writer scale sweep, so speedup has a baseline).
|
|
464
|
+
_CONCURRENCY: dict[str, int] = {
|
|
465
|
+
"events.emit": 1000,
|
|
466
|
+
"channels.write": 1000,
|
|
467
|
+
"files.write": 10,
|
|
468
|
+
"runs.save": 10,
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def build_cases(tier: str = "fast") -> list[Workload]:
|
|
473
|
+
"""All cases for ``tier``: scale sweep (1 writer) + concurrency sweep."""
|
|
474
|
+
full = tier == "full"
|
|
475
|
+
writer_counts = [2, 4] if full else [2]
|
|
476
|
+
cases: list[Workload] = []
|
|
477
|
+
for op, spec in _OPS.items():
|
|
478
|
+
scales = spec.full_scales if full else spec.fast_scales
|
|
479
|
+
for scale in scales:
|
|
480
|
+
cases.append(
|
|
481
|
+
Workload(
|
|
482
|
+
op,
|
|
483
|
+
spec.store,
|
|
484
|
+
spec.unit,
|
|
485
|
+
scale,
|
|
486
|
+
writers=1,
|
|
487
|
+
setup=spec.factory(scale),
|
|
488
|
+
tier=tier,
|
|
489
|
+
bytes_per_unit=spec.bytes_per_unit,
|
|
490
|
+
)
|
|
491
|
+
)
|
|
492
|
+
for op, rep_scale in _CONCURRENCY.items():
|
|
493
|
+
spec = _OPS[op]
|
|
494
|
+
for w in writer_counts:
|
|
495
|
+
cases.append(
|
|
496
|
+
Workload(
|
|
497
|
+
op,
|
|
498
|
+
spec.store,
|
|
499
|
+
spec.unit,
|
|
500
|
+
rep_scale,
|
|
501
|
+
writers=w,
|
|
502
|
+
setup=None,
|
|
503
|
+
tier=tier,
|
|
504
|
+
bytes_per_unit=spec.bytes_per_unit,
|
|
505
|
+
)
|
|
506
|
+
)
|
|
507
|
+
return cases
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
id: generic_dmm
|
|
2
|
+
manufacturer: Generic
|
|
3
|
+
model: DMM
|
|
4
|
+
name: Generic Digital Multimeter
|
|
5
|
+
description: General-purpose DMM for DC/AC voltage, current, and resistance measurement
|
|
6
|
+
type: dmm
|
|
7
|
+
scaffold: true
|
|
8
|
+
channels:
|
|
9
|
+
'1':
|
|
10
|
+
terminals:
|
|
11
|
+
- hi
|
|
12
|
+
- lo
|
|
13
|
+
connector: binding_post
|
|
14
|
+
ground: shared
|
|
15
|
+
capabilities:
|
|
16
|
+
- function: dc_voltage
|
|
17
|
+
direction: input
|
|
18
|
+
signals:
|
|
19
|
+
voltage:
|
|
20
|
+
range:
|
|
21
|
+
min: 0.001
|
|
22
|
+
max: 1000
|
|
23
|
+
unit: V
|
|
24
|
+
- function: ac_voltage
|
|
25
|
+
direction: input
|
|
26
|
+
signals:
|
|
27
|
+
voltage:
|
|
28
|
+
range:
|
|
29
|
+
min: 0.001
|
|
30
|
+
max: 750
|
|
31
|
+
unit: V
|
|
32
|
+
attributes:
|
|
33
|
+
bandwidth:
|
|
34
|
+
range:
|
|
35
|
+
min: 3
|
|
36
|
+
max: 300000
|
|
37
|
+
unit: Hz
|
|
38
|
+
- function: dc_current
|
|
39
|
+
direction: input
|
|
40
|
+
signals:
|
|
41
|
+
current:
|
|
42
|
+
range:
|
|
43
|
+
min: 1.0e-07
|
|
44
|
+
max: 10
|
|
45
|
+
unit: A
|
|
46
|
+
- function: ac_current
|
|
47
|
+
direction: input
|
|
48
|
+
signals:
|
|
49
|
+
current:
|
|
50
|
+
range:
|
|
51
|
+
min: 1.0e-06
|
|
52
|
+
max: 10
|
|
53
|
+
unit: A
|
|
54
|
+
- function: resistance
|
|
55
|
+
direction: input
|
|
56
|
+
signals:
|
|
57
|
+
resistance:
|
|
58
|
+
range:
|
|
59
|
+
min: 0.01
|
|
60
|
+
max: 100000000
|
|
61
|
+
unit: ohm
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
id: generic_eload
|
|
2
|
+
manufacturer: Generic
|
|
3
|
+
model: Electronic Load
|
|
4
|
+
name: Generic DC Electronic Load
|
|
5
|
+
description: Programmable DC electronic load for sinking current from UUT outputs
|
|
6
|
+
type: eload
|
|
7
|
+
scaffold: true
|
|
8
|
+
channels:
|
|
9
|
+
'1':
|
|
10
|
+
terminals:
|
|
11
|
+
- hi
|
|
12
|
+
- lo
|
|
13
|
+
connector: binding_post
|
|
14
|
+
ground: shared
|
|
15
|
+
capabilities:
|
|
16
|
+
- function: dc_current
|
|
17
|
+
direction: input
|
|
18
|
+
signals:
|
|
19
|
+
current:
|
|
20
|
+
range:
|
|
21
|
+
min: 0
|
|
22
|
+
max: 30
|
|
23
|
+
unit: A
|
|
24
|
+
- function: dc_voltage
|
|
25
|
+
direction: input
|
|
26
|
+
signals:
|
|
27
|
+
voltage:
|
|
28
|
+
range:
|
|
29
|
+
min: 0
|
|
30
|
+
max: 150
|
|
31
|
+
unit: V
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
id: generic_fgen
|
|
2
|
+
manufacturer: Generic
|
|
3
|
+
model: Function Generator
|
|
4
|
+
name: Generic Function Generator
|
|
5
|
+
description: Arbitrary waveform and function generator for signal sourcing
|
|
6
|
+
type: fgen
|
|
7
|
+
scaffold: true
|
|
8
|
+
channels:
|
|
9
|
+
CH1:
|
|
10
|
+
terminals:
|
|
11
|
+
- signal
|
|
12
|
+
connector: bnc
|
|
13
|
+
ground: shared
|
|
14
|
+
CH2:
|
|
15
|
+
terminals:
|
|
16
|
+
- signal
|
|
17
|
+
connector: bnc
|
|
18
|
+
ground: shared
|
|
19
|
+
capabilities:
|
|
20
|
+
- function: waveform
|
|
21
|
+
direction: output
|
|
22
|
+
signals:
|
|
23
|
+
voltage:
|
|
24
|
+
range:
|
|
25
|
+
min: 0.001
|
|
26
|
+
max: 10
|
|
27
|
+
unit: Vpp
|
|
28
|
+
frequency:
|
|
29
|
+
range:
|
|
30
|
+
min: 0.001
|
|
31
|
+
max: 25000000
|
|
32
|
+
unit: Hz
|
|
33
|
+
attributes:
|
|
34
|
+
waveforms:
|
|
35
|
+
options:
|
|
36
|
+
- sine
|
|
37
|
+
- square
|
|
38
|
+
- triangle
|
|
39
|
+
- ramp
|
|
40
|
+
- pulse
|
|
41
|
+
- noise
|
|
42
|
+
- dc
|
|
43
|
+
channels:
|
|
44
|
+
- CH1
|
|
45
|
+
- CH2
|
|
46
|
+
- function: dc_voltage
|
|
47
|
+
direction: output
|
|
48
|
+
signals:
|
|
49
|
+
voltage:
|
|
50
|
+
range:
|
|
51
|
+
min: -10
|
|
52
|
+
max: 10
|
|
53
|
+
unit: V
|
|
54
|
+
channels:
|
|
55
|
+
- CH1
|
|
56
|
+
- CH2
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
id: generic_oscilloscope
|
|
2
|
+
manufacturer: Generic
|
|
3
|
+
model: Oscilloscope
|
|
4
|
+
name: Generic Digital Oscilloscope
|
|
5
|
+
description: Multi-channel oscilloscope for time-domain voltage waveform capture
|
|
6
|
+
type: scope
|
|
7
|
+
scaffold: true
|
|
8
|
+
channels:
|
|
9
|
+
CH1:
|
|
10
|
+
terminals:
|
|
11
|
+
- signal
|
|
12
|
+
connector: bnc
|
|
13
|
+
ground: shared
|
|
14
|
+
CH2:
|
|
15
|
+
terminals:
|
|
16
|
+
- signal
|
|
17
|
+
connector: bnc
|
|
18
|
+
ground: shared
|
|
19
|
+
CH3:
|
|
20
|
+
terminals:
|
|
21
|
+
- signal
|
|
22
|
+
connector: bnc
|
|
23
|
+
ground: shared
|
|
24
|
+
CH4:
|
|
25
|
+
terminals:
|
|
26
|
+
- signal
|
|
27
|
+
connector: bnc
|
|
28
|
+
ground: shared
|
|
29
|
+
capabilities:
|
|
30
|
+
- function: waveform
|
|
31
|
+
direction: input
|
|
32
|
+
signals:
|
|
33
|
+
voltage:
|
|
34
|
+
range:
|
|
35
|
+
min: 0.001
|
|
36
|
+
max: 400
|
|
37
|
+
unit: V
|
|
38
|
+
attributes:
|
|
39
|
+
bandwidth:
|
|
40
|
+
value: 50000000
|
|
41
|
+
unit: Hz
|
|
42
|
+
sample_rate:
|
|
43
|
+
value: 1000000000
|
|
44
|
+
unit: Sa/s
|
|
45
|
+
channels: CH[1:4]
|