JobStoreTest.java revision 7060b04f6d92351b67222e636ab378a0273bf3e7
13d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamspackage com.android.server.task;
23d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
33d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
43d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamsimport android.content.ComponentName;
53d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamsimport android.content.Context;
67060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobInfo;
77060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport android.app.job.JobInfo.Builder;
83d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamsimport android.os.PersistableBundle;
93d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamsimport android.test.AndroidTestCase;
103d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamsimport android.test.RenamingDelegatingContext;
113d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamsimport android.util.Log;
123d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
137060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport com.android.server.job.JobMapReadFinishedListener;
147060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport com.android.server.job.JobStore;
157060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport com.android.server.job.controllers.JobStatus;
163d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
173d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamsimport java.util.List;
183d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
197060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tateimport static com.android.server.job.JobStore.initAndGet;
203d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams/**
213d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams * Test reading and writing correctly from file.
223d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams */
233d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williamspublic class TaskStoreTest extends AndroidTestCase {
243d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private static final String TAG = "TaskStoreTest";
253d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private static final String TEST_PREFIX = "_test_";
263d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    // private static final int USER_NON_0 = 3;
273d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private static final int SOME_UID = 34234;
283d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private ComponentName mComponent;
293d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private static final long IO_WAIT = 600L;
303d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
317060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    JobStore mTaskStoreUnderTest;
323d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    Context mTestContext;
337060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    JobMapReadFinishedListener mTaskMapReadFinishedListenerStub =
347060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate            new JobMapReadFinishedListener() {
353d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        @Override
367060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        public void onJobMapReadFinished(List<JobStatus> tasks) {
373d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams            // do nothing.
383d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        }
393d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    };
403d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
413d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    @Override
423d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    public void setUp() throws Exception {
433d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX);
443d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'");
457060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        mTaskStoreUnderTest = JobStore.initAndGetForTesting(mTestContext,
463d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                mTestContext.getFilesDir(), mTaskMapReadFinishedListenerStub);
473d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName());
483d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
493d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
503d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    @Override
513d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    public void tearDown() throws Exception {
523d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTaskStoreUnderTest.clear();
533d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
543d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
553d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    public void testMaybeWriteStatusToDisk() throws Exception {
563d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        int taskId = 5;
573d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        long runByMillis = 20000L; // 20s
583d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        long runFromMillis = 2000L; // 2s
593d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        long initialBackoff = 10000L; // 10s
603d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
617060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobInfo task = new Builder(taskId, mComponent)
623d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setRequiresCharging(true)
637060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                .setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY)
647060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                .setBackoffCriteria(initialBackoff, JobInfo.BackoffPolicy.EXPONENTIAL)
653d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setOverrideDeadline(runByMillis)
663d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setMinimumLatency(runFromMillis)
673d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .build();
687060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobStatus ts = new JobStatus(task, SOME_UID, true /* persisted */);
693d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTaskStoreUnderTest.add(ts);
703d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        Thread.sleep(IO_WAIT);
713d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        // Manually load tasks from xml file.
727060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() {
733d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams            @Override
747060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate            public void onJobMapReadFinished(List<JobStatus> tasks) {
753d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                assertEquals("Didn't get expected number of persisted tasks.", 1, tasks.size());
767060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                JobStatus loadedTaskStatus = tasks.get(0);
777060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                assertTasksEqual(task, loadedTaskStatus.getJob());
783d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                assertEquals("Different uids.", SOME_UID, tasks.get(0).getUid());
793d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
803d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                        ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime());
813d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
823d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                        ts.getLatestRunTimeElapsed(), loadedTaskStatus.getLatestRunTimeElapsed());
833d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams            }
843d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        });
853d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
863d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
873d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
883d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    public void testWritingTwoFilesToDisk() throws Exception {
897060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobInfo task1 = new Builder(8, mComponent)
903d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setRequiresDeviceIdle(true)
913d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setPeriodic(10000L)
923d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setRequiresCharging(true)
933d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .build();
947060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobInfo task2 = new Builder(12, mComponent)
953d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setMinimumLatency(5000L)
967060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                .setBackoffCriteria(15000L, JobInfo.BackoffPolicy.LINEAR)
973d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setOverrideDeadline(30000L)
987060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                .setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED)
993d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .build();
1007060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobStatus taskStatus1 = new JobStatus(task1, SOME_UID, true /* persisted */);
1017060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobStatus taskStatus2 = new JobStatus(task2, SOME_UID, true /* persisted */);
1023d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTaskStoreUnderTest.add(taskStatus1);
1033d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTaskStoreUnderTest.add(taskStatus2);
1043d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        Thread.sleep(IO_WAIT);
1057060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() {
1063d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams            @Override
1077060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate            public void onJobMapReadFinished(List<JobStatus> tasks) {
1083d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                assertEquals("Incorrect # of persisted tasks.", 2, tasks.size());
1097060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                JobStatus loaded1 = tasks.get(0);
1107060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                JobStatus loaded2 = tasks.get(1);
1117060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                assertTasksEqual(task1, loaded1.getJob());
1127060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                assertTasksEqual(task2, loaded2.getJob());
1133d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1143d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                // Check that the loaded task has the correct runtimes.
1153d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
1163d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                        taskStatus1.getEarliestRunTime(), loaded1.getEarliestRunTime());
1173d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
1183d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                        taskStatus1.getLatestRunTimeElapsed(), loaded1.getLatestRunTimeElapsed());
1193d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
1203d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                        taskStatus2.getEarliestRunTime(), loaded2.getEarliestRunTime());
1213d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
1223d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                        taskStatus2.getLatestRunTimeElapsed(), loaded2.getLatestRunTimeElapsed());
1233d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams            }
1243d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        });
1253d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1263d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
1273d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1283d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    public void testWritingTaskWithExtras() throws Exception {
1297060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        JobInfo.Builder b = new Builder(8, mComponent)
1303d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setRequiresDeviceIdle(true)
1313d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setPeriodic(10000L)
1323d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                .setRequiresCharging(true);
1333d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1343d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        PersistableBundle extras = new PersistableBundle();
1353d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        extras.putDouble("hello", 3.2);
1363d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        extras.putString("hi", "there");
1373d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        extras.putInt("into", 3);
1383d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        b.setExtras(extras);
1397060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        final JobInfo task = b.build();
1407060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        JobStatus taskStatus = new JobStatus(task, SOME_UID, true /* persisted */);
1413d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1423d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        mTaskStoreUnderTest.add(taskStatus);
1433d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        Thread.sleep(IO_WAIT);
1447060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate        mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() {
1453d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams            @Override
1467060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate            public void onJobMapReadFinished(List<JobStatus> tasks) {
1473d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                assertEquals("Incorrect # of persisted tasks.", 1, tasks.size());
1487060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                JobStatus loaded = tasks.get(0);
1497060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                assertTasksEqual(task, loaded.getJob());
1503d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams            }
1513d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        });
1523d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1533d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
1543d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1553d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    /**
1563d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     * Helper function to throw an error if the provided task and TaskStatus objects are not equal.
1573d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     */
1587060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate    private void assertTasksEqual(JobInfo first, JobInfo second) {
1593d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different task ids.", first.getId(), second.getId());
1603d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different components.", first.getService(), second.getService());
1613d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different periodic status.", first.isPeriodic(), second.isPeriodic());
1623d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different period.", first.getIntervalMillis(), second.getIntervalMillis());
1633d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different inital backoff.", first.getInitialBackoffMillis(),
1643d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.getInitialBackoffMillis());
1653d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Different backoff policy.", first.getBackoffPolicy(),
1663d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.getBackoffPolicy());
1673d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1683d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Invalid charging constraint.", first.isRequireCharging(),
1693d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.isRequireCharging());
1703d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(),
1713d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.isRequireDeviceIdle());
1723d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Invalid unmetered constraint.",
1737060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                first.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED,
1747060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                second.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED);
1753d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Invalid connectivity constraint.",
1767060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                first.getNetworkCapabilities() == JobInfo.NetworkType.ANY,
1777060b04f6d92351b67222e636ab378a0273bf3e7Christopher Tate                second.getNetworkCapabilities() == JobInfo.NetworkType.ANY);
1783d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Invalid deadline constraint.",
1793d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                first.hasLateConstraint(),
1803d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.hasLateConstraint());
1813d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Invalid delay constraint.",
1823d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                first.hasEarlyConstraint(),
1833d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                second.hasEarlyConstraint());
1843d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertEquals("Extras don't match",
1853d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams                first.getExtras().toString(), second.getExtras().toString());
1863d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
1873d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1883d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    /**
1893d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     * When comparing timestamps before and after DB read/writes (to make sure we're saving/loading
1903d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     * the correct values), there is some latency involved that terrorises a naive assertEquals().
1913d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     * We define a <code>DELTA_MILLIS</code> as a function variable here to make this comparision
1923d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     * more reasonable.
1933d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams     */
1943d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private void compareTimestampsSubjectToIoLatency(String error, long ts1, long ts2) {
1953d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        final long DELTA_MILLIS = 700L;  // We allow up to 700ms of latency for IO read/writes.
1963d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams        assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS + IO_WAIT);
1973d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    }
1983d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
1993d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams    private static class StubClass {}
2003d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams
2013d86fd2bb9db6067c49634bc4c6cdb4d5235ad36Matthew Williams}