Skip to content
This repository has been archived by the owner on Apr 8, 2020. It is now read-only.

Commit

Permalink
Merge pull request #183 from firebase/0_8_5
Browse files Browse the repository at this point in the history
v0.8.5
  • Loading branch information
unEgor committed Nov 14, 2017
2 parents caa46d1 + fa00498 commit 472f652
Show file tree
Hide file tree
Showing 28 changed files with 1,172 additions and 370 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Play services is unavailable.<br>
Add the following to your `build.gradle`'s dependencies section:

```
compile 'com.firebase:firebase-jobdispatcher:0.8.4'
compile 'com.firebase:firebase-jobdispatcher:0.8.5'
```

### Usage
Expand Down
2 changes: 1 addition & 1 deletion common/constants.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
project.ext {
projectName = "firebase-jobdispatcher-android"
group = "com.firebase"
version = "0.8.4"
version = "0.8.5"
buildtools = "25.0.0"
supportLibraryVersion = "25.0.0"
compileSdk = 25
Expand Down
7 changes: 5 additions & 2 deletions jobdispatcher/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,20 @@ dependencies {

def junit = 'junit:junit:4.12'
def robolectric = 'org.robolectric:robolectric:3.3.2'
def guava = 'com.google.guava:guava:23.2-android'

// The common test library uses JUnit
testLibCompile junit

// The unit tests are written using JUnit, Robolectric, and Mockito
// The unit tests are written using JUnit, Robolectric, Mockito, and Guava
testCompile junit
testCompile robolectric
testCompile guava
testCompile 'org.mockito:mockito-core:2.2.5'

// The Android (e2e) tests are written using JUnit and the test support lib
// The Android (e2e) tests are written using JUnit, the test support lib, and Guava
androidTestCompile junit
androidTestCompile guava
androidTestCompile 'com.android.support.test:runner:0.5'
}

Expand Down
15 changes: 12 additions & 3 deletions jobdispatcher/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,20 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<instrumentation
android:targetPackage="com.firebase.jobdispatcher"
android:name="android.test.InstrumentationTestRunner" />
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.firebase.jobdispatcher"/>

<application>
<service android:exported="false" android:name=".TestJobService">
<service
android:name=".TestJobService"
android:exported="false">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
</intent-filter>
</service>
<service android:name=".WorkerProcessTestJobService"
android:process=":worker"
android:exported="false">
<intent-filter>
<action android:name="com.firebase.jobdispatcher.ACTION_EXECUTE"/>
</intent-filter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,26 @@

package com.firebase.jobdispatcher;

import static org.junit.Assert.assertTrue;
import static com.firebase.jobdispatcher.TestUtil.assertBundlesEqual;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import com.google.common.util.concurrent.SettableFuture;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -36,21 +49,29 @@ public final class EndToEndTest {
private Context appContext;
private FirebaseJobDispatcher dispatcher;

private static final String FIRST_JOB = "first_job";
private static final String SECOND_JOB = "second_job";

@Before
public void setUp() {
appContext = InstrumentationRegistry.getTargetContext();
dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(appContext));
TestJobService.reset();
}

@After
public void tearDown() {
dispatcher.cancelAll();
}

@Test
public void basicImmediateJob() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
public void basicImmediateJob() throws Exception {
final SettableFuture<Bundle> bundleFuture = SettableFuture.create();
TestJobService.setProxy(
new TestJobService.JobServiceProxy() {
@Override
public boolean onStartJob(JobParameters params) {
latch.countDown();
bundleFuture.set(params.getExtras());
return false;
}

Expand All @@ -60,14 +81,212 @@ public boolean onStopJob(JobParameters params) {
}
});

Bundle extras = new Bundle();
extras.putBoolean("extras", true);

dispatcher.mustSchedule(
dispatcher
.newJobBuilder()
.setService(TestJobService.class)
.setTrigger(Trigger.NOW)
.setTag("basic-immediate-job")
.setExtras(extras)
.build());

assertBundlesEqual(extras, bundleFuture.get(120, TimeUnit.SECONDS));
}

@Test
public void startAndRescheduleTwoJobs_sameProcess_stopsOnReschedule() throws Exception {
CountDownLatch startLatch = new CountDownLatch(2);
// Does not need to be concurrent because access is guarded by the startLatch.
Set<String> startedJobs = new HashSet<>();
CountDownLatch stopLatch = new CountDownLatch(2);
// Does not need to be concurrent because access is guarded by the stopLatch.
Set<String> stoppedJobs = new HashSet<>();
TestJobService.setProxy(
new TestJobService.JobServiceProxy() {
@Override
public boolean onStartJob(JobParameters params) {
startedJobs.add(params.getTag());
startLatch.countDown();
return true;
}

@Override
public boolean onStopJob(JobParameters params) {
stoppedJobs.add(params.getTag());
stopLatch.countDown();
return false;
}
});

Bundle extras = new Bundle();
extras.putBoolean("extras", true);

dispatcher.mustSchedule(
dispatcher
.newJobBuilder()
.setService(TestJobService.class)
.setTrigger(Trigger.NOW)
.setTag(FIRST_JOB)
.setExtras(extras)
.build());

dispatcher.mustSchedule(
dispatcher
.newJobBuilder()
.setService(TestJobService.class)
.setTrigger(Trigger.NOW)
.setTag(SECOND_JOB)
.setExtras(extras)
.build());
assertTrue(startLatch.await(120, TimeUnit.SECONDS));
assertEquals(2, startedJobs.size());
assertTrue(startedJobs.contains(FIRST_JOB));
assertTrue(startedJobs.contains(SECOND_JOB));
assertTrue(stoppedJobs.isEmpty());

// reschedule
dispatcher.mustSchedule(
dispatcher
.newJobBuilder()
.setService(TestJobService.class)
.setTrigger(Trigger.NOW)
.setTag(FIRST_JOB)
.setExtras(extras)
.build());

dispatcher.mustSchedule(
dispatcher
.newJobBuilder()
.setService(TestJobService.class)
.setTrigger(Trigger.NOW)
.setTag(SECOND_JOB)
.setExtras(extras)
.build());

assertTrue(stopLatch.await(120, TimeUnit.SECONDS));
assertEquals(2, stoppedJobs.size());
assertTrue(stoppedJobs.contains(FIRST_JOB));
assertTrue(stoppedJobs.contains(SECOND_JOB));
}

@Test
public void startAndRescheduleTwoJobs_workerProcess_stopOnReschedule() throws Exception {
CountDownLatch startLatch = new CountDownLatch(2);
// Does not need to be concurrent because access is guarded by the startLatch.
Set<String> startedJobs = new HashSet<>();
CountDownLatch stopLatch = new CountDownLatch(2);
// Does not need to be concurrent because access is guarded by the stopLatch.
Set<String> stoppedJobs = new HashSet<>();

appContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.i(WorkerProcessTestJobService.TAG, "BroadcastReceiver intent " + intent);
int eventType =
intent.getExtras().getInt(WorkerProcessTestJobService.EXTRA_EVENT_TYPE, -1);
String tag = intent.getExtras().getString(WorkerProcessTestJobService.EXTRA_BUNDLE_TAG);
assertNotNull(tag);

switch (eventType) {
case WorkerProcessTestJobService.EVENT_TYPE_ON_START_JOB:
startedJobs.add(tag);
startLatch.countDown();
break;
case WorkerProcessTestJobService.EVENT_TYPE_ON_STOP_JOB:
stoppedJobs.add(tag);
stopLatch.countDown();
break;
default:
fail("Unexpected event type " + eventType);
}
Bundle results = new Bundle();
results.putBoolean(WorkerProcessTestJobService.EXTRA_MORE_WORK_REMAINING, true);
setResultExtras(results);
}
},
new IntentFilter(WorkerProcessTestJobService.ACTION_JOBSERVICE_EVENT));

dispatcher.mustSchedule(
dispatcher
.newJobBuilder()
.setService(WorkerProcessTestJobService.class)
.setTrigger(Trigger.NOW)
.setTag(FIRST_JOB)
.build());

dispatcher.mustSchedule(
dispatcher
.newJobBuilder()
.setService(WorkerProcessTestJobService.class)
.setTrigger(Trigger.NOW)
.setTag(SECOND_JOB)
.build());
assertTrue(startLatch.await(120, TimeUnit.SECONDS));
assertEquals(2, startedJobs.size());
assertTrue(startedJobs.contains(FIRST_JOB));
assertTrue(startedJobs.contains(SECOND_JOB));
assertTrue(stoppedJobs.isEmpty());

// reschedule
dispatcher.mustSchedule(
dispatcher
.newJobBuilder()
.setService(WorkerProcessTestJobService.class)
.setTrigger(Trigger.NOW)
.setTag(FIRST_JOB)
.build());

dispatcher.mustSchedule(
dispatcher
.newJobBuilder()
.setService(WorkerProcessTestJobService.class)
.setTrigger(Trigger.NOW)
.setTag(SECOND_JOB)
.build());

assertTrue(stopLatch.await(120, TimeUnit.SECONDS));
assertEquals(2, stoppedJobs.size());
assertTrue(stoppedJobs.contains(FIRST_JOB));
assertTrue(stoppedJobs.contains(SECOND_JOB));
}

/**
* Tests that JobServices work correctly when defined in alternative processes. Relies on the
* broadcast-sending code in {@link WorkerProcessTestJobService}.
*/
@Test
public void workerProcessImmediateJob() throws Exception {
final SettableFuture<Bundle> bundleFuture = SettableFuture.create();
appContext.registerReceiver(
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle results = new Bundle();
results.putBoolean(WorkerProcessTestJobService.EXTRA_MORE_WORK_REMAINING, false);
setResultExtras(results);

bundleFuture.set(
intent.getBundleExtra(WorkerProcessTestJobService.EXTRA_BUNDLE_EXTRAS));
}
},
new IntentFilter(WorkerProcessTestJobService.ACTION_JOBSERVICE_EVENT));

Bundle extras = new Bundle();
extras.putBoolean("extras", true);

dispatcher.mustSchedule(
dispatcher
.newJobBuilder()
.setService(WorkerProcessTestJobService.class)
.setTrigger(Trigger.NOW)
.setTag("worker-process-immediate-job")
.setExtras(extras)
.build());

assertTrue("Latch wasn't counted down as expected", latch.await(120, TimeUnit.SECONDS));
assertBundlesEqual(extras, bundleFuture.get(120, TimeUnit.SECONDS));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import static org.junit.Assert.fail;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import java.util.HashSet;
Expand Down Expand Up @@ -125,9 +127,7 @@ public void execute_unbind_jobStopped() throws InterruptedException {
executionDelegator.executeJob(jobInvocation);
assertTrue("Job should be started.", startLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));

synchronized (ExecutionDelegator.serviceConnections) {
ExecutionDelegator.serviceConnections.get(jobInvocation).unbind();
}
ExecutionDelegator.getJobServiceConnection(jobInvocation.getService()).unbind();

assertTrue("Job should be stopped.", stopLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));
}
Expand Down Expand Up @@ -182,9 +182,8 @@ public void executeTwoJobs_stopFirst_secondStays() throws InterruptedException {

assertTrue("First job should be stopped.", stopLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));

synchronized (ExecutionDelegator.serviceConnections) {
assertFalse(ExecutionDelegator.serviceConnections.get(secondJobInvocation).wasUnbound());
}
assertFalse(
ExecutionDelegator.getJobServiceConnection(secondJobInvocation.getService()).wasUnbound());

startLatch = new CountDownLatch(1);
executionDelegator.executeJob(jobInvocation);
Expand All @@ -195,11 +194,10 @@ public void executeTwoJobs_stopFirst_secondStays() throws InterruptedException {

private void verifyStopRequestWasProcessed(boolean withResult) throws InterruptedException {
ExecutionDelegator.stopJob(jobInvocation, withResult);

CountDownLatch latch = new CountDownLatch(1);
executionDelegator.responseHandler.post(() -> latch.countDown());
assertTrue(
"All messages need to be processed.", latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));
// Idle the main looper twice; once to process the call to onStopJob and once to process the
// corresponding jobFinishedCallback that sets the finishedJobInvocation variable.
idleMainLooper();
idleMainLooper();

if (withResult) {
assertEquals(jobInvocation, finishedJobInvocation);
Expand All @@ -209,4 +207,11 @@ private void verifyStopRequestWasProcessed(boolean withResult) throws Interrupte
assertEquals(-1, jobResult);
}
}

private static void idleMainLooper() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
new Handler(Looper.getMainLooper()).post(latch::countDown);
assertTrue(
"Looper didn't run posted runnable.", latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS));
}
}

0 comments on commit 472f652

Please sign in to comment.