aboutsummaryrefslogtreecommitdiff
path: root/app/src/test/java/invalid/lena/scrcpy/SyncTest.java
diff options
context:
space:
mode:
authorLena <lena@omega>2026-01-01 00:00:00 +0000
committerLena <lena@omega>2026-06-24 22:14:50 +0300
commiteb0c8951196c637e44daf3c0617b131b997d5d2c (patch)
tree2f83b6a41cee745467e53fc401942b82cea286ea /app/src/test/java/invalid/lena/scrcpy/SyncTest.java
downloadscrcpy-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.java137
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;
+ }
+}