Cleanup: SegmentStatisticUpdater.

This commit is contained in:
Dennis Guse
2025-11-11 08:12:24 +01:00
committed by Dennis Guse
parent ebcdd87481
commit 653352255e
4 changed files with 83 additions and 92 deletions
@@ -16,20 +16,21 @@
package de.dennisguse.opentracks.stats; package de.dennisguse.opentracks.stats;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import de.dennisguse.opentracks.data.models.AltitudeExtremities;
import de.dennisguse.opentracks.data.models.AltitudeGainLoss;
import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.Statistics;
/** /**
* Tests for {@link SegmentStatisticUpdater}. * Tests for {@link SegmentStatisticUpdater}.
@@ -40,89 +41,87 @@ import de.dennisguse.opentracks.data.models.Speed;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class SegmentStatisticUpdaterTest { public class SegmentStatisticUpdaterTest {
private SegmentStatisticUpdater statistics; @Deprecated //TODO SegmentStatisticsUpdater should always have data, right?
@Before
public void setUp() {
statistics = new SegmentStatisticUpdater();
}
@Test @Test
public void testMerge_no_data() { public void testMerge_no_data() {
// given // given
SegmentStatisticUpdater statistics2 = new SegmentStatisticUpdater(); SegmentStatisticUpdater subject = new SegmentStatisticUpdater();
SegmentStatisticUpdater append = new SegmentStatisticUpdater();
// when // when
statistics.merge(statistics2); subject.merge(append);
Statistics result = subject.getStatistics();
// then // then
assertNull(statistics.getStartTime()); assertEquals(
assertNull(statistics.getStopTime()); new Statistics(
assertEquals(Duration.ZERO, statistics.getMovingTime()); null,
assertEquals(Duration.ZERO, statistics.getTotalTime()); null,
Duration.ZERO,
assertNull(statistics.getTotalAltitudeGain()); Duration.ZERO,
assertNull(statistics.getTotalAltitudeLoss()); Distance.ZERO,
assertEquals(Double.NEGATIVE_INFINITY, statistics.getMaxAltitude(), 0.0); Speed.ZERO,
assertEquals(Double.POSITIVE_INFINITY, statistics.getMinAltitude(), 0.0); null,
assertEquals(0.0, statistics.getMaxSpeed().toMPS(), 0.0); null,
assertEquals(0.0, statistics.getAverageSpeed().toMPS(), 0.0); null,
assertEquals(0.0, statistics.getAverageMovingSpeed().toMPS(), 0.0); null
assertNull(statistics.getAverageHeartRate()); ),
result
);
} }
@Test @Test
public void testMerge() { public void testMerge() {
// given // given
SegmentStatisticUpdater statistics2 = new SegmentStatisticUpdater(); SegmentStatisticUpdater subject = new SegmentStatisticUpdater(
statistics.setStartTime(Instant.ofEpochMilli(1000)); // Resulting start time new Statistics(
statistics.setStopTime(Instant.ofEpochMilli(2500)); Instant.ofEpochMilli(1000),
statistics2.setStartTime(Instant.ofEpochMilli(3000)); Instant.ofEpochMilli(2500),
statistics2.setStopTime(Instant.ofEpochMilli(4000)); // Resulting stop time Duration.ofMillis(1500),
statistics.setTotalTime(Duration.ofMillis(1500)); Duration.ofMillis(700),
statistics2.setTotalTime(Duration.ofMillis(1000)); // Result: 1500+1000 Distance.of(750.0),
statistics.setMovingTime(Duration.ofMillis(700)); Speed.of(60.0),
statistics2.setMovingTime(Duration.ofMillis(600)); // Result: 700+600 new AltitudeExtremities(1200, 1250),
statistics.setTotalDistance(Distance.of(750.0)); new AltitudeGainLoss(50, 0),
statistics2.setTotalDistance(Distance.of(350.0)); // Result: 750+350 HeartRate.of(100),
statistics.setTotalAltitudeGain(50.0f); null
statistics2.setTotalAltitudeGain(850.0f); // Result: 850+50 )
statistics.setMaxSpeed(Speed.of(60.0)); // Resulting max speed );
statistics2.setMaxSpeed(Speed.of(30.0));
statistics.setMaxAltitude(1250.0); SegmentStatisticUpdater append = new SegmentStatisticUpdater(
statistics.setMinAltitude(1200.0); // Resulting min altitude new Statistics(
statistics2.setMaxAltitude(3575.0); // Resulting max altitude Instant.ofEpochMilli(3000),
statistics2.setMinAltitude(2800.0); Instant.ofEpochMilli(4000),
statistics.setAverageHeartRate(HeartRate.of(100f)); Duration.ofMillis(1000),
statistics2.setAverageHeartRate(HeartRate.of(200f)); Duration.ofMillis(600),
Distance.of(350.0),
Speed.of(30.0),
new AltitudeExtremities(2800.0, 3575.0),
new AltitudeGainLoss(850, 0),
HeartRate.of(200),
null
)
);
// when // when
statistics.merge(statistics2); subject.merge(append);
Statistics result = subject.getStatistics();
// then // then
assertEquals(Instant.ofEpochMilli(1000), statistics.getStartTime()); assertEquals(
assertEquals(Instant.ofEpochMilli(4000), statistics.getStopTime()); new Statistics(
assertEquals(Duration.ofMillis(2500), statistics.getTotalTime()); Instant.ofEpochMilli(1000),
assertEquals(Duration.ofMillis(1300), statistics.getMovingTime()); Instant.ofEpochMilli(4000),
assertEquals(1100.0, statistics.getTotalDistance().toM(), 0.001); Duration.ofMillis(2500),
assertEquals(900.0, statistics.getTotalAltitudeGain(), 0.001); Duration.ofMillis(1300),
assertEquals(Speed.of(statistics.getTotalDistance(), statistics.getMovingTime()).toMPS(), statistics.getMaxSpeed().toMPS(), 0.001); Distance.of(1100),
assertEquals(1200.0, statistics.getMinAltitude(), 0.001); Speed.of(846.1538461538461), //TODO Check why this is not 60
assertEquals(3575.0, statistics.getMaxAltitude(), 0.001); new AltitudeExtremities(1200, 3575),
assertEquals(150.0, statistics.getAverageHeartRate().getBPM(), 0.001); new AltitudeGainLoss(900, 0),
} HeartRate.of(150),
null
@Test )
public void testGetAverageSpeed() { , result
statistics.setTotalDistance(Distance.of(1000.0)); );
statistics.setTotalTime(Duration.ofMillis(50000));
assertEquals(20.0, statistics.getAverageSpeed().toMPS(), 0.001);
}
@Test
public void testGetAverageMovingSpeed() {
statistics.setTotalDistance(Distance.of(1000.0));
statistics.setMovingTime(Duration.ofMillis(20000));
assertEquals(50.0, statistics.getAverageMovingSpeed().toMPS(), 0.001);
} }
} }
@@ -5,6 +5,7 @@ import androidx.annotation.Nullable;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
//TODO Add @NonNull to attributes
public record Statistics( public record Statistics(
Instant startTime, Instant startTime,
Instant stopTime, Instant stopTime,
@@ -14,7 +15,7 @@ public record Statistics(
Duration movingTime, // Based on when we believe the user is traveling Duration movingTime, // Based on when we believe the user is traveling
Distance totalDistance, Distance totalDistance,
//TODO Check if this is persisted; if not: remove
Speed maxSpeed, Speed maxSpeed,
@Nullable @Nullable
@@ -265,20 +265,18 @@ public class SegmentStatisticUpdater {
return movingTime; return movingTime;
} }
@VisibleForTesting
public void setMovingTime(Duration movingTime) { public void setMovingTime(Duration movingTime) {
this.movingTime = movingTime; this.movingTime = movingTime;
} }
public void addMovingTime(TrackPoint trackPoint, TrackPoint lastTrackPoint) { public void addMovingTime(TrackPoint trackPoint, TrackPoint lastTrackPoint) {
addMovingTime(Duration.between(lastTrackPoint.getTime(), trackPoint.getTime())); Duration movingDuration = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
}
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) if (movingDuration.isNegative()) {
public void addMovingTime(Duration time) {
if (time.isNegative()) {
throw new RuntimeException("Moving time cannot be negative"); throw new RuntimeException("Moving time cannot be negative");
} }
movingTime = movingTime.plus(time); movingTime = movingTime.plus(movingDuration);
} }
public boolean isIdle() { public boolean isIdle() {
@@ -289,16 +287,12 @@ public class SegmentStatisticUpdater {
isIdle = idle; isIdle = idle;
} }
@VisibleForTesting
@Nullable @Nullable
public HeartRate getAverageHeartRate() { public HeartRate getAverageHeartRate() {
return avgHeartRate; return avgHeartRate;
} }
@Nullable
public Power getAveragePower() {
return avgPower;
}
/** /**
* Gets the average speed. * Gets the average speed.
* This calculation only takes into account the displacement until the last point that was accounted for in statistics. * This calculation only takes into account the displacement until the last point that was accounted for in statistics.
@@ -322,26 +316,22 @@ public class SegmentStatisticUpdater {
this.maxSpeed = maxSpeed; this.maxSpeed = maxSpeed;
} }
@VisibleForTesting
@Deprecated
public double getMinAltitude() { public double getMinAltitude() {
return altitudeExtremities.getMin(); return altitudeExtremities.getMin();
} }
public void setMinAltitude(double altitude_m) {
altitudeExtremities.setMin(altitude_m);
}
/** /**
* Gets the maximum altitude. * Gets the maximum altitude.
* This is calculated from the smoothed altitude, so this can actually be less than the current altitude. * This is calculated from the smoothed altitude, so this can actually be less than the current altitude.
*/ */
@VisibleForTesting
@Deprecated
public double getMaxAltitude() { public double getMaxAltitude() {
return altitudeExtremities.getMax(); return altitudeExtremities.getMax();
} }
public void setMaxAltitude(double altitude_m) {
altitudeExtremities.setMax(altitude_m);
}
public void updateAltitudeExtremities(Altitude altitude) { public void updateAltitudeExtremities(Altitude altitude) {
if (altitude != null) { if (altitude != null) {
altitudeExtremities.update(altitude.toM()); altitudeExtremities.update(altitude.toM());
@@ -214,11 +214,12 @@ public class TrackStatisticsUpdater {
} }
} }
@NonNull
@Override @Override
public String toString() { public String toString() {
return "TrackStatisticsUpdater{" + return "TrackStatisticsUpdater{" +
"trackStatistics=" + segmentStatisticUpdater + "segmentStatisticUpdater=" + segmentStatisticUpdater +
", currentSegment=" + currentSegment +
", lastTrackPoint=" + lastTrackPoint +
'}'; '}';
} }
} }