diff --git a/src/androidTest/java/com/todoroo/astrid/repeats/NewRepeatTests.java b/src/androidTest/java/com/todoroo/astrid/repeats/NewRepeatTests.java index 9d663d497..d732342d1 100644 --- a/src/androidTest/java/com/todoroo/astrid/repeats/NewRepeatTests.java +++ b/src/androidTest/java/com/todoroo/astrid/repeats/NewRepeatTests.java @@ -1,255 +1,25 @@ -/** - * Copyright (c) 2012 Todoroo Inc - * - * See the file "LICENSE" for the full license governing this code. - */ package com.todoroo.astrid.repeats; -import android.content.Intent; import android.support.test.runner.AndroidJUnit4; import com.google.ical.values.Frequency; import com.google.ical.values.RRule; import com.google.ical.values.Weekday; import com.google.ical.values.WeekdayNum; -import com.todoroo.andlib.sql.Query; -import com.todoroo.andlib.utility.AndroidUtilities; -import com.todoroo.andlib.utility.DateUtilities; -import com.todoroo.astrid.api.AstridApiConstants; -import com.todoroo.astrid.dao.TaskDao; -import com.todoroo.astrid.dao.TaskDao.TaskCriteria; -import com.todoroo.astrid.data.SyncFlags; import com.todoroo.astrid.data.Task; -import com.todoroo.astrid.test.DatabaseTestCase; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import org.tasks.R; -import org.tasks.injection.TestComponent; -import org.tasks.preferences.Preferences; import org.tasks.time.DateTime; import java.text.ParseException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import javax.inject.Inject; - -import static android.support.test.InstrumentationRegistry.getTargetContext; import static com.todoroo.astrid.repeats.RepeatTaskCompleteListener.computeNextDueDate; +import static java.util.Arrays.asList; import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; -import static org.tasks.date.DateTimeUtils.newDateTime; @RunWith(AndroidJUnit4.class) -public class NewRepeatTests extends DatabaseTestCase { - - @Inject TaskDao taskDao; - @Inject Preferences preferences; - - @Override - public void setUp() { - super.setUp(); - preferences.setStringFromInteger(R.string.p_default_urgency_key, 0); - } - - @Override - protected void inject(TestComponent component) { - component.inject(this); - } - - private void saveAndTriggerRepeatListener(Task task) { - task.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true); - if(task.isSaved()) - taskDao.saveExisting(task); - else - taskDao.createNew(task); - - Intent intent = new Intent(AstridApiConstants.BROADCAST_EVENT_TASK_COMPLETED); - intent.putExtra(AstridApiConstants.EXTRAS_TASK_ID, task.getId()); - new RepeatTaskCompleteListener().onReceive(getTargetContext(), intent); - } - - private void waitAndSync() { - // Subclasses can override this to insert sync functionality - AndroidUtilities.sleepDeep(200L); // Delay to make sure changes persist - } - - private void assertTimesMatch(long expectedTime, long newDueDate) { - assertTrue(String.format("Expected %s, was %s", newDateTime(expectedTime), newDateTime(newDueDate)), - Math.abs(expectedTime - newDueDate) < 5000); - } - - private void assertTimesWithinOneHour(long expectedTime, long newDueDate) { - assertTrue(String.format("Expected %s, was %s", newDateTime(expectedTime), newDateTime(newDueDate)), - Math.abs(expectedTime - newDueDate) <= DateUtilities.ONE_HOUR); - } - - /* - * Tests for no sync - */ - - @Test - public void testNoRepeat() { - Task t = new Task(); - t.setTitle("no repeat"); - taskDao.save(t); - - t.setCompletionDate(DateUtilities.now()); - saveAndTriggerRepeatListener(t); - - assertEquals(1, taskDao.toList(Query.select(Task.ID)).size()); - } - - private void testRepeating(boolean completeBefore, boolean fromCompletion, - RRule rrule, Frequency frequency, String title) { - Task t = new Task(); - t.setTitle(title); - long dueDate = DateUtilities.now() + DateUtilities.ONE_DAY * 3; - DateTime adjustDate = newDateTime(dueDate).withSecondOfMinute(1); - dueDate = adjustDate.getMillis(); - dueDate = (dueDate / 1000L) * 1000L; // Strip milliseconds - - t.setDueDate(dueDate); - - if (rrule == null) { - rrule = new RRule(); - rrule.setFreq(frequency); - int interval = 2; - rrule.setInterval(interval); - } - - String result = rrule.toIcal(); - if (fromCompletion) - result = result + ";FROM=COMPLETION"; - t.setRecurrence(result); - taskDao.save(t); - - waitAndSync(); - t = taskDao.fetch(t.getId(), Task.PROPERTIES); // Refetch - - long completionDate; - if (completeBefore) - completionDate = dueDate - DateUtilities.ONE_DAY; - else - completionDate = dueDate + DateUtilities.ONE_DAY; - t.setCompletionDate(completionDate); - saveAndTriggerRepeatListener(t); - System.err.println("Completion date: " + newDateTime(completionDate)); - - waitAndSync(); - - List tasks = taskDao.toList(Query.select(Task.PROPERTIES).where(TaskCriteria.notDeleted())); - assertEquals(1, tasks.size()); - t = tasks.get(0); - assertEquals(title, t.getTitle()); - assertFalse(t.isCompleted()); - long newDueDate = t.getDueDate(); - assertTrue(t.hasDueTime()); - - long fromDate = (fromCompletion? completionDate : dueDate); - long expectedTime = computeNextDueDateFromDate(fromDate, rrule, fromCompletion); - - if (frequency == Frequency.WEEKLY) // We do this because DST was making the results be off by an hour - assertTimesWithinOneHour(expectedTime, newDueDate); - else - assertTimesMatch(expectedTime, newDueDate); - } - - private long computeWeeklyCaseDueDate(long fromDate, RRule rrule, boolean fromCompletion) { - long result = fromDate; - Frequency frequency = rrule.getFreq(); - assertTrue(frequency.equals(Frequency.WEEKLY)); - List weekdayNums = rrule.getByDay(); - - if (weekdayNums.size() == 0) { - result += DateUtilities.ONE_WEEK * rrule.getInterval(); - return result; - } - HashSet weekdays = new HashSet<>(); - for (WeekdayNum curr : weekdayNums) { - weekdays.add(curr.wday); - } - - Weekday[] allWeekdays = Weekday.values(); - DateTime date = newDateTime(result); - Weekday start = allWeekdays[date.getDayOfWeek()]; - int i; - for (i = 0; i < allWeekdays.length; i++) { - if (start == allWeekdays[i]) break; - } - int index = i; - int daysToAdd = 0; - Weekday next = null; - for (i = index + 1; i < allWeekdays.length; i++) { - Weekday curr = allWeekdays[i]; - daysToAdd++; - if (weekdays.contains(curr)) { - next = curr; - break; - } - } - - if (next == null) { - for (i = 0; i < index + 1; i++) { - Weekday curr = allWeekdays[i]; - daysToAdd++; - if (weekdays.contains(curr)) { - break; - } - } - } - - if (fromCompletion) { - result += DateUtilities.ONE_WEEK * (rrule.getInterval() - 1); - } - result += DateUtilities.ONE_DAY * daysToAdd; - return result; - } - - - /** Advanced weekly repeating tests */ - private long computeNextDueDateFromDate(long fromDate, RRule rrule, boolean fromCompletion) { - long expectedTime = fromDate; - Frequency frequency = rrule.getFreq(); - int interval = rrule.getInterval(); - if (frequency.equals(Frequency.MINUTELY)) { - expectedTime += DateUtilities.ONE_MINUTE * interval; - } else if (frequency.equals(Frequency.HOURLY)) { - expectedTime += DateUtilities.ONE_HOUR * interval; - } else if (frequency.equals(Frequency.DAILY)) { - expectedTime += DateUtilities.ONE_DAY * interval; - } else if (frequency.equals(Frequency.WEEKLY)) { - expectedTime = computeWeeklyCaseDueDate(fromDate, rrule, fromCompletion); - } else if (frequency.equals(Frequency.MONTHLY)) { - DateTime originalDate = newDateTime(expectedTime); - for (int i = 0; i < interval; i++) { - int month = originalDate.getMonthOfYear(); - if (month == 11) { // Roll over the year and set the month to January - originalDate = originalDate - .withYear(originalDate.getYear() + 1) - .withMonthOfYear(1); - } else { - originalDate = originalDate.withMonthOfYear(originalDate.getMonthOfYear() + 1); - } - } - expectedTime = originalDate.getMillis(); - } else if (frequency.equals(Frequency.YEARLY)) { - DateTime originalCompleteDate = newDateTime(expectedTime); - originalCompleteDate = originalCompleteDate.withYear(originalCompleteDate.getYear() + interval); - expectedTime = originalCompleteDate.getMillis(); - } - return expectedTime; - } - - private void testFromCompletionDate(boolean completeBefore, Frequency frequency, String title) { - testRepeating(completeBefore, true, null, frequency, title); - } - - /** Tests for repeating from due date */ +public class NewRepeatTests { @Test public void testRepeatMinutelyFromDueDate() throws ParseException { @@ -301,129 +71,138 @@ public class NewRepeatTests extends DatabaseTestCase { /** Tests for repeating from completionDate */ - @Ignore @Test - public void testRepeatMinutelyFromCompleteDateCompleteBefore() { - testFromCompletionDate(true, Frequency.MINUTELY, "minutely-before"); - } + public void testRepeatMinutelyFromCompleteDateCompleteBefore() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.MINUTELY, 1, dueDateTime, completionDateTime); - @Ignore - @Test - public void testRepeatMinutelyFromCompleteDateCompleteAfter() { - testFromCompletionDate(false, Frequency.MINUTELY, "minutely-after"); + assertEquals(newDayTime(2016, 8, 29, 0, 15), calculateNextDueDate(task)); } - @Ignore @Test - public void testRepeatHourlyFromCompleteDateCompleteBefore() { - testFromCompletionDate(true, Frequency.HOURLY, "hourly-before"); - } + public void testRepeatMinutelyFromCompleteDateCompleteAfter() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.MINUTELY, 1, dueDateTime, completionDateTime); - @Ignore - @Test - public void testRepeatHourlyFromCompleteDateCompleteAfter() { - testFromCompletionDate(false, Frequency.HOURLY, "hourly-after"); + assertEquals(newDayTime(2016, 8, 29, 0, 15), calculateNextDueDate(task)); } - @Ignore @Test - public void testRepeatDailyFromCompleteDateCompleteBefore() { - testFromCompletionDate(true, Frequency.DAILY, "daily-before"); - } + public void testRepeatHourlyFromCompleteDateCompleteBefore() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.HOURLY, 1, dueDateTime, completionDateTime); - @Ignore - @Test - public void testRepeatDailyFromCompleteDateCompleteAfter() { - testFromCompletionDate(false, Frequency.DAILY, "daily-after"); + assertEquals(newDayTime(2016, 8, 29, 1, 14), calculateNextDueDate(task)); } - @Ignore @Test - public void testRepeatWeeklyFromCompleteDateCompleteBefore() { - testFromCompletionDate(true, Frequency.WEEKLY, "weekly-before"); - } + public void testRepeatHourlyFromCompleteDateCompleteAfter() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.HOURLY, 1, dueDateTime, completionDateTime); - @Ignore - @Test - public void testRepeatWeeklyFromCompleteDateCompleteAfter() { - testFromCompletionDate(false, Frequency.WEEKLY, "weekly-after"); + assertEquals(newDayTime(2016, 8, 29, 1, 14), calculateNextDueDate(task)); } - @Ignore @Test - public void testRepeatMonthlyFromCompleteDateCompleteBefore() { - testFromCompletionDate(true, Frequency.MONTHLY, "monthly-before"); + public void testRepeatDailyFromCompleteDateCompleteBefore() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.DAILY, 1, dueDateTime, completionDateTime); + + assertEquals(newDayTime(2016, 8, 30, 0, 25), calculateNextDueDate(task)); } - @Ignore @Test - public void testRepeatMonthlyFromCompleteDateCompleteAfter() { - testFromCompletionDate(false, Frequency.MONTHLY, "monthly-after"); + public void testRepeatDailyFromCompleteDateCompleteAfter() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.DAILY, 1, dueDateTime, completionDateTime); + + assertEquals(newDayTime(2016, 8, 30, 0, 4), calculateNextDueDate(task)); } - @Ignore @Test - public void testRepeatYearlyFromCompleteDateCompleteBefore() { - testFromCompletionDate(true, Frequency.YEARLY, "yearly-before"); + public void testRepeatWeeklyFromCompleteDateCompleteBefore() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.WEEKLY, 1, dueDateTime, completionDateTime); + + assertEquals(newDayTime(2016, 9, 5, 0, 25), calculateNextDueDate(task)); } - @Ignore @Test - public void testRepeatYearlyFromCompleteDateCompleteAfter() { - testFromCompletionDate(false, Frequency.YEARLY, "yearly-after"); + public void testRepeatWeeklyFromCompleteDateCompleteAfter() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.WEEKLY, 1, dueDateTime, completionDateTime); + + assertEquals(newDayTime(2016, 9, 5, 0, 4), calculateNextDueDate(task)); } - private void testAdvancedWeeklyFromDueDate(boolean completeBefore, String title) { - RRule rrule = new RRule(); - rrule.setFreq(Frequency.WEEKLY); + @Test + public void testRepeatMonthlyFromCompleteDateCompleteBefore() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.MONTHLY, 1, dueDateTime, completionDateTime); - int interval = 1; - rrule.setInterval(interval); - List weekdays = new ArrayList<>(); - weekdays.add(new WeekdayNum(0, Weekday.MO)); - weekdays.add(new WeekdayNum(0, Weekday.WE)); - rrule.setByDay(weekdays); - testRepeating(completeBefore, false, rrule, Frequency.WEEKLY, title); + assertEquals(newDayTime(2016, 9, 29, 0, 25), calculateNextDueDate(task)); } - private void testAdvancedWeeklyFromCompleteDate(boolean completeBefore, String title) { - RRule rrule = new RRule(); - rrule.setFreq(Frequency.WEEKLY); + @Test + public void testRepeatMonthlyFromCompleteDateCompleteAfter() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.MONTHLY, 1, dueDateTime, completionDateTime); - int interval = 1; - rrule.setInterval(interval); - List weekdays = new ArrayList<>(); - weekdays.add(new WeekdayNum(0, Weekday.MO)); - weekdays.add(new WeekdayNum(0, Weekday.WE)); - rrule.setByDay(weekdays); - testRepeating(completeBefore, true, rrule, Frequency.WEEKLY, title); + assertEquals(newDayTime(2016, 9, 29, 0, 4), calculateNextDueDate(task)); } + @Test + public void testRepeatYearlyFromCompleteDateCompleteBefore() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 30, 0, 25); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.YEARLY, 1, dueDateTime, completionDateTime); + assertEquals(newDayTime(2017, 8, 29, 0, 25), calculateNextDueDate(task)); + } - // disabled until test can be fixed - @Ignore @Test - public void testAdvancedRepeatWeeklyFromDueDateCompleteBefore() { - testAdvancedWeeklyFromDueDate(true, "advanced-weekly-before"); + public void testRepeatYearlyFromCompleteDateCompleteAfter() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 28, 0, 4); + DateTime completionDateTime = newDayTime(2016, 8, 29, 0, 14); + Task task = newFromCompleted(Frequency.YEARLY, 1, dueDateTime, completionDateTime); + + assertEquals(newDayTime(2017, 8, 29, 0, 4), calculateNextDueDate(task)); } - @Ignore @Test - public void testAdvancedRepeatWeeklyFromDueDateCompleteAfter() { - testAdvancedWeeklyFromDueDate(false, "advanced-weekly-after"); + public void testAdvancedRepeatWeeklyFromDueDate() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 29, 0, 25); + Task task = newWeeklyFromDue(1, dueDateTime, new WeekdayNum(0, Weekday.MO), new WeekdayNum(0, Weekday.WE)); + + assertEquals(newDayTime(2016, 8, 31, 0, 25), calculateNextDueDate(task)); } - @Ignore @Test - public void testAdvancedRepeatWeeklyFromCompleteDateCompleteBefore() { - testAdvancedWeeklyFromCompleteDate(true, "advanced-weekly-before"); + public void testAdvancedRepeatWeeklyFromCompleteDateCompleteBefore() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 29, 0, 25); + DateTime completionDateTime = newDayTime(2016, 8, 28, 1, 9); + Task task = newWeeklyFromCompleted(1, dueDateTime, completionDateTime, new WeekdayNum(0, Weekday.MO), new WeekdayNum(0, Weekday.WE)); + + assertEquals(newDayTime(2016, 8, 29, 0, 25), calculateNextDueDate(task)); } - @Ignore @Test - public void testAdvancedRepeatWeeklyFromCompleteDateCompleteAfter() { - testAdvancedWeeklyFromCompleteDate(false, "advanced-weekly-after"); + public void testAdvancedRepeatWeeklyFromCompleteDateCompleteAfter() throws ParseException { + DateTime dueDateTime = newDayTime(2016, 8, 29, 0, 25); + DateTime completionDateTime = newDayTime(2016, 9, 1, 1, 9); + Task task = newWeeklyFromCompleted(1, dueDateTime, completionDateTime, new WeekdayNum(0, Weekday.MO), new WeekdayNum(0, Weekday.WE)); + + assertEquals(newDayTime(2016, 9, 5, 0, 25), calculateNextDueDate(task)); } private DateTime newDayTime(int year, int month, int day, int hour, int minute) { @@ -441,10 +220,36 @@ public class NewRepeatTests extends DatabaseTestCase { }}; } - private String getRecurrenceRule(Frequency frequency, int interval, boolean fromCompletion) { + private Task newWeeklyFromDue(int interval, DateTime dueDate, WeekdayNum... weekdays) { + return new Task() {{ + setRecurrence(getRecurrenceRule(Frequency.WEEKLY, interval, false, weekdays)); + setDueDate(dueDate.getMillis()); + }}; + } + + private Task newFromCompleted(Frequency frequency, int interval, DateTime dueDate, DateTime completionDate) { + return new Task() {{ + setRecurrence(getRecurrenceRule(frequency, interval, true)); + setDueDate(dueDate.getMillis()); + setCompletionDate(completionDate.getMillis()); + }}; + } + + private Task newWeeklyFromCompleted(int interval, DateTime dueDate, DateTime completionDate, WeekdayNum... weekdays) { + return new Task() {{ + setRecurrence(getRecurrenceRule(Frequency.WEEKLY, interval, true, weekdays)); + setDueDate(dueDate.getMillis()); + setCompletionDate(completionDate.getMillis()); + }}; + } + + private String getRecurrenceRule(Frequency frequency, int interval, boolean fromCompletion, WeekdayNum... weekdays) { RRule rrule = new RRule(); rrule.setFreq(frequency); rrule.setInterval(interval); + if (weekdays != null) { + rrule.setByDay(asList(weekdays)); + } String result = rrule.toIcal(); if (fromCompletion) { result += ";FROM=COMPLETION";