aboutsummaryrefslogtreecommitdiff
path: root/app/src/test/java/invalid/lena/scrcpy/TouchMapTest.java
diff options
context:
space:
mode:
authorLena <lena@omega>2026-08-01 00:00:00 +0000
committerLena <lena@omega>2026-08-01 00:00:00 +0000
commit852a8a00273c9128efeed0217a8e5d3fcd8bf780 (patch)
treec72f959731fa3c7c809cf1a0222363b6c9c9b03b /app/src/test/java/invalid/lena/scrcpy/TouchMapTest.java
parentf379b93d52bf0dbd3816ec3f64d165314771d2a6 (diff)
downloadscrcpy-android-852a8a00273c9128efeed0217a8e5d3fcd8bf780.tar.gz
app: harden mirroring lifecycle and state
Diffstat (limited to 'app/src/test/java/invalid/lena/scrcpy/TouchMapTest.java')
-rw-r--r--app/src/test/java/invalid/lena/scrcpy/TouchMapTest.java85
1 files changed, 85 insertions, 0 deletions
diff --git a/app/src/test/java/invalid/lena/scrcpy/TouchMapTest.java b/app/src/test/java/invalid/lena/scrcpy/TouchMapTest.java
new file mode 100644
index 0000000..becacc1
--- /dev/null
+++ b/app/src/test/java/invalid/lena/scrcpy/TouchMapTest.java
@@ -0,0 +1,85 @@
+package invalid.lena.scrcpy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+// The arithmetic that decides where a tap lands. Until this existed the
+// mapping was verified by nothing: the only coverage was an emulator
+// self-mirror, where the target and the view are the same size, so the
+// transform is the identity and every scaling or offset bug passes.
+public class TouchMapTest {
+
+ // A 1080x2400 phone showing a 1080x1920 target: fitted to 864x1920,
+ // leaving 108px bars left and right.
+ private static final int VIEW_X = 108, VIEW_W = 864, TARGET_W = 1080;
+
+ @Test
+ public void leftEdgeOfTheVideoIsTheLeftEdgeOfTheTarget() {
+ assertEquals(0, TouchMap.map(VIEW_X, VIEW_X, VIEW_W, TARGET_W));
+ }
+
+ // The view has fewer pixels than the target, so the last column the
+ // mapping can produce is floor((span-1) * target / span) - 1078 here,
+ // not 1079. That is granularity, not an off-by-one: a 864px-wide view
+ // cannot address all 1080 target columns. What matters is that the
+ // right edge lands on the last addressable column and stays inside.
+ @Test
+ public void rightEdgeIsTheLastAddressableColumn() {
+ int got = TouchMap.map(VIEW_X + VIEW_W - 1, VIEW_X, VIEW_W, TARGET_W);
+ assertEquals((VIEW_W - 1) * TARGET_W / VIEW_W, got);
+ assertTrue("must stay inside the target", got < TARGET_W);
+ assertTrue("must be within one step of the far edge",
+ got >= TARGET_W - (TARGET_W / VIEW_W) - 1);
+ }
+
+ @Test
+ public void centreMapsToCentre() {
+ assertEquals(TARGET_W / 2, TouchMap.map(VIEW_X + VIEW_W / 2, VIEW_X, VIEW_W, TARGET_W));
+ }
+
+ @Test
+ public void theLetterboxBarIsClampedNotWrapped() {
+ // Touches on the bars must land on the near edge. Subtracting the
+ // origin without clamping would make the left bar negative and the
+ // right bar overflow past the target's width.
+ assertEquals(0, TouchMap.map(0, VIEW_X, VIEW_W, TARGET_W));
+ assertEquals(0, TouchMap.map(VIEW_X - 50, VIEW_X, VIEW_W, TARGET_W));
+ int far = (VIEW_W - 1) * TARGET_W / VIEW_W; // last addressable column
+ assertEquals(far, TouchMap.map(VIEW_X + VIEW_W + 50, VIEW_X, VIEW_W, TARGET_W));
+ assertEquals(far, TouchMap.map(2000, VIEW_X, VIEW_W, TARGET_W));
+ }
+
+ @Test
+ public void neverEscapesTheTargetForAnyCoordinate() {
+ for (int c = -500; c < 3000; c++) {
+ int t = TouchMap.map(c, VIEW_X, VIEW_W, TARGET_W);
+ if (t < 0 || t >= TARGET_W) {
+ throw new AssertionError("coord " + c + " mapped outside the target: " + t);
+ }
+ }
+ }
+
+ @Test
+ public void noOverflowAtFourK() {
+ // 3840 * 2160 exceeds int when multiplied in 32 bits; the mapping
+ // has to widen before it scales.
+ assertEquals(2159, TouchMap.map(3839, 0, 3840, 2160));
+ assertEquals(0, TouchMap.map(0, 0, 3840, 2160));
+ }
+
+ @Test
+ public void identityWhenTheViewMatchesTheTarget() {
+ // The self-mirror case the e2e exercises: nothing should move.
+ for (int c : new int[]{0, 1, 539, 1079}) {
+ assertEquals(c, TouchMap.map(c, 0, 1080, 1080));
+ }
+ }
+
+ @Test
+ public void degenerateGeometryIsRefusedRatherThanDividingByZero() {
+ assertEquals(0, TouchMap.map(100, 0, 0, 1080));
+ assertEquals(0, TouchMap.map(100, 0, 1080, 0));
+ }
+}