fixed application of score corrections for valid MaxPointsReason and no rank; adjusted test case to accomodate for changed behavior of bug4167

Change-Id: Ic36eaafcde53a0dd2f0816366561b48b73831794
This commit is contained in:
Axel Uhl committed 2017-07-01 08:48:18 +02:00
1 parent 1ee5e21092
commit bfb4e41642
2 files changed
+55 -29

No files matched your search

@@ -112,7 +112,9 @@ public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTest
final List<Competitor> rankedCompetitorsBeforeApplying = leaderboard.getCompetitorsFromBestToWorst(later);
assertEquals(competitors, rankedCompetitorsBeforeApplying); // no effects of preliminary results list yet
f1RaceState.setFinishPositioningConfirmed(now);
final Function<Competitor, Double> expectedPoints = (c)->scores.get(c)==null?(mprs.get(c) == null || mprs.get(c) == MaxPointsReason.NONE ? competitors.indexOf(c)+1 : competitors.size()+1):scores.get(c);
final Function<Competitor, Double> expectedPoints =
(c)->scores.get(c)==null?(mprs.get(c) == null || mprs.get(c) == MaxPointsReason.NONE ?
competitors.indexOf(c)+1 : competitors.size()+1):scores.get(c);
for (final Competitor c : competitors) {
assertEquals(expectedPoints.apply(c), leaderboard.getTotalPoints(c, f1Column, now), 0.00000001);
}
@@ -169,16 +171,22 @@ public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTest
}
/**
* See bug 4025: When a scoring race log event is received it may contain a partial competitor set. It shall depend on
* the previous state resulting from the race log, without the last event, what needs to be done with the leaderboard
* score corrections. If a competitor which which an earlier message did have a correction now no longer is part of
* the latest scoring race log event, and if the score corrections in the leaderboard match up with what the previous
* event had caused, remove that score correction again.
* See bugs 4025 and 4167: When a scoring race log event is received it may contain a partial competitor set. Before bug4167
* there was an implementation that reset score corrections if previous messages had a result for a competitor, and later
* messages did not. With bug 4167 we found that it should be possible to send in partial results, owed to the insight that
* multiple devices may be used for the same race, not all in sync at all times, therefore producing partial results that
* should not clear or overwrite valid results input by other devices.<p>
*
* Therefore, now, with bug4167, this test has been modified such that first it ensures that a partial result
* does <em>not</em> clear a previous score correction. Furthermore it tests that when sending a result that
* has 0 as the rank and {@link MaxPointsReason#NONE} as the {@link MaxPointsReason} with no explicit score set,
* the score correction for the competitor will be reset in the leaderboard.
*/
@Test
public void testApplyingOCSThenClearingOCS() {
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = new MillisecondsTimePoint(now.asMillis()+1000);
TimePoint later = now.plus(1000);
TimePoint yetLater = later.plus(1000);
final int numberOfCompetitors = 2;
setUp(numberOfCompetitors, now, ScoringSchemeType.LOW_POINT);
final CompetitorResults results = new CompetitorResultsImpl();
@@ -202,9 +210,15 @@ public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTest
setResultForCompetitor(competitors.get(1), /* one-based rank */ 0, resultsWithOCSCleared, MaxPointsReason.OCS, /* explicit score */ null);
f1RaceState.setFinishPositioningListChanged(later, resultsWithOCSCleared);
f1RaceState.setFinishPositioningConfirmed(later);
// validate that it got cleared in leaderboard
// validate that it did not get cleared in leaderboard (changed by bug 4167):
assertScoreCorrections(leaderboard, f1Column, competitors.get(1), MaxPointsReason.OCS, numberOfCompetitors+1, /* score is corrected */ false, later);
assertScoreCorrections(leaderboard, f1Column, competitors.get(0), MaxPointsReason.NONE, 1, /* score is corrected */ false, later); // ranking first, one point in low-point scheme
assertScoreCorrections(leaderboard, f1Column, competitors.get(0), MaxPointsReason.OCS, numberOfCompetitors+1, /* score is corrected */ false, later); // ranking first, one point in low-point scheme
// now explicitly overwrite with a 0/null/null result, validating that this will clear the corrections for competitor 0:
setResultForCompetitor(competitors.get(0), /* one-based rank */ 0, resultsWithOCSCleared, /* maxPointsReason */ null, /* explicit score */ null);
f1RaceState.setFinishPositioningListChanged(yetLater, resultsWithOCSCleared);
f1RaceState.setFinishPositioningConfirmed(yetLater);
assertScoreCorrections(leaderboard, f1Column, competitors.get(1), MaxPointsReason.OCS, numberOfCompetitors+1, /* score is corrected */ false, yetLater);
assertScoreCorrections(leaderboard, f1Column, competitors.get(0), MaxPointsReason.NONE, 1, /* score is corrected */ false, yetLater); // ranking first, one point in low-point scheme
}
private void assertScoreCorrections(Leaderboard leaderboard, RaceColumn raceColumn, Competitor competitor,
@@ -219,7 +233,4 @@ public class ApplyScoresFromRaceLogTest extends LeaderboardScoringAndRankingTest
final CompetitorResults results, MaxPointsReason maxPointsReason, Double explicitScore) {
results.add(new CompetitorResultImpl(competitor.getId(), competitor.getName(), oneBasedRank, maxPointsReason, explicitScore, /* finishingTime */ null, /* comment */ null));
}
}
@@ -71,15 +71,19 @@ public class RaceLogScoringReplicator implements RaceColumnListenerWithDefaultAc
* <li>When no {@link CompetitorResult} object is received for a competitor, no change is applied to that
* competitor's score corrections.</li>
* <li>When a {@link CompetitorResult} object is received for a competitor, if it contains {@code null} for the
* score and 0 for the rank, any existing score correction (points) is removed for that competitor, otherwise, the
* score and 0 for the rank or {@code null} for the score and a valid non-{@link MaxPointsReason#NONE NONE}
* {@link MaxPointsReason}, any existing score correction (points) is removed for that competitor, otherwise, the
* score correction (points) from the CompetitorResult object is applied if provided (in this case a rank provided
* in the {@link CompetitorResult} object is ignored); if only a rank but no score is provided in the
* {@link CompetitorResult}, the scoring scheme will compute the score from the rank, and that score will be applied
* as a score correction.</li>
* in the {@link CompetitorResult} object is ignored); if no {@link MaxPointsReason} is provided and only a rank but
* no score is provided in the {@link CompetitorResult}, the scoring scheme will compute the score from the rank,
* considering any {@link MaxPointsReason} provided in the result, and that score will be applied as a score
* correction. If a {@link MaxPointsReason} but no explicit score is provided, the rank is ignored because
* the scoring scheme is expected to compute the score from the {@link MaxPointsReason}; therefore, the score
* correction (points) is cleared in this case so that the scoring scheme will be used for score calculation.</li>
* <li>When a {@link CompetitorResult} object is received for a competitor, if it contains {@code null} for the
* {@link MaxPointsReason}, any existing {@link MaxPointsReason} is removed for that competitor; otherwise, the
* {@link MaxPointsReason} from the {@link CompetitorResult} object is applied. This is handled entirely
* independently of the score (points).</li>
* {@link MaxPointsReason} from the {@link CompetitorResult} object is applied, and if no explicit score is provided
* in the result, the score is computed by the scoring scheme, ignoring any rank provided in the result.</li>
* </ul>
* <p>
*
@@ -116,7 +120,7 @@ public class RaceLogScoringReplicator implements RaceColumnListenerWithDefaultAc
final Competitor competitor = service.getBaseDomainFactory().getExistingCompetitorById(positionedCompetitor.getCompetitorId());
int rankByRaceCommittee = getRankInPositioningListByRaceCommittee(positionedCompetitor);
correctScoreInLeaderboardIfNecessary(leaderboard, raceColumn, timePoint, numberOfCompetitorsInRace, competitor,
rankByRaceCommittee, positionedCompetitor.getScore());
rankByRaceCommittee, positionedCompetitor.getScore(), positionedCompetitor.getMaxPointsReason());
setMaxPointsReasonInLeaderboardIfNecessary(leaderboard, raceColumn, timePoint, positionedCompetitor.getMaxPointsReason(), competitor);
}
// Since the metadata update is used by the Sailing suite to determine the final state of a race, it has to
@@ -137,11 +141,13 @@ public class RaceLogScoringReplicator implements RaceColumnListenerWithDefaultAc
}
}
private void correctScoreInLeaderboardIfNecessary(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint timePoint,
final int numberOfCompetitorsInRace,
Competitor competitor, int rankByRaceCommittee, Double optionalExplicitScore) {
final Double scoreByRaceCommittee = getScoreFromRaceCommittee(leaderboard, raceColumn, timePoint,
numberOfCompetitorsInRace, competitor, rankByRaceCommittee, optionalExplicitScore);
private void correctScoreInLeaderboardIfNecessary(Leaderboard leaderboard, RaceColumn raceColumn,
TimePoint timePoint, final int numberOfCompetitorsInRace, Competitor competitor, int rankByRaceCommittee,
Double optionalExplicitScore, MaxPointsReason maxPointsReason) {
final Double scoreByRaceCommittee = optionalExplicitScore==null && maxPointsReason != null &&
maxPointsReason != MaxPointsReason.NONE ? null :
getScoreFromRaceCommittee(leaderboard, raceColumn, timePoint,
numberOfCompetitorsInRace, competitor, rankByRaceCommittee, optionalExplicitScore, maxPointsReason);
// Apply the score correction if it will cause a change in the explicit score set for the competitor
if (!Util.equalsWithNull(leaderboard.getScoreCorrection().getExplicitScoreCorrection(competitor, raceColumn), scoreByRaceCommittee)) {
applyScoreCorrectionOperation(leaderboard, raceColumn, competitor, scoreByRaceCommittee, timePoint);
@@ -149,19 +155,28 @@ public class RaceLogScoringReplicator implements RaceColumnListenerWithDefaultAc
}
/**
* If a non-{@code null} {@code optionalExplicitScore} is provided, it it returned. Otherwise, a score is determined
* from the {@code oneBasedRankByRaceCommittee} using the leaderboard scoring scheme's
* If a non-{@code null} {@code optionalExplicitScore} is provided, it it returned. Otherwise, if a valid
* {@link MaxPointsReason} is provided, {@code null} is returned as the explicit score because the scoring scheme
* shall be used dynamically to determine the penalty score (see
* {@link ScoringScheme#getPenaltyScore(RaceColumn, Competitor, MaxPointsReason, Integer, com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher, TimePoint)}).
* If no explicit score is provided and no {@link MaxPointsReason} is specified, a score is determined from the
* {@code oneBasedRankByRaceCommittee} using the leaderboard scoring scheme's
* {@link ScoringScheme#getScoreForRank(Leaderboard, RaceColumn, Competitor, int, java.util.concurrent.Callable, com.sap.sailing.domain.leaderboard.NumberOfCompetitorsInLeaderboardFetcher, TimePoint)
* getScoreForRank(...)} method. Usually, scoring schemes will return {@code null} as score for {@code 0} as
* (one-based) rank.
*/
private Double getScoreFromRaceCommittee(Leaderboard leaderboard, RaceColumn raceColumn, TimePoint timePoint,
final int numberOfCompetitorsInRace, Competitor competitor, int oneBasedRankByRaceCommittee,
Double optionalExplicitScore) {
Double optionalExplicitScore, MaxPointsReason maxPointsReason) {
final Double scoreByRaceCommittee;
if (optionalExplicitScore == null) {
scoreByRaceCommittee = leaderboard.getScoringScheme().getScoreForRank(leaderboard, raceColumn, competitor,
oneBasedRankByRaceCommittee, ()->numberOfCompetitorsInRace, leaderboard.getNumberOfCompetitorsInLeaderboardFetcher(), timePoint);
if (maxPointsReason != null && maxPointsReason != MaxPointsReason.NONE) {
// let scoring scheme calculate the penalty score dynamically
scoreByRaceCommittee = null;
} else {
scoreByRaceCommittee = leaderboard.getScoringScheme().getScoreForRank(leaderboard, raceColumn, competitor,
oneBasedRankByRaceCommittee, ()->numberOfCompetitorsInRace, leaderboard.getNumberOfCompetitorsInLeaderboardFetcher(), timePoint);
}
} else {
scoreByRaceCommittee = optionalExplicitScore;
}