Files
go-task/internal/gitignore/pattern_test.go
Valentin Maerten 70fe29314f refactor: replace sabhiram/go-gitignore with vendored go-git matcher
The sabhiram/go-gitignore dependency is unmaintained (since 2021) and has
known gitignore spec gaps (* traverses /, ? broken, weak ** handling).

Vendor the pattern/matcher core of go-git (plumbing/format/gitignore,
v5.19.1) into internal/gitignore instead. It is ~185 lines of pure-stdlib,
spec-correct code (proper **, anchoring, dir-only and negation handling).
The file-walking helpers (dir.go) are intentionally omitted as they pull in
go-billy and other go-git internals; importing the upstream package directly
would re-add that transitive weight, which is why go-git was dropped before.

This gains gitignore spec compliance while removing a direct dependency
rather than adding one. The vendored code keeps its Apache-2.0 license
(internal/gitignore/LICENSE) with attribution headers; upstream test cases
are ported to table-driven stdlib tests to avoid an extra test dependency.
2026-06-07 16:07:54 +02:00

81 lines
5.9 KiB
Go

// Test cases ported from go-git: github.com/go-git/go-git/v5 v5.19.1,
// plumbing/format/gitignore/pattern_test.go (originally written against
// gopkg.in/check.v1; rewritten here as table-driven stdlib tests to avoid an
// extra test dependency). Licensed under the Apache License 2.0; see LICENSE.
package gitignore
import "testing"
func TestParsePattern_Match(t *testing.T) {
t.Parallel()
tests := []struct {
name string
pattern string
domain []string
path []string
isDir bool
want MatchResult
}{
{"inclusion", "!vul?ano", nil, []string{"value", "vulkano", "tail"}, false, Include},
{"domainLonger_mismatch", "value", []string{"head", "middle", "tail"}, []string{"head", "middle"}, false, NoMatch},
{"domainSameLength_mismatch", "value", []string{"head", "middle", "tail"}, []string{"head", "middle", "tail"}, false, NoMatch},
{"domainMismatch_mismatch", "value", []string{"head", "middle", "tail"}, []string{"head", "middle", "_tail_", "value"}, false, NoMatch},
{"withDomain", "middle/", []string{"value", "volcano"}, []string{"value", "volcano", "middle", "tail"}, false, Exclude},
{"onlyMatchInDomain_mismatch", "volcano/", []string{"value", "volcano"}, []string{"value", "volcano", "tail"}, true, NoMatch},
{"atStart", "value", nil, []string{"value", "tail"}, false, Exclude},
{"inTheMiddle", "value", nil, []string{"head", "value", "tail"}, false, Exclude},
{"atEnd", "value", nil, []string{"head", "value"}, false, Exclude},
{"atStart_dirWanted", "value/", nil, []string{"value", "tail"}, false, Exclude},
{"inTheMiddle_dirWanted", "value/", nil, []string{"head", "value", "tail"}, false, Exclude},
{"atEnd_dirWanted", "value/", nil, []string{"head", "value"}, true, Exclude},
{"atEnd_dirWanted_notADir_mismatch", "value/", nil, []string{"head", "value"}, false, NoMatch},
{"mismatch", "value", nil, []string{"head", "val", "tail"}, false, NoMatch},
{"valueLonger_mismatch", "val", nil, []string{"head", "value", "tail"}, false, NoMatch},
{"withAsterisk", "v*o", nil, []string{"value", "vulkano", "tail"}, false, Exclude},
{"withQuestionMark", "vul?ano", nil, []string{"value", "vulkano", "tail"}, false, Exclude},
{"magicChars", "v[ou]l[kc]ano", nil, []string{"value", "volcano"}, false, Exclude},
{"wrongPattern_mismatch", "v[ou]l[", nil, []string{"value", "vol["}, false, NoMatch},
{"glob_fromRootWithSlash", "/value/vul?ano", nil, []string{"value", "vulkano", "tail"}, false, Exclude},
{"glob_withDomain", "middle/tail/", []string{"value", "volcano"}, []string{"value", "volcano", "middle", "tail"}, true, Exclude},
{"glob_onlyMatchInDomain_mismatch", "volcano/tail", []string{"value", "volcano"}, []string{"value", "volcano", "tail"}, false, NoMatch},
{"glob_fromRootWithoutSlash", "value/vul?ano", nil, []string{"value", "vulkano", "tail"}, false, Exclude},
{"glob_fromRoot_mismatch", "value/vulkano", nil, []string{"value", "volcano"}, false, NoMatch},
{"glob_fromRoot_tooShort_mismatch", "value/vul?ano", nil, []string{"value"}, false, NoMatch},
{"glob_fromRoot_notAtRoot_mismatch", "/value/volcano", nil, []string{"value", "value", "volcano"}, false, NoMatch},
{"glob_leadingAsterisks_atStart", "**/*lue/vol?ano", nil, []string{"value", "volcano", "tail"}, false, Exclude},
{"glob_leadingAsterisks_notAtStart", "**/*lue/vol?ano", nil, []string{"head", "value", "volcano", "tail"}, false, Exclude},
{"glob_leadingAsterisks_mismatch", "**/*lue/vol?ano", nil, []string{"head", "value", "Volcano", "tail"}, false, NoMatch},
{"glob_leadingAsterisks_isDir", "**/*lue/vol?ano/", nil, []string{"head", "value", "volcano", "tail"}, false, Exclude},
{"glob_leadingAsterisks_isDirAtEnd", "**/*lue/vol?ano/", nil, []string{"head", "value", "volcano"}, true, Exclude},
{"glob_leadingAsterisks_isDir_mismatch", "**/*lue/vol?ano/", nil, []string{"head", "value", "Colcano"}, true, NoMatch},
{"glob_leadingAsterisks_isDirNoDirAtEnd_mismatch", "**/*lue/vol?ano/", nil, []string{"head", "value", "volcano"}, false, NoMatch},
{"glob_tailingAsterisks", "/*lue/vol?ano/**", nil, []string{"value", "volcano", "tail", "moretail"}, false, Exclude},
{"glob_tailingAsterisks_exactMatch", "/*lue/vol?ano/**", nil, []string{"value", "volcano"}, false, Exclude},
{"glob_middleAsterisks_emptyMatch", "/*lue/**/vol?ano", nil, []string{"value", "volcano"}, false, Exclude},
{"glob_middleAsterisks_oneMatch", "/*lue/**/vol?ano", nil, []string{"value", "middle", "volcano"}, false, Exclude},
{"glob_middleAsterisks_multiMatch", "/*lue/**/vol?ano", nil, []string{"value", "middle1", "middle2", "volcano"}, false, Exclude},
{"glob_middleAsterisks_isDir_trailing", "/*lue/**/vol?ano/", nil, []string{"value", "middle1", "middle2", "volcano"}, true, Exclude},
{"glob_middleAsterisks_isDir_trailing_mismatch", "/*lue/**/vol?ano/", nil, []string{"value", "middle1", "middle2", "volcano"}, false, NoMatch},
{"glob_middleAsterisks_isDir", "/*lue/**/vol?ano/", nil, []string{"value", "middle1", "middle2", "volcano", "tail"}, false, Exclude},
{"glob_wrongDoubleAsterisk_mismatch", "/*lue/**foo/vol?ano", nil, []string{"value", "foo", "volcano", "tail"}, false, NoMatch},
{"glob_magicChars", "**/head/v[ou]l[kc]ano", nil, []string{"value", "head", "volcano"}, false, Exclude},
{"glob_wrongPattern_noTraversal_mismatch", "**/head/v[ou]l[", nil, []string{"value", "head", "vol["}, false, NoMatch},
{"glob_wrongPattern_onTraversal_mismatch", "/value/**/v[ou]l[", nil, []string{"value", "head", "vol["}, false, NoMatch},
{"glob_issue_923", "**/android/**/GeneratedPluginRegistrant.java", nil, []string{"packages", "flutter_tools", "lib", "src", "android", "gradle.dart"}, false, NoMatch},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
p := ParsePattern(tt.pattern, tt.domain)
if got := p.Match(tt.path, tt.isDir); got != tt.want {
t.Errorf("ParsePattern(%q, %v).Match(%v, %t) = %v, want %v",
tt.pattern, tt.domain, tt.path, tt.isDir, got, tt.want)
}
})
}
}