mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-03 17:49:40 +00:00
Merge branch 'master' into tracapi
This commit is contained in:
commit
d4c3b5309d
50 files changed
+1612
-159
No files matched your search
Executable
+197
@@ -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();
|
||||
}
|
||||
}
|
||||
Executable
+133
@@ -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();
|
||||
}
|
||||
}
|
||||
+182
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user