github.com/advanderveer/restic@v0.8.1-0.20171209104529-42a8c19aaea6/internal/filter/filter_test.go (about)

     1  package filter_test
     2  
     3  import (
     4  	"bufio"
     5  	"compress/bzip2"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/restic/restic/internal/filter"
    13  )
    14  
    15  var matchTests = []struct {
    16  	pattern string
    17  	path    string
    18  	match   bool
    19  }{
    20  	{"", "", true},
    21  	{"", "foo", true},
    22  	{"", "/x/y/z/foo", true},
    23  	{"*.go", "/foo/bar/test.go", true},
    24  	{"*.c", "/foo/bar/test.go", false},
    25  	{"*", "/foo/bar/test.go", true},
    26  	{"foo*", "/foo/bar/test.go", true},
    27  	{"bar*", "/foo/bar/test.go", true},
    28  	{"/bar*", "/foo/bar/test.go", false},
    29  	{"bar/*", "/foo/bar/test.go", true},
    30  	{"baz/*", "/foo/bar/test.go", false},
    31  	{"bar/test.go", "/foo/bar/test.go", true},
    32  	{"bar/*.go", "/foo/bar/test.go", true},
    33  	{"ba*/*.go", "/foo/bar/test.go", true},
    34  	{"bb*/*.go", "/foo/bar/test.go", false},
    35  	{"test.*", "/foo/bar/test.go", true},
    36  	{"tesT.*", "/foo/bar/test.go", false},
    37  	{"bar/*", "/foo/bar/baz", true},
    38  	{"bar", "/foo/bar", true},
    39  	{"/foo/bar", "/foo/bar", true},
    40  	{"/foo/bar/", "/foo/bar", true},
    41  	{"/foo/bar", "/foo/baz", false},
    42  	{"/foo/bar", "/foo/baz/", false},
    43  	{"/foo///bar", "/foo/bar", true},
    44  	{"/foo/../bar", "/foo/bar", false},
    45  	{"/foo/../bar", "/bar", true},
    46  	{"/foo", "/foo/baz", true},
    47  	{"/foo/", "/foo/baz", true},
    48  	{"/foo/*", "/foo", false},
    49  	{"/foo/*", "/foo/baz", true},
    50  	{"bar", "/foo/bar/baz", true},
    51  	{"bar", "/foo/bar/test.go", true},
    52  	{"/foo/*test.*", "/foo/bar/test.go", false},
    53  	{"/foo/*/test.*", "/foo/bar/test.go", true},
    54  	{"/foo/*/bar/test.*", "/foo/bar/test.go", false},
    55  	{"/*/*/bar/test.*", "/foo/bar/test.go", false},
    56  	{"/*/*/bar/test.*", "/foo/bar/baz/test.go", false},
    57  	{"/*/*/baz/test.*", "/foo/bar/baz/test.go", true},
    58  	{"/*/foo/bar/test.*", "/foo/bar/baz/test.go", false},
    59  	{"/*/foo/bar/test.*", "/foo/bar/baz/test.go", false},
    60  	{"/foo/bar/test.*", "bar/baz/test.go", false},
    61  	{"/x/y/bar/baz/test.*", "bar/baz/test.go", false},
    62  	{"/x/y/bar/baz/test.c", "bar/baz/test.go", false},
    63  	{"baz/test.*", "bar/baz/test.go", true},
    64  	{"baz/tesT.*", "bar/baz/test.go", false},
    65  	{"test.go", "bar/baz/test.go", true},
    66  	{"*.go", "bar/baz/test.go", true},
    67  	{"*.c", "bar/baz/test.go", false},
    68  	{"sdk", "/foo/bar/sdk", true},
    69  	{"sdk", "/foo/bar/sdk/test/sdk_foo.go", true},
    70  	{
    71  		"sdk/*/cpp/*/*vars*.html",
    72  		"/usr/share/doc/libreoffice/sdk/docs/cpp/ref/a00517.html",
    73  		false,
    74  	},
    75  	{"foo/**/bar/*.go", "/home/user/foo/work/special/project/bar/test.go", true},
    76  	{"foo/**/bar/*.go", "/home/user/foo/bar/test.go", true},
    77  	{"foo/**/bar/*.go", "x/foo/bar/test.go", true},
    78  	{"foo/**/bar/*.go", "foo/bar/test.go", true},
    79  	{"foo/**/bar/*.go", "foo/bar/baz/bar/test.go", true},
    80  	{"foo/**/bar/*.go", "/home/user/foo/test.c", false},
    81  	{"foo/**/bar/*.go", "bar/foo/main.go", false},
    82  	{"foo/**/bar/*.go", "/foo/bar/main.go", true},
    83  	{"foo/**/bar/*.go", "bar/main.go", false},
    84  	{"foo/**/bar", "/home/user/foo/x/y/bar", true},
    85  	{"foo/**/bar", "/home/user/foo/x/y/bar/main.go", true},
    86  	{"user/**/important*", "/home/user/work/x/y/hidden/x", false},
    87  	{"user/**/hidden*/**/c", "/home/user/work/x/y/hidden/z/a/b/c", true},
    88  	{"c:/foo/*test.*", "c:/foo/bar/test.go", false},
    89  	{"c:/foo", "c:/foo/bar", true},
    90  	{"c:/foo/", "c:/foo/bar", true},
    91  	{"c:/foo/*/test.*", "c:/foo/bar/test.go", true},
    92  	{"c:/foo/*/bar/test.*", "c:/foo/bar/test.go", false},
    93  }
    94  
    95  func testpattern(t *testing.T, pattern, path string, shouldMatch bool) {
    96  	match, err := filter.Match(pattern, path)
    97  	if err != nil {
    98  		t.Errorf("test pattern %q failed: expected no error for path %q, but error returned: %v",
    99  			pattern, path, err)
   100  	}
   101  
   102  	if match != shouldMatch {
   103  		t.Errorf("test: filter.Match(%q, %q): expected %v, got %v",
   104  			pattern, path, shouldMatch, match)
   105  	}
   106  }
   107  
   108  func TestMatch(t *testing.T) {
   109  	for _, test := range matchTests {
   110  		testpattern(t, test.pattern, test.path, test.match)
   111  
   112  		// Test with native path separator
   113  		if filepath.Separator != '/' {
   114  			// Test with pattern as native
   115  			pattern := strings.Replace(test.pattern, "/", string(filepath.Separator), -1)
   116  			testpattern(t, pattern, test.path, test.match)
   117  
   118  			// Test with path as native
   119  			path := strings.Replace(test.path, "/", string(filepath.Separator), -1)
   120  			testpattern(t, test.pattern, path, test.match)
   121  
   122  			// Test with both pattern and path as native
   123  			testpattern(t, pattern, path, test.match)
   124  		}
   125  	}
   126  }
   127  
   128  var childMatchTests = []struct {
   129  	pattern string
   130  	path    string
   131  	match   bool
   132  }{
   133  	{"", "", true},
   134  	{"", "/foo", true},
   135  	{"", "/x/y/z/foo", true},
   136  	{"foo/bar", "/foo", true},
   137  	{"baz/bar", "/foo", true},
   138  	{"foo", "/foo/bar", true},
   139  	{"bar", "/foo", true},
   140  	{"baz", "/foo/bar", true},
   141  	{"*", "/foo", true},
   142  	{"*", "/foo/bar", true},
   143  	{"/foo/bar", "/foo", true},
   144  	{"/foo/bar/baz", "/foo", true},
   145  	{"/foo/bar/baz", "/foo/bar", true},
   146  	{"/foo/bar/baz", "/foo/baz", false},
   147  	{"/foo/**/baz", "/foo/bar/baz", true},
   148  	{"/foo/**/baz", "/foo/bar/baz/blah", true},
   149  	{"/foo/**/qux", "/foo/bar/baz/qux", true},
   150  	{"/baz/bar", "/foo", false},
   151  	{"/foo", "/foo/bar", true},
   152  	{"/*", "/foo", true},
   153  	{"/*", "/foo/bar", true},
   154  	{"/foo", "/foo/bar", true},
   155  	{"/**", "/foo", true},
   156  	{"/*/**", "/foo", true},
   157  	{"/*/**", "/foo/bar", true},
   158  	{"/*/bar", "/foo", true},
   159  	{"/bar/*", "/foo", false},
   160  	{"/foo/*/baz", "/foo/bar", true},
   161  	{"/foo/*/baz", "/foo/baz", true},
   162  	{"/foo/*/baz", "/bar/baz", false},
   163  	{"/**/*", "/foo", true},
   164  	{"/**/bar", "/foo/bar", true},
   165  }
   166  
   167  func testchildpattern(t *testing.T, pattern, path string, shouldMatch bool) {
   168  	match, err := filter.ChildMatch(pattern, path)
   169  	if err != nil {
   170  		t.Errorf("test child pattern %q failed: expected no error for path %q, but error returned: %v",
   171  			pattern, path, err)
   172  	}
   173  
   174  	if match != shouldMatch {
   175  		t.Errorf("test: filter.ChildMatch(%q, %q): expected %v, got %v",
   176  			pattern, path, shouldMatch, match)
   177  	}
   178  }
   179  
   180  func TestChildMatch(t *testing.T) {
   181  	for _, test := range childMatchTests {
   182  		testchildpattern(t, test.pattern, test.path, test.match)
   183  
   184  		// Test with native path separator
   185  		if filepath.Separator != '/' {
   186  			// Test with pattern as native
   187  			pattern := strings.Replace(test.pattern, "/", string(filepath.Separator), -1)
   188  			testchildpattern(t, pattern, test.path, test.match)
   189  
   190  			// Test with path as native
   191  			path := strings.Replace(test.path, "/", string(filepath.Separator), -1)
   192  			testchildpattern(t, test.pattern, path, test.match)
   193  
   194  			// Test with both pattern and path as native
   195  			testchildpattern(t, pattern, path, test.match)
   196  		}
   197  	}
   198  }
   199  
   200  func ExampleMatch() {
   201  	match, _ := filter.Match("*.go", "/home/user/file.go")
   202  	fmt.Printf("match: %v\n", match)
   203  	// Output:
   204  	// match: true
   205  }
   206  
   207  func ExampleMatch_wildcards() {
   208  	match, _ := filter.Match("/home/[uU]ser/?.go", "/home/user/F.go")
   209  	fmt.Printf("match: %v\n", match)
   210  	// Output:
   211  	// match: true
   212  }
   213  
   214  var filterListTests = []struct {
   215  	patterns []string
   216  	path     string
   217  	match    bool
   218  }{
   219  	{[]string{"*.go"}, "/foo/bar/test.go", true},
   220  	{[]string{"*.c"}, "/foo/bar/test.go", false},
   221  	{[]string{"*.go", "*.c"}, "/foo/bar/test.go", true},
   222  	{[]string{"*"}, "/foo/bar/test.go", true},
   223  	{[]string{"x"}, "/foo/bar/test.go", false},
   224  	{[]string{"?"}, "/foo/bar/test.go", false},
   225  	{[]string{"?", "x"}, "/foo/bar/x", true},
   226  	{[]string{"/*/*/bar/test.*"}, "/foo/bar/test.go", false},
   227  	{[]string{"/*/*/bar/test.*", "*.go"}, "/foo/bar/test.go", true},
   228  	{[]string{"", "*.c"}, "/foo/bar/test.go", false},
   229  }
   230  
   231  func TestList(t *testing.T) {
   232  	for i, test := range filterListTests {
   233  		match, _, err := filter.List(test.patterns, test.path)
   234  		if err != nil {
   235  			t.Errorf("test %d failed: expected no error for patterns %q, but error returned: %v",
   236  				i, test.patterns, err)
   237  			continue
   238  		}
   239  
   240  		if match != test.match {
   241  			t.Errorf("test %d: filter.MatchList(%q, %q): expected %v, got %v",
   242  				i, test.patterns, test.path, test.match, match)
   243  		}
   244  	}
   245  }
   246  
   247  func ExampleList() {
   248  	match, _, _ := filter.List([]string{"*.c", "*.go"}, "/home/user/file.go")
   249  	fmt.Printf("match: %v\n", match)
   250  	// Output:
   251  	// match: true
   252  }
   253  
   254  func extractTestLines(t testing.TB) (lines []string) {
   255  	f, err := os.Open("testdata/libreoffice.txt.bz2")
   256  	if err != nil {
   257  		t.Fatal(err)
   258  	}
   259  
   260  	defer func() {
   261  		if err := f.Close(); err != nil {
   262  			t.Fatal(err)
   263  		}
   264  	}()
   265  
   266  	sc := bufio.NewScanner(bzip2.NewReader(f))
   267  	for sc.Scan() {
   268  		lines = append(lines, sc.Text())
   269  	}
   270  
   271  	return lines
   272  }
   273  
   274  func TestFilterPatternsFile(t *testing.T) {
   275  	lines := extractTestLines(t)
   276  
   277  	var testPatterns = []struct {
   278  		pattern string
   279  		hits    uint
   280  	}{
   281  		{"*.html", 18249},
   282  		{"sdk", 22186},
   283  		{"sdk/*/cpp/*/*vars.html", 3},
   284  	}
   285  
   286  	for _, test := range testPatterns {
   287  		var c uint
   288  		for _, line := range lines {
   289  			match, err := filter.Match(test.pattern, line)
   290  			if err != nil {
   291  				t.Error(err)
   292  				continue
   293  			}
   294  
   295  			if match {
   296  				c++
   297  				// fmt.Printf("pattern %q, line %q\n", test.pattern, line)
   298  			}
   299  		}
   300  
   301  		if c != test.hits {
   302  			t.Errorf("wrong number of hits for pattern %q: want %d, got %d",
   303  				test.pattern, test.hits, c)
   304  		}
   305  	}
   306  }
   307  
   308  func BenchmarkFilterLines(b *testing.B) {
   309  	pattern := "sdk/*/cpp/*/*vars.html"
   310  	lines := extractTestLines(b)
   311  	var c uint
   312  
   313  	b.ResetTimer()
   314  
   315  	for i := 0; i < b.N; i++ {
   316  		c = 0
   317  		for _, line := range lines {
   318  			match, err := filter.Match(pattern, line)
   319  			if err != nil {
   320  				b.Fatal(err)
   321  			}
   322  
   323  			if match {
   324  				c++
   325  			}
   326  		}
   327  
   328  		if c != 3 {
   329  			b.Fatalf("wrong number of matches: expected 3, got %d", c)
   330  		}
   331  	}
   332  }
   333  
   334  func BenchmarkFilterPatterns(b *testing.B) {
   335  	patterns := []string{
   336  		"sdk/*",
   337  		"*.html",
   338  	}
   339  	lines := extractTestLines(b)
   340  	var c uint
   341  
   342  	b.ResetTimer()
   343  
   344  	for i := 0; i < b.N; i++ {
   345  		c = 0
   346  		for _, line := range lines {
   347  			match, _, err := filter.List(patterns, line)
   348  			if err != nil {
   349  				b.Fatal(err)
   350  			}
   351  
   352  			if match {
   353  				c++
   354  			}
   355  		}
   356  
   357  		if c != 22185 {
   358  			b.Fatalf("wrong number of matches: expected 22185, got %d", c)
   359  		}
   360  	}
   361  }