package hid import ( "os" "testing" "time" ) func TestReadTimesOutWithoutResponse(t *testing.T) { reader, writer, err := os.Pipe() if err != nil { t.Fatalf("os.Pipe() error = %v", err) } defer reader.Close() defer writer.Close() oldTimeout := hidReadTimeout hidReadTimeout = 10 * time.Millisecond defer func() { hidReadTimeout = oldTimeout }() device := Device{fd: int(reader.Fd())} if _, err := device.Read(make([]byte, 1)); err == nil { t.Fatal("Read() error = nil, want timeout") } } func TestParseHex(t *testing.T) { cases := []struct { name string input string want uint16 err bool }{ {"0x36B0", "36b0", 0x36b0, false}, {"0x6666", "6666", 0x6666, false}, {"0x0001", "0001", 0x0001, false}, {"0xFFFF", "ffff", 0xffff, false}, {"0x0000", "0000", 0x0000, false}, {"uppercase", "ABCD", 0xabcd, false}, {"mixed case", "AbCd", 0xabcd, false}, {"6 chars", "0036b0", 0x36b0, false}, {"invalid hex", "12gh", 0, true}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got, err := parseHex(tc.input) if tc.err { if err == nil { t.Errorf("parseHex(%q) expected error, got nil", tc.input) } return } if err != nil { t.Errorf("parseHex(%q) unexpected error: %v", tc.input, err) return } if got != tc.want { t.Errorf("parseHex(%q) = %d, want %d", tc.input, got, tc.want) } }) } } func TestSplitLines(t *testing.T) { cases := []struct { name string input string want int }{ {"single line", "hello", 1}, {"two lines", "hello\nworld", 2}, {"trailing newline", "hello\n", 1}, {"empty", "", 0}, {"single newline", "\n", 1}, {"double newline", "\n\n", 2}, {"with content and newlines", "foo\nbar\n", 2}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got := splitLines(tc.input) if len(got) != tc.want { t.Errorf("splitLines(%q) = %d lines, want %d lines", tc.input, len(got), tc.want) } }) } } func TestIsRawHIDDescriptor(t *testing.T) { rawHID := []byte{ 0x06, 0x60, 0xff, 0x09, 0x61, 0xa1, 0x01, 0x09, 0x62, 0x15, 0x00, 0x26, 0xff, 0x00, 0x95, 0x20, 0x75, 0x08, 0x81, 0x02, 0x09, 0x63, 0x15, 0x00, 0x26, 0xff, 0x00, 0x95, 0x20, 0x75, 0x08, 0x91, 0x02, 0xc0, } keyboard := []byte{ 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0xc0, } if !isRawHIDDescriptor(rawHID) { t.Error("isRawHIDDescriptor(rawHID) = false, want true") } if isRawHIDDescriptor(keyboard) { t.Error("isRawHIDDescriptor(keyboard) = true, want false") } } func TestParseUEVENT(t *testing.T) { ueventData := `DRIVER=hid-generic HID_ID=0003:000036B0:0000309F HID_NAME=RDMCTMZT Impact 80 HID_PHYS=usb-0000:10:00.0-5.4.1.4.4.1/input0 HID_UNIQ= MODALIAS=hid:b0003g0001v000036B0p0000309F ` vid, pid := parseUEVENT(ueventData) if vid != 0x36B0 { t.Errorf("parseUEVENT() vid = 0x%04X, want 0x36B0", vid) } if pid != 0x309F { t.Errorf("parseUEVENT() pid = 0x%04X, want 0x309F", pid) } // Mouse mouse := `DRIVER=hid-generic HID_ID=0003:0000046D:0000C041 HID_NAME=Logitech USB Gaming Mouse ` vid, pid = parseUEVENT(mouse) if vid != 0x046D { t.Errorf("parseUEVENT() mouse vid = 0x%04X, want 0x046D", vid) } if pid != 0xC041 { t.Errorf("parseUEVENT() mouse pid = 0x%04X, want 0xC041", pid) } }