forked from upstream-mirrors/OpenTracks
Bringing My Maps code back.
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="lib" path="libs/AndroidMockRuntime.jar"/>
|
||||
<classpathentry kind="src" path=".apt_generated">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/MyTracks"/>
|
||||
<classpathentry kind="lib" path="/MyTracks/libs/google-common.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1,6 @@
|
||||
<factorypath>
|
||||
<factorypathentry kind="WKSPJAR" id="/MyTracksTest/libs/AndroidMockGenerator.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="ANDROID_SDK/platforms/android-11/android.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="ANDROID_SDK/add-ons/addon_google_apis_google_inc_11/libs/maps.jar" enabled="true" runInBatchMode="false"/>
|
||||
<factorypathentry kind="VARJAR" id="MYTRACKS_BIN" enabled="true" runInBatchMode="false"/>
|
||||
</factorypath>
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>MyTracksTest</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
<project>MyTracks</project>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,7 @@
|
||||
#Mon Nov 01 02:42:03 BRST 2010
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.apt.aptEnabled=true
|
||||
org.eclipse.jdt.apt.genSrcDir=.apt_generated
|
||||
org.eclipse.jdt.apt.processorOptions/bin_dir=%PROJECT.DIR%/bin
|
||||
org.eclipse.jdt.apt.processorOptions/logfile=%PROJECT.DIR%/annotations.log
|
||||
org.eclipse.jdt.apt.reconcileEnabled=false
|
||||
@@ -0,0 +1,13 @@
|
||||
#Thu Aug 12 19:30:48 BRT 2010
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.processAnnotations=enabled
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.maps.mytracks.test"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0"
|
||||
android:debuggable="true">
|
||||
<application android:icon="@android:drawable/sym_def_app_icon"
|
||||
android:label="My Tracks Tests">
|
||||
<uses-library android:name="android.test.runner" />
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="3" />
|
||||
<instrumentation android:targetPackage="com.google.android.maps.mytracks"
|
||||
android:name="android.test.InstrumentationTestRunner" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="com.android.ide.eclipse.adt.junit.launchConfigurationType">
|
||||
<stringAttribute key="com.android.ide.eclipse.adt.commandline" value=""/>
|
||||
<intAttribute key="com.android.ide.eclipse.adt.delay" value="0"/>
|
||||
<stringAttribute key="com.android.ide.eclipse.adt.instrumentation" value="android.test.InstrumentationTestRunner"/>
|
||||
<booleanAttribute key="com.android.ide.eclipse.adt.nobootanim" value="true"/>
|
||||
<intAttribute key="com.android.ide.eclipse.adt.speed" value="0"/>
|
||||
<booleanAttribute key="com.android.ide.eclipse.adt.target" value="true"/>
|
||||
<booleanAttribute key="com.android.ide.eclipse.adt.wipedata" value="false"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/MyTracksTests"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="4"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=MyTracksTests"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit3"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="MyTracksTests"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value=""/>
|
||||
</launchConfiguration>
|
||||
@@ -0,0 +1,13 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system use,
|
||||
# "build.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=Google Inc.:Google APIs:11
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.android.apps.mytracks.ChartValueSeries.ZoomSettings;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.graphics.Paint.Style;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
/**
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ChartValueSeriesTest extends AndroidTestCase {
|
||||
private ChartValueSeries series;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
series = new ChartValueSeries(getContext(),
|
||||
"###,###",
|
||||
R.color.elevation_fill,
|
||||
R.color.elevation_border,
|
||||
new ZoomSettings(5, new int[] {100}),
|
||||
R.string.elevation);
|
||||
}
|
||||
|
||||
public void testInitialConditions() {
|
||||
assertEquals(0, series.getInterval());
|
||||
assertEquals(1, series.getMaxLabelLength());
|
||||
assertEquals(0, series.getMin());
|
||||
assertEquals(0, series.getMax());
|
||||
assertEquals(0.0, series.getSpread());
|
||||
assertEquals(Style.STROKE, series.getPaint().getStyle());
|
||||
assertEquals(getContext().getString(R.string.elevation),
|
||||
series.getTitle());
|
||||
assertTrue(series.isEnabled());
|
||||
}
|
||||
|
||||
public void testEnabled() {
|
||||
series.setEnabled(false);
|
||||
assertFalse(series.isEnabled());
|
||||
}
|
||||
|
||||
public void testSmallUpdates() {
|
||||
series.update(0);
|
||||
series.update(10);
|
||||
series.updateDimension();
|
||||
assertEquals(100, series.getInterval());
|
||||
assertEquals(3, series.getMaxLabelLength());
|
||||
assertEquals(0, series.getMin());
|
||||
assertEquals(100, series.getMax());
|
||||
assertEquals(100.0, series.getSpread());
|
||||
}
|
||||
|
||||
public void testBigUpdates() {
|
||||
series.update(0);
|
||||
series.update(901);
|
||||
series.updateDimension();
|
||||
assertEquals(100, series.getInterval());
|
||||
assertEquals(5, series.getMaxLabelLength());
|
||||
assertEquals(0, series.getMin());
|
||||
assertEquals(1000, series.getMax());
|
||||
assertEquals(1000.0, series.getSpread());
|
||||
}
|
||||
|
||||
public void testNotZeroBasedUpdates() {
|
||||
series.update(500);
|
||||
series.update(1401);
|
||||
series.updateDimension();
|
||||
assertEquals(100, series.getInterval());
|
||||
assertEquals(5, series.getMaxLabelLength());
|
||||
assertEquals(500, series.getMin());
|
||||
assertEquals(1500, series.getMax());
|
||||
assertEquals(1000.0, series.getSpread());
|
||||
}
|
||||
|
||||
public void testZoomSettings_invalidArgs() {
|
||||
try {
|
||||
new ZoomSettings(0, new int[] {10, 50, 100});
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// OK.
|
||||
}
|
||||
try {
|
||||
new ZoomSettings(1, null);
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// OK.
|
||||
}
|
||||
try {
|
||||
new ZoomSettings(1, new int[] {});
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// OK.
|
||||
}
|
||||
try {
|
||||
new ZoomSettings(1, new int[] {1, 3, 2});
|
||||
fail("Expected IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// OK.
|
||||
}
|
||||
}
|
||||
|
||||
public void testZoomSettings_minAligned() {
|
||||
ZoomSettings settings = new ZoomSettings(5, new int[] {10, 50, 100});
|
||||
assertEquals(10, settings.calculateInterval(0, 15));
|
||||
assertEquals(10, settings.calculateInterval(0, 50));
|
||||
assertEquals(50, settings.calculateInterval(0, 111));
|
||||
assertEquals(50, settings.calculateInterval(0, 250));
|
||||
assertEquals(100, settings.calculateInterval(0, 251));
|
||||
assertEquals(100, settings.calculateInterval(0, 10000));
|
||||
}
|
||||
|
||||
public void testZoomSettings_minNotAligned() {
|
||||
ZoomSettings settings = new ZoomSettings(5, new int[] {10, 50, 100});
|
||||
assertEquals(50, settings.calculateInterval(5, 55));
|
||||
assertEquals(10, settings.calculateInterval(10, 60));
|
||||
assertEquals(50, settings.calculateInterval(7, 250));
|
||||
assertEquals(100, settings.calculateInterval(7, 257));
|
||||
assertEquals(100, settings.calculateInterval(11, 10000));
|
||||
|
||||
// A regression test.
|
||||
settings = new ZoomSettings(5, new int[] {5, 10, 20});
|
||||
assertEquals(10, settings.calculateInterval(-37.14, -11.89));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.android.apps.mytracks.io.sendtogoogle.SendDialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
|
||||
/**
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class DialogManagerTest
|
||||
extends ActivityInstrumentationTestCase2<MyTracks> {
|
||||
|
||||
public DialogManagerTest() {
|
||||
super(MyTracks.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
MyTracks.clearInstance();
|
||||
assertNull(MyTracks.getInstance());
|
||||
}
|
||||
|
||||
public void test_onCreateChartSettings() {
|
||||
Dialog d =
|
||||
getActivity().onCreateDialog(DialogManager.DIALOG_CHART_SETTINGS, null);
|
||||
assertNotNull(d);
|
||||
assertTrue(d instanceof ChartSettingsDialog);
|
||||
}
|
||||
|
||||
public void test_onCreateImportProgress() {
|
||||
Dialog d =
|
||||
getActivity().onCreateDialog(
|
||||
DialogManager.DIALOG_IMPORT_PROGRESS, null);
|
||||
assertNotNull(d);
|
||||
assertTrue(d instanceof ProgressDialog);
|
||||
}
|
||||
|
||||
public void test_onCreateProgress() {
|
||||
Dialog d =
|
||||
getActivity().onCreateDialog(DialogManager.DIALOG_PROGRESS, null);
|
||||
assertNotNull(d);
|
||||
assertTrue(d instanceof ProgressDialog);
|
||||
ProgressDialog pd = (ProgressDialog) d;
|
||||
assertEquals(100, pd.getMax());
|
||||
assertEquals(10, pd.getProgress());
|
||||
}
|
||||
|
||||
public void test_onCreateSendToGoogle() {
|
||||
Dialog d =
|
||||
getActivity().onCreateDialog(DialogManager.DIALOG_SEND_TO_GOOGLE, null);
|
||||
assertNotNull(d);
|
||||
assertTrue(d instanceof SendDialog);
|
||||
}
|
||||
|
||||
public void test_onCreateWriteProgress() {
|
||||
Dialog d =
|
||||
getActivity().onCreateDialog(
|
||||
DialogManager.DIALOG_WRITE_PROGRESS, null);
|
||||
assertNotNull(d);
|
||||
assertTrue(d instanceof ProgressDialog);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
import com.google.android.maps.MapView;
|
||||
import com.google.android.maps.Projection;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.location.Location;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for the MyTracks map overlay.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class MyTracksOverlayTest extends AndroidTestCase {
|
||||
private Canvas canvas;
|
||||
private MockMyTracksOverlay myTracksOverlay;
|
||||
private MapView mockView;
|
||||
private Projection mockProjection;
|
||||
|
||||
/**
|
||||
* A mock version of {@code MyTracksOverlay} that does not use
|
||||
* {@class MapView}.
|
||||
*/
|
||||
private class MockMyTracksOverlay extends MyTracksOverlay {
|
||||
public MockMyTracksOverlay(Context context) {
|
||||
super(context);
|
||||
}
|
||||
@Override
|
||||
Projection getMapProjection(MapView mapView) {
|
||||
return mockProjection;
|
||||
}
|
||||
@Override
|
||||
Rect getMapViewRect(MapView mapView) {
|
||||
return new Rect(0, 0, 100, 100);
|
||||
}
|
||||
@Override
|
||||
Path newPath() {
|
||||
return new MockPath();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A mock class that intercepts {@code Path}'s and records calls to
|
||||
* {@code #moveTo()} and {@code #lineTo()}.
|
||||
*/
|
||||
private static class MockPath extends Path {
|
||||
/** A list of disjoined path segments. */
|
||||
public final List<List<PointF>> segments = new LinkedList<List<PointF>>();
|
||||
/** The total number of points in this path. */
|
||||
public int totalPoints;
|
||||
|
||||
private List<PointF> currentSegment;
|
||||
|
||||
@Override
|
||||
public void lineTo(float x, float y) {
|
||||
super.lineTo(x, y);
|
||||
assertNotNull(currentSegment);
|
||||
currentSegment.add(new PointF(x, y));
|
||||
totalPoints++;
|
||||
}
|
||||
@Override
|
||||
public void moveTo(float x, float y) {
|
||||
super.moveTo(x, y);
|
||||
segments.add(currentSegment =
|
||||
new ArrayList<PointF>(Arrays.asList(new PointF(x, y))));
|
||||
totalPoints++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A mock {@code Projection} that acts as the identity matrix.
|
||||
*/
|
||||
private static class MockProjection implements Projection {
|
||||
@Override
|
||||
public Point toPixels(GeoPoint in, Point out) {
|
||||
return out;
|
||||
}
|
||||
@Override
|
||||
public float metersToEquatorPixels(float meters) {
|
||||
return meters;
|
||||
}
|
||||
@Override
|
||||
public GeoPoint fromPixels(int x, int y) {
|
||||
return new GeoPoint(y, x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
canvas = new Canvas();
|
||||
myTracksOverlay = new MockMyTracksOverlay(getContext());
|
||||
// Enable drawing.
|
||||
myTracksOverlay.setTrackDrawingEnabled(true);
|
||||
mockView = null;
|
||||
mockProjection = new MockProjection();
|
||||
}
|
||||
|
||||
public void testAddLocation() throws Exception {
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10);
|
||||
location.setLongitude(20);
|
||||
myTracksOverlay.addLocation(location);
|
||||
assertEquals(1, myTracksOverlay.getNumLocations());
|
||||
assertEquals(0, myTracksOverlay.getNumWaypoints());
|
||||
|
||||
location.setLatitude(20);
|
||||
location.setLongitude(30);
|
||||
myTracksOverlay.addLocation(location);
|
||||
assertEquals(2, myTracksOverlay.getNumLocations());
|
||||
assertEquals(0, myTracksOverlay.getNumWaypoints());
|
||||
assertNull(myTracksOverlay.getLastPath());
|
||||
|
||||
// Draw and make sure that we don't lose any point.
|
||||
myTracksOverlay.draw(canvas, mockView, false);
|
||||
assertEquals(2, myTracksOverlay.getNumLocations());
|
||||
assertEquals(0, myTracksOverlay.getNumWaypoints());
|
||||
assertNotNull(myTracksOverlay.getLastPath());
|
||||
assertEquals(2, ((MockPath)myTracksOverlay.getLastPath()).totalPoints);
|
||||
|
||||
myTracksOverlay.draw(canvas, mockView, true);
|
||||
assertEquals(2, myTracksOverlay.getNumLocations());
|
||||
assertEquals(0, myTracksOverlay.getNumWaypoints());
|
||||
assertNotNull(myTracksOverlay.getLastPath());
|
||||
}
|
||||
|
||||
public void testClearPoints() throws Exception {
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10);
|
||||
location.setLongitude(20);
|
||||
myTracksOverlay.addLocation(location);
|
||||
assertEquals(1, myTracksOverlay.getNumLocations());
|
||||
myTracksOverlay.clearPoints();
|
||||
assertEquals(0, myTracksOverlay.getNumLocations());
|
||||
|
||||
// Same after drawing on canvas.
|
||||
final int locations = 100;
|
||||
for (int i = 0; i < locations; ++i) {
|
||||
myTracksOverlay.addLocation(location);
|
||||
}
|
||||
assertEquals(locations, myTracksOverlay.getNumLocations());
|
||||
myTracksOverlay.draw(canvas, mockView, false);
|
||||
myTracksOverlay.draw(canvas, mockView, true);
|
||||
myTracksOverlay.clearPoints();
|
||||
assertEquals(0, myTracksOverlay.getNumLocations());
|
||||
}
|
||||
|
||||
public void testAddWaypoint() throws Exception {
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10);
|
||||
location.setLongitude(20);
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setLocation(location);
|
||||
myTracksOverlay.addWaypoint(waypoint);
|
||||
assertEquals(1, myTracksOverlay.getNumWaypoints());
|
||||
assertEquals(0, myTracksOverlay.getNumLocations());
|
||||
assertNull(myTracksOverlay.getLastPath());
|
||||
|
||||
final int waypoints = 10;
|
||||
for (int i = 0; i < waypoints; ++i) {
|
||||
waypoint = new Waypoint();
|
||||
waypoint.setLocation(location);
|
||||
myTracksOverlay.addWaypoint(waypoint);
|
||||
}
|
||||
assertEquals(1 + waypoints, myTracksOverlay.getNumWaypoints());
|
||||
assertEquals(0, myTracksOverlay.getNumLocations());
|
||||
assertNull(myTracksOverlay.getLastPath());
|
||||
}
|
||||
|
||||
public void testClearWaypoints() throws Exception {
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10);
|
||||
location.setLongitude(20);
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setLocation(location);
|
||||
myTracksOverlay.addWaypoint(waypoint);
|
||||
assertEquals(1, myTracksOverlay.getNumWaypoints());
|
||||
myTracksOverlay.clearWaypoints();
|
||||
assertEquals(0, myTracksOverlay.getNumWaypoints());
|
||||
}
|
||||
|
||||
public void testDrawing() {
|
||||
Location location = new Location("gps");
|
||||
location.setLatitude(10);
|
||||
for (int i = 0; i < 40; ++i) {
|
||||
location.setLongitude(20 + i);
|
||||
Waypoint waypoint = new Waypoint();
|
||||
waypoint.setLocation(location);
|
||||
myTracksOverlay.addWaypoint(waypoint);
|
||||
}
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
location = new Location("gps");
|
||||
location.setLatitude(20 + i / 2);
|
||||
location.setLongitude(150 - i);
|
||||
myTracksOverlay.addLocation(location);
|
||||
}
|
||||
|
||||
// Shadow.
|
||||
myTracksOverlay.draw(canvas, mockView, true);
|
||||
// We don't expect to do anything if
|
||||
assertNull(myTracksOverlay.getLastPath());
|
||||
assertEquals(40, myTracksOverlay.getNumWaypoints());
|
||||
assertEquals(100, myTracksOverlay.getNumLocations());
|
||||
|
||||
// No shadow.
|
||||
myTracksOverlay.draw(canvas, mockView, false);
|
||||
assertNotNull(myTracksOverlay.getLastPath());
|
||||
assertTrue(myTracksOverlay.getLastPath() instanceof MockPath);
|
||||
MockPath path = (MockPath) myTracksOverlay.getLastPath();
|
||||
assertEquals(40, myTracksOverlay.getNumWaypoints());
|
||||
assertEquals(100, myTracksOverlay.getNumLocations());
|
||||
assertEquals(100, path.totalPoints);
|
||||
// TODO: Check the points from the path (and the segments).
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation.ActivityMonitor;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.net.Uri;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.widget.Button;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* A unit test for {@link MyTracks} activity.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class MyTracksTest extends ActivityInstrumentationTestCase2<MyTracks>{
|
||||
public MyTracksTest() {
|
||||
super(MyTracks.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
MyTracks.clearInstance();
|
||||
assertNull(MyTracks.getInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
clearSelectedAndRecordingTracks();
|
||||
waitForIdle();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testInitialization_mainAction() {
|
||||
// Make sure we can start MyTracks and the activity doesn't start recording.
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
assertNotNull(getActivity().getSharedPreferences());
|
||||
|
||||
// Check if not recording.
|
||||
assertFalse(getActivity().isRecording());
|
||||
assertEquals(-1, getActivity().getRecordingTrackId());
|
||||
long selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
}
|
||||
|
||||
public void testInitialization_viewActionWithNoData() {
|
||||
// Simulate start with ACTION_VIEW intent.
|
||||
Intent startIntent = new Intent();
|
||||
startIntent.setAction(Intent.ACTION_VIEW);
|
||||
setActivityIntent(startIntent);
|
||||
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
assertNotNull(getActivity().getSharedPreferences());
|
||||
|
||||
// Check if not recording.
|
||||
assertFalse(getActivity().isRecording());
|
||||
assertEquals(-1, getActivity().getRecordingTrackId());
|
||||
long selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
}
|
||||
|
||||
public void testInitialization_viewActionWithValidData() throws Exception {
|
||||
// Simulate start with ACTION_VIEW intent.
|
||||
Intent startIntent = new Intent();
|
||||
startIntent.setAction(Intent.ACTION_VIEW);
|
||||
Uri uri = Uri.fromFile(File.createTempFile("valid", ".gpx"));
|
||||
|
||||
// TODO: Add a valid GPX.
|
||||
|
||||
startIntent.setData(uri);
|
||||
setActivityIntent(startIntent);
|
||||
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
assertNotNull(getActivity().getSharedPreferences());
|
||||
|
||||
// Check if not recording.
|
||||
assertFalse(getActivity().isRecording());
|
||||
assertEquals(-1, getActivity().getRecordingTrackId());
|
||||
long selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
|
||||
// TODO: Finish this test.
|
||||
}
|
||||
|
||||
public void testInitialization_viewActionWithInvalidData() throws Exception {
|
||||
// Simulate start with ACTION_VIEW intent.
|
||||
Intent startIntent = new Intent();
|
||||
startIntent.setAction(Intent.ACTION_VIEW);
|
||||
Uri uri = Uri.fromFile(File.createTempFile("invalid", ".gpx"));
|
||||
startIntent.setData(uri);
|
||||
setActivityIntent(startIntent);
|
||||
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
assertNotNull(getActivity().getSharedPreferences());
|
||||
|
||||
// Check if not recording.
|
||||
assertFalse(getActivity().isRecording());
|
||||
assertEquals(-1, getActivity().getRecordingTrackId());
|
||||
long selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
|
||||
// TODO: Finish this test.
|
||||
}
|
||||
|
||||
public void testRecording_startAndStop() throws Exception {
|
||||
// Make sure we can start MyTracks and the activity doesn't start recording.
|
||||
assertNotNull(getActivity());
|
||||
assertNotNull(MyTracks.getInstance());
|
||||
assertNotNull(getActivity().getSharedPreferences());
|
||||
|
||||
// Check if not recording.
|
||||
clearSelectedAndRecordingTracks();
|
||||
waitForIdle();
|
||||
|
||||
assertFalse(getActivity().isRecording());
|
||||
assertEquals(-1, getActivity().getRecordingTrackId());
|
||||
long selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
|
||||
// Start a new track.
|
||||
getActivity().startRecording();
|
||||
long recordingTrackId = awaitRecordingStatus(5000, true);
|
||||
assertTrue(recordingTrackId >= 0);
|
||||
|
||||
// Wait until we are done and make sure that selectedTrack = recordingTrack.
|
||||
waitForIdle();
|
||||
assertEquals(recordingTrackId, getActivity().getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.recording_track_key), -1));
|
||||
selectedTrackId = getActivity().getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.selected_track_key), -1);
|
||||
assertEquals(recordingTrackId, selectedTrackId);
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
|
||||
// Watch for MyTracksDetails activity.
|
||||
ActivityMonitor monitor = getInstrumentation().addMonitor(
|
||||
MyTracksDetails.class.getName(), null, false);
|
||||
|
||||
// Now, stop the track and make sure that it is still selected, but
|
||||
// no longer recording.
|
||||
getActivity().stopRecording();
|
||||
|
||||
// Check if we got back MyTracksDetails activity.
|
||||
Activity activity = getInstrumentation().waitForMonitor(monitor);
|
||||
assertTrue(activity instanceof MyTracksDetails);
|
||||
|
||||
// TODO: Update track name and other properties and test if they were
|
||||
// properly saved.
|
||||
|
||||
// Simulate a click on Save button.
|
||||
Button save = (Button) activity.findViewById(R.id.trackdetails_save);
|
||||
save.performClick();
|
||||
|
||||
// Check the remaining properties.
|
||||
recordingTrackId = awaitRecordingStatus(5000, false);
|
||||
assertEquals(-1, recordingTrackId);
|
||||
assertEquals(recordingTrackId, getActivity().getRecordingTrackId());
|
||||
assertEquals(recordingTrackId, getActivity().getSharedPreferences().getLong(
|
||||
getActivity().getString(R.string.recording_track_key), -1));
|
||||
// Make sure this is the same track as the last recording track ID.
|
||||
assertEquals(selectedTrackId, getActivity().getSelectedTrackId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the UI thread becomes idle.
|
||||
*/
|
||||
private void waitForIdle() throws InterruptedException {
|
||||
// Note: We can't use getInstrumentation().waitForIdleSync() here.
|
||||
final Object semaphore = new Object();
|
||||
synchronized (semaphore) {
|
||||
final AtomicBoolean isIdle = new AtomicBoolean();
|
||||
getInstrumentation().waitForIdle(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (semaphore) {
|
||||
isIdle.set(true);
|
||||
semaphore.notify();
|
||||
}
|
||||
}
|
||||
});
|
||||
while (!isIdle.get()) {
|
||||
semaphore.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears {selected,recording}TrackId in the {@link SharedPreferences}.
|
||||
*/
|
||||
private void clearSelectedAndRecordingTracks() {
|
||||
Editor editor = getActivity().getSharedPreferences().edit();
|
||||
editor.putLong(getActivity().getString(R.string.selected_track_key), -1);
|
||||
editor.putLong(getActivity().getString(R.string.recording_track_key), -1);
|
||||
|
||||
editor.clear();
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the recording state changes to the given status.
|
||||
*
|
||||
* @param timeout the maximum time to wait, in milliseconds.
|
||||
* @param isRecording the final status to await.
|
||||
* @return the recording track ID.
|
||||
*/
|
||||
private long awaitRecordingStatus(long timeout, boolean isRecording)
|
||||
throws TimeoutException, InterruptedException {
|
||||
long startTime = System.nanoTime();
|
||||
while (getActivity().isRecording() != isRecording) {
|
||||
if (System.nanoTime() - startTime > timeout * 1000000) {
|
||||
throw new TimeoutException("Timeout while waiting for recording!");
|
||||
}
|
||||
Thread.sleep(20);
|
||||
}
|
||||
waitForIdle();
|
||||
assertEquals(isRecording, getActivity().isRecording());
|
||||
return getActivity().getRecordingTrackId();
|
||||
}
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.MyTracksProviderUtils.LocationFactory;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIterator;
|
||||
import com.google.android.apps.mytracks.services.TrackRecordingServiceTest.MockContext;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.RenamingDelegatingContext;
|
||||
import android.test.mock.MockContentResolver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A unit test for {@link MyTracksProviderUtilsImpl}.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class MyTracksProviderUtilsImplTest extends AndroidTestCase {
|
||||
private Context context;
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
MockContentResolver mockContentResolver = new MockContentResolver();
|
||||
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
|
||||
getContext(), getContext(), "test.");
|
||||
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);
|
||||
}
|
||||
|
||||
public void testLocationIterator_noPoints() {
|
||||
testIterator(1, 0, 1, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
public void testLocationIterator_customFactory() {
|
||||
final Location location = new Location("test_location");
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
testIterator(1, 15, 4, false, new LocationFactory() {
|
||||
@Override
|
||||
public Location createLocation() {
|
||||
counter.incrementAndGet();
|
||||
return location;
|
||||
}
|
||||
});
|
||||
// Make sure we were called exactly as many times as we had track points.
|
||||
assertEquals(15, counter.get());
|
||||
}
|
||||
|
||||
public void testLocationIterator_nullFactory() {
|
||||
try {
|
||||
testIterator(1, 15, 4, false, null);
|
||||
fail("Expecting IllegalArgumentException");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
public void testLocationIterator_noBatchAscending() {
|
||||
testIterator(1, 50, 100, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 50, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
public void testLocationIterator_noBatchDescending() {
|
||||
testIterator(1, 50, 100, true, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 50, true, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
public void testLocationIterator_batchAscending() {
|
||||
testIterator(1, 50, 11, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 25, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
public void testLocationIterator_batchDescending() {
|
||||
testIterator(1, 50, 11, true, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
testIterator(2, 50, 25, true, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
public void testLocationIterator_largeTrack() {
|
||||
testIterator(1, 20000, 2000, false, MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
}
|
||||
|
||||
private List<Location> testIterator(long trackId, int numPoints, int batchSize,
|
||||
boolean descending, LocationFactory locationFactory) {
|
||||
long lastPointId = initializeTrack(trackId, numPoints);
|
||||
((MyTracksProviderUtilsImpl) providerUtils).setDefaultCursorBatchSize(batchSize);
|
||||
List<Location> locations = new ArrayList<Location>(numPoints);
|
||||
LocationIterator it = providerUtils.getLocationIterator(trackId, -1, descending, locationFactory);
|
||||
try {
|
||||
while (it.hasNext()) {
|
||||
Location loc = it.next();
|
||||
assertNotNull(loc);
|
||||
locations.add(loc);
|
||||
// Make sure the IDs are returned in the right order.
|
||||
assertEquals(descending ? lastPointId - locations.size() + 1
|
||||
: lastPointId - numPoints + locations.size(), it.getLocationId());
|
||||
}
|
||||
assertEquals(numPoints, locations.size());
|
||||
} finally {
|
||||
it.close();
|
||||
}
|
||||
return locations;
|
||||
}
|
||||
|
||||
private long initializeTrack(long id, int numPoints) {
|
||||
Track track = new Track();
|
||||
track.setId(id);
|
||||
track.setName("Test: " + id);
|
||||
track.setNumberOfPoints(numPoints);
|
||||
providerUtils.insertTrack(track);
|
||||
track = providerUtils.getTrack(id);
|
||||
assertNotNull(track);
|
||||
|
||||
Location[] locations = new Location[numPoints];
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
Location loc = new Location("test");
|
||||
loc.setLatitude(37.0 + (double) i / 10000.0);
|
||||
loc.setLongitude(57.0 - (double) i / 10000.0);
|
||||
loc.setAccuracy((float) i / 100.0f);
|
||||
loc.setAltitude(i * 2.5);
|
||||
locations[i] = loc;
|
||||
}
|
||||
providerUtils.bulkInsertTrackPoints(locations, numPoints, id);
|
||||
|
||||
// Load all inserted locations.
|
||||
long lastPointId = -1;
|
||||
int counter = 0;
|
||||
LocationIterator it = providerUtils.getLocationIterator(id, -1, false,
|
||||
MyTracksProviderUtils.DEFAULT_LOCATION_FACTORY);
|
||||
try {
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
lastPointId = it.getLocationId();
|
||||
counter++;
|
||||
}
|
||||
} finally {
|
||||
it.close();
|
||||
}
|
||||
|
||||
assertTrue(numPoints == 0 || lastPointId > 0);
|
||||
assertEquals(numPoints, track.getNumberOfPoints());
|
||||
assertEquals(numPoints, counter);
|
||||
|
||||
return lastPointId;
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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_MARKER;
|
||||
Parcel p = Parcel.obtain();
|
||||
original.writeToParcel(p, 0);
|
||||
p.setDataPosition(0);
|
||||
WaypointCreationRequest copy = WaypointCreationRequest.CREATOR.createFromParcel(p);
|
||||
assertEquals(original.getType(), copy.getType());
|
||||
assertNull(copy.getName());
|
||||
assertNull(copy.getDescription());
|
||||
assertNull(copy.getIconUrl());
|
||||
}
|
||||
|
||||
public void testAllAttributesParceling() {
|
||||
WaypointCreationRequest original =
|
||||
new WaypointCreationRequest(WaypointType.MARKER, "name", "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());
|
||||
assertEquals("name", copy.getName());
|
||||
assertEquals("description", copy.getDescription());
|
||||
assertEquals("img.png", copy.getIconUrl());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
/**
|
||||
* Tests for the CSV track exporter.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class CsvTrackWriterTest extends TrackFormatWriterTest {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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.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,
|
||||
SAXException, IOException {
|
||||
testInvalidXML(INVALID_LOCATION_TEST_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid time - track should be deleted.
|
||||
*/
|
||||
public void testImportTimeFailure() throws ParserConfigurationException,
|
||||
SAXException, IOException {
|
||||
testInvalidXML(INVALID_TIME_TEST_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid xml - track should be deleted.
|
||||
*/
|
||||
public void testImportXMLFailure() throws ParserConfigurationException,
|
||||
SAXException, IOException {
|
||||
testInvalidXML(INVALID_XML_TEST_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid altitude - track should be deleted.
|
||||
*/
|
||||
public void testImportInvalidAltitude() throws ParserConfigurationException,
|
||||
SAXException, IOException {
|
||||
testInvalidXML(INVALID_ALTITUDE_TEST_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid latitude - track should be deleted.
|
||||
*/
|
||||
public void testImportInvalidLatitude() throws ParserConfigurationException,
|
||||
SAXException, IOException {
|
||||
testInvalidXML(INVALID_LATITUDE_TEST_GPX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with invalid longitude - track should be deleted.
|
||||
*/
|
||||
public void testImportInvalidLongitude() throws ParserConfigurationException,
|
||||
SAXException, 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,65 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for the GPX track exporter.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class GpxTrackWriterTest extends TrackFormatWriterTest {
|
||||
|
||||
public void testXmlOutput() throws Exception {
|
||||
TrackFormatWriter writer = new GpxTrackWriter();
|
||||
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"));
|
||||
assertEquals(Long.toString(TRACK_ID),
|
||||
getChildTextValue(trackTag, "number"));
|
||||
List<Element> segmentTags = getChildElements(trackTag, "trkseg", 2);
|
||||
List<Element> segPointTags = getChildElements(segmentTags.get(0), "trkpt", 2);
|
||||
assertTagMatchesLocation(segPointTags.get(0),
|
||||
"0", "0", "1970-01-01T02:46:40Z", "0");
|
||||
assertTagMatchesLocation(segPointTags.get(1),
|
||||
"1", "-1", "1970-01-01T02:46:41Z", "5000000");
|
||||
|
||||
segPointTags = getChildElements(segmentTags.get(1), "trkpt", 2);
|
||||
assertTagMatchesLocation(segPointTags.get(0),
|
||||
"2", "-2", "1970-01-01T02:46:42Z", "10000000");
|
||||
assertTagMatchesLocation(segPointTags.get(1),
|
||||
"3", "-3", "1970-01-01T02:46:43Z", "15000000");
|
||||
|
||||
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-01T02:46:41Z", "5000000");
|
||||
|
||||
wptTag = waypointTags.get(1);
|
||||
assertEquals(WAYPOINT2_NAME, getChildTextValue(wptTag, "name"));
|
||||
assertEquals(WAYPOINT2_DESCRIPTION, getChildTextValue(wptTag, "desc"));
|
||||
assertTagMatchesLocation(wptTag,
|
||||
"2", "-2", "1970-01-01T02:46:42Z", "10000000");
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given tag describes the location given by the
|
||||
* Strings lat, lon, time, and ele.
|
||||
*/
|
||||
private void assertTagMatchesLocation(Element tag, String lat,
|
||||
String lon, String time, String ele) {
|
||||
assertEquals(lat, tag.getAttribute("lat"));
|
||||
assertEquals(lon, tag.getAttribute("lon"));
|
||||
assertEquals(time, getChildTextValue(tag, "time"));
|
||||
assertEquals(ele, getChildTextValue(tag, "ele"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
/**
|
||||
* Tests for the KML track exporter.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class KmlTrackWriterTest extends TrackFormatWriterTest {
|
||||
private static final String FULL_TRACK_DESCRIPTION = "full track description";
|
||||
|
||||
/**
|
||||
* A fake version of {@link StringUtils} which returns a fixed track
|
||||
* description, thus not depending on the context.
|
||||
*/
|
||||
private class FakeStringUtils extends StringUtils {
|
||||
public FakeStringUtils() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTrackDescription(Track track,
|
||||
Vector<Double> distances, Vector<Double> elevations) {
|
||||
assertSame(KmlTrackWriterTest.super.track, track);
|
||||
assertTrue(distances.isEmpty());
|
||||
assertTrue(elevations.isEmpty());
|
||||
return FULL_TRACK_DESCRIPTION;
|
||||
}
|
||||
}
|
||||
|
||||
public void testXmlOutput() throws Exception {
|
||||
KmlTrackWriter writer = new KmlTrackWriter(new FakeStringUtils());
|
||||
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 5 placemarks - start, segments, end, waypoint 1, waypoint 2
|
||||
List<Element> placemarkTags = getChildElements(docTag, "Placemark", 5);
|
||||
assertTagIsPlacemark(placemarkTags.get(0),
|
||||
"(Start)", TRACK_DESCRIPTION, location1);
|
||||
assertTagIsPlacemark(placemarkTags.get(2),
|
||||
"(End)", FULL_TRACK_DESCRIPTION, location4);
|
||||
assertTagIsPlacemark(placemarkTags.get(3),
|
||||
WAYPOINT1_NAME, WAYPOINT1_DESCRIPTION, location2);
|
||||
assertTagIsPlacemark(placemarkTags.get(4),
|
||||
WAYPOINT2_NAME, WAYPOINT2_DESCRIPTION, location3);
|
||||
|
||||
Element trackPlacemarkTag = placemarkTags.get(1);
|
||||
assertEquals(TRACK_NAME, getChildTextValue(trackPlacemarkTag, "name"));
|
||||
assertEquals(TRACK_DESCRIPTION,
|
||||
getChildTextValue(trackPlacemarkTag, "description"));
|
||||
Element geometryTag = getChildElement(trackPlacemarkTag, "MultiGeometry");
|
||||
List<Element> segmentTags = getChildElements(geometryTag, "LineString", 2);
|
||||
assertTagHasPoints(segmentTags.get(0), location1, location2);
|
||||
assertTagHasPoints(segmentTags.get(1), location3, location4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given XML tag is a placemark with the given properties.
|
||||
*
|
||||
* @param tag the tag to analyze
|
||||
* @param name the expected name for the placemark
|
||||
* @param description the expected description for the placemark
|
||||
* @param location the expected location of the placemark
|
||||
*/
|
||||
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 expectedCoords =
|
||||
location.getLongitude() + "," + location.getLatitude();
|
||||
String actualCoords = getChildTextValue(pointTag, "coordinates");
|
||||
assertEquals(expectedCoords, actualCoords);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given tag has a "coordinates" subtag with the given
|
||||
* locations.
|
||||
*
|
||||
* @param tag the tag to analyze
|
||||
* @param locs the locations to expect in the coordinates
|
||||
*/
|
||||
private void assertTagHasPoints(Element tag, Location... locs) {
|
||||
StringBuilder expectedBuilder = new StringBuilder();
|
||||
for (Location loc : locs) {
|
||||
expectedBuilder.append(loc.getLongitude());
|
||||
expectedBuilder.append(',');
|
||||
expectedBuilder.append(loc.getLatitude());
|
||||
expectedBuilder.append(',');
|
||||
expectedBuilder.append(loc.getAltitude());
|
||||
expectedBuilder.append(' ');
|
||||
}
|
||||
String expectedCoordinates = expectedBuilder.toString().trim();
|
||||
String actualCoordinates = getChildTextValue(tag, "coordinates").trim();
|
||||
assertEquals(expectedCoordinates, actualCoordinates);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksLocation;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for the GPX track exporter.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class TcxTrackWriterTest extends TrackFormatWriterTest {
|
||||
|
||||
public void testXmlOutput() throws Exception {
|
||||
TrackFormatWriter writer = new TcxTrackWriter();
|
||||
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 points, in the same order.
|
||||
*/
|
||||
private void assertTagsMatchPoints(List<Element> tags, MyTracksLocation... locs) {
|
||||
assertEquals(locs.length, tags.size());
|
||||
for (int i = 0; i < locs.length; i++) {
|
||||
Element tag = tags.get(i);
|
||||
MyTracksLocation loc = locs[i];
|
||||
|
||||
assertTagMatchesLocation(tag, loc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the given tag describes the given location.
|
||||
*/
|
||||
private void assertTagMatchesLocation(Element tag, MyTracksLocation loc) {
|
||||
Element posTag = getChildElement(tag, "Position");
|
||||
assertEquals(Double.toString(loc.getLatitude()),
|
||||
getChildTextValue(posTag, "LatitudeDegrees"));
|
||||
assertEquals(Double.toString(loc.getLongitude()),
|
||||
getChildTextValue(posTag, "LongitudeDegrees"));
|
||||
assertEquals(
|
||||
TcxTrackWriter.TIMESTAMP_FORMAT.format(new Date(loc.getTime())),
|
||||
getChildTextValue(tag, "Time"));
|
||||
assertEquals(Double.toString(loc.getAltitude()),
|
||||
getChildTextValue(tag, "AltitudeMeters"));
|
||||
assertTrue(loc.getSensorDataSet() != null);
|
||||
Sensor.SensorDataSet sds = loc.getSensorDataSet();
|
||||
|
||||
List<Element> bpm = getChildElements(tag, "HeartRateBpm", 1);
|
||||
assertEquals(Integer.toString(sds.getHeartRate().getValue()),
|
||||
getChildTextValue(bpm.get(0), "Value"));
|
||||
|
||||
List<Element> ext = getChildElements(tag, "Extensions", 1);
|
||||
List<Element> tpx = getChildElements(ext.get(0), "TPX", 1);
|
||||
assertEquals(Integer.toString(sds.getPower().getValue()),
|
||||
getChildTextValue(tpx.get(0), "Watts"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static com.google.android.testing.mocking.AndroidMock.expect;
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class TempFileCleanerTest extends AndroidTestCase {
|
||||
|
||||
@UsesMocks({
|
||||
File.class,
|
||||
})
|
||||
|
||||
public void test_noDir() {
|
||||
File dir = AndroidMock.createMock(File.class, "/no_file");
|
||||
TempFileCleaner cleaner = new TempFileCleaner(0);
|
||||
expect(dir.exists()).andStubReturn(false);
|
||||
AndroidMock.replay(dir);
|
||||
assertEquals(0, cleaner.cleanTmpDirectory(dir));
|
||||
AndroidMock.verify(dir);
|
||||
}
|
||||
|
||||
public void test_emptyDir() {
|
||||
File dir = AndroidMock.createMock(File.class, "/no_file");
|
||||
TempFileCleaner cleaner = new TempFileCleaner(0);
|
||||
expect(dir.exists()).andStubReturn(true);
|
||||
expect(dir.listFiles()).andStubReturn(new File[0]);
|
||||
AndroidMock.replay(dir);
|
||||
assertEquals(0, cleaner.cleanTmpDirectory(dir));
|
||||
AndroidMock.verify(dir);
|
||||
}
|
||||
|
||||
public void test_newFile() {
|
||||
File dir = AndroidMock.createMock(File.class, "/no_file");
|
||||
long now = 100000000;
|
||||
TempFileCleaner cleaner = new TempFileCleaner(now);
|
||||
expect(dir.exists()).andStubReturn(true);
|
||||
File file = AndroidMock.createMock(File.class, "/no_file/foo");
|
||||
expect(file.lastModified()).andStubReturn(now);
|
||||
File[] list = { file };
|
||||
expect(dir.listFiles()).andStubReturn(list);
|
||||
|
||||
AndroidMock.replay(dir, file);
|
||||
assertEquals(0, cleaner.cleanTmpDirectory(dir));
|
||||
AndroidMock.verify(dir, file);
|
||||
}
|
||||
|
||||
public void test_oldFile() {
|
||||
File dir = AndroidMock.createMock(File.class, "/no_file");
|
||||
long now = 100000000;
|
||||
TempFileCleaner cleaner = new TempFileCleaner(now);
|
||||
expect(dir.exists()).andStubReturn(true);
|
||||
File file = AndroidMock.createMock(File.class, "/no_file/foo");
|
||||
expect(file.lastModified()).andStubReturn(now - 3600001);
|
||||
expect(file.delete()).andStubReturn(true);
|
||||
File[] list = { file };
|
||||
expect(dir.listFiles()).andStubReturn(list);
|
||||
|
||||
AndroidMock.replay(dir, file);
|
||||
assertEquals(1, cleaner.cleanTmpDirectory(dir));
|
||||
AndroidMock.verify(dir, file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
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 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;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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_DESCRIPTION = "The long ]]> journey home";
|
||||
protected static final String WAYPOINT1_NAME = "point]]>1";
|
||||
protected static final String WAYPOINT1_DESCRIPTION = "point 1]]>description";
|
||||
protected static final String WAYPOINT2_NAME = "point]]>2";
|
||||
protected static final String WAYPOINT2_DESCRIPTION = "point 2]]>description";
|
||||
private static final int BUFFER_SIZE = 10240;
|
||||
protected static final long TRACK_ID = 12345L;
|
||||
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.setId(TRACK_ID);
|
||||
track.setName(TRACK_NAME);
|
||||
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.setDescription(WAYPOINT1_DESCRIPTION);
|
||||
wp2.setLocation(location3);
|
||||
wp2.setName(WAYPOINT2_NAME);
|
||||
wp2.setDescription(WAYPOINT2_DESCRIPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the given locations with coordinates and time.
|
||||
*/
|
||||
protected void populateLocations(MyTracksLocation... locs) {
|
||||
for (int i = 0; i < locs.length; i++) {
|
||||
MyTracksLocation loc = locs[i];
|
||||
loc.setAltitude(i * 5000000);
|
||||
loc.setLatitude(i);
|
||||
loc.setLongitude(-i);
|
||||
loc.setTime(10000000 + i * 1000);
|
||||
Sensor.SensorData.Builder hr = Sensor.SensorData.newBuilder()
|
||||
.setValue(100 + i)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorData.Builder power = Sensor.SensorData.newBuilder()
|
||||
.setValue(400 + i)
|
||||
.setState(Sensor.SensorState.SENDING);
|
||||
Sensor.SensorDataSet sds =
|
||||
Sensor.SensorDataSet.newBuilder().setHeartRate(hr.build())
|
||||
.setPower(power)
|
||||
.build();
|
||||
loc.setSensorData(sds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
OutputStream output = new ByteArrayOutputStream(BUFFER_SIZE);
|
||||
writer.prepare(track, output);
|
||||
writer.writeHeader();
|
||||
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.writeWaypoint(wp1);
|
||||
writer.writeWaypoint(wp2);
|
||||
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,366 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
|
||||
package com.google.android.apps.mytracks.io;
|
||||
|
||||
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.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
|
||||
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.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 TrackWriter} subclass which mocks out methods called from
|
||||
* {@link TrackWriter#openFile}.
|
||||
*/
|
||||
private static final class OpenFileTrackWriter extends TrackWriter {
|
||||
private final ByteArrayOutputStream stream;
|
||||
private final boolean canWrite;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param stream the stream to return from
|
||||
* {@link TrackWriter#newOutputStream}, or null to throw a
|
||||
* {@link FileNotFoundException}
|
||||
* @param canWrite the value that {@link TrackWriter#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;
|
||||
}
|
||||
|
||||
@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 TrackWriter} subclass which mocks out methods called from
|
||||
* {@link TrackWriter#writeTrack}.
|
||||
*/
|
||||
private final class WriteTracksTrackWriter extends TrackWriter {
|
||||
private final boolean openResult;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param openResult the return value for {@link TrackWriter#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 TrackWriter 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);
|
||||
|
||||
// Expect the completion callback to be run
|
||||
Runnable completionCallback = mocksControl.createMock(Runnable.class);
|
||||
completionCallback.run();
|
||||
|
||||
mocksControl.replay();
|
||||
writer.setOnCompletion(completionCallback);
|
||||
writer.writeTrack();
|
||||
|
||||
assertEquals(1, writeDocumentCalls);
|
||||
assertEquals(1, openFileCalls);
|
||||
mocksControl.verify();
|
||||
}
|
||||
|
||||
public void testWriteTrack_openFails() {
|
||||
writer = new WriteTracksTrackWriter(getContext(), providerUtils, track,
|
||||
formatWriter, false);
|
||||
|
||||
// Expect the completion callback to be run
|
||||
Runnable completionCallback = mocksControl.createMock(Runnable.class);
|
||||
completionCallback.run();
|
||||
|
||||
mocksControl.replay();
|
||||
writer.setOnCompletion(completionCallback);
|
||||
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() {
|
||||
writer = new TrackWriter(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() {
|
||||
writer = new TrackWriter(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.writeWaypoint(wptEq(wps[1]));
|
||||
formatWriter.writeWaypoint(wptEq(wps[2]));
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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.backup;
|
||||
|
||||
import com.google.android.apps.mytracks.content.ContentTypeIds;
|
||||
|
||||
import android.database.MatrixCursor;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link DatabaseDumper}.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class DatabaseDumperTest extends TestCase {
|
||||
/**
|
||||
* This class is the same as {@link MatrixCursor}, except this class
|
||||
* implements {@link #getBlob} ({@link MatrixCursor} leaves it
|
||||
* unimplemented).
|
||||
*/
|
||||
private class BlobAwareMatrixCursor extends MatrixCursor {
|
||||
public BlobAwareMatrixCursor(String[] columnNames) {
|
||||
super(columnNames);
|
||||
}
|
||||
|
||||
@Override public byte[] getBlob(int columnIndex) {
|
||||
return getString(columnIndex).getBytes();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String[] COLUMN_NAMES = {
|
||||
"intCol", "longCol", "floatCol", "doubleCol", "stringCol", "boolCol", "blobCol"
|
||||
};
|
||||
private static final byte[] COLUMN_TYPES = {
|
||||
ContentTypeIds.INT_TYPE_ID, ContentTypeIds.LONG_TYPE_ID,
|
||||
ContentTypeIds.FLOAT_TYPE_ID, ContentTypeIds.DOUBLE_TYPE_ID,
|
||||
ContentTypeIds.STRING_TYPE_ID, ContentTypeIds.BOOLEAN_TYPE_ID,
|
||||
ContentTypeIds.BLOB_TYPE_ID
|
||||
};
|
||||
private static final String[][] FAKE_DATA = {
|
||||
{ "42", "123456789", "3.1415", "2.72", "lolcat", "1", "blob" },
|
||||
{ null, "123456789", "3.1415", "2.72", "lolcat", "1", "blob" },
|
||||
{ "42", null, "3.1415", "2.72", "lolcat", "1", "blob" },
|
||||
{ "42", "123456789", null, "2.72", "lolcat", "1", "blob" },
|
||||
{ "42", "123456789", "3.1415", null, "lolcat", "1", "blob" },
|
||||
{ "42", "123456789", "3.1415", "2.72", null, "1", "blob" },
|
||||
{ "42", "123456789", "3.1415", "2.72", "lolcat", null, "blob" },
|
||||
{ "42", "123456789", "3.1415", "2.72", "lolcat", "1", null },
|
||||
};
|
||||
private static final long[] EXPECTED_FIELD_SETS = {
|
||||
0x7F, 0x7E, 0x7D, 0x7B, 0x77, 0x6F, 0x5F, 0x3F
|
||||
};
|
||||
|
||||
private BlobAwareMatrixCursor cursor;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// Add fake data to the cursor
|
||||
cursor = new BlobAwareMatrixCursor(COLUMN_NAMES);
|
||||
for (String[] row : FAKE_DATA) {
|
||||
cursor.addRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
public void testWriteAllRows_noNulls() throws Exception {
|
||||
testWriteAllRows(false);
|
||||
}
|
||||
|
||||
public void testWriteAllRows_withNulls() throws Exception {
|
||||
testWriteAllRows(true);
|
||||
}
|
||||
|
||||
private void testWriteAllRows(boolean hasNullFields) throws Exception {
|
||||
// Dump it
|
||||
DatabaseDumper dumper = new DatabaseDumper(COLUMN_NAMES, COLUMN_TYPES, hasNullFields);
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);
|
||||
DataOutputStream writer = new DataOutputStream(outStream );
|
||||
dumper.writeAllRows(cursor, writer);
|
||||
|
||||
// Read the results
|
||||
byte[] result = outStream.toByteArray();
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(result);
|
||||
DataInputStream reader = new DataInputStream(inputStream);
|
||||
|
||||
// Verify the header
|
||||
assertHeader(reader);
|
||||
|
||||
// Verify the number of rows
|
||||
assertEquals(FAKE_DATA.length, reader.readInt());
|
||||
|
||||
// Verify the row contents
|
||||
byte[] blob = new byte[4];
|
||||
|
||||
// Row 0 -- everything populated
|
||||
assertEquals(EXPECTED_FIELD_SETS[0], reader.readLong());
|
||||
assertEquals(42, reader.readInt());
|
||||
assertEquals(123456789L, reader.readLong());
|
||||
assertEquals(3.1415f, reader.readFloat());
|
||||
assertEquals(2.72, reader.readDouble());
|
||||
assertEquals("lolcat", reader.readUTF());
|
||||
assertTrue(reader.readBoolean());
|
||||
assertEquals(4, reader.readInt());
|
||||
assertEquals("blob", readBlob(reader, 4));
|
||||
|
||||
// Row 1 -- no int
|
||||
assertEquals(EXPECTED_FIELD_SETS[1], reader.readLong());
|
||||
if (hasNullFields) reader.readInt();
|
||||
assertEquals(123456789L, reader.readLong());
|
||||
assertEquals(3.1415f, reader.readFloat());
|
||||
assertEquals(2.72, reader.readDouble());
|
||||
assertEquals("lolcat", reader.readUTF());
|
||||
assertTrue(reader.readBoolean());
|
||||
assertEquals(4, reader.readInt());
|
||||
assertEquals("blob", readBlob(reader, 4));
|
||||
|
||||
// Row 2 -- no long
|
||||
assertEquals(EXPECTED_FIELD_SETS[2], reader.readLong());
|
||||
assertEquals(42, reader.readInt());
|
||||
if (hasNullFields) reader.readLong();
|
||||
assertEquals(3.1415f, reader.readFloat());
|
||||
assertEquals(2.72, reader.readDouble());
|
||||
assertEquals("lolcat", reader.readUTF());
|
||||
assertTrue(reader.readBoolean());
|
||||
assertEquals(4, reader.readInt());
|
||||
assertEquals("blob", readBlob(reader, 4));
|
||||
|
||||
// Row 3 -- no float
|
||||
assertEquals(EXPECTED_FIELD_SETS[3], reader.readLong());
|
||||
assertEquals(42, reader.readInt());
|
||||
assertEquals(123456789L, reader.readLong());
|
||||
if (hasNullFields) reader.readFloat();
|
||||
assertEquals(2.72, reader.readDouble());
|
||||
assertEquals("lolcat", reader.readUTF());
|
||||
assertTrue(reader.readBoolean());
|
||||
assertEquals(4, reader.readInt());
|
||||
assertEquals("blob", readBlob(reader, 4));
|
||||
|
||||
// Row 4 -- no double
|
||||
assertEquals(EXPECTED_FIELD_SETS[4], reader.readLong());
|
||||
assertEquals(42, reader.readInt());
|
||||
assertEquals(123456789L, reader.readLong());
|
||||
assertEquals(3.1415f, reader.readFloat());
|
||||
if (hasNullFields) reader.readDouble();
|
||||
assertEquals("lolcat", reader.readUTF());
|
||||
assertTrue(reader.readBoolean());
|
||||
assertEquals(4, reader.readInt());
|
||||
assertEquals("blob", readBlob(reader, 4));
|
||||
|
||||
// Row 5 -- no string
|
||||
assertEquals(EXPECTED_FIELD_SETS[5], reader.readLong());
|
||||
assertEquals(42, reader.readInt());
|
||||
assertEquals(123456789L, reader.readLong());
|
||||
assertEquals(3.1415f, reader.readFloat());
|
||||
assertEquals(2.72, reader.readDouble());
|
||||
if (hasNullFields) reader.readUTF();
|
||||
assertTrue(reader.readBoolean());
|
||||
assertEquals(4, reader.readInt());
|
||||
assertEquals("blob", readBlob(reader, 4));
|
||||
|
||||
// Row 6 -- no boolean
|
||||
assertEquals(EXPECTED_FIELD_SETS[6], reader.readLong());
|
||||
assertEquals(42, reader.readInt());
|
||||
assertEquals(123456789L, reader.readLong());
|
||||
assertEquals(3.1415f, reader.readFloat());
|
||||
assertEquals(2.72, reader.readDouble());
|
||||
assertEquals("lolcat", reader.readUTF());
|
||||
if (hasNullFields) reader.readBoolean();
|
||||
assertEquals(4, reader.readInt());
|
||||
assertEquals("blob", readBlob(reader, 4));
|
||||
|
||||
// Row 7 -- no blob
|
||||
assertEquals(EXPECTED_FIELD_SETS[7], reader.readLong());
|
||||
assertEquals(42, reader.readInt());
|
||||
assertEquals(123456789L, reader.readLong());
|
||||
assertEquals(3.1415f, reader.readFloat());
|
||||
assertEquals(2.72, reader.readDouble());
|
||||
assertEquals("lolcat", reader.readUTF());
|
||||
assertTrue(reader.readBoolean());
|
||||
if (hasNullFields) {
|
||||
int length = reader.readInt();
|
||||
readBlob(reader, length);
|
||||
}
|
||||
}
|
||||
|
||||
public void testFewerRows() throws Exception {
|
||||
// Dump only the first two rows
|
||||
DatabaseDumper dumper = new DatabaseDumper(COLUMN_NAMES, COLUMN_TYPES, false);
|
||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024);
|
||||
DataOutputStream writer = new DataOutputStream(outStream);
|
||||
|
||||
dumper.writeHeaders(cursor, 2, writer);
|
||||
cursor.moveToFirst();
|
||||
dumper.writeOneRow(cursor, writer);
|
||||
cursor.moveToNext();
|
||||
dumper.writeOneRow(cursor, writer);
|
||||
|
||||
// Read the results
|
||||
byte[] result = outStream.toByteArray();
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(result);
|
||||
DataInputStream reader = new DataInputStream(inputStream);
|
||||
|
||||
// Verify the header
|
||||
assertHeader(reader);
|
||||
|
||||
// Verify the number of rows
|
||||
assertEquals(2, reader.readInt());
|
||||
|
||||
// Row 0
|
||||
assertEquals(EXPECTED_FIELD_SETS[0], reader.readLong());
|
||||
assertEquals(42, reader.readInt());
|
||||
assertEquals(123456789L, reader.readLong());
|
||||
assertEquals(3.1415f, reader.readFloat());
|
||||
assertEquals(2.72, reader.readDouble());
|
||||
assertEquals("lolcat", reader.readUTF());
|
||||
assertTrue(reader.readBoolean());
|
||||
assertEquals(4, reader.readInt());
|
||||
assertEquals("blob", readBlob(reader, 4));
|
||||
|
||||
// Row 1
|
||||
assertEquals(EXPECTED_FIELD_SETS[1], reader.readLong());
|
||||
// Null field not read
|
||||
assertEquals(123456789L, reader.readLong());
|
||||
assertEquals(3.1415f, reader.readFloat());
|
||||
assertEquals(2.72, reader.readDouble());
|
||||
assertEquals("lolcat", reader.readUTF());
|
||||
assertTrue(reader.readBoolean());
|
||||
assertEquals(4, reader.readInt());
|
||||
assertEquals("blob", readBlob(reader, 4));
|
||||
}
|
||||
|
||||
private void assertHeader(DataInputStream reader) throws IOException {
|
||||
assertEquals(COLUMN_NAMES.length, reader.readInt());
|
||||
for (int i = 0; i < COLUMN_NAMES.length; i++) {
|
||||
assertEquals(COLUMN_NAMES[i], reader.readUTF());
|
||||
assertEquals(COLUMN_TYPES[i], reader.readByte());
|
||||
}
|
||||
}
|
||||
|
||||
private String readBlob(InputStream reader, int length) throws Exception {
|
||||
if (length == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
byte[] blob = new byte[length];
|
||||
assertEquals(length, reader.read(blob));
|
||||
return new String(blob);
|
||||
}
|
||||
}
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 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.backup;
|
||||
|
||||
import com.google.android.apps.mytracks.content.ContentTypeIds;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link DatabaseImporter}.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class DatabaseImporterTest extends TestCase {
|
||||
|
||||
private static final Uri DESTINATION_URI = Uri.parse("http://www.google.com/");
|
||||
private static final int TEST_BULK_SIZE = 10;
|
||||
private ArrayList<ContentValues> insertedValues;
|
||||
|
||||
private class TestableDatabaseImporter extends DatabaseImporter {
|
||||
public TestableDatabaseImporter(boolean readNullFields) {
|
||||
super(DESTINATION_URI, null, readNullFields, TEST_BULK_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doBulkInsert(ContentValues[] values) {
|
||||
insertedValues.ensureCapacity(insertedValues.size() + values.length);
|
||||
|
||||
// We need to make a copy of the values since the objects are re-used
|
||||
for (ContentValues contentValues : values) {
|
||||
insertedValues.add(new ContentValues(contentValues));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
insertedValues = new ArrayList<ContentValues>();
|
||||
}
|
||||
|
||||
public void testImportAllRows() throws Exception {
|
||||
testImportAllRows(false);
|
||||
}
|
||||
|
||||
public void testImportAllRows_readNullFields() throws Exception {
|
||||
testImportAllRows(true);
|
||||
}
|
||||
|
||||
private void testImportAllRows(boolean readNullFields) throws Exception {
|
||||
// Create a fake data stream to be read
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
|
||||
DataOutputStream writer = new DataOutputStream(outputStream);
|
||||
|
||||
writeFullHeader(writer);
|
||||
|
||||
// Add the number of rows
|
||||
writer.writeInt(2);
|
||||
|
||||
// Add a row with all fields present
|
||||
writer.writeLong(0x7F);
|
||||
writer.writeInt(42);
|
||||
writer.writeBoolean(true);
|
||||
writer.writeUTF("lolcat");
|
||||
writer.writeFloat(3.1415f);
|
||||
writer.writeDouble(2.72);
|
||||
writer.writeLong(123456789L);
|
||||
writer.writeInt(4);
|
||||
writer.writeBytes("blob");
|
||||
|
||||
// Add a row with some missing fields
|
||||
writer.writeLong(0x15);
|
||||
writer.writeInt(42);
|
||||
if (readNullFields) writer.writeBoolean(false);
|
||||
writer.writeUTF("lolcat");
|
||||
if (readNullFields) writer.writeFloat(0.0f);
|
||||
writer.writeDouble(2.72);
|
||||
if (readNullFields) writer.writeLong(0L);
|
||||
if (readNullFields) writer.writeInt(0); // empty blob
|
||||
|
||||
writer.flush();
|
||||
|
||||
// Do the importing
|
||||
DatabaseImporter importer = new TestableDatabaseImporter(readNullFields);
|
||||
byte[] dataBytes = outputStream.toByteArray();
|
||||
importer.importAllRows(new DataInputStream(new ByteArrayInputStream(dataBytes)));
|
||||
|
||||
assertEquals(2, insertedValues.size());
|
||||
|
||||
// Verify the first row
|
||||
ContentValues value = insertedValues.get(0);
|
||||
assertEquals(value.toString(), 7, value.size());
|
||||
|
||||
assertValue(42, "col1", value);
|
||||
assertValue(true, "col2", value);
|
||||
assertValue("lolcat", "col3", value);
|
||||
assertValue(3.1415f, "col4", value);
|
||||
assertValue(2.72, "col5", value);
|
||||
assertValue(123456789L, "col6", value);
|
||||
assertBlobValue("blob", "col7", value);
|
||||
|
||||
// Verify the second row
|
||||
value = insertedValues.get(1);
|
||||
assertEquals(value.toString(), 3, value.size());
|
||||
|
||||
assertValue(42, "col1", value);
|
||||
assertValue("lolcat", "col3", value);
|
||||
assertValue(2.72, "col5", value);
|
||||
}
|
||||
|
||||
public void testImportAllRows_noRows() throws Exception {
|
||||
// Create a fake data stream to be read
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
|
||||
DataOutputStream writer = new DataOutputStream(outputStream);
|
||||
|
||||
writeFullHeader(writer);
|
||||
|
||||
// Add the number of rows
|
||||
writer.writeInt(0);
|
||||
|
||||
writer.flush();
|
||||
|
||||
// Do the importing
|
||||
DatabaseImporter importer = new TestableDatabaseImporter(false);
|
||||
byte[] dataBytes = outputStream.toByteArray();
|
||||
importer.importAllRows(new DataInputStream(new ByteArrayInputStream(dataBytes)));
|
||||
|
||||
assertTrue(insertedValues.isEmpty());
|
||||
}
|
||||
|
||||
public void testImportAllRows_emptyRows() throws Exception {
|
||||
testImportAllRowsWithEmptyRows(false);
|
||||
}
|
||||
|
||||
public void testImportAllRows_emptyRowsWithNulls() throws Exception {
|
||||
testImportAllRowsWithEmptyRows(true);
|
||||
}
|
||||
|
||||
private void testImportAllRowsWithEmptyRows(boolean readNullFields) throws Exception {
|
||||
// Create a fake data stream to be read
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
|
||||
DataOutputStream writer = new DataOutputStream(outputStream);
|
||||
|
||||
writeFullHeader(writer);
|
||||
|
||||
// Add the number of rows
|
||||
writer.writeInt(3);
|
||||
|
||||
// Add 2 rows with no fields
|
||||
for (int i = 0; i < 2; i++) {
|
||||
writer.writeLong(0);
|
||||
if (readNullFields) {
|
||||
writer.writeInt(0);
|
||||
writer.writeBoolean(false);
|
||||
writer.writeUTF("");
|
||||
writer.writeFloat(0.0f);
|
||||
writer.writeDouble(0.0);
|
||||
writer.writeLong(0L);
|
||||
writer.writeInt(0); // empty blob
|
||||
}
|
||||
}
|
||||
|
||||
// Add a row with some missing fields
|
||||
writer.writeLong(0x15);
|
||||
writer.writeInt(42);
|
||||
if (readNullFields) writer.writeBoolean(false);
|
||||
writer.writeUTF("lolcat");
|
||||
if (readNullFields) writer.writeFloat(0.0f);
|
||||
writer.writeDouble(2.72);
|
||||
if (readNullFields) writer.writeLong(0L);
|
||||
if (readNullFields) writer.writeInt(0); // empty blob
|
||||
|
||||
writer.flush();
|
||||
|
||||
// Do the importing
|
||||
DatabaseImporter importer = new TestableDatabaseImporter(readNullFields);
|
||||
byte[] dataBytes = outputStream.toByteArray();
|
||||
importer.importAllRows(new DataInputStream(new ByteArrayInputStream(dataBytes)));
|
||||
|
||||
assertEquals(insertedValues.toString(), 3, insertedValues.size());
|
||||
|
||||
ContentValues value = insertedValues.get(0);
|
||||
assertEquals(value.toString(), 0, value.size());
|
||||
|
||||
value = insertedValues.get(1);
|
||||
assertEquals(value.toString(), 0, value.size());
|
||||
|
||||
// Verify the third row (only one with values)
|
||||
value = insertedValues.get(2);
|
||||
assertEquals(value.toString(), 3, value.size());
|
||||
|
||||
assertFalse(value.containsKey("col2"));
|
||||
assertFalse(value.containsKey("col4"));
|
||||
assertFalse(value.containsKey("col6"));
|
||||
|
||||
assertValue(42, "col1", value);
|
||||
assertValue("lolcat", "col3", value);
|
||||
assertValue(2.72, "col5", value);
|
||||
}
|
||||
|
||||
public void testImportAllRows_bulks() throws Exception {
|
||||
// Create a fake data stream to be read
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
|
||||
DataOutputStream writer = new DataOutputStream(outputStream);
|
||||
|
||||
// Add the header
|
||||
writer.writeInt(2);
|
||||
writer.writeUTF("col1");
|
||||
writer.writeByte(ContentTypeIds.INT_TYPE_ID);
|
||||
writer.writeUTF("col2");
|
||||
writer.writeByte(ContentTypeIds.STRING_TYPE_ID);
|
||||
|
||||
// Add lots of rows (so the insertions are split in multiple bulks)
|
||||
int numRows = TEST_BULK_SIZE * 5 / 2;
|
||||
writer.writeInt(numRows);
|
||||
for (int i = 0; i < numRows; i++) {
|
||||
writer.writeLong(3);
|
||||
writer.writeInt(i);
|
||||
writer.writeUTF(Integer.toString(i * 2));
|
||||
}
|
||||
|
||||
writer.flush();
|
||||
|
||||
// Do the importing
|
||||
DatabaseImporter importer = new TestableDatabaseImporter(false);
|
||||
byte[] dataBytes = outputStream.toByteArray();
|
||||
importer.importAllRows(new DataInputStream(new ByteArrayInputStream(dataBytes)));
|
||||
|
||||
// Verify the rows
|
||||
assertEquals(numRows, insertedValues.size());
|
||||
for (int i = 0; i < numRows; i++) {
|
||||
ContentValues value = insertedValues.get(i);
|
||||
assertEquals(value.toString(), 2, value.size());
|
||||
assertValue(i, "col1", value);
|
||||
assertValue(Integer.toString(i * 2), "col2", value);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFullHeader(DataOutputStream writer) throws IOException {
|
||||
// Add the header
|
||||
writer.writeInt(7);
|
||||
writer.writeUTF("col1");
|
||||
writer.writeByte(ContentTypeIds.INT_TYPE_ID);
|
||||
writer.writeUTF("col2");
|
||||
writer.writeByte(ContentTypeIds.BOOLEAN_TYPE_ID);
|
||||
writer.writeUTF("col3");
|
||||
writer.writeByte(ContentTypeIds.STRING_TYPE_ID);
|
||||
writer.writeUTF("col4");
|
||||
writer.writeByte(ContentTypeIds.FLOAT_TYPE_ID);
|
||||
writer.writeUTF("col5");
|
||||
writer.writeByte(ContentTypeIds.DOUBLE_TYPE_ID);
|
||||
writer.writeUTF("col6");
|
||||
writer.writeByte(ContentTypeIds.LONG_TYPE_ID);
|
||||
writer.writeUTF("col7");
|
||||
writer.writeByte(ContentTypeIds.BLOB_TYPE_ID);
|
||||
}
|
||||
|
||||
private <T> void assertValue(T expectedValue, String name, ContentValues values) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T value = (T) values.get(name);
|
||||
assertNotNull(value);
|
||||
assertEquals(expectedValue, value);
|
||||
}
|
||||
|
||||
private void assertBlobValue(String expectedValue, String name, ContentValues values ){
|
||||
byte[] blob = values.getAsByteArray(name);
|
||||
assertEquals(expectedValue, new String(blob));
|
||||
}
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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.backup;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link PreferenceBackupHelper}.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class PreferenceBackupHelperTest extends TestCase {
|
||||
private Map<String, ?> preferenceValues;
|
||||
private SharedPreferences preferences;
|
||||
private PreferenceBackupHelper preferenceBackupHelper;
|
||||
|
||||
/**
|
||||
* Mock shared preferences editor which does not persist state.
|
||||
*/
|
||||
private class MockPreferenceEditor implements SharedPreferences.Editor {
|
||||
private Map<String, Object> newPreferences = new HashMap<String, Object>(preferenceValues);
|
||||
|
||||
@Override
|
||||
public Editor clear() {
|
||||
newPreferences.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commit() {
|
||||
preferenceValues = newPreferences;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putBoolean(String key, boolean value) {
|
||||
return put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putFloat(String key, float value) {
|
||||
return put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putInt(String key, int value) {
|
||||
return put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putLong(String key, long value) {
|
||||
return put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putString(String key, String value) {
|
||||
return put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor putStringSet(String key, Set<String> value) {
|
||||
return put(key, value);
|
||||
}
|
||||
|
||||
private <T> Editor put(String key, T value) {
|
||||
newPreferences.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor remove(String key) {
|
||||
newPreferences.remove(key);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mock shared preferences which does not persist state.
|
||||
*/
|
||||
private class MockPreferences implements SharedPreferences {
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return preferenceValues.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor edit() {
|
||||
return new MockPreferenceEditor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ?> getAll() {
|
||||
return preferenceValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(String key, boolean defValue) {
|
||||
return get(key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFloat(String key, float defValue) {
|
||||
return get(key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(String key, int defValue) {
|
||||
return get(key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(String key, long defValue) {
|
||||
return get(key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key, String defValue) {
|
||||
return get(key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getStringSet(String key, Set<String> defValue) {
|
||||
return get(key, defValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerOnSharedPreferenceChangeListener(
|
||||
OnSharedPreferenceChangeListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterOnSharedPreferenceChangeListener(
|
||||
OnSharedPreferenceChangeListener listener) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T get(String key, T defValue) {
|
||||
Object value = preferenceValues.get(key);
|
||||
if (value == null) return defValue;
|
||||
return (T) value;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
preferenceValues = new HashMap<String, Object>();
|
||||
preferences = new MockPreferences();
|
||||
preferenceBackupHelper = new PreferenceBackupHelper();
|
||||
}
|
||||
|
||||
public void testExportImportPreferences() throws Exception {
|
||||
// Populate with some initial values
|
||||
Editor editor = preferences.edit();
|
||||
editor.clear();
|
||||
editor.putBoolean("bool1", true);
|
||||
editor.putBoolean("bool2", false);
|
||||
editor.putFloat("flt1", 3.14f);
|
||||
editor.putInt("int1", 42);
|
||||
editor.putLong("long1", 123456789L);
|
||||
editor.putString("str1", "lolcat");
|
||||
editor.commit();
|
||||
|
||||
// Export it
|
||||
byte[] exported = preferenceBackupHelper.exportPreferences(preferences);
|
||||
|
||||
// Mess with the previous values
|
||||
editor = preferences.edit();
|
||||
editor.clear();
|
||||
editor.putString("str2", "Shouldn't be there after restore");
|
||||
editor.putBoolean("bool2", true);
|
||||
editor.commit();
|
||||
|
||||
// Import it back
|
||||
preferenceBackupHelper.importPreferences(exported, preferences);
|
||||
|
||||
assertFalse(preferences.contains("str2"));
|
||||
assertTrue(preferences.getBoolean("bool1", false));
|
||||
assertFalse(preferences.getBoolean("bool2", true));
|
||||
assertEquals(3.14f, preferences.getFloat("flt1", 0.0f));
|
||||
assertEquals(42, preferences.getInt("int1", 0));
|
||||
assertEquals(123456789L, preferences.getLong("long1", 0));
|
||||
assertEquals("lolcat", preferences.getString("str1", ""));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.docs;
|
||||
|
||||
import com.google.android.apps.mytracks.io.gdata.GDataWrapper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.test.mock.MockContext;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Tests for {@link DocsHelper}, with the exception of
|
||||
* {@link DocsHelper#addTrackRow}, which is in
|
||||
* {@link DocsHelper_AddTrackRowTest}.
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class DocsHelperTest extends TestCase {
|
||||
private Context mockContext = new MockContext();
|
||||
|
||||
// TODO(simmonmt): Use AndroidMock to mock this class. We do it the hard
|
||||
// way because use of AndroidMock to mock GDataWrapper triggers a compile
|
||||
// error in an unrelated file. Specifically, it causes a NoClassDefFound
|
||||
// exception for com.google.wireless.gdata2.client.AuthenticationException,
|
||||
// (wrongly) attributed to the first source file in the project. Using the
|
||||
// @UsesMocks(GDataWrapper.class) annotation is enough -- you don't have to
|
||||
// touch AndroidMock at all to get this failure.
|
||||
// The bug is filed with Android Mock as
|
||||
// http://code.google.com/p/android-mock/issues/detail?id=3
|
||||
private class MockGDataWrapper extends GDataWrapper {
|
||||
private final boolean returnValue;
|
||||
|
||||
MockGDataWrapper(boolean returnValue) {
|
||||
this.returnValue = returnValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean runQuery(QueryFunction queryFunction) {
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void testCreateSpreadsheet_noException() throws Exception {
|
||||
// Our mock GDataWrapper isn't able to affect the return value from
|
||||
// DocsHelper#createSpreadsheet. As such, we're only able to simulate the
|
||||
// case where there weren't any GData errors, but not spreadsheet ID was
|
||||
// actually returned. createSpreadsheet is defined to return null in that
|
||||
// situation.
|
||||
assertNull(new DocsHelper().createSpreadsheet(
|
||||
mockContext, new MockGDataWrapper(true), "sheetName"));
|
||||
}
|
||||
|
||||
public void testCreateSpreadsheet_exception() throws Exception {
|
||||
try {
|
||||
new DocsHelper().createSpreadsheet(
|
||||
mockContext, new MockGDataWrapper(false), "sheetName");
|
||||
fail();
|
||||
} catch (IOException expected) {}
|
||||
}
|
||||
|
||||
public void testRequestSpreadsheetId_noException() throws Exception {
|
||||
assertNull(new DocsHelper().requestSpreadsheetId(
|
||||
new MockGDataWrapper(true), "sheetName"));
|
||||
}
|
||||
|
||||
public void testRequestSpreadsheetId_exception() throws Exception {
|
||||
try {
|
||||
new DocsHelper().requestSpreadsheetId(new MockGDataWrapper(false),
|
||||
"sheetName");
|
||||
fail();
|
||||
} catch (IOException expected) {}
|
||||
}
|
||||
|
||||
public void testGetWorksheetId_noException() throws Exception {
|
||||
assertNull(new DocsHelper().getWorksheetId(new MockGDataWrapper(true),
|
||||
"sheetId"));
|
||||
}
|
||||
|
||||
public void testGetWorksheetId_exception() throws Exception {
|
||||
try {
|
||||
new DocsHelper().getWorksheetId(new MockGDataWrapper(false), "sheetId");
|
||||
fail();
|
||||
} catch (IOException expected) {}
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.docs;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.io.AuthManager;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
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.res.Resources;
|
||||
import android.test.mock.MockContext;
|
||||
import android.test.mock.MockResources;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link DocsHelper#addTrackRow}
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class DocsHelper_AddTrackRowTest extends TestCase {
|
||||
private static final long TIME = 1288721514000L;
|
||||
|
||||
private static class StringWritingDocsHelper extends DocsHelper {
|
||||
String writtenSheetUri = null;
|
||||
String writtenData = null;
|
||||
|
||||
@Override
|
||||
protected void writeRowData(AuthManager trixAuth, String worksheetUri,
|
||||
String postText) {
|
||||
writtenSheetUri = worksheetUri;
|
||||
writtenData = postText;
|
||||
}
|
||||
}
|
||||
|
||||
public void testAddTrackRow_imperial() throws Exception {
|
||||
StringWritingDocsHelper docsHelper = new StringWritingDocsHelper();
|
||||
addTrackRow(docsHelper, false);
|
||||
|
||||
String expectedData =
|
||||
"<entry xmlns='http://www.w3.org/2005/Atom' "
|
||||
+ "xmlns:gsx='http://schemas.google.com/spreadsheets/2006/extended'>"
|
||||
+ "<gsx:name><![CDATA[trackName]]></gsx:name>"
|
||||
+ "<gsx:description><![CDATA[trackDescription]]></gsx:description>"
|
||||
// We format the date here because we can't guarantee/force the timezone
|
||||
+ "<gsx:date><![CDATA[" + String.format("%tc", TIME) + "]]></gsx:date>"
|
||||
+ "<gsx:totaltime><![CDATA[0:00:05]]></gsx:totaltime>"
|
||||
+ "<gsx:movingtime><![CDATA[0:00:04]]></gsx:movingtime>"
|
||||
+ "<gsx:distance><![CDATA[12.43]]></gsx:distance>"
|
||||
+ "<gsx:distanceunit><![CDATA[mile]]></gsx:distanceunit>"
|
||||
+ "<gsx:averagespeed><![CDATA[8,947.75]]></gsx:averagespeed>"
|
||||
+ "<gsx:averagemovingspeed><![CDATA[11,184.68]]>"
|
||||
+ "</gsx:averagemovingspeed>"
|
||||
+ "<gsx:maxspeed><![CDATA[3,355.40]]></gsx:maxspeed>"
|
||||
+ "<gsx:speedunit><![CDATA[mph]]></gsx:speedunit>"
|
||||
+ "<gsx:elevationgain><![CDATA[19,685]]></gsx:elevationgain>"
|
||||
+ "<gsx:minelevation><![CDATA[-1,640]]></gsx:minelevation>"
|
||||
+ "<gsx:maxelevation><![CDATA[1,804]]></gsx:maxelevation>"
|
||||
+ "<gsx:elevationunit><![CDATA[feet]]></gsx:elevationunit>"
|
||||
+ "<gsx:map>"
|
||||
+ "<![CDATA[http://maps.google.com/maps/ms?msa=0&msid=trackMapId]]>"
|
||||
+ "</gsx:map>"
|
||||
+ "</entry>";
|
||||
|
||||
assertEquals(
|
||||
"http://spreadsheets.google.com/feeds/list/ssid/wsid/private/full",
|
||||
docsHelper.writtenSheetUri);
|
||||
assertEquals(expectedData, docsHelper.writtenData);
|
||||
}
|
||||
|
||||
public void testAddTrackRow_metric() throws Exception {
|
||||
StringWritingDocsHelper docsHelper = new StringWritingDocsHelper();
|
||||
addTrackRow(docsHelper, true);
|
||||
|
||||
// The imperial test verifies that the tags come out in the proper order,
|
||||
// and with the proper names. We need only verify that the labels are
|
||||
// correct, and that at least one of the unit-dependent value tags is
|
||||
// correct.
|
||||
assertTrue(docsHelper.writtenData.contains(
|
||||
"<gsx:distanceunit><![CDATA[km]]></gsx:distanceunit>"));
|
||||
assertTrue(docsHelper.writtenData.contains(
|
||||
"<gsx:speedunit><![CDATA[kph]]></gsx:speedunit>"));
|
||||
assertTrue(docsHelper.writtenData.contains(
|
||||
"<gsx:elevationunit><![CDATA[meter]]></gsx:elevationunit>"));
|
||||
|
||||
assertTrue(docsHelper.writtenData.contains(
|
||||
"<gsx:distance><![CDATA[20.00]]></gsx:distance>"));
|
||||
}
|
||||
|
||||
/** Adds a row to the spreadsheet, using the provided helper. */
|
||||
@UsesMocks({AuthManager.class, MockResources.class, Track.class})
|
||||
private void addTrackRow(DocsHelper docsHelper, boolean useMetric)
|
||||
throws IOException {
|
||||
final Resources mockResources = AndroidMock.createMock(MockResources.class);
|
||||
|
||||
if (useMetric) {
|
||||
AndroidMock.expect(mockResources.getString(R.string.kilometer))
|
||||
.andReturn("km");
|
||||
AndroidMock.expect(mockResources.getString(R.string.kilometer_per_hour))
|
||||
.andReturn("kph");
|
||||
AndroidMock.expect(mockResources.getString(R.string.meter))
|
||||
.andReturn("meter");
|
||||
} else {
|
||||
AndroidMock.expect(mockResources.getString(R.string.mile))
|
||||
.andReturn("mile");
|
||||
AndroidMock.expect(mockResources.getString(R.string.mile_per_hour))
|
||||
.andReturn("mph");
|
||||
AndroidMock.expect(mockResources.getString(R.string.feet))
|
||||
.andReturn("feet");
|
||||
}
|
||||
AndroidMock.replay(mockResources);
|
||||
|
||||
Context mockContext = new MockContext() {
|
||||
@Override
|
||||
public Resources getResources() {
|
||||
return mockResources;
|
||||
}
|
||||
};
|
||||
|
||||
AuthManager mockAuthManager = AndroidMock.createMock(AuthManager.class);
|
||||
AndroidMock.replay(mockAuthManager);
|
||||
|
||||
TripStatistics stats = new TripStatistics();
|
||||
stats.setStartTime(TIME);
|
||||
stats.setTotalTime(5000);
|
||||
stats.setMovingTime(4000);
|
||||
stats.setTotalDistance(20000);
|
||||
stats.setMaxSpeed(1500);
|
||||
stats.setTotalElevationGain(6000);
|
||||
stats.setMinElevation(-500);
|
||||
stats.setMaxElevation(550);
|
||||
|
||||
Track track = new Track();
|
||||
track.setName("trackName");
|
||||
track.setDescription("trackDescription");
|
||||
track.setMapId("trackMapId");
|
||||
track.setStatistics(stats);
|
||||
|
||||
docsHelper.addTrackRow(mockContext, mockAuthManager, "ssid", "wsid",
|
||||
track, useMetric);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.docs;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* This class tests {@link DocsTagBuilder}
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class DocsTagBuilderTest extends TestCase {
|
||||
public void testAppend() {
|
||||
assertEquals("<gsx:tag><![CDATA[value]]></gsx:tag>",
|
||||
new DocsTagBuilder(false).append("tag", "value").build());
|
||||
}
|
||||
|
||||
public void testMultiAppend() {
|
||||
String actual = new DocsTagBuilder(false)
|
||||
.append("tag1", "value1")
|
||||
.append("tag2", "value2")
|
||||
.build();
|
||||
|
||||
assertEquals("<gsx:tag1><![CDATA[value1]]></gsx:tag1>"
|
||||
+ "<gsx:tag2><![CDATA[value2]]></gsx:tag2>", actual);
|
||||
}
|
||||
|
||||
public void testAppendLargeUnits_imperial() {
|
||||
assertEquals("<gsx:tag><![CDATA[62.14]]></gsx:tag>",
|
||||
new DocsTagBuilder(false).appendLargeUnits("tag", 100.0).build());
|
||||
}
|
||||
|
||||
public void testAppendLargeUnits_metric() {
|
||||
assertEquals("<gsx:tag><![CDATA[100.00]]></gsx:tag>",
|
||||
new DocsTagBuilder(true).appendLargeUnits("tag", 100.0).build());
|
||||
}
|
||||
|
||||
public void testAppendSmallUnits_imperial() {
|
||||
assertEquals("<gsx:tag><![CDATA[328]]></gsx:tag>",
|
||||
new DocsTagBuilder(false).appendSmallUnits("tag", 100.0).build());
|
||||
}
|
||||
|
||||
public void testAppendSmallUnits_metric() {
|
||||
assertEquals("<gsx:tag><![CDATA[100]]></gsx:tag>",
|
||||
new DocsTagBuilder(true).appendSmallUnits("tag", 100.0).build());
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.sendtogoogle;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracks;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.view.View;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests {@link ResultDialogFactory}.
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class ResultDialogFactoryTest extends ActivityInstrumentationTestCase2<MyTracks> {
|
||||
public ResultDialogFactoryTest() {
|
||||
super(MyTracks.class);
|
||||
}
|
||||
|
||||
private List<SendResult> makeResults(SendResult... results) {
|
||||
List<SendResult> resultsList = new ArrayList<SendResult>();
|
||||
for (SendResult result : results) {
|
||||
resultsList.add(result);
|
||||
}
|
||||
return resultsList;
|
||||
}
|
||||
|
||||
private DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {}
|
||||
};
|
||||
|
||||
public void testSuccess_noShare() {
|
||||
List<SendResult> results = makeResults(new SendResult(SendType.MYMAPS, true),
|
||||
new SendResult(SendType.DOCS, true));
|
||||
|
||||
AlertDialog dialog = ResultDialogFactory.makeDialog(getActivity(), results, clickListener, null);
|
||||
dialog.show();
|
||||
|
||||
ListView listView = (ListView) dialog.findViewById(R.id.send_to_google_result_list);
|
||||
ListAdapter listAdapter = listView.getAdapter();
|
||||
assertEquals(2, listAdapter.getCount());
|
||||
|
||||
assertEquals(SendType.MYMAPS, ((SendResult) listAdapter.getItem(0)).getType());
|
||||
assertEquals(SendType.DOCS, ((SendResult) listAdapter.getItem(1)).getType());
|
||||
|
||||
// Checking the return code of Dialog#getButton doesn't appear to be
|
||||
// sufficient, as a Button will be returned even if it doesn't appear in
|
||||
// the rendered dialog.
|
||||
assertEquals(View.VISIBLE, dialog.getButton(AlertDialog.BUTTON_POSITIVE).getVisibility());
|
||||
assertEquals(View.GONE, dialog.getButton(AlertDialog.BUTTON_NEUTRAL).getVisibility());
|
||||
|
||||
assertEquals(View.VISIBLE,
|
||||
dialog.findViewById(R.id.send_to_google_result_comment).getVisibility());
|
||||
assertEquals(View.GONE,
|
||||
dialog.findViewById(R.id.send_to_google_result_error).getVisibility());
|
||||
|
||||
// TODO: Test correct listener invocation. Neither TouchUtils#clickView
|
||||
// nor Button#performClick appear to cause listener invocation. This
|
||||
// might be Android bug
|
||||
// http://code.google.com/p/android/issues/detail?id=6564
|
||||
}
|
||||
|
||||
public void testSuccess_share() {
|
||||
List<SendResult> results = makeResults(new SendResult(SendType.MYMAPS, true),
|
||||
new SendResult(SendType.DOCS, true));
|
||||
|
||||
AlertDialog dialog = ResultDialogFactory.makeDialog(getActivity(), results,
|
||||
clickListener, clickListener);
|
||||
dialog.show();
|
||||
|
||||
assertEquals(View.VISIBLE, dialog.getButton(AlertDialog.BUTTON_POSITIVE).getVisibility());
|
||||
assertEquals(View.VISIBLE, dialog.getButton(AlertDialog.BUTTON_NEUTRAL).getVisibility());
|
||||
}
|
||||
|
||||
public void testFailure_noShare() {
|
||||
List<SendResult> results = makeResults(new SendResult(SendType.MYMAPS, true),
|
||||
new SendResult(SendType.DOCS, false));
|
||||
|
||||
AlertDialog dialog = ResultDialogFactory.makeDialog(getActivity(), results, clickListener, null);
|
||||
dialog.show();
|
||||
|
||||
assertEquals(View.GONE,
|
||||
dialog.findViewById(R.id.send_to_google_result_comment).getVisibility());
|
||||
assertEquals(View.VISIBLE,
|
||||
dialog.findViewById(R.id.send_to_google_result_error).getVisibility());
|
||||
}
|
||||
|
||||
public void testFailure_share() {
|
||||
List<SendResult> results = makeResults(new SendResult(SendType.MYMAPS, true),
|
||||
new SendResult(SendType.DOCS, false));
|
||||
|
||||
AlertDialog dialog = ResultDialogFactory.makeDialog(getActivity(), results,
|
||||
clickListener, clickListener);
|
||||
dialog.show();
|
||||
|
||||
assertEquals(View.VISIBLE, dialog.getButton(AlertDialog.BUTTON_POSITIVE).getVisibility());
|
||||
assertEquals(View.VISIBLE, dialog.getButton(AlertDialog.BUTTON_NEUTRAL).getVisibility());
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.sendtogoogle;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests {@link ResultListAdapter}.
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class ResultListAdapterTest extends AndroidTestCase {
|
||||
private static class TestResultListAdapter extends ResultListAdapter {
|
||||
public View contentView;
|
||||
public int drawableId;
|
||||
public int nameId;
|
||||
public int urlId;
|
||||
|
||||
public TestResultListAdapter(Context context, List<SendResult> results) {
|
||||
super(context, 0, results);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImage(View content, int drawableId) {
|
||||
saveContentView(content);
|
||||
this.drawableId = drawableId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setName(View content, int nameId) {
|
||||
saveContentView(content);
|
||||
this.nameId = nameId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUrl(View content, int urlId) {
|
||||
saveContentView(content);
|
||||
this.urlId = urlId;
|
||||
}
|
||||
|
||||
private void saveContentView(View contentView) {
|
||||
if (this.contentView == null) {
|
||||
this.contentView = contentView;
|
||||
} else {
|
||||
AndroidTestCase.assertEquals(this.contentView, contentView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final SendType sendType = SendType.MYMAPS;
|
||||
|
||||
private View inflateContentView(int viewId) {
|
||||
LayoutInflater layoutInflater
|
||||
= (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
return layoutInflater.inflate(viewId, null);
|
||||
}
|
||||
|
||||
private List<SendResult> makeResult(SendType type, boolean success) {
|
||||
List<SendResult> results = new ArrayList<SendResult>();
|
||||
results.add(new SendResult(type, success));
|
||||
return results;
|
||||
}
|
||||
|
||||
public void testSuccess_createsNewView() {
|
||||
TestResultListAdapter adapter = new TestResultListAdapter(getContext(),
|
||||
makeResult(sendType, true));
|
||||
adapter.getView(0, null, null);
|
||||
|
||||
assertEquals(R.drawable.success, adapter.drawableId);
|
||||
assertEquals(sendType.getServiceName(), adapter.nameId);
|
||||
assertEquals(sendType.getServiceUrl(), adapter.urlId);
|
||||
}
|
||||
|
||||
public void testSuccess_reusesView() {
|
||||
View contentView = inflateContentView(R.layout.send_to_google_result_list_item);
|
||||
TestResultListAdapter adapter = new TestResultListAdapter(getContext(),
|
||||
makeResult(sendType, true));
|
||||
adapter.getView(0, contentView, null);
|
||||
|
||||
assertEquals(contentView, adapter.contentView);
|
||||
assertEquals(R.drawable.success, adapter.drawableId);
|
||||
assertEquals(sendType.getServiceName(), adapter.nameId);
|
||||
assertEquals(sendType.getServiceUrl(), adapter.urlId);
|
||||
}
|
||||
|
||||
public void testFailure() {
|
||||
TestResultListAdapter adapter = new TestResultListAdapter(getContext(),
|
||||
makeResult(sendType, false));
|
||||
adapter.getView(0, null, null);
|
||||
|
||||
assertEquals(R.drawable.failure, adapter.drawableId);
|
||||
assertEquals(sendType.getServiceName(), adapter.nameId);
|
||||
assertEquals(sendType.getServiceUrl(), adapter.urlId);
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.services;
|
||||
|
||||
import android.content.Context;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Tests {@link DefaultTrackNameFactory}
|
||||
*
|
||||
* @author Matthew Simmons
|
||||
*/
|
||||
public class DefaultTrackNameFactoryTest extends AndroidTestCase {
|
||||
/**
|
||||
* A version of the factory which allows us to supply our own answer as to
|
||||
* whether a timestamp-based track name should be used.
|
||||
*/
|
||||
private static class MockDefaultTrackNameFactory
|
||||
extends DefaultTrackNameFactory {
|
||||
private final boolean useTimestamp;
|
||||
|
||||
MockDefaultTrackNameFactory(Context context, boolean useTimestamp) {
|
||||
super(context);
|
||||
this.useTimestamp = useTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean useTimestampTrackName() {
|
||||
return useTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
private static final long TIMESTAMP = 1288213406000L;
|
||||
|
||||
public void testTimestampTrackName() {
|
||||
DefaultTrackNameFactory factory =
|
||||
new MockDefaultTrackNameFactory(getContext(), true);
|
||||
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
|
||||
assertEquals(formatter.format(new Date(TIMESTAMP)),
|
||||
factory.newTrackName(1, TIMESTAMP));
|
||||
}
|
||||
|
||||
public void testIncrementingTrackName() {
|
||||
DefaultTrackNameFactory factory =
|
||||
new MockDefaultTrackNameFactory(getContext(), false);
|
||||
|
||||
assertEquals("Track 1", factory.newTrackName(1, TIMESTAMP));
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.services;
|
||||
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
|
||||
import android.media.AudioManager;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link StatusAnnouncerFactory}.
|
||||
* These tests require Donut+ to run.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class StatusAnnouncerFactoryTest extends AndroidTestCase {
|
||||
/**
|
||||
* Mock version of the {@link ApiFeatures} class.
|
||||
*/
|
||||
private class MockApiFeatures extends ApiFeatures {
|
||||
private boolean hasTts;
|
||||
|
||||
public void setHasTextToSpeech(boolean hasTts) {
|
||||
this.hasTts = hasTts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTextToSpeech() {
|
||||
return hasTts;
|
||||
}
|
||||
}
|
||||
|
||||
private MockApiFeatures apiFeatures;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
apiFeatures = new MockApiFeatures();
|
||||
}
|
||||
|
||||
public void testCreate() {
|
||||
apiFeatures.setHasTextToSpeech(true);
|
||||
StatusAnnouncerFactory factory =
|
||||
new StatusAnnouncerFactory(apiFeatures);
|
||||
PeriodicTask task = factory.create(getContext());
|
||||
assertTrue(task instanceof StatusAnnouncerTask);
|
||||
}
|
||||
|
||||
public void testCreate_notAvailable() {
|
||||
apiFeatures.setHasTextToSpeech(false);
|
||||
StatusAnnouncerFactory factory =
|
||||
new StatusAnnouncerFactory(apiFeatures);
|
||||
PeriodicTask task = factory.create(getContext());
|
||||
assertNull(task);
|
||||
}
|
||||
|
||||
public void testGetVolumeStream() {
|
||||
apiFeatures.setHasTextToSpeech(true);
|
||||
StatusAnnouncerFactory factory =
|
||||
new StatusAnnouncerFactory(apiFeatures);
|
||||
assertEquals(
|
||||
TextToSpeech.Engine.DEFAULT_STREAM,
|
||||
factory.getVolumeStream());
|
||||
}
|
||||
|
||||
public void testGetVolumeStream_notAvailable() {
|
||||
apiFeatures.setHasTextToSpeech(false);
|
||||
StatusAnnouncerFactory factory =
|
||||
new StatusAnnouncerFactory(apiFeatures);
|
||||
assertEquals(
|
||||
AudioManager.USE_DEFAULT_STREAM_TYPE,
|
||||
factory.getVolumeStream());
|
||||
}
|
||||
}
|
||||
+503
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
* 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.services;
|
||||
|
||||
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.same;
|
||||
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.speech.tts.TextToSpeech.OnInitListener;
|
||||
import android.speech.tts.TextToSpeech.OnUtteranceCompletedListener;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.easymock.Capture;
|
||||
|
||||
/**
|
||||
* Tests for {@link StatusAnnouncerTask}.
|
||||
* WARNING: I'm not responsible if your eyes start bleeding while reading this
|
||||
* code. You have been warned. It's still better than no test, though.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class StatusAnnouncerTaskTest extends AndroidTestCase {
|
||||
|
||||
// Use something other than our hardcoded value
|
||||
private static final Locale DEFAULT_LOCALE = Locale.KOREAN;
|
||||
private static final String ANNOUNCEMENT = "I can haz cheeseburger?";
|
||||
private Locale oldDefaultLocale;
|
||||
|
||||
private StatusAnnouncerTask task;
|
||||
private StringUtils stringUtils;
|
||||
private StatusAnnouncerTask mockTask;
|
||||
private Capture<OnInitListener> initListenerCapture;
|
||||
private Capture<PhoneStateListener> phoneListenerCapture;
|
||||
private TextToSpeechDelegate ttsDelegate;
|
||||
private TextToSpeechInterface tts;
|
||||
|
||||
/**
|
||||
* Mockable interface that we delegate TTS calls to.
|
||||
*/
|
||||
interface TextToSpeechInterface {
|
||||
int addEarcon(String earcon, String packagename, int resourceId);
|
||||
int addEarcon(String earcon, String filename);
|
||||
int addSpeech(String text, String packagename, int resourceId);
|
||||
int addSpeech(String text, String filename);
|
||||
boolean areDefaultsEnforced();
|
||||
String getDefaultEngine();
|
||||
Locale getLanguage();
|
||||
int isLanguageAvailable(Locale loc);
|
||||
boolean isSpeaking();
|
||||
int playEarcon(String earcon, int queueMode,
|
||||
HashMap<String, String> params);
|
||||
int playSilence(long durationInMs, int queueMode,
|
||||
HashMap<String, String> params);
|
||||
int setEngineByPackageName(String enginePackageName);
|
||||
int setLanguage(Locale loc);
|
||||
int setOnUtteranceCompletedListener(OnUtteranceCompletedListener listener);
|
||||
int setPitch(float pitch);
|
||||
int setSpeechRate(float speechRate);
|
||||
void shutdown();
|
||||
int speak(String text, int queueMode, HashMap<String, String> params);
|
||||
int stop();
|
||||
int synthesizeToFile(String text, HashMap<String, String> params,
|
||||
String filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass of {@link TextToSpeech} which delegates calls to the interface
|
||||
* above.
|
||||
* The logic here is stupid and the author is ashamed of having to write it
|
||||
* like this, but basically the issue is that TextToSpeech cannot be mocked
|
||||
* without running its constructor, its constructor runs async operations
|
||||
* which call other methods (and then if the methods are part of a mock we'd
|
||||
* have to set a behavior, but we can't 'cause the object hasn't been fully
|
||||
* built yet).
|
||||
* The logic is that calls made during the constructor (when tts is not yet
|
||||
* set) will go up to the original class, but after tts is set we'll forward
|
||||
* them all to the mock.
|
||||
*/
|
||||
private class TextToSpeechDelegate
|
||||
extends TextToSpeech implements TextToSpeechInterface {
|
||||
public TextToSpeechDelegate(Context context, OnInitListener listener) {
|
||||
super(context, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addEarcon(String earcon, String packagename, int resourceId) {
|
||||
if (tts == null) {
|
||||
return super.addEarcon(earcon, packagename, resourceId);
|
||||
}
|
||||
return tts.addEarcon(earcon, packagename, resourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addEarcon(String earcon, String filename) {
|
||||
if (tts == null) {
|
||||
return super.addEarcon(earcon, filename);
|
||||
}
|
||||
return tts.addEarcon(earcon, filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addSpeech(String text, String packagename, int resourceId) {
|
||||
if (tts == null) {
|
||||
return super.addSpeech(text, packagename, resourceId);
|
||||
}
|
||||
return tts.addSpeech(text, packagename, resourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addSpeech(String text, String filename) {
|
||||
if (tts == null) {
|
||||
return super.addSpeech(text, filename);
|
||||
}
|
||||
return tts.addSpeech(text, filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLanguage() {
|
||||
if (tts == null) {
|
||||
return super.getLanguage();
|
||||
}
|
||||
return tts.getLanguage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int isLanguageAvailable(Locale loc) {
|
||||
if (tts == null) {
|
||||
return super.isLanguageAvailable(loc);
|
||||
}
|
||||
return tts.isLanguageAvailable(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSpeaking() {
|
||||
if (tts == null) {
|
||||
return super.isSpeaking();
|
||||
}
|
||||
return tts.isSpeaking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int playEarcon(String earcon, int queueMode,
|
||||
HashMap<String, String> params) {
|
||||
if (tts == null) {
|
||||
return super.playEarcon(earcon, queueMode, params);
|
||||
}
|
||||
return tts.playEarcon(earcon, queueMode, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int playSilence(long durationInMs, int queueMode,
|
||||
HashMap<String, String> params) {
|
||||
if (tts == null) {
|
||||
return super.playSilence(durationInMs, queueMode, params);
|
||||
}
|
||||
return tts.playSilence(durationInMs, queueMode, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setLanguage(Locale loc) {
|
||||
if (tts == null) {
|
||||
return super.setLanguage(loc);
|
||||
}
|
||||
return tts.setLanguage(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setOnUtteranceCompletedListener(
|
||||
OnUtteranceCompletedListener listener) {
|
||||
if (tts == null) {
|
||||
return super.setOnUtteranceCompletedListener(listener);
|
||||
}
|
||||
return tts.setOnUtteranceCompletedListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setPitch(float pitch) {
|
||||
if (tts == null) {
|
||||
return super.setPitch(pitch);
|
||||
}
|
||||
return tts.setPitch(pitch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int setSpeechRate(float speechRate) {
|
||||
if (tts == null) {
|
||||
return super.setSpeechRate(speechRate);
|
||||
}
|
||||
return tts.setSpeechRate(speechRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (tts == null) {
|
||||
super.shutdown();
|
||||
return;
|
||||
}
|
||||
tts.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int speak(
|
||||
String text, int queueMode, HashMap<String, String> params) {
|
||||
if (tts == null) {
|
||||
return super.speak(text, queueMode, params);
|
||||
}
|
||||
return tts.speak(text, queueMode, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stop() {
|
||||
if (tts == null) {
|
||||
return super.stop();
|
||||
}
|
||||
return tts.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int synthesizeToFile(String text, HashMap<String, String> params,
|
||||
String filename) {
|
||||
if (tts == null) {
|
||||
return super.synthesizeToFile(text, params, filename);
|
||||
}
|
||||
return tts.synthesizeToFile(text, params, filename);
|
||||
}
|
||||
}
|
||||
|
||||
@UsesMocks({
|
||||
StatusAnnouncerTask.class,
|
||||
StringUtils.class,
|
||||
})
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
oldDefaultLocale = Locale.getDefault();
|
||||
Locale.setDefault(DEFAULT_LOCALE);
|
||||
stringUtils = AndroidMock.createMock(StringUtils.class, getContext());
|
||||
|
||||
// Eww, the effort required just to mock TextToSpeech is insane
|
||||
final AtomicBoolean listenerCalled = new AtomicBoolean();
|
||||
OnInitListener blockingListener = new OnInitListener() {
|
||||
@Override
|
||||
public void onInit(int status) {
|
||||
synchronized (this) {
|
||||
listenerCalled.set(true);
|
||||
notify();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ttsDelegate = new TextToSpeechDelegate(getContext(), blockingListener);
|
||||
|
||||
// Wait for all async operations done in the constructor to finish.
|
||||
synchronized (blockingListener) {
|
||||
while (!listenerCalled.get()) {
|
||||
// Releases the synchronized lock until we're woken up.
|
||||
blockingListener.wait();
|
||||
}
|
||||
}
|
||||
|
||||
// Phew, done, now we can start forwarding calls
|
||||
tts = AndroidMock.createMock(TextToSpeechInterface.class);
|
||||
|
||||
initListenerCapture = new Capture<OnInitListener>();
|
||||
phoneListenerCapture = new Capture<PhoneStateListener>();
|
||||
|
||||
// Create a partial forwarding mock
|
||||
mockTask = AndroidMock.createMock(StatusAnnouncerTask.class,
|
||||
getContext(), stringUtils);
|
||||
task = new StatusAnnouncerTask(getContext(), stringUtils) {
|
||||
@Override
|
||||
protected TextToSpeech newTextToSpeech(Context ctx,
|
||||
OnInitListener onInitListener) {
|
||||
return mockTask.newTextToSpeech(ctx, onInitListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAnnouncement(TripStatistics stats) {
|
||||
return mockTask.getAnnouncement(stats);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void listenToPhoneState(
|
||||
PhoneStateListener listener, int events) {
|
||||
mockTask.listenToPhoneState(listener, events);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() {
|
||||
Locale.setDefault(oldDefaultLocale);
|
||||
}
|
||||
|
||||
public void testStart() {
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
assertNotNull(ttsInitListener);
|
||||
|
||||
expect(tts.isLanguageAvailable(DEFAULT_LOCALE))
|
||||
.andStubReturn(TextToSpeech.LANG_AVAILABLE);
|
||||
expect(tts.setLanguage(DEFAULT_LOCALE))
|
||||
.andReturn(TextToSpeech.LANG_AVAILABLE);
|
||||
expect(tts.setSpeechRate(StatusAnnouncerTask.TTS_SPEECH_RATE))
|
||||
.andReturn(TextToSpeech.SUCCESS);
|
||||
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
|
||||
ttsInitListener.onInit(TextToSpeech.SUCCESS);
|
||||
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
public void testStart_languageNotSupported() {
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
assertNotNull(ttsInitListener);
|
||||
|
||||
expect(tts.isLanguageAvailable(DEFAULT_LOCALE))
|
||||
.andStubReturn(TextToSpeech.LANG_NOT_SUPPORTED);
|
||||
expect(tts.setLanguage(Locale.ENGLISH))
|
||||
.andReturn(TextToSpeech.LANG_AVAILABLE);
|
||||
expect(tts.setSpeechRate(StatusAnnouncerTask.TTS_SPEECH_RATE))
|
||||
.andReturn(TextToSpeech.SUCCESS);
|
||||
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
|
||||
ttsInitListener.onInit(TextToSpeech.SUCCESS);
|
||||
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
public void testStart_notReady() {
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
assertNotNull(ttsInitListener);
|
||||
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
|
||||
ttsInitListener.onInit(TextToSpeech.ERROR);
|
||||
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
public void testShutdown() {
|
||||
// First, start
|
||||
doStart();
|
||||
AndroidMock.verify(mockTask);
|
||||
AndroidMock.reset(mockTask);
|
||||
|
||||
// Then, shut down
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
mockTask.listenToPhoneState(
|
||||
same(phoneListener), eq(PhoneStateListener.LISTEN_NONE));
|
||||
tts.shutdown();
|
||||
AndroidMock.replay(mockTask, tts, stringUtils);
|
||||
task.shutdown();
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
public void testRun() throws Exception {
|
||||
// Expect service data calls
|
||||
TripStatistics stats = new TripStatistics();
|
||||
|
||||
// Expect announcement building call
|
||||
expect(mockTask.getAnnouncement(same(stats))).andStubReturn(ANNOUNCEMENT);
|
||||
|
||||
// Put task in "ready" state
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
// Expect actual announcement call
|
||||
expect(tts.speak(
|
||||
eq(ANNOUNCEMENT), eq(TextToSpeech.QUEUE_FLUSH),
|
||||
AndroidMock.<HashMap<String, String>>isNull()))
|
||||
.andReturn(TextToSpeech.SUCCESS);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
task.runWithStatistics(stats);
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
public void testRun_notReady() throws Exception {
|
||||
// Put task in "not ready" state
|
||||
startTask(TextToSpeech.ERROR);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
task.runWithStatistics(null);
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
public void testRun_duringCall() throws Exception {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
expect(tts.isSpeaking()).andStubReturn(false);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, null);
|
||||
task.runWithStatistics(null);
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
public void testRun_ringWhileSpeaking() throws Exception {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
expect(tts.isSpeaking()).andStubReturn(true);
|
||||
expect(tts.stop()).andReturn(TextToSpeech.SUCCESS);
|
||||
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
|
||||
// Update the state to ringing - this should stop the current announcement.
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null);
|
||||
|
||||
// Run the announcement - this should do nothing.
|
||||
task.runWithStatistics(null);
|
||||
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
public void testRun_whileRinging() throws Exception {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
expect(tts.isSpeaking()).andStubReturn(false);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null);
|
||||
task.runWithStatistics(null);
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
public void testRun_noService() throws Exception {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
public void testRun_noStats() throws Exception {
|
||||
// Expect service data calls
|
||||
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
task.runWithStatistics(null);
|
||||
AndroidMock.verify(mockTask, tts, stringUtils);
|
||||
}
|
||||
|
||||
private void startTask(int state) {
|
||||
AndroidMock.resetToNice(tts, stringUtils);
|
||||
AndroidMock.replay(tts, stringUtils);
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
ttsInitListener.onInit(state);
|
||||
AndroidMock.resetToDefault(tts, stringUtils);
|
||||
}
|
||||
|
||||
private void doStart() {
|
||||
mockTask.listenToPhoneState(capture(phoneListenerCapture),
|
||||
eq(PhoneStateListener.LISTEN_CALL_STATE));
|
||||
expect(mockTask.newTextToSpeech(
|
||||
same(getContext()), capture(initListenerCapture)))
|
||||
.andStubReturn(ttsDelegate);
|
||||
AndroidMock.replay(mockTask);
|
||||
task.start();
|
||||
}
|
||||
}
|
||||
+829
@@ -0,0 +1,829 @@
|
||||
/*
|
||||
* 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.services;
|
||||
|
||||
import static com.google.android.apps.mytracks.MyTracksConstants.RESUME_TRACK_EXTRA_NAME;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProvider;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
import com.google.android.apps.mytracks.util.ApiFeatures;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.location.Location;
|
||||
import android.os.IBinder;
|
||||
import android.test.RenamingDelegatingContext;
|
||||
import android.test.ServiceTestCase;
|
||||
import android.test.mock.MockContentResolver;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests for the MyTracks track recording service.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*
|
||||
* TODO: The original class, ServiceTestCase, has a few limitations, e.g.
|
||||
* it's not possible to properly shutdown the service, unless tearDown()
|
||||
* is called, which prevents from testing multiple scenarios in a single
|
||||
* test (see runFunctionTest for more details).
|
||||
*/
|
||||
public class TrackRecordingServiceTest
|
||||
extends ServiceTestCase<TrackRecordingService> {
|
||||
|
||||
private Context context;
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
/*
|
||||
* In order to support starting and binding to the service in the same
|
||||
* unit test, we provide a workaround, as the original class doesn't allow
|
||||
* to bind after the service has been previously started.
|
||||
*/
|
||||
|
||||
private boolean bound;
|
||||
private Intent serviceIntent;
|
||||
|
||||
public TrackRecordingServiceTest() {
|
||||
super(TrackRecordingService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* A context wrapper with the user provided {@link ContentResolver}.
|
||||
*
|
||||
* TODO: Move to test utils package.
|
||||
*/
|
||||
public static class MockContext extends ContextWrapper {
|
||||
private final ContentResolver contentResolver;
|
||||
|
||||
public MockContext(ContentResolver contentResolver, Context base) {
|
||||
super(base);
|
||||
this.contentResolver = contentResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentResolver getContentResolver() {
|
||||
return contentResolver;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A mock class that forces API level < 5 to make sure we can workaround a bug
|
||||
* in ServiceTestCase (throwing a NPE).
|
||||
* See http://code.google.com/p/android/issues/detail?id=12122 for more
|
||||
* details.
|
||||
*/
|
||||
private static class MockApiFeatures extends ApiFeatures {
|
||||
@Override
|
||||
protected int getApiLevel() {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IBinder bindService(Intent intent) {
|
||||
if (getService() != null) {
|
||||
if (bound) {
|
||||
throw new IllegalStateException(
|
||||
"Service: " + getService() + " is already bound");
|
||||
}
|
||||
bound = true;
|
||||
serviceIntent = intent.cloneFilter();
|
||||
return getService().onBind(intent);
|
||||
} else {
|
||||
return super.bindService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdownService() {
|
||||
if (bound) {
|
||||
assertNotNull(getService());
|
||||
getService().onUnbind(serviceIntent);
|
||||
bound = false;
|
||||
}
|
||||
super.shutdownService();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
ApiFeatures.injectInstance(new MockApiFeatures());
|
||||
|
||||
MockContentResolver mockContentResolver = new MockContentResolver();
|
||||
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
|
||||
getContext(), getContext(), "test.");
|
||||
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);
|
||||
|
||||
sharedPreferences = context.getSharedPreferences(
|
||||
MyTracksSettings.SETTINGS_NAME, 0);
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().commit();
|
||||
|
||||
// Disable auto resume by default.
|
||||
updateAutoResumePrefs(0, -1);
|
||||
// No recording track.
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putLong(context.getString(R.string.recording_track_key), -1);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testStartable() {
|
||||
startService(createStartIntent());
|
||||
assertNotNull(getService());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testBindable() {
|
||||
IBinder service = bindService(createStartIntent());
|
||||
assertNotNull(service);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testResumeAfterReboot_shouldResume() throws Exception {
|
||||
// Insert a dummy track and mark it as recording track.
|
||||
createDummyTrack(123, System.currentTimeMillis(), true);
|
||||
|
||||
// Clear the number of attempts and set the timeout to 10 min.
|
||||
updateAutoResumePrefs(0, 10);
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent();
|
||||
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
|
||||
startService(startIntent);
|
||||
assertNotNull(getService());
|
||||
|
||||
// We expect to resume the previous track.
|
||||
assertTrue(getService().isRecording());
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertEquals(123, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
// TODO: shutdownService() has a bug and doesn't set mServiceCreated
|
||||
// to false, thus preventing from a second call to onCreate().
|
||||
// Report the bug to Android team. Until then, the following tests
|
||||
// and checks must be commented out.
|
||||
//
|
||||
// TODO: If fixed, remove "disabled" prefix from the test name.
|
||||
@MediumTest
|
||||
public void disabledTestResumeAfterReboot_simulateReboot() throws Exception {
|
||||
updateAutoResumePrefs(0, 10);
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// Simulate recording a track.
|
||||
long id = service.startNewTrack();
|
||||
assertTrue(service.isRecording());
|
||||
assertEquals(id, service.getRecordingTrackId());
|
||||
shutdownService();
|
||||
assertEquals(id, sharedPreferences.getLong(
|
||||
context.getString(R.string.recording_track_key), -1));
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent();
|
||||
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
|
||||
startService(startIntent);
|
||||
assertNotNull(getService());
|
||||
|
||||
assertTrue(getService().isRecording());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testResumeAfterReboot_noRecordingTrack() throws Exception {
|
||||
// Insert a dummy track and mark it as recording track.
|
||||
createDummyTrack(123, System.currentTimeMillis(), false);
|
||||
|
||||
// Clear the number of attempts and set the timeout to 10 min.
|
||||
updateAutoResumePrefs(0, 10);
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent();
|
||||
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
|
||||
startService(startIntent);
|
||||
assertNotNull(getService());
|
||||
|
||||
// We don't expect to resume the previous track, because it was stopped.
|
||||
assertFalse(getService().isRecording());
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertEquals(-1, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testResumeAfterReboot_expiredTrack() throws Exception {
|
||||
// Insert a dummy track last updated 20 min ago.
|
||||
createDummyTrack(123, System.currentTimeMillis() - 20 * 60 * 1000, true);
|
||||
|
||||
// Clear the number of attempts and set the timeout to 10 min.
|
||||
updateAutoResumePrefs(0, 10);
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent();
|
||||
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
|
||||
startService(startIntent);
|
||||
assertNotNull(getService());
|
||||
|
||||
// We don't expect to resume the previous track, because it has expired.
|
||||
assertFalse(getService().isRecording());
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertEquals(-1, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testResumeAfterReboot_tooManyAttempts() throws Exception {
|
||||
// Insert a dummy track.
|
||||
createDummyTrack(123, System.currentTimeMillis(), true);
|
||||
|
||||
// Set the number of attempts to max.
|
||||
updateAutoResumePrefs(
|
||||
TrackRecordingService.MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS, 10);
|
||||
|
||||
// Start the service in "resume" mode (simulates the on-reboot action).
|
||||
Intent startIntent = createStartIntent();
|
||||
startIntent.putExtra(RESUME_TRACK_EXTRA_NAME, true);
|
||||
startService(startIntent);
|
||||
assertNotNull(getService());
|
||||
|
||||
// We don't expect to resume the previous track, because there were already
|
||||
// too many attempts.
|
||||
assertFalse(getService().isRecording());
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertEquals(-1, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testRecording_noTracks() throws Exception {
|
||||
List<Track> tracks = providerUtils.getAllTracks();
|
||||
assertTrue(tracks.isEmpty());
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
// Test if we start in no-recording mode by default.
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testRecording_oldTracks() throws Exception {
|
||||
createDummyTrack(123, -1, false);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testRecording_orphanedRecordingTrack() throws Exception {
|
||||
// Just set recording track to a bogus value.
|
||||
setRecordingTrack(256);
|
||||
|
||||
// Make sure that the service will not start recording and will clear
|
||||
// the bogus track.
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronous/waitable broadcast receiver to be used in testing.
|
||||
*/
|
||||
private class BlockingBroadcastReceiver extends BroadcastReceiver {
|
||||
private static final long MAX_WAIT_TIME_MS = 10000;
|
||||
private List<Intent> receivedIntents = new ArrayList<Intent>();
|
||||
|
||||
public List<Intent> getReceivedIntents() {
|
||||
return receivedIntents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Log.d("MyTracksTest", "Got broadcast: " + intent);
|
||||
synchronized (receivedIntents) {
|
||||
receivedIntents.add(intent);
|
||||
receivedIntents.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean waitUntilReceived(int receiveCount) {
|
||||
long deadline = System.currentTimeMillis() + MAX_WAIT_TIME_MS;
|
||||
synchronized (receivedIntents) {
|
||||
while (receivedIntents.size() < receiveCount) {
|
||||
try {
|
||||
// Wait releases synchronized lock until it returns
|
||||
receivedIntents.wait(500);
|
||||
} catch (InterruptedException e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
if (System.currentTimeMillis() > deadline) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testStartNewTrack_noRecording() throws Exception {
|
||||
// NOTICE: due to the way Android permissions work, if this fails,
|
||||
// uninstall the test apk then retry - the test must be installed *after*
|
||||
// My Tracks (go figure).
|
||||
// Reference: http://code.google.com/p/android/issues/detail?id=5521
|
||||
BlockingBroadcastReceiver startReceiver = new BlockingBroadcastReceiver();
|
||||
String startAction = context.getString(R.string.track_started_broadcast_action);
|
||||
context.registerReceiver(startReceiver, new IntentFilter(startAction));
|
||||
|
||||
List<Track> tracks = providerUtils.getAllTracks();
|
||||
assertTrue(tracks.isEmpty());
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
long id = service.startNewTrack();
|
||||
assertTrue(id >= 0);
|
||||
assertTrue(service.isRecording());
|
||||
Track track = providerUtils.getTrack(id);
|
||||
assertNotNull(track);
|
||||
assertEquals(id, track.getId());
|
||||
assertEquals(id, sharedPreferences.getLong(
|
||||
context.getString(R.string.recording_track_key), -1));
|
||||
assertEquals(id, service.getRecordingTrackId());
|
||||
|
||||
// Verify that the start broadcast was received.
|
||||
assertTrue(startReceiver.waitUntilReceived(1));
|
||||
List<Intent> receivedIntents = startReceiver.getReceivedIntents();
|
||||
assertEquals(1, receivedIntents.size());
|
||||
Intent broadcastIntent = receivedIntents.get(0);
|
||||
assertEquals(startAction, broadcastIntent.getAction());
|
||||
assertEquals(id, broadcastIntent.getLongExtra(
|
||||
context.getString(R.string.track_id_broadcast_extra), -1));
|
||||
|
||||
context.unregisterReceiver(startReceiver);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testStartNewTrack_alreadyRecording() throws Exception {
|
||||
createDummyTrack(123, -1, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
try {
|
||||
service.startNewTrack();
|
||||
fail("Expecting IllegalStateException");
|
||||
} catch (IllegalStateException e) {
|
||||
// Expected.
|
||||
}
|
||||
assertEquals(123, sharedPreferences.getLong(
|
||||
context.getString(R.string.recording_track_key), 0));
|
||||
assertEquals(123, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testEndCurrentTrack_alreadyRecording() throws Exception {
|
||||
// See comment above if this fails randomly.
|
||||
BlockingBroadcastReceiver stopReceiver = new BlockingBroadcastReceiver();
|
||||
String stopAction = context.getString(R.string.track_stopped_broadcast_action);
|
||||
context.registerReceiver(stopReceiver, new IntentFilter(stopAction));
|
||||
|
||||
createDummyTrack(123, -1, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
// End the current track.
|
||||
service.endCurrentTrack();
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1, sharedPreferences.getLong(
|
||||
context.getString(R.string.recording_track_key), 0));
|
||||
assertEquals(-1, service.getRecordingTrackId());
|
||||
|
||||
// Verify that the stop broadcast was received.
|
||||
assertTrue(stopReceiver.waitUntilReceived(1));
|
||||
List<Intent> receivedIntents = stopReceiver.getReceivedIntents();
|
||||
assertEquals(1, receivedIntents.size());
|
||||
Intent broadcastIntent = receivedIntents.get(0);
|
||||
assertEquals(stopAction, broadcastIntent.getAction());
|
||||
assertEquals(123, broadcastIntent.getLongExtra(
|
||||
context.getString(R.string.track_id_broadcast_extra), -1));
|
||||
|
||||
context.unregisterReceiver(stopReceiver);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testEndCurrentTrack_noRecording() throws Exception {
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// End the current track.
|
||||
try {
|
||||
service.endCurrentTrack();
|
||||
fail("Expecting IllegalStateException");
|
||||
} catch (IllegalStateException e) {
|
||||
// Expected.
|
||||
}
|
||||
assertEquals(-1, sharedPreferences.getLong(
|
||||
context.getString(R.string.recording_track_key), 0));
|
||||
assertEquals(-1, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testIntegration_completeRecordingSession() throws Exception {
|
||||
List<Track> tracks = providerUtils.getAllTracks();
|
||||
assertTrue(tracks.isEmpty());
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testDeleteAllTracks_noRecording() throws Exception {
|
||||
createDummyTrack(123, -1, false);
|
||||
assertEquals(1, providerUtils.getAllTracks().size());
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// Deleting all tracks should succeed.
|
||||
service.deleteAllTracks();
|
||||
assertFalse(service.isRecording());
|
||||
assertTrue(providerUtils.getAllTracks().isEmpty());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testDeleteAllTracks_noTracks() throws Exception {
|
||||
assertTrue(providerUtils.getAllTracks().isEmpty());
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// Deleting all tracks should succeed.
|
||||
service.deleteAllTracks();
|
||||
assertFalse(service.isRecording());
|
||||
assertTrue(providerUtils.getAllTracks().isEmpty());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testDeleteAllTracks_trackInProgress() throws Exception {
|
||||
createDummyTrack(123, -1, true);
|
||||
assertEquals(1, providerUtils.getAllTracks().size());
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
// Since we have a track in progress, we expect to fail.
|
||||
try {
|
||||
service.deleteAllTracks();
|
||||
fail("Expecting IllegalStateException");
|
||||
} catch (IllegalStateException e) {
|
||||
// Expected.
|
||||
}
|
||||
assertTrue(service.isRecording());
|
||||
assertEquals(1, providerUtils.getAllTracks().size());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testHasRecorded_noTracks() throws Exception {
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
assertFalse(service.hasRecorded());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testHasRecorded_trackInProgress() throws Exception {
|
||||
createDummyTrack(123, -1, true);
|
||||
assertEquals(1, providerUtils.getAllTracks().size());
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
assertTrue(service.hasRecorded());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testHasRecorded_oldTracks() throws Exception {
|
||||
createDummyTrack(123, -1, false);
|
||||
assertEquals(1, providerUtils.getAllTracks().size());
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
assertTrue(service.hasRecorded());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testInsertStatisticsMarker_noRecordingTrack() throws Exception {
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
try {
|
||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
fail("Expecting IllegalStateException");
|
||||
} catch (IllegalStateException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testInsertStatisticsMarker_validLocation() throws Exception {
|
||||
createDummyTrack(123, -1, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||
assertEquals(2, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||
|
||||
Waypoint wpt = providerUtils.getWaypoint(1);
|
||||
assertEquals(getContext().getString(R.string.stats_icon_url),
|
||||
wpt.getIcon());
|
||||
assertEquals(getContext().getString(R.string.statistics),
|
||||
wpt.getName());
|
||||
assertEquals(Waypoint.TYPE_STATISTICS, wpt.getType());
|
||||
assertEquals(123, wpt.getTrackId());
|
||||
assertEquals(0.0, wpt.getLength());
|
||||
assertNotNull(wpt.getLocation());
|
||||
assertNotNull(wpt.getStatistics());
|
||||
// TODO check the rest of the params.
|
||||
|
||||
// TODO: Check waypoint 2.
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testInsertWaypointMarker_noRecordingTrack() throws Exception {
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
try {
|
||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_MARKER);
|
||||
fail("Expecting IllegalStateException");
|
||||
} catch (IllegalStateException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testInsertWaypointMarker_validWaypoint() throws Exception {
|
||||
createDummyTrack(123, -1, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_MARKER));
|
||||
Waypoint wpt = providerUtils.getWaypoint(1);
|
||||
assertEquals(getContext().getString(R.string.waypoint_icon_url),
|
||||
wpt.getIcon());
|
||||
assertEquals(getContext().getString(R.string.waypoint),
|
||||
wpt.getName());
|
||||
assertEquals(Waypoint.TYPE_WAYPOINT, wpt.getType());
|
||||
assertEquals(123, wpt.getTrackId());
|
||||
assertEquals(0.0, wpt.getLength());
|
||||
assertNotNull(wpt.getLocation());
|
||||
assertNull(wpt.getStatistics());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_noAnnouncementFreq() throws Exception {
|
||||
functionalTest(R.string.announcement_frequency_key, (Object) null);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_defaultAnnouncementFreq() throws Exception {
|
||||
functionalTest(R.string.announcement_frequency_key, 1);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_noMaxRecordingDist() throws Exception {
|
||||
functionalTest(R.string.max_recording_distance_key, (Object) null);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_defaultMaxRecordingDist() throws Exception {
|
||||
functionalTest(R.string.max_recording_distance_key, 5);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_noMinRecordingDist() throws Exception {
|
||||
functionalTest(R.string.min_recording_distance_key, (Object) null);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_defaultMinRecordingDist() throws Exception {
|
||||
functionalTest(R.string.min_recording_distance_key, 2);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_noSplitFreq() throws Exception {
|
||||
functionalTest(R.string.split_frequency_key, (Object) null);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_defaultSplitFreqByDist() throws Exception {
|
||||
functionalTest(R.string.split_frequency_key, 5);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_defaultSplitFreqByTime() throws Exception {
|
||||
functionalTest(R.string.split_frequency_key, -2);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_noMetricUnits() throws Exception {
|
||||
functionalTest(R.string.metric_units_key, (Object) null);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_metricUnitsEnabled() throws Exception {
|
||||
functionalTest(R.string.metric_units_key, true);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_metricUnitsDisabled() throws Exception {
|
||||
functionalTest(R.string.metric_units_key, false);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_noMinRecordingInterval() throws Exception {
|
||||
functionalTest(R.string.min_recording_interval_key, (Object) null);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_defaultMinRecordingInterval()
|
||||
throws Exception {
|
||||
functionalTest(R.string.min_recording_interval_key, 3);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_noMinRequiredAccuracy() throws Exception {
|
||||
functionalTest(R.string.min_required_accuracy_key, (Object) null);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_defaultMinRequiredAccuracy() throws Exception {
|
||||
functionalTest(R.string.min_required_accuracy_key, 500);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_noSensorType() throws Exception {
|
||||
functionalTest(R.string.sensor_type_key, (Object) null);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_zephyrSensorType() throws Exception {
|
||||
functionalTest(R.string.sensor_type_key,
|
||||
context.getString(R.string.zephyr_sensor_type));
|
||||
}
|
||||
|
||||
private ITrackRecordingService bindAndGetService(Intent intent) {
|
||||
ITrackRecordingService service = ITrackRecordingService.Stub.asInterface(
|
||||
bindService(intent));
|
||||
assertNotNull(service);
|
||||
return service;
|
||||
}
|
||||
|
||||
private Track createDummyTrack(long id, long stopTime, boolean isRecording) {
|
||||
Track dummyTrack = new Track();
|
||||
dummyTrack.setId(id);
|
||||
dummyTrack.setName("Dummy Track");
|
||||
TripStatistics tripStatistics = new TripStatistics();
|
||||
tripStatistics.setStopTime(stopTime);
|
||||
dummyTrack.setStatistics(tripStatistics);
|
||||
addTrack(dummyTrack, isRecording);
|
||||
return dummyTrack;
|
||||
}
|
||||
|
||||
private void updateAutoResumePrefs(int attempts, int timeoutMins) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putInt(context.getString(
|
||||
R.string.auto_resume_track_current_retry_key), attempts);
|
||||
editor.putInt(context.getString(
|
||||
R.string.auto_resume_track_timeout_key), timeoutMins);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private Intent createStartIntent() {
|
||||
Intent startIntent = new Intent();
|
||||
startIntent.setClass(context, TrackRecordingService.class);
|
||||
return startIntent;
|
||||
}
|
||||
|
||||
private void addTrack(Track track, boolean isRecording) {
|
||||
assertTrue(track.getId() >= 0);
|
||||
providerUtils.insertTrack(track);
|
||||
assertEquals(track.getId(), providerUtils.getTrack(track.getId()).getId());
|
||||
setRecordingTrack(isRecording ? track.getId() : -1);
|
||||
}
|
||||
|
||||
private void setRecordingTrack(long id) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putLong(context.getString(R.string.recording_track_key), id);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
// TODO: We support multiple values for readability, however this test's
|
||||
// base class doesn't properly shutdown the service, so it's not possible
|
||||
// to pass more than 1 value at a time.
|
||||
private void functionalTest(int resourceId, Object ...values)
|
||||
throws Exception {
|
||||
final String key = context.getString(resourceId);
|
||||
for (Object value : values) {
|
||||
// Remove all properties and set the property for the given key.
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.clear();
|
||||
if (value instanceof String) {
|
||||
editor.putString(key, (String) value);
|
||||
} else if (value instanceof Long) {
|
||||
editor.putLong(key, (Long) value);
|
||||
} else if (value instanceof Integer) {
|
||||
editor.putInt(key, (Integer) value);
|
||||
} else if (value instanceof Boolean) {
|
||||
editor.putBoolean(key, (Boolean) value);
|
||||
} else if (value == null) {
|
||||
// Do nothing, as clear above has already removed this property.
|
||||
}
|
||||
editor.commit();
|
||||
|
||||
fullRecordingSession();
|
||||
}
|
||||
}
|
||||
|
||||
private void fullRecordingSession() throws Exception {
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// Start a track.
|
||||
long id = service.startNewTrack();
|
||||
assertTrue(id >= 0);
|
||||
assertTrue(service.isRecording());
|
||||
Track track = providerUtils.getTrack(id);
|
||||
assertNotNull(track);
|
||||
assertEquals(id, track.getId());
|
||||
assertEquals(id, sharedPreferences.getLong(
|
||||
context.getString(R.string.recording_track_key), -1));
|
||||
assertEquals(id, service.getRecordingTrackId());
|
||||
|
||||
// Insert a few points, markers and statistics.
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < 30; i++) {
|
||||
Location loc = new Location("gps");
|
||||
loc.setLongitude(35.0f + i / 10.0f);
|
||||
loc.setLatitude(45.0f - i / 5.0f);
|
||||
loc.setAccuracy(5);
|
||||
loc.setSpeed(10);
|
||||
loc.setTime(startTime + i * 10000);
|
||||
loc.setBearing(3.0f);
|
||||
service.recordLocation(loc);
|
||||
|
||||
if (i % 10 == 0) {
|
||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS);
|
||||
} else if (i % 7 == 0) {
|
||||
service.insertWaypoint(WaypointCreationRequest.DEFAULT_MARKER);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the track. Validate if it has correct data.
|
||||
service.endCurrentTrack();
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1, service.getRecordingTrackId());
|
||||
track = providerUtils.getTrack(id);
|
||||
assertNotNull(track);
|
||||
assertEquals(id, track.getId());
|
||||
TripStatistics tripStatistics = track.getStatistics();
|
||||
assertNotNull(tripStatistics);
|
||||
assertTrue(tripStatistics.getStartTime() > 0);
|
||||
assertTrue(tripStatistics.getStopTime() >= tripStatistics.getStartTime());
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2009 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.services.sensors;
|
||||
|
||||
import com.dsi.ant.AntMesg;
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
public class AntDirectSensorManagerTest extends AndroidTestCase {
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
private AntDirectSensorManager manager;
|
||||
|
||||
public void setUp() {
|
||||
sharedPreferences = getContext().getSharedPreferences(
|
||||
MyTracksSettings.SETTINGS_NAME, 0);
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().commit();
|
||||
manager = new AntDirectSensorManager(getContext());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@SmallTest
|
||||
public void testBroadcastData() {
|
||||
manager.setDeviceNumberHRM((short) 42);
|
||||
byte[] buff = new byte[11];
|
||||
buff[0] = 9;
|
||||
buff[1] = AntMesg.MESG_BROADCAST_DATA_ID;
|
||||
buff[2] = 0; // HRM CHANNEL
|
||||
buff[10] = (byte) 220;
|
||||
manager.handleMessage(buff);
|
||||
|
||||
Sensor.SensorDataSet sds = manager.getSensorDataSet();
|
||||
assertNotNull(sds);
|
||||
assertTrue(sds.hasHeartRate());
|
||||
assertEquals(Sensor.SensorState.SENDING,
|
||||
sds.getHeartRate().getState());
|
||||
assertEquals(220, sds.getHeartRate().getValue());
|
||||
|
||||
assertFalse(sds.hasCadence());
|
||||
assertFalse(sds.hasPower());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@SmallTest
|
||||
public void testChannelId() {
|
||||
byte[] buff = new byte[11];
|
||||
buff[0] = 9;
|
||||
buff[1] = AntMesg.MESG_CHANNEL_ID_ID;
|
||||
buff[3] = 42;
|
||||
manager.handleMessage(buff);
|
||||
|
||||
assertEquals(42, manager.getDeviceNumberHRM());
|
||||
assertEquals(42,
|
||||
sharedPreferences.getInt(
|
||||
getContext().getString(R.string.ant_heart_rate_sensor_id_key), -1));
|
||||
assertNull(manager.getSensorDataSet());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@SmallTest
|
||||
public void testResponseEvent() {
|
||||
assertEquals(Sensor.SensorState.NONE, manager.getSensorState());
|
||||
byte[] buff = new byte[5];
|
||||
buff[0] = 3; // length
|
||||
buff[1] = AntMesg.MESG_RESPONSE_EVENT_ID;
|
||||
buff[2] = 0; // channel
|
||||
buff[3] = AntMesg.MESG_UNASSIGN_CHANNEL_ID;
|
||||
buff[4] = 0; // code
|
||||
manager.handleMessage(buff);
|
||||
|
||||
assertEquals(Sensor.SensorState.DISCONNECTED, manager.getSensorState());
|
||||
assertNull(manager.getSensorDataSet());
|
||||
}
|
||||
|
||||
// TODO: Test timeout too.
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.services.sensors;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.MoreAsserts;
|
||||
|
||||
public class AntSensorManagerTest extends AndroidTestCase {
|
||||
private class TestAntSensorManager extends AntSensorManager {
|
||||
public byte messageId;
|
||||
public byte[] messageData;
|
||||
|
||||
public TestAntSensorManager() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupAntSensorChannels() {}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void handleMessage(byte[] rawMessage) {
|
||||
super.handleMessage(rawMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(byte messageId, byte[] messageData) {
|
||||
this.messageId = messageId;
|
||||
this.messageData = messageData;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private final TestAntSensorManager sensorManager = new TestAntSensorManager();
|
||||
|
||||
public void testSimple() {
|
||||
byte[] rawMessage = {
|
||||
0x03, // length
|
||||
0x12, // message id
|
||||
0x11, 0x22, 0x33, // body
|
||||
};
|
||||
|
||||
byte[] expectedBody = { 0x11, 0x22, 0x33 };
|
||||
|
||||
sensorManager.handleMessage(rawMessage);
|
||||
assertEquals((byte) 0x12, sensorManager.messageId);
|
||||
MoreAsserts.assertEquals(expectedBody, sensorManager.messageData);
|
||||
}
|
||||
|
||||
public void testTooShort() {
|
||||
byte[] rawMessage = {
|
||||
0x53, // length
|
||||
0x12 // message id
|
||||
};
|
||||
|
||||
sensorManager.handleMessage(rawMessage);
|
||||
assertEquals(0, sensorManager.messageId);
|
||||
assertNull(sensorManager.messageData);
|
||||
}
|
||||
|
||||
public void testLengthWrong() {
|
||||
byte[] rawMessage = {
|
||||
0x53, // length
|
||||
0x12, // message id
|
||||
0x34, // body
|
||||
};
|
||||
|
||||
sensorManager.handleMessage(rawMessage);
|
||||
assertEquals(0, sensorManager.messageId);
|
||||
assertNull(sensorManager.messageData);
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksSettings;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
public class SensorManagerFactoryTest extends AndroidTestCase {
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
sharedPreferences = getContext().getSharedPreferences(
|
||||
MyTracksSettings.SETTINGS_NAME, 0);
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().commit();
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testDefaultSettings() throws Exception {
|
||||
assertNull(SensorManagerFactory.getSensorManager(getContext()));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testCreateZephyr() throws Exception {
|
||||
assertClassForName(ZephyrSensorManager.class, R.string.zephyr_sensor_type);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testCreateAnt() throws Exception {
|
||||
assertClassForName(AntDirectSensorManager.class, R.string.ant_sensor_type);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testCreateAntSRM() throws Exception {
|
||||
assertClassForName(AntSRMSensorManager.class, R.string.srm_ant_bridge_sensor_type);
|
||||
}
|
||||
|
||||
private void assertClassForName(Class<?> c, int i) {
|
||||
sharedPreferences.edit()
|
||||
.putString(getContext().getString(R.string.sensor_type_key),
|
||||
getContext().getString(i))
|
||||
.commit();
|
||||
SensorManager sm = SensorManagerFactory.getSensorManager(getContext());
|
||||
assertNotNull(sm);
|
||||
assertTrue(c.isInstance(sm));
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class ZephyrMessageParserTest extends TestCase {
|
||||
|
||||
ZephyrMessageParser parser = new ZephyrMessageParser();
|
||||
|
||||
public void testIsValid() {
|
||||
byte[] buf = new byte[60];
|
||||
assertFalse(parser.isValid(buf));
|
||||
buf[0] = 0x02;
|
||||
assertFalse(parser.isValid(buf));
|
||||
buf[59] = 0x03;
|
||||
assertTrue(parser.isValid(buf));
|
||||
}
|
||||
|
||||
public void testParseBuffer() {
|
||||
byte[] buf = new byte[60];
|
||||
buf[12] = 50;
|
||||
Sensor.SensorDataSet sds = parser.parseBuffer(buf);
|
||||
assertTrue(sds.hasHeartRate());
|
||||
assertTrue(sds.getHeartRate().getState() == Sensor.SensorState.SENDING);
|
||||
assertEquals(50, sds.getHeartRate().getValue());
|
||||
}
|
||||
|
||||
public void testFindNextAlignment() {
|
||||
byte[] buf = new byte[60];
|
||||
assertEquals(-1, parser.findNextAlignment(buf));
|
||||
buf[10] = 0x03;
|
||||
buf[11] = 0x02;
|
||||
assertEquals(10, parser.findNextAlignment(buf));
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.services.sensors.ant;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
public class AntChannelIdMessageTest extends AndroidTestCase {
|
||||
public void testParse() {
|
||||
byte[] rawMessage = {
|
||||
0, // channel number
|
||||
0x34, 0x12, // device number
|
||||
(byte) 0xaa, // device type id
|
||||
(byte) 0xbb, // transmission type
|
||||
};
|
||||
|
||||
AntChannelIdMessage message = new AntChannelIdMessage(rawMessage);
|
||||
assertEquals(0, message.getChannelNumber());
|
||||
assertEquals(0x1234, message.getDeviceNumber());
|
||||
assertEquals((byte) 0xaa, message.getDeviceTypeId());
|
||||
assertEquals((byte) 0xbb, message.getTransmissionType());
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.services.sensors.ant;
|
||||
|
||||
import com.dsi.ant.AntDefine;
|
||||
import com.dsi.ant.AntMesg;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
public class AntChannelResponseMessageTest extends AndroidTestCase {
|
||||
public void testParse() {
|
||||
byte[] rawMessage = {
|
||||
0,
|
||||
AntMesg.MESG_EVENT_ID,
|
||||
AntDefine.EVENT_RX_SEARCH_TIMEOUT
|
||||
};
|
||||
AntChannelResponseMessage message = new AntChannelResponseMessage(rawMessage);
|
||||
|
||||
assertEquals(0, message.getChannelNumber());
|
||||
assertEquals(AntMesg.MESG_EVENT_ID, message.getMessageId());
|
||||
assertEquals(AntDefine.EVENT_RX_SEARCH_TIMEOUT, message.getMessageCode());
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.services.sensors.ant;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
public class AntMessageTest extends AndroidTestCase {
|
||||
private static class TestAntMessage extends AntMessage {
|
||||
public static short decodeShort(byte low, byte high) {
|
||||
return AntMessage.decodeShort(low, high);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDecode() {
|
||||
assertEquals(0x1234, TestAntMessage.decodeShort((byte) 0x34, (byte) 0x12));
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.services.sensors.ant;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
public class AntStartupMessageTest extends AndroidTestCase {
|
||||
public void testParse() {
|
||||
byte[] rawMessage = {
|
||||
0x12,
|
||||
};
|
||||
|
||||
AntStartupMessage message = new AntStartupMessage(rawMessage);
|
||||
assertEquals(0x12, message.getMessage());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2009 Google Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
package com.google.android.apps.mytracks.stats;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Test for the DoubleBuffer class.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class DoubleBufferTest extends TestCase {
|
||||
|
||||
/**
|
||||
* Tests that the constructor leaves the buffer in a valid state.
|
||||
*/
|
||||
public void testConstructor() {
|
||||
DoubleBuffer buffer = new DoubleBuffer(10);
|
||||
assertFalse(buffer.isFull());
|
||||
assertEquals(0.0, buffer.getAverage());
|
||||
double[] averageAndVariance = buffer.getAverageAndVariance();
|
||||
assertEquals(0.0, averageAndVariance[0]);
|
||||
assertEquals(0.0, averageAndVariance[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple test with 10 of the same values.
|
||||
*/
|
||||
public void testBasic() {
|
||||
DoubleBuffer buffer = new DoubleBuffer(10);
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
buffer.setNext(1.0);
|
||||
assertFalse(buffer.isFull());
|
||||
assertEquals(1.0, buffer.getAverage());
|
||||
double[] averageAndVariance = buffer.getAverageAndVariance();
|
||||
assertEquals(1.0, averageAndVariance[0]);
|
||||
assertEquals(0.0, averageAndVariance[1]);
|
||||
}
|
||||
buffer.setNext(1);
|
||||
assertTrue(buffer.isFull());
|
||||
assertEquals(1.0, buffer.getAverage());
|
||||
double[] averageAndVariance = buffer.getAverageAndVariance();
|
||||
assertEquals(1.0, averageAndVariance[0]);
|
||||
assertEquals(0.0, averageAndVariance[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests with 5 entries of -10 and 5 entries of 10.
|
||||
*/
|
||||
public void testSplit() {
|
||||
DoubleBuffer buffer = new DoubleBuffer(10);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
buffer.setNext(-10);
|
||||
assertFalse(buffer.isFull());
|
||||
assertEquals(-10.0, buffer.getAverage());
|
||||
double[] averageAndVariance = buffer.getAverageAndVariance();
|
||||
assertEquals(-10.0, averageAndVariance[0]);
|
||||
assertEquals(0.0, averageAndVariance[1]);
|
||||
}
|
||||
|
||||
for (int i = 1; i < 5; i++) {
|
||||
buffer.setNext(10);
|
||||
assertFalse(buffer.isFull());
|
||||
double expectedAverage = ((i * 10.0) - 50.0) / (i + 5);
|
||||
assertEquals(buffer.toString(),
|
||||
expectedAverage, buffer.getAverage(), 0.01);
|
||||
double[] averageAndVariance = buffer.getAverageAndVariance();
|
||||
assertEquals(expectedAverage, averageAndVariance[0]);
|
||||
}
|
||||
buffer.setNext(10);
|
||||
assertTrue(buffer.isFull());
|
||||
assertEquals(0.0, buffer.getAverage());
|
||||
double[] averageAndVariance = buffer.getAverageAndVariance();
|
||||
assertEquals(0.0, averageAndVariance[0]);
|
||||
assertEquals(100.0, averageAndVariance[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that reset leaves the buffer in a valid state.
|
||||
*/
|
||||
public void testReset() {
|
||||
DoubleBuffer buffer = new DoubleBuffer(10);
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
buffer.setNext(i);
|
||||
}
|
||||
assertTrue(buffer.isFull());
|
||||
|
||||
buffer.reset();
|
||||
assertFalse(buffer.isFull());
|
||||
assertEquals(0.0, buffer.getAverage());
|
||||
double[] averageAndVariance = buffer.getAverageAndVariance();
|
||||
assertEquals(0.0, averageAndVariance[0]);
|
||||
assertEquals(0.0, averageAndVariance[1]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests that if a lot of items are inserted the smoothing and looping works.
|
||||
*/
|
||||
public void testLoop() {
|
||||
DoubleBuffer buffer = new DoubleBuffer(10);
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
buffer.setNext(i);
|
||||
assertEquals(i >= 9, buffer.isFull());
|
||||
if (i > 10) {
|
||||
assertEquals(i - 4.5, buffer.getAverage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright 2009 Google Inc. All Rights Reserved.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.stats;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This class test the ExtremityMonitor class.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ExtremityMonitorTest extends TestCase {
|
||||
|
||||
public ExtremityMonitorTest(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public void testInitialize() {
|
||||
ExtremityMonitor monitor = new ExtremityMonitor();
|
||||
assertEquals(Double.POSITIVE_INFINITY, monitor.getMin());
|
||||
assertEquals(Double.NEGATIVE_INFINITY, monitor.getMax());
|
||||
}
|
||||
|
||||
public void testSimple() {
|
||||
ExtremityMonitor monitor = new ExtremityMonitor();
|
||||
assertTrue(monitor.update(0));
|
||||
assertTrue(monitor.update(1));
|
||||
assertEquals(0.0, monitor.getMin());
|
||||
assertEquals(1.0, monitor.getMax());
|
||||
assertFalse(monitor.update(1));
|
||||
assertFalse(monitor.update(0.5));
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a bunch of random numbers between [0,1] at the monitor.
|
||||
*/
|
||||
public void testRandom() {
|
||||
ExtremityMonitor monitor = new ExtremityMonitor();
|
||||
Random random = new Random(42);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
monitor.update(random.nextDouble());
|
||||
}
|
||||
assertTrue(monitor.getMin() < 0.1);
|
||||
assertTrue(monitor.getMax() < 1.0);
|
||||
assertTrue(monitor.getMin() >= 0.0);
|
||||
assertTrue(monitor.getMax() > 0.9);
|
||||
}
|
||||
|
||||
public void testReset() {
|
||||
ExtremityMonitor monitor = new ExtremityMonitor();
|
||||
assertTrue(monitor.update(0));
|
||||
assertTrue(monitor.update(1));
|
||||
monitor.reset();
|
||||
assertEquals(Double.POSITIVE_INFINITY, monitor.getMin());
|
||||
assertEquals(Double.NEGATIVE_INFINITY, monitor.getMax());
|
||||
assertTrue(monitor.update(0));
|
||||
assertTrue(monitor.update(1));
|
||||
assertEquals(0.0, monitor.getMin());
|
||||
assertEquals(1.0, monitor.getMax());
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
// Copyright 2009 Google Inc. All Rights Reserved.
|
||||
|
||||
package com.google.android.apps.mytracks.stats;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
|
||||
import android.location.Location;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Test the the function of the TripStatisticsBuilder class.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class TripStatisticsBuilderTest extends TestCase {
|
||||
|
||||
private TripStatisticsBuilder builder = null;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
builder = new TripStatisticsBuilder(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public void testAddLocationSimple() throws Exception {
|
||||
builder = new TripStatisticsBuilder(1000);
|
||||
TripStatistics stats = builder.getStatistics();
|
||||
|
||||
assertEquals(0.0, builder.getSmoothedElevation());
|
||||
assertEquals(Double.POSITIVE_INFINITY, stats.getMinElevation());
|
||||
assertEquals(Double.NEGATIVE_INFINITY, stats.getMaxElevation());
|
||||
assertEquals(0.0, stats.getMaxSpeed());
|
||||
assertEquals(Double.POSITIVE_INFINITY, stats.getMinGrade());
|
||||
assertEquals(Double.NEGATIVE_INFINITY, stats.getMaxGrade());
|
||||
assertEquals(0.0, stats.getTotalElevationGain());
|
||||
assertEquals(0, stats.getMovingTime());
|
||||
assertEquals(0.0, stats.getTotalDistance());
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Location l = new Location("test");
|
||||
l.setAccuracy(1.0f);
|
||||
l.setLongitude(45.0);
|
||||
|
||||
// Going up by 5 meters each time.
|
||||
l.setAltitude(i);
|
||||
// Moving by .1% of a degree latitude.
|
||||
l.setLatitude(i * .001);
|
||||
l.setSpeed(11.1f);
|
||||
// Each time slice is 10 seconds.
|
||||
long time = 1000 + 10000 * i;
|
||||
l.setTime(time);
|
||||
boolean moving = builder.addLocation(l, time);
|
||||
assertEquals((i != 0), moving);
|
||||
|
||||
stats = builder.getStatistics();
|
||||
assertEquals(10000 * i, stats.getTotalTime());
|
||||
assertEquals(10000 * i, stats.getMovingTime());
|
||||
assertEquals(i, builder.getSmoothedElevation(),
|
||||
MyTracksConstants.ELEVATION_SMOOTHING_FACTOR / 2);
|
||||
assertEquals(0.0, stats.getMinElevation());
|
||||
assertEquals(i, stats.getMaxElevation(),
|
||||
MyTracksConstants.ELEVATION_SMOOTHING_FACTOR / 2);
|
||||
assertEquals(i, stats.getTotalElevationGain(),
|
||||
MyTracksConstants.ELEVATION_SMOOTHING_FACTOR);
|
||||
|
||||
if (i > MyTracksConstants.SPEED_SMOOTHING_FACTOR) {
|
||||
assertEquals(11.1f, stats.getMaxSpeed(), 0.1);
|
||||
}
|
||||
if ((i > MyTracksConstants.GRADE_SMOOTHING_FACTOR)
|
||||
&& (i > MyTracksConstants.ELEVATION_SMOOTHING_FACTOR)) {
|
||||
assertEquals(0.009, stats.getMinGrade(), 0.0001);
|
||||
assertEquals(0.009, stats.getMaxGrade(), 0.0001);
|
||||
}
|
||||
// 1 degree = 111 km
|
||||
// 1 timeslice = 0.001 degree = 111 m
|
||||
assertEquals(111.0 * i, stats.getTotalDistance(), 100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that elevation works if the user is stable.
|
||||
*/
|
||||
public void testElevationSimple() throws Exception {
|
||||
for (double elevation = 0; elevation < 1000; elevation += 10) {
|
||||
builder = new TripStatisticsBuilder(System.currentTimeMillis());
|
||||
for (int j = 0; j < 100; j++) {
|
||||
assertEquals(0.0, builder.updateElevation(elevation));
|
||||
assertEquals(elevation, builder.getSmoothedElevation());
|
||||
TripStatistics data = builder.getStatistics();
|
||||
assertEquals(elevation, data.getMinElevation());
|
||||
assertEquals(elevation, data.getMaxElevation());
|
||||
assertEquals(0.0, data.getTotalElevationGain());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testElevationGain() throws Exception {
|
||||
for (double i = 0; i < 1000; i++) {
|
||||
double expectedGain;
|
||||
if (i < (MyTracksConstants.ELEVATION_SMOOTHING_FACTOR - 1)) {
|
||||
expectedGain = 0;
|
||||
} else if (i < MyTracksConstants.ELEVATION_SMOOTHING_FACTOR) {
|
||||
expectedGain = 0.5;
|
||||
} else {
|
||||
expectedGain = 1.0;
|
||||
}
|
||||
assertEquals(expectedGain,
|
||||
builder.updateElevation(i));
|
||||
assertEquals(i, builder.getSmoothedElevation(), 20);
|
||||
TripStatistics data = builder.getStatistics();
|
||||
assertEquals(0.0, data.getMinElevation(), 0.0);
|
||||
assertEquals(i, data.getMaxElevation(),
|
||||
MyTracksConstants.ELEVATION_SMOOTHING_FACTOR);
|
||||
assertEquals(i, data.getTotalElevationGain(),
|
||||
MyTracksConstants.ELEVATION_SMOOTHING_FACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
public void testGradeSimple() throws Exception {
|
||||
for (double i = 0; i < 1000; i++) {
|
||||
// The value of the elevation does not matter. This is just to fill the
|
||||
// buffer.
|
||||
builder.updateElevation(i);
|
||||
builder.updateGrade(100, 100);
|
||||
if ((i > MyTracksConstants.GRADE_SMOOTHING_FACTOR)
|
||||
&& (i > MyTracksConstants.ELEVATION_SMOOTHING_FACTOR)) {
|
||||
assertEquals(1.0, builder.getStatistics().getMaxGrade());
|
||||
assertEquals(1.0, builder.getStatistics().getMinGrade());
|
||||
}
|
||||
}
|
||||
for (double i = 0; i < 1000; i++) {
|
||||
// The value of the elevation does not matter. This is just to fill the
|
||||
// buffer.
|
||||
builder.updateElevation(i);
|
||||
builder.updateGrade(100, -100);
|
||||
if ((i > MyTracksConstants.GRADE_SMOOTHING_FACTOR)
|
||||
&& (i > MyTracksConstants.ELEVATION_SMOOTHING_FACTOR)) {
|
||||
assertEquals(1.0, builder.getStatistics().getMaxGrade());
|
||||
assertEquals(-1.0, builder.getStatistics().getMinGrade());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testGradeIgnoreShort() throws Exception {
|
||||
for (double i = 0; i < 100; i++) {
|
||||
// The value of the elevation does not matter. This is just to fill the
|
||||
// buffer.
|
||||
builder.updateElevation(i);
|
||||
builder.updateGrade(1, 100);
|
||||
assertEquals(Double.NEGATIVE_INFINITY, builder.getStatistics().getMaxGrade());
|
||||
assertEquals(Double.POSITIVE_INFINITY, builder.getStatistics().getMinGrade());
|
||||
}
|
||||
}
|
||||
|
||||
public void testUpdateSpeedIncludeZero() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
builder.updateSpeed(i + 1000, 0.0, i, 4.0);
|
||||
assertEquals(0.0, builder.getStatistics().getMaxSpeed());
|
||||
assertEquals((i + 1) * 1000, builder.getStatistics().getMovingTime());
|
||||
}
|
||||
}
|
||||
|
||||
public void testUpdateSpeedIngoreErrorCode() {
|
||||
builder.updateSpeed(12345000, 128.0, 12344000, 0.0);
|
||||
assertEquals(0.0, builder.getStatistics().getMaxSpeed());
|
||||
assertEquals(1000, builder.getStatistics().getMovingTime());
|
||||
}
|
||||
|
||||
public void testUpdateSpeedIngoreLargeAcceleration() {
|
||||
builder.updateSpeed(12345000, 100.0, 12344000, 1.0);
|
||||
assertEquals(0.0, builder.getStatistics().getMaxSpeed());
|
||||
assertEquals(1000, builder.getStatistics().getMovingTime());
|
||||
}
|
||||
|
||||
public void testUpdateSpeed() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
builder.updateSpeed(i + 1000, 4.0, i, 4.0);
|
||||
assertEquals((i + 1) * 1000, builder.getStatistics().getMovingTime());
|
||||
if (i > MyTracksConstants.SPEED_SMOOTHING_FACTOR) {
|
||||
assertEquals(4.0, builder.getStatistics().getMaxSpeed());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.stats;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link TripStatistics}.
|
||||
* This only tests non-trivial pieces of that class.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class TripStatisticsTest extends TestCase {
|
||||
|
||||
private TripStatistics statistics;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
statistics = new TripStatistics();
|
||||
}
|
||||
|
||||
public void testSetBounds() {
|
||||
// This is not a trivial setter, conversion happens in it
|
||||
statistics.setBounds(12345, -34567, 56789, -98765);
|
||||
assertEquals(12345, statistics.getLeft());
|
||||
assertEquals(-34567, statistics.getTop());
|
||||
assertEquals(56789, statistics.getRight());
|
||||
assertEquals(-98765, statistics.getBottom());
|
||||
}
|
||||
|
||||
public void testMerge() {
|
||||
TripStatistics statistics2 = new TripStatistics();
|
||||
statistics.setStartTime(1000L); // Resulting start time
|
||||
statistics.setStopTime(2500L);
|
||||
statistics2.setStartTime(3000L);
|
||||
statistics2.setStopTime(4000L); // Resulting stop time
|
||||
statistics.setTotalTime(1500L);
|
||||
statistics2.setTotalTime(1000L); // Result: 1500+1000
|
||||
statistics.setMovingTime(700L);
|
||||
statistics2.setMovingTime(600L); // Result: 700+600
|
||||
statistics.setTotalDistance(750.0);
|
||||
statistics2.setTotalDistance(350.0); // Result: 750+350
|
||||
statistics.setTotalElevationGain(50.0);
|
||||
statistics2.setTotalElevationGain(850.0); // Result: 850+50
|
||||
statistics.setMaxSpeed(60.0); // Resulting max speed
|
||||
statistics2.setMaxSpeed(30.0);
|
||||
statistics.setMaxElevation(1250.0);
|
||||
statistics.setMinElevation(1200.0); // Resulting min elevation
|
||||
statistics2.setMaxElevation(3575.0); // Resulting max elevation
|
||||
statistics2.setMinElevation(2800.0);
|
||||
statistics.setMaxGrade(15.0);
|
||||
statistics.setMinGrade(-25.0); // Resulting min grade
|
||||
statistics2.setMaxGrade(35.0); // Resulting max grade
|
||||
statistics2.setMinGrade(0.0);
|
||||
|
||||
// Resulting bounds: -10000, 35000, 30000, -40000
|
||||
statistics.setBounds(-10000, 20000, 30000, -40000);
|
||||
statistics2.setBounds(-5000, 35000, 0, 20000);
|
||||
|
||||
statistics.merge(statistics2);
|
||||
|
||||
assertEquals(1000L, statistics.getStartTime());
|
||||
assertEquals(4000L, statistics.getStopTime());
|
||||
assertEquals(2500L, statistics.getTotalTime());
|
||||
assertEquals(1300L, statistics.getMovingTime());
|
||||
assertEquals(1100.0, statistics.getTotalDistance());
|
||||
assertEquals(900.0, statistics.getTotalElevationGain());
|
||||
assertEquals(60.0, statistics.getMaxSpeed());
|
||||
assertEquals(-10000, statistics.getLeft());
|
||||
assertEquals(30000, statistics.getRight());
|
||||
assertEquals(35000, statistics.getTop());
|
||||
assertEquals(-40000, statistics.getBottom());
|
||||
assertEquals(1200.0, statistics.getMinElevation());
|
||||
assertEquals(3575.0, statistics.getMaxElevation());
|
||||
assertEquals(-25.0, statistics.getMinGrade());
|
||||
assertEquals(35.0, statistics.getMaxGrade());
|
||||
}
|
||||
|
||||
public void testGetAverageSpeed() {
|
||||
statistics.setTotalDistance(1000.0);
|
||||
statistics.setTotalTime(50000); // in milliseconds
|
||||
assertEquals(20.0, statistics.getAverageSpeed());
|
||||
}
|
||||
|
||||
public void testGetAverageMovingSpeed() {
|
||||
statistics.setTotalDistance(1000.0);
|
||||
statistics.setMovingTime(20000); // in milliseconds
|
||||
assertEquals(50.0, statistics.getAverageMovingSpeed());
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
|
||||
package com.google.android.apps.mytracks.testing;
|
||||
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.MyTracksProviderUtils.Factory;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* A fake factory for {@link MyTracksProviderUtils} which always returns a
|
||||
* predefined instance.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class TestingProviderUtilsFactory extends Factory {
|
||||
private MyTracksProviderUtils instance;
|
||||
|
||||
public TestingProviderUtilsFactory(MyTracksProviderUtils instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MyTracksProviderUtils newForContext(Context context) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static Factory installWithInstance(MyTracksProviderUtils instance) {
|
||||
Factory oldFactory = Factory.getInstance();
|
||||
Factory factory = new TestingProviderUtilsFactory(instance);
|
||||
MyTracksProviderUtils.Factory.overrideInstance(factory);
|
||||
return oldFactory;
|
||||
}
|
||||
|
||||
public static void restoreOldFactory(Factory factory) {
|
||||
MyTracksProviderUtils.Factory.overrideInstance(factory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests the API feature detection code in {@link ApiFeatures}.
|
||||
* This test requires Froyo+ to run.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class ApiFeaturesTest extends TestCase {
|
||||
|
||||
private TestableApiFeatures features;
|
||||
|
||||
private class TestableApiFeatures extends ApiFeatures {
|
||||
private int apiLevel;
|
||||
|
||||
public void setApiLevel(int apiLevel) {
|
||||
this.apiLevel = apiLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getApiLevel() {
|
||||
return apiLevel;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
features = new TestableApiFeatures();
|
||||
}
|
||||
|
||||
public void testHasBackup() {
|
||||
for (int i = 3; i <= 7; i++) {
|
||||
features.setApiLevel(i);
|
||||
assertFalse(features.hasBackup());
|
||||
}
|
||||
features.setApiLevel(8);
|
||||
assertTrue(features.hasBackup());
|
||||
}
|
||||
|
||||
public void testHasTextToSpeech() {
|
||||
features.setApiLevel(3);
|
||||
assertFalse(features.hasTextToSpeech());
|
||||
for (int i = 4; i <= 8; i++) {
|
||||
features.setApiLevel(i);
|
||||
assertTrue(features.hasTextToSpeech());
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetApiPlatformAdapter() {
|
||||
assertNotNull(features.getApiPlatformAdapter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2009 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.stats.TripStatistics;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for the Chart URL generator.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class ChartURLGeneratorTest extends TestCase {
|
||||
|
||||
public void testgetChartUrl() {
|
||||
Vector<Double> distances = new Vector<Double>();
|
||||
Vector<Double> elevations = new Vector<Double>();
|
||||
Track t = new Track();
|
||||
TripStatistics stats = t.getStatistics();
|
||||
stats.setMinElevation(0);
|
||||
stats.setMaxElevation(2000);
|
||||
stats.setTotalDistance(100);
|
||||
|
||||
distances.add(0.0);
|
||||
elevations.add(10.0);
|
||||
|
||||
distances.add(10.0);
|
||||
elevations.add(300.0);
|
||||
|
||||
distances.add(20.0);
|
||||
elevations.add(800.0);
|
||||
|
||||
distances.add(50.0);
|
||||
elevations.add(1900.0);
|
||||
|
||||
distances.add(75.0);
|
||||
elevations.add(1200.0);
|
||||
|
||||
distances.add(90.0);
|
||||
elevations.add(700.0);
|
||||
|
||||
distances.add(100.0);
|
||||
elevations.add(70.0);
|
||||
|
||||
String chart = ChartURLGenerator.getChartUrl(distances,
|
||||
elevations,
|
||||
t,
|
||||
"Title",
|
||||
true);
|
||||
|
||||
assertEquals(
|
||||
"http://chart.apis.google.com/chart?&chs=600x350&cht=lxy&"
|
||||
+ "chtt=Title&chxt=x,y&chxr=0,0,0,0|1,0.0,2100.0,300&chco=009A00&"
|
||||
+ "chm=B,00AA00,0,0,0&chg=100000,14.285714285714286,1,0&"
|
||||
+ "chd=e:AAGZMzf.v.5l..,ATJJYY55kkVVCI",
|
||||
chart);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* A unit test for {@link ChartsExtendedEncoder}.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class ChartsExtendedEncoderTest extends TestCase {
|
||||
|
||||
public void testGetEncodedValue_validArguments() {
|
||||
// Valid arguments.
|
||||
assertEquals("AK", ChartsExtendedEncoder.getEncodedValue(10));
|
||||
assertEquals("JO", ChartsExtendedEncoder.getEncodedValue(590));
|
||||
assertEquals("AA", ChartsExtendedEncoder.getEncodedValue(0));
|
||||
// 64^2 = 4096.
|
||||
assertEquals("..", ChartsExtendedEncoder.getEncodedValue(4095));
|
||||
}
|
||||
|
||||
public void testGetEncodedValue_invalidArguments() {
|
||||
// Invalid arguments.
|
||||
assertEquals("__", ChartsExtendedEncoder.getEncodedValue(4096));
|
||||
assertEquals("__", ChartsExtendedEncoder.getEncodedValue(1234564096));
|
||||
assertEquals("__", ChartsExtendedEncoder.getEncodedValue(-10));
|
||||
assertEquals("__", ChartsExtendedEncoder.getEncodedValue(-12324435));
|
||||
}
|
||||
|
||||
public void testGetSeparator() {
|
||||
assertEquals(",", ChartsExtendedEncoder.getSeparator());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link FileUtils}.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class FileUtilsTest extends TestCase {
|
||||
private static final String ORIGINAL_NAME = "Swim\10ming-^across: the/ pacific (ocean).";
|
||||
private static final String SANITIZED_NAME = "Swimming-across the pacific (ocean).";
|
||||
|
||||
private FileUtils fileUtils;
|
||||
private Set<String> existingFiles;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
existingFiles = new HashSet<String>();
|
||||
fileUtils = new FileUtils() {
|
||||
@Override
|
||||
protected boolean fileExists(File directory, String fullName) {
|
||||
return existingFiles.contains(fullName);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void testBuildExternalDirectoryPath() {
|
||||
String expectedName = Environment.getExternalStorageDirectory()
|
||||
+ File.separator
|
||||
+ MyTracksConstants.SDCARD_TOP_DIR
|
||||
+ File.separator
|
||||
+ "a"
|
||||
+ File.separator
|
||||
+ "b"
|
||||
+ File.separator
|
||||
+ "c";
|
||||
|
||||
String dirName = fileUtils.buildExternalDirectoryPath("a", "b", "c");
|
||||
assertEquals(expectedName, dirName);
|
||||
}
|
||||
|
||||
public void testSanitizeName() {
|
||||
assertEquals(SANITIZED_NAME, fileUtils.sanitizeName(ORIGINAL_NAME));
|
||||
}
|
||||
|
||||
public void testBuildUniqueFileName_someExist() {
|
||||
existingFiles = new HashSet<String>();
|
||||
existingFiles.add("Filename.ext");
|
||||
existingFiles.add("Filename (1).ext");
|
||||
existingFiles.add("Filename (2).ext");
|
||||
existingFiles.add("Filename (3).ext");
|
||||
existingFiles.add("Filename (4).ext");
|
||||
|
||||
String filename = fileUtils.buildUniqueFileName(null, "Filename", "ext");
|
||||
assertEquals("Filename (5).ext", filename);
|
||||
}
|
||||
|
||||
public void testBuildUniqueFileName_oneExists() {
|
||||
existingFiles.add("Filename.ext");
|
||||
|
||||
String filename = fileUtils.buildUniqueFileName(null, "Filename", "ext");
|
||||
assertEquals("Filename (1).ext", filename);
|
||||
}
|
||||
|
||||
public void testBuildUniqueFileName_noneExists() {
|
||||
String filename = fileUtils.buildUniqueFileName(null, "Filename", "ext");
|
||||
assertEquals("Filename.ext", filename);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link StringUtils}.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class StringUtilsTest extends TestCase {
|
||||
public void testParseXmlDateTime() {
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01",
|
||||
2010, 5, 4, 3, 2, 1, 0);
|
||||
}
|
||||
|
||||
public void testParseXmlDateTime_fractional() {
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.3",
|
||||
2010, 5, 4, 3, 2, 1, 300);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.35",
|
||||
2010, 5, 4, 3, 2, 1, 350);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.352",
|
||||
2010, 5, 4, 3, 2, 1, 352);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.3525",
|
||||
2010, 5, 4, 3, 2, 1, 352);
|
||||
}
|
||||
|
||||
public void testParseXmlDateTime_timezone() {
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01Z",
|
||||
2010, 5, 4, 3, 2, 1, 0);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01+00:00",
|
||||
2010, 5, 4, 3, 2, 1, 0);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01-00:00",
|
||||
2010, 5, 4, 3, 2, 1, 0);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01+01:00",
|
||||
2010, 5, 4, 2, 2, 1, 0);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01+10:30",
|
||||
2010, 5, 3, 16, 32, 1, 0);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01-09:30",
|
||||
2010, 5, 4, 12, 32, 1, 0);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01-05:00",
|
||||
2010, 5, 4, 8, 2, 1, 0);
|
||||
}
|
||||
|
||||
public void testParseXmlDateTime_fractionalAndTimezone() {
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.352Z",
|
||||
2010, 5, 4, 3, 2, 1, 352);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.47+00:00",
|
||||
2010, 5, 4, 3, 2, 1, 470);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.5791+03:00",
|
||||
2010, 5, 4, 0, 2, 1, 579);
|
||||
assertParseXmlDateTime("2010-05-04T03:02:01.8-05:30",
|
||||
2010, 5, 4, 8, 32, 1, 800);
|
||||
}
|
||||
|
||||
private void assertParseXmlDateTime(String dateTime,
|
||||
int year, int month, int day, int hour, int min, int second, int millis) {
|
||||
long timestamp = StringUtils.parseXmlDateTime(dateTime);
|
||||
GregorianCalendar calendar =
|
||||
new GregorianCalendar(TimeZone.getTimeZone("UTC"));
|
||||
calendar.set(year, month - 1, day, hour, min, second);
|
||||
calendar.set(GregorianCalendar.MILLISECOND, millis);
|
||||
assertEquals(calendar.getTimeInMillis(), timestamp);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user