318 lines
14 KiB
Go
318 lines
14 KiB
Go
package input
|
|
|
|
import "testing"
|
|
|
|
func TestQuitConsumesBalancedChord(t *testing.T) {
|
|
for _, modifierFirst := range []bool{false, true} {
|
|
router := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(DesktopKeyboardLayout))
|
|
check := func(symbol uint32, code uint16, pressed, consume bool, command Command) {
|
|
t.Helper()
|
|
gotConsume, gotCommand := router.Handle(symbol, code, 0, pressed)
|
|
if gotConsume != consume || gotCommand != command {
|
|
t.Fatalf("got %v %q, want %v %q", gotConsume, gotCommand, consume, command)
|
|
}
|
|
}
|
|
check('q', 24, true, false, "")
|
|
check('q', 24, false, false, "")
|
|
check(0xff63, 118, true, true, "")
|
|
check('q', 24, true, true, Quit)
|
|
check('q', 24, true, true, "") // auto-repeat
|
|
if modifierFirst {
|
|
check(0xff63, 118, false, true, "")
|
|
check('q', 24, false, true, "")
|
|
} else {
|
|
check('q', 24, false, true, "")
|
|
check(0xff63, 118, false, true, "")
|
|
}
|
|
check('q', 24, true, false, "")
|
|
}
|
|
}
|
|
|
|
func TestUnknownChordPassesThrough(t *testing.T) {
|
|
router := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(DesktopKeyboardLayout))
|
|
router.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := router.Handle('x', 53, 0, true); consumed || command != "" {
|
|
t.Fatal("unknown command consumed")
|
|
}
|
|
router.Reset()
|
|
if consumed, command := router.Handle('x', 53, 0, false); consumed || command != "" {
|
|
t.Fatal("unknown command release consumed after pass-through reset")
|
|
}
|
|
router.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := router.Handle('q', 24, 4, true); consumed || command != "" {
|
|
t.Fatal("Control+Insert+q became quit")
|
|
}
|
|
router.Reset()
|
|
router.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := router.Handle('q', 24, 2|16, true); !consumed || command != Quit {
|
|
t.Fatal("lock keys prevented quit chord")
|
|
}
|
|
}
|
|
|
|
func TestBindingTableRoutesPreferencesOnPress(t *testing.T) {
|
|
bindings := DefaultCommandBindings(DesktopKeyboardLayout)
|
|
router := NewCommandRouter([]uint32{0xff63, 0xff9e}, bindings)
|
|
bindings[CommandChord{Symbol: ' ', CthulhuModifier: true}] = Quit
|
|
|
|
if consumed, command := router.Handle(0xff9e, 90, 0, true); !consumed || command != "" {
|
|
t.Fatalf("modifier press = %v %q", consumed, command)
|
|
}
|
|
if consumed, command := router.Handle(' ', 65, 0, true); !consumed || command != Preferences {
|
|
t.Fatalf("space press = %v %q", consumed, command)
|
|
}
|
|
if consumed, command := router.Handle(0xff9e, 90, 0, false); !consumed || command != "" {
|
|
t.Fatalf("modifier release = %v %q", consumed, command)
|
|
}
|
|
if consumed, command := router.Handle(' ', 65, 0, false); !consumed || command != "" {
|
|
t.Fatalf("space release = %v %q, want no command", consumed, command)
|
|
}
|
|
}
|
|
|
|
func TestApplicationPreferencesShortcut(t *testing.T) {
|
|
router := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(DesktopKeyboardLayout))
|
|
if consumed, command := router.Handle(0xff63, 90, 0, true); !consumed || command != "" {
|
|
t.Fatalf("modifier press = %v %q", consumed, command)
|
|
}
|
|
if consumed, command := router.Handle(' ', 65, controlModifier, true); !consumed || command != ApplicationPreferences {
|
|
t.Fatalf("Cthulhu+Ctrl+Space = %v %q", consumed, command)
|
|
}
|
|
if consumed, command := router.Handle(' ', 65, controlModifier, false); !consumed || command != "" {
|
|
t.Fatalf("shortcut release = %v %q", consumed, command)
|
|
}
|
|
}
|
|
|
|
func TestApplicationPreferenceShiftShortcutsInBothLayouts(t *testing.T) {
|
|
for _, layout := range []KeyboardLayout{DesktopKeyboardLayout, LaptopKeyboardLayout} {
|
|
for _, tc := range []struct {
|
|
modifiers uint16
|
|
want Command
|
|
}{
|
|
{1, ApplicationPreferences}, {controlModifier | 1, ChooseApplicationPreferences},
|
|
} {
|
|
router := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(layout))
|
|
router.Handle(0xff63, 90, 0, true)
|
|
if consumed, command := router.Handle(' ', 65, tc.modifiers, true); !consumed || command != tc.want {
|
|
t.Fatalf("layout %v shortcut = %v %q, want %q", layout, consumed, command, tc.want)
|
|
}
|
|
if consumed, command := router.Handle(' ', 65, tc.modifiers, false); !consumed || command != "" {
|
|
t.Fatalf("shortcut release = %v %q", consumed, command)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLaptopBindingRoutesWhereAmI(t *testing.T) {
|
|
router := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(LaptopKeyboardLayout))
|
|
router.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := router.Handle(returnKeySymbol, 36, 0, true); !consumed || command != WhereAmI {
|
|
t.Fatalf("Return press = %v %q", consumed, command)
|
|
}
|
|
if consumed, command := router.Handle(returnKeySymbol, 36, 0, false); !consumed || command != "" {
|
|
t.Fatalf("Return release = %v %q", consumed, command)
|
|
}
|
|
if consumed, command := router.Handle(0xff63, 118, 0, false); !consumed || command != "" {
|
|
t.Fatalf("modifier release = %v %q, want no command", consumed, command)
|
|
}
|
|
}
|
|
|
|
func TestCellFormulaBindingAndLaptopOverride(t *testing.T) {
|
|
chord := CommandChord{Symbol: '=', CthulhuModifier: true}
|
|
if got := DefaultCommandBindings(DesktopKeyboardLayout)[chord]; got != PresentCellFormula {
|
|
t.Fatalf("desktop formula binding = %q", got)
|
|
}
|
|
if got := DefaultCommandBindings(LaptopKeyboardLayout)[chord]; got != "" {
|
|
t.Fatalf("laptop formula should be unbound by default: %q", got)
|
|
}
|
|
overrides := []BindingOverride{{Layout: LaptopKeyboardLayout, Scope: "global",
|
|
Command: PresentCellFormula, Keys: []string{"Cthulhu+="}}}
|
|
if err := ValidateBindingOverrides(overrides); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bindings, err := ResolveCommandBindings(LaptopKeyboardLayout, false, overrides)
|
|
if err != nil || bindings[chord] != PresentCellFormula {
|
|
t.Fatalf("laptop formula override = %q, %v", bindings[chord], err)
|
|
}
|
|
}
|
|
|
|
func TestCharacterAttributesShortcutInBothLayouts(t *testing.T) {
|
|
for _, layout := range []KeyboardLayout{DesktopKeyboardLayout, LaptopKeyboardLayout} {
|
|
bindings := DefaultCommandBindings(layout)
|
|
if got := bindings[CommandChord{Symbol: 'f', CthulhuModifier: true}]; got != PresentCharacterAttributes {
|
|
t.Fatalf("layout %v Cthulhu+F = %q", layout, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBrowseReturnActivatesWithoutChangingDefaultReturn(t *testing.T) {
|
|
for name, bindings := range map[string]CommandBindings{
|
|
"default": DefaultCommandBindings(DesktopKeyboardLayout),
|
|
"browse": BrowseCommandBindings(DesktopKeyboardLayout),
|
|
} {
|
|
router := NewCommandRouter([]uint32{0xff63}, bindings)
|
|
consumed, command := router.Handle(returnKeySymbol, 36, 0, true)
|
|
if name == "browse" && (!consumed || command != DocumentActivate) {
|
|
t.Fatalf("browse Return = %v %q", consumed, command)
|
|
}
|
|
if name == "default" && (consumed || command != "") {
|
|
t.Fatalf("default Return = %v %q", consumed, command)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDesktopBindingRoutesWhereAmIWithoutModifier(t *testing.T) {
|
|
router := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(DesktopKeyboardLayout))
|
|
if consumed, command := router.Handle(keypadEnterKeySymbol, 104, 0, true); !consumed || command != WhereAmI {
|
|
t.Fatalf("KP Enter press = %v %q", consumed, command)
|
|
}
|
|
if consumed, command := router.Handle(keypadEnterKeySymbol, 104, 0, false); !consumed || command != "" {
|
|
t.Fatalf("KP Enter release = %v %q, want no command", consumed, command)
|
|
}
|
|
}
|
|
|
|
func TestWhereAmIBindingsAreLayoutSpecific(t *testing.T) {
|
|
desktop := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(DesktopKeyboardLayout))
|
|
desktop.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := desktop.Handle(returnKeySymbol, 36, 0, true); consumed || command != "" {
|
|
t.Fatalf("desktop Cthulhu+Return = %v %q, want unbound", consumed, command)
|
|
}
|
|
|
|
laptop := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(LaptopKeyboardLayout))
|
|
if consumed, command := laptop.Handle(keypadEnterKeySymbol, 104, 0, true); consumed || command != "" {
|
|
t.Fatalf("laptop KP Enter = %v %q, want unbound", consumed, command)
|
|
}
|
|
}
|
|
|
|
func TestObjectNavigationBindingsMatchPython(t *testing.T) {
|
|
tests := []struct {
|
|
symbol uint32
|
|
command Command
|
|
}{
|
|
{upKeySymbol, ObjectParent},
|
|
{downKeySymbol, ObjectFirstChild},
|
|
{leftKeySymbol, ObjectPreviousSibling},
|
|
{rightKeySymbol, ObjectNextSibling},
|
|
}
|
|
for _, test := range tests {
|
|
router := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(DesktopKeyboardLayout))
|
|
router.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := router.Handle(test.symbol, 111, controlModifier, true); !consumed || command != test.command {
|
|
t.Fatalf("symbol %#x press = %v %q", test.symbol, consumed, command)
|
|
}
|
|
if consumed, command := router.Handle(test.symbol, 111, controlModifier, false); !consumed || command != "" {
|
|
t.Fatalf("symbol %#x release = %v %q", test.symbol, consumed, command)
|
|
}
|
|
if consumed, command := router.Handle(0xff63, 118, controlModifier, false); !consumed || command != "" {
|
|
t.Fatalf("symbol %#x modifier release = %v %q, want %q", test.symbol, consumed, command, Command(""))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestObjectNavigationRequiresControl(t *testing.T) {
|
|
router := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(DesktopKeyboardLayout))
|
|
router.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := router.Handle(upKeySymbol, 111, 0, true); !consumed || command != IncreaseSpeechSetting {
|
|
t.Fatalf("Cthulhu+Up = %v %q, want speech adjustment", consumed, command)
|
|
}
|
|
}
|
|
|
|
func TestControlPressDoesNotMakeObjectNavigationPassThrough(t *testing.T) {
|
|
router := NewCommandRouter([]uint32{0xff63}, DefaultCommandBindings(DesktopKeyboardLayout))
|
|
router.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := router.Handle(leftControlKeySymbol, 37, 0, true); consumed || command != "" {
|
|
t.Fatalf("Control press = %v %q", consumed, command)
|
|
}
|
|
if !router.IsModifier(leftControlKeySymbol) || !router.IsModifier(rightControlKeySymbol) {
|
|
t.Fatal("Control key was not classified as a chord modifier")
|
|
}
|
|
if consumed, command := router.Handle(upKeySymbol, 111, controlModifier, true); !consumed || command != ObjectParent {
|
|
t.Fatalf("Up press = %v %q", consumed, command)
|
|
}
|
|
}
|
|
|
|
func TestBindingTableMatchesExactAdditionalModifiers(t *testing.T) {
|
|
bindings := CommandBindings{{Symbol: 'x', Modifiers: 4, CthulhuModifier: true}: Preferences}
|
|
router := NewCommandRouter([]uint32{0xff63}, bindings)
|
|
router.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := router.Handle('x', 53, 0, true); consumed || command != "" {
|
|
t.Fatal("binding matched without its Control modifier")
|
|
}
|
|
router.Reset()
|
|
router.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := router.Handle('x', 53, 4, true); !consumed || command != Preferences {
|
|
t.Fatal("binding did not match its Control modifier")
|
|
}
|
|
}
|
|
|
|
func TestBrowseCommandsPreserveShiftAndReleaseOrder(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
symbol uint32
|
|
mods uint16
|
|
want Command
|
|
}{
|
|
{'h', 0, DocumentNextHeading}, {'h', 1, DocumentPreviousHeading},
|
|
{'k', 0, DocumentNextLink}, {'k', 1, DocumentPreviousLink},
|
|
{'b', 0, DocumentNextButton}, {'b', 1, DocumentPreviousButton},
|
|
{'x', 0, DocumentNextCheckBox}, {'x', 1, DocumentPreviousCheckBox},
|
|
{'l', 0, DocumentNextList}, {'l', 1, DocumentPreviousList},
|
|
{'s', 0, DocumentNextSeparator}, {'s', 1, DocumentPreviousSeparator},
|
|
{'q', 0, DocumentNextBlockQuote}, {'q', 1, DocumentPreviousBlockQuote},
|
|
{'c', 0, DocumentNextComboBox}, {'c', 1, DocumentPreviousComboBox},
|
|
{'e', 0, DocumentNextEntry}, {'e', 1, DocumentPreviousEntry},
|
|
{'f', 0, DocumentNextFormField}, {'f', 1, DocumentPreviousFormField},
|
|
{'g', 0, DocumentNextImage}, {'g', 1, DocumentPreviousImage},
|
|
{'m', 0, DocumentNextLandmark}, {'m', 1, DocumentPreviousLandmark},
|
|
{'i', 0, DocumentNextListItem}, {'i', 1, DocumentPreviousListItem},
|
|
{'d', 0, DocumentNextLiveRegion}, {'d', 1, DocumentPreviousLiveRegion},
|
|
{'p', 0, DocumentNextParagraph}, {'p', 1, DocumentPreviousParagraph},
|
|
{'r', 0, DocumentNextRadioButton}, {'r', 1, DocumentPreviousRadioButton},
|
|
{'t', 0, DocumentNextTable}, {'t', 1, DocumentPreviousTable},
|
|
{'u', 0, DocumentNextUnvisitedLink}, {'u', 1, DocumentPreviousUnvisitedLink},
|
|
{'v', 0, DocumentNextVisitedLink}, {'v', 1, DocumentPreviousVisitedLink},
|
|
{'a', 0, DocumentNextClickable}, {'a', 1, DocumentPreviousClickable},
|
|
{'o', 0, DocumentNextChunk}, {'o', 1, DocumentPreviousChunk},
|
|
{'1', 0, DocumentNextHeadingAtLevel1}, {'1', 1, DocumentPreviousHeadingAtLevel1},
|
|
{'2', 0, DocumentNextHeadingAtLevel2}, {'2', 1, DocumentPreviousHeadingAtLevel2},
|
|
{'3', 0, DocumentNextHeadingAtLevel3}, {'3', 1, DocumentPreviousHeadingAtLevel3},
|
|
{'4', 0, DocumentNextHeadingAtLevel4}, {'4', 1, DocumentPreviousHeadingAtLevel4},
|
|
{'5', 0, DocumentNextHeadingAtLevel5}, {'5', 1, DocumentPreviousHeadingAtLevel5},
|
|
{'6', 0, DocumentNextHeadingAtLevel6}, {'6', 1, DocumentPreviousHeadingAtLevel6},
|
|
{'h', 4, ""}, {'k', 8, ""},
|
|
} {
|
|
router := NewCommandRouter([]uint32{0xff63}, BrowseCommandBindings(DesktopKeyboardLayout))
|
|
consumed, command := router.Handle(tc.symbol, 43, tc.mods, true)
|
|
if consumed != (tc.want != "") || command != tc.want {
|
|
t.Fatalf("press: %v %s", consumed, command)
|
|
}
|
|
consumed, command = router.Handle(tc.symbol, 43, tc.mods, false)
|
|
if consumed != (tc.want != "") || command != "" {
|
|
t.Fatalf("release: %v %s want %s", consumed, command, Command(""))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSpeechSettingBindings(t *testing.T) {
|
|
for _, layout := range []KeyboardLayout{DesktopKeyboardLayout, LaptopKeyboardLayout} {
|
|
for _, bindings := range []CommandBindings{DefaultCommandBindings(layout), BrowseCommandBindings(layout)} {
|
|
for _, test := range []struct {
|
|
symbol uint32
|
|
modifiers uint16
|
|
command Command
|
|
}{
|
|
{leftKeySymbol, 0, SelectPreviousSpeechSetting}, {rightKeySymbol, 0, SelectNextSpeechSetting},
|
|
{upKeySymbol, 0, IncreaseSpeechSetting}, {downKeySymbol, 0, DecreaseSpeechSetting},
|
|
{'s', controlModifier | 1, SaveSpeechSettings},
|
|
} {
|
|
router := NewCommandRouter([]uint32{0xff63}, bindings)
|
|
router.Handle(0xff63, 118, 0, true)
|
|
if consumed, command := router.Handle(test.symbol, 111, test.modifiers, true); !consumed || command != test.command {
|
|
t.Fatalf("press: %v %q", consumed, command)
|
|
}
|
|
router.Handle(0xff63, 118, 0, false)
|
|
if consumed, command := router.Handle(test.symbol, 111, test.modifiers, false); !consumed || command != "" {
|
|
t.Fatalf("release: %v %q", consumed, command)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|