From 1120f56dd4e7b3698016dbc73e549e49e336686a Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Thu, 24 Mar 2016 21:36:41 -0400 Subject: [PATCH] Write tests for CSV export --- .../unit/models/CheckmarkListTest.java | 24 +++++++++++++ .../uhabits/unit/models/HabitFixtures.java | 6 ++++ .../isoron/uhabits/unit/models/HabitTest.java | 20 +++++++++++ .../uhabits/unit/models/ScoreListTest.java | 27 ++++++++++++++ .../isoron/uhabits/unit/models/ScoreTest.java | 9 ++--- .../isoron/uhabits/models/CheckmarkList.java | 27 ++++++++++++++ .../java/org/isoron/uhabits/models/Habit.java | 35 ++++++------------- .../org/isoron/uhabits/models/ScoreList.java | 18 +++++++++- 8 files changed, 133 insertions(+), 33 deletions(-) diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/CheckmarkListTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/models/CheckmarkListTest.java index 0485607fb..2fe1f8c1b 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/CheckmarkListTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/models/CheckmarkListTest.java @@ -29,6 +29,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.IOException; +import java.io.StringWriter; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.isoron.uhabits.models.Checkmark.CHECKED_EXPLICITLY; @@ -140,6 +143,27 @@ public class CheckmarkListTest assertThat(nonDailyHabit.checkmarks.getTodayValue(), equalTo(UNCHECKED)); } + @Test + public void writeCSV() throws IOException + { + String expectedCSV = + "2015-01-16,2\n" + + "2015-01-17,2\n" + + "2015-01-18,1\n" + + "2015-01-19,0\n" + + "2015-01-20,2\n" + + "2015-01-21,2\n" + + "2015-01-22,2\n" + + "2015-01-23,1\n" + + "2015-01-24,0\n" + + "2015-01-25,2\n"; + + StringWriter writer = new StringWriter(); + nonDailyHabit.checkmarks.writeCSV(writer); + + assertThat(writer.toString(), equalTo(expectedCSV)); + } + private void travelInTime(int days) { DateHelper.setFixedLocalTime(HabitFixtures.FIXED_LOCAL_TIME + diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitFixtures.java b/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitFixtures.java index 1a15249ff..6b53fd35a 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitFixtures.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitFixtures.java @@ -19,6 +19,7 @@ package org.isoron.uhabits.unit.models; +import org.isoron.uhabits.helpers.ColorHelper; import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; @@ -31,6 +32,8 @@ public class HabitFixtures public static Habit createNonDailyHabit() { Habit habit = new Habit(); + habit.name = "Wake up early"; + habit.description = "Did you wake up before 6am?"; habit.freqNum = 2; habit.freqDen = 3; habit.save(); @@ -48,6 +51,9 @@ public class HabitFixtures public static Habit createEmptyHabit() { Habit habit = new Habit(); + habit.name = "Meditate"; + habit.description = "Did you meditate this morning?"; + habit.color = ColorHelper.palette[3]; habit.freqNum = 1; habit.freqDen = 1; habit.save(); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java index c7420aa40..82bc600bd 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitTest.java @@ -23,12 +23,15 @@ import android.graphics.Color; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; +import org.hamcrest.MatcherAssert; import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.IOException; +import java.io.StringWriter; import java.util.LinkedList; import java.util.List; @@ -353,4 +356,21 @@ public class HabitTest h.clearReminder(); assertThat(h.hasReminder(), is(false)); } + + @Test + public void writeCSV() throws IOException + { + HabitFixtures.createEmptyHabit(); + HabitFixtures.createNonDailyHabit(); + + String expectedCSV = + "Name,Description,NumRepetitions,Interval,Color\n" + + "Meditate,Did you meditate this morning?,1,1,#AFB42B\n" + + "Wake up early,Did you wake up before 6am?,2,3,#00897B\n"; + + StringWriter writer = new StringWriter(); + Habit.writeCSV(Habit.getAll(true), writer); + + MatcherAssert.assertThat(writer.toString(), equalTo(expectedCSV)); + } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreListTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreListTest.java index bc094139c..04821f23e 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreListTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreListTest.java @@ -31,6 +31,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.IOException; +import java.io.StringWriter; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -130,6 +133,30 @@ public class ScoreListTest assertThat(actualValues, equalTo(expectedValues)); } + @Test + public void writeCSV() throws IOException + { + HabitFixtures.purgeHabits(); + Habit habit = HabitFixtures.createNonDailyHabit(); + + String expectedCSV = + "2015-01-16,0.0519\n" + + "2015-01-17,0.1021\n" + + "2015-01-18,0.0986\n" + + "2015-01-19,0.0952\n" + + "2015-01-20,0.1439\n" + + "2015-01-21,0.1909\n" + + "2015-01-22,0.2364\n" + + "2015-01-23,0.2283\n" + + "2015-01-24,0.2205\n" + + "2015-01-25,0.2649\n"; + + StringWriter writer = new StringWriter(); + habit.scores.writeCSV(writer); + + assertThat(writer.toString(), equalTo(expectedCSV)); + } + private void toggleRepetitions(final int from, final int to) { DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command() diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreTest.java index cfad09f37..886f41e90 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/models/ScoreTest.java @@ -22,18 +22,13 @@ package org.isoron.uhabits.unit.models; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; +import org.isoron.uhabits.models.Checkmark; +import org.isoron.uhabits.models.Score; import org.junit.Test; import org.junit.runner.RunWith; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - -import org.isoron.uhabits.models.Score; -import org.isoron.uhabits.models.Checkmark; @RunWith(AndroidJUnit4.class) @SmallTest diff --git a/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java b/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java index c744a226e..8c5f5f577 100644 --- a/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java +++ b/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java @@ -76,6 +76,7 @@ public class CheckmarkList public int[] getValues(long fromTimestamp, long toTimestamp) { compute(fromTimestamp, toTimestamp); + if(fromTimestamp > toTimestamp) return new int[0]; String query = "select value, timestamp from Checkmarks where " + @@ -127,6 +128,21 @@ public class CheckmarkList return getValues(fromTimestamp, toTimestamp); } + /** + * Computes and stores one checkmark for each day, since the first repetition until today. + * Days that already have a corresponding checkmark are skipped. + */ + protected void computeAll() + { + Repetition oldestRep = habit.repetitions.getOldest(); + if(oldestRep == null) return; + + Long fromTimestamp = oldestRep.timestamp; + Long toTimestamp = DateHelper.getStartOfToday(); + + compute(fromTimestamp, toTimestamp); + } + /** * Computes and stores one checkmark for each day that falls inside the specified interval of * time. Days that already have a corresponding checkmark are skipped. @@ -234,8 +250,18 @@ public class CheckmarkList else return Checkmark.UNCHECKED; } + /** + * Writes the entire list of checkmarks to the given writer, in CSV format. There is one + * line for each checkmark. Each line contains two fields: timestamp and value. + * + * @param out the writer where the CSV will be output + * @throws IOException in case write operations fail + */ + public void writeCSV(Writer out) throws IOException { + computeAll(); + SimpleDateFormat dateFormat = DateHelper.getCSVDateFormat(); String query = "select timestamp, value from checkmarks where habit = ? order by timestamp"; @@ -255,5 +281,6 @@ public class CheckmarkList } while(cursor.moveToNext()); cursor.close(); + out.close(); } } diff --git a/app/src/main/java/org/isoron/uhabits/models/Habit.java b/app/src/main/java/org/isoron/uhabits/models/Habit.java index cd8b6a7ef..a3c51e3fe 100644 --- a/app/src/main/java/org/isoron/uhabits/models/Habit.java +++ b/app/src/main/java/org/isoron/uhabits/models/Habit.java @@ -20,7 +20,6 @@ package org.isoron.uhabits.models; import android.annotation.SuppressLint; -import android.graphics.Color; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -34,16 +33,13 @@ import com.activeandroid.query.From; import com.activeandroid.query.Select; import com.activeandroid.query.Update; import com.activeandroid.util.SQLiteUtils; -import com.opencsv.CSVReader; import com.opencsv.CSVWriter; import org.isoron.uhabits.helpers.ColorHelper; import org.isoron.uhabits.helpers.DateHelper; import java.io.IOException; -import java.io.Reader; import java.io.Writer; -import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -478,9 +474,18 @@ public class Habit extends Model reminderDays = DateHelper.ALL_WEEK_DAYS; } + /** + * Writes the list of habits to the given writer, in CSV format. There is one line for each + * habit, containing the fields name, description, frequency numerator, frequency denominator + * and color. The color is written in HTML format (#000000). + * + * @param habits the list of habits to write + * @param out the writer that will receive the result + * @throws IOException if write operations fail + */ public static void writeCSV(List habits, Writer out) throws IOException { - String header[] = { "Name", "Description", "FrequencyNumerator", "FrequencyDenominator", "Color" }; + String header[] = { "Name", "Description", "NumRepetitions", "Interval", "Color" }; CSVWriter csv = new CSVWriter(out); csv.writeNext(header, false); @@ -494,24 +499,4 @@ public class Habit extends Model csv.close(); } - - public List parseCSV(Reader in) - { - CSVReader csv = new CSVReader(in); - List habits = new LinkedList<>(); - - for(String cols[] : csv) - { - Habit habit = new Habit(); - - habit.name = cols[0]; - habit.description = cols[1]; - habit.freqNum = Integer.parseInt(cols[2]); - habit.freqDen = Integer.parseInt(cols[3]); - habit.color = Color.parseColor(cols[4]); - habits.add(habit); - } - - return habits; - } } diff --git a/app/src/main/java/org/isoron/uhabits/models/ScoreList.java b/app/src/main/java/org/isoron/uhabits/models/ScoreList.java index 57f4e42a0..1972dc411 100644 --- a/app/src/main/java/org/isoron/uhabits/models/ScoreList.java +++ b/app/src/main/java/org/isoron/uhabits/models/ScoreList.java @@ -99,6 +99,19 @@ public class ScoreList .execute(); } + /** + * Computes and saves the scores that are missing since the first repetition of the habit. + */ + private void computeAll() + { + Repetition oldestRep = habit.repetitions.getOldest(); + if(oldestRep == null) return; + + long fromTimestamp = oldestRep.timestamp; + long toTimestamp = DateHelper.getStartOfToday(); + compute(fromTimestamp, toTimestamp); + } + /** * Computes and saves the scores that are missing inside a given time interval. Scores that * have already been computed are skipped, therefore there is no harm in calling this function @@ -285,6 +298,8 @@ public class ScoreList public void writeCSV(Writer out) throws IOException { + computeAll(); + SimpleDateFormat dateFormat = DateHelper.getCSVDateFormat(); String query = "select timestamp, score from score where habit = ? order by timestamp"; @@ -298,11 +313,12 @@ public class ScoreList do { String timestamp = dateFormat.format(new Date(cursor.getLong(0))); - String score = String.format("%.2f", ((float) cursor.getInt(1)) / Score.MAX_VALUE); + String score = String.format("%.4f", ((float) cursor.getInt(1)) / Score.MAX_VALUE); out.write(String.format("%s,%s\n", timestamp, score)); } while(cursor.moveToNext()); cursor.close(); + out.close(); } }