Merge branch 'master' into tracapi

This commit is contained in:
Axel Uhl committed 2014-05-10 18:14:02 +02:00
commit d4c3b5309d
50 files changed
+1612 -159

No files matched your search

@@ -0,0 +1,197 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.CompetitorChangeListener;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Nationality;
import com.sap.sailing.domain.base.Team;
import com.sap.sailing.domain.base.WithNationality;
import com.sap.sailing.domain.base.impl.DomainFactoryImpl;
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
import com.sap.sailing.domain.common.Color;
import com.sap.sailing.domain.common.impl.RGBColor;
/**
* Tests the {@link CompetitorChangeListener} stuff including the serialization of the transitive listener
* pattern on the {@link Team} structure.
*
* @author Axel Uhl (D043530)
*
*/
public class CompetitorListenerTest extends AbstractSerializationTest {
private CompetitorChangeListener listener;
private boolean nationalityChanged;
private Object whatChangedNationality;
private Object oldNationality;
private Object newNationality;
private boolean sailIdChanged;
private String oldSailId;
private String newSailId;
private boolean nameChanged;
private String oldName;
private String newName;
private boolean colorChanged;
private Color oldColor;
private Color newColor;
private DynamicCompetitor competitor;
private DomainFactoryImpl baseDomainFactory;
@Before
public void setUp() {
baseDomainFactory = new DomainFactoryImpl();
competitor = TrackBasedTest.createCompetitor("Hasso");
nationalityChanged = false;
whatChangedNationality = null;
oldNationality = null;
newNationality = null;
sailIdChanged = false;
oldSailId = null;
newSailId = null;
nameChanged = false;
oldName = null;
newName = null;
colorChanged = false;
oldColor = null;
newColor = null;
listener = new CompetitorChangeListener() {
private static final long serialVersionUID = 4581029778988240209L;
@Override
public void nationalityChanged(WithNationality what, Nationality oldNationality, Nationality newNationality) {
nationalityChanged = true;
whatChangedNationality = what;
CompetitorListenerTest.this.oldNationality = oldNationality;
CompetitorListenerTest.this.newNationality = newNationality;
}
@Override
public void sailIdChanged(String oldSailId, String newSailId) {
sailIdChanged = true;
CompetitorListenerTest.this.oldSailId = oldSailId;
CompetitorListenerTest.this.newSailId = newSailId;
}
@Override
public void nameChanged(String oldName, String newName) {
nameChanged = true;
CompetitorListenerTest.this.oldName = oldName;
CompetitorListenerTest.this.newName = newName;
}
@Override
public void colorChanged(Color oldColor, Color newColor) {
colorChanged = true;
CompetitorListenerTest.this.oldColor = oldColor;
CompetitorListenerTest.this.newColor = newColor;
}
};
competitor.addCompetitorChangeListener(listener);
}
@Test
public void testSimpleCompetitorListenerPatternForName() {
competitor.setName("Dr. Hasso Plattner");
assertTrue(nameChanged);
assertEquals("Hasso", oldName);
assertEquals("Dr. Hasso Plattner", newName);
assertFalse(sailIdChanged);
assertFalse(colorChanged);
assertFalse(nationalityChanged);
}
@Test
public void testSimpleCompetitorListenerPatternForSailId() {
final String myOldSailId = competitor.getBoat().getSailID();
competitor.getBoat().setSailId("POR 40");
assertFalse(nameChanged);
assertTrue(sailIdChanged);
assertEquals(myOldSailId, oldSailId);
assertEquals("POR 40", newSailId);
assertFalse(colorChanged);
assertFalse(nationalityChanged);
}
@Test
public void testSimpleCompetitorListenerPatternForColor() {
final Color myOldColor = competitor.getColor();
final RGBColor myNewColor = new RGBColor(123, 12, 234);
competitor.setColor(myNewColor);
assertFalse(nameChanged);
assertFalse(sailIdChanged);
assertTrue(colorChanged);
assertEquals(myOldColor, oldColor);
assertEquals(myNewColor, newColor);
assertFalse(nationalityChanged);
}
@Test
public void testSimpleCompetitorListenerPatternForNationality() {
final Nationality myOldNationality = competitor.getTeam().getNationality();
final Nationality myNewNationality = DomainFactory.INSTANCE.getOrCreateNationality("POR");
competitor.getTeam().setNationality(myNewNationality);
assertFalse(nameChanged);
assertFalse(sailIdChanged);
assertFalse(colorChanged);
assertTrue(nationalityChanged);
assertEquals(myOldNationality, oldNationality);
assertEquals(myNewNationality, newNationality);
assertSame(competitor.getTeam(), whatChangedNationality);
}
@Test
public void testCompetitorSerializationLosesAllExternalListeners() throws ClassNotFoundException, IOException {
DomainFactory baseDomainFactory = new DomainFactoryImpl();
DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory);
clonedCompetitor.setName("Dr. Hasso Plattner");
clonedCompetitor.getBoat().setSailId("POR 40");
final RGBColor myNewColor = new RGBColor(123, 12, 234);
clonedCompetitor.setColor(myNewColor);
final Nationality myNewNationality = baseDomainFactory.getOrCreateNationality("POR");
clonedCompetitor.getTeam().setNationality(myNewNationality);
assertFalse(nameChanged);
assertFalse(sailIdChanged);
assertFalse(colorChanged);
assertFalse(nationalityChanged);
}
@Test
public void testCompetitorSerializationListenerPatternForNameOnSerializedCompetitor() throws ClassNotFoundException, IOException {
DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory);
competitor = clonedCompetitor;
competitor.addCompetitorChangeListener(listener);
testSimpleCompetitorListenerPatternForName();
}
@Test
public void testCompetitorSerializationListenerPatternForColorOnSerializedCompetitor() throws ClassNotFoundException, IOException {
DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory);
competitor = clonedCompetitor;
competitor.addCompetitorChangeListener(listener);
testSimpleCompetitorListenerPatternForColor();
}
@Test
public void testCompetitorSerializationListenerPatternForSailIdOnSerializedCompetitor() throws ClassNotFoundException, IOException {
DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory);
competitor = clonedCompetitor;
competitor.addCompetitorChangeListener(listener);
testSimpleCompetitorListenerPatternForSailId();
}
@Test
public void testCompetitorSerializationListenerPatternForNationalityOnSerializedCompetitor() throws ClassNotFoundException, IOException {
DynamicCompetitor clonedCompetitor = cloneBySerialization(competitor, baseDomainFactory);
competitor = clonedCompetitor;
competitor.addCompetitorChangeListener(listener);
testSimpleCompetitorListenerPatternForNationality();
}
}
@@ -0,0 +1,133 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
import org.junit.Test;
import com.sap.sailing.util.impl.ConcurrentWeakHashMap;
/**
* Tests the class {@link ConcurrentWeakHashMap}.
*
* @author Axel Uhl (D043530)
*
*/
public class ConcurrentWeakHashMapTest {
@Test
public void testPutAndGet() {
ConcurrentWeakHashMap<String, String> m = new ConcurrentWeakHashMap<>();
m.put("a", "b");
assertEquals("b", m.get("a"));
}
@Test
public void testPutAndIterate() {
ConcurrentWeakHashMap<String, String> m = new ConcurrentWeakHashMap<>();
m.put("a", "b");
assertEquals("b", m.values().iterator().next());
assertEquals("a", m.keySet().iterator().next());
assertEquals("a", m.entrySet().iterator().next().getKey());
}
@Test
public void testPutAndPutAgain() {
ConcurrentWeakHashMap<Integer, String> m = new ConcurrentWeakHashMap<>();
Integer one1 = new Integer(1);
Integer one2 = new Integer(1);
assertNotSame(one1, one2);
m.put(one1, "a");
String replaced = m.put(one2, "b");
assertEquals("a", replaced);
assertEquals(1, m.size());
assertEquals(m.keySet().iterator().next(), one2);
}
@Test
public void testRemoveWithSame() {
ConcurrentWeakHashMap<Integer, String> m = new ConcurrentWeakHashMap<>();
Integer one = new Integer(1);
m.put(one, "a");
m.remove(one);
assertTrue(m.isEmpty());
}
@Test
public void testRemoveImpactOnKeySetAndEntrySet() {
ConcurrentWeakHashMap<Integer, String> m = new ConcurrentWeakHashMap<>();
Integer one = new Integer(1);
m.put(one, "a");
Set<Integer> keySet = m.keySet();
Set<Entry<Integer, String>> entrySet = m.entrySet();
assertEquals(1, keySet.size());
assertEquals(1, entrySet.size());
m.remove(one);
assertTrue(m.isEmpty());
assertTrue(keySet.isEmpty());
assertTrue(entrySet.isEmpty());
}
@Test
public void testRemoveWithEqual() {
ConcurrentWeakHashMap<Integer, String> m = new ConcurrentWeakHashMap<>();
Integer one1 = new Integer(1);
Integer one2 = new Integer(1);
assertNotSame(one1, one2);
m.put(one1, "a");
m.remove(one2);
assertTrue(m.isEmpty());
}
@Test
public void removeThrougKeySet() {
ConcurrentWeakHashMap<Integer, String> m = new ConcurrentWeakHashMap<>();
Integer one1 = new Integer(1);
m.put(one1, "a");
final Set<Integer> keySet = m.keySet();
Iterator<Integer> keySetIter = keySet.iterator();
keySetIter.next();
keySetIter.remove();
assertTrue(m.isEmpty());
assertTrue(keySet.isEmpty());
assertTrue(m.entrySet().isEmpty());
}
@Test
public void testPutAndGetWithSubsequentGC() {
Object key = createKeyObject();
ConcurrentWeakHashMap<Object, String> m = new ConcurrentWeakHashMap<>();
m.put(key, "b");
assertEquals("b", m.get(key));
key = null;
forceEnqueuableWeakReferencesToBeEnqueued();
assertTrue(m.isEmpty());
}
private void forceEnqueuableWeakReferencesToBeEnqueued() {
System.gc();
try {
long[] hugeArrayThatWontGetAllocated = new long[Integer.MAX_VALUE];
assertTrue(hugeArrayThatWontGetAllocated.length > 0);
} catch (OutOfMemoryError e) {
// Ignore OME
}
try {
Thread.sleep(50); // give GC some time to finish enqueuing and finalizing all weakly-referenced objects
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* Use a separate method for this, assuming that the compiler won't put the object on the using method's stack
* @return
*/
private Object createKeyObject() {
return new Object();
}
}
@@ -0,0 +1,182 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.TrackBasedEstimationWindTrackImpl;
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl;
/**
* See the issues documented in bug 1923 (http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=1923).
* This test mocks {@link TrackedRace} to the degree necessary and fires more or less random "measured"
* wind fixes against the {@link TrackBasedEstimationWindTrackImpl} while concurrently reading massively
* from it. With this, the test is supposed to reproduce conditions as observed during events, leading
* up to bug 1923. This can then be the basis for a fix.
*
* @author Axel Uhl (D043530)
*
*/
public class WindEstimationLockingUnderLoadTest {
private static final Logger logger = Logger.getLogger(WindEstimationLockingUnderLoadTest.class.getName());
private static final int MIN_WIND_SPEED_IN_KNOTS = 5;
private static final int MAX_WIND_SPEED_IN_KNOTS = 20;
private TrackedRace mockedTrackedRace;
private WindSource realWindSource;
private TrackBasedEstimationWindTrackImpl estimationTrack;
private WindTrackImpl measuredTrack;
@Before
public void setUp() {
realWindSource = new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, "1");
measuredTrack = new WindTrackImpl(WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, /* useSpeed */ true, /* nameForReadWriteLock */ "Test wind track in "+getClass().getName());
mockedTrackedRace = mockTrackedRace();
estimationTrack = new TrackBasedEstimationWindTrackImpl(mockedTrackedRace, WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, 0.5);
}
@After
public void tearDown() {
// clean up all Mockito stubbing leaks, particularly the InvocationImpl objects attached to any ThreadLocal;
// see also bug 1923, comment #9.
Mockito.reset();
}
private TrackedRace mockTrackedRace() {
TrackedRace result = mock(TrackedRace.class);
RaceDefinition mockedRaceDefinition = mock(RaceDefinition.class);
when(result.getRace()).thenReturn(mockedRaceDefinition);
when(mockedRaceDefinition.getName()).thenReturn("Test Race");
when(result.getEstimatedWindDirectionWithConfidence((Position) any(), (TimePoint) any())).thenAnswer(new Answer<WindWithConfidence<TimePoint>>() {
public WindWithConfidence<TimePoint> answer(InvocationOnMock invocation) {
return randomWindOrNull();
}
});
when(result.getOrCreateWindTrack(realWindSource)).thenReturn(measuredTrack);
when(result.getStatus()).thenReturn(new TrackedRaceStatusImpl(TrackedRaceStatusEnum.TRACKING, /* loadingProgress */ 1.0));
when(result.getMillisecondsOverWhichToAverageWind()).thenReturn(30000l);
return result;
}
private WindWithConfidence<TimePoint> randomWindOrNull() {
final WindWithConfidence<TimePoint> result;
double speedInKnots = MAX_WIND_SPEED_IN_KNOTS*Math.random();
if (speedInKnots < MIN_WIND_SPEED_IN_KNOTS) {
result = null;
} else {
double directionInDegrees = 360*Math.random();
double confidence = Math.random();
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
result = new WindWithConfidenceImpl<TimePoint>(new WindImpl(new DegreePosition(49, 8), now, new KnotSpeedWithBearingImpl(speedInKnots, new DegreeBearingImpl(directionInDegrees))),
confidence, now, /* useSpeed */ true);
}
return result;
}
@Test
public void testSimpleWindEstimation() {
// We can expect MIN_WIND_SPEED_IN_KNOTS/MAX_WIND_SPEED_IN_KNOTS results to be null, but as there is a significant
// difference between the two, with a sufficient amount of random samples there has to be at least one non-null
// result
int notNullCount = 0;
for (int i=0; i<1000; i++) {
Wind estimatedWind = estimationTrack.getAveragedWind(/* position */ null, MillisecondsTimePoint.now().minus(10000*i));
if (estimatedWind != null) {
notNullCount++;
}
}
assertTrue(notNullCount > 0);
}
@Test
public void testSendingNewWindFixToEstimationTrack() {
final WindImpl wind = new WindImpl(new DegreePosition(49, 8), MillisecondsTimePoint.now(),
new KnotSpeedWithBearingImpl(/* speedInKnots */12., new DegreeBearingImpl(47)));
addMeasuredWindFix(wind);
// no verdict, really; just expecting that there is no exception raised
}
@Test
public void testAddingManyWindFixesWhileReadingLikeCrazy() throws InterruptedException {
Thread inserter = new Thread("Inserter thread in "+getClass().getName()+".testAddingManyWindFixesWhileReadingLikeCrazy") {
@Override
public void run() {
TimePoint now = MillisecondsTimePoint.now();
for (int i = 0; i < 100000; i++) {
final WindImpl wind = new WindImpl(new DegreePosition(49, 8), now, new KnotSpeedWithBearingImpl(
/* speedInKnots */12., new DegreeBearingImpl(47)));
addMeasuredWindFix(wind);
now = now.plus(10);
}
logger.info("Inserter thread done");
}
};
Runnable readerRunnable = new Runnable() {
@Override
public void run() {
TimePoint now = MillisecondsTimePoint.now();
for (int i=0; i<100000; i++) {
estimationTrack.getAveragedWind(/* position */ null, now);
now = now.plus(10); // the cache quantizes to a full second; so not all time points will automatically fetch uncached values
}
logger.info(Thread.currentThread().getName()+" done");
}
};
inserter.start();
List<Thread> readers = new ArrayList<Thread>();
for (int i = 0; i < 4; i++) {
Thread reader = new Thread(readerRunnable, "Reader thread "+i+" in " + getClass().getName()
+ ".testAddingManyWindFixesWhileReadingLikeCrazy");
reader.start();
readers.add(reader);
}
inserter.join();
for (Thread reader : readers) {
reader.join();
}
}
/**
* Adds the wind fix to the {@link #measuredTrack} and explicitly notifies {@link #estimationTrack} which is
* not a listener on the mocked tracked race or the wind track and therefore otherwise wouldn't receive the fix
*/
private void addMeasuredWindFix(final WindImpl wind) {
measuredTrack.add(wind);
estimationTrack.windDataReceived(wind, realWindSource);
}
}