TP-1 — Setup de l’environnement
Docker, Java 17, Gradle, JUnit 5, Python et Git : l’environnement des TP du cours
Installer Docker et l’image du cours, puis vérifier, commande par commande, la chaîne de travail de tous les TP : compiler et exécuter un programme Java, créer un projet Gradle, lancer des tests JUnit 5, produire une figure avec Python, enregistrer les versions du projet avec Git. Le TP-1 est à terminer avant la Séance 1, dont le TP commence par new-project et gradle --no-daemon test. Durée : une séance de TP.
À rendre avant la Séance 1. Un compte rendu d’une à deux pages (PDF, nommé rapport-tp1-PRENOM-NOM.pdf) : les outils installés et leurs versions, les pannes rencontrées et leur solution, les réponses aux questions marquées Observer, Expliquer et Modifier, et trois captures d’écran : le message d’accueil du conteneur, le résultat de gradle --no-daemon test avec les tests passés, la figure test_complexites.png.
Principe et prérequis
Les fichiers s’éditent sur la machine, avec l’éditeur de son choix (VS Code, Sublime Text, Notepad++…) réglé en UTF-8. Ils se compilent, se testent et s’exécutent dans un conteneur Docker : un environnement Linux isolé, construit à partir de l’image elbachirtoure/dsa-java:v2.0, qui voit le dossier de travail de la machine sous le nom /workspace. Tous les étudiants disposent ainsi des mêmes versions, quelle que soit leur machine.
| Outil de l’image | Commandes | Usage |
|---|---|---|
| OpenJDK 17 (Temurin) | javac, java |
compiler et exécuter du Java |
| Gradle 8.14.3 | gradle --no-daemon … |
construire, tester et lancer un projet |
| JUnit 5 | (par Gradle, sans réseau) | tests unitaires |
| Python 3 et matplotlib | python3 |
figures des TP |
| Git | git |
versions du projet |
Machine. Windows, macOS ou Linux en 64 bits, 8 Go de mémoire (minimum de Docker Desktop sous Windows), 10 Go d’espace disque libre et, sous Windows, la virtualisation activée dans le BIOS ou l’UEFI. Une connexion Internet n’est nécessaire qu’au premier lancement, pour télécharger l’image : JUnit y est déjà rangé, et les commandes gradle des TP s’exécutent ensuite sans réseau. Une machine qui ne peut pas faire tourner Docker suit l’annexe « Poste sans Docker ».
Fichiers fournis (tp1-fichiers.zip) :
| Fichier | Rôle |
|---|---|
Hello.java |
premier programme, compilé à la main (étape 3) |
hello-dsa-fichiers/src/main/java/Main.java |
classe du premier projet Gradle (étape 4) |
hello-dsa-fichiers/src/test/java/MainTest.java |
trois tests JUnit 5 de Main (étape 5) |
hello-dsa-fichiers/.gitignore |
fichiers et dossiers que Git ne doit pas enregistrer (étape 7) |
test_figure.py |
figure de contrôle de matplotlib (étape 6) |
COMMANDES.sh |
aide-mémoire : les commandes de la fiche, dans l’ordre |
Étape 1 : installer Docker
Windows.
- Vérifier la version de Windows (commande
winver) : Windows 11 à jour (23H2, build 22631, ou plus récent), en 64 bits. Windows 10 22H2 (build 19045) fait encore tourner Docker Desktop, mais n’est plus suivi par Microsoft depuis octobre 2025. - Dans PowerShell ouvert en administrateur, installer ou mettre à jour WSL 2 :
wsl --install --no-distribution(ouwsl --updates’il est déjà présent), puis redémarrer. - Télécharger Docker Desktop (https://www.docker.com/products/docker-desktop/), lancer
Docker Desktop Installer.exeen gardant l’option WSL 2, redémarrer, puis lancer Docker Desktop et attendre que le moteur soit démarré (Engine running). - Installer Git for Windows (https://gitforwindows.org/), qui fournit le terminal Git Bash. Les commandes
dockeret celles du conteneur se tapent dans Git Bash, ni dans CMD ni dans PowerShell ; seules les commandeswslci-dessus etwingetde l’annexe se tapent dans PowerShell.
macOS. Télécharger Docker.dmg à la même adresse, dans la version de la puce (menu Pomme, « À propos de ce Mac » : Apple silicon ou Intel), glisser Docker dans Applications et le lancer. Docker Desktop prend en charge la version courante de macOS et les deux précédentes. Les commandes se tapent dans le Terminal. L’image du cours existe pour les deux familles de processeurs (linux/amd64 et linux/arm64) : Docker télécharge d’office celle de la machine, Intel ou Apple silicon, qui tourne sans émulation.
Linux (Ubuntu, Debian).
sudo apt-get update
sudo apt-get install docker.io
sudo usermod -aG docker $USER
# fermer la session puis la rouvrir (le groupe docker n'est lu qu'a l'ouverture)Vérification 1. Dans le terminal (Git Bash sous Windows), docker --version affiche un numéro de version, et docker run hello-world affiche Hello from Docker! : le moteur tourne et le registre d’images est joignable.
Étape 2 : lancer le conteneur du cours
Créer le dossier de travail du cours :
mkdir ~/dsa
cd ~/dsaDécompresser tp1-fichiers.zip dans ce dossier : le dossier ~/dsa/tp1-fichiers/ apparaît. Sous Windows, ~ désigne le dossier de l’utilisateur, C:\Users\<nom>. Le conteneur se lance depuis ce dossier :
# Linux, macOS
docker run -it --rm -v "$(pwd):/workspace" elbachirtoure/dsa-java:v2.0
# Windows (Git Bash)
MSYS_NO_PATHCONV=1 docker run -it --rm -v "$(pwd):/workspace" elbachirtoure/dsa-java:v2.0L’option -it relie le terminal au conteneur ; --rm fait effacer le conteneur par Docker à sa sortie (exit) ; -v "$(pwd):/workspace" rend le dossier courant de la machine visible dans le conteneur sous /workspace ; sous Windows, MSYS_NO_PATHCONV=1 empêche Git Bash de réécrire /workspace en chemin Windows. Au premier lancement, Docker télécharge l’image (environ 600 Mo à télécharger) ; les lancements suivants la réutilisent. Le message d’accueil du cours s’affiche : help-tp donne l’aide-mémoire. L’invite du terminal change : les commandes s’exécutent désormais dans le conteneur, jusqu’à exit.
cd /workspace
ls
java --version
javac --version
gradle --version
python3 --version
git --versionVérification 2. Java 17, Gradle 8.14.3, Python 3 et Git répondent, et ls montre tp1-fichiers.
- Observer. Recopier la première ligne affichée par chacune des cinq commandes de version.
- Observer. Créer sur la machine, avec l’éditeur, un fichier
note.txtdans~/dsa, puis taperls /workspacedans le conteneur. Que montre la commande ? - Expliquer. Dans le conteneur, taper
touch /workspace/a.txtpuistouch /root/b.txt; quitter parexit, relancer la même commandedocker runet chercher les deux fichiers. Lequel existe encore, et pourquoi ? Où se trouve-t-il sur la machine ? - Expliquer. Que contient
/workspacequanddocker runest lancé depuis un autre dossier que~/dsa?
Étape 3 : Java en ligne de commande
public class Hello {
public static void main(String[] args) {
System.out.println("Structures de donnees et algorithmes");
System.out.println("Environnement pret !");
}
}Dans le conteneur :
cd /workspace/tp1-fichiers
javac Hello.java
java HelloSortie attendue \(\rightarrow\)
Structures de donnees et algorithmes
Environnement pret !Vérification 3. Le programme affiche les deux lignes. Le modifier sur la machine pour qu’il affiche aussi le nom de l’étudiant, recompiler et relancer.
- Observer. Quel fichier
javaca-t-il créé dans le dossier ? Est-il visible sur la machine ? - Expliquer. La commande
javap -v Hello.class | grep majoraffiche un numéro : lequel ? Que désigne-t-il, et quel lien a-t-il avec Java 17 ? - Modifier. Lancer
java Hello.classau lieu dejava Hello. Quel message s’affiche ? Qu’attend la commandejavacomme argument ? - Modifier. Copier le fichier dans un dossier
essai/:mkdir essai,cp Hello.java essai/. Sur la machine, dansessai/Hello.java, renommer la classe enBonjoursans renommer le fichier ; puis, dans le conteneur,cd essaietjavac Hello.java. Quel messagejavacdonne-t-il, et quelle règle de Java rappelle-t-il ?
Étape 4 : premier projet Gradle
Gradle compile le projet, trouve ses dépendances (JUnit, déjà rangé dans l’image), lance les tests et le programme. La commande new-project de l’image crée un projet dans un nouveau dossier ; les fichiers fournis remplacent ensuite le Main.java, le MainTest.java et le .gitignore qu’elle a créés.
cd /workspace
new-project hello-dsa
cd hello-dsa
cp -r ../tp1-fichiers/hello-dsa-fichiers/. .Sous Linux : sur la machine, sudo chown -R $USER: ~/dsa/hello-dsa avant de modifier les fichiers dans l’éditeur ; Git demandera alors, dans le conteneur, git config --global --add safe.directory /workspace/hello-dsa à chaque docker run.
Le projet suit la disposition standard de Gradle, celle de tous les TP du cours :
hello-dsa/
build.gradle configuration : plugins, dépendances (JUnit), classe principale
settings.gradle nom du projet
src/main/java/Main.java code du programme
src/test/java/MainTest.java tests JUnit
.gitignore fichiers que Git ignorepublic class Main {
private String nom;
public Main(String nom) {
this.nom = nom;
}
public String message() {
return "Bienvenue en DSA, " + nom + " !";
}
public static void main(String[] args) {
String nom = args.length > 0 ? args[0] : "Fatou";
Main s = new Main(nom);
System.out.println(s.message());
}
}gradle --no-daemon build
gradle --no-daemon runLigne écrite par le programme (parmi celles de Gradle) \(\rightarrow\)
Bienvenue en DSA, Fatou !--no-daemon ?
Par défaut, Gradle laisse en mémoire un processus (le daemon) pour accélérer les commandes suivantes. Le conteneur est recréé à chaque docker run, ce processus n’y sert donc pas, et sur les dossiers partagés avec Windows il peut provoquer des erreurs d’entrée/sortie. Avec --no-daemon, tout le travail se fait dans un processus qui s’arrête avec la commande. Le premier build prend de 10 à 20 secondes, les suivants moins.
Vérification 4. gradle --no-daemon run affiche Bienvenue en DSA, Fatou !.
- Observer. Afficher
build.gradle(cat build.gradle) et recopier la ligne qui désigne la classe lancée pargradle runet celle qui déclare JUnit. - Observer. Après le
build, où Gradle a-t-il rangéMain.classetMainTest.class(find build -name "*.class") ? - Modifier. Lancer successivement
gradle --no-daemon run --args="Awa",--args="Awa Diop"et--args="'Awa Diop'". Recopier les trois affichages. - Expliquer. Comment le texte de
--argsparvient-il à la méthodemain? Expliquer la deuxième et la troisième sortie. Le TP de la Séance 1 choisit ainsi un mode d’observation :--args="observer".
Étape 5 : premiers tests JUnit
Un test appelle une méthode sur des données choisies et compare le résultat à la valeur attendue.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class MainTest {
@Test
void testMessage() {
Main s = new Main("Moussa");
assertEquals("Bienvenue en DSA, Moussa !", s.message());
}
@Test
void testAutreNom() {
Main s = new Main("Aminata");
assertTrue(s.message().contains("Aminata"));
}
@Test
void testNonVide() {
Main s = new Main("Cheikh");
assertFalse(s.message().isEmpty());
}
}Le build de l’étape 4 a déjà lancé ces tests ; cleanTest efface leur résultat, et test les relance en affichant une ligne par test.
gradle --no-daemon cleanTest testSortie attendue \(\rightarrow\)
MainTest > testAutreNom() PASSED
MainTest > testMessage() PASSED
MainTest > testNonVide() PASSED
BUILD SUCCESSFUL in 7sassertEquals(attendu, obtenu): vérifie l’égalité (avecequalspour les objets) ;assertTrue(condition),assertFalse(condition): vérifient qu’une condition est vraie, fausse ;assertThrows(UneException.class, () -> …): vérifie que l’instruction lève l’exception indiquée.
Gradle écrit aussi un rapport HTML, build/reports/tests/test/index.html, qui s’ouvre sur la machine dans un navigateur.
Vérification 5. Les trois tests passent (PASSED) et la dernière ligne est BUILD SUCCESSFUL.
- Observer. Dans quel ordre les tests s’exécutent-ils ? Est-ce l’ordre du fichier ? Qu’en déduire pour l’écriture d’un test ?
- Modifier. Dans
message(), remplacerBienvenueparBienvenu, puis relancer les tests. Quels tests échouent ? Relever, dans le rapport HTML, la valeur attendue et la valeur obtenue. Pourquoi les autres tests passent-ils ? Revenir au code correct. - Modifier. Ajouter à
MainTestun quatrième test, au choix, et relancer les tests. - Expliquer. Relancer
gradle --no-daemon testsans rien modifier. Que montre la sortie, et pourquoi ? Quelle option degradlerelance toutes les tâches ?
Étape 6 : Python et matplotlib
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
n = np.arange(1, 50)
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(n, np.ones_like(n), label='O(1)', linewidth=2)
ax.plot(n, np.log2(n), label='O(log n)', linewidth=2)
ax.plot(n, n, label='O(n)', linewidth=2)
ax.plot(n, n * np.log2(n), label='O(n log n)', linewidth=2)
ax.set_xlabel('n')
ax.set_ylabel("Operations")
ax.set_title('Comparaison des complexites')
ax.legend()
ax.set_ylim(0, 200)
ax.grid(True, alpha=0.3)
plt.tight_layout()
fig.savefig('test_complexites.png', dpi=150)
print("Figure sauvegardee : test_complexites.png")cd /workspace/tp1-fichiers
python3 test_figure.pyLe fichier test_complexites.png apparaît dans ~/dsa/tp1-fichiers/ : l’ouvrir sur la machine.
Vérification 6. La figure montre quatre courbes, avec leur légende et les noms des axes.
- Expliquer. Une des courbes sort du cadre avant \(n=49\). Laquelle, vers quelle valeur de \(n\), et quelle ligne du script en décide ? Calculer \(n\log_2 n\) pour \(n=38\) et \(n=39\).
Étape 7 : Git, les bases
Le dépôt se crée dans le dossier du projet. L’identité de l’auteur des versions s’enregistre dans le projet lui-même (option --global absente).
cd /workspace/hello-dsa
git init -b main
git config user.name "Prenom Nom"
git config user.email "prenom.nom@ucad.edu.sn"
git status
git add .
git commit -m "TP-1 : premier projet avec JUnit"Modifier ensuite Main.java pour que le prénom par défaut (ligne 13) soit celui de l’étudiant, relancer gradle --no-daemon run, puis :
git add .
git commit -m "TP-1 : prenom par defaut"
git log --onelineSortie attendue \(\rightarrow\)
b3ab077 TP-1 : prenom par defaut
3ee982c TP-1 : premier projet avec JUnitVérification 7. git log --oneline affiche deux versions (les identifiants à 7 caractères diffèrent d’une machine à l’autre).
- Observer. Quels fichiers et dossiers
git statusliste-t-il avantgit add? Les dossiersbuild/et.gradle/existent pourtant : quel fichier les écarte, et pourquoi ne pas les enregistrer ? - Expliquer. Afficher
.git/config. Pourquoi cette identité est-elle encore là aprèsexitet un nouveaudocker run, alors qu’ungit config --globaltapé dans le conteneur serait à refaire ?
Pannes fréquentes
| Symptôme | Cause | Remède |
|---|---|---|
docker: command not found (sur la machine) |
Docker absent, ou terminal ouvert avant l’installation | installer Docker (étape 1), rouvrir le terminal |
failed to connect to the docker API … if the daemon is running (sous Windows, message qui cite dockerDesktopLinuxEngine) |
moteur Docker arrêté | lancer Docker Desktop et attendre Engine running ; sous Linux, sudo systemctl start docker |
| Windows : Docker Desktop ne démarre pas, avec un message sur la virtualisation ou sur WSL | virtualisation désactivée dans le BIOS ou l’UEFI, ou WSL 2 absent ou trop ancien | activer la virtualisation (Intel VT-x, AMD-V ou SVM) dans le BIOS ou l’UEFI ; dans PowerShell en administrateur, wsl --update, puis redémarrer |
permission denied while trying to connect to the docker API (Linux) |
utilisateur hors du groupe docker, ou session pas rouverte |
sudo usermod -aG docker $USER, puis fermer et rouvrir la session |
Git Bash refuse -it (message qui parle de TTY ou de winpty) |
le terminal de Git Bash ne fournit pas de terminal à Docker | MSYS_NO_PATHCONV=1 winpty docker run -it --rm -v "$(pwd):/workspace" elbachirtoure/dsa-java:v2.0, ou Git Bash ouvert dans le Terminal Windows |
/workspace vide ou sans tp1-fichiers |
docker run lancé depuis un autre dossier |
exit, cd ~/dsa, relancer |
Windows : erreur sur le chemin du volume, ou /workspace vide depuis le bon dossier |
MSYS_NO_PATHCONV=1 oublié |
retaper la commande avec ce préfixe |
javac: command not found |
commande tapée sur la machine et non dans le conteneur (sans Docker : PATH) |
vérifier l’invite, lancer docker run ; sans Docker, voir l’annexe |
UnsupportedClassVersionError … class file version 65.0 … up to 61.0 |
classe compilée par un JDK 21 et lancée par Java 17 | recompiler dans le conteneur, ou avec javac --release 17 ; comparer java --version et javac --version |
ERROR: JAVA_HOME is set to an invalid directory |
JAVA_HOME désigne un dossier qui n’est pas un JDK |
donner à JAVA_HOME le dossier du JDK, celui qui contient bin/javac |
package org.junit.jupiter.api does not exist |
JUnit absent du classpath : test rangé dans src/main/java, ou compilation à la main sans le jar |
ranger les tests dans src/test/java ; à la main, -cp avec le jar de JUnit (annexe) |
0 tests found (lanceur console de JUnit) |
dossier des classes de test absent du -cp |
ajouter build/test-classes au -cp |
Directory '/workspace' does not contain a Gradle build. |
gradle lancé hors du dossier du projet |
cd /workspace/hello-dsa |
Could not resolve … |
dépendance absente de l’image (ajoutée ou changée dans build.gradle) et Internet injoignable |
garder les dépendances écrites par new-project, ou rétablir la connexion et relancer |
unmappable character (0xC3) for encoding US-ASCII, ou é affiché à la place de é |
fichier en UTF-8 lu dans un autre encodage (environnement sans UTF-8, Java 17 sous Windows) | javac -encoding UTF-8 ; le code du cours n’a pas d’accents, commentaires compris |
fatal: detected dubious ownership in repository |
dossier partagé qui appartient à un autre utilisateur que celui de Git | git config --global --add safe.directory /workspace/hello-dsa, à retaper après chaque docker run |
| Linux : fichiers créés dans le conteneur non modifiables sur la machine | le conteneur travaille en root : ces fichiers appartiennent à root |
sudo chown -R $USER: ~/dsa |
Author identity unknown |
identité absente du dépôt | git config user.name et user.email dans le projet (étape 7) |
Annexe : poste sans Docker
Sur une machine qui ne peut pas faire tourner Docker, un JDK installé directement compile et exécute le code des TP, et le lanceur console de JUnit remplace gradle test.
JDK 17 (Eclipse Temurin). Dans un terminal neuf, java --version et javac --version doivent ensuite afficher 17.0.*x*. Un JDK 21 convient aussi, à condition de compiler avec --release 17.
# Windows (PowerShell) ; ou l'installateur .msi de https://adoptium.net, option JAVA_HOME cochee
winget install EclipseAdoptium.Temurin.17.JDK
# macOS (Homebrew) ; ou l'installateur .pkg de https://adoptium.net
brew install --cask temurin@17
# Ubuntu, Debian 12
sudo apt-get install openjdk-17-jdk
# Debian 13 : JDK 21, compilation avec --release 17
sudo apt-get install openjdk-21-jdkJUnit. Télécharger le lanceur console de JUnit 5.10.1, junit-platform-console-standalone-1.10.1.jar, depuis Maven Central (https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/1.10.1/) et le placer dans le dossier du projet (le .gitignore fourni l’écarte de Git). Ce jar contient JUnit (la version de l’image) et son lanceur : il sert à compiler les tests et à les exécuter.
Commandes (dans tp1-fichiers/hello-dsa-fichiers/ ; sous Windows, le séparateur du -cp est ; et la liste se met entre guillemets : -cp "build/classes;junit-platform-console-standalone-1.10.1.jar").
javac --release 17 -d build/classes src/main/java/*.java
java -cp build/classes Main
javac --release 17 -d build/test-classes -cp build/classes:junit-platform-console-standalone-1.10.1.jar src/test/java/*.java
java -jar junit-platform-console-standalone-1.10.1.jar execute -cp build/classes:build/test-classes --scan-class-pathLe lanceur affiche l’arbre des tests, puis un résumé dont voici les lignes sur les tests :
Sortie attendue \(\rightarrow\)
[ 3 tests found ]
[ 3 tests successful ]
[ 0 tests failed ]Dans les TP suivants, les deux commandes ci-dessous font le même travail :
gradle --no-daemon run --args="observer" # avec Docker
java -cp build/classes Main observer # sans DockerUne option de la machine virtuelle se place avant -cp : le TP de la Séance 4, qui agrandit la pile par -Xss16m dans build.gradle, se lance ainsi par java -Xss16m -cp build/classes Main <mode>.
Aide-mémoire
| Commande | Rôle |
|---|---|
docker run -it --rm -v "$(pwd):/workspace" elbachirtoure/dsa-java:v2.0 |
lancer le conteneur depuis ~/dsa (préfixe MSYS_NO_PATHCONV=1 sous Windows) |
exit |
quitter le conteneur |
help-tp |
aide-mémoire du conteneur |
new-project <nom> |
créer un projet Gradle |
javac Fichier.java, java Classe |
compiler, exécuter (hors projet) |
gradle --no-daemon build |
compiler et tester |
gradle --no-daemon test |
tester |
gradle --no-daemon cleanTest test |
relancer des tests déjà passés |
gradle --no-daemon run --args="…" |
exécuter Main, avec ses arguments |
python3 script.py |
exécuter un script Python |
git add . puis git commit -m "message" |
enregistrer une version |
git status, git log --oneline |
état du dépôt, liste des versions |
Pour cette séance
À télécharger
- Fichiers du TP-1 (ZIP, 3 Ko)