/* * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file 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 mobileconnectors.geo.tracker; import android.location.Location; import com.amazonaws.mobileconnectors.geo.tracker.EmptyTrackingListener; import com.amazonaws.mobileconnectors.geo.tracker.TrackingPublisher; import com.amazonaws.services.geo.AmazonLocationClient; import com.amazonaws.services.geo.model.BatchUpdateDevicePositionRequest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.robolectric.RobolectricTestRunner; import org.robolectric.shadows.ShadowLog; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static mobileconnectors.geo.tracker.MockLocationFactory.DEFAULT_ALTITUDE; import static mobileconnectors.geo.tracker.MockLocationFactory.DEFAULT_LATITUDE; import static mobileconnectors.geo.tracker.MockLocationFactory.DEFAULT_LONGITUDE; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @RunWith(RobolectricTestRunner.class) public class TrackingPublisherTest { private static final int WORKER_POOL_SIZE = 5; private static final long PUBLISH_INTERVAL_MS = TimeUnit.SECONDS.toMillis(2); private static final int BATCH_SIZE = 10; private static final long LATCH_WAIT_BASE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(3); private static final String TRACKER_NAME = "TRACKER_NAME"; private TrackingPublisher trackingPublisher; private AmazonLocationClient mockLocationClient; @Before public void setup() { ShadowLog.stream = System.out; mockLocationClient = mock(AmazonLocationClient.class); trackingPublisher = new TrackingPublisher(mockLocationClient, "UNIT_TEST_DEVICE_ID", TRACKER_NAME, WORKER_POOL_SIZE, PUBLISH_INTERVAL_MS, BATCH_SIZE, new EmptyTrackingListener()); } /** * Enqueue a location and check that it going into the pending updates queue. */ @Test public void enqueueLocationTest() { Location androidLocation = MockLocationFactory.createAndroidLocation(DEFAULT_LONGITUDE, DEFAULT_LATITUDE, DEFAULT_ALTITUDE); trackingPublisher.enqueue(androidLocation); assertEquals(1, trackingPublisher.pendingPositionUpdates()); } /** * Enqueue enough locations to create one batch. Verify the batch gets enqueued * and any remaining locations are left in the location queue for an eventual next batch. * @throws InterruptedException Not expected. */ @Test public void enqueueMoreThanBatchSizeTest() throws InterruptedException { int numOfLocationUpdates = 15; int expectedNumOfBatchesPublished = 1; int expectedNumOfLocationsRemaining = 5; final CountDownLatch latch = new CountDownLatch(expectedNumOfBatchesPublished); Location androidLocation = MockLocationFactory.createAndroidLocation(DEFAULT_LONGITUDE, DEFAULT_LATITUDE, DEFAULT_ALTITUDE); // Mock the batchUpdateDevicePosition method and countdown the latch when invoked MockLocationClientBehavior.latchedBatchUpdates(mockLocationClient, latch); // Enqueue 15 location updates for(int i = 0; i < numOfLocationUpdates; i++) { trackingPublisher.enqueue(androidLocation); } // There should be 1 batch queued. assertEquals(expectedNumOfBatchesPublished, trackingPublisher.pendingBatches()); // There should be 5 location updates pending in the queue assertEquals(expectedNumOfLocationsRemaining, trackingPublisher.pendingPositionUpdates()); latch.await(LATCH_WAIT_BASE_TIMEOUT_MS * expectedNumOfBatchesPublished, TimeUnit.SECONDS); // The client should have been called once. ArgumentCaptor<BatchUpdateDevicePositionRequest> requestArgumentCaptor = ArgumentCaptor.forClass(BatchUpdateDevicePositionRequest.class); verify(mockLocationClient, times(expectedNumOfBatchesPublished)).batchUpdateDevicePosition(requestArgumentCaptor.capture()); // Verify the tracker name was set to the expected value. assertEquals(TRACKER_NAME, requestArgumentCaptor.getValue().getTrackerName()); // No pending batches should be left. assertEquals(0, trackingPublisher.pendingBatches()); } /** * Enqueue enough to create multiple batches. */ @Test public void enqueueMultipleBatchesTest() throws InterruptedException { int numOfLocationUpdates = 53; int expectedNumOfBatchesPublished = 5; int expectedNumOfLocationsRemaining = 3; final CountDownLatch latch = new CountDownLatch(expectedNumOfBatchesPublished); Location androidLocation = MockLocationFactory.createAndroidLocation(DEFAULT_LONGITUDE, DEFAULT_LATITUDE, DEFAULT_ALTITUDE); // Mock the batchUpdateDevicePosition method and countdown the latch when invoked MockLocationClientBehavior.latchedBatchUpdates(mockLocationClient, latch); for(int i = 0; i < numOfLocationUpdates; i++) { trackingPublisher.enqueue(androidLocation); } assertEquals(expectedNumOfLocationsRemaining, trackingPublisher.pendingPositionUpdates()); assertEquals(expectedNumOfBatchesPublished, trackingPublisher.pendingBatches()); latch.await(LATCH_WAIT_BASE_TIMEOUT_MS * expectedNumOfBatchesPublished, TimeUnit.SECONDS); // The client should have been called once. verify(mockLocationClient, times(expectedNumOfBatchesPublished)).batchUpdateDevicePosition(any(BatchUpdateDevicePositionRequest.class)); // No pending batches should be left. assertEquals(0, trackingPublisher.pendingBatches()); } @Test public void forceFlushTest() throws InterruptedException { int numOfLocationUpdates = 5; int expectedNumOfBatchesPublished = 1; int expectedNumOfLocationsRemaining = 5; final CountDownLatch latch = new CountDownLatch(expectedNumOfBatchesPublished); Location androidLocation = MockLocationFactory.createAndroidLocation(DEFAULT_LONGITUDE, DEFAULT_LATITUDE, DEFAULT_ALTITUDE); // Mock the batchUpdateDevicePosition method and countdown the latch when invoked MockLocationClientBehavior.latchedBatchUpdates(mockLocationClient, latch); // Enqueue 5 location updates for(int i = 0; i < numOfLocationUpdates; i++) { trackingPublisher.enqueue(androidLocation); } // There should be 5 location updates pending in the queue assertEquals(expectedNumOfLocationsRemaining, trackingPublisher.pendingPositionUpdates()); trackingPublisher.forceFlush(); latch.await(LATCH_WAIT_BASE_TIMEOUT_MS * expectedNumOfBatchesPublished, TimeUnit.SECONDS); // The client should have been called once. verify(mockLocationClient, times(expectedNumOfBatchesPublished)).batchUpdateDevicePosition(any(BatchUpdateDevicePositionRequest.class)); // No pending batches should be left. assertEquals(0, trackingPublisher.pendingBatches()); } }