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,42 @@
|
|
|
1
|
+
"""Runner-neutral instrument-event emission.
|
|
2
|
+
|
|
3
|
+
Each runner emits ``InstrumentConnected`` events for the instruments
|
|
4
|
+
its session resolved. The shape is the same across runners — only
|
|
5
|
+
the source of the records differs (pytest reads
|
|
6
|
+
``get_instrument_records()`` from the active state; OpenHTF reads
|
|
7
|
+
its own plug registry, etc.).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from testerkit.data.events import InstrumentConnected
|
|
15
|
+
from testerkit.execution.run_scope import (
|
|
16
|
+
RunScope,
|
|
17
|
+
instrument_cal_fields,
|
|
18
|
+
instrument_info_fields,
|
|
19
|
+
)
|
|
20
|
+
from testerkit.models.instrument import InstrumentRecord
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def emit_instrument_events(
|
|
24
|
+
logger: RunScope,
|
|
25
|
+
event_log: Any,
|
|
26
|
+
records: dict[str, InstrumentRecord],
|
|
27
|
+
) -> None:
|
|
28
|
+
"""Emit one ``InstrumentConnected`` event per resolved instrument."""
|
|
29
|
+
for role, rec in records.items():
|
|
30
|
+
event = InstrumentConnected(
|
|
31
|
+
session_id=logger._session_id,
|
|
32
|
+
run_id=logger.test_run.id,
|
|
33
|
+
role=role,
|
|
34
|
+
instrument_id=rec.instrument_id,
|
|
35
|
+
driver=rec.driver,
|
|
36
|
+
resource=rec.resource,
|
|
37
|
+
protocol=rec.protocol,
|
|
38
|
+
**instrument_info_fields(rec),
|
|
39
|
+
**instrument_cal_fields(rec),
|
|
40
|
+
mocked=rec.mocked,
|
|
41
|
+
)
|
|
42
|
+
event_log.emit(event)
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""Limit derivation from part specifications.
|
|
2
|
+
|
|
3
|
+
This module provides functions to derive test limits from part
|
|
4
|
+
characteristics and spec bands, including guardband application.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from testerkit.models.capability import SpecBand
|
|
10
|
+
from testerkit.models.enums import Comparator
|
|
11
|
+
from testerkit.models.part import PartCharacteristic
|
|
12
|
+
from testerkit.models.test_config import Limit
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def derive_limit(
|
|
16
|
+
char: PartCharacteristic,
|
|
17
|
+
conditions: dict[str, Any] | None = None,
|
|
18
|
+
guardband_pct: float = 0.0,
|
|
19
|
+
comparator: Comparator = Comparator.GELE,
|
|
20
|
+
limit_low: float | None = None,
|
|
21
|
+
limit_high: float | None = None,
|
|
22
|
+
char_id: str | None = None,
|
|
23
|
+
) -> Limit:
|
|
24
|
+
"""Derive test limit from characteristic at given conditions.
|
|
25
|
+
|
|
26
|
+
This function:
|
|
27
|
+
1. Finds the SpecBand matching the given conditions
|
|
28
|
+
2. Calculates bounds from value ± accuracy (or explicit limits)
|
|
29
|
+
3. Applies guardband to tighten the limits
|
|
30
|
+
4. Sets characteristic_id for structured traceability
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
char: The part characteristic containing spec values.
|
|
34
|
+
conditions: Operating point parameters (e.g., {"temperature": 25}).
|
|
35
|
+
guardband_pct: Percentage to tighten limits (0-100).
|
|
36
|
+
comparator: How to compare measured value against limits.
|
|
37
|
+
limit_low: Explicit low limit override.
|
|
38
|
+
limit_high: Explicit high limit override.
|
|
39
|
+
char_id: Optional characteristic ID for characteristic_id traceability.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
A Limit object with derived low/high bounds and characteristic_id.
|
|
43
|
+
|
|
44
|
+
Raises:
|
|
45
|
+
ValueError: If no SpecBand matches the given conditions.
|
|
46
|
+
"""
|
|
47
|
+
params = conditions or {}
|
|
48
|
+
|
|
49
|
+
# Find matching spec band
|
|
50
|
+
band = char.get_spec_at(params)
|
|
51
|
+
if band is None:
|
|
52
|
+
cond_str = ", ".join(f"{k}={v}" for k, v in params.items())
|
|
53
|
+
avail = [dict(s.when) for s in char.bands]
|
|
54
|
+
raise ValueError(f"No spec band matches: {cond_str}. Available when clauses: {avail}")
|
|
55
|
+
|
|
56
|
+
# Calculate spec bounds from SpecBand
|
|
57
|
+
spec_low, spec_high = _calculate_bounds(band, comparator, limit_low, limit_high)
|
|
58
|
+
|
|
59
|
+
# Apply guardband (tighten limits)
|
|
60
|
+
final_low, final_high = _apply_guardband(spec_low, spec_high, guardband_pct, comparator.value)
|
|
61
|
+
|
|
62
|
+
characteristic_id = char_id
|
|
63
|
+
spec_ref = _build_spec_ref(char, params)
|
|
64
|
+
|
|
65
|
+
nominal = float(band.value) if isinstance(band.value, (int, float)) else None
|
|
66
|
+
|
|
67
|
+
return Limit(
|
|
68
|
+
low=final_low,
|
|
69
|
+
high=final_high,
|
|
70
|
+
nominal=nominal,
|
|
71
|
+
unit=char.unit or "",
|
|
72
|
+
comparator=comparator,
|
|
73
|
+
characteristic_id=characteristic_id,
|
|
74
|
+
spec_ref=spec_ref,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _calculate_bounds(
|
|
79
|
+
band: SpecBand,
|
|
80
|
+
comparator: Comparator,
|
|
81
|
+
limit_low: float | None,
|
|
82
|
+
limit_high: float | None,
|
|
83
|
+
) -> tuple[float | None, float | None]:
|
|
84
|
+
"""Calculate spec bounds from a SpecBand.
|
|
85
|
+
|
|
86
|
+
Priority:
|
|
87
|
+
1. Explicit limit_low/limit_high overrides
|
|
88
|
+
2. SpecBand.value ± SpecBand.accuracy (via total_uncertainty)
|
|
89
|
+
3. SpecBand.value alone (exact match)
|
|
90
|
+
|
|
91
|
+
For single-sided comparators (LE, GE), only the relevant bound is set.
|
|
92
|
+
"""
|
|
93
|
+
# Explicit limits take precedence
|
|
94
|
+
if limit_low is not None or limit_high is not None:
|
|
95
|
+
return limit_low, limit_high
|
|
96
|
+
|
|
97
|
+
if band.value is None or not isinstance(band.value, (int, float)):
|
|
98
|
+
return None, None
|
|
99
|
+
|
|
100
|
+
val: float = band.value
|
|
101
|
+
|
|
102
|
+
# Single-sided comparators
|
|
103
|
+
if comparator in (Comparator.LE, Comparator.LT):
|
|
104
|
+
return None, val
|
|
105
|
+
if comparator in (Comparator.GE, Comparator.GT):
|
|
106
|
+
return val, None
|
|
107
|
+
|
|
108
|
+
# Range comparator: derive from accuracy
|
|
109
|
+
if band.accuracy is not None:
|
|
110
|
+
uncertainty = band.accuracy.total_uncertainty(val, val)
|
|
111
|
+
return val - uncertainty, val + uncertainty
|
|
112
|
+
|
|
113
|
+
# No accuracy — exact value
|
|
114
|
+
return val, val
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _apply_guardband(
|
|
118
|
+
spec_low: float | None,
|
|
119
|
+
spec_high: float | None,
|
|
120
|
+
guardband_pct: float,
|
|
121
|
+
comparator: str,
|
|
122
|
+
) -> tuple[float | None, float | None]:
|
|
123
|
+
"""Apply guardband to tighten spec limits.
|
|
124
|
+
|
|
125
|
+
Guardband reduces the acceptable range by moving limits inward.
|
|
126
|
+
"""
|
|
127
|
+
if guardband_pct == 0.0:
|
|
128
|
+
return spec_low, spec_high
|
|
129
|
+
|
|
130
|
+
range_comparators = {"GELE", "GELT", "GTLE", "GTLT"}
|
|
131
|
+
|
|
132
|
+
if comparator in range_comparators and spec_low is not None and spec_high is not None:
|
|
133
|
+
range_size = spec_high - spec_low
|
|
134
|
+
guardband_amount = range_size * guardband_pct / 100.0 / 2.0
|
|
135
|
+
return spec_low + guardband_amount, spec_high - guardband_amount
|
|
136
|
+
|
|
137
|
+
if comparator in {"LE", "LT"} and spec_high is not None:
|
|
138
|
+
guardband_amount = abs(spec_high) * guardband_pct / 100.0
|
|
139
|
+
return spec_low, spec_high - guardband_amount
|
|
140
|
+
|
|
141
|
+
if comparator in {"GE", "GT"} and spec_low is not None:
|
|
142
|
+
guardband_amount = abs(spec_low) * guardband_pct / 100.0
|
|
143
|
+
return spec_low + guardband_amount, spec_high
|
|
144
|
+
|
|
145
|
+
return spec_low, spec_high
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _build_spec_ref(char: PartCharacteristic, conditions: dict[str, Any]) -> str:
|
|
149
|
+
"""Build a spec reference string for traceability."""
|
|
150
|
+
base_ref = char.datasheet_ref or "spec"
|
|
151
|
+
if conditions:
|
|
152
|
+
cond_str = ", ".join(f"{k}={v}" for k, v in sorted(conditions.items()))
|
|
153
|
+
return f"{base_ref} @ {cond_str}"
|
|
154
|
+
return base_ref
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Runner-neutral run-metadata assembly.
|
|
2
|
+
|
|
3
|
+
:class:`RunScope` takes a fat kwargs dict — UUT serial, station
|
|
4
|
+
identity, part identity, fixture id, environment capture, project
|
|
5
|
+
name, profile name + facets, session inputs, instrument records, etc.
|
|
6
|
+
The dict is the same regardless of which runner is driving; only the
|
|
7
|
+
*sources* differ (pytest reads CLI options + session fixtures, OpenHTF
|
|
8
|
+
reads its config object, etc.).
|
|
9
|
+
|
|
10
|
+
This module owns the assembly: :func:`build_run_metadata` takes
|
|
11
|
+
already-resolved inputs and returns the kwargs dict ready to hand to
|
|
12
|
+
:class:`RunScope`. Each runner's plugin gathers the inputs in its
|
|
13
|
+
own way and calls in.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from testerkit.environment import capture_environment
|
|
22
|
+
from testerkit.execution._git import get_project_name
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_run_metadata(
|
|
26
|
+
*,
|
|
27
|
+
uut_serial: str | None,
|
|
28
|
+
uut_part_number: str | None = None,
|
|
29
|
+
uut_revision: str | None = None,
|
|
30
|
+
uut_lot_number: str | None = None,
|
|
31
|
+
station_id: str | None = None,
|
|
32
|
+
station_config: Any | None = None,
|
|
33
|
+
fixture_config: Any | None = None,
|
|
34
|
+
part: Any | None = None,
|
|
35
|
+
operator_id: str | None = None,
|
|
36
|
+
project_dir: Path,
|
|
37
|
+
data_dir: str | None = None,
|
|
38
|
+
test_phase: str | None = None,
|
|
39
|
+
profile_name: str | None = None,
|
|
40
|
+
profile_facets: dict[str, str] | None = None,
|
|
41
|
+
session_inputs: dict[str, str] | None = None,
|
|
42
|
+
instrument_records: dict[str, Any] | None = None,
|
|
43
|
+
) -> dict[str, Any]:
|
|
44
|
+
"""Build the kwargs dict :class:`RunScope` expects.
|
|
45
|
+
|
|
46
|
+
Resolves derived fields (part info from the active ``part``,
|
|
47
|
+
station fields from ``station_config``, environment capture, git
|
|
48
|
+
project name) so the runner's plugin doesn't have to. ``uut_part_number``
|
|
49
|
+
and ``uut_revision`` fall back to the active part when not
|
|
50
|
+
supplied explicitly.
|
|
51
|
+
"""
|
|
52
|
+
# Part identity from the active Part
|
|
53
|
+
part_id = part_name = part_revision = None
|
|
54
|
+
if part is not None:
|
|
55
|
+
part_id = part.id
|
|
56
|
+
part_name = part.name
|
|
57
|
+
part_revision = part.revision
|
|
58
|
+
|
|
59
|
+
# Fixture id
|
|
60
|
+
fixture_id = None
|
|
61
|
+
if fixture_config is not None:
|
|
62
|
+
fixture_id = getattr(fixture_config, "id", None) or getattr(fixture_config, "name", None)
|
|
63
|
+
|
|
64
|
+
# Station info
|
|
65
|
+
station_name = station_type = station_location = None
|
|
66
|
+
if station_config is not None:
|
|
67
|
+
station_name = station_config.name
|
|
68
|
+
station_type = getattr(station_config, "station_type", None) or getattr(
|
|
69
|
+
station_config, "type", None
|
|
70
|
+
)
|
|
71
|
+
station_location = station_config.location
|
|
72
|
+
|
|
73
|
+
# UUT defaults from part spec
|
|
74
|
+
if uut_part_number is None and part is not None:
|
|
75
|
+
uut_part_number = part.part_number
|
|
76
|
+
if uut_revision is None and part is not None:
|
|
77
|
+
uut_revision = part.revision
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
"uut_serial": uut_serial,
|
|
81
|
+
"uut_part_number": uut_part_number,
|
|
82
|
+
"uut_revision": uut_revision,
|
|
83
|
+
"uut_lot_number": uut_lot_number,
|
|
84
|
+
"station_id": station_id,
|
|
85
|
+
"station_name": station_name,
|
|
86
|
+
"station_type": station_type,
|
|
87
|
+
"station_location": station_location,
|
|
88
|
+
"operator_id": operator_id,
|
|
89
|
+
"part_id": part_id,
|
|
90
|
+
"part_name": part_name,
|
|
91
|
+
"part_revision": part_revision,
|
|
92
|
+
"fixture_id": fixture_id,
|
|
93
|
+
"project_name": get_project_name(project_dir),
|
|
94
|
+
"project_dir": project_dir,
|
|
95
|
+
"data_dir": data_dir,
|
|
96
|
+
"test_phase": test_phase,
|
|
97
|
+
"profile": profile_name,
|
|
98
|
+
"profile_facets": dict(profile_facets or {}),
|
|
99
|
+
"session_inputs": dict(session_inputs or {}),
|
|
100
|
+
"instruments": instrument_records,
|
|
101
|
+
"environment": capture_environment(),
|
|
102
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Runner-neutral mock installation.
|
|
2
|
+
|
|
3
|
+
Given a merged dict of ``{target: MockEntry}`` and runner-specific
|
|
4
|
+
callbacks for fixture resolution + cleanup registration, install
|
|
5
|
+
each mock via :func:`unittest.mock.patch.object` and bind teardown
|
|
6
|
+
to the runner's per-test cleanup hook.
|
|
7
|
+
|
|
8
|
+
The runner provides:
|
|
9
|
+
|
|
10
|
+
* ``resolve_fixture(name) -> Any | None`` — return the fixture value,
|
|
11
|
+
or ``None`` if it doesn't exist on this test (callback emits a
|
|
12
|
+
warning and skips).
|
|
13
|
+
* ``register_cleanup(callable)`` — register a zero-arg cleanup that
|
|
14
|
+
fires when the test completes (pytest: ``request.addfinalizer``;
|
|
15
|
+
unittest: ``addCleanup``; OpenHTF: phase teardown).
|
|
16
|
+
|
|
17
|
+
This module owns the unittest.mock interaction so each runner can
|
|
18
|
+
share the same mock-lifecycle without copy-pasting.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import warnings
|
|
24
|
+
from collections.abc import Callable
|
|
25
|
+
from typing import Any
|
|
26
|
+
|
|
27
|
+
from testerkit.models.test_config import MockEntry
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def install_mocks(
|
|
31
|
+
by_target: dict[str, MockEntry],
|
|
32
|
+
*,
|
|
33
|
+
resolve_fixture: Callable[[str], Any],
|
|
34
|
+
register_cleanup: Callable[[Callable[[], None]], None],
|
|
35
|
+
fixture_lookup_error: type[Exception] = LookupError,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Install each mock and register its teardown.
|
|
38
|
+
|
|
39
|
+
``by_target`` is the de-duplicated merged map (later scopes
|
|
40
|
+
overwrite earlier ones by target — caller does the cascade).
|
|
41
|
+
Each :class:`MockEntry`'s ``target`` is ``"<fixture>.<attr>"``;
|
|
42
|
+
the callback resolves the fixture, this function patches the
|
|
43
|
+
attribute and binds cleanup. Missing fixtures emit a warning
|
|
44
|
+
(caught via ``fixture_lookup_error`` from the runner's host) and
|
|
45
|
+
skip — a typo in ``target`` shouldn't fail the run, just record.
|
|
46
|
+
"""
|
|
47
|
+
from unittest.mock import patch as _patch
|
|
48
|
+
|
|
49
|
+
for target, entry in by_target.items():
|
|
50
|
+
fixture_name, _, attr = target.partition(".")
|
|
51
|
+
try:
|
|
52
|
+
fixture_value = resolve_fixture(fixture_name)
|
|
53
|
+
except fixture_lookup_error:
|
|
54
|
+
warnings.warn(
|
|
55
|
+
f"testerkit_mocks target {target!r}: fixture {fixture_name!r} not "
|
|
56
|
+
"found on this test — mock skipped. Check the entry `target:` "
|
|
57
|
+
"matches a fixture in the test's signature.",
|
|
58
|
+
stacklevel=2,
|
|
59
|
+
)
|
|
60
|
+
continue
|
|
61
|
+
patcher = _patch.object(fixture_value, attr, **entry.patch_kwargs())
|
|
62
|
+
patcher.start()
|
|
63
|
+
register_cleanup(patcher.stop)
|