Fix Stats view.

- when paused, do not update current location
- handle possible null pointer for getActivity

In MyTraksProviderUtils, change to
getLastValidLocation
This commit is contained in:
Jimmy Shih
2012-08-23 18:24:08 -07:00
commit 0ddd11aef5
695 changed files with 65216 additions and 0 deletions
@@ -0,0 +1,214 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R;
import android.test.AndroidTestCase;
import android.util.Pair;
/**
* Tests for {@link DescriptionGeneratorImpl}.
*
* @author Jimmy Shih
*/
public class DescriptionGeneratorImplTest extends AndroidTestCase {
private static final long START_TIME = 1288721514000L;
private DescriptionGeneratorImpl descriptionGenerator;
@Override
protected void setUp() throws Exception {
descriptionGenerator = new DescriptionGeneratorImpl(getContext());
}
/**
* Tests {@link DescriptionGeneratorImpl#generateTrackDescription(Track,
* java.util.Vector, java.util.Vector, boolean)}.
*/
public void testGenerateTrackDescription() {
Track track = new Track();
TripStatistics stats = new TripStatistics();
stats.setTotalDistance(20000);
stats.setTotalTime(600000);
stats.setMovingTime(300000);
stats.setMaxSpeed(100);
stats.setMaxElevation(550);
stats.setMinElevation(-500);
stats.setTotalElevationGain(6000);
stats.setMaxGrade(0.42);
stats.setMinGrade(0.11);
stats.setStartTime(START_TIME);
track.setTripStatistics(stats);
track.setCategory("hiking");
String expected = "Created by"
+ " <a href='http://www.google.com/mobile/mytracks'>My Tracks</a> on Android.<p>"
+ "Name: -<br>"
+ "Activity type: hiking<br>"
+ "Description: -<br>"
+ "Total distance: 20.00 km (12.4 mi)<br>"
+ "Total time: 10:00<br>"
+ "Moving time: 05:00<br>"
+ "Average speed: 120.00 km/h (74.6 mi/h)<br>"
+ "Average moving speed: 240.00 km/h (149.1 mi/h)<br>"
+ "Max speed: 360.00 km/h (223.7 mi/h)<br>"
+ "Average pace: 0.50 min/km (0.8 min/mi)<br>"
+ "Average moving pace: 0.25 min/km (0.4 min/mi)<br>"
+ "Fastest pace: 0.17 min/km (0.3 min/mi)<br>"
+ "Max elevation: 550 m (1804 ft)<br>"
+ "Min elevation: -500 m (-1640 ft)<br>"
+ "Elevation gain: 6000 m (19685 ft)<br>"
+ "Max grade: 42 %<br>"
+ "Min grade: 11 %<br>"
+ "Recorded: " + StringUtils.formatDateTime(getContext(), START_TIME) + "<br>";
assertEquals(expected, descriptionGenerator.generateTrackDescription(track, null, null, true));
}
/**
* Tests {@link DescriptionGeneratorImpl#generateWaypointDescription(Waypoint)}.
*/
public void testGenerateWaypointDescription() {
Waypoint waypoint = new Waypoint();
TripStatistics stats = new TripStatistics();
stats.setTotalDistance(20000);
stats.setTotalTime(600000);
stats.setMovingTime(300000);
stats.setMaxSpeed(100);
stats.setMaxElevation(550);
stats.setMinElevation(-500);
stats.setTotalElevationGain(6000);
stats.setMaxGrade(0.42);
stats.setMinGrade(0.11);
stats.setStartTime(START_TIME);
waypoint.setTripStatistics(stats);
String expected = "Total distance: 20.00 km (12.4 mi)\n"
+ "Total time: 10:00\n"
+ "Moving time: 05:00\n"
+ "Average speed: 120.00 km/h (74.6 mi/h)\n"
+ "Average moving speed: 240.00 km/h (149.1 mi/h)\n"
+ "Max speed: 360.00 km/h (223.7 mi/h)\n"
+ "Average pace: 0.50 min/km (0.8 min/mi)\n"
+ "Average moving pace: 0.25 min/km (0.4 min/mi)\n"
+ "Fastest pace: 0.17 min/km (0.3 min/mi)\n"
+ "Max elevation: 550 m (1804 ft)\n"
+ "Min elevation: -500 m (-1640 ft)\n"
+ "Elevation gain: 6000 m (19685 ft)\n"
+ "Max grade: 42 %\n"
+ "Min grade: 11 %\n"
+ "Recorded: " + StringUtils.formatDateTime(getContext(), START_TIME) + "\n";
assertEquals(
expected, descriptionGenerator.generateWaypointDescription(waypoint.getTripStatistics()));
}
/**
* Tests {@link DescriptionGeneratorImpl#writeDistance(double, StringBuilder,
* int, String)}.
*/
public void testWriteDistance() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeDistance(1100, builder, R.string.description_total_distance, "<br>");
assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#writeTime(long, StringBuilder, int,
* String)}.
*/
public void testWriteTime() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeTime(1000, builder, R.string.description_total_time, "<br>");
assertEquals("Total time: 00:01<br>", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#writeSpeed(double, StringBuilder,
* int, String)}.
*/
public void testWriteSpeed() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeSpeed(1.1, builder, R.string.description_average_speed, "\n");
assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#writeElevation(double, StringBuilder,
* int, String)}.
*/
public void testWriteElevation() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeElevation(4.2, builder, R.string.description_min_elevation, "<br>");
assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#writePace(Pair, StringBuilder, int,
* String)}.
*/
public void testWritePace() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writePace(
new Pair<Double, Double>(1.1, 2.2), builder, R.string.description_average_pace, "\n");
assertEquals("Average pace: 54.55 min/km (27.3 min/mi)\n", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
* int, String)}.
*/
public void testWriteGrade() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeGrade(.042, builder, R.string.description_max_grade, "<br>");
assertEquals("Max grade: 4 %<br>", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
* int, String)} with a NaN.
*/
public void testWriteGrade_nan() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeGrade(Double.NaN, builder, R.string.description_max_grade, "<br>");
assertEquals("Max grade: 0 %<br>", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#writeGrade(double, StringBuilder,
* int, String)} with an infinite number.
*/
public void testWriteGrade_infinite() {
StringBuilder builder = new StringBuilder();
descriptionGenerator.writeGrade(
Double.POSITIVE_INFINITY, builder, R.string.description_max_grade, "<br>");
assertEquals("Max grade: 0 %<br>", builder.toString());
}
/**
* Tests {@link DescriptionGeneratorImpl#getPace(double)}.
*/
public void testGetPace() {
assertEquals(12.0, descriptionGenerator.getPace(5));
}
/**
* Tests {@link DescriptionGeneratorImpl#getPace(double)} with zero speed.
*/
public void testGetPace_zero() {
assertEquals(0.0, descriptionGenerator.getPace(0));
}
}
@@ -0,0 +1,196 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.content.MyTracksProvider.DatabaseHelper;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.test.AndroidTestCase;
/**
* A unit test for {@link MyTracksProvider}.
*
* @author Youtao Liu
*/
public class MyTracksProviderTest extends AndroidTestCase {
private SQLiteDatabase db;
private MyTracksProvider myTracksProvider;
private String DATABASE_NAME = "mytrackstest.db";
@Override
protected void setUp() throws Exception {
getContext().deleteDatabase(DATABASE_NAME);
db = (new DatabaseHelper(getContext(), DATABASE_NAME)).getWritableDatabase();
myTracksProvider = new MyTracksProvider();
super.setUp();
}
/**
* Tests the method {@link MyTracksProvider.DatabaseHelper#onCreate()}.
*/
public void testDatabaseHelper_OnCreate() {
assertTrue(checkTable(TrackPointsColumns.TABLE_NAME));
assertTrue(checkTable(TracksColumns.TABLE_NAME));
assertTrue(checkTable(WaypointsColumns.TABLE_NAME));
}
/**
* Tests the method
* {@link MyTracksProvider.DatabaseHelper#onUpgrade(SQLiteDatabase, int, int)}
* when version is less than 17.
*/
public void testDatabaseHelper_onUpgrade_Version16() {
DatabaseHelper databaseHelper = new DatabaseHelper(getContext());
dropTable(TrackPointsColumns.TABLE_NAME);
dropTable(TracksColumns.TABLE_NAME);
dropTable(WaypointsColumns.TABLE_NAME);
databaseHelper.onUpgrade(db, 16, 20);
assertTrue(checkTable(TrackPointsColumns.TABLE_NAME));
assertTrue(checkTable(TracksColumns.TABLE_NAME));
assertTrue(checkTable(WaypointsColumns.TABLE_NAME));
}
/**
* Tests the method
* {@link MyTracksProvider.DatabaseHelper#onUpgrade(SQLiteDatabase, int, int)}
* when version is 17.
*/
public void testDatabaseHelper_onUpgrade_Version17() {
DatabaseHelper databaseHelper = new DatabaseHelper(getContext());
// Make two table is only contains one normal integer column.
dropTable(TrackPointsColumns.TABLE_NAME);
dropTable(TracksColumns.TABLE_NAME);
createEmptyTable(TrackPointsColumns.TABLE_NAME);
createEmptyTable(TracksColumns.TABLE_NAME);
databaseHelper.onUpgrade(db, 17, 20);
assertTrue(isColumnExisted(TrackPointsColumns.TABLE_NAME, TrackPointsColumns.SENSOR));
assertTrue(isColumnExisted(TracksColumns.TABLE_NAME, TracksColumns.TABLEID));
assertTrue(isColumnExisted(TracksColumns.TABLE_NAME, TracksColumns.ICON));
}
/**
* Tests the method
* {@link MyTracksProvider.DatabaseHelper#onUpgrade(SQLiteDatabase, int, int)}
* when version is 18.
*/
public void testDatabaseHelper_onUpgrade_Version18() {
DatabaseHelper databaseHelper = new DatabaseHelper(getContext());
// Make two table is only contains one normal integer column.
dropTable(TrackPointsColumns.TABLE_NAME);
dropTable(TracksColumns.TABLE_NAME);
createEmptyTable(TrackPointsColumns.TABLE_NAME);
createEmptyTable(TracksColumns.TABLE_NAME);
databaseHelper.onUpgrade(db, 18, 20);
assertFalse(isColumnExisted(TrackPointsColumns.TABLE_NAME, TrackPointsColumns.SENSOR));
assertTrue(isColumnExisted(TracksColumns.TABLE_NAME, TracksColumns.TABLEID));
assertTrue(isColumnExisted(TracksColumns.TABLE_NAME, TracksColumns.ICON));
}
/**
* Tests the method
* {@link MyTracksProvider.DatabaseHelper#onUpgrade(SQLiteDatabase, int, int)}
* when version is 19.
*/
public void testDatabaseHelper_onUpgrade_Version19() {
DatabaseHelper databaseHelper = new DatabaseHelper(getContext());
// Make two table is only contains one normal integer column.
dropTable(TrackPointsColumns.TABLE_NAME);
dropTable(TracksColumns.TABLE_NAME);
createEmptyTable(TrackPointsColumns.TABLE_NAME);
createEmptyTable(TracksColumns.TABLE_NAME);
databaseHelper.onUpgrade(db, 19, 20);
assertFalse(isColumnExisted(TrackPointsColumns.TABLE_NAME, TrackPointsColumns.SENSOR));
assertFalse(isColumnExisted(TracksColumns.TABLE_NAME, TracksColumns.TABLEID));
assertTrue(isColumnExisted(TracksColumns.TABLE_NAME, TracksColumns.ICON));
}
/**
* Tests the method {@link MyTracksProvider#onCreate()}.
*/
public void testOnCreate() {
assertTrue(myTracksProvider.onCreate(getContext()));
}
/**
* Tests the method {@link MyTracksProvider#getType(Uri)}.
*/
public void testGetType() {
assertEquals(TrackPointsColumns.CONTENT_TYPE,
myTracksProvider.getType(TrackPointsColumns.CONTENT_URI));
assertEquals(TracksColumns.CONTENT_TYPE, myTracksProvider.getType(TracksColumns.CONTENT_URI));
assertEquals(WaypointsColumns.CONTENT_TYPE,
myTracksProvider.getType(WaypointsColumns.CONTENT_URI));
}
/**
* Creates an table only contains one column.
*
* @param table the name of table
*/
private void createEmptyTable(String table) {
db.execSQL("CREATE TABLE " + table + " (test INTEGER)");
}
/**
* Drops a table in database.
*
* @param table
*/
private void dropTable(String table) {
db.execSQL("Drop TABLE " + table);
}
/**
* Checks whether a table is existed.
*
* @param table the name of table
* @return true means the table has existed
*/
private boolean checkTable(String table) {
try {
db.rawQuery("select count(*) from " + table, null);
return true;
} catch (Exception e) {
return false;
}
}
/**
* Checks whether a column in a table is existed by whether can order by the
* column.
*
* @param table the name of table
* @param column the name of column
* @return true means the column has existed
*/
private boolean isColumnExisted(String table, String column) {
try {
db.execSQL("SElECT count(*) from " + table + " order by " + column);
} catch (Exception e) {
if (e.getMessage().indexOf("no such column") > -1) {
return false;
}
}
return true;
}
}
@@ -0,0 +1,326 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.content.SearchEngine.ScoredResult;
import com.google.android.apps.mytracks.content.SearchEngine.SearchQuery;
import com.google.android.apps.mytracks.services.TrackRecordingServiceTest.MockContext;
import com.google.android.apps.mytracks.stats.TripStatistics;
import android.content.ContentUris;
import android.location.Location;
import android.net.Uri;
import android.test.AndroidTestCase;
import android.test.RenamingDelegatingContext;
import android.test.mock.MockContentResolver;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Tests for {@link SearchEngine}.
* These are not meant to be quality tests, but instead feature-by-feature tests
* (in other words, they don't test the mixing of different score boostings, just
* each boosting separately)
*
* @author Rodrigo Damazio
*/
public class SearchEngineTest extends AndroidTestCase {
private static final Location HERE = new Location("gps");
private static final long NOW = 1234567890000L; // After OLDEST_ALLOWED_TIMESTAMP
private MyTracksProviderUtils providerUtils;
private SearchEngine engine;
@Override
protected void setUp() throws Exception {
super.setUp();
MockContentResolver mockContentResolver = new MockContentResolver();
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
getContext(), getContext(), "test.");
MockContext context = new MockContext(mockContentResolver, targetContext);
MyTracksProvider provider = new MyTracksProvider();
provider.attachInfo(context, null);
mockContentResolver.addProvider(MyTracksProviderUtils.AUTHORITY, provider);
setContext(context);
providerUtils = MyTracksProviderUtils.Factory.get(context);
engine = new SearchEngine(providerUtils);
}
@Override
protected void tearDown() throws Exception {
providerUtils.deleteAllTracks();
super.tearDown();
}
private long insertTrack(String title, String description, String category, double distance, long hoursAgo) {
Track track = new Track();
track.setName(title);
track.setDescription(description);
track.setCategory(category);
TripStatistics stats = track.getTripStatistics();
if (hoursAgo > 0) {
// Started twice hoursAgo, so the average time is hoursAgo.
stats.setStartTime(NOW - hoursAgo * 1000L * 60L * 60L * 2);
stats.setStopTime(NOW);
}
int latitude = (int) ((HERE.getLatitude() + distance) * 1E6);
int longitude = (int) ((HERE.getLongitude() + distance) * 1E6);
stats.setBounds(latitude, longitude, latitude, longitude);
Uri uri = providerUtils.insertTrack(track);
return ContentUris.parseId(uri);
}
private long insertTrack(String title, String description, String category) {
return insertTrack(title, description, category, 0, -1);
}
private long insertTrack(String title, double distance) {
return insertTrack(title, "", "", distance, -1);
}
private long insertTrack(String title, long hoursAgo) {
return insertTrack(title, "", "", 0.0, hoursAgo);
}
private long insertWaypoint(String title, String description, String category, double distance, long hoursAgo, long trackId) {
Waypoint waypoint = new Waypoint();
waypoint.setName(title);
waypoint.setDescription(description);
waypoint.setCategory(category);
waypoint.setTrackId(trackId);
Location location = new Location(HERE);
location.setLatitude(location.getLatitude() + distance);
location.setLongitude(location.getLongitude() + distance);
if (hoursAgo >= 0) {
location.setTime(NOW - hoursAgo * 1000L * 60L * 60L);
}
waypoint.setLocation(location);
Uri uri = providerUtils.insertWaypoint(waypoint);
return ContentUris.parseId(uri);
}
private long insertWaypoint(String title, String description, String category) {
return insertWaypoint(title, description, category, 0.0, -1, -1);
}
private long insertWaypoint(String title, double distance) {
return insertWaypoint(title, "", "", distance, -1, -1);
}
private long insertWaypoint(String title, long hoursAgo) {
return insertWaypoint(title, "", "", 0.0, hoursAgo, -1);
}
private long insertWaypoint(String title, long hoursAgo, long trackId) {
return insertWaypoint(title, "", "", 0.0, hoursAgo, trackId);
}
public void testSearchText() {
// Insert 7 tracks (purposefully out of result order):
// - one which won't match
// - one which will match the description
// - one which will match the category
// - one which will match the title
// - one which will match in title and category
// - one which will match in title and description
// - one which will match in all fields
insertTrack("bb", "cc", "dd");
long descriptionMatchId = insertTrack("bb", "aa", "cc");
long categoryMatchId = insertTrack("bb", "cc", "aa");
long titleMatchId = insertTrack("aa", "bb", "cc");
long titleCategoryMatchId = insertTrack("aa", "bb", "ca");
long titleDescriptionMatchId = insertTrack("aa", "ba", "cc");
long allMatchId = insertTrack("aa", "ba", "ca");
SearchQuery query = new SearchQuery("a", null, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
// Title > Description > Category.
assertTrackResults(results,
allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId,
categoryMatchId);
}
public void testSearchWaypointText() {
// Insert 7 waypoints (purposefully out of result order):
// - one which won't match
// - one which will match the description
// - one which will match the category
// - one which will match the title
// - one which will match in title and category
// - one which will match in title and description
// - one which will match in all fields
insertWaypoint("bb", "cc", "dd");
long descriptionMatchId = insertWaypoint("bb", "aa", "cc");
long categoryMatchId = insertWaypoint("bb", "cc", "aa");
long titleMatchId = insertWaypoint("aa", "bb", "cc");
long titleCategoryMatchId = insertWaypoint("aa", "bb", "ca");
long titleDescriptionMatchId = insertWaypoint("aa", "ba", "cc");
long allMatchId = insertWaypoint("aa", "ba", "ca");
SearchQuery query = new SearchQuery("a", null, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
// Title > Description > Category.
assertWaypointResults(results,
allMatchId, titleDescriptionMatchId, titleCategoryMatchId, titleMatchId, descriptionMatchId,
categoryMatchId);
}
public void testSearchMixedText() {
// Insert 5 entries (purposefully out of result order):
// - one waypoint which will match by description
// - one waypoint which won't match
// - one waypoint which will match by title
// - one track which won't match
// - one track which will match by title
long descriptionWaypointId = insertWaypoint("bb", "aa", "cc");
insertWaypoint("bb", "cc", "dd");
long titleWaypointId = insertWaypoint("aa", "bb", "cc");
insertTrack("bb", "cc", "dd");
long trackId = insertTrack("aa", "bb", "cc");
SearchQuery query = new SearchQuery("a", null, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
// Title > Description > Category.
assertEquals(results.toString(), 3, results.size());
assertTrackResult(trackId, results.get(0));
assertWaypointResult(titleWaypointId, results.get(1));
assertWaypointResult(descriptionWaypointId, results.get(2));
}
public void testSearchTrackDistance() {
// All results match text, but they're at difference distances from the user.
long farFarAwayId = insertTrack("aa", 0.3);
long nearId = insertTrack("ab", 0.1);
long farId = insertTrack("ac", 0.2);
SearchQuery query = new SearchQuery("a", HERE, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
// Distance order.
assertTrackResults(results, nearId, farId, farFarAwayId);
}
public void testSearchWaypointDistance() {
// All results match text, but they're at difference distances from the user.
long farFarAwayId = insertWaypoint("aa", 0.3);
long nearId = insertWaypoint("ab", 0.1);
long farId = insertWaypoint("ac", 0.2);
SearchQuery query = new SearchQuery("a", HERE, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
// Distance order.
assertWaypointResults(results, nearId, farId, farFarAwayId);
}
public void testSearchTrackRecent() {
// All results match text, but they're were recorded at different times.
long oldestId = insertTrack("aa", 3);
long recentId = insertTrack("ab", 1);
long oldId = insertTrack("ac", 2);
SearchQuery query = new SearchQuery("a", null, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
// Reverse time order.
assertTrackResults(results, recentId, oldId, oldestId);
}
public void testSearchWaypointRecent() {
// All results match text, but they're were recorded at different times.
long oldestId = insertWaypoint("aa", 2);
long recentId = insertWaypoint("ab", 0);
long oldId = insertWaypoint("ac", 1);
SearchQuery query = new SearchQuery("a", null, -1, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
// Reverse time order.
assertWaypointResults(results, recentId, oldId, oldestId);
}
public void testSearchCurrentTrack() {
// All results match text, but one of them is the current track.
long currentId = insertTrack("ab", 1);
long otherId = insertTrack("aa", 1);
SearchQuery query = new SearchQuery("a", null, currentId, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
// Current track should be demoted.
assertTrackResults(results, otherId, currentId);
}
public void testSearchCurrentTrackWaypoint() {
// All results match text, but one of them is in the current track.
long otherId = insertWaypoint("aa", 1, 456);
long currentId = insertWaypoint("ab", 1, 123);
SearchQuery query = new SearchQuery("a", null, 123, NOW);
ArrayList<ScoredResult> results = new ArrayList<ScoredResult>(engine.search(query));
// Waypoint in current track should be promoted.
assertWaypointResults(results, currentId, otherId);
}
private void assertTrackResult(long trackId, ScoredResult result) {
assertNotNull("Not a track", result.track);
assertNull("Ambiguous result", result.waypoint);
assertEquals(trackId, result.track.getId());
}
private void assertTrackResults(List<ScoredResult> results, long... trackIds) {
String errMsg = "Expected IDs=" + Arrays.toString(trackIds) + "; results=" + results;
assertEquals(results.size(), trackIds.length);
for (int i = 0; i < results.size(); i++) {
ScoredResult result = results.get(i);
assertNotNull(errMsg, result.track);
assertNull(errMsg, result.waypoint);
assertEquals(errMsg, trackIds[i], result.track.getId());
}
}
private void assertWaypointResult(long waypointId, ScoredResult result) {
assertNotNull("Not a waypoint", result.waypoint);
assertNull("Ambiguous result", result.track);
assertEquals(waypointId, result.waypoint.getId());
}
private void assertWaypointResults(List<ScoredResult> results, long... waypointIds) {
String errMsg = "Expected IDs=" + Arrays.toString(waypointIds) + "; results=" + results;
assertEquals(results.size(), waypointIds.length);
for (int i = 0; i < results.size(); i++) {
ScoredResult result = results.get(i);
assertNotNull(errMsg, result.waypoint);
assertNull(errMsg, result.track);
assertEquals(errMsg, waypointIds[i], result.waypoint.getId());
}
}
}
@@ -0,0 +1,970 @@
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.content;
import static com.google.android.testing.mocking.AndroidMock.capture;
import static com.google.android.testing.mocking.AndroidMock.eq;
import static com.google.android.testing.mocking.AndroidMock.expect;
import static com.google.android.testing.mocking.AndroidMock.isA;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.TrackStubUtils;
import com.google.android.apps.mytracks.content.DataSourceManager.CurrentLocationListener;
import com.google.android.apps.mytracks.content.DataSourceManager.HeadingListener;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationFactory;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
import com.google.android.apps.mytracks.content.TrackDataListener.LocationState;
import com.google.android.apps.mytracks.services.TrackRecordingServiceTest.MockContext;
import com.google.android.apps.mytracks.util.PreferencesUtils;
import com.google.android.maps.mytracks.R;
import com.google.android.testing.mocking.AndroidMock;
import com.google.android.testing.mocking.UsesMocks;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.location.Location;
import android.location.LocationListener;
import android.provider.BaseColumns;
import android.test.AndroidTestCase;
import android.test.RenamingDelegatingContext;
import android.test.mock.MockContentResolver;
import java.lang.reflect.Constructor;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import org.easymock.Capture;
import org.easymock.IAnswer;
/**
* Tests for {@link TrackDataHub}.
*
* @author Rodrigo Damazio
*/
public class TrackDataHubTest extends AndroidTestCase {
private static final long TRACK_ID = 42L;
private static final int TARGET_POINTS = 50;
private MockContext context;
private SharedPreferences sharedPreferences;
private MyTracksProviderUtils myTracksProviderUtils;
private DataSource dataSource;
private TrackDataManager trackDataManager;
private TrackDataHub trackDataHub;
private TrackDataListener trackDataListener1;
private TrackDataListener trackDataListener2;
private Capture<OnSharedPreferenceChangeListener> preferenceChangeListenerCapture = new Capture<
SharedPreferences.OnSharedPreferenceChangeListener>();
private float declination;
@UsesMocks({ MyTracksProviderUtils.class, DataSource.class, TrackDataListener.class })
@Override
protected void setUp() throws Exception {
super.setUp();
context = new MockContext(new MockContentResolver(), new RenamingDelegatingContext(
getContext(), getContext(), "test."));
sharedPreferences = context.getSharedPreferences(Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
myTracksProviderUtils = AndroidMock.createMock(MyTracksProviderUtils.class);
dataSource = AndroidMock.createMock(DataSource.class, context);
trackDataManager = new TrackDataManager();
trackDataHub = new TrackDataHub(
context, trackDataManager, myTracksProviderUtils, TARGET_POINTS) {
@Override
protected DataSource newDataSource() {
return dataSource;
}
@Override
protected void runInHanderThread(Runnable runnable) {
// Run everything in the same thread
runnable.run();
}
@Override
protected float getDeclination(Location location, long timestamp) {
return declination;
}
};
trackDataListener1 = AndroidMock.createStrictMock(
"trackDataListener1", TrackDataListener.class);
trackDataListener2 = AndroidMock.createStrictMock(
"trackDataListener2", TrackDataListener.class);
PreferencesUtils.setLong(context, R.string.recording_track_id_key, TRACK_ID);
PreferencesUtils.setLong(context, R.string.selected_track_id_key, TRACK_ID);
}
@Override
protected void tearDown() throws Exception {
AndroidMock.reset(dataSource);
// Expect everything to be unregistered.
dataSource.unregisterContentObserver(isA(ContentObserver.class));
AndroidMock.expectLastCall().times(3);
dataSource.unregisterLocationListener(isA(LocationListener.class));
dataSource.unregisterHeadingListener(isA(SensorEventListener.class));
dataSource.unregisterOnSharedPreferenceChangeListener(
isA(OnSharedPreferenceChangeListener.class));
dataSource.close();
AndroidMock.replay(dataSource);
trackDataHub.stop();
trackDataHub = null;
super.tearDown();
}
/**
* Tests registering for tracks table update.
*/
public void testTracksTableUpdate() {
// Register two listeners
Capture<ContentObserver> contentObserverCapture = new Capture<ContentObserver>();
Track track = new Track();
expect(myTracksProviderUtils.getTrack(TRACK_ID)).andStubReturn(track);
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
dataSource.registerContentObserver(
eq(TracksColumns.CONTENT_URI), capture(contentObserverCapture));
trackDataListener1.onTrackUpdated(track);
trackDataListener2.onTrackUpdated(track);
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.TRACKS_TABLE));
trackDataHub.registerTrackDataListener(
trackDataListener2, EnumSet.of(TrackDataType.TRACKS_TABLE));
verifyAndReset();
// Causes tracks table update
ContentObserver contentObserver = contentObserverCapture.getValue();
expect(myTracksProviderUtils.getTrack(TRACK_ID)).andStubReturn(track);
trackDataListener1.onTrackUpdated(track);
trackDataListener2.onTrackUpdated(track);
replay();
contentObserver.onChange(false);
verifyAndReset();
// Unregister one listener
expect(myTracksProviderUtils.getTrack(TRACK_ID)).andStubReturn(track);
trackDataListener2.onTrackUpdated(track);
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener1);
contentObserver.onChange(false);
verifyAndReset();
// Unregister the second listener
dataSource.unregisterContentObserver(contentObserver);
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener2);
contentObserver.onChange(false);
verifyAndReset();
}
/**
* Tests registering for waypoints table update.
*/
public void testWaypointsTableUpdate() {
Waypoint waypoint1 = new Waypoint();
Waypoint waypoint2 = new Waypoint();
Waypoint waypoint3 = new Waypoint();
Waypoint waypoint4 = new Waypoint();
Location location = new Location("gps");
location.setLatitude(10.0);
location.setLongitude(8.0);
waypoint1.setLocation(location);
waypoint2.setLocation(location);
waypoint3.setLocation(location);
waypoint4.setLocation(location);
// Register two listeners
Capture<ContentObserver> contentObserverCapture = new Capture<ContentObserver>();
expect(myTracksProviderUtils.getWaypointsCursor(
eq(TRACK_ID), AndroidMock.leq(0L), eq(Constants.MAX_DISPLAYED_WAYPOINTS_POINTS)))
.andStubAnswer(new FixedSizeCursorAnswer(2));
expect(myTracksProviderUtils.createWaypoint(isA(Cursor.class)))
.andReturn(waypoint1).andReturn(waypoint2).andReturn(waypoint1).andReturn(waypoint2);
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
dataSource.registerContentObserver(
eq(WaypointsColumns.CONTENT_URI), capture(contentObserverCapture));
trackDataListener1.clearWaypoints();
trackDataListener2.clearWaypoints();
trackDataListener1.onNewWaypoint(waypoint1);
trackDataListener2.onNewWaypoint(waypoint1);
trackDataListener1.onNewWaypoint(waypoint2);
trackDataListener2.onNewWaypoint(waypoint2);
trackDataListener1.onNewWaypointsDone();
trackDataListener2.onNewWaypointsDone();
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
trackDataHub.registerTrackDataListener(
trackDataListener2, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
verifyAndReset();
// Cause waypoints table update
ContentObserver contentObserver = contentObserverCapture.getValue();
expect(myTracksProviderUtils.getWaypointsCursor(
eq(TRACK_ID), AndroidMock.leq(0L), eq(Constants.MAX_DISPLAYED_WAYPOINTS_POINTS)))
.andStubAnswer(new FixedSizeCursorAnswer(3));
expect(myTracksProviderUtils.createWaypoint(isA(Cursor.class)))
.andReturn(waypoint1).andReturn(waypoint2).andReturn(waypoint3);
trackDataListener1.clearWaypoints();
trackDataListener2.clearWaypoints();
trackDataListener1.onNewWaypoint(waypoint1);
trackDataListener2.onNewWaypoint(waypoint1);
trackDataListener1.onNewWaypoint(waypoint2);
trackDataListener2.onNewWaypoint(waypoint2);
trackDataListener1.onNewWaypoint(waypoint3);
trackDataListener2.onNewWaypoint(waypoint3);
trackDataListener1.onNewWaypointsDone();
trackDataListener2.onNewWaypointsDone();
replay();
contentObserver.onChange(false);
verifyAndReset();
// Unregister one listener
expect(myTracksProviderUtils.getWaypointsCursor(
eq(TRACK_ID), AndroidMock.leq(0L), eq(Constants.MAX_DISPLAYED_WAYPOINTS_POINTS)))
.andStubAnswer(new FixedSizeCursorAnswer(4));
expect(myTracksProviderUtils.createWaypoint(isA(Cursor.class)))
.andReturn(waypoint1).andReturn(waypoint2).andReturn(waypoint3).andReturn(waypoint4);
trackDataListener2.clearWaypoints();
trackDataListener2.onNewWaypoint(waypoint1);
trackDataListener2.onNewWaypoint(waypoint2);
trackDataListener2.onNewWaypoint(waypoint3);
trackDataListener2.onNewWaypoint(waypoint4);
trackDataListener2.onNewWaypointsDone();
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener1);
contentObserver.onChange(false);
verifyAndReset();
// Unregister the second listener
dataSource.unregisterContentObserver(contentObserver);
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener2);
contentObserver.onChange(false);
verifyAndReset();
}
/**
* Tests track points table update.
*/
public void testTrackPointsTableUpdate() {
// Register one listener
Capture<ContentObserver> contentObserverCapture = new Capture<ContentObserver>();
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
dataSource.registerContentObserver(
eq(TrackPointsColumns.CONTENT_URI), capture(contentObserverCapture));
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID)).andReturn(10L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
// Register a second listener
locationIterator = new FixedSizeLocationIterator(1, 10, 5);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID)).andReturn(10L);
trackDataListener2.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener2);
trackDataListener2.onNewTrackPointsDone();
replay();
trackDataHub.registerTrackDataListener(
trackDataListener2, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
// Deliver more points - should go to both listeners without clearing
ContentObserver contentObserver = contentObserverCapture.getValue();
locationIterator = new FixedSizeLocationIterator(11, 10, 1);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(11L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID)).andReturn(20L);
locationIterator.expectLocationsDelivered(trackDataListener1);
locationIterator.expectLocationsDelivered(trackDataListener2);
trackDataListener1.onNewTrackPointsDone();
trackDataListener2.onNewTrackPointsDone();
replay();
contentObserver.onChange(false);
verifyAndReset();
// Unregister one listener and change track
locationIterator = new FixedSizeLocationIterator(101, 10);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID + 1), eq(0L), eq(false), isA(LocationFactory.class)))
.andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID + 1)).andReturn(110L);
trackDataListener2.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener2);
trackDataListener2.onNewTrackPointsDone();
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener1);
trackDataHub.loadTrack(TRACK_ID + 1);
verifyAndReset();
}
/**
* Tests track points table update with registering the same listener.
*/
public void testTrackPointsTableUpdate_reRegister() {
// Register one listener
Capture<ContentObserver> contentObserverCapture = new Capture<ContentObserver>();
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
dataSource.registerContentObserver(
eq(TrackPointsColumns.CONTENT_URI), capture(contentObserverCapture));
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID)).andReturn(10L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
// Unregister the listener
ContentObserver observer = contentObserverCapture.getValue();
dataSource.unregisterContentObserver(observer);
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener1);
verifyAndReset();
// Register again
dataSource.registerContentObserver(
eq(TrackPointsColumns.CONTENT_URI), capture(contentObserverCapture));
locationIterator = new FixedSizeLocationIterator(1, 10, 5);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID)).andReturn(10L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
}
/**
* Tests tracks point able change. Register a listener after a track change.
*/
public void testTrackPointsTableUpdate_reRegisterAfterTrackChange() {
// Register one listener
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
dataSource.registerContentObserver(
eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture));
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 10, 5);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID)).andReturn(10L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
// Unregister the listener
ContentObserver observer = observerCapture.getValue();
dataSource.unregisterContentObserver(observer);
replay();
trackDataHub.unregisterTrackDataListener(trackDataListener1);
verifyAndReset();
// Register the listener after a new track
dataSource.registerContentObserver(
eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture));
locationIterator = new FixedSizeLocationIterator(1, 10);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID + 1), eq(0L), eq(false), isA(LocationFactory.class)))
.andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID + 1)).andReturn(10L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.loadTrack(TRACK_ID + 1);
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
}
/**
* Tests track points table update with large track sampling.
*/
public void testTrackPointsTableUpdate_largeTrackSampling() {
Capture<ContentObserver> contentObserverCapture = new Capture<ContentObserver>();
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
dataSource.registerContentObserver(
eq(TrackPointsColumns.CONTENT_URI), capture(contentObserverCapture));
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID)).andReturn(200L);
AndroidMock.expectLastCall().anyTimes();
FixedSizeLocationIterator locationIterator1 = new FixedSizeLocationIterator(
1, 200, 4, 25, 71, 120);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator1);
FixedSizeLocationIterator locationIterator2 = new FixedSizeLocationIterator(
1, 200, 4, 25, 71, 120);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator2);
trackDataListener1.clearTrackPoints();
locationIterator1.expectSampledLocationsDelivered(trackDataListener1, 4, false);
trackDataListener1.onNewTrackPointsDone();
trackDataListener2.clearTrackPoints();
locationIterator2.expectSampledLocationsDelivered(trackDataListener2, 4, true);
trackDataListener2.onNewTrackPointsDone();
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
trackDataHub.registerTrackDataListener(trackDataListener2, EnumSet.of(
TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE, TrackDataType.SAMPLED_OUT_TRACK_POINTS_TABLE));
verifyAndReset();
}
/**
* Tests track points table update with resampling.
*/
public void testTrackPointsTableUpdate_resampling() {
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
dataSource.registerContentObserver(
eq(TrackPointsColumns.CONTENT_URI), capture(observerCapture));
// Deliver 30 points (no sampling happens)
FixedSizeLocationIterator locationIterator = new FixedSizeLocationIterator(1, 30, 5);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID)).andReturn(30L);
trackDataListener1.clearTrackPoints();
locationIterator.expectLocationsDelivered(trackDataListener1);
trackDataListener1.onNewTrackPointsDone();
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.SAMPLED_IN_TRACK_POINTS_TABLE));
verifyAndReset();
// Now deliver 30 more (incrementally sampled)
ContentObserver observer = observerCapture.getValue();
locationIterator = new FixedSizeLocationIterator(31, 30);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(31L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID)).andReturn(60L);
locationIterator.expectSampledLocationsDelivered(trackDataListener1, 2, false);
trackDataListener1.onNewTrackPointsDone();
replay();
observer.onChange(false);
verifyAndReset();
// Now another 30 (triggers resampling)
locationIterator = new FixedSizeLocationIterator(1, 90);
expect(myTracksProviderUtils.getLocationIterator(
eq(TRACK_ID), eq(0L), eq(false), isA(LocationFactory.class))).andReturn(locationIterator);
expect(myTracksProviderUtils.getLastTrackLocationId(TRACK_ID)).andReturn(90L);
trackDataListener1.clearTrackPoints();
locationIterator.expectSampledLocationsDelivered(trackDataListener1, 2, false);
trackDataListener1.onNewTrackPointsDone();
replay();
observer.onChange(false);
verifyAndReset();
}
/**
* Tests headings change.
*/
public void testHeadingsChange() throws Exception {
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
Capture<SensorEventListener> sensorEventListenerCapture = new Capture<SensorEventListener>();
dataSource.registerHeadingListener(capture(sensorEventListenerCapture));
Capture<LocationListener> locationListenerCapture = new Capture<LocationListener>();
dataSource.registerLocationListener(capture(locationListenerCapture));
SensorEvent event = newSensorEvent();
// Expect location state changed
trackDataListener1.onLocationStateChanged(isA(LocationState.class));
AndroidMock.expectLastCall().anyTimes();
// First, get a dummy heading update
trackDataListener1.onHeadingChanged(0.0);
// Second, get a heading update without known location
trackDataListener1.onHeadingChanged(42.0f);
replay();
// Register one listener and update heading value
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.HEADING, TrackDataType.LOCATION));
SensorEventListener sensorListener = sensorEventListenerCapture.getValue();
LocationListener locationListener = locationListenerCapture.getValue();
event.values[0] = 42.0f;
sensorListener.onSensorChanged(event);
verifyAndReset();
// Expect location state changed
trackDataListener1.onLocationStateChanged(isA(LocationState.class));
AndroidMock.expectLastCall().anyTimes();
// Expect location changed
trackDataListener1.onLocationChanged(isA(Location.class));
AndroidMock.expectLastCall().anyTimes();
// Expect a heading update with declination
trackDataListener1.onHeadingChanged(52.0);
AndroidMock.expect(dataSource.isAllowed()).andReturn(true);
replay();
// Update location and sensor
Location location = new Location("gps");
location.setLatitude(10.0);
location.setLongitude(20.0);
location.setAltitude(30.0);
declination = 10.0f;
locationListener.onLocationChanged(location);
sensorListener.onSensorChanged(event);
verifyAndReset();
trackDataListener1.onHeadingChanged(52.0);
replay();
/*
* Change declination. Should still get the old value since the declination
* is only updated once an hour.
*/
declination = 20.0f;
sensorListener.onSensorChanged(event);
verifyAndReset();
}
/**
* Tests preferences change.
*/
public void testPreferencesChange() throws Exception {
// Register two listeners
PreferencesUtils.setBoolean(context, R.string.report_speed_key, true);
PreferencesUtils.setBoolean(context, R.string.metric_units_key, true);
PreferencesUtils.setInt(context, R.string.min_recording_distance_key,
PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
expect(trackDataListener1.onMetricUnitsChanged(true)).andReturn(false);
expect(trackDataListener1.onReportSpeedChanged(true)).andReturn(false);
expect(trackDataListener1.onMinRecordingDistanceChanged(
PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT)).andReturn(false);
expect(trackDataListener2.onMetricUnitsChanged(true)).andReturn(false);
expect(trackDataListener2.onReportSpeedChanged(true)).andReturn(false);
expect(trackDataListener2.onMinRecordingDistanceChanged(
PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT)).andReturn(false);
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(
trackDataListener1, EnumSet.of(TrackDataType.PREFERENCE));
trackDataHub.registerTrackDataListener(
trackDataListener2, EnumSet.of(TrackDataType.PREFERENCE));
verifyAndReset();
// Change report speed to false
expect(trackDataListener1.onReportSpeedChanged(false)).andReturn(false);
expect(trackDataListener2.onReportSpeedChanged(false)).andReturn(false);
replay();
PreferencesUtils.setBoolean(context, R.string.report_speed_key, false);
OnSharedPreferenceChangeListener listener = preferenceChangeListenerCapture.getValue();
listener.onSharedPreferenceChanged(
sharedPreferences, PreferencesUtils.getKey(context, R.string.report_speed_key));
verifyAndReset();
// Change metric units to false
expect(trackDataListener1.onMetricUnitsChanged(false)).andReturn(false);
expect(trackDataListener2.onMetricUnitsChanged(false)).andReturn(false);
replay();
PreferencesUtils.setBoolean(context, R.string.metric_units_key, false);
listener.onSharedPreferenceChanged(
sharedPreferences, PreferencesUtils.getKey(context, R.string.metric_units_key));
verifyAndReset();
}
/**
* Creates a new sensor event.
*/
private SensorEvent newSensorEvent() throws Exception {
Constructor<SensorEvent> constructor = SensorEvent.class.getDeclaredConstructor(int.class);
constructor.setAccessible(true);
return constructor.newInstance(3);
}
/**
* Replays mocks.
*/
private void replay() {
AndroidMock.replay(myTracksProviderUtils, dataSource, trackDataListener1, trackDataListener2);
}
/**
* Verifies and resets mocks.
*/
private void verifyAndReset() {
AndroidMock.verify(myTracksProviderUtils, dataSource, trackDataListener1, trackDataListener2);
AndroidMock.reset(myTracksProviderUtils, dataSource, trackDataListener1, trackDataListener2);
}
/**
* Fixed size cursor answer.
*
* @author Jimmy Shih
*/
private static class FixedSizeCursorAnswer implements IAnswer<Cursor> {
private final int size;
public FixedSizeCursorAnswer(int size) {
this.size = size;
}
@Override
public Cursor answer() throws Throwable {
MatrixCursor cursor = new MatrixCursor(new String[] { BaseColumns._ID });
for (long i = 0; i < size; i++) {
cursor.addRow(new Object[] { i });
}
return cursor;
}
}
/**
* Fixed size location iterator.
*
* @author Jimmy Shih
*/
private static class FixedSizeLocationIterator implements LocationIterator {
private final long startId;
private final Location[] locations;
private final Set<Integer> splitIndexSet = new HashSet<Integer>();
private int currentIndex = -1;
public FixedSizeLocationIterator(long startId, int size) {
this(startId, size, null);
}
public FixedSizeLocationIterator(long startId, int size, int... splitIndexes) {
this.startId = startId;
this.locations = new Location[size];
for (int i = 0; i < size; i++) {
Location location = new Location("gps");
location.setLatitude(-15.0 + i / 1000.0);
location.setLongitude(37 + i / 1000.0);
location.setAltitude(i);
locations[i] = location;
}
if (splitIndexes != null) {
for (int splitIndex : splitIndexes) {
splitIndexSet.add(splitIndex);
Location splitLocation = locations[splitIndex];
splitLocation.setLatitude(100.0);
splitLocation.setLongitude(200.0);
}
}
}
public void expectLocationsDelivered(TrackDataListener listener) {
for (int i = 0; i < locations.length; i++) {
if (splitIndexSet.contains(i)) {
listener.onSegmentSplit(locations[i]);
} else {
listener.onSampledInTrackPoint(locations[i]);
}
}
}
public void expectSampledLocationsDelivered(
TrackDataListener listener, int sampleFrequency, boolean includeSampledOut) {
boolean includeNext = false;
for (int i = 0; i < locations.length; i++) {
if (splitIndexSet.contains(i)) {
listener.onSegmentSplit(locations[i]);
includeNext = true;
} else if (includeNext || (i % sampleFrequency == 0)) {
listener.onSampledInTrackPoint(locations[i]);
includeNext = false;
} else if (includeSampledOut) {
listener.onSampledOutTrackPoint(locations[i]);
}
}
}
@Override
public boolean hasNext() {
return currentIndex < locations.length - 1;
}
@Override
public Location next() {
currentIndex++;
return locations[currentIndex];
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public long getLocationId() {
return startId + currentIndex;
}
@Override
public void close() {
// Do nothing
}
}
/**
* Tests the method {@link TrackDataHub#start()}. This method would also cover
* some logic of {@link TrackDataHub#loadDataForAll()} and
* {@link TrackDataHub#notifySelectedTrackChanged(Set)}.
*/
public void testRegisterSelectedTrackListener() {
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
Track track = TrackStubUtils.createTrack(1);
expect(myTracksProviderUtils.getTrack(capture(new Capture<Long>()))).andReturn(track);
// Make the track id is unique.
PreferencesUtils.setLong(context, R.string.recording_track_id_key, System.currentTimeMillis());
trackDataListener1.onSelectedTrackChanged(track);
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.SELECTED_TRACK));
verifyAndReset();
}
/**
* Tests the method {@link TrackDataHub#start()}. This method would also cover
* some logic of PreferencesUtils and
* {@link TrackDataHub#notifyTracksTableUpdate(Set)}.
*/
public void testRegisterTracksTableListener() {
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
dataSource.registerContentObserver(eq(TracksColumns.CONTENT_URI), capture(observerCapture));
Track track = TrackStubUtils.createTrack(1);
expect(myTracksProviderUtils.getTrack(capture(new Capture<Long>()))).andReturn(track);
// Make the track id is unique.
PreferencesUtils.setLong(context, R.string.recording_track_id_key, System.currentTimeMillis());
trackDataListener1.onTrackUpdated(track);
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.TRACKS_TABLE));
verifyAndReset();
}
/**
* Tests the method {@link TrackDataHub#start()}. This method would also cover
* some logic of {@link TrackDataHub#loadDataForAll()} and
* {@link TrackDataHub#notifyWaypointsTableUpdate(Set)}.
*/
public void testRegisterWaypointsTableListener() {
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
Capture<ContentObserver> observerCapture = new Capture<ContentObserver>();
dataSource.registerContentObserver(eq(WaypointsColumns.CONTENT_URI), capture(observerCapture));
expect(myTracksProviderUtils.getWaypointsCursor(capture(new Capture<Long>()), capture(new Capture<Long>()),
capture(new Capture<Integer>()))).andReturn(null);
trackDataListener1.clearWaypoints();
trackDataListener1.onNewWaypointsDone();
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.WAYPOINTS_TABLE));
verifyAndReset();
}
/**
* Tests the method {@link TrackDataHub#start()}. This method would also cover
* some logic of {@link TrackDataHub#loadDataForAll()} and
* {@link TrackDataHub#notifyLocationStateChanged(Set)} when no last seen
* location.
*/
public void testRegisterLocationListener_noLastSeenLocation() {
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
Capture<CurrentLocationListener> currentLocationListener = new Capture<CurrentLocationListener>();
dataSource.registerLocationListener(capture(currentLocationListener));
trackDataListener1.onLocationStateChanged(capture(new Capture<LocationState>()));
replay();
trackDataHub.setLastSeenLocation(null);
trackDataHub.start();
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.LOCATION));
verifyAndReset();
}
/**
* Tests the method {@link TrackDataHub#start()}. This method would also cover
* some logic of {@link TrackDataHub#loadDataForAll()} and
* {@link TrackDataHub#notifyLocationChanged(Location, boolean, Set)} when has
* last seen location.
*/
public void testRegisterLocationListener_hasLastSeenLocation() {
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
Capture<CurrentLocationListener> currentLocationListener = new Capture<CurrentLocationListener>();
dataSource.registerLocationListener(capture(currentLocationListener));
trackDataListener1.onLocationStateChanged(capture(new Capture<LocationState>()));
trackDataListener1.onLocationChanged(capture(new Capture<Location>()));
replay();
trackDataHub.setLastSeenLocation(new Location("gps"));
trackDataHub.start();
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.LOCATION));
verifyAndReset();
}
/**
* Tests the method {@link TrackDataHub#start()}. This method would also cover
* some logic of {@link TrackDataHub#loadDataForAllListeners()} and
* {@link TrackDataHub#notifyHeadingChanged(float)}.
*/
public void testRegisterHeadingListener() {
dataSource.registerOnSharedPreferenceChangeListener(capture(preferenceChangeListenerCapture));
Capture<HeadingListener> compassListener = new Capture<HeadingListener>();
dataSource.registerHeadingListener(capture(compassListener));
trackDataListener1.onHeadingChanged(capture(new Capture<Float>()));
replay();
trackDataHub.start();
trackDataHub.registerTrackDataListener(trackDataListener1, EnumSet.of(TrackDataType.HEADING));
verifyAndReset();
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.min_required_accuracy_key.
*/
public void testNotifyPreferenceChanged_minRequiredAccuracy() {
int value = 1;
PreferencesUtils.setInt(context, R.string.min_required_accuracy_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.min_required_accuracy_key));
assertEquals(value, trackDataHub.getMinRequiredAccuracy());
PreferencesUtils.setInt(context, R.string.min_required_accuracy_key, value + 1);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.min_required_accuracy_key));
assertEquals(value + 1, trackDataHub.getMinRequiredAccuracy());
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.metric_units_key.
*/
public void testNotifyPreferenceChanged_metricUnitsNoNotify() {
boolean value = false;
PreferencesUtils.setBoolean(context, R.string.metric_units_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.metric_units_key));
assertEquals(value, trackDataHub.isMetricUnits());
PreferencesUtils.setBoolean(context, R.string.metric_units_key, !value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.metric_units_key));
assertEquals(!value, trackDataHub.isMetricUnits());
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.metric_units_key.
*/
public void testNotifyPreferenceChanged_reportSpeedNoNotify() {
boolean value = false;
PreferencesUtils.setBoolean(context, R.string.report_speed_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.report_speed_key));
assertEquals(value, trackDataHub.isReportSpeed());
PreferencesUtils.setBoolean(context, R.string.report_speed_key, !value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.report_speed_key));
assertEquals(!value, trackDataHub.isReportSpeed());
}
/**
* Tests the method {@link TrackDataHub#notifyPreferenceChanged(String)} when
* the key is R.string.selected_track_id_key and no listener is registered.
*/
public void testNotifyPreferenceChanged_trackIdNoListener() {
long value = 1;
PreferencesUtils.setLong(context, R.string.selected_track_id_key, value);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.selected_track_id_key));
assertEquals(value, trackDataHub.getSelectedTrackId());
PreferencesUtils.setLong(context, R.string.selected_track_id_key, value + 1);
trackDataHub.notifyPreferenceChanged(PreferencesUtils
.getKey(context, R.string.selected_track_id_key));
assertEquals(value + 1, trackDataHub.getSelectedTrackId());
}
}
@@ -0,0 +1,58 @@
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.android.apps.mytracks.content;
import com.google.android.apps.mytracks.content.WaypointCreationRequest.WaypointType;
import android.os.Parcel;
import android.test.AndroidTestCase;
/**
* Tests for the WaypointCreationRequest class.
* {@link WaypointCreationRequest}
*
* @author Sandor Dornbush
*/
public class WaypointCreationRequestTest extends AndroidTestCase {
public void testTypeParceling() {
WaypointCreationRequest original = WaypointCreationRequest.DEFAULT_WAYPOINT;
Parcel p = Parcel.obtain();
original.writeToParcel(p, 0);
p.setDataPosition(0);
WaypointCreationRequest copy = WaypointCreationRequest.CREATOR.createFromParcel(p);
assertEquals(original.getType(), copy.getType());
assertFalse(copy.isTrackStatistics());
assertNull(copy.getName());
assertNull(copy.getDescription());
assertNull(copy.getIconUrl());
}
public void testAllAttributesParceling() {
WaypointCreationRequest original = new WaypointCreationRequest(
WaypointType.WAYPOINT, false, "name", "category", "description", "img.png");
Parcel p = Parcel.obtain();
original.writeToParcel(p, 0);
p.setDataPosition(0);
WaypointCreationRequest copy = WaypointCreationRequest.CREATOR.createFromParcel(p);
assertEquals(original.getType(), copy.getType());
assertFalse(copy.isTrackStatistics());
assertEquals("name", copy.getName());
assertEquals("category", copy.getCategory());
assertEquals("description", copy.getDescription());
assertEquals("img.png", copy.getIconUrl());
}
}