diff --git a/app/src/androidTest/assets/habitbull.csv b/app/src/androidTest/assets/habitbull.csv new file mode 100644 index 000000000..977a8e8df --- /dev/null +++ b/app/src/androidTest/assets/habitbull.csv @@ -0,0 +1,19 @@ +HabitName,HabitDescription,HabitCategory,CalendarDate,Value,CommentText +Breed dragons,with love and fire,Diet & Food,2016-03-18,1, +Breed dragons,with love and fire,Diet & Food,2016-03-19,1, +Breed dragons,with love and fire,Diet & Food,2016-03-21,1, +Reduce sleep,only 2 hours per day,Time Management,2016-03-15,1, +Reduce sleep,only 2 hours per day,Time Management,2016-03-16,1, +Reduce sleep,only 2 hours per day,Time Management,2016-03-17,1, +Reduce sleep,only 2 hours per day,Time Management,2016-03-21,1, +No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1, +No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1, +No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1, +No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1, +No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1, +No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1, +No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1, +No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1, +Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-15,1, +Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-17,1, +Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-21,1, diff --git a/app/src/androidTest/assets/loop.db b/app/src/androidTest/assets/loop.db new file mode 100644 index 000000000..25f0f32cd Binary files /dev/null and b/app/src/androidTest/assets/loop.db differ diff --git a/app/src/androidTest/assets/rewire.db b/app/src/androidTest/assets/rewire.db new file mode 100644 index 000000000..21f55032f Binary files /dev/null and b/app/src/androidTest/assets/rewire.db differ diff --git a/app/src/androidTest/assets/tickmate.db b/app/src/androidTest/assets/tickmate.db new file mode 100644 index 000000000..a4bec769c Binary files /dev/null and b/app/src/androidTest/assets/tickmate.db differ diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/io/ImportTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/io/ImportTest.java new file mode 100644 index 000000000..c6dcfcbae --- /dev/null +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/io/ImportTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + +package org.isoron.uhabits.unit.io; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import org.isoron.uhabits.helpers.DatabaseHelper; +import org.isoron.uhabits.helpers.DateHelper; +import org.isoron.uhabits.io.GenericImporter; +import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.unit.models.HabitFixtures; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.GregorianCalendar; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ImportTest +{ + private File baseDir; + private Context context; + private Context targetContext; + + @Before + public void setup() + { + HabitFixtures.purgeHabits(); + + context = InstrumentationRegistry.getInstrumentation().getContext(); + targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + baseDir = DatabaseHelper.getFilesDir(targetContext, "Backups"); + if(baseDir == null) fail("baseDir should not be null"); + } + + private void copyAssetToFile(String assetPath, File dst) throws IOException + { + InputStream in = context.getAssets().open(assetPath); + DatabaseHelper.copy(in, dst); + } + + private void importFromFile(String assetFilename) throws IOException + { + File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename)); + copyAssetToFile(assetFilename, file); + assertTrue(file.exists()); + assertTrue(file.canRead()); + + GenericImporter importer = new GenericImporter(); + assertThat(importer.canHandle(file), is(true)); + + importer.importHabitsFromFile(file); + } + + private boolean containsRepetition(Habit h, int year, int month, int day) + { + GregorianCalendar date = DateHelper.getStartOfTodayCalendar(); + date.set(year, month - 1, day); + return h.repetitions.contains(date.getTimeInMillis()); + } + + @Test + public void tickmateDB() throws IOException + { + importFromFile("tickmate.db"); + + List habits = Habit.getAll(true); + assertThat(habits.size(), equalTo(3)); + + Habit h = habits.get(0); + assertThat(h.name, equalTo("Vegan")); + assertTrue(containsRepetition(h, 2016, 1, 24)); + assertTrue(containsRepetition(h, 2016, 2, 5)); + assertTrue(containsRepetition(h, 2016, 3, 18)); + assertFalse(containsRepetition(h, 2016, 3, 14)); + } + + @Test + public void rewireDB() throws IOException + { + importFromFile("rewire.db"); + + List habits = Habit.getAll(true); + assertThat(habits.size(), equalTo(3)); + + Habit habit = habits.get(0); + assertThat(habit.name, equalTo("Wake up early")); + assertThat(habit.freqNum, equalTo(3)); + assertThat(habit.freqDen, equalTo(7)); + assertFalse(habit.hasReminder()); + assertFalse(containsRepetition(habit, 2015, 12, 31)); + assertTrue(containsRepetition(habit, 2016, 1, 18)); + assertTrue(containsRepetition(habit, 2016, 1, 28)); + assertFalse(containsRepetition(habit, 2016, 3, 10)); + + habit = habits.get(1); + assertThat(habit.name, equalTo("brush teeth")); + assertThat(habit.freqNum, equalTo(3)); + assertThat(habit.freqDen, equalTo(7)); + assertThat(habit.reminderHour, equalTo(8)); + assertThat(habit.reminderMin, equalTo(0)); + boolean[] reminderDays = {false, true, true, true, true, true, false}; + assertThat(habit.reminderDays, equalTo(DateHelper.packWeekdayList(reminderDays))); + } + + @Test + public void habitbullCSV() throws IOException + { + importFromFile("habitbull.csv"); + + List habits = Habit.getAll(true); + assertThat(habits.size(), equalTo(4)); + + Habit habit = habits.get(0); + assertThat(habit.name, equalTo("Breed dragons")); + assertThat(habit.description, equalTo("with love and fire")); + assertThat(habit.freqNum, equalTo(1)); + assertThat(habit.freqDen, equalTo(1)); + assertTrue(containsRepetition(habit, 2016, 3, 18)); + assertTrue(containsRepetition(habit, 2016, 3, 19)); + assertFalse(containsRepetition(habit, 2016, 3, 20)); + } + + @Test + public void loopDB() throws IOException + { + importFromFile("loop.db"); + + List habits = Habit.getAll(true); + assertThat(habits.size(), equalTo(9)); + + Habit habit = habits.get(0); + assertThat(habit.name, equalTo("Wake up early")); + assertThat(habit.freqNum, equalTo(3)); + assertThat(habit.freqDen, equalTo(7)); + assertTrue(containsRepetition(habit, 2016, 3, 14)); + assertTrue(containsRepetition(habit, 2016, 3, 16)); + assertFalse(containsRepetition(habit, 2016, 3, 17)); + } +} 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 bb2559cb8..1a15249ff 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 @@ -28,7 +28,7 @@ public class HabitFixtures public static boolean NON_DAILY_HABIT_CHECKS[] = { true, false, false, true, true, true, false, false, true, true }; - static Habit createNonDailyHabit() + public static Habit createNonDailyHabit() { Habit habit = new Habit(); habit.freqNum = 2; @@ -45,7 +45,7 @@ public class HabitFixtures return habit; } - static Habit createEmptyHabit() + public static Habit createEmptyHabit() { Habit habit = new Habit(); habit.freqNum = 1; @@ -54,7 +54,7 @@ public class HabitFixtures return habit; } - static void purgeHabits() + public static void purgeHabits() { for(Habit h : Habit.getAll(true)) h.cascadeDelete(); diff --git a/app/src/main/java/org/isoron/uhabits/HabitsApplication.java b/app/src/main/java/org/isoron/uhabits/HabitsApplication.java index 78eefa30a..810620465 100644 --- a/app/src/main/java/org/isoron/uhabits/HabitsApplication.java +++ b/app/src/main/java/org/isoron/uhabits/HabitsApplication.java @@ -21,20 +21,25 @@ package org.isoron.uhabits; import android.app.Application; import android.content.Context; +import android.support.annotation.Nullable; import com.activeandroid.ActiveAndroid; import org.isoron.uhabits.helpers.DatabaseHelper; +import java.io.File; + public class HabitsApplication extends Application { + @Nullable private static Context context; - private boolean isTestMode() + public static boolean isTestMode() { try { - getClassLoader().loadClass("org.isoron.uhabits.unit.models.HabitTest"); + if(context != null) + context.getClassLoader().loadClass("org.isoron.uhabits.unit.models.HabitTest"); return true; } catch (final Exception e) @@ -43,6 +48,7 @@ public class HabitsApplication extends Application } } + @Nullable public static Context getContext() { return context; @@ -54,15 +60,13 @@ public class HabitsApplication extends Application super.onCreate(); HabitsApplication.context = this; - String databaseFilename = BuildConfig.databaseFilename; - if (isTestMode()) { - databaseFilename = "test.db"; - DatabaseHelper.deleteDatabase(this, databaseFilename); + File db = DatabaseHelper.getDatabaseFile(); + if(db.exists()) db.delete(); } - DatabaseHelper.initializeActiveAndroid(this, databaseFilename); + DatabaseHelper.initializeActiveAndroid(this); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java b/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java index 901e9d218..476d6e2aa 100644 --- a/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java +++ b/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * 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 . + */ + package org.isoron.uhabits.helpers; import android.content.Context; @@ -8,6 +27,7 @@ import com.activeandroid.ActiveAndroid; import com.activeandroid.Configuration; import org.isoron.uhabits.BuildConfig; +import org.isoron.uhabits.HabitsApplication; import org.isoron.uhabits.models.Checkmark; import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Repetition; @@ -18,7 +38,8 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.channels.FileChannel; +import java.io.InputStream; +import java.io.OutputStream; import java.text.SimpleDateFormat; public class DatabaseHelper @@ -27,11 +48,22 @@ public class DatabaseHelper { FileInputStream inStream = new FileInputStream(src); FileOutputStream outStream = new FileOutputStream(dst); - FileChannel inChannel = inStream.getChannel(); - FileChannel outChannel = outStream.getChannel(); - inChannel.transferTo(0, inChannel.size(), outChannel); - inStream.close(); - outStream.close(); + copy(inStream, outStream); + } + + public static void copy(InputStream inStream, File dst) throws IOException + { + FileOutputStream outStream = new FileOutputStream(dst); + copy(inStream, outStream); + } + + public static void copy(InputStream in, OutputStream out) throws IOException + { + int numBytes; + byte[] buffer = new byte[1024]; + + while ((numBytes = in.read(buffer)) != -1) + out.write(buffer, 0, numBytes); } public interface Command @@ -54,9 +86,9 @@ public class DatabaseHelper } @SuppressWarnings("ResultOfMethodCallIgnored") - public static String saveDatabaseCopy(Context context, File dir) throws IOException + public static String saveDatabaseCopy(File dir) throws IOException { - File db = getDatabaseFile(context, BuildConfig.databaseFilename); + File db = getDatabaseFile(); SimpleDateFormat dateFormat = DateHelper.getBackupDateFormat(); String date = dateFormat.format(DateHelper.getLocalTime()); @@ -67,17 +99,27 @@ public class DatabaseHelper return dbCopy.getAbsolutePath(); } - public static void deleteDatabase(Context context, String databaseFilename) + @NonNull + public static File getDatabaseFile() { - File db = getDatabaseFile(context, databaseFilename); - if(db.exists()) db.delete(); + Context context = HabitsApplication.getContext(); + if(context == null) throw new RuntimeException("No application context found"); + + String databaseFilename = getDatabaseFilename(); + + return new File(String.format("%s/../databases/%s", + context.getApplicationContext().getFilesDir().getPath(), databaseFilename)); } @NonNull - public static File getDatabaseFile(Context context, String databaseFilename) + public static String getDatabaseFilename() { - return new File(String.format("%s/../databases/%s", - context.getApplicationContext().getFilesDir().getPath(), databaseFilename)); + String databaseFilename = BuildConfig.databaseFilename; + + if (HabitsApplication.isTestMode()) + databaseFilename = "test.db"; + + return databaseFilename; } @Nullable @@ -93,10 +135,10 @@ public class DatabaseHelper } @SuppressWarnings("unchecked") - public static void initializeActiveAndroid(Context context, String databaseFilename) + public static void initializeActiveAndroid(Context context) { Configuration dbConfig = new Configuration.Builder(context) - .setDatabaseName(databaseFilename) + .setDatabaseName(getDatabaseFilename()) .setDatabaseVersion(BuildConfig.databaseVersion) .addModelClasses(Checkmark.class, Habit.class, Repetition.class, Score.class, Streak.class) diff --git a/app/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java b/app/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java index 66bd80dd2..5ae993d73 100644 --- a/app/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java +++ b/app/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java @@ -57,12 +57,11 @@ public class LoopDBImporter extends AbstractImporter { ActiveAndroid.dispose(); Context context = HabitsApplication.getContext(); - - File originalDB = DatabaseHelper.getDatabaseFile(context, BuildConfig.databaseFilename); + File originalDB = DatabaseHelper.getDatabaseFile(); File backupDir = DatabaseHelper.getFilesDir(context, "Backups"); - DatabaseHelper.saveDatabaseCopy(context, backupDir); + DatabaseHelper.saveDatabaseCopy(backupDir); DatabaseHelper.copy(file, originalDB); - DatabaseHelper.initializeActiveAndroid(context, BuildConfig.databaseFilename); + DatabaseHelper.initializeActiveAndroid(context); } } diff --git a/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java b/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java index 02b8e5720..f5f70809f 100644 --- a/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java +++ b/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java @@ -85,7 +85,7 @@ public class ExportDBTask extends AsyncTask File dir = DatabaseHelper.getFilesDir(activity, "Backups"); if(dir == null) return null; - filename = DatabaseHelper.saveDatabaseCopy(activity, dir); + filename = DatabaseHelper.saveDatabaseCopy(dir); } catch(IOException e) {