Skip to content

Commit 13d3490

Browse files
committed
Add humanize for packet count
1 parent 8e473bb commit 13d3490

File tree

4 files changed

+155
-1
lines changed

4 files changed

+155
-1
lines changed

app/src/androidTest/java/com/pcapplusplus/toyvpn/StatsScreenTest.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,48 @@ class StatsScreenTest {
162162
}
163163
}
164164

165+
@Test
166+
fun testHumanizePacketCount() {
167+
val totalPacketCount = 996099
168+
val ipv4PacketCount = 123
169+
val ipv6PacketCount = 1234
170+
val tcpPacketCount = 12_345
171+
val udpPacketCount = 122_000
172+
val dnsPacketCount = 4_599_888
173+
val tlsPacketCount = 34_829_007
174+
val tcpConnectionCount = 999_999_999
175+
val udpConnectionCount = 1_000_990_000
176+
177+
renderScreen(
178+
totalPacketCount = totalPacketCount,
179+
ipv4PacketCount = ipv4PacketCount,
180+
ipv6PacketCount = ipv6PacketCount,
181+
tcpPacketCount = tcpPacketCount,
182+
udpPacketCount = udpPacketCount,
183+
dnsPacketCount = dnsPacketCount,
184+
tlsPacketCount = tlsPacketCount,
185+
tcpConnectionCount = tcpConnectionCount,
186+
udpConnectionCount = udpConnectionCount,
187+
)
188+
189+
val expectedValues =
190+
listOf(
191+
Pair("IPv4", "123"),
192+
Pair("IPv6", "1234"),
193+
Pair("TCP", "12.35K"),
194+
Pair("UDP", "122K"),
195+
Pair("DNS", "4.6M"),
196+
Pair("TLS", "34.83M"),
197+
Pair("TCPConn", "1B"),
198+
Pair("UDPConn", "1.001B"),
199+
)
200+
201+
composeTestRule.onNodeWithText("Total Packets").onSibling().assertTextEquals(totalPacketCount.toString())
202+
expectedValues.forEach { (testTag, humanizedCount) ->
203+
composeTestRule.onNodeWithTag("${testTag}_count").assertTextEquals(humanizedCount)
204+
}
205+
}
206+
165207
@Test
166208
fun testVpnConnectedWithTopDnsDomainData() {
167209
val topDnsDomains =

app/src/main/java/com/pcapplusplus/toyvpn/StatsScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ fun TrafficStatRow(stat: TrafficStat) {
246246
total = if (stat.total != 0) stat.total else 1,
247247
)
248248
Text(
249-
text = stat.count.toString(),
249+
text = stat.count.humanize(),
250250
style = MaterialTheme.typography.titleMedium.copy(fontSize = 20.sp),
251251
modifier = Modifier.align(Alignment.CenterVertically).testTag("${stat.testTag}_count"),
252252
textAlign = TextAlign.End,

app/src/main/java/com/pcapplusplus/toyvpn/Utils.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.pcapplusplus.toyvpn
33
import android.content.Intent
44
import android.os.Build
55
import android.os.Parcelable
6+
import kotlin.collections.ArrayList
67

78
@Suppress("DEPRECATION")
89
inline fun <reified T : Parcelable> Intent.getParcelableArrayListExtraCompat(key: String): ArrayList<T>? {
@@ -12,3 +13,41 @@ inline fun <reified T : Parcelable> Intent.getParcelableArrayListExtraCompat(key
1213
getParcelableArrayListExtra(key)
1314
}
1415
}
16+
17+
fun Int.humanize(): String {
18+
if (this < 10000) {
19+
return this.toString()
20+
}
21+
22+
var (divisor, suffix) =
23+
when {
24+
this < 1000000 -> 1000.0 to "K"
25+
this < 1000000000 -> 1000000.0 to "M"
26+
else -> 1000000000.0 to "B"
27+
}
28+
29+
var value = this / divisor
30+
31+
if (value >= 999.95) {
32+
when (suffix) {
33+
"K" -> {
34+
value /= 1000
35+
suffix = "M"
36+
}
37+
"M" -> {
38+
value /= 1000
39+
suffix = "B"
40+
}
41+
}
42+
}
43+
44+
val format =
45+
when {
46+
value < 10 -> "%.3f"
47+
value < 100 -> "%.2f"
48+
else -> "%.1f"
49+
}
50+
51+
val formatted = String.format(format, value).toDouble().toString().removeSuffix(".0")
52+
return "$formatted$suffix"
53+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.pcapplusplus.toyvpn
2+
3+
import org.junit.jupiter.api.Assertions.assertEquals
4+
import org.junit.jupiter.api.Test
5+
6+
class HumanizeTest {
7+
@Test
8+
fun `test numbers less than 10,000`() {
9+
assertEquals("0", 0.humanize())
10+
assertEquals("1", 1.humanize())
11+
assertEquals("42", 42.humanize())
12+
assertEquals("999", 999.humanize())
13+
assertEquals("1000", 1000.humanize())
14+
assertEquals("9999", 9999.humanize())
15+
}
16+
17+
@Test
18+
fun `test numbers in thousands range`() {
19+
assertEquals("10K", 10000.humanize())
20+
assertEquals("10.01K", 10010.humanize())
21+
assertEquals("12.35K", 12345.humanize())
22+
assertEquals("25.36K", 25359.humanize())
23+
assertEquals("52K", 51999.humanize())
24+
assertEquals("40K", 39999.humanize())
25+
assertEquals("39.1K", 39099.humanize())
26+
assertEquals("99.95K", 99950.humanize())
27+
assertEquals("99.99K", 99990.humanize())
28+
assertEquals("100K", 99997.humanize())
29+
assertEquals("100K", 100000.humanize())
30+
assertEquals("150.3K", 150300.humanize())
31+
assertEquals("150.6K", 150554.humanize())
32+
assertEquals("999.5K", 999500.humanize())
33+
}
34+
35+
@Test
36+
fun `test boundary cases for K to M transition`() {
37+
assertEquals("999.9K", 999900.humanize())
38+
assertEquals("1M", 999950.humanize())
39+
assertEquals("1M", 999999.humanize())
40+
assertEquals("1M", 1000000.humanize())
41+
assertEquals("1M", 1000010.humanize())
42+
}
43+
44+
@Test
45+
fun `test numbers in millions range`() {
46+
assertEquals("1.001M", 1001000.humanize())
47+
assertEquals("1.234M", 1234000.humanize())
48+
assertEquals("1.335M", 1334556.humanize())
49+
assertEquals("9.999M", 9999000.humanize())
50+
assertEquals("10.45M", 10446888.humanize())
51+
assertEquals("50.01M", 50010000.humanize())
52+
assertEquals("99.99M", 99990000.humanize())
53+
assertEquals("100M", 100000000.humanize())
54+
assertEquals("500.5M", 500500000.humanize())
55+
assertEquals("999.5M", 999500000.humanize())
56+
}
57+
58+
@Test
59+
fun `test boundary cases for M to B transition`() {
60+
assertEquals("999.9M", 999900000.humanize())
61+
assertEquals("1B", 999950000.humanize())
62+
assertEquals("1B", 999999900.humanize())
63+
assertEquals("1B", 1000000000.humanize())
64+
}
65+
66+
@Test
67+
fun `test numbers in billions range`() {
68+
assertEquals("1.001B", 1001000000.humanize())
69+
assertEquals("1.5B", 1500000000.humanize())
70+
assertEquals("1.672B", 1671900246.humanize())
71+
assertEquals("2.147B", Int.MAX_VALUE.humanize())
72+
}
73+
}

0 commit comments

Comments
 (0)