github.com/posener/flag@v0.0.0-20170525115107-e8c69325eea7/complete_test.go (about) 1 package flag_test 2 3 import ( 4 "io/ioutil" 5 "os" 6 "sort" 7 "strings" 8 "sync" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 13 "github.com/posener/flag" 14 ) 15 16 var once = sync.Once{} 17 18 func chdir() { 19 once.Do(func() { 20 os.Chdir("tests") 21 }) 22 } 23 24 func constCompleter(last string) ([]string, bool) { 25 if strings.HasPrefix("const", last) { 26 return []string{"const"}, true 27 } 28 return []string{}, true 29 } 30 31 func TestComplete(t *testing.T) { 32 // NoParallel: this test modifies environment variables and stdout 33 chdir() 34 35 tests := []struct { 36 line string 37 want []string 38 file string 39 dir string 40 bool bool 41 string string 42 choice string 43 comp string 44 parseError bool 45 }{ 46 { 47 line: "command ", 48 want: []string{"-file", "-dir", "-bool", "-any", "-choice", "-comp"}, 49 }, 50 { 51 line: "command -file", 52 want: []string{"-file"}, 53 parseError: true, 54 }, 55 { 56 line: "command -file ", 57 want: []string{"./", "sub/", "readme.md"}, 58 }, 59 { 60 line: "command -file .", 61 want: []string{"./", "./sub/", "./readme.md"}, 62 file: ".", 63 }, 64 { 65 line: "command -file r", 66 want: []string{"readme.md"}, 67 file: "r", 68 }, 69 { 70 line: "command -file readme.md", 71 want: []string{"readme.md"}, 72 file: "readme.md", 73 }, 74 { 75 line: "command -file s", 76 want: []string{"sub/", "sub/readme.md"}, 77 file: "s", 78 }, 79 { 80 line: "command -file x", 81 want: []string{}, 82 file: "x", 83 }, 84 { 85 line: "command -dir ", 86 want: []string{"./", "sub/"}, 87 }, 88 { 89 line: "command -dir s", 90 want: []string{"sub/"}, 91 dir: "s", 92 }, 93 { 94 line: "command -dir x", 95 want: []string{}, 96 dir: "x", 97 }, 98 { 99 line: "command -dir .", 100 want: []string{"./", "./sub/"}, 101 dir: ".", 102 }, 103 { 104 line: "command -bool ", 105 want: []string{"-file", "-dir", "-bool", "-any", "-choice", "-comp"}, 106 bool: true, 107 }, 108 { 109 line: "command -any ", 110 want: []string{}, 111 }, 112 { 113 line: "command -choice ", 114 want: []string{"a", "b", "c"}, 115 parseError: true, 116 }, 117 { 118 line: "command -choice d", 119 want: []string{}, 120 parseError: true, 121 }, 122 { 123 line: "command -choice a", 124 want: []string{"a"}, 125 choice: "a", 126 }, 127 { 128 line: "command -comp ", 129 want: []string{"const"}, 130 }, 131 { 132 line: "command -comp c", 133 want: []string{"const"}, 134 comp: "c", 135 }, 136 { 137 line: "command -comp x", 138 want: []string{}, 139 comp: "x", 140 }, 141 } 142 143 for _, tt := range tests { 144 t.Run(tt.line, func(t *testing.T) { 145 146 fs := flag.NewFlagSet("command", 0) 147 148 var ( 149 file = fs.File("file", "*.md", "", "file value") 150 dir = fs.Dir("dir", "*", "", "dir value") 151 b = fs.Bool("bool", false, "bool value") 152 s = fs.String("any", "", "string value") 153 choice = fs.Choice("choice", []string{"a", "b", "c"}, "", "choice of string values") 154 comp = fs.StringCompleter("comp", flag.CompleteFn(constCompleter), "", "custom completion function") 155 ) 156 157 os.Setenv("COMP_LINE", tt.line) 158 r, w, err := os.Pipe() 159 if err != nil { 160 t.Fatal(err) 161 } 162 defer r.Close() 163 os.Stdout = w 164 if !fs.Complete() { 165 t.Error("failed to complete") 166 } 167 w.Close() 168 buf, err := ioutil.ReadAll(r) 169 if err != nil { 170 t.Fatal(err) 171 } 172 got := []string{} 173 for _, word := range strings.Split(string(buf), "\n") { 174 if word != "" && notTestFlag(word) { 175 got = append(got, word) 176 } 177 } 178 179 sort.Strings(got) 180 sort.Strings(tt.want) 181 assert.Equal(t, tt.want, got, t.Name()) 182 183 err = fs.Parse(strings.Split(tt.line, " ")[1:]) 184 if err == nil && tt.parseError { 185 t.Error("parse did not parseError when expected", t.Name()) 186 } else if err != nil && !tt.parseError { 187 t.Error("parse parseError when not expected", r, t.Name()) 188 } 189 assert.Equal(t, tt.file, *file, t.Name()) 190 assert.Equal(t, tt.dir, *dir, t.Name()) 191 assert.Equal(t, tt.bool, *b, t.Name()) 192 assert.Equal(t, tt.string, *s, t.Name()) 193 assert.Equal(t, tt.choice, *choice, t.Name()) 194 assert.Equal(t, tt.comp, *comp, t.Name()) 195 }) 196 } 197 } 198 199 // ExampleComplete shows how bash completion works 200 func ExampleComplete() { 201 chdir() 202 203 // define a flag 204 flag.File("file", "*.md", "", "file value") 205 206 // set the environment variable used for setting the completion 207 // look that there is a space after the '-file' flag, indicating that the user 208 // want to complete by that flag 209 os.Setenv("COMP_LINE", "demo -file ") 210 211 // complete 212 flag.Complete() 213 214 // Unordered output: readme.md 215 // sub/ 216 // ./ 217 } 218 219 func notTestFlag(f string) bool { 220 return !strings.HasPrefix(f, "-") || !strings.Contains(f, "test.") 221 }