mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Add tests for HabitCardListCache; refactor TaskRunners
This commit is contained in:
@@ -23,6 +23,7 @@ import android.content.*;
|
|||||||
|
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.models.sqlite.*;
|
import org.isoron.uhabits.models.sqlite.*;
|
||||||
|
import org.isoron.uhabits.tasks.*;
|
||||||
|
|
||||||
import javax.inject.*;
|
import javax.inject.*;
|
||||||
|
|
||||||
@@ -56,4 +57,11 @@ public class AndroidModule
|
|||||||
{
|
{
|
||||||
return HabitsApplication.getContext();
|
return HabitsApplication.getContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
static TaskRunner provideTaskRunner()
|
||||||
|
{
|
||||||
|
return new AndroidTaskRunner();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public abstract class HabitList implements Iterable<Habit>
|
|||||||
* @return the habit at that position
|
* @return the habit at that position
|
||||||
* @throws IndexOutOfBoundsException when the position is invalid
|
* @throws IndexOutOfBoundsException when the position is invalid
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@NonNull
|
||||||
public abstract Habit getByPosition(int position);
|
public abstract Habit getByPosition(int position);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* 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.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
import javax.inject.*;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class AndroidTaskRunner implements TaskRunner
|
||||||
|
{
|
||||||
|
private final LinkedList<CustomAsyncTask> activeTasks;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public AndroidTaskRunner()
|
||||||
|
{
|
||||||
|
activeTasks = new LinkedList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(Task task)
|
||||||
|
{
|
||||||
|
task.onAttached(this);
|
||||||
|
new CustomAsyncTask(task).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishProgress(Task task, int progress)
|
||||||
|
{
|
||||||
|
for (CustomAsyncTask asyncTask : activeTasks)
|
||||||
|
if (asyncTask.getTask() == task) asyncTask.publish(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void waitForTasks(long timeout)
|
||||||
|
throws TimeoutException, InterruptedException
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
|
||||||
|
throw new UnsupportedOperationException("waitForTasks requires API 16+");
|
||||||
|
|
||||||
|
int poolInterval = 100;
|
||||||
|
|
||||||
|
while(timeout > 0)
|
||||||
|
{
|
||||||
|
if(activeTasks.isEmpty()) return;
|
||||||
|
|
||||||
|
timeout -= poolInterval;
|
||||||
|
Thread.sleep(poolInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new TimeoutException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CustomAsyncTask extends AsyncTask<Void, Integer, Void>
|
||||||
|
{
|
||||||
|
private final Task task;
|
||||||
|
|
||||||
|
public CustomAsyncTask(Task task)
|
||||||
|
{
|
||||||
|
this.task = task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task getTask()
|
||||||
|
{
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void publish(int progress)
|
||||||
|
{
|
||||||
|
publishProgress(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... params)
|
||||||
|
{
|
||||||
|
task.doInBackground();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Void aVoid)
|
||||||
|
{
|
||||||
|
task.onPostExecute();
|
||||||
|
activeTasks.remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProgressUpdate(Integer... values)
|
||||||
|
{
|
||||||
|
task.onProgressUpdate(values[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute()
|
||||||
|
{
|
||||||
|
activeTasks.add(this);
|
||||||
|
task.onPreExecute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,22 +19,29 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.tasks;
|
package org.isoron.uhabits.tasks;
|
||||||
|
|
||||||
/**
|
import java.util.concurrent.*;
|
||||||
* Simple progress bar, used to indicate the progress of a task.
|
|
||||||
*/
|
public class SingleThreadTaskRunner implements TaskRunner
|
||||||
public interface ProgressBar
|
|
||||||
{
|
{
|
||||||
/**
|
@Override
|
||||||
* Hides the progress bar.
|
public void execute(Task task)
|
||||||
*/
|
{
|
||||||
default void hide() {}
|
task.onAttached(this);
|
||||||
|
task.onPreExecute();
|
||||||
default void setCurrent(int current) {}
|
task.doInBackground();
|
||||||
|
task.onPostExecute();
|
||||||
default void setTotal(int total) {}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Shows the progress bar.
|
public void publishProgress(Task task, int progress)
|
||||||
*/
|
{
|
||||||
default void show() {}
|
task.onProgressUpdate(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void waitForTasks(long timeout)
|
||||||
|
throws TimeoutException, InterruptedException
|
||||||
|
{
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,99 +19,14 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.tasks;
|
package org.isoron.uhabits.tasks;
|
||||||
|
|
||||||
import android.os.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
import javax.inject.*;
|
public interface TaskRunner
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class TaskRunner
|
|
||||||
{
|
{
|
||||||
private final LinkedList<CustomAsyncTask> activeTasks;
|
void execute(Task task);
|
||||||
|
|
||||||
@Inject
|
void publishProgress(Task task, int progress);
|
||||||
public TaskRunner()
|
|
||||||
{
|
void waitForTasks(long timeout)
|
||||||
activeTasks = new LinkedList<>();
|
throws TimeoutException, InterruptedException;
|
||||||
}
|
|
||||||
|
|
||||||
public void execute(Task task)
|
|
||||||
{
|
|
||||||
task.onAttached(this);
|
|
||||||
new CustomAsyncTask(task).execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCurrentProgress(Task task, int progress)
|
|
||||||
{
|
|
||||||
for (CustomAsyncTask asyncTask : activeTasks)
|
|
||||||
if (asyncTask.getTask() == task) asyncTask.publish(progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void waitForTasks(long timeout)
|
|
||||||
throws TimeoutException, InterruptedException
|
|
||||||
{
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
|
|
||||||
throw new UnsupportedOperationException("waitForTasks requires API 16+");
|
|
||||||
|
|
||||||
int poolInterval = 100;
|
|
||||||
|
|
||||||
while(timeout > 0)
|
|
||||||
{
|
|
||||||
if(activeTasks.isEmpty()) return;
|
|
||||||
|
|
||||||
timeout -= poolInterval;
|
|
||||||
Thread.sleep(poolInterval);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new TimeoutException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CustomAsyncTask extends AsyncTask<Void, Integer, Void>
|
|
||||||
{
|
|
||||||
private final Task task;
|
|
||||||
|
|
||||||
public CustomAsyncTask(Task task)
|
|
||||||
{
|
|
||||||
this.task = task;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task getTask()
|
|
||||||
{
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void publish(int progress)
|
|
||||||
{
|
|
||||||
publishProgress(progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... params)
|
|
||||||
{
|
|
||||||
task.doInBackground();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void aVoid)
|
|
||||||
{
|
|
||||||
task.onPostExecute();
|
|
||||||
activeTasks.remove(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onProgressUpdate(Integer... values)
|
|
||||||
{
|
|
||||||
task.onProgressUpdate(values[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute()
|
|
||||||
{
|
|
||||||
activeTasks.add(this);
|
|
||||||
task.onPreExecute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.ui;
|
|
||||||
|
|
||||||
import android.view.*;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.tasks.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Android implementation of {@link ProgressBar}.
|
|
||||||
*/
|
|
||||||
public class AndroidProgressBar implements ProgressBar
|
|
||||||
{
|
|
||||||
private final android.widget.ProgressBar progressBar;
|
|
||||||
|
|
||||||
public AndroidProgressBar(android.widget.ProgressBar progressBar)
|
|
||||||
{
|
|
||||||
this.progressBar = progressBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void hide()
|
|
||||||
{
|
|
||||||
progressBar.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setTotal(int total)
|
|
||||||
{
|
|
||||||
if(total == 0)
|
|
||||||
progressBar.setIndeterminate(true);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
progressBar.setIndeterminate(false);
|
|
||||||
progressBar.setMax(total);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCurrent(int current)
|
|
||||||
{
|
|
||||||
progressBar.setProgress(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void show()
|
|
||||||
{
|
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,7 +31,6 @@ import android.support.v7.widget.*;
|
|||||||
import android.view.*;
|
import android.view.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.tasks.*;
|
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -100,21 +99,6 @@ public abstract class BaseScreen
|
|||||||
selectionMenu.finish();
|
selectionMenu.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the progress bar that is currently visible on the screen.
|
|
||||||
* <p>
|
|
||||||
* If the root view attached to the screen does not provide any progress
|
|
||||||
* bars, returns null.
|
|
||||||
*
|
|
||||||
* @return current progress bar, or null if there are none.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public ProgressBar getProgressBar()
|
|
||||||
{
|
|
||||||
if (rootView == null) return null;
|
|
||||||
return new AndroidProgressBar(rootView.getProgressBar());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the screen that its contents should be updated.
|
* Notifies the screen that its contents should be updated.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -67,8 +67,6 @@ public class ListHabitsActivity extends BaseActivity
|
|||||||
selectionMenu = new ListHabitsSelectionMenu(habits, screen, adapter);
|
selectionMenu = new ListHabitsSelectionMenu(habits, screen, adapter);
|
||||||
controller = new ListHabitsController(habits, screen, system, adapter);
|
controller = new ListHabitsController(habits, screen, system, adapter);
|
||||||
|
|
||||||
adapter.setProgressBar(
|
|
||||||
new AndroidProgressBar(rootView.getProgressBar()));
|
|
||||||
screen.setMenu(menu);
|
screen.setMenu(menu);
|
||||||
screen.setController(controller);
|
screen.setController(controller);
|
||||||
screen.setSelectionMenu(selectionMenu);
|
screen.setSelectionMenu(selectionMenu);
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import android.view.*;
|
|||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.tasks.*;
|
|
||||||
import org.isoron.uhabits.ui.habits.list.views.*;
|
import org.isoron.uhabits.ui.habits.list.views.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -254,11 +253,6 @@ public class HabitCardListAdapter
|
|||||||
this.listView = listView;
|
this.listView = listView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProgressBar(ProgressBar progressBar)
|
|
||||||
{
|
|
||||||
cache.setProgressBar(progressBar);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects or deselects the item at a given position.
|
* Selects or deselects the item at a given position.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class HabitCardListCache implements CommandRunner.Listener
|
|||||||
|
|
||||||
private Task currentFetchTask;
|
private Task currentFetchTask;
|
||||||
|
|
||||||
@Nullable
|
@NonNull
|
||||||
private Listener listener;
|
private Listener listener;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -63,6 +63,7 @@ public class HabitCardListCache implements CommandRunner.Listener
|
|||||||
{
|
{
|
||||||
this.allHabits = allHabits;
|
this.allHabits = allHabits;
|
||||||
this.filteredHabits = allHabits;
|
this.filteredHabits = allHabits;
|
||||||
|
this.listener = new Listener() {};
|
||||||
data = new CacheData();
|
data = new CacheData();
|
||||||
|
|
||||||
BaseComponent component = HabitsApplication.getComponent();
|
BaseComponent component = HabitsApplication.getComponent();
|
||||||
@@ -145,7 +146,7 @@ public class HabitCardListCache implements CommandRunner.Listener
|
|||||||
data.checkmarks.remove(id);
|
data.checkmarks.remove(id);
|
||||||
data.scores.remove(id);
|
data.scores.remove(id);
|
||||||
|
|
||||||
if (listener != null) listener.onItemRemoved(position);
|
listener.onItemRemoved(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reorder(int from, int to)
|
public void reorder(int from, int to)
|
||||||
@@ -153,7 +154,7 @@ public class HabitCardListCache implements CommandRunner.Listener
|
|||||||
Habit fromHabit = data.habits.get(from);
|
Habit fromHabit = data.habits.get(from);
|
||||||
data.habits.remove(from);
|
data.habits.remove(from);
|
||||||
data.habits.add(to, fromHabit);
|
data.habits.add(to, fromHabit);
|
||||||
if (listener != null) listener.onItemMoved(from, to);
|
listener.onItemMoved(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCheckmarkCount(int checkmarkCount)
|
public void setCheckmarkCount(int checkmarkCount)
|
||||||
@@ -166,29 +167,24 @@ public class HabitCardListCache implements CommandRunner.Listener
|
|||||||
filteredHabits = allHabits.getFiltered(matcher);
|
filteredHabits = allHabits.getFiltered(matcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(@Nullable Listener listener)
|
public void setListener(@NonNull Listener listener)
|
||||||
{
|
{
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProgressBar(@NonNull ProgressBar progressBar)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface definition for a callback to be invoked when the data on the
|
* Interface definition for a callback to be invoked when the data on the
|
||||||
* cache has been modified.
|
* cache has been modified.
|
||||||
*/
|
*/
|
||||||
public interface Listener
|
public interface Listener
|
||||||
{
|
{
|
||||||
void onItemChanged(int position);
|
default void onItemChanged(int position) {}
|
||||||
|
|
||||||
void onItemInserted(int position);
|
default void onItemInserted(int position) {}
|
||||||
|
|
||||||
void onItemMoved(int oldPosition, int newPosition);
|
default void onItemMoved(int oldPosition, int newPosition) {}
|
||||||
|
|
||||||
void onItemRemoved(int position);
|
default void onItemRemoved(int position) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CacheData
|
private class CacheData
|
||||||
@@ -267,7 +263,7 @@ public class HabitCardListCache implements CommandRunner.Listener
|
|||||||
isCancelled = false;
|
isCancelled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RefreshTask(Long targetId)
|
public RefreshTask(long targetId)
|
||||||
{
|
{
|
||||||
newData = new CacheData();
|
newData = new CacheData();
|
||||||
this.targetId = targetId;
|
this.targetId = targetId;
|
||||||
@@ -286,11 +282,11 @@ public class HabitCardListCache implements CommandRunner.Listener
|
|||||||
newData.copyScoresFrom(data);
|
newData.copyScoresFrom(data);
|
||||||
newData.copyCheckmarksFrom(data);
|
newData.copyCheckmarksFrom(data);
|
||||||
|
|
||||||
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
|
||||||
long day = DateUtils.millisecondsInOneDay;
|
long day = DateUtils.millisecondsInOneDay;
|
||||||
|
long dateTo = DateUtils.getStartOfDay(DateUtils.getLocalTime());
|
||||||
long dateFrom = dateTo - (checkmarkCount - 1) * day;
|
long dateFrom = dateTo - (checkmarkCount - 1) * day;
|
||||||
|
|
||||||
runner.setCurrentProgress(this, -1);
|
runner.publishProgress(this, -1);
|
||||||
|
|
||||||
for (int position = 0; position < newData.habits.size(); position++)
|
for (int position = 0; position < newData.habits.size(); position++)
|
||||||
{
|
{
|
||||||
@@ -304,7 +300,7 @@ public class HabitCardListCache implements CommandRunner.Listener
|
|||||||
newData.checkmarks.put(id,
|
newData.checkmarks.put(id,
|
||||||
habit.getCheckmarks().getValues(dateFrom, dateTo));
|
habit.getCheckmarks().getValues(dateFrom, dateTo));
|
||||||
|
|
||||||
runner.setCurrentProgress(this, position);
|
runner.publishProgress(this, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,22 +330,32 @@ public class HabitCardListCache implements CommandRunner.Listener
|
|||||||
data.id_to_habit.put(id, habit);
|
data.id_to_habit.put(id, habit);
|
||||||
data.scores.put(id, newData.scores.get(id));
|
data.scores.put(id, newData.scores.get(id));
|
||||||
data.checkmarks.put(id, newData.checkmarks.get(id));
|
data.checkmarks.put(id, newData.checkmarks.get(id));
|
||||||
if (listener != null) listener.onItemInserted(position);
|
listener.onItemInserted(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performMove(Habit habit, int fromPosition, int toPosition)
|
private void performMove(Habit habit, int fromPosition, int toPosition)
|
||||||
{
|
{
|
||||||
data.habits.remove(fromPosition);
|
data.habits.remove(fromPosition);
|
||||||
data.habits.add(toPosition, habit);
|
data.habits.add(toPosition, habit);
|
||||||
if (listener != null)
|
|
||||||
listener.onItemMoved(fromPosition, toPosition);
|
listener.onItemMoved(fromPosition, toPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performUpdate(Long id, int position)
|
private void performUpdate(Long id, int position)
|
||||||
{
|
{
|
||||||
data.scores.put(id, newData.scores.get(id));
|
Integer oldScore = data.scores.get(id);
|
||||||
data.checkmarks.put(id, newData.checkmarks.get(id));
|
int[] oldCheckmarks = data.checkmarks.get(id);
|
||||||
if (listener != null) listener.onItemChanged(position);
|
|
||||||
|
Integer newScore = newData.scores.get(id);
|
||||||
|
int[] newCheckmarks = newData.checkmarks.get(id);
|
||||||
|
|
||||||
|
boolean unchanged = true;
|
||||||
|
if (!oldScore.equals(newScore)) unchanged = false;
|
||||||
|
if (!Arrays.equals(oldCheckmarks, newCheckmarks)) unchanged = false;
|
||||||
|
if(unchanged) return;
|
||||||
|
|
||||||
|
data.scores.put(id, newScore);
|
||||||
|
data.checkmarks.put(id, newCheckmarks);
|
||||||
|
listener.onItemChanged(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processPosition(int currentPosition)
|
private void processPosition(int currentPosition)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.commands.*;
|
||||||
import org.isoron.uhabits.intents.*;
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.io.*;
|
import org.isoron.uhabits.io.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
@@ -60,6 +61,9 @@ public class BaseUnitTest
|
|||||||
@Inject
|
@Inject
|
||||||
protected DirFinder dirFinder;
|
protected DirFinder dirFinder;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected CommandRunner commandRunner;
|
||||||
|
|
||||||
protected TestComponent testComponent;
|
protected TestComponent testComponent;
|
||||||
|
|
||||||
protected HabitFixtures fixtures;
|
protected HabitFixtures fixtures;
|
||||||
|
|||||||
@@ -19,11 +19,11 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
import org.isoron.uhabits.commands.*;
|
|
||||||
import org.isoron.uhabits.intents.*;
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.io.*;
|
import org.isoron.uhabits.io.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.models.memory.*;
|
import org.isoron.uhabits.models.memory.*;
|
||||||
|
import org.isoron.uhabits.tasks.*;
|
||||||
import org.isoron.uhabits.ui.common.dialogs.*;
|
import org.isoron.uhabits.ui.common.dialogs.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
@@ -36,13 +36,6 @@ import static org.mockito.Mockito.*;
|
|||||||
@Module
|
@Module
|
||||||
public class TestModule
|
public class TestModule
|
||||||
{
|
{
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
CommandRunner provideCommandRunner()
|
|
||||||
{
|
|
||||||
return mock(CommandRunner.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
DialogFactory provideDialogFactory()
|
DialogFactory provideDialogFactory()
|
||||||
@@ -125,4 +118,11 @@ public class TestModule
|
|||||||
{
|
{
|
||||||
return mock(WidgetPreferences.class);
|
return mock(WidgetPreferences.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
TaskRunner provideTaskRunner()
|
||||||
|
{
|
||||||
|
return new SingleThreadTaskRunner();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.isoron.uhabits.*;
|
||||||
|
import org.junit.*;
|
||||||
|
import org.junit.runner.*;
|
||||||
|
import org.junit.runners.*;
|
||||||
|
import org.mockito.*;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class SingleThreadTaskRunnerTest extends BaseUnitTest
|
||||||
|
{
|
||||||
|
private SingleThreadTaskRunner runner;
|
||||||
|
|
||||||
|
private Task task;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp()
|
||||||
|
{
|
||||||
|
super.setUp();
|
||||||
|
runner = new SingleThreadTaskRunner();
|
||||||
|
task = mock(Task.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test()
|
||||||
|
{
|
||||||
|
runner.execute(task);
|
||||||
|
|
||||||
|
InOrder inOrder = inOrder(task);
|
||||||
|
inOrder.verify(task).onAttached(runner);
|
||||||
|
inOrder.verify(task).onPreExecute();
|
||||||
|
inOrder.verify(task).doInBackground();
|
||||||
|
inOrder.verify(task).onPostExecute();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* 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.ui.habits.list.model;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.commands.*;
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.*;
|
||||||
|
import static org.hamcrest.MatcherAssert.*;
|
||||||
|
import static org.hamcrest.core.IsEqual.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
public class HabitCardListCacheTest extends BaseUnitTest
|
||||||
|
{
|
||||||
|
private HabitCardListCache cache;
|
||||||
|
|
||||||
|
private HabitCardListCache.Listener listener;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp()
|
||||||
|
{
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
fixtures.purgeHabits();
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
if (i == 3) fixtures.createLongHabit();
|
||||||
|
else fixtures.createShortHabit();
|
||||||
|
}
|
||||||
|
|
||||||
|
cache = new HabitCardListCache(habitList);
|
||||||
|
cache.setCheckmarkCount(10);
|
||||||
|
cache.refreshAllHabits();
|
||||||
|
cache.onAttached();
|
||||||
|
|
||||||
|
listener = mock(HabitCardListCache.Listener.class);
|
||||||
|
cache.setListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tearDown()
|
||||||
|
{
|
||||||
|
cache.onDetached();
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCommandListener_all()
|
||||||
|
{
|
||||||
|
assertThat(cache.getHabitCount(), equalTo(10));
|
||||||
|
|
||||||
|
Habit h = habitList.getByPosition(0);
|
||||||
|
commandRunner.execute(
|
||||||
|
new DeleteHabitsCommand(habitList, Collections.singletonList(h)),
|
||||||
|
null);
|
||||||
|
|
||||||
|
verify(listener).onItemRemoved(0);
|
||||||
|
assertThat(cache.getHabitCount(), equalTo(9));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCommandListener_single()
|
||||||
|
{
|
||||||
|
Habit h2 = habitList.getByPosition(2);
|
||||||
|
long today = DateUtils.getStartOfToday();
|
||||||
|
commandRunner.execute(new ToggleRepetitionCommand(h2, today),
|
||||||
|
h2.getId());
|
||||||
|
|
||||||
|
verify(listener).onItemChanged(2);
|
||||||
|
verifyNoMoreInteractions(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGet()
|
||||||
|
{
|
||||||
|
assertThat(cache.getHabitCount(), equalTo(10));
|
||||||
|
|
||||||
|
Habit h = habitList.getByPosition(3);
|
||||||
|
assertNotNull(h.getId());
|
||||||
|
int score = h.getScores().getTodayValue();
|
||||||
|
|
||||||
|
assertThat(cache.getHabitByPosition(3), equalTo(h));
|
||||||
|
assertThat(cache.getScore(h.getId()), equalTo(score));
|
||||||
|
|
||||||
|
long today = DateUtils.getStartOfToday();
|
||||||
|
long day = DateUtils.millisecondsInOneDay;
|
||||||
|
|
||||||
|
int[] actualCheckmarks = cache.getCheckmarks(h.getId());
|
||||||
|
int[] expectedCheckmarks =
|
||||||
|
h.getCheckmarks().getValues(today - 9 * day, today);
|
||||||
|
|
||||||
|
assertThat(actualCheckmarks, equalTo(expectedCheckmarks));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoval()
|
||||||
|
{
|
||||||
|
removeHabitAt(0);
|
||||||
|
removeHabitAt(3);
|
||||||
|
|
||||||
|
cache.refreshAllHabits();
|
||||||
|
verify(listener).onItemRemoved(0);
|
||||||
|
verify(listener).onItemRemoved(3);
|
||||||
|
assertThat(cache.getHabitCount(), equalTo(8));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReorder_onCache()
|
||||||
|
{
|
||||||
|
Habit h2 = cache.getHabitByPosition(2);
|
||||||
|
Habit h3 = cache.getHabitByPosition(3);
|
||||||
|
Habit h7 = cache.getHabitByPosition(7);
|
||||||
|
|
||||||
|
cache.reorder(2, 7);
|
||||||
|
|
||||||
|
assertThat(cache.getHabitByPosition(2), equalTo(h3));
|
||||||
|
assertThat(cache.getHabitByPosition(7), equalTo(h2));
|
||||||
|
assertThat(cache.getHabitByPosition(6), equalTo(h7));
|
||||||
|
verify(listener).onItemMoved(2, 7);
|
||||||
|
verifyNoMoreInteractions(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReorder_onList()
|
||||||
|
{
|
||||||
|
Habit h2 = habitList.getByPosition(2);
|
||||||
|
Habit h3 = habitList.getByPosition(3);
|
||||||
|
Habit h7 = habitList.getByPosition(7);
|
||||||
|
|
||||||
|
assertThat(cache.getHabitByPosition(2), equalTo(h2));
|
||||||
|
assertThat(cache.getHabitByPosition(7), equalTo(h7));
|
||||||
|
reset(listener);
|
||||||
|
|
||||||
|
habitList.reorder(h2, h7);
|
||||||
|
cache.refreshAllHabits();
|
||||||
|
|
||||||
|
assertThat(cache.getHabitByPosition(2), equalTo(h3));
|
||||||
|
assertThat(cache.getHabitByPosition(7), equalTo(h2));
|
||||||
|
assertThat(cache.getHabitByPosition(6), equalTo(h7));
|
||||||
|
|
||||||
|
verify(listener).onItemMoved(3, 2);
|
||||||
|
verify(listener).onItemMoved(4, 3);
|
||||||
|
verify(listener).onItemMoved(5, 4);
|
||||||
|
verify(listener).onItemMoved(6, 5);
|
||||||
|
verify(listener).onItemMoved(7, 6);
|
||||||
|
verifyNoMoreInteractions(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeHabitAt(int position)
|
||||||
|
{
|
||||||
|
Habit h = habitList.getByPosition(position);
|
||||||
|
assertNotNull(h);
|
||||||
|
habitList.remove(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user