Validate stream snapshot counts and verify native metadata mappings

This commit is contained in:
evgeny committed 2026-10-29 13:34:24 +01:00
1 parent a1dcf28bf9
commit ffa5dece4f
2 files changed
+51 -2

No files matched your search

@@ -7,14 +7,17 @@ data class StreamBufferPositions(val first: Long = 0, val head: Long = 0, val to
else ((next.coerceIn(first, head) - first).toDouble() / (head - first)).toFloat()
}
/** Все поля в байтах; ACK — десять завершённых интервалов по 100ms, для всех соединений ядра. */
/** TX snapshot: sampleUs — MONOTONIC мкс, счётчики данных — байты, connections — число соединений.
* acked1s — десять завершённых интервалов по 100 мс для всех соединений ядра.
* buffer.first/head и peer worst/best — seq пачек, total — все получатели, peers — до 16 отставших.
* fromNative читает 13 + 4*peerCount полей JNI; peer ID сохраняет uint64, ошибки формата бросаются caller. */
data class StreamTxStats(val id: Long, val sampleUs: Long, val videoBytes: Long, val audioBytes: Long,
val streamBytes: Long, val transportBytes: Long, val inflightBytes: Long,
val acked1s: Long, val totalBytes: Long, val connections: Long, val buffer: StreamBufferPositions) {
companion object {
fun fromNative(id: Long, data: LongArray): StreamTxStats {
require(data.size >= 13 && data.take(13).all { it >= 0 } && data[12] <= 16)
require(data.size == 13 + data[12].toInt() * 4 && data[9] <= data[10] && data[12] <= data[11])
require(data.size == 13 + data[12].toInt() * 4 && data[9] <= data[10] && data[12] <= data[11] && data[11] <= Int.MAX_VALUE)
val peers = List(data[12].toInt()) { i ->
val offset = 13 + i * 4
require(data[offset + 1] >= 0 && data[offset + 2] >= data[offset + 1] && data[offset + 3] in 1..255)
@@ -0,0 +1,46 @@
package com.utun.chat.data
import org.junit.Assert.*
import org.junit.Test
class StreamMetadataTest {
@Test fun sourceIdsAndEpochsPreserveUnsigned64BitsAndEmptyNamesUseTheId() {
val source = StreamSource.parse("""[{"id":"18446744073709551615","epoch":"ffffffffffffffff","name":"","self":1}]""").single()
assertEquals(-1L, source.id); assertEquals(-1L, source.epoch)
assertEquals("18446744073709551615", source.displayName); assertTrue(source.self)
}
@Test fun sourceSnapshotsRejectZeroAndDuplicateIdentities() {
for (json in listOf("""[{"id":"0","epoch":"1","name":"a","self":0}]""",
"""[{"id":"1","epoch":"0","name":"a","self":0}]""",
"""[{"id":"1","epoch":"a","name":"a","self":0},{"id":"1","epoch":"b","name":"b","self":1}]""")) {
assertThrows(IllegalArgumentException::class.java) { StreamSource.parse(json) }
}
}
@Test fun transmitSnapshotMapsUnitsAndKeepsUnsignedPeerId() {
val data = longArrayOf(100, 1, 2, 3, 4, 5, 6, 7, 8, 10, 20, 2, 1, -1, 12, 18, 3)
val stats = StreamTxStats.fromNative(42, data)
assertEquals(100L, stats.sampleUs); assertEquals(8L, stats.connections); assertEquals(6L, stats.acked1s)
assertEquals(2, stats.buffer.total); assertEquals(StreamBufferPeer(-1, 12, 18, 3), stats.buffer.peers.single())
assertEquals(0f, stats.buffer.fraction(0), 0f); assertEquals(0.5f, stats.buffer.fraction(15), 0f)
assertEquals(1f, stats.buffer.fraction(30), 0f); assertEquals(1f, StreamBufferPositions().fraction(0), 0f)
}
@Test fun malformedStatsCannotOverflowCountsOrReverseTheBufferWindow() {
val valid = longArrayOf(100, 1, 2, 3, 4, 5, 6, 7, 8, 10, 20, 2, 1, -1, 12, 18, 3)
for (data in listOf(valid.copyOf(12), valid + 0, valid.copyOf().also { it[11] = Int.MAX_VALUE.toLong() + 1 },
valid.copyOf().also { it[9] = 21 }, valid.copyOf().also { it[12] = 17 },
valid.copyOf().also { it[15] = 11 }, valid.copyOf().also { it[16] = 256 })) {
assertThrows(IllegalArgumentException::class.java) { StreamTxStats.fromNative(42, data) }
}
}
@Test fun nativeTelemetryKeepsKnownUnknownAndLocalReserveSeparate() {
val data = """{"self":"local","nodes":[{"name":"relay","flags":7,"us":2000000,"frames":4},{"name":"source","flags":0,"us":0,"frames":0}]}"""
val stats = StreamPlaybackTelemetry.fromNative(data.toByteArray(), 500000, 1100)
assertEquals(3, stats.nodes.size); assertEquals(2_000_000L, stats.nodes[0].durationUs)
assertTrue(stats.nodes[0].stale); assertTrue(stats.nodes[0].down); assertNull(stats.nodes[1].durationUs)
assertEquals(500_000L, stats.nodes[2].durationUs); assertEquals("1.1x", stats.speed)
}
}