ajout: Tests unitaires de Dijkstra

This commit is contained in:
Brendan Saint Germes 2026-05-19 10:49:02 +02:00
parent 0b08b70cb9
commit 5fc064e863
4 changed files with 242 additions and 7 deletions

View file

@ -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

View file

@ -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

View file

@ -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();
}
}

View file

@ -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<Object> 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<Object> 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<pathDijkstra.getArcs().size(); i++) {
final Arc arcDijkstra = pathDijkstra.getArcs().get(i);
final Node from1 = arcDijkstra.getOrigin();
final Node to1 = arcDijkstra.getDestination();
final Arc arcBellman = pathBellman.getArcs().get(i);
final Node from2 = arcBellman.getOrigin();
final Node to2 = arcBellman.getDestination();
assertEquals(arcDijkstra.getLength(), arcBellman.getLength(), 0.0);
assertEquals(from1, from2);
assertEquals(to1, to2);
}
}
@Test
public void testCompareCost() {
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();
double coutDijkstra = 0;
for (Arc arc : dijkstraPath.getArcs()) coutDijkstra += scenario.arcInspector.getCost(arc);
// On doit comparer le cout de dijkstra (calculé au dessus) au cout du shortest path passant par les mêmes nodes.
final List<Node> 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);
}
}
}
}