Refactor Tasks for better testability

pull/77/merge
Alinson S. Xavier 10 years ago
parent 67b88c5012
commit 2ba7246e5f

@ -0,0 +1,53 @@
/*
* 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;
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);
}
}

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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);
}
}

@ -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;

@ -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;

@ -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;

@ -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;

@ -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;

@ -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;

@ -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<Habit> 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();
}
}

@ -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();
}
}

@ -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);

@ -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;

@ -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();
}
}

@ -0,0 +1,61 @@
/*
* 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.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<Void, Void, Void>
{
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();
}
}

@ -32,7 +32,7 @@ import java.io.File;
import java.io.IOException;
import java.util.List;
public class ExportCSVTask extends AsyncTask<Void, Void, Void>
public class ExportCSVTask extends BaseTask
{
public interface Listener
{
@ -58,6 +58,8 @@ public class ExportCSVTask extends AsyncTask<Void, Void, Void>
@Override
protected void onPreExecute()
{
super.onPreExecute();
if(progressBar != null)
{
progressBar.setIndeterminate(true);
@ -73,6 +75,8 @@ public class ExportCSVTask extends AsyncTask<Void, Void, Void>
if(progressBar != null)
progressBar.setVisibility(View.GONE);
super.onPostExecute(null);
}
@Override

@ -29,7 +29,7 @@ import org.isoron.uhabits.helpers.DatabaseHelper;
import java.io.File;
import java.io.IOException;
public class ExportDBTask extends AsyncTask<Void, Void, Void>
public class ExportDBTask extends BaseTask
{
public interface Listener
{
@ -53,6 +53,8 @@ public class ExportDBTask extends AsyncTask<Void, Void, Void>
@Override
protected void onPreExecute()
{
super.onPreExecute();
if(progressBar != null)
{
progressBar.setIndeterminate(true);
@ -68,6 +70,8 @@ public class ExportDBTask extends AsyncTask<Void, Void, Void>
if(progressBar != null)
progressBar.setVisibility(View.GONE);
super.onPostExecute(null);
}
@Override

@ -29,7 +29,7 @@ import org.isoron.uhabits.io.GenericImporter;
import java.io.File;
public class ImportDataTask extends AsyncTask<Void, Void, Void>
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<Void, Void, Void>
@Override
protected void onPreExecute()
{
super.onPreExecute();
if(progressBar != null)
{
progressBar.setIndeterminate(true);
@ -79,6 +81,8 @@ public class ImportDataTask extends AsyncTask<Void, Void, Void>
progressBar.setVisibility(View.GONE);
if(listener != null) listener.onImportFinished(result);
super.onPostExecute(null);
}
@Override

@ -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();

Loading…
Cancel
Save