github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/utils/argparser/args_test.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package argparser 16 17 import ( 18 "reflect" 19 "testing" 20 21 "github.com/stretchr/testify/assert" 22 "github.com/stretchr/testify/require" 23 ) 24 25 var forceOpt = &Option{"force", "f", "", OptionalFlag, "force desc", nil, false} 26 var messageOpt = &Option{"message", "m", "msg", OptionalValue, "msg desc", nil, false} 27 var fileTypeOpt = &Option{"file-type", "", "", OptionalValue, "file type", nil, false} 28 var notOpt = &Option{"not", "", "", OptionalValue, "not desc", nil, true} 29 30 func TestParsing(t *testing.T) { 31 tests := []struct { 32 name string 33 options []*Option 34 args []string 35 expectedOpts map[string]string 36 expectedArgs []string 37 expectedErr string 38 }{ 39 { 40 name: "empty", 41 options: []*Option{}, 42 args: []string{}, 43 expectedOpts: map[string]string{}, 44 expectedArgs: []string{}, 45 }, 46 { 47 name: "no options", 48 options: []*Option{}, 49 args: []string{"a", "b", "c"}, 50 expectedOpts: map[string]string{}, 51 expectedArgs: []string{"a", "b", "c"}, 52 }, 53 { 54 name: "-h", 55 options: []*Option{}, 56 args: []string{"a", "-h", "c"}, 57 expectedErr: "Help", 58 }, 59 { 60 name: "--help", 61 options: []*Option{}, 62 args: []string{"a", "--help", "c"}, 63 expectedErr: "Help", 64 }, 65 { 66 name: "force", 67 options: []*Option{forceOpt}, 68 args: []string{"--force", "b", "c"}, 69 expectedOpts: map[string]string{"force": ""}, 70 expectedArgs: []string{"b", "c"}, 71 }, 72 { 73 name: "force abbrev", 74 options: []*Option{forceOpt}, 75 args: []string{"b", "-f", "c"}, 76 expectedOpts: map[string]string{"force": ""}, 77 expectedArgs: []string{"b", "c"}, 78 }, 79 { 80 name: "message", 81 options: []*Option{forceOpt, messageOpt}, 82 args: []string{"-m", "b", "c"}, 83 expectedOpts: map[string]string{"message": "b"}, 84 expectedArgs: []string{"c"}, 85 }, 86 { 87 name: "message equals value", 88 options: []*Option{forceOpt, messageOpt}, 89 args: []string{"b", "--message=value", "c"}, 90 expectedOpts: map[string]string{"message": "value"}, 91 expectedArgs: []string{"b", "c"}, 92 }, 93 { 94 name: "message colon value", 95 options: []*Option{forceOpt, messageOpt}, 96 args: []string{"b", "--message:value", "c"}, 97 expectedOpts: map[string]string{"message": "value"}, 98 expectedArgs: []string{"b", "c"}, 99 }, 100 { 101 name: "empty string", 102 options: []*Option{forceOpt, messageOpt}, 103 args: []string{"b", "--message=value", ""}, 104 expectedOpts: map[string]string{"message": "value"}, 105 expectedArgs: []string{"b", ""}, 106 }, 107 { 108 name: "value attached to flag", 109 options: []*Option{forceOpt}, 110 args: []string{"-fvalue"}, 111 expectedOpts: map[string]string{"force": ""}, 112 expectedArgs: []string{"value"}, 113 }, 114 { 115 name: "-mvalue", 116 options: []*Option{forceOpt, messageOpt}, 117 args: []string{"b", "-mvalue", "c"}, 118 expectedOpts: map[string]string{"message": "value"}, 119 expectedArgs: []string{"b", "c"}, 120 }, 121 { 122 name: "--messagevalue", 123 options: []*Option{forceOpt, messageOpt}, 124 args: []string{"b", "--messagevalue", "c"}, 125 expectedErr: "error: unknown option `messagevalue'", 126 }, 127 { 128 name: "-fm football", 129 options: []*Option{forceOpt, messageOpt}, 130 args: []string{"-fm football"}, 131 expectedOpts: map[string]string{"message": "football", "force": ""}, 132 expectedArgs: []string{}, 133 }, 134 { 135 name: "-ffootball", 136 options: []*Option{forceOpt, messageOpt}, 137 args: []string{"-ffootball"}, 138 expectedOpts: map[string]string{"force": ""}, 139 expectedArgs: []string{"football"}, 140 }, 141 { 142 name: "-m f", 143 options: []*Option{forceOpt, messageOpt}, 144 args: []string{"-m f"}, 145 expectedOpts: map[string]string{"message": "f"}, 146 expectedArgs: []string{}, 147 }, 148 { 149 name: "-fm", 150 options: []*Option{forceOpt, messageOpt}, 151 args: []string{"-fm"}, 152 expectedErr: "error: no value for option `m'", 153 }, 154 { 155 name: "-m f value", 156 options: []*Option{forceOpt, messageOpt}, 157 args: []string{"-m f", "value"}, 158 expectedOpts: map[string]string{"message": "f"}, 159 expectedArgs: []string{"value"}, 160 }, 161 { 162 name: "--not string list value", 163 options: []*Option{forceOpt, messageOpt, notOpt}, 164 args: []string{"-m f", "value", "--not", "main", "branch"}, 165 expectedOpts: map[string]string{"message": "f", "not": "main,branch"}, 166 expectedArgs: []string{"value"}, 167 }, 168 { 169 name: "-fm value", 170 options: []*Option{forceOpt, messageOpt}, 171 args: []string{"-fm", "value"}, 172 expectedOpts: map[string]string{"message": "value", "force": ""}, 173 expectedArgs: []string{}, 174 }, 175 { 176 name: "file-type not force", 177 options: []*Option{forceOpt, messageOpt, fileTypeOpt}, 178 args: []string{"--file-type=csv"}, 179 expectedOpts: map[string]string{"file-type": "csv"}, 180 expectedArgs: []string{}, 181 }, 182 { 183 name: "unsupported arg", 184 options: []*Option{forceOpt, messageOpt}, 185 args: []string{"-v"}, 186 expectedErr: "error: unknown option `v'", 187 }, 188 { 189 name: "duplicate arg", 190 options: []*Option{forceOpt, messageOpt}, 191 args: []string{"-f", "-f"}, 192 expectedErr: "error: multiple values provided for `force'", 193 }, 194 } 195 196 for _, test := range tests { 197 t.Run(test.name, func(t *testing.T) { 198 parser := NewArgParserWithVariableArgs("test") 199 200 for _, opt := range test.options { 201 parser.SupportOption(opt) 202 } 203 204 exp := &ArgParseResults{test.expectedOpts, test.expectedArgs, parser, NO_POSITIONAL_ARGS} 205 206 res, err := parser.Parse(test.args) 207 if test.expectedErr != "" { 208 require.Error(t, err, test.expectedErr) 209 } else { 210 require.NoError(t, err) 211 assert.Equal(t, exp, res) 212 } 213 }) 214 } 215 } 216 217 func TestValidation(t *testing.T) { 218 ap := NewArgParserWithVariableArgs("test") 219 ap.SupportsString("string", "s", "string_value", "A string") 220 ap.SupportsString("string2", "", "string_value", "Another string") 221 ap.SupportsFlag("flag", "f", "A flag") 222 ap.SupportsFlag("flag2", "", "Another flag") 223 ap.SupportsInt("integer", "n", "num", "A number") 224 ap.SupportsInt("integer2", "", "num", "Another number") 225 apr, err := ap.Parse([]string{"-s", "string", "--flag", "--n", "1234", "a", "b", "c"}) 226 227 if err != nil { 228 t.Fatal(err.Error()) 229 } 230 231 if !apr.ContainsAll("string", "flag", "integer") { 232 t.Error("Missing expected parameter(s)") 233 } 234 235 if apr.ContainsAny("string2", "flag2", "integer2") { 236 t.Error("Contains unexpected parameter(s)") 237 } 238 239 if val := apr.MustGetValue("string"); val != "string" { 240 t.Error("Bad val for -string") 241 } 242 243 if val := apr.GetValueOrDefault("string2", "default"); val != "default" { 244 t.Error("Bad val for -string2") 245 } 246 247 if _, ok := apr.GetValue("string2"); ok { 248 t.Error("Should not be able to get missing parameter string2") 249 } 250 251 if val, ok := apr.GetValue("string"); !ok || val != "string" { 252 t.Error("Bad val for --string") 253 } 254 255 if val, ok := apr.GetInt("integer"); !ok || val != 1234 { 256 t.Error("Bad val for --integer") 257 } 258 259 if val := apr.GetIntOrDefault("integer2", 5678); val != 5678 { 260 t.Error("Bad val for --integer2") 261 } 262 263 trueFlags := apr.AnyFlagsEqualTo(true) 264 falseFlags := apr.AnyFlagsEqualTo(false) 265 266 if trueFlags.Size() != 1 || falseFlags.Size() != 1 { 267 t.Error("AnyFlagsEqualTo error") 268 } 269 270 trueSet := apr.FlagsEqualTo([]string{"flag"}, true) 271 falseSet := apr.FlagsEqualTo([]string{"flag"}, false) 272 273 if trueSet.Size() != 1 && falseSet.Size() != 0 { 274 t.Error("FlagsEqualTo error") 275 } 276 277 expectedArgs := []string{"a", "b", "c"} 278 279 if apr.NArg() != 3 || apr.Arg(0) != "a" || !reflect.DeepEqual(apr.Args, expectedArgs) { 280 t.Error("Arg list issues") 281 } 282 } 283 284 func TestDropValue(t *testing.T) { 285 ap := NewArgParserWithVariableArgs("test") 286 287 ap.SupportsString("string", "", "string_value", "A string") 288 ap.SupportsFlag("flag", "", "A flag") 289 290 apr, err := ap.Parse([]string{"--string", "str", "--flag", "1234"}) 291 if err != nil { 292 t.Fatal(err.Error()) 293 } 294 295 newApr1 := apr.DropValue("string") 296 require.NotEqualf(t, apr, newApr1, "Original value and new value are equal") 297 298 _, hasVal := newApr1.GetValue("string") 299 if hasVal { 300 t.Error("DropValue failed to drop string") 301 } 302 _, hasVal = newApr1.GetValue("flag") 303 if !hasVal { 304 t.Error("DropValue dropped the wrong value") 305 } 306 if newApr1.NArg() != 1 || newApr1.Arg(0) != "1234" { 307 t.Error("DropValue didn't preserve args") 308 } 309 310 newApr2 := apr.DropValue("flag") 311 require.NotEqualf(t, apr, newApr2, "DropValue failes to drop flag") 312 313 _, hasVal = newApr2.GetValue("string") 314 if !hasVal { 315 t.Error("DropValue dropped the wrong value") 316 } 317 _, hasVal = newApr2.GetValue("flag") 318 if hasVal { 319 t.Error("DropValue failed to drop flag") 320 } 321 if newApr2.NArg() != 1 || newApr2.Arg(0) != "1234" { 322 t.Error("DropValue didn't preserve args") 323 } 324 325 }