12 mai 2023
Le but de cet exercice est :
A la fin de cet exercice, vous serez capable d’analyser un texte comme celui-ci :
<?xml version = "1.0"?>
<Group label="testGroup" x="0" y="1">
<Circle label="testCircle1" x="2" y="3" r="4" color="Black"/>
<Circle label="testCircle2" x="5" y="6" r="7" color="Black"/>
</Group>
qui spécifie :
testGroup
et de coordonnées (x,y) (0,1)
testCircle1
, de coordonnées (x,y) (2,3)
, de rayon r 4
et de color Black
,testCircle2
, de coordonnées (x,y) (5,6)
, de rayon r 7
et de color Black
.NB : Pour simplifier cet exercice, nous nous contenterons de stocker dans une std::string
(et non dans un fichier) les octets du texte au format XML.
Par ailleurs, à la fin de cet exercice, vous aurez automatisé vos tests avec Google Test.
C:\CPP
).SampleGoogleTest
qui a été créé en, par exemple, AnalyseXML
.cmake
de ce document.Nous souhaitons être en mesure de lire le XML suivant :
<?xml version = "1.0"?>
<Circle label="testCircle" x="0" y="1" r="2" color="Black"/>
src/Circle.h
dans lequel vous définissez une structure Circle
contenant :
label
de type std::string
,x
, y
, r
de type double
,color
de type std::string
,src/Circle.h
, ajoutez la ligne suivante (après la ligne #pragma once
ou la ligne #define CIRCLE_H
, selon ce qu’a généré votre IDE) pour que le compilateur sache comment std::string
est déclaré :#include <string>
src/myMain.cpp
, ajoutez #include "pugixml.hpp"
#include "Circle.h"
Circle
au format XML :std::string sc = R"(<?xml version = "1.0"?>
<Circle label="testCircle" x="0" y="1" r="2" color="Black" />)";
s
dans la variable node
:
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_string(sc.c_str());if (!result) {
std::cerr << result.description();
1);
exit(
} "Circle"); pugi::xml_node node = doc.child(
#include
(cette ligne permet de dire au compilateur comment est déclarée la variable std::cerr
qui, pour information, est l’équivalent de stderr
en langage C) :#include <iostream>
std::
devant les noms standards (comme, par exemple, std::cerr
), vous pouvez ajouter la ligne suivante après vos #include
(ATTENTION : Ne jamais faire cet ajout dans un fichier .h
) :using namespace std;
Circle c
en initialisant ses différents champs (par exemple, pour label
: node.attribute("label").as_string()
).Circle c
a été correctement initialisé à partir du XML, nous allons l’afficher.
src/Circle.h
, après la déclaration des champs de votre structure, ajoutez la déclaration : std::string dump() const;
src/Circle.cpp
dans lequel vous implémentez Circle::dump
ostringstream oss;
(ce qui nécessite d’ajouter une ligne #include <sstream>
au début de votre fichier)oss << donnee_a_stocker_dans_la_sortie
que nécessaire. NB : Vous pouvez enchaîner les <<
sur une même ligne. Par exemple, oss << "x: " << x << "," << std::endl;
(où std::endl
indique qu’il faut aller à la ligne)return oss.str();
src/myMain.cpp
, ajoutez la ligne cout << c.dump();
mainLauncher.exe
à droite de la flèche verte dans la deuxième barre de menu et vérifiez que vous obtenez un affichage similaire à :Circle "testCircle", x: 0, y: 1, r: 2, color: "Black"
Procédez de la même manière que précédemment pour lire le XML suivant :
<?xml version = "1.0"?>
<Group label="testGroup" x="0" y="1">
<Circle label="testCircle1" x="2" y="3" r="4" color="Black"/>
<Circle label="testCircle2" x="5" y="6" r="7" color="Black"/>
</Group>
et obtenir l’affichage suivant :
Group "testGroup", x: 0, y: 1, children: [
Circle "testCircle1", x: 2, y: 3, r: 4, color: "Black"
Circle "testCircle2", x: 5, y: 6, r: 7, color: "Black"
]
NB : Pour traiter tous les enfants Circle
de votre Group
, inspirez-vous d’un exemple de boucle dans la documentation pugixml (Cherchez cet exemple de boucle en cherchant le mot children
dans cette documentation).
Dans l’affichage précédent du Group
"testGroup"
, les Circle
testCircle1
et testCircle2
ne sont pas indentés par rapport au Group
testGroup
dont ils font partie : On comprend moins bien qu’ils en font partie. Modifiez votre code pour ajouter une indentation et obtenir :
Group "testGroup", x: 0, y: 1, children: [
| Circle "testCircle1", x: 2, y: 3, r: 4, color: "Black"
| Circle "testCircle2", x: 5, y: 6, r: 7, color: "Black"
]
Circle
ou d’un Group
à partir d’un fichier XML s’est bien passée. Dans cette partie, nous automatisons cette vérification.unitTests/unitTests.cpp
TEST(TestCaseName, TestName)
en TEST(TestReadXML, TestCircle)
.circle c
à partir de votre XML, en recopiant le code que vous avez développé dans src/myMain.cpp
et en remplaçant :if (!result) {
cerr << result.description();1);
exit( }
// Si jamais result est faux, indique que le test est faux *et*
ASSERT_TRUE(result) << result.description(); // affiche la string result.description() (qui contient la raison
// de l'erreur)
string c_dump_ref
que vous initialisez avec le contenu du dump que vous vous attendez à avoir avec c.dump()
.EXPECT_EQ(c.dump(), c_dump_ref);
unitTests.cpp(22): error: Expected equality of these values:
c.dump()
Which is: "Circle \"testCircle\", x: 0, y: 1, r: 2, color: \"Black\"\n"
c_dump_ref
Which is: "Circle \"testCircle\", x: 0, y: 1, r: 2, color: \"Orange\"\n"
unitTests.cpp
. il s’attendait à une égalité entre c.dump()
et c_dump_ref
et a trouvé une (ou plusieurs) différences. C’est pourquoi il vous affiche les valeurs.
"Orange"
au lieu de "Black"
dans c_dump_ref
) afin de voir le comportement de GoogleTest en cas d’erreur.TEST(TestAnalyseXML, TestGroup)
et testez-le. Si un problème est détecté, comme les chaînes de caractères sont sur plusieurs lignes, GoogleTest vous affiche en plus un diff pour vous aider retrouver la (ou les) ligne(s) qui posent souci. Par exemple : unitTests.cpp(53): error: Expected equality of these values:
g.dump()
Which is: "Group \"testGroup\", x: 0, y: 1, children: [\n| Circle \"testCircle1\", x: 2, y: 3, r: 4, color: \"Black\"\n| Circle \"testCircle2\", x: 5, y: 6, r: 7, color: \"Black\"\n]\n"
g_dump_ref
Which is: "Group \"testGroup\", x: 0, y: 1, children: [\n| Circle \"testCircle1\", x: 2, y: 3, r: 4, color: \"Orange\"\n| Circle \"testCircle2\", x: 5, y: 6, r: 7, color: \"Black\"\n]\n"
With diff:
@@ -1,4 +1,4 @@
Group \"testGroup\", x: 0, y: 1, children: [
-| Circle \"testCircle1\", x: 2, y: 3, r: 4, color: \"Black\"
+| Circle \"testCircle1\", x: 2, y: 3, r: 4, color: \"Orange\"
| Circle \"testCircle2\", x: 5, y: 6, r: 7, color: \"Black\"
]\n
unitTests.cpp
. Le diff
et les lignes subséquentes commençant par “-” et “+” montrent que le souci concerne la 2ème ligne contenue dans chaque chaîne de caractères : A vous de corriger.
"Orange"
au lieu de "Black"
dans c_dump_ref
) afin de voir le comportement de GoogleTest en cas d’erreur.Corrigé (à exploiter selon la procédure cmake
de ce document).
L’automatisation des tests facilite beaucoup la vie des développeur·ses.
C’est pourquoi, dans la suite des TPs, en général (cf. exercices sur les polynômes et sur l’outil de visualisation), nous commencerons par écrire des tests, puis réaliserons l’implémentation afin que ces tests soient corrects (Démarche TDD = Test Driven Development).
Et, c’est seulement après coup que nous nous préoccuperons des éventuels affichages faits par le programme.