package main import ( "errors" "reflect" "testing" intrgb "netdome.biz/paul/qmk-rgb/internal/rgb" "netdome.biz/paul/qmk-rgb/internal/via" ) type commandReport struct { channel via.Channel param uint8 value uint8 hue uint8 saturation uint8 } type fakeZoneProtocol struct { reports []commandReport failAt int channels []via.Channel } // DetectChannels reports the board's three channels unless a test asks for a // different set. func (f *fakeZoneProtocol) DetectChannels() ([]via.Channel, error) { if f.channels == nil { return impact80Channels(), nil } return f.channels, nil } func (f *fakeZoneProtocol) SetValue(channel via.Channel, param, value uint8) error { if f.failAt >= 0 && f.failAt == len(f.reports) { return errors.New("write failed") } f.reports = append(f.reports, commandReport{ channel: channel, param: param, value: value, }) return nil } func (f *fakeZoneProtocol) SetColor(channel via.Channel, hue, saturation uint8) error { if f.failAt >= 0 && f.failAt == len(f.reports) { return errors.New("write failed") } f.reports = append(f.reports, commandReport{ channel: channel, param: 4, hue: hue, saturation: saturation, }) return nil } func (f *fakeZoneProtocol) GetValue(via.Channel, uint8) ([]byte, error) { return nil, errors.New("unexpected GetValue call") } func (f *fakeZoneProtocol) Close() error { return nil } func TestSelectedChannelOperations(t *testing.T) { defaultZones := []via.Channel{via.ChannelRgblight, via.ChannelRgbMatrix, via.ChannelAudio} sideZones := []via.Channel{via.ChannelAudio} operations := []struct { name string zones []via.Channel sideZones []via.Channel run func(zoneProtocol, []via.Channel) error want []commandReport wantSide []commandReport }{ { name: "brightness", zones: defaultZones, sideZones: sideZones, run: func(protocol zoneProtocol, zones []via.Channel) error { return setBrightnessOnChannels(protocol, zones, 160) }, want: []commandReport{ {channel: 2, param: 1, value: 160}, {channel: 3, param: 1, value: 160}, {channel: 4, param: 1, value: 160}, }, wantSide: []commandReport{{channel: 4, param: 1, value: 160}}, }, { name: "speed", zones: defaultZones, sideZones: sideZones, run: func(protocol zoneProtocol, zones []via.Channel) error { return setSpeedChannels(protocol, zones, 2) }, want: []commandReport{ {channel: 2, param: 3, value: 2}, {channel: 3, param: 3, value: 2}, {channel: 4, param: 3, value: 2}, }, wantSide: []commandReport{{channel: 4, param: 3, value: 2}}, }, { name: "color", zones: defaultZones, sideZones: sideZones, run: func(protocol zoneProtocol, zones []via.Channel) error { return setColorOnChannels(protocol, zones, 85, 255) }, want: []commandReport{ {channel: 2, param: 4, hue: 85, saturation: 255}, {channel: 3, param: 4, hue: 85, saturation: 255}, {channel: 4, param: 4, hue: 85, saturation: 255}, }, wantSide: []commandReport{{channel: 4, param: 4, hue: 85, saturation: 255}}, }, { name: "disable", zones: defaultZones, sideZones: sideZones, run: disableLightingOnChannels, want: []commandReport{ {channel: 2, param: 2, value: 0}, {channel: 2, param: 1, value: 0}, {channel: 3, param: 2, value: 0}, {channel: 3, param: 1, value: 0}, {channel: 4, param: 2, value: 0}, {channel: 4, param: 1, value: 0}, }, wantSide: []commandReport{ {channel: 4, param: 2, value: 0}, {channel: 4, param: 1, value: 0}, }, }, { name: "enable", zones: defaultZones, sideZones: sideZones, run: func(protocol zoneProtocol, channels []via.Channel) error { catalog, _ := intrgb.CatalogFor(0x36B0, 0x309F) return enableLightingOnChannels(protocol, channels, catalog) }, want: []commandReport{ {channel: 2, param: 2, value: 4}, {channel: 2, param: 1, value: 160}, {channel: 3, param: 2, value: 5}, {channel: 3, param: 1, value: 160}, {channel: 4, param: 2, value: 4}, {channel: 4, param: 1, value: 160}, }, wantSide: []commandReport{ {channel: 4, param: 2, value: 4}, {channel: 4, param: 1, value: 160}, }, }, { name: "mode", zones: defaultZones, sideZones: sideZones, run: func(protocol zoneProtocol, zones []via.Channel) error { return setModeOnChannels(protocol, zones, 17) }, want: []commandReport{ {channel: 2, param: 2, value: 17}, {channel: 3, param: 2, value: 17}, {channel: 4, param: 2, value: 17}, }, wantSide: []commandReport{{channel: 4, param: 2, value: 17}}, }, } for _, tc := range operations { t.Run(tc.name+"/default", func(t *testing.T) { protocol := &fakeZoneProtocol{failAt: -1} if err := tc.run(protocol, tc.zones); err != nil { t.Fatalf("operation error = %v", err) } if !reflect.DeepEqual(protocol.reports, tc.want) { t.Errorf("reports = %v, want %v", protocol.reports, tc.want) } }) t.Run(tc.name+"/side", func(t *testing.T) { protocol := &fakeZoneProtocol{failAt: -1} if err := tc.run(protocol, tc.sideZones); err != nil { t.Fatalf("operation error = %v", err) } if !reflect.DeepEqual(protocol.reports, tc.wantSide) { t.Errorf("reports = %v, want %v", protocol.reports, tc.wantSide) } }) } } func TestSelectedChannelStopsOnFirstError(t *testing.T) { protocol := &fakeZoneProtocol{failAt: 1} err := setBrightnessOnChannels(protocol, impact80Channels(), 160) if err == nil { t.Fatal("setBrightnessOnChannels() expected error, got nil") } if len(protocol.reports) != 1 { t.Errorf("reports = %v, want one report before failure", protocol.reports) } }