package org.onebusaway.android.util.test; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.common.api.GoogleApiClient; import org.onebusaway.android.app.Application; import org.onebusaway.android.util.LocationUtils; import org.onebusaway.android.util.TestUtils; import android.location.Location; import android.os.Build; import android.os.SystemClock; import android.test.AndroidTestCase; import android.util.Log; /** * Tests to evaluate location utilities */ public class LocationUtilsTest extends AndroidTestCase { public static final String TAG = "LocationUtilTest"; public static final long FRESH_LOCATION_THRESHOLD_MS = 1000 * 60 * 30; // Within last 30 minutes - see #737 /** * GoogleApiClient being used for Location Services */ GoogleApiClient mGoogleApiClient; @Override protected void setUp() throws Exception { super.setUp(); // Init Google Play Services as early as possible in the Fragment lifecycle to give it time GoogleApiAvailability api = GoogleApiAvailability.getInstance(); if (api.isGooglePlayServicesAvailable(getContext()) == ConnectionResult.SUCCESS) { mGoogleApiClient = LocationUtils.getGoogleApiClientWithCallbacks(getContext()); mGoogleApiClient.connect(); } } @Override protected void tearDown() throws Exception { super.tearDown(); // Tear down GoogleApiClient if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) { mGoogleApiClient.disconnect(); } } public void testLocationComparisonByTime() { boolean result; Location a; Location b; // Test that non-null location is preferred a = new Location("test"); b = null; result = LocationUtils.compareLocationsByTime(a, b); assertTrue(result); a = null; b = new Location("test"); result = LocationUtils.compareLocationsByTime(a, b); assertFalse(result); // Test that location with greater (i.e., newer) timestamp is preferred a = new Location("test"); a.setTime(1001); b = new Location("test"); b.setTime(1000); result = LocationUtils.compareLocationsByTime(a, b); assertTrue(result); a.setTime(1000); b.setTime(1001); result = LocationUtils.compareLocationsByTime(a, b); assertFalse(result); } public void testLocationComparison() { boolean result; Location a; Location b; // Test that non-null location is preferred a = new Location("test"); b = null; result = LocationUtils.compareLocations(a, b); assertTrue(result); a = null; b = new Location("test"); result = LocationUtils.compareLocations(a, b); assertFalse(result); long time = System .currentTimeMillis(); // We have to use current time, because of the age threshold evaluation // We always want the newer location a = new Location("test"); a.setTime(time + 1); b = new Location("test"); b.setTime(time); result = LocationUtils.compareLocations(a, b); assertTrue(result); a.setTime(time); b.setTime(time + 1); result = LocationUtils.compareLocations(a, b); assertFalse(result); // Test that the new location would be saved if the old location is older than the time // threshold, even if the accuracy is worse a = new Location("test"); a.setTime(time); // A is newer a.setAccuracy(LocationUtils.ACC_THRESHOLD + 1); // 1 meter worse than threshold b = new Location("test"); b.setAccuracy(LocationUtils.ACC_THRESHOLD - 1); // 1 meter better than threshold b.setTime(time - LocationUtils.TIME_THRESHOLD - 1); // older than time threshold result = LocationUtils.compareLocations(a, b); assertTrue(result); // A is older, so this should fail, since we never want an older location a = new Location("test"); a.setTime(time - LocationUtils.TIME_THRESHOLD - 2); // A is older a.setAccuracy(LocationUtils.ACC_THRESHOLD + 1); // 1 meter worse than threshold b = new Location("test"); b.setAccuracy(LocationUtils.ACC_THRESHOLD - 1); // 1 meter better than threshold b.setTime(time - LocationUtils.TIME_THRESHOLD - 1); // older than time threshold result = LocationUtils.compareLocations(a, b); assertFalse(result); // Test that location with greater (i.e., newer) timestamp is preferred, as long as it has // a reasonable accuracy a = new Location("test"); a.setTime(time + 1); // A is newer a.setAccuracy(LocationUtils.ACC_THRESHOLD - 1); // 1 meter better than threshold b = new Location("test"); b.setTime(time); result = LocationUtils.compareLocations(a, b); assertTrue(result); a = new Location("test"); a.setTime(time + 1); // A is newer a.setAccuracy(LocationUtils.ACC_THRESHOLD + 1); // 1 meter worse than threshold b = new Location("test"); b.setTime(time); result = LocationUtils.compareLocations(a, b); assertFalse(result); } public void testLocationApiV1() { Location loc; // Make sure we're not running on an emulator, since we'll get a null location there if (!TestUtils.isRunningOnEmulator()) { /** * Test without Google Play Services - should be a Location API v1 location. * Typically this is "gps" or "network", but some devices (e.g., HTC EVO LTE) * have custom Android framework providers such as "hybrid" that might should up here. * So, we can't test for "gps" or "network" specifically. */ loc = Application.getLastKnownLocation(getContext(), null); /** * On devices that behave correctly the following non-null test should pass. However, it's * possible that it can fail on some devices (e.g., on a fresh reboot on a device without * a network connection) */ assertNotNull(loc); Log.d(TAG, "Location Provider for Location API v1 test is '" + loc.getProvider() + "'"); assertFreshLocation(loc); } } public void testLocationServices() { Location loc; // Test with Google Play Services, if its supported, and if we're not running on an emulator GoogleApiAvailability api = GoogleApiAvailability.getInstance(); if (api.isGooglePlayServicesAvailable(getContext()) == ConnectionResult.SUCCESS && !TestUtils.isRunningOnEmulator()) { /** * Could return either a fused or Location API v1 location */ loc = Application.getLastKnownLocation(getContext(), mGoogleApiClient); assertNotNull(loc); Log.d(TAG, "Location Provider for Location Services test is '" + loc.getProvider() + "'"); assertFreshLocation(loc); } } /** * Tests whether location is fresh (i.e., fairly recent) * * @param location Location to test */ private void assertFreshLocation(Location location) { assertTrue(checkFreshLocation(location)); } /** * Returns true if this is a fairly recent location * * @param location Location to check * @return true if this is a fairly recent location, false if it is not. */ private boolean checkFreshLocation(Location location) { long timeDiff; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { timeDiff = SystemClock.elapsedRealtimeNanos() - location.getElapsedRealtimeNanos(); Log.d(TAG, "Location from " + LocationUtils.printLocationDetails(location)); // Use elapsed real-time nanos, since its guaranteed monotonic return timeDiff <= (FRESH_LOCATION_THRESHOLD_MS * 1E6); } else { timeDiff = System.currentTimeMillis() - location.getTime(); Log.d(TAG, "Location from " + LocationUtils.printLocationDetails(location)); return timeDiff <= FRESH_LOCATION_THRESHOLD_MS; } } }