TP2 - TDD - Palindrome
Qualité de Développement - R4.02
Première mise en pratique du TDD.
Afin de mettre en pratique le TDD et de poursuivre la prise en main du langage Rust, vous allez développer deux petits projets :
-
un programme capable de déterminer si une chaîne de caractères est un palindrome.
-
une petite bibliothèque de géométrie permettant de manipuler des points et des vecteurs 2D.
Mise en place de Git, du workflow et de l’intégration continue
Dans le dépôt Git que vous avez créé pour la matière, vous allez, pour chaque TP, créer une branche dédiée.
De plus, pour chaque projet, vous mettrez en place un workflow de développement basé sur :
-
La branche dédiée, avec éventuellement des sous branches pour les différentes fonctionnalités.
-
Une "merge request" depuis la branche dédiée visant à intégrer les modifications correspondant au TP dans la branche principale,
main.
Création de la branche et du package
Avant de vous lancer dans le code, vous devez créer une branche pour le
palindrome nommée palindrome. Vous pouvez le faire depuis l’interface GitLab ou
bien dans votre dépôt local.
Une fois la branche créée, basculez dans celle-ci puis créez un package Rust nommé tp2_palindrome avec la commande :
cargo new tp2_palindrome
Création de la "merge request" pour la branche
Afin de pouvoir identifier clairement les contributions au dépôt qui concernent le TP, vous allez créer une "merge request" pour la branche palindrome. Les push se feront vers la branche source de cette merge request, c’est-à-dire la branche palindrome et déclencheront les pipelines GitLab.
Une fois votre TP terminé, vous pourrez accepter la merge request.
Mise en place de l’intégration continue
Pour l’intégration continue, vous allez utiliser les pipelines GitLab.
Pour cela, vous devez ajouter un fichier .gitlab-ci.yml à la racine de votre dépôt.
Ce fichier vous permet de configurer les pipelines qui seront lancés à chaque push sur votre branche.
Voici un exemple de fichier vous permettant de configurer un pipeline pour le TP palindrome :
image:
name: rust:1.83.0-bookworm
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
- if: $CI_COMMIT_BRANCH
test-palindrome:
stage: test
script:
- echo "Testing palindrome lab"
- cd tp2_palindrome
- cargo test
La section image permet de spécifier l’image Docker à utiliser pour les jobs
du pipeline. Ici, nous utilisons l’image rust qui contient l’environnement de
développement Rust.
La section workflow permet de définir les règles qui déclenchent les jobs. Le
but ici est de ne lancer les tests que sur les push sur la branche qui est la
source d’une merge request.
La section test-palindrome définit le "job" qui lance les tests.
Le résultat du pipeline sera celui du test.
Validation du workflow
Une fois votre workflow en place, validez que celui-ci fonctionne en commitant et poussant votre projet qui doit contenir le package tp2_palindrome.
Cela doit bien sûr être fait dans la branche palindrome.
Une fois le push fait, validez que le pipeline se déclenche bien et que les tests passent depuis l’interface GitLab. Vos pipelines sont dans la rubrique "Build > Pipelines" de votre dépôt.
Vous devriez voir quelque chose comme ça dans la liste de vos pipelines :
puis après quelques instants :
Si vous cliquez sur le statut du pipeline, vous devriez voir quelque chose comme :
Palindrome
Spécifications
-
Détecter qu’une chaîne de caractères est un palindrome : c’est-à-dire qu’elle correspond au même mot à l’envers.
-
Des mots comme "non" ou "radar" sont des palindromes.
-
Des mots comme "aspirateur" ou "petit" ne sont pas des palindromes.
-
-
La casse n’a pas d’influence sur le fait d’être un palindrome : "Non" ou "Radar" sont également des palindromes.
-
Il devra également être capable de détecter des phrases palindromes comme "Was It A Rat I Saw" ou "Engage le jeu que je le gagne".
Mise en pratique du TDD sur le problème du palindrome
Premier test - "Write a failing test"
Créez votre premier test dans le fichier main.rs, en dessous de la fonction
main, de votre nouveau projet. Adaptez le nom du test à l’exigence que vous
testez :
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn radar_is_palindrome() {
assert!(is_palindrome("radar"));
}
}
Votre IDE devrait vous offrir la possibilité de passer les tests, sinon avec cargo :
$ cargo test
...
error[E0425]: cannot find function `is_palindrome` in this scope
...
Vous devriez constater que l’étape de compilation ne passe logiquement pas. Vous êtes arrivés à la fin de la première étape du cycle TDD :
Bravo ! vous avez pratiqué la première étape du TDD. Mais ce n’est pas fini : il va falloir continuer le cycle.
Code pour le premier test - "Make the test pass"
Votre tâche est maintenant d’écrire le minimum de code pour que le test passe. Dans un cas simple comme celui du palindrome, vous serez peut-être tentés de commencer à écrire des choses compliqués. Ce n’est pas la bonne façon de faire dans le cas général.
Le premier bout de code doit ressembler à ça :
fn is_palindrome(chaine: &str) -> bool {
true
}
Constatez que le test passe maintenant.
Le principal intérêt de ce test est de valider le prototype de la fonction et de valider que les outils de test fonctionnent.
Vous êtes arrivés à la deuxième étape du cycle. Ce serait le moment de faire du refactoring. En l’occurrence le code et les tests sont tellement simples qu’il n’y a rien à modifier.
Vous pouvez voir les détails de ce qui s’est passé en explorant les information sur le "job" de votre pipeline.
Itérez
Maintenant que le test passe, revenez au début du cycle : Écrire un nouveau test qui échoue.
Pensez à faire un commit à chaque itération ! C’est-à-dire à chaque fin du cycle TDD.
Vous n’avez pas besoin de ré-écrire tout le module de test, uniquement d’ajouter une fonction avec l’annotation #[test]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn radar_is_palindrome() {
assert!(is_palindrome("radar"));
}
#[test]
fn aspirateur_is_not() {
assert!(!is_palindrome("aspirateur"));
}
}
Écrivez un code qui fait passer les tests
Ne vous souciez pas tout de suite des autres contraintes sur le palindrome, uniquement ce qui fera passer les tests.
Une version valide pourrait être :
fn is_palindrome(chaine: &str) -> bool {
if chaine == "radar" {
return true;
} else {
return false;
}
}
Ou sa version plus idiomatique :
fn is_palindrome(chaine: &str) -> bool {
chaine == "radar"
}
Mais on va arrêter la torture à ce stade, écrivez un premier algorithme qui détecte un mot palindrome.
Pensez à bien utiliser les
nombreuses fonctions disponibles sur le type str
Refactoring
À ce stade vous avez une première version de l’algorithme de détection de palindrome qui fait passer deux tests.
Vous êtes donc dans la phase de refactoring. Il peut être utile à ce stade d’examiner votre code et vos tests pour vérifier si une amélioration ne pourrait pas être faites. Si vous décidez de modifier votre code ou vos tests, vous devez valider que les tests continuent de passer avant d’écrire un nouveau test.
Terminez le détecteur de palindrome
Continuez d’itérer dans le cycle TDD jusqu’à respecter les spécifications.
Pensez à faire un commit à chaque itération ! C’est-à-dire à chaque fin du cycle TDD.
Pensez à vérifier régulièrement que votre pipeline fonctionne bien.
Une fois que vous avez terminé, fusionnez votre branche dans la branche main
depuis l’interface GitLab, dans la rubrique "Merge requests"
S’il vous reste du temps
Si vous avez terminé, fusionné et tagué votre travail sur le palindrome, ajoutez un exécutable à votre projet pour générer des palindromes.
Vous aurez besoin d’une liste de mots français. Vous pouvez en trouver une ici : https://www.pallier.org/extra/liste.de.mots.francais.frgut.txt