ExportImportTest: made tests deterministic (simulate GPS/sensors).

Fixes #761.
This commit is contained in:
Dennis Guse
2021-07-16 08:00:42 +02:00
parent 86a0054c24
commit 997ddbb4f1
8 changed files with 100 additions and 34 deletions
@@ -104,8 +104,8 @@ public class SensorDataCyclingTest {
current.compute(previous, Distance.ofMM(2150)); current.compute(previous, Distance.ofMM(2150));
// then // then
assertEquals(2.15, current.getValue().distance.toM(), 0.01); assertEquals(2.15, current.getValue().getDistance().toM(), 0.01);
assertEquals(1.20, current.getValue().speed.toMPS(), 0.01); assertEquals(1.20, current.getValue().getSpeed().toMPS(), 0.01);
} }
@Test @Test
@@ -118,8 +118,8 @@ public class SensorDataCyclingTest {
current.compute(previous, Distance.ofMM(2000)); current.compute(previous, Distance.ofMM(2000));
// then // then
assertEquals(2, current.getValue().distance.toM(), 0.01); assertEquals(2, current.getValue().getDistance().toM(), 0.01);
assertEquals(2, current.getValue().speed.toMPS(), 0.01); assertEquals(2, current.getValue().getSpeed().toMPS(), 0.01);
} }
@Test @Test
@@ -3,9 +3,11 @@ package de.dennisguse.opentracks.io.file.importer;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.location.Location;
import android.net.Uri; import android.net.Uri;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -19,6 +21,7 @@ import org.junit.BeforeClass;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -34,7 +37,6 @@ import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Altitude;
import de.dennisguse.opentracks.content.data.Distance; import de.dennisguse.opentracks.content.data.Distance;
import de.dennisguse.opentracks.content.data.Marker; import de.dennisguse.opentracks.content.data.Marker;
import de.dennisguse.opentracks.content.data.Speed; import de.dennisguse.opentracks.content.data.Speed;
@@ -42,10 +44,16 @@ import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.Track; import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPoint; import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils; import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.sensor.SensorDataCycling;
import de.dennisguse.opentracks.content.sensor.SensorDataCyclingPower;
import de.dennisguse.opentracks.content.sensor.SensorDataHeartRate;
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.exporter.TrackExporter; import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
import de.dennisguse.opentracks.services.TrackRecordingService; import de.dennisguse.opentracks.services.TrackRecordingService;
import de.dennisguse.opentracks.services.handlers.HandlerServer; import de.dennisguse.opentracks.services.handlers.HandlerServer;
import de.dennisguse.opentracks.services.sensors.AltitudeSumManager;
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
import de.dennisguse.opentracks.stats.TrackStatistics; import de.dennisguse.opentracks.stats.TrackStatistics;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -81,7 +89,6 @@ public class ExportImportTest {
private File tmpFile; private File tmpFile;
private Uri tmpFileUri; private Uri tmpFileUri;
private Track track;
private List<Marker> markers = new ArrayList<>(); private List<Marker> markers = new ArrayList<>();
private List<TrackPoint> trackPoints = new ArrayList<>(); private List<TrackPoint> trackPoints = new ArrayList<>();
@@ -119,26 +126,30 @@ public class ExportImportTest {
Distance sensorDistance = hasSensorDistance ? Distance.of(5) : null; Distance sensorDistance = hasSensorDistance ? Distance.of(5) : null;
service.newTrackPoint(createTrackPoint(Instant.parse("2020-02-02T02:02:03Z"), 3, 14, 10, 15, 10, 1, 66, 3, 50, sensorDistance), Distance.of(0)); sendLocation(handlerServer, Instant.parse("2020-02-02T02:02:03Z"), 3, 14, 10, 15, 10, 1, 66, 3, 50, sensorDistance);
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null); service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
service.newTrackPoint(createTrackPoint(Instant.parse("2020-02-02T02:02:04Z"), 3, 14.001, 10, 15, 10, 0, 66, 3, 50, sensorDistance), Distance.of(0)); sendLocation(handlerServer, Instant.parse("2020-02-02T02:02:04Z"), 3, 14.001, 10, 15, 10, 0, 66, 3, 50, sensorDistance);
service.newTrackPoint(createTrackPoint(Instant.parse("2020-02-02T02:02:05Z"), 3, 14.002, 10, 15, 10, 0, 66, 3, 50, sensorDistance), Distance.of(0)); sendLocation(handlerServer, Instant.parse("2020-02-02T02:02:05Z"), 3, 14.002, 10, 15, 10, 0, 66, 3, 50, sensorDistance);
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null); service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
handlerServer.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:06Z"), ZoneId.of("CET"))); handlerServer.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:06Z"), ZoneId.of("CET")));
handlerServer.setRemoteSensorManager(new BluetoothRemoteSensorManager(context));
service.pauseCurrentTrack(); service.pauseCurrentTrack();
handlerServer.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:20Z"), ZoneId.of("CET"))); handlerServer.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:20Z"), ZoneId.of("CET")));
service.resumeCurrentTrack(); service.resumeCurrentTrack();
service.newTrackPoint(createTrackPoint(Instant.parse("2020-02-02T02:02:21Z"), 3, 14.003, 10, 15, 10, 0, 66, 3, 50, sensorDistance), Distance.of(0)); handlerServer.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:21Z"), ZoneId.of("CET")));
service.newTrackPoint(createTrackPoint(Instant.parse("2020-02-02T02:02:22Z"), 3, 16, 10, 15, 10, 0, 66, 3, 50, sensorDistance), Distance.of(0)); sendLocation(handlerServer, Instant.parse("2020-02-02T02:02:21Z"), 3, 14.003, 10, 15, 10, 0, 66, 3, 50, sensorDistance);
service.newTrackPoint(createTrackPoint(Instant.parse("2020-02-02T02:02:23Z"), 3, 16.001, 10, 15, 10, 0, 66, 3, 50, sensorDistance), Distance.of(0));
sendLocation(handlerServer, Instant.parse("2020-02-02T02:02:22Z"), 3, 16, 10, 15, 10, 0, 66, 3, 50, sensorDistance);
sendLocation(handlerServer, Instant.parse("2020-02-02T02:02:23Z"), 3, 16.001, 10, 15, 10, 0, 66, 3, 50, sensorDistance);
handlerServer.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:24Z"), ZoneId.of("CET"))); handlerServer.setClock(Clock.fixed(Instant.parse("2020-02-02T02:02:24Z"), ZoneId.of("CET")));
handlerServer.setRemoteSensorManager(new BluetoothRemoteSensorManager(context));
service.endCurrentTrack(); service.endCurrentTrack();
track = contentProviderUtils.getTrack(trackId); Track track = contentProviderUtils.getTrack(trackId);
track.setIcon(TRACK_ICON); track.setIcon(TRACK_ICON);
track.setCategory(TRACK_CATEGORY); track.setCategory(TRACK_CATEGORY);
track.setDescription(TRACK_DESCRIPTION); track.setDescription(TRACK_DESCRIPTION);
@@ -369,16 +380,49 @@ public class ExportImportTest {
} }
} }
private static TrackPoint createTrackPoint(Instant time, double latitude, double longitude, float accuracy, float speed, float altitude, float altitudeGain, float heartRate, float cyclingCadence, float power, Distance distance) { private void sendLocation(HandlerServer handlerServer, Instant time, double latitude, double longitude, float accuracy, float speed, float altitude, float altitudeGain, float heartRate, float cyclingCadence, float power, Distance distance) {
TrackPoint tp = new TrackPoint(latitude, longitude, Altitude.WGS84.of(altitude), time); Location location = new Location("mock");
tp.setHorizontalAccuracy(Distance.of(accuracy)); location.setLatitude(latitude);
tp.setSpeed(Speed.of(speed)); location.setLongitude(longitude);
tp.setHeartRate_bpm(heartRate); location.setAccuracy(accuracy);
tp.setCyclingCadence_rpm(cyclingCadence); location.setSpeed(speed);
tp.setPower(power); location.setAltitude(altitude);
tp.setAltitudeGain(altitudeGain);
tp.setAltitudeLoss(altitudeGain); //TODO handlerServer.setAltitudeSumManager(new AltitudeSumManager() {
tp.setSensorDistance(distance); @Override
return tp; public void fill(@NonNull TrackPoint trackPoint) {
trackPoint.setAltitudeGain(altitudeGain);
trackPoint.setAltitudeLoss(altitudeGain);
}
});
handlerServer.setRemoteSensorManager(new BluetoothRemoteSensorManager(context) {
@Override
public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
SensorDataSet sensorDataSet = new SensorDataSet();
sensorDataSet.set(new SensorDataCyclingPower("power", "power", power));
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", heartRate));
SensorDataCycling.Cadence cadence = Mockito.mock(SensorDataCycling.Cadence.class);
Mockito.when(cadence.hasValue()).thenReturn(true);
Mockito.when(cadence.getValue()).thenReturn(cyclingCadence);
sensorDataSet.set(cadence);
SensorDataCycling.DistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCycling.DistanceSpeed.Data.class);
Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance);
Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed));
SensorDataCycling.DistanceSpeed distanceSpeed = Mockito.mock(SensorDataCycling.DistanceSpeed.class);
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData);
sensorDataSet.set(distanceSpeed);
sensorDataSet.fillTrackPoint(trackPoint);
return sensorDataSet;
}
});
handlerServer.setClock(Clock.fixed(time, ZoneId.of("CET")));
handlerServer.getLocationHandler().onLocationChanged(location);
} }
} }
@@ -203,6 +203,9 @@ public class TrackPoint {
if (hasAltitude()) { if (hasAltitude()) {
location.setAltitude(altitude.toM()); location.setAltitude(altitude.toM());
} }
if (hasSpeed()) {
location.setSpeed((float) speed.toMPS());
}
return location; return location;
} }
@@ -169,9 +169,9 @@ public final class SensorDataCycling {
} }
public static class Data { public static class Data {
public final Distance distance; private final Distance distance;
public final Distance distanceOverall; private final Distance distanceOverall;
public final Speed speed; private final Speed speed;
private Data(Distance distance, Distance distanceOverall, Speed speed) { private Data(Distance distance, Distance distanceOverall, Speed speed) {
this.distance = distance; this.distance = distance;
@@ -179,12 +179,24 @@ public final class SensorDataCycling {
this.speed = speed; this.speed = speed;
} }
public Distance getDistance() {
return distance;
}
public Distance getDistanceOverall() {
return distanceOverall;
}
public Speed getSpeed() {
return speed;
}
@Override @Override
public String toString() { public String toString() {
return "Data{" + return "Data{" +
"distance_m=" + distance + "distance=" + getDistance() +
", distance_overall_m=" + distanceOverall + ", distance_overall=" + getDistanceOverall() +
", speed_mps=" + speed + ", speed=" + getSpeed() +
'}'; '}';
} }
} }
@@ -65,8 +65,8 @@ public final class SensorDataSet {
} }
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue()) { if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue()) {
trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue().distanceOverall); trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue().getDistanceOverall());
trackPoint.setSpeed(cyclingDistanceSpeed.getValue().speed); trackPoint.setSpeed(cyclingDistanceSpeed.getValue().getSpeed());
} }
if (cyclingPower != null && cyclingPower.hasValue()) { if (cyclingPower != null && cyclingPower.hasValue()) {
@@ -258,7 +258,7 @@ public class StatisticsRecordingFragment extends Fragment {
if (sensorDataSet != null && sensorDataSet.getCyclingDistanceSpeed() != null) { if (sensorDataSet != null && sensorDataSet.getCyclingDistanceSpeed() != null) {
SensorDataCycling.DistanceSpeed data = sensorDataSet.getCyclingDistanceSpeed(); SensorDataCycling.DistanceSpeed data = sensorDataSet.getCyclingDistanceSpeed();
if (data.hasValue() && data.isRecent()) { if (data.hasValue() && data.isRecent()) {
speed = data.getValue().speed; speed = data.getValue().getSpeed();
} }
} }
@@ -177,6 +177,11 @@ public class HandlerServer {
this.clock = clock; this.clock = clock;
} }
@VisibleForTesting
public LocationHandler getLocationHandler() {
return locationHandler;
}
void sendGpsStatus(GpsStatusValue gpsStatusValue) { void sendGpsStatus(GpsStatusValue gpsStatusValue) {
service.newGpsStatus(gpsStatusValue); service.newGpsStatus(gpsStatusValue);
} }
@@ -9,6 +9,7 @@ import android.os.Bundle;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import java.time.Duration; import java.time.Duration;
@@ -18,7 +19,8 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
import de.dennisguse.opentracks.util.LocationUtils; import de.dennisguse.opentracks.util.LocationUtils;
import de.dennisguse.opentracks.util.PreferencesUtils; import de.dennisguse.opentracks.util.PreferencesUtils;
class LocationHandler implements LocationListener, GpsStatus.GpsStatusListener { @VisibleForTesting(otherwise = 3)
public class LocationHandler implements LocationListener, GpsStatus.GpsStatusListener {
private final String TAG = LocationHandler.class.getSimpleName(); private final String TAG = LocationHandler.class.getSimpleName();