github.com/lambdatest/go-gitignore@v0.0.0-20230214141342-7fe15342e580/defn_test.go (about)

     1  package gitignore_test
     2  
     3  import (
     4  	"path/filepath"
     5  	"strings"
     6  
     7  	"github.com/denormal/go-gitignore"
     8  )
     9  
    10  type token struct {
    11  	Type           gitignore.TokenType
    12  	Name           string
    13  	Token          string
    14  	Line           int
    15  	Column         int
    16  	NewLine        int // token offset for newline end of line
    17  	CarriageReturn int // token offset for carriage return end of line
    18  } // token{}
    19  
    20  type match struct {
    21  	Path    string // test path
    22  	Pattern string // matching pattern (if any)
    23  	Ignore  bool   // whether the path is ignored or included
    24  	Exclude bool   // whether the match comes from the GIT_DIR/info/exclude file
    25  } // match{}
    26  
    27  func (m match) Local() string {
    28  	_path := m.Path
    29  	if m.IsDir() {
    30  		_path = strings.TrimSuffix(m.Path, "/")
    31  	}
    32  
    33  	// generate the local representation of the match path
    34  	return filepath.Join(strings.Split(_path, "/")...)
    35  } // Local()
    36  
    37  func (m match) IsDir() bool {
    38  	return strings.HasSuffix(m.Path, "/")
    39  } // IsDir()
    40  
    41  type position struct {
    42  	File   string
    43  	Line   int
    44  	Column int
    45  	Offset int
    46  	String string
    47  } // position{}
    48  
    49  // define the constants for the unit tests
    50  const (
    51  	// define the example .gitignore file contents
    52  	_GITIGNORE = `
    53  # example .gitignore
    54  
    55  !*.go
    56  
    57  *.o
    58  *.a
    59  
    60  /ignore/this/path/
    61  
    62  # the following line has trailing whitespace
    63  /and/**/all/**/these/**  	 
    64  !/but/not/this\ 
    65  
    66  we support   spaces
    67  
    68  /**/this.is.not/a ** valid/pattern
    69  /**/nor/is/***/this
    70  /nor/is***this
    71  northis** 	 x
    72  
    73  but \this\ is / valid\#
    74  \
    75  
    76  so	is this#
    77  and this is #3 ok too
    78   / //
    79  `
    80  
    81  	// define the example .gitignore file contents for the Match tests
    82  	// these tests have been taken from
    83  	//		https://github.com/sdobz/backup/gitignore
    84  	//
    85  	// https://github.com/sdobz/backup/blob/master/gitignore/gitignore_test.go
    86  	_GITMATCH = `
    87  
    88  *.[oa]
    89  *.html
    90  *.min.js
    91  
    92  !foo*.html
    93  foo-excl.html
    94  
    95  vmlinux*
    96  
    97  \!important!.txt
    98  
    99  log/*.log
   100  !/log/foo.log
   101  
   102  **/logdir/log
   103  **/foodir/bar
   104  exclude/**
   105  
   106  !findthis*
   107  
   108  **/hide/**
   109  subdir/subdir2/
   110  
   111  /rootsubdir/
   112  
   113  dirpattern/
   114  
   115  README.md
   116  
   117  # arch/foo/kernel/.gitignore
   118  !arch/foo/kernel/vmlinux*
   119  
   120  # htmldoc/.gitignore
   121  !htmldoc/*.html
   122  
   123  # git-sample-3/.gitignore
   124  git-sample-3/*
   125  !git-sample-3/foo
   126  git-sample-3/foo/*
   127  !git-sample-3/foo/bar
   128  
   129  Documentation/*.pdf
   130  Documentation/**/p*.pdf
   131  `
   132  
   133  	// define the number of good & bad patterns in the .gitignore above
   134  	_GITPATTERNS    = 12
   135  	_GITBADPATTERNS = 4
   136  
   137  	// define the number of good & bad patterns in the match .gitignore above
   138  	_GITMATCHPATTERNS    = 24
   139  	_GITBADMATCHPATTERNS = 0
   140  
   141  	// define the number of good and bad patterns returned when the
   142  	// gitignore.Parser error handler returns false upon receiving an error
   143  	_GITPATTERNSFALSE    = 7
   144  	_GITBADPATTERNSFALSE = 1
   145  
   146  	// define the base path for a git repository
   147  	_GITBASE = "/my/git/repository"
   148  
   149  	// define the directory mask for any directories created during testing
   150  	_GITMASK = 0700
   151  
   152  	// define a .gitignore that will trigger lexer errors
   153  	_GITINVALID = "" +
   154  		"# the following two lines will trigger repeated lexer errors\n" +
   155  		"x\rx\rx\rx\n" +
   156  		"\rx\rx\rx\n" +
   157  		"!\rx\n" +
   158  		"/my/valid/pattern\n" +
   159  		"!\n" +
   160  		"** *\n" +
   161  		"/\r"
   162  
   163  	// define the number of invalid patterns and errors
   164  	_GITINVALIDERRORS        = 10
   165  	_GITINVALIDERRORSFALSE   = 1
   166  	_GITINVALIDPATTERNS      = 1
   167  	_GITINVALIDPATTERNSFALSE = 0
   168  
   169  	// define the expected number of errors during repository matching
   170  	_GITREPOSITORYERRORS      = 38
   171  	_GITREPOSITORYERRORSFALSE = 1
   172  
   173  	// define a .gitignore file the contains just whitespace & comments
   174  	_GITIGNORE_WHITESPACE = `
   175  # this is an empty .gitignore file
   176  #	- the following lines contains just whitespace
   177   
   178    		  	
   179  `
   180  )
   181  
   182  var (
   183  	// define the positions of the bad patterns
   184  	_GITBADPOSITION = []gitignore.Position{
   185  		gitignore.Position{File: "", Line: 17, Column: 19, Offset: 189},
   186  		gitignore.Position{File: "", Line: 18, Column: 14, Offset: 219},
   187  		gitignore.Position{File: "", Line: 19, Column: 8, Offset: 233},
   188  		gitignore.Position{File: "", Line: 20, Column: 8, Offset: 248},
   189  	}
   190  
   191  	// define the positions of the good patterns
   192  	_GITPOSITION = []gitignore.Position{
   193  		gitignore.Position{File: "", Line: 4, Column: 1, Offset: 23},
   194  		gitignore.Position{File: "", Line: 6, Column: 1, Offset: 30},
   195  		gitignore.Position{File: "", Line: 7, Column: 1, Offset: 34},
   196  		gitignore.Position{File: "", Line: 9, Column: 1, Offset: 39},
   197  		gitignore.Position{File: "", Line: 12, Column: 1, Offset: 104},
   198  		gitignore.Position{File: "", Line: 13, Column: 1, Offset: 132},
   199  		gitignore.Position{File: "", Line: 15, Column: 1, Offset: 150},
   200  		gitignore.Position{File: "", Line: 22, Column: 1, Offset: 256},
   201  		gitignore.Position{File: "", Line: 23, Column: 1, Offset: 280},
   202  		gitignore.Position{File: "", Line: 25, Column: 1, Offset: 283},
   203  		gitignore.Position{File: "", Line: 26, Column: 1, Offset: 295},
   204  		gitignore.Position{File: "", Line: 27, Column: 1, Offset: 317},
   205  	}
   206  
   207  	// define the token stream for the _GITIGNORE .gitignore
   208  	_GITTOKENS = []token{
   209  		// 1:
   210  		{gitignore.EOL, "EOL", "\n", 1, 1, 0, 0},
   211  		// 2: # example .gitignore contents
   212  		{gitignore.COMMENT, "COMMENT", "# example .gitignore", 2, 1, 1, 2},
   213  		{gitignore.EOL, "EOL", "\n", 2, 21, 21, 22},
   214  		// 3:
   215  		{gitignore.EOL, "EOL", "\n", 3, 1, 22, 24},
   216  		// 4: !*.go
   217  		{gitignore.NEGATION, "NEGATION", "!", 4, 1, 23, 26},
   218  		{gitignore.PATTERN, "PATTERN", "*.go", 4, 2, 24, 27},
   219  		{gitignore.EOL, "EOL", "\n", 4, 6, 28, 31},
   220  		// 5:
   221  		{gitignore.EOL, "EOL", "\n", 5, 1, 29, 33},
   222  		// 6: *.o
   223  		{gitignore.PATTERN, "PATTERN", "*.o", 6, 1, 30, 35},
   224  		{gitignore.EOL, "EOL", "\n", 6, 4, 33, 38},
   225  		// 7: *.a
   226  		{gitignore.PATTERN, "PATTERN", "*.a", 7, 1, 34, 40},
   227  		{gitignore.EOL, "EOL", "\n", 7, 4, 37, 43},
   228  		// 8:
   229  		{gitignore.EOL, "EOL", "\n", 8, 1, 38, 45},
   230  		// 9: /ignore/this/path/
   231  		{gitignore.SEPARATOR, "SEPARATOR", "/", 9, 1, 39, 47},
   232  		{gitignore.PATTERN, "PATTERN", "ignore", 9, 2, 40, 48},
   233  		{gitignore.SEPARATOR, "SEPARATOR", "/", 9, 8, 46, 54},
   234  		{gitignore.PATTERN, "PATTERN", "this", 9, 9, 47, 55},
   235  		{gitignore.SEPARATOR, "SEPARATOR", "/", 9, 13, 51, 59},
   236  		{gitignore.PATTERN, "PATTERN", "path", 9, 14, 52, 60},
   237  		{gitignore.SEPARATOR, "SEPARATOR", "/", 9, 18, 56, 64},
   238  		{gitignore.EOL, "EOL", "\n", 9, 19, 57, 65},
   239  		// 10:
   240  		{gitignore.EOL, "EOL", "\n", 10, 1, 58, 67},
   241  		// 11: # the following line has trailing whitespace
   242  		{gitignore.COMMENT, "COMMENT",
   243  			"# the following line has trailing whitespace",
   244  			11, 1, 59, 69},
   245  		{gitignore.EOL, "EOL", "\n", 11, 45, 103, 113},
   246  		// 12: /and/**/all/**/these/**
   247  		{gitignore.SEPARATOR, "SEPARATOR", "/", 12, 1, 104, 115},
   248  		{gitignore.PATTERN, "PATTERN", "and", 12, 2, 105, 116},
   249  		{gitignore.SEPARATOR, "SEPARATOR", "/", 12, 5, 108, 119},
   250  		{gitignore.ANY, "ANY", "**", 12, 6, 109, 120},
   251  		{gitignore.SEPARATOR, "SEPARATOR", "/", 12, 8, 111, 122},
   252  		{gitignore.PATTERN, "PATTERN", "all", 12, 9, 112, 123},
   253  		{gitignore.SEPARATOR, "SEPARATOR", "/", 12, 12, 115, 126},
   254  		{gitignore.ANY, "ANY", "**", 12, 13, 116, 127},
   255  		{gitignore.SEPARATOR, "SEPARATOR", "/", 12, 15, 118, 129},
   256  		{gitignore.PATTERN, "PATTERN", "these", 12, 16, 119, 130},
   257  		{gitignore.SEPARATOR, "SEPARATOR", "/", 12, 21, 124, 135},
   258  		{gitignore.ANY, "ANY", "**", 12, 22, 125, 136},
   259  		{gitignore.WHITESPACE, "WHITESPACE", "  \t ", 12, 24, 127, 138},
   260  		{gitignore.EOL, "EOL", "\n", 12, 28, 131, 142},
   261  		// 13: !/but/not/this\
   262  		{gitignore.NEGATION, "NEGATION", "!", 13, 1, 132, 144},
   263  		{gitignore.SEPARATOR, "SEPARATOR", "/", 13, 2, 133, 145},
   264  		{gitignore.PATTERN, "PATTERN", "but", 13, 3, 134, 146},
   265  		{gitignore.SEPARATOR, "SEPARATOR", "/", 13, 6, 137, 149},
   266  		{gitignore.PATTERN, "PATTERN", "not", 13, 7, 138, 150},
   267  		{gitignore.SEPARATOR, "SEPARATOR", "/", 13, 10, 141, 153},
   268  		{gitignore.PATTERN, "PATTERN", "this\\ ", 13, 11, 142, 154},
   269  		{gitignore.EOL, "EOL", "\n", 13, 17, 148, 160},
   270  		// 14:
   271  		{gitignore.EOL, "EOL", "\n", 14, 1, 149, 162},
   272  		// 15: we support   spaces
   273  		{gitignore.PATTERN, "PATTERN", "we", 15, 1, 150, 164},
   274  		{gitignore.WHITESPACE, "WHITESPACE", " ", 15, 3, 152, 166},
   275  		{gitignore.PATTERN, "PATTERN", "support", 15, 4, 153, 167},
   276  		{gitignore.WHITESPACE, "WHITESPACE", "   ", 15, 11, 160, 174},
   277  		{gitignore.PATTERN, "PATTERN", "spaces", 15, 14, 163, 177},
   278  		{gitignore.EOL, "EOL", "\n", 15, 20, 169, 183},
   279  		// 16:
   280  		{gitignore.EOL, "EOL", "\n", 16, 1, 170, 185},
   281  		// 17: /**/this.is.not/a ** valid/pattern
   282  		{gitignore.SEPARATOR, "SEPARATOR", "/", 17, 1, 171, 187},
   283  		{gitignore.ANY, "ANY", "**", 17, 2, 172, 188},
   284  		{gitignore.SEPARATOR, "SEPARATOR", "/", 17, 4, 174, 190},
   285  		{gitignore.PATTERN, "PATTERN", "this.is.not", 17, 5, 175, 191},
   286  		{gitignore.SEPARATOR, "SEPARATOR", "/", 17, 16, 186, 202},
   287  		{gitignore.PATTERN, "PATTERN", "a", 17, 17, 187, 203},
   288  		{gitignore.WHITESPACE, "WHITESPACE", " ", 17, 18, 188, 204},
   289  		{gitignore.ANY, "ANY", "**", 17, 19, 189, 205},
   290  		{gitignore.WHITESPACE, "WHITESPACE", " ", 17, 21, 191, 207},
   291  		{gitignore.PATTERN, "PATTERN", "valid", 17, 22, 192, 208},
   292  		{gitignore.SEPARATOR, "SEPARATOR", "/", 17, 27, 197, 213},
   293  		{gitignore.PATTERN, "PATTERN", "pattern", 17, 28, 198, 214},
   294  		{gitignore.EOL, "EOL", "\n", 17, 35, 205, 221},
   295  		// 18: /**/nor/is/***/this
   296  		{gitignore.SEPARATOR, "SEPARATOR", "/", 18, 1, 206, 223},
   297  		{gitignore.ANY, "ANY", "**", 18, 2, 207, 224},
   298  		{gitignore.SEPARATOR, "SEPARATOR", "/", 18, 4, 209, 226},
   299  		{gitignore.PATTERN, "PATTERN", "nor", 18, 5, 210, 227},
   300  		{gitignore.SEPARATOR, "SEPARATOR", "/", 18, 8, 213, 230},
   301  		{gitignore.PATTERN, "PATTERN", "is", 18, 9, 214, 231},
   302  		{gitignore.SEPARATOR, "SEPARATOR", "/", 18, 11, 216, 233},
   303  		{gitignore.ANY, "ANY", "**", 18, 12, 217, 234},
   304  		{gitignore.PATTERN, "PATTERN", "*", 18, 14, 219, 236},
   305  		{gitignore.SEPARATOR, "SEPARATOR", "/", 18, 15, 220, 237},
   306  		{gitignore.PATTERN, "PATTERN", "this", 18, 16, 221, 238},
   307  		{gitignore.EOL, "EOL", "\n", 18, 20, 225, 242},
   308  		// 19: /nor/is***this
   309  		{gitignore.SEPARATOR, "SEPARATOR", "/", 19, 1, 226, 244},
   310  		{gitignore.PATTERN, "PATTERN", "nor", 19, 2, 227, 245},
   311  		{gitignore.SEPARATOR, "SEPARATOR", "/", 19, 5, 230, 248},
   312  		{gitignore.PATTERN, "PATTERN", "is", 19, 6, 231, 249},
   313  		{gitignore.ANY, "ANY", "**", 19, 8, 233, 251},
   314  		{gitignore.PATTERN, "PATTERN", "*this", 19, 10, 235, 253},
   315  		{gitignore.EOL, "EOL", "\n", 19, 15, 240, 258},
   316  		// 20: northis** 	 x
   317  		{gitignore.PATTERN, "PATTERN", "northis", 20, 1, 241, 260},
   318  		{gitignore.ANY, "ANY", "**", 20, 8, 248, 267},
   319  		{gitignore.WHITESPACE, "WHITESPACE", " \t ", 20, 10, 250, 269},
   320  		{gitignore.PATTERN, "PATTERN", "x", 20, 13, 253, 272},
   321  		{gitignore.EOL, "EOL", "\n", 20, 14, 254, 273},
   322  		// 21:
   323  		{gitignore.EOL, "EOL", "\n", 21, 1, 255, 275},
   324  		// 22: but \this\ is / valid
   325  		{gitignore.PATTERN, "PATTERN", "but", 22, 1, 256, 277},
   326  		{gitignore.WHITESPACE, "WHITESPACE", " ", 22, 4, 259, 280},
   327  		{gitignore.PATTERN, "PATTERN", "\\this\\ is", 22, 5, 260, 281},
   328  		{gitignore.WHITESPACE, "WHITESPACE", " ", 22, 14, 269, 290},
   329  		{gitignore.SEPARATOR, "SEPARATOR", "/", 22, 15, 270, 291},
   330  		{gitignore.WHITESPACE, "WHITESPACE", " ", 22, 16, 271, 292},
   331  		{gitignore.PATTERN, "PATTERN", "valid\\#", 22, 17, 272, 293},
   332  		{gitignore.EOL, "EOL", "\n", 22, 24, 279, 300},
   333  		// 23: \
   334  		{gitignore.PATTERN, "PATTERN", "\\", 23, 1, 280, 302},
   335  		{gitignore.EOL, "EOL", "\n", 23, 2, 281, 303},
   336  		// 24:
   337  		{gitignore.EOL, "EOL", "\n", 24, 1, 282, 305},
   338  		// 25: so is this#
   339  		{gitignore.PATTERN, "PATTERN", "so", 25, 1, 283, 307},
   340  		{gitignore.WHITESPACE, "WHITESPACE", "	", 25, 3, 285, 309},
   341  		{gitignore.PATTERN, "PATTERN", "is", 25, 4, 286, 310},
   342  		{gitignore.WHITESPACE, "WHITESPACE", " ", 25, 6, 288, 312},
   343  		{gitignore.PATTERN, "PATTERN", "this#", 25, 7, 289, 313},
   344  		{gitignore.EOL, "EOL", "\n", 25, 12, 294, 318},
   345  		// 26: and this is #3 ok too
   346  		{gitignore.PATTERN, "PATTERN", "and", 26, 1, 295, 320},
   347  		{gitignore.WHITESPACE, "WHITESPACE", " ", 26, 4, 298, 323},
   348  		{gitignore.PATTERN, "PATTERN", "this", 26, 5, 299, 324},
   349  		{gitignore.WHITESPACE, "WHITESPACE", " ", 26, 9, 303, 328},
   350  		{gitignore.PATTERN, "PATTERN", "is", 26, 10, 304, 329},
   351  		{gitignore.WHITESPACE, "WHITESPACE", " ", 26, 12, 306, 331},
   352  		{gitignore.PATTERN, "PATTERN", "#3", 26, 13, 307, 332},
   353  		{gitignore.WHITESPACE, "WHITESPACE", " ", 26, 15, 309, 334},
   354  		{gitignore.PATTERN, "PATTERN", "ok", 26, 16, 310, 335},
   355  		{gitignore.WHITESPACE, "WHITESPACE", " ", 26, 18, 312, 337},
   356  		{gitignore.PATTERN, "PATTERN", "too", 26, 19, 313, 338},
   357  		{gitignore.EOL, "EOL", "\n", 26, 22, 316, 341},
   358  		// 27: / //
   359  		{gitignore.WHITESPACE, "WHITESPACE", " ", 27, 1, 317, 343},
   360  		{gitignore.SEPARATOR, "SEPARATOR", "/", 27, 2, 318, 344},
   361  		{gitignore.WHITESPACE, "WHITESPACE", " ", 27, 3, 319, 345},
   362  		{gitignore.SEPARATOR, "SEPARATOR", "/", 27, 4, 320, 346},
   363  		{gitignore.SEPARATOR, "SEPARATOR", "/", 27, 5, 321, 347},
   364  		{gitignore.EOL, "EOL", "\n", 27, 6, 322, 348},
   365  
   366  		{gitignore.EOF, "EOF", "", 28, 1, 323, 350},
   367  	}
   368  
   369  	// define match tests and their expected results
   370  	_GITMATCHES = []match{
   371  		{"!important!.txt", "\\!important!.txt", true, false},
   372  		{"arch/", "", false, false},
   373  		{"arch/foo/", "", false, false},
   374  		{"arch/foo/kernel/", "", false, false},
   375  		{"arch/foo/kernel/vmlinux.lds.S", "!arch/foo/kernel/vmlinux*", false, false},
   376  		{"arch/foo/vmlinux.lds.S", "vmlinux*", true, false},
   377  		{"bar/", "", false, false},
   378  		{"bar/testfile", "", false, false},
   379  		{"dirpattern", "", false, false},
   380  		{"my/other/path/to/dirpattern", "", false, false},
   381  		{"my/path/to/dirpattern/", "dirpattern/", true, false},
   382  		{"my/path/to/dirpattern/some_file.txt", "", false, false},
   383  		{"Documentation/", "", false, false},
   384  		{"Documentation/foo-excl.html", "foo-excl.html", true, false},
   385  		{"Documentation/foo.html", "!foo*.html", false, false},
   386  		{"Documentation/gitignore.html", "*.html", true, false},
   387  		{"Documentation/test.a.html", "*.html", true, false},
   388  		{"exclude/", "exclude/**", true, false},
   389  		{"exclude/dir1/", "exclude/**", true, false},
   390  		{"exclude/dir1/dir2/", "exclude/**", true, false},
   391  		{"exclude/dir1/dir2/dir3/", "exclude/**", true, false},
   392  		{"exclude/dir1/dir2/dir3/testfile", "exclude/**", true, false},
   393  		{"exclude/other_file.txt", "exclude/**", true, false},
   394  		{"file.o", "*.[oa]", true, false},
   395  		{"foo/exclude/some_file.txt", "", false, false},
   396  		{"foo/exclude/other/file.txt", "", false, false},
   397  		{"foodir/", "", false, false},
   398  		{"foodir/bar/", "**/foodir/bar", true, false},
   399  		{"foodir/bar/testfile", "", false, false},
   400  		{"git-sample-3/", "", false, false},
   401  		{"git-sample-3/foo/", "!git-sample-3/foo", false, false},
   402  		{"git-sample-3/foo/bar/", "!git-sample-3/foo/bar", false, false},
   403  		{"git-sample-3/foo/test/", "git-sample-3/foo/*", true, false},
   404  		{"git-sample-3/test/", "git-sample-3/*", true, false},
   405  		{"htmldoc/", "", false, false},
   406  		{"htmldoc/docs.html", "!htmldoc/*.html", false, false},
   407  		{"htmldoc/jslib.min.js", "*.min.js", true, false},
   408  		{"lib.a", "*.[oa]", true, false},
   409  		{"log/", "", false, false},
   410  		{"log/foo.log", "!/log/foo.log", false, false},
   411  		{"log/test.log", "log/*.log", true, false},
   412  		{"rootsubdir/", "/rootsubdir/", true, false},
   413  		{"rootsubdir/foo", "", false, false},
   414  		{"src/", "", false, false},
   415  		{"src/findthis.o", "!findthis*", false, false},
   416  		{"src/internal.o", "*.[oa]", true, false},
   417  		{"subdir/", "", false, false},
   418  		{"subdir/hide/", "**/hide/**", true, false},
   419  		{"subdir/hide/foo", "**/hide/**", true, false},
   420  		{"subdir/logdir/", "", false, false},
   421  		{"subdir/logdir/log/", "**/logdir/log", true, false},
   422  		{"subdir/logdir/log/findthis.log", "!findthis*", false, false},
   423  		{"subdir/logdir/log/foo.log", "", false, false},
   424  		{"subdir/logdir/log/test.log", "", false, false},
   425  		{"subdir/rootsubdir/", "", false, false},
   426  		{"subdir/rootsubdir/foo", "", false, false},
   427  		{"subdir/subdir2/", "subdir/subdir2/", true, false},
   428  		{"subdir/subdir2/bar", "", false, false},
   429  		{"README.md", "README.md", true, false},
   430  		{"my-path/README.md", "README.md", true, false},
   431  		{"my-path/also/README.md", "README.md", true, false},
   432  		{"Documentation/git.pdf", "Documentation/*.pdf", true, false},
   433  		{"Documentation/ppc/ppc.pdf", "Documentation/**/p*.pdf", true, false},
   434  		{"tools/perf/Documentation/perf.pdf", "", false, false},
   435  	}
   436  
   437  	// define the cache tests
   438  	_CACHETEST = map[string]gitignore.GitIgnore{
   439  		"a":     null(),
   440  		"a/b":   null(),
   441  		"a/b/c": nil,
   442  	}
   443  
   444  	// define a set of cache keys known not to be in the cache tests above
   445  	_CACHEUNKNOWN = []string{
   446  		"b",
   447  		"b/c",
   448  	}
   449  
   450  	// define the set of .gitignore files for a repository
   451  	_GITREPOSITORY = map[string]string{
   452  		// define the top-level .gitignore file
   453  		"": `
   454  # ignore .bak files
   455  *.bak
   456  `,
   457  		// define subdirectory .gitignore files
   458  		"a": `
   459  # ignore .go files
   460  *.go
   461  
   462  # ignore every c directory
   463  #	- this should be the same as c/
   464  **/c/
   465  `,
   466  		"a/b": `
   467  # include .go files in this directory
   468  !*.go
   469  
   470  # include everything under e
   471  !**/e/**
   472  `,
   473  		"a/b/d": `
   474  # include c directories
   475  !c/
   476  hidden/
   477  `,
   478  	}
   479  
   480  	// define the patterns for $GIT_DIR/info/exclude
   481  	_GITEXCLUDE = `
   482  # exclude every file using 'exclude' in its name
   483  *exclude*
   484  `
   485  
   486  	// define repository match tests and their expected results
   487  	_REPOSITORYMATCHES = []match{
   488  		// matching against the nested .gitignore files
   489  		{"include.go", "", false, false},
   490  		{"ignore.go.bak", "*.bak", true, false},
   491  		{"a/ignore.go", "*.go", true, false},
   492  		{"a/ignore.go.bak", "*.bak", true, false},
   493  		{"a/include.sh", "", false, false},
   494  		{"a/c/ignore.go", "**/c/", true, false},
   495  		{"a/c/ignore.go.bak", "**/c/", true, false},
   496  		{"a/c/ignore.sh", "**/c/", true, false},
   497  		{"a/c/", "**/c/", true, false},
   498  		{"a/b/c/d/ignore.go", "**/c/", true, false},
   499  		{"a/b/c/d/ignore.go.bak", "**/c/", true, false},
   500  		{"a/b/c/d/ignore.sh", "**/c/", true, false},
   501  		{"a/b/c/d/", "**/c/", true, false},
   502  		{"a/b/c/", "**/c/", true, false},
   503  		{"a/b/include.go", "!*.go", false, false},
   504  		{"a/b/ignore.go.bak", "*.bak", true, false},
   505  		{"a/b/include.sh", "", false, false},
   506  		{"a/b/d/include.go", "!*.go", false, false},
   507  		{"a/b/d/ignore.go.bak", "*.bak", true, false},
   508  		{"a/b/d/include.sh", "", false, false},
   509  		{"a/b/d/c/", "!c/", false, false},
   510  		{"a/b/d/c/include.go", "!*.go", false, false},
   511  		{"a/b/d/c/ignore.go.bak", "*.bak", true, false},
   512  		{"a/b/d/c/include.sh", "", false, false},
   513  		{"a/b/e/c/", "!**/e/**", false, false},
   514  		{"a/b/e/c/include.go", "!**/e/**", false, false},
   515  		{"a/b/e/c/include.go.bak", "!**/e/**", false, false},
   516  		{"a/b/e/c/include.sh", "!**/e/**", false, false},
   517  
   518  		// matching against GIT_DIR/info/exclude
   519  		{"exclude.me", "*exclude*", true, true},
   520  		{"a/exclude.me", "*exclude*", true, true},
   521  		{"a/b/exclude.me", "*exclude*", true, true},
   522  		{"a/b/c/exclude.me", "**/c/", true, false},
   523  		{"a/b/c/d/exclude.me", "**/c/", true, false},
   524  		{"a/c/exclude.me", "**/c/", true, false},
   525  		{"a/b/exclude.me", "*exclude*", true, true},
   526  		{"a/b/d/exclude.me", "*exclude*", true, true},
   527  		{"a/b/d/c/exclude.me", "*exclude*", true, true},
   528  		{"a/b/e/c/exclude.me", "!**/e/**", false, false},
   529  	}
   530  
   531  	// define the repository match tests and their expected results when the
   532  	// error handler returns false
   533  	_REPOSITORYMATCHESFALSE = []match{
   534  		{"a/b/c_/d/e_/f/g/h/include.go~", "", false, false},
   535  	}
   536  
   537  	// define the position tests
   538  	_POSITIONS = []position{
   539  		{"", 0, 0, 0, "+0"},
   540  		{"", 1, 0, 0, "1"},
   541  		{"", 0, 1, 0, "+0"},
   542  		{"", 0, 0, 1, "+1"},
   543  		{"", 1, 2, 0, "1:2"},
   544  		{"", 1, 0, 3, "1"},
   545  		{"", 1, 2, 3, "1:2"},
   546  		{"file", 0, 0, 0, "file: +0"},
   547  		{"file", 1, 0, 0, "file: 1"},
   548  		{"file", 0, 1, 0, "file: +0"},
   549  		{"file", 0, 0, 1, "file: +1"},
   550  		{"file", 1, 2, 0, "file: 1:2"},
   551  		{"file", 1, 0, 3, "file: 1"},
   552  		{"file", 1, 2, 3, "file: 1:2"},
   553  	}
   554  
   555  	// define the token tests
   556  	//		- we us the same position for all tokens, and ignore the
   557  	//		  token string (i.e. the sequence of runes that comprise this
   558  	//		  token), since we test the correctness of rune mappings to toknes
   559  	//	      in the above tests of example .gitignore files
   560  	_TOKENS = []token{
   561  		{gitignore.ILLEGAL, "ILLEGAL", "", 1, 2, 3, 4},
   562  		{gitignore.EOF, "EOF", "", 1, 2, 3, 4},
   563  		{gitignore.EOL, "EOL", "", 1, 2, 3, 4},
   564  		{gitignore.WHITESPACE, "WHITESPACE", "", 1, 2, 3, 4},
   565  		{gitignore.COMMENT, "COMMENT", "", 1, 2, 3, 4},
   566  		{gitignore.SEPARATOR, "SEPARATOR", "", 1, 2, 3, 4},
   567  		{gitignore.NEGATION, "NEGATION", "", 1, 2, 3, 4},
   568  		{gitignore.PATTERN, "PATTERN", "", 1, 2, 3, 4},
   569  		{gitignore.ANY, "ANY", "", 1, 2, 3, 4},
   570  		{gitignore.BAD, "BAD TOKEN", "", 1, 2, 3, 4},
   571  
   572  		// invalid tokens
   573  		{-1, "BAD TOKEN", "", 1, 2, 3, 4},
   574  		{12345, "BAD TOKEN", "", 1, 2, 3, 4},
   575  	}
   576  
   577  	// define the beginning position for the parser & lexer
   578  	_BEGINNING = gitignore.Position{File: "", Line: 1, Column: 1, Offset: 0}
   579  
   580  	// define the tokens from the invalid .gitignore above
   581  	_TOKENSINVALID = []token{
   582  		// 1: # the following two lines will trigger repeated lexer errors
   583  		{gitignore.COMMENT,
   584  			"COMMENT",
   585  			"# the following two lines will trigger repeated lexer errors",
   586  			1, 1, 0, 0},
   587  		{gitignore.EOL, "EOL", "\n", 1, 61, 60, 60},
   588  		// 2: x\rx\rx\rx
   589  		{gitignore.PATTERN, "PATTERN", "x", 2, 1, 61, 62},
   590  		{gitignore.BAD, "BAD TOKEN", "\r", 2, 2, 62, 63},
   591  		{gitignore.PATTERN, "PATTERN", "x", 2, 3, 63, 64},
   592  		{gitignore.BAD, "BAD TOKEN", "\r", 2, 4, 64, 65},
   593  		{gitignore.PATTERN, "PATTERN", "x", 2, 5, 65, 66},
   594  		{gitignore.BAD, "BAD TOKEN", "\r", 2, 6, 66, 67},
   595  		{gitignore.PATTERN, "PATTERN", "x", 2, 7, 67, 68},
   596  		{gitignore.EOL, "EOL", "\n", 2, 8, 68, 69},
   597  		// 3: x\rx\rx\rx
   598  		{gitignore.BAD, "BAD TOKEN", "\r", 3, 1, 69, 71},
   599  		{gitignore.PATTERN, "PATTERN", "x", 3, 2, 70, 72},
   600  		{gitignore.BAD, "BAD TOKEN", "\r", 3, 3, 71, 73},
   601  		{gitignore.PATTERN, "PATTERN", "x", 3, 4, 72, 74},
   602  		{gitignore.BAD, "BAD TOKEN", "\r", 3, 5, 73, 75},
   603  		{gitignore.PATTERN, "PATTERN", "x", 3, 6, 74, 76},
   604  		{gitignore.EOL, "EOL", "\n", 3, 7, 75, 77},
   605  		// 4: !\rx
   606  		{gitignore.NEGATION, "NEGATION", "!", 4, 1, 76, 79},
   607  		{gitignore.BAD, "BAD TOKEN", "\r", 4, 2, 77, 80},
   608  		{gitignore.PATTERN, "PATTERN", "x", 4, 3, 78, 81},
   609  		{gitignore.EOL, "EOL", "\n", 4, 4, 79, 82},
   610  		// 5: /my/valid/pattern
   611  		{gitignore.SEPARATOR, "SEPARATOR", "/", 5, 1, 80, 84},
   612  		{gitignore.PATTERN, "PATTERN", "my", 5, 2, 81, 85},
   613  		{gitignore.SEPARATOR, "SEPARATOR", "/", 5, 4, 83, 87},
   614  		{gitignore.PATTERN, "PATTERN", "valid", 5, 5, 84, 88},
   615  		{gitignore.SEPARATOR, "SEPARATOR", "/", 5, 10, 89, 93},
   616  		{gitignore.PATTERN, "PATTERN", "pattern", 5, 11, 90, 94},
   617  		{gitignore.EOL, "EOL", "\n", 5, 18, 97, 101},
   618  		// 6: !
   619  		{gitignore.NEGATION, "NEGATION", "!", 6, 1, 98, 103},
   620  		{gitignore.EOL, "EOL", "\n", 6, 2, 99, 104},
   621  		// 7: ** *
   622  		{gitignore.ANY, "ANY", "**", 7, 1, 100, 106},
   623  		{gitignore.WHITESPACE, "WHITESPACE", " ", 7, 3, 102, 108},
   624  		{gitignore.PATTERN, "PATTERN", "*", 7, 4, 103, 109},
   625  		{gitignore.EOL, "EOL", "\n", 7, 5, 104, 110},
   626  		// 8: /\r
   627  		{gitignore.SEPARATOR, "SEPARATOR", "/", 8, 1, 105, 112},
   628  		{gitignore.BAD, "BAD TOKEN", "\r", 8, 2, 106, 113},
   629  
   630  		{gitignore.EOF, "EOF", "", 8, 3, 107, 114},
   631  	}
   632  
   633  	// define the patterns & errors expected during invalid content parsing
   634  	_GITINVALIDPATTERN = []string{"/my/valid/pattern"}
   635  	_GITINVALIDERROR   = []error{
   636  		gitignore.CarriageReturnError,
   637  		gitignore.CarriageReturnError,
   638  		gitignore.CarriageReturnError,
   639  		gitignore.CarriageReturnError,
   640  		gitignore.CarriageReturnError,
   641  		gitignore.CarriageReturnError,
   642  		gitignore.CarriageReturnError,
   643  		gitignore.InvalidPatternError,
   644  		gitignore.InvalidPatternError,
   645  		gitignore.CarriageReturnError,
   646  	}
   647  )