package main import ( "errors" "strings" "testing" intrgb "netdome.biz/paul/qmk-rgb/internal/rgb" "netdome.biz/paul/qmk-rgb/internal/via" ) // The Impact 80's logo and side channels do not accept effect ID 0: the // firmware reads it as "switch the lighting off" and leaves the mode register // on its previous value. A set of "none" therefore leaves the effect running // while the command reports success, which is the contradiction this read-back // exists to prevent. func TestSetEffectVerifiedReportsHeldEffect(t *testing.T) { proto := &verifyingProtocol{applied: map[via.Channel]uint8{via.ChannelRgblight: 1}} targets := []intrgb.EffectTarget{{Channel: via.ChannelRgblight, ID: 0}} results, err := setEffectVerified(proto, targets, impact80Display(), impact80Catalog(t)) if err != nil { t.Fatalf("setEffectVerified() error = %v", err) } if len(results) != 1 { t.Fatalf("results = %d entries, want 1", len(results)) } got := results[0] if got.Name != "logo" { t.Errorf("results[0].Name = %q, want %q", got.Name, "logo") } if got.RequestedID != 0 || got.RequestedName != "none" { t.Errorf("results[0].Requested = %d/%q, want 0/%q", got.RequestedID, got.RequestedName, "none") } if got.AppliedID != 1 || got.AppliedName != "wave" { t.Errorf("results[0].Applied = %d/%q, want 1/%q", got.AppliedID, got.AppliedName, "wave") } if !got.Mismatch() { t.Error("results[0].Mismatch() = false, want true (keyboard kept effect 1)") } } func TestSetEffectVerifiedMatchesWhenApplied(t *testing.T) { proto := &verifyingProtocol{ applied: map[via.Channel]uint8{ via.ChannelRgblight: 1, via.ChannelRgbMatrix: 28, via.ChannelAudio: 1, }, } // "wave" is a different ID on every channel of this board, so the // read-back has to compare per channel, not one value for all. targets := []intrgb.EffectTarget{ {Channel: via.ChannelRgblight, ID: 1}, {Channel: via.ChannelRgbMatrix, ID: 28}, {Channel: via.ChannelAudio, ID: 1}, } results, err := setEffectVerified(proto, targets, impact80Display(), impact80Catalog(t)) if err != nil { t.Fatalf("setEffectVerified() error = %v", err) } if proto.getCalls != 3 { t.Errorf("GetValue calls = %d, want 3", proto.getCalls) } for _, r := range results { if r.Mismatch() { t.Errorf("%s: Mismatch() = true, want false (requested %d, board holds %d)", r.Name, r.RequestedID, r.AppliedID) } } } // An effect the catalog does not name must be reported as unknown rather than // dropped, so a set to a raw index the catalog does not cover stays visible. // 46 is the board's highest ID and is named, so an index above it is what is // left uncatalogued. func TestSetEffectVerifiedNamesUncataloguedEffect(t *testing.T) { proto := &verifyingProtocol{applied: map[via.Channel]uint8{via.ChannelRgbMatrix: 99}} targets := []intrgb.EffectTarget{{Channel: via.ChannelRgbMatrix, ID: 99}} results, err := setEffectVerified(proto, targets, impact80Display(), impact80Catalog(t)) if err != nil { t.Fatalf("setEffectVerified() error = %v", err) } if got := results[0].AppliedName; got != "unknown" { t.Errorf("results[0].AppliedName = %q, want %q", got, "unknown") } } func TestSetEffectVerifiedPropagatesReadError(t *testing.T) { proto := &verifyingProtocol{getErr: errors.New("read timeout")} targets := []intrgb.EffectTarget{{Channel: via.ChannelRgblight, ID: 1}} if _, err := setEffectVerified(proto, targets, impact80Display(), impact80Catalog(t)); err == nil { t.Fatal("setEffectVerified() expected read error, got nil") } } // A keyboard that ignored the set must not be reported as having taken the // effect. func TestEffectCommandReportsHeldEffect(t *testing.T) { t.Cleanup(vendoredDefinitions(t)) proto := &verifyingProtocol{applied: map[via.Channel]uint8{via.ChannelRgblight: 1}} stdout, _, err := executeEffectCommand(t, proto, "logo", "none") if err != nil { t.Fatalf("effect returned error: %v", err) } if strings.Contains(stdout, `Effect set to "none"`) { t.Errorf("stdout = %q, want no success claim for an effect the keyboard kept", stdout) } for _, want := range []string{"logo", "wave", "none"} { if !strings.Contains(stdout, want) { t.Errorf("stdout = %q, want it to name %q", stdout, want) } } } func TestEffectCommandStaysQuietWhenApplied(t *testing.T) { t.Cleanup(vendoredDefinitions(t)) proto := &verifyingProtocol{ applied: map[via.Channel]uint8{via.ChannelRgblight: 1}, } // wave is ID 1 on rgblight; the backlight channel numbers its own effects // differently, so a zone is named rather than every channel. stdout, _, err := executeEffectCommand(t, proto, "rgblight", "wave") if err != nil { t.Fatalf("effect returned error: %v", err) } if got, want := stdout, "Effect set to \"wave\"\n"; got != want { t.Errorf("stdout = %q, want %q", got, want) } }