effect_test.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package main
  2. import (
  3. "bytes"
  4. "reflect"
  5. "strings"
  6. "testing"
  7. intrgb "netdome.biz/paul/qmk-rgb/internal/rgb"
  8. "netdome.biz/paul/qmk-rgb/internal/via"
  9. )
  10. // impact80Catalog is the catalog of the board these cases describe.
  11. func impact80Catalog(t *testing.T) *intrgb.Catalog {
  12. t.Helper()
  13. catalog, ok := intrgb.CatalogFor(0x36B0, 0x309F)
  14. if !ok {
  15. t.Fatal("CatalogFor(0x36b0, 0x309f) = not found")
  16. }
  17. return catalog
  18. }
  19. func TestResolveEffectTargetsBacklightOnly(t *testing.T) {
  20. targets, skipped, err := resolveEffectTargets(impact80Catalog(t), "rainbow_moving_chevron", impact80Channels())
  21. if err != nil {
  22. t.Fatalf("resolveEffectTargets() unexpected error: %v", err)
  23. }
  24. wantTargets := []intrgb.EffectTarget{{Channel: via.ChannelRgbMatrix, ID: 17}}
  25. if !reflect.DeepEqual(targets, wantTargets) {
  26. t.Errorf("resolveEffectTargets() targets = %v, want %v", targets, wantTargets)
  27. }
  28. wantSkipped := []string{"rgblight", "audio"}
  29. if !reflect.DeepEqual(skipped, wantSkipped) {
  30. t.Errorf("resolveEffectTargets() skipped = %v, want %v", skipped, wantSkipped)
  31. }
  32. }
  33. func TestResolveEffectTargetsBreathing(t *testing.T) {
  34. targets, skipped, err := resolveEffectTargets(impact80Catalog(t), "breathing", impact80Channels())
  35. if err != nil {
  36. t.Fatalf("resolveEffectTargets() unexpected error: %v", err)
  37. }
  38. wantTargets := []intrgb.EffectTarget{
  39. {Channel: via.ChannelRgblight, ID: 4},
  40. {Channel: via.ChannelRgbMatrix, ID: 5},
  41. {Channel: via.ChannelAudio, ID: 4},
  42. }
  43. if !reflect.DeepEqual(targets, wantTargets) {
  44. t.Errorf("resolveEffectTargets() targets = %v, want %v", targets, wantTargets)
  45. }
  46. if len(skipped) != 0 {
  47. t.Errorf("resolveEffectTargets() skipped = %v, want none", skipped)
  48. }
  49. }
  50. func TestResolveEffectTargetsExplicitUnsupported(t *testing.T) {
  51. cases := []struct {
  52. name string
  53. zones []via.Channel
  54. }{
  55. {"rainbow_moving_chevron", []via.Channel{via.ChannelRgblight}},
  56. {"rainbow_wave", []via.Channel{via.ChannelRgbMatrix}},
  57. {"splash", []via.Channel{via.ChannelAudio}},
  58. }
  59. for _, tc := range cases {
  60. t.Run(tc.name+string(tc.zones[0]), func(t *testing.T) {
  61. targets, skipped, err := resolveEffectTargets(impact80Catalog(t), tc.name, tc.zones)
  62. if err == nil {
  63. t.Fatal("resolveEffectTargets() expected error, got nil")
  64. }
  65. if len(targets) != 0 {
  66. t.Errorf("resolveEffectTargets() targets = %v, want none", targets)
  67. }
  68. if len(skipped) != 0 {
  69. t.Errorf("resolveEffectTargets() skipped = %v, want none", skipped)
  70. }
  71. })
  72. }
  73. }
  74. func TestResolveEffectTargetsUnknownName(t *testing.T) {
  75. targets, skipped, err := resolveEffectTargets(impact80Catalog(t), "not_an_effect", impact80Channels())
  76. if err == nil {
  77. t.Fatal("resolveEffectTargets() expected error, got nil")
  78. }
  79. if len(targets) != 0 {
  80. t.Errorf("resolveEffectTargets() targets = %v, want none", targets)
  81. }
  82. if len(skipped) != 0 {
  83. t.Errorf("resolveEffectTargets() skipped = %v, want none", skipped)
  84. }
  85. }
  86. func TestResolveEffectTargetsStaticCompatibility(t *testing.T) {
  87. targets, skipped, err := resolveEffectTargets(impact80Catalog(t), "static", impact80Channels())
  88. if err != nil {
  89. t.Fatalf("resolveEffectTargets() unexpected error: %v", err)
  90. }
  91. wantTargets := []intrgb.EffectTarget{
  92. {Channel: via.ChannelRgblight, ID: 5},
  93. {Channel: via.ChannelRgbMatrix, ID: 1},
  94. {Channel: via.ChannelAudio, ID: 5},
  95. }
  96. if !reflect.DeepEqual(targets, wantTargets) {
  97. t.Errorf("resolveEffectTargets() targets = %v, want %v", targets, wantTargets)
  98. }
  99. if len(skipped) != 0 {
  100. t.Errorf("resolveEffectTargets() skipped = %v, want none", skipped)
  101. }
  102. }
  103. func executeEffectCommand(t *testing.T, protocol *fakeZoneProtocol, target string, args ...string) (string, string, error) {
  104. t.Helper()
  105. originalZone := targetZone
  106. t.Cleanup(func() { targetZone = originalZone })
  107. targetZone = target
  108. t.Cleanup(stubOpenTarget(t, protocol, impact80Display(), impact80Channels(), 0x36B0, 0x309F))
  109. var stdout bytes.Buffer
  110. var stderr bytes.Buffer
  111. cmd := NewEffectCmd()
  112. cmd.SetOut(&stdout)
  113. cmd.SetErr(&stderr)
  114. cmd.SilenceErrors = true
  115. cmd.SilenceUsage = true
  116. cmd.SetArgs(args)
  117. err := cmd.Execute()
  118. return stdout.String(), stderr.String(), err
  119. }
  120. func TestEffectCommandWarnsForSkippedZones(t *testing.T) {
  121. protocol := &fakeZoneProtocol{failAt: -1}
  122. stdout, stderr, err := executeEffectCommand(t, protocol, "", "rainbow_moving_chevron")
  123. if err != nil {
  124. t.Fatalf("Execute() unexpected error: %v", err)
  125. }
  126. if !strings.Contains(stderr, "effect not supported on channel(s): rgblight, audio") {
  127. t.Errorf("stderr = %q, want skipped-zone warning", stderr)
  128. }
  129. if !strings.Contains(stdout, `Effect set to "rainbow_moving_chevron"`) {
  130. t.Errorf("stdout = %q, want success output", stdout)
  131. }
  132. if len(protocol.reports) != 1 {
  133. t.Fatalf("reports = %v, want one Backlight report", protocol.reports)
  134. }
  135. if protocol.reports[0].channel != 3 || protocol.reports[0].param != 2 || protocol.reports[0].value != 17 {
  136. t.Errorf("report = %+v, want Backlight Effect 17", protocol.reports[0])
  137. }
  138. }
  139. func TestEffectCommandRejectsBeforeOpeningProtocol(t *testing.T) {
  140. protocol := &fakeZoneProtocol{failAt: -1}
  141. stdout, _, err := executeEffectCommand(t, protocol, "logo", "rainbow_moving_chevron")
  142. if err == nil {
  143. t.Fatal("Execute() expected error, got nil")
  144. }
  145. if len(protocol.reports) != 0 {
  146. t.Errorf("reports = %v, want no writes before rejection", protocol.reports)
  147. }
  148. if stdout != "" {
  149. t.Errorf("stdout = %q, want no success output", stdout)
  150. }
  151. }
  152. func TestEffectCommandSuppressesSuccessAfterLaterWriteFailure(t *testing.T) {
  153. protocol := &fakeZoneProtocol{failAt: 1}
  154. stdout, _, err := executeEffectCommand(t, protocol, "", "breathing")
  155. if err == nil {
  156. t.Fatal("Execute() expected write error, got nil")
  157. }
  158. if len(protocol.reports) != 1 {
  159. t.Fatalf("reports = %v, want one report before failure", protocol.reports)
  160. }
  161. if strings.Contains(stdout, "Effect set") {
  162. t.Errorf("stdout = %q, want success output suppressed", stdout)
  163. }
  164. }