mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-17 11:19:15 +00:00
added a first test for dynamic route update
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+57
-10
@@ -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
|
||||
|
||||
+3
-1
@@ -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;
|
||||
}
|
||||
|
||||
+59
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+9
-6
@@ -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
|
||||
|
||||
+8
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user