1#!/bin/sh 2 3test_description='wildmatch tests' 4 5TEST_PASSES_SANITIZE_LEAK=true 6. ./test-lib.sh 7 8# Disable expensive chain-lint tests; all of the tests in this script 9# are variants of a few trivial test-tool invocations, and there are a lot of 10# them. 11GIT_TEST_CHAIN_LINT_HARDER_DEFAULT=0 12 13should_create_test_file() { 14 file=$1 15 16 case $file in 17 # `touch .` will succeed but obviously not do what we intend 18 # here. 19 ".") 20 return 1 21 ;; 22 # We cannot create a file with an empty filename. 23 "") 24 return 1 25 ;; 26 # The tests that are testing that e.g. foo//bar is matched by 27 # foo/*/bar can't be tested on filesystems since there's no 28 # way we're getting a double slash. 29 *//*) 30 return 1 31 ;; 32 # When testing the difference between foo/bar and foo/bar/ we 33 # can't test the latter. 34 */) 35 return 1 36 ;; 37 # On Windows, \ in paths is silently converted to /, which 38 # would result in the "touch" below working, but the test 39 # itself failing. See 6fd1106aa4 ("t3700: Skip a test with 40 # backslashes in pathspec", 2009-03-13) for prior art and 41 # details. 42 *\\*) 43 if ! test_have_prereq BSLASHPSPEC 44 then 45 return 1 46 fi 47 # NOTE: The ;;& bash extension is not portable, so 48 # this test needs to be at the end of the pattern 49 # list. 50 # 51 # If we want to add more conditional returns we either 52 # need a new case statement, or turn this whole thing 53 # into a series of "if" tests. 54 ;; 55 esac 56 57 58 # On Windows proper (i.e. not Cygwin) many file names which 59 # under Cygwin would be emulated don't work. 60 if test_have_prereq MINGW 61 then 62 case $file in 63 " ") 64 # Files called " " are forbidden on Windows 65 return 1 66 ;; 67 *\<*|*\>*|*:*|*\"*|*\|*|*\?*|*\**) 68 # Files with various special characters aren't 69 # allowed on Windows. Sourced from 70 # https://stackoverflow.com/a/31976060 71 return 1 72 ;; 73 esac 74 fi 75 76 return 0 77} 78 79match_with_function() { 80 text=$1 81 pattern=$2 82 match_expect=$3 83 match_function=$4 84 85 if test "$match_expect" = 1 86 then 87 test_expect_success "$match_function: match '$text' '$pattern'" " 88 test-tool wildmatch $match_function '$text' '$pattern' 89 " 90 elif test "$match_expect" = 0 91 then 92 test_expect_success "$match_function: no match '$text' '$pattern'" " 93 test_must_fail test-tool wildmatch $match_function '$text' '$pattern' 94 " 95 else 96 test_expect_success "PANIC: Test framework error. Unknown matches value $match_expect" 'false' 97 fi 98 99} 100 101match_with_ls_files() { 102 text=$1 103 pattern=$2 104 match_expect=$3 105 match_function=$4 106 ls_files_args=$5 107 108 match_stdout_stderr_cmp=" 109 tr -d '\0' <actual.raw >actual && 110 test_must_be_empty actual.err && 111 test_cmp expect actual" 112 113 if test "$match_expect" = 'E' 114 then 115 if test -e .git/created_test_file 116 then 117 test_expect_success EXPENSIVE_ON_WINDOWS "$match_function (via ls-files): match dies on '$pattern' '$text'" " 118 printf '%s' '$text' >expect && 119 test_must_fail git$ls_files_args ls-files -z -- '$pattern' 120 " 121 else 122 test_expect_failure EXPENSIVE_ON_WINDOWS "$match_function (via ls-files): match skip '$pattern' '$text'" 'false' 123 fi 124 elif test "$match_expect" = 1 125 then 126 if test -e .git/created_test_file 127 then 128 test_expect_success EXPENSIVE_ON_WINDOWS "$match_function (via ls-files): match '$pattern' '$text'" " 129 printf '%s' '$text' >expect && 130 git$ls_files_args ls-files -z -- '$pattern' >actual.raw 2>actual.err && 131 $match_stdout_stderr_cmp 132 " 133 else 134 test_expect_failure EXPENSIVE_ON_WINDOWS "$match_function (via ls-files): match skip '$pattern' '$text'" 'false' 135 fi 136 elif test "$match_expect" = 0 137 then 138 if test -e .git/created_test_file 139 then 140 test_expect_success EXPENSIVE_ON_WINDOWS "$match_function (via ls-files): no match '$pattern' '$text'" " 141 >expect && 142 git$ls_files_args ls-files -z -- '$pattern' >actual.raw 2>actual.err && 143 $match_stdout_stderr_cmp 144 " 145 else 146 test_expect_failure EXPENSIVE_ON_WINDOWS "$match_function (via ls-files): no match skip '$pattern' '$text'" 'false' 147 fi 148 else 149 test_expect_success "PANIC: Test framework error. Unknown matches value $match_expect" 'false' 150 fi 151} 152 153match() { 154 if test "$#" = 6 155 then 156 # When test-tool wildmatch and git ls-files produce the same 157 # result. 158 match_glob=$1 159 match_file_glob=$match_glob 160 match_iglob=$2 161 match_file_iglob=$match_iglob 162 match_pathmatch=$3 163 match_file_pathmatch=$match_pathmatch 164 match_pathmatchi=$4 165 match_file_pathmatchi=$match_pathmatchi 166 text=$5 167 pattern=$6 168 elif test "$#" = 10 169 then 170 match_glob=$1 171 match_iglob=$2 172 match_pathmatch=$3 173 match_pathmatchi=$4 174 match_file_glob=$5 175 match_file_iglob=$6 176 match_file_pathmatch=$7 177 match_file_pathmatchi=$8 178 text=$9 179 pattern=${10} 180 fi 181 182 test_expect_success EXPENSIVE_ON_WINDOWS 'cleanup after previous file test' ' 183 if test -e .git/created_test_file 184 then 185 git reset && 186 git clean -df 187 fi 188 ' 189 190 printf '%s' "$text" >.git/expected_test_file 191 192 test_expect_success EXPENSIVE_ON_WINDOWS "setup match file test for $text" ' 193 file=$(cat .git/expected_test_file) && 194 if should_create_test_file "$file" 195 then 196 dirs=${file%/*} 197 if test "$file" != "$dirs" 198 then 199 mkdir -p -- "$dirs" && 200 touch -- "./$text" 201 else 202 touch -- "./$file" 203 fi && 204 git add -A && 205 printf "%s" "$file" >.git/created_test_file 206 elif test -e .git/created_test_file 207 then 208 rm .git/created_test_file 209 fi 210 ' 211 212 # $1: Case sensitive glob match: test-tool wildmatch & ls-files 213 match_with_function "$text" "$pattern" $match_glob "wildmatch" 214 match_with_ls_files "$text" "$pattern" $match_file_glob "wildmatch" " --glob-pathspecs" 215 216 # $2: Case insensitive glob match: test-tool wildmatch & ls-files 217 match_with_function "$text" "$pattern" $match_iglob "iwildmatch" 218 match_with_ls_files "$text" "$pattern" $match_file_iglob "iwildmatch" " --glob-pathspecs --icase-pathspecs" 219 220 # $3: Case sensitive path match: test-tool wildmatch & ls-files 221 match_with_function "$text" "$pattern" $match_pathmatch "pathmatch" 222 match_with_ls_files "$text" "$pattern" $match_file_pathmatch "pathmatch" "" 223 224 # $4: Case insensitive path match: test-tool wildmatch & ls-files 225 match_with_function "$text" "$pattern" $match_pathmatchi "ipathmatch" 226 match_with_ls_files "$text" "$pattern" $match_file_pathmatchi "ipathmatch" " --icase-pathspecs" 227} 228 229# Basic wildmatch features 230match 1 1 1 1 foo foo 231match 0 0 0 0 foo bar 232match 1 1 1 1 '' "" 233match 1 1 1 1 foo '???' 234match 0 0 0 0 foo '??' 235match 1 1 1 1 foo '*' 236match 1 1 1 1 foo 'f*' 237match 0 0 0 0 foo '*f' 238match 1 1 1 1 foo '*foo*' 239match 1 1 1 1 foobar '*ob*a*r*' 240match 1 1 1 1 aaaaaaabababab '*ab' 241match 1 1 1 1 'foo*' 'foo\*' 242match 0 0 0 0 foobar 'foo\*bar' 243match 1 1 1 1 'f\oo' 'f\\oo' 244match 1 1 1 1 ball '*[al]?' 245match 0 0 0 0 ten '[ten]' 246match 1 1 1 1 ten '**[!te]' 247match 0 0 0 0 ten '**[!ten]' 248match 1 1 1 1 ten 't[a-g]n' 249match 0 0 0 0 ten 't[!a-g]n' 250match 1 1 1 1 ton 't[!a-g]n' 251match 1 1 1 1 ton 't[^a-g]n' 252match 1 1 1 1 'a]b' 'a[]]b' 253match 1 1 1 1 a-b 'a[]-]b' 254match 1 1 1 1 'a]b' 'a[]-]b' 255match 0 0 0 0 aab 'a[]-]b' 256match 1 1 1 1 aab 'a[]a-]b' 257match 1 1 1 1 ']' ']' 258 259# Extended slash-matching features 260match 0 0 1 1 'foo/baz/bar' 'foo*bar' 261match 0 0 1 1 'foo/baz/bar' 'foo**bar' 262match 1 1 1 1 'foobazbar' 'foo**bar' 263match 1 1 1 1 'foo/baz/bar' 'foo/**/bar' 264match 1 1 0 0 'foo/baz/bar' 'foo/**/**/bar' 265match 1 1 1 1 'foo/b/a/z/bar' 'foo/**/bar' 266match 1 1 1 1 'foo/b/a/z/bar' 'foo/**/**/bar' 267match 1 1 0 0 'foo/bar' 'foo/**/bar' 268match 1 1 0 0 'foo/bar' 'foo/**/**/bar' 269match 0 0 1 1 'foo/bar' 'foo?bar' 270match 0 0 1 1 'foo/bar' 'foo[/]bar' 271match 0 0 1 1 'foo/bar' 'foo[^a-z]bar' 272match 0 0 1 1 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' 273match 1 1 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' 274match 1 1 0 0 'foo' '**/foo' 275match 1 1 1 1 'XXX/foo' '**/foo' 276match 1 1 1 1 'bar/baz/foo' '**/foo' 277match 0 0 1 1 'bar/baz/foo' '*/foo' 278match 0 0 1 1 'foo/bar/baz' '**/bar*' 279match 1 1 1 1 'deep/foo/bar/baz' '**/bar/*' 280match 0 0 1 1 'deep/foo/bar/baz/' '**/bar/*' 281match 1 1 1 1 'deep/foo/bar/baz/' '**/bar/**' 282match 0 0 0 0 'deep/foo/bar' '**/bar/*' 283match 1 1 1 1 'deep/foo/bar/' '**/bar/**' 284match 0 0 1 1 'foo/bar/baz' '**/bar**' 285match 1 1 1 1 'foo/bar/baz/x' '*/bar/**' 286match 0 0 1 1 'deep/foo/bar/baz/x' '*/bar/**' 287match 1 1 1 1 'deep/foo/bar/baz/x' '**/bar/*/*' 288 289# Various additional tests 290match 0 0 0 0 'acrt' 'a[c-c]st' 291match 1 1 1 1 'acrt' 'a[c-c]rt' 292match 0 0 0 0 ']' '[!]-]' 293match 1 1 1 1 'a' '[!]-]' 294match 0 0 0 0 '' '\' 295match 0 0 0 0 \ 296 1 1 1 1 '\' '\' 297match 0 0 0 0 'XXX/\' '*/\' 298match 1 1 1 1 'XXX/\' '*/\\' 299match 1 1 1 1 'foo' 'foo' 300match 1 1 1 1 '@foo' '@foo' 301match 0 0 0 0 'foo' '@foo' 302match 1 1 1 1 '[ab]' '\[ab]' 303match 1 1 1 1 '[ab]' '[[]ab]' 304match 1 1 1 1 '[ab]' '[[:]ab]' 305match 0 0 0 0 '[ab]' '[[::]ab]' 306match 1 1 1 1 '[ab]' '[[:digit]ab]' 307match 1 1 1 1 '[ab]' '[\[:]ab]' 308match 1 1 1 1 '?a?b' '\??\?b' 309match 1 1 1 1 'abc' '\a\b\c' 310match 0 0 0 0 \ 311 E E E E 'foo' '' 312match 1 1 1 1 'foo/bar/baz/to' '**/t[o]' 313 314# Character class tests 315match 1 1 1 1 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]' 316match 0 1 0 1 'a' '[[:digit:][:upper:][:space:]]' 317match 1 1 1 1 'A' '[[:digit:][:upper:][:space:]]' 318match 1 1 1 1 '1' '[[:digit:][:upper:][:space:]]' 319match 0 0 0 0 '1' '[[:digit:][:upper:][:spaci:]]' 320match 1 1 1 1 ' ' '[[:digit:][:upper:][:space:]]' 321match 0 0 0 0 '.' '[[:digit:][:upper:][:space:]]' 322match 1 1 1 1 '.' '[[:digit:][:punct:][:space:]]' 323match 1 1 1 1 '5' '[[:xdigit:]]' 324match 1 1 1 1 'f' '[[:xdigit:]]' 325match 1 1 1 1 'D' '[[:xdigit:]]' 326match 1 1 1 1 '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' 327match 1 1 1 1 '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]' 328match 1 1 1 1 '5' '[a-c[:digit:]x-z]' 329match 1 1 1 1 'b' '[a-c[:digit:]x-z]' 330match 1 1 1 1 'y' '[a-c[:digit:]x-z]' 331match 0 0 0 0 'q' '[a-c[:digit:]x-z]' 332 333# Additional tests, including some malformed wildmatch patterns 334match 1 1 1 1 ']' '[\\-^]' 335match 0 0 0 0 '[' '[\\-^]' 336match 1 1 1 1 '-' '[\-_]' 337match 1 1 1 1 ']' '[\]]' 338match 0 0 0 0 '\]' '[\]]' 339match 0 0 0 0 '\' '[\]]' 340match 0 0 0 0 'ab' 'a[]b' 341match 0 0 0 0 \ 342 1 1 1 1 'a[]b' 'a[]b' 343match 0 0 0 0 \ 344 1 1 1 1 'ab[' 'ab[' 345match 0 0 0 0 'ab' '[!' 346match 0 0 0 0 'ab' '[-' 347match 1 1 1 1 '-' '[-]' 348match 0 0 0 0 '-' '[a-' 349match 0 0 0 0 '-' '[!a-' 350match 1 1 1 1 '-' '[--A]' 351match 1 1 1 1 '5' '[--A]' 352match 1 1 1 1 ' ' '[ --]' 353match 1 1 1 1 '$' '[ --]' 354match 1 1 1 1 '-' '[ --]' 355match 0 0 0 0 '0' '[ --]' 356match 1 1 1 1 '-' '[---]' 357match 1 1 1 1 '-' '[------]' 358match 0 0 0 0 'j' '[a-e-n]' 359match 1 1 1 1 '-' '[a-e-n]' 360match 1 1 1 1 'a' '[!------]' 361match 0 0 0 0 '[' '[]-a]' 362match 1 1 1 1 '^' '[]-a]' 363match 0 0 0 0 '^' '[!]-a]' 364match 1 1 1 1 '[' '[!]-a]' 365match 1 1 1 1 '^' '[a^bc]' 366match 1 1 1 1 '-b]' '[a-]b]' 367match 0 0 0 0 '\' '[\]' 368match 1 1 1 1 '\' '[\\]' 369match 0 0 0 0 '\' '[!\\]' 370match 1 1 1 1 'G' '[A-\\]' 371match 0 0 0 0 'aaabbb' 'b*a' 372match 0 0 0 0 'aabcaa' '*ba*' 373match 1 1 1 1 ',' '[,]' 374match 1 1 1 1 ',' '[\\,]' 375match 1 1 1 1 '\' '[\\,]' 376match 1 1 1 1 '-' '[,-.]' 377match 0 0 0 0 '+' '[,-.]' 378match 0 0 0 0 '-.]' '[,-.]' 379match 1 1 1 1 '2' '[\1-\3]' 380match 1 1 1 1 '3' '[\1-\3]' 381match 0 0 0 0 '4' '[\1-\3]' 382match 1 1 1 1 '\' '[[-\]]' 383match 1 1 1 1 '[' '[[-\]]' 384match 1 1 1 1 ']' '[[-\]]' 385match 0 0 0 0 '-' '[[-\]]' 386 387# Test recursion 388match 1 1 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' 389match 0 0 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' 390match 0 0 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' 391match 1 1 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*' 392match 0 0 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*' 393match 1 1 1 1 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t' 394match 0 0 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t' 395match 0 0 0 0 foo '*/*/*' 396match 0 0 0 0 foo/bar '*/*/*' 397match 1 1 1 1 foo/bba/arr '*/*/*' 398match 0 0 1 1 foo/bb/aa/rr '*/*/*' 399match 1 1 1 1 foo/bb/aa/rr '**/**/**' 400match 1 1 1 1 abcXdefXghi '*X*i' 401match 0 0 1 1 ab/cXd/efXg/hi '*X*i' 402match 1 1 1 1 ab/cXd/efXg/hi '*/*X*/*/*i' 403match 1 1 1 1 ab/cXd/efXg/hi '**/*X*/**/*i' 404 405# Extra pathmatch tests 406match 0 0 0 0 foo fo 407match 1 1 1 1 foo/bar foo/bar 408match 1 1 1 1 foo/bar 'foo/*' 409match 0 0 1 1 foo/bba/arr 'foo/*' 410match 1 1 1 1 foo/bba/arr 'foo/**' 411match 0 0 1 1 foo/bba/arr 'foo*' 412match 0 0 1 1 \ 413 1 1 1 1 foo/bba/arr 'foo**' 414match 0 0 1 1 foo/bba/arr 'foo/*arr' 415match 0 0 1 1 foo/bba/arr 'foo/**arr' 416match 0 0 0 0 foo/bba/arr 'foo/*z' 417match 0 0 0 0 foo/bba/arr 'foo/**z' 418match 0 0 1 1 foo/bar 'foo?bar' 419match 0 0 1 1 foo/bar 'foo[/]bar' 420match 0 0 1 1 foo/bar 'foo[^a-z]bar' 421match 0 0 1 1 ab/cXd/efXg/hi '*Xg*i' 422 423# Extra case-sensitivity tests 424match 0 1 0 1 'a' '[A-Z]' 425match 1 1 1 1 'A' '[A-Z]' 426match 0 1 0 1 'A' '[a-z]' 427match 1 1 1 1 'a' '[a-z]' 428match 0 1 0 1 'a' '[[:upper:]]' 429match 1 1 1 1 'A' '[[:upper:]]' 430match 0 1 0 1 'A' '[[:lower:]]' 431match 1 1 1 1 'a' '[[:lower:]]' 432match 0 1 0 1 'A' '[B-Za]' 433match 1 1 1 1 'a' '[B-Za]' 434match 0 1 0 1 'A' '[B-a]' 435match 1 1 1 1 'a' '[B-a]' 436match 0 1 0 1 'z' '[Z-y]' 437match 1 1 1 1 'Z' '[Z-y]' 438 439test_done 440