// Copyright 2009 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.stats;
import com.google.android.apps.mytracks.util.CalorieUtils.ActivityType;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import android.location.Location;
import junit.framework.TestCase;
/**
* Tests {@link TripStatisticsUpdater}.
*
* @author Sandor Dornbush
*/
public class TripStatisticsUpdaterTest extends TestCase {
private static final long ONE_SECOND = 1000;
private static final long TEN_SECONDS = 10 * ONE_SECOND;
private static final float MOVING_SPEED = 11.1f;
private static final double DEFAULT_WEIGHT = 65.0;
private TripStatisticsUpdater tripStatisticsUpdater = null;
@Override
protected void setUp() throws Exception {
tripStatisticsUpdater = new TripStatisticsUpdater(System.currentTimeMillis());
}
/**
* Sends some moving and waiting locations and then checks the statistics.
*/
public void testAddLocationSimple() {
long startTime = 1000;
tripStatisticsUpdater = new TripStatisticsUpdater(startTime);
TripStatistics tripStatistics = tripStatisticsUpdater.getTripStatistics();
assertEquals(0.0, tripStatisticsUpdater.getSmoothedElevation());
assertEquals(Double.POSITIVE_INFINITY, tripStatistics.getMinElevation());
assertEquals(Double.NEGATIVE_INFINITY, tripStatistics.getMaxElevation());
assertEquals(0.0, tripStatistics.getMaxSpeed());
assertEquals(Double.POSITIVE_INFINITY, tripStatistics.getMinGrade());
assertEquals(Double.NEGATIVE_INFINITY, tripStatistics.getMaxGrade());
assertEquals(0.0, tripStatistics.getTotalElevationGain());
assertEquals(0, tripStatistics.getMovingTime());
assertEquals(0.0, tripStatistics.getTotalDistance());
// Time:0 ~ 99; Location:0 ~ 99
addMoveLocations(100, startTime, tripStatistics, 0, 0);
// Time:100 ~ 199; Location:99
addWaitLocations(100, startTime, tripStatistics, 100, 99);
// Time:200 ~ 299; Location:100 ~ 199
addMoveLocations(100, startTime, tripStatistics, 200, 100);
// Time:300 ~ 399; Location:199
addWaitLocations(100, startTime, tripStatistics, 300, 199);
// Time:400 ~ 499; Location:200 ~ 299
addMoveLocations(100, startTime, tripStatistics, 400, 200);
// Time:500 ~ 599; Location:299
addWaitLocations(100, startTime, tripStatistics, 500, 299);
// Time:600 ~ 699; Location:300 ~ 399
addMoveLocations(100, startTime, tripStatistics, 600, 300);
}
/**
* Sends some disordered locations and checks the statistics. In some
* situation, especially when signal is not good, MyTracks may receive such
* data.
*/
public void testAddLocation_disorderedLocatiions() {
long startTime = 1000;
tripStatisticsUpdater = new TripStatisticsUpdater(startTime);
TripStatistics tripStatistics = tripStatisticsUpdater.getTripStatistics();
addLocations(5, startTime, tripStatistics, 0, 0);
addLocations(5, startTime, tripStatistics, 5, 0);
addLocations(5, startTime, tripStatistics, 10, -5);
addLocations(5, startTime, tripStatistics, 15, 5);
}
/**
* Tests {@link TripStatisticsUpdater#updateElevation(double)} with constant
* elevations.
*/
public void testElevationSimple() throws Exception {
for (double elevation = 0; elevation < 1000; elevation += 10) {
tripStatisticsUpdater = new TripStatisticsUpdater(System.currentTimeMillis());
for (int i = 0; i < 100; i++) {
tripStatisticsUpdater.updateElevation(elevation);
assertEquals(elevation, tripStatisticsUpdater.getSmoothedElevation());
TripStatistics tripStatistics = tripStatisticsUpdater.getTripStatistics();
assertEquals(elevation, tripStatistics.getMinElevation());
assertEquals(elevation, tripStatistics.getMaxElevation());
assertEquals(elevation, tripStatistics.getTotalElevationGain());
}
}
}
/**
* Tests {@link TripStatisticsUpdater#updateGrade(double, double)} with
* elevation gain.
*/
public void testElevationGain() throws Exception {
for (double i = 0; i < 1000; i++) {
tripStatisticsUpdater.updateElevation(i);
assertEquals(i, tripStatisticsUpdater.getSmoothedElevation(),
TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR / 2);
TripStatistics data = tripStatisticsUpdater.getTripStatistics();
assertEquals(0.0, data.getMinElevation());
assertEquals(i, data.getMaxElevation(), TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR / 2);
assertEquals(
i, data.getTotalElevationGain(), TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR);
}
}
/**
* Tests {@link TripStatisticsUpdater#updateGrade(double, double)} with grade
* of 1 and -1.
*/
public void testGradeSimple() throws Exception {
for (double i = 0; i < 1000; i++) {
tripStatisticsUpdater.updateGrade(100.0, 100.0);
assertEquals(1.0, tripStatisticsUpdater.getTripStatistics().getMaxGrade());
assertEquals(1.0, tripStatisticsUpdater.getTripStatistics().getMinGrade());
}
for (double i = 0; i < 1000; i++) {
tripStatisticsUpdater.updateGrade(100.0, -100.0);
if (i >= TripStatisticsUpdater.GRADE_SMOOTHING_FACTOR) {
assertEquals(1.0, tripStatisticsUpdater.getTripStatistics().getMaxGrade());
// add 0.1 delta since changing min grade from 1 to -1
assertEquals(-1.0, tripStatisticsUpdater.getTripStatistics().getMinGrade(), 0.1);
}
}
}
/**
* Tests {@link TripStatisticsUpdater#updateGrade(double, double)} with
* distance of 1. The grade should get ignored.
*/
public void testGradeIgnoreShort() throws Exception {
for (double i = 0; i < 100; i++) {
/*
* The value of the elevation does not matter. This is just to fill the
* elevation buffer.
*/
tripStatisticsUpdater.updateElevation(i);
tripStatisticsUpdater.updateGrade(1.0, 100.0);
assertEquals(
Double.NEGATIVE_INFINITY, tripStatisticsUpdater.getTripStatistics().getMaxGrade());
assertEquals(
Double.POSITIVE_INFINITY, tripStatisticsUpdater.getTripStatistics().getMinGrade());
}
}
/**
* Tests {@link TripStatisticsUpdater#updateSpeed(long, double, long, double)}
* with speed of zero.
*/
public void testUpdateSpeedIncludeZero() {
for (int i = 0; i < 1000; i++) {
tripStatisticsUpdater.updateSpeed(i + ONE_SECOND, 0.0, i, 4.0);
assertEquals(0.0, tripStatisticsUpdater.getTripStatistics().getMaxSpeed());
}
}
/**
* Tests {@link TripStatisticsUpdater#updateSpeed(long, double, long, double)}
* with the error code 128. The speed should get ignored.
*/
public void testUpdateSpeedIngoreErrorCode() {
long time = 12344000;
tripStatisticsUpdater.updateSpeed(time + ONE_SECOND, 128.0, time, 0.0);
assertEquals(0.0, tripStatisticsUpdater.getTripStatistics().getMaxSpeed());
}
/**
* Tests {@link TripStatisticsUpdater#updateSpeed(long, double, long, double)}
* with a large speed change. The speed should get ignored.
*/
public void testUpdateSpeedIngoreLargeAcceleration() {
long time = 12344000;
tripStatisticsUpdater.updateSpeed(time + ONE_SECOND, 100.0, time, 1.0);
assertEquals(0.0, tripStatisticsUpdater.getTripStatistics().getMaxSpeed());
}
/**
* Tests {@link TripStatisticsUpdater#updateSpeed(long, double, long, double)}
* with constant speed.
*/
public void testUpdateSpeed() {
double speed = 4.0;
for (int i = 0; i < 1000; i++) {
tripStatisticsUpdater.updateSpeed(i + ONE_SECOND, speed, i, speed);
assertEquals(speed, tripStatisticsUpdater.getTripStatistics().getMaxSpeed());
}
}
/**
* Sends some locations which keeping moving and checks the statistics.
*
* @param points number of locations
* @param startTime start time of this track
* @param tripStatistics the TripStatistics object
* @param timeOffset offset to start time
* @param locationOffset location offset to start
*/
private void addMoveLocations(int points, long startTime, TripStatistics tripStatistics,
int timeOffset, int locationOffset) {
for (int i = 0; i < points; i++) {
// Going up by 1 meter each time.
// Moving by .001 degree latitude (111 meters).
// Each time slice is 10 seconds.
Location location = getLocation(i + locationOffset, (i + locationOffset) * .001, MOVING_SPEED,
startTime + (timeOffset + i) * TEN_SECONDS);
tripStatisticsUpdater.addLocation(location,
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, true, ActivityType.WALKING,
DEFAULT_WEIGHT);
tripStatistics = tripStatisticsUpdater.getTripStatistics();
assertEquals((timeOffset + i) * TEN_SECONDS, tripStatistics.getTotalTime());
assertEquals((locationOffset + i) * TEN_SECONDS, tripStatistics.getMovingTime());
assertEquals(i + locationOffset, tripStatisticsUpdater.getSmoothedElevation(),
TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR / 2);
if (i + locationOffset >= TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR) {
assertEquals(0.0, tripStatistics.getMinElevation());
assertEquals(i + locationOffset, tripStatistics.getMaxElevation(),
TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR / 2);
assertEquals(i + locationOffset, tripStatistics.getTotalElevationGain(),
TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR);
}
if (i + locationOffset >= TripStatisticsUpdater.SPEED_SMOOTHING_FACTOR) {
assertEquals(MOVING_SPEED, tripStatistics.getMaxSpeed(), 0.1);
}
// If there are only moving locations in the track.
if (locationOffset == 0 && (i + locationOffset) >= TripStatisticsUpdater.RUN_SMOOTHING_FACTOR
+ TripStatisticsUpdater.GRADE_SMOOTHING_FACTOR) {
// 0.5 m / 111 m = .0045
assertEquals(0.0045, tripStatistics.getMinGrade(), 0.0001);
// 1 m / 111 m = .009
assertEquals(0.009, tripStatistics.getMaxGrade(), 0.0001);
}
assertEquals((i + locationOffset) * 111.0, tripStatistics.getTotalDistance(),
(i + locationOffset) * 111.0 * 0.01);
}
}
/**
* Sends some locations which are not moving and checks the statistics.
*
* @param points number of locations
* @param startTime start time of this track
* @param tripStatistics the TripStatistics object
* @param timeOffset offset to start time
* @param locationOffset location offset to start
*/
private void addWaitLocations(int points, long startTime, TripStatistics tripStatistics,
int timeOffset, int locationOffset) {
for (int i = 0; i < points; i++) {
Location location = getLocation(
locationOffset, locationOffset * .001, 0, startTime + (i + timeOffset) * TEN_SECONDS);
tripStatisticsUpdater.addLocation(location,
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, false, ActivityType.WALKING,
DEFAULT_WEIGHT);
tripStatistics = tripStatisticsUpdater.getTripStatistics();
assertEquals((i + timeOffset) * TEN_SECONDS, tripStatistics.getTotalTime());
assertEquals((locationOffset) * TEN_SECONDS, tripStatistics.getMovingTime());
assertEquals(locationOffset, tripStatisticsUpdater.getSmoothedElevation(),
TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR / 2);
assertEquals(0.0, tripStatistics.getMinElevation());
assertEquals(locationOffset, tripStatistics.getMaxElevation(),
TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR / 2);
assertEquals(locationOffset, tripStatistics.getTotalElevationGain(),
TripStatisticsUpdater.ELEVATION_SMOOTHING_FACTOR);
assertEquals(MOVING_SPEED, tripStatistics.getMaxSpeed(), 0.1);
assertEquals(MOVING_SPEED, tripStatistics.getMaxSpeed(), 0.1);
assertEquals(
locationOffset * 111.0, tripStatistics.getTotalDistance(), locationOffset * 111.0 * 0.01);
}
}
/**
* Sends some locations which are moving and checks some simple policy.
*
* @param points number of locations
* @param startTime start time of this track
* @param tripStatistics the TripStatistics object
* @param timeOffset offset to start time
* @param locationOffset location offset to start
*/
private void addLocations(int points, long startTime, TripStatistics tripStatistics,
int timeOffset, int locationOffset) {
for (int i = 0; i < points; i++) {
// 99999 means a speed should bigger than given speed.
Location location = getLocation(i + locationOffset, (i + locationOffset) * .001, 99999,
startTime + (timeOffset + i) * TEN_SECONDS);
tripStatisticsUpdater.addLocation(location,
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, true, ActivityType.WALKING,
DEFAULT_WEIGHT);
tripStatistics = tripStatisticsUpdater.getTripStatistics();
assertTrue(tripStatistics.getMovingTime() <= tripStatistics.getTotalTime());
assertTrue(tripStatistics.getAverageSpeed() <= tripStatistics.getAverageMovingSpeed());
assertTrue(tripStatistics.getAverageMovingSpeed() <= tripStatistics.getMaxSpeed());
assertTrue(tripStatistics.getStopTime() >= tripStatistics.getStartTime());
}
}
/**
* Creates a location and returns it.
*
* @param altitude altitude of location
* @param latitude latitude of location
* @param speed speed of location
* @param time time of location
*/
private Location getLocation(double altitude, double latitude, float speed, long time) {
Location location = new Location("test");
location.setAccuracy(1.0f);
location.setLongitude(45.0);
location.setAltitude(altitude);
location.setLatitude(latitude);
location.setSpeed(speed);
location.setTime(time);
return location;
}
}