mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 17:18:52 -06:00
Refactor and write unit tests for RepetitionList
This commit is contained in:
@@ -42,33 +42,13 @@ public class CheckmarkListTest
|
|||||||
Habit nonDailyHabit;
|
Habit nonDailyHabit;
|
||||||
private Habit emptyHabit;
|
private Habit emptyHabit;
|
||||||
|
|
||||||
public static final long FIXED_LOCAL_TIME = 1422172800000L; // 8:00am, January 25th, 2015 (UTC)
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void prepare()
|
public void prepare()
|
||||||
{
|
{
|
||||||
DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME);
|
HabitFixtures.purgeHabits();
|
||||||
createNonDailyHabit();
|
DateHelper.setFixedLocalTime(HabitFixtures.FIXED_LOCAL_TIME);
|
||||||
|
nonDailyHabit = HabitFixtures.createNonDailyHabit();
|
||||||
emptyHabit = new Habit();
|
emptyHabit = HabitFixtures.createEmptyHabit();
|
||||||
emptyHabit.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createNonDailyHabit()
|
|
||||||
{
|
|
||||||
nonDailyHabit = new Habit();
|
|
||||||
nonDailyHabit.freqNum = 2;
|
|
||||||
nonDailyHabit.freqDen = 3;
|
|
||||||
nonDailyHabit.save();
|
|
||||||
|
|
||||||
boolean check[] = { true, false, false, true, true, true, false, false, true, true };
|
|
||||||
|
|
||||||
long timestamp = DateHelper.getStartOfToday();
|
|
||||||
for(boolean c : check)
|
|
||||||
{
|
|
||||||
if(c) nonDailyHabit.repetitions.toggle(timestamp);
|
|
||||||
timestamp -= DateHelper.millisecondsInOneDay;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -162,6 +142,7 @@ public class CheckmarkListTest
|
|||||||
|
|
||||||
private void travelInTime(int days)
|
private void travelInTime(int days)
|
||||||
{
|
{
|
||||||
DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME + days * DateHelper.millisecondsInOneDay);
|
DateHelper.setFixedLocalTime(HabitFixtures.FIXED_LOCAL_TIME +
|
||||||
|
days * DateHelper.millisecondsInOneDay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.models;
|
||||||
|
|
||||||
|
import org.isoron.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
|
public class HabitFixtures
|
||||||
|
{
|
||||||
|
public static final long FIXED_LOCAL_TIME = 1422172800000L; // 8:00am, January 25th, 2015 (UTC)
|
||||||
|
public static boolean NON_DAILY_HABIT_CHECKS[] = { true, false, false, true, true, true, false,
|
||||||
|
false, true, true };
|
||||||
|
|
||||||
|
static Habit createNonDailyHabit()
|
||||||
|
{
|
||||||
|
Habit habit = new Habit();
|
||||||
|
habit.freqNum = 2;
|
||||||
|
habit.freqDen = 3;
|
||||||
|
habit.save();
|
||||||
|
|
||||||
|
long timestamp = DateHelper.getStartOfToday();
|
||||||
|
for(boolean c : NON_DAILY_HABIT_CHECKS)
|
||||||
|
{
|
||||||
|
if(c) habit.repetitions.toggle(timestamp);
|
||||||
|
timestamp -= DateHelper.millisecondsInOneDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
return habit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Habit createEmptyHabit()
|
||||||
|
{
|
||||||
|
Habit habit = new Habit();
|
||||||
|
habit.save();
|
||||||
|
return habit;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void purgeHabits()
|
||||||
|
{
|
||||||
|
for(Habit h : Habit.getAll(true))
|
||||||
|
h.cascadeDelete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,8 +40,7 @@ public class HabitTest
|
|||||||
@Before
|
@Before
|
||||||
public void prepare()
|
public void prepare()
|
||||||
{
|
{
|
||||||
for(Habit h : Habit.getAll(true))
|
HabitFixtures.purgeHabits();
|
||||||
h.cascadeDelete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit.models;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.helpers.DateHelper;
|
||||||
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class RepetitionListTest
|
||||||
|
{
|
||||||
|
Habit habit;
|
||||||
|
private Habit emptyHabit;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void prepare()
|
||||||
|
{
|
||||||
|
HabitFixtures.purgeHabits();
|
||||||
|
DateHelper.setFixedLocalTime(HabitFixtures.FIXED_LOCAL_TIME);
|
||||||
|
habit = HabitFixtures.createNonDailyHabit();
|
||||||
|
emptyHabit = HabitFixtures.createEmptyHabit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown()
|
||||||
|
{
|
||||||
|
DateHelper.setFixedLocalTime(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void contains_testNonDailyHabit()
|
||||||
|
{
|
||||||
|
long current = DateHelper.getStartOfToday();
|
||||||
|
|
||||||
|
for(boolean b : HabitFixtures.NON_DAILY_HABIT_CHECKS)
|
||||||
|
{
|
||||||
|
assertThat(habit.repetitions.contains(current), equalTo(b));
|
||||||
|
current -= DateHelper.millisecondsInOneDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
assertThat(habit.repetitions.contains(current), equalTo(false));
|
||||||
|
current -= DateHelper.millisecondsInOneDay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void delete_test()
|
||||||
|
{
|
||||||
|
long timestamp = DateHelper.getStartOfToday();
|
||||||
|
assertThat(habit.repetitions.contains(timestamp), equalTo(true));
|
||||||
|
|
||||||
|
habit.repetitions.delete(timestamp);
|
||||||
|
assertThat(habit.repetitions.contains(timestamp), equalTo(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toggle_test()
|
||||||
|
{
|
||||||
|
long timestamp = DateHelper.getStartOfToday();
|
||||||
|
assertThat(habit.repetitions.contains(timestamp), equalTo(true));
|
||||||
|
|
||||||
|
habit.repetitions.toggle(timestamp);
|
||||||
|
assertThat(habit.repetitions.contains(timestamp), equalTo(false));
|
||||||
|
|
||||||
|
habit.repetitions.toggle(timestamp);
|
||||||
|
assertThat(habit.repetitions.contains(timestamp), equalTo(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWeekDayFrequency_test()
|
||||||
|
{
|
||||||
|
Random random = new Random();
|
||||||
|
Integer weekdayCount[][] = new Integer[12][7];
|
||||||
|
Integer monthCount[] = new Integer[12];
|
||||||
|
|
||||||
|
Arrays.fill(monthCount, 0);
|
||||||
|
for(Integer row[] : weekdayCount)
|
||||||
|
Arrays.fill(row, 0);
|
||||||
|
|
||||||
|
GregorianCalendar day = DateHelper.getStartOfTodayCalendar();
|
||||||
|
|
||||||
|
// Sets the current date to the end of November
|
||||||
|
day.set(2015, 10, 30);
|
||||||
|
DateHelper.setFixedLocalTime(day.getTimeInMillis());
|
||||||
|
|
||||||
|
// Add repetitions randomly from January to December
|
||||||
|
// Leaves the month of March empty, to check that it returns null
|
||||||
|
day.set(2015, 0, 1);
|
||||||
|
for(int i = 0; i < 365; i ++)
|
||||||
|
{
|
||||||
|
if(random.nextBoolean())
|
||||||
|
{
|
||||||
|
int month = day.get(Calendar.MONTH);
|
||||||
|
int week = day.get(Calendar.DAY_OF_WEEK) % 7;
|
||||||
|
|
||||||
|
if(month != 2)
|
||||||
|
{
|
||||||
|
if (month <= 10)
|
||||||
|
{
|
||||||
|
weekdayCount[month][week]++;
|
||||||
|
monthCount[month]++;
|
||||||
|
}
|
||||||
|
emptyHabit.repetitions.toggle(day.getTimeInMillis());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
day.add(Calendar.DAY_OF_YEAR, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<Long, Integer[]> freq = emptyHabit.repetitions.getWeekdayFrequency();
|
||||||
|
|
||||||
|
// Repetitions until November should be counted correctly
|
||||||
|
for(int month = 0; month < 11; month++)
|
||||||
|
{
|
||||||
|
day.set(2015, month, 1);
|
||||||
|
Integer actualCount[] = freq.get(day.getTimeInMillis());
|
||||||
|
if(monthCount[month] == 0)
|
||||||
|
assertThat(actualCount, equalTo(null));
|
||||||
|
else
|
||||||
|
assertThat(actualCount, equalTo(weekdayCount[month]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repetitions in December should be discarded
|
||||||
|
day.set(2015, 11, 1);
|
||||||
|
assertThat(freq.get(day.getTimeInMillis()), equalTo(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ import android.support.v4.content.LocalBroadcastManager;
|
|||||||
|
|
||||||
import org.isoron.helpers.DateHelper;
|
import org.isoron.helpers.DateHelper;
|
||||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||||
|
import org.isoron.uhabits.models.Checkmark;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -145,7 +146,7 @@ public class HabitBroadcastReceiver extends BroadcastReceiver
|
|||||||
Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
|
Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
|
||||||
Long reminderTime = intent.getLongExtra("reminderTime", DateHelper.getStartOfToday());
|
Long reminderTime = intent.getLongExtra("reminderTime", DateHelper.getStartOfToday());
|
||||||
|
|
||||||
if (habit.repetitions.hasImplicitRepToday()) return;
|
if (habit.checkmarks.getTodayValue() != Checkmark.UNCHECKED) return;
|
||||||
|
|
||||||
habit.highlight = 1;
|
habit.highlight = 1;
|
||||||
habit.save();
|
habit.save();
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public class RepetitionList
|
|||||||
{
|
{
|
||||||
return new Select().from(Repetition.class)
|
return new Select().from(Repetition.class)
|
||||||
.where("habit = ?", habit.getId())
|
.where("habit = ?", habit.getId())
|
||||||
|
.and("timestamp <= ?", DateHelper.getStartOfToday())
|
||||||
.orderBy("timestamp");
|
.orderBy("timestamp");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,12 +56,23 @@ public class RepetitionList
|
|||||||
return select().and("timestamp >= ?", timeFrom).and("timestamp <= ?", timeTo);
|
return select().and("timestamp >= ?", timeFrom).and("timestamp <= ?", timeTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether there is a repetition at a given timestamp.
|
||||||
|
*
|
||||||
|
* @param timestamp the timestamp to check
|
||||||
|
* @return true if there is a repetition
|
||||||
|
*/
|
||||||
public boolean contains(long timestamp)
|
public boolean contains(long timestamp)
|
||||||
{
|
{
|
||||||
int count = select().where("timestamp = ?", timestamp).count();
|
int count = select().where("timestamp = ?", timestamp).count();
|
||||||
return (count > 0);
|
return (count > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the repetition at a given timestamp, if it exists.
|
||||||
|
*
|
||||||
|
* @param timestamp the timestamp of the repetition to delete
|
||||||
|
*/
|
||||||
public void delete(long timestamp)
|
public void delete(long timestamp)
|
||||||
{
|
{
|
||||||
new Delete().from(Repetition.class)
|
new Delete().from(Repetition.class)
|
||||||
@@ -69,11 +81,12 @@ public class RepetitionList
|
|||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Repetition getOldestNewerThan(long timestamp)
|
/**
|
||||||
{
|
* Toggles the repetition at a certain timestamp. That is, deletes the repetition if it exists
|
||||||
return select().where("timestamp > ?", timestamp).limit(1).executeSingle();
|
* or creates one if it does not.
|
||||||
}
|
*
|
||||||
|
* @param timestamp the timestamp of the repetition to toggle
|
||||||
|
*/
|
||||||
public void toggle(long timestamp)
|
public void toggle(long timestamp)
|
||||||
{
|
{
|
||||||
timestamp = DateHelper.getStartOfDay(timestamp);
|
timestamp = DateHelper.getStartOfDay(timestamp);
|
||||||
@@ -95,18 +108,27 @@ public class RepetitionList
|
|||||||
habit.streaks.deleteNewerThan(timestamp);
|
habit.streaks.deleteNewerThan(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the oldest repetition for the habit. If there is no repetition, returns null.
|
||||||
|
* Repetitions in the future are discarded.
|
||||||
|
*
|
||||||
|
* @return oldest repetition for the habit
|
||||||
|
*/
|
||||||
public Repetition getOldest()
|
public Repetition getOldest()
|
||||||
{
|
{
|
||||||
return (Repetition) select().limit(1).executeSingle();
|
return (Repetition) select().limit(1).executeSingle();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasImplicitRepToday()
|
/**
|
||||||
{
|
* Returns the total number of repetitions for each month, from the first repetition until
|
||||||
long today = DateHelper.getStartOfToday();
|
* today, grouped by day of week. The repetitions are returned in a HashMap. The key is the
|
||||||
int reps[] = habit.checkmarks.getValues(today - DateHelper.millisecondsInOneDay, today);
|
* timestamp for the first day of the month, at midnight (00:00). The value is an integer
|
||||||
return (reps[0] > 0);
|
* array with 7 entries. The first entry contains the total number of repetitions during
|
||||||
}
|
* the specified month that occurred on a Saturday. The second entry corresponds to Sunday,
|
||||||
|
* and so on. If there are no repetitions during a certain month, the value is null.
|
||||||
|
*
|
||||||
|
* @return total number of repetitions by month versus day of week
|
||||||
|
*/
|
||||||
public HashMap<Long, Integer[]> getWeekdayFrequency()
|
public HashMap<Long, Integer[]> getWeekdayFrequency()
|
||||||
{
|
{
|
||||||
Repetition oldestRep = getOldest();
|
Repetition oldestRep = getOldest();
|
||||||
@@ -116,10 +138,11 @@ public class RepetitionList
|
|||||||
"strftime('%m', timestamp / 1000, 'unixepoch') as month," +
|
"strftime('%m', timestamp / 1000, 'unixepoch') as month," +
|
||||||
"strftime('%w', timestamp / 1000, 'unixepoch') as weekday, " +
|
"strftime('%w', timestamp / 1000, 'unixepoch') as weekday, " +
|
||||||
"count(*) from repetitions " +
|
"count(*) from repetitions " +
|
||||||
"where habit = ? " +
|
"where habit = ? and timestamp <= ? " +
|
||||||
"group by year, month, weekday";
|
"group by year, month, weekday";
|
||||||
|
|
||||||
String[] params = { habit.getId().toString() };
|
String[] params = { habit.getId().toString(),
|
||||||
|
Long.toString(DateHelper.getStartOfToday()) };
|
||||||
|
|
||||||
SQLiteDatabase db = Cache.openDatabase();
|
SQLiteDatabase db = Cache.openDatabase();
|
||||||
Cursor cursor = db.rawQuery(query, params);
|
Cursor cursor = db.rawQuery(query, params);
|
||||||
|
|||||||
Reference in New Issue
Block a user