// 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; } }