chromium/services/device/geolocation/android/junit/src/org/chromium/device/geolocation/LocationProviderTest.java

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.device.geolocation;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import android.content.Context;
import android.location.LocationManager;

import com.google.android.gms.location.FusedLocationProviderClient;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.ParameterizedRobolectricTestRunner;
import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLocationManager;
import org.robolectric.shadows.ShadowLog;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.BaseRobolectricTestRule;
import org.chromium.base.test.util.Feature;

import java.util.Arrays;
import java.util.Collection;

/** Test suite for Java Geolocation. */
@RunWith(ParameterizedRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class LocationProviderTest {
    public static enum LocationProviderType {
        MOCK,
        ANDROID,
        GMS_CORE
    }

    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(
                new Object[][] {
                    {LocationProviderType.MOCK},
                    {LocationProviderType.ANDROID},
                    {LocationProviderType.GMS_CORE}
                });
    }

    @Rule(order = -2)
    public BaseRobolectricTestRule mBaseRule = new BaseRobolectricTestRule();

    private LocationManager mLocationManager;
    private ShadowLocationManager mShadowLocationManager;

    private LocationProviderAdapter mLocationProviderAdapter;

    private final LocationProviderType mApi;

    public LocationProviderTest(LocationProviderType api) {
        mApi = api;
    }

    @Before
    public void setUp() {
        ShadowLog.stream = System.out;
        MockitoAnnotations.initMocks(this);
        mLocationManager = RuntimeEnvironment.application.getSystemService(LocationManager.class);
    }

    /** Verify a normal start/stop call pair with the given LocationProvider. */
    @Test
    @Feature({"Location"})
    public void testStartStop() {
        assertTrue("Should be on UI thread", ThreadUtils.runningOnUiThread());

        setLocationProvider();

        createLocationProviderAdapter();
        startLocationProviderAdapter(false);
        stopLocationProviderAdapter();
    }

    /** Verify a start/upgrade/stop call sequencewith the given LocationProvider. */
    @Test
    @Feature({"Location"})
    public void testStartUpgradeStop() {
        assertTrue("Should be on UI thread", ThreadUtils.runningOnUiThread());

        setLocationProvider();

        createLocationProviderAdapter();
        startLocationProviderAdapter(false);
        startLocationProviderAdapter(true);
        stopLocationProviderAdapter();
    }

    private void createLocationProviderAdapter() {
        mLocationProviderAdapter = LocationProviderAdapter.create();
        assertNotNull("LocationProvider", mLocationProviderAdapter);
    }

    private void setLocationProvider() {
        if (mApi == LocationProviderType.MOCK) {
            setLocationProviderMock();
        } else if (mApi == LocationProviderType.ANDROID) {
            setLocationProviderAndroid();
        } else if (mApi == LocationProviderType.GMS_CORE) {
            setLocationProviderGmsCore();
        } else {
            assert false;
        }
    }

    private void setLocationProviderMock() {
        LocationProviderFactory.setLocationProviderImpl(new MockLocationProvider());
    }

    private void setLocationProviderAndroid() {
        LocationProviderAndroid locationProviderAndroid = new LocationProviderAndroid();

        // Robolectric has a ShadowLocationManager class that mocks the behaviour of the real
        // class very closely. Use it here.
        mShadowLocationManager = Shadows.shadowOf(mLocationManager);
        locationProviderAndroid.setLocationManagerForTesting(mLocationManager);
        LocationProviderFactory.setLocationProviderImpl(locationProviderAndroid);
    }

    private void setLocationProviderGmsCore() {
        Context context = Mockito.mock(Context.class);
        FusedLocationProviderClient fusedLocationProviderClient =
                Mockito.mock(FusedLocationProviderClient.class);

        LocationProviderGmsCore locationProviderGmsCore =
                new LocationProviderGmsCore(context, fusedLocationProviderClient);

        LocationProviderFactory.setLocationProviderImpl(locationProviderGmsCore);
    }

    private void startLocationProviderAdapter(boolean highResolution) {
        mLocationProviderAdapter.start(highResolution);
        assertTrue("Should be running", mLocationProviderAdapter.isRunning());
    }

    private void stopLocationProviderAdapter() {
        mLocationProviderAdapter.stop();
        assertFalse("Should have stopped", mLocationProviderAdapter.isRunning());
    }
}