Un séquenceur de test exécute des phases ordonnées, décide lesquelles ignorer, collecte les entrées opérateur et enregistre le résultat. Ce guide montre comment en construire un avec OpenHTF et enregistrer les résultats structurés dans TofuPilot.
Prérequis
- Python 3.8+
- Compte TofuPilot et clé API
- OpenHTF installé :
pip install openhtf tofupilot
Ce que fait un séquenceur de test
Un séquenceur est plus qu'une liste d'étapes de test. Il contrôle :
| Responsabilité | Exemple |
|---|---|
| Ordonnancement des phases | Mise sous tension avant les tests fonctionnels |
| Logique de saut | Ignorer la calibration RF si la variante matérielle n'a pas d'antenne |
| Interaction opérateur | Demander le numéro de série, confirmer l'inspection visuelle |
| Agrégation des résultats | Un seul résultat réussite/échec à partir de toutes les phases |
| Teardown | Mise hors tension même quand une phase échoue |
OpenHTF correspond directement à ces responsabilités. Chaque phase est une fonction Python. L'objet test les séquence, évalue les mesures et transmet les résultats à TofuPilot.
Étape 1 : Définir les phases
Chaque phase est une fonction décorée. Ajoutez des mesures pour capturer les résultats numériques avec des limites.
import openhtf as htffrom openhtf.util import units@htf.measures( htf.Measurement('supply_voltage') .in_range(minimum=4.75, maximum=5.25) .with_units(units.VOLT))def power_on_test(test): voltage = read_supply_voltage() test.measurements.supply_voltage = voltage@htf.measures( htf.Measurement('loop_back_result').equals(True))def uart_loopback_test(test): result = send_uart_loopback() test.measurements.loop_back_result = result@htf.measures( htf.Measurement('output_current') .in_range(minimum=0.9, maximum=1.1) .with_units(units.AMPERE))def load_test(test): current = measure_output_current() test.measurements.output_current = currentÉtape 2 : Utiliser les PhaseGroups pour le setup et le teardown
PhaseGroup garantit que le teardown s'exécute même quand une phase de test échoue. C'est essentiel pour les tests matériels qui maintiennent des relais, alimentations ou montages.
from openhtf import PhaseGroupdef power_on(test): enable_power_supply() test.logger.info('Alimentation activée')def power_off(test): # S'exécute même si functional_tests échoue disable_power_supply() test.logger.info('Alimentation désactivée')# Construire le groupe : setup -> main -> teardowntest_group = PhaseGroup( setup=[power_on], main=[power_on_test, uart_loopback_test, load_test], teardown=[power_off],)Les phases teardown s'exécutent quel que soit le résultat. Les échecs de setup ignorent main mais exécutent quand même teardown.
Étape 3 : Injecter des plugs pour l'accès matériel
Les plugs encapsulent la communication avec les instruments ou les montages. Injectez-les avec @htf.plug.
import openhtf as htfclass PowerSupplyPlug(htf.plugs.BasePlug): def setUp(self): self._conn = open_supply_connection() def read_voltage(self): return self._conn.query('MEAS:VOLT?') def tearDown(self): self._conn.close()from openhtf.util import unitsfrom plugs.supply import PowerSupplyPlug@htf.plug(supply=PowerSupplyPlug)@htf.measures( htf.Measurement('supply_voltage') .in_range(minimum=4.75, maximum=5.25) .with_units(units.VOLT))def power_on_test(test, supply): test.measurements.supply_voltage = supply.read_voltage()Notez la forme @htf.plug(name=PlugClass). N'utilisez pas les annotations de type pour l'injection de plugs.
Étape 4 : Ajouter la logique de saut pour l'exécution conditionnelle
Retournez htf.PhaseResult.SKIP pour ignorer une phase selon la variante du DUT.
import openhtf as htf@htf.measures( htf.Measurement('rf_output_power') .in_range(minimum=18.0, maximum=22.0))def rf_calibration_test(test): if not has_rf_module(): test.logger.info('Aucun module RF détecté, saut de la calibration RF') return htf.PhaseResult.SKIP power = measure_rf_output() test.measurements.rf_output_power = powerGardez les décisions de saut basées sur les données plutôt que codées en dur. Lisez la configuration matérielle dans une phase précédente ou depuis un fichier de configuration.
Étape 5 : Construire des séquences multi-SKU
Les différentes variantes de produits nécessitent des ensembles de phases différents. Composez les séquences dynamiquement à partir d'une table de SKU.
from openhtf import PhaseGroup# Phases de base exécutées sur chaque SKUBASE_PHASES = [power_on_test, uart_loopback_test]# Phases supplémentaires par SKUSKU_PHASES = { 'MODEL_A': [load_test], 'MODEL_B': [load_test, rf_calibration_test], 'MODEL_C': [], # base uniquement}def build_sequence(sku: str): extra = SKU_PHASES.get(sku, []) return PhaseGroup( setup=[power_on], main=BASE_PHASES + extra, teardown=[power_off], )Étape 6 : Enregistrer les résultats dans TofuPilot
Encapsulez l'exécution du test avec TofuPilot. Il capture chaque phase, mesure et résultat.
import openhtf as htffrom tofupilot.openhtf import TofuPilotdef main(sku: str = 'MODEL_A'): sequence = build_sequence(sku) test = htf.Test( sequence, test_name=f'Functional Test {sku}', ) with TofuPilot(test): test.execute(test_start=lambda: input('Entrez le numéro de série : '))if __name__ == '__main__': import sys sku = sys.argv[1] if len(sys.argv) > 1 else 'MODEL_A' main(sku)Référence des résultats de phase
| Valeur de retour | Effet |
|---|---|
htf.PhaseResult.CONTINUE | Phase réussie, continuer la séquence |
htf.PhaseResult.SKIP | Phase ignorée, continuer la séquence |
htf.PhaseResult.STOP | Phase échouée, arrêter la séquence (exécuter le teardown) |
| Mesure hors limites | La phase échoue automatiquement |
| Exception non gérée | La phase échoue, le teardown s'exécute |
Exemple complet
import sysimport openhtf as htffrom openhtf import PhaseGroupfrom openhtf.util import unitsfrom tofupilot.openhtf import TofuPilot# -- Phases --@htf.measures( htf.Measurement('supply_voltage') .in_range(minimum=4.75, maximum=5.25) .with_units(units.VOLT))def power_on_test(test): test.measurements.supply_voltage = read_supply_voltage()@htf.measures( htf.Measurement('uart_loopback').equals(True))def uart_loopback_test(test): test.measurements.uart_loopback = send_uart_loopback()@htf.measures( htf.Measurement('output_current') .in_range(minimum=0.9, maximum=1.1) .with_units(units.AMPERE))def load_test(test): test.measurements.output_current = measure_output_current()@htf.measures( htf.Measurement('rf_output_power') .in_range(minimum=18.0, maximum=22.0))def rf_calibration_test(test): if not has_rf_module(): return htf.PhaseResult.SKIP test.measurements.rf_output_power = measure_rf_output()def power_on(test): enable_power_supply()def power_off(test): disable_power_supply()# -- Construction de la séquence --BASE_PHASES = [power_on_test, uart_loopback_test]SKU_PHASES = { 'MODEL_A': [load_test], 'MODEL_B': [load_test, rf_calibration_test],}def build_sequence(sku): return PhaseGroup( setup=[power_on], main=BASE_PHASES + SKU_PHASES.get(sku, []), teardown=[power_off], )# -- Point d'entrée --def main(): sku = sys.argv[1] if len(sys.argv) > 1 else 'MODEL_A' test = htf.Test(build_sequence(sku), test_name=f'Functional Test {sku}') with TofuPilot(test): test.execute(test_start=lambda: input('Entrez le numéro de série : '))if __name__ == '__main__': main()