bug5154: added a first trivial test case for the reconciler

This commit is contained in:
Axel Uhl committed 2020-03-04 17:06:09 +01:00
1 parent 117cbff8c8
commit b707fa6cb9
5 files changed
+157 -6

No files matched your search

@@ -26,7 +26,6 @@ public class AbortingFlagFinder extends RaceLogAnalyzer<RaceLogFlagEvent> {
}
}
}
return null;
}
@@ -0,0 +1,78 @@
package com.sap.sailing.domain.tracking;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogFlagEvent;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.AbortingFlagFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogPassChangeEventImpl;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.impl.RaceAndCompetitorStatusWithRaceLogReconciler;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.tractrac.model.lib.api.event.IRace;
import com.tractrac.model.lib.api.event.RaceStatusType;
/**
* See also bug 5154 and {@link RaceAndCompetitorStatusWithRaceLogReconciler}.
*
* @author Axel Uhl (D043530)
*
*/
public class TestTracTracRaceAndCompetitorStatusReconciler {
private AbstractLogEventAuthor author;
private TrackedRace trackedRace;
private IRace tractracRace;
private RaceLog raceLog;
private RaceAndCompetitorStatusWithRaceLogReconciler reconciler;
private TimePoint startOfPass;
@Before
public void setUp() {
author = new LogEventAuthorImpl("me", 1);
startOfPass = MillisecondsTimePoint.now();
tractracRace = mock(IRace.class);
trackedRace = mock(TrackedRace.class);
raceLog = new RaceLogImpl("RaceLogID");
when(trackedRace.getAttachedRaceLogs()).thenReturn(Collections.singleton(raceLog));
reconciler = new RaceAndCompetitorStatusWithRaceLogReconciler(DomainFactory.INSTANCE, new RaceLogResolver() {
@Override
public RaceLog resolve(SimpleRaceLogIdentifier identifier) {
return raceLog;
}
});
raceLog.add(new RaceLogPassChangeEventImpl(startOfPass, author, /* pPassId */ 1));
}
@Test
public void testAbandonInFirstPass() {
assertNull(new AbortingFlagFinder(raceLog).analyze());
when(tractracRace.getStatus()).thenReturn(RaceStatusType.ABANDONED);
final TimePoint abandonTimePoint = startOfPass.plus(Duration.ONE_MINUTE);
when(tractracRace.getStatusTime()).thenReturn(abandonTimePoint.asMillis());
reconciler.reconcileRaceStatus(tractracRace, trackedRace);
final RaceLogFlagEvent abandonFlagEvent = new AbortingFlagFinder(raceLog).analyze();
assertNotNull(abandonFlagEvent);
assertSame(Flags.NOVEMBER, abandonFlagEvent.getUpperFlag());
assertTrue(abandonFlagEvent.isDisplayed());
assertEquals(abandonTimePoint, abandonFlagEvent.getLogicalTimePoint());
}
}
@@ -6,10 +6,13 @@ import java.util.stream.StreamSupport;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogFlagEvent;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.AbortingFlagFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
import com.sap.sailing.domain.abstractlog.race.state.impl.ReadonlyRaceStateImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sse.common.Util;
@@ -38,11 +41,73 @@ public class RaceAndCompetitorStatusWithRaceLogReconciler {
this.raceLogResolver = raceLogResolver;
}
/**
* The following race status types are currently available on the TracTrac side (see {@link RaceStatusType}):
* <ul>
* <li>NONE(0)</li>
* <li>START(4)</li>
* <li>RACING(5)</li>
* <li>UNOFFICIAL(7)</li>
* <li>ABANDONED(8)</li>
* <li>OFFICIAL(9)</li>
* <li>GENERAL_RECALL(10)</li>
* <li>POSTPONED(11)</li>
* </ul>
* We are interested in status transitions that need to be reflected by an "N" ("November", abort), "AP" (answering
* pennant, postponement), or 1st substitute (general recall) flag status in the race log. The TracTrac-provided
* status transition has a time stamp on it (see {@link IRace#getStatusTime()}), and so would any aborting flag
* event in a {@link RaceLog} as well as any pass change event. If the TracTrac status time is later than the last
* race log-based aborting flag event from the current pass and the TracTrac status is none of {@code ABANDONED},
* {@code GENERAL_RECALL} or {@code POSTPONED}, a new pass will be established in the race log. If the TracTrac
* status is any of {@code ABANDONED}, {@code GENERAL_RECALL} or {@code POSTPONED}, and the race log has not the
* matching aborting flag in the current pass, and the TracTrac status update time is later than the last race log
* status, the corresponding race log event that represents {@code ABANDONED}, {@code GENERAL_RECALL} or
* {@code POSTPONED}, respectively, will be appended to the {@link #getDefaultRaceLog(TrackedRace) default race
* log}.
* <p>
*
* There is no API currently that allows us to determine the start mode flag. Manual intervention would be required
* if a non-default start mode flag is to be shown.
* <p>
*
* If multiple race logs are attached, a "default" race log will be determined, e.g., based on the one that already
* has the most events in it. See {@link #getDefaultRaceLog}. Here go the cases:
* <ul>
* <li>any to {@code ABANDONED}: ensure the current pass has the "November" (N) flag set</li>
* <li>any to {@code GENERAL_RECALL}: ensure the current pass has the "1st Substitute" flag set</li>
* <li>any to {@code POSTPONED}: ensure the current pass has the "Answering Pennant" (AP) flag set</li>
* <li></li>
* <li></li>
* </ul>
*/
public void reconcileRaceStatus(IRace tractracRace, TrackedRace trackedRace) {
final RaceStatusType status = tractracRace.getStatus();
final RaceStatusType raceStatus = tractracRace.getStatus();
final long raceStatusUpdateTime = tractracRace.getStatusTime();
for (final RaceLog raceLog : trackedRace.getAttachedRaceLogs()) {
final ReadonlyRaceState raceState = ReadonlyRaceStateImpl.getOrCreate(raceLogResolver, raceLog);
final RaceLogRaceStatus raceLogRaceStatus = raceState.getStatus();
final AbortingFlagFinder abortingFlagFinder = new AbortingFlagFinder(raceLog);
final RaceLogFlagEvent abortingFlagEvent = abortingFlagFinder.analyze();
// TODO bug5154:
}
// TODO bug5154 check IRace.getStatus() and somewhere update status once it reached RaceStatusType.OFFICIAL
}
/**
* If no race log is attached to the {@code trackedRace}, {@code null} is returned. If exactly one race log
* is attached, it is returned. If multiple race logs are attached, the one with the most entries is returned.
*/
private RaceLog getDefaultRaceLog(TrackedRace trackedRace) {
final RaceLog result;
if (Util.isEmpty(trackedRace.getAttachedRaceLogs())) {
result = null;
} else {
result = Util.stream(trackedRace.getAttachedRaceLogs()).max((rl1, rl2)->Integer.compare(rl1.size(), rl2.size())).get();
}
return result;
}
/**
* If an official finish time exists on the {@link IRaceCompetitor} but not in any of the {@link TrackedRace}'s
* {@link RaceLog}-based results, or the corresponding TracTrac information is newer than the race log-based result,
@@ -60,7 +125,7 @@ public class RaceAndCompetitorStatusWithRaceLogReconciler {
*/
public void reconcileCompetitorStatus(IRaceCompetitor raceCompetitor, TrackedRace trackedRace) {
final Competitor competitor = domainFactory.resolveCompetitor(raceCompetitor.getCompetitor());
final RaceCompetitorStatusType status = raceCompetitor.getStatus();
final RaceCompetitorStatusType competitorStatus = raceCompetitor.getStatus();
final int officialRank = raceCompetitor.getOfficialRank();
final long officialFinishingTime = raceCompetitor.getOfficialFinishTime();
final long timePointForStatusEvent = raceCompetitor.getStatusTime();
@@ -550,13 +550,13 @@ public class RaceContext {
RaceLogFlagEvent abortingFlagEvent = checkForAbortFlagEvent();
if (abortingFlagEvent != null) {
Flags upperFlag = abortingFlagEvent.getUpperFlag();
if(upperFlag.equals(Flags.AP)) {
if (upperFlag.equals(Flags.AP)) {
return RaceViewState.POSTPONED;
}
if(upperFlag.equals(Flags.NOVEMBER)) {
if (upperFlag.equals(Flags.NOVEMBER)) {
return RaceViewState.ABANDONED;
}
if(upperFlag.equals(Flags.FIRSTSUBSTITUTE)) {
if (upperFlag.equals(Flags.FIRSTSUBSTITUTE)) {
return RaceViewState.ABANDONED;
}
}
@@ -16,6 +16,7 @@ import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import com.sap.sse.common.util.MappingIterable;
@@ -928,4 +929,12 @@ public class Util {
c++;
}
}
/**
* @return a non-parallel stream for the {@link Iterable} passed. Short for
* {@code StreamSupport.stream(iterable.spliterator(), false)}.
*/
public static <T> Stream<T> stream(Iterable<T> iterable) {
return StreamSupport.stream(iterable.spliterator(), /* parallel */ false);
}
}