package device import ( "bytes" "encoding/binary" "io" "net" "testing" "time" ) func TestTouchPacket(t *testing.T) { req := TouchRequest{DeviceID: "phone", Action: 0, X: 123, Y: 456, Width: 576, Height: 1280} b := encodeTouch(req) want := []byte{2, 0, 255, 255, 255, 255, 255, 255, 255, 254, 0, 0, 0, 123, 0, 0, 1, 200, 2, 64, 5, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0} if !bytes.Equal(b, want) { t.Fatalf("unexpected touch packet %x", b) } for _, action := range []int{1, 3} { req.Action = action if binary.BigEndian.Uint16(encodeTouch(req)[22:24]) != 0 { t.Fatal("release pressure is nonzero") } } } func TestTouchIsWrittenBeforeRelease(t *testing.T) { writer, reader := net.Pipe() defer writer.Close() defer reader.Close() st := &previewStream{running: true, serial: "phone", controlConn: writer} s := &Service{stream: st} received := make(chan []byte, 4) go func() { for { b := make([]byte, 32) if _, err := io.ReadFull(reader, b); err != nil { return } received <- b } }() req := TouchRequest{DeviceID: "phone", Action: 0, X: 1, Y: 2, Width: 576, Height: 1280} for _, action := range []int{0, 2, 1} { req.Action = action if err := s.Touch(req); err != nil { t.Fatal(err) } select { case packet := <-received: if int(packet[1]) != action { t.Fatal("touch events reordered") } case <-time.After(time.Second): t.Fatal("touch was buffered until release") } } if st.touchDown { t.Fatal("pointer stuck after release") } req.Action = 2 if err := s.Touch(req); err != nil { t.Fatal(err) } select { case <-received: t.Fatal("move injected without down") default: } req.Action = 0 req.DeviceID = "other" if err := s.Touch(req); err == nil { t.Fatal("input reached a different device") } } func TestScreenOffRestoresDisplayOnStop(t *testing.T) { writer, reader := net.Pipe() defer reader.Close() st := &previewStream{running: true, serial: "phone", controlConn: writer} s := &Service{stream: st} packets := make(chan []byte, 1) go func() { data, _ := io.ReadAll(reader); packets <- data }() if err := s.SetScreenOff("other", true); err == nil { t.Fatal("screen power reached a different device") } if err := s.SetScreenOff("phone", true); err != nil { t.Fatal(err) } if err := s.SetScreenOff("phone", true); err != nil { t.Fatal(err) } s.StopPreviewStream() select { case data := <-packets: if !bytes.Equal(data, []byte{10, 0, 10, 1}) { t.Fatalf("expected scrcpy display off followed by restore, got %x", data) } case <-time.After(time.Second): t.Fatal("screen restore was not sent before disconnect") } if st.screenOff { t.Fatal("screen-off state leaked into next connection") } if err := s.SetScreenOff("phone", true); err == nil { t.Fatal("screen power accepted after stream stopped") } } func TestScreenPowerFailureDoesNotCommitState(t *testing.T) { writer, reader := net.Pipe() reader.Close() defer writer.Close() st := &previewStream{running: true, serial: "phone", controlConn: writer} if err := (&Service{stream: st}).SetScreenOff("phone", true); err == nil { t.Fatal("closed control channel was accepted") } if st.screenOff { t.Fatal("failed update was reported as applied") } }