diff options
| author | Lena <lena@omega> | 2026-01-01 00:00:00 +0000 |
|---|---|---|
| committer | Lena <lena@omega> | 2026-06-24 22:14:50 +0300 |
| commit | eb0c8951196c637e44daf3c0617b131b997d5d2c (patch) | |
| tree | 2f83b6a41cee745467e53fc401942b82cea286ea /app/src/test/java/invalid/lena/scrcpy/SyncTest.java | |
| download | scrcpy-android-0.1.tar.gz | |
scrcpy-android: mirror an Android device over wireless ADB0.1
Native Java app for Android 12+ that mirrors another Android
device over wireless ADB, forwarding video, audio, touch input,
and clipboard. Bundles a pinned scrcpy-server.jar and the
vendored libadb-android stack. Supports h264/h265/av1 video and
raw/opus audio with in-app codec selection. No NDK, no Kotlin.
Includes JVM unit tests and a Docker-based emulator e2e rig.
Diffstat (limited to 'app/src/test/java/invalid/lena/scrcpy/SyncTest.java')
| -rw-r--r-- | app/src/test/java/invalid/lena/scrcpy/SyncTest.java | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/app/src/test/java/invalid/lena/scrcpy/SyncTest.java b/app/src/test/java/invalid/lena/scrcpy/SyncTest.java new file mode 100644 index 0000000..57493ec --- /dev/null +++ b/app/src/test/java/invalid/lena/scrcpy/SyncTest.java @@ -0,0 +1,137 @@ +package invalid.lena.scrcpy; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class SyncTest { + + private static final String PATH = "/data/local/tmp/scrcpy-server.jar"; + private static final int MODE = 0100644; + private static final int MTIME = 0x12345678; + + @Test + public void emptyPayloadFraming() throws Exception { + byte[] payload = new byte[0]; + ByteArrayOutputStream framed = new ByteArrayOutputStream(); + ByteArrayInputStream resp = okayResponse(); + + long total = Sync.push(new ByteArrayInputStream(payload), framed, resp, PATH, MODE, MTIME); + assertEquals(0L, total); + + byte[] bytes = framed.toByteArray(); + int pos = 0; + + // SEND + le32(headerLen) + header + String header = PATH + "," + MODE; + byte[] hb = header.getBytes(StandardCharsets.UTF_8); + pos = assertTag(bytes, pos, "SEND"); + assertEquals(hb.length, Wire.readLe32(bytes, pos)); pos += 4; + for (int i = 0; i < hb.length; i++, pos++) assertEquals(hb[i], bytes[pos]); + + // No DATA chunks for empty payload. + // DONE + le32(mtime) + pos = assertTag(bytes, pos, "DONE"); + assertEquals(MTIME, Wire.readLe32(bytes, pos)); pos += 4; + + assertEquals(bytes.length, pos); + } + + @Test + public void singleByteIsOneDataChunk() throws Exception { + ByteArrayOutputStream framed = new ByteArrayOutputStream(); + long total = Sync.push(new ByteArrayInputStream(new byte[]{0x42}), + framed, okayResponse(), PATH, MODE, MTIME); + assertEquals(1L, total); + + byte[] bytes = framed.toByteArray(); + int pos = skipSendHeader(bytes); + pos = assertTag(bytes, pos, "DATA"); + assertEquals(1, Wire.readLe32(bytes, pos)); pos += 4; + assertEquals(0x42, bytes[pos]); pos += 1; + pos = assertTag(bytes, pos, "DONE"); + assertEquals(MTIME, Wire.readLe32(bytes, pos)); pos += 4; + assertEquals(bytes.length, pos); + } + + @Test + public void payloadLargerThanChunkSplits() throws Exception { + int n = Sync.CHUNK + 1; + byte[] payload = new byte[n]; + for (int i = 0; i < n; i++) payload[i] = (byte) i; + + ByteArrayOutputStream framed = new ByteArrayOutputStream(); + long total = Sync.push(new ByteArrayInputStream(payload), + framed, okayResponse(), PATH, MODE, MTIME); + assertEquals(n, total); + + byte[] bytes = framed.toByteArray(); + int pos = skipSendHeader(bytes); + + // First DATA = full chunk + pos = assertTag(bytes, pos, "DATA"); + assertEquals(Sync.CHUNK, Wire.readLe32(bytes, pos)); pos += 4; + pos += Sync.CHUNK; + + // Second DATA = the remaining one byte + pos = assertTag(bytes, pos, "DATA"); + assertEquals(1, Wire.readLe32(bytes, pos)); pos += 4; + pos += 1; + + pos = assertTag(bytes, pos, "DONE"); + assertEquals(MTIME, Wire.readLe32(bytes, pos)); pos += 4; + assertEquals(bytes.length, pos); + } + + @Test + public void failResponseRaises() { + ByteArrayOutputStream framed = new ByteArrayOutputStream(); + ByteArrayInputStream resp = failResponse("disk full"); + try { + Sync.push(new ByteArrayInputStream(new byte[]{1, 2, 3}), framed, resp, + PATH, MODE, MTIME); + fail("expected IOException"); + } catch (IOException e) { + assertTrue(e.getMessage(), e.getMessage().contains("disk full")); + assertTrue(e.getMessage(), e.getMessage().startsWith("sync FAIL")); + } + } + + private static ByteArrayInputStream okayResponse() { + byte[] r = new byte[8]; + r[0] = 'O'; r[1] = 'K'; r[2] = 'A'; r[3] = 'Y'; + // length 0 + return new ByteArrayInputStream(r); + } + + private static ByteArrayInputStream failResponse(String msg) { + byte[] mb = msg.getBytes(StandardCharsets.UTF_8); + byte[] r = new byte[8 + mb.length]; + r[0] = 'F'; r[1] = 'A'; r[2] = 'I'; r[3] = 'L'; + Wire.writeLe32(r, 4, mb.length); + System.arraycopy(mb, 0, r, 8, mb.length); + return new ByteArrayInputStream(r); + } + + private static int assertTag(byte[] bytes, int pos, String tag) { + for (int i = 0; i < 4; i++) { + assertEquals("tag pos=" + pos + " idx=" + i, + (byte) tag.charAt(i), bytes[pos + i]); + } + return pos + 4; + } + + private static int skipSendHeader(byte[] bytes) { + // SEND + le32(len) + header + int pos = 4; + int len = Wire.readLe32(bytes, pos); + return pos + 4 + len; + } +} |