completion_test.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. package flags
  2. import (
  3. "bytes"
  4. "io"
  5. "os"
  6. "path"
  7. "path/filepath"
  8. "reflect"
  9. "runtime"
  10. "strings"
  11. "testing"
  12. )
  13. type TestComplete struct {
  14. }
  15. func (t *TestComplete) Complete(match string) []Completion {
  16. options := []string{
  17. "hello world",
  18. "hello universe",
  19. "hello multiverse",
  20. }
  21. ret := make([]Completion, 0, len(options))
  22. for _, o := range options {
  23. if strings.HasPrefix(o, match) {
  24. ret = append(ret, Completion{
  25. Item: o,
  26. })
  27. }
  28. }
  29. return ret
  30. }
  31. var completionTestOptions struct {
  32. Verbose bool `short:"v" long:"verbose" description:"Verbose messages"`
  33. Debug bool `short:"d" long:"debug" description:"Enable debug"`
  34. Info bool `short:"i" description:"Display info"`
  35. Version bool `long:"version" description:"Show version"`
  36. Required bool `long:"required" required:"true" description:"This is required"`
  37. Hidden bool `long:"hidden" hidden:"true" description:"This is hidden"`
  38. AddCommand struct {
  39. Positional struct {
  40. Filename Filename
  41. } `positional-args:"yes"`
  42. } `command:"add" description:"add an item"`
  43. AddMultiCommand struct {
  44. Positional struct {
  45. Filename []Filename
  46. } `positional-args:"yes"`
  47. Extra []Filename `short:"f"`
  48. } `command:"add-multi" description:"add multiple items"`
  49. AddMultiCommandFlag struct {
  50. Files []Filename `short:"f"`
  51. } `command:"add-multi-flag" description:"add multiple items via flags"`
  52. RemoveCommand struct {
  53. Other bool `short:"o"`
  54. File Filename `short:"f" long:"filename"`
  55. } `command:"rm" description:"remove an item"`
  56. RenameCommand struct {
  57. Completed TestComplete `short:"c" long:"completed"`
  58. } `command:"rename" description:"rename an item"`
  59. }
  60. type completionTest struct {
  61. Args []string
  62. Completed []string
  63. ShowDescriptions bool
  64. }
  65. var completionTests []completionTest
  66. func init() {
  67. _, sourcefile, _, _ := runtime.Caller(0)
  68. completionTestSourcedir := filepath.Join(filepath.SplitList(path.Dir(sourcefile))...)
  69. completionTestFilename := []string{filepath.Join(completionTestSourcedir, "completion.go"), filepath.Join(completionTestSourcedir, "completion_test.go")}
  70. completionTests = []completionTest{
  71. {
  72. // Short names
  73. []string{"-"},
  74. []string{"--debug", "--required", "--verbose", "--version", "-i"},
  75. false,
  76. },
  77. {
  78. // Short names full
  79. []string{"-i"},
  80. []string{"-i"},
  81. false,
  82. },
  83. {
  84. // Short names concatenated
  85. []string{"-dv"},
  86. []string{"-dv"},
  87. false,
  88. },
  89. {
  90. // Long names
  91. []string{"--"},
  92. []string{"--debug", "--required", "--verbose", "--version"},
  93. false,
  94. },
  95. {
  96. // Long names with descriptions
  97. []string{"--"},
  98. []string{
  99. "--debug # Enable debug",
  100. "--required # This is required",
  101. "--verbose # Verbose messages",
  102. "--version # Show version",
  103. },
  104. true,
  105. },
  106. {
  107. // Long names partial
  108. []string{"--ver"},
  109. []string{"--verbose", "--version"},
  110. false,
  111. },
  112. {
  113. // Commands
  114. []string{""},
  115. []string{"add", "add-multi", "add-multi-flag", "rename", "rm"},
  116. false,
  117. },
  118. {
  119. // Commands with descriptions
  120. []string{""},
  121. []string{
  122. "add # add an item",
  123. "add-multi # add multiple items",
  124. "add-multi-flag # add multiple items via flags",
  125. "rename # rename an item",
  126. "rm # remove an item",
  127. },
  128. true,
  129. },
  130. {
  131. // Commands partial
  132. []string{"r"},
  133. []string{"rename", "rm"},
  134. false,
  135. },
  136. {
  137. // Positional filename
  138. []string{"add", filepath.Join(completionTestSourcedir, "completion")},
  139. completionTestFilename,
  140. false,
  141. },
  142. {
  143. // Multiple positional filename (1 arg)
  144. []string{"add-multi", filepath.Join(completionTestSourcedir, "completion")},
  145. completionTestFilename,
  146. false,
  147. },
  148. {
  149. // Multiple positional filename (2 args)
  150. []string{"add-multi", filepath.Join(completionTestSourcedir, "completion.go"), filepath.Join(completionTestSourcedir, "completion")},
  151. completionTestFilename,
  152. false,
  153. },
  154. {
  155. // Multiple positional filename (3 args)
  156. []string{"add-multi", filepath.Join(completionTestSourcedir, "completion.go"), filepath.Join(completionTestSourcedir, "completion.go"), filepath.Join(completionTestSourcedir, "completion")},
  157. completionTestFilename,
  158. false,
  159. },
  160. {
  161. // Flag filename
  162. []string{"rm", "-f", path.Join(completionTestSourcedir, "completion")},
  163. completionTestFilename,
  164. false,
  165. },
  166. {
  167. // Flag short concat last filename
  168. []string{"rm", "-of", path.Join(completionTestSourcedir, "completion")},
  169. completionTestFilename,
  170. false,
  171. },
  172. {
  173. // Flag concat filename
  174. []string{"rm", "-f" + path.Join(completionTestSourcedir, "completion")},
  175. []string{"-f" + completionTestFilename[0], "-f" + completionTestFilename[1]},
  176. false,
  177. },
  178. {
  179. // Flag equal concat filename
  180. []string{"rm", "-f=" + path.Join(completionTestSourcedir, "completion")},
  181. []string{"-f=" + completionTestFilename[0], "-f=" + completionTestFilename[1]},
  182. false,
  183. },
  184. {
  185. // Flag concat long filename
  186. []string{"rm", "--filename=" + path.Join(completionTestSourcedir, "completion")},
  187. []string{"--filename=" + completionTestFilename[0], "--filename=" + completionTestFilename[1]},
  188. false,
  189. },
  190. {
  191. // Flag long filename
  192. []string{"rm", "--filename", path.Join(completionTestSourcedir, "completion")},
  193. completionTestFilename,
  194. false,
  195. },
  196. {
  197. // Custom completed
  198. []string{"rename", "-c", "hello un"},
  199. []string{"hello universe"},
  200. false,
  201. },
  202. {
  203. // Multiple flag filename
  204. []string{"add-multi-flag", "-f", filepath.Join(completionTestSourcedir, "completion")},
  205. completionTestFilename,
  206. false,
  207. },
  208. }
  209. }
  210. func TestCompletion(t *testing.T) {
  211. p := NewParser(&completionTestOptions, Default)
  212. c := &completion{parser: p}
  213. for _, test := range completionTests {
  214. if test.ShowDescriptions {
  215. continue
  216. }
  217. ret := c.complete(test.Args)
  218. items := make([]string, len(ret))
  219. for i, v := range ret {
  220. items[i] = v.Item
  221. }
  222. if !reflect.DeepEqual(items, test.Completed) {
  223. t.Errorf("Args: %#v, %#v\n Expected: %#v\n Got: %#v", test.Args, test.ShowDescriptions, test.Completed, items)
  224. }
  225. }
  226. }
  227. func TestParserCompletion(t *testing.T) {
  228. for _, test := range completionTests {
  229. if test.ShowDescriptions {
  230. os.Setenv("GO_FLAGS_COMPLETION", "verbose")
  231. } else {
  232. os.Setenv("GO_FLAGS_COMPLETION", "1")
  233. }
  234. tmp := os.Stdout
  235. r, w, _ := os.Pipe()
  236. os.Stdout = w
  237. out := make(chan string)
  238. go func() {
  239. var buf bytes.Buffer
  240. io.Copy(&buf, r)
  241. out <- buf.String()
  242. }()
  243. p := NewParser(&completionTestOptions, None)
  244. p.CompletionHandler = func(items []Completion) {
  245. comp := &completion{parser: p}
  246. comp.print(items, test.ShowDescriptions)
  247. }
  248. _, err := p.ParseArgs(test.Args)
  249. w.Close()
  250. os.Stdout = tmp
  251. if err != nil {
  252. t.Fatalf("Unexpected error: %s", err)
  253. }
  254. got := strings.Split(strings.Trim(<-out, "\n"), "\n")
  255. if !reflect.DeepEqual(got, test.Completed) {
  256. t.Errorf("Expected: %#v\nGot: %#v", test.Completed, got)
  257. }
  258. }
  259. os.Setenv("GO_FLAGS_COMPLETION", "")
  260. }