Flatten uhabits-android into a single gradle module

This commit is contained in:
2021-01-03 20:44:55 -06:00
parent 4a4356b72a
commit 68f9b8339f
640 changed files with 325 additions and 536 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2016-2020 Á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.platform.gui
import android.graphics.Bitmap
import org.isoron.uhabits.BaseViewTest
import org.junit.Test
class AndroidCanvasTest : BaseViewTest() {
@Test
fun testDrawTestImage() {
similarityCutoff = 0.0005
val bmp = Bitmap.createBitmap(1000, 800, Bitmap.Config.ARGB_8888)
val canvas = AndroidCanvas()
canvas.context = testContext
canvas.innerDensity = 2.0
canvas.innerCanvas = android.graphics.Canvas(bmp)
canvas.innerBitmap = bmp
canvas.drawTestImage()
assertRenders(bmp, "CanvasTest.png")
}
}

View File

@@ -0,0 +1,289 @@
/*
* 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.appwidget.*;
import android.content.*;
import android.content.res.*;
import android.os.*;
import android.util.*;
import androidx.annotation.*;
import androidx.test.filters.*;
import androidx.test.platform.app.*;
import androidx.test.uiautomator.*;
import junit.framework.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.core.utils.*;
import org.isoron.uhabits.inject.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import java.io.*;
import java.time.*;
import java.util.*;
import java.util.concurrent.*;
import static androidx.test.platform.app.InstrumentationRegistry.*;
import static androidx.test.uiautomator.UiDevice.*;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
@MediumTest
public class BaseAndroidTest extends TestCase
{
// 8:00am, January 25th, 2015 (UTC)
public static final long FIXED_LOCAL_TIME = 1422172800000L;
protected Context testContext;
protected Context targetContext;
protected Preferences prefs;
protected HabitList habitList;
protected TaskRunner taskRunner;
protected HabitFixtures fixtures;
protected CountDownLatch latch;
protected HabitsApplicationTestComponent appComponent;
protected ModelFactory modelFactory;
protected HabitsActivityTestComponent component;
private boolean isDone = false;
private UiDevice device;
@Override
@Before
public void setUp()
{
if (Looper.myLooper() == null) Looper.prepare();
device = getInstance(getInstrumentation());
targetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
testContext = InstrumentationRegistry.getInstrumentation().getContext();
DateUtils.setFixedLocalTime(FIXED_LOCAL_TIME);
DateUtils.setStartDayOffset(0, 0);
setResolution(2.0f);
setTheme(R.style.AppBaseTheme);
setLocale("en", "US");
latch = new CountDownLatch(1);
Context context = targetContext.getApplicationContext();
File dbFile = DatabaseUtils.getDatabaseFile(context);
appComponent = DaggerHabitsApplicationTestComponent
.builder()
.appContextModule(new AppContextModule(context))
.habitsModule(new HabitsModule(dbFile))
.build();
HabitsApplication.Companion.setComponent(appComponent);
prefs = appComponent.getPreferences();
habitList = appComponent.getHabitList();
taskRunner = appComponent.getTaskRunner();
modelFactory = appComponent.getModelFactory();
prefs.clear();
fixtures = new HabitFixtures(modelFactory, habitList);
fixtures.purgeHabits(appComponent.getHabitList());
Habit habit = fixtures.createEmptyHabit();
component = DaggerHabitsActivityTestComponent
.builder()
.activityContextModule(new ActivityContextModule(targetContext))
.habitsApplicationComponent(appComponent)
.build();
}
protected void assertWidgetProviderIsInstalled(Class componentClass)
{
ComponentName provider =
new ComponentName(targetContext, componentClass);
AppWidgetManager manager = AppWidgetManager.getInstance(targetContext);
List<ComponentName> installedProviders = new LinkedList<>();
for (AppWidgetProviderInfo info : manager.getInstalledProviders())
installedProviders.add(info.provider);
assertThat(installedProviders, hasItems(provider));
}
protected void awaitLatch() throws InterruptedException
{
assertTrue(latch.await(1, TimeUnit.SECONDS));
}
protected void setLocale(@NonNull String language, @NonNull String country)
{
Locale locale = new Locale(language, country);
Locale.setDefault(locale);
Resources res = targetContext.getResources();
Configuration config = res.getConfiguration();
config.setLocale(locale);
}
protected void setResolution(float r)
{
DisplayMetrics dm = targetContext.getResources().getDisplayMetrics();
dm.density = r;
dm.scaledDensity = r;
InterfaceUtils.setFixedResolution(r);
}
protected void runConcurrently(Runnable... runnableList) throws Exception
{
isDone = false;
ExecutorService executor = Executors.newFixedThreadPool(100);
List<Future> futures = new LinkedList<>();
for (Runnable r : runnableList)
futures.add(executor.submit(() ->
{
while (!isDone) r.run();
return null;
}));
Thread.sleep(3000);
isDone = true;
executor.shutdown();
for(Future f : futures) f.get();
while (!executor.isTerminated()) Thread.sleep(50);
}
protected void setTheme(@StyleRes int themeId)
{
targetContext.setTheme(themeId);
StyledResources.setFixedTheme(themeId);
}
protected void sleep(int time)
{
try
{
Thread.sleep(time);
}
catch (InterruptedException e)
{
fail();
}
}
public long timestamp(int year, int month, int day)
{
GregorianCalendar cal = DateUtils.getStartOfTodayCalendar();
cal.set(year, month, day);
return cal.getTimeInMillis();
}
protected void startTracing()
{
File dir = new AndroidDirFinder(targetContext).getFilesDir("Profile");
assertNotNull(dir);
String tracePath = dir.getAbsolutePath() + "/performance.trace";
Log.d("PerformanceTest", String.format("Saving trace file to %s", tracePath));
Debug.startMethodTracingSampling(tracePath, 0, 1000);
}
protected void stopTracing()
{
Debug.stopMethodTracing();
}
protected Timestamp day(int offset)
{
return DateUtils.getToday().minus(offset);
}
public void setSystemTime(String tz,
int year,
int javaMonth,
int day,
int hourOfDay,
int minute) throws Exception
{
GregorianCalendar cal = new GregorianCalendar();
cal.set(Calendar.SECOND, 0);
cal.set(year, javaMonth, day, hourOfDay, minute);
cal.setTimeZone(TimeZone.getTimeZone(tz));
setSystemTime(cal);
}
private void setSystemTime(GregorianCalendar cal) throws Exception
{
ZoneId tz = cal.getTimeZone().toZoneId();
// Set time zone (temporary)
String command = String.format("service call alarm 3 s16 %s", tz);
device.executeShellCommand(command);
// Set time zone (permanent)
command = String.format("setprop persist.sys.timezone %s", tz);
device.executeShellCommand(command);
// Set time
String date = String.format("%02d%02d%02d%02d%02d.%02d",
cal.get(Calendar.MONTH) + 1,
cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
cal.get(Calendar.YEAR),
cal.get(Calendar.SECOND));
// Set time (method 1)
// Run twice to override daylight saving time
device.executeShellCommand("date " + date);
device.executeShellCommand("date " + date);
// Set time (method 2)
// Run in addition to the method above because one of these mail fail, depending
// on the Android API version.
command = String.format("date -u @%d", cal.getTimeInMillis() / 1000);
device.executeShellCommand(command);
// Wait for system events to settle
Thread.sleep(1000);
}
private GregorianCalendar savedCalendar = null;
public void saveSystemTime()
{
savedCalendar = new GregorianCalendar();
}
public void restoreSystemTime() throws Exception
{
if (savedCalendar == null) throw new NullPointerException();
setSystemTime(savedCalendar);
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2017 Á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.*;
import androidx.test.uiautomator.*;
import com.linkedin.android.testbutler.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.ui.screens.habits.list.*;
import org.isoron.uhabits.core.utils.*;
import org.isoron.uhabits.inject.*;
import org.junit.*;
import static androidx.test.core.app.ApplicationProvider.*;
import static androidx.test.platform.app.InstrumentationRegistry.*;
import static androidx.test.uiautomator.UiDevice.*;
public class BaseUserInterfaceTest
{
private static final String PKG = "org.isoron.uhabits";
public static final String EMPTY_DESCRIPTION_HABIT_NAME = "Read books";
public static UiDevice device;
private HabitsApplicationComponent component;
private HabitList habitList;
private Preferences prefs;
private HabitFixtures fixtures;
private HabitCardListCache cache;
public static void startActivity(Class cls)
{
Intent intent = new Intent();
intent.setComponent(new ComponentName(PKG, cls.getCanonicalName()));
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(intent);
}
@Before
public void setUp() throws Exception
{
device = getInstance(getInstrumentation());
TestButler.setup(getApplicationContext());
TestButler.verifyAnimationsDisabled(getApplicationContext());
HabitsApplication app =
(HabitsApplication) getApplicationContext().getApplicationContext();
component = app.getComponent();
habitList = component.getHabitList();
prefs = component.getPreferences();
cache = component.getHabitCardListCache();
fixtures = new HabitFixtures(component.getModelFactory(), habitList);
resetState();
}
@After
public void tearDown() throws Exception
{
for (int i = 0; i < 10; i++) device.pressBack();
TestButler.teardown(getApplicationContext());
}
private void resetState() throws Exception
{
prefs.clear();
prefs.setFirstRun(false);
prefs.updateLastHint(100, DateUtils.getToday());
habitList.removeAll();
cache.refreshAllHabits();
Thread.sleep(1000);
Habit h1 = fixtures.createEmptyHabit();
h1.setName("Wake up early");
h1.setQuestion("Did you wake up early today?");
h1.setDescription("test description 1");
h1.setColor(new PaletteColor(5));
habitList.update(h1);
Habit h2 = fixtures.createShortHabit();
h2.setName("Track time");
h2.setQuestion("Did you track your time?");
h2.setDescription("test description 2");
h2.setColor(new PaletteColor(5));
habitList.update(h2);
Habit h3 = fixtures.createLongHabit();
h3.setName("Meditate");
h3.setQuestion("Did meditate today?");
h3.setDescription("test description 3");
h3.setColor(new PaletteColor(10));
habitList.update(h3);
Habit h4 = fixtures.createEmptyHabit();
h4.setName(EMPTY_DESCRIPTION_HABIT_NAME);
h4.setQuestion("Did you read books today?");
h4.setDescription("");
h4.setColor(new PaletteColor(2));
habitList.update(h4);
}
protected void rotateDevice() throws Exception
{
device.setOrientationLeft();
device.setOrientationNatural();
}
}

View File

@@ -0,0 +1,198 @@
/*
* 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.graphics.*;
import android.view.*;
import android.widget.*;
import androidx.annotation.*;
import androidx.test.platform.app.*;
import org.isoron.uhabits.utils.*;
import org.isoron.uhabits.widgets.*;
import java.io.*;
import java.util.*;
import static android.view.View.MeasureSpec.*;
public class BaseViewTest extends BaseAndroidTest
{
public double similarityCutoff = 0.00018;
@Override
public void setUp()
{
super.setUp();
}
protected void assertRenders(View view, String expectedImagePath)
throws IOException
{
Bitmap actual = renderView(view);
if(actual == null) throw new IllegalStateException("actual is null");
assertRenders(actual, expectedImagePath);
}
protected void assertRenders(Bitmap actual, String expectedImagePath) throws IOException {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
expectedImagePath = "views/" + expectedImagePath;
try
{
Bitmap expected = getBitmapFromAssets(expectedImagePath);
double distance = distance(actual, expected);
if (distance > similarityCutoff)
{
saveBitmap(expectedImagePath, ".expected", expected);
String path = saveBitmap(expectedImagePath, "", actual);
fail(String.format("Image differs from expected " +
"(distance=%f). Actual rendered " +
"image saved to %s", distance, path));
}
expected.recycle();
}
catch (IOException e)
{
String path = saveBitmap(expectedImagePath, "", actual);
fail(String.format("Could not open expected image. Actual " +
"rendered image saved to %s", path));
throw e;
}
}
@NonNull
protected FrameLayout convertToView(BaseWidget widget,
int width,
int height)
{
widget.setDimensions(
new WidgetDimensions(width, height, width, height));
FrameLayout view = new FrameLayout(targetContext);
RemoteViews remoteViews = widget.getPortraitRemoteViews();
view.addView(remoteViews.apply(targetContext, view));
measureView(view, width, height);
return view;
}
protected float dpToPixels(int dp)
{
return InterfaceUtils.dpToPixels(targetContext, dp);
}
protected void measureView(View view, float width, float height)
{
int specWidth = makeMeasureSpec((int) width, View.MeasureSpec.EXACTLY);
int specHeight = makeMeasureSpec((int) height, View.MeasureSpec.EXACTLY);
view.setLayoutParams(new ViewGroup.LayoutParams((int) width, (int) height));
view.measure(specWidth, specHeight);
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
}
protected void skipAnimation(View view)
{
ViewPropertyAnimator animator = view.animate();
animator.setDuration(0);
animator.start();
}
private int[] colorToArgb(int c1)
{
return new int[]{
(c1 >> 24) & 0xff, //alpha
(c1 >> 16) & 0xff, //red
(c1 >> 8) & 0xff, //green
(c1) & 0xff //blue
};
}
private double distance(Bitmap b1, Bitmap b2)
{
if (b1.getWidth() != b2.getWidth()) return 1.0;
if (b1.getHeight() != b2.getHeight()) return 1.0;
Random random = new Random();
double distance = 0.0;
for (int x = 0; x < b1.getWidth(); x++)
{
for (int y = 0; y < b1.getHeight(); y++)
{
if (random.nextInt(4) != 0) continue;
int[] argb1 = colorToArgb(b1.getPixel(x, y));
int[] argb2 = colorToArgb(b2.getPixel(x, y));
distance += Math.abs(argb1[0] - argb2[0]);
distance += Math.abs(argb1[1] - argb2[1]);
distance += Math.abs(argb1[2] - argb2[2]);
distance += Math.abs(argb1[3] - argb2[3]);
}
}
distance /= 255.0 * 16 * b1.getWidth() * b1.getHeight();
return distance;
}
private Bitmap getBitmapFromAssets(String path) throws IOException
{
InputStream stream = testContext.getAssets().open(path);
return BitmapFactory.decodeStream(stream);
}
private String saveBitmap(String filename, String suffix, Bitmap bitmap)
throws IOException
{
File dir = FileUtils.getSDCardDir("test-screenshots");
if (dir == null)
dir = new AndroidDirFinder(targetContext).getFilesDir("test-screenshots");
if (dir == null) throw new RuntimeException(
"Could not find suitable dir for screenshots");
filename = filename.replaceAll("\\.png$", suffix + ".png");
String absolutePath =
String.format("%s/%s", dir.getAbsolutePath(), filename);
File parent = new File(absolutePath).getParentFile();
if (!parent.exists() && !parent.mkdirs()) throw new RuntimeException(
String.format("Could not create dir: %s",
parent.getAbsolutePath()));
FileOutputStream out = new FileOutputStream(absolutePath);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
return absolutePath;
}
public Bitmap renderView(View view)
{
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
if(view.isLayoutRequested())
measureView(view, width, height);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.invalidate();
view.draw(canvas);
return bitmap;
}
}

View File

@@ -0,0 +1,164 @@
/*
* 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 org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.utils.DateUtils;
import static org.isoron.uhabits.core.models.Entry.*;
public class HabitFixtures
{
public boolean LONG_HABIT_ENTRIES[] = {
true, false, false, true, true, true, false, false, true, true
};
public int LONG_NUMERICAL_HABIT_ENTRIES[] = {
200000, 0, 150000, 137000, 0, 0, 500000, 30000, 100000, 0, 300000,
100000, 0, 100000
};
private ModelFactory modelFactory;
private final HabitList habitList;
public HabitFixtures(ModelFactory modelFactory, HabitList habitList)
{
this.modelFactory = modelFactory;
this.habitList = habitList;
}
public Habit createEmptyHabit()
{
return createEmptyHabit(null);
}
public Habit createEmptyHabit(Long id)
{
Habit habit = modelFactory.buildHabit();
habit.setName("Meditate");
habit.setQuestion("Did you meditate this morning?");
habit.setDescription("This is a test description");
habit.setColor(new PaletteColor(5));
habit.setFrequency(Frequency.DAILY);
habit.setId(id);
habitList.add(habit);
return habit;
}
public Habit createLongHabit()
{
Habit habit = createEmptyHabit();
habit.setFrequency(new Frequency(3, 7));
habit.setColor(new PaletteColor(7));
Timestamp today = DateUtils.getToday();
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.getOriginalEntries().add(new Entry(today.minus(mark), YES_MANUAL));
habit.recompute();
return habit;
}
public Habit createVeryLongHabit()
{
Habit habit = createEmptyHabit();
habit.setFrequency(new Frequency(1, 2));
habit.setColor(new PaletteColor(11));
Timestamp today = DateUtils.getToday();
int marks[] = {0, 3, 5, 6, 7, 10, 13, 14, 15, 18, 21, 22, 23, 24, 27, 28, 30, 31, 34, 37,
39, 42, 43, 46, 47, 48, 51, 52, 54, 55, 57, 59, 62, 65, 68, 71, 73, 76, 79,
80, 81, 83, 85, 86, 89, 90, 91, 94, 96, 98, 100, 103, 104, 106, 109, 111,
112, 113, 115, 117, 120, 123, 126, 129, 132, 134, 136, 139, 141, 142, 145,
148, 149, 151, 152, 154, 156, 157, 159, 161, 162, 163, 164, 166, 168, 170,
172, 173, 174, 175, 176, 178, 180, 181, 184, 185, 188, 189, 190, 191, 194,
195, 197, 198, 199, 200, 202, 205, 208, 211, 213, 215, 216, 218, 220, 222,
223, 225, 227, 228, 230, 231, 232, 234, 235, 238, 241, 242, 244, 247, 250,
251, 253, 254, 257, 260, 261, 263, 264, 266, 269, 272, 273, 276, 279, 281,
284, 285, 288, 291, 292, 294, 296, 297, 299, 300, 301, 303, 306, 307, 308,
309, 310, 313, 316, 319, 322, 324, 326, 329, 330, 332, 334, 335, 337, 338,
341, 344, 345, 346, 347, 350, 352, 355, 358, 360, 361, 362, 363, 365, 368,
371, 373, 374, 376, 379, 380, 382, 384, 385, 387, 389, 390, 392, 393, 395,
396, 399, 401, 404, 407, 410, 411, 413, 414, 416, 417, 419, 420, 423, 424,
427, 429, 431, 433, 436, 439, 440, 442, 445, 447, 450, 453, 454, 456, 459,
460, 461, 464, 466, 468, 470, 473, 474, 475, 477, 479, 481, 482, 483, 486,
489, 491, 493, 495, 497, 498, 500, 503, 504, 507, 510, 511, 512, 515, 518,
519, 521, 522, 525, 528, 531, 532, 534, 537, 539, 541, 543, 544, 547, 550,
551, 554, 556, 557, 560, 561, 564, 567, 568, 569, 570, 572, 575, 576, 579,
582, 583, 584, 586, 589};
for (int mark : marks)
habit.getOriginalEntries().add(new Entry(today.minus(mark), YES_MANUAL));
habit.recompute();
return habit;
}
public Habit createLongNumericalHabit()
{
Habit habit = modelFactory.buildHabit();
habit.setName("Read");
habit.setQuestion("How many pages did you walk today?");
habit.setType(Habit.NUMBER_HABIT);
habit.setTargetType(Habit.AT_LEAST);
habit.setTargetValue(200.0);
habit.setUnit("pages");
habitList.add(habit);
Timestamp timestamp = DateUtils.getToday();
for (int value : LONG_NUMERICAL_HABIT_ENTRIES)
{
habit.getOriginalEntries().add(new Entry(timestamp, value));
timestamp = timestamp.minus(1);
}
habit.recompute();
return habit;
}
public Habit createShortHabit()
{
Habit habit = modelFactory.buildHabit();
habit.setName("Wake up early");
habit.setQuestion("Did you wake up before 6am?");
habit.setFrequency(new Frequency(2, 3));
habitList.add(habit);
Timestamp timestamp = DateUtils.getToday();
for (boolean c : LONG_HABIT_ENTRIES)
{
if (c) habit.getOriginalEntries().add(new Entry(timestamp, YES_MANUAL));
timestamp = timestamp.minus(1);
}
habit.recompute();
return habit;
}
public synchronized void purgeHabits(HabitList habitList)
{
habitList.removeAll();
}
}

View File

@@ -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
import dagger.Component
import dagger.Module
import dagger.Provides
import org.isoron.uhabits.activities.habits.list.ListHabitsModule
import org.isoron.uhabits.activities.habits.list.views.CheckmarkButtonViewFactory
import org.isoron.uhabits.activities.habits.list.views.CheckmarkPanelViewFactory
import org.isoron.uhabits.activities.habits.list.views.HabitCardViewFactory
import org.isoron.uhabits.activities.habits.list.views.NumberButtonViewFactory
import org.isoron.uhabits.activities.habits.list.views.NumberPanelViewFactory
import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior
import org.isoron.uhabits.inject.ActivityContextModule
import org.isoron.uhabits.inject.ActivityScope
import org.isoron.uhabits.inject.HabitModule
import org.isoron.uhabits.inject.HabitsActivityModule
import org.isoron.uhabits.inject.HabitsApplicationComponent
import org.mockito.Mockito.mock
@Module
class TestModule {
@Provides fun ListHabitsBehavior() = mock(ListHabitsBehavior::class.java)
}
@ActivityScope
@Component(
modules = arrayOf(
ActivityContextModule::class,
HabitsActivityModule::class,
ListHabitsModule::class,
HabitModule::class,
TestModule::class
),
dependencies = arrayOf(HabitsApplicationComponent::class)
)
interface HabitsActivityTestComponent {
fun getCheckmarkPanelViewFactory(): CheckmarkPanelViewFactory
fun getHabitCardViewFactory(): HabitCardViewFactory
fun getEntryButtonViewFactory(): CheckmarkButtonViewFactory
fun getNumberButtonViewFactory(): NumberButtonViewFactory
fun getNumberPanelViewFactory(): NumberPanelViewFactory
}

View File

@@ -0,0 +1,47 @@
/*
* 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 androidx.test.filters.*;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HabitsApplicationTest extends BaseAndroidTest
{
@Test
public void test_getLogcat() throws IOException
{
String msg = "LOGCAT TEST";
new RuntimeException(msg).printStackTrace();
String log = new AndroidBugReporter(targetContext).getLogcat();
assertThat(log, containsString(msg));
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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 org.isoron.uhabits.core.*;
import org.isoron.uhabits.core.tasks.*;
import org.isoron.uhabits.inject.*;
import org.isoron.uhabits.intents.*;
import dagger.*;
@AppScope
@Component(modules = {
AppContextModule.class,
HabitsModule.class,
SingleThreadModule.class,
})
public interface HabitsApplicationTestComponent
extends HabitsApplicationComponent
{
IntentScheduler getIntentScheduler();
}
@Module
class SingleThreadModule
{
@Provides
@AppScope
static TaskRunner provideTaskRunner()
{
return new SingleThreadTaskRunner();
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2017 Á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.acceptance;
import androidx.test.filters.*;
import androidx.test.runner.*;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.junit.*;
import org.junit.runner.*;
import static org.isoron.uhabits.acceptance.steps.CommonSteps.*;
import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.*;
import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class AboutTest extends BaseUserInterfaceTest
{
@Test
public void shouldDisplayAboutScreen() {
launchApp();
clickMenu(ABOUT);
verifyDisplaysText("Loop Habit Tracker");
verifyDisplaysText("Rate this app on Google Play");
verifyDisplaysText("Developers");
verifyDisplaysText("Translators");
}
@Test
public void shouldDisplayAboutScreenFromSettings() {
launchApp();
clickMenu(SETTINGS);
clickText("About");
verifyDisplaysText("Translators");
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2016-2020 Á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.acceptance
import androidx.test.filters.LargeTest
import org.isoron.uhabits.BaseUserInterfaceTest
import org.isoron.uhabits.acceptance.steps.CommonSteps.clickText
import org.isoron.uhabits.acceptance.steps.CommonSteps.launchApp
import org.isoron.uhabits.acceptance.steps.CommonSteps.longClickText
import org.isoron.uhabits.acceptance.steps.CommonSteps.verifyDisplaysText
import org.isoron.uhabits.acceptance.steps.CommonSteps.verifyDoesNotDisplayText
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps
import org.isoron.uhabits.acceptance.steps.clearBackupFolder
import org.isoron.uhabits.acceptance.steps.clearDownloadFolder
import org.isoron.uhabits.acceptance.steps.copyBackupToDownloadFolder
import org.isoron.uhabits.acceptance.steps.exportFullBackup
import org.isoron.uhabits.acceptance.steps.importBackupFromDownloadFolder
import org.junit.Test
@LargeTest
class BackupTest : BaseUserInterfaceTest() {
@Test
fun shouldExportAndImportBackup() {
launchApp()
clearDownloadFolder()
clearBackupFolder()
exportFullBackup()
copyBackupToDownloadFolder()
longClickText("Wake up early")
ListHabitsSteps.clickMenu(ListHabitsSteps.MenuItem.DELETE)
clickText("Yes")
verifyDoesNotDisplayText("Wake up early")
importBackupFromDownloadFolder()
verifyDisplaysText("Wake up early")
}
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright (C) 2017 Á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.acceptance;
import androidx.test.filters.*;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.junit.*;
import org.junit.runner.*;
import static org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.*;
import static org.isoron.uhabits.acceptance.steps.CommonSteps.*;
import static org.isoron.uhabits.acceptance.steps.EditHabitSteps.*;
import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.*;
import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class HabitsTest extends BaseUserInterfaceTest
{
@Test
public void shouldCreateHabit() throws Exception {
shouldCreateHabit("this is a test description");
}
@Test
public void shouldCreateHabitBlankDescription() throws Exception {
shouldCreateHabit("");
}
private void shouldCreateHabit(String description) throws Exception
{
launchApp();
verifyShowsScreen(LIST_HABITS);
clickMenu(ADD);
verifyShowsScreen(SELECT_HABIT_TYPE);
clickText("Yes or No");
verifyShowsScreen(EDIT_HABIT);
String testName = "Hello world";
typeName(testName);
typeQuestion("Did you say hello to the world today?");
typeDescription(description);
pickFrequency();
pickColor(5);
clickSave();
verifyShowsScreen(LIST_HABITS);
verifyDisplaysText(testName);
}
@Test
public void shouldShowHabitStatistics() throws Exception
{
launchApp();
verifyShowsScreen(LIST_HABITS);
clickText("Track time");
verifyShowsScreen(SHOW_HABIT);
verifyDisplayGraphs();
}
@Test
public void shouldDeleteHabit() throws Exception
{
launchApp();
verifyShowsScreen(LIST_HABITS);
longClickText("Track time");
clickMenu(DELETE);
clickText("Yes");
verifyDoesNotDisplayText("Track time");
}
@Test
public void shouldEditHabit() throws Exception {
shouldEditHabit("this is a test description");
}
@Test
public void shouldEditHabitBlankDescription() throws Exception {
shouldEditHabit("");
}
private void shouldEditHabit(String description) throws Exception
{
launchApp();
verifyShowsScreen(LIST_HABITS);
longClickText("Track time");
clickMenu(EDIT);
verifyShowsScreen(EDIT_HABIT);
typeName("Take a walk");
typeQuestion("Did you take a walk today?");
typeDescription(description);
clickSave();
verifyShowsScreen(LIST_HABITS);
verifyDisplaysTextInSequence("Wake up early", "Take a walk", "Meditate");
verifyDoesNotDisplayText("Track time");
}
@Test
public void shouldEditHabit_fromStatisticsScreen() throws Exception
{
launchApp();
verifyShowsScreen(LIST_HABITS);
clickText("Track time");
verifyShowsScreen(SHOW_HABIT);
clickMenu(EDIT);
verifyShowsScreen(EDIT_HABIT);
typeName("Take a walk");
typeQuestion("Did you take a walk today?");
pickColor(10);
clickSave();
verifyShowsScreen(SHOW_HABIT);
verifyDisplaysText("Take a walk");
pressBack();
verifyShowsScreen(LIST_HABITS);
verifyDisplaysText("Take a walk");
verifyDoesNotDisplayText("Track time");
}
@Test
public void shouldArchiveAndUnarchiveHabits() throws Exception
{
launchApp();
verifyShowsScreen(LIST_HABITS);
longClickText("Track time");
clickMenu(ARCHIVE);
verifyDoesNotDisplayText("Track time");
clickMenu(TOGGLE_ARCHIVED);
verifyDisplaysText("Track time");
longClickText("Track time");
clickMenu(UNARCHIVE);
clickMenu(TOGGLE_ARCHIVED);
verifyDisplaysText("Track time");
}
@Test
public void shouldToggleCheckmarksAndUpdateScore() throws Exception
{
launchApp();
verifyShowsScreen(LIST_HABITS);
longPressCheckmarks("Wake up early", 2);
clickText("Wake up early");
verifyShowsScreen(SHOW_HABIT);
verifyDisplaysText("10%");
}
@Test
public void shouldHideCompleted() throws Exception
{
launchApp();
verifyShowsScreen(LIST_HABITS);
verifyDisplaysText("Track time");
verifyDisplaysText("Wake up early");
clickMenu(TOGGLE_COMPLETED);
verifyDoesNotDisplayText("Track time");
verifyDisplaysText("Wake up early");
longPressCheckmarks("Wake up early", 1);
verifyDoesNotDisplayText("Wake up early");
clickMenu(TOGGLE_COMPLETED);
verifyDisplaysText("Track time");
verifyDisplaysText("Wake up early");
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2017 Á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.acceptance;
import androidx.test.filters.*;
import androidx.test.runner.*;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.junit.*;
import org.junit.runner.*;
import static org.isoron.uhabits.acceptance.steps.CommonSteps.*;
import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.*;
import static org.isoron.uhabits.acceptance.steps.ListHabitsSteps.*;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class LinksTest extends BaseUserInterfaceTest
{
@Test
public void shouldLinkToSourceCode() throws Exception
{
launchApp();
clickMenu(ABOUT);
clickText("View source code at GitHub");
verifyOpensWebsite("github.com");
}
@Test
public void shouldLinkToTranslationWebsite() throws Exception
{
launchApp();
clickMenu(ABOUT);
clickText("Help translate this app");
verifyOpensWebsite("translate.loophabits.org");
}
@Test
public void shouldLinkToHelp() throws Exception {
launchApp();
clickMenu(HELP);
verifyOpensWebsite("github.com");
}
@Test
public void shouldLinkToHelpFromSettings() throws Exception {
launchApp();
clickMenu(SETTINGS);
clickText("Help & FAQ");
verifyOpensWebsite("github.com");
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2017 Á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.acceptance;
import androidx.test.filters.*;
import org.isoron.uhabits.*;
import org.junit.*;
import static org.isoron.uhabits.acceptance.steps.CommonSteps.*;
import static org.isoron.uhabits.acceptance.steps.WidgetSteps.*;
import static org.isoron.uhabits.acceptance.steps.WidgetSteps.clickText;
@LargeTest
public class WidgetTest extends BaseUserInterfaceTest
{
@Test
public void shouldCreateAndToggleCheckmarkWidget() throws Exception
{
dragCheckmarkWidgetToHomeScreen();
Thread.sleep(3000);
clickText("Wake up early");
clickText("Save");
verifyCheckmarkWidgetIsShown();
clickCheckmarkWidget();
launchApp();
clickText("Wake up early");
verifyDisplaysText("5%");
pressHome();
clickCheckmarkWidget();
launchApp();
clickText("Wake up early");
verifyDisplaysText("0%");
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2016-2020 Á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.acceptance.steps
import androidx.test.uiautomator.UiSelector
import org.isoron.uhabits.acceptance.steps.CommonSteps.clickText
import org.isoron.uhabits.acceptance.steps.CommonSteps.device
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.SETTINGS
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.clickMenu
const val BACKUP_FOLDER = "/sdcard/Android/data/org.isoron.uhabits/files/Backups/"
const val DOWNLOAD_FOLDER = "/sdcard/Download/"
fun exportFullBackup() {
clickMenu(SETTINGS)
clickText("Export full backup")
device.pressBack()
}
fun clearDownloadFolder() {
device.executeShellCommand("rm -rf /sdcard/Download/")
}
fun clearBackupFolder() {
device.executeShellCommand("rm -rf $BACKUP_FOLDER")
}
fun copyBackupToDownloadFolder() {
device.executeShellCommand("mv $BACKUP_FOLDER $DOWNLOAD_FOLDER")
device.executeShellCommand("chown root $DOWNLOAD_FOLDER")
}
fun importBackupFromDownloadFolder() {
clickMenu(SETTINGS)
clickText("Import data")
device.click(100, 180) // Click menu button
device.findObject(UiSelector().textContains("Download")).click()
device.findObject(UiSelector().textContains("Loop")).click()
}
fun openLauncher() {
device.pressHome()
device.waitForIdle()
val h = device.displayHeight
val w = device.displayWidth
device.swipe(w / 2, h / 2, w / 2, 0, 8)
}

View File

@@ -0,0 +1,177 @@
/*
* Copyright (C) 2017 Á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.acceptance.steps;
import android.view.*;
import androidx.annotation.*;
import androidx.recyclerview.widget.*;
import androidx.test.espresso.*;
import androidx.test.espresso.contrib.*;
import androidx.test.uiautomator.*;
import org.hamcrest.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.habits.list.*;
import static android.os.Build.VERSION.*;
import static androidx.test.espresso.Espresso.*;
import static androidx.test.espresso.action.ViewActions.*;
import static androidx.test.espresso.assertion.PositionAssertions.*;
import static androidx.test.espresso.assertion.ViewAssertions.*;
import static androidx.test.espresso.matcher.ViewMatchers.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
public class CommonSteps extends BaseUserInterfaceTest
{
public static void pressBack()
{
device.pressBack();
}
public static void clickText(String text)
{
scrollToText(text);
onView(withText(text)).perform(click());
}
public static void clickText(@StringRes int id)
{
onView(withText(id)).perform(click());
}
public static void launchApp()
{
startActivity(ListHabitsActivity.class);
assertTrue(
device.wait(Until.hasObject(By.pkg("org.isoron.uhabits")), 5000));
device.waitForIdle();
}
public static void longClickText(String text)
{
scrollToText(text);
onView(withText(text)).perform(longClick());
}
public static void pressHome()
{
device.pressHome();
device.waitForIdle();
}
public static void scrollToText(String text)
{
try
{
if (device
.findObject(new UiSelector().className(RecyclerView.class))
.exists())
{
onView(instanceOf(RecyclerView.class)).perform(
RecyclerViewActions.scrollTo(
hasDescendant(withText(text))));
}
else
{
onView(withText(text)).perform(scrollTo());
}
}
catch (PerformException e)
{
//ignored
}
}
public static void verifyDisplayGraphs()
{
verifyDisplaysView("HistoryCard");
verifyDisplaysView("ScoreCard");
}
public static void verifyDisplaysText(String text)
{
scrollToText(text);
onView(withText(text)).check(matches(isEnabled()));
}
public static void verifyDisplaysTextInSequence(String... text)
{
verifyDisplaysText(text[0]);
for(int i = 1; i < text.length; i++) {
verifyDisplaysText(text[i]);
onView(withText(text[i])).check(isCompletelyBelow(withText(text[i-1])));
}
}
private static void verifyDisplaysView(String className)
{
onView(withClassName(endsWith(className))).check(matches(isEnabled()));
}
public static void verifyDoesNotDisplayText(String text)
{
onView(withText(text)).check(doesNotExist());
}
public static void verifyOpensWebsite(String url) throws Exception
{
String browser_pkg = "org.chromium.webview_shell";
if(SDK_INT <= 23) {
browser_pkg = "com.android.browser";
}
assertTrue(device.wait(Until.hasObject(By.pkg(browser_pkg)), 5000));
device.waitForIdle();
assertTrue(device.findObject(new UiSelector().textContains(url)).exists());
}
public enum Screen
{
LIST_HABITS, SHOW_HABIT, EDIT_HABIT, SELECT_HABIT_TYPE
}
public static void verifyShowsScreen(Screen screen)
{
switch(screen)
{
case LIST_HABITS:
onView(withClassName(endsWith("ListHabitsRootView")))
.check(matches(isDisplayed()));
break;
case SHOW_HABIT:
onView(withId(R.id.subtitleCard)).check(matches(isDisplayed()));
break;
case EDIT_HABIT:
onView(withId(R.id.questionInput)).check(matches(isDisplayed()));
break;
case SELECT_HABIT_TYPE:
onView(withText(R.string.yes_or_no_example)).check(matches(isDisplayed()));
break;
default:
throw new IllegalStateException();
}
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2017 Á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.acceptance.steps;
import androidx.test.uiautomator.*;
import org.isoron.uhabits.*;
import static androidx.test.espresso.Espresso.*;
import static androidx.test.espresso.action.ViewActions.*;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.matcher.ViewMatchers.*;
import static org.isoron.uhabits.BaseUserInterfaceTest.*;
public class EditHabitSteps
{
public static void clickSave()
{
onView(withId(R.id.buttonSave)).perform(click());
}
public static void pickFrequency()
{
onView(withId(R.id.boolean_frequency_picker)).perform(click());
onView(withText("SAVE")).perform(click());
}
public static void pickColor(int color)
{
onView(withId(R.id.colorButton)).perform(click());
device.findObject(By.descStartsWith(String.format("Color %d", color))).click();
}
public static void typeName(String name)
{
typeTextWithId(R.id.nameInput, name);
}
public static void typeQuestion(String name)
{
typeTextWithId(R.id.questionInput, name);
}
public static void typeDescription(String description)
{
typeTextWithId(R.id.notesInput, description);
}
public static void setReminder()
{
onView(withId(R.id.reminderTimePicker)).perform(click());
onView(withId(R.id.done_button)).perform(click());
}
public static void clickReminderDays()
{
onView(withId(R.id.reminderDatePicker)).perform(click());
}
public static void unselectAllDays()
{
onView(withText("Saturday")).perform(click());
onView(withText("Sunday")).perform(click());
onView(withText("Monday")).perform(click());
onView(withText("Tuesday")).perform(click());
onView(withText("Wednesday")).perform(click());
onView(withText("Thursday")).perform(click());
onView(withText("Friday")).perform(click());
}
private static void typeTextWithId(int id, String name)
{
onView(withId(id)).perform(clearText(), typeText(name), closeSoftKeyboard());
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (C) 2017 Á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.acceptance.steps;
import androidx.test.espresso.*;
import android.view.*;
import org.hamcrest.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.habits.list.views.*;
import java.util.*;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.CoreMatchers.*;
import static org.isoron.uhabits.BaseUserInterfaceTest.device;
import static org.isoron.uhabits.acceptance.steps.CommonSteps.clickText;
public abstract class ListHabitsSteps
{
public static void clickMenu(MenuItem item)
{
switch (item)
{
case ABOUT:
clickTextInsideOverflowMenu(R.string.about);
break;
case HELP:
clickTextInsideOverflowMenu(R.string.help);
break;
case SETTINGS:
clickTextInsideOverflowMenu(R.string.settings);
break;
case ADD:
clickViewWithId(R.id.actionCreateHabit);
break;
case EDIT:
clickViewWithId(R.id.action_edit_habit);
break;
case DELETE:
clickTextInsideOverflowMenu(R.string.delete);
break;
case ARCHIVE:
clickTextInsideOverflowMenu(R.string.archive);
break;
case UNARCHIVE:
clickTextInsideOverflowMenu(R.string.unarchive);
break;
case TOGGLE_ARCHIVED:
clickViewWithId(R.id.action_filter);
clickText(R.string.hide_archived);
break;
case TOGGLE_COMPLETED:
clickViewWithId(R.id.action_filter);
clickText(R.string.hide_completed);
break;
}
}
private static void clickTextInsideOverflowMenu(int id) {
onView(allOf(withContentDescription("More options"), withParent(withParent(withClassName(endsWith("Toolbar")))))).perform(click());
onView(withText(id)).perform(click());
}
private static void clickViewWithId(int id)
{
onView(withId(id)).perform(click());
}
private static ViewAction longClickDescendantWithClass(Class cls, int count)
{
return new ViewAction()
{
@Override
public Matcher<View> getConstraints()
{
return isEnabled();
}
@Override
public String getDescription()
{
return "perform on children";
}
@Override
public void perform(UiController uiController, View view)
{
LinkedList<ViewGroup> stack = new LinkedList<>();
if (view instanceof ViewGroup) stack.push((ViewGroup) view);
int countRemaining = count;
while (!stack.isEmpty())
{
ViewGroup vg = stack.pop();
for (int i = 0; i < vg.getChildCount(); i++)
{
View v = vg.getChildAt(i);
if (v instanceof ViewGroup) stack.push((ViewGroup) v);
if (cls.isInstance(v) && countRemaining > 0)
{
v.performLongClick();
uiController.loopMainThreadUntilIdle();
countRemaining--;
}
}
}
}
};
}
public static void longPressCheckmarks(String habit, int count)
{
CommonSteps.scrollToText(habit);
onView(allOf(hasDescendant(withText(habit)),
withClassName(endsWith("HabitCardView")))).perform(
longClickDescendantWithClass(CheckmarkButtonView.class, count));
device.waitForIdle();
}
public enum MenuItem
{
ABOUT, HELP, SETTINGS, EDIT, DELETE, ARCHIVE, TOGGLE_ARCHIVED,
UNARCHIVE, TOGGLE_COMPLETED, ADD
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2017 Á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.acceptance.steps;
import androidx.test.uiautomator.*;
import static android.os.Build.VERSION.SDK_INT;
import static org.junit.Assert.*;
import static org.isoron.uhabits.BaseUserInterfaceTest.*;
public class WidgetSteps {
public static void clickCheckmarkWidget() throws Exception {
String view_id = "org.isoron.uhabits:id/imageView";
device.findObject(new UiSelector().resourceId(view_id)).click();
}
public static void clickText(String s) throws Exception {
UiObject object = device.findObject(new UiSelector().text(s));
if (!object.waitForExists(1000)) {
object = device.findObject(new UiSelector().text(s.toUpperCase()));
}
object.click();
}
public static void dragCheckmarkWidgetToHomeScreen() throws Exception {
openWidgetScreen();
dragWidgetToHomeScreen();
}
private static void dragWidgetToHomeScreen() throws Exception {
int height = device.getDisplayHeight();
int width = device.getDisplayWidth();
device.findObject(new UiSelector().text("Checkmark"))
.dragTo(width / 2, height / 2, 40);
}
private static void openWidgetScreen() throws Exception {
int h = device.getDisplayHeight();
int w = device.getDisplayWidth();
if (SDK_INT <= 21) {
device.pressHome();
device.waitForIdle();
device.findObject(new UiSelector().description("Apps")).click();
device.findObject(new UiSelector().description("Apps")).click();
device.findObject(new UiSelector().description("Widgets")).click();
} else {
String list_id = "com.android.launcher3:id/widgets_list_view";
device.pressHome();
device.waitForIdle();
device.drag(w / 2, h / 2, w / 2, h / 2, 8);
UiObject button = device.findObject(new UiSelector().text("WIDGETS"));
if(!button.waitForExists(1000)) {
button = device.findObject(new UiSelector().text("Widgets"));
}
button.click();
if (SDK_INT >= 28) {
new UiScrollable(new UiSelector().resourceId(list_id))
.scrollForward();
}
new UiScrollable(new UiSelector().resourceId(list_id))
.scrollIntoView(new UiSelector().text("Checkmark"));
}
}
public static void verifyCheckmarkWidgetIsShown() throws Exception {
String view_id = "org.isoron.uhabits:id/imageView";
assertTrue(device.findObject(new UiSelector().resourceId(view_id)).exists());
assertFalse(device.findObject(new UiSelector().textStartsWith("Habit deleted")).exists());
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.activities.common.views;
import androidx.test.ext.junit.runners.*;
import androidx.test.filters.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class FrequencyChartTest extends BaseViewTest
{
public static final String BASE_PATH = "common/FrequencyChart/";
private FrequencyChart view;
@Override
@Before
public void setUp()
{
super.setUp();
fixtures.purgeHabits(habitList);
Habit habit = fixtures.createLongHabit();
view = new FrequencyChart(targetContext);
view.setFrequency(habit.getOriginalEntries().computeWeekdayFrequency(
habit.isNumerical()
));
view.setColor(PaletteUtilsKt.toFixedAndroidColor(habit.getColor()));
measureView(view, dpToPixels(300), dpToPixels(100));
}
@Test
public void testRender() throws Throwable
{
assertRenders(view, BASE_PATH + "render.png");
}
@Test
public void testRender_withDataOffset() throws Throwable
{
view.onScroll(null, null, -dpToPixels(150), 0);
view.invalidate();
assertRenders(view, BASE_PATH + "renderDataOffset.png");
}
@Test
public void testRender_withDifferentSize() throws Throwable
{
measureView(view, dpToPixels(200), dpToPixels(200));
assertRenders(view, BASE_PATH + "renderDifferentSize.png");
}
@Test
public void testRender_withTransparentBackground() throws Throwable
{
view.setIsBackgroundTransparent(true);
assertRenders(view, BASE_PATH + "renderTransparent.png");
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.activities.common.views;
import android.graphics.*;
import androidx.test.filters.*;
import androidx.test.runner.*;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
import java.io.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class RingViewTest extends BaseViewTest
{
private static final String BASE_PATH = "common/RingView/";
private RingView view;
@Override
@Before
public void setUp()
{
super.setUp();
view = new RingView(targetContext);
view.setPercentage(0.6f);
view.setText("60%");
view.setColor(PaletteUtils.getAndroidTestColor(0));
view.setBackgroundColor(Color.WHITE);
view.setThickness(dpToPixels(3));
}
@Test
public void testRender_base() throws IOException
{
measureView(view, dpToPixels(100), dpToPixels(100));
assertRenders(view, BASE_PATH + "render.png");
}
@Test
public void testRender_withDifferentParams() throws IOException
{
view.setPercentage(0.25f);
view.setColor(PaletteUtils.getAndroidTestColor(5));
measureView(view, dpToPixels(200), dpToPixels(200));
assertRenders(view, BASE_PATH + "renderDifferentParams.png");
}
}

View File

@@ -0,0 +1,109 @@
/*
* 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.activities.common.views;
import androidx.test.ext.junit.runners.*;
import androidx.test.filters.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.ui.screens.habits.show.views.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ScoreChartTest extends BaseViewTest
{
private static final String BASE_PATH = "common/ScoreChart/";
private Habit habit;
private ScoreChart view;
@Override
@Before
public void setUp()
{
super.setUp();
fixtures.purgeHabits(habitList);
habit = fixtures.createLongHabit();
ScoreCardState state = ScoreCardPresenter.Companion.buildState(habit, prefs.getFirstWeekdayInt(), 0);
view = new ScoreChart(targetContext);
view.setScores(state.getScores());
view.setColor(PaletteUtilsKt.toFixedAndroidColor(state.getColor()));
view.setBucketSize(state.getBucketSize());
measureView(view, dpToPixels(300), dpToPixels(200));
}
@Test
public void testRender() throws Throwable
{
assertRenders(view, BASE_PATH + "render.png");
}
@Test
public void testRender_withDataOffset() throws Throwable
{
view.onScroll(null, null, -dpToPixels(150), 0);
view.invalidate();
assertRenders(view, BASE_PATH + "renderDataOffset.png");
}
@Test
public void testRender_withDifferentSize() throws Throwable
{
measureView(view, dpToPixels(200), dpToPixels(200));
assertRenders(view, BASE_PATH + "renderDifferentSize.png");
}
@Test
public void testRender_withMonthlyBucket() throws Throwable
{
ScoreCardState model = ScoreCardPresenter.Companion.buildState(habit, prefs.getFirstWeekdayInt(), 2);
view.setScores(model.getScores());
view.setBucketSize(model.getBucketSize());
view.invalidate();
assertRenders(view, BASE_PATH + "renderMonthly.png");
}
@Test
public void testRender_withTransparentBackground() throws Throwable
{
view.setIsTransparencyEnabled(true);
assertRenders(view, BASE_PATH + "renderTransparent.png");
}
@Test
public void testRender_withYearlyBucket() throws Throwable
{
ScoreCardState model = ScoreCardPresenter.Companion.buildState(habit, prefs.getFirstWeekdayInt(), 4);
view.setScores(model.getScores());
view.setBucketSize(model.getBucketSize());
view.invalidate();
assertRenders(view, BASE_PATH + "renderYearly.png");
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.activities.common.views;
import androidx.test.filters.*;
import androidx.test.runner.*;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.utils.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class StreakChartTest extends BaseViewTest
{
private static final String BASE_PATH = "common/StreakChart/";
private StreakChart view;
@Override
@Before
public void setUp()
{
super.setUp();
fixtures.purgeHabits(habitList);
Habit habit = fixtures.createLongHabit();
view = new StreakChart(targetContext);
view.setColor(PaletteUtilsKt.toFixedAndroidColor(habit.getColor()));
view.setStreaks(habit.getStreaks().getBest(5));
measureView(view, dpToPixels(300), dpToPixels(100));
}
@Test
public void testRender() throws Throwable
{
assertRenders(view, BASE_PATH + "render.png");
}
@Test
public void testRender_withSmallSize() throws Throwable
{
measureView(view, dpToPixels(100), dpToPixels(100));
assertRenders(view, BASE_PATH + "renderSmallSize.png");
}
@Test
public void testRender_withTransparentBackground() throws Throwable
{
view.setIsBackgroundTransparent(true);
assertRenders(view, BASE_PATH + "renderTransparent.png");
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.activities.habits.list.views
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.core.models.Entry
import org.isoron.uhabits.utils.PaletteUtils
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class EntryButtonViewTest : BaseViewTest() {
private val PATH = "habits/list/CheckmarkButtonView"
lateinit var view: CheckmarkButtonView
var toggled = false
@Before
override fun setUp() {
super.setUp()
view = component.getEntryButtonViewFactory().create().apply {
value = Entry.NO
color = PaletteUtils.getAndroidTestColor(5)
onToggle = { toggled = true }
}
measureView(view, dpToPixels(48), dpToPixels(48))
}
@Test
fun testRender_explicitCheck() {
view.value = Entry.YES_MANUAL
assertRendersCheckedExplicitly()
}
@Test
fun testRender_implicitCheck() {
view.value = Entry.YES_AUTO
assertRendersCheckedImplicitly()
}
@Test
fun testRender_unchecked() {
view.value = Entry.NO
assertRendersUnchecked()
}
@Test
fun testClick_withShortToggleDisabled() {
prefs.isShortToggleEnabled = false
view.performClick()
assertFalse(toggled)
}
@Test
fun testClick_withShortToggleEnabled() {
prefs.isShortToggleEnabled = true
view.performClick()
assertTrue(toggled)
}
@Test
fun testLongClick() {
view.performLongClick()
assertTrue(toggled)
}
private fun assertRendersCheckedExplicitly() {
assertRenders(view, "$PATH/render_explicit_check.png")
}
private fun assertRendersCheckedImplicitly() {
assertRenders(view, "$PATH/render_implicit_check.png")
}
private fun assertRendersUnchecked() {
assertRenders(view, "$PATH/render_unchecked.png")
}
}

View File

@@ -0,0 +1,119 @@
/*
* 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.activities.habits.list.views
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.core.models.Entry.Companion.NO
import org.isoron.uhabits.core.models.Entry.Companion.YES_AUTO
import org.isoron.uhabits.core.models.Entry.Companion.YES_MANUAL
import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.utils.PaletteUtils
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class EntryPanelViewTest : BaseViewTest() {
private val PATH = "habits/list/CheckmarkPanelView"
private lateinit var view: CheckmarkPanelView
@Before
override fun setUp() {
super.setUp()
prefs.isCheckmarkSequenceReversed = false
val checkmarks = intArrayOf(
YES_MANUAL,
YES_MANUAL,
YES_AUTO,
NO,
NO,
NO,
YES_MANUAL
)
view = component.getCheckmarkPanelViewFactory().create().apply {
values = checkmarks
buttonCount = 4
color = PaletteUtils.getAndroidTestColor(7)
}
view.onAttachedToWindow()
measureView(view, dpToPixels(200), dpToPixels(200))
}
@After
public override fun tearDown() {
super.tearDown()
}
@Test
fun testRender() {
assertRenders(view, "$PATH/render.png")
}
// // Flaky test
// @Test
// fun testRender_withDifferentColor() {
// view.color = PaletteUtils.getAndroidTestColor(1)
// assertRenders(view, "$PATH/render_different_color.png")
// }
// // Flaky test
// @Test
// fun testRender_Reversed() {
// prefs.isCheckmarkSequenceReversed = true
// assertRenders(view, "$PATH/render_reversed.png")
// }
// // Flaky test
// @Test
// fun testRender_withOffset() {
// view.dataOffset = 3
// assertRenders(view, "$PATH/render_offset.png")
// }
@Test
fun testToggle() {
val timestamps = mutableListOf<Timestamp>()
view.onToggle = { t, _ -> timestamps.add(t) }
view.buttons[0].performLongClick()
view.buttons[2].performLongClick()
view.buttons[3].performLongClick()
assertThat(timestamps, equalTo(listOf(day(0), day(2), day(3))))
}
@Test
fun testToggle_withOffset() {
val timestamps = mutableListOf<Timestamp>()
view.dataOffset = 3
view.onToggle = { t, _ -> timestamps += t }
view.buttons[0].performLongClick()
view.buttons[2].performLongClick()
view.buttons[3].performLongClick()
assertThat(timestamps, equalTo(listOf(day(3), day(5), day(6))))
}
}

View File

@@ -0,0 +1,103 @@
/*
* 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.activities.habits.list.views
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.R
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.PaletteColor
import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.core.utils.DateUtils
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class HabitCardViewTest : BaseViewTest() {
val PATH = "habits/list/HabitCardView"
private lateinit var view: HabitCardView
private lateinit var habit1: Habit
private lateinit var habit2: Habit
private lateinit var today: Timestamp
override fun setUp() {
super.setUp()
setTheme(R.style.AppBaseTheme)
habit1 = fixtures.createLongHabit()
habit2 = fixtures.createLongNumericalHabit()
today = DateUtils.getTodayWithOffset()
val entries = habit1
.computedEntries
.getByInterval(today.minus(300), today)
.map { it.value }.toIntArray()
view = component.getHabitCardViewFactory().create().apply {
habit = habit1
values = entries
score = habit1.scores.get(today).value
isSelected = false
buttonCount = 5
}
latch.countDown()
latch.await()
measureView(view, dpToPixels(400), dpToPixels(50))
}
@Test
fun testRender() {
assertRenders(view, "$PATH/render.png")
}
@Test
fun testRender_selected() {
view.isSelected = true
measureView(view, dpToPixels(400), dpToPixels(50))
assertRenders(view, "$PATH/render_selected.png")
}
@Test
fun testRender_numerical() {
val entries = habit2
.computedEntries
.getByInterval(today.minus(300), today)
.map { it.value }.toIntArray()
view.apply {
habit = habit2
values = entries
}
assertRenders(view, "$PATH/render_numerical.png")
}
@Test
fun testChangeModel() {
habit1.name = "Wake up early"
habit1.color = PaletteColor(2)
habit1.observable.notifyListeners()
Thread.sleep(500)
assertRenders(view, "$PATH/render_changed.png")
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.activities.habits.list.views;
import androidx.test.filters.*;
import androidx.test.runner.*;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.preferences.*;
import org.isoron.uhabits.core.utils.*;
import org.junit.*;
import org.junit.runner.*;
import static org.mockito.Mockito.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HeaderViewTest extends BaseViewTest
{
public static final String PATH = "habits/list/HeaderView/";
private HeaderView view;
private Preferences prefs;
private MidnightTimer midnightTimer;
@Override
@Before
public void setUp()
{
super.setUp();
prefs = mock(Preferences.class);
midnightTimer = mock(MidnightTimer.class);
view = new HeaderView(targetContext, prefs, midnightTimer);
view.setButtonCount(5);
measureView(view, dpToPixels(600), dpToPixels(48));
}
@Test
public void testRender() throws Exception
{
when(prefs.isCheckmarkSequenceReversed()).thenReturn(false);
assertRenders(view, PATH + "render.png");
verify(prefs).isCheckmarkSequenceReversed();
verifyNoMoreInteractions(prefs);
}
@Test
public void testRender_reverse() throws Exception
{
when(prefs.isCheckmarkSequenceReversed()).thenReturn(true);
assertRenders(view, PATH + "render_reverse.png");
verify(prefs).isCheckmarkSequenceReversed();
verifyNoMoreInteractions(prefs);
}
}

View File

@@ -0,0 +1,80 @@
/*
* 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.activities.habits.list.views;
import androidx.test.filters.*;
import androidx.test.runner.*;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.ui.screens.habits.list.*;
import org.junit.*;
import org.junit.runner.*;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
import static org.mockito.Mockito.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HintViewTest extends BaseViewTest
{
public static final String PATH = "habits/list/HintView/";
private HintView view;
private HintList list;
@Before
@Override
public void setUp()
{
super.setUp();
list = mock(HintList.class);
view = new HintView(targetContext, list);
measureView(view, 400, 200);
String text =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
when(list.shouldShow()).thenReturn(true);
when(list.pop()).thenReturn(text);
view.showNext();
skipAnimation(view);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
@Test
public void testClick() throws Exception
{
assertThat(view.getAlpha(), equalTo(1f));
view.performClick();
skipAnimation(view);
assertThat(view.getAlpha(), equalTo(0f));
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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.activities.habits.list.views
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.utils.PaletteUtils
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class NumberButtonViewTest : BaseViewTest() {
private val PATH = "habits/list/NumberButtonView"
private lateinit var view: NumberButtonView
var edited = false
@Before
override fun setUp() {
super.setUp()
view = component.getNumberButtonViewFactory().create().apply {
units = "steps"
threshold = 100.0
color = PaletteUtils.getAndroidTestColor(8)
onEdit = { edited = true }
}
measureView(view, dpToPixels(48), dpToPixels(48))
}
@Test
fun testFormatValue() {
assertThat(0.1235.toShortString(), equalTo("0.12"))
assertThat(0.1000.toShortString(), equalTo("0.1"))
assertThat(5.0.toShortString(), equalTo("5"))
assertThat(5.25.toShortString(), equalTo("5.25"))
assertThat(12.3456.toShortString(), equalTo("12.3"))
assertThat(123.123.toShortString(), equalTo("123"))
assertThat(321.2.toShortString(), equalTo("321"))
assertThat(4321.2.toShortString(), equalTo("4.3k"))
assertThat(54321.2.toShortString(), equalTo("54.3k"))
assertThat(654321.2.toShortString(), equalTo("654k"))
assertThat(7654321.2.toShortString(), equalTo("7.7M"))
assertThat(87654321.2.toShortString(), equalTo("87.7M"))
assertThat(987654321.2.toShortString(), equalTo("988M"))
assertThat(1987654321.2.toShortString(), equalTo("2.0G"))
}
@Test
fun testRender_aboveThreshold() {
view.value = 500.0
assertRenders(view, "$PATH/render_above.png")
}
@Test
fun testRender_belowThreshold() {
view.value = 99.0
assertRenders(view, "$PATH/render_below.png")
}
@Test
fun testRender_zero() {
view.value = 0.0
assertRenders(view, "$PATH/render_zero.png")
}
@Test
fun testClick_shortToggleDisabled() {
prefs.isShortToggleEnabled = false
view.performClick()
assertFalse(edited)
}
@Test
fun testClick_shortToggleEnabled() {
prefs.isShortToggleEnabled = true
view.performClick()
assertTrue(edited)
}
@Test
fun testLongClick() {
view.performLongClick()
assertTrue(edited)
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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.activities.habits.list.views
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.core.models.Timestamp
import org.isoron.uhabits.utils.PaletteUtils
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class NumberPanelViewTest : BaseViewTest() {
private val PATH = "habits/list/NumberPanelView"
private lateinit var view: NumberPanelView
@Before
override fun setUp() {
super.setUp()
prefs.isCheckmarkSequenceReversed = false
val checkmarks = doubleArrayOf(
1400.0,
5300.0,
0.0,
14600.0,
2500.0,
45000.0
)
view = component.getNumberPanelViewFactory().create().apply {
values = checkmarks
buttonCount = 4
color = PaletteUtils.getAndroidTestColor(7)
units = "steps"
threshold = 5000.0
}
view.onAttachedToWindow()
measureView(view, dpToPixels(200), dpToPixels(200))
}
@After
public override fun tearDown() {
view.onDetachedFromWindow()
}
@Test
fun testRender() {
assertRenders(view, "$PATH/render.png")
}
// // Flaky test
// @Test
// fun testRender_withDifferentColor() {
// view.color = PaletteUtils.getAndroidTestColor(1)
// assertRenders(view, "$PATH/render_different_color.png")
// }
// // Flaky test
// @Test
// fun testRender_Reversed() {
// prefs.isCheckmarkSequenceReversed = true
// assertRenders(view, "$PATH/render_reversed.png")
// }
// // Flaky test
// @Test
// fun testRender_withOffset() {
// view.dataOffset = 3
// assertRenders(view, "$PATH/render_offset.png")
// }
@Test
fun testEdit() {
val timestamps = mutableListOf<Timestamp>()
view.onEdit = { timestamps.plusAssign(it) }
view.buttons[0].performLongClick()
view.buttons[2].performLongClick()
view.buttons[3].performLongClick()
assertThat(timestamps, equalTo(listOf(day(0), day(2), day(3))))
}
@Test
fun testEdit_withOffset() {
val timestamps = mutableListOf<Timestamp>()
view.dataOffset = 3
view.onEdit = { timestamps += it }
view.buttons[0].performLongClick()
view.buttons[2].performLongClick()
view.buttons[3].performLongClick()
assertThat(timestamps, equalTo(listOf(day(3), day(5), day(6))))
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.activities.habits.show.views
import android.view.LayoutInflater
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.R
import org.isoron.uhabits.core.ui.screens.habits.show.views.FrequencyCardPresenter
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class FrequencyCardViewTest : BaseViewTest() {
val PATH = "habits/show/FrequencyCard/"
private lateinit var view: FrequencyCardView
@Before
override fun setUp() {
super.setUp()
val habit = fixtures.createLongHabit()
view = LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById<View>(R.id.frequencyCard) as FrequencyCardView
view.setState(FrequencyCardPresenter.buildState(habit = habit, firstWeekday = 0))
measureView(view, 800f, 600f)
}
@Test
fun testRender() {
assertRenders(view, PATH + "render.png")
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.activities.habits.show.views
import android.view.LayoutInflater
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.platform.time.DayOfWeek.SUNDAY
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.R
import org.isoron.uhabits.core.ui.screens.habits.show.views.HistoryCardPresenter
import org.isoron.uhabits.core.ui.views.LightTheme
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class HistoryCardViewTest : BaseViewTest() {
private lateinit var view: HistoryCardView
val PATH = "habits/show/HistoryCard/"
@Before
override fun setUp() {
super.setUp()
val habit = fixtures.createLongHabit()
view = LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById<View>(R.id.historyCard) as HistoryCardView
view.setState(
HistoryCardPresenter.buildState(
habit = habit,
firstWeekday = SUNDAY,
theme = LightTheme(),
)
)
measureView(view, 800f, 600f)
}
@Test
fun testRender() {
assertRenders(view, PATH + "render.png")
}
}

View File

@@ -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.activities.habits.show.views
import android.view.LayoutInflater
import android.view.View.GONE
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.hamcrest.Matchers.equalTo
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.R
import org.isoron.uhabits.core.ui.screens.habits.show.views.NotesCardState
import org.junit.Assert.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class NotesCardViewTest : BaseViewTest() {
val PATH = "habits/show/NotesCard"
private lateinit var view: NotesCardView
@Before
override fun setUp() {
super.setUp()
view = LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById(R.id.notesCard)
view.setState(NotesCardState(description = "This is a test description"))
measureView(view, 800f, 200f)
}
@Test
fun testRender() {
assertRenders(view, "$PATH/render.png")
}
@Test
fun testRenderEmptyDescription() {
view.setState(NotesCardState(description = ""))
assertThat(view.visibility, equalTo(GONE))
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.activities.habits.show.views
import android.view.LayoutInflater
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.R
import org.isoron.uhabits.core.models.PaletteColor
import org.isoron.uhabits.core.ui.screens.habits.show.views.OverviewCardState
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class OverviewCardViewTest : BaseViewTest() {
val PATH = "habits/show/OverviewCard/"
private lateinit var view: OverviewCardView
@Before
override fun setUp() {
super.setUp()
view = LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById<View>(R.id.overviewCard) as OverviewCardView
view.setState(
OverviewCardState(
scoreToday = 0.74f,
scoreMonthDiff = 0.23f,
scoreYearDiff = 0.74f,
totalCount = 44,
color = PaletteColor(7),
)
)
measureView(view, 800f, 300f)
}
@Test
fun testRender() {
assertRenders(view, PATH + "render.png")
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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.activities.habits.show.views
import android.view.LayoutInflater
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.R
import org.isoron.uhabits.core.ui.screens.habits.show.views.ScoreCardPresenter
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class ScoreCardViewTest : BaseViewTest() {
val PATH = "habits/show/ScoreCard/"
private lateinit var view: ScoreCardView
@Before
override fun setUp() {
super.setUp()
val habit = fixtures.createLongHabit()
view = LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById<View>(R.id.scoreCard) as ScoreCardView
view.setState(
ScoreCardPresenter.buildState(
habit = habit,
firstWeekday = 0,
spinnerPosition = 0,
)
)
measureView(view, 800f, 600f)
}
@Test
fun testRender() {
assertRenders(view, PATH + "render.png")
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.activities.habits.show.views
import android.view.LayoutInflater
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.R
import org.isoron.uhabits.core.ui.screens.habits.show.views.StreakCardState
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class StreakCardViewTest : BaseViewTest() {
val PATH = "habits/show/StreakCard/"
private lateinit var view: StreakCardView
@Before
override fun setUp() {
super.setUp()
val habit = fixtures.createLongHabit()
view = LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById<View>(R.id.streakCard) as StreakCardView
view.setState(
StreakCardState(
bestStreaks = habit.streaks.getBest(10),
color = habit.color,
)
)
measureView(view, 800f, 600f)
}
@Test
fun testRender() {
assertRenders(view, PATH + "render.png")
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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.activities.habits.show.views
import android.view.LayoutInflater
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
import org.isoron.uhabits.BaseViewTest
import org.isoron.uhabits.R
import org.isoron.uhabits.core.models.Frequency
import org.isoron.uhabits.core.models.PaletteColor
import org.isoron.uhabits.core.models.Reminder
import org.isoron.uhabits.core.models.WeekdayList.EVERY_DAY
import org.isoron.uhabits.core.ui.screens.habits.show.views.SubtitleCardState
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@MediumTest
class SubtitleCardViewTest : BaseViewTest() {
val PATH = "habits/show/SubtitleCard/"
private lateinit var view: SubtitleCardView
@Before
override fun setUp() {
super.setUp()
view = LayoutInflater
.from(targetContext)
.inflate(R.layout.show_habit, null)
.findViewById(R.id.subtitleCard)
view.setState(
SubtitleCardState(
color = PaletteColor(7),
frequency = Frequency(3, 7),
isNumerical = false,
question = "Did you meditate this morning?",
reminder = Reminder(8, 30, EVERY_DAY),
unit = "",
targetValue = 0.0,
)
)
measureView(view, 800f, 200f)
}
@Test
fun testRender() {
assertRenders(view, PATH + "render.png")
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2015-2017 Á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.database;
import android.database.sqlite.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.database.*;
import org.junit.*;
import java.util.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
public class AndroidDatabaseTest extends BaseAndroidTest
{
private AndroidDatabase db;
@Override
public void setUp()
{
super.setUp();
db = new AndroidDatabase(SQLiteDatabase.create(null), null);
db.execute("create table test(color int, name string)");
}
@Test
public void testInsert() throws Exception
{
HashMap<String, Object> map = new HashMap<>();
map.put("name", "asd");
map.put("color", null);
db.insert("test", map);
Cursor c = db.query("select * from test");
c.moveToNext();
assertNull(c.getInt(0));
assertThat(c.getString(1), equalTo("asd"));
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2016-2020 Á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.database
import org.isoron.uhabits.AndroidDirFinder
import org.isoron.uhabits.BaseAndroidTest
import org.isoron.uhabits.core.utils.DateUtils
import org.junit.Test
import java.io.File
import java.io.FileOutputStream
class AutoBackupTest : BaseAndroidTest() {
@Test
fun testRun() {
DateUtils.setFixedLocalTime(40 * DateUtils.DAY_LENGTH)
val basedir = AndroidDirFinder(targetContext).getFilesDir("Backups")!!
createTestFiles(basedir, 30)
val autoBackup = AutoBackup(targetContext)
autoBackup.run(keep = 5)
for (k in 1..25) assertDoesNotExist("${basedir.path}/test-$k.txt")
for (k in 26..30) assertExists("${basedir.path}/test-$k.txt")
assertExists("${basedir.path}/Loop Habits Backup 1970-02-10 000000.db")
}
@Test
fun testRunWithEmptyDir() {
val basedir = AndroidDirFinder(targetContext).getFilesDir("Backups")!!
removeAllFiles(basedir)
basedir.delete()
// Should not crash
val autoBackup = AutoBackup(targetContext)
autoBackup.run()
}
private fun assertExists(path: String) {
assertTrue("File $path should exist", File(path).exists())
}
private fun assertDoesNotExist(path: String) {
assertFalse("File $path should not exist", File(path).exists())
}
private fun createTestFiles(basedir: File, nfiles: Int) {
removeAllFiles(basedir)
for (k in 1..nfiles) {
touch("${basedir.path}/test-$k.txt", DateUtils.DAY_LENGTH * k)
}
}
private fun touch(path: String, time: Long) {
val file = File(path)
FileOutputStream(file).close()
file.setLastModified(time)
}
private fun removeAllFiles(dir: File) {
dir.list().forEach { path ->
val file = File("${dir.path}/$path")
assertTrue(file.delete())
}
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2016-2020 Á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.intents
import android.content.ContentUris.parseId
import androidx.test.filters.MediumTest
import org.hamcrest.Matchers.equalTo
import org.isoron.uhabits.BaseAndroidTest
import org.isoron.uhabits.core.reminders.ReminderScheduler.SchedulerResult.OK
import org.isoron.uhabits.receivers.ReminderReceiver
import org.isoron.uhabits.receivers.WidgetReceiver
import org.junit.After
import org.junit.Assert.assertThat
import org.junit.Before
import org.junit.Test
import java.util.Calendar.DAY_OF_MONTH
import java.util.Calendar.DECEMBER
import java.util.Calendar.HOUR_OF_DAY
import java.util.Calendar.JUNE
import java.util.Calendar.MAY
import java.util.Calendar.MINUTE
import java.util.Calendar.MONTH
import java.util.Calendar.YEAR
import java.util.GregorianCalendar
import java.util.TimeZone
class IntentSchedulerTest : BaseAndroidTest() {
@Before
override fun setUp() {
super.setUp()
saveSystemTime()
}
@After
override fun tearDown() {
restoreSystemTime()
super.tearDown()
}
@Test
@MediumTest
@Throws(Exception::class)
fun testSetSystemTime() {
setSystemTime("America/Chicago", 2020, JUNE, 1, 12, 40)
var cal = GregorianCalendar()
assertThat(cal.timeZone, equalTo(TimeZone.getTimeZone("America/Chicago")))
assertThat(cal[YEAR], equalTo(2020))
assertThat(cal[MONTH], equalTo(JUNE))
assertThat(cal[DAY_OF_MONTH], equalTo(1))
assertThat(cal[HOUR_OF_DAY], equalTo(12))
assertThat(cal[MINUTE], equalTo(40))
setSystemTime("Europe/Paris", 2019, MAY, 15, 6, 30)
cal = GregorianCalendar()
assertThat(cal.timeZone, equalTo(TimeZone.getTimeZone("Europe/Paris")))
assertThat(cal[YEAR], equalTo(2019))
assertThat(cal[MONTH], equalTo(MAY))
assertThat(cal[DAY_OF_MONTH], equalTo(15))
assertThat(cal[HOUR_OF_DAY], equalTo(6))
assertThat(cal[MINUTE], equalTo(30))
setSystemTime("Asia/Tokyo", 2021, DECEMBER, 20, 18, 0)
cal = GregorianCalendar()
assertThat(cal.timeZone, equalTo(TimeZone.getTimeZone("Asia/Tokyo")))
assertThat(cal[YEAR], equalTo(2021))
assertThat(cal[MONTH], equalTo(DECEMBER))
assertThat(cal[DAY_OF_MONTH], equalTo(20))
assertThat(cal[HOUR_OF_DAY], equalTo(18))
assertThat(cal[MINUTE], equalTo(0))
}
@Test
@MediumTest
fun testScheduleShowReminder() {
for (h in habitList) h.reminder = null
ReminderReceiver.clearLastReceivedIntent()
setSystemTime("America/Chicago", 2020, JUNE, 1, 12, 30)
val reminderTime = 1591155900000 // 2020-06-02 22:45:00 (America/Chicago)
val habit = habitList.getByPosition(0)
val scheduler = appComponent.intentScheduler
assertThat(scheduler.scheduleShowReminder(reminderTime, habit, 0), equalTo(OK))
setSystemTime("America/Chicago", 2020, JUNE, 2, 22, 44)
assertNull(ReminderReceiver.getLastReceivedIntent())
setSystemTime("America/Chicago", 2020, JUNE, 2, 22, 46)
val intent = ReminderReceiver.getLastReceivedIntent()
assertNotNull(intent)
assertThat(parseId(intent.data!!), equalTo(habit.id))
}
@Test
@MediumTest
fun testScheduleWidgetUpdate() {
WidgetReceiver.clearLastReceivedIntent()
setSystemTime("America/Chicago", 2020, JUNE, 1, 12, 30)
val updateTime = 1591155900000 // 2020-06-02 22:45:00 (America/Chicago)
val scheduler = appComponent.intentScheduler
assertThat(scheduler.scheduleWidgetUpdate(updateTime), equalTo(OK))
setSystemTime("America/Chicago", 2020, JUNE, 2, 22, 44)
assertNull(WidgetReceiver.getLastReceivedIntent())
setSystemTime("America/Chicago", 2020, JUNE, 2, 22, 46)
val intent = WidgetReceiver.getLastReceivedIntent()
assertNotNull(intent)
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2017 Á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.performance;
import androidx.test.ext.junit.runners.*;
import androidx.test.filters.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.commands.*;
import org.isoron.uhabits.core.database.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.models.sqlite.*;
import org.junit.*;
import org.junit.runner.*;
import static org.isoron.uhabits.core.models.Timestamp.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class PerformanceTest extends BaseAndroidTest
{
private Habit habit;
@Override
public void setUp()
{
super.setUp();
habit = fixtures.createLongHabit();
}
@Ignore
@Test(timeout = 5000)
public void benchmarkCreateHabitCommand()
{
Database db = ((SQLModelFactory) modelFactory).getDatabase();
db.beginTransaction();
for (int i = 0; i < 1_000; i++)
{
Habit model = modelFactory.buildHabit();
new CreateHabitCommand(modelFactory, habitList, model).run();
}
db.setTransactionSuccessful();
db.endTransaction();
}
@Ignore
@Test(timeout = 5000)
public void benchmarkCreateRepetitionCommand()
{
Database db = ((SQLModelFactory) modelFactory).getDatabase();
db.beginTransaction();
Habit habit = fixtures.createEmptyHabit();
for (int i = 0; i < 5_000; i++)
{
Timestamp timestamp = new Timestamp(i * DAY_LENGTH);
new CreateRepetitionCommand(habitList, habit, timestamp, 1).run();
}
db.setTransactionSuccessful();
db.endTransaction();
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2020 Á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.regression
import androidx.test.filters.LargeTest
import org.isoron.uhabits.BaseUserInterfaceTest
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.EDIT_HABIT
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.LIST_HABITS
import org.isoron.uhabits.acceptance.steps.CommonSteps.Screen.SELECT_HABIT_TYPE
import org.isoron.uhabits.acceptance.steps.CommonSteps.clickText
import org.isoron.uhabits.acceptance.steps.CommonSteps.launchApp
import org.isoron.uhabits.acceptance.steps.CommonSteps.longClickText
import org.isoron.uhabits.acceptance.steps.CommonSteps.verifyDisplaysText
import org.isoron.uhabits.acceptance.steps.CommonSteps.verifyShowsScreen
import org.isoron.uhabits.acceptance.steps.EditHabitSteps.clickSave
import org.isoron.uhabits.acceptance.steps.EditHabitSteps.typeName
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.ADD
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.MenuItem.DELETE
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.clickMenu
import org.isoron.uhabits.acceptance.steps.ListHabitsSteps.longPressCheckmarks
import org.junit.Test
@LargeTest
class ListHabitsRegressionTest : BaseUserInterfaceTest() {
/**
* https://github.com/iSoron/uhabits/issues/539
*/
@Test
@Throws(Exception::class)
fun should_not_crash_after_deleting_then_adding_a_habit() {
launchApp()
verifyShowsScreen(LIST_HABITS)
longClickText("Track time")
clickMenu(DELETE)
clickText("Yes")
clickMenu(ADD)
verifyShowsScreen(SELECT_HABIT_TYPE)
clickText("Yes or No")
verifyShowsScreen(EDIT_HABIT)
typeName("Hello world")
clickSave()
verifyDisplaysText("Hello world")
longPressCheckmarks("Hello world", 3)
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2020 Á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.regression
import androidx.test.filters.LargeTest
import org.isoron.uhabits.BaseUserInterfaceTest
import org.isoron.uhabits.acceptance.steps.CommonSteps.launchApp
import org.isoron.uhabits.activities.about.AboutActivity
import org.junit.Test
import java.lang.Thread.sleep
@LargeTest
class SavedStateTest : BaseUserInterfaceTest() {
/**
* Make sure that the main activity can be recreated by using
* BundleSavedState after being destroyed. See bug:
* https://github.com/iSoron/uhabits/issues/287
*/
@Test
@Throws(Exception::class)
fun testBundleSavedState() {
launchApp()
startActivity(AboutActivity::class.java)
sleep(1000)
device.pressBack()
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2016-2020 Á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.sync
import androidx.test.filters.MediumTest
import com.fasterxml.jackson.databind.ObjectMapper
import io.ktor.client.HttpClient
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.MockRequestHandleScope
import io.ktor.client.engine.mock.respond
import io.ktor.client.engine.mock.respondError
import io.ktor.client.engine.mock.respondOk
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.request.HttpRequestData
import io.ktor.client.request.HttpResponseData
import io.ktor.http.HttpStatusCode
import io.ktor.http.fullPath
import io.ktor.http.headersOf
import kotlinx.coroutines.runBlocking
import org.isoron.uhabits.BaseAndroidTest
import org.isoron.uhabits.core.sync.AbstractSyncServer
import org.isoron.uhabits.core.sync.GetDataVersionResponse
import org.isoron.uhabits.core.sync.KeyNotFoundException
import org.isoron.uhabits.core.sync.RegisterReponse
import org.isoron.uhabits.core.sync.ServiceUnavailable
import org.isoron.uhabits.core.sync.SyncData
import org.junit.Test
@MediumTest
class RemoteSyncServerTest : BaseAndroidTest() {
private val mapper = ObjectMapper()
val data = SyncData(1, "Hello world")
@Test
fun when_register_succeeds_should_return_key() = runBlocking {
val server = server("/register") {
respondWithJson(RegisterReponse("ABCDEF"))
}
assertEquals("ABCDEF", server.register())
}
@Test(expected = ServiceUnavailable::class)
fun when_register_fails_should_raise_correct_exception() = runBlocking {
val server = server("/register") {
respondError(HttpStatusCode.ServiceUnavailable)
}
server.register()
return@runBlocking
}
@Test
fun when_get_data_version_succeeds_should_return_version() = runBlocking {
server("/db/ABC/version") {
respondWithJson(GetDataVersionResponse(5))
}.apply {
assertEquals(5, getDataVersion("ABC"))
}
return@runBlocking
}
@Test(expected = ServiceUnavailable::class)
fun when_get_data_version_with_server_error_should_raise_exception() = runBlocking {
server("/db/ABC/version") {
respondError(HttpStatusCode.InternalServerError)
}.apply {
getDataVersion("ABC")
}
return@runBlocking
}
@Test(expected = KeyNotFoundException::class)
fun when_get_data_version_with_invalid_key_should_raise_exception() = runBlocking {
server("/db/ABC/version") {
respondError(HttpStatusCode.NotFound)
}.apply {
getDataVersion("ABC")
}
return@runBlocking
}
@Test
fun when_get_data_succeeds_should_return_data() = runBlocking {
server("/db/ABC") {
respondWithJson(data)
}.apply {
assertEquals(data, getData("ABC"))
}
return@runBlocking
}
@Test(expected = KeyNotFoundException::class)
fun when_get_data_with_invalid_key_should_raise_exception() = runBlocking {
server("/db/ABC") {
respondError(HttpStatusCode.NotFound)
}.apply {
getData("ABC")
}
return@runBlocking
}
@Test
fun when_put_succeeds_should_not_raise_exceptions() = runBlocking {
server("/db/ABC") {
respondOk()
}.apply {
put("ABC", data)
}
return@runBlocking
}
private fun server(
expectedPath: String,
action: MockRequestHandleScope.(HttpRequestData) -> HttpResponseData
): AbstractSyncServer {
return RemoteSyncServer(
httpClient = HttpClient(MockEngine) {
install(JsonFeature)
engine {
addHandler { request ->
when (request.url.fullPath) {
expectedPath -> action(request)
else -> error("unexpected call: ${request.url.fullPath}")
}
}
}
},
preferences = prefs
)
}
private fun MockRequestHandleScope.respondWithJson(content: Any) =
respond(
mapper.writeValueAsBytes(content),
headers = headersOf("Content-Type" to listOf("application/json"))
)
}

View File

@@ -0,0 +1,95 @@
/*
* 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.widgets;
import android.widget.*;
import androidx.test.ext.junit.runners.*;
import androidx.test.filters.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.utils.*;
import org.junit.*;
import org.junit.runner.*;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
import static org.isoron.uhabits.core.models.Entry.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class CheckmarkWidgetTest extends BaseViewTest
{
private static final String PATH = "widgets/CheckmarkWidget/";
private Habit habit;
private EntryList entries;
private FrameLayout view;
private Timestamp today = DateUtils.getTodayWithOffset();
@Override
public void setUp()
{
super.setUp();
setTheme(R.style.WidgetTheme);
prefs.setWidgetOpacity(255);
prefs.setSkipEnabled(true);
habit = fixtures.createVeryLongHabit();
entries = habit.getComputedEntries();
CheckmarkWidget widget = new CheckmarkWidget(targetContext, 0, habit);
view = convertToView(widget, 150, 200);
assertThat(entries.get(today).getValue(), equalTo(YES_MANUAL));
}
@Test
public void testClick() throws Exception
{
Button button = (Button) view.findViewById(R.id.button);
assertThat(button, is(not(nullValue())));
// A better test would be to capture the intent, but it doesn't seem
// possible to capture intents sent to BroadcastReceivers.
button.performClick();
sleep(1000);
assertThat(entries.get(today).getValue(), equalTo(SKIP));
button.performClick();
sleep(1000);
assertThat(entries.get(today).getValue(), equalTo(NO));
}
@Test
public void testIsInstalled()
{
assertWidgetProviderIsInstalled(CheckmarkWidgetProvider.class);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.widgets;
import android.widget.*;
import androidx.test.ext.junit.runners.*;
import androidx.test.filters.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
import org.junit.runner.*;
import java.util.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class FrequencyWidgetTest extends BaseViewTest
{
private static final String PATH = "widgets/FrequencyWidget/";
private Habit habit;
private FrameLayout view;
@Override
public void setUp()
{
super.setUp();
setTheme(R.style.WidgetTheme);
prefs.setWidgetOpacity(255);
habit = fixtures.createVeryLongHabit();
FrequencyWidget widget = new FrequencyWidget(targetContext, 0, habit, Calendar.SUNDAY);
view = convertToView(widget, 400, 400);
}
@Test
public void testIsInstalled()
{
assertWidgetProviderIsInstalled(FrequencyWidgetProvider.class);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.widgets;
import android.widget.*;
import androidx.test.ext.junit.runners.*;
import androidx.test.filters.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.junit.*;
import org.junit.runner.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class HistoryWidgetTest extends BaseViewTest
{
private static final String PATH = "widgets/HistoryWidget/";
private Habit habit;
private FrameLayout view;
@Override
public void setUp()
{
super.setUp();
setTheme(R.style.WidgetTheme);
prefs.setWidgetOpacity(255);
habit = fixtures.createVeryLongHabit();
HistoryWidget widget = new HistoryWidget(targetContext, 0, habit);
view = convertToView(widget, 400, 400);
}
@Test
public void testIsInstalled()
{
assertWidgetProviderIsInstalled(HistoryWidgetProvider.class);
}
@Test
public void testRender() throws Exception
{
assertRenders(view, PATH + "render.png");
}
}

Some files were not shown because too many files have changed in this diff Show More