|
|
@ -3,105 +3,211 @@ package com.todoroo.astrid.repeats;
|
|
|
|
import android.annotation.SuppressLint;
|
|
|
|
import android.annotation.SuppressLint;
|
|
|
|
import android.support.test.runner.AndroidJUnit4;
|
|
|
|
import android.support.test.runner.AndroidJUnit4;
|
|
|
|
|
|
|
|
|
|
|
|
import com.google.ical.values.Frequency;
|
|
|
|
|
|
|
|
import com.google.ical.values.RRule;
|
|
|
|
import com.google.ical.values.RRule;
|
|
|
|
|
|
|
|
import com.todoroo.astrid.alarms.AlarmService;
|
|
|
|
|
|
|
|
import com.todoroo.astrid.dao.TaskDao;
|
|
|
|
import com.todoroo.astrid.data.Task;
|
|
|
|
import com.todoroo.astrid.data.Task;
|
|
|
|
|
|
|
|
import com.todoroo.astrid.gcal.GCalHelper;
|
|
|
|
|
|
|
|
import com.todoroo.astrid.test.DatabaseTestCase;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.junit.After;
|
|
|
|
|
|
|
|
import org.junit.Before;
|
|
|
|
import org.junit.Test;
|
|
|
|
import org.junit.Test;
|
|
|
|
import org.junit.runner.RunWith;
|
|
|
|
import org.junit.runner.RunWith;
|
|
|
|
|
|
|
|
import org.mockito.InOrder;
|
|
|
|
|
|
|
|
import org.tasks.LocalBroadcastManager;
|
|
|
|
|
|
|
|
import org.tasks.injection.TestComponent;
|
|
|
|
import org.tasks.time.DateTime;
|
|
|
|
import org.tasks.time.DateTime;
|
|
|
|
|
|
|
|
|
|
|
|
import java.text.ParseException;
|
|
|
|
import java.text.ParseException;
|
|
|
|
|
|
|
|
|
|
|
|
import static com.google.ical.values.Frequency.DAILY;
|
|
|
|
import javax.inject.Inject;
|
|
|
|
import static com.google.ical.values.Frequency.HOURLY;
|
|
|
|
|
|
|
|
import static com.google.ical.values.Frequency.MINUTELY;
|
|
|
|
import static com.natpryce.makeiteasy.MakeItEasy.with;
|
|
|
|
import static com.google.ical.values.Frequency.WEEKLY;
|
|
|
|
|
|
|
|
import static com.google.ical.values.Frequency.YEARLY;
|
|
|
|
|
|
|
|
import static com.todoroo.andlib.utility.DateUtilities.addCalendarMonthsToUnixtime;
|
|
|
|
|
|
|
|
import static com.todoroo.astrid.repeats.RepeatTaskHelper.computeNextDueDate;
|
|
|
|
|
|
|
|
import static java.util.concurrent.TimeUnit.DAYS;
|
|
|
|
|
|
|
|
import static java.util.concurrent.TimeUnit.HOURS;
|
|
|
|
|
|
|
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
|
|
|
|
|
|
|
import static junit.framework.Assert.assertEquals;
|
|
|
|
import static junit.framework.Assert.assertEquals;
|
|
|
|
|
|
|
|
import static org.mockito.Mockito.inOrder;
|
|
|
|
|
|
|
|
import static org.mockito.Mockito.mock;
|
|
|
|
|
|
|
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
|
|
|
|
|
|
import static org.tasks.makers.TaskMaker.AFTER_COMPLETE;
|
|
|
|
|
|
|
|
import static org.tasks.makers.TaskMaker.COMPLETION_TIME;
|
|
|
|
|
|
|
|
import static org.tasks.makers.TaskMaker.DUE_TIME;
|
|
|
|
|
|
|
|
import static org.tasks.makers.TaskMaker.ID;
|
|
|
|
|
|
|
|
import static org.tasks.makers.TaskMaker.RRULE;
|
|
|
|
|
|
|
|
import static org.tasks.makers.TaskMaker.newTask;
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressLint("NewApi")
|
|
|
|
@SuppressLint("NewApi")
|
|
|
|
@RunWith(AndroidJUnit4.class)
|
|
|
|
@RunWith(AndroidJUnit4.class)
|
|
|
|
public class RepeatTaskHelperTest {
|
|
|
|
public class RepeatTaskHelperTest extends DatabaseTestCase {
|
|
|
|
|
|
|
|
|
|
|
|
private final Task task = new Task();
|
|
|
|
private final Task task = new Task();
|
|
|
|
private final long dueDate;
|
|
|
|
|
|
|
|
private final long completionDate;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
completionDate = new DateTime(2014, 1, 7, 17, 17, 32, 900).getMillis();
|
|
|
|
task.setDueDate(new DateTime(2013, 12, 31, 17, 17, 32, 900).getMillis());
|
|
|
|
dueDate = new DateTime(2013, 12, 31, 17, 17, 32, 900).getMillis();
|
|
|
|
task.setCompletionDate(new DateTime(2014, 1, 7, 17, 17, 32, 900).getMillis());
|
|
|
|
task.setDueDate(dueDate);
|
|
|
|
}
|
|
|
|
task.setCompletionDate(completionDate);
|
|
|
|
|
|
|
|
|
|
|
|
@Inject TaskDao taskDao;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private LocalBroadcastManager localBroadcastManager;
|
|
|
|
|
|
|
|
private AlarmService alarmService;
|
|
|
|
|
|
|
|
private GCalHelper gCalHelper;
|
|
|
|
|
|
|
|
private RepeatTaskHelper helper;
|
|
|
|
|
|
|
|
private InOrder mocks;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Before
|
|
|
|
|
|
|
|
public void before() {
|
|
|
|
|
|
|
|
alarmService = mock(AlarmService.class);
|
|
|
|
|
|
|
|
gCalHelper = mock(GCalHelper.class);
|
|
|
|
|
|
|
|
localBroadcastManager = mock(LocalBroadcastManager.class);
|
|
|
|
|
|
|
|
mocks = inOrder(alarmService, gCalHelper, localBroadcastManager);
|
|
|
|
|
|
|
|
helper = new RepeatTaskHelper(gCalHelper, alarmService, taskDao, localBroadcastManager);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@After
|
|
|
|
|
|
|
|
public void after() {
|
|
|
|
|
|
|
|
verifyNoMoreInteractions(localBroadcastManager, gCalHelper, alarmService);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
|
|
public void noRepeat() {
|
|
|
|
|
|
|
|
helper.handleRepeat(newTask(with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30))));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
|
|
public void testMinutelyRepeat() throws ParseException {
|
|
|
|
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
|
|
|
|
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
|
|
|
|
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;INTERVAL=30")));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repeatAndVerify(task,
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 13, 30, 1),
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 14, 0, 1));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
|
|
public void testMinutelyRepeatAfterCompletion() throws ParseException {
|
|
|
|
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
|
|
|
|
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
|
|
|
|
|
|
|
|
with(COMPLETION_TIME, new DateTime(2017, 10, 4, 13, 17, 45, 340)),
|
|
|
|
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;INTERVAL=30")),
|
|
|
|
|
|
|
|
with(AFTER_COMPLETE, true));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repeatAndVerify(task,
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 13, 30, 1),
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 13, 47, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
|
public void testMinutelyRepeat() {
|
|
|
|
public void testMinutelyDecrementCount() throws ParseException {
|
|
|
|
checkFrequency(6, MINUTES.toMillis(1), MINUTELY);
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
|
|
|
|
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
|
|
|
|
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;COUNT=2;INTERVAL=30")));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repeatAndVerify(task,
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 13, 30, 1),
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 14, 0, 1));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assertEquals(1, new RRule(task.getRecurrenceWithoutFrom()).getCount());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
|
|
public void testMinutelyLastOccurrence() throws ParseException {
|
|
|
|
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
|
|
|
|
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
|
|
|
|
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=MINUTELY;COUNT=1;INTERVAL=30")));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
helper.handleRepeat(task);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
|
|
public void testHourlyRepeat() throws ParseException {
|
|
|
|
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
|
|
|
|
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
|
|
|
|
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=HOURLY;INTERVAL=6")));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repeatAndVerify(task,
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 13, 30, 1),
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 19, 30, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
|
public void testHourlyRepeat() {
|
|
|
|
public void testHourlyRepeatAfterCompletion() throws ParseException {
|
|
|
|
checkFrequency(6, HOURS.toMillis(1), HOURLY);
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
|
|
|
|
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
|
|
|
|
|
|
|
|
with(COMPLETION_TIME, new DateTime(2017, 10, 4, 13, 17, 45, 340)),
|
|
|
|
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=HOURLY;INTERVAL=6")),
|
|
|
|
|
|
|
|
with(AFTER_COMPLETE, true));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repeatAndVerify(task,
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 13, 30, 1),
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 19, 17, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
|
public void testDailyRepeat() {
|
|
|
|
public void testDailyRepeat() throws ParseException {
|
|
|
|
checkFrequency(6, DAYS.toMillis(1), DAILY);
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
|
|
|
|
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
|
|
|
|
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=DAILY;INTERVAL=6")));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repeatAndVerify(task,
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 13, 30, 1),
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 10, 13, 30, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
|
public void testWeeklyRepeat() {
|
|
|
|
public void testRepeatWeeklyNoDays() throws ParseException {
|
|
|
|
checkFrequency(6, DAYS.toMillis(7), WEEKLY);
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
|
|
|
|
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
|
|
|
|
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=WEEKLY;INTERVAL=2")));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repeatAndVerify(task,
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 13, 30, 1),
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 18, 13, 30, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
|
public void testMonthlyRepeat() {
|
|
|
|
public void testYearly() throws ParseException {
|
|
|
|
assertEquals(
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
new DateTime(2014, 7, 7, 17, 17, 1, 0).getMillis(),
|
|
|
|
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
|
|
|
|
nextDueDate(6, Frequency.MONTHLY, true));
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=YEARLY;INTERVAL=3")));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repeatAndVerify(task,
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 13, 30, 1),
|
|
|
|
|
|
|
|
new DateTime(2020, 10, 4, 13, 30, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
|
public void testMonthlyRepeatAtEndOfMonth() {
|
|
|
|
public void testMonthlyRepeat() throws ParseException {
|
|
|
|
assertEquals(
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
new DateTime(2014, 6, 30, 17, 17, 1, 0).getMillis(),
|
|
|
|
with(DUE_TIME, new DateTime(2017, 10, 4, 13, 30)),
|
|
|
|
nextDueDate(6, Frequency.MONTHLY, false));
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=MONTHLY;INTERVAL=3")));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repeatAndVerify(task,
|
|
|
|
|
|
|
|
new DateTime(2017, 10, 4, 13, 30, 1),
|
|
|
|
|
|
|
|
new DateTime(2018, 1, 4, 13, 30, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
@Test
|
|
|
|
public void testYearlyRepeat() {
|
|
|
|
public void testMonthlyRepeatAtEndOfMonth() throws ParseException {
|
|
|
|
checkExpected(6, addCalendarMonthsToUnixtime(dueDate, 6 * 12), YEARLY, false);
|
|
|
|
Task task = newTask(with(ID, 1L),
|
|
|
|
checkExpected(6, addCalendarMonthsToUnixtime(completionDate, 6 * 12), YEARLY, true);
|
|
|
|
with(DUE_TIME, new DateTime(2017, 1, 31, 13, 30)),
|
|
|
|
}
|
|
|
|
with(RRULE, new RRule("RRULE:FREQ=MONTHLY;INTERVAL=1")));
|
|
|
|
|
|
|
|
|
|
|
|
private void checkFrequency(int count, long interval, Frequency frequency) {
|
|
|
|
repeatAndVerify(task,
|
|
|
|
checkExpected(count, dueDate + count * interval, frequency, false);
|
|
|
|
new DateTime(2017, 1, 31, 13, 30, 1),
|
|
|
|
checkExpected(count, completionDate + count * interval, frequency, true);
|
|
|
|
new DateTime(2017, 2, 28, 13, 30, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void checkExpected(int count, long expected, Frequency frequency, boolean repeatAfterCompletion) {
|
|
|
|
private void repeatAndVerify(Task task, DateTime oldDueDate, DateTime newDueDate) {
|
|
|
|
assertEquals(
|
|
|
|
helper.handleRepeat(task);
|
|
|
|
new DateTime(expected)
|
|
|
|
|
|
|
|
.withSecondOfMinute(1)
|
|
|
|
mocks.verify(gCalHelper).rescheduleRepeatingTask(task);
|
|
|
|
.withMillisOfSecond(0)
|
|
|
|
mocks.verify(alarmService).rescheduleAlarms(1, oldDueDate.getMillis(), newDueDate.getMillis());
|
|
|
|
.getMillis(),
|
|
|
|
mocks.verify(localBroadcastManager).broadcastRepeat(1, oldDueDate.getMillis(), newDueDate.getMillis());
|
|
|
|
nextDueDate(count, frequency, repeatAfterCompletion));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
private long nextDueDate(int count, Frequency frequency, boolean repeatAfterCompletion) {
|
|
|
|
protected void inject(TestComponent component) {
|
|
|
|
RRule rrule = new RRule();
|
|
|
|
component.inject(this);
|
|
|
|
rrule.setInterval(count);
|
|
|
|
|
|
|
|
rrule.setFreq(frequency);
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
return computeNextDueDate(task, rrule.toIcal(), repeatAfterCompletion);
|
|
|
|
|
|
|
|
} catch (ParseException e) {
|
|
|
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|