Fix the run time error in UploadResultActivityTest and add some assertions. Remove a sysout statement in CreateAndSendTrackTest.

This commit is contained in:
youtaol
2012-06-08 11:55:28 +08:00
commit 20641c694f
639 changed files with 65306 additions and 0 deletions
@@ -0,0 +1,88 @@
/*
* 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.io.file;
/**
* Tests for {@link CsvTrackWriter}.
*
* @author Rodrigo Damazio
*/
public class CsvTrackWriterTest extends TrackFormatWriterTest {
private static final String BEGIN_TAG = "\"";
private static final String END_TAG = "\"\n";
private static final String SEPARATOR = "\",\"";
public void testCsvOutput() throws Exception {
String expectedTrackHeader = getExpectedLine(
"Name", "Activity type", "Description");
String expectedTrack = getExpectedLine(TRACK_NAME, TRACK_CATEGORY, TRACK_DESCRIPTION);
String expectedMarkerHeader = getExpectedLine("Name",
"Marker type",
"Description",
"Latitude (deg)",
"Longitude (deg)",
"Altitude (m)",
"Bearing (deg)",
"Accuracy (m)",
"Speed (m/s)",
"Time");
String expectedMarker1 = getExpectedLine(WAYPOINT1_NAME, WAYPOINT1_CATEGORY,
WAYPOINT1_DESCRIPTION, "1.0", "-1.0", "10.0", "100.0", "1,000", "10,000",
"1970-01-01T00:01:40.000Z");
String expectedMarker2 = getExpectedLine(WAYPOINT2_NAME, WAYPOINT2_CATEGORY,
WAYPOINT2_DESCRIPTION, "2.0", "-2.0", "20.0", "200.0", "2,000", "20,000",
"1970-01-01T00:03:20.000Z");
String expectedPointHeader = getExpectedLine("Segment", "Point", "Latitude (deg)",
"Longitude (deg)", "Altitude (m)", "Bearing (deg)", "Accuracy (m)", "Speed (m/s)", "Time",
"Power (W)", "Cadence (rpm)", "Heart rate (bpm)", "Battery level (%)");
String expectedPoint1 = getExpectedLine("1", "1", "0.0", "0.0", "0.0", "0.0", "0", "0",
"1970-01-01T00:00:00.000Z", "100.0", "200.0", "300.0", "400.0");
String expectedPoint2 = getExpectedLine("1", "2", "1.0", "-1.0", "10.0", "100.0", "1,000",
"10,000", "1970-01-01T00:01:40.000Z", "101.0", "201.0", "301.0", "401.0");
String expectedPoint3 = getExpectedLine("2", "1", "2.0", "-2.0", "20.0", "200.0", "2,000",
"20,000", "1970-01-01T00:03:20.000Z", "102.0", "202.0", "302.0", "402.0");
String expectedPoint4 = getExpectedLine("2", "2", "3.0", "-3.0", "30.0", "300.0", "3,000",
"30,000", "1970-01-01T00:05:00.000Z", "103.0", "203.0", "303.0", "403.0");
String expected = expectedTrackHeader + expectedTrack + "\n"
+ expectedMarkerHeader + expectedMarker1 + expectedMarker2 + "\n"
+ expectedPointHeader + expectedPoint1 + expectedPoint2 + expectedPoint3 + expectedPoint4;
CsvTrackWriter writer = new CsvTrackWriter(getContext());
assertEquals(expected, writeTrack(writer));
}
/**
* Gets the expected CSV line from a list of expected values.
*
* @param values expected values
*/
private String getExpectedLine(String... values) {
StringBuilder builder = new StringBuilder();
builder.append(BEGIN_TAG);
boolean first = true;
for (String value : values) {
if (!first) {
builder.append(SEPARATOR);
}
first = false;
builder.append(value);
}
builder.append(END_TAG);
return builder.toString();
}
}
@@ -0,0 +1,295 @@
/*
* 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.io.file;
import static com.google.android.testing.mocking.AndroidMock.eq;
import static com.google.android.testing.mocking.AndroidMock.expect;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.TracksColumns;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
import com.google.android.apps.mytracks.io.file.GpxImporter;
import com.google.android.apps.mytracks.testing.TestingProviderUtilsFactory;
import com.google.android.testing.mocking.AndroidMock;
import com.google.android.testing.mocking.UsesMocks;
import android.content.ContentUris;
import android.location.Location;
import android.location.LocationManager;
import android.net.Uri;
import android.test.AndroidTestCase;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.SimpleTimeZone;
import javax.xml.parsers.ParserConfigurationException;
import org.easymock.Capture;
import org.easymock.IArgumentMatcher;
import org.xml.sax.SAXException;
/**
* Tests for the GPX importer.
*
* @author Steffen Horlacher
*/
public class GpxImporterTest extends AndroidTestCase {
private static final String TRACK_NAME = "blablub";
private static final String TRACK_DESC = "s'Laebe isch koi Schlotzer";
private static final String TRACK_LAT_1 = "48.768364";
private static final String TRACK_LON_1 = "9.177886";
private static final String TRACK_ELE_1 = "324.0";
private static final String TRACK_TIME_1 = "2010-04-22T18:21:00Z";
private static final String TRACK_LAT_2 = "48.768374";
private static final String TRACK_LON_2 = "9.177816";
private static final String TRACK_ELE_2 = "333.0";
private static final String TRACK_TIME_2 = "2010-04-22T18:21:50.123";
private static final SimpleDateFormat DATE_FORMAT1 =
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
private static final SimpleDateFormat DATE_FORMAT2 =
new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss'Z'");
static {
// We can't omit the timezones in the test, otherwise it'll use the local
// timezone and fail depending on where the test runner is.
SimpleTimeZone utc = new SimpleTimeZone(0, "UTC");
DATE_FORMAT1.setTimeZone(utc);
DATE_FORMAT2.setTimeZone(utc);
}
// TODO: use real files from different sources with more track points.
private static final String VALID_TEST_GPX = "<gpx><trk><name><![CDATA["
+ TRACK_NAME + "]]></name><desc><![CDATA[" + TRACK_DESC
+ "]]></desc><trkseg>" + "<trkpt lat=\"" + TRACK_LAT_1 + "\" lon=\""
+ TRACK_LON_1 + "\"><ele>" + TRACK_ELE_1 + "</ele><time>" + TRACK_TIME_1
+ "</time></trkpt> +" + "<trkpt lat=\"" + TRACK_LAT_2 + "\" lon=\""
+ TRACK_LON_2 + "\"><ele>" + TRACK_ELE_2 + "</ele><time>" + TRACK_TIME_2
+ "</time></trkpt>" + "</trkseg></trk></gpx>";
// invalid xml
private static final String INVALID_XML_TEST_GPX = VALID_TEST_GPX.substring(
0, VALID_TEST_GPX.length() - 50);
private static final String INVALID_LOCATION_TEST_GPX = VALID_TEST_GPX
.replaceAll(TRACK_LAT_1, "1000.0");
private static final String INVALID_TIME_TEST_GPX = VALID_TEST_GPX
.replaceAll(TRACK_TIME_1, "invalid");
private static final String INVALID_ALTITUDE_TEST_GPX = VALID_TEST_GPX
.replaceAll(TRACK_ELE_1, "invalid");
private static final String INVALID_LATITUDE_TEST_GPX = VALID_TEST_GPX
.replaceAll(TRACK_LAT_1, "invalid");
private static final String INVALID_LONGITUDE_TEST_GPX = VALID_TEST_GPX
.replaceAll(TRACK_LON_1, "invalid");
private static final long TRACK_ID = 1;
private static final long TRACK_POINT_ID_1 = 1;
private static final long TRACK_POINT_ID_2 = 2;
private static final Uri TRACK_ID_URI = ContentUris.appendId(
TracksColumns.CONTENT_URI.buildUpon(), TRACK_ID).build();
private MyTracksProviderUtils providerUtils;
private Factory oldProviderUtilsFactory;
@UsesMocks(MyTracksProviderUtils.class)
@Override
protected void setUp() throws Exception {
super.setUp();
providerUtils = AndroidMock.createMock(MyTracksProviderUtils.class);
oldProviderUtilsFactory =
TestingProviderUtilsFactory.installWithInstance(providerUtils);
}
@Override
protected void tearDown() throws Exception {
TestingProviderUtilsFactory.restoreOldFactory(oldProviderUtilsFactory);
super.tearDown();
}
/**
* Test import success.
*/
public void testImportSuccess() throws Exception {
Capture<Track> trackParam = new Capture<Track>();
Location loc1 = new Location(LocationManager.GPS_PROVIDER);
loc1.setTime(DATE_FORMAT2.parse(TRACK_TIME_1).getTime());
loc1.setLatitude(Double.parseDouble(TRACK_LAT_1));
loc1.setLongitude(Double.parseDouble(TRACK_LON_1));
loc1.setAltitude(Double.parseDouble(TRACK_ELE_1));
Location loc2 = new Location(LocationManager.GPS_PROVIDER);
loc2.setTime(DATE_FORMAT1.parse(TRACK_TIME_2).getTime());
loc2.setLatitude(Double.parseDouble(TRACK_LAT_2));
loc2.setLongitude(Double.parseDouble(TRACK_LON_2));
loc2.setAltitude(Double.parseDouble(TRACK_ELE_2));
expect(providerUtils.insertTrack(AndroidMock.capture(trackParam)))
.andReturn(TRACK_ID_URI);
expect(providerUtils.getLastLocationId(TRACK_ID)).andReturn(TRACK_POINT_ID_1).andReturn(TRACK_POINT_ID_2);
// A flush happens after the first insertion to get the starting point ID,
// which is why we get two calls
expect(providerUtils.bulkInsertTrackPoints(LocationsMatcher.eqLoc(loc1),
eq(1), eq(TRACK_ID))).andReturn(1);
expect(providerUtils.bulkInsertTrackPoints(LocationsMatcher.eqLoc(loc2),
eq(1), eq(TRACK_ID))).andReturn(1);
providerUtils.updateTrack(AndroidMock.capture(trackParam));
AndroidMock.replay(providerUtils);
InputStream is = new ByteArrayInputStream(VALID_TEST_GPX.getBytes());
GpxImporter.importGPXFile(is, providerUtils);
AndroidMock.verify(providerUtils);
// verify track parameter
Track track = trackParam.getValue();
assertEquals(TRACK_NAME, track.getName());
assertEquals(TRACK_DESC, track.getDescription());
assertEquals(DATE_FORMAT2.parse(TRACK_TIME_1).getTime(), track.getStatistics()
.getStartTime());
assertNotSame(-1, track.getStartId());
assertNotSame(-1, track.getStopId());
}
/**
* Test with invalid location - track should be deleted.
*/
public void testImportLocationFailure() throws ParserConfigurationException, IOException {
testInvalidXML(INVALID_LOCATION_TEST_GPX);
}
/**
* Test with invalid time - track should be deleted.
*/
public void testImportTimeFailure() throws ParserConfigurationException, IOException {
testInvalidXML(INVALID_TIME_TEST_GPX);
}
/**
* Test with invalid xml - track should be deleted.
*/
public void testImportXMLFailure() throws ParserConfigurationException, IOException {
testInvalidXML(INVALID_XML_TEST_GPX);
}
/**
* Test with invalid altitude - track should be deleted.
*/
public void testImportInvalidAltitude() throws ParserConfigurationException, IOException {
testInvalidXML(INVALID_ALTITUDE_TEST_GPX);
}
/**
* Test with invalid latitude - track should be deleted.
*/
public void testImportInvalidLatitude() throws ParserConfigurationException, IOException {
testInvalidXML(INVALID_LATITUDE_TEST_GPX);
}
/**
* Test with invalid longitude - track should be deleted.
*/
public void testImportInvalidLongitude() throws ParserConfigurationException, IOException {
testInvalidXML(INVALID_LONGITUDE_TEST_GPX);
}
private void testInvalidXML(String xml) throws ParserConfigurationException,
IOException {
expect(providerUtils.insertTrack((Track) AndroidMock.anyObject()))
.andReturn(TRACK_ID_URI);
expect(providerUtils.bulkInsertTrackPoints((Location[]) AndroidMock.anyObject(),
AndroidMock.anyInt(), AndroidMock.anyLong())).andStubReturn(1);
expect(providerUtils.getLastLocationId(TRACK_ID)).andStubReturn(TRACK_POINT_ID_1);
providerUtils.deleteTrack(TRACK_ID);
AndroidMock.replay(providerUtils);
InputStream is = new ByteArrayInputStream(xml.getBytes());
try {
GpxImporter.importGPXFile(is, providerUtils);
} catch (SAXException e) {
// expected exception
}
AndroidMock.verify(providerUtils);
}
/**
* Workaround because of capture bug 2617107 in easymock:
* http://sourceforge.net
* /tracker/?func=detail&aid=2617107&group_id=82958&atid=567837
*/
private static class LocationsMatcher implements IArgumentMatcher {
private final Location[] matchLocs;
private LocationsMatcher(Location[] expected) {
this.matchLocs = expected;
}
public static Location[] eqLoc(Location[] expected) {
IArgumentMatcher matcher = new LocationsMatcher(expected);
AndroidMock.reportMatcher(matcher);
return null;
}
public static Location[] eqLoc(Location expected) {
return eqLoc(new Location[] { expected});
}
@Override
public void appendTo(StringBuffer buf) {
buf.append("eqLoc(").append(Arrays.toString(matchLocs)).append(")");
}
@Override
public boolean matches(Object obj) {
if (! (obj instanceof Location[])) { return false; }
Location[] locs = (Location[]) obj;
if (locs.length < matchLocs.length) { return false; }
// Only check the first elements (those that will be taken into account)
for (int i = 0; i < matchLocs.length; i++) {
if (!locationsMatch(locs[i], matchLocs[i])) {
return false;
}
}
return true;
}
private boolean locationsMatch(Location loc1, Location loc2) {
return (loc1.getTime() == loc2.getTime()) &&
(loc1.getLatitude() == loc2.getLatitude()) &&
(loc1.getLongitude() == loc2.getLongitude()) &&
(loc1.getAltitude() == loc2.getAltitude());
}
}
}
@@ -0,0 +1,76 @@
/*
* 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.io.file;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Tests for {@link GpxTrackWriter}.
*
* @author Rodrigo Damazio
*/
public class GpxTrackWriterTest extends TrackFormatWriterTest {
public void testXmlOutput() throws Exception {
TrackFormatWriter writer = new GpxTrackWriter(getContext());
String result = writeTrack(writer);
Document doc = parseXmlDocument(result);
Element gpxTag = getChildElement(doc, "gpx");
Element trackTag = getChildElement(gpxTag, "trk");
assertEquals(TRACK_NAME, getChildTextValue(trackTag, "name"));
assertEquals(TRACK_DESCRIPTION, getChildTextValue(trackTag, "desc"));
List<Element> segmentTags = getChildElements(trackTag, "trkseg", 2);
List<Element> segPointTags = getChildElements(segmentTags.get(0), "trkpt", 2);
assertTagMatchesLocation(segPointTags.get(0), "0", "0", "1970-01-01T00:00:00.000Z", "0");
assertTagMatchesLocation(segPointTags.get(1), "1", "-1", "1970-01-01T00:01:40.000Z", "10");
segPointTags = getChildElements(segmentTags.get(1), "trkpt", 2);
assertTagMatchesLocation(segPointTags.get(0), "2", "-2", "1970-01-01T00:03:20.000Z", "20");
assertTagMatchesLocation(segPointTags.get(1), "3", "-3", "1970-01-01T00:05:00.000Z", "30");
List<Element> waypointTags = getChildElements(gpxTag, "wpt", 2);
Element wptTag = waypointTags.get(0);
assertEquals(WAYPOINT1_NAME, getChildTextValue(wptTag, "name"));
assertEquals(WAYPOINT1_DESCRIPTION, getChildTextValue(wptTag, "desc"));
assertTagMatchesLocation(wptTag, "1", "-1", "1970-01-01T00:01:40.000Z", "10");
wptTag = waypointTags.get(1);
assertEquals(WAYPOINT2_NAME, getChildTextValue(wptTag, "name"));
assertEquals(WAYPOINT2_DESCRIPTION, getChildTextValue(wptTag, "desc"));
assertTagMatchesLocation(wptTag, "2", "-2", "1970-01-01T00:03:20.000Z", "20");
}
/**
* Asserts that the given tag describes a location.
*
* @param tag the tag
* @param latitude the location's latitude
* @param longitude the location's longitude
* @param time the location's time
* @param elevation the location's elevation
*/
private void assertTagMatchesLocation(
Element tag, String latitude, String longitude, String time, String elevation) {
assertEquals(latitude, tag.getAttribute("lat"));
assertEquals(longitude, tag.getAttribute("lon"));
assertEquals(time, getChildTextValue(tag, "time"));
assertEquals(elevation, getChildTextValue(tag, "ele"));
}
}
@@ -0,0 +1,125 @@
/*
* 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.io.file;
import com.google.android.apps.mytracks.content.DescriptionGenerator;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import android.location.Location;
import java.util.List;
import java.util.Vector;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Tests for {@link KmlTrackWriter}.
*
* @author Rodrigo Damazio
*/
public class KmlTrackWriterTest extends TrackFormatWriterTest {
private static final String FULL_TRACK_DESCRIPTION = "full track description";
/**
* A fake version of {@link DescriptionGenerator} which returns a fixed track
* description, thus not depending on the context.
*/
private class FakeDescriptionGenerator implements DescriptionGenerator {
@Override
public String generateTrackDescription(
Track aTrack, Vector<Double> distances, Vector<Double> elevations, boolean html) {
return FULL_TRACK_DESCRIPTION;
}
@Override
public String generateWaypointDescription(Waypoint waypoint) {
return null;
}
}
public void testXmlOutput() throws Exception {
KmlTrackWriter writer = new KmlTrackWriter(getContext(), new FakeDescriptionGenerator());
String result = writeTrack(writer);
Document doc = parseXmlDocument(result);
Element kmlTag = getChildElement(doc, "kml");
Element docTag = getChildElement(kmlTag, "Document");
assertEquals(TRACK_NAME, getChildTextValue(docTag, "name"));
assertEquals(TRACK_DESCRIPTION, getChildTextValue(docTag, "description"));
// There are 3 placemarks - start, track, and end
List<Element> placemarkTags = getChildElements(docTag, "Placemark", 3);
assertTagIsPlacemark(
placemarkTags.get(0), TRACK_NAME + " (Start)", TRACK_DESCRIPTION, location1);
assertTagIsPlacemark(
placemarkTags.get(2), TRACK_NAME + " (End)", FULL_TRACK_DESCRIPTION, location4);
List<Element> folderTag = getChildElements(docTag, "Folder", 1);
List<Element> folderPlacemarkTags = getChildElements(folderTag.get(0), "Placemark", 2);
assertTagIsPlacemark(
folderPlacemarkTags.get(0), WAYPOINT1_NAME, WAYPOINT1_DESCRIPTION, location2);
assertTagIsPlacemark(
folderPlacemarkTags.get(1), WAYPOINT2_NAME, WAYPOINT2_DESCRIPTION, location3);
Element trackPlacemarkTag = placemarkTags.get(1);
assertEquals(TRACK_NAME, getChildTextValue(trackPlacemarkTag, "name"));
assertEquals(TRACK_DESCRIPTION, getChildTextValue(trackPlacemarkTag, "description"));
Element multiTrackTag = getChildElement(trackPlacemarkTag, "gx:MultiTrack");
List<Element> trackTags = getChildElements(multiTrackTag, "gx:Track", 2);
assertTagHasPoints(trackTags.get(0), location1, location2);
assertTagHasPoints(trackTags.get(1), location3, location4);
}
/**
* Asserts that the given tag is a placemark with the given properties.
*
* @param tag the tag
* @param name the expected placemark name
* @param description the expected placemark description
* @param location the expected placemark location
*/
private void assertTagIsPlacemark(
Element tag, String name, String description, Location location) {
assertEquals(name, getChildTextValue(tag, "name"));
assertEquals(description, getChildTextValue(tag, "description"));
Element pointTag = getChildElement(tag, "Point");
String expected = location.getLongitude() + "," + location.getLatitude() + ","
+ location.getAltitude();
String actual = getChildTextValue(pointTag, "coordinates");
assertEquals(expected, actual);
}
/**
* Asserts that the given tag has a list of "gx:coord" subtags matching the
* expected locations.
*
* @param tag the parent tag
* @param locations list of expected locations
*/
private void assertTagHasPoints(Element tag, Location... locations) {
List<Element> coordTags = getChildElements(tag, "gx:coord", locations.length);
for (int i = 0; i < locations.length; i++) {
Location location = locations[i];
String expected = location.getLongitude() + " " + location.getLatitude() + " "
+ location.getAltitude();
String actual = coordTags.get(i).getFirstChild().getTextContent();
assertEquals(expected, actual);
}
}
}
@@ -0,0 +1,64 @@
/*
* 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.io.file;
import java.io.File;
/**
* A simple, fake {@link TrackWriter} subclass with all methods mocked out.
* Tests are expected to override {@link #writeTrack}.
*
* @author Matthew Simmons
*
*/
public class MockTrackWriter implements TrackWriter {
public OnWriteListener onWriteListener;
@Override
public void setOnWriteListener(OnWriteListener onWriteListener) {
this.onWriteListener = onWriteListener;
}
@Override
public void setDirectory(File directory) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public String getAbsolutePath() {
throw new UnsupportedOperationException("not implemented");
}
@Override
public void writeTrack() {
throw new UnsupportedOperationException("not implemented");
}
@Override
public void stopWriteTrack() {
throw new UnsupportedOperationException("not implemented");
}
@Override
public boolean wasSuccess() {
return false;
}
@Override
public int getErrorMessage() {
return 0;
}
}
@@ -0,0 +1,96 @@
/*
* 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.io.file;
import com.google.android.apps.mytracks.content.MyTracksLocation;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.util.StringUtils;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Tests for {@link TcxTrackWriter}.
*
* @author Sandor Dornbush
*/
public class TcxTrackWriterTest extends TrackFormatWriterTest {
public void testXmlOutput() throws Exception {
TrackFormatWriter writer = new TcxTrackWriter(getContext());
String result = writeTrack(writer);
Document doc = parseXmlDocument(result);
Element root = getChildElement(doc, "TrainingCenterDatabase");
Element activitiesTag = getChildElement(root, "Activities");
Element activityTag = getChildElement(activitiesTag, "Activity");
Element lapTag = getChildElement(activityTag, "Lap");
List<Element> segmentTags = getChildElements(lapTag, "Track", 2);
Element segment1Tag = segmentTags.get(0);
Element segment2Tag = segmentTags.get(1);
List<Element> seg1PointTags = getChildElements(segment1Tag, "Trackpoint", 2);
List<Element> seg2PointTags = getChildElements(segment2Tag, "Trackpoint", 2);
assertTagsMatchPoints(seg1PointTags, location1, location2);
assertTagsMatchPoints(seg2PointTags, location3, location4);
}
/**
* Asserts that the given tags describe the given locations in the same order.
*
* @param tags list of tags
* @param locations list of locations
*/
private void assertTagsMatchPoints(List<Element> tags, MyTracksLocation... locations) {
assertEquals(locations.length, tags.size());
for (int i = 0; i < locations.length; i++) {
assertTagMatchesLocation(tags.get(i), locations[i]);
}
}
/**
* Asserts that the given tag describes the given location.
*
* @param tag the tag
* @param location the location
*/
private void assertTagMatchesLocation(Element tag, MyTracksLocation location) {
assertEquals(StringUtils.formatDateTimeIso8601(location.getTime()), getChildTextValue(tag, "Time"));
Element positionTag = getChildElement(tag, "Position");
assertEquals(
Double.toString(location.getLatitude()), getChildTextValue(positionTag, "LatitudeDegrees"));
assertEquals(Double.toString(location.getLongitude()),
getChildTextValue(positionTag, "LongitudeDegrees"));
assertEquals(Double.toString(location.getAltitude()), getChildTextValue(tag, "AltitudeMeters"));
assertTrue(location.getSensorDataSet() != null);
Sensor.SensorDataSet sds = location.getSensorDataSet();
List<Element> heartRate = getChildElements(tag, "HeartRateBpm", 1);
assertEquals(Integer.toString(sds.getHeartRate().getValue()),
getChildTextValue(heartRate.get(0), "Value"));
List<Element> extensions = getChildElements(tag, "Extensions", 1);
List<Element> tpx = getChildElements(extensions.get(0), "TPX", 1);
assertEquals(
Integer.toString(sds.getCadence().getValue()), getChildTextValue(tpx.get(0), "RunCadence"));
assertEquals(
Integer.toString(sds.getPower().getValue()), getChildTextValue(tpx.get(0), "Watts"));
}
}
@@ -0,0 +1,219 @@
// Copyright 2010 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.file;
import com.google.android.apps.mytracks.content.MyTracksLocation;
import com.google.android.apps.mytracks.content.Sensor;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import android.test.AndroidTestCase;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Base class for track format writer tests, which sets up a fake track and
* gives auxiliary methods for verifying XML output.
*
* @author Rodrigo Damazio
*/
public abstract class TrackFormatWriterTest extends AndroidTestCase {
// All the user-provided strings have "]]>" to ensure that proper escaping is
// being done.
protected static final String TRACK_NAME = "Home]]>";
protected static final String TRACK_CATEGORY = "Hiking";
protected static final String TRACK_DESCRIPTION = "The long ]]> journey home";
protected static final String WAYPOINT1_NAME = "point]]>1";
protected static final String WAYPOINT1_CATEGORY = "Statistics";
protected static final String WAYPOINT1_DESCRIPTION = "point 1]]>description";
protected static final String WAYPOINT2_NAME = "point]]>2";
protected static final String WAYPOINT2_CATEGORY = "Waypoint";
protected static final String WAYPOINT2_DESCRIPTION = "point 2]]>description";
private static final int BUFFER_SIZE = 10240;
protected Track track;
protected MyTracksLocation location1, location2, location3, location4;
protected Waypoint wp1, wp2;
@Override
protected void setUp() throws Exception {
super.setUp();
track = new Track();
track.setName(TRACK_NAME);
track.setCategory(TRACK_CATEGORY);
track.setDescription(TRACK_DESCRIPTION);
location1 = new MyTracksLocation("mock");
location2 = new MyTracksLocation("mock");
location3 = new MyTracksLocation("mock");
location4 = new MyTracksLocation("mock");
populateLocations(location1, location2, location3, location4);
wp1 = new Waypoint();
wp2 = new Waypoint();
wp1.setLocation(location2);
wp1.setName(WAYPOINT1_NAME);
wp1.setCategory(WAYPOINT1_CATEGORY);
wp1.setDescription(WAYPOINT1_DESCRIPTION);
wp2.setLocation(location3);
wp2.setName(WAYPOINT2_NAME);
wp2.setCategory(WAYPOINT2_CATEGORY);
wp2.setDescription(WAYPOINT2_DESCRIPTION);
}
/**
* Populates a list of locations with values.
*
* @param locations a list of locations
*/
private void populateLocations(MyTracksLocation... locations) {
for (int i = 0; i < locations.length; i++) {
MyTracksLocation location = locations[i];
location.setLatitude(i);
location.setLongitude(-i);
location.setAltitude(i * 10);
location.setBearing(i * 100);
location.setAccuracy(i * 1000);
location.setSpeed(i * 10000);
location.setTime(i * 100000);
Sensor.SensorData.Builder power = Sensor.SensorData.newBuilder().setValue(100 + i)
.setState(Sensor.SensorState.SENDING);
Sensor.SensorData.Builder cadence = Sensor.SensorData.newBuilder().setValue(200 + i)
.setState(Sensor.SensorState.SENDING);
Sensor.SensorData.Builder heartRate = Sensor.SensorData.newBuilder().setValue(300 + i)
.setState(Sensor.SensorState.SENDING);
Sensor.SensorData.Builder batteryLevel = Sensor.SensorData.newBuilder().setValue(400 + i)
.setState(Sensor.SensorState.SENDING);
Sensor.SensorDataSet sensorDataSet = Sensor.SensorDataSet.newBuilder().setPower(power)
.setCadence(cadence).setHeartRate(heartRate).setBatteryLevel(batteryLevel).build();
location.setSensorData(sensorDataSet);
}
}
/**
* Makes the right sequence of calls to the writer in order to write the fake
* track in {@link #track}.
*
* @param writer the writer to write to
* @return the written contents
*/
protected String writeTrack(TrackFormatWriter writer) throws Exception {
OutputStream output = new ByteArrayOutputStream(BUFFER_SIZE);
writer.prepare(track, output);
writer.writeHeader();
writer.writeBeginWaypoints();
writer.writeWaypoint(wp1);
writer.writeWaypoint(wp2);
writer.writeEndWaypoints();
writer.writeBeginTrack(location1);
writer.writeOpenSegment();
writer.writeLocation(location1);
writer.writeLocation(location2);
writer.writeCloseSegment();
writer.writeOpenSegment();
writer.writeLocation(location3);
writer.writeLocation(location4);
writer.writeCloseSegment();
writer.writeEndTrack(location4);
writer.writeFooter();
writer.close();
return output.toString();
}
/**
* Gets the text data contained inside a tag.
*
* @param parent the parent of the tag containing the text
* @param elementName the name of the tag containing the text
* @return the text contents
*/
protected String getChildTextValue(Element parent, String elementName) {
Element child = getChildElement(parent, elementName);
assertTrue(child.hasChildNodes());
NodeList children = child.getChildNodes();
int length = children.getLength();
assertTrue(length > 0);
// The children may be a sucession of text elements, just concatenate them
String result = "";
for (int i = 0; i < length; i++) {
Text textNode = (Text) children.item(i);
result += textNode.getNodeValue();
}
return result;
}
/**
* Returns all child elements of a given parent which have the given name.
*
* @param parent the parent to get children from
* @param elementName the element name to look for
* @param expectedChildren the number of children we're expected to find
* @return a list of the found elements
*/
protected List<Element> getChildElements(Node parent, String elementName,
int expectedChildren) {
assertTrue(parent.hasChildNodes());
NodeList children = parent.getChildNodes();
int length = children.getLength();
List<Element> result = new ArrayList<Element>();
for (int i = 0; i < length; i++) {
Node childNode = children.item(i);
if (childNode.getNodeType() == Node.ELEMENT_NODE
&& childNode.getNodeName().equalsIgnoreCase(elementName)) {
result.add((Element) childNode);
}
}
assertTrue(children.toString(), result.size() == expectedChildren);
return result;
}
/**
* Returns the single child element of the given parent with the given type.
*
* @param parent the parent to get a child from
* @param elementName the name of the child to look for
* @return the child element
*/
protected Element getChildElement(Node parent, String elementName) {
return getChildElements(parent, elementName, 1).get(0);
}
/**
* Parses the given XML contents and returns a DOM {@link Document} for it.
*/
protected Document parseXmlDocument(String contents)
throws FactoryConfigurationError, ParserConfigurationException,
SAXException, IOException {
DocumentBuilderFactory builderFactory =
DocumentBuilderFactory.newInstance();
builderFactory.setCoalescing(true);
// TODO: Somehow do XML validation on Android
// builderFactory.setValidating(true);
builderFactory.setNamespaceAware(true);
builderFactory.setIgnoringComments(true);
builderFactory.setIgnoringElementContentWhitespace(true);
DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
Document doc = documentBuilder.parse(
new InputSource(new StringReader(contents)));
return doc;
}
}
@@ -0,0 +1,363 @@
// Copyright 2010 Google Inc. All Rights Reserved.
package com.google.android.apps.mytracks.io.file;
import static org.easymock.EasyMock.expect;
import com.google.android.apps.mytracks.content.MyTracksProvider;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.services.TrackRecordingServiceTest.MockContext;
import com.google.android.apps.mytracks.testing.TestingProviderUtilsFactory;
import android.content.Context;
import android.location.Location;
import android.test.AndroidTestCase;
import android.test.RenamingDelegatingContext;
import android.test.mock.MockContentResolver;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import org.easymock.EasyMock;
import org.easymock.IArgumentMatcher;
import org.easymock.IMocksControl;
/**
* Tests for the track writer.
*
* @author Rodrigo Damazio
*/
public class TrackWriterTest extends AndroidTestCase {
/**
* {@link TrackWriterImpl} subclass which mocks out methods called from
* {@link TrackWriterImpl#openFile}.
*/
private static final class OpenFileTrackWriter extends TrackWriterImpl {
private final ByteArrayOutputStream stream;
private final boolean canWrite;
/**
* Constructor.
*
* @param stream the stream to return from
* {@link TrackWriterImpl#newOutputStream}, or null to throw a
* {@link FileNotFoundException}
* @param canWrite the value that {@link TrackWriterImpl#canWriteFile} will
* return
*/
private OpenFileTrackWriter(Context context,
MyTracksProviderUtils providerUtils, Track track,
TrackFormatWriter writer, ByteArrayOutputStream stream,
boolean canWrite) {
super(context, providerUtils, track, writer);
this.stream = stream;
this.canWrite = canWrite;
// The directory is set in the canWriteFile. However, this class
// overwrites canWriteFile, thus needs to set it.
setDirectory(new File("/"));
}
@Override
protected boolean canWriteFile() {
return canWrite;
}
@Override
protected OutputStream newOutputStream(String fileName)
throws FileNotFoundException {
assertEquals(FULL_TRACK_NAME, fileName);
if (stream == null) {
throw new FileNotFoundException();
}
return stream;
}
}
/**
* {@link TrackWriterImpl} subclass which mocks out methods called from
* {@link TrackWriterImpl#writeTrack}.
*/
private final class WriteTracksTrackWriter extends TrackWriterImpl {
private final boolean openResult;
/**
* Constructor.
*
* @param openResult the return value for {@link TrackWriterImpl#openFile}
*/
private WriteTracksTrackWriter(Context context,
MyTracksProviderUtils providerUtils, Track track,
TrackFormatWriter writer, boolean openResult) {
super(context, providerUtils, track, writer);
this.openResult = openResult;
}
@Override
protected boolean openFile() {
openFileCalls++;
return openResult;
}
@Override
void writeDocument() {
writeDocumentCalls++;
}
@Override
protected void runOnUiThread(Runnable runnable) {
runnable.run();
}
}
private static final long TRACK_ID = 1234567L;
private static final String EXTENSION = "ext";
private static final String TRACK_NAME = "Swimming across the pacific";
private static final String FULL_TRACK_NAME =
"Swimming across the pacific.ext";
private Track track;
private TrackFormatWriter formatWriter;
private TrackWriterImpl writer;
private IMocksControl mocksControl;
private MyTracksProviderUtils providerUtils;
private Factory oldProviderUtilsFactory;
// State used in specific tests
private int writeDocumentCalls;
private int openFileCalls;
@Override
protected void setUp() throws Exception {
super.setUp();
MockContentResolver mockContentResolver = new MockContentResolver();
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
getContext(), getContext(), "test.");
Context 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);
oldProviderUtilsFactory = TestingProviderUtilsFactory.installWithInstance(providerUtils);
mocksControl = EasyMock.createStrictControl();
formatWriter = mocksControl.createMock(TrackFormatWriter.class);
expect(formatWriter.getExtension()).andStubReturn(EXTENSION);
track = new Track();
track.setName(TRACK_NAME);
track.setId(TRACK_ID);
}
@Override
protected void tearDown() throws Exception {
TestingProviderUtilsFactory.restoreOldFactory(oldProviderUtilsFactory);
super.tearDown();
}
public void testWriteTrack() {
writer = new WriteTracksTrackWriter(getContext(), providerUtils, track,
formatWriter, true);
mocksControl.replay();
writer.writeTrack();
assertEquals(1, writeDocumentCalls);
assertEquals(1, openFileCalls);
mocksControl.verify();
}
public void testWriteTrack_openFails() {
writer = new WriteTracksTrackWriter(getContext(), providerUtils, track,
formatWriter, false);
mocksControl.replay();
writer.writeTrack();
assertEquals(0, writeDocumentCalls);
assertEquals(1, openFileCalls);
mocksControl.verify();
}
public void testOpenFile() {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
writer = new OpenFileTrackWriter(
getContext(), providerUtils, track, formatWriter, stream, true);
formatWriter.prepare(track, stream);
mocksControl.replay();
assertTrue(writer.openFile());
mocksControl.verify();
}
public void testOpenFile_cantWrite() {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
writer = new OpenFileTrackWriter(
getContext(), providerUtils, track, formatWriter, stream, false);
mocksControl.replay();
assertFalse(writer.openFile());
mocksControl.verify();
}
public void testOpenFile_streamError() {
writer = new OpenFileTrackWriter(
getContext(), providerUtils, track, formatWriter, null, true);
mocksControl.replay();
assertFalse(writer.openFile());
mocksControl.verify();
}
public void testWriteDocument_emptyTrack() throws Exception {
writer = new TrackWriterImpl(getContext(), providerUtils, track, formatWriter);
// Set expected mock behavior
formatWriter.writeHeader();
formatWriter.writeFooter();
formatWriter.close();
mocksControl.replay();
writer.writeDocument();
assertTrue(writer.wasSuccess());
mocksControl.verify();
}
public void testWriteDocument() throws Exception {
writer = new TrackWriterImpl(getContext(), providerUtils, track, formatWriter);
final Location[] locs = {
new Location("fake0"),
new Location("fake1"),
new Location("fake2"),
new Location("fake3"),
new Location("fake4"),
new Location("fake5")
};
Waypoint[] wps = { new Waypoint(), new Waypoint(), new Waypoint() };
// Fill locations with valid values
fillLocations(locs);
// Make location 3 invalid
locs[2].setLatitude(100);
assertEquals(locs.length, providerUtils.bulkInsertTrackPoints(locs, locs.length, TRACK_ID));
for (int i = 0; i < wps.length; ++i) {
Waypoint wpt = wps[i];
wpt.setTrackId(TRACK_ID);
assertNotNull(providerUtils.insertWaypoint(wpt));
wpt.setId(i + 1);
}
formatWriter.writeHeader();
// Expect reading/writing of the waypoints (except the first)
formatWriter.writeBeginWaypoints();
formatWriter.writeWaypoint(wptEq(wps[1]));
formatWriter.writeWaypoint(wptEq(wps[2]));
formatWriter.writeEndWaypoints();
// Begin the track
formatWriter.writeBeginTrack(locEq(locs[0]));
// Write locations 1-2
formatWriter.writeOpenSegment();
formatWriter.writeLocation(locEq(locs[0]));
formatWriter.writeLocation(locEq(locs[1]));
formatWriter.writeCloseSegment();
// Location 3 is not written - it's invalid
// Write locations 4-6
formatWriter.writeOpenSegment();
formatWriter.writeLocation(locEq(locs[3]));
formatWriter.writeLocation(locEq(locs[4]));
formatWriter.writeLocation(locEq(locs[5]));
formatWriter.writeCloseSegment();
// End the track
formatWriter.writeEndTrack(locEq(locs[5]));
formatWriter.writeFooter();
formatWriter.close();
mocksControl.replay();
writer.writeDocument();
assertTrue(writer.wasSuccess());
mocksControl.verify();
}
private static Waypoint wptEq(final Waypoint wpt) {
EasyMock.reportMatcher(new IArgumentMatcher() {
@Override
public boolean matches(Object wptObj2) {
if (wptObj2 == null || wpt == null) return wpt == wptObj2;
Waypoint wpt2 = (Waypoint) wptObj2;
return wpt.getId() == wpt2.getId();
}
@Override
public void appendTo(StringBuffer buffer) {
buffer.append("wptEq(");
buffer.append(wpt);
buffer.append(")");
}
});
return null;
}
private static Location locEq(final Location loc) {
EasyMock.reportMatcher(new IArgumentMatcher() {
@Override
public boolean matches(Object locObj2) {
if (locObj2 == null || loc == null) return loc == locObj2;
Location loc2 = (Location) locObj2;
return loc.hasAccuracy() == loc2.hasAccuracy()
&& (!loc.hasAccuracy() || loc.getAccuracy() == loc2.getAccuracy())
&& loc.hasAltitude() == loc2.hasAltitude()
&& (!loc.hasAltitude() || loc.getAltitude() == loc2.getAltitude())
&& loc.hasBearing() == loc2.hasBearing()
&& (!loc.hasBearing() || loc.getBearing() == loc2.getBearing())
&& loc.hasSpeed() == loc2.hasSpeed()
&& (!loc.hasSpeed() || loc.getSpeed() == loc2.getSpeed())
&& loc.getLatitude() == loc2.getLatitude()
&& loc.getLongitude() == loc2.getLongitude()
&& loc.getTime() == loc2.getTime();
}
@Override
public void appendTo(StringBuffer buffer) {
buffer.append("locEq(");
buffer.append(loc);
buffer.append(")");
}
});
return null;
}
private void fillLocations(Location... locs) {
assertTrue(locs.length < 90);
for (int i = 0; i < locs.length; i++) {
Location location = locs[i];
location.setLatitude(i + 1);
location.setLongitude(i + 1);
location.setTime(i + 1000);
}
}
}