builder.setTag(tag); builder.setService(service); builder.setTrigger(trigger); builder.setRetryStrategy(retryStrategy); builder.setRecurring(recur);
@Test public void handleMessage_startExecution() throws Exception { Message startExecutionMsg = Message.obtain(); startExecutionMsg.what = GooglePlayMessageHandler.MSG_START_EXEC; Bundle data = new Bundle(); data.putString(REQUEST_PARAM_TAG, "TAG"); startExecutionMsg.setData(data); startExecutionMsg.replyTo = messengerMock; JobInvocation jobInvocation = new Builder() .setTag("tag") .setService(TestJobService.class.getName()) .setTrigger(Trigger.NOW) .build(); when(receiverMock.prepareJob(any(GooglePlayMessengerCallback.class), eq(data))) .thenReturn(jobInvocation); when(contextMock.bindService( any(Intent.class), any(JobServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(true); handler.handleMessage(startExecutionMsg); verify(contextMock) .bindService(any(Intent.class), jobServiceConnectionCaptor.capture(), eq(BIND_AUTO_CREATE)); assertTrue(jobServiceConnectionCaptor.getValue().hasJobInvocation(jobInvocation)); }
@Test public void executeJob_startedButNotConnected_doNotStartAgain() { JobInvocation jobInvocation = new JobInvocation.Builder() .setTag("tag") .setService("service") .setTrigger(Trigger.NOW) .build(); when(mockContext.bindService( any(Intent.class), any(ServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(true); executionDelegator.executeJob(jobInvocation); verify(mockContext) .bindService(intentCaptor.capture(), connCaptor.capture(), eq(BIND_AUTO_CREATE)); reset(mockContext); executionDelegator.executeJob(jobInvocation); verify(mockContext, never()) .bindService(any(Intent.class), any(JobServiceConnection.class), eq(BIND_AUTO_CREATE)); }
/** * Stops the job if it is running. * * <p>Needed to avoid possibility of sending job result before the reschedule request is received * by Google Play services. */ static void onSchedule(Job job) { // Stop if running synchronized (callbacks) { SimpleArrayMap<String, JobCallback> jobs = callbacks.get(job.getService()); if (jobs == null) { // not running return; } JobCallback jobCallback = jobs.get(job.getTag()); if (jobCallback == null) { // not running return; } } JobInvocation key = new JobInvocation.Builder() .setTag(job.getTag()) .setService(job.getService()) .setTrigger(job.getTrigger()) .build(); ExecutionDelegator.stopJob(key, false /* must not send the result */); } }
@Override public JobInvocation build( String tag, boolean replaceCurrent, List<Integer> constraintList, boolean recurring, int lifetime, JobTrigger trigger, Class<TestJobService> service, Bundle extras, RetryStrategy rs) { // noinspection WrongConstant return new JobInvocation.Builder() .setTag(tag) .setReplaceCurrent(replaceCurrent) .setRecurring(recurring) .setConstraints(toIntArray(constraintList)) .setLifetime(lifetime) .setTrigger(trigger) .setService(service.getName()) .addExtras(extras) .setRetryStrategy(rs) .build(); } }
@Test public void executeJob_alreadyRunning_doesNotBindSecondTime() { JobInvocation jobInvocation = new JobInvocation.Builder() .setTag("tag") .setService("service") .setTrigger(Trigger.NOW) .build(); when(mockContext.bindService( any(Intent.class), any(ServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(true); executionDelegator.executeJob(jobInvocation); verify(mockContext) .bindService(intentCaptor.capture(), connCaptor.capture(), eq(BIND_AUTO_CREATE)); connCaptor.getValue().onServiceConnected(null, noopBinder); assertFalse(connCaptor.getValue().wasUnbound()); assertTrue(connCaptor.getValue().isConnected()); reset(mockContext); executionDelegator.executeJob(jobInvocation); assertFalse(connCaptor.getValue().wasUnbound()); verify(mockContext, never()).unbindService(connCaptor.getValue()); verify(mockContext, never()) .bindService(any(Intent.class), any(ServiceConnection.class), eq(BIND_AUTO_CREATE)); }
@Test public void executeJob_wasStartedButDisconnected_startAgain() { JobInvocation jobInvocation = new JobInvocation.Builder() .setTag("tag") .setService("service") .setTrigger(Trigger.NOW) .build(); when(mockContext.bindService( any(Intent.class), any(ServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(true); executionDelegator.executeJob(jobInvocation); verify(mockContext) .bindService(intentCaptor.capture(), connCaptor.capture(), eq(BIND_AUTO_CREATE)); JobServiceConnection jobServiceConnection = connCaptor.getValue(); jobServiceConnection.onServiceConnected(null, noopBinder); jobServiceConnection.unbind(); verify(mockContext).unbindService(jobServiceConnection); reset(mockContext); executionDelegator.executeJob(jobInvocation); verify(mockContext) .bindService(any(Intent.class), any(JobServiceConnection.class), eq(BIND_AUTO_CREATE)); }
@Test public void jobFinished() throws RemoteException { JobInvocation jobInvocation = new JobInvocation.Builder() .setTag("tag") .setService("service") .setTrigger(Trigger.NOW) .build(); when(mockContext.bindService( any(Intent.class), any(ServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(true); executionDelegator.executeJob(jobInvocation); verify(mockContext) .bindService(intentCaptor.capture(), connCaptor.capture(), eq(BIND_AUTO_CREATE)); JobServiceConnection connection = connCaptor.getValue(); when(iBinderMock.queryLocalInterface(IRemoteJobService.class.getName())) .thenReturn(jobServiceMock); connection.onServiceConnected(null, iBinderMock); verify(jobServiceMock).start(bundleCaptor.capture(), jobCallbackCaptor.capture()); jobCallbackCaptor .getValue() .jobFinished(bundleCaptor.getValue(), JobService.RESULT_FAIL_NORETRY); assertNull(ExecutionDelegator.getJobServiceConnection("service")); assertEquals(JobService.RESULT_FAIL_NORETRY, receiver.lastResult); assertTrue(connection.wasUnbound()); }
@Test public void securityException_unbinds() throws Exception { JobInvocation job = new JobInvocation.Builder() .setTag("TAG") .setTrigger(getContentUriTrigger()) .setService(TestJobService.class.getName()) .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) .build(); when(mockContext.bindService( any(Intent.class), any(ServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenThrow(new SecurityException("should have gracefully handled this security exception")); executionDelegator.executeJob(job); // Verify that bindService was actually called verify(mockContext).bindService(any(Intent.class), connCaptor.capture(), eq(BIND_AUTO_CREATE)); // And that unbindService was called in response to the security exception verify(mockContext).unbindService(connCaptor.getValue()); // And that we should have sent RESULT_FAIL_RETRY back to the calling component assertEquals(JobService.RESULT_FAIL_RETRY, receiver.lastResult); }
@Test public void onStartCommand_executeAction() { JobInvocation job = new JobInvocation.Builder() .setTag("tag") .setService("com.example.foo.FooService") .setTrigger(Trigger.NOW) .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) .setLifetime(Lifetime.UNTIL_NEXT_BOOT) .setConstraints(new int[] {Constraint.DEVICE_IDLE}) .build(); Intent execIntent = new Intent("com.google.android.gms.gcm.ACTION_TASK_READY") .putExtra( "extras", new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX).encode(job, new Bundle())) .putExtra("callback", new InspectableBinder().toPendingCallback()); assertResultWasStartNotSticky(receiver.onStartCommand(execIntent, 0, 101)); verify(receiver, never()).stopSelf(anyInt()); verify(executionDelegatorMock).executeJob(any(JobInvocation.class)); receiver.onJobFinished(job, JobService.RESULT_SUCCESS); verify(receiver).stopSelf(101); }
@Test public void failedToBind_unbind() throws Exception { JobInvocation job = new JobInvocation.Builder() .setTag("TAG") .setTrigger(getContentUriTrigger()) .setService(TestJobService.class.getName()) .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) .build(); when(mockContext.bindService( any(Intent.class), any(ServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(false); executionDelegator.executeJob(job); verify(mockContext) .bindService(intentCaptor.capture(), connCaptor.capture(), eq(BIND_AUTO_CREATE)); verify(mockContext).unbindService(connCaptor.getValue()); assertEquals(JobService.RESULT_FAIL_RETRY, receiver.lastResult); }
@Test public void stop_withCallback_retry() throws Exception { StoppableJobService service = spy(new StoppableJobService(/* shouldReschedule= */ false)); JobInvocation job = new Builder() .setTag("Tag") .setTrigger(Trigger.NOW) .setService(StoppableJobService.class.getName()) .build(); Bundle jobSpecData = getJobCoder().encode(job, new Bundle()); FutureSettingJobCallback callback = new FutureSettingJobCallback(); // start the service IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, callback); IRemoteJobService.Stub.asInterface(service.onBind(null)).stop(jobSpecData, true); flush(service); assertEquals(1, service.getNumberOfStopRequestsReceived()); callback.verifyCalledWithJobAndResult(job, JobService.RESULT_SUCCESS); }
@Test public void stop_withCallback_done() throws Exception { StoppableJobService service = spy(new StoppableJobService(/* shouldReschedule= */ true)); JobInvocation job = new Builder() .setTag("Tag") .setTrigger(Trigger.NOW) .setService(StoppableJobService.class.getName()) .build(); Bundle jobSpecData = getJobCoder().encode(job, new Bundle()); FutureSettingJobCallback callback = new FutureSettingJobCallback(); IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, callback); IRemoteJobService.Stub.asInterface(service.onBind(null)).stop(jobSpecData, true); flush(service); assertEquals(1, service.getNumberOfStopRequestsReceived()); callback.verifyCalledWithJobAndResult(job, JobService.RESULT_FAIL_RETRY); }
@Test public void executeJob_constraintsUnsatisfied_schedulesJobRetry() throws RemoteException { // Simulate job constraints not being met. JobInvocation jobInvocation = new JobInvocation.Builder() .setTag("tag") .setService("service") .setTrigger(Trigger.NOW) .build(); when(constraintChecker.areConstraintsSatisfied(eq(jobInvocation))).thenReturn(false); executionDelegator.executeJob(jobInvocation); // Confirm that service not bound verify(mockContext, never()) .bindService(any(Intent.class), any(JobServiceConnection.class), anyInt()); assertThat(ExecutionDelegator.getJobServiceConnection("service")).isNull(); // Verify that job is set for a retry later. assertThat(receiver.lastResult).isEqualTo(JobService.RESULT_FAIL_RETRY); }
@Test public void stop_noCallback_finished() throws Exception { JobService service = spy(new StoppableJobService(/* shouldReschedule= */ false)); JobInvocation job = new Builder() .setTag("Tag") .setTrigger(Trigger.NOW) .setService(StoppableJobService.class.getName()) .build(); IRemoteJobService.Stub.asInterface(service.onBind(null)) .stop(getJobCoder().encode(job, new Bundle()), true); flush(service); verify(service, never()).onStopJob(job); }
@Test public void testHandleMessage_doesntCrashOnBadJobData() { JobInvocation j = new JobInvocation.Builder() .setService(TestJobService.class.getName()) .setTag("tag") .setTrigger(Trigger.NOW) .build(); executionDelegator.executeJob(j); // noinspection WrongConstant verify(mockContext).bindService(intentCaptor.capture(), connCaptor.capture(), anyInt()); Intent executeReq = intentCaptor.getValue(); assertEquals(JobService.ACTION_EXECUTE, executeReq.getAction()); }
@Test public void contract_hashCode_equals_triggerShouldBeIgnored() { JobInvocation jobInvocation = builder.build(); JobInvocation periodic = builder.setTrigger(Trigger.executionWindow(0, 1)).build(); assertEquals(jobInvocation, periodic); assertEquals(jobInvocation.hashCode(), periodic.hashCode()); } }
@Test public void onJobFinished_notRecurringContentJob_sendResult() { jobInvocationBuilder.setTrigger( Trigger.contentUriTrigger(Arrays.asList(new ObservedUri(Contacts.CONTENT_URI, 0)))); JobInvocation jobInvocation = receiver.prepareJob(callbackMock, TestUtil.getBundleForContentJobExecution()); receiver.onJobFinished(jobInvocation, JobService.RESULT_SUCCESS); verify(callbackMock).jobFinished(JobService.RESULT_SUCCESS); verifyZeroInteractions(driverMock); }
@Before public void setUp() { MockitoAnnotations.initMocks(this); context = RuntimeEnvironment.application; constraintChecker = new ConstraintChecker(context); jobBuilder = new JobInvocation.Builder().setTag(JOB_TAG).setService(JOB_SERVICE).setTrigger(Trigger.NOW); ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); shadowConnectivityManager = shadowOf(connectivityManager); shadowNetworkInfo = shadowOf(connectivityManager.getActiveNetworkInfo()); }
@Before public void setUp() { builder = new Builder() .setTag("tag") .setService(TestJobService.class.getName()) .setTrigger(Trigger.NOW); }