Skip to content

Commit 40b01f7

Browse files
authored
Adding unit tests to maestro-utils module (#2081)
* test: Adding unit tests to the MaestroTimerTest class * test: Adding unit tests to the SocketUtils class * test: Adding unit tests to the Strings class * test: Adding unit tests to the Collections class * test: Adding unit tests to the DepthTracker class * test: Adding unit tests to the Insight class * test: Adding unit tests to the Errors class * test: Refactoring SocketUtils unit tests * test: Resolving commands_optional_tournee test * test: Refactoring test about localhost address * test: Refactoring the tests about localIp method * chore: Adding mockk dependency
1 parent ab9e87c commit 40b01f7

File tree

9 files changed

+379
-0
lines changed

9 files changed

+379
-0
lines changed

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ junit = "5.10.2"
3636
kotlin = "1.8.22"
3737
kotlinResult = "1.1.18"
3838
ktor = "2.3.6"
39+
mockk = "1.12.0"
3940
mozillaRhino = "1.7.14"
4041
picocli = "4.6.3"
4142
selenium = "4.13.0"
@@ -100,6 +101,7 @@ ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
100101
ktor-server-cors = { module = "io.ktor:ktor-server-cors", version.ref = "ktor" }
101102
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }
102103
ktor-server-status-pages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" }
104+
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
103105
mozilla-rhino = { module = "org.mozilla:rhino", version.ref = "mozillaRhino" }
104106
picocli = { module = "info.picocli:picocli", version.ref = "picocli" }
105107
picocli-codegen = { module = "info.picocli:picocli-codegen", version.ref = "picocli" }

maestro-utils/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ plugins {
1010
dependencies {
1111
api(libs.square.okio)
1212

13+
testImplementation(libs.mockk)
1314
testImplementation(libs.junit.jupiter.api)
1415
testRuntimeOnly(libs.junit.jupiter.engine)
1516
testImplementation(libs.google.truth)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import maestro.utils.isRegularFile
2+
import maestro.utils.isSingleFile
3+
import org.junit.jupiter.api.Assertions.assertFalse
4+
import org.junit.jupiter.api.Assertions.assertTrue
5+
import org.junit.jupiter.api.Test
6+
import java.nio.file.Files
7+
8+
class CollectionsTest {
9+
10+
@Test
11+
fun `isSingleFile should return true for a single regular file`() {
12+
val file = Files.createTempFile("testFile", ".txt").toFile()
13+
val files = listOf(file)
14+
assertTrue(files.isSingleFile)
15+
file.delete()
16+
}
17+
18+
@Test
19+
fun `isSingleFile should return false for multiple files`() {
20+
val file1 = Files.createTempFile("testFile1", ".txt").toFile()
21+
val file2 = Files.createTempFile("testFile2", ".txt").toFile()
22+
val files = listOf(file1, file2)
23+
assertFalse(files.isSingleFile)
24+
file1.delete()
25+
file2.delete()
26+
}
27+
28+
@Test
29+
fun `isSingleFile should return false for a single directory`() {
30+
val dir = Files.createTempDirectory("testDir").toFile()
31+
val files = listOf(dir)
32+
assertFalse(files.isSingleFile)
33+
dir.delete()
34+
}
35+
36+
@Test
37+
fun `isRegularFile should return true for a single regular file`() {
38+
val file = Files.createTempFile("testFile", ".txt")
39+
val paths = listOf(file)
40+
assertTrue(paths.isRegularFile)
41+
Files.delete(file)
42+
}
43+
44+
@Test
45+
fun `isRegularFile should return false for multiple files`() {
46+
val file1 = Files.createTempFile("testFile1", ".txt")
47+
val file2 = Files.createTempFile("testFile2", ".txt")
48+
val paths = listOf(file1, file2)
49+
assertFalse(paths.isRegularFile)
50+
Files.delete(file1)
51+
Files.delete(file2)
52+
}
53+
54+
@Test
55+
fun `isRegularFile should return false for a single directory`() {
56+
val dir = Files.createTempDirectory("testDir")
57+
val paths = listOf(dir)
58+
assertFalse(paths.isRegularFile)
59+
Files.delete(dir)
60+
}
61+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import maestro.utils.DepthTracker
2+
import org.junit.jupiter.api.Assertions.assertEquals
3+
import org.junit.jupiter.api.BeforeEach
4+
import org.junit.jupiter.api.Test
5+
6+
class DepthTrackerTest {
7+
8+
@BeforeEach
9+
fun setUp() {
10+
DepthTracker.trackDepth(0)
11+
}
12+
13+
@Test
14+
fun `trackDepth should update currentDepth and maxDepth`() {
15+
DepthTracker.trackDepth(10)
16+
assertEquals(10, DepthTracker.getMaxDepth())
17+
}
18+
19+
@Test
20+
fun `getMaxDepth should return the maximum depth tracked`() {
21+
DepthTracker.trackDepth(3)
22+
DepthTracker.trackDepth(2)
23+
DepthTracker.trackDepth(5)
24+
25+
assertEquals(5, DepthTracker.getMaxDepth())
26+
}
27+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import maestro.utils.Insight
2+
import maestro.utils.Insights
3+
import org.junit.jupiter.api.Assertions.assertEquals
4+
import org.junit.jupiter.api.Assertions.assertTrue
5+
import org.junit.jupiter.api.Test
6+
7+
class InsightsTest {
8+
9+
@Test
10+
fun `report should update insight and notify listeners`() {
11+
val insight = Insight("Test message", Insight.Level.INFO)
12+
var notifiedInsight: Insight? = null
13+
14+
Insights.onInsightsUpdated { notifiedInsight = it }
15+
Insights.report(insight)
16+
17+
assertEquals(insight, notifiedInsight)
18+
}
19+
20+
@Test
21+
fun `onInsightsUpdated should add a listener`() {
22+
val insight = Insight("Test message", Insight.Level.INFO)
23+
var notified = false
24+
25+
Insights.onInsightsUpdated { notified = true }
26+
Insights.report(insight)
27+
28+
assertTrue(notified)
29+
}
30+
31+
@Test
32+
fun `unregisterListener should remove a listener`() {
33+
val insight = Insight("Test message", Insight.Level.INFO)
34+
var notified = false
35+
val listener: (Insight) -> Unit = { notified = true }
36+
37+
Insights.onInsightsUpdated(listener)
38+
Insights.unregisterListener(listener)
39+
Insights.report(insight)
40+
41+
assertTrue(!notified)
42+
}
43+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import maestro.utils.MaestroTimer
2+
import org.junit.jupiter.api.Assertions.*
3+
import org.junit.jupiter.api.BeforeEach
4+
import org.junit.jupiter.api.Test
5+
6+
class MaestroTimerTest {
7+
8+
@BeforeEach
9+
fun setUp() {
10+
MaestroTimer.setTimerFunc { _, ms -> Thread.sleep(ms) }
11+
}
12+
13+
@Test
14+
fun `withTimeout should return result within timeout`() {
15+
val result = MaestroTimer.withTimeout(1000) {
16+
"Success"
17+
}
18+
19+
assertEquals("Success", result)
20+
}
21+
22+
@Test
23+
fun `withTimeout should return null if body is null`() {
24+
val result = MaestroTimer.withTimeout(1000) {
25+
null
26+
}
27+
28+
assertNull(result)
29+
}
30+
31+
@Test
32+
fun `retryUntilTrue should return true if block succeeds within timeout`() {
33+
val result = MaestroTimer.retryUntilTrue(1000) {
34+
true
35+
}
36+
37+
assertTrue(result)
38+
}
39+
40+
@Test
41+
fun `retryUntilTrue should return false if block fails within timeout`() {
42+
val result = MaestroTimer.retryUntilTrue(100) {
43+
false
44+
}
45+
46+
assertFalse(result)
47+
}
48+
49+
@Test
50+
fun `retryUntilTrue should handle exceptions and continue retrying`() {
51+
var attempts = 0
52+
val result = MaestroTimer.retryUntilTrue(1000, 100, { }) {
53+
attempts++
54+
if (attempts < 3) throw Exception("Test exception")
55+
true
56+
}
57+
58+
assertTrue(result)
59+
assertEquals(3, attempts)
60+
}
61+
62+
@Test
63+
fun `setTimerFunc should change the sleep function`() {
64+
var sleepCalled = false
65+
MaestroTimer.setTimerFunc { _, _ -> sleepCalled = true }
66+
MaestroTimer.sleep(MaestroTimer.Reason.BUFFER, 100)
67+
68+
assertTrue(sleepCalled)
69+
}
70+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import io.mockk.every
2+
import io.mockk.mockkStatic
3+
import maestro.utils.SocketUtils
4+
import org.junit.jupiter.api.Assertions.assertEquals
5+
import org.junit.jupiter.api.Assertions.assertNotNull
6+
import org.junit.jupiter.api.Assertions.assertThrows
7+
import org.junit.jupiter.api.Assertions.assertTrue
8+
import org.junit.jupiter.api.Test
9+
import java.net.Inet4Address
10+
import java.net.InetAddress
11+
import java.net.NetworkInterface
12+
import java.util.*
13+
14+
class SocketUtilsTest {
15+
16+
private fun <T> List<T>.toEnumeration(): Enumeration<T> = Collections.enumeration(this)
17+
18+
@Test
19+
fun `nextFreePort should return a free port within the specified range`() {
20+
val from = 5000
21+
val to = 5100
22+
val port = SocketUtils.nextFreePort(from, to)
23+
24+
assertTrue(port in from..to)
25+
}
26+
27+
@Test
28+
fun `nextFreePort should throw IllegalStateException when no ports are available in the range`() {
29+
val from = 100000
30+
val to = 100010
31+
32+
assertThrows(IllegalStateException::class.java) {
33+
SocketUtils.nextFreePort(from, to)
34+
}
35+
}
36+
37+
@Test
38+
fun `localIp should return local IP address`() {
39+
val ip = SocketUtils.localIp()
40+
41+
assertNotNull(ip)
42+
assertTrue(ip.startsWith("192") || ip.startsWith("10") || ip.startsWith("172") || ip.startsWith("127"))
43+
assertTrue(InetAddress.getByName(ip) is Inet4Address)
44+
}
45+
46+
@Test
47+
fun `localIp should return localhost address if no network interfaces are available`() {
48+
mockkStatic(NetworkInterface::class)
49+
every { NetworkInterface.getNetworkInterfaces() } returns listOf<NetworkInterface>().toEnumeration()
50+
51+
val ip = SocketUtils.localIp()
52+
53+
assertEquals(InetAddress.getLocalHost().hostAddress, ip)
54+
}
55+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import maestro.utils.chunkStringByWordCount
2+
import org.junit.jupiter.api.Assertions.assertEquals
3+
import org.junit.jupiter.api.Test
4+
5+
class StringsTest {
6+
7+
@Test
8+
fun `chunkStringByWordCount should return empty list for empty string`() {
9+
val result = "".chunkStringByWordCount(2)
10+
assertEquals(emptyList<String>(), result)
11+
}
12+
13+
@Test
14+
fun `chunkStringByWordCount should return single chunk for string with fewer words than chunk size`() {
15+
val result = "hello world".chunkStringByWordCount(3)
16+
assertEquals(listOf("hello world"), result)
17+
}
18+
19+
@Test
20+
fun `chunkStringByWordCount should return multiple chunks for string with more words than chunk size`() {
21+
val result = "hello world this is a test".chunkStringByWordCount(2)
22+
assertEquals(listOf("hello world", "this is", "a test"), result)
23+
}
24+
25+
@Test
26+
fun `chunkStringByWordCount should handle exact chunk size`() {
27+
val result = "hello world this is a test".chunkStringByWordCount(5)
28+
assertEquals(listOf("hello world this is a", "test"), result)
29+
}
30+
31+
@Test
32+
fun `chunkStringByWordCount should handle trailing spaces`() {
33+
val result = " hello world ".chunkStringByWordCount(1)
34+
assertEquals(listOf("hello", "world"), result)
35+
}
36+
37+
@Test
38+
fun `chunkStringByWordCount should handle multiple spaces between words`() {
39+
val result = "hello world this is a test".chunkStringByWordCount(2)
40+
assertEquals(listOf("hello world", "this is", "a test"), result)
41+
}
42+
}

0 commit comments

Comments
 (0)