Analyse d'un fichier XML

Table des matières

1 Introduction

Le but de cet exercice est :

  1. d'analyser et parcourir un fichier au format XML,
  2. d'automatiser les tests vérifiant que l'analyse et le parcours sont corrects.

A la fin de cet exercice, vous serez capable d'analyser un fichier 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 :

  • Un Group de label testGroup et de coordonnées (x,y) (0,1)
  • Ce groupe contenant deux Circle :
    • Un Circle de label testCircle1, de coordonnées (x,y) (2,3), de rayon r 4 et de color Black,
    • Un Circle de label 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 les octets sensés être lus dans un fichier au format XML.

Par ailleurs, à la fin de cet exercice, vous aurez automatisé vos tests avec Google Test.

2 Création du projet

  • Décompressez l'archive SampleGoogleTestAndPugixml.zip dans le répertoire de votre choix (par exemple, dans C:\users\votre_login\CSC4526).
  • Renommez le répertoire SampleGoogleTest qui a été créé en, par exemple, AnalyseXML.
  • Exploitez le canevas de projet obtenu selon la procédure cmake de ce document.

3 Codage de la lecture d'un Circle

  • 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"/>
  • Créer un fichier Circle.h dans lequel vous définissez une structure Circle contenant :
    • un champ label de type string,
    • trois champs x, y, r de type double,
    • un champ color de type string,
  • Dans le fichier myMain.cpp
    • Ajoutez #include "pugixml.hpp"
      • Cette ligne signifie que, dans votre code, vous allez nous servir des classes et méthodes offertes par la bibliothèque pugixml.
    • Ajoutez #include "Circle.h"
    • Ajoutez la définition d'une string contenant un Circle au format XML :
std::string s = R"(<?xml version = "1.0"?>
		   <Circle label="testCircle" x="0" y="1" r="2" color="Black" />)";
  • Toujours dans le fichier myMain.cpp, ajoutez les lignes suivantes permettant de charger le Circle stocké dans s dans la variable node :
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_string(s.c_str());
if (!result) {
    cerr << result.description();
    exit(1);
} 
pugi::xml_node node = doc.child("Circle");
  • Il ne vous reste plus qu'à définir un Circle c en initialisant ses différents champs (par exemple, pour label : node.attribute("label").as_string()).
  • Pour vérifier que le Circle c a été correctement initialisé à partir du XML, nous allons l'afficher.
    • Dans Circle.h, après la déclaration des champs de votre structure, ajoutez la déclaration : std::string dump() const;
    • Créez un fichier Circle.cpp dans lequel vous implémentez Circle::dump
      • Cette méthode commence par définir un ostringstream oss;
      • Elle fait autant de oss << donnee_a_stocker_dans_la_sortie que nécessaire
      • Elle se termine par return oss.str();
    • Dans myMain.cpp, ajoutez la ligne cout << c.dump();
    • Exécutez votre code en sélectionnant 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"

4 Codage de la lecture d'un Group

  • Procédez de la même manière 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
  vecCircle: [
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, cherchez un exemple de boucle avec le mot children dans la documentation pugixml.
  • Si vous souhaitez (et avez le temps de) aller plus loin, remarquez que, dans cet affichage, 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, par exemple :
Group "testGroup"
  x: 0
  y: 1
  vecCircle: [
  Circle "testCircle1"
    x: 2
    y: 3
    r: 4
    color: "Black"
  Circle "testCircle2"
    x: 5
    y: 6
    r: 7
    color: "Black"
  ]

5 Automatisation de vos tests

  • Le code développé jusqu'à maintenant présente l'inconvénient de nécessiter l'intervention d'un humain pour vérifier que la lecture d'un Circle ou d'un Group à partir d'un fichier XML s'est bien passée. Dans cette partie, nous automatisons cette vérification.
  • Dans votre fichier unitTests.cpp
    • Renommez TEST(TestCaseName, TestName) en TEST(TestReadXML, TestCircle).
    • Ajoutez-y la création de votre circle c à partir de votre XML, en recopiant le code que vous avez développé dans myMain.cpp et en remplaçant le if (!result) {...} par ASSERT_TRUE(result) << result.description(); // Si jamais result est faux, indique que le test est faux *et* affiche la string result.description() (qui contient la raison de l'erreur)
    • Ajoutez la définition d'une string c_dump_ref que vous initialisez avec le contenu du dump que vous vous attendez à avoir avec c.dump().
    • Ajoutez la vérification EXPECT_EQ(c.dump(), c_dump_ref);
  • Exécutez votre test unitaire en exécutant la procédure Google Test de ce document.
  • Si votre test est FAILED, regardez les lignes au dessus du mot "FAILED" : Elles vous indiquent les différences trouvées entre les deux affichages. Par exemple :
@@ -3,3 +3,3 @@
   y: 1
   r: 2
-  color: \"Black\"\n
+  color: \"Black\"
  • signifie que Google Test a trouvé une différence au niveau de la troisième ligne de chacune des chaînes de caractères : La première chaîne c.dump() se termine par un \n, ce qui n'est pas le cas de la deuxième c_dump_ref. Corrigez le souci.
  • Ecrivez TEST(TestAnalyseXML, TestGroup) et testez-le.