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/ControlStreamTest.java | |
| download | scrcpy-android-eb0c8951196c637e44daf3c0617b131b997d5d2c.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/ControlStreamTest.java')
| -rw-r--r-- | app/src/test/java/invalid/lena/scrcpy/ControlStreamTest.java | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/app/src/test/java/invalid/lena/scrcpy/ControlStreamTest.java b/app/src/test/java/invalid/lena/scrcpy/ControlStreamTest.java new file mode 100644 index 0000000..cafff81 --- /dev/null +++ b/app/src/test/java/invalid/lena/scrcpy/ControlStreamTest.java @@ -0,0 +1,152 @@ +package invalid.lena.scrcpy; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +public class ControlStreamTest { + + private static final class RecordingSink implements ControlStream.InboundSink { + final List<String> clipboards = new ArrayList<>(); + @Override public void onRemoteClipboard(String text) { clipboards.add(text); } + } + + // ---- inbound (DeviceMessage) parsing ---- + + @Test + public void clipboardMessageDispatched() throws Exception { + String text = "héllo world"; + byte[] payload = text.getBytes(StandardCharsets.UTF_8); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos); + out.writeByte(0); // TYPE_CLIPBOARD + out.writeInt(payload.length); + out.write(payload); + + RecordingSink sink = new RecordingSink(); + ControlStream cs = new ControlStream( + new ByteArrayInputStream(bos.toByteArray()), + new ByteArrayOutputStream()); + cs.setInboundSink(sink); + cs.runReader(); // synchronous; returns on EOF + + assertEquals(1, sink.clipboards.size()); + assertEquals(text, sink.clipboards.get(0)); + } + + @Test + public void ackClipboardConsumedSilently() throws Exception { + // type=1, then a long sequence number. No dispatch should occur. + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(bos); + out.writeByte(1); + out.writeLong(0x1234567890abcdefL); + + RecordingSink sink = new RecordingSink(); + ControlStream cs = new ControlStream( + new ByteArrayInputStream(bos.toByteArray()), + new ByteArrayOutputStream()); + cs.setInboundSink(sink); + cs.runReader(); + + assertEquals(0, sink.clipboards.size()); + } + + @Test + public void unknownTypeAborts() throws Exception { + // type=42 - runReader should log and return. + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + bos.write(42); + // a few trailing bytes so the reader doesn't EOF before processing + bos.write(new byte[]{0, 0, 0, 0}); + + RecordingSink sink = new RecordingSink(); + ControlStream cs = new ControlStream( + new ByteArrayInputStream(bos.toByteArray()), + new ByteArrayOutputStream()); + cs.setInboundSink(sink); + cs.runReader(); + assertEquals(0, sink.clipboards.size()); + } + + // ---- outbound (send + writer) ---- + + @Test + public void writerDrainsInOrder() throws Exception { + // We submit three messages, then run the writer until the + // outbox is empty AND we've signalled stop. + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ControlStream cs = new ControlStream( + new ByteArrayInputStream(new byte[0]), out); + + byte[] a = new byte[]{1, 2, 3}; + byte[] b = new byte[]{4, 5}; + byte[] c = new byte[]{6}; + cs.send(a); + cs.send(b); + cs.send(c); + + Thread writer = new Thread(cs::runWriter, "writer-under-test"); + writer.start(); + + // give the writer a moment to drain + for (int i = 0; i < 50 && out.size() < 6; i++) Thread.sleep(10); + cs.stop(); + writer.join(1_000); + + byte[] got = out.toByteArray(); + assertEquals(6, got.length); + assertEquals(1, got[0]); assertEquals(2, got[1]); assertEquals(3, got[2]); + assertEquals(4, got[3]); assertEquals(5, got[4]); + assertEquals(6, got[5]); + } + + @Test + public void keyEventLandsAfterOutboxOverflow() throws Exception { + // Fill the bounded outbox with touch-MOVE messages, then enqueue + // one key event. The MOVE must be evicted to make room; the key + // event must be preserved and end up last in the drain. + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ControlStream cs = new ControlStream( + new ByteArrayInputStream(new byte[0]), out); + + for (int i = 0; i < 256; i++) { + cs.send(ControlMessages.touch(/* MOVE */ 2, 0L, i, i, 1080, 2400, 0xffff, 0, 0)); + } + byte[] key = ControlMessages.keycode(0, 29, 0, 0); + cs.send(key); + + Thread w = new Thread(cs::runWriter, "drain"); + w.start(); + + // Drain to quiescence (size stable for 100ms). + long deadline = System.currentTimeMillis() + 2_000; + int last = -1, stable = 0; + while (System.currentTimeMillis() < deadline) { + int now = out.size(); + if (now == last) { stable++; if (stable > 5) break; } + else { stable = 0; last = now; } + Thread.sleep(20); + } + cs.stop(); + w.join(1_000); + + byte[] bytes = out.toByteArray(); + assertNotEquals(0, bytes.length); + + // The key event should be the final 14 bytes of the drained stream + // (queued last, FIFO order, only touch-MOVEs evicted on overflow). + byte[] tail = new byte[14]; + System.arraycopy(bytes, bytes.length - 14, tail, 0, 14); + for (int i = 0; i < 14; i++) assertEquals(key[i], tail[i]); + } +} |