Merge branch 'development' into sessionscreen_ui

This commit is contained in:
rdyselin 2023-04-18 15:41:04 +02:00 committed by GitHub Enterprise
commit 9d2c53d4e6
26 changed files with 477 additions and 79 deletions

View file

@ -26,6 +26,7 @@ import be.ugent.sel.studeez.screens.profile.ProfileScreen
import be.ugent.sel.studeez.screens.sign_up.SignUpScreen import be.ugent.sel.studeez.screens.sign_up.SignUpScreen
import be.ugent.sel.studeez.screens.splash.SplashScreen import be.ugent.sel.studeez.screens.splash.SplashScreen
import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewScreen import be.ugent.sel.studeez.screens.timer_overview.TimerOverviewScreen
import be.ugent.sel.studeez.screens.timer_selection.TimerSelectionScreen
import be.ugent.sel.studeez.ui.theme.StudeezTheme import be.ugent.sel.studeez.ui.theme.StudeezTheme
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -130,4 +131,8 @@ fun NavGraphBuilder.studeezGraph(appState: StudeezAppstate) {
composable(StudeezDestinations.EDIT_PROFILE_SCREEN) { composable(StudeezDestinations.EDIT_PROFILE_SCREEN) {
EditProfileScreen(goBack, openAndPopUp) EditProfileScreen(goBack, openAndPopUp)
} }
composable(StudeezDestinations.TIMER_SELECTION_SCREEN) {
TimerSelectionScreen(open, openAndPopUp)
}
} }

View file

@ -0,0 +1,14 @@
package be.ugent.sel.studeez.data
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
import javax.inject.Inject
import javax.inject.Singleton
/**
* Used to communicate the SelectedTimer from the selection screen to the session screen.
* Because this is a singleton-class the view-models of both screens observe the same data.
*/
@Singleton
class SelectedTimerState @Inject constructor(){
var selectedTimer: FunctionalTimer? = null
}

View file

@ -1,3 +1,3 @@
package be.ugent.sel.studeez.data.local.models package be.ugent.sel.studeez.data.local.models
data class User(val id: String = "", val isAnonymous: Boolean = true) data class User(val id: String = "")

View file

@ -3,14 +3,14 @@ package be.ugent.sel.studeez.data.local.models.timer_functional
class FunctionalCustomTimer(studyTime: Int) : FunctionalTimer(studyTime) { class FunctionalCustomTimer(studyTime: Int) : FunctionalTimer(studyTime) {
override fun tick() { override fun tick() {
if (time.getTime() == 0) { if (time.time == 0) {
view = "Done!" view = StudyState.DONE
} else { } else {
time.minOne() time.minOne()
} }
} }
override fun hasEnded(): Boolean { override fun hasEnded(): Boolean {
return time.getTime() == 0 return time.time == 0
} }
} }

View file

@ -5,23 +5,23 @@ class FunctionalPomodoroTimer(
private var breakTime: Int, repeats: Int private var breakTime: Int, repeats: Int
) : FunctionalTimer(studyTime) { ) : FunctionalTimer(studyTime) {
private var breaksRemaining = repeats var breaksRemaining = repeats
private var isInBreak = false var isInBreak = false
override fun tick() { override fun tick() {
if (time.getTime() == 0 && breaksRemaining == 0){ if (time.time == 0 && breaksRemaining == 0) {
view = "Done!" view = StudyState.DONE
return return
} }
if (time.getTime() == 0) { if (time.time == 0) {
if (isInBreak) { if (isInBreak) {
breaksRemaining-- breaksRemaining--
view = "Focus! ($breaksRemaining breaks remaining)" view = StudyState.FOCUS_REMAINING
time.setTime(studyTime) time.time = studyTime
} else { } else {
view = "Take a break!" view = StudyState.BREAK
time.setTime(breakTime) time.time = breakTime
} }
isInBreak = !isInBreak isInBreak = !isInBreak
} }
@ -29,6 +29,6 @@ class FunctionalPomodoroTimer(
} }
override fun hasEnded(): Boolean { override fun hasEnded(): Boolean {
return breaksRemaining == 0 && time.getTime() == 0 return breaksRemaining == 0 && time.time == 0
} }
} }

View file

@ -1,17 +1,13 @@
package be.ugent.sel.studeez.data.local.models.timer_functional package be.ugent.sel.studeez.data.local.models.timer_functional
abstract class FunctionalTimer(initialValue: Int) { abstract class FunctionalTimer(initialValue: Int) {
protected val time: Time = Time(initialValue) val time: Time = Time(initialValue)
protected var view: String = "Focus" var view: StudyState = StudyState.FOCUS
fun getHoursMinutesSeconds(): HoursMinutesSeconds { fun getHoursMinutesSeconds(): HoursMinutesSeconds {
return time.getAsHMS() return time.getAsHMS()
} }
fun getViewString(): String {
return view
}
abstract fun tick() abstract fun tick()
abstract fun hasEnded(): Boolean abstract fun hasEnded(): Boolean
@ -19,4 +15,9 @@ abstract class FunctionalTimer(initialValue: Int) {
fun hasCurrentCountdownEnded(): Boolean { fun hasCurrentCountdownEnded(): Boolean {
return time.getTime() == 0 return time.getTime() == 0
} }
enum class StudyState {
FOCUS, DONE, BREAK, FOCUS_REMAINING
}
} }

View file

@ -2,7 +2,7 @@ package be.ugent.sel.studeez.data.local.models.timer_functional
class Time(initialTime: Int) { class Time(initialTime: Int) {
private var time = initialTime var time = initialTime
fun minOne() { fun minOne() {
time-- time--
@ -12,14 +12,6 @@ class Time(initialTime: Int) {
time++ time++
} }
fun setTime(newTime: Int) {
time = newTime
}
fun getTime(): Int {
return time
}
fun getAsHMS(): HoursMinutesSeconds { fun getAsHMS(): HoursMinutesSeconds {
val hours: Int = time / (60 * 60) val hours: Int = time / (60 * 60)
val minutes: Int = (time / (60)) % 60 val minutes: Int = (time / (60)) % 60

View file

@ -3,7 +3,7 @@ package be.ugent.sel.studeez.data.local.models.timer_info
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
class BreakTimerInfo( class PomodoroTimerInfo(
name: String, name: String,
description: String, description: String,
private val studyTime: Int, private val studyTime: Int,

View file

@ -39,7 +39,7 @@ class FirebaseAccountDAO @Inject constructor(
get() = callbackFlow { get() = callbackFlow {
val listener = val listener =
FirebaseAuth.AuthStateListener { auth -> FirebaseAuth.AuthStateListener { auth ->
this.trySend(auth.currentUser?.let { User(it.uid, it.isAnonymous) } ?: User()) this.trySend(auth.currentUser?.let { User(it.uid) } ?: User())
} }
auth.addAuthStateListener(listener) auth.addAuthStateListener(listener)
awaitClose { auth.removeAuthStateListener(listener) } awaitClose { auth.removeAuthStateListener(listener) }

View file

@ -30,7 +30,7 @@ class ToTimerConverter {
it.studyTime, it.studyTime,
it.id it.id
) }, ) },
TimerType.BREAK to TimerFactory { BreakTimerInfo( TimerType.BREAK to TimerFactory { PomodoroTimerInfo(
it.name, it.name,
it.description, it.description,
it.studyTime, it.studyTime,

View file

@ -7,6 +7,7 @@ object StudeezDestinations {
const val HOME_SCREEN = "home" const val HOME_SCREEN = "home"
const val TIMER_OVERVIEW_SCREEN = "timer_overview" const val TIMER_OVERVIEW_SCREEN = "timer_overview"
const val TIMER_SELECTION_SCREEN = "timer_selection"
const val SESSION_SCREEN = "session" const val SESSION_SCREEN = "session"
// const val TASKS_SCREEN = "tasks" // const val TASKS_SCREEN = "tasks"
// const val SESSIONS_SCREEN = "sessions" // const val SESSIONS_SCREEN = "sessions"

View file

@ -15,14 +15,7 @@ class HomeViewModel @Inject constructor(
logService: LogService logService: LogService
) : StudeezViewModel(logService) { ) : StudeezViewModel(logService) {
fun onStartSessionClick(openAndPopUp: (String) -> Unit) { fun onStartSessionClick(open: (String) -> Unit) {
openAndPopUp(StudeezDestinations.SESSION_SCREEN) open(StudeezDestinations.TIMER_SELECTION_SCREEN)
}
fun onLogoutClick(openAndPopup: (String, String) -> Unit) {
launchCatching {
accountDAO.signOut()
openAndPopup(LOGIN_SCREEN, HOME_SCREEN)
}
} }
} }

View file

@ -14,16 +14,28 @@ import androidx.compose.material.Text
import androidx.compose.material.TextButton import androidx.compose.material.TextButton
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import be.ugent.sel.studeez.navigation.StudeezDestinations
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.navigation.StudeezDestinations import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer.StudyState
import be.ugent.sel.studeez.resources
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds
var timerEnd = false var timerEnd = false
@ -31,7 +43,6 @@ var timerEnd = false
fun SessionScreen( fun SessionScreen(
open: (String) -> Unit, open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit, openAndPopUp: (String, String) -> Unit,
viewModel: SessionViewModel = hiltViewModel()
) { ) {
val context = LocalContext.current val context = LocalContext.current
val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
@ -86,9 +97,9 @@ fun SessionScreen(
private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: MediaPlayer) { private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: MediaPlayer) {
var tikker by remember { mutableStateOf(false) } var tikker by remember { mutableStateOf(false) }
LaunchedEffect(tikker) { LaunchedEffect(tikker) {
delay(1000) delay(1.seconds)
viewModel.getTimer().tick() viewModel.getTimer().tick()
tikker = !tikker ticker = !ticker
} }
if (viewModel.getTimer().hasCurrentCountdownEnded() && !viewModel.getTimer().hasEnded()) { if (viewModel.getTimer().hasCurrentCountdownEnded() && !viewModel.getTimer().hasEnded()) {
@ -111,8 +122,18 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 40.sp, fontSize = 40.sp,
) )
val stateString: String = when (viewModel.getTimer().view) {
StudyState.DONE -> resources().getString(R.string.state_done)
StudyState.FOCUS -> resources().getString(R.string.state_focus)
StudyState.BREAK -> resources().getString(R.string.state_take_a_break)
StudyState.FOCUS_REMAINING ->
(viewModel.getTimer() as FunctionalPomodoroTimer?)?.breaksRemaining?.let {
resources().getQuantityString(R.plurals.state_focus_remaining, it, it)
}.toString()
}
Text( Text(
text = viewModel.getTimer().getViewString(), text = stateString,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
fontWeight = FontWeight.Light, fontWeight = FontWeight.Light,
@ -142,4 +163,6 @@ private fun Timer(viewModel: SessionViewModel = hiltViewModel(), mediaplayer: Me
} }
} }
} }
} }

View file

@ -1,14 +1,15 @@
package be.ugent.sel.studeez.screens.session package be.ugent.sel.studeez.screens.session
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
import be.ugent.sel.studeez.domain.LogService import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.screens.StudeezViewModel import be.ugent.sel.studeez.screens.StudeezViewModel
import be.ugent.sel.studeez.data.SelectedTimerState
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class SessionViewModel @Inject constructor( class SessionViewModel @Inject constructor(
private val selectedTimerState: SelectedTimerState,
logService: LogService logService: LogService
) : StudeezViewModel(logService) { ) : StudeezViewModel(logService) {
@ -16,7 +17,7 @@ class SessionViewModel @Inject constructor(
private val task : String = "No task selected" // placeholder for tasks implementation private val task : String = "No task selected" // placeholder for tasks implementation
fun getTimer() : FunctionalTimer { fun getTimer() : FunctionalTimer {
return timer return selectedTimerState.selectedTimer!!
} }
fun getTask(): String { fun getTask(): String {

View file

@ -1,5 +1,6 @@
package be.ugent.sel.studeez.screens.timer_overview package be.ugent.sel.studeez.screens.timer_overview
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -46,12 +47,12 @@ fun TimerOverviewScreen(
) { ) {
// Default Timers, cannot be edited // Default Timers, cannot be edited
items(viewModel.getDefaultTimers()) { items(viewModel.getDefaultTimers()) {
TimerEntry(timerInfo = it, canEdit = false) TimerEntry(timerInfo = it, canDisplay = false)
} }
// User timers, can be edited // User timers, can be edited
items(timers.value) { items(timers.value) {
TimerEntry(timerInfo = it, true) { timerInfo -> TimerEntry(timerInfo = it, true, R.string.edit) { timerInfo ->
viewModel.update(timerInfo) viewModel.update(timerInfo)
} }
} }
@ -65,7 +66,12 @@ fun TimerOverviewScreen(
} }
@Composable @Composable
fun TimerEntry(timerInfo: TimerInfo, canEdit: Boolean, update: (TimerInfo) -> Unit = {}) { fun TimerEntry(
timerInfo: TimerInfo,
canDisplay: Boolean,
@StringRes buttonName: Int = -1,
buttonFunction: (TimerInfo) -> Unit = {}
) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -83,9 +89,9 @@ fun TimerEntry(timerInfo: TimerInfo, canEdit: Boolean, update: (TimerInfo) -> Un
fontSize = 15.sp fontSize = 15.sp
) )
} }
if (canEdit) { if (canDisplay) {
BasicButton(R.string.edit, Modifier.card()) { BasicButton(buttonName, Modifier.card()) {
// TODO buttonFunction(timerInfo)
} }
} }

View file

@ -0,0 +1,44 @@
package be.ugent.sel.studeez.screens.timer_selection
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.*
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import be.ugent.sel.studeez.R
import be.ugent.sel.studeez.common.composable.PrimaryScreenTemplate
import be.ugent.sel.studeez.resources
import be.ugent.sel.studeez.screens.timer_overview.TimerEntry
@Composable
fun TimerSelectionScreen(
open: (String) -> Unit,
openAndPopUp: (String, String) -> Unit,
viewModel: TimerSelectionViewModel = hiltViewModel()
) {
val timers = viewModel.getAllTimers().collectAsState(initial = emptyList())
PrimaryScreenTemplate(
title = resources().getString(R.string.timers),
open = open,
openAndPopUp = openAndPopUp,
) {
LazyColumn(verticalArrangement = Arrangement.spacedBy(7.dp)) {
// All timers
items(timers.value) {
TimerEntry(
timerInfo = it,
canDisplay = true,
buttonName = R.string.start
) { timerInfo ->
viewModel.startSession(open, timerInfo)
}
}
}
}
}

View file

@ -0,0 +1,28 @@
package be.ugent.sel.studeez.screens.timer_selection
import be.ugent.sel.studeez.data.SelectedTimerState
import be.ugent.sel.studeez.data.local.models.timer_info.TimerInfo
import be.ugent.sel.studeez.domain.LogService
import be.ugent.sel.studeez.domain.TimerDAO
import be.ugent.sel.studeez.navigation.StudeezDestinations
import be.ugent.sel.studeez.screens.StudeezViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
@HiltViewModel
class TimerSelectionViewModel @Inject constructor(
private val timerDAO: TimerDAO,
private val selectedTimerState: SelectedTimerState,
logService: LogService
) : StudeezViewModel(logService) {
fun getAllTimers() : Flow<List<TimerInfo>> {
return timerDAO.getAllTimers()
}
fun startSession(open: (String) -> Unit, timerInfo: TimerInfo) {
selectedTimerState.selectedTimer = timerInfo.getFunctionalTimer()
open(StudeezDestinations.SESSION_SCREEN)
}
}

View file

@ -1,4 +0,0 @@
package be.ugent.sel.studeez.screens.timers
class TimerScreen {
}

View file

@ -13,6 +13,7 @@
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="go_back">Go back</string> <string name="go_back">Go back</string>
<string name="next">Next</string> <string name="next">Next</string>
<string name="start">Start</string>
<!-- Messages --> <!-- Messages -->
<string name="success">Success!</string> <string name="success">Success!</string>
@ -63,6 +64,14 @@
<string name="timers">Timers</string> <string name="timers">Timers</string>
<string name="edit">Edit</string> <string name="edit">Edit</string>
<string name="add_timer">Add timer</string> <string name="add_timer">Add timer</string>
<string name="state_focus">Focus!</string>
<plurals name="state_focus_remaining">
<item quantity="zero">Focus one more time!</item>
<item quantity="one">Focus! (%d break remaining)</item>
<item quantity="other">Focus! (%d breaks remaining)</item>
</plurals>
<string name="state_done">Done!</string>
<string name="state_take_a_break">Take a break!</string>
<!-- Settings --> <!-- Settings -->
<string name="settings">Settings</string> <string name="settings">Settings</string>

View file

@ -1,17 +0,0 @@
package be.ugent.sel.studeez
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View file

@ -0,0 +1,44 @@
package be.ugent.sel.studeez.timer_functional
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalCustomTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
import org.junit.Assert
import org.junit.Test
class FunctionalCustomTimerUnitTest : FunctionalTimerUnitTest() {
override fun setTimer() {
timer = FunctionalCustomTimer(time)
}
@Test
override fun testOneTick() {
timer.tick()
Assert.assertEquals(
time - 1,
timer.time.time,
)
}
@Test
override fun multipleTicks() {
val n = 10
for (i in 1..n) {
timer.tick()
}
Assert.assertEquals(
time - n,
timer.time.time,
)
}
@Test
override fun testEnded() {
timer = FunctionalCustomTimer(0)
timer.tick()
Assert.assertTrue(timer.hasEnded())
Assert.assertEquals(
FunctionalTimer.StudyState.DONE,
timer.view
)
}
}

View file

@ -0,0 +1,46 @@
package be.ugent.sel.studeez.timer_functional
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalEndlessTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
import org.junit.Assert
import org.junit.Test
class FunctionalEndlessTimerUnitTest : FunctionalTimerUnitTest() {
override fun setTimer() {
timer = FunctionalEndlessTimer()
}
@Test
override fun testOneTick() {
timer.tick()
Assert.assertEquals(
1,
timer.time.time
)
}
@Test
override fun multipleTicks() {
val n = 10
for (i in 1..n) {
timer.tick()
}
Assert.assertEquals(
n,
timer.time.time
)
}
@Test
override fun testEnded() {
val n = 1000
for (i in 1..n) {
timer.tick()
Assert.assertFalse(timer.hasEnded())
Assert.assertEquals(
FunctionalTimer.StudyState.FOCUS,
timer.view
)
}
}
}

View file

@ -0,0 +1,98 @@
package be.ugent.sel.studeez.timer_functional
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalPomodoroTimer
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
import org.junit.Assert
import org.junit.Test
class FunctionalPomodoroTimerUnitTest : FunctionalTimerUnitTest() {
private val breakTime = 10
private val breaks = 2
override val hours = 0
override val minutes = 0
override val seconds = 10
private lateinit var pomodoroTimer: FunctionalPomodoroTimer
override fun setTimer() {
pomodoroTimer = FunctionalPomodoroTimer(time, breakTime, breaks)
}
@Test
override fun testOneTick() {
pomodoroTimer.tick()
Assert.assertEquals(
time - 1,
pomodoroTimer.time.time,
)
Assert.assertFalse(pomodoroTimer.isInBreak)
Assert.assertEquals(
breaks,
pomodoroTimer.breaksRemaining,
)
Assert.assertEquals(
FunctionalTimer.StudyState.FOCUS,
pomodoroTimer.view,
)
}
@Test
override fun multipleTicks() {
val n = 10
for (i in 1..n) {
pomodoroTimer.tick()
}
Assert.assertEquals(
time - n,
pomodoroTimer.time.time
)
}
@Test
override fun testEnded() {
pomodoroTimer = FunctionalPomodoroTimer(0, 0, 0)
pomodoroTimer.tick()
Assert.assertTrue(pomodoroTimer.hasEnded())
Assert.assertEquals(
FunctionalTimer.StudyState.DONE,
pomodoroTimer.view,
)
}
@Test
fun switchToBreak() {
for (i in 0..10) {
pomodoroTimer.tick()
}
Assert.assertFalse(pomodoroTimer.hasEnded())
Assert.assertTrue(pomodoroTimer.isInBreak)
Assert.assertEquals(
FunctionalTimer.StudyState.BREAK,
pomodoroTimer.view
)
}
@Test
fun switchToStudying() {
for (i in 0..time) {
pomodoroTimer.tick()
}
Assert.assertTrue(pomodoroTimer.isInBreak)
Assert.assertEquals(
FunctionalTimer.StudyState.BREAK,
pomodoroTimer.view
)
for (i in 0..breakTime) {
pomodoroTimer.tick()
}
Assert.assertFalse(pomodoroTimer.isInBreak)
val breaksRemaining = breaks - 1
Assert.assertEquals(
breaksRemaining,
pomodoroTimer.breaksRemaining
)
Assert.assertEquals(
FunctionalTimer.StudyState.FOCUS_REMAINING,
pomodoroTimer.view
)
}
}

View file

@ -0,0 +1,33 @@
package be.ugent.sel.studeez.timer_functional
import be.ugent.sel.studeez.data.local.models.timer_functional.FunctionalTimer
import org.junit.Before
import org.junit.Test
abstract class FunctionalTimerUnitTest {
protected lateinit var timer: FunctionalTimer
protected open val hours = 4
protected open val minutes = 20
protected open val seconds = 39
protected var time: Int = 0
@Before
fun setup() {
time = seconds + minutes * 60 + hours * 60 * 60
setTimer()
}
/**
* The timer-property should be set to the right implementation in this method.
*/
abstract fun setTimer()
@Test
abstract fun testOneTick()
@Test
abstract fun multipleTicks()
@Test
abstract fun testEnded()
}

View file

@ -0,0 +1,81 @@
package be.ugent.sel.studeez.timer_functional
import be.ugent.sel.studeez.data.local.models.timer_functional.HoursMinutesSeconds
import be.ugent.sel.studeez.data.local.models.timer_functional.Time
import org.junit.Assert
import org.junit.Before
import org.junit.Test
class TimeUnitTest {
private val hours = 4
private val minutes = 20
private val seconds = 39
private val time: Time = Time(seconds + minutes * 60 + hours * 60 * 60)
@Before
fun setup() {
}
@Test
fun formatTime() {
Assert.assertEquals(
HoursMinutesSeconds(
hours.toString().padStart(2, '0'),
minutes.toString().padStart(2, '0'),
seconds.toString().padStart(2, '0'),
),
time.getAsHMS(),
)
}
@Test
fun getTime() {
Assert.assertEquals(
seconds + minutes * 60 + hours * 60 * 60,
time.time,
)
}
@Test
fun minOne() {
time.minOne()
Assert.assertEquals(
(seconds + minutes * 60 + hours * 60 * 60) - 1,
time.time,
)
}
@Test
fun plusOne() {
time.plusOne()
Assert.assertEquals(
(seconds + minutes * 60 + hours * 60 * 60) + 1,
time.time,
)
}
@Test
fun minMultiple() {
val n = 10
for (i in 1 .. n) {
time.minOne()
}
Assert.assertEquals(
(seconds + minutes * 60 + hours * 60 * 60) - n,
time.time,
)
}
@Test
fun plusMultiple() {
val n = 10
for (i in 1 .. n) {
time.plusOne()
}
Assert.assertEquals(
(seconds + minutes * 60 + hours * 60 * 60) + n,
time.time,
)
}
}