28 mars 2025
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/core/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/core/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/core/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
:::xml_document doc;
pugi::xml_parse_result result = doc.load_string(sc.c_str());
pugiif (!result) {
std::cerr << result.description();
(1);
exit}
::xml_node node = doc.child("Circle"); pugi
#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/core/Circle.h
, après la déclaration des champs
de votre structure, ajoutez la déclaration :
std::string dump() const;
src/core/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/core/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/core/myMain.cpp
et en remplaçant :if (!result) {
<< result.description();
cerr (1);
exit}
(result) << result.description(); // Si jamais result est faux, indique que le test est faux *et*
ASSERT_TRUE// 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.