github.com/boyter/gocodewalker@v1.3.2/go-gitignore/defn_test.go (about)

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