Utilisation d’un framework de tests unitaires et création de votre bibliothèque de code.

Cartouche

Champ Valeur
Auteur·e Élise
Édition 2024-02-02
Taille des équipes 1 personne
Rendu via git, et dépôt libstu, droits en lecture à delivery_collector

Barème

Critère Points
Fonctions obligatoires 8
Compilation de l’archive 3
Écriture et qualité des tests unitaires 8
Compilation et exécution des TU 3
Total des critères 20
Note maximale 20

Règlement

La réalisation de cet exercice est assujettie aux règles en vigueur dans l’école en matière de triche et de normalisation. Tricher vous expose à de graves sanctions.

Vous devez respecter les règles de normalisation suivantes : https://git.ecole-89.com/eriizu/coding_style/src/branch/main/norm.md

Le non-respect de la norme vous fera perdre une partie ou la totalité des points qui auraient pu être acquis, sur l’ensemble du rendu.

Instructions de rendu

Respectez à la lettre les noms de fichiers et leur emplacement dans le dépôt de rendu. S’il vous est demandé de rendre un fichier nommé hello.c sans qu’un nom de dossier soit précisé : rendez un fichier hello.c à la racine de votre dépôt.

La présence d’une étoile * signifie que le nom du fichier peut être n’importe lequel dès lors qu’il porte l’extension ou l’affixe (préfixe/suffixe) demandée. Elle signifie aussi qu’il est possible de rendre plusieurs fichiers.

La présence d’une double étoile ** signifie que les fichiers peuvent être placés n’importe où dans un dossier, y compris dans des sous-dossiers.

Pensez à faire des commits et des push fréquemment. Autrement nous ne pouvons pas vous aider à retrouver vos fichiers perdus.

Fonctions autorisées et interdites

Par défaut, toute fonction système (comme write) ou fonction des bibliothèques (comme printf ou puts) sont interdites.

À chaque exercice vous sera donné une liste de fonction autorisées, le cas échéant.

Synopsis

Aujourd’hui on va résoudre plusieurs problèmes que vous avez depuis le début de l’année :

  • copier vos fonctions utilitaires à chaque projet : c’est pas pratique ;
  • avoir plein de main de test que l’on ne peut pas exécuter en même temps : c’est pas pratique ;
  • ne pas avoir de façon simple de rapporter les erreurs dans un main de test : c’est pas pratique ;
  • rechercher les commandes à faire pour compiler votre projet ou vos tests : c’est pas pratique non plus.

Pour les résoudre, vous allez créer votre propre bibliothèque qui sera accessible à tous vos projets. Vous allez utiliser un framework de tests unitaires (et découvrir ce que c’est qu’un test unitaire). Et vous aller consigner les commandes que vous utilisez pour compiler dans un justfile.

Étape 1 : bibliothèque

Vous allez avoir besoin au minimum des fonctions suivantes :

unsigned int stu_strlen(const char *s);
char *stu_strcpy(char *dest, const char *src);
char *stu_strcat(char *dest, const char *src);
int stu_strcmp(const char *s1, const char *s2);
char *stu_strdup(const char *src);
char *stu_strchr(const char *s, char c);
int stu_atoi(const char *str);
int print_base10(int nb);
int stu_puts(const char *s);
int has_opt(int ac, char **av, char opt);
char *has_opt_value(int ac, char **av, char opt);

Vous n’y êtes pas limités. Vous êtes encouragés à ajouter des fonctions qui vous sont pratiques dans vos projets.

Créez un dépôt libstu. Mettez les fichiers de ces fonctions dans le dossier src de ce dépôt.

Écrivez les prototypes de toutes ses fonctions dans include/stu.h

Maintenant, pour faire une bibliothèque, on a besoin de transformer les fichiers sources en fichier objets. L’option -c de gcc permet de transformer une fichier source en objet, au lieu d’essayer d’en faire un exécutable.

Enfin, la forme la plus basique d’une bibliothèque de code sur Linux, c’est une archive crée avec ar.

ar rc libstu.a [tout vos fichiers .o]

Vous pouvez vérifier les fichiers dans une archive avec :

nm libstu.a

Écrivez les commandes qui fabriquent vos objets et créent votre archive dans le fichier justfile, à la racine de votre dépôt libstu, sous le nom de recette build_lib.

Un fichier justfile à une syntaxe assez simple :

nom_de_recette:
	une commande par ligne

Exemple:

compile:
	gcc -Iinclude -Wall -Wextra -Werror src/*.c -o patate
clean:
	find -name "*~" -delete -print -o -name "*#" -delete -print -o -name "*.o" -delete -print -o -name "*.gch" -delete -print -o -name "a.out" -delete -print -o -name "vgcore*" -delete -print

Commandes shell pour utiliser :

  • compile : just compile
  • clean : just clean

Étape 2 : tests unitaires

Un test unitaire : qu’est-ce que c’est ? C’est à peu près ce qu’on fait avec les main de tests depuis le début de l’année. C’est un test qui s’assure qu’une fonction ou une petite partie d’un programme à le comportement que l’on souhaite. C’est une façon de gagner en confiance sur le fait que notre programme soit vraiment capable de remplir son métier.

On les distingue des tests fonctionnels ou d’intégration. Qui sont des tests qui sont fait sur un ensemble plus large. Sur un programme entier par exemple.

On va utiliser une bibliothèque qui s’appelle Criterion. Elle a sa propre syntaxe pour déclarer des tests et elle vous permet plus d’organisation qu’un pauvre main.

Exemple d’un fichier test/strlen.c :

#include <criterion/criterion.h>
#include <criterion/new/assert.h>
#include "stu.h"

Test(strlen, normal_1) {
    cr_assert(eq(i32, stu_strlen("hello"), 5));
}

Test(strlen, normal_2) {
    cr_assert(eq(i32, stu_strlen("abcdef"), 6));
}

Test(strlen, empty) {
    cr_assert(eq(i32, stu_strlen(""), 0));
}

Si on compile avec gcc -Iinclude src/*.c test/*.c -lcriterion -o unit_test.out et qu’on lance ./unit_test.out on voit les résultats des tests qui s’affichent.

eriizu@laconia in ~/E89/01_projects/piscine/libstu
> ./unit_test.out
[====] Synthesis: Tested: 3 | Passing: 3 | Failing: 0 | Crashing: 0

Écrivez un fichier de tests Criterion par fonction de votre bibliothèque, dans le dossier test/.

Ajoutez une recette test dans votre justfile qui compile et exécute vos tests.