-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add test for adjacent activities calculation
- Loading branch information
Showing
3 changed files
with
202 additions
and
76 deletions.
There are no files selected for viewing
83 changes: 83 additions & 0 deletions
83
...example/util/simpletimetracker/domain/interactor/CalculateAdjacentActivitiesInteractor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package com.example.util.simpletimetracker.domain.interactor | ||
|
||
import com.example.util.simpletimetracker.domain.extension.orZero | ||
import com.example.util.simpletimetracker.domain.model.RecordBase | ||
import javax.inject.Inject | ||
|
||
class CalculateAdjacentActivitiesInteractor @Inject constructor() { | ||
|
||
// Doesn't count multitasked activities. | ||
// Only whose that started after current ended. | ||
fun calculateNextActivities( | ||
typeId: Long, | ||
records: List<RecordBase>, | ||
): List<CalculationResult> { | ||
val counts = mutableMapOf<Long, Long>() | ||
|
||
val recordsSorted = records.sortedBy { it.timeStarted } | ||
var currentRecord: RecordBase? = null | ||
recordsSorted.forEach { record -> | ||
val currentTimeEnded = currentRecord?.timeEnded | ||
if (currentTimeEnded != null && | ||
currentTimeEnded <= record.timeStarted | ||
) { | ||
record.typeIds.firstOrNull()?.let { id -> | ||
counts[id] = counts[id].orZero() + 1 | ||
} | ||
currentRecord = null | ||
} | ||
if (currentRecord == null && typeId in record.typeIds) { | ||
currentRecord = record | ||
} | ||
} | ||
|
||
return counts.keys | ||
.sortedByDescending { counts[it].orZero() } | ||
.take(MAX_COUNT) | ||
.map { CalculationResult(it, counts[it].orZero()) } | ||
} | ||
|
||
// TODO make more precise calculations? | ||
fun calculateMultitasking( | ||
typeId: Long, | ||
records: List<RecordBase>, | ||
): List<CalculationResult> { | ||
val counts = mutableMapOf<Long, Long>() | ||
|
||
val recordsSorted = records.sortedBy { it.timeStarted } | ||
var currentRecord: RecordBase? = null | ||
recordsSorted.forEach { record -> | ||
val currentTimeStarted = currentRecord?.timeStarted | ||
val currentTimeEnded = currentRecord?.timeEnded | ||
if (currentTimeStarted != null && | ||
currentTimeEnded != null && | ||
// Find next records that was started after this one but before this one ends. | ||
currentTimeStarted <= record.timeStarted && | ||
currentTimeEnded > record.timeStarted && | ||
// Cutoff short intersections. | ||
currentTimeEnded - record.timeStarted > 1_000L | ||
) { | ||
record.typeIds.firstOrNull()?.let { id -> | ||
counts[id] = counts[id].orZero() + 1 | ||
} | ||
} | ||
if (typeId in record.typeIds) { | ||
currentRecord = record | ||
} | ||
} | ||
|
||
return counts.keys | ||
.sortedByDescending { counts[it].orZero() } | ||
.take(MAX_COUNT) | ||
.map { CalculationResult(it, counts[it].orZero()) } | ||
} | ||
|
||
data class CalculationResult( | ||
val typeId: Long, | ||
val count: Long, | ||
) | ||
|
||
companion object { | ||
private const val MAX_COUNT = 5 | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
...example/util/simpletimetracker/domain/mapper/CalculateAdjacentActivitiesInteractorTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package com.example.util.simpletimetracker.domain.mapper | ||
|
||
import com.example.util.simpletimetracker.domain.interactor.CalculateAdjacentActivitiesInteractor | ||
import com.example.util.simpletimetracker.domain.interactor.CalculateAdjacentActivitiesInteractor.CalculationResult | ||
import com.example.util.simpletimetracker.domain.model.Record | ||
import com.example.util.simpletimetracker.domain.model.RecordBase | ||
import org.junit.Assert.assertEquals | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.junit.runners.Parameterized | ||
|
||
@RunWith(Parameterized::class) | ||
class CalculateAdjacentActivitiesInteractorTest( | ||
private val input: Pair<Long, List<RecordBase>>, | ||
private val output: List<CalculationResult>, | ||
) { | ||
|
||
private val subject = CalculateAdjacentActivitiesInteractor() | ||
|
||
@Suppress("UNCHECKED_CAST") | ||
@Test | ||
fun map() { | ||
val expected = output | ||
val actual = subject.calculateNextActivities( | ||
typeId = input.first, | ||
records = input.second, | ||
) | ||
|
||
assertEquals( | ||
"Test failed for params $input", | ||
expected, | ||
actual, | ||
) | ||
} | ||
|
||
companion object { | ||
private val record: Record = Record( | ||
id = 0L, | ||
typeId = 0L, | ||
timeStarted = 0L, | ||
timeEnded = 0L, | ||
comment = "", | ||
tagIds = emptyList(), | ||
) | ||
|
||
@JvmStatic | ||
@Parameterized.Parameters | ||
fun data() = listOf( | ||
// Empty. | ||
arrayOf( | ||
0L to emptyList<RecordBase>(), | ||
emptyList<CalculationResult>(), | ||
), | ||
arrayOf( | ||
0L to listOf(record), | ||
emptyList<CalculationResult>(), | ||
), | ||
arrayOf( | ||
0L to listOf( | ||
record.copy(typeId = 1), | ||
record.copy(typeId = 2), | ||
), | ||
emptyList<CalculationResult>(), | ||
), | ||
// Multitasked. | ||
arrayOf( | ||
0L to listOf( | ||
record.copy(typeId = 0, timeStarted = 1, timeEnded = 10), | ||
record.copy(typeId = 1, timeStarted = 2, timeEnded = 3), | ||
record.copy(typeId = 2, timeStarted = 4, timeEnded = 15), | ||
), | ||
emptyList<CalculationResult>(), | ||
), | ||
// Only before. | ||
arrayOf( | ||
0L to listOf( | ||
record.copy(typeId = 1, timeStarted = 1, timeEnded = 2), | ||
record.copy(typeId = 2, timeStarted = 2, timeEnded = 3), | ||
record.copy(typeId = 0, timeStarted = 3, timeEnded = 4), | ||
), | ||
emptyList<CalculationResult>(), | ||
), | ||
// One after. | ||
arrayOf( | ||
0L to listOf( | ||
record.copy(typeId = 0, timeStarted = 0, timeEnded = 1), | ||
record.copy(typeId = 1, timeStarted = 2, timeEnded = 3), | ||
), | ||
listOf( | ||
CalculationResult(1, 1), | ||
), | ||
), | ||
// Several. | ||
arrayOf( | ||
0L to listOf( | ||
record.copy(typeId = 1, timeStarted = 0, timeEnded = 1), | ||
record.copy(typeId = 0, timeStarted = 1, timeEnded = 2), | ||
record.copy(typeId = 1, timeStarted = 2, timeEnded = 3), | ||
record.copy(typeId = 2, timeStarted = 3, timeEnded = 4), | ||
record.copy(typeId = 0, timeStarted = 4, timeEnded = 5), | ||
record.copy(typeId = 2, timeStarted = 5, timeEnded = 6), | ||
record.copy(typeId = 0, timeStarted = 6, timeEnded = 7), | ||
record.copy(typeId = 1, timeStarted = 10, timeEnded = 11), | ||
), | ||
listOf( | ||
CalculationResult(1, 2), | ||
CalculationResult(2, 1), | ||
), | ||
), | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters