From 5fc064e86377e813c84b72f1cca24da68884d7cb Mon Sep 17 00:00:00 2001 From: Brendan Saint-Germes Date: Tue, 19 May 2026 10:49:02 +0200 Subject: [PATCH] ajout: Tests unitaires de Dijkstra --- .../graphs/algorithm/ArcInspectorFactory.java | 12 +- .../shortestpath/DijkstraAlgorithm.java | 3 +- .../algorithm/shortestpath/DijkstraTest.java | 11 + .../ShortestPathAlgorithmTest.java | 223 ++++++++++++++++++ 4 files changed, 242 insertions(+), 7 deletions(-) create mode 100644 be-graphes-algos/src/test/java/org/insa/graphs/algorithm/shortestpath/DijkstraTest.java create mode 100644 be-graphes-algos/src/test/java/org/insa/graphs/algorithm/shortestpath/ShortestPathAlgorithmTest.java diff --git a/be-graphes-algos/src/main/java/org/insa/graphs/algorithm/ArcInspectorFactory.java b/be-graphes-algos/src/main/java/org/insa/graphs/algorithm/ArcInspectorFactory.java index 7bf06da..aeb8fe3 100644 --- a/be-graphes-algos/src/main/java/org/insa/graphs/algorithm/ArcInspectorFactory.java +++ b/be-graphes-algos/src/main/java/org/insa/graphs/algorithm/ArcInspectorFactory.java @@ -12,7 +12,7 @@ import org.insa.graphs.model.AccessRestrictions.AccessRestriction; public class ArcInspectorFactory { - private static class NoFilterByLengthArcInspector implements ArcInspector { + public static class NoFilterByLengthArcInspector implements ArcInspector { @Override public boolean isAllowed(Arc arc) { @@ -40,7 +40,7 @@ public class ArcInspectorFactory { } }; - private static class OnlyCarsByLengthArcInspector + public static class OnlyCarsByLengthArcInspector extends NoFilterByLengthArcInspector { @Override @@ -57,7 +57,7 @@ public class ArcInspectorFactory { } }; - private static class OnlyCarsByTimeArcInspector + public static class OnlyCarsByTimeArcInspector extends NoFilterByLengthArcInspector { @Override @@ -76,7 +76,7 @@ public class ArcInspectorFactory { } }; - private static class OnlyPedestrianByTime implements ArcInspector { + public static class OnlyPedestrianByTime implements ArcInspector { static final int maxPedestrianSpeed = 5; @@ -110,7 +110,7 @@ public class ArcInspectorFactory { } }; - private static class BicyleByTime implements ArcInspector { + public static class BicyleByTime implements ArcInspector { static int maxBicycleSpeed = 20; @Override @@ -142,7 +142,7 @@ public class ArcInspectorFactory { } } - private static class BicyleByLength implements ArcInspector { + public static class BicyleByLength implements ArcInspector { static int maxBicycleSpeed = 20; @Override diff --git a/be-graphes-algos/src/main/java/org/insa/graphs/algorithm/shortestpath/DijkstraAlgorithm.java b/be-graphes-algos/src/main/java/org/insa/graphs/algorithm/shortestpath/DijkstraAlgorithm.java index 68b8e1b..f2f8931 100644 --- a/be-graphes-algos/src/main/java/org/insa/graphs/algorithm/shortestpath/DijkstraAlgorithm.java +++ b/be-graphes-algos/src/main/java/org/insa/graphs/algorithm/shortestpath/DijkstraAlgorithm.java @@ -30,7 +30,6 @@ public class DijkstraAlgorithm extends ShortestPathAlgorithm { heap.insert(label); labels.put(data.getOrigin().getId(), label); System.out.println("==> Tas initialisté"); - //System.out.println(heap.toStringTree()); System.out.println("==> Dijkstra commence"); notifyOriginProcessed(data.getOrigin()); @@ -41,6 +40,8 @@ public class DijkstraAlgorithm extends ShortestPathAlgorithm { break; currentLabel.setMarque(true); + // On constate que les coûts des sommets marqués est bel et bien croissant + //System.out.println("-> Sommet marqué: " + currentLabel.getCoutRealise()); notifyNodeMarked(currentLabel.getNode()); // Si on atteint notre destination, on arrête l'algo diff --git a/be-graphes-algos/src/test/java/org/insa/graphs/algorithm/shortestpath/DijkstraTest.java b/be-graphes-algos/src/test/java/org/insa/graphs/algorithm/shortestpath/DijkstraTest.java new file mode 100644 index 0000000..4f9dd92 --- /dev/null +++ b/be-graphes-algos/src/test/java/org/insa/graphs/algorithm/shortestpath/DijkstraTest.java @@ -0,0 +1,11 @@ +package org.insa.graphs.algorithm.shortestpath; + +public class DijkstraTest extends ShortestPathAlgorithmTest { + + @Override + public ShortestPathSolution runAlgo(ShortestPathData algoData) { + final DijkstraAlgorithm algo = new DijkstraAlgorithm(algoData); + return algo.doRun(); + } + +} diff --git a/be-graphes-algos/src/test/java/org/insa/graphs/algorithm/shortestpath/ShortestPathAlgorithmTest.java b/be-graphes-algos/src/test/java/org/insa/graphs/algorithm/shortestpath/ShortestPathAlgorithmTest.java new file mode 100644 index 0000000..e9d6fc7 --- /dev/null +++ b/be-graphes-algos/src/test/java/org/insa/graphs/algorithm/shortestpath/ShortestPathAlgorithmTest.java @@ -0,0 +1,223 @@ +package org.insa.graphs.algorithm.shortestpath; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +import org.insa.graphs.algorithm.ArcInspector; +import org.insa.graphs.algorithm.ArcInspectorFactory; +import org.insa.graphs.algorithm.AbstractInputData.Mode; +import org.insa.graphs.model.Arc; +import org.insa.graphs.model.Graph; +import org.insa.graphs.model.Node; +import org.insa.graphs.model.Path; +import org.insa.graphs.model.io.BinaryGraphReader; +import org.insa.graphs.model.io.GraphReader; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public abstract class ShortestPathAlgorithmTest { + + public abstract ShortestPathSolution runAlgo(ShortestPathData algoData); + + private static String MAP_CARRE = "/mnt/commetud/3eme Annee MIC/Graphes-et-Algorithmes/Maps/carre.mapgr"; + private static String MAP_INSA = "/mnt/commetud/3eme Annee MIC/Graphes-et-Algorithmes/Maps/insa.mapgr"; + private static String MAP_HAITI = "/mnt/commetud/3eme Annee MIC/Graphes-et-Algorithmes/Maps/haiti-and-domrep.mapgr"; + private static String MAP_TOULOUSE = "/mnt/commetud/3eme Annee MIC/Graphes-et-Algorithmes/Maps/toulouse.mapgr"; + private static String MAP_FRANCE = "/mnt/commetud/3eme Annee MIC/Graphes-et-Algorithmes/Maps/france.mapgr"; + + private static Graph carreGraph, insaGraph, haitiGraph, toulouseGraph, franceGraph; + + protected static class TestScenario { + public final Graph graph; // chemin du fichier mapgr + public final Node origin, destination; + public final ArcInspector arcInspector; + public final boolean isFeasible; + + public TestScenario(Graph graph, int originId, int destinationId, boolean isFeasible, ArcInspector arcInspector) { + this.graph = graph; + this.origin = graph.get(originId); + this.destination = graph.get(destinationId); + this.isFeasible = isFeasible; + this.arcInspector = arcInspector; + } + + public TestScenario(Graph graph, int originId, int destinationId, boolean isFeasible) { + this(graph, originId, destinationId, isFeasible, new ArcInspectorFactory.NoFilterByLengthArcInspector()); // par défaut, aucune restriction sur les arcs + } + }; + + private static Graph loadGraph(String pathname) { + Graph graph = null; + + try (final GraphReader reader = new BinaryGraphReader(new DataInputStream(new BufferedInputStream(new FileInputStream(pathname))))) { + graph = reader.read(); + } catch (FileNotFoundException e) { + System.err.println("> Impossible de trouver le fichier " + MAP_CARRE); + e.printStackTrace(); + } catch (IOException e) { + System.err.println("> Impossible d'ouvrir le fichier " + MAP_CARRE); + e.printStackTrace(); + } + + return graph; + } + + @Parameters + public static Collection data() { + carreGraph = loadGraph(MAP_CARRE); + assertNotNull("Impossible de charger le graphe carré", carreGraph); + insaGraph = loadGraph(MAP_INSA); + assertNotNull("Impossible de charger le graphe INSA", insaGraph); + haitiGraph = loadGraph(MAP_HAITI); + assertNotNull("Impossible de charger le graphe Haïti", haitiGraph); + toulouseGraph = loadGraph(MAP_TOULOUSE); + assertNotNull("Impossible de charger le graphe Toulouse", toulouseGraph); + franceGraph = loadGraph(MAP_FRANCE); + assertNotNull("Impossible de charger le graphe France", franceGraph); + + Collection objects = new ArrayList<>(); + + // Exemple trajet court, toutes routes + objects.add(new TestScenario(carreGraph, 9, 11, true)); + // Exemple trajet moyen, toutes routes + objects.add(new TestScenario(insaGraph, 286, 823, true)); + // Exemple trajet infaisable (composantes non connexes) + objects.add(new TestScenario(haitiGraph, 265362, 92314, false)); + // Exemple trajet moyen, à pieds + objects.add(new TestScenario(toulouseGraph, 16824, 4028, true, new ArcInspectorFactory.OnlyPedestrianByTime())); + // Exemple trajet de longueur nulle + objects.add(new TestScenario(insaGraph, 297, 297, true)); + // Exemple trajet très long + //objects.add(new TestScenario(franceGraph, 981717, 5539046, true)); + + return objects; + } + + @Parameter + public TestScenario scenario; + + @Test + public void testScenario() { + assertNotNull(scenario.graph); + assertNotNull(scenario.origin); + assertNotNull(scenario.destination); + assertNotNull(scenario.arcInspector); + } + + @Test + public void testFeasability() { + final ShortestPathData algoData = new ShortestPathData(scenario.graph, scenario.origin, scenario.destination, scenario.arcInspector); + final ShortestPathSolution solution = this.runAlgo(algoData); + assertEquals(solution.isFeasible(), scenario.isFeasible); + + if (solution.isFeasible()) assertTrue(solution.getPath().isValid()); + } + + @Test + public void testCompareWithBellman() { + // On ne compare avec Bellman qui si le scenario est faisable + Assume.assumeTrue(scenario.isFeasible); + // Si un chemin de longueur nulle (origin==destination) on ne compare pas avec Bellman, qui ne respecte pas les mêmes conventions que notre Dijkstra + Assume.assumeFalse(scenario.origin.equals(scenario.destination)); + + final ShortestPathData algoData = new ShortestPathData(scenario.graph, scenario.origin, scenario.destination, scenario.arcInspector); + + final ShortestPathSolution solutionDijkstra = this.runAlgo(algoData); + final ShortestPathSolution solutionBellman = new BellmanFordAlgorithm(algoData).doRun(); + + final Path pathDijkstra = solutionDijkstra.getPath(); + final Path pathBellman = solutionBellman.getPath(); + + assertEquals(pathDijkstra.getArcs().size(), pathBellman.getArcs().size()); + + for (int i=0; i nodes = dijkstraPath.getArcs().stream().map(arc -> arc.getOrigin()).collect(Collectors.toList()); + if (!nodes.isEmpty()) nodes.add(dijkstraPath.getArcs().getLast().getDestination()); + final Path computedPath = Path.createShortestPathFromNodes(scenario.graph, nodes); + double coutComputed = 0; + for (Arc arc : computedPath.getArcs()) coutComputed += scenario.arcInspector.getCost(arc); + + assertEquals(coutDijkstra, coutComputed, 0.0); + } + + // Tout sous chemin d'un PCC est un PCC + @Test + public void testConherence() { + Assume.assumeTrue(scenario.isFeasible); + + final ShortestPathData algoData = new ShortestPathData(scenario.graph, scenario.origin, scenario.destination, scenario.arcInspector); + final ShortestPathSolution solution = this.runAlgo(algoData); + final Path dijkstraPath = solution.getPath(); + + System.out.println("testCoherence()"); + if (dijkstraPath.getArcs().size() > 0) { + System.out.println("OUI"); + Random random = new Random(); + for (int i=0; i<10; i++) { + final int k = random.nextInt(1, dijkstraPath.getArcs().size()-1); + final Node randomNode = dijkstraPath.getArcs().get(k).getOrigin(); + + final ShortestPathData algoDataSousChemin = new ShortestPathData(scenario.graph, scenario.origin, randomNode, scenario.arcInspector); + final ShortestPathSolution solutionSousChemin = this.runAlgo(algoDataSousChemin); + final Path dijkstraPathSousChemin = solutionSousChemin.getPath(); + + double coutDijkstraSousChemin = 0; + for (Arc arc : dijkstraPathSousChemin.getArcs()) coutDijkstraSousChemin += scenario.arcInspector.getCost(arc); + + double coutDijkstraPortion = 0; + for (Arc arc : dijkstraPath.getArcs()) { + coutDijkstraPortion += scenario.arcInspector.getCost(arc); + // Si on arrive au noeud random sur le dijkstra de référence, on s'arrête pour comparer les couts du sous chemin + if (arc.getDestination().equals(randomNode)) break; + } + + assertEquals(coutDijkstraSousChemin, coutDijkstraPortion, 0.0); + } + } + } +}