flags_test.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package main
  2. import (
  3. "os"
  4. "strings"
  5. "testing"
  6. "github.com/spf13/cobra"
  7. )
  8. // The --device flag accepts the 1-based number that `keyboard info` prints.
  9. // Its help text is the only place a user learns that, so it must not
  10. // describe the HID path the flag rejects.
  11. func TestDeviceFlagUsageDescribesANumberNotAPath(t *testing.T) {
  12. flag := newRootCommand().PersistentFlags().Lookup("device")
  13. if flag == nil {
  14. t.Fatal("--device flag not found")
  15. }
  16. if strings.Contains(flag.Usage, "path") {
  17. t.Errorf("--device usage = %q, must not mention a path; the flag rejects HID paths", flag.Usage)
  18. }
  19. if !strings.Contains(flag.Usage, "number") {
  20. t.Errorf("--device usage = %q, want it to say the argument is a number", flag.Usage)
  21. }
  22. }
  23. // Cobra reads a back-quoted word in a flag's usage as the value placeholder
  24. // and renders it where the type name would go, turning `--device into
  25. // `--device keyboard info`. The help must therefore contain no backticks.
  26. func TestFlagUsageHasNoValuePlaceholder(t *testing.T) {
  27. root := newRootCommand()
  28. for _, name := range []string{"device", "definition", "json"} {
  29. flag := root.PersistentFlags().Lookup(name)
  30. if flag == nil {
  31. t.Errorf("--%s is registered on the root, want it", name)
  32. continue
  33. }
  34. if strings.Contains(flag.Usage, "`") {
  35. t.Errorf("--%s usage = %q, backticks make cobra render a bogus value placeholder", name, flag.Usage)
  36. }
  37. }
  38. }
  39. // The flag usage is not the only place a user learns what --device takes. The
  40. // same promise is repeated in three documentation files, and it was wrong in
  41. // all of them at once. Pin the docs to the flag's actual contract by looking
  42. // for a value that looks like a HID path, not for the word "path" — prose
  43. // about why paths are unstable, and the rule that forbids them, must pass.
  44. func TestDocsDoNotOfferAPathValueForDevice(t *testing.T) {
  45. pathLike := []string{"/dev/", "hidraw", `\\?\hid`, "IO/HIDDevice"}
  46. for _, path := range []string{
  47. "../../README.md",
  48. "../../AGENTS.md",
  49. "../../.claude/skills/qmk-rgb/SKILL.md",
  50. } {
  51. data, err := os.ReadFile(path)
  52. if err != nil {
  53. t.Errorf("read %s: %v", path, err)
  54. continue
  55. }
  56. for _, line := range strings.Split(string(data), "\n") {
  57. if !strings.Contains(line, "--device") {
  58. continue
  59. }
  60. for _, needle := range pathLike {
  61. if strings.Contains(line, needle) {
  62. t.Errorf("%s offers a path value for --device: %q", path, strings.TrimSpace(line))
  63. }
  64. }
  65. }
  66. }
  67. }
  68. // The docs must not teach a zone name the resolver rejects. The subsystem
  69. // names are the vocabulary; a board's own channel names come from its definition file.
  70. func TestDocsDoNotTeachAZoneNameTheResolverRejects(t *testing.T) {
  71. for _, path := range []string{
  72. "../../README.md",
  73. "../../AGENTS.md",
  74. "../../.claude/skills/qmk-rgb/SKILL.md",
  75. } {
  76. data, err := os.ReadFile(path)
  77. if err != nil {
  78. t.Errorf("read %s: %v", path, err)
  79. continue
  80. }
  81. body := string(data)
  82. for _, wrong := range []string{"zone logo|backlight", "zone matrix", "zone=matrix"} {
  83. if strings.Contains(body, wrong) {
  84. t.Errorf("%s contains %q, want only the canonical zone names", path, wrong)
  85. }
  86. }
  87. }
  88. }
  89. // The zone is a positional argument now, so its help is the Long text the
  90. // command carries rather than a flag's usage string. Every subsystem name has to
  91. // be discoverable there, because a user reading `brightness --help` has nowhere
  92. // else to learn what a zone may be.
  93. func TestZoneHelpNamesTheChannelVocabulary(t *testing.T) {
  94. cmds := map[string]*cobra.Command{
  95. "effect": NewEffectCmd(),
  96. "brightness": NewBrightnessCmd(),
  97. "speed": NewSpeedCmd(),
  98. "color": NewColorCmd(),
  99. "enable": NewEnableCmd(),
  100. "disable": NewDisableCmd(),
  101. }
  102. for name, cmd := range cmds {
  103. t.Run(name, func(t *testing.T) {
  104. for _, zone := range []string{"backlight", "rgblight", "rgb_matrix", "audio", "led_matrix"} {
  105. if !strings.Contains(cmd.Long, zone) {
  106. t.Errorf("%s help does not name the subsystem %q", name, zone)
  107. }
  108. }
  109. if !strings.Contains(cmd.Long, "all") {
  110. t.Errorf("%s help does not say that all is a zone", name)
  111. }
  112. if !strings.Contains(cmd.Long, "comma") {
  113. t.Errorf("%s help does not say that several zones may be written at once", name)
  114. }
  115. // The board-supplied names are not a fixed vocabulary, so the help
  116. // must not claim they are.
  117. if strings.Contains(cmd.Long, "logo") || strings.Contains(cmd.Long, "side") {
  118. t.Errorf("%s help names channels a board supplies", name)
  119. }
  120. })
  121. }
  122. }