channel_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. package via
  2. import (
  3. "errors"
  4. "strings"
  5. "testing"
  6. )
  7. // probeQueue scripts one answer per channel the probe visits, so a test can say
  8. // which channels the keyboard has.
  9. func probeQueue(present ...Channel) [][]byte {
  10. queue := make([][]byte, 0, probeChannelMax)
  11. for c := 1; c <= probeChannelMax; c++ {
  12. buf := make([]byte, 32)
  13. if containsChannel(present, Channel(c)) {
  14. buf[0] = byte(CustomGet)
  15. buf[3] = 160
  16. } else {
  17. buf[0] = byte(Unhandled)
  18. }
  19. buf[1] = byte(c)
  20. buf[2] = probeValueID
  21. queue = append(queue, buf)
  22. }
  23. return queue
  24. }
  25. func containsChannel(channels []Channel, want Channel) bool {
  26. for _, c := range channels {
  27. if c == want {
  28. return true
  29. }
  30. }
  31. return false
  32. }
  33. func TestDetectChannelsFindsTheAnsweredChannels(t *testing.T) {
  34. transport := &fakeTransport{queue: probeQueue(ChannelRgblight, ChannelRgbMatrix, ChannelAudio)}
  35. protocol := Protocol{handle: transport}
  36. got, err := protocol.DetectChannels()
  37. if err != nil {
  38. t.Fatalf("DetectChannels() error = %v", err)
  39. }
  40. want := []Channel{ChannelRgblight, ChannelRgbMatrix, ChannelAudio}
  41. if len(got) != len(want) {
  42. t.Fatalf("DetectChannels() = %v, want %v", got, want)
  43. }
  44. for i := range want {
  45. if got[i] != want[i] {
  46. t.Errorf("DetectChannels()[%d] = %d, want %d", i, got[i], want[i])
  47. }
  48. }
  49. }
  50. func TestDetectChannelsAsksBrightnessOnEveryChannelFromOneToFifteen(t *testing.T) {
  51. transport := &fakeTransport{queue: probeQueue()}
  52. protocol := Protocol{handle: transport}
  53. if _, err := protocol.DetectChannels(); err != nil {
  54. t.Fatalf("DetectChannels() error = %v", err)
  55. }
  56. if len(transport.reports) != probeChannelMax {
  57. t.Fatalf("requests = %d, want %d", len(transport.reports), probeChannelMax)
  58. }
  59. for i, report := range transport.reports {
  60. wantChannel := byte(i + 1)
  61. if report[0] != byte(CustomGet) {
  62. t.Errorf("request %d command = 0x%02x, want 0x%02x", i, report[0], CustomGet)
  63. }
  64. if report[1] != wantChannel {
  65. t.Errorf("request %d channel = %d, want %d", i, report[1], wantChannel)
  66. }
  67. if report[2] != probeValueID {
  68. t.Errorf("request %d value = 0x%02x, want 0x%02x (brightness)", i, report[2], probeValueID)
  69. }
  70. }
  71. }
  72. // A channel list missing entries is indistinguishable from a complete one, so a
  73. // transport failure has to abort the probe instead of shrinking it.
  74. func TestDetectChannelsFailsOnATransportErrorMidProbe(t *testing.T) {
  75. transport := &fakeTransport{
  76. queue: probeQueue(ChannelRgblight, ChannelRgbMatrix, ChannelAudio),
  77. readErr: errors.New("read: interrupted system call"),
  78. readErrAfter: 3,
  79. }
  80. protocol := Protocol{handle: transport}
  81. _, err := protocol.DetectChannels()
  82. if err == nil {
  83. t.Fatal("DetectChannels() expected an error, got nil")
  84. }
  85. if !strings.Contains(err.Error(), "channel 4") {
  86. t.Errorf("error = %q, want it to name the channel that failed", err)
  87. }
  88. }
  89. // A 0xFF that matches nothing is a valid "no such channel". Only a 0xFF whose
  90. // echoed channel disagrees is a desynchronised stream.
  91. func TestDetectChannelsAcceptsAWellFormedUnhandledAnswer(t *testing.T) {
  92. transport := &fakeTransport{queue: probeQueue(ChannelRgblight)}
  93. protocol := Protocol{handle: transport}
  94. got, err := protocol.DetectChannels()
  95. if err != nil {
  96. t.Fatalf("DetectChannels() error = %v", err)
  97. }
  98. if len(got) != 1 || got[0] != ChannelRgblight {
  99. t.Errorf("DetectChannels() = %v, want [2]", got)
  100. }
  101. }
  102. func TestDetectChannelsReportsNoChannelsWhenNoneArePresent(t *testing.T) {
  103. transport := &fakeTransport{queue: probeQueue()}
  104. protocol := Protocol{handle: transport}
  105. got, err := protocol.DetectChannels()
  106. if err != nil {
  107. t.Fatalf("DetectChannels() error = %v", err)
  108. }
  109. if len(got) != 0 {
  110. t.Errorf("DetectChannels() = %v, want empty", got)
  111. }
  112. }
  113. func TestChannelSubsystemNames(t *testing.T) {
  114. tests := []struct {
  115. channel Channel
  116. want string
  117. }{
  118. {ChannelBacklight, "backlight"},
  119. {ChannelRgblight, "rgblight"},
  120. {ChannelRgbMatrix, "rgb_matrix"},
  121. {ChannelAudio, "audio"},
  122. {ChannelLedMatrix, "led_matrix"},
  123. {Channel(9), ""},
  124. }
  125. for _, tt := range tests {
  126. if got := tt.channel.Subsystem(); got != tt.want {
  127. t.Errorf("Channel(%d).Subsystem() = %q, want %q", tt.channel, got, tt.want)
  128. }
  129. }
  130. }
  131. // A stale 0xFF — the answer to an earlier request, still in the read buffer —
  132. // looks exactly like absence. Reading the channel out of it is what tells the
  133. // two apart, and a mismatch means the request/response stream is out of step.
  134. func TestDetectChannelsRejectsAnUnhandledAnswerForAnotherChannel(t *testing.T) {
  135. queue := probeQueue(ChannelRgblight, ChannelRgbMatrix, ChannelAudio)
  136. // Channel 2 answers "unhandled", but for a different channel: the stream
  137. // is answering a request that is not this one.
  138. stale := make([]byte, 32)
  139. stale[0] = byte(Unhandled)
  140. stale[1] = 0x07
  141. stale[2] = probeValueID
  142. queue[1] = stale
  143. transport := &fakeTransport{queue: queue}
  144. protocol := Protocol{handle: transport}
  145. _, err := protocol.DetectChannels()
  146. if err == nil {
  147. t.Fatal("DetectChannels() expected an error for an answer belonging to another channel")
  148. }
  149. if !strings.Contains(err.Error(), "channel 2") {
  150. t.Errorf("error = %q, want it to name the channel whose answer did not match", err)
  151. }
  152. }