added a first test for dynamic route update

This commit is contained in:
Axel Uhl
2011-06-11 16:36:58 +02:00
parent 3607a459e7
commit be3d1f2df7
7 changed files with 170 additions and 20 deletions
@@ -29,7 +29,7 @@ public class Patch<T> {
private List<Delta<T>> deltas = new LinkedList<Delta<T>>();
/**
* Apply this patch to the given target
* Apply this patch to the given target, producing a new list as result
* @return the patched text
* @throws PatchFailedException if can't apply patch
*/
@@ -43,6 +43,21 @@ public class Patch<T> {
return result;
}
/**
* Apply this patch to the given target in-place, updating the <code>target</code> list
* @return the patched text
* @throws PatchFailedException if can't apply patch
*/
public List<T> applyToInPlace(List<T> target) throws PatchFailedException {
List<T> result = target;
ListIterator<Delta<T>> it = getDeltas().listIterator(deltas.size());
while (it.hasPrevious()) {
Delta<T> delta = it.previous();
delta.applyTo(result);
}
return result;
}
/**
* Restore the text to original. Opposite to applyTo() method.
* @param target the given target
@@ -74,4 +89,20 @@ public class Patch<T> {
Collections.sort(deltas, new DeltaComparator<T>());
return deltas;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append('[');
boolean first = true;
for (Delta<T> delta : getDeltas()) {
if (first) {
first = false;
} else {
result.append(", ");
}
result.append(delta);
}
return result.toString();
}
}
@@ -2,17 +2,21 @@ package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.CourseListener;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Waypoint;
@@ -22,19 +26,25 @@ import com.sap.sailing.domain.tracking.DynamicTrackedEvent;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.Receiver;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sailing.domain.tractracadapter.impl.RaceCourseReceiver;
import com.sap.sailing.util.Util;
import com.sap.sailing.util.Util.Triple;
import com.tractrac.clientmodule.Race;
import com.tractrac.clientmodule.Route;
import com.tractrac.clientmodule.data.RouteData;
import difflib.Chunk;
import difflib.Delta;
import difflib.DiffUtils;
import difflib.Patch;
import difflib.PatchFailedException;
public class CourseUpdateTest extends AbstractTracTracLiveTest {
private Course course;
private Event domainEvent;
private DynamicTrackedEvent trackedEvent;
private final RouteData[] routeData = new RouteData[1];
public CourseUpdateTest() throws URISyntaxException, MalformedURLException {
super();
}
@@ -44,15 +54,26 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest {
super.setUp();
domainEvent = DomainFactory.INSTANCE.createEvent(getEvent());
trackedEvent = DomainFactory.INSTANCE.trackEvent(domainEvent);
Iterable<Receiver> myReceivers = DomainFactory.INSTANCE.getUpdateReceivers(trackedEvent, getEvent(),
EmptyWindStore.INSTANCE, ReceiverType.RACECOURSE);
List<Receiver> myReceivers = new ArrayList<Receiver>();
myReceivers.add(new RaceCourseReceiver(trackedEvent, getEvent(), EmptyWindStore.INSTANCE, /* millisecondsOverWhichToAverageWind */
30000,
/* millisecondsOverWhichToAverageSpeed */30000) {
@Override
protected void handleEvent(Triple<Route, RouteData, Race> event) {
synchronized (routeData) {
routeData[0] = event.getB();
routeData.notifyAll();
}
super.handleEvent(event);
}
});
addListenersForStoredDataAndStartController(myReceivers);
RaceDefinition race = DomainFactory.INSTANCE.getRaceDefinition(getEvent().getRaceList().iterator().next());
course = race.getCourse();
assertNotNull(course);
assertEquals(3, Util.size(course.getWaypoints()));
}
@Test
public void testWaypointListDiff() {
Waypoint wp1 = new WaypointImpl(new BuoyImpl("b1"));
@@ -68,7 +89,7 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest {
changedWaypoints.add(wp1);
changedWaypoints.add(wp3);
changedWaypoints.add(wp4);
Patch<Waypoint> patch = DiffUtils.diff(waypoints, changedWaypoints);
assertEquals(1, patch.getDeltas().size());
Delta<Waypoint> firstDelta = patch.getDeltas().iterator().next();
@@ -79,10 +100,36 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest {
assertEquals(1, deletedWaypoints.size());
assertEquals(wp2, deletedWaypoints.iterator().next());
}
@Test
public void testLastWaypointRemoved() {
Iterable<Waypoint> waypoints = course.getWaypoints();
// TODO continue with testLastWaypointRemoved()...
public void testLastWaypointRemoved() throws PatchFailedException, InterruptedException {
final boolean[] result = new boolean[1];
final com.tractrac.clientmodule.ControlPoint cp1 = new com.tractrac.clientmodule.ControlPoint(
UUID.randomUUID(), "CP1", /* hasTwo */false) {
};
synchronized (routeData) {
while (routeData[0] == null) {
routeData.wait();
}
}
final List<com.tractrac.clientmodule.ControlPoint> controlPoints = new ArrayList<com.tractrac.clientmodule.ControlPoint>(
routeData[0].getPoints());
controlPoints.add(cp1);
final DomainFactory domainFactory = DomainFactory.INSTANCE;
course.addCourseListener(new CourseListener() {
@Override
public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
System.out.println("waypointAdded " + zeroBasedIndex + " / " + waypointThatGotAdded);
ControlPoint cp = domainFactory.getControlPoint(cp1);
result[0] = zeroBasedIndex == controlPoints.size() - 1 && waypointThatGotAdded.getControlPoint() == cp;
}
@Override
public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) {
System.out.println("waypointRemoved " + zeroBasedIndex + " / " + waypointThatGotRemoved);
}
});
domainFactory.updateCourseWaypoints(course, controlPoints);
assertTrue(result[0]);
}
}
@@ -10,5 +10,6 @@ Require-Bundle: com.sap.sailing.domain,
org.json.simple;bundle-version="1.1.0",
com.tractrac.resultapi,
com.googlecode.java-diff-utils;bundle-version="1.3.0"
Export-Package: com.sap.sailing.domain.tractracadapter
Export-Package: com.sap.sailing.domain.tractracadapter,
com.sap.sailing.domain.tractracadapter.impl;x-friends:="com.sap.sailing.domain.test"
Bundle-ActivationPolicy: lazy
@@ -35,6 +35,8 @@ import com.tractrac.clientmodule.data.ControlPointPositionData;
import com.tractrac.clientmodule.data.DataController;
import com.tractrac.clientmodule.data.Position;
import difflib.PatchFailedException;
public interface DomainFactory {
static DomainFactory INSTANCE = new DomainFactoryImpl();
@@ -130,5 +132,5 @@ public interface DomainFactory {
* of waypoints. The waypoints are created from the control points and represent usages of the control points
* in a course. A single control point may be used more than once in a course's list of waypoints.
*/
void updateCourseWaypoints(Course courseToUpdate, List<ControlPoint> controlPoints);
void updateCourseWaypoints(Course courseToUpdate, List<ControlPoint> controlPoints) throws PatchFailedException;
}
@@ -0,0 +1,59 @@
package com.sap.sailing.domain.tractracadapter.impl;
import java.util.AbstractList;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.util.Util;
import difflib.Patch;
/**
* Wraps a {@link Course} as a {@link List<Waypoint>} based on the course's {@link Course#addWaypoint(int, Waypoint)}
* and {@link Course#removeWaypoint(Waypoint)} methods. This is convenient in case a {@link Patch<Waypoint>} is to be
* {@link Patch#applyTo(java.util.List) applied} to a {@link Course}.
*
* @author Axel Uhl (D043530)
*
*/
public class CourseAsWaypointList extends AbstractList<Waypoint> {
private final Course course;
public CourseAsWaypointList(Course course) {
super();
this.course = course;
}
@Override
public void add(int index, Waypoint element) {
course.addWaypoint(index, element);
}
@Override
public Waypoint remove(int index) {
Waypoint toRemove = get(index);
course.removeWaypoint(toRemove);
return toRemove;
}
@Override
public Waypoint get(int index) {
if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int i=0;
for (Waypoint waypoint : course.getWaypoints()) {
if (i == index) {
return waypoint;
}
i++;
}
throw new ArrayIndexOutOfBoundsException(index);
}
@Override
public int size() {
return Util.size(course.getWaypoints());
}
}
@@ -69,6 +69,7 @@ import com.tractrac.clientmodule.data.ControlPointPositionData;
import difflib.DiffUtils;
import difflib.Patch;
import difflib.PatchFailedException;
public class DomainFactoryImpl implements DomainFactory {
private static final Logger logger = Logger.getLogger(DomainFactoryImpl.class.getName());
@@ -128,7 +129,7 @@ public class DomainFactoryImpl implements DomainFactory {
}
@Override
public void updateCourseWaypoints(Course courseToUpdate, List<ControlPoint> controlPoints) {
public void updateCourseWaypoints(Course courseToUpdate, List<ControlPoint> controlPoints) throws PatchFailedException {
Iterable<Waypoint> courseWaypoints = courseToUpdate.getWaypoints();
List<Waypoint> newWaypointList = new LinkedList<Waypoint>();
for (ControlPoint tractracControlPoint : controlPoints) {
@@ -136,8 +137,8 @@ public class DomainFactoryImpl implements DomainFactory {
newWaypointList.add(waypoint);
}
Patch<Waypoint> patch = DiffUtils.diff(courseWaypoints, newWaypointList);
// now for each chunk in the patch make the corresponding update to the TrackedLeg and TrackedLegOfCompetitor
// collections in TrackedRace TODO which means this API is ill-defined...
CourseAsWaypointList courseAsWaypointList = new CourseAsWaypointList(courseToUpdate);
patch.applyToInPlace(courseAsWaypointList);
}
@Override
@@ -309,9 +310,11 @@ public class DomainFactoryImpl implements DomainFactory {
}
@Override
public Iterable<Receiver> getUpdateReceivers(DynamicTrackedEvent trackedEvent, com.tractrac.clientmodule.Event tractracEvent, WindStore windStore) {
return getUpdateReceivers(trackedEvent, tractracEvent, windStore,
ReceiverType.RACECOURSE, ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS);
public Iterable<Receiver> getUpdateReceivers(DynamicTrackedEvent trackedEvent,
com.tractrac.clientmodule.Event tractracEvent, WindStore windStore) {
return getUpdateReceivers(trackedEvent, tractracEvent, windStore, ReceiverType.RACECOURSE,
ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACESTARTFINISH,
ReceiverType.RAWPOSITIONS);
}
@Override
@@ -20,6 +20,8 @@ import com.tractrac.clientmodule.Route;
import com.tractrac.clientmodule.data.ICallbackData;
import com.tractrac.clientmodule.data.RouteData;
import difflib.PatchFailedException;
/**
* The ordering of the {@link ControlPoint}s of a {@link Course} are received
* dynamically through a callback interface. Therefore, when connected to an
@@ -80,7 +82,12 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<Route, RouteDa
logger.log(Level.INFO, "Received course update for existing race "+event.getC().getName());
// race already exists; this means that we obviously found a course re-definition (yuck...)
// Therefore, don't create TrackedRace again because it already exists.
domainFactory.updateCourseWaypoints(course, event.getB().getPoints());
try {
domainFactory.updateCourseWaypoints(course, event.getB().getPoints());
} catch (PatchFailedException e) {
logger.log(Level.SEVERE, "Internal error updating race course "+course+": "+e.getMessage());
logger.throwing(RaceCourseReceiver.class.getName(), "handleEvent", e);
}
} else {
logger.log(Level.INFO, "Received course for non-existing race "+event.getC().getName()+". Creating RaceDefinition.");
// create race redefinition