Skip to content
Snippets Groups Projects
Commit 0bb7435b authored by Derrode Stéphane's avatar Derrode Stéphane :dromedary_camel:
Browse files

first commit depot eleve

parents
Branches
No related tags found
No related merge requests found
Showing with 464 additions and 0 deletions
*A compléter*
\ No newline at end of file
seance1_4h/figures/AllClasses.png

29.7 KiB

seance1_4h/figures/Emprunt.png

30.4 KiB

seance1_4h/figures/LivreLecteur.png

64.6 KiB

<mxfile host="Electron" modified="2020-08-24T05:34:03.311Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.6.2 Chrome/83.0.4103.122 Electron/9.2.0 Safari/537.36" etag="JyznIXuSCOPoCPcxa0e5" version="13.6.2" type="device"><diagram name="Page-1" id="c4acf3e9-155e-7222-9cf6-157b1a14988f">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</diagram></mxfile>
\ No newline at end of file
seance1_4h/figures/LivreLecteurBiblio.png

72.4 KiB

# <center>TD1 : Bibliothèque</center>
_Remarque introductive_ : Les sujets de BE sont rédigés dans le format _Markdown_.
> _Markdown est un langage de balisage léger créé en 2004 par John Gruber avec l'aide d'Aaron Swartz. Son but est d'offrir une syntaxe facile à lire et à écrire. Un document balisé par Markdown peut être lu en l'état sans donner l'impression d'avoir été balisé ou formaté par des instructions particulières. Il peut être converti en HTML, en PDF ou en d'autres formats._
Si cela vous intéresse, vous trouverez des informations et un tutoriel en suivant ce lien : [Wikipédia markdown](https://fr.wikipedia.org/wiki/Markdown). Noter que savoir utiliser le format Markdown n'est pas nécessaire pour cet enseignement mais pourrait être d'un intérêt certain durant votre scolarité. Ici, il permet de faciliter les copier-coller de code que vous rencontrerez dans les sujets ; les formats propriétaires Word ou Pdf introduisent des caractères spéciaux ou cachés qui brouillent l'interpréteur Python, et génèrent bien souvent des incompréhensions pour les programmeurs débutants.
Pour améliorer la lisibilité, nous avons fait appel à quelques commandes html complémentaires.
---
# Objectif du sujet
Dans cet énoncé, nous abordons deux concepts de base de la programmation orientée objet, __l'encapsulation__ et la __composition__, qui ont été abordées lors du premier cours. Nous nous exercerons également aux diagrammes de classe du langage graphique UML. Pour vos propres diagrammes, vous pouvez utiliser https://app.diagrams.net pour dessiner en ligne (et sauvegarder localement vos diagrammes sur votre machine).
Dans ce BE, il s'agit de concevoir et de réaliser un programme _simple_ de gestion d'une bibliothèque, intégrant des lecteurs, des livres et des emprunts.
_Remarque_ : Cet énoncé part d'un problème simple et connu qui permet d'en faire la conception et la réalisation dans le temps qui nous est imparti par les contraintes scolaires. Les choix de conception et de réalisation sont donc orientés par ces contraintes et par les objectifs pédagogiques, à savoir : apprendre la programmation orientée objet en Python. Il est clair que le même problème dans un cadre professionnel serait traité d’une autre manière et une solution basée sur des bases de données émergerait naturellement, solution que nous écartons a priori car en dehors du périmètre de ce cours.
# Cahier des charges
Le cahier des charges de notre application est décrit ci-dessous. Il est volontairement donné de manière informelle.
On doit pouvoir gérer le fond documentaire d'une bibliothèque identifiée par son nom.
1. Pour simplifier, on considérera que tous les ouvrages sont des livres caractérisés par :
- Le nom de l’auteur,
- Le titre de l’ouvrage,
- Un numéro de livre (attribué de manière unique par les bibliothécaires),
- Le nombre d’exemplaires achetés.
1. Notre application doit être capable de gérer également des lecteurs. Chacun d’eux est caractérisé par :
- Son nom,
- Son prénom,
- Son adresse,
- Un numéro (entier positif attribué de manière unique par les bibliothécaires).
1. On doit pouvoir ajouter un lecteur à tout moment et bien sûr rechercher un lecteur (par son numéro de lecteur).
On doit également pouvoir ajouter des ouvrages (achat de livres), et rechercher un ouvrage (par numéro).
1. Tout lecteur peut emprunter des livres dans la bibliothèque. Un lecteur pour emprunter plusieurs livres différents simultanément ou à des dates différentes et un même livre peut être emprunté par plusieurs lecteurs (s’il existe plusieurs exemplaires). Au moment de l’emprunt, il faut donc vérifier qu’un exemplaire de l'ouvrage est bien disponible et qu'il n'a pas été emprunté par le même lecteur. De manière symétrique, il faut également gérer le retour des livres.
1. On désire également pouvoir éditer des états détaillés :
- Liste de tous les lecteurs
- Liste de tous les livres
- Liste de tous les emprunts
1. Da manière facultative, on souhaite pouvoir retirer un lecteur s'il n'a plus d'emprunt en cours, et retirer un exemplaire non emprunté d'un livre (désherbage ou vol). Un livre qui n'aurait plus d’exemplaire ne doit plus apparaître dans la liste des livres à disposition de la bibliothèque.
Cet énoncé a pour objectif de vous accompagner pour répondre au cahier des charges, __suivez le guide...__
_Remarque_ : Dans chaque titre de chapitre, vous verrez figurer un temps entre parenthèses : celui-ci correspond au temps approximatif à passer pour répondre aux questions du chapitre. Si vous prenez trop retard par rapport à ces _milestones_, demandez de l'aide à votre encadrant!
_Remarque_ : Même si ce n'est pas pas obligatoire, il vous est demandé de développer chaque classe dans un fichier python séparé (tous les fichiers seront enregistrés dans le même répertoire).
---
# Classe Lecteur et classe Livre (75 minutes)
**Point 1. du cahier des charges**
1. Dessinez la boîte UML de la classe __Lecteur__, en respectant le cahier des charges ci-dessus et les conventions syntaxiques présentées en cours.
* Quels attributs devront être publiques, lesquels devront être privés? Pour rappel: selon les principes de l'encapsulation, un attribut devra être privé si on veut/peut exercer un contrôle dessus.
* Prévoyez une méthode qui affiche l'état d'un lecteur (pour rappel: l'état d'un objet est l'ensemble des valeurs de ses attributs à un instant donné).
1. Dans un fichier appelé _Lecteur.py_, implémentez votre classe progressivement, et développez simultanément le programme principal qui permet de vérifier le comportement attendu de votre classe (vu en cours: un programme principal commence par la commande `if __name__ == '__main__')`. Pour être plus précis:
* Écrivez en premier lieu le constructeur de votre classe et créez un ou deux lecteurs dans votre programme principal. L'exécution du programme ne produit aucun résultat visible mais elle permet de vérifier que le programme est syntaxiquement correct. Ne passez pas à la méthode suivante tant que l'exécution produit des erreurs.
* Passez ensuite au codage de la méthode d'affichage (méthode dont le nom est `__str__`). Testez cette méthode en modifiant votre programme principal pour qu'il affiche les deux lecteurs précédemment créés. L'exécution du programme vous donne-t-elle satisfaction ?
1. Continuez ainsi pour les méthodes que vous avez prévues. Il est fort probable que nous soyons amenés à revenir sur cette classe par la suite, pour la compléter avec de nouvelles méthodes.
Un programme principal typique aura l'allure suivante:
```python
if __name__ == '__main__':
# Des lecteurs
L1 = Lecteur('Mzai', 'Ahmed', 'rue de la Paix',1)
L2 = Lecteur('Dupond', 'John', 'rue de la Gare',2)
L3 = Lecteur('Levgueni', 'Dimitri','rue La Fayette',3)
L4 = Lecteur('Rodriguez','Alfonso','rue du Stade', 4)
print('L1 -->', L1)
print('L2 -->', L2)
print('L3 -->', L3)
print('L4 -->', L4)
```
**Point 2. du cahier des charges**
4. Réalisez la même démarche pour la classe __Livre__, que vous coderez dans le fichier _Livre.py_. Un programme principal typique aura l'allure suivante:
```python
if __name__ == '__main__':
# Des livres
B1 = Livre('Le Père Goriot', 'Honoré de Balzac', 2,101)
B2 = Livre('Les Hauts de Hurlevent','Émilie Bronte', 2,102)
B3 = Livre('Le Petit Prince', 'Antoine de Saint-Éxupery',2,103)
B4 = Livre("L'Étranger", 'Albert Camus', 2,104)
print('B1 -->', B1)
print('B2 -->', B2)
print('B3 -->', B3)
print('B4 -->', B4)
```
---
# Classe Bibliothèque (75 minutes)
**Points 3. et 5. du cahier des charges**
1. Un bibliothèque gère une collection de livres et un répertoire de lecteurs (et des emprunts, mais nous verrons cela plus tard). Quels liens entre les classes __Bibliotheque__, __Livre__ et __Lecteur__ vous semblent les plus adaptés pour exprimer ces relations ? Dessinez alors le schéma UML entre ces 3 classes sur une feuille de papier. N'oubliez pas d'ajouter les cardinalité de part et d'autre des liens.
Dessinez ensuite le détail de la boîte UML de la classe __Bibliothèque__, de manière à répondre au point 3. du cahier des charges.
1. Dans un nouveau fichier appelé _Bibliotheque.py_, commencez par coder le constructeur de la classe __Bibliotheque__, en choisissant la structure de données adéquate pour gérer les lecteurs et les livres. Créez un programme principal, dans lequel vous créerez une bibliothèque (de nom _Michel Serre_ ?). Codez ensuite la méthode `__str__` qui affiche simplement le nom de la bibliothèque. Testez dans votre programme principal.
1. On se concentre sur les lecteurs. Codez successivement l'implémentation des méthodes permettant
* d'ajouter un lecteur (on fera l'hypothèse qu'on n'essaye pas d'ajouter un lecteur déjà présent),
* d'afficher la liste des lecteurs de la bibliothèque,
* de chercher un lecteur par son numéro,
* de chercher un lecteur par son nom,
N'oubliez pas de tester chacune des méthodes avant de passer à la suivante! N'hésitez pas à compléter la classe __Lecteur__ s'il vous manque des méthodes...
1. Faites de même avec les livres. On ne vérifiera pas si le livre déjà présent dans la collection avant de l'ajouter.
# Les emprunts (90 minutes)
**Points 4. et 5. du cahier des charges**
Un emprunt serra modélisé par un objet qui associe un lecteur (connu par son identifiant) avec un livre (connu par son identifiant) à une date donnée.
1. Dessinez la boite UML de la classe __Emprunt__. Celle-ci doit permettre de créer un nouvel emprunt, et d'afficher son état. Prévoyez des getter pour les attributs privés. Comment modéliser en UML les relations de la classe __Emprunt__ avec les classes __Lecteur__ et __Livre__?
1. Implémentez votre classe dans un fichier appelé _Emprunt.py_. Complétez, en parallèle de l'implémentation des méthodes, un programme principal qui pourra finalement avoir l'allure suivante:
```python
if __name__ == '__main__':
# Creation d'une Emprunt
E1 = Emprunt(1, 2)
print('E1 --> ', E1)
print("Num lecteur de l'emprunt E1: ", E1.get_numero_lecteur())
```
Résultat:
```console
E1 --> Emprunt - Numero lecteur : 1, Numero livre: 2, Date : 2020-08-23
Num lecteur de l'emprunt E1: 1
```
_Remarque_ : Pour la date, on pourra utiliser l’instruction `date.isoformat(date.today())`, en ayant pris soin d'importer la librairie, à l'aide de la commande `from datetime import date` (à positionner tout en haut de la classe).
1. À ce stade du développement, les emprunts sont indépendants de la bibliothèque. Compléter votre schéma UML précédent pour modéliser le fait que c'est la bibliothèque qui gère tous les emprunts. Quel nouvel attribut de la classe __Bibliotheque__ prévoyez-vous pour stocker la collection des emprunts ?
1. Codez la méthode `emprunt_livre(...)` dans la classe __Bibliotheque__. Cette méthode est sensée
1. rechercher le lecteur à partir de son numéro,
1. rechercher le livre à partir de son numéro et vérifier qu'il reste au moins un exemplaire à emprunter, et
1. créer un nouvel objet `emprunt` que l'on stockera dans la liste des emprunts.
_Tip_ : Pensez à
1. modifier la classe __Lecteur__ de manière à lui ajouter un compteur de livres empruntés (à sa création, un lecteur n'a pas d'emprunt). Prévoyez une méthode publique appelée `incremente_nb_emprunts()`.
1. modifier la classe __Livre__ de manière à lui ajouter un compteur qui tient à jour le nombre d'exemplaires disponibles (à sa création, un livre à autant d'exemplaires disponibles qu'il a de nombre d'exemplaires). Prévoyez une méthode publique appelée `decremente__dispo()` qui décrémentera le nombre d'exemplaires disponibles et renverra `True` s'il reste au moins un exemplaire, sinon `False`.
Pensez à modifier les méthodes `__str__` des 2 classes pour y inclure l'affichage de leur nouvel attribut.
Vous ajouterez également une méthode `affiche_emprunts()` pour visualiser la liste des emprunts et testerez vos nouvelles méthodes avant de passer à la suite. Pour tester votre méthode, ajouter ces quelques lignes à votre programme principal:
```python
if __name__ == '__main__':
...
# Quelques emprunts
print('\n--- Quelques emprunts :')
b.emprunt_livre(1,101)
b.emprunt_livre(1,104)
b.emprunt_livre(2,101)
b.emprunt_livre(3,101)
print(b.chercher_livre_numero(101))
# Affichage des emprunts, des lecteurs et des livres
print('\n--- Liste des emprunts :')
b.affiche_emprunts()
```
et vérifiez que vous obtenez un affichage similaire à celui-ci
```console
--- Quelques emprunts :
->Livre 101 - Emprunt impossible
Livre - Nom auteur: Honoré de Balzac, Titre ouvrage: Le Père Goriot, \
Nb examplaires: 2, Numero: 101, Nb dispo: 0.
--- Liste des emprunts :
Emprunt - Numero lecteur : 1, Numero livre: 101, Date : 2020-08-23
Emprunt - Numero lecteur : 1, Numero livre: 104, Date : 2020-08-23
Emprunt - Numero lecteur : 2, Numero livre: 101, Date : 2020-08-23
```
1. Pour simuler le rendu d'un livre par un lecteur à la bibliothèque, implémentez une méthode _retour\_livre(self, numero_lecteur, numero_livre)_. Pour cela, on pourra faire appel à une __méthode privée__ appelée _\_\_chercher_emprunt(self, numero_lecteur, numero_livre)_ qui renverra l'instance de l'emprunt s'il fait partie de la liste des emprunts, ou _None_ dans le cas contraire. La méthode _retour\_livre_ affichera un message d'erreur si l'emprunt n'existe pas. Dans le cas contraire, détruisez l'emprunt et pensez à mettre à jour le nombre d’exemplaires du livre ainsi que le nombre d’emprunts du lecteur.
Tester le retour dans votre programme principal grâce au code suivant :
``` python
if __name__ == '__main__':
...
# Quelques retours de livres
print('\n--- Quelques retours de livres :')
b.retour_livre(1,101)
b.retour_livre(1,102)
b.retour_livre(10,108)
```
qui devra afficher quelque chose de similaire à :
```console
--- Quelques retours de livres :
Aucun emprunt ne correspond a ces informations : 1 102
Aucun emprunt ne correspond a ces informations : 10 108
```
# Questions ouvertes supplémentaires
**Point 6. (facultatif) du cahier des charges**
1. Implémentez une méthode permettant de supprimer un livre (si tous les exemplaires sont rendus).
```python
def retrait_livre(self,numero):
# On cherche le livre
livre = self.chercher_livre_numero(numero)
if livre == None:
return False
# On verifie que le livre n'est pas en cours d'emprunt
for e in self.__emprunts:
if e.get_numero_livre()==numero:
return False
# On peut ici retirer le livre de la liste
self.__livres.remove(livre)
return True
```
Les commandes
```python
if __name__ == '__main__':
...
# Suppression de quelques livres
print('\n--- Suppression de quelques livres :')
rep = b.retrait_livre(101)
if not rep:
print('Retrait du livre impossible')
else:
print('Retrait du livre effectue')
b.retour_livre(2,101)
rep = b.retrait_livre(101)
if not rep:
print('Retrait du livre impossible')
else:
print('Retrait du livre effectue')
```
donnent
```console
--- Suppression de quelques livres :
Retrait du livre impossible
Retrait du livre effectue
```
1. Implémentez une méthode permettant de supprimer un lecteur (si il n'est redevable d'aucun emprunt).
```python
def retrait_lecteur(self,numero):
# On cherche le lecteur
lecteur = self.chercher_lecteur_numero(numero)
if lecteur == None:
return False
# On verifie qu'il n'a pas d'emprunt en cours
for e in self.__emprunts:
if e.get_numero_lecteur()==numero:
return False
# On peut ici retirer le lecteur de la liste
self.__lecteurs.remove(lecteur)
return True
```
Les commandes
```python
if __name__ == '__main__':
...
# Suppression de quelques lecteurs
print('\n--- Suppression de quelques lecteurs :')
rep = b.retrait_lecteur(1)
if not rep:
print('Retrait du lecteur impossible')
else:
print('Retrait du lecteur effectue')
b.retour_livre(1,104)
rep = b.retrait_lecteur(1)
if not rep:
print('Retrait du lecteur impossible')
else:
print('Retrait du lecteur effectue')
```
donnent
``` console
--- Suppression de quelques lecteurs :
Retrait du lecteur impossible
Retrait du lecteur effectue
```
1. Un livre qui n'aurait plus d’exemplaire ne doit plus apparaître dans la liste des livres à disposition de la bibliothèque.
1. Comment implémenter un mécanisme de vérification de l'unicité de l'identifiant d'un lecteur et de celui d'un livre ?
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="348px" height="181px" viewBox="-0.5 -0.5 348 181" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-09-02T07:51:36.488Z&quot; agent=&quot;5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36&quot; etag=&quot;fuW20ZwMJLSmCLgNqiah&quot; version=&quot;13.6.6&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;QcHEdDH7hetbROZjSo9L&quot; name=&quot;Page-1&quot;&gt;7Vptb5swEP41+biJd8LHkDabJrWa1kprP1rBCdYMjsBpyH79TLAD2CGFDPKyVZEifDZn5567x75zRuY0yr4kYBU+kADikaEF2ci8GxmGO3bYdy7YFgLLtQrBMkFBIdJLwRP6DblQ49I1CmBaG0gJwRSt6sI5iWM4pzUZSBKyqQ9bEFyfdQWWUBE8zQFWpT9RQMNCOjbcUv4VomUoZtYdr+iJgBjMf0kagoBsKiLzfmROE0Jo8RRlU4hz2wm7FO/NGnr3C0tgTNu8ED4uXp2H1ewh3fjPmanRb9rsE9fyBvCa/2C+WLoVFoBxMMkNyVpzDNIUzUemH9IIM4HOHtV1cKUwqJmWr+oLJBGkyZYN2JQGtbmRwoothSyBGFD0VgcEcFyXe3X7Gb4TxFZiaNwFDY3r2QqItLqKlKyTOeRvVQ0oKbKNdxRRkCwhVRSxh8rPLkU7fDpgZfz7WJlO3cTeiVDJevbrOxNU5vtQJWQdBzBXojFgNiGi8GkF5nnvhjFpHbcFwnhKMElYOyYxG+SnNCG/oBCODPN+4viOs+8RbHUU9jeYUJgdBV5Y1JOcX1i44hjWAcdwtWYfqFm9q4mtTtHAjRaANNzZXL+xuGiyfufAkBWdmcPsD9R6oDNF0cCouQpqmQIb4xFah6fOUBzLA0wGMFrG+Y7FYIRM7ueshNgRbMI7IhQE+TQHWbLOowsS0xmIEM4t9YwidnQ0tEe4Yd8/SATifsjQbjpSvEOGxlBkOFYA2v7XAJmmtP9bFwZIOEjnw1vKQpsePdO1sKhyVvB3H9bD5uQ5lykmq7SvnWAtS4LZPJFgZUXnJlhdzcPC2w3gfMghZ+shsC0psPWxfeHAVrMy/IFciwTi8pTcIkm7PkqGGaIvHND8+bXyfJdVG1vRiJm1XvjKdo3Xak/50q4l3mpEvKDUFhFRUGaLgdeymdgNDtp5M5EU6XIOPPRm0iY1xhit0ibe6ObKTaTRpmjh267p3fXDMIZcnzvAMLp2wIHkzb4/iuk13RWhr1dCXz8a+qcHsTDllcTmvowkoJXreW1j03C8z3Zd1ZkrILrTp080InwlwKmVC+9E4GQPkBUNDZuaYt/AaeHa3UMNx/1tTQ+Rfe591/twkTNQf4lzZxeRDwiqqqGv8NQyUHLDpdQB00VDTvRt9TB31nTRUEs0yQ0XWYeETt7z3UtDp9ZoVGLeJ0RpCFZwR8RkHZx0NfsX5ldY3HEnvjcb6lbXki4yxi0zphNudVmz/K9LwaflH4bM+z8=&lt;/diagram&gt;&lt;/mxfile&gt;"><defs/><g><path d="M 7 30 L 320.63 30" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 325.88 30 L 318.88 33.5 L 320.63 30 L 318.88 26.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 167 10 L 167 163.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 167 168.88 L 163.5 161.88 L 167 163.63 L 170.5 161.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="197" y="80" width="40" height="70" fill="none" stroke="#ea6b66" pointer-events="all"/><path d="M 197 80 L 197 30" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 197 80 L 167 80" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><rect x="307" y="30" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 40px; margin-left: 308px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Times New Roman; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">x</div></div></div></foreignObject><text x="327" y="44" fill="#000000" font-family="Times New Roman" font-size="12px" text-anchor="middle">x</text></switch></g><rect x="137" y="160" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 170px; margin-left: 138px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Times New Roman; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">y</div></div></div></foreignObject><text x="157" y="174" fill="#000000" font-family="Times New Roman" font-size="12px" text-anchor="middle">y</text></switch></g><path d="M 247 145.88 L 247 84.12" fill="none" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 247 148.88 L 245 144.88 L 247 145.88 L 249 144.88 Z" fill="#b3b3b3" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="all"/><path d="M 247 81.12 L 249 85.12 L 247 84.12 L 245 85.12 Z" fill="#b3b3b3" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="all"/><rect x="237" y="105" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 115px; margin-left: 238px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Times New Roman; color: #B3B3B3; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">h</div></div></div></foreignObject><text x="257" y="119" fill="#B3B3B3" font-family="Times New Roman" font-size="12px" text-anchor="middle">h</text></switch></g><rect x="197" y="160" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 170px; margin-left: 198px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Times New Roman; color: #B3B3B3; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">l</div></div></div></foreignObject><text x="217" y="174" fill="#B3B3B3" font-family="Times New Roman" font-size="12px" text-anchor="middle">l</text></switch></g><path d="M 201.12 160 L 232.88 160" fill="none" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 198.12 160 L 202.12 158 L 201.12 160 L 202.12 162 Z" fill="#b3b3b3" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="all"/><path d="M 235.88 160 L 231.88 162 L 232.88 160 L 231.88 158 Z" fill="#b3b3b3" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="all"/><ellipse cx="77" cy="90" rx="50" ry="30" fill="none" stroke="#b5739d" pointer-events="all"/><path d="M 77 90 L 76.5 30" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 167 89 L 77 89" fill="none" stroke="#000000" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 76.5 115.88 L 76.5 94.12" fill="none" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 76.5 118.88 L 74.5 114.88 L 76.5 115.88 L 78.5 114.88 Z" fill="#b3b3b3" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="all"/><path d="M 76.5 91.12 L 78.5 95.12 L 76.5 94.12 L 74.5 95.12 Z" fill="#b3b3b3" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="all"/><path d="M 72.88 89.5 L 31.12 89.5" fill="none" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 75.88 89.5 L 71.88 91.5 L 72.88 89.5 L 71.88 87.5 Z" fill="#b3b3b3" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="all"/><path d="M 28.12 89.5 L 32.12 87.5 L 31.12 89.5 L 32.12 91.5 Z" fill="#b3b3b3" stroke="#b3b3b3" stroke-miterlimit="10" pointer-events="all"/><rect x="37" y="70" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 80px; margin-left: 38px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Times New Roman; color: #B3B3B3; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">rx</div></div></div></foreignObject><text x="57" y="84" fill="#B3B3B3" font-family="Times New Roman" font-size="12px" text-anchor="middle">rx</text></switch></g><rect x="67" y="90" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 100px; margin-left: 68px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Times New Roman; color: #B3B3B3; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">ry</div></div></div></foreignObject><text x="87" y="104" fill="#B3B3B3" font-family="Times New Roman" font-size="12px" text-anchor="middle">ry</text></switch></g><path d="M 232 17.5 C 212 17.5 207 35 223 38.5 C 207 46.2 225 63 238 56 C 247 70 277 70 287 56 C 307 56 307 42 294.5 35 C 307 21 287 7 269.5 14 C 257 3.5 237 3.5 232 17.5 Z" fill="none" stroke="#67ab9f" stroke-miterlimit="10" pointer-events="all"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>
\ No newline at end of file
seance2_4h/figures/spyder-tests-dark.png

45.4 KiB

seance2_4h/figures/spyder-tests.png

40.3 KiB

seance2_4h/figures/spyder-unittest.png

4.52 KiB

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="701px" height="168px" viewBox="-0.5 -0.5 701 168" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-09-02T12:41:38.977Z&quot; agent=&quot;5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36&quot; etag=&quot;ruTGXMro8CwttfHMEE6F&quot; version=&quot;13.6.6&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;ZFG0j7v5SNXi1jvCDSdx&quot; name=&quot;Page-1&quot;&gt;7Zptb9owEMc/TV5SkYQE9pKHPkkwdWVapb2Z3MRJrJoYOaZAP/0uiU0Ah422S9NJlhDE53Nyvt/Z5B+w3PFic83RMpmxEFPL6YYby51YjtMf+PCeG7alwXPc0hBzEpYmuzLMyQuWxq60rkiIswNHwRgVZHloDFia4kAc2BDnbH3oFjF6eNUlirFmmAeI6tYHEoqktA6cfmW/wSRO1JVt/0vZs0DKWc4kS1DI1nsm99Jyx5wxUR4tNmNM89ypvJTjrk707gLjOBXnDHBiPhOb7Y8vs5+j+c2v+2A4CzqeU57mGdGVnPE9pBGlMSSgDFtsVS6yNVlQlEJrFLFUzGWPDW1ESZzCcQDBYA6GZ8wFgTQOZYdgS7AGCaHhFG3ZKg85Eyh4Uq1Rwjh5gdMiKs8J3VzIinD8A495PhLMXbBynIHPncqDfWSaoc2B4xRlQhoCRilaZuRxN40F4jFJR0wItpBOiAcyBtvLg8KQcSQYHzPKYKKTlBUJUbm5IpQedynw+eliirJMHqsxU5KqALInLIJEBcxWaYhD2VpiThYYsjtfooCksTRHexe0HDeKIicIiuxx9oT3ekL/0ffyNOqFI2spZ4Y3eyZZSNeYwYX5FlzUspY1LRe1q9rraons6j7ZXx5+Ty5NuSzj3amryoUDWbyvKWRfK2TLGZURD+ED5kqgMI9LGuYrtGRJbpGOUpU5xZE4WeRZyWda+Ex6leVeJiI3MRgb0aIsEhKGOC14CyRQWY0FcUZSUSTKG8EL8jnuXniWB4GPoW1XbXjl7lyMWQpzQaQgi6HU1zgv9xrmf94O/l4JEr3jn0de+b0H/O232+kLXw8f7nvrYNgJEbv62rFPcN8a7k1yVyM+gnt9yK4GvgNNarA3ib0/aHm56/crOfXEUG+Sut3ttb3avZpt/ggzJeXtVoFZ3azbb2K8AFoUV1C/58wnHVsD7+rg3RrIFD1iescyIgjLz89L3yP47fF1z7x/GzS0qvW93Bo71rCrMTZL+d2oP3AHr4+5r8G+pBT0vBGcRnCeEpyu/ekUp39KeRjF+ZodrH92KXwWxXnqSYNRnI1yb19xDmq1B5frPWSrx5qHpob7O7m3Ljl16VFg3xrsTWJvX3P6XaM5G+TbtubUd3OjOZtC3bbm9PXHhpZ7ZfSm0Zsn9KbnvVVvuk3tV3bd15HRm6/cvXY7wf+jN23zE2cr4FsXnL7+WPzi4sKA/tegP1Jh1odsft1qEO/ZSrKxb+6au08jNZphbTe3aUOz+rdm0bf3l1f38jc=&lt;/diagram&gt;&lt;/mxfile&gt;"><defs><clipPath id="mx-clip-4-31-212-26-0"><rect x="4" y="31" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-57-212-26-0"><rect x="4" y="57" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-83-212-26-0"><rect x="4" y="83" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-109-212-26-0"><rect x="4" y="109" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-143-212-26-0"><rect x="4" y="143" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-31-212-26-0"><rect x="244" y="31" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-57-212-26-0"><rect x="244" y="57" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-83-212-26-0"><rect x="244" y="83" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-109-212-26-0"><rect x="244" y="109" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-143-212-26-0"><rect x="244" y="143" width="212" height="26"/></clipPath><clipPath id="mx-clip-484-31-212-26-0"><rect x="484" y="31" width="212" height="26"/></clipPath><clipPath id="mx-clip-484-57-212-26-0"><rect x="484" y="57" width="212" height="26"/></clipPath><clipPath id="mx-clip-484-83-212-26-0"><rect x="484" y="83" width="212" height="26"/></clipPath><clipPath id="mx-clip-484-117-212-26-0"><rect x="484" y="117" width="212" height="26"/></clipPath></defs><g><path d="M 0 26 L 0 0 L 220 0 L 220 26" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/><path d="M 0 26 L 0 164 L 220 164 L 220 26" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 26 L 220 26" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="109.5" y="17.5">Rectangle</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-31-212-26-0)" font-size="12px"><text x="5.5" y="43.5">+ x: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-57-212-26-0)" font-size="12px"><text x="5.5" y="69.5">+ y: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-83-212-26-0)" font-size="12px"><text x="5.5" y="95.5">- l: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-109-212-26-0)" font-size="12px"><text x="5.5" y="121.5">- h: entier</text></g><path d="M 0 134 L 220 134" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-143-212-26-0)" font-size="12px"/><path d="M 240 26 L 240 0 L 460 0 L 460 26" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 26 L 240 164 L 460 164 L 460 26" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 26 L 460 26" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="349.5" y="17.5">Ellipse</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-31-212-26-0)" font-size="12px"><text x="245.5" y="43.5">+ x: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-57-212-26-0)" font-size="12px"><text x="245.5" y="69.5">+ y: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-83-212-26-0)" font-size="12px"><text x="245.5" y="95.5">- rx: double</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-109-212-26-0)" font-size="12px"><text x="245.5" y="121.5">- ry: double</text></g><path d="M 240 134 L 460 134" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-143-212-26-0)" font-size="12px"/><path d="M 480 26 L 480 0 L 700 0 L 700 26" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 480 26 L 480 138 L 700 138 L 700 26" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 480 26 L 700 26" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="589.5" y="17.5">?</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-31-212-26-0)" font-size="12px"><text x="485.5" y="43.5">+ x: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-57-212-26-0)" font-size="12px"><text x="485.5" y="69.5">+ y: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-83-212-26-0)" font-size="12px"><text x="485.5" y="95.5">...</text></g><path d="M 480 108 L 700 108" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-117-212-26-0)" font-size="12px"/></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="701px" height="286px" viewBox="-0.5 -0.5 701 286" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-09-02T12:43:26.686Z&quot; agent=&quot;5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36&quot; etag=&quot;WZoK8idbTfEnltmXA0ep&quot; version=&quot;13.6.6&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;ZFG0j7v5SNXi1jvCDSdx&quot; name=&quot;Page-1&quot;&gt;7Vtdb+I4FP01PFKRT+ARaJkZCVbdstqR5mVlEpNYNTFyTIH++r3BNiFxYJh2A+1sJNTGN7Zzfc/x9YkNLWe03H7haBVPWYhpy+6E25Zz37Ltbs+Hv5lhJw1u15WGiJNQmqzcMCOvWBk7yromIU4LFQVjVJBV0RiwJMGBKNgQ52xTrLZgtPjUFYqwYZgFiJrW7yQUsbT27G5u/4pJFOsnW35f3lkiXVmNJI1RyDZHJueh5Yw4Y0JeLbcjTLPY6bjIduMTdw+OcZyISxrYEZ+K7e7v/vTHcPb1n6dgMA3ani27eUF0rUb8BGFESQQBkG6LnY5FuiFLihIoDRcsETN1x4IyoiRK4DoAZzAHwwvmgkAYB+qGYCuwBjGh4QTt2DpzORUoeNalYcw4eYVuEVV9wm0uFCNsv1BjlrUEcwesHKdQ51HHwSqZpmhbqDhBqVCGgFGKVimZH4axRDwiyZAJwZaqEuKB8sHyMqcwRBwJxkeMMhjofcL2AdGxGRNKy7c08Fl3EUVpqq51mwlJtAPpMxZBrB1m6yTEoSqtMCdLDNGdrVBAkkiZF0cPbNnOYrGwg2AfPc6e8dGd0J/7XhZGkziKSxlmeHtkUkT6ghk8mO+gip7WitNqUju6vMmnyIH3cWF6KL4hNS2jQ9c5c+FCkfdXiOwYRG5DEfoYwD8YKQFalgkNoxVGqBRqCxNITXKKF+IkxVOJzmRf597NLU8qDJmJQdsF3ZMiJmGIkz3aAgkkubjHm5FE7MPkDeED0Rx17ryWB46PoGzlZfhk1bkYsQTGgsgeVwxE3+CM7BWIn08GP+eBAt72L8Nd13sP7N/+/DZ55ZvB9yd3EwzaIWLjP9pm+spQjxvU60Rdt7gG6tUuewbsBsqUyOS7R1kv3dabIF4CWBTnmP6VQX7ftgzcHRN3pwJjiuaYPrKUCMKy/rmsW8L+ZvB2e5fB26sLXd9Ed2S3Bh0D42YmvxPq3hXzd7XLXQPrB0pB3Dfqs1Gfp9SnY308+dmrFCJ8K5VIyNbziheqJn9VJ4PPoz9NIbKHfdfAXifsNxegfqcRoPXBe0UBWjmpG/15LaRvrj99cwOh5Ywb7dlozxPa0/PeqD17tVHY3Pm8u7trctX5XHWY+B9Sa1a73Ox61QjvpZqyLtFhVUjKRnXUArV/YcqubyabuwUGyjgJB9n5NZTmlGVr/jAWS60QcBhhrUIghDGLWILoQ24tLaHQm17LfVnMlmu9GheyhX1uoUzZmgf4nAZUIwEFE+FzsLkquNlIzoJ2hJJXAZK2cUyRIC+44G4VcuoJjxlRj/aUnNK63u8Uu5AjV63so2P2cke9UkduqSMZGaMjABvtjqqpiXTSYatfeo7XOeuXa5+tDxfSg5zVBwzeQfT+70v07uckut0p8aD3RqK75Rmjsf0J0f8rbnUrFszfhFt6Sfts3DIocW1u/WoS9ctvcx8xibrmftSY8WVzQtbsUpzapSifkB2S/u1OyFzzpLdlD6XLzZd1Ln3ZOeSCz7Nt4VWt00PpYAN8fcDf/JDMveAtt9mveiu8t/6WVtcy0f1/71cV5cBpVXEsIQwNVRtfajxqg2L+MwapevPfgjgP/wI=&lt;/diagram&gt;&lt;/mxfile&gt;"><defs><clipPath id="mx-clip-4-201-212-26-0"><rect x="4" y="201" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-227-212-26-0"><rect x="4" y="227" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-261-212-26-0"><rect x="4" y="261" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-201-212-26-0"><rect x="244" y="201" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-227-212-26-0"><rect x="244" y="227" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-261-212-26-0"><rect x="244" y="261" width="212" height="26"/></clipPath><clipPath id="mx-clip-484-201-212-26-0"><rect x="484" y="201" width="212" height="26"/></clipPath><clipPath id="mx-clip-484-235-212-26-0"><rect x="484" y="235" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-31-212-26-0"><rect x="244" y="31" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-57-212-26-0"><rect x="244" y="57" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-91-212-26-0"><rect x="244" y="91" width="212" height="26"/></clipPath></defs><g><path d="M 0 196 L 0 170 L 220 170 L 220 196" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/><path d="M 0 196 L 0 282 L 220 282 L 220 196" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 196 L 220 196" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="109.5" y="187.5">Rectangle</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-201-212-26-0)" font-size="12px"><text x="5.5" y="213.5">- l: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-227-212-26-0)" font-size="12px"><text x="5.5" y="239.5">- h: entier</text></g><path d="M 0 252 L 220 252" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-261-212-26-0)" font-size="12px"/><path d="M 240 196 L 240 170 L 460 170 L 460 196" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 196 L 240 282 L 460 282 L 460 196" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 196 L 460 196" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="349.5" y="187.5">Ellipse</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-201-212-26-0)" font-size="12px"><text x="245.5" y="213.5">- rx: double</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-227-212-26-0)" font-size="12px"><text x="245.5" y="239.5">- ry: double</text></g><path d="M 240 252 L 460 252" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-261-212-26-0)" font-size="12px"/><path d="M 480 196 L 480 170 L 700 170 L 700 196" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 480 196 L 480 256 L 700 256 L 700 196" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 480 196 L 700 196" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="589.5" y="187.5">?</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-201-212-26-0)" font-size="12px"><text x="485.5" y="213.5">...</text></g><path d="M 480 226 L 700 226" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-235-212-26-0)" font-size="12px"/><path d="M 120 170 L 120 150 L 350 150 L 350 132.24" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 350 114.24 L 359 132.24 L 341 132.24 Z" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 350 170 L 350 132.24" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 350 114.24 L 359 132.24 L 341 132.24 Z" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 580 170 L 580 150 L 350 150 L 350 132.24" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 350 114.24 L 359 132.24 L 341 132.24 Z" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 26 L 240 0 L 460 0 L 460 26" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 26 L 240 112 L 460 112 L 460 26" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 26 L 460 26" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="349.5" y="17.5">Forme</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-31-212-26-0)" font-size="12px"><text x="245.5" y="43.5">+ x: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-57-212-26-0)" font-size="12px"><text x="245.5" y="69.5">+ y: entier</text></g><path d="M 240 82 L 460 82" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-91-212-26-0)" font-size="12px"/></g></svg>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="701px" height="482px" viewBox="-0.5 -0.5 701 482" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-09-02T12:43:54.091Z&quot; agent=&quot;5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36&quot; etag=&quot;lFbpCapjlEMSZ9oZFN9N&quot; version=&quot;13.6.6&quot; type=&quot;device&quot;&gt;&lt;diagram id=&quot;ZFG0j7v5SNXi1jvCDSdx&quot; name=&quot;Page-1&quot;&gt;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&lt;/diagram&gt;&lt;/mxfile&gt;"><defs><clipPath id="mx-clip-4-231-212-26-0"><rect x="4" y="231" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-257-212-26-0"><rect x="4" y="257" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-291-212-34-0"><rect x="4" y="291" width="212" height="34"/></clipPath><clipPath id="mx-clip-4-325-212-26-0"><rect x="4" y="325" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-351-212-26-0"><rect x="4" y="351" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-377-212-26-0"><rect x="4" y="377" width="212" height="26"/></clipPath><clipPath id="mx-clip-4-403-212-32-0"><rect x="4" y="403" width="212" height="32"/></clipPath><clipPath id="mx-clip-4-435-212-46-0"><rect x="4" y="435" width="212" height="46"/></clipPath><clipPath id="mx-clip-244-231-212-26-0"><rect x="244" y="231" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-257-212-26-0"><rect x="244" y="257" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-291-212-34-0"><rect x="244" y="291" width="212" height="34"/></clipPath><clipPath id="mx-clip-244-325-212-26-0"><rect x="244" y="325" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-351-212-26-0"><rect x="244" y="351" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-377-212-26-0"><rect x="244" y="377" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-403-212-34-0"><rect x="244" y="403" width="212" height="34"/></clipPath><clipPath id="mx-clip-244-437-212-46-0"><rect x="244" y="437" width="212" height="46"/></clipPath><clipPath id="mx-clip-484-231-212-26-0"><rect x="484" y="231" width="212" height="26"/></clipPath><clipPath id="mx-clip-484-265-212-26-0"><rect x="484" y="265" width="212" height="26"/></clipPath><clipPath id="mx-clip-484-291-212-26-0"><rect x="484" y="291" width="212" height="26"/></clipPath><clipPath id="mx-clip-484-317-212-38-0"><rect x="484" y="317" width="212" height="38"/></clipPath><clipPath id="mx-clip-484-355-212-46-0"><rect x="484" y="355" width="212" height="46"/></clipPath><clipPath id="mx-clip-244-31-212-26-0"><rect x="244" y="31" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-57-212-26-0"><rect x="244" y="57" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-91-212-26-0"><rect x="244" y="91" width="212" height="26"/></clipPath><clipPath id="mx-clip-244-117-212-26-0"><rect x="244" y="117" width="212" height="26"/></clipPath></defs><g><path d="M 0 226 L 0 200 L 220 200 L 220 226" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/><path d="M 0 226 L 0 476 L 220 476 L 220 226" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 0 226 L 220 226" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="109.5" y="217.5">Rectangle</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-231-212-26-0)" font-size="12px"><text x="5.5" y="243.5">- l: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-257-212-26-0)" font-size="12px"><text x="5.5" y="269.5">- h: entier</text></g><path d="M 0 282 L 220 282" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-291-212-34-0)" font-size="12px"><text x="5.5" y="303.5">+ constructeur(x: entier, y: entier,</text><text x="5.5" y="317.5">l: entier, h: entier)</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-325-212-26-0)" font-size="12px"><text x="5.5" y="337.5">+ affiche()</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-351-212-26-0)" font-size="12px"><text x="5.5" y="363.5">+ get_dim(): l: entier, h: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-377-212-26-0)" font-size="12px"><text x="5.5" y="389.5">+ set_dim(l: entier, h: entier)</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-403-212-32-0)" font-size="12px"><text x="5.5" y="415.5">+ contient_point(x: entier, y: entier):</text><text x="5.5" y="429.5">booléen</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-4-435-212-46-0)" font-size="12px"><text x="5.5" y="447.5">+ redimension_par_points(x0: entier,</text><text x="5.5" y="461.5">y0: entier, x1: entier, y1: entier)</text></g><path d="M 240 226 L 240 200 L 460 200 L 460 226" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 226 L 240 478 L 460 478 L 460 226" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 226 L 460 226" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="349.5" y="217.5">Ellipse</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-231-212-26-0)" font-size="12px"><text x="245.5" y="243.5">- rx: double</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-257-212-26-0)" font-size="12px"><text x="245.5" y="269.5">- ry: double</text></g><path d="M 240 282 L 460 282" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-291-212-34-0)" font-size="12px"><text x="245.5" y="303.5">+ constructeur(x: entier, y: entier,</text><text x="245.5" y="317.5">rx: double, ry: double)</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-325-212-26-0)" font-size="12px"><text x="245.5" y="337.5">+ affiche()</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-351-212-26-0)" font-size="12px"><text x="245.5" y="363.5">+ get_dim(): rx: double, ry: double</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-377-212-26-0)" font-size="12px"><text x="245.5" y="389.5">+ set_dim(rx: double, ry: double)</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-403-212-34-0)" font-size="12px"><text x="245.5" y="415.5">+ contient_point(x: entier, y: entier):</text><text x="245.5" y="429.5">booléen</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-437-212-46-0)" font-size="12px"><text x="245.5" y="449.5">+ redimension_par_points(x0: entier,</text><text x="245.5" y="463.5">y0: entier, x1: entier, y1: entier)</text></g><path d="M 480 226 L 480 200 L 700 200 L 700 226" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 480 226 L 480 396 L 700 396 L 700 226" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 480 226 L 700 226" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="589.5" y="217.5">?</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-231-212-26-0)" font-size="12px"><text x="485.5" y="243.5">...</text></g><path d="M 480 256 L 700 256" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-265-212-26-0)" font-size="12px"><text x="485.5" y="277.5">+ constructeur(...)</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-291-212-26-0)" font-size="12px"><text x="485.5" y="303.5">+ affiche()</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-317-212-38-0)" font-size="12px"><text x="485.5" y="329.5">+ contient_point(x: entier, y: entier):</text><text x="485.5" y="343.5">booléen</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-484-355-212-46-0)" font-size="12px"><text x="485.5" y="367.5">+ redimension_par_points(x0: entier,</text><text x="485.5" y="381.5">y0: entier, x1: entier, y1: entier)</text></g><path d="M 120 200 L 120 180 L 350 180 L 350 158.24" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 350 140.24 L 359 158.24 L 341 158.24 Z" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 350 200 L 350 158.24" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 350 140.24 L 359 158.24 L 341 158.24 Z" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 580 200 L 580 180 L 350 180 L 350 158.24" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 350 140.24 L 359 158.24 L 341 158.24 Z" fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 26 L 240 0 L 460 0 L 460 26" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 26 L 240 138 L 460 138 L 460 26" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><path d="M 240 26 L 460 26" fill="none" stroke="#d6b656" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" font-weight="bold" pointer-events="none" text-anchor="middle" font-size="12px"><text x="349.5" y="17.5">Forme</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-31-212-26-0)" font-size="12px"><text x="245.5" y="43.5">+ x: entier</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-57-212-26-0)" font-size="12px"><text x="245.5" y="69.5">+ y: entier</text></g><path d="M 240 82 L 460 82" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none"/><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-91-212-26-0)" font-size="12px"><text x="245.5" y="103.5">+ constructeur(x: entier, y: entier)</text></g><g fill="#000000" font-family="Helvetica" pointer-events="none" clip-path="url(#mx-clip-244-117-212-26-0)" font-size="12px"><text x="245.5" y="129.5">+ deplacement(dx: entier, dy: entier)</text></g></g></svg>
\ No newline at end of file
# <center>TD2 : Modélisation de formes géométriques</center>
Nous allons aborder dans ce TD le concept d'héritage de la programmation objet, et l'utilisation de tests unitaires pour guider le développement logiciel et améliorer sa qualité.
Le but de ce TD est de concevoir un module pour manipuler des formes géométriques avec Python. Ce module sera utilisé dans les TDs suivants, donc les tests seront essentiels pour limiter les éventuels bugs. Vous commencerez par définir les classes et leurs attributs, puis écrirez les tests unitaires de votre module, et terminerez par l'implémentation des méthodes.
# Modélisation avec UML (1h)
Les formes géométriques sont représentées par des classes, et l'héritage sera utilisé pour factoriser les propriétés communes. Nous nous limitons à un repère à deux dimensions orthonormé, avec les axes croissant vers la droite et le bas. Les coordonnées dans ce repère sont des entiers relatifs (c'est-à-dire possiblement négatifs). Dans cet espace, nous choisissons de représenter les formes suivantes :
* Les rectangles caractérisés par leur origine (`x`, `y`) et leurs dimensions (`l`, `h`).
* Les ellipses caractérisées par leur origine (`x`, `y`) et leurs rayons aux axes (`rx`, `ry`).
* Un type de forme de votre choix (ex. triangle, polygone, étoile, ...), qui possède au moins une origine (`x`, `y`).
<center><img src="figures/formes.svg" style="width:50%"/></center>
__Exercice 1 -__ Représentez les 3 classes dans un diagramme de classes UML (_voir https://app.diagrams.net pour dessiner en ligne, avec l'onglet UML sur la gauche_). Il est recommandé de commencer les noms des classes par une majuscule et les attributs par une minuscule. Les attributs devraient-ils être publics ou privés ?
Les attributs `x` et `y` étant partagés par les trois classes, on introduit l'héritage pour les regrouper. Toutes les formes géométriques hériteront d'une même classe __Forme__. L'intérêt de cette classe est double :
* Du point de vue des développeurs du module, les méthodes dont le code est identique entre formes (ex. translation) sont fusionnées dans __Forme__, réduisant la quantité de code à (et donc la multiplication des erreurs possibles).
* Du point de vue des utilisateurs du module, on peut écrire du code qui manipule des rectangles et des ellipses (ex. système de collisions de formes) sans avoir à écrire du code séparément pour les rectangles et les ellipses. Cet aspect sera illustré dans un prochain TD.
__Exercice 2 -__ Mettez à jour le diagramme UML en incluant la classe __Forme__ et les relations d'héritage. Seuls les attributs seront inclus pour le moment.
Enfin, on vous demande de supporter a minima pour chaque forme les méthodes suivantes :
* `deplacement(dx, dy)`, qui effectue une translation selon un vecteur donné.
* `contient_point(x, y)`, qui renvoie `True` si et seulement si le point donné est à l'intérieur de la forme ou sur sa frontière.
* `redimension_par_points(x0, y0, x1, y1)`, qui redimensionne la forme pour faire correspondre sa [boîte englobante](https://en.wikipedia.org/wiki/Minimum_bounding_rectangle) avec celle représentée par les points donnés.
__Exercice 3 -__ Complétez le diagramme UML avec ces méthodes. Les constructeurs devront également être renseignés (méthode `__init__` en Python), ainsi que les méthodes d'affichage (méthode `__str__` en Python).
__Exercice 4 -__ Écrivez un squelette de code correspondant à votre diagramme UML, dans un fichier _formes.py_. Seuls les constructeurs devront être implémentés. À l'intérieur des autres méthodes, vous mettrez l'instruction `pass` de Python (qui ne fait rien mais vous rappelle que le code est inachevé).
# Tests unitaires (1h)
Il convient à présent de rédiger des tests, qui échoueront tant que chaque fonction ne sera pas implémentée et correcte. Dans la méthodologie _Test Driven Development_, on les écrit toujours avant le code, au début ils échouent tous, et à mesure de l'avancement du projet le nombre de tests passés augmente. Nous utiliserons le module _pytest_ présenté en cours.
## Installation de _pytest_
Nous allons d'abord installer _pytest_, ainsi qu'un module permettant de lancer les tests depuis l'interface de Spyder. Ouvrez le terminal d'Anaconda (sous Windows, Menu Démarrer -> Anaconda -> Anaconda Prompt, sous Linux/Mac le terminal de base suffit). Exécutez-y la commande suivante :
```sh
conda install -c spyder-ide spyder-unittest pytest
```
❗ Si vous rencontrez une erreur comme `conda: command not found`, c'est que l'exécutable `conda` n'est présent dans aucun des dossiers visités par le terminal (essayez `echo %PATH%` pour en afficher la liste sous Windows, et `echo $PATH` sous Linux/Mac). Sous Windows, vérifiez que vous ouvrez bien le terminal d'Anaconda (pas le terminal par défaut du système). Sous Linux/Mac, la commande `export PATH=~/anaconda3/bin:/usr/local/anaconda3/bin:/usr/anaconda3/bin:$PATH` va ajouter (temporairement) une liste de répertoires usuels à la liste de recherche.
Une fois les modules installés, __redémarrez Spyder__ et créez un fichier _test_formes.py_ avec l'exemple de code suivant :
```python
from formes import *
def test_heritage():
assert issubclass(Rectangle, Forme)
assert issubclass(Ellipse, Forme)
```
__Exécutez ce fichier__ dans Spyder (même s'il ne fait rien), ce qui a pour effet d'initialiser le répertoire courant de Spyder à votre répertoire de travail. Allez ensuite dans le menu Run -> Run unit tests, pour configurer le module _spyder-unittest_.
<center>![](figures/spyder-unittest.png)</center>
Sélectionnez _pytest_, vérifiez que le dossier indiqué correspond à votre dossier de travail (celui contenant les fichiers _formes.py_ et _test_formes.py_), et validez. Un nouvel onglet _Unit testing_ apparaît dans l'espace en haut à droite, avec un bouton _Run tests_. Lorsque vous cliquez dessus :
* _pytest_ cherche (dans le dossier que vous venez de configurer) tous les fichiers de la forme _test\_\*.py_ et _\*\_test.py_.
* Dans chacun de ces fichiers, _pytest_ exécute toutes les fonctions préfixées par `test`.
* Chaque test qui s'exécute sans déclencher d'exception est considéré valide.
* La fonction `test_heritage` dans le fichier _test_formes.py_ correspond à ces critères, donc elle est exécutée et son résultat contribue à un test "passé".
<center>![](figures/spyder-tests.png)</center>
## Définition des tests
__Exercice 5 -__ Dans le fichier _test_formes.py_, ajoutez une fonction `test_Rectangle_contient_point()` qui instancie un __Rectangle__ avec des coordonnées de votre choix, et vérifie avec `assert` que la méthode `contient_point` renvoie le bon résultat pour différentes coordonnées. L'exécution des tests doit échouer puisque votre code est encore vide.
Pour l'exercice suivant on vous donne un exemple d'implémentation de la méthode `contient_point` pour la classe __Rectangle__. La classe __Forme__ a été omise pour réduire la taille du code (mais dans votre code elle devra bien être présente).
```python
class Rectangle:
def __init__(self, x, y, l, h):
self.x = x
self.y = y
self.__l = l
self.__h = h
def contient_point(self, x, y):
return self.x < x < self.__l or \
self.y < y < self.__h
```
__Exercice 6 -__ Cette méthode est buggée. Comment la corriger ? Vos tests l'avaient-ils repéré ? Si ce n'est pas le cas, trouvez les coordonnées qui donnent un mauvais résultat et ajoutez-les en tests dans la fonction `test_Rectangle_contient_point`.
# Implémentation des méthodes (2h)
__Exercice 7 -__ Implémentez les méthodes d'affichage (`__str__`) de chacune des classes. Il ne sera pas nécessaire d'écrire des tests pour ces méthodes.
__Exercice 8 -__ Implémentez les méthodes d'accès getter/setter pour les champs privés de chacune des classes. À l'aide de [pytest.raises](https://docs.pytest.org/en/stable/assert.html#assertions-about-expected-exceptions), vous testerez le déclenchement d'erreurs si on essaie d'accéder directement aux attributs.
__Exercice 9 -__ Implémentez les méthodes `contient_point` des deux sous-classes restantes. Vous fournirez pour chacune une fonction de tests avec des jeux de coordonnées pertinents.
__Exercice 10 -__ Implémentez les méthodes `redimension_par_points` de chacune des sous-classes. Vous fournirez également des tests validant leur fonctionnement quels que soient les points en entrée.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment