Skip to content

Commit c0c36b0

Browse files
committed
Some more task tests
has issues with mocking logging ingame thats why some tests are disabled for now
1 parent f0ed228 commit c0c36b0

File tree

4 files changed

+210
-5
lines changed

4 files changed

+210
-5
lines changed

common/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ val fuelVersion: String by project
2525
val resultVersion: String by project
2626
val mockitoKotlin: String by project
2727
val mockitoInline: String by project
28+
val mockkVersion: String by project
2829

2930
base.archivesName = "${base.archivesName.get()}-api"
3031

@@ -62,6 +63,7 @@ dependencies {
6263
testImplementation(kotlin("test"))
6364
testImplementation("org.mockito.kotlin:mockito-kotlin:$mockitoKotlin")
6465
testImplementation("org.mockito:mockito-inline:$mockitoInline")
66+
testImplementation("io.mockk:mockk:${mockkVersion}")
6567
}
6668

6769
tasks {

common/src/main/kotlin/com/lambda/task/Task.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ typealias TaskGeneratorOrNull<R> = SafeContext.(R) -> Task<*>?
3737
typealias TaskGeneratorUnit<R> = SafeContext.(R) -> Unit
3838

3939
abstract class Task<Result> : Nameable, Muteable {
40-
private var parent: Task<*>? = null
40+
var parent: Task<*>? = null
4141
val subTasks = mutableListOf<Task<*>>()
4242
var state = State.INIT
4343
override val isMuted: Boolean get() = state == State.PAUSED || state == State.INIT

common/src/test/kotlin/TaskTest.kt

Lines changed: 206 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@ import com.lambda.context.SafeContext
33
import com.lambda.task.RootTask
44
import com.lambda.task.RootTask.run
55
import com.lambda.task.Task
6+
import com.lambda.util.Communication
7+
import com.lambda.util.Communication.log
8+
import io.mockk.every
9+
import io.mockk.mockkObject
610
import org.junit.jupiter.api.AfterEach
711
import org.junit.jupiter.api.BeforeEach
812
import org.junit.jupiter.api.Test
13+
import org.junit.jupiter.api.assertThrows
914
import org.mockito.Mock
1015
import org.mockito.MockedConstruction
11-
import org.mockito.Mockito
12-
import org.mockito.Mockito.`when`
16+
import org.mockito.Mockito.mockConstruction
1317
import org.mockito.MockitoAnnotations
18+
import org.mockito.kotlin.whenever
1419
import kotlin.test.assertEquals
20+
import kotlin.test.assertFalse
1521
import kotlin.test.assertTrue
1622

1723
/*
@@ -54,9 +60,11 @@ class TaskTest {
5460
@BeforeEach
5561
fun setUp() {
5662
MockitoAnnotations.openMocks(this)
57-
clientContextMock = Mockito.mockConstruction(ClientContext::class.java) { mock, _ ->
58-
`when`(mock.toSafe()).thenReturn(mockSafeContext)
63+
clientContextMock = mockConstruction(ClientContext::class.java) { mock, _ ->
64+
whenever(mock.toSafe()).thenReturn(mockSafeContext)
5965
}
66+
mockkObject(Communication)
67+
every { Communication.log(any(), any(), any(), any()) } returns Unit
6068
}
6169

6270
@AfterEach
@@ -65,6 +73,200 @@ class TaskTest {
6573
RootTask.clear()
6674
}
6775

76+
@Test
77+
fun `task initial state is INIT`() {
78+
val task = TestTask(0)
79+
assertEquals(Task.State.INIT, task.state)
80+
assertTrue(task.subTasks.isEmpty())
81+
assertEquals(0, task.age)
82+
}
83+
84+
@Test
85+
fun `execute transitions to RUNNING and adds to parent subTasks`() {
86+
val parent = TestTask(0)
87+
val child = TestTask(1)
88+
89+
child.execute(parent)
90+
91+
assertEquals(Task.State.COMPLETED, child.state)
92+
assertTrue(parent.subTasks.contains(child))
93+
assertEquals(parent, child.parent)
94+
}
95+
96+
@Test
97+
fun `success transitions to COMPLETED and executes finally block`() {
98+
val task = TestTask(5)
99+
var finallyCalled = false
100+
101+
task.finally { result ->
102+
assertEquals(6, result)
103+
finallyCalled = true
104+
}.run()
105+
106+
assertEquals(Task.State.COMPLETED, task.state)
107+
assertTrue(finallyCalled)
108+
}
109+
110+
// @Test
111+
// fun `cancel transitions to CANCELLED and cancels subTasks`() {
112+
// val parent = TestTask(0)
113+
// val child = TestTask(1).apply { execute(parent) }
114+
//
115+
// child.cancel()
116+
//
117+
// assertEquals(Task.State.CANCELLED, child.state)
118+
// assertTrue(child.subTasks.all { it.state == Task.State.CANCELLED })
119+
// }
120+
121+
@Test
122+
fun `pause and activate change state between PAUSED and RUNNING`() {
123+
val task = TestTask(0).apply { state = Task.State.RUNNING }
124+
125+
task.pause()
126+
assertEquals(Task.State.PAUSED, task.state)
127+
128+
task.activate()
129+
assertEquals(Task.State.RUNNING, task.state)
130+
}
131+
132+
// @Test
133+
// fun `subtask pauses parent when executed with pauseParent true`() {
134+
// val parent = TestTask(0).apply { state = Task.State.RUNNING }
135+
// val child = TestTask(1)
136+
//
137+
// child.execute(parent, pauseParent = true)
138+
//
139+
// assertEquals(Task.State.PAUSED, parent.state)
140+
// }
141+
142+
@Test
143+
fun `then chains tasks in sequence`() {
144+
val task1 = TestTask(1)
145+
val task2 = TestTask(2)
146+
147+
task1.then(task2).run()
148+
149+
task1.success(6) // Simulate success to trigger next task
150+
assertTrue(task1.parent?.subTasks?.contains(task2) == true)
151+
}
152+
153+
// @Test
154+
// fun `finally block is called on failure`() {
155+
// var finallyCalled = false
156+
// val task = object : Task<Unit>() {
157+
// override val name = "FailingTask"
158+
// override fun SafeContext.onStart() {
159+
// throw RuntimeException("Simulated failure")
160+
// }
161+
// }.finally { finallyCalled = true }
162+
//
163+
// task.run()
164+
//
165+
// assertEquals(Task.State.FAILED, task.state)
166+
// assertTrue(finallyCalled)
167+
// }
168+
169+
@Test
170+
fun `execute with self as owner throws exception`() {
171+
val task = TestTask(0)
172+
assertThrows<IllegalArgumentException> {
173+
task.execute(task)
174+
}
175+
}
176+
177+
@Test
178+
fun `then with self throws exception`() {
179+
val task = TestTask(0)
180+
assertThrows<IllegalArgumentException> {
181+
task.then(task)
182+
}
183+
}
184+
185+
// @Test
186+
// fun `duration is formatted correctly`() {
187+
// val task = TestTask(0).apply { age = 120 } // 120 * 50ms = 6000ms
188+
// assertEquals("000:00:00:06.00", task.duration)
189+
// }
190+
191+
@Test
192+
fun `toString includes task hierarchy and state`() {
193+
val parent = TestTask(1)
194+
val child = TestTask(2).apply { execute(parent) }
195+
196+
val expected = """
197+
TestTask of 1 [Initialized]
198+
TestTask of 2 [Running] 0ms
199+
""".trimIndent().replace("\n", System.lineSeparator())
200+
201+
assertTrue(parent.toString().contains("TestTask of 1"))
202+
assertTrue(parent.toString().contains("TestTask of 2"))
203+
}
204+
205+
@Test
206+
fun `clear removes all subTasks`() {
207+
val parent = TestTask(0)
208+
TestTask(1).execute(parent)
209+
TestTask(2).execute(parent)
210+
211+
parent.clear()
212+
213+
assertTrue(parent.subTasks.isEmpty())
214+
}
215+
216+
@Test
217+
fun `isMuted returns true when PAUSED or INIT`() {
218+
val task = TestTask(0)
219+
assertTrue(task.isMuted) // INIT state
220+
221+
task.state = Task.State.PAUSED
222+
assertTrue(task.isMuted)
223+
224+
task.state = Task.State.RUNNING
225+
assertFalse(task.isMuted)
226+
}
227+
228+
// @Test
229+
// fun `failure propagates to parent with stacktrace`() {
230+
// val grandParent = TestTask(0)
231+
// val parent = TestTask(1).apply { execute(grandParent) }
232+
// val child = TestTask(2).apply { execute(parent) }
233+
//
234+
// val exception = RuntimeException("Child failed")
235+
// child.failure(exception)
236+
//
237+
// assertEquals(Task.State.FAILED, child.state)
238+
// assertEquals(Task.State.FAILED, parent.state)
239+
// assertEquals(Task.State.FAILED, grandParent.state)
240+
// }
241+
242+
@Test
243+
fun `task with thenOrNull executes next task conditionally`() {
244+
val task = TestTask(0)
245+
var nextTaskExecuted = false
246+
247+
task.thenOrNull { result ->
248+
if (result == 1) TestTask(1).also { nextTaskExecuted = true } else null
249+
}
250+
251+
task.success(1)
252+
assertTrue(nextTaskExecuted)
253+
254+
nextTaskExecuted = false
255+
task.success(0)
256+
assertFalse(nextTaskExecuted)
257+
}
258+
259+
@Test
260+
fun `subtask resumes parent when completed`() {
261+
val parent = TestTask(0).apply { state = Task.State.RUNNING }
262+
val child = TestTask(1).apply { execute(parent, pauseParent = true) }
263+
264+
child.success(2)
265+
266+
assertEquals(Task.State.COMPLETED, child.state)
267+
assertEquals(Task.State.RUNNING, parent.state)
268+
}
269+
68270
@Test
69271
fun `test task`() {
70272
val task = TestTask(5)

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ fuelVersion=2.3.1
3636
resultVersion=5.6.0
3737
mockitoKotlin=5.4.0
3838
mockitoInline=5.2.0
39+
mockkVersion=1.13.17
3940

4041
# Fabric https://fabricmc.net/develop/
4142
fabricLoaderVersion=0.16.9

0 commit comments

Comments
 (0)