| Index: chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java
|
| diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b448d9d3e4ab45199ca7512e99a8d480a848f572
|
| --- /dev/null
|
| +++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeaderTest.java
|
| @@ -0,0 +1,393 @@
|
| +// Copyright 2017 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.chrome.browser.omnibox.geo;
|
| +
|
| +import static org.junit.Assert.assertEquals;
|
| +import static org.junit.Assert.assertNull;
|
| +import static org.mockito.Mockito.when;
|
| +
|
| +import android.content.Context;
|
| +import android.location.Location;
|
| +import android.os.Build;
|
| +import android.os.SystemClock;
|
| +import android.util.Base64;
|
| +
|
| +import org.junit.Before;
|
| +import org.junit.Rule;
|
| +import org.junit.Test;
|
| +import org.junit.runner.RunWith;
|
| +import org.mockito.Mock;
|
| +import org.mockito.MockitoAnnotations;
|
| +import org.robolectric.annotation.Config;
|
| +import org.robolectric.annotation.Implementation;
|
| +import org.robolectric.annotation.Implements;
|
| +
|
| +import org.chromium.base.library_loader.ProcessInitException;
|
| +import org.chromium.base.metrics.RecordHistogram;
|
| +import org.chromium.chrome.browser.ChromeFeatureList;
|
| +import org.chromium.chrome.browser.omnibox.geo.GeolocationHeaderTest.ShadowRecordHistogram;
|
| +import org.chromium.chrome.browser.omnibox.geo.GeolocationHeaderTest.ShadowUrlUtilities;
|
| +import org.chromium.chrome.browser.omnibox.geo.GeolocationHeaderTest.ShadowWebsitePreferenceBridge;
|
| +import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleCell;
|
| +import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleWifi;
|
| +import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge;
|
| +import org.chromium.chrome.browser.tab.Tab;
|
| +import org.chromium.chrome.browser.util.UrlUtilities;
|
| +import org.chromium.chrome.test.util.browser.Features;
|
| +import org.chromium.testing.local.LocalRobolectricTestRunner;
|
| +
|
| +import java.util.Arrays;
|
| +import java.util.HashSet;
|
| +import java.util.Locale;
|
| +
|
| +/**
|
| + * Robolectric tests for {@link GeolocationHeader}.
|
| + */
|
| +@RunWith(LocalRobolectricTestRunner.class)
|
| +@Config(manifest = Config.NONE,
|
| + shadows = {ShadowUrlUtilities.class, ShadowRecordHistogram.class,
|
| + ShadowWebsitePreferenceBridge.class})
|
| +public class GeolocationHeaderTest {
|
| + private static final String SEARCH_URL = "https://www.google.com/search?q=potatoes";
|
| +
|
| + private static final double LOCATION_LAT = 20.3;
|
| + private static final double LOCATION_LONG = 155.8;
|
| + private static final float LOCATION_ACCURACY = 20f;
|
| + private static final long LOCATION_TIME = 400;
|
| + // Encoded location for LOCATION_LAT, LOCATION_LONG, LOCATION_ACCURACY and LOCATION_TIME.
|
| + private static final String ENCODED_PROTO_LOCATION = "CAEQDBiAtRgqCg3AiBkMFYAx3Vw9AECcRg==";
|
| + private static final String ENCODED_ASCII_LOCATION = "cm9sZToxIHByb2R1Y2VyOjEyIHRpbWVzdGFtcDo0M"
|
| + + "DAwMDAgbGF0bG5ne2xhdGl0dWRlX2U3OjIwMzAwMDAwMCBsb25naXR1ZGVfZTc6MTU1ODAwMDAwMH0gcmFka"
|
| + + "XVzOjIwMDAw";
|
| +
|
| + private static final VisibleWifi VISIBLE_WIFI1 =
|
| + VisibleWifi.create("ssid1", "11:11:11:11:11:11", -1, 10L);
|
| + private static final VisibleWifi VISIBLE_WIFI_NO_LEVEL =
|
| + VisibleWifi.create("ssid1", "11:11:11:11:11:11", null, 10L);
|
| + private static final VisibleWifi VISIBLE_WIFI2 =
|
| + VisibleWifi.create("ssid2", "11:11:11:11:11:12", -10, 20L);
|
| + private static final VisibleWifi VISIBLE_WIFI3 =
|
| + VisibleWifi.create("ssid3", "11:11:11:11:11:13", -30, 30L);
|
| + private static final VisibleWifi VISIBLE_WIFI_NOMAP =
|
| + VisibleWifi.create("ssid1_nomap", "11:11:11:11:11:11", -1, 10L);
|
| + private static final VisibleWifi VISIBLE_WIFI_OPTOUT =
|
| + VisibleWifi.create("ssid1_optout", "11:11:11:11:11:11", -1, 10L);
|
| + private static final VisibleCell VISIBLE_CELL1 =
|
| + VisibleCell.builder(VisibleCell.CDMA_RADIO_TYPE)
|
| + .setCellId(10)
|
| + .setLocationAreaCode(11)
|
| + .setMobileCountryCode(12)
|
| + .setMobileNetworkCode(13)
|
| + .setTimestamp(10L)
|
| + .build();
|
| + private static final VisibleCell VISIBLE_CELL2 = VisibleCell.builder(VisibleCell.GSM_RADIO_TYPE)
|
| + .setCellId(20)
|
| + .setLocationAreaCode(21)
|
| + .setMobileCountryCode(22)
|
| + .setMobileNetworkCode(23)
|
| + .setTimestamp(20L)
|
| + .build();
|
| + // Encoded proto location for VISIBLE_WIFI1 connected, VISIBLE_WIFI3 not connected,
|
| + // VISIBLE_CELL1 connected, VISIBLE_CELL2 not connected.
|
| + private static final String ENCODED_PROTO_VISIBLE_NETWORKS =
|
| + "CAEQDLoBJAoeChExMToxMToxMToxMToxMToxMRD___________8BGAEgCroBJAoeChExMToxMToxMToxMTox"
|
| + + "MToxMxDi__________8BGAAgHroBEBIKCAMQChgLIAwoDRgBIAq6ARASCggBEBQYFSAWKBcYACAU";
|
| +
|
| + private static int sRefreshVisibleNetworksRequests = 0;
|
| + private static int sRefreshLastKnownLocation = 0;
|
| +
|
| + @Rule
|
| + public Features.Processor mFeatureProcessor = new Features.Processor();
|
| +
|
| + @Mock
|
| + private Tab mTab;
|
| +
|
| + @Before
|
| + public void setUp() {
|
| + MockitoAnnotations.initMocks(this);
|
| + GeolocationTracker.setLocationAgeForTesting(null);
|
| + GeolocationHeader.setLocationSourceForTesting(
|
| + GeolocationHeader.LOCATION_SOURCE_HIGH_ACCURACY);
|
| + GeolocationHeader.setAppPermissionGrantedForTesting(true);
|
| + when(mTab.isIncognito()).thenReturn(false);
|
| + sRefreshVisibleNetworksRequests = 0;
|
| + sRefreshLastKnownLocation = 0;
|
| + }
|
| +
|
| + @Test
|
| + public void testEncodeAsciiLocation() throws ProcessInitException {
|
| + Location location = generateMockLocation("should_not_matter", LOCATION_TIME);
|
| + String encodedAsciiLocation = GeolocationHeader.encodeAsciiLocation(location);
|
| + assertEquals(ENCODED_ASCII_LOCATION, encodedAsciiLocation);
|
| + }
|
| +
|
| + @Test
|
| + public void testEncodeProtoLocation() throws ProcessInitException {
|
| + Location location = generateMockLocation("should_not_matter", LOCATION_TIME);
|
| + String encodedProtoLocation = GeolocationHeader.encodeProtoLocation(location);
|
| + assertEquals(ENCODED_PROTO_LOCATION, encodedProtoLocation);
|
| + }
|
| +
|
| + @Test
|
| + public void voidtestTrimVisibleNetworks() throws ProcessInitException {
|
| + VisibleNetworks visibleNetworks =
|
| + VisibleNetworks.create(VISIBLE_WIFI_NO_LEVEL, VISIBLE_CELL1,
|
| + new HashSet(Arrays.asList(VISIBLE_WIFI1, VISIBLE_WIFI2, VISIBLE_WIFI3)),
|
| + new HashSet(Arrays.asList(VISIBLE_CELL1, VISIBLE_CELL2)));
|
| +
|
| + // We expect trimming to replace connected Wifi (since it will have level), and select only
|
| + // the visible wifi different from the connected one, with strongest level.
|
| + VisibleNetworks expectedTrimmed = VisibleNetworks.create(VISIBLE_WIFI1, VISIBLE_CELL1,
|
| + new HashSet(Arrays.asList(VISIBLE_WIFI3)),
|
| + new HashSet(Arrays.asList(VISIBLE_CELL2)));
|
| +
|
| + VisibleNetworks trimmed = GeolocationHeader.trimVisibleNetworks(visibleNetworks);
|
| + assertEquals(expectedTrimmed, trimmed);
|
| + }
|
| +
|
| + @Test
|
| + public void testTrimVisibleNetworksEmptyOrNull() throws ProcessInitException {
|
| + VisibleNetworks visibleNetworks = VisibleNetworks.create(
|
| + VisibleWifi.create("whatever", null, null, null),
|
| + null, new HashSet(), new HashSet());
|
| + assertNull(GeolocationHeader.trimVisibleNetworks(visibleNetworks));
|
| + assertNull(GeolocationHeader.trimVisibleNetworks(null));
|
| + }
|
| +
|
| + @Test
|
| + public void testEncodeProtoVisibleNetworks() throws ProcessInitException {
|
| + VisibleNetworks visibleNetworks = VisibleNetworks.create(VISIBLE_WIFI1, VISIBLE_CELL1,
|
| + new HashSet(Arrays.asList(VISIBLE_WIFI3)),
|
| + new HashSet(Arrays.asList(VISIBLE_CELL2)));
|
| + String encodedProtoLocation = GeolocationHeader.encodeProtoVisibleNetworks(visibleNetworks);
|
| + assertEquals(ENCODED_PROTO_VISIBLE_NETWORKS, encodedProtoLocation);
|
| + }
|
| +
|
| + @Test
|
| + public void testEncodeProtoVisibleNetworksEmptyOrNull() throws ProcessInitException {
|
| + VisibleNetworks visibleNetworks =
|
| + VisibleNetworks.create(null, null, new HashSet(), new HashSet());
|
| + assertNull(GeolocationHeader.encodeProtoVisibleNetworks(visibleNetworks));
|
| + assertNull(GeolocationHeader.encodeProtoVisibleNetworks(null));
|
| + }
|
| +
|
| + @Test
|
| + public void testEncodeProtoVisibleNetworksExcludeNoMapOrOptout() throws ProcessInitException {
|
| + VisibleNetworks visibleNetworks = VisibleNetworks.create(VISIBLE_WIFI_NOMAP, null,
|
| + new HashSet(Arrays.asList(VISIBLE_WIFI_OPTOUT)), new HashSet());
|
| + String encodedProtoLocation = GeolocationHeader.encodeProtoVisibleNetworks(visibleNetworks);
|
| + assertNull(encodedProtoLocation);
|
| + }
|
| +
|
| + @Test
|
| + @Features({@Features.Register(ChromeFeatureList.CONSISTENT_OMNIBOX_GEOLOCATION),
|
| + @Features.Register(ChromeFeatureList.XGEO_VISIBLE_NETWORKS)})
|
| + public void testGetGeoHeaderFreshLocation() throws ProcessInitException {
|
| + VisibleNetworks visibleNetworks = VisibleNetworks.create(VISIBLE_WIFI1, VISIBLE_CELL1,
|
| + new HashSet(Arrays.asList(VISIBLE_WIFI3)),
|
| + new HashSet(Arrays.asList(VISIBLE_CELL2)));
|
| + VisibleNetworksTracker.setVisibleNetworksForTesting(visibleNetworks);
|
| + Location location = generateMockLocation("should_not_matter", LOCATION_TIME);
|
| + GeolocationTracker.setLocationForTesting(location, null);
|
| + // 1 minute should be good enough and not require visible networks.
|
| + GeolocationTracker.setLocationAgeForTesting(1 * 60 * 1000L);
|
| + String header = GeolocationHeader.getGeoHeader(SEARCH_URL, mTab);
|
| + assertEquals("X-Geo: w " + ENCODED_PROTO_LOCATION, header);
|
| + }
|
| +
|
| + @Test
|
| + @Features({@Features.Register(ChromeFeatureList.CONSISTENT_OMNIBOX_GEOLOCATION),
|
| + @Features.Register(ChromeFeatureList.XGEO_VISIBLE_NETWORKS)})
|
| + public void testGetGeoHeaderLocationMissing() throws ProcessInitException {
|
| + VisibleNetworks visibleNetworks = VisibleNetworks.create(VISIBLE_WIFI1, VISIBLE_CELL1,
|
| + new HashSet(Arrays.asList(VISIBLE_WIFI3)),
|
| + new HashSet(Arrays.asList(VISIBLE_CELL2)));
|
| + VisibleNetworksTracker.setVisibleNetworksForTesting(visibleNetworks);
|
| + GeolocationTracker.setLocationForTesting(null, null);
|
| + String header = GeolocationHeader.getGeoHeader(SEARCH_URL, mTab);
|
| + assertEquals("X-Geo: w " + ENCODED_PROTO_VISIBLE_NETWORKS, header);
|
| + }
|
| +
|
| + @Test
|
| + @Features({@Features.Register(ChromeFeatureList.CONSISTENT_OMNIBOX_GEOLOCATION),
|
| + @Features.Register(ChromeFeatureList.XGEO_VISIBLE_NETWORKS)})
|
| + public void testGetGeoHeaderOldLocationHighAccuracy() throws ProcessInitException {
|
| + GeolocationHeader.setLocationSourceForTesting(
|
| + GeolocationHeader.LOCATION_SOURCE_HIGH_ACCURACY);
|
| + // Visible networks should be included
|
| + checkOldLocation(
|
| + "X-Geo: w " + ENCODED_PROTO_LOCATION + " w " + ENCODED_PROTO_VISIBLE_NETWORKS);
|
| + }
|
| +
|
| + @Test
|
| + @Features({@Features.Register(ChromeFeatureList.CONSISTENT_OMNIBOX_GEOLOCATION),
|
| + @Features.Register(ChromeFeatureList.XGEO_VISIBLE_NETWORKS)})
|
| + public void testGetGeoHeaderOldLocationBatterySaving() throws ProcessInitException {
|
| + GeolocationHeader.setLocationSourceForTesting(
|
| + GeolocationHeader.LOCATION_SOURCE_BATTERY_SAVING);
|
| + checkOldLocation(
|
| + "X-Geo: w " + ENCODED_PROTO_LOCATION + " w " + ENCODED_PROTO_VISIBLE_NETWORKS);
|
| + }
|
| +
|
| + @Test
|
| + @Features({@Features.Register(ChromeFeatureList.CONSISTENT_OMNIBOX_GEOLOCATION),
|
| + @Features.Register(ChromeFeatureList.XGEO_VISIBLE_NETWORKS)})
|
| + public void testGetGeoHeaderOldLocationGpsOnly() throws ProcessInitException {
|
| + GeolocationHeader.setLocationSourceForTesting(GeolocationHeader.LOCATION_SOURCE_GPS_ONLY);
|
| + // In GPS only mode, networks should never be included.
|
| + checkOldLocation("X-Geo: w " + ENCODED_PROTO_LOCATION);
|
| + }
|
| +
|
| + @Test
|
| + @Features({@Features.Register(ChromeFeatureList.XGEO_VISIBLE_NETWORKS),
|
| + @Features.Register(ChromeFeatureList.CONSISTENT_OMNIBOX_GEOLOCATION)})
|
| + public void testGetGeoHeaderOldLocationLocationOff() throws ProcessInitException {
|
| + GeolocationHeader.setLocationSourceForTesting(GeolocationHeader.LOCATION_SOURCE_MASTER_OFF);
|
| + // If the master switch is off, networks should never be included (old location might).
|
| + checkOldLocation("X-Geo: w " + ENCODED_PROTO_LOCATION);
|
| + }
|
| +
|
| + @Test
|
| + @Features({@Features.Register(ChromeFeatureList.CONSISTENT_OMNIBOX_GEOLOCATION),
|
| + @Features.Register(ChromeFeatureList.XGEO_VISIBLE_NETWORKS)})
|
| + public void testGetGeoHeaderOldLocationAppPermissionDenied() throws ProcessInitException {
|
| + GeolocationHeader.setLocationSourceForTesting(
|
| + GeolocationHeader.LOCATION_SOURCE_HIGH_ACCURACY);
|
| + GeolocationHeader.setAppPermissionGrantedForTesting(false);
|
| + // Nothing should be included when app permission is missing.
|
| + checkOldLocation(null);
|
| + }
|
| +
|
| + @Test
|
| + @Features({@Features.Register(ChromeFeatureList.CONSISTENT_OMNIBOX_GEOLOCATION),
|
| + @Features.Register(value = ChromeFeatureList.XGEO_VISIBLE_NETWORKS, enabled = false)})
|
| + public void testGetGeoHeaderOldLocationFeatureOff() throws ProcessInitException {
|
| + long timestamp = LOCATION_TIME * 1000;
|
| + int latitudeE7 = (int) (LOCATION_LAT * 10000000);
|
| + int longitudeE7 = (int) (LOCATION_LONG * 10000000);
|
| + int radius = (int) (LOCATION_ACCURACY * 1000);
|
| + String locationAscii = String.format(Locale.US,
|
| + "role:1 producer:12 timestamp:%d latlng{latitude_e7:%d longitude_e7:%d} "
|
| + + "radius:%d",
|
| + timestamp, latitudeE7, longitudeE7, radius);
|
| + String expectedHeader =
|
| + "X-Geo: a " + new String(Base64.encode(locationAscii.getBytes(), Base64.NO_WRAP));
|
| + checkOldLocation(expectedHeader);
|
| + }
|
| +
|
| + @Test
|
| + @Config(shadows = {ShadowVisibleNetworksTracker.class, ShadowGeolocationTracker.class})
|
| + @Features(@Features.Register(value = ChromeFeatureList.XGEO_VISIBLE_NETWORKS, enabled = false))
|
| + public void testPrimeLocationForGeoHeaderFeatureOff() throws ProcessInitException {
|
| + GeolocationHeader.primeLocationForGeoHeader();
|
| + assertEquals(1, sRefreshLastKnownLocation);
|
| + assertEquals(0, sRefreshVisibleNetworksRequests);
|
| + }
|
| +
|
| + @Test
|
| + @Config(shadows = {ShadowVisibleNetworksTracker.class, ShadowGeolocationTracker.class})
|
| + @Features(@Features.Register(ChromeFeatureList.XGEO_VISIBLE_NETWORKS))
|
| + public void testPrimeLocationForGeoHeaderFeatureOn() throws ProcessInitException {
|
| + GeolocationHeader.primeLocationForGeoHeader();
|
| + assertEquals(1, sRefreshLastKnownLocation);
|
| + assertEquals(1, sRefreshVisibleNetworksRequests);
|
| + }
|
| +
|
| + @Test
|
| + @Config(shadows = {ShadowVisibleNetworksTracker.class, ShadowGeolocationTracker.class})
|
| + @Features(@Features.Register(ChromeFeatureList.XGEO_VISIBLE_NETWORKS))
|
| + public void testPrimeLocationForGeoHeaderPermissionOff() throws ProcessInitException {
|
| + GeolocationHeader.setAppPermissionGrantedForTesting(false);
|
| + GeolocationHeader.primeLocationForGeoHeader();
|
| + assertEquals(0, sRefreshLastKnownLocation);
|
| + assertEquals(0, sRefreshVisibleNetworksRequests);
|
| + }
|
| +
|
| + private void checkOldLocation(String expectedHeader) throws ProcessInitException {
|
| + VisibleNetworks visibleNetworks = VisibleNetworks.create(VISIBLE_WIFI1, VISIBLE_CELL1,
|
| + new HashSet(Arrays.asList(VISIBLE_WIFI3)),
|
| + new HashSet(Arrays.asList(VISIBLE_CELL2)));
|
| + VisibleNetworksTracker.setVisibleNetworksForTesting(visibleNetworks);
|
| + Location location = generateMockLocation("should_not_matter", LOCATION_TIME);
|
| + GeolocationTracker.setLocationForTesting(location, null);
|
| + // 6 minutes should hit the age limit, but the feature is off.
|
| + GeolocationTracker.setLocationAgeForTesting(6 * 60 * 1000L);
|
| + String header = GeolocationHeader.getGeoHeader(SEARCH_URL, mTab);
|
| + assertEquals(expectedHeader, header);
|
| + }
|
| +
|
| + private Location generateMockLocation(String provider, long time) {
|
| + Location location = new Location(provider);
|
| + location.setLatitude(LOCATION_LAT);
|
| + location.setLongitude(LOCATION_LONG);
|
| + location.setAccuracy(LOCATION_ACCURACY);
|
| + location.setTime(time);
|
| + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
| + location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()
|
| + + 1000000 * (time - System.currentTimeMillis()));
|
| + }
|
| + return location;
|
| + }
|
| +
|
| + /**
|
| + * Shadow for UrlUtilities
|
| + */
|
| + @Implements(UrlUtilities.class)
|
| + public static class ShadowUrlUtilities {
|
| + @Implementation
|
| + public static boolean nativeIsGoogleSearchUrl(String url) {
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Shadow for RecordHistogram
|
| + */
|
| + @Implements(RecordHistogram.class)
|
| + public static class ShadowRecordHistogram {
|
| + @Implementation
|
| + public static void recordEnumeratedHistogram(String name, int sample, int boundary) {
|
| + // Noop.
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Shadow for WebsitePreferenceBridge
|
| + */
|
| + @Implements(WebsitePreferenceBridge.class)
|
| + public static class ShadowWebsitePreferenceBridge {
|
| + @Implementation
|
| + public static boolean shouldUseDSEGeolocationSetting(String origin, boolean isIncognito) {
|
| + return true;
|
| + }
|
| +
|
| + @Implementation
|
| + public static boolean getDSEGeolocationSetting() {
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Shadow for VisibleNetworksTracker
|
| + */
|
| + @Implements(VisibleNetworksTracker.class)
|
| + public static class ShadowVisibleNetworksTracker {
|
| + @Implementation
|
| + public static void refreshVisibleNetworks(final Context context) {
|
| + sRefreshVisibleNetworksRequests++;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Shadow for GeolocationTracker
|
| + */
|
| + @Implements(GeolocationTracker.class)
|
| + public static class ShadowGeolocationTracker {
|
| + @Implementation
|
| + public static void refreshLastKnownLocation(Context context, long maxAge) {
|
| + sRefreshLastKnownLocation++;
|
| + }
|
| + }
|
| +}
|
|
|