mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-08 20:33:06 +02:00
Add a class EndToEndTestRunner which extends InstrumentationTestRunner and can get the port of emulator. Then test can be run on more than one emulators at the same time.
This commit is contained in:
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests the {@link AdaptiveLocationListenerPolicy}.
|
||||
*
|
||||
* @author youtaol
|
||||
*/
|
||||
public class AdaptiveLocationListenerPolicyTest extends TestCase {
|
||||
|
||||
private AdaptiveLocationListenerPolicy adocationListenerPolicy;
|
||||
private static final long MIN = 1000;
|
||||
private static final long MAX = 3000;
|
||||
private static final int MIN_DISTANCE = 10;
|
||||
private static final long NEW_IDLE_TIME_BIG = 10000;
|
||||
private static final long NEW_IDLE_TIME_NORMAL = 5000;
|
||||
private static final long NEW_IDLE_TIME_SMALL = 2000;
|
||||
private static final long NEW_IDLE_TIME_LESS_THAN_MIN = 500;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
adocationListenerPolicy = new AdaptiveLocationListenerPolicy(MIN, MAX, MIN_DISTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the
|
||||
* {@link AdaptiveLocationListenerPolicy#getDesiredPollingInterval()} in four
|
||||
* situations.
|
||||
* <ul>
|
||||
* <li>The newIdleTime is bigger than max interval.</li>
|
||||
* <li>The newIdleTime is between min and max interval.</li>
|
||||
* <li>The newIdleTime is smaller than max interval.</li>
|
||||
* <li>The newIdleTime is smaller than the smallest interval unit.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public void testGetDesiredPollingInterval() {
|
||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_BIG);
|
||||
assertEquals(MAX, adocationListenerPolicy.getDesiredPollingInterval());
|
||||
|
||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
|
||||
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the
|
||||
// nearest second.
|
||||
assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000,
|
||||
adocationListenerPolicy.getDesiredPollingInterval());
|
||||
|
||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
||||
assertEquals(MIN, adocationListenerPolicy.getDesiredPollingInterval());
|
||||
|
||||
adocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_LESS_THAN_MIN);
|
||||
assertEquals(MIN, adocationListenerPolicy.getDesiredPollingInterval());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method {@link AdaptiveLocationListenerPolicy#getMinDistance()}.
|
||||
*/
|
||||
public void testGetMinDistance() {
|
||||
assertEquals(MIN_DISTANCE, adocationListenerPolicy.getMinDistance());
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.services;
|
||||
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.RemoteException;
|
||||
import android.test.ServiceTestCase;
|
||||
|
||||
import org.easymock.EasyMock;
|
||||
|
||||
/**
|
||||
* Tests {@link ControlRecordingService}.
|
||||
*
|
||||
* @author Youtao Liu
|
||||
*/
|
||||
public class ControlRecordingServiceTest extends ServiceTestCase<ControlRecordingService> {
|
||||
|
||||
private Context context;
|
||||
private ControlRecordingService controlRecordingService;
|
||||
|
||||
public ControlRecordingServiceTest() {
|
||||
super(ControlRecordingService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
context = getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the start of {@link ControlRecordingService} and tests the method
|
||||
* {@link ControlRecordingService#onHandleIntent(Intent, ITrackRecordingService)}
|
||||
* to start a track recording.
|
||||
*/
|
||||
@UsesMocks(ITrackRecordingService.class)
|
||||
public void testStartRecording() {
|
||||
assertNull(controlRecordingService);
|
||||
Intent intent = startControlRecordingService(context.getString(R.string.track_action_start));
|
||||
assertNotNull(controlRecordingService);
|
||||
|
||||
ITrackRecordingService iTrackRecordingServiceMock = EasyMock
|
||||
.createStrictMock(ITrackRecordingService.class);
|
||||
try {
|
||||
EasyMock.expect(iTrackRecordingServiceMock.startNewTrack()).andReturn(1L);
|
||||
EasyMock.replay(iTrackRecordingServiceMock);
|
||||
controlRecordingService.onHandleIntent(intent, iTrackRecordingServiceMock);
|
||||
EasyMock.verify(iTrackRecordingServiceMock);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the method
|
||||
* {@link ControlRecordingService#onHandleIntent(Intent, ITrackRecordingService)}
|
||||
* to stop a track recording.
|
||||
*/
|
||||
@UsesMocks(ITrackRecordingService.class)
|
||||
public void testStopRecording() {
|
||||
Intent intent = startControlRecordingService(context.getString(R.string.track_action_end));
|
||||
|
||||
ITrackRecordingService iTrackRecordingServiceMock = EasyMock
|
||||
.createStrictMock(ITrackRecordingService.class);
|
||||
try {
|
||||
iTrackRecordingServiceMock.endCurrentTrack();
|
||||
EasyMock.replay(iTrackRecordingServiceMock);
|
||||
controlRecordingService.onHandleIntent(intent, iTrackRecordingServiceMock);
|
||||
EasyMock.verify(iTrackRecordingServiceMock);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a ControlRecordingService with a specified action.
|
||||
*
|
||||
* @param action the action string in the start intent
|
||||
*/
|
||||
private Intent startControlRecordingService(String action) {
|
||||
Intent intent = new Intent(context, ControlRecordingService.class);
|
||||
intent.setAction(action);
|
||||
startService(intent);
|
||||
controlRecordingService = getService();
|
||||
return intent;
|
||||
}
|
||||
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.expect;
|
||||
|
||||
import com.google.android.testing.mocking.AndroidMock;
|
||||
import com.google.android.testing.mocking.UsesMocks;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Tests {@link RemoveTempFilesService}.
|
||||
*
|
||||
* @author Sandor Dornbush
|
||||
*/
|
||||
public class RemoveTempFilesServiceTest extends AndroidTestCase {
|
||||
|
||||
private static final String DIR_NAME = "/tmp";
|
||||
private static final String FILE_NAME = "foo";
|
||||
|
||||
private RemoveTempFilesService service;
|
||||
|
||||
@UsesMocks({ File.class, })
|
||||
protected void setUp() throws Exception {
|
||||
service = new RemoveTempFilesService();
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests when the directory doesn't exists.
|
||||
*/
|
||||
public void test_noDir() {
|
||||
File dir = AndroidMock.createMock(File.class, DIR_NAME);
|
||||
expect(dir.exists()).andStubReturn(false);
|
||||
AndroidMock.replay(dir);
|
||||
|
||||
assertEquals(0, service.cleanTempDirectory(dir));
|
||||
AndroidMock.verify(dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests when the directory is empty.
|
||||
*/
|
||||
public void test_emptyDir() {
|
||||
File dir = AndroidMock.createMock(File.class, DIR_NAME);
|
||||
expect(dir.exists()).andStubReturn(true);
|
||||
expect(dir.listFiles()).andStubReturn(new File[0]);
|
||||
AndroidMock.replay(dir);
|
||||
|
||||
assertEquals(0, service.cleanTempDirectory(dir));
|
||||
AndroidMock.verify(dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests when there is a new file and it shouldn't get deleted.
|
||||
*/
|
||||
public void test_newFile() {
|
||||
File file = AndroidMock.createMock(File.class, DIR_NAME + FILE_NAME);
|
||||
expect(file.lastModified()).andStubReturn(System.currentTimeMillis());
|
||||
|
||||
File dir = AndroidMock.createMock(File.class, DIR_NAME);
|
||||
expect(dir.exists()).andStubReturn(true);
|
||||
expect(dir.listFiles()).andStubReturn(new File[] { file });
|
||||
AndroidMock.replay(dir, file);
|
||||
|
||||
assertEquals(0, service.cleanTempDirectory(dir));
|
||||
AndroidMock.verify(dir, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests when there is an old file and it should get deleted.
|
||||
*/
|
||||
public void test_oldFile() {
|
||||
File file = AndroidMock.createMock(File.class, DIR_NAME + FILE_NAME);
|
||||
// qSet to one hour and 1 millisecond later than the current time
|
||||
expect(file.lastModified()).andStubReturn(System.currentTimeMillis() - 3600001);
|
||||
expect(file.delete()).andStubReturn(true);
|
||||
|
||||
File dir = AndroidMock.createMock(File.class, DIR_NAME);
|
||||
expect(dir.exists()).andStubReturn(true);
|
||||
expect(dir.listFiles()).andStubReturn(new File[] { file });
|
||||
AndroidMock.replay(dir, file);
|
||||
|
||||
assertEquals(1, service.cleanTempDirectory(dir));
|
||||
AndroidMock.verify(dir, file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
|
||||
package com.google.android.apps.mytracks.services;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.Service;
|
||||
import android.test.ServiceTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* A {@link TrackRecordingService} that can be used with {@link ServiceTestCase}.
|
||||
* {@link ServiceTestCase} throws a null pointer exception when the service
|
||||
* calls {@link Service#startForeground(int, android.app.Notification)} and
|
||||
* {@link Service#stopForeground(boolean)}.
|
||||
* <p>
|
||||
* See http://code.google.com/p/android/issues/detail?id=12122
|
||||
* <p>
|
||||
* Wrap these two methods in wrappers and override them.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class TestRecordingService extends TrackRecordingService {
|
||||
|
||||
private static final String TAG = TestRecordingService.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
protected void startForegroundService(Notification notification) {
|
||||
try {
|
||||
Method setForegroundMethod = Service.class.getMethod("setForeground", boolean.class);
|
||||
setForegroundMethod.invoke(this, true);
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "Unable to start a service in foreground", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.e(TAG, "Unable to start a service in foreground", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, "Unable to start a service in foreground", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(TAG, "Unable to start a service in foreground", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.e(TAG, "Unable to start a service in foreground", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopForegroundService() {
|
||||
try {
|
||||
Method setForegroundMethod = Service.class.getMethod("setForeground", boolean.class);
|
||||
setForegroundMethod.invoke(this, false);
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "Unable to start a service in foreground", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.e(TAG, "Unable to start a service in foreground", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(TAG, "Unable to start a service in foreground", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(TAG, "Unable to start a service in foreground", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.e(TAG, "Unable to start a service in foreground", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+768
@@ -0,0 +1,768 @@
|
||||
/*
|
||||
* 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.Constants.RESUME_TRACK_EXTRA_NAME;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
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.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
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.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.Settings;
|
||||
import android.test.RenamingDelegatingContext;
|
||||
import android.test.ServiceTestCase;
|
||||
import android.test.mock.MockContentProvider;
|
||||
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<TestRecordingService> {
|
||||
|
||||
private Context context;
|
||||
private MyTracksProviderUtils providerUtils;
|
||||
|
||||
/*
|
||||
* 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(TestRecordingService.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;
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@TargetApi(9)
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
/*
|
||||
* Create a mock context that uses a mock content resolver and a renaming
|
||||
* delegating context.
|
||||
*/
|
||||
MockContentResolver mockContentResolver = new MockContentResolver();
|
||||
RenamingDelegatingContext renamingDelegatingContext = new RenamingDelegatingContext(
|
||||
getContext(), getContext(), "test.");
|
||||
context = new MockContext(mockContentResolver, renamingDelegatingContext);
|
||||
|
||||
// Set up the mock content resolver
|
||||
MyTracksProvider myTracksProvider = new MyTracksProvider();
|
||||
myTracksProvider.attachInfo(context, null);
|
||||
mockContentResolver.addProvider(MyTracksProviderUtils.AUTHORITY, myTracksProvider);
|
||||
MockContentProvider settingsProvider = new MockContentProvider(context) {
|
||||
@Override
|
||||
public Bundle call(String method, String arg, Bundle extras) {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
mockContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
|
||||
|
||||
// Set the context
|
||||
setContext(context);
|
||||
|
||||
providerUtils = MyTracksProviderUtils.Factory.get(context);
|
||||
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().apply();
|
||||
|
||||
// Disable auto resume by default.
|
||||
updateAutoResumePrefs(PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT, 0);
|
||||
// No recording track.
|
||||
PreferencesUtils.setLong(
|
||||
context, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
}
|
||||
|
||||
@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(123L, System.currentTimeMillis(), true);
|
||||
|
||||
// Clear the number of attempts and set the timeout to 10 min.
|
||||
updateAutoResumePrefs(PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
|
||||
// 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(123L, 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(PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
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, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
|
||||
// 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(123L, System.currentTimeMillis(), false);
|
||||
|
||||
// Clear the number of attempts and set the timeout to 10 min.
|
||||
updateAutoResumePrefs(PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
|
||||
// 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(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testResumeAfterReboot_expiredTrack() throws Exception {
|
||||
// Insert a dummy track last updated 20 min ago.
|
||||
createDummyTrack(123L, System.currentTimeMillis() - 20 * 60 * 1000, true);
|
||||
|
||||
// Clear the number of attempts and set the timeout to 10 min.
|
||||
updateAutoResumePrefs(PreferencesUtils.AUTO_RESUME_TRACK_CURRENT_RETRY_DEFAULT,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
|
||||
// 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(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testResumeAfterReboot_tooManyAttempts() throws Exception {
|
||||
// Insert a dummy track.
|
||||
createDummyTrack(123L, System.currentTimeMillis(), true);
|
||||
|
||||
// Set the number of attempts to max.
|
||||
updateAutoResumePrefs(TrackRecordingService.MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS,
|
||||
PreferencesUtils.AUTO_RESUME_TRACK_TIMEOUT_DEFAULT);
|
||||
|
||||
// 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(-1L, 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(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testRecording_oldTracks() throws Exception {
|
||||
createDummyTrack(123L, -1L, false);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1L, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testRecording_orphanedRecordingTrack() throws Exception {
|
||||
// Just set recording track to a bogus value.
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, 256L);
|
||||
|
||||
// Make sure that the service will not start recording and will clear
|
||||
// the bogus track.
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1L, 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 ctx, 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(PreferencesUtils.getString(
|
||||
context, R.string.default_activity_key, PreferencesUtils.DEFAULT_ACTIVITY_DEFAULT),
|
||||
track.getCategory());
|
||||
assertEquals(id, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
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), -1L));
|
||||
|
||||
context.unregisterReceiver(startReceiver);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testStartNewTrack_alreadyRecording() throws Exception {
|
||||
createDummyTrack(123L, -1L, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
// Starting a new track when there is a recording should just return -1L.
|
||||
long newTrack = service.startNewTrack();
|
||||
assertEquals(-1L, newTrack);
|
||||
|
||||
assertEquals(123L, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
assertEquals(123L, 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(123L, -1L, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
|
||||
// End the current track.
|
||||
service.endCurrentTrack();
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT,
|
||||
PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, 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(123L, broadcastIntent.getLongExtra(
|
||||
context.getString(R.string.track_id_broadcast_extra), -1L));
|
||||
|
||||
context.unregisterReceiver(stopReceiver);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testEndCurrentTrack_noRecording() throws Exception {
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
// Ending the current track when there is no recording should not result in any error.
|
||||
service.endCurrentTrack();
|
||||
|
||||
assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT,
|
||||
PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
assertEquals(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT, service.getRecordingTrackId());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testIntegration_completeRecordingSession() throws Exception {
|
||||
List<Track> tracks = providerUtils.getAllTracks();
|
||||
assertTrue(tracks.isEmpty());
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@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(123L, -1L, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
insertLocation(service);
|
||||
|
||||
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||
assertEquals(2, service.insertWaypoint(WaypointCreationRequest.DEFAULT_STATISTICS));
|
||||
|
||||
Waypoint wpt = providerUtils.getWaypoint(2);
|
||||
assertEquals(getContext().getString(R.string.marker_statistics_icon_url), wpt.getIcon());
|
||||
assertEquals(getContext().getString(R.string.marker_split_name_format, 1), wpt.getName());
|
||||
assertEquals(Waypoint.TYPE_STATISTICS, wpt.getType());
|
||||
assertEquals(123L, wpt.getTrackId());
|
||||
assertEquals(0.0, wpt.getLength());
|
||||
assertNotNull(wpt.getLocation());
|
||||
assertNotNull(wpt.getTripStatistics());
|
||||
// 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_WAYPOINT);
|
||||
fail("Expecting IllegalStateException");
|
||||
} catch (IllegalStateException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testInsertWaypointMarker_validWaypoint() throws Exception {
|
||||
createDummyTrack(123L, -1L, true);
|
||||
|
||||
ITrackRecordingService service = bindAndGetService(createStartIntent());
|
||||
assertTrue(service.isRecording());
|
||||
insertLocation(service);
|
||||
|
||||
assertEquals(1, service.insertWaypoint(WaypointCreationRequest.DEFAULT_WAYPOINT));
|
||||
Waypoint wpt = providerUtils.getWaypoint(1);
|
||||
assertEquals(getContext().getString(R.string.marker_waypoint_icon_url),
|
||||
wpt.getIcon());
|
||||
assertEquals(getContext().getString(R.string.marker_name_format, 1), wpt.getName());
|
||||
assertEquals(Waypoint.TYPE_WAYPOINT, wpt.getType());
|
||||
assertEquals(123L, wpt.getTrackId());
|
||||
assertEquals(0.0, wpt.getLength());
|
||||
assertNotNull(wpt.getLocation());
|
||||
assertNull(wpt.getTripStatistics());
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_announcementFrequencyDefault() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.announcement_frequency_key,
|
||||
PreferencesUtils.ANNOUNCEMENT_FREQUENCY_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_announcementFrequencyByDistance() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.announcement_frequency_key, -1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_announcementFrequencyByTime() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.announcement_frequency_key, 1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_maxRecordingDistanceDefault() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key,
|
||||
PreferencesUtils.MAX_RECORDING_DISTANCE_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_maxRecordingDistance() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key, 50);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_minRecordingDistanceDefault() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_distance_key,
|
||||
PreferencesUtils.MIN_RECORDING_DISTANCE_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_minRecordingDistance() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_distance_key, 2);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_splitFrequencyDefault() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key,
|
||||
PreferencesUtils.SPLIT_FREQUENCY_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_splitFrequencyByDistance() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, -1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_splitFrequencyByTime() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.split_frequency_key, 1);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_metricUnitsDefault() throws Exception {
|
||||
PreferencesUtils.setBoolean(
|
||||
context, R.string.metric_units_key, PreferencesUtils.METRIC_UNITS_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_metricUnitsDisabled() throws Exception {
|
||||
PreferencesUtils.setBoolean(context, R.string.metric_units_key, false);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_minRecordingIntervalDefault() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key,
|
||||
PreferencesUtils.MIN_RECORDING_INTERVAL_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_minRecordingInterval() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key, 2);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_minRequiredAccuracyDefault() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.min_required_accuracy_key,
|
||||
PreferencesUtils.MIN_REQUIRED_ACCURACY_DEFAULT);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_minRequiredAccuracy() throws Exception {
|
||||
PreferencesUtils.setInt(context, R.string.min_required_accuracy_key, 500);
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_sensorTypeDefault() throws Exception {
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.sensor_type_key, context.getString(R.string.sensor_type_value_none));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
public void testWithProperties_sensorTypeZephyr() throws Exception {
|
||||
PreferencesUtils.setString(
|
||||
context, R.string.sensor_type_key, context.getString(R.string.sensor_type_value_zephyr));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
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.setTripStatistics(tripStatistics);
|
||||
addTrack(dummyTrack, isRecording);
|
||||
return dummyTrack;
|
||||
}
|
||||
|
||||
private void updateAutoResumePrefs(int attempts, int timeoutMins) {
|
||||
PreferencesUtils.setInt(context, R.string.auto_resume_track_current_retry_key, attempts);
|
||||
PreferencesUtils.setInt(context, R.string.auto_resume_track_timeout_key, timeoutMins);
|
||||
}
|
||||
|
||||
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());
|
||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId()
|
||||
: PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
}
|
||||
|
||||
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, PreferencesUtils.getLong(context, R.string.recording_track_id_key));
|
||||
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_WAYPOINT);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop the track. Validate if it has correct data.
|
||||
service.endCurrentTrack();
|
||||
assertFalse(service.isRecording());
|
||||
assertEquals(-1L, service.getRecordingTrackId());
|
||||
track = providerUtils.getTrack(id);
|
||||
assertNotNull(track);
|
||||
assertEquals(id, track.getId());
|
||||
TripStatistics tripStatistics = track.getTripStatistics();
|
||||
assertNotNull(tripStatistics);
|
||||
assertTrue(tripStatistics.getStartTime() > 0);
|
||||
assertTrue(tripStatistics.getStopTime() >= tripStatistics.getStartTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a location and waits for 100ms.
|
||||
*
|
||||
* @param trackRecordingService the track recording service
|
||||
*/
|
||||
private void insertLocation(ITrackRecordingService trackRecordingService)
|
||||
throws RemoteException, InterruptedException {
|
||||
Location location = new Location("gps");
|
||||
location.setLongitude(35.0f);
|
||||
location.setLatitude(45.0f);
|
||||
location.setAccuracy(5);
|
||||
location.setSpeed(10);
|
||||
location.setTime(System.currentTimeMillis());
|
||||
location.setBearing(3.0f);
|
||||
trackRecordingService.recordLocation(location);
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.content.Sensor;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class PolarMessageParserTest extends TestCase {
|
||||
|
||||
PolarMessageParser parser = new PolarMessageParser();
|
||||
// A complete and valid Polar HxM packet
|
||||
// FE08F701D1001104FE08F702D1001104
|
||||
private final byte[] originalBuf =
|
||||
{(byte) 0xFE, 0x08, (byte) 0xF7, 0x01, (byte) 0xD1, 0x00, 0x11, 0x04, (byte) 0xFE, 0x08,
|
||||
(byte) 0xF7, 0x02, (byte) 0xD1, 0x00, 0x11, 0x04};
|
||||
private byte[] buf;
|
||||
|
||||
@TargetApi(9)
|
||||
public void setUp() {
|
||||
buf = Arrays.copyOf(originalBuf, originalBuf.length);
|
||||
}
|
||||
|
||||
public void testIsValid() {
|
||||
assertTrue(parser.isValid(buf));
|
||||
}
|
||||
|
||||
public void testIsValid_invalidHeader() {
|
||||
// Invalidate header.
|
||||
buf[0] = 0x03;
|
||||
assertFalse(parser.isValid(buf));
|
||||
}
|
||||
|
||||
public void testIsValid_invalidCheckbyte() {
|
||||
// Invalidate checkbyte.
|
||||
buf[2] = 0x03;
|
||||
assertFalse(parser.isValid(buf));
|
||||
}
|
||||
|
||||
public void testIsValid_invalidSequence() {
|
||||
// Invalidate sequence.
|
||||
buf[3] = 0x11;
|
||||
assertFalse(parser.isValid(buf));
|
||||
}
|
||||
|
||||
public void testParseBuffer() {
|
||||
buf[5] = 70;
|
||||
Sensor.SensorDataSet sds = parser.parseBuffer(buf);
|
||||
assertTrue(sds.hasHeartRate());
|
||||
assertTrue(sds.getHeartRate().getState() == Sensor.SensorState.SENDING);
|
||||
assertEquals(70, sds.getHeartRate().getValue());
|
||||
}
|
||||
|
||||
public void testFindNextAlignment_offset() {
|
||||
// The first 4 bytes are garbage
|
||||
buf = new byte[originalBuf.length + 4];
|
||||
buf[0] = 4;
|
||||
buf[1] = 2;
|
||||
buf[2] = 4;
|
||||
buf[3] = 2;
|
||||
|
||||
// Then the valid message.
|
||||
System.arraycopy(originalBuf, 0, buf, 4, originalBuf.length);
|
||||
assertEquals(4, parser.findNextAlignment(buf));
|
||||
}
|
||||
|
||||
public void testFindNextAlignment_invalid() {
|
||||
buf[0] = 0;
|
||||
assertEquals(-1, parser.findNextAlignment(buf));
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package com.google.android.apps.mytracks.services.sensors;
|
||||
|
||||
import com.google.android.apps.mytracks.Constants;
|
||||
import com.google.android.apps.mytracks.util.PreferencesUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
public class SensorManagerFactoryTest extends AndroidTestCase {
|
||||
|
||||
@TargetApi(9)
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
SharedPreferences sharedPreferences = getContext().getSharedPreferences(
|
||||
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().apply();
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testDefaultSettings() throws Exception {
|
||||
assertNull(SensorManagerFactory.getSystemSensorManager(getContext()));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testCreateZephyr() throws Exception {
|
||||
assertClassForName(ZephyrSensorManager.class, R.string.sensor_type_value_zephyr);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testCreatePolar() throws Exception {
|
||||
assertClassForName(PolarSensorManager.class, R.string.sensor_type_value_polar);
|
||||
}
|
||||
|
||||
private void assertClassForName(Class<?> c, int i) {
|
||||
PreferencesUtils.setString(getContext(), R.string.sensor_type_key, getContext().getString(i));
|
||||
SensorManager sm = SensorManagerFactory.getSystemSensorManager(getContext());
|
||||
assertNotNull(sm);
|
||||
assertTrue(c.isInstance(sm));
|
||||
SensorManagerFactory.releaseSystemSensorManager();
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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 junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* @author Dominik Ršttsches
|
||||
*/
|
||||
public class StrideReadingsTest extends TestCase {
|
||||
|
||||
public void testNoReadingOnStartup() {
|
||||
StrideReadings strideReadings = new StrideReadings();
|
||||
assertEquals(StrideReadings.CADENCE_NOT_AVAILABLE, strideReadings.getCadence());
|
||||
}
|
||||
|
||||
public void testAverageCadenceAvailable() {
|
||||
StrideReadings strideReadings = new StrideReadings();
|
||||
// 2 steps / second => Cadence is 120 / minute
|
||||
for (int i = 1; i <= 30; i++) {
|
||||
strideReadings.updateStrideReading(i*2);
|
||||
if (i >= StrideReadings.MIN_READINGS_FOR_AVERAGE) {
|
||||
assertEquals(120, strideReadings.getCadence());
|
||||
} else {
|
||||
assertEquals(StrideReadings.CADENCE_NOT_AVAILABLE, strideReadings.getCadence());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for correct calculation after rolling over at 128 strides,
|
||||
* just like the HxM seems to do it.
|
||||
*/
|
||||
public void testRollover() {
|
||||
StrideReadings strideReadings = new StrideReadings();
|
||||
// 1 step per second => Cadence is 60 / minute
|
||||
// Updating readings counting upwards from initialStrides -
|
||||
// initialStrides set to a value below 128 to ensure rollover.
|
||||
int initialStrides = 128 - StrideReadings.NUM_READINGS_FOR_AVERAGE - 5;
|
||||
|
||||
for (int i = 1; i <= StrideReadings.NUM_READINGS_FOR_AVERAGE + 10; i++) {
|
||||
strideReadings.updateStrideReading((initialStrides + i) % 128);
|
||||
if (i >= StrideReadings.MIN_READINGS_FOR_AVERAGE) {
|
||||
assertEquals(60, strideReadings.getCadence());
|
||||
} else {
|
||||
assertEquals(StrideReadings.CADENCE_NOT_AVAILABLE, strideReadings.getCadence());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
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[] smallBuf = new byte[59];
|
||||
assertFalse(parser.isValid(smallBuf));
|
||||
// A complete and valid Zephyr HxM packet
|
||||
byte[] buf = { 2,38,55,26,0,49,101,80,0,49,98,100,42,113,120,-53,-24,-60,-123,-61,117,-69,42,-75,74,-78,51,-79,27,-83,28,-88,28,-93,29,-98,25,-103,26,-108,26,-113,59,-118,0,0,0,0,0,0,-22,3,125,1,48,0,96,4,30,0 };
|
||||
// Make buffer invalid
|
||||
buf[0] = buf[58] = buf[59] = 0;
|
||||
assertFalse(parser.isValid(buf));
|
||||
buf[0] = 0x02;
|
||||
assertFalse(parser.isValid(buf));
|
||||
buf[58] = 0x1E;
|
||||
assertFalse(parser.isValid(buf));
|
||||
buf[59] = 0x03;
|
||||
assertTrue(parser.isValid(buf));
|
||||
}
|
||||
|
||||
public void testParseBuffer() {
|
||||
byte[] buf = new byte[60];
|
||||
// Heart Rate (-1 =^ 255 unsigned byte)
|
||||
buf[12] = -1;
|
||||
// Battery Level
|
||||
buf[11] = 51;
|
||||
// Cadence (=^ 255*16 strides/min)
|
||||
buf[56] = -1;
|
||||
buf[57] = 15;
|
||||
Sensor.SensorDataSet sds = parser.parseBuffer(buf);
|
||||
assertTrue(sds.hasHeartRate());
|
||||
assertTrue(sds.getHeartRate().getState() == Sensor.SensorState.SENDING);
|
||||
assertEquals(255, sds.getHeartRate().getValue());
|
||||
assertTrue(sds.hasBatteryLevel());
|
||||
assertTrue(sds.getBatteryLevel().getState() == Sensor.SensorState.SENDING);
|
||||
assertEquals(51, sds.getBatteryLevel().getValue());
|
||||
assertTrue(sds.hasCadence());
|
||||
assertTrue(sds.getCadence().getState() == Sensor.SensorState.SENDING);
|
||||
assertEquals(255, sds.getCadence().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));
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.tasks;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link StatusAnnouncerFactory}.
|
||||
* These tests require Donut+ to run.
|
||||
*
|
||||
* @author Rodrigo Damazio
|
||||
*/
|
||||
public class StatusAnnouncerFactoryTest extends AndroidTestCase {
|
||||
public void testCreate() {
|
||||
PeriodicTaskFactory factory = new StatusAnnouncerFactory();
|
||||
PeriodicTask task = factory.create(getContext());
|
||||
assertTrue(task instanceof StatusAnnouncerTask);
|
||||
}
|
||||
}
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
* 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.tasks;
|
||||
|
||||
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 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);
|
||||
|
||||
// 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());
|
||||
task = new StatusAnnouncerTask(getContext()) {
|
||||
@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);
|
||||
|
||||
AndroidMock.replay(tts);
|
||||
|
||||
ttsInitListener.onInit(TextToSpeech.SUCCESS);
|
||||
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testStart_notReady() {
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
assertNotNull(ttsInitListener);
|
||||
|
||||
AndroidMock.replay(tts);
|
||||
|
||||
ttsInitListener.onInit(TextToSpeech.ERROR);
|
||||
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
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);
|
||||
task.shutdown();
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
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(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);
|
||||
|
||||
// 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);
|
||||
task.announce(stats);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_notReady() throws Exception {
|
||||
// Put task in "not ready" state
|
||||
startTask(TextToSpeech.ERROR);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_duringCall() throws Exception {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
expect(tts.isSpeaking()).andStubReturn(false);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK, null);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_ringWhileSpeaking() throws Exception {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
expect(tts.isSpeaking()).andStubReturn(true);
|
||||
expect(tts.stop()).andReturn(TextToSpeech.SUCCESS);
|
||||
|
||||
AndroidMock.replay(tts);
|
||||
|
||||
// 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.run(null);
|
||||
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_whileRinging() throws Exception {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
expect(tts.isSpeaking()).andStubReturn(false);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
PhoneStateListener phoneListener = phoneListenerCapture.getValue();
|
||||
phoneListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING, null);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_noService() throws Exception {
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
public void testRun_noStats() throws Exception {
|
||||
// Expect service data calls
|
||||
|
||||
startTask(TextToSpeech.SUCCESS);
|
||||
|
||||
// Run the announcement
|
||||
AndroidMock.replay(tts);
|
||||
task.run(null);
|
||||
AndroidMock.verify(mockTask, tts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StatusAnnouncerTask#getAnnounceTime(long)} with time zero.
|
||||
*/
|
||||
public void testGetAnnounceTime_time_zero() {
|
||||
long time = 0; // 0 seconds
|
||||
assertEquals("0 minutes 0 seconds", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StatusAnnouncerTask#getAnnounceTime(long)} with time one.
|
||||
*/
|
||||
public void testGetAnnounceTime_time_one() {
|
||||
long time = 1 * 1000; // 1 second
|
||||
assertEquals("0 minutes 1 second", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StatusAnnouncerTask#getAnnounceTime(long)} with singular
|
||||
* numbers with the hour unit.
|
||||
*/
|
||||
public void testGetAnnounceTime_singular_has_hour() {
|
||||
long time = (1 * 60 * 60 * 1000) + (1 * 60 * 1000) + (1 * 1000); // 1 hour 1 minute 1 second
|
||||
assertEquals("1 hour 1 minute", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StatusAnnouncerTask#getAnnounceTime(long)} with plural numbers
|
||||
* with the hour unit.
|
||||
*/
|
||||
public void testGetAnnounceTime_plural_has_hour() {
|
||||
long time = (2 * 60 * 60 * 1000) + (2 * 60 * 1000) + (2 * 1000); // 2 hours 2 minutes 2 seconds
|
||||
assertEquals("2 hours 2 minutes", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StatusAnnouncerTask#getAnnounceTime(long)} with singular
|
||||
* numbers without the hour unit.
|
||||
*/
|
||||
public void testGetAnnounceTime_singular_no_hour() {
|
||||
long time = (1 * 60 * 1000) + (1 * 1000); // 1 minute 1 second
|
||||
assertEquals("1 minute 1 second", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link StatusAnnouncerTask#getAnnounceTime(long)} with plural numbers
|
||||
* without the hour unit.
|
||||
*/
|
||||
public void testGetAnnounceTime_plural_no_hour() {
|
||||
long time = (2 * 60 * 1000) + (2 * 1000); // 2 minutes 2 seconds
|
||||
assertEquals("2 minutes 2 seconds", task.getAnnounceTime(time));
|
||||
}
|
||||
|
||||
private void startTask(int state) {
|
||||
AndroidMock.resetToNice(tts);
|
||||
AndroidMock.replay(tts);
|
||||
doStart();
|
||||
OnInitListener ttsInitListener = initListenerCapture.getValue();
|
||||
ttsInitListener.onInit(state);
|
||||
AndroidMock.resetToDefault(tts);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user