Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test code of EunGabiController and rename some variables #60

Merged
merged 1 commit into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class EunGabiController {
var graph: EunGabiGraph
get() {
return _graph ?: error("Graph is not set")
} set(value) {
}
set(value) {
if (backQueue.isEmpty()) {
val initialEntity =
createEntry(
Expand Down Expand Up @@ -66,27 +67,26 @@ class EunGabiController {
* It holds the history of the navigation entries emitted by [backQueue].
*/
private val _backStack = MutableStateFlow<List<EunGabiEntry>>(listOf())
val backStack: StateFlow<List<EunGabiEntry>> = _backStack.asStateFlow()
val backStack: StateFlow<List<EunGabiEntry>> = _backStack.asStateFlow()

/**
* Navigates up in the back stack.
* It uses [findPreviousEntity] to find the previous entity in the back stack.
* It uses [findPreviousEntry] to find the previous entity in the back stack.
* then removes all the entries after the previous entity.
*/
fun navigateUp(): Boolean {
if (backQueue.size <= 1) return false
var currentEntity = backQueue.lastOrNull() ?: return false
var removedEntry: EunGabiEntry?
val targetRoute = findPreviousEntity(currentEntity).eunGabiDestination.route
var currentEntry = backQueue.last()
val targetRoute = findPreviousEntry(currentEntry)

do {
removedEntry = backQueue.removeLastOrNull()
backQueue.lastOrNull()?.also { currentEntity = it }
} while (currentEntity.eunGabiDestination.route != targetRoute)
backQueue.removeLast()
currentEntry = backQueue.last()
} while (currentEntry.id != targetRoute.id)

_backStack.update { backQueue.toList() }
isPop.value = true
return removedEntry != null
return backQueue.last() == targetRoute
}

/**
Expand All @@ -109,12 +109,12 @@ class EunGabiController {
/**
* Finds the previous entity in the back stack via [NavOptions] of the given entity.
*
* @param entity The entity to find the previous entity for.
* @param entry The entity to find the previous entity for.
* @return The previous [EunGabiEntry] of the given [EunGabiEntry] in the back stack.
*/
internal fun findPreviousEntity(entity: EunGabiEntry): EunGabiEntry {
val targetRoute = entity.navOptions.popUpToRoute
val inclusive = entity.navOptions.inclusive
internal fun findPreviousEntry(entry: EunGabiEntry): EunGabiEntry {
val targetRoute = entry.navOptions.popUpToRoute
val inclusive = entry.navOptions.inclusive

val index =
backQueue
Expand Down Expand Up @@ -176,6 +176,7 @@ class EunGabiController {
fun restoreState() {
val result = EunGabiState.restore()
backQueue = result
_backStack.update { backQueue.toList() }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ internal fun EunGabiNavHostInternal(
if (inPredictiveBack) {
LaunchedEffect(entity) {
println("entity changed: ${entity.eunGabiDestination.route}")
previousEntry = controller.findPreviousEntity(entity)
previousEntry = controller.findPreviousEntry(entity)
}
LaunchedEffect(progress) {
previousEntry?.also { transitionState.seekTo(progress, it) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.easternkite.eungabi.navigation
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class EunGabiControllerTest {
private val mockGraph: EunGabiGraph = EunGabiGraphBuilder()
Expand Down Expand Up @@ -50,4 +52,122 @@ class EunGabiControllerTest {
val actual = mockController.navigateUp()
assertEquals(expected = expected, actual = actual)
}
}

@Test
fun GIVEN_navigationStack_with_A_B_C_WHEN_navigateUp_called_THEN_returns_true_and_current_destination_is_B() {
val expected = "B"
mockController.navigate("B")
mockController.navigate("C")
val isNavigateUpSuccess = mockController.navigateUp()
assertEquals(true, actual = mockController.isPop.value)
val actual = mockController.backStack.value
.last()
.eunGabiDestination.route
assertEquals(expected = true, actual = isNavigateUpSuccess)
assertEquals(expected = expected, actual = actual)
}

@Test
fun GIVEN_navigation_stack_A_B_C_with_popupto_option_WHEN_findPreviousEntry_called_THEN_returns_A() {
val expected = "A"
mockController.navigate("B")
mockController.navigate("C") {
popUpTo("A") { inclusive = false }
}
val lastEntry = mockController.backStack.value.last()
val actual = mockController.findPreviousEntry(lastEntry)
assertEquals(expected = expected, actual = actual.eunGabiDestination.route)
}

@Test
fun GIVEN_navigation_stack_A_B_C_with_inclusive_popupto_option_WHEN_findPreviousEntry_called_THEN_returns_A() {
val expected = "A"
mockController.navigate("B")
mockController.navigate("C") {
popUpTo("A") { inclusive = true }
}
val lastEntry = mockController.backStack.value.last()
val actual = mockController.findPreviousEntry(lastEntry)
assertEquals(expected = expected, actual = actual.eunGabiDestination.route)
}

@Test
fun GIVEN_navigation_stack_A_B_C_D_with_inclusive_popupto_option_WHEN_findPreviousEntry_called_THEN_returns_A() {
val expected = "A"
mockController.navigate("B")
mockController.navigate("C")
mockController.navigate("D") {
popUpTo("B") { inclusive = true }
}
val lastEntry = mockController.backStack.value.last()
val actual = mockController.findPreviousEntry(lastEntry)
assertEquals(expected = expected, actual = actual.eunGabiDestination.route)
}

@Test
fun GIVEN_navigation_stack_A_B_with_inclusive_popupto_option_WHEN_findPreviousEntry_called_THEN_returns_A() {
val expected = "A"
mockController.navigate("B") {
popUpTo("ZZZ") { inclusive = true }
}
val lastEntry = mockController.backStack.value.last()
val actual = mockController.findPreviousEntry(lastEntry)
assertEquals(expected = expected, actual = actual.eunGabiDestination.route)
}

@Test
fun GIVEN_navigation_route_Z_WHEN_navigate_called_THEN_throws_exception() {
assertFailsWith(
exceptionClass = IllegalStateException::class,
message = "Destination not found",
block = { mockController.navigate("Z") }
)
}

@Test
fun is_pop_test() {
mockController.navigate("B")
assertFalse(actual = mockController.isPop.value)

mockController.navigateUp()
assertTrue(actual = mockController.isPop.value)

mockController.navigate("B")
assertFalse(actual = mockController.isPop.value)
}

@Test
fun save_restore_test() {
mockController.navigate("B")
mockController.navigate("C")
mockController.saveState()

val newController = EunGabiController()
newController.restoreState()
newController.graph = mockGraph

val expected = mockController.backStack.value
val actual = newController.backStack.value
assertEquals(expected = expected, actual = actual)
}

@Test
fun navigate_up_test() {
mockController.navigate("B")
mockController.navigate("C")
mockController.navigate("D") {
popUpTo("A") { inclusive = true }
}
val isNavigateUpSuccess = mockController.navigateUp()
assertTrue(actual = isNavigateUpSuccess)
val expected = "A"
val actual = mockController.backStack.value
.last()
.eunGabiDestination.route

assertEquals(expected = expected, actual = actual)
repeat(100) {
assertFalse(mockController.navigateUp())
}
}
}