Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
omkar-tenkale committed Jul 27, 2024
1 parent a79b1b5 commit 2f30b77
Show file tree
Hide file tree
Showing 6 changed files with 242 additions and 1 deletion.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
voyager-koin = { module = "cafe.adriel.voyager:voyager-koin", version.ref = "voyager" }
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlin-coroutines" }
mockk = { module = "io.mockk:mockk", version = "1.13.12" }
kotlinx-serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "kotlinx-serialization" }
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
kotlinx-atomicfu = { group = "org.jetbrains.kotlinx", name = "atomicfu", version.ref = "kotlinx-atomicfu" }
Expand Down
2 changes: 2 additions & 0 deletions nodal/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ kotlin {
val commonTest by getting {
dependencies {
implementation(libs.kotlin.test)
implementation(libs.coroutines.test)
implementation(libs.mockk)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package dev.omkartenkale.nodal

import dev.omkartenkale.nodal.plugin.NodalPlugins
import dev.omkartenkale.nodal.util.MainDispatcherRule
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.currentTime
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.yield
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertFails
import kotlin.test.assertFailsWith
import kotlin.test.fail

class NodeDependenciesTest {

@get:Rule
val coroutineRule = MainDispatcherRule()

@Before
fun setUp() {

}

@Test
fun `verify node provides dependencies`() = runTest {
class Some
class ANode: Node()

class RootNode : Node() {
override val providesDependencies: DependencyDeclaration = {
provides { Some() }
}
}

val rootNode = Node.createRootNode(
klass = RootNode::class,
onRequestRemove = { }) {} as RootNode

val some = rootNode.addChild<ANode>().dependencies.getOrNull<Some>()
assert(some != null)
}

@Test
fun `verify eager instances are created`() = runTest {
class Some
class CustomInstantiationException: Exception()

class RootNode : Node() {
val some: Some by dependencies<Some>()

override val providesDependencies: DependencyDeclaration = {
provides<Some> {
throw CustomInstantiationException()
Some()
}
}
}

assertFails {
Node.createRootNode(
klass = RootNode::class,
nodalConfig = NodalConfig(createEagerInstances = true),
onRequestRemove = { }) {} as RootNode
}

try {
Node.createRootNode(
klass = RootNode::class,
nodalConfig = NodalConfig(createEagerInstances = false),
onRequestRemove = { }) {} as RootNode

}catch (e:Exception){
var cause: Throwable? = e
while (cause!= null){
if(e is CustomInstantiationException) {
fail("Un-eager instance created eagerly")
}
cause = e.cause
}
}
}


}
120 changes: 120 additions & 0 deletions nodal/src/androidUnitTest/kotlin/dev/omkartenkale/nodal/NodeTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package dev.omkartenkale.nodal

import dev.omkartenkale.nodal.Node.Companion.ui
import dev.omkartenkale.nodal.util.MainDispatcherRule
import dev.omkartenkale.nodal.util.doOnAdded
import dev.omkartenkale.nodal.util.doOnRemoved
import dev.omkartenkale.nodal.util.isAdded
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.isActive
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import kotlin.test.assertFails

class NodeTest {
class RootNode : Node()
class ANode : Node()
class BNode : Node()

private lateinit var rootNode: RootNode

@get:Rule
val coroutineRule = MainDispatcherRule()

@Before
fun setUp() {
rootNode = Node.createRootNode(
klass = RootNode::class,
nodalConfig = NodalConfig(false),
onRequestRemove = { }) {} as RootNode
}

@Test
fun `verify node addition`() = runTest {
val nodeA = rootNode.addChild<ANode>()
assert(rootNode.children.contains(nodeA))
assert(nodeA.isAdded)

val nodeB = rootNode.addChild<BNode>()
assert(rootNode.children.contains(nodeB))
assert(nodeB.isAdded)
assert(rootNode.children.size == 2)
}

@Test
fun `verify node removal`() = runTest {
val nodeA = rootNode.addChild<ANode>()
nodeA.removeSelf()
assert(nodeA.isAdded.not())
assert(rootNode.children.contains(nodeA).not())
}

@Test
fun `verify node scope cancelled after removed`() = runTest {
val nodeA = rootNode.addChild<ANode>()
nodeA.removeSelf()
assert(nodeA.coroutineScope.isActive.not())
}

@Test
fun `verify node added callback is invoked`() = runTest {
val callback = mockk<() -> Unit>(relaxed = true)
val nodeA = rootNode.addChild<ANode>()
nodeA.doOnAdded {
callback()
}
verify { callback.invoke() }
}

@Test
fun `verify node isDead property is false before removed`() = runTest {
val nodeA = rootNode.addChild<ANode>()
assert(nodeA.isDead.not())
}

@Test
fun `verify node isDead property is updated after removed`() = runTest {
val nodeA = rootNode.addChild<ANode>()
nodeA.removeSelf()
assert(nodeA.isDead)
}

@Test
fun `verify node removed callback is invoked`() = runTest {
val callback = mockk<() -> Unit>(relaxed = true)
val nodeA = rootNode.addChild<ANode>()
nodeA.doOnRemoved {
callback()
}
nodeA.removeSelf()
verify { callback.invoke() }
}

@Test
fun `verify node doOnInit callback is invoked`() = runTest {
class SomeNode : Node() {
var doOnInitCalled = false

init {
doOnInit {
doOnInitCalled = true
}
}
}
rootNode.addChild<SomeNode>().apply {
assert(doOnInitCalled)
}
}

@Test
fun `verify node does not have ui dependency in unit tests`() = runTest {
val nodeA = rootNode.addChild<ANode>()
assertFails {
nodeA.ui
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package dev.omkartenkale.nodal.util

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.junit.rules.TestWatcher
import org.junit.runner.Description


class MainDispatcherRule @OptIn(ExperimentalCoroutinesApi::class) constructor(
val testDispatcher: TestDispatcher = UnconfinedTestDispatcher(),
) : TestWatcher() {

@OptIn(ExperimentalCoroutinesApi::class)
override fun starting(description: Description) {
super.starting(description)
Dispatchers.setMain(testDispatcher)
}

@OptIn(ExperimentalCoroutinesApi::class)
override fun finished(description: Description) {
super.finished(description)
Dispatchers.resetMain()
}
}
2 changes: 1 addition & 1 deletion nodal/src/commonMain/kotlin/dev.omkartenkale.nodal/Node.kt
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public open class Node {

public fun createRootNode(
klass: KClass<out Node>,
nodalConfig: NodalConfig,
nodalConfig: NodalConfig = NodalConfig(),
onRequestRemove: (Node) -> Unit,
plugins: List<NodalPlugin> = emptyList(),
dependencyDeclaration: DependencyDeclaration,
Expand Down

0 comments on commit 2f30b77

Please sign in to comment.