mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Add unit tests for numerical habits
This commit is contained in:
@@ -50,6 +50,19 @@ class HabitFixtures(private val modelFactory: ModelFactory, private val habitLis
|
||||
return habit
|
||||
}
|
||||
|
||||
fun createEmptyNumericalHabit(targetType: NumericalHabitType): Habit {
|
||||
val habit = modelFactory.buildHabit()
|
||||
habit.type = HabitType.NUMERICAL
|
||||
habit.name = "Run"
|
||||
habit.question = "How many miles did you run today?"
|
||||
habit.unit = "miles"
|
||||
habit.targetType = targetType
|
||||
habit.targetValue = 2.0
|
||||
habit.color = PaletteColor(1)
|
||||
saveIfSQLite(habit)
|
||||
return habit
|
||||
}
|
||||
|
||||
fun createLongHabit(): Habit {
|
||||
val habit = createEmptyHabit()
|
||||
habit.frequency = Frequency(3, 7)
|
||||
|
||||
@@ -28,14 +28,36 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.ArrayList
|
||||
|
||||
class ScoreListTest : BaseUnitTest() {
|
||||
private lateinit var habit: Habit
|
||||
private lateinit var today: Timestamp
|
||||
open class BaseScoreListTest : BaseUnitTest() {
|
||||
protected lateinit var habit: Habit
|
||||
protected lateinit var today: Timestamp
|
||||
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
today = getToday()
|
||||
}
|
||||
|
||||
protected fun checkScoreValues(expectedValues: DoubleArray) {
|
||||
var current = today
|
||||
val scores = habit.scores
|
||||
for (expectedValue in expectedValues) {
|
||||
assertThat(scores[current].value, IsCloseTo.closeTo(expectedValue, E))
|
||||
current = current.minus(1)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val E = 1e-6
|
||||
}
|
||||
}
|
||||
|
||||
class YesNoScoreListTest : BaseScoreListTest() {
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
habit = fixtures.createEmptyHabit()
|
||||
}
|
||||
|
||||
@@ -122,18 +144,6 @@ class ScoreListTest : BaseUnitTest() {
|
||||
checkScoreValues(expectedValues)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_withZeroTarget() {
|
||||
habit = fixtures.createNumericalHabit()
|
||||
habit.targetValue = 0.0
|
||||
habit.recompute()
|
||||
assertTrue(habit.scores[today].value.isFinite())
|
||||
|
||||
habit.targetType = NumericalHabitType.AT_MOST
|
||||
habit.recompute()
|
||||
assertTrue(habit.scores[today].value.isFinite())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_imperfectNonDaily() {
|
||||
// If the habit should be performed 3 times per week and the user misses 1 repetition
|
||||
@@ -259,17 +269,204 @@ class ScoreListTest : BaseUnitTest() {
|
||||
val entries = habit.originalEntries
|
||||
entries.add(Entry(today.minus(day), Entry.SKIP))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkScoreValues(expectedValues: DoubleArray) {
|
||||
var current = today
|
||||
val scores = habit.scores
|
||||
for (expectedValue in expectedValues) {
|
||||
assertThat(scores[current].value, IsCloseTo.closeTo(expectedValue, E))
|
||||
current = current.minus(1)
|
||||
}
|
||||
open class NumericalScoreListTest : BaseScoreListTest() {
|
||||
protected fun addEntry(day: Int, value: Int) {
|
||||
val entries = habit.originalEntries
|
||||
entries.add(Entry(today.minus(day), value))
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val E = 1e-6
|
||||
protected fun addEntries(from: Int, to: Int, value: Int) {
|
||||
val entries = habit.originalEntries
|
||||
for (i in from until to) entries.add(Entry(today.minus(i), value))
|
||||
habit.recompute()
|
||||
}
|
||||
}
|
||||
|
||||
class NumericalAtLeastScoreListTest : NumericalScoreListTest() {
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
habit = fixtures.createEmptyNumericalHabit(NumericalHabitType.AT_LEAST)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_withZeroTarget() {
|
||||
habit = fixtures.createNumericalHabit()
|
||||
habit.targetValue = 0.0
|
||||
habit.recompute()
|
||||
assertTrue(habit.scores[today].value.isFinite())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_getValue() {
|
||||
addEntries(0, 20, 2000)
|
||||
val expectedValues = doubleArrayOf(
|
||||
0.655747,
|
||||
0.636894,
|
||||
0.617008,
|
||||
0.596033,
|
||||
0.573910,
|
||||
0.550574,
|
||||
0.525961,
|
||||
0.500000,
|
||||
0.472617,
|
||||
0.443734,
|
||||
0.413270,
|
||||
0.381137,
|
||||
0.347244,
|
||||
0.311495,
|
||||
0.273788,
|
||||
0.234017,
|
||||
0.192067,
|
||||
0.147820,
|
||||
0.101149,
|
||||
0.051922,
|
||||
0.000000,
|
||||
0.000000,
|
||||
0.000000
|
||||
)
|
||||
checkScoreValues(expectedValues)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_recompute() {
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.0, E))
|
||||
addEntries(0, 2, 2000)
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.101149, E))
|
||||
habit.frequency = Frequency(1, 2)
|
||||
habit.recompute()
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.072631, E))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldAchieveHighScoreInReasonableTime() {
|
||||
// Daily habits should achieve at least 99% in 3 months
|
||||
habit = fixtures.createEmptyNumericalHabit(NumericalHabitType.AT_LEAST)
|
||||
habit.frequency = Frequency.DAILY
|
||||
for (i in 0..89) addEntry(i, 2000)
|
||||
habit.recompute()
|
||||
assertThat(habit.scores[today].value, OrderingComparison.greaterThan(0.99))
|
||||
|
||||
// Weekly habits should achieve at least 99% in 9 months
|
||||
habit = fixtures.createEmptyNumericalHabit(NumericalHabitType.AT_LEAST)
|
||||
habit.frequency = Frequency.WEEKLY
|
||||
for (i in 0..38) addEntry(7 * i, 2000)
|
||||
habit.recompute()
|
||||
assertThat(habit.scores[today].value, OrderingComparison.greaterThan(0.99))
|
||||
|
||||
// Monthly habits should achieve at least 99% in 18 months
|
||||
habit.frequency = Frequency(1, 30)
|
||||
for (i in 0..17) addEntry(30 * i, 2000)
|
||||
habit.recompute()
|
||||
assertThat(habit.scores[today].value, OrderingComparison.greaterThan(0.99))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldAchieveComparableScoreToProgress() {
|
||||
addEntries(0, 500, 1000)
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.5, E))
|
||||
|
||||
addEntries(0, 500, 500)
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.25, E))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun overeachievingIsntRelevant() {
|
||||
addEntry(0, 10000000)
|
||||
habit.recompute()
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.051922, E))
|
||||
}
|
||||
}
|
||||
|
||||
class NumericalAtMostScoreListTest : NumericalScoreListTest() {
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
habit = fixtures.createEmptyNumericalHabit(NumericalHabitType.AT_MOST)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_withZeroTarget() {
|
||||
habit = fixtures.createNumericalHabit()
|
||||
habit.targetType = NumericalHabitType.AT_MOST
|
||||
habit.targetValue = 0.0
|
||||
habit.recompute()
|
||||
assertTrue(habit.scores[today].value.isFinite())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_getValue() {
|
||||
addEntry(20, 1000)
|
||||
addEntries(0, 20, 5000)
|
||||
val expectedValues = doubleArrayOf(
|
||||
0.344253,
|
||||
0.363106,
|
||||
0.382992,
|
||||
0.403967,
|
||||
0.426090,
|
||||
0.449426,
|
||||
0.474039,
|
||||
0.500000,
|
||||
0.527383,
|
||||
0.556266,
|
||||
0.586730,
|
||||
0.618863,
|
||||
0.652756,
|
||||
0.688505,
|
||||
0.726212,
|
||||
0.765983,
|
||||
0.807933,
|
||||
0.852180,
|
||||
0.898851,
|
||||
0.948078,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
checkScoreValues(expectedValues)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_recompute() {
|
||||
habit.recompute()
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(1.0, E))
|
||||
addEntries(0, 2, 5000)
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.898850, E))
|
||||
habit.frequency = Frequency(1, 2)
|
||||
habit.recompute()
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.927369, E))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldAchieveComparableScoreToProgress() {
|
||||
addEntries(0, 500, 3000)
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.5, E))
|
||||
|
||||
addEntries(0, 500, 3500)
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.25, E))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun undereachievingIsntRelevant() {
|
||||
addEntry(1, 10000000)
|
||||
habit.recompute()
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.950773, E))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun overeachievingIsntRelevant() {
|
||||
addEntry(0, 5000)
|
||||
|
||||
addEntry(1, 0)
|
||||
habit.recompute()
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.948077, E))
|
||||
|
||||
addEntry(1, 1000)
|
||||
habit.recompute()
|
||||
assertThat(habit.scores[today].value, IsCloseTo.closeTo(0.948077, E))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user