Simplify code and add tests for mouse.go (#30)

This commit is contained in:
Aleksandr Krivoshchekov
2020-11-07 08:43:12 +03:00
committed by GitHub
parent 3aa00243ff
commit 02a0509e34
2 changed files with 431 additions and 26 deletions

View File

@@ -58,6 +58,7 @@ var mouseEventTypes = map[MouseEventType]string{
//
// ESC [M Cb Cx Cy
//
// See: http://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking
func parseX10MouseEvent(buf []byte) (m MouseEvent, err error) {
if len(buf) != 6 || string(buf[:3]) != "\x1b[M" {
return m, errors.New("not an X10 mouse event")
@@ -65,38 +66,55 @@ func parseX10MouseEvent(buf []byte) (m MouseEvent, err error) {
e := buf[3] - 32
switch e {
case 35:
m.Type = MouseMotion
case 64:
m.Type = MouseWheelUp
case 65:
m.Type = MouseWheelDown
default:
switch e & 3 {
case 0:
if e&64 != 0 {
m.Type = MouseWheelUp
} else {
m.Type = MouseLeft
}
case 1:
if e&64 != 0 {
m.Type = MouseWheelDown
} else {
m.Type = MouseMiddle
}
case 2:
const (
bitShift = 0b0000_0100
bitAlt = 0b0000_1000
bitCtrl = 0b0001_0000
bitMotion = 0b0010_0000
bitWheel = 0b0100_0000
bitsMask = 0b0000_0011
bitsLeft = 0b0000_0000
bitsMiddle = 0b0000_0001
bitsRight = 0b0000_0010
bitsRelease = 0b0000_0011
bitsWheelUp = 0b0000_0000
bitsWheelDown = 0b0000_0001
)
if e&bitWheel != 0 {
// Check the low two bits.
switch e & bitsMask {
case bitsWheelUp:
m.Type = MouseWheelUp
case bitsWheelDown:
m.Type = MouseWheelDown
}
} else {
// Check the low two bits.
// We do not separate clicking and dragging.
switch e & bitsMask {
case bitsLeft:
m.Type = MouseLeft
case bitsMiddle:
m.Type = MouseMiddle
case bitsRight:
m.Type = MouseRight
case 3:
m.Type = MouseRelease
case bitsRelease:
if e&bitMotion != 0 {
m.Type = MouseMotion
} else {
m.Type = MouseRelease
}
}
}
if e&8 != 0 {
if e&bitAlt != 0 {
m.Alt = true
}
if e&16 != 0 {
if e&bitCtrl != 0 {
m.Ctrl = true
}

387
mouse_test.go Normal file
View File

@@ -0,0 +1,387 @@
package tea
import "testing"
func TestMouseEvent_String(t *testing.T) {
tt := []struct {
name string
event MouseEvent
expected string
}{
{
name: "unknown",
event: MouseEvent{Type: MouseUnknown},
expected: "unknown",
},
{
name: "left",
event: MouseEvent{Type: MouseLeft},
expected: "left",
},
{
name: "right",
event: MouseEvent{Type: MouseRight},
expected: "right",
},
{
name: "middle",
event: MouseEvent{Type: MouseMiddle},
expected: "middle",
},
{
name: "release",
event: MouseEvent{Type: MouseRelease},
expected: "release",
},
{
name: "wheel up",
event: MouseEvent{Type: MouseWheelUp},
expected: "wheel up",
},
{
name: "wheel down",
event: MouseEvent{Type: MouseWheelDown},
expected: "wheel down",
},
{
name: "motion",
event: MouseEvent{Type: MouseMotion},
expected: "motion",
},
{
name: "alt+left",
event: MouseEvent{
Type: MouseLeft,
Alt: true,
},
expected: "alt+left",
},
{
name: "ctrl+left",
event: MouseEvent{
Type: MouseLeft,
Ctrl: true,
},
expected: "ctrl+left",
},
{
name: "ctrl+alt+left",
event: MouseEvent{
Type: MouseLeft,
Alt: true,
Ctrl: true,
},
expected: "ctrl+alt+left",
},
{
name: "ignore coordinates",
event: MouseEvent{
X: 100,
Y: 200,
Type: MouseLeft,
},
expected: "left",
},
{
name: "broken type",
event: MouseEvent{
Type: MouseEventType(-1000),
},
expected: "",
},
}
for i := range tt {
tc := tt[i]
t.Run(tc.name, func(t *testing.T) {
actual := tc.event.String()
if tc.expected != actual {
t.Fatalf("expected %q but got %q",
tc.expected,
actual,
)
}
})
}
}
func TestParseX10MouseEvent(t *testing.T) {
encode := func(b byte, x, y int) []byte {
return []byte{
'\x1b',
'[',
'M',
byte(32) + b,
byte(x + 32 + 1),
byte(y + 32 + 1),
}
}
tt := []struct {
name string
buf []byte
expected MouseEvent
}{
// Position.
{
name: "zero position",
buf: encode(0b0010_0000, 0, 0),
expected: MouseEvent{
X: 0,
Y: 0,
Type: MouseLeft,
},
},
{
name: "max position",
buf: encode(0b0010_0000, 222, 222), // Because 255 (max int8) - 32 - 1.
expected: MouseEvent{
X: 222,
Y: 222,
Type: MouseLeft,
},
},
// Simple.
{
name: "left",
buf: encode(0b0000_0000, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseLeft,
},
},
{
name: "left in motion",
buf: encode(0b0010_0000, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseLeft,
},
},
{
name: "middle",
buf: encode(0b0000_0001, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseMiddle,
},
},
{
name: "middle in motion",
buf: encode(0b0010_0001, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseMiddle,
},
},
{
name: "right",
buf: encode(0b0000_0010, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseRight,
},
},
{
name: "right in motion",
buf: encode(0b0010_0010, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseRight,
},
},
{
name: "motion",
buf: encode(0b0010_0011, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseMotion,
},
},
{
name: "wheel up",
buf: encode(0b0100_0000, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseWheelUp,
},
},
{
name: "wheel down",
buf: encode(0b0100_0001, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseWheelDown,
},
},
{
name: "release",
buf: encode(0b0000_0011, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseRelease,
},
},
// Combinations.
{
name: "alt+right",
buf: encode(0b0010_1010, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseRight,
Alt: true,
},
},
{
name: "ctrl+right",
buf: encode(0b0011_0010, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseRight,
Ctrl: true,
},
},
{
name: "ctrl+alt+right",
buf: encode(0b0011_1010, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseRight,
Alt: true,
Ctrl: true,
},
},
{
name: "alt+wheel down",
buf: encode(0b0100_1001, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseWheelDown,
Alt: true,
},
},
{
name: "ctrl+wheel down",
buf: encode(0b0101_0001, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseWheelDown,
Ctrl: true,
},
},
{
name: "ctrl+alt+wheel down",
buf: encode(0b0101_1001, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseWheelDown,
Alt: true,
Ctrl: true,
},
},
// Unknown.
{
name: "wheel with unknown bit",
buf: encode(0b0100_0010, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseUnknown,
},
},
{
name: "unknown with modifier",
buf: encode(0b0100_1010, 32, 16),
expected: MouseEvent{
X: 32,
Y: 16,
Type: MouseUnknown,
Alt: true,
},
},
// Overflow position.
{
name: "overflow position",
buf: encode(0b0010_0000, 250, 223), // Because 255 (max int8) - 32 - 1.
expected: MouseEvent{
X: -6,
Y: -33,
Type: MouseLeft,
},
},
}
for i := range tt {
tc := tt[i]
t.Run(tc.name, func(t *testing.T) {
actual, err := parseX10MouseEvent(tc.buf)
if err != nil {
t.Fatalf("unexpected error: %v",
err,
)
}
if tc.expected != actual {
t.Fatalf("expected %#v but got %#v",
tc.expected,
actual,
)
}
})
}
}
func TestParseX10MouseEvent_error(t *testing.T) {
tt := []struct {
name string
buf []byte
}{
{
name: "empty buf",
buf: nil,
},
{
name: "wrong high bit",
buf: []byte("\x1a[M@A1"),
},
{
name: "short buf",
buf: []byte("\x1b[M@A"),
},
{
name: "long buf",
buf: []byte("\x1b[M@A11"),
},
}
for i := range tt {
tc := tt[i]
t.Run(tc.name, func(t *testing.T) {
_, err := parseX10MouseEvent(tc.buf)
if err == nil {
t.Fatalf("expected error but got nil")
}
})
}
}