brightness_verify_test.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package main
  2. import (
  3. "bytes"
  4. "errors"
  5. "strings"
  6. "testing"
  7. "netdome.biz/paul/qmk-rgb/internal/via"
  8. )
  9. // verifyingProtocol records writes and reports what the keyboard actually
  10. // applied, which on real hardware is not always the requested value.
  11. type verifyingProtocol struct {
  12. reports []commandReport
  13. applied map[via.Channel]uint8
  14. channels []via.Channel
  15. getErr error
  16. getCalls int
  17. }
  18. // impact80Display is the channel naming the Impact 80's keyboards.json entry
  19. // supplies, so summary lines name logo, backlight and side.
  20. func impact80Display() map[uint16]string {
  21. return map[uint16]string{2: "logo", 3: "backlight", 4: "side"}
  22. }
  23. func impact80Channels() []via.Channel {
  24. return []via.Channel{via.ChannelRgblight, via.ChannelRgbMatrix, via.ChannelAudio}
  25. }
  26. // DetectChannels reports the three channels of the board under test unless a
  27. // test asks for a different set.
  28. func (f *verifyingProtocol) DetectChannels() ([]via.Channel, error) {
  29. if f.channels == nil {
  30. return []via.Channel{via.ChannelRgblight, via.ChannelRgbMatrix, via.ChannelAudio}, nil
  31. }
  32. return f.channels, nil
  33. }
  34. func (f *verifyingProtocol) SetValue(channel via.Channel, param, value uint8) error {
  35. f.reports = append(f.reports, commandReport{channel: channel, param: param, value: value})
  36. return nil
  37. }
  38. func (f *verifyingProtocol) SetColor(via.Channel, uint8, uint8) error { return nil }
  39. func (f *verifyingProtocol) GetValue(channel via.Channel, _ uint8) ([]byte, error) {
  40. f.getCalls++
  41. if f.getErr != nil {
  42. return nil, f.getErr
  43. }
  44. return []byte{f.applied[channel]}, nil
  45. }
  46. func (f *verifyingProtocol) Close() error { return nil }
  47. func appliedFor(p *verifyingProtocol, channel via.Channel) uint8 { return p.applied[channel] }
  48. func TestSetBrightnessVerifiedReportsAppliedValue(t *testing.T) {
  49. tests := []struct {
  50. name string
  51. requested uint8
  52. applied uint8
  53. wantWarning bool
  54. }{
  55. {"keyboard honours the value", 100, 100, false},
  56. {"keyboard clamps to 160", 200, 160, true},
  57. {"keyboard scales up", 100, 159, true},
  58. {"both zones clamp", 255, 160, true},
  59. }
  60. for _, tt := range tests {
  61. t.Run(tt.name, func(t *testing.T) {
  62. proto := &verifyingProtocol{
  63. applied: map[via.Channel]uint8{
  64. via.ChannelRgblight: tt.applied,
  65. via.ChannelRgbMatrix: tt.applied,
  66. via.ChannelAudio: tt.applied,
  67. },
  68. }
  69. zones := []via.Channel{via.ChannelRgblight}
  70. results, err := setBrightnessVerified(proto, zones, impact80Display(), tt.requested)
  71. if err != nil {
  72. t.Fatalf("setBrightnessVerified() error = %v", err)
  73. }
  74. if len(results) != 1 {
  75. t.Fatalf("results = %d entries, want 1", len(results))
  76. }
  77. got := results[0]
  78. if got.Name != "logo" {
  79. t.Errorf("results[0].Name = %q, want %q", got.Name, "logo")
  80. }
  81. if got.Requested != tt.requested {
  82. t.Errorf("results[0].Requested = %d, want %d", got.Requested, tt.requested)
  83. }
  84. if got.Applied != tt.applied {
  85. t.Errorf("results[0].Applied = %d, want %d", got.Applied, tt.applied)
  86. }
  87. if got.Mismatch() != tt.wantWarning {
  88. t.Errorf("results[0].Mismatch() = %t, want %t", got.Mismatch(), tt.wantWarning)
  89. }
  90. })
  91. }
  92. }
  93. // The read-back must happen once per written zone, on the right channel.
  94. func TestSetBrightnessVerifiedReadsBackEveryZone(t *testing.T) {
  95. proto := &verifyingProtocol{
  96. applied: map[via.Channel]uint8{
  97. via.ChannelRgblight: 160,
  98. via.ChannelRgbMatrix: 255,
  99. via.ChannelAudio: 160,
  100. },
  101. }
  102. results, err := setBrightnessVerified(proto, impact80Channels(), impact80Display(), 200)
  103. if err != nil {
  104. t.Fatalf("setBrightnessVerified() error = %v", err)
  105. }
  106. if len(results) != 3 {
  107. t.Fatalf("results = %d entries, want 3", len(results))
  108. }
  109. if proto.getCalls != 3 {
  110. t.Errorf("GetValue calls = %d, want 3", proto.getCalls)
  111. }
  112. // A set that fails must not be reported as applied.
  113. if got := appliedFor(proto, via.ChannelRgblight); got != 160 {
  114. t.Errorf("unexpected applied value %d", got)
  115. }
  116. want := map[string]uint8{
  117. "logo": 160,
  118. "backlight": 255,
  119. "side": 160,
  120. }
  121. for _, r := range results {
  122. if r.Applied != want[r.Name] {
  123. t.Errorf("%s applied = %d, want %d", r.Name, r.Applied, want[r.Name])
  124. }
  125. if !r.Mismatch() {
  126. t.Errorf("%s: Mismatch() = false, want true (requested 200)", r.Name)
  127. }
  128. }
  129. }
  130. // A failed read-back must not be silently reported as success.
  131. func TestSetBrightnessVerifiedPropagatesReadError(t *testing.T) {
  132. proto := &verifyingProtocol{getErr: errors.New("read timeout")}
  133. if _, err := setBrightnessVerified(proto, []via.Channel{via.ChannelRgblight}, impact80Display(), 100); err == nil {
  134. t.Fatal("setBrightnessVerified() expected read error, got nil")
  135. }
  136. }
  137. func TestBrightnessCommandStaysQuietWhenApplied(t *testing.T) {
  138. proto := &verifyingProtocol{applied: map[via.Channel]uint8{via.ChannelRgblight: 200}}
  139. t.Cleanup(impact80Target(t, proto))
  140. var out, errOut bytes.Buffer
  141. cmd := NewBrightnessCmd()
  142. cmd.SetOut(&out)
  143. cmd.SetErr(&errOut)
  144. cmd.SetArgs([]string{"200"})
  145. if err := cmd.Execute(); err != nil {
  146. t.Fatalf("brightness returned error: %v", err)
  147. }
  148. if errOut.Len() != 0 {
  149. t.Errorf("stderr = %q, want no warning when the keyboard applied the requested value", errOut.String())
  150. }
  151. if !strings.Contains(out.String(), "200") {
  152. t.Errorf("stdout = %q, want the applied value reported", out.String())
  153. }
  154. }