1# shellcheck shell=sh disable=SC1083,SC2004,SC2016 2 3Describe "getoptions()" 4 Include ./lib/getoptions_base.sh 5 6 parse() { 7 eval "$(getoptions parser_definition _parse)" 8 case $# in 9 0) _parse ;; 10 *) _parse "$@" ;; 11 esac 12 } 13 14 restargs() { 15 parse "$@" 16 eval "set -- $ARGS" 17 if [ $# -gt 0 ]; then 18 echo "$@" 19 fi 20 } 21 22 It "generates option parser" 23 parser_definition() { setup ARGS; echo 'called' >&2; } 24 When call parse 25 The word 1 of stderr should eq "called" 26 The status should be success 27 End 28 29 It "generates option parser with help" 30 parser_definition() { setup ARGS help:usage; } 31 getoptions_help() { echo 'getoptions_help called'; } 32 When call parse 33 The output should eq 'getoptions_help called' 34 The status should be success 35 End 36 37 It "parses options with default parser name" 38 parse() { 39 eval "$(getoptions parser_definition -)" 40 printf '%s ' "$@" 41 } 42 parser_definition() { setup ARGS; echo 'called' >&2; } 43 When call parse 1 2 3 44 The word 1 of stderr should eq "called" 45 The output should eq "1 2 3 " 46 The status should be success 47 End 48 49 Describe 'handling arguments' 50 Context 'when scanning mode is default' 51 parser_definition() { 52 setup ARGS -- 'foo bar' 53 flag FLAG_A -a 54 } 55 Specify "treats non-options as arguments" 56 When call restargs -a 1 -a 2 -a 3 - -- -a 57 The variable FLAG_A should eq 1 58 The output should eq "1 2 3 - -a" 59 End 60 End 61 62 Context "when scanning mode is '+'" 63 parser_definition() { 64 setup ARGS mode:'+' 65 flag FLAG_A -a 66 } 67 Specify "treats rest following a non-option as arguments" 68 When call restargs -a 1 -a 2 -a 3 -- -a 69 The variable FLAG_A should eq 1 70 The output should eq "1 -a 2 -a 3 -- -a" 71 End 72 73 Specify "treats -- as not arguments" 74 When call restargs -a -- -a 75 The variable FLAG_A should eq 1 76 The output should eq "-a" 77 End 78 End 79 80 Context "when scanning mode is '@'" 81 parser_definition() { 82 setup ARGS mode:'@' 83 flag FLAG_A -a 84 } 85 Specify "treats rest following a non-option as arguments" 86 When call restargs -a 1 -a 2 -a 3 -- -a 87 The variable FLAG_A should eq 1 88 The output should eq "1 -a 2 -a 3 -- -a" 89 End 90 91 Specify "treats -- as arguments" 92 When call restargs -a -- -a 93 The variable FLAG_A should eq 1 94 The output should eq "-- -a" 95 End 96 End 97 98 Context 'when the plus attribute disabled (default)' 99 parser_definition() { setup ARGS; } 100 Specify "treats as arguments" 101 When call restargs +o 102 The output should eq "+o" 103 End 104 End 105 End 106 107 Describe 'parser function' 108 Context 'when the option parser ends normally' 109 parser_definition() { 110 setup ARGS 111 flag FLAG_A -a 112 } 113 It "resets OPTIND and OPTARG" 114 # Workaround for ksh 88 115 foo() { [ "$OPTIND" -eq 1 ] || unset OPTIND; } 116 OPTIND=1 && foo 117 118 When call parse -a 119 The variable OPTIND should eq 1 120 The variable OPTARG should be undefined 121 End 122 End 123 End 124 125 Describe 'Default error handler' 126 Context "when specified unknown option" 127 parser_definition() { setup ARGS; } 128 It "displays error" 129 When run parse -x 130 The stderr should eq "Unrecognized option: -x" 131 The status should be failure 132 End 133 End 134 135 Context "when specified unknown long option" 136 parser_definition() { setup ARGS; } 137 It "displays error" 138 When run parse --long 139 The stderr should eq "Unrecognized option: --long" 140 The status should be failure 141 End 142 End 143 144 Context "when specified an argument to flag" 145 parser_definition() { setup ARGS; flag FLAG --flag; } 146 It "displays error" 147 When run parse --flag=value 148 The stderr should eq "Does not allow an argument: --flag" 149 The status should be failure 150 End 151 End 152 153 Context "when missing an argument for parameter" 154 parser_definition() { setup ARGS; param PARAM --param; } 155 It "displays error" 156 When run parse --param 157 The stderr should eq "Requires an argument: --param" 158 The status should be failure 159 End 160 End 161 162 Context 'when the plus attribute enabled' 163 parser_definition() { setup ARGS plus:true; } 164 It "displays error if unknown +option specified" 165 When run restargs +o 166 The stderr should eq "Unrecognized option: +o" 167 The status should be failure 168 End 169 End 170 End 171 172 Describe 'alternative mode' 173 parser_definition() { 174 setup ARGS alt:true 175 flag FLAG --flag 176 param PARAM --param 177 option OPTION --option 178 } 179 It "allow long options to start with a single '-'" 180 When call parse -flag -param p -option=o 181 The variable FLAG should eq 1 182 The variable PARAM should eq "p" 183 The variable OPTION should eq "o" 184 End 185 End 186 187 Describe 'prehook' 188 parser_definition() { 189 prehook() { echo "$@" >&2; invoke "$@"; } 190 setup ARGS alt:true 191 flag FLAG --flag 192 param PARAM --param 193 option OPTION --option 194 msg -- 'message' 195 } 196 It "called before helper functions is called" 197 When call parse -flag -param p -option=o 198 The line 1 of stderr should eq "setup ARGS alt:true" 199 The line 2 of stderr should eq "flag FLAG --flag" 200 The line 3 of stderr should eq "param PARAM --param" 201 The line 4 of stderr should eq "option OPTION --option" 202 The line 5 of stderr should eq "msg -- message" 203 The line 6 of stderr should eq "flag FLAG --flag" 204 The line 7 of stderr should eq "param PARAM --param" 205 The line 8 of stderr should eq "option OPTION --option" 206 The line 9 of stderr should eq "msg -- message" 207 End 208 End 209 210 Describe 'custom error handler' 211 parser_definition() { 212 setup RESTARGS error 213 param PARAM -p 214 param PARAM -q 215 param PARAM --pattern pattern:'foo | bar' 216 param VALID -v validate:'valid "$1"' 217 param ARG --arg validate:arg 218 flag FLAG --flag 219 } 220 valid() { [ "$1" = "-v" ] && return 3; } 221 arg() { false; } 222 error() { 223 case $2 in 224 unknown) echo "custom $2: $3 [$OPTARG]"; return 20 ;; 225 valid:3) echo "valid $2: $3 [$OPTARG]"; return 30 ;; 226 pattern:'foo | bar') echo "pattern $2: $3 [$OPTARG]"; return 40 ;; 227 arg:*) echo "invalid argument [$OPTARG]"; return 1 ;; 228 noarg) echo "noarg [$OPTARG]"; return 1 ;; 229 esac 230 [ "$3" = "-q" ] && echo "$1 [$OPTARG]" && return 1 231 return 0 232 } 233 234 It "display custom error message" 235 When run parse -x 236 The stderr should eq "custom unknown: -x []" 237 The status should eq 20 238 End 239 240 It "display default error message when custom error handler succeeded" 241 When run parse -p 242 The stderr should eq "Requires an argument: -p" 243 The status should eq 1 244 End 245 246 It "receives default error message" 247 When run parse -q 248 The stderr should eq "Requires an argument: -q []" 249 The status should eq 1 250 End 251 252 It "receives exit status of custom validation" 253 When run parse -v value 254 The stderr should eq "valid valid:3: -v [value]" 255 The status should eq 30 256 End 257 258 It "receives pattern" 259 When run parse --pattern baz 260 The stderr should eq "pattern pattern:foo | bar: --pattern [baz]" 261 The status should eq 40 262 End 263 264 It "can refer to the OPTARG variable" 265 When run parse --arg argument 266 The stderr should eq "invalid argument [argument]" 267 The status should eq 1 268 End 269 270 It "can refer to the OPTARG variable" 271 When run parse --flag=argument 272 The stderr should eq "noarg [argument]" 273 The status should eq 1 274 End 275 End 276 277 Describe 'flag helper' 278 It "handles flags" 279 parser_definition() { 280 setup ARGS 281 flag FLAG_A -a 282 flag FLAG_B +b 283 flag FLAG_C --flag-c 284 flag FLAG_D --{no-}flag-d 285 flag FLAG_E --no-flag-e 286 flag FLAG_F --{no-}flag-f 287 flag FLAG_G --with{out}-flag-g 288 flag FLAG_H --without-flag-h 289 flag FLAG_I --with{out}-flag-i 290 } 291 When call parse -a +b --flag-c --flag-d --no-flag-e --no-flag-f --with-flag-g --without-flag-h --without-flag-i 292 The variable FLAG_A should eq 1 293 The variable FLAG_B should eq "" 294 The variable FLAG_C should eq 1 295 The variable FLAG_D should eq 1 296 The variable FLAG_E should eq "" 297 The variable FLAG_F should eq "" 298 The variable FLAG_G should eq 1 299 The variable FLAG_H should eq "" 300 The variable FLAG_I should eq "" 301 End 302 303 It "can change the set value" 304 parser_definition() { 305 setup ARGS 306 flag FLAG_A -a on:ON no:NO 307 flag FLAG_B +b on:ON no:NO 308 } 309 When call parse -a +b 310 The variable FLAG_A should eq "ON" 311 The variable FLAG_B should eq "NO" 312 End 313 314 It "set initial value when not specified flag" 315 BeforeCall FLAG_N=none FLAG_E="" 316 parser_definition() { 317 setup ARGS 318 flag FLAG_A -a on:ON no:NO init:@on 319 flag FLAG_B -b on:ON no:NO init:@no 320 flag FLAG_C -c on:ON no:NO init:'FLAG_C=func' 321 flag FLAG_D -d on:ON no:NO 322 flag FLAG_Q -q on:"a'b\"" 323 flag FLAG_U -u init:@unset 324 flag FLAG_N -n init:@none 325 flag FLAG_E -n init:@export 326 } 327 When call parse -q 328 The variable FLAG_A should eq "ON" 329 The variable FLAG_B should eq "NO" 330 The variable FLAG_C should eq "func" 331 The variable FLAG_D should eq "" 332 The variable FLAG_Q should eq "a'b\"" 333 The variable FLAG_U should be undefined 334 The variable FLAG_N should eq "none" 335 The variable FLAG_E should be exported 336 End 337 338 It "can be used combined short flags" 339 parser_definition() { 340 setup ARGS 341 flag FLAG_A -a 342 flag FLAG_B -b 343 flag FLAG_C -c 344 flag FLAG_D +d init:@on 345 flag FLAG_E +e init:@on 346 flag FLAG_F +f init:@on 347 } 348 When call parse -abc +def 349 The variable FLAG_A should be present 350 The variable FLAG_B should be present 351 The variable FLAG_C should be present 352 The variable FLAG_D should be blank 353 The variable FLAG_E should be blank 354 The variable FLAG_F should be blank 355 End 356 357 It "counts flags" 358 parser_definition() { 359 setup ARGS 360 flag COUNT -c +c counter:true 361 } 362 When call parse -c -c -c +c -c 363 The variable COUNT should eq 3 364 End 365 366 It "calls the function" 367 parser_definition() { 368 setup ARGS 369 flag :'foo "$1"' -f on:ON 370 } 371 foo() { echo "set [$OPTARG] : ${*:-}"; } 372 When run parse -f 373 The output should eq "set [ON] : -f" 374 End 375 376 It "calls the validator" 377 valid() { echo "$OPTARG" "$@"; } 378 parser_definition() { 379 setup ARGS 380 flag FLAG -f +f on:ON no:NO validate:'valid "$1"' 381 } 382 When call parse -f +f 383 The line 1 should eq "ON -f" 384 The line 2 should eq "NO +f" 385 End 386 387 Context 'when common flag value is specified' 388 parser_definition() { 389 setup ARGS on:ON no:NO 390 flag FLAG_A -a 391 flag FLAG_B +b 392 } 393 It "can change the set value" 394 When call parse -a +b 395 The variable FLAG_A should eq "ON" 396 The variable FLAG_B should eq "NO" 397 End 398 End 399 End 400 401 Describe 'param helper' 402 It "handles parameters" 403 parser_definition() { 404 setup ARGS 405 param PARAM_P -p 406 param PARAM_Q -q 407 param PARAM --param 408 } 409 When call parse -p value1 -qvalue2 --param=value3 410 The variable PARAM_P should eq "value1" 411 The variable PARAM_Q should eq "value2" 412 The variable PARAM should eq "value3" 413 End 414 415 It "remains initial value when not specified parameter" 416 parser_definition() { 417 setup ARGS 418 param PARAM_P -p init:="initial" 419 } 420 When call parse 421 The variable PARAM_P should eq "initial" 422 End 423 424 It "calls the function" 425 parser_definition() { 426 setup ARGS 427 param :'foo "$1"' -p 428 } 429 foo() { echo "set [$OPTARG] : ${*:-}"; } 430 When run parse -p 123 431 The output should eq "set [123] : -p" 432 End 433 434 It "calls the validator" 435 valid() { echo "$OPTARG" "$@"; } 436 parser_definition() { 437 setup ARGS 438 param PARAM_P -p validate:'valid "$1"' 439 param PARAM_Q -q validate:'valid "$1"' 440 param PARAM --param validate:'valid "$1"' 441 } 442 When call parse -p value1 -qvalue2 --param=value3 443 The line 1 should eq "value1 -p" 444 The line 2 should eq "value2 -q" 445 The line 3 should eq "value3 --param" 446 End 447 448 Context 'when specified pattern attribute' 449 Parameters 450 FOO success stdout "" 451 BAZ failure stderr "Does not match the pattern (FOO | BAR): -p" 452 End 453 454 It "checks if it matches the pattern" 455 parser_definition() { 456 setup ARGS 457 param PARAM -p pattern:'FOO | BAR' 458 } 459 When run parse -p "$1" 460 The status should be "$2" 461 The "$3" should eq "$4" 462 End 463 End 464 End 465 466 Describe 'option helper' 467 It "handles options" 468 parser_definition() { 469 setup ARGS 470 option OPTION --option 471 option OPTION_O -o on:"default" 472 option OPTION_P -p 473 option OPTION_N --no-option no:"omission" 474 } 475 When call parse --option=value1 -o -pvalue2 --no-option 476 The variable OPTION should eq "value1" 477 The variable OPTION_O should eq "default" 478 The variable OPTION_P should eq "value2" 479 The variable OPTION_N should eq "omission" 480 End 481 482 Context "when specified an argument to --no-option" 483 parser_definition() { 484 setup ARGS 485 option OPTION --no-option 486 } 487 It "displays error" 488 When run parse --no-option=value 489 The stderr should eq "Does not allow an argument: --no-option" 490 The status should be failure 491 End 492 End 493 494 Context "when specified an argument to --without-option" 495 parser_definition() { 496 setup ARGS 497 option OPTION --without-option 498 } 499 It "displays error" 500 When run parse --without-option=value 501 The stderr should eq "Does not allow an argument: --without-option" 502 The status should be failure 503 End 504 End 505 506 It "remains initial value when not specified parameter" 507 parser_definition() { 508 setup ARGS 509 option OPTION_O -p init:="initial" 510 } 511 When call parse 512 The variable OPTION_O should eq "initial" 513 End 514 515 It "calls the function" 516 parser_definition() { 517 setup ARGS 518 option :'foo "$1"' -o 519 } 520 foo() { echo "set [$OPTARG] : ${*:-}"; } 521 When run parse -o123 522 The output should eq "set [123] : -o" 523 End 524 525 It "calls the validator" 526 valid() { echo "$OPTARG" "$@"; } 527 parser_definition() { 528 setup ARGS 529 option OPTION_O -o validate:'valid "$1"' on:"default" 530 option OPTION_P -p validate:'valid "$1"' 531 option OPTION --option validate:'valid "$1"' 532 } 533 When call parse -o -pvalue1 --option=value2 534 The line 1 should eq "default -o" 535 The line 2 should eq "value1 -p" 536 The line 3 should eq "value2 --option" 537 End 538 539 Context 'when specified pattern attribute' 540 Parameters 541 foo success stdout "" 542 baz failure stderr "Does not match the pattern (foo | bar): -o" 543 End 544 545 It "checks if it matches the pattern" 546 parser_definition() { 547 setup ARGS 548 option OPTION -o pattern:'foo | bar' 549 } 550 When run parse -o"$1" 551 The status should be "$2" 552 The "$3" should eq "$4" 553 End 554 End 555 End 556 557 Describe 'disp helper' 558 BeforeRun VERSION=1.0 559 560 It "displays the variable" 561 parser_definition() { 562 setup ARGS 563 disp VERSION -v 564 } 565 When run parse -v 566 The output should eq "1.0" 567 End 568 569 It "calls the function" 570 version() { echo "func: $VERSION"; } 571 parser_definition() { 572 setup ARGS 573 disp :version -v 574 } 575 When run parse -v 576 The output should eq "func: 1.0" 577 End 578 End 579 580 Describe 'msg helper' 581 It "does nothing" 582 parser_definition() { 583 setup ARGS 584 msg -- 'test' 'foo bar' 585 } 586 When run parse 587 The output should be blank 588 End 589 End 590 591 Describe 'subcommand' 592 parser_definition() { 593 setup ARGS 594 flag FLAG -f 595 cmd list 596 } 597 598 Context "when specify a subcommand that exists" 599 Specify "treat subcommands and the rest as arguments" 600 When call restargs -f list -g 1 2 601 The output should eq "list -g 1 2" 602 The variable FLAG should eq "1" 603 End 604 End 605 606 Context "when not specify a subcommand" 607 Specify "parse global options only" 608 When call restargs -f 609 The output should eq "" 610 The variable FLAG should eq "1" 611 End 612 End 613 614 Context "when no subcommand and only arguments are passed" 615 Specify "the first argument is --" 616 When call restargs -f -- list 1 2 617 The output should eq "-- list 1 2" 618 The variable FLAG should eq "1" 619 End 620 End 621 622 Context "when specify a subcommand that not exists" 623 Specify "displays error" 624 When run restargs -f unknown -g 1 2 625 The error should eq "Not a command: unknown" 626 The status should be failure 627 End 628 End 629 End 630End 631