t / t9902-completion.shon commit Merge branch 'js/t6026-clean-up' into maint (8e26535)
   1#!/bin/sh
   2#
   3# Copyright (c) 2012 Felipe Contreras
   4#
   5
   6test_description='test bash completion'
   7
   8. ./lib-bash.sh
   9
  10complete ()
  11{
  12        # do nothing
  13        return 0
  14}
  15
  16# Be careful when updating this list:
  17#
  18# (1) The build tree may have build artifact from different branch, or
  19#     the user's $PATH may have a random executable that may begin
  20#     with "git-check" that are not part of the subcommands this build
  21#     will ship, e.g.  "check-ignore".  The tests for completion for
  22#     subcommand names tests how "check" is expanded; we limit the
  23#     possible candidates to "checkout" and "check-attr" to make sure
  24#     "check-attr", which is known by the filter function as a
  25#     subcommand to be thrown out, while excluding other random files
  26#     that happen to begin with "check" to avoid letting them get in
  27#     the way.
  28#
  29# (2) A test makes sure that common subcommands are included in the
  30#     completion for "git <TAB>", and a plumbing is excluded.  "add",
  31#     "filter-branch" and "ls-files" are listed for this.
  32
  33GIT_TESTING_COMMAND_COMPLETION='add checkout check-attr filter-branch ls-files'
  34
  35. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash"
  36
  37# We don't need this function to actually join words or do anything special.
  38# Also, it's cleaner to avoid touching bash's internal completion variables.
  39# So let's override it with a minimal version for testing purposes.
  40_get_comp_words_by_ref ()
  41{
  42        while [ $# -gt 0 ]; do
  43                case "$1" in
  44                cur)
  45                        cur=${_words[_cword]}
  46                        ;;
  47                prev)
  48                        prev=${_words[_cword-1]}
  49                        ;;
  50                words)
  51                        words=("${_words[@]}")
  52                        ;;
  53                cword)
  54                        cword=$_cword
  55                        ;;
  56                esac
  57                shift
  58        done
  59}
  60
  61print_comp ()
  62{
  63        local IFS=$'\n'
  64        echo "${COMPREPLY[*]}" > out
  65}
  66
  67run_completion ()
  68{
  69        local -a COMPREPLY _words
  70        local _cword
  71        _words=( $1 )
  72        test "${1: -1}" = ' ' && _words[${#_words[@]}+1]=''
  73        (( _cword = ${#_words[@]} - 1 ))
  74        __git_wrap__git_main && print_comp
  75}
  76
  77# Test high-level completion
  78# Arguments are:
  79# 1: typed text so far (cur)
  80# 2: expected completion
  81test_completion ()
  82{
  83        if test $# -gt 1
  84        then
  85                printf '%s\n' "$2" >expected
  86        else
  87                sed -e 's/Z$//' >expected
  88        fi &&
  89        run_completion "$1" &&
  90        test_cmp expected out
  91}
  92
  93# Test __gitcomp.
  94# The first argument is the typed text so far (cur); the rest are
  95# passed to __gitcomp.  Expected output comes is read from the
  96# standard input, like test_completion().
  97test_gitcomp ()
  98{
  99        local -a COMPREPLY &&
 100        sed -e 's/Z$//' >expected &&
 101        cur="$1" &&
 102        shift &&
 103        __gitcomp "$@" &&
 104        print_comp &&
 105        test_cmp expected out
 106}
 107
 108# Test __gitcomp_nl
 109# Arguments are:
 110# 1: current word (cur)
 111# -: the rest are passed to __gitcomp_nl
 112test_gitcomp_nl ()
 113{
 114        local -a COMPREPLY &&
 115        sed -e 's/Z$//' >expected &&
 116        cur="$1" &&
 117        shift &&
 118        __gitcomp_nl "$@" &&
 119        print_comp &&
 120        test_cmp expected out
 121}
 122
 123invalid_variable_name='${foo.bar}'
 124
 125actual="$TRASH_DIRECTORY/actual"
 126
 127test_expect_success 'setup for __gitdir tests' '
 128        mkdir -p subdir/subsubdir &&
 129        git init otherrepo
 130'
 131
 132test_expect_success '__gitdir - from command line (through $__git_dir)' '
 133        echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
 134        (
 135                __git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
 136                __gitdir >"$actual"
 137        ) &&
 138        test_cmp expected "$actual"
 139'
 140
 141test_expect_success '__gitdir - repo as argument' '
 142        echo "otherrepo/.git" >expected &&
 143        __gitdir "otherrepo" >"$actual" &&
 144        test_cmp expected "$actual"
 145'
 146
 147test_expect_success '__gitdir - remote as argument' '
 148        echo "remote" >expected &&
 149        __gitdir "remote" >"$actual" &&
 150        test_cmp expected "$actual"
 151'
 152
 153test_expect_success '__gitdir - .git directory in cwd' '
 154        echo ".git" >expected &&
 155        __gitdir >"$actual" &&
 156        test_cmp expected "$actual"
 157'
 158
 159test_expect_success '__gitdir - .git directory in parent' '
 160        echo "$(pwd -P)/.git" >expected &&
 161        (
 162                cd subdir/subsubdir &&
 163                __gitdir >"$actual"
 164        ) &&
 165        test_cmp expected "$actual"
 166'
 167
 168test_expect_success '__gitdir - cwd is a .git directory' '
 169        echo "." >expected &&
 170        (
 171                cd .git &&
 172                __gitdir >"$actual"
 173        ) &&
 174        test_cmp expected "$actual"
 175'
 176
 177test_expect_success '__gitdir - parent is a .git directory' '
 178        echo "$(pwd -P)/.git" >expected &&
 179        (
 180                cd .git/refs/heads &&
 181                __gitdir >"$actual"
 182        ) &&
 183        test_cmp expected "$actual"
 184'
 185
 186test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' '
 187        echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
 188        (
 189                GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
 190                export GIT_DIR &&
 191                __gitdir >"$actual"
 192        ) &&
 193        test_cmp expected "$actual"
 194'
 195
 196test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' '
 197        echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
 198        (
 199                GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
 200                export GIT_DIR &&
 201                cd subdir &&
 202                __gitdir >"$actual"
 203        ) &&
 204        test_cmp expected "$actual"
 205'
 206
 207test_expect_success '__gitdir - non-existing $GIT_DIR' '
 208        (
 209                GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
 210                export GIT_DIR &&
 211                test_must_fail __gitdir
 212        )
 213'
 214
 215function pwd_P_W () {
 216        if test_have_prereq MINGW
 217        then
 218                pwd -W
 219        else
 220                pwd -P
 221        fi
 222}
 223
 224test_expect_success '__gitdir - gitfile in cwd' '
 225        echo "$(pwd_P_W)/otherrepo/.git" >expected &&
 226        echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git &&
 227        test_when_finished "rm -f subdir/.git" &&
 228        (
 229                cd subdir &&
 230                __gitdir >"$actual"
 231        ) &&
 232        test_cmp expected "$actual"
 233'
 234
 235test_expect_success '__gitdir - gitfile in parent' '
 236        echo "$(pwd_P_W)/otherrepo/.git" >expected &&
 237        echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git &&
 238        test_when_finished "rm -f subdir/.git" &&
 239        (
 240                cd subdir/subsubdir &&
 241                __gitdir >"$actual"
 242        ) &&
 243        test_cmp expected "$actual"
 244'
 245
 246test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' '
 247        echo "$(pwd -P)/otherrepo/.git" >expected &&
 248        mkdir otherrepo/dir &&
 249        test_when_finished "rm -rf otherrepo/dir" &&
 250        ln -s otherrepo/dir link &&
 251        test_when_finished "rm -f link" &&
 252        (
 253                cd link &&
 254                __gitdir >"$actual"
 255        ) &&
 256        test_cmp expected "$actual"
 257'
 258
 259test_expect_success '__gitdir - not a git repository' '
 260        (
 261                cd subdir/subsubdir &&
 262                GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
 263                export GIT_CEILING_DIRECTORIES &&
 264                test_must_fail __gitdir
 265        )
 266'
 267
 268test_expect_success '__gitcomp - trailing space - options' '
 269        test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message=
 270                --reset-author" <<-EOF
 271        --reuse-message=Z
 272        --reedit-message=Z
 273        --reset-author Z
 274        EOF
 275'
 276
 277test_expect_success '__gitcomp - trailing space - config keys' '
 278        test_gitcomp "br" "branch. branch.autosetupmerge
 279                branch.autosetuprebase browser." <<-\EOF
 280        branch.Z
 281        branch.autosetupmerge Z
 282        branch.autosetuprebase Z
 283        browser.Z
 284        EOF
 285'
 286
 287test_expect_success '__gitcomp - option parameter' '
 288        test_gitcomp "--strategy=re" "octopus ours recursive resolve subtree" \
 289                "" "re" <<-\EOF
 290        recursive Z
 291        resolve Z
 292        EOF
 293'
 294
 295test_expect_success '__gitcomp - prefix' '
 296        test_gitcomp "branch.me" "remote merge mergeoptions rebase" \
 297                "branch.maint." "me" <<-\EOF
 298        branch.maint.merge Z
 299        branch.maint.mergeoptions Z
 300        EOF
 301'
 302
 303test_expect_success '__gitcomp - suffix' '
 304        test_gitcomp "branch.me" "master maint next pu" "branch." \
 305                "ma" "." <<-\EOF
 306        branch.master.Z
 307        branch.maint.Z
 308        EOF
 309'
 310
 311test_expect_success '__gitcomp - doesnt fail because of invalid variable name' '
 312        __gitcomp "$invalid_variable_name"
 313'
 314
 315read -r -d "" refs <<-\EOF
 316maint
 317master
 318next
 319pu
 320EOF
 321
 322test_expect_success '__gitcomp_nl - trailing space' '
 323        test_gitcomp_nl "m" "$refs" <<-EOF
 324        maint Z
 325        master Z
 326        EOF
 327'
 328
 329test_expect_success '__gitcomp_nl - prefix' '
 330        test_gitcomp_nl "--fixup=m" "$refs" "--fixup=" "m" <<-EOF
 331        --fixup=maint Z
 332        --fixup=master Z
 333        EOF
 334'
 335
 336test_expect_success '__gitcomp_nl - suffix' '
 337        test_gitcomp_nl "branch.ma" "$refs" "branch." "ma" "." <<-\EOF
 338        branch.maint.Z
 339        branch.master.Z
 340        EOF
 341'
 342
 343test_expect_success '__gitcomp_nl - no suffix' '
 344        test_gitcomp_nl "ma" "$refs" "" "ma" "" <<-\EOF
 345        maintZ
 346        masterZ
 347        EOF
 348'
 349
 350test_expect_success '__gitcomp_nl - doesnt fail because of invalid variable name' '
 351        __gitcomp_nl "$invalid_variable_name"
 352'
 353
 354test_expect_success '__git_remotes - list remotes from $GIT_DIR/remotes and from config file' '
 355        cat >expect <<-EOF &&
 356        remote_from_file_1
 357        remote_from_file_2
 358        remote_in_config_1
 359        remote_in_config_2
 360        EOF
 361        test_when_finished "rm -rf .git/remotes" &&
 362        mkdir -p .git/remotes &&
 363        >.git/remotes/remote_from_file_1 &&
 364        >.git/remotes/remote_from_file_2 &&
 365        test_when_finished "git remote remove remote_in_config_1" &&
 366        git remote add remote_in_config_1 git://remote_1 &&
 367        test_when_finished "git remote remove remote_in_config_2" &&
 368        git remote add remote_in_config_2 git://remote_2 &&
 369        __git_remotes >actual &&
 370        test_cmp expect actual
 371'
 372
 373test_expect_success '__git_get_config_variables' '
 374        cat >expect <<-EOF &&
 375        name-1
 376        name-2
 377        EOF
 378        test_config interesting.name-1 good &&
 379        test_config interesting.name-2 good &&
 380        test_config subsection.interesting.name-3 bad &&
 381        __git_get_config_variables interesting >actual &&
 382        test_cmp expect actual
 383'
 384
 385test_expect_success '__git_pretty_aliases' '
 386        cat >expect <<-EOF &&
 387        author
 388        hash
 389        EOF
 390        test_config pretty.author "%an %ae" &&
 391        test_config pretty.hash %H &&
 392        __git_pretty_aliases >actual &&
 393        test_cmp expect actual
 394'
 395
 396test_expect_success '__git_aliases' '
 397        cat >expect <<-EOF &&
 398        ci
 399        co
 400        EOF
 401        test_config alias.ci commit &&
 402        test_config alias.co checkout &&
 403        __git_aliases >actual &&
 404        test_cmp expect actual
 405'
 406
 407test_expect_success 'basic' '
 408        run_completion "git " &&
 409        # built-in
 410        grep -q "^add \$" out &&
 411        # script
 412        grep -q "^filter-branch \$" out &&
 413        # plumbing
 414        ! grep -q "^ls-files \$" out &&
 415
 416        run_completion "git f" &&
 417        ! grep -q -v "^f" out
 418'
 419
 420test_expect_success 'double dash "git" itself' '
 421        test_completion "git --" <<-\EOF
 422        --paginate Z
 423        --no-pager Z
 424        --git-dir=
 425        --bare Z
 426        --version Z
 427        --exec-path Z
 428        --exec-path=
 429        --html-path Z
 430        --man-path Z
 431        --info-path Z
 432        --work-tree=
 433        --namespace=
 434        --no-replace-objects Z
 435        --help Z
 436        EOF
 437'
 438
 439test_expect_success 'double dash "git checkout"' '
 440        test_completion "git checkout --" <<-\EOF
 441        --quiet Z
 442        --ours Z
 443        --theirs Z
 444        --track Z
 445        --no-track Z
 446        --merge Z
 447        --conflict=
 448        --orphan Z
 449        --patch Z
 450        EOF
 451'
 452
 453test_expect_success 'general options' '
 454        test_completion "git --ver" "--version " &&
 455        test_completion "git --hel" "--help " &&
 456        test_completion "git --exe" <<-\EOF &&
 457        --exec-path Z
 458        --exec-path=
 459        EOF
 460        test_completion "git --htm" "--html-path " &&
 461        test_completion "git --pag" "--paginate " &&
 462        test_completion "git --no-p" "--no-pager " &&
 463        test_completion "git --git" "--git-dir=" &&
 464        test_completion "git --wor" "--work-tree=" &&
 465        test_completion "git --nam" "--namespace=" &&
 466        test_completion "git --bar" "--bare " &&
 467        test_completion "git --inf" "--info-path " &&
 468        test_completion "git --no-r" "--no-replace-objects "
 469'
 470
 471test_expect_success 'general options plus command' '
 472        test_completion "git --version check" "checkout " &&
 473        test_completion "git --paginate check" "checkout " &&
 474        test_completion "git --git-dir=foo check" "checkout " &&
 475        test_completion "git --bare check" "checkout " &&
 476        test_completion "git --exec-path=foo check" "checkout " &&
 477        test_completion "git --html-path check" "checkout " &&
 478        test_completion "git --no-pager check" "checkout " &&
 479        test_completion "git --work-tree=foo check" "checkout " &&
 480        test_completion "git --namespace=foo check" "checkout " &&
 481        test_completion "git --paginate check" "checkout " &&
 482        test_completion "git --info-path check" "checkout " &&
 483        test_completion "git --no-replace-objects check" "checkout "
 484'
 485
 486test_expect_success 'git --help completion' '
 487        test_completion "git --help ad" "add " &&
 488        test_completion "git --help core" "core-tutorial "
 489'
 490
 491test_expect_success 'setup for ref completion' '
 492        echo content >file1 &&
 493        echo more >file2 &&
 494        git add . &&
 495        git commit -m one &&
 496        git branch mybranch &&
 497        git tag mytag
 498'
 499
 500test_expect_success 'checkout completes ref names' '
 501        test_completion "git checkout m" <<-\EOF
 502        master Z
 503        mybranch Z
 504        mytag Z
 505        EOF
 506'
 507
 508test_expect_success 'show completes all refs' '
 509        test_completion "git show m" <<-\EOF
 510        master Z
 511        mybranch Z
 512        mytag Z
 513        EOF
 514'
 515
 516test_expect_success '<ref>: completes paths' '
 517        test_completion "git show mytag:f" <<-\EOF
 518        file1 Z
 519        file2 Z
 520        EOF
 521'
 522
 523test_expect_success 'complete tree filename with spaces' '
 524        echo content >"name with spaces" &&
 525        git add . &&
 526        git commit -m spaces &&
 527        test_completion "git show HEAD:nam" <<-\EOF
 528        name with spaces Z
 529        EOF
 530'
 531
 532test_expect_success 'complete tree filename with metacharacters' '
 533        echo content >"name with \${meta}" &&
 534        git add . &&
 535        git commit -m meta &&
 536        test_completion "git show HEAD:nam" <<-\EOF
 537        name with ${meta} Z
 538        name with spaces Z
 539        EOF
 540'
 541
 542test_expect_success 'send-email' '
 543        test_completion "git send-email --cov" "--cover-letter " &&
 544        test_completion "git send-email ma" "master "
 545'
 546
 547test_expect_success 'complete files' '
 548        git init tmp && cd tmp &&
 549        test_when_finished "cd .. && rm -rf tmp" &&
 550
 551        echo "expected" > .gitignore &&
 552        echo "out" >> .gitignore &&
 553
 554        git add .gitignore &&
 555        test_completion "git commit " ".gitignore" &&
 556
 557        git commit -m ignore &&
 558
 559        touch new &&
 560        test_completion "git add " "new" &&
 561
 562        git add new &&
 563        git commit -a -m new &&
 564        test_completion "git add " "" &&
 565
 566        git mv new modified &&
 567        echo modify > modified &&
 568        test_completion "git add " "modified" &&
 569
 570        touch untracked &&
 571
 572        : TODO .gitignore should not be here &&
 573        test_completion "git rm " <<-\EOF &&
 574        .gitignore
 575        modified
 576        EOF
 577
 578        test_completion "git clean " "untracked" &&
 579
 580        : TODO .gitignore should not be here &&
 581        test_completion "git mv " <<-\EOF &&
 582        .gitignore
 583        modified
 584        EOF
 585
 586        mkdir dir &&
 587        touch dir/file-in-dir &&
 588        git add dir/file-in-dir &&
 589        git commit -m dir &&
 590
 591        mkdir untracked-dir &&
 592
 593        : TODO .gitignore should not be here &&
 594        test_completion "git mv modified " <<-\EOF &&
 595        .gitignore
 596        dir
 597        modified
 598        untracked
 599        untracked-dir
 600        EOF
 601
 602        test_completion "git commit " "modified" &&
 603
 604        : TODO .gitignore should not be here &&
 605        test_completion "git ls-files " <<-\EOF &&
 606        .gitignore
 607        dir
 608        modified
 609        EOF
 610
 611        touch momified &&
 612        test_completion "git add mom" "momified"
 613'
 614
 615test_expect_success "completion uses <cmd> completion for alias: !sh -c 'git <cmd> ...'" '
 616        test_config alias.co "!sh -c '"'"'git checkout ...'"'"'" &&
 617        test_completion "git co m" <<-\EOF
 618        master Z
 619        mybranch Z
 620        mytag Z
 621        EOF
 622'
 623
 624test_expect_success 'completion uses <cmd> completion for alias: !f () { VAR=val git <cmd> ... }' '
 625        test_config alias.co "!f () { VAR=val git checkout ... ; } f" &&
 626        test_completion "git co m" <<-\EOF
 627        master Z
 628        mybranch Z
 629        mytag Z
 630        EOF
 631'
 632
 633test_expect_success 'completion used <cmd> completion for alias: !f() { : git <cmd> ; ... }' '
 634        test_config alias.co "!f() { : git checkout ; if ... } f" &&
 635        test_completion "git co m" <<-\EOF
 636        master Z
 637        mybranch Z
 638        mytag Z
 639        EOF
 640'
 641
 642test_expect_failure 'complete with tilde expansion' '
 643        git init tmp && cd tmp &&
 644        test_when_finished "cd .. && rm -rf tmp" &&
 645
 646        touch ~/tmp/file &&
 647
 648        test_completion "git add ~/tmp/" "~/tmp/file"
 649'
 650
 651test_done