TP 3
Finalisation du contrôleur, tests d’intégration et exigences avancées de qualité du code.
Meta
- Date limite : Dimanche 12 avril, 23 h 59
- Remise : GitLab
- Équipes : Triplets d’étudiants, tel qu’annoncé en classe
- Remise en retard : Impossible. Vous serez évalués selon le contenu présent sur votre branche principale à la date limite
Il est de votre responsabilité de soumettre votre code en temps opportun. N’attendez pas à la dernière minute, GitLab peut échouer.
Objectifs d’apprentissage
Dans ce troisième TP, vous apprendrez à appliquer des exigences avancées de qualité du code sur du code existant et nouveau. De plus, vous apprendrez à utiliser des joueurs robotiques comme tests d’intégration, afin de guider une implémentation correcte des règles du jeu.
Plus concrètement, pour le troisième TP, vous devrez...
-
compléter votre implémentation du
Controllerde Skyjo, et intégrer des concepts avancés pour assurer la qualité du code dans le processus de build. Toutes les exigences de qualité du code doivent être appliquées viapom.xml. -
Votre code doit être testé avec des tests classiques et des tests de mutation. Vous devez vous assurer qu’un niveau de couverture et de couverture de mutation requis est atteint.
- Votre code doit respecter des règles de formatage de code de niveau industriel, et respecter un seuil de complexité requis.
-
Votre code doit être entièrement documenté avec des commentaires JavaDoc pour toutes les méthodes et classes publiques.
-
utiliser les tests d’intégration fournis pour guider la complétion de votre implémentation du contrôleur, c’est-à-dire l’implémentation des règles du jeu.
-
Votre implémentation du jeu doit prendre en charge correctement toutes les actions du jeu, dans toutes les situations.
- Les décideurs fournis (joueurs robotiques déterministes) serviront de référence pour vérifier si votre implémentation du modèle et du contrôleur respecte correctement les règles. Ces joueurs robotiques sont utilisés dans les tests d’intégration fournis. Si un test d’intégration échoue, vous devez comparer les parcours d’exécution attendus et réels du jeu afin d’identifier et corriger les erreurs potentielles d’implémentation.
Aperçu étape par étape
- Corrections : Vérifiez votre soumission précédente.
- Utilisez les rétroactions reçues sur GitLab (cela peut encore prendre quelques jours, vous pouvez déjà relancer vos tests).
- Corrigez tout problème, en particulier les tests échoués. Utilisez le débogueur.
- Qualité du code :
- Activez un seuil de complexité cyclomatique de 7, via
google_checks.xmletpom.xml. - Activez une vérification stricte du Javadoc via
pom.xml. - Augmentez la couverture de code à
85%viapom.xml. - Activez les tests de mutation, et définissez une couverture minimale des tests de mutation via
pom.xml.
- Activez un seuil de complexité cyclomatique de 7, via
- Code du contrôleur : Complétez votre implémentation du contrôleur afin que le jeu soit entièrement jouable.
- Tests d’intégration :
- Séparez les tests d’intégration des tests réguliers en utilisant le suffixe
*ITviapom.xml. - Étendez la classe abstraite de test d’intégration fournie pour activer les tests d’intégration, et assurez-vous que tous les tests d’intégration passent.
- Séparez les tests d’intégration des tests réguliers en utilisant le suffixe
Vous pouvez entrelacer 3. et 4.
Les tests d’intégration sont également un moyen efficace de guider l’implémentation (voir TDD), c’est-à-dire que vous pouvez d’abord activer les tests d’intégration, puis finaliser progressivement votre base de code jusqu’à ce que tous les tests d’intégration s’exécutent correctement. Les tests d’intégration fournis sont aussi progressifs, c’est-à-dire que vous pouvez commencer par un test court qui ne joue que quelques tours, puis passer aux versions plus complexes par la suite.
Instructions détaillées
Plusieurs changements sont nécessaires pour réussir ce troisième jalon. Voici une description détaillée de ce qui est attendu.
Corrections
- Vérifiez que vous avez bien étendu tous les tests requis du jalon TP2.
- Vérifiez que votre implémentation passe tous les tests précédents.
- Dès que vous recevez une rétroaction individuelle sur votre soumission TP2, assurez-vous de corriger tous les problèmes avant d’ajouter de nouvelles fonctionnalités.
Qualité du code
Pour ce TP3, votre code doit respecter diverses métriques de qualité.
- Il ne s’agit pas d’options. Il ne suffit pas de les vérifier manuellement dans votre IDE.
- Elles doivent être appliquées sous forme de plugins Maven.
- Les plugins ne doivent pas être configurés pour émettre uniquement des avertissements, c’est-à-dire que la compilation doit échouer s’il y a des problèmes dans votre code.
Votre soumission sera rejetée si les mesures de qualité du code ont été ignorées. Ce n’est pas un luxe, mais une exigence minimale pour une ingénierie logicielle responsable.
Complexité cyclomatique
- En classe, vous avez vu que la complexité cyclomatique peut être appliquée à l’aide du plugin
checkstyle. -
Ouvrez votre
google_checks.xmlet assurez-vous que le seuil de complexité cyclomatique est défini à7:- Recherchez
CyclomaticComplexityet assurez-vous que le bloc correspondant est défini comme suit :
- Si Maven signale des erreurs indiquant que votre code est trop complexe, refactorisez le code correspondant afin de réduire la complexité.
- Recherchez
Vérification stricte du Javadoc
Votre code doit être documenté à 100 % :
- Toutes les classes non privées doivent avoir une description JavaDoc
- Toutes les méthodes non privées doivent avoir une description JavaDoc
- Si une méthode implémente une interface existante, aucune documentation supplémentaire ne doit être fournie, puisque l’interface est déjà documentée. Placez simplement une annotation
@Overrideavant la signature de la méthode.
Utilisez le plugin Maven javadoc pour vérifier le Javadoc dans votre code (vu en classe / laboratoires).
- Assurez-vous que la vérification est stricte, c’est-à-dire qu’elle ne génère pas d’avertissements mais des erreurs de compilation.
- S’il y a des erreurs de compilation, corrigez votre code et ajoutez la documentation manquante.
Couverture de lignes
- Votre code du contrôleur et du modèle doit atteindre une couverture d’au moins 85 % des lignes de code.
- Ajoutez le plugin Maven
jacocopour évaluer votre code dans le cadre du processus de build. - Assurez-vous que
jacocoest strict, c’est-à-dire qu’il rejette la compilation si moins de85 %des lignes de votre code sont couvertes par des tests. - S’il y a des erreurs, étendez vos tests afin d’améliorer la couverture.
À la fin de ce fichier se trouve un exemple de configuration pour le plugin jacoco.
Couverture de mutation
- Vos tests doivent avoir suffisamment d’assertions pour détecter et éliminer
65 %de tous les mutants. - Activez PIT, tel que vu en classe.
- Assurez-vous que PIT est configuré pour faire échouer le processus de build si moins de
65 %des mutants sont éliminés. - Si la compilation échoue, améliorez vos tests afin d’augmenter la couverture de mutation.
Exclure les tests d’intégration
N’appliquez pas PIT aux tests d’intégration. Ils ne passent pas à l’échelle et seront très lents. Consultez le matériel du cours pour savoir comment configurer PIT afin qu’il ne crée des mutations que pour les tests unitaires, en excluant les tests d’intégration.
Code du contrôleur
Jusqu’à présent, vous n’avez implémenté que l’initialisation du jeu (création du modèle) et des actions simples disponibles pour le premier joueur. Dans ce TP, vous implémentez le reste des fonctionnalités du contrôleur afin que votre jeu soit réellement jouable.
- Aucune nouvelle interface de contrôleur n’a été ajoutée, mais vous pouvez toujours accéder à la documentation existante.
- Comme vous l’avez vu dans le TP2, la boucle principale du jeu fonctionne comme un échange de type ping-pong entre les joueurs :
- Le jeu propose des options disponibles.
- Le joueur sélectionne l’une des options proposées.
- Ce concept est également appelé « architecture/patron Blackboard » : des options sont proposées sur un tableau, puis une option préférée est sélectionnée.
L’intérêt principal de cette boucle de contrôle est de s’assurer que les joueurs n’ont pas d’accès direct au modèle, ne peuvent pas tricher, et que le jeu respecte toujours les règles correctes.
Architecture Blackboard
L’architecture Blackboard de Skyjo n’est pas très complexe, mais il est important de traduire correctement les règles du jeu en un modèle de diagramme de flux, définissant quelles options sont disponibles à quel moment du jeu.
- Traduire (ou rétroconcevoir) les règles du jeu en un chemin d’exécution de programme dépasserait le cadre de ce cours de premier cycle.
- Par conséquent, cet énoncé de TP vous fournit le modèle formel de diagramme de flux afin de vous aider dans votre implémentation.
flowchart TD
K(Start) --> |**Reveal deck** card| L(Card in buffer)
L --> |"**Reveal** player card<br>(reject buffer)"| M(Player card replaced or revealed)
L --> |"**Replace** player card<br>(using buffer)"| M
M --> |Eliminate<br>col / row| M
M --> |Cede|O(End)
K --> |"**Replace** player card<br>(using deck)"| M
Le graphique ci-dessus guide directement votre implémentation : à chaque état (boîte), le joueur courant dispose d’une ou plusieurs options (flèches) :
- Une flèche peut correspondre à plusieurs instances d’options, par exemple révéler une carte peut s’appliquer à n’importe quelle carte du joueur.
- L’ordre est important : les options doivent toujours être triées, afin que les tests d’intégration (par exemple en choisissant toujours la première option disponible) suivent de manière fiable le même chemin d’exécution.
- Concernant l’ordre : s’il y a plusieurs flèches, suivez de gauche à droite. Lorsqu’il y a plusieurs instances d’options, triez-les selon la carte à laquelle elles se réfèrent (de haut en bas, de gauche à droite).
Utilisez les exemples fournis
La définition ci-dessus reste quelque peu formelle, vous pouvez donc chercher des exemples. Heureusement, il y en a plusieurs ! Vous pouvez utiliser les journaux de parties d’exemple fournis pour observer quelles options exactes doivent être proposées à un joueur dans des états de jeu donnés. Voir le lien dans la section suivante.
Tests d’intégration
Deux joueurs robotiques et une série de tests d’intégration sont fournis. Les modifications suivantes à votre base de code sont nécessaires pour les activer :
-
Modifiez votre dépendance d’interface vers la version TP3 : (accès aux joueurs robotiques)
-
Modifiez votre dépendance de test vers la version TP3 : (accès aux tests d’intégration)
Séparation des phases de test
- Par défaut, tous les tests (unitaires et d’intégration) sont exécutés dans la phase
test. - Utilisez le plugin
surefirepour associer l’exécution de tous les tests d’intégration (se terminant par*IT) à la phaseverify. Consultez le matériel du cours pour la configuration correspondante du plugin. - Configurez le plugin de tests de mutation PIT pour exclure les tests d’intégration. Ils sont plus lents et entraîneront des délais d’expiration. Les tests de mutation doivent tout de même être appliqués aux tests unitaires standards.
- Étendez la classe
DefaultSizeAbstractITfournie, en fournissant des scénarios de tests d’intégration.- Ne copiez pas cette classe, elle est déjà incluse comme dépendance Maven.
- Étendez-la simplement, c’est-à-dire créez une nouvelle classe dérivée dans votre répertoire
test, et implémentez les deux méthodes requises.
Phase verify
- Vérifiez que les tests d’intégration sont désormais exécutés exclusivement durant la phase
verify. - Si des tests d’intégration échouent, comparez les traces dans la sortie de vos tests avec les traces fournies à la fin de ce document.
- Vérifiez itération par itération à quel moment votre jeu Skyjo diverge, et corrigez le code correspondant dans votre contrôleur.
Barème
| Critère | Pourcentage max |
|---|---|
| Limite de complexité cyclomatique 7 respectée, aucun avertissement checkstyle | 25 % |
| Tests d’intégration supplémentaires non divulgués réussis | 20 % |
| Couverture de lignes de test > 85 %, testée avec le plugin jacoco | 15 % |
| Couverture de mutation > 65 %, testée avec le plugin PIT | 15 % |
| Tests d’intégration fournis réussis | 10 % |
| Javadoc appliqué dans pom, aucun avertissement | 10 % |
| Dépôt sans encombrement | 5 % |
Recommandation
Simulez le processus d’évaluation avant la remise : reclonez votre propre projet et exécutez mvn clean verify, puis assurez-vous que votre projet se construit sans avertissements ni erreurs, en utilisant le pom.xml fourni pour ce TP3.
Rejet automatique
Voici une liste de vérification des éléments à éviter à tout prix. Si au moins un élément de cette liste s’applique, votre soumission perdra tous les points liés au code. D’autres critères de rejet automatique peuvent s’appliquer.
- La solution n’est pas fournie dans le répertoire GitLab dédié, ou n’est pas sur la branche
main. - La solution est répartie sur plusieurs branches.
- Le dépôt contient un fichier zip.
- Le code ne compile pas avec la commande Maven fournie.
- Le
pom.xmlcontient d’autres plugins ou dépendances que ceux autorisés. - Les classes fournies n’implémentent pas les interfaces fournies.
- L’implémentation des classes de test abstraites n’est pas fournie dans le package de test.
- La structure du projet a été modifiée / n’est pas respectée.
- Le programme tente une communication réseau à l’exécution.
- Le programme bloque à l’exécution.
DIVERS
Cette section fournit des ressources supplémentaires pour la réalisation technique de votre TP.
Parties d’exemple
Les parties d’exemple sont des parties préenregistrées, c’est-à-dire des traces de sessions avec 2 joueurs robotiques ou plus. Vous pouvez utiliser ces exemples pour soit...
- compléter votre compréhension du diagramme de flux théorique fourni pour l’implémentation de votre contrôleur.
- identifier des erreurs dans un test d’intégration, en comparant la sortie de vos tests avec la trace attendue des sessions de jeu.
Fichiers
Les traces fournies varient en complexité. Les itérations plus faibles représentent des parties plus courtes (moins de décisions demandées aux joueurs robotiques), tandis que les itérations plus élevées correspondent à des parties plus longues ou plus complexes. Commencez par les parties plus courtes et progressez graduellement vers les plus complexes.
Tous les fichiers dans la grille ci-dessous se réfèrent aux tests d’intégration définis dans le fichier Java DefaultSizeAbstractIT fourni.
| Itérations | Joueurs | Fichier de traces |
|---|---|---|
| 2 | Keksli VS Keksli | keksliVsKeksliR42I2.txt |
| 5 | Keksli VS Keksli | keksliVsKeksliR42I5.txt |
| 2 | Keksli VS Keksli | keksliVsKeksliR43I2.txt |
| 3 | Keksli VS Keksli | keksliVsKeksliR43I3.txt |
| 3 | MadMax VS MadMax | madMaxVsMadMaxR43I3.txt |
| 25 | MadMax VS MadMax VS Keksli VS Keksli | madMaxVsMadMaxVsKeksliVsKeksliR43I25.txt |
Code du lanceur
Les tests ne nécessitent pas de code de lancement, mais il peut être pratique d’avoir un lanceur fonctionnel permettant de jouer manuellement à votre implémentation.
Utilisez le code de lancement ci-dessous pour votre implémentation du TP3. Seules deux modifications du code ci-dessous sont nécessaires :
- L’
importde votre classeControllerImpl. - L’utilisation du constructeur de votre
ControllerImplpour créer de nouvelles instances de jeu.
package ca.uqam.info.max.skyjo.view;
import ca.uqam.info.max.skyjo.controller.Command;
import ca.uqam.info.max.skyjo.controller.Controller;
import ca.uqam.info.max.skyjo.controller.ModelPreset;
// TODO: Import YOUR ControllerImpl class here.
/**
* Launcher for a textual / TTY session with all physical players sharing one keyboard / screen.
*
* @author Maximilian Schiedermeier
*/
public class LauncherTp3 {
/**
* Default constructor, as imposed by javadoc.
*/
public LauncherTp3() {
}
/**
* Replace command selector by robot players to obtain an automated game (also used for
* integration testing).
*/
private static CommandSelector commandSelector;
/**
* Starts game by creating a new controller (which in turn creates a new model). Then keeps
* prompting players for choices until game end is reached.
*
* @param args not used.
*/
public static void main(String[] args) {
// Register UI to automatically refresh on model updates
boolean useTtyColours = true;
// Create a model, using your model constructor.
// Make sure your model implements the provided model readonly interface
String[] playerNames = new String[] {"Max", "Ryan", "Maram", "Quentin"};
// TODO: Initialize a new game, using YOUR ControllerImpl class here.
Controller controller = ... // Something like "new ControllerImpl(...)";
// Register UI to automatically refresh on model updates
controller.initializeModel(ModelPreset.DEFAULT, playerNames, null);
controller.addModelObserver(new TextualVisualizer(controller.getModel(), useTtyColours));
// Register UI to automatically refresh on model updates
controller.addModelObserver(new TextualVisualizer(controller.getModel(), useTtyColours));
// Initialize commandSelector for interactive / TTY mode
commandSelector = new TextualCommandSelector(useTtyColours, false);
// Play the game :)
playUntilGameEnd(controller);
}
/**
* Note: This method is not concerned with updating model state representations, for the model
* adheres to the observer pattern for this purpose.
* This loop is only about retrieving user inputs until game end. The model is automatically
* notified and re-rendered after each executed command.
*
* @param controller as the MVC controller allowing to progress the game command by command.
* Note that the view has no direct access to the model, and can only
* manipulate model state by executing commands.
*/
private static void playUntilGameEnd(Controller controller) {
// Initialize options for game start
Command[] options = controller.getCurrentPlayerCommands();
// Keep playing until controller offers no more options (game end)
while (options.length > 0) {
// Request a choice from human player - "undo"s have no relevance for INF2050, leave at
// "false".
int selectedCommand = commandSelector.selectCommand(options, false);
// Execute choice (this implicitly re-renders the model)
controller.doCommand(selectedCommand);
// Update options
options = controller.getCurrentPlayerCommands();
}
}
}
Jacoco
Voici un exemple de configuration Maven pour le plugin jacoco :
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<configuration>
<excludes>
<!-- ignore view package, so your launcher is exempted -->
<exclude>**/view/*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<!-- Associate with verify phase, to grab unit AND integration test results.-->
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>jacoco-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>PACKAGE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>85%</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>