diff --git a/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java b/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java new file mode 100644 index 000000000..cd2252d06 --- /dev/null +++ b/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java @@ -0,0 +1,53 @@ +/* + * 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; + +import android.content.Context; +import android.os.Looper; +import android.support.test.InstrumentationRegistry; + +import org.isoron.uhabits.tasks.BaseTask; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class BaseTest +{ + protected Context testContext; + protected Context targetContext; + private static boolean isLooperPrepared; + + protected void setup() + { + if(!isLooperPrepared) + { + Looper.prepare(); + isLooperPrepared = true; + } + + targetContext = InstrumentationRegistry.getTargetContext(); + testContext = InstrumentationRegistry.getContext(); + } + + protected void waitForAsyncTasks() throws InterruptedException, TimeoutException + { + BaseTask.waitForTasks(30, TimeUnit.SECONDS); + } +} diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitFixtures.java b/app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java similarity index 70% rename from app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitFixtures.java rename to app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java index 6b53fd35a..ccd97bcf8 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/HabitFixtures.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java @@ -17,7 +17,7 @@ * with this program. If not, see . */ -package org.isoron.uhabits.unit.models; +package org.isoron.uhabits.unit; import org.isoron.uhabits.helpers.ColorHelper; import org.isoron.uhabits.helpers.DateHelper; @@ -60,9 +60,39 @@ public class HabitFixtures return habit; } + public static Habit createLongHabit() + { + Habit habit = createEmptyHabit(); + habit.freqNum = 3; + habit.freqDen = 7; + habit.color = ColorHelper.palette[4]; + habit.save(); + + long day = DateHelper.millisecondsInOneDay; + long today = DateHelper.getStartOfToday(); + int marks[] = { 0, 1, 3, 5, 7, 8, 9, 10, 12, 14, 15, 17, 19, 20, 26, 27, 28, 50, 51, 52, + 53, 54, 58, 60, 63, 65, 70, 71, 72, 73, 74, 75, 80, 81, 83, 89, 90, 91, 95, + 102, 103, 108, 109, 120}; + + for(int mark : marks) + habit.repetitions.toggle(today - mark * day); + + return habit; + } + public static void purgeHabits() { for(Habit h : Habit.getAll(true)) h.cascadeDelete(); } + + public static void fixTime() + { + DateHelper.setFixedLocalTime(FIXED_LOCAL_TIME); + } + + public static void releaseTime() + { + DateHelper.setFixedLocalTime(null); + } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/io/HabitsCSVExporterTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/io/HabitsCSVExporterTest.java index 6cd998515..42e4478c5 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/io/HabitsCSVExporterTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/io/HabitsCSVExporterTest.java @@ -27,7 +27,7 @@ import android.test.suitebuilder.annotation.SmallTest; import org.isoron.uhabits.helpers.DatabaseHelper; import org.isoron.uhabits.io.HabitsCSVExporter; import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.unit.models.HabitFixtures; +import org.isoron.uhabits.unit.HabitFixtures; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; 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 index 99208a7c5..6256a282f 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/io/ImportTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/io/ImportTest.java @@ -28,7 +28,7 @@ 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.isoron.uhabits.unit.HabitFixtures; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; 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 2fe1f8c1b..07361a749 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 @@ -24,6 +24,7 @@ import android.test.suitebuilder.annotation.SmallTest; import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.unit.HabitFixtures; import org.junit.After; import org.junit.Before; import org.junit.Test; 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 82bc600bd..04fcfd5c5 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 @@ -26,6 +26,7 @@ import android.test.suitebuilder.annotation.SmallTest; import org.hamcrest.MatcherAssert; import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.unit.HabitFixtures; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/RepetitionListTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/models/RepetitionListTest.java index 9ca8ad4b2..25210273e 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/RepetitionListTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/models/RepetitionListTest.java @@ -24,6 +24,7 @@ import android.test.suitebuilder.annotation.SmallTest; import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.unit.HabitFixtures; import org.junit.After; import org.junit.Before; import org.junit.Test; 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 04821f23e..bf9d88a1e 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 @@ -26,6 +26,7 @@ import org.isoron.uhabits.helpers.DatabaseHelper; import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Score; +import org.isoron.uhabits.unit.HabitFixtures; import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportCSVTaskTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportCSVTaskTest.java index a899a86f0..420c80dfb 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportCSVTaskTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportCSVTaskTest.java @@ -19,22 +19,20 @@ package org.isoron.uhabits.unit.tasks; -import android.content.Context; -import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.widget.ProgressBar; +import org.isoron.uhabits.BaseTest; import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.tasks.ExportCSVTask; -import org.isoron.uhabits.unit.models.HabitFixtures; +import org.isoron.uhabits.unit.HabitFixtures; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import static junit.framework.Assert.assertTrue; import static org.hamcrest.MatcherAssert.assertThat; @@ -44,17 +42,20 @@ import static org.hamcrest.core.IsNot.not; @RunWith(AndroidJUnit4.class) @SmallTest -public class ExportCSVTaskTest +public class ExportCSVTaskTest extends BaseTest { - @Test - public void exportCSV() throws InterruptedException + @Before + public void setup() { - Context context = InstrumentationRegistry.getContext(); - final CountDownLatch latch = new CountDownLatch(1); + super.setup(); + } + @Test + public void exportCSV() throws Throwable + { HabitFixtures.createNonDailyHabit(); List habits = Habit.getAll(true); - ProgressBar bar = new ProgressBar(context); + ProgressBar bar = new ProgressBar(targetContext); ExportCSVTask task = new ExportCSVTask(habits, bar); task.setListener(new ExportCSVTask.Listener() @@ -67,11 +68,10 @@ public class ExportCSVTaskTest File f = new File(archiveFilename); assertTrue(f.exists()); assertTrue(f.canRead()); - latch.countDown(); } }); task.execute(); - latch.await(30, TimeUnit.SECONDS); + waitForAsyncTasks(); } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportDBTaskTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportDBTaskTest.java index e744b5ede..0006e6ba6 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportDBTaskTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ExportDBTaskTest.java @@ -25,13 +25,13 @@ import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.widget.ProgressBar; +import org.isoron.uhabits.BaseTest; import org.isoron.uhabits.tasks.ExportDBTask; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import static junit.framework.Assert.assertTrue; import static org.hamcrest.MatcherAssert.assertThat; @@ -41,13 +41,18 @@ import static org.hamcrest.core.IsNot.not; @RunWith(AndroidJUnit4.class) @SmallTest -public class ExportDBTaskTest +public class ExportDBTaskTest extends BaseTest { + @Before + public void setup() + { + super.setup(); + } + @Test - public void exportCSV() throws InterruptedException + public void exportCSV() throws Throwable { Context context = InstrumentationRegistry.getContext(); - final CountDownLatch latch = new CountDownLatch(1); ProgressBar bar = new ProgressBar(context); ExportDBTask task = new ExportDBTask(bar); @@ -61,11 +66,10 @@ public class ExportDBTaskTest File f = new File(filename); assertTrue(f.exists()); assertTrue(f.canRead()); - latch.countDown(); } }); task.execute(); - latch.await(30, TimeUnit.SECONDS); + waitForAsyncTasks(); } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ImportDataTaskTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ImportDataTaskTest.java index dc3f0681a..43546ab60 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ImportDataTaskTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ImportDataTaskTest.java @@ -19,13 +19,12 @@ package org.isoron.uhabits.unit.tasks; -import android.content.Context; import android.support.annotation.NonNull; -import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.widget.ProgressBar; +import org.isoron.uhabits.BaseTest; import org.isoron.uhabits.helpers.DatabaseHelper; import org.isoron.uhabits.tasks.ImportDataTask; import org.junit.Before; @@ -44,15 +43,14 @@ import static org.junit.Assert.fail; @RunWith(AndroidJUnit4.class) @SmallTest -public class ImportDataTaskTest +public class ImportDataTaskTest extends BaseTest { - private Context context; private File baseDir; @Before public void setup() { - context = InstrumentationRegistry.getContext(); + super.setup(); baseDir = DatabaseHelper.getFilesDir("Backups"); if(baseDir == null) fail("baseDir should not be null"); @@ -60,14 +58,12 @@ public class ImportDataTaskTest private void copyAssetToFile(String assetPath, File dst) throws IOException { - InputStream in = context.getAssets().open(assetPath); + InputStream in = testContext.getAssets().open(assetPath); DatabaseHelper.copy(in, dst); } - private void assertTaskResult(final int expectedResult, String assetFilename) - throws IOException, InterruptedException + private void assertTaskResult(final int expectedResult, String assetFilename) throws Throwable { - final CountDownLatch latch = new CountDownLatch(1); ImportDataTask task = createTask(assetFilename); task.setListener(new ImportDataTask.Listener() @@ -76,18 +72,17 @@ public class ImportDataTaskTest public void onImportFinished(int result) { assertThat(result, equalTo(expectedResult)); - latch.countDown(); } }); task.execute(); - latch.await(30, TimeUnit.SECONDS); + waitForAsyncTasks(); } @NonNull private ImportDataTask createTask(String assetFilename) throws IOException { - ProgressBar bar = new ProgressBar(context); + ProgressBar bar = new ProgressBar(targetContext); File file = new File(String.format("%s/%s", baseDir.getPath(), assetFilename)); copyAssetToFile(assetFilename, file); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java index 1ac8894c8..32ebda45a 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java @@ -24,7 +24,7 @@ import android.test.suitebuilder.annotation.SmallTest; import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; -import org.isoron.uhabits.unit.models.HabitFixtures; +import org.isoron.uhabits.unit.HabitFixtures; import org.isoron.uhabits.views.CheckmarkView; import org.junit.Before; import org.junit.Test; diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java index 8047435c6..a5ee34f49 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java @@ -19,14 +19,15 @@ package org.isoron.uhabits.unit.views; -import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.support.test.InstrumentationRegistry; +import android.os.SystemClock; +import android.view.GestureDetector; +import android.view.MotionEvent; import android.view.View; +import org.isoron.uhabits.BaseTest; import org.isoron.uhabits.helpers.DialogHelper; -import org.junit.Before; import java.io.File; import java.io.FileOutputStream; @@ -35,21 +36,11 @@ import java.io.InputStream; import static junit.framework.Assert.fail; -public class ViewTest +public class ViewTest extends BaseTest { protected static final double SIMILARITY_CUTOFF = 0.02; public static final int HISTOGRAM_BIN_SIZE = 8; - protected Context testContext; - protected Context targetContext; - - @Before - public void setup() - { - targetContext = InstrumentationRegistry.getTargetContext(); - testContext = InstrumentationRegistry.getContext(); - } - protected void measureView(int width, int height, View view) { int specWidth = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); @@ -190,4 +181,13 @@ public class ViewTest { return (int) DialogHelper.dpToPixels(targetContext, dp); } + + protected void tap(GestureDetector.OnGestureListener view, int x, int y) throws InterruptedException + { + long now = SystemClock.uptimeMillis(); + MotionEvent e = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, dpToPixels(x), + dpToPixels(y), 0); + view.onSingleTapUp(e); + e.recycle(); + } } diff --git a/app/src/main/java/org/isoron/uhabits/tasks/BaseTask.java b/app/src/main/java/org/isoron/uhabits/tasks/BaseTask.java new file mode 100644 index 000000000..e97896be9 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/tasks/BaseTask.java @@ -0,0 +1,61 @@ +/* + * 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.tasks; + +import android.os.AsyncTask; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class BaseTask extends AsyncTask +{ + private static CountDownLatch latch; + private static int activeTaskCount; + + @Override + protected Void doInBackground(Void... params) + { + return null; + } + + @Override + protected void onPreExecute() + { + activeTaskCount++; + latch = new CountDownLatch(activeTaskCount); + } + + @Override + protected void onPostExecute(Void aVoid) + { + activeTaskCount--; + latch.countDown(); + } + + public static void waitForTasks(long timeout, TimeUnit unit) + throws TimeoutException, InterruptedException + { + if(activeTaskCount == 0) return; + + boolean successful = latch.await(timeout, unit); + if(!successful) throw new TimeoutException(); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/tasks/ExportCSVTask.java b/app/src/main/java/org/isoron/uhabits/tasks/ExportCSVTask.java index 2ebd05cc8..dc3a53305 100644 --- a/app/src/main/java/org/isoron/uhabits/tasks/ExportCSVTask.java +++ b/app/src/main/java/org/isoron/uhabits/tasks/ExportCSVTask.java @@ -32,7 +32,7 @@ import java.io.File; import java.io.IOException; import java.util.List; -public class ExportCSVTask extends AsyncTask +public class ExportCSVTask extends BaseTask { public interface Listener { @@ -58,6 +58,8 @@ public class ExportCSVTask extends AsyncTask @Override protected void onPreExecute() { + super.onPreExecute(); + if(progressBar != null) { progressBar.setIndeterminate(true); @@ -73,6 +75,8 @@ public class ExportCSVTask extends AsyncTask if(progressBar != null) progressBar.setVisibility(View.GONE); + + super.onPostExecute(null); } @Override 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 abc31b490..ebdb213a9 100644 --- a/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java +++ b/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java @@ -29,7 +29,7 @@ import org.isoron.uhabits.helpers.DatabaseHelper; import java.io.File; import java.io.IOException; -public class ExportDBTask extends AsyncTask +public class ExportDBTask extends BaseTask { public interface Listener { @@ -53,6 +53,8 @@ public class ExportDBTask extends AsyncTask @Override protected void onPreExecute() { + super.onPreExecute(); + if(progressBar != null) { progressBar.setIndeterminate(true); @@ -68,6 +70,8 @@ public class ExportDBTask extends AsyncTask if(progressBar != null) progressBar.setVisibility(View.GONE); + + super.onPostExecute(null); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java b/app/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java index baf2da75d..fe7f03528 100644 --- a/app/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java +++ b/app/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java @@ -29,7 +29,7 @@ import org.isoron.uhabits.io.GenericImporter; import java.io.File; -public class ImportDataTask extends AsyncTask +public class ImportDataTask extends BaseTask { public static final int SUCCESS = 1; public static final int NOT_RECOGNIZED = 2; @@ -65,6 +65,8 @@ public class ImportDataTask extends AsyncTask @Override protected void onPreExecute() { + super.onPreExecute(); + if(progressBar != null) { progressBar.setIndeterminate(true); @@ -79,6 +81,8 @@ public class ImportDataTask extends AsyncTask progressBar.setVisibility(View.GONE); if(listener != null) listener.onImportFinished(result); + + super.onPostExecute(null); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java b/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java index 8ad2be7c5..fbae274b7 100644 --- a/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java +++ b/app/src/main/java/org/isoron/uhabits/views/ScrollableDataView.java @@ -25,6 +25,7 @@ import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; +import android.view.ViewParent; import android.widget.Scroller; public abstract class ScrollableDataView extends View implements GestureDetector.OnGestureListener, @@ -89,7 +90,10 @@ public abstract class ScrollableDataView extends View implements GestureDetector return false; if(Math.abs(dx) > Math.abs(dy)) - getParent().requestDisallowInterceptTouchEvent(true); + { + ViewParent parent = getParent(); + if(parent != null) parent.requestDisallowInterceptTouchEvent(true); + } scroller.startScroll(scroller.getCurrX(), scroller.getCurrY(), (int) -dx, (int) dy, 0); scroller.computeScrollOffset();