CommunityRédaction et éditiongithub.com

PedroGilo12/claude-skills

Génère une suite de tests unitaires pour les fichiers .c/.h de firmware embarqué, basée sur les tests par spécification de Maurício Aniche (partitions → limites → cas → couverture). Détecte l'environnement : Zephyr utilise twister + ztest + FFF sur native_sim ; ESP-IDF utilise Unity natif en construction hôte + gcovr ; autrement suit le framework de test existant du projet (Unity, CMock, Ceedling, GoogleTest…) ou s'arrête et demande s'il n'y en a pas. À utiliser TOUJOURS lorsque l'utilisateur demande de générer/écrire/créer des tests unitaires pour des modules .c/.h — par exemple "écris les tests unitaires pour motor_ctrl.c", "crée des tests unitaires pour le uart_driver", "teste cette fonction avec des partitions/limites", "couverture à 95% sur ce module", ou en portugais "gera os testes unitários de motor_ctrl.c", "cria unit test pro uart_driver", "cobre esses .c/.h com testes". Se déclenche sans le mot "skill" et sans nommer le framework. Ne PAS utiliser pour les tests d'intégration, e2e, HIL, système ou Robot Framework ; ni pour scanner le projet à la recherche de cibles (suite…)

Qu'est-ce que claude-skills ?

Ce skill génère une suite complète de tests unitaires pour des fichiers .c/.h de firmware embarqué en suivant les 7 étapes des tests par spécification de Maurício Aniche, issues de son livre *Effective Software Testing* (chapitre 2). L'étape clé est le partitionnement correct du domaine de chaque fonction, qui permet de dériver les limites, de concevoir la plus petite suite de tests détectant encore les bugs, de l'automatiser dans le framework approprié du projet et d'itérer jusqu'à atteindre l'objectif de couverture. Le skill détecte automatiquement l'environnement : pour Zephyr, il génère des tests avec twister, ztest et FFF sur native_sim ; pour ESP-IDF, il utilise Unity natif en construction hôte avec gcovr ; pour d'autres environnements, il suit le framework de test unitaire existant (Unity, CMock, Ceedling, GoogleTest) ou demande à l'utilisateur. Il est conçu pour être autonome : il génère, exécute et rapporte les résultats sans demander de confirmation, sauf dans deux cas spécifiques où il s'arrête pour demander à l'utilisateur. Le skill garantit une traçabilité complète entre les partitions/limites et les tests, avec un objectif de couverture par défaut de 95 % pour les fonctions et les lignes, et 90 % pour les branches.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/PedroGilo12/claude-skills/tree/HEAD/plugins/firmware-testing/skills/embedded-unit-testing

Installed? Explore more Rédaction et édition skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Tests unitaires embarqués (par spécification)

Génère une suite de tests unitaires pour un ensemble de fichiers .c/.h remis par l'utilisateur, en utilisant les 7 étapes des tests par spécification de Maurício Aniche (Effective Software Testing, ch. 2). Le jugement qui fait de ceci un skill : bien partitionner le domaine de chaque fonction (l'étape la plus précieuse), dériver les limites, concevoir la plus petite suite qui détecte encore les bugs, l'automatiser dans le bon framework du projet et itérer jusqu'à ce que la couverture atteigne la cible.

Lisez references/specification-based-testing.md avant la Phase 3 — c'est l'étalonnage pour savoir comment penser les partitions et les limites, et les pragmatiques qui évitent une suite gonflée ou fragile.

Invariants de périmètre (à lire en premier)

  • Sortie en anglais. Tout ce que ce skill produit est en anglais, même lorsque l'utilisateur écrit dans une autre langue : code de test, noms de test, commentaires, catalogue, annotations Covers:, messages de saut/ignoré, la déclaration de chemin et le résumé final.
  • Tests unitaires uniquement. Ne jamais générer de tests d'intégration, e2e, système, HIL ou Robot Framework. La granularité est la fonction/module isolé, avec les dépendances simulées/moquées.
  • Seulement les fichiers donnés. La suite concerne l'ensemble .c/.h passé par l'utilisateur. Ne pas scanner le projet pour d'autres cibles ni tester les modules voisins de votre propre chef.
  • Ne jamais modifier les fichiers cibles — pas temporairement, pas "pour tester et revenir en arrière". La suite compile les fichiers .c originaux, sans copie dans le répertoire de test. Pour atteindre quelque chose de difficile, utilisez l'API publique, les fausses dépendances ou un binaire de test séparé.
  • Autonome. Générer, exécuter et rapporter sans demander de confirmation pour exécuter. Il y a deux exceptions où vous vous arrêtez et demandez : (a) Chemin C (ni Zephyr ni ESP-IDF) où vous ne pouvez identifier aucun framework de test unitaire (Phase 0) ; (b) toutes les 2 itérations de couverture sans atteindre la cible (Phase 8).
  • Aucun rapport rédigé par vous. Vous n'écrivez pas de rapport de résultats. Exécutez les outils et pointez vers les artefacts qu'ils génèrent (HTML de couverture, sortie de l'exécuteur). Le "résultat" est ce que l'outil émet, pas votre prose.
  • Objectif de couverture : fonction ≥ 95 %, ligne ≥ 95 %, branche ≥ 90 % (95/95/90) par défaut. Une fois la cible atteinte, arrêtez d'itérer et listez les lignes/branches non couvertes restantes. Allez vers 100/100/100 seulement si l'utilisateur le demande. En dessous de la cible seulement lorsque ce qui manque est prouvablement inaccessible (bruit de log/assert, limitation de testabilité du module), avec chaque ligne/branche restante justifiée.

Le flux (7 étapes d'Aniche → 9 phases)

Les 7 étapes ne sont pas un pipeline rigide — elles sont itératives (la Phase 3 révèle souvent quelque chose qui vous renvoie à la Phase 1). Les phases ci-dessous sont la colonne vertébrale.

PhaseÉtape d'AnicheCe que vous faites
0—Détecter l'environnement → Chemin A (Zephyr), B (ESP-IDF) ou C (autre)
11 ComprendreBesoin, entrées, sorties de chaque fonction donnée
22 Explorer le codeCartographier les dépendances à isoler avec des fausses/maquettes
33 PartitionsCatalogue numéroté des partitions (l'étape la plus importante)
44 LimitesCatalogue numéroté des limites (point on/off)
55 Concevoir les casCombiner partitions/limites, réduire les combinaisons
66 AutomatiserÉcrire la suite dans le framework du chemin, avec traçabilité
7—Exécuter les outils : exécution + couverture
87 AugmenterItérer sur les écarts (couverture + éléments du catalogue sans test)

Phase 0 — Détection de l'environnement

Identifiez l'environnement du projet contenant les fichiers donnés, dans cet ordre, et choisissez le chemin. Robot Framework, HIL, pytest d'intégration ne sont PAS des frameworks de test unitaire — ils ne comptent jamais dans aucun chemin.

  1. Zephyr ? → Chemin A. Indices : west.yml/west.yaml ; CMakeLists.txt avec find_package(Zephyr ...) ; prj.conf ; testcase.yaml/sample.yaml ; tests/ ; ztest/ZTEST_SUITE. Outillage : twister + ztest + FFF + couverture. Lisez references/zephyr-path.md.

  2. ESP-IDF ? → Chemin B. Indices : CMakeLists.txt avec idf_component_register( ; sdkconfig/sdkconfig.defaults ; utilisation de $ENV{IDF_PATH}/idf.py ; un composant main/. Outillage : Unity natif d'ESP-IDF (construction hôte, cible linux) + gcov/gcovr. Utilisez Unity directement — ne vous arrêtez pas pour demander. Exception : si le projet utilise déjà systématiquement un autre framework de test unitaire (Ceedling, GoogleTest…), suivez celui-là. Lisez references/espidf-path.md.

  3. Autre (ni Zephyr ni ESP-IDF) → Chemin C. Inspectez la structure de test unitaire existante (Unity, CMock, Ceedling, GoogleTest, personnalisé) et suivez le motif du projet fidèlement. Si aucun framework de test unitaire n'est trouvé, ARRÊTEZ et demandez à l'utilisateur lequel utiliser — ne supposez jamais une valeur par défaut silencieuse dans le Chemin C. Lisez references/non-zephyr-path.md.

Indiquez dans le texte de réponse (pas seulement dans votre raisonnement), avant de continuer, le chemin choisi et les indices concrets qui le justifient, avec le fichier et ce que vous avez trouvé — par exemple "Chemin A (Zephyr) : zephyr/west.yml, find_package(Zephyr) dans tests/.../CMakeLists.txt, suites ZTEST_SUITE dans tests/lib/."


Phase 1 — Comprendre le besoin, les entrées et les sorties

Pour chaque fonction que la suite couvrira, extrayez de la lecture du .c/.h :

  • la règle métier : ce qu'elle fait et ce qu'elle ne doit pas faire (coins inclus) ;
  • les entrées : types, plage/domaine, contraintes entre variables ;
  • les sorties : comment les entrées deviennent le résultat (et les effets secondaires observables : sortie écrite, erreur retournée, mouvement émis/non émis).

Une partie est explicite ; une partie est des règles de domaine implicites (saturation, enroulement, unités, signe, jeu) que vous inférez. Ces règles implicites sont là où les bugs se cachent.

Phase 2 — Explorer le code

Lisez l'ensemble du .c/.h et construisez la carte des dépendances à isoler : HAL, pilotes, noyau/RTOS, E/S, temps, appels vers d'autres modules. Chaque dépendance devient une fausse/maquette dans la Phase 6. Identifiez également comment la logique réelle est atteinte (API publique directe, ou fonctions static via handle/vtable — appelez-les via l'API réelle, n'inventez jamais une signature).

Phase 3 — Identifier les partitions (l'étape la plus importante)

Appliquez les 3 lentilles de references/specification-based-testing.md à chaque fonction : chaque entrée isolément, les entrées en combinaison, et les classes de sortie. Considérez NULL/vide et les règles de domaine implicites. Si la fonction a un état, partitionnez également par l'état du module/contexte.

Produisez, par fonction, un catalogue nommé et numéroté avec des ID stables dans la suite :

  • Partitions : <func>/P1, P2, …
  • Limites (remplies dans la Phase 4) : <func>/B1, B2, …

Chaque élément obtient une courte description. Ce catalogue est le contrat de traçabilité de la suite (matérialisé dans la Phase 6) et la source de vérité pour les écarts (Phase 8). Une Phase 3 bien faite devrait couvrir presque tout dès le premier passage de couverture.

Phase 4 — Analyser les limites

Pour chaque limite entre partitions voisines, ajoutez au catalogue le point on (la valeur dans la condition) et le point off (le voisin le plus proche dans la partition opposée) ; les égalités donnent un point on et deux points off. Supprimez une limite déjà couverte par un autre cas — ne dupliquez pas. "Partition ou limite ? Peu importe" : ce qui compte, c'est que le cas existe.

Phase 5 — Concevoir les cas de test

Combinez les partitions entre catégories d'entrée, mais réduisez l'explosion combinatoire : testez l'exceptionnel (NULL/vide) une fois et ne le combinez pas avec tout ; utilisez le domaine pour couper. Utilisez une seule entrée de base, en faisant varier le minimum par cas (facilite le débogage). En cas de doute, l'entrée la plus simple. Pour les entrées qui ne participent pas au comportement, des valeurs raisonnables/réalistes. Si un cas a la même structure que plusieurs autres, planifiez-le comme paramétré. Listez les cas et, pour chacun, les ID du catalogue qu'il exerce.

Phase 6 — Automatiser (avec traçabilité obligatoire)

Écrivez la suite dans le framework du chemin détecté (syntaxe, isolation et détails d'échafaudage dans references/zephyr-path.md, references/espidf-path.md ou references/non-zephyr-path.md).

Le catalogue des Phases 3/4 devient des commentaires dans la suite, à deux niveaux :

  1. En-tête par fonction — un bloc au début de la section de test de cette fonction, listant l'ensemble du catalogue (chaque partition/limite avec son ID et sa courte description).
  2. Annotation par test — chaque test référence quel(s) élément(s) il exerce, par exemple /* Covers: P3 + B1 */.

Règles obligatoires (c'est ce qui rend la traçabilité précieuse) :

  • Pas de test orphelin : chaque test référence ≥ 1 élément du catalogue.
  • Pas d'élément orphelin : chaque partition/limite est exercée par ≥ 1 test. Un élément sans test est signalé comme un écart (alimente la Phase 8).
  • ID stables et identiques entre l'en-tête et les annotations — pour qu'un grep "P4" liste l'en-tête et tous les tests touchant cette partition.
  • Un test couvrant une combinaison liste tous les ID (/* Covers: P2 + P5 */).
  • Covers: dit la vérité : chaque ID annoté correspond à ce que le cas exerce réellement (entrée, état et assert), pas seulement à un élément qui existe dans le catalogue. Avant la Phase 7, relisez les annotations par rapport à chaque corps de test et corrigez celles qui ne correspondent pas.
  • Dans les Chemins B et C, le même schéma, adapté à la syntaxe du framework (commentaire au-dessus de chaque TEST(...)/TEST_CASE(...)/TEST_F(...)).

Forme canonique (Chemin A, ztest) :

/*
 * Catalogue — motor_ctrl_transform  (Phase 3 · tests par spécification)
 * Partitions :
 *   P1  p_axis_values == NULL        -> rejeté, sortie non écrite
 *   P2  axis_count valide (1..N)      -> transforme
 *   P3  valeur positive               -> pas positifs
 *   P4  valeur négative               -> signe conservé
 *   P5  valeur nulle                   -> pas nuls
 * Limites :
 *   B1  axis_count == N (max)        -> accepté
 *   B2  axis_count == N+1            -> rejeté
 */

/* Covers: P2 + P3 */
ZTEST(motor_ctrl_suite, test_transform_positive_gives_positive_steps) { /* ... */ }

/* Covers: P1 */
ZTEST(motor_ctrl_suite, test_transform_null_input_is_rejected) { /* ... */ }

Remarque : dans Zephyr récent, l'exécuteur exige que les noms de test commencent par test_ (ZTEST(suite, test_...)) — sans ce préfixe, twister ne collecte pas le cas.

Défaut trouvé : le code diverge de la documentation

Lorsque le code contredit l'en-tête, le commentaire de fonction ou le document de contrat API (par exemple, retourne un code d'erreur différent de celui documenté, ne restaure pas l'état en cas d'échec), ne laissez pas la suite en rouge et n'utilisez pas ZTEST_EXPECT_FAIL. Écrivez deux tests :

  1. Comportement documenté, ignoré avec une raison — l'assertion de ce que la documentation promet, sautée avec la raison : ztest_test_skip() (Chemin A) ou TEST_IGNORE_MESSAGE("BUG: ...") (Chemin B). Commentaire au-dessus : /* Covers: Pn — BUG: <les docs disent X ; le code fait Y> */.
  2. Caractérisation — épingle le comportement actuel, afin que tout changement (y compris la correction) apparaisse. Commentaire : /* Covers: Pn — caractérisation du BUG ci-dessus */.

La suite se termine en vert, le bug apparaît à chaque exécution (en saut/ignoré) et le binaire entre dans les mesures. Syntaxe dans les fichiers de chemin. Listez les défauts dans le résumé final.

Phase 7 — Exécution + couverture

Avant d'exécuter, activez l'environnement du projet. L'outil de test est rarement sur le PATH global — il vit généralement dans un virtualenv du dépôt. Ne concluez pas "impossible d'exécuter" avant d'avoir cherché et activé l'environnement (cela s'applique à chaque chemin) :

  • Cherchez un venv à la racine du firmware (.venv/, venv/, env/) et utilisez son binaire (.venv/bin/west, .venv/bin/python) ou source .venv/bin/activate. Le venv est souvent un lien symbolique : vérifiez avec test -e .venv/bin/activate ou ls -laL (qui suit le lien), jamais seulement avec ls -d/find -type d. Dites ce que vous avez trouvé : le chemin du venv utilisé, ou "pas de venv dans <répertoires vérifiés>".
  • Zephyr : définissez ZEPHYR_BASE (par exemple deps/zephyr, ou west topdir/.west/config) et, pour l'hôte, ZEPHYR_TOOLCHAIN_VARIANT=host. Si west n'est pas sur le PATH mais que l'arbre Zephyr existe, exécutez twister directement : python3 $ZEPHYR_BASE/scripts/twister ....
  • ESP-IDF : assurez-vous que l'environnement IDF est défini (. $IDF_PATH/export.sh lorsqu'il existe) pour la construction hôte ; sinon compilez avec gcc directement (voir espidf-path.md).

Ce n'est qu'après avoir épuisé cette recherche qu'il est honnête de signaler que l'exécution n'a pas pu avoir lieu ici.

Économie de contexte (les constructions de firmware sont verbeuses et chaque tour relit tout) :

  • Redirigez la construction, twister et gcovr vers un fichier (> build.log 2>&1) et montrez seulement le résumé (tail -n 30 build.log, grep -E 'error|FAIL|PASS|lines|branches' build.log). Ouvrez l'ensemble du journal seulement lorsque le résumé n'explique pas l'échec.
  • Écrivez chaque fichier de test en entier en une fois ; corrigez avec des modifications ciblées.
  • Regroupez les lectures et vérifications indépendantes dans le même tour ; ne relisez pas les fichiers que vous avez déjà lus.

Exécutez l'outil du chemin et pointez vers les artefacts qu'il génère (n'écrivez pas de rapport) :

  • Chemin A (Zephyr) : west twister --coverage -p native_sim -T <chemin_suite>. Artefacts : twister-out/coverage/index.html, twister-out/coverage.json, twister-out/twister.xml, twister-out/twister.json.
  • Chemin B (ESP-IDF) : construction hôte avec Unity + gcov/gcovr (voir espidf-path.md) ; pointez vers le rapport de couverture (coverage.html/.xml) et la sortie de test.
  • Chemin C (autre) : le propre exécuteur du projet + couverture (par exemple ceedling gcov, ou gcov/gcovr sur la construction de test). Conservez et pointez vers les artefacts émis.

Détails et dépannage dans le fichier de chaque chemin.

Phase 8 — Itération de couverture

Comparez la couverture rapportée par les outils avec la cible 95/95/90 (ou celle demandée par l'utilisateur). En dessous de la cible, chaque ligne/branche atteignable sans test est un écart ; une fois la cible atteinte, arrêtez et listez ce qui reste non couvert. Pour chaque écart, ajoutez des cas en utilisant les deux sources : la

couverture numérique (lignes/branches rouges) et …

[…] (tronqué pour la traduction)

Skills associés