// Copyright 2016 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.firebase.jobdispatcher; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Parcelable; import android.support.annotation.NonNull; import android.text.TextUtils; import java.util.Arrays; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = 23) public class GooglePlayDriverTest { @Mock public Context mMockContext; private TestJobDriver mDriver; private FirebaseJobDispatcher mDispatcher; @Before public void setUp() { MockitoAnnotations.initMocks(this); mDriver = new TestJobDriver(new GooglePlayDriver(mMockContext)); mDispatcher = new FirebaseJobDispatcher(mDriver); when(mMockContext.getPackageName()).thenReturn("foo.bar.whatever"); } @Test public void testSchedule_failsWhenPlayServicesIsUnavailable() throws Exception { markBackendUnavailable(); mockPackageManagerInfo(); Job job = null; try { job = mDispatcher.newJobBuilder() .setService(TestJobService.class) .setTag("foobar") .setConstraints(Constraint.DEVICE_CHARGING) .setTrigger(Trigger.executionWindow(0, 60)) .build(); } catch (ValidationEnforcer.ValidationException ve) { fail(TextUtils.join("\n", ve.getErrors())); } assertEquals("Expected schedule() request to fail when backend is unavailable", FirebaseJobDispatcher.SCHEDULE_RESULT_NO_DRIVER_AVAILABLE, mDispatcher.schedule(job)); } @Test public void testCancelJobs_backendUnavailable() throws Exception { markBackendUnavailable(); assertEquals("Expected cancelAll() request to fail when backend is unavailable", FirebaseJobDispatcher.CANCEL_RESULT_NO_DRIVER_AVAILABLE, mDispatcher.cancelAll()); } @Test public void testSchedule_sendsAppropriateBroadcast() { ArgumentCaptor<Intent> pmQueryIntentCaptor = mockPackageManagerInfo(); Job job = mDispatcher.newJobBuilder() .setConstraints(Constraint.DEVICE_CHARGING) .setService(TestJobService.class) .setTrigger(Trigger.executionWindow(0, 60)) .setRecurring(false) .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) .setTag("foobar") .build(); Intent pmQueryIntent = pmQueryIntentCaptor.getValue(); assertEquals(JobService.ACTION_EXECUTE, pmQueryIntent.getAction()); assertEquals(TestJobService.class.getName(), pmQueryIntent.getComponent().getClassName()); assertEquals("Expected schedule() request to succeed", FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS, mDispatcher.schedule(job)); final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); verify(mMockContext).sendBroadcast(captor.capture()); Intent broadcast = captor.getValue(); assertNotNull(broadcast); assertEquals("com.google.android.gms.gcm.ACTION_SCHEDULE", broadcast.getAction()); assertEquals("SCHEDULE_TASK", broadcast.getStringExtra("scheduler_action")); assertEquals("com.google.android.gms", broadcast.getPackage()); assertEquals(8, broadcast.getIntExtra("source", -1)); assertEquals(1, broadcast.getIntExtra("source_version", -1)); final Parcelable parcelablePendingIntent = broadcast.getParcelableExtra("app"); assertTrue("Expected 'app' value to be a PendingIntent", parcelablePendingIntent instanceof PendingIntent); } private ArgumentCaptor<Intent> mockPackageManagerInfo() { PackageManager packageManager = mock(PackageManager.class); when(mMockContext.getPackageManager()).thenReturn(packageManager); ArgumentCaptor<Intent> intentArgCaptor = ArgumentCaptor.forClass(Intent.class); ResolveInfo info = new ResolveInfo(); info.serviceInfo = new ServiceInfo(); info.serviceInfo.enabled = true; //noinspection WrongConstant when(packageManager.queryIntentServices(intentArgCaptor.capture(), eq(0))) .thenReturn(Arrays.asList(info)); return intentArgCaptor; } @Test public void testCancel_sendsAppropriateBroadcast() { mDispatcher.cancel("foobar"); ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); verify(mMockContext).sendBroadcast(captor.capture()); Intent broadcast = captor.getValue(); assertNotNull(broadcast); assertEquals("foobar", broadcast.getStringExtra("tag")); } private void markBackendUnavailable() { mDriver.available = false; } public final static class TestJobDriver implements Driver { public boolean available = true; private final Driver wrappedDriver; public TestJobDriver(Driver wrappedDriver) { this.wrappedDriver = wrappedDriver; } @Override public int schedule(@NonNull Job job) { return this.wrappedDriver.schedule(job); } @Override public int cancel(@NonNull String tag) { return this.wrappedDriver.cancel(tag); } @Override public int cancelAll() { return this.wrappedDriver.cancelAll(); } @NonNull @Override public JobValidator getValidator() { return this.wrappedDriver.getValidator(); } @Override public boolean isAvailable() { return available; } } }