1 package com.android.server.job; 2 3 4 import android.content.ComponentName; 5 import android.content.Context; 6 import android.app.job.JobInfo; 7 import android.app.job.JobInfo.Builder; 8 import android.os.PersistableBundle; 9 import android.test.AndroidTestCase; 10 import android.test.RenamingDelegatingContext; 11 import android.util.Log; 12 import android.util.ArraySet; 13 14 import com.android.server.job.controllers.JobStatus; 15 16 import java.util.Iterator; 17 18 /** 19 * Test reading and writing correctly from file. 20 */ 21 public class JobStoreTest extends AndroidTestCase { 22 private static final String TAG = "TaskStoreTest"; 23 private static final String TEST_PREFIX = "_test_"; 24 25 private static final int SOME_UID = 34234; 26 private ComponentName mComponent; 27 private static final long IO_WAIT = 1000L; 28 29 JobStore mTaskStoreUnderTest; 30 Context mTestContext; 31 32 @Override setUp()33 public void setUp() throws Exception { 34 mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX); 35 Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'"); 36 mTaskStoreUnderTest = 37 JobStore.initAndGetForTesting(mTestContext, mTestContext.getFilesDir()); 38 mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName()); 39 } 40 41 @Override tearDown()42 public void tearDown() throws Exception { 43 mTaskStoreUnderTest.clear(); 44 } 45 testMaybeWriteStatusToDisk()46 public void testMaybeWriteStatusToDisk() throws Exception { 47 int taskId = 5; 48 long runByMillis = 20000L; // 20s 49 long runFromMillis = 2000L; // 2s 50 long initialBackoff = 10000L; // 10s 51 52 final JobInfo task = new Builder(taskId, mComponent) 53 .setRequiresCharging(true) 54 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 55 .setBackoffCriteria(initialBackoff, JobInfo.BACKOFF_POLICY_EXPONENTIAL) 56 .setOverrideDeadline(runByMillis) 57 .setMinimumLatency(runFromMillis) 58 .setPersisted(true) 59 .build(); 60 final JobStatus ts = new JobStatus(task, SOME_UID); 61 mTaskStoreUnderTest.add(ts); 62 Thread.sleep(IO_WAIT); 63 // Manually load tasks from xml file. 64 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>(); 65 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet); 66 67 assertEquals("Didn't get expected number of persisted tasks.", 1, jobStatusSet.size()); 68 final JobStatus loadedTaskStatus = jobStatusSet.iterator().next(); 69 assertTasksEqual(task, loadedTaskStatus.getJob()); 70 assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(ts)); 71 assertEquals("Different uids.", SOME_UID, loadedTaskStatus.getUid()); 72 compareTimestampsSubjectToIoLatency("Early run-times not the same after read.", 73 ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime()); 74 compareTimestampsSubjectToIoLatency("Late run-times not the same after read.", 75 ts.getLatestRunTimeElapsed(), loadedTaskStatus.getLatestRunTimeElapsed()); 76 77 } 78 testWritingTwoFilesToDisk()79 public void testWritingTwoFilesToDisk() throws Exception { 80 final JobInfo task1 = new Builder(8, mComponent) 81 .setRequiresDeviceIdle(true) 82 .setPeriodic(10000L) 83 .setRequiresCharging(true) 84 .setPersisted(true) 85 .build(); 86 final JobInfo task2 = new Builder(12, mComponent) 87 .setMinimumLatency(5000L) 88 .setBackoffCriteria(15000L, JobInfo.BACKOFF_POLICY_LINEAR) 89 .setOverrideDeadline(30000L) 90 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) 91 .setPersisted(true) 92 .build(); 93 final JobStatus taskStatus1 = new JobStatus(task1, SOME_UID); 94 final JobStatus taskStatus2 = new JobStatus(task2, SOME_UID); 95 mTaskStoreUnderTest.add(taskStatus1); 96 mTaskStoreUnderTest.add(taskStatus2); 97 Thread.sleep(IO_WAIT); 98 99 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>(); 100 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet); 101 assertEquals("Incorrect # of persisted tasks.", 2, jobStatusSet.size()); 102 Iterator<JobStatus> it = jobStatusSet.iterator(); 103 JobStatus loaded1 = it.next(); 104 JobStatus loaded2 = it.next(); 105 assertTasksEqual(task1, loaded1.getJob()); 106 assertTasksEqual(task2, loaded2.getJob()); 107 assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(taskStatus1)); 108 assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(taskStatus2)); 109 // Check that the loaded task has the correct runtimes. 110 compareTimestampsSubjectToIoLatency("Early run-times not the same after read.", 111 taskStatus1.getEarliestRunTime(), loaded1.getEarliestRunTime()); 112 compareTimestampsSubjectToIoLatency("Late run-times not the same after read.", 113 taskStatus1.getLatestRunTimeElapsed(), loaded1.getLatestRunTimeElapsed()); 114 compareTimestampsSubjectToIoLatency("Early run-times not the same after read.", 115 taskStatus2.getEarliestRunTime(), loaded2.getEarliestRunTime()); 116 compareTimestampsSubjectToIoLatency("Late run-times not the same after read.", 117 taskStatus2.getLatestRunTimeElapsed(), loaded2.getLatestRunTimeElapsed()); 118 119 } 120 testWritingTaskWithExtras()121 public void testWritingTaskWithExtras() throws Exception { 122 JobInfo.Builder b = new Builder(8, mComponent) 123 .setRequiresDeviceIdle(true) 124 .setPeriodic(10000L) 125 .setRequiresCharging(true) 126 .setPersisted(true); 127 128 PersistableBundle extras = new PersistableBundle(); 129 extras.putDouble("hello", 3.2); 130 extras.putString("hi", "there"); 131 extras.putInt("into", 3); 132 b.setExtras(extras); 133 final JobInfo task = b.build(); 134 JobStatus taskStatus = new JobStatus(task, SOME_UID); 135 136 mTaskStoreUnderTest.add(taskStatus); 137 Thread.sleep(IO_WAIT); 138 139 final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>(); 140 mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet); 141 assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); 142 JobStatus loaded = jobStatusSet.iterator().next(); 143 assertTasksEqual(task, loaded.getJob()); 144 } 145 146 /** 147 * Helper function to throw an error if the provided task and TaskStatus objects are not equal. 148 */ assertTasksEqual(JobInfo first, JobInfo second)149 private void assertTasksEqual(JobInfo first, JobInfo second) { 150 assertEquals("Different task ids.", first.getId(), second.getId()); 151 assertEquals("Different components.", first.getService(), second.getService()); 152 assertEquals("Different periodic status.", first.isPeriodic(), second.isPeriodic()); 153 assertEquals("Different period.", first.getIntervalMillis(), second.getIntervalMillis()); 154 assertEquals("Different inital backoff.", first.getInitialBackoffMillis(), 155 second.getInitialBackoffMillis()); 156 assertEquals("Different backoff policy.", first.getBackoffPolicy(), 157 second.getBackoffPolicy()); 158 159 assertEquals("Invalid charging constraint.", first.isRequireCharging(), 160 second.isRequireCharging()); 161 assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(), 162 second.isRequireDeviceIdle()); 163 assertEquals("Invalid unmetered constraint.", 164 first.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED, 165 second.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED); 166 assertEquals("Invalid connectivity constraint.", 167 first.getNetworkType() == JobInfo.NETWORK_TYPE_ANY, 168 second.getNetworkType() == JobInfo.NETWORK_TYPE_ANY); 169 assertEquals("Invalid deadline constraint.", 170 first.hasLateConstraint(), 171 second.hasLateConstraint()); 172 assertEquals("Invalid delay constraint.", 173 first.hasEarlyConstraint(), 174 second.hasEarlyConstraint()); 175 assertEquals("Extras don't match", 176 first.getExtras().toString(), second.getExtras().toString()); 177 } 178 179 /** 180 * When comparing timestamps before and after DB read/writes (to make sure we're saving/loading 181 * the correct values), there is some latency involved that terrorises a naive assertEquals(). 182 * We define a <code>DELTA_MILLIS</code> as a function variable here to make this comparision 183 * more reasonable. 184 */ compareTimestampsSubjectToIoLatency(String error, long ts1, long ts2)185 private void compareTimestampsSubjectToIoLatency(String error, long ts1, long ts2) { 186 final long DELTA_MILLIS = 700L; // We allow up to 700ms of latency for IO read/writes. 187 assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS + IO_WAIT); 188 } 189 190 private static class StubClass {} 191 192 } 193