| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- package main
- import (
- "bytes"
- "path/filepath"
- "reflect"
- "strings"
- "testing"
- intrgb "netdome.biz/paul/qmk-rgb/internal/rgb"
- "netdome.biz/paul/qmk-rgb/internal/via"
- )
- // impact80Catalog is the catalog of the board these cases describe, which comes
- // from the vendored definition file: that file is where every effect name now
- // comes from.
- func impact80Catalog(t *testing.T) *intrgb.Catalog {
- t.Helper()
- def, err := intrgb.LoadDefinition(filepath.Join("..", "..", "definitions", "impact80.json"))
- if err != nil {
- t.Fatalf("LoadDefinition(definitions/impact80.json) error = %v", err)
- }
- return def.Catalog
- }
- // A name the board has but a named channel does not is refused, however many
- // channels can do it. `effect all` asks for every channel, so a name only one of
- // them has is a request the tool cannot carry out rather than one channel written
- // and reported as done.
- func TestResolveEffectTargetsRefusesANameNotEveryNamedChannelHas(t *testing.T) {
- targets, skipped, err := resolveEffectTargets(impact80Catalog(t), "rainbow_moving_chevron", impact80Channels(), true)
- if err == nil {
- t.Fatal("resolveEffectTargets() expected error, got nil")
- }
- if len(targets) != 0 {
- t.Errorf("resolveEffectTargets() targets = %v, want none", targets)
- }
- if len(skipped) != 0 {
- t.Errorf("resolveEffectTargets() skipped = %v, want none", skipped)
- }
- }
- // Without a channel named, a name the board does not have everywhere is skipped
- // with a warning, which is the shape `load` uses when it applies a whole profile.
- func TestResolveEffectTargetsSkipsWhenNoChannelWasNamed(t *testing.T) {
- targets, skipped, err := resolveEffectTargets(impact80Catalog(t), "rainbow_moving_chevron", impact80Channels(), false)
- if err != nil {
- t.Fatalf("resolveEffectTargets() unexpected error: %v", err)
- }
- wantTargets := []intrgb.EffectTarget{{Channel: via.ChannelRgbMatrix, ID: 17}}
- if !reflect.DeepEqual(targets, wantTargets) {
- t.Errorf("resolveEffectTargets() targets = %v, want %v", targets, wantTargets)
- }
- wantSkipped := []string{"rgblight", "audio"}
- if !reflect.DeepEqual(skipped, wantSkipped) {
- t.Errorf("resolveEffectTargets() skipped = %v, want %v", skipped, wantSkipped)
- }
- }
- func TestResolveEffectTargetsBreathing(t *testing.T) {
- targets, skipped, err := resolveEffectTargets(impact80Catalog(t), "breathing", impact80Channels(), true)
- if err != nil {
- t.Fatalf("resolveEffectTargets() unexpected error: %v", err)
- }
- wantTargets := []intrgb.EffectTarget{
- {Channel: via.ChannelRgblight, ID: 4},
- {Channel: via.ChannelRgbMatrix, ID: 5},
- {Channel: via.ChannelAudio, ID: 4},
- }
- if !reflect.DeepEqual(targets, wantTargets) {
- t.Errorf("resolveEffectTargets() targets = %v, want %v", targets, wantTargets)
- }
- if len(skipped) != 0 {
- t.Errorf("resolveEffectTargets() skipped = %v, want none", skipped)
- }
- }
- func TestResolveEffectTargetsExplicitUnsupported(t *testing.T) {
- cases := []struct {
- name string
- zones []via.Channel
- }{
- {"rainbow_moving_chevron", []via.Channel{via.ChannelRgblight}},
- {"rainbow_wave", []via.Channel{via.ChannelRgbMatrix}},
- {"splash", []via.Channel{via.ChannelAudio}},
- }
- for _, tc := range cases {
- t.Run(tc.name+string(tc.zones[0]), func(t *testing.T) {
- targets, skipped, err := resolveEffectTargets(impact80Catalog(t), tc.name, tc.zones, true)
- if err == nil {
- t.Fatal("resolveEffectTargets() expected error, got nil")
- }
- if len(targets) != 0 {
- t.Errorf("resolveEffectTargets() targets = %v, want none", targets)
- }
- if len(skipped) != 0 {
- t.Errorf("resolveEffectTargets() skipped = %v, want none", skipped)
- }
- })
- }
- }
- func TestResolveEffectTargetsUnknownName(t *testing.T) {
- targets, skipped, err := resolveEffectTargets(impact80Catalog(t), "not_an_effect", impact80Channels(), true)
- if err == nil {
- t.Fatal("resolveEffectTargets() expected error, got nil")
- }
- if len(targets) != 0 {
- t.Errorf("resolveEffectTargets() targets = %v, want none", targets)
- }
- if len(skipped) != 0 {
- t.Errorf("resolveEffectTargets() skipped = %v, want none", skipped)
- }
- }
- func TestResolveEffectTargetsStaticCompatibility(t *testing.T) {
- targets, skipped, err := resolveEffectTargets(impact80Catalog(t), "static", impact80Channels(), true)
- if err != nil {
- t.Fatalf("resolveEffectTargets() unexpected error: %v", err)
- }
- wantTargets := []intrgb.EffectTarget{
- {Channel: via.ChannelRgblight, ID: 5},
- {Channel: via.ChannelRgbMatrix, ID: 1},
- {Channel: via.ChannelAudio, ID: 5},
- }
- if !reflect.DeepEqual(targets, wantTargets) {
- t.Errorf("resolveEffectTargets() targets = %v, want %v", targets, wantTargets)
- }
- if len(skipped) != 0 {
- t.Errorf("resolveEffectTargets() skipped = %v, want none", skipped)
- }
- }
- func executeEffectCommand(t *testing.T, protocol rgbProtocol, zone string, args ...string) (string, string, error) {
- t.Helper()
- t.Cleanup(stubOpenTarget(t, protocol, impact80Display(), impact80Channels(), 0x36B0, 0x309F))
- var stdout bytes.Buffer
- var stderr bytes.Buffer
- cmd := NewEffectCmd()
- cmd.SetOut(&stdout)
- cmd.SetErr(&stderr)
- cmd.SilenceErrors = true
- cmd.SilenceUsage = true
- // The zone is the first argument, so the effect name follows it, which is
- // the spelling a user would type.
- cmd.SetArgs(append([]string{zone}, args...))
- err := cmd.Execute()
- return stdout.String(), stderr.String(), err
- }
- // `effect all breathing` reaches every channel, because the name means the same
- // index on all three subsystems of this board.
- func TestEffectAllWritesEveryChannelTheNameIsOn(t *testing.T) {
- t.Cleanup(vendoredDefinitions(t))
- protocol := &fakeZoneProtocol{failAt: -1}
- stdout, _, err := executeEffectCommand(t, protocol, "all", "breathing")
- if err != nil {
- t.Fatalf("Execute() unexpected error: %v", err)
- }
- if !strings.Contains(stdout, `Effect set to "breathing"`) {
- t.Errorf("stdout = %q, want success output", stdout)
- }
- if len(protocol.reports) != 3 {
- t.Fatalf("reports = %v, want one per channel", protocol.reports)
- }
- want := []commandReport{
- {channel: 2, param: 2, value: 4},
- {channel: 3, param: 2, value: 5},
- {channel: 4, param: 2, value: 4},
- }
- if !reflect.DeepEqual(protocol.reports, want) {
- t.Errorf("reports = %v, want %v", protocol.reports, want)
- }
- }
- // Several zones in one argument reach exactly those channels and no others.
- func TestEffectWritesEveryChannelOfAZoneList(t *testing.T) {
- t.Cleanup(vendoredDefinitions(t))
- protocol := &fakeZoneProtocol{failAt: -1}
- _, _, err := executeEffectCommand(t, protocol, "side,logo", "breathing")
- if err != nil {
- t.Fatalf("Execute() unexpected error: %v", err)
- }
- want := []commandReport{
- {channel: 2, param: 2, value: 4},
- {channel: 4, param: 2, value: 4},
- }
- if !reflect.DeepEqual(protocol.reports, want) {
- t.Errorf("reports = %v, want %v", protocol.reports, want)
- }
- }
- func TestEffectCommandWritesNothingForAnUnsupportedEffect(t *testing.T) {
- protocol := &fakeZoneProtocol{failAt: -1}
- stdout, _, err := executeEffectCommand(t, protocol, "logo", "rainbow_moving_chevron")
- if err == nil {
- t.Fatal("Execute() expected error, got nil")
- }
- if len(protocol.reports) != 0 {
- t.Errorf("reports = %v, want no writes before rejection", protocol.reports)
- }
- if stdout != "" {
- t.Errorf("stdout = %q, want no success output", stdout)
- }
- }
- func TestEffectCommandSuppressesSuccessAfterLaterWriteFailure(t *testing.T) {
- t.Cleanup(vendoredDefinitions(t))
- protocol := &fakeZoneProtocol{failAt: 1}
- stdout, _, err := executeEffectCommand(t, protocol, "all", "breathing")
- if err == nil {
- t.Fatal("Execute() expected write error, got nil")
- }
- if len(protocol.reports) != 1 {
- t.Fatalf("reports = %v, want one report before failure", protocol.reports)
- }
- if strings.Contains(stdout, "Effect set") {
- t.Errorf("stdout = %q, want success output suppressed", stdout)
- }
- }
|