/*
This file is part of Fantom.
Fantom is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Fantom is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fantom. If not, see <http://www.gnu.org/licenses/>.
*/
package cz.matfyz.aai.fantom.game;
import org.junit.Test;
import cz.matfyz.aai.fantom.game.Graph.Edge;
import cz.matfyz.aai.fantom.game.Graph.Node;
import junit.framework.TestCase;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class GraphTest extends TestCase {
public static final String[] SIMPLE_GRAPH = {
"game: 5, reveal: 1 2 3 4 5",
"transport: car, users: phantom+detective",
"transport: metro, users: all",
"nodes: first_two, from: 1, to: 2",
"node: 3",
"edge: 1-2@car",
"edge: 2-3@metro",
"phantom: Fantomas, car: 1, metro: 1",
"detective: Poirot, car: 10, metro: 10"
};
public static final String[] MISSING_TRANSPORT_TYPE = {
"node: 1",
"node: 2",
"edge: 1-2"
};
public static final String[] INVALID_TRANSPORT_TYPE = {
"node: 1",
"node: 2",
"edge: 1-2@car"
};
public static final String[] MULTITRANSPORT_GRAPH = {
"node: 1",
"node: 2",
"transport: tram, users: phantom+detective",
"transport: taxi, users: phantom",
"edge: 1-2@tram+taxi",
"phantom: Fantomas, taxi: 1, tram: 1",
"detective: Poirot, taxi: 10, tram: 10"
};
public static final String[] UNORIENTED_GRAPH = {
"node: 1",
"node: 2",
"node: 3",
"transport: tram, users: all",
"transport: bus, users: all",
"edge: 1=2@tram",
"edge: 2=3@bus+tram",
"edge: 1=3@bus",
"phantom: Fantomas, tram: 1, bus: 1",
"detective: Poirot, tram: 1, bus: 1",
};
public static final String[] TICKET_GRAPH = {
"node: 0",
"node: 1",
"node: 2",
"node: 3",
"node: 4",
"transport: tram, users: phantom+detective",
"transport: taxi, users: phantom+detective",
"transport: boat, users: phantom",
"transport: walk, users: detective",
"edge: 0-1@taxi+tram+walk",
"edge: 1-0@taxi+tram+walk",
"edge: 1-2@taxi+tram+walk",
"edge: 2-1@taxi+tram+walk",
"edge: 0-2@boat",
"edge: 2-0@boat",
"edge: 2-3@walk",
"edge: 3-2@walk",
"edge: 3-4@walk",
"edge: 4-2@walk",
"phantom: Fantomas, tram: 1, boat: 2, universal: 1, double: 1",
"detective: Poirot, tram: 10, boat: 10, universal: 10, double: 0"
};
public void testClonedGraphPositions() throws Exception {
Graph g = new Graph();
g.loadGraph(SIMPLE_GRAPH);
Node n1 = g.getNode("1");
assertNotNull(n1);
Actor phantom = g.getActor("Fantomas");
assertNotNull(phantom);
assertNull(phantom.getCurrentPosition());
phantom.setCurrentPosition(n1);
assertNotNull(phantom.getCurrentPosition());
Graph gc = (Graph)g.clone();
Node n1c = gc.getNode("1");
assertNotNull(n1c);
Actor phantomc = gc.getActor("Fantomas");
assertNotNull(phantomc);
assertNotNull(phantomc.getCurrentPosition());
assertSame(n1c, phantomc.getCurrentPosition());
}
@Test
public void testLoadUnorientedGraph() throws Exception {
Graph g = new Graph();
g.loadGraph(UNORIENTED_GRAPH);
assertEquals(3, g.getTransportTypes().size());
assertEquals(3, g.getNodes().size());
assertEquals(14, g.getEdges().size());
Node n1 = g.getNode("1");
assertNotNull(n1);
HashSet<String> n1OutgoingEdges = new HashSet<String>();
assertEquals(4, n1.getOutgoingEdges().size());
for(Edge e : n1.getOutgoingEdges())
n1OutgoingEdges.add(e.toString());
assertEquals(4, n1OutgoingEdges.size());
assertTrue(n1OutgoingEdges.contains("edge:1-2@tram"));
assertTrue(n1OutgoingEdges.contains("edge:1-2@universal"));
assertTrue(n1OutgoingEdges.contains("edge:1-3@bus"));
assertTrue(n1OutgoingEdges.contains("edge:1-3@universal"));
HashSet<String> n1IncomingEdges = new HashSet<String>();
assertEquals(4, n1.getIncomingEdges().size());
for(Edge e : n1.getIncomingEdges())
n1IncomingEdges.add(e.toString());
assertEquals(4, n1IncomingEdges.size());
assertTrue(n1IncomingEdges.contains("edge:2-1@tram"));
assertTrue(n1IncomingEdges.contains("edge:2-1@universal"));
assertTrue(n1IncomingEdges.contains("edge:3-1@bus"));
assertTrue(n1IncomingEdges.contains("edge:3-1@universal"));
Node n2 = g.getNode("2");
assertNotNull(n2);
HashSet<String> n2OutgoingEdges = new HashSet<String>();
assertEquals(5, n2.getOutgoingEdges().size());
for(Edge e : n2.getOutgoingEdges())
n2OutgoingEdges.add(e.toString());
assertEquals(5, n2OutgoingEdges.size());
assertTrue(n2OutgoingEdges.contains("edge:2-1@tram"));
assertTrue(n2OutgoingEdges.contains("edge:2-1@universal"));
assertTrue(n2OutgoingEdges.contains("edge:2-3@bus"));
assertTrue(n2OutgoingEdges.contains("edge:2-3@tram"));
assertTrue(n2OutgoingEdges.contains("edge:2-3@universal"));
HashSet<String> n2IncomingEdges = new HashSet<String>();
assertEquals(5, n2.getIncomingEdges().size());
for(Edge e : n2.getIncomingEdges())
n2IncomingEdges.add(e.toString());
assertEquals(5, n2IncomingEdges.size());
assertTrue(n2IncomingEdges.contains("edge:1-2@tram"));
assertTrue(n2IncomingEdges.contains("edge:1-2@universal"));
assertTrue(n2IncomingEdges.contains("edge:3-2@bus"));
assertTrue(n2IncomingEdges.contains("edge:3-2@tram"));
assertTrue(n2IncomingEdges.contains("edge:3-2@universal"));
Node n3 = g.getNode("3");
assertNotNull(n3);
HashSet<String> n3OutgoingEdges = new HashSet<String>();
assertEquals(5, n3.getOutgoingEdges().size());
for(Edge e : n3.getOutgoingEdges())
n3OutgoingEdges.add(e.toString());
assertEquals(5, n3OutgoingEdges.size());
assertTrue(n3OutgoingEdges.contains("edge:3-2@bus"));
assertTrue(n3OutgoingEdges.contains("edge:3-2@tram"));
assertTrue(n3OutgoingEdges.contains("edge:3-2@universal"));
assertTrue(n3OutgoingEdges.contains("edge:3-1@bus"));
assertTrue(n3OutgoingEdges.contains("edge:3-1@universal"));
HashSet<String> n3IncomingEdges = new HashSet<String>();
assertEquals(5, n3.getIncomingEdges().size());
for(Edge e : n3.getIncomingEdges())
n3IncomingEdges.add(e.toString());
assertEquals(5, n3IncomingEdges.size());
assertTrue(n3IncomingEdges.contains("edge:2-3@bus"));
assertTrue(n3IncomingEdges.contains("edge:2-3@tram"));
assertTrue(n3IncomingEdges.contains("edge:2-3@universal"));
assertTrue(n3IncomingEdges.contains("edge:1-3@bus"));
assertTrue(n3IncomingEdges.contains("edge:1-3@universal"));
}
@Test
public void testLoadGraph() throws Exception {
Graph g = new Graph();
g.loadGraph(SIMPLE_GRAPH);
assertEquals(3, g.getTransportTypes().size());
assertEquals(3, g.getNodes().size());
assertEquals(4, g.getEdges().size());
assertTrue(g.getTransportTypes().containsKey("car"));
assertTrue(g.getTransportTypes().containsKey("metro"));
assertTrue(g.getNodes().containsKey("1"));
assertTrue(g.getNodes().containsKey("2"));
assertTrue(g.getNodes().containsKey("3"));
Graph.Node nd1 = g.getNodes().get("1");
Graph.Node nd2 = g.getNodes().get("2");
Graph.Node nd3 = g.getNodes().get("3");
assertNotNull(nd1);
assertNotNull(nd2);
assertNotNull(nd3);
assertNotSame(nd1, nd2);
assertNotSame(nd1, nd3);
assertNotSame(nd2, nd3);
assertEquals("1", nd1.getId());
assertNotNull(nd1.getOutgoingEdges());
assertEquals(2, nd1.getOutgoingEdges().size());
assertNotNull(nd1.getIncomingEdges());
assertEquals(0, nd1.getIncomingEdges().size());
Graph.Edge e_1_2 = nd1.getOutgoingEdges().get(0);
assertNotNull(e_1_2);
assertSame(nd1, e_1_2.getSource());
assertSame(nd2, e_1_2.getTarget());
assertEquals("2", nd2.getId());
assertNotNull(nd2.getOutgoingEdges());
}
@Test
public void testLoadInvalidTransportType() {
Graph g = new Graph();
try {
g.loadGraph(INVALID_TRANSPORT_TYPE);
fail("The invalid graph was loaded");
}
catch(GraphFormatException err) {
assertNotNull(err.getLineNumber());
assertEquals(2, err.getLineNumber().intValue());
}
}
@Test
public void testLoadMissingTransportType() {
Graph g = new Graph();
try {
g.loadGraph(MISSING_TRANSPORT_TYPE);
fail("The invalid graph was loaded");
}
catch(GraphFormatException err) {
assertNotNull(err.getLineNumber());
assertEquals(2, err.getLineNumber().intValue());
}
}
@Test
public void testMultiTransport() throws GraphFormatException {
Graph g = new Graph();
g.loadGraph(MULTITRANSPORT_GRAPH);
assertEquals(3, g.getTransportTypes().size());
assertEquals(2, g.getNodes().size());
assertEquals(3, g.getEdges().size());
assertTrue(g.getTransportTypes().containsKey("tram"));
assertTrue(g.getTransportTypes().containsKey("taxi"));
assertTrue(g.getNodes().containsKey("1"));
assertTrue(g.getNodes().containsKey("2"));
Graph.Node nd1 = g.getNodes().get("1");
Graph.Node nd2 = g.getNodes().get("2");
assertNotNull(nd1);
assertNotNull(nd2);
assertNotSame(nd1, nd2);
assertEquals("1", nd1.getId());
assertNotNull(nd1.getOutgoingEdges());
assertEquals(3, nd1.getOutgoingEdges().size());
assertNotNull(nd1.getIncomingEdges());
assertEquals(0, nd1.getIncomingEdges().size());
assertEquals("2", nd2.getId());
assertNotNull(nd2.getOutgoingEdges());
assertEquals(3, nd2.getIncomingEdges().size());
assertNotNull(nd2.getOutgoingEdges());
assertEquals(0, nd2.getOutgoingEdges().size());
boolean hasTram = false;
boolean hasTaxi = false;
boolean hasUniversal = false;
for(Graph.Edge e_1_2 : nd1.getOutgoingEdges()) {
assertNotNull(e_1_2);
assertSame(nd1, e_1_2.getSource());
assertSame(nd2, e_1_2.getTarget());
assertTrue("tram".equals(e_1_2.getTransportType().getName())
|| "taxi".equals(e_1_2.getTransportType().getName())
|| "universal".equals(e_1_2.getTransportType().getName()));
if("tram".equals(e_1_2.getTransportType().getName())) {
hasTram = true;
}
if("taxi".equals(e_1_2.getTransportType().getName())) {
hasTaxi = true;
}
if("universal".equals(e_1_2.getTransportType().getName())) {
hasUniversal = true;
}
}
assertTrue(hasTram);
assertTrue(hasTaxi);
assertTrue(hasUniversal);
}
@Test
public void testTickets() throws GraphFormatException {
Graph g = new Graph(TICKET_GRAPH);
List<Actor> actors = g.getActors();
assertEquals(2, actors.size());
Actor phantom = actors.get(0);
TransportType boatTransport = g.getTransportTypes().get("boat");
assertNotNull(boatTransport);
assertEquals(2, phantom.getNumberOfTickets(boatTransport));
TransportType tramTransport = g.getTransportTypes().get("tram");
assertNotNull(tramTransport);
assertEquals(1, phantom.getNumberOfTickets(tramTransport));
TransportType universalTransport = g.getTransportType("universal");
assertNotNull(universalTransport);
assertEquals(1, phantom.getNumberOfTickets(universalTransport));
assertEquals(3, phantom.getTickets().size());
assertEquals(1, phantom.getDoubleMoves());
Graph.Node[] node = new Graph.Node[5];
for(int i = 0; i < node.length; i++) {
Graph.Node n = g.getNodes().get(Integer.toString(i));
assertNotNull(n);
node[i] = n;
}
assertTrue(phantom.canMoveSingleEdge(node[0], node[1])); // tram|universal
assertTrue(phantom.canMoveSingleEdge(node[1], node[2])); // tram|universal
assertTrue(phantom.canMoveSingleEdge(node[0], node[2])); // boat|universal
assertFalse(phantom.canMoveSingleEdge(node[0], node[3]));
assertFalse(phantom.canMoveSingleEdge(node[0], node[4]));
assertTrue(phantom.canMoveSingleEdge(node[2], node[1])); // tram
for(Graph.Node n : node) {
assertFalse(phantom.canMoveSingleEdge(n, n));
}
assertTrue(phantom.canMoveDoubleMove(node[0], node[1])); // boat+[tram|universal]
assertTrue(phantom.canMoveDoubleMove(node[0], node[2])); // tram+universal
assertTrue(phantom.canMoveDoubleMove(node[0], node[0])); // [tram|boat]+universal (or vice versa)
assertFalse(phantom.canMoveDoubleMove(node[2], node[4])); // Two walks, but none available. Just one universal ticket at hand.
assertFalse(phantom.canMoveDoubleMove(node[4], node[4])); // Two walks, but none available. Just one universal ticket at hand.
assertFalse(phantom.canMoveDoubleMove(node[3], node[4])); // No way to make to moves on a single edge and no walk-around.
assertTrue(phantom.canMove(node[0], node[1])); // tram or the universal ticket or boat+[tram|universal]
assertTrue(phantom.canMove(node[0], node[2])); // tram+universal or boat|universal
assertTrue(phantom.canMove(node[0], node[3])); // boat+universal
assertFalse(phantom.canMove(node[2], node[4])); // Two walks, but none available. Just one universal ticket at hand.
assertFalse(phantom.canMove(node[4], node[4])); // Two walks, but none available. Just one universal ticket at hand.
assertTrue(phantom.canMove(node[3], node[4])); // Using the universal ticket.
phantom.setCurrentPosition(node[0]);
assertTrue(phantom.canMoveTo(node[1])); // tram|universal
assertTrue(phantom.canMoveTo(node[2])); // tram+universal or boat|universal
assertTrue(phantom.canMoveTo(node[3])); // boat+universal
assertFalse(phantom.canMoveTo(node[4]));
phantom.setCurrentPosition(node[1]);
assertSame(node[1], phantom.getCurrentPosition());
}
public void testSerializeSimple() throws GraphFormatException {
Graph g = new Graph(SIMPLE_GRAPH);
Properties[] props = g.serialize();
assertNotNull(props);
assertEquals(g.getActors().size() + g.getEdges().size() + g.getNodes().size() + g.getTransportTypes().size() + 1, props.length);
for(int i = 0; i < props.length; i++) {
assertNotNull(props[i]);
}
int gameRecords = 0;
for(int i = 0; i < props.length; i++) {
if(props[i].containsKey(Graph.PROPERTY_GAME)) {
gameRecords++;
assertEquals(5, Integer.parseInt(props[i].getProperty(Graph.PROPERTY_GAME)));
assertNotNull(props[i].getProperty(Graph.PROPERTY_REVEALS));
String[] revealsStr = props[i].getProperty(Graph.PROPERTY_REVEALS).split("\\s+");
int revealsCount = g.getPhantomReveals().size();
for(String s : revealsStr) {
if(s.isEmpty())
continue;
int reveal = Integer.parseInt(s);
revealsCount--;
assertTrue(g.getPhantomReveals().contains(reveal));
}
assertEquals(0, revealsCount);
}
}
assertEquals(1, gameRecords);
int nodeRecords = 0;
Set<String> nodeNames = new HashSet<String>();
for(int i = 0; i < props.length; i++) {
if(props[i].containsKey(Graph.PROPERTY_NODE)) {
String nodeName = props[i].getProperty(Graph.PROPERTY_NODE);
assertNotNull(nodeName);
nodeNames.add(nodeName);
nodeRecords++;
}
}
assertEquals(3, nodeRecords);
assertEquals(nodeRecords, nodeNames.size());
assertTrue(nodeNames.contains("1"));
assertTrue(nodeNames.contains("2"));
assertTrue(nodeNames.contains("3"));
int edgeRecords = 0;
Set<String> edgeSpecs = new HashSet<String>();
for(int i = 0; i < props.length; i++) {
if(props[i].containsKey(Graph.PROPERTY_EDGE)) {
String edgeSpec = props[i].getProperty(Graph.PROPERTY_EDGE);
assertNotNull(edgeSpec);
edgeSpecs.add(edgeSpec);
edgeRecords++;
}
}
assertEquals(4, edgeRecords);
assertEquals(edgeRecords, edgeSpecs.size());
assertTrue(edgeSpecs.contains("1-2@car"));
assertTrue(edgeSpecs.contains("2-3@metro"));
assertTrue(edgeSpecs.contains("1-2@universal"));
assertTrue(edgeSpecs.contains("2-3@universal"));
int transportRecords = 0;
Map<String, String> transportTypes = new HashMap<String, String>();
for(int i = 0; i < props.length; i++) {
if(props[i].containsKey(Graph.PROPERTY_TRANSPORT)) {
String name = props[i].getProperty(Graph.PROPERTY_TRANSPORT);
assertNotNull(name);
String users = props[i].getProperty(TransportType.PROPERTY_USERS);
assertNotNull(users);
transportTypes.put(name, users);
transportRecords++;
}
}
assertEquals(3, transportRecords);
assertEquals(transportRecords, transportTypes.size());
assertTrue(transportTypes.containsKey("car"));
assertTrue("phantom+detective".equals(transportTypes.get("car"))
|| "all".equals(transportTypes.get("car")));
assertTrue(transportTypes.containsKey("metro"));
assertTrue("phantom+detective".equals(transportTypes.get("metro"))
|| "all".equals(transportTypes.get("metro")));
assertTrue(transportTypes.containsKey("universal"));
assertTrue("phantom+detective".equals(transportTypes.get("universal"))
|| "all".equals(transportTypes.get("universal")));
}
public void testSerializeLoad() throws Exception {
Graph g = new Graph(SIMPLE_GRAPH);
Properties[] records = g.serialize();
Graph g2 = new Graph(records);
assertEquals(g.getNodes().size(), g2.getNodes().size());
assertEquals(g.getEdges().size(), g2.getEdges().size());
assertEquals(g.getTransportTypes().size(), g2.getTransportTypes().size());
}
public static final String[] FORBIDDEN_TRANSPORT_TYPE = new String[] {
"game: 10, reveal: 1 2 5",
"transport: car, users: all",
"transport: message, users: all",
};
public void testForbiddenTransportType() {
try {
new Graph(FORBIDDEN_TRANSPORT_TYPE);
fail("Graph with forbidden transport type name was loaded");
}
catch(GraphFormatException e) {
assertNotNull(e.getLineNumber());
assertEquals(2, e.getLineNumber().intValue());
assertEquals("Forbidden transport type name: message", e.getMessage());
}
}
}