remote.focus lets a controller keyboard-capture lease ask core to enter or leave remote focus; the reply acknowledges admission only. Core reports the actual state with remote.focus events and forwards routed transitions as remote.input events carrying the source keycode and, on fresh presses, a canonical key name. A full focus mailbox revokes the lease; revocation drains pending focus work. No native capture owner is attached yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
82 lines
3.1 KiB
Go
82 lines
3.1 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package tentaclewire
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestRemoteCapturedKeyValidation(t *testing.T) {
|
|
for _, key := range []RemoteCapturedKey{
|
|
{Code: 8, Name: "a", Pressed: true}, {Code: 255, Name: "Shift_R", Pressed: true},
|
|
{Code: 38, Pressed: true, Repeat: true}, {Code: 38},
|
|
} {
|
|
if !ValidRemoteCapturedKey(key) {
|
|
t.Fatal("valid key rejected", key)
|
|
}
|
|
data, _ := json.Marshal(key)
|
|
var decoded RemoteCapturedKey
|
|
if json.Unmarshal(data, &decoded) != nil || decoded != key {
|
|
t.Fatal("round trip changed key", key)
|
|
}
|
|
}
|
|
for _, raw := range []string{
|
|
`{"code":7,"name":"a","pressed":true}`, `{"code":256,"name":"a","pressed":true}`,
|
|
`{"code":38,"name":"Prior","pressed":true}`, `{"code":38,"name":"a","pressed":false}`,
|
|
`{"code":38,"pressed":false,"repeat":true}`, `{"code":38,"pressed":true}`,
|
|
`{"name":"a","pressed":true}`, `{"code":38,"name":"a"}`,
|
|
`{"code":38,"name":"a","pressed":true,"origin":"local"}`, `{"code":38,"code":39,"name":"a","pressed":true}`,
|
|
} {
|
|
var key RemoteCapturedKey
|
|
if json.Unmarshal([]byte(raw), &key) == nil {
|
|
t.Fatal("invalid key decoded", raw)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRemoteFocusRequestAndEventValidation(t *testing.T) {
|
|
lease := strings.Repeat("a", 32)
|
|
var request RemoteFocusRequest
|
|
if json.Unmarshal([]byte(`{"lease":"`+lease+`","epoch":1,"sequence":2,"remote":false}`), &request) != nil || request.Remote || request.Sequence != 2 {
|
|
t.Fatal("valid focus request rejected", request)
|
|
}
|
|
for _, raw := range []string{
|
|
`{"lease":"` + lease + `","epoch":1,"sequence":2}`,
|
|
`{"lease":"` + lease + `","epoch":1,"sequence":0,"remote":true}`,
|
|
`{"lease":"short","epoch":1,"sequence":2,"remote":true}`,
|
|
`{"lease":"` + lease + `","epoch":1,"sequence":2,"remote":"yes"}`,
|
|
} {
|
|
if json.Unmarshal([]byte(raw), &request) == nil {
|
|
t.Fatal("invalid focus request decoded", raw)
|
|
}
|
|
}
|
|
key := RemoteCapturedKey{Code: 38, Name: "a", Pressed: true}
|
|
valid := []SessionEvent{
|
|
{Sequence: 1, Epoch: 1, Kind: "remote.input", Lease: lease, Input: &key},
|
|
{Sequence: 1, Epoch: 1, Kind: "remote.focus", Lease: lease, Focus: "remote", Reason: "requested"},
|
|
{Sequence: 1, Epoch: 1, Kind: "remote.focus", Lease: lease, Focus: "local", Reason: "escape"},
|
|
}
|
|
for _, event := range valid {
|
|
if !ValidSessionEvent(event) {
|
|
t.Fatal("valid event rejected", event)
|
|
}
|
|
}
|
|
invalid := []SessionEvent{
|
|
{Sequence: 1, Epoch: 1, Kind: "remote.input", Lease: lease},
|
|
{Sequence: 1, Epoch: 1, Kind: "remote.input", Lease: lease, Input: &key, Focus: "remote"},
|
|
{Sequence: 1, Epoch: 1, Kind: "remote.focus", Lease: lease, Focus: "remote", Reason: "network"},
|
|
{Sequence: 1, Epoch: 1, Kind: "remote.focus", Lease: lease, Focus: "elsewhere", Reason: "requested"},
|
|
{Sequence: 1, Epoch: 1, Kind: "remote.revoked", Lease: lease, Reason: "closed", Input: &key},
|
|
{Sequence: 1, Epoch: 1, Kind: "remote.revoked", Lease: lease, Reason: "closed", Focus: "local"},
|
|
}
|
|
for _, event := range invalid {
|
|
if ValidSessionEvent(event) {
|
|
t.Fatal("invalid event accepted", event)
|
|
}
|
|
}
|
|
}
|