Merge branch 'dev' into feat-1074

pull/1103/head
Alinson S. Xavier 4 years ago
commit 4972257635
No known key found for this signature in database
GPG Key ID: DCA0DAD4D2F58624

@ -1,6 +1,6 @@
plugins {
val kotlinVersion = "1.5.0"
id("com.android.application") version ("7.0.2") apply (false)
id("com.android.application") version ("7.0.3") apply (false)
id("org.jetbrains.kotlin.android") version kotlinVersion apply (false)
id("org.jetbrains.kotlin.kapt") version kotlinVersion apply (false)
id("org.jetbrains.kotlin.android.extensions") version kotlinVersion apply (false)

@ -86,7 +86,7 @@ android {
}
dependencies {
val daggerVersion = "2.39"
val daggerVersion = "2.40"
val kotlinVersion = "1.5.31"
val kxCoroutinesVersion = "1.5.2"
val ktorVersion = "1.6.4"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -55,6 +55,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@LargeTest
class HabitsTest : BaseUserInterfaceTest() {
@Test
@Throws(Exception::class)
fun shouldCreateHabit() {
@ -180,6 +181,8 @@ class HabitsTest : BaseUserInterfaceTest() {
longPressCheckmarks("Wake up early", count = 2)
clickText("Wake up early")
verifyShowsScreen(SHOW_HABIT)
// TODO: find a better way than sleeping in tests
Thread.sleep(2001L)
verifyDisplaysText("10%")
}
@ -194,6 +197,8 @@ class HabitsTest : BaseUserInterfaceTest() {
verifyDoesNotDisplayText("Track time")
verifyDisplaysText("Wake up early")
longPressCheckmarks("Wake up early", count = 1)
// TODO: find a better way than sleeping in tests
Thread.sleep(2001L)
verifyDoesNotDisplayText("Wake up early")
clickMenu(TOGGLE_COMPLETED)
verifyDisplaysText("Track time")

@ -53,6 +53,7 @@ class ListHabitsActivity : AppCompatActivity(), Preferences.Listener {
override fun onQuestionMarksChanged() {
invalidateOptionsMenu()
menu.behavior.onPreferencesChanged()
}
override fun onCreate(savedInstanceState: Bundle?) {

@ -39,7 +39,7 @@ class ListHabitsMenu @Inject constructor(
@ActivityContext context: Context,
private val preferences: Preferences,
private val themeSwitcher: ThemeSwitcher,
private val behavior: ListHabitsMenuBehavior
val behavior: ListHabitsMenuBehavior
) {
val activity = (context as AppCompatActivity)

@ -37,9 +37,9 @@ 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.preferences.Preferences
import org.isoron.uhabits.inject.ActivityContext
import org.isoron.uhabits.utils.dim
import org.isoron.uhabits.utils.drawNotesIndicator
import org.isoron.uhabits.utils.getFontAwesome
import org.isoron.uhabits.utils.sp
import org.isoron.uhabits.utils.sres
import org.isoron.uhabits.utils.toMeasureSpec
import javax.inject.Inject
@ -154,6 +154,11 @@ class CheckmarkButtonView(
}
else -> R.string.fa_check
}
paint.textSize = when {
id == R.string.fa_question -> sp(12.0f)
value == YES_AUTO -> sp(13.0f)
else -> sp(14.0f)
}
if (value == YES_AUTO) {
paint.strokeWidth = 5f
paint.style = Paint.Style.STROKE
@ -162,11 +167,6 @@ class CheckmarkButtonView(
paint.style = Paint.Style.FILL
}
paint.textSize = when (id) {
UNKNOWN -> dim(R.dimen.smallerTextSize)
else -> dim(R.dimen.smallTextSize)
}
val label = resources.getString(id)
val em = paint.measureText("m")

@ -36,6 +36,7 @@ import android.widget.TextView
import org.isoron.platform.gui.toInt
import org.isoron.uhabits.R
import org.isoron.uhabits.activities.common.views.RingView
import org.isoron.uhabits.activities.habits.list.views.HabitCardView.Companion.delay
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.ModelObservable
import org.isoron.uhabits.core.models.Timestamp
@ -150,7 +151,11 @@ class HabitCardView(
checkmarkPanel = checkmarkPanelFactory.create().apply {
onToggle = { timestamp, value ->
triggerRipple(timestamp)
habit?.let { behavior.onToggle(it, timestamp, value) }
habit?.let {
{
behavior.onToggle(it, timestamp, value)
}.delay(TOGGLE_DELAY_MILLIS)
}
}
onEdit = { timestamp ->
triggerRipple(timestamp)
@ -274,4 +279,12 @@ class HabitCardView(
}
innerFrame.setBackgroundResource(background)
}
companion object {
const val TOGGLE_DELAY_MILLIS = 2000L
fun (() -> Unit).delay(delayInMillis: Long) {
Handler(Looper.getMainLooper()).postDelayed(this, delayInMillis)
}
}
}

@ -23,17 +23,17 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import org.isoron.uhabits.HabitsApplication
import org.isoron.uhabits.activities.AndroidThemeSwitcher
import org.isoron.uhabits.core.models.HabitMatcherBuilder
import org.isoron.uhabits.core.models.HabitMatcher
class EditSettingActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val app = applicationContext as HabitsApplication
val habits = app.component.habitList.getFiltered(
HabitMatcherBuilder()
.setArchivedAllowed(false)
.setCompletedAllowed(true)
.build()
HabitMatcher(
isArchivedAllowed = false,
isCompletedAllowed = true,
)
)
AndroidThemeSwitcher(this, app.component.preferences).apply()

@ -0,0 +1 @@
2015-01-25,0.0000
1 2015-01-25 0.0000

@ -0,0 +1,10 @@
2015-01-25,2
2015-01-24,0
2015-01-23,1
2015-01-22,2
2015-01-21,2
2015-01-20,2
2015-01-19,1
2015-01-18,1
2015-01-17,2
2015-01-16,2
1 2015-01-25 2
2 2015-01-24 0
3 2015-01-23 1
4 2015-01-22 2
5 2015-01-21 2
6 2015-01-20 2
7 2015-01-19 1
8 2015-01-18 1
9 2015-01-17 2
10 2015-01-16 2

@ -0,0 +1,10 @@
2015-01-25,0.2557
2015-01-24,0.2226
2015-01-23,0.1991
2015-01-22,0.1746
2015-01-21,0.1379
2015-01-20,0.0995
2015-01-19,0.0706
2015-01-18,0.0515
2015-01-17,0.0315
2015-01-16,0.0107
1 2015-01-25 0.2557
2 2015-01-24 0.2226
3 2015-01-23 0.1991
4 2015-01-22 0.1746
5 2015-01-21 0.1379
6 2015-01-20 0.0995
7 2015-01-19 0.0706
8 2015-01-18 0.0515
9 2015-01-17 0.0315
10 2015-01-16 0.0107

@ -0,0 +1,11 @@
Date,Meditate,Wake up early,
2015-01-25,-1,2,
2015-01-24,-1,0,
2015-01-23,-1,1,
2015-01-22,-1,2,
2015-01-21,-1,2,
2015-01-20,-1,2,
2015-01-19,-1,1,
2015-01-18,-1,1,
2015-01-17,-1,2,
2015-01-16,-1,2,
1 Date Meditate Wake up early
2 2015-01-25 -1 2
3 2015-01-24 -1 0
4 2015-01-23 -1 1
5 2015-01-22 -1 2
6 2015-01-21 -1 2
7 2015-01-20 -1 2
8 2015-01-19 -1 1
9 2015-01-18 -1 1
10 2015-01-17 -1 2
11 2015-01-16 -1 2

@ -0,0 +1,3 @@
Position,Name,Question,Description,NumRepetitions,Interval,Color
001,Meditate,Did you meditate this morning?,,1,1,#FF8F00
002,Wake up early,Did you wake up before 6am?,,2,3,#00897B
1 Position Name Question Description NumRepetitions Interval Color
2 001 Meditate Did you meditate this morning? 1 1 #FF8F00
3 002 Wake up early Did you wake up before 6am? 2 3 #00897B

@ -0,0 +1,11 @@
Date,Meditate,Wake up early,
2015-01-25,0.0000,0.2557,
2015-01-24,0.0000,0.2226,
2015-01-23,0.0000,0.1991,
2015-01-22,0.0000,0.1746,
2015-01-21,0.0000,0.1379,
2015-01-20,0.0000,0.0995,
2015-01-19,0.0000,0.0706,
2015-01-18,0.0000,0.0515,
2015-01-17,0.0000,0.0315,
2015-01-16,0.0000,0.0107,
1 Date Meditate Wake up early
2 2015-01-25 0.0000 0.2557
3 2015-01-24 0.0000 0.2226
4 2015-01-23 0.0000 0.1991
5 2015-01-22 0.0000 0.1746
6 2015-01-21 0.0000 0.1379
7 2015-01-20 0.0000 0.0995
8 2015-01-19 0.0000 0.0706
9 2015-01-18 0.0000 0.0515
10 2015-01-17 0.0000 0.0315
11 2015-01-16 0.0000 0.0107

@ -43,7 +43,7 @@ kotlin {
val jvmMain by getting {
dependencies {
implementation(kotlin("stdlib-jdk8"))
compileOnly("com.google.dagger:dagger:2.39")
compileOnly("com.google.dagger:dagger:2.40")
implementation("com.google.guava:guava:31.0.1-android")
implementation("org.jetbrains.kotlin:kotlin-stdlib:1.5.31")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2")

@ -167,7 +167,7 @@ class HabitsCSVExporter(
checksWriter.write(sb.toString())
scoresWriter.write(sb.toString())
for (j in selectedHabits.indices) {
checksWriter.write(checkmarks[j][i].toString())
checksWriter.write(checkmarks[j][i].value.toString())
checksWriter.write(delimiter)
val score = String.format(Locale.US, "%.4f", scores[j][i].value)
scoresWriter.write(score)

@ -68,17 +68,10 @@ data class Habit(
}
}
fun isFailedToday(): Boolean {
fun isEnteredToday(): Boolean {
val today = DateUtils.getTodayWithOffset()
val value = computedEntries.get(today).value
return if (isNumerical) {
when (targetType) {
NumericalHabitType.AT_LEAST -> value / 1000.0 < targetValue
NumericalHabitType.AT_MOST -> value / 1000.0 > targetValue
}
} else {
value == Entry.NO
}
return value != Entry.UNKNOWN
}
fun recompute() {

@ -43,7 +43,7 @@ abstract class HabitList : Iterable<Habit> {
*/
constructor() {
observable = ModelObservable()
filter = HabitMatcherBuilder().setArchivedAllowed(true).build()
filter = HabitMatcher(isArchivedAllowed = true)
}
protected constructor(filter: HabitMatcher) {

@ -22,19 +22,21 @@ data class HabitMatcher(
val isArchivedAllowed: Boolean = false,
val isReminderRequired: Boolean = false,
val isCompletedAllowed: Boolean = true,
val isEnteredAllowed: Boolean = true,
) {
fun matches(habit: Habit): Boolean {
if (!isArchivedAllowed && habit.isArchived) return false
if (isReminderRequired && !habit.hasReminder()) return false
if (!isCompletedAllowed && (habit.isCompletedToday() || habit.isFailedToday())) return false
if (!isCompletedAllowed && habit.isCompletedToday()) return false
if (!isEnteredAllowed && habit.isEnteredToday()) return false
return true
}
companion object {
@JvmField
val WITH_ALARM = HabitMatcherBuilder()
.setArchivedAllowed(true)
.setReminderRequired(true)
.build()
val WITH_ALARM = HabitMatcher(
isArchivedAllowed = true,
isReminderRequired = true,
)
}
}

@ -1,48 +0,0 @@
/*
* Copyright (C) 2016-2021 Álinson Santos Xavier <git@axavier.org>
*
* 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.core.models
class HabitMatcherBuilder {
private var archivedAllowed = false
private var reminderRequired = false
private var completedAllowed = true
fun build(): HabitMatcher {
return HabitMatcher(
isArchivedAllowed = archivedAllowed,
isReminderRequired = reminderRequired,
isCompletedAllowed = completedAllowed,
)
}
fun setArchivedAllowed(archivedAllowed: Boolean): HabitMatcherBuilder {
this.archivedAllowed = archivedAllowed
return this
}
fun setCompletedAllowed(completedAllowed: Boolean): HabitMatcherBuilder {
this.completedAllowed = completedAllowed
return this
}
fun setReminderRequired(reminderRequired: Boolean): HabitMatcherBuilder {
this.reminderRequired = reminderRequired
return this
}
}

@ -20,7 +20,6 @@ package org.isoron.uhabits.core.ui.screens.habits.list
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.HabitMatcher
import org.isoron.uhabits.core.models.HabitMatcherBuilder
import org.isoron.uhabits.core.preferences.Preferences
import org.isoron.uhabits.core.ui.ThemeSwitcher
import javax.inject.Inject
@ -33,6 +32,7 @@ class ListHabitsMenuBehavior @Inject constructor(
) {
private var showCompleted: Boolean
private var showArchived: Boolean
fun onCreateHabit() {
screen.showSelectHabitTypeDialog()
}
@ -97,13 +97,26 @@ class ListHabitsMenuBehavior @Inject constructor(
screen.applyTheme()
}
fun onPreferencesChanged() {
updateAdapterFilter()
}
private fun updateAdapterFilter() {
adapter.setFilter(
HabitMatcherBuilder()
.setArchivedAllowed(showArchived)
.setCompletedAllowed(showCompleted)
.build()
)
if (preferences.areQuestionMarksEnabled) {
adapter.setFilter(
HabitMatcher(
isArchivedAllowed = showArchived,
isEnteredAllowed = showCompleted,
)
)
} else {
adapter.setFilter(
HabitMatcher(
isArchivedAllowed = showArchived,
isCompletedAllowed = showCompleted,
)
)
}
adapter.refresh()
}

@ -77,11 +77,11 @@ abstract class DateUtils {
}
@JvmStatic
fun getLocalTime(): Long {
fun getLocalTime(utcTimeInMillis: Long? = null): Long {
if (fixedLocalTime != null) return fixedLocalTime as Long
val tz = getTimeZone()
val now = Date().time
val now = utcTimeInMillis ?: Date().time
return now + tz.getOffset(now)
}
@ -100,7 +100,7 @@ abstract class DateUtils {
format: Int,
firstWeekDay: Int
): Array<String> {
val calendar = GregorianCalendar()
val calendar = GregorianCalendar(getLocale())
calendar.set(DAY_OF_WEEK, firstWeekDay)
val daysNullable = ArrayList<String>()
@ -149,7 +149,7 @@ abstract class DateUtils {
*/
@JvmStatic
fun getFirstWeekdayNumberAccordingToLocale(): Int {
return GregorianCalendar().firstDayOfWeek
return GregorianCalendar(getLocale()).firstDayOfWeek
}
/**
@ -214,13 +214,7 @@ abstract class DateUtils {
@JvmStatic
fun getStartOfTodayCalendarWithOffset(): GregorianCalendar = getCalendar(getStartOfTodayWithOffset())
@JvmStatic
fun getTimeZone(): TimeZone {
return fixedTimeZone ?: TimeZone.getDefault()
}
@JvmStatic
fun getTimezone(): TimeZone {
private fun getTimeZone(): TimeZone {
return fixedTimeZone ?: TimeZone.getDefault()
}
@ -236,8 +230,7 @@ abstract class DateUtils {
startDayMinuteOffset = minuteOffset
}
@JvmStatic
fun getLocale(): Locale {
private fun getLocale(): Locale {
return fixedLocale ?: Locale.getDefault()
}

@ -34,6 +34,7 @@ import java.util.zip.ZipFile
class HabitsCSVExporterTest : BaseUnitTest() {
private lateinit var baseDir: File
@Before
@Throws(Exception::class)
override fun setUp() {
@ -41,12 +42,7 @@ class HabitsCSVExporterTest : BaseUnitTest() {
habitList.add(fixtures.createShortHabit())
habitList.add(fixtures.createEmptyHabit())
baseDir = Files.createTempDirectory("csv").toFile()
}
@Throws(Exception::class)
override fun tearDown() {
FileUtils.deleteDirectory(baseDir)
super.tearDown()
baseDir.deleteOnExit()
}
@Test
@ -63,14 +59,20 @@ class HabitsCSVExporterTest : BaseUnitTest() {
assertAbsolutePathExists(filename)
val archive = File(filename)
unzip(archive)
assertPathExists("Habits.csv")
assertPathExists("001 Meditate/Checkmarks.csv")
assertPathExists("001 Meditate/Scores.csv")
assertPathExists("002 Wake up early")
assertPathExists("002 Wake up early/Checkmarks.csv")
assertPathExists("002 Wake up early/Scores.csv")
assertPathExists("Checkmarks.csv")
assertPathExists("Scores.csv")
val filesToCheck = arrayOf(
"001 Meditate/Checkmarks.csv",
"001 Meditate/Scores.csv",
"002 Wake up early/Checkmarks.csv",
"002 Wake up early/Scores.csv",
"Checkmarks.csv",
"Habits.csv",
"Scores.csv"
)
for (file in filesToCheck) {
assertPathExists(file)
assertFileAndReferenceAreEqual(file)
}
}
@Throws(IOException::class)
@ -104,4 +106,18 @@ class HabitsCSVExporterTest : BaseUnitTest() {
file.exists()
)
}
private fun assertFileAndReferenceAreEqual(s: String) {
val assetFilename = String.format("csv_export/%s", s)
val file = File.createTempFile("asset", "")
file.deleteOnExit()
copyAssetToFile(assetFilename, file)
assertTrue(
FileUtils.contentEquals(
file,
File(String.format("%s/%s", baseDir.absolutePath, s))
)
)
}
}

@ -53,12 +53,12 @@ class HabitListTest : BaseUnitTest() {
habitsArray[1].isArchived = true
habitsArray[4].isArchived = true
habitsArray[7].isArchived = true
activeHabits = habitList.getFiltered(HabitMatcherBuilder().build())
activeHabits = habitList.getFiltered(HabitMatcher())
reminderHabits = habitList.getFiltered(
HabitMatcherBuilder()
.setArchivedAllowed(true)
.setReminderRequired(true)
.build()
HabitMatcher(
isArchivedAllowed = true,
isReminderRequired = true,
)
)
}
@ -181,10 +181,10 @@ class HabitListTest : BaseUnitTest() {
fun testOrder_inherit() {
habitList.primaryOrder = HabitList.Order.BY_COLOR_ASC
val filteredList = habitList.getFiltered(
HabitMatcherBuilder()
.setArchivedAllowed(false)
.setCompletedAllowed(false)
.build()
HabitMatcher(
isArchivedAllowed = false,
isCompletedAllowed = false,
)
)
assertEquals(filteredList.primaryOrder, HabitList.Order.BY_COLOR_ASC)
}

@ -82,12 +82,12 @@ class HabitTest : BaseUnitTest() {
@Test
@Throws(Exception::class)
fun test_isFailed() {
fun test_isEntered() {
val h = modelFactory.buildHabit()
assertFalse(h.isFailedToday())
assertFalse(h.isEnteredToday())
h.originalEntries.add(Entry(getToday(), Entry.NO))
h.recompute()
assertTrue(h.isFailedToday())
assertTrue(h.isEnteredToday())
}
@Test
@ -119,35 +119,6 @@ class HabitTest : BaseUnitTest() {
assertTrue(h.isCompletedToday())
}
@Test
@Throws(Exception::class)
fun test_isFailedNumerical() {
val h = modelFactory.buildHabit()
h.type = HabitType.NUMERICAL
h.targetType = NumericalHabitType.AT_LEAST
h.targetValue = 100.0
assertTrue(h.isFailedToday())
h.originalEntries.add(Entry(getToday(), 200000))
h.recompute()
assertFalse(h.isFailedToday())
h.originalEntries.add(Entry(getToday(), 100000))
h.recompute()
assertFalse(h.isFailedToday())
h.originalEntries.add(Entry(getToday(), 50000))
h.recompute()
assertTrue(h.isFailedToday())
h.targetType = NumericalHabitType.AT_MOST
h.originalEntries.add(Entry(getToday(), 200000))
h.recompute()
assertTrue(h.isFailedToday())
h.originalEntries.add(Entry(getToday(), 100000))
h.recompute()
assertFalse(h.isFailedToday())
h.originalEntries.add(Entry(getToday(), 50000))
h.recompute()
assertFalse(h.isFailedToday())
}
@Test
@Throws(Exception::class)
fun testURI() {

@ -28,7 +28,7 @@ import org.isoron.uhabits.core.database.Database
import org.isoron.uhabits.core.database.Repository
import org.isoron.uhabits.core.models.Habit
import org.isoron.uhabits.core.models.HabitList
import org.isoron.uhabits.core.models.HabitMatcherBuilder
import org.isoron.uhabits.core.models.HabitMatcher
import org.isoron.uhabits.core.models.ModelObservable
import org.isoron.uhabits.core.models.Reminder
import org.isoron.uhabits.core.models.WeekdayList
@ -69,12 +69,12 @@ class SQLiteHabitListTest : BaseUnitTest() {
habitsArray[4].isArchived = true
habitsArray[7].isArchived = true
habitList.update(habitsArray)
activeHabits = habitList.getFiltered(HabitMatcherBuilder().build())
activeHabits = habitList.getFiltered(HabitMatcher())
reminderHabits = habitList.getFiltered(
HabitMatcherBuilder()
.setArchivedAllowed(true)
.setReminderRequired(true)
.build()
HabitMatcher(
isArchivedAllowed = true,
isReminderRequired = true,
)
)
habitList.observable.addListener(listener)
}

@ -37,6 +37,7 @@ import org.isoron.uhabits.core.utils.DateUtils.Companion.truncate
import org.junit.Before
import org.junit.Test
import java.util.Calendar
import java.util.GregorianCalendar
import java.util.Locale
import java.util.TimeZone
@ -58,6 +59,129 @@ class DateUtilsTest : BaseUnitTest() {
assertThat(formatted, equalTo("Thu\n31"))
}
@Test
fun testGetLocalTime() {
setFixedLocalTime(null)
setFixedTimeZone(TimeZone.getTimeZone("Australia/Sydney"))
val utcTestTimeInMillis = unixTime(2015, Calendar.JANUARY, 11)
val localTimeInMillis = DateUtils.getLocalTime(utcTestTimeInMillis)
val expectedUnixTimeOffsetForSydney = 11 * 60 * 60 * 1000
val expectedUnixTimeForSydney = utcTestTimeInMillis + expectedUnixTimeOffsetForSydney
assertThat(expectedUnixTimeForSydney, equalTo(localTimeInMillis))
}
@Test
fun testGetWeekdaySequence() {
val weekdaySequence = DateUtils.getWeekdaySequence(3)
assertThat(arrayOf(3, 4, 5, 6, 7, 1, 2), equalTo(weekdaySequence))
}
@Test
fun testGetFirstWeekdayNumberAccordingToLocale_germany() {
setFixedLocale(Locale.GERMANY)
val firstWeekdayNumber = DateUtils.getFirstWeekdayNumberAccordingToLocale()
assertThat(2, equalTo(firstWeekdayNumber))
}
@Test
fun testGetFirstWeekdayNumberAccordingToLocale_us() {
setFixedLocale(Locale.US)
val firstWeekdayNumber = DateUtils.getFirstWeekdayNumberAccordingToLocale()
assertThat(1, equalTo(firstWeekdayNumber))
}
@Test
fun testGetLongWeekdayNames_germany() {
setFixedLocale(Locale.GERMANY)
val longWeekdayNames = DateUtils.getLongWeekdayNames(Calendar.SATURDAY)
assertThat(arrayOf("Samstag", "Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag"), equalTo(longWeekdayNames))
}
@Test
fun testGetLongWeekdayNames_us() {
setFixedLocale(Locale.US)
val longWeekdayNames = DateUtils.getLongWeekdayNames(Calendar.SATURDAY)
assertThat(arrayOf("Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"), equalTo(longWeekdayNames))
}
@Test
fun testGetShortWeekdayNames_germany() {
setFixedLocale(Locale.GERMANY)
val longWeekdayNames = DateUtils.getShortWeekdayNames(Calendar.SATURDAY)
assertThat(arrayOf("Sa.", "So.", "Mo.", "Di.", "Mi.", "Do.", "Fr."), equalTo(longWeekdayNames))
}
@Test
fun testGetShortWeekdayNames_us() {
setFixedLocale(Locale.US)
val longWeekdayNames = DateUtils.getShortWeekdayNames(Calendar.SATURDAY)
assertThat(arrayOf("Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"), equalTo(longWeekdayNames))
}
@Test
fun testGetToday() {
setFixedLocalTime(FIXED_LOCAL_TIME)
val today = DateUtils.getToday()
assertThat(Timestamp(FIXED_LOCAL_TIME), equalTo(today))
}
@Test
fun testGetStartOfDay() {
val expectedStartOfDayUtc = fixedStartOfToday()
val laterInTheDayUtc = fixedStartOfTodayWithOffset(20)
val startOfDay = DateUtils.getStartOfDay(laterInTheDayUtc)
assertThat(expectedStartOfDayUtc, equalTo(startOfDay))
}
@Test
fun testGetStartOfToday() {
val expectedStartOfDayUtc = fixedStartOfToday()
val laterInTheDayUtc = fixedStartOfTodayWithOffset(20)
setFixedLocalTime(laterInTheDayUtc)
val startOfToday = DateUtils.getStartOfToday()
assertThat(expectedStartOfDayUtc, equalTo(startOfToday))
}
@Test
fun testGetStartOfTomorrowWithOffset_priorToOffset() {
val priorToOffset = HOUR_OFFSET - 1
testGetStartOfTomorrowWithOffset(priorToOffset)
}
@Test
fun testGetStartOfTomorrowWithOffset_afterOffset() {
val afterOffset = HOUR_OFFSET + 1 - HOURS_IN_ONE_DAY
testGetStartOfTomorrowWithOffset(afterOffset)
}
private fun testGetStartOfTomorrowWithOffset(startOfTodayOffset: Int) {
configureOffsetTest(startOfTodayOffset)
assertThat(
fixedStartOfTodayWithOffset(HOUR_OFFSET),
equalTo(DateUtils.getStartOfTomorrowWithOffset())
)
}
@Test
fun testGetStartOfTodayWithOffset_priorToOffset() {
val priorToOffset = HOURS_IN_ONE_DAY + HOUR_OFFSET - 1
testGetStartOfTodayWithOffset(priorToOffset)
}
@Test
fun testGetStartOfTodayWithOffset_afterOffset() {
val afterOffset = HOUR_OFFSET + 1
testGetStartOfTodayWithOffset(afterOffset)
}
private fun testGetStartOfTodayWithOffset(startOfTodayOffset: Int) {
configureOffsetTest(startOfTodayOffset)
assertThat(
fixedStartOfToday(),
equalTo(DateUtils.getStartOfTodayWithOffset())
)
}
@Test
fun testTruncate_dayOfWeek() {
val field = DateUtils.TruncateField.WEEK_NUMBER
@ -142,22 +266,39 @@ class DateUtilsTest : BaseUnitTest() {
assertThat(truncate(field, t2, firstWeekday), equalTo(expected))
}
@Test
fun testTruncate_timestamp() {
val field = DateUtils.TruncateField.YEAR
val nonTruncatedDate = unixTime(2016, Calendar.MAY, 30)
val expected = Timestamp(unixTime(2016, Calendar.JANUARY, 1))
assertThat(expected, equalTo(truncate(field, Timestamp(nonTruncatedDate), firstWeekday)))
}
@Test
fun testGetUpcomingTimeInMillis() {
setFixedLocalTime(FIXED_LOCAL_TIME)
setFixedTimeZone(TimeZone.getTimeZone("GMT"))
val expected = unixTime(2015, Calendar.JANUARY, 25, 10, 1)
val upcomingTimeMillis = DateUtils.getUpcomingTimeInMillis(10, 1)
assertThat(expected, equalTo(upcomingTimeMillis))
}
@Test
@Throws(Exception::class)
fun testMillisecondsUntilTomorrow() {
setFixedTimeZone(TimeZone.getTimeZone("GMT"))
setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59))
assertThat(millisecondsUntilTomorrowWithOffset(), equalTo(DateUtils.MINUTE_LENGTH))
setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 20, 0))
setFixedLocalTime(fixedStartOfTodayWithOffset(20))
assertThat(
millisecondsUntilTomorrowWithOffset(),
equalTo(4 * DateUtils.HOUR_LENGTH)
)
setStartDayOffset(3, 30)
setStartDayOffset(HOUR_OFFSET, 30)
setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 1, 23, 59))
assertThat(
millisecondsUntilTomorrowWithOffset(),
equalTo(3 * DateUtils.HOUR_LENGTH + 31 * DateUtils.MINUTE_LENGTH)
equalTo(HOUR_OFFSET * DateUtils.HOUR_LENGTH + 31 * DateUtils.MINUTE_LENGTH)
)
setFixedLocalTime(unixTime(2017, Calendar.JANUARY, 2, 1, 0))
assertThat(
@ -166,6 +307,52 @@ class DateUtilsTest : BaseUnitTest() {
)
}
@Test
fun testGetStartOfTodayCalendar() {
setFixedLocalTime(FIXED_LOCAL_TIME)
setFixedLocale(Locale.GERMANY)
val expectedStartOfDay = unixTime(2015, Calendar.JANUARY, 25, 0, 0)
val expectedCalendar = GregorianCalendar(TimeZone.getTimeZone("GMT"), Locale.GERMANY)
expectedCalendar.timeInMillis = expectedStartOfDay
val startOfTodayCalendar = DateUtils.getStartOfTodayCalendar()
assertThat(expectedCalendar, equalTo(startOfTodayCalendar))
}
@Test
fun testGetStartOfTodayCalendarWithOffset_priorToOffset() {
val priorToOffset = HOUR_OFFSET - 1
testGetStartOfTodayCalendarWithOffset(priorToOffset)
}
@Test
fun testGetStartOfTodayCalendarWithOffset_afterOffset() {
val afterOffset = HOUR_OFFSET + 1
testGetStartOfTodayCalendarWithOffset(afterOffset)
}
private fun testGetStartOfTodayCalendarWithOffset(startOfTodayOffset: Int) {
configureOffsetTest(startOfTodayOffset)
setFixedLocale(Locale.GERMANY)
val expectedCalendar = GregorianCalendar(TimeZone.getTimeZone("GMT"), Locale.GERMANY)
expectedCalendar.timeInMillis = fixedStartOfToday()
assertThat(
expectedCalendar,
equalTo(DateUtils.getStartOfTodayCalendar())
)
}
private fun configureOffsetTest(startOfTodayOffset: Int) {
setStartDayOffset(HOUR_OFFSET, 0)
setFixedTimeZone(TimeZone.getTimeZone("GMT"))
setFixedLocalTime(fixedStartOfTodayWithOffset(startOfTodayOffset))
}
private fun fixedStartOfToday() = fixedStartOfTodayWithOffset(0)
private fun fixedStartOfTodayWithOffset(hourOffset: Int): Long {
return unixTime(2017, Calendar.JANUARY, 1, hourOffset, 0)
}
@Test
@Throws(Exception::class)
fun testGetTodayWithOffset() {
@ -469,4 +656,9 @@ class DateUtilsTest : BaseUnitTest() {
unixTime(2018, Calendar.APRIL, 1, 18, 0)
)
}
companion object {
const val HOUR_OFFSET = 3
const val HOURS_IN_ONE_DAY = 24
}
}

@ -0,0 +1,17 @@
package org.isoron.uhabits.core.utils
import org.isoron.uhabits.core.BaseUnitTest
import org.junit.Test
import java.io.File
import kotlin.test.assertTrue
class FileExtensionsTest : BaseUnitTest() {
@Test
fun testIsSQLite3File() {
val file = File.createTempFile("asset", "")
copyAssetToFile("loop.db", file)
val isSqlite3File = file.isSQLite3File()
assertTrue(isSqlite3File)
}
}

@ -1,19 +0,0 @@
HabitName,HabitDescription,HabitCategory,CalendarDate,Value,CommentText
Breed dragons,with love and fire,Diet & Food,2016-03-18,1,
Breed dragons,with love and fire,Diet & Food,2016-03-19,1,
Breed dragons,with love and fire,Diet & Food,2016-03-21,1,
Reduce sleep,only 2 hours per day,Time Management,2016-03-15,1,
Reduce sleep,only 2 hours per day,Time Management,2016-03-16,1,
Reduce sleep,only 2 hours per day,Time Management,2016-03-17,1,
Reduce sleep,only 2 hours per day,Time Management,2016-03-21,1,
No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1,
No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1,
No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1,
No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1,
No-arms pushup,Become like water my friend!,Fitness,2016-03-15,1,
No-arms pushup,Become like water my friend!,Fitness,2016-03-16,1,
No-arms pushup,Become like water my friend!,Fitness,2016-03-18,1,
No-arms pushup,Become like water my friend!,Fitness,2016-03-21,1,
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-15,1,
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-17,1,
Grow spiritually,"transcend ego, practice compassion, smile and breath",Meditation,2016-03-21,1,
1 HabitName HabitDescription HabitCategory CalendarDate Value CommentText
2 Breed dragons with love and fire Diet & Food 2016-03-18 1
3 Breed dragons with love and fire Diet & Food 2016-03-19 1
4 Breed dragons with love and fire Diet & Food 2016-03-21 1
5 Reduce sleep only 2 hours per day Time Management 2016-03-15 1
6 Reduce sleep only 2 hours per day Time Management 2016-03-16 1
7 Reduce sleep only 2 hours per day Time Management 2016-03-17 1
8 Reduce sleep only 2 hours per day Time Management 2016-03-21 1
9 No-arms pushup Become like water my friend! Fitness 2016-03-15 1
10 No-arms pushup Become like water my friend! Fitness 2016-03-16 1
11 No-arms pushup Become like water my friend! Fitness 2016-03-18 1
12 No-arms pushup Become like water my friend! Fitness 2016-03-21 1
13 No-arms pushup Become like water my friend! Fitness 2016-03-15 1
14 No-arms pushup Become like water my friend! Fitness 2016-03-16 1
15 No-arms pushup Become like water my friend! Fitness 2016-03-18 1
16 No-arms pushup Become like water my friend! Fitness 2016-03-21 1
17 Grow spiritually transcend ego, practice compassion, smile and breath Meditation 2016-03-15 1
18 Grow spiritually transcend ego, practice compassion, smile and breath Meditation 2016-03-17 1
19 Grow spiritually transcend ego, practice compassion, smile and breath Meditation 2016-03-21 1

@ -22,7 +22,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
application
id("kotlin")
id("com.github.johnrengelman.shadow") version "7.0.0"
id("com.github.johnrengelman.shadow") version "7.1.0"
}

Loading…
Cancel
Save