|
|
@@ -126,6 +126,113 @@ func TestDetectChannelsReportsNoChannelsWhenNoneArePresent(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// effectResponse is one scripted answer to an effect get or set, carrying the
|
|
|
+// value the keyboard is left with.
|
|
|
+func effectResponse(command Message, ch Channel, value byte) []byte {
|
|
|
+ buf := make([]byte, 32)
|
|
|
+ buf[0] = byte(command)
|
|
|
+ buf[1] = byte(ch)
|
|
|
+ buf[2] = effectValueID
|
|
|
+ buf[3] = value
|
|
|
+ return buf
|
|
|
+}
|
|
|
+
|
|
|
+// QMK clamps rather than rejects: a mode at or above the top comes down to it.
|
|
|
+// A single write above the top therefore returns the top itself, which is the
|
|
|
+// whole reason the probe is one round trip instead of a search.
|
|
|
+func TestEffectTopReadsTheTopOffWhatTheFirmwareClampedTo(t *testing.T) {
|
|
|
+ const ch = ChannelRgbMatrix
|
|
|
+ transport := &fakeTransport{queue: [][]byte{
|
|
|
+ effectResponse(CustomGet, ch, 12), // the effect the channel is on
|
|
|
+ effectResponse(CustomSet, ch, 255), // the probe, echoed
|
|
|
+ effectResponse(CustomGet, ch, 45), // what the clamp left
|
|
|
+ effectResponse(CustomSet, ch, 12), // the restore, echoed
|
|
|
+ }}
|
|
|
+ protocol := Protocol{handle: transport}
|
|
|
+
|
|
|
+ got, err := protocol.EffectTop(ch)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("EffectTop() error = %v", err)
|
|
|
+ }
|
|
|
+ if got != 45 {
|
|
|
+ t.Errorf("EffectTop() = %d, want 45", got)
|
|
|
+ }
|
|
|
+ if len(transport.reports) != 4 {
|
|
|
+ t.Fatalf("requests = %d, want 4 (read, probe, read back, restore)", len(transport.reports))
|
|
|
+ }
|
|
|
+ last := transport.reports[3]
|
|
|
+ if last[0] != byte(CustomSet) || last[3] != 12 {
|
|
|
+ t.Errorf("last request = %v, want the original effect 12 written back", last[:4])
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// A channel already sitting on its last effect answers the probe with the value
|
|
|
+// it held, which is the same number the clamp would give. The two cases need not
|
|
|
+// be told apart, so the answer is still the top.
|
|
|
+func TestEffectTopReportsTheTopWhenTheChannelAlreadySatOnIt(t *testing.T) {
|
|
|
+ const ch = ChannelRgbMatrix
|
|
|
+ transport := &fakeTransport{queue: [][]byte{
|
|
|
+ effectResponse(CustomGet, ch, 45),
|
|
|
+ effectResponse(CustomSet, ch, 255),
|
|
|
+ effectResponse(CustomGet, ch, 45),
|
|
|
+ effectResponse(CustomSet, ch, 45),
|
|
|
+ }}
|
|
|
+ protocol := Protocol{handle: transport}
|
|
|
+
|
|
|
+ got, err := protocol.EffectTop(ch)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("EffectTop() error = %v", err)
|
|
|
+ }
|
|
|
+ if got != 45 {
|
|
|
+ t.Errorf("EffectTop() = %d, want 45", got)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// VIA reads a 0 on the effect parameter as "turn this channel off", so the probe
|
|
|
+// must not write one. The restore may: a channel that was on effect 0 goes back
|
|
|
+// to 0, which is the state it was in.
|
|
|
+func TestEffectTopProbesWithoutSwitchingTheChannelOff(t *testing.T) {
|
|
|
+ if effectProbeValue == 0 {
|
|
|
+ t.Fatal("effectProbeValue is 0, which switches the channel off instead of asking")
|
|
|
+ }
|
|
|
+
|
|
|
+ const ch = ChannelRgbMatrix
|
|
|
+ transport := &fakeTransport{queue: [][]byte{
|
|
|
+ effectResponse(CustomGet, ch, 0),
|
|
|
+ effectResponse(CustomSet, ch, effectProbeValue),
|
|
|
+ effectResponse(CustomGet, ch, 6),
|
|
|
+ effectResponse(CustomSet, ch, 0),
|
|
|
+ }}
|
|
|
+ protocol := Protocol{handle: transport}
|
|
|
+
|
|
|
+ if _, err := protocol.EffectTop(ch); err != nil {
|
|
|
+ t.Fatalf("EffectTop() error = %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ probe := transport.reports[1]
|
|
|
+ if probe[0] != byte(CustomSet) || probe[2] != effectValueID {
|
|
|
+ t.Fatalf("request 1 = %v, want a set on the effect parameter", probe[:4])
|
|
|
+ }
|
|
|
+ if probe[3] != effectProbeValue {
|
|
|
+ t.Errorf("probe wrote effect %d, want %d", probe[3], effectProbeValue)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// An effect that cannot be read is a channel the probe cannot restore, so it
|
|
|
+// stops before it writes rather than leaving the channel on the last effect.
|
|
|
+func TestEffectTopStopsBeforeWritingWhenTheEffectCannotBeRead(t *testing.T) {
|
|
|
+ const ch = ChannelRgbMatrix
|
|
|
+ transport := &fakeTransport{queue: [][]byte{effectResponse(Unhandled, ch, 0)}}
|
|
|
+ protocol := Protocol{handle: transport}
|
|
|
+
|
|
|
+ if _, err := protocol.EffectTop(ch); err == nil {
|
|
|
+ t.Fatal("EffectTop() = nil error, want a failure for an effect that cannot be read")
|
|
|
+ }
|
|
|
+ if len(transport.reports) != 1 {
|
|
|
+ t.Errorf("requests = %d, want only the read that failed", len(transport.reports))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func TestChannelSubsystemNames(t *testing.T) {
|
|
|
tests := []struct {
|
|
|
channel Channel
|