github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/filter/filter_test.go (about) 1 package filter 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "strings" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/rclone/rclone/fs" 13 "github.com/rclone/rclone/fstest/mockobject" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestNewFilterDefault(t *testing.T) { 19 f, err := NewFilter(nil) 20 require.NoError(t, err) 21 assert.False(t, f.Opt.DeleteExcluded) 22 assert.Equal(t, fs.SizeSuffix(-1), f.Opt.MinSize) 23 assert.Equal(t, fs.SizeSuffix(-1), f.Opt.MaxSize) 24 assert.Len(t, f.fileRules.rules, 0) 25 assert.Len(t, f.dirRules.rules, 0) 26 assert.Len(t, f.metaRules.rules, 0) 27 assert.Nil(t, f.files) 28 assert.True(t, f.InActive()) 29 } 30 31 // testFile creates a temp file with the contents 32 func testFile(t *testing.T, contents string) string { 33 out, err := os.CreateTemp("", "filter_test") 34 require.NoError(t, err) 35 defer func() { 36 err := out.Close() 37 require.NoError(t, err) 38 }() 39 _, err = out.Write([]byte(contents)) 40 require.NoError(t, err) 41 s := out.Name() 42 return s 43 } 44 45 func TestNewFilterForbiddenMixOfFilesFromAndFilterRule(t *testing.T) { 46 Opt := DefaultOpt 47 48 // Set up the input 49 Opt.FilterRule = []string{"- filter1", "- filter1b"} 50 Opt.FilesFrom = []string{testFile(t, "#comment\nfiles1\nfiles2\n")} 51 52 rm := func(p string) { 53 err := os.Remove(p) 54 if err != nil { 55 t.Logf("error removing %q: %v", p, err) 56 } 57 } 58 // Reset the input 59 defer func() { 60 rm(Opt.FilesFrom[0]) 61 }() 62 63 _, err := NewFilter(&Opt) 64 require.Error(t, err) 65 require.Contains(t, err.Error(), "the usage of --files-from overrides all other filters") 66 } 67 68 func TestNewFilterForbiddenMixOfFilesFromRawAndFilterRule(t *testing.T) { 69 Opt := DefaultOpt 70 71 // Set up the input 72 Opt.FilterRule = []string{"- filter1", "- filter1b"} 73 Opt.FilesFromRaw = []string{testFile(t, "#comment\nfiles1\nfiles2\n")} 74 75 rm := func(p string) { 76 err := os.Remove(p) 77 if err != nil { 78 t.Logf("error removing %q: %v", p, err) 79 } 80 } 81 // Reset the input 82 defer func() { 83 rm(Opt.FilesFromRaw[0]) 84 }() 85 86 _, err := NewFilter(&Opt) 87 require.Error(t, err) 88 require.Contains(t, err.Error(), "the usage of --files-from-raw overrides all other filters") 89 } 90 91 func TestNewFilterWithFilesFromAlone(t *testing.T) { 92 Opt := DefaultOpt 93 94 // Set up the input 95 Opt.FilesFrom = []string{testFile(t, "#comment\nfiles1\nfiles2\n")} 96 97 rm := func(p string) { 98 err := os.Remove(p) 99 if err != nil { 100 t.Logf("error removing %q: %v", p, err) 101 } 102 } 103 // Reset the input 104 defer func() { 105 rm(Opt.FilesFrom[0]) 106 }() 107 108 f, err := NewFilter(&Opt) 109 require.NoError(t, err) 110 assert.Len(t, f.files, 2) 111 for _, name := range []string{"files1", "files2"} { 112 _, ok := f.files[name] 113 if !ok { 114 t.Errorf("Didn't find file %q in f.files", name) 115 } 116 } 117 } 118 119 func TestNewFilterWithFilesFromRaw(t *testing.T) { 120 Opt := DefaultOpt 121 122 // Set up the input 123 Opt.FilesFromRaw = []string{testFile(t, "#comment\nfiles1\nfiles2\n")} 124 125 rm := func(p string) { 126 err := os.Remove(p) 127 if err != nil { 128 t.Logf("error removing %q: %v", p, err) 129 } 130 } 131 // Reset the input 132 defer func() { 133 rm(Opt.FilesFromRaw[0]) 134 }() 135 136 f, err := NewFilter(&Opt) 137 require.NoError(t, err) 138 assert.Len(t, f.files, 3) 139 for _, name := range []string{"#comment", "files1", "files2"} { 140 _, ok := f.files[name] 141 if !ok { 142 t.Errorf("Didn't find file %q in f.files", name) 143 } 144 } 145 } 146 147 func TestNewFilterFullExceptFilesFromOpt(t *testing.T) { 148 Opt := DefaultOpt 149 150 mins := fs.SizeSuffix(100 * 1024) 151 maxs := fs.SizeSuffix(1000 * 1024) 152 153 // Set up the input 154 Opt.DeleteExcluded = true 155 Opt.FilterRule = []string{"- filter1", "- filter1b"} 156 Opt.FilterFrom = []string{testFile(t, "#comment\n+ filter2\n- filter3\n")} 157 Opt.ExcludeRule = []string{"exclude1"} 158 Opt.ExcludeFrom = []string{testFile(t, "#comment\nexclude2\nexclude3\n")} 159 Opt.IncludeRule = []string{"include1"} 160 Opt.IncludeFrom = []string{testFile(t, "#comment\ninclude2\ninclude3\n")} 161 Opt.MinSize = mins 162 Opt.MaxSize = maxs 163 164 rm := func(p string) { 165 err := os.Remove(p) 166 if err != nil { 167 t.Logf("error removing %q: %v", p, err) 168 } 169 } 170 // Reset the input 171 defer func() { 172 rm(Opt.FilterFrom[0]) 173 rm(Opt.ExcludeFrom[0]) 174 rm(Opt.IncludeFrom[0]) 175 }() 176 177 f, err := NewFilter(&Opt) 178 require.NoError(t, err) 179 assert.True(t, f.Opt.DeleteExcluded) 180 assert.Equal(t, f.Opt.MinSize, mins) 181 assert.Equal(t, f.Opt.MaxSize, maxs) 182 got := f.DumpFilters() 183 want := `--- File filter rules --- 184 + (^|/)include1$ 185 + (^|/)include2$ 186 + (^|/)include3$ 187 - (^|/)exclude1$ 188 - (^|/)exclude2$ 189 - (^|/)exclude3$ 190 - (^|/)filter1$ 191 - (^|/)filter1b$ 192 + (^|/)filter2$ 193 - (^|/)filter3$ 194 - ^.*$ 195 --- Directory filter rules --- 196 + ^.*$ 197 - ^.*$` 198 assert.Equal(t, want, got) 199 assert.False(t, f.InActive()) 200 } 201 202 type includeTest struct { 203 in string 204 size int64 205 modTime int64 206 want bool 207 } 208 209 func testInclude(t *testing.T, f *Filter, tests []includeTest) { 210 for _, test := range tests { 211 got := f.Include(test.in, test.size, time.Unix(test.modTime, 0), nil) 212 assert.Equal(t, test.want, got, fmt.Sprintf("in=%q, size=%v, modTime=%v", test.in, test.size, time.Unix(test.modTime, 0))) 213 } 214 } 215 216 type includeDirTest struct { 217 in string 218 want bool 219 } 220 221 func testDirInclude(t *testing.T, f *Filter, tests []includeDirTest) { 222 for _, test := range tests { 223 got, err := f.IncludeDirectory(context.Background(), nil)(test.in) 224 require.NoError(t, err) 225 assert.Equal(t, test.want, got, test.in) 226 } 227 } 228 229 func TestNewFilterIncludeFiles(t *testing.T) { 230 f, err := NewFilter(nil) 231 require.NoError(t, err) 232 err = f.AddFile("file1.jpg") 233 require.NoError(t, err) 234 err = f.AddFile("/file2.jpg") 235 require.NoError(t, err) 236 assert.Equal(t, FilesMap{ 237 "file1.jpg": {}, 238 "file2.jpg": {}, 239 }, f.files) 240 assert.Equal(t, FilesMap{}, f.dirs) 241 testInclude(t, f, []includeTest{ 242 {"file1.jpg", 0, 0, true}, 243 {"file2.jpg", 1, 0, true}, 244 {"potato/file2.jpg", 2, 0, false}, 245 {"file3.jpg", 3, 0, false}, 246 }) 247 assert.False(t, f.InActive()) 248 } 249 250 func TestNewFilterIncludeFilesDirs(t *testing.T) { 251 f, err := NewFilter(nil) 252 require.NoError(t, err) 253 for _, path := range []string{ 254 "path/to/dir/file1.png", 255 "/path/to/dir/file2.png", 256 "/path/to/file3.png", 257 "/path/to/dir2/file4.png", 258 } { 259 err = f.AddFile(path) 260 require.NoError(t, err) 261 } 262 assert.Equal(t, FilesMap{ 263 "path": {}, 264 "path/to": {}, 265 "path/to/dir": {}, 266 "path/to/dir2": {}, 267 }, f.dirs) 268 testDirInclude(t, f, []includeDirTest{ 269 {"path", true}, 270 {"path/to", true}, 271 {"path/to/", true}, 272 {"/path/to", true}, 273 {"/path/to/", true}, 274 {"path/to/dir", true}, 275 {"path/to/dir2", true}, 276 {"path/too", false}, 277 {"path/three", false}, 278 {"four", false}, 279 }) 280 } 281 282 func TestNewFilterHaveFilesFrom(t *testing.T) { 283 f, err := NewFilter(nil) 284 require.NoError(t, err) 285 286 assert.Equal(t, false, f.HaveFilesFrom()) 287 288 require.NoError(t, f.AddFile("file")) 289 290 assert.Equal(t, true, f.HaveFilesFrom()) 291 } 292 293 func TestNewFilterMakeListR(t *testing.T) { 294 f, err := NewFilter(nil) 295 require.NoError(t, err) 296 297 // Check error if no files 298 listR := f.MakeListR(context.Background(), nil) 299 err = listR(context.Background(), "", nil) 300 assert.EqualError(t, err, errFilesFromNotSet.Error()) 301 302 // Add some files 303 for _, path := range []string{ 304 "path/to/dir/file1.png", 305 "/path/to/dir/file2.png", 306 "/path/to/file3.png", 307 "/path/to/dir2/file4.png", 308 "notfound", 309 } { 310 err = f.AddFile(path) 311 require.NoError(t, err) 312 } 313 314 assert.Equal(t, 5, len(f.files)) 315 316 // NewObject function for MakeListR 317 newObjects := FilesMap{} 318 var newObjectMu sync.Mutex 319 NewObject := func(ctx context.Context, remote string) (fs.Object, error) { 320 newObjectMu.Lock() 321 defer newObjectMu.Unlock() 322 if remote == "notfound" { 323 return nil, fs.ErrorObjectNotFound 324 } else if remote == "error" { 325 return nil, assert.AnError 326 } 327 newObjects[remote] = struct{}{} 328 return mockobject.New(remote), nil 329 330 } 331 332 // Callback for ListRFn 333 listRObjects := FilesMap{} 334 var callbackMu sync.Mutex 335 listRcallback := func(entries fs.DirEntries) error { 336 callbackMu.Lock() 337 defer callbackMu.Unlock() 338 for _, entry := range entries { 339 listRObjects[entry.Remote()] = struct{}{} 340 } 341 return nil 342 } 343 344 // Make the listR and call it 345 listR = f.MakeListR(context.Background(), NewObject) 346 err = listR(context.Background(), "", listRcallback) 347 require.NoError(t, err) 348 349 // Check that the correct objects were created and listed 350 want := FilesMap{ 351 "path/to/dir/file1.png": {}, 352 "path/to/dir/file2.png": {}, 353 "path/to/file3.png": {}, 354 "path/to/dir2/file4.png": {}, 355 } 356 assert.Equal(t, want, newObjects) 357 assert.Equal(t, want, listRObjects) 358 359 // Now check an error is returned from NewObject 360 require.NoError(t, f.AddFile("error")) 361 err = listR(context.Background(), "", listRcallback) 362 require.EqualError(t, err, assert.AnError.Error()) 363 364 // The checker will exit by the error above 365 ci := fs.GetConfig(context.Background()) 366 ci.Checkers = 1 367 368 // Now check an error is returned from NewObject 369 require.NoError(t, f.AddFile("error")) 370 err = listR(context.Background(), "", listRcallback) 371 require.EqualError(t, err, assert.AnError.Error()) 372 } 373 374 func TestNewFilterMinSize(t *testing.T) { 375 f, err := NewFilter(nil) 376 require.NoError(t, err) 377 f.Opt.MinSize = 100 378 testInclude(t, f, []includeTest{ 379 {"file1.jpg", 100, 0, true}, 380 {"file2.jpg", 101, 0, true}, 381 {"potato/file2.jpg", 99, 0, false}, 382 }) 383 assert.False(t, f.InActive()) 384 } 385 386 func TestNewFilterMaxSize(t *testing.T) { 387 f, err := NewFilter(nil) 388 require.NoError(t, err) 389 f.Opt.MaxSize = 100 390 testInclude(t, f, []includeTest{ 391 {"file1.jpg", 100, 0, true}, 392 {"file2.jpg", 101, 0, false}, 393 {"potato/file2.jpg", 99, 0, true}, 394 }) 395 assert.False(t, f.InActive()) 396 } 397 398 func TestNewFilterMinAndMaxAge(t *testing.T) { 399 f, err := NewFilter(nil) 400 require.NoError(t, err) 401 f.ModTimeFrom = time.Unix(1440000002, 0) 402 f.ModTimeTo = time.Unix(1440000003, 0) 403 testInclude(t, f, []includeTest{ 404 {"file1.jpg", 100, 1440000000, false}, 405 {"file2.jpg", 101, 1440000001, false}, 406 {"file3.jpg", 102, 1440000002, true}, 407 {"potato/file1.jpg", 98, 1440000003, true}, 408 {"potato/file2.jpg", 99, 1440000004, false}, 409 }) 410 assert.False(t, f.InActive()) 411 } 412 413 func TestNewFilterMinAge(t *testing.T) { 414 f, err := NewFilter(nil) 415 require.NoError(t, err) 416 f.ModTimeTo = time.Unix(1440000002, 0) 417 testInclude(t, f, []includeTest{ 418 {"file1.jpg", 100, 1440000000, true}, 419 {"file2.jpg", 101, 1440000001, true}, 420 {"file3.jpg", 102, 1440000002, true}, 421 {"potato/file1.jpg", 98, 1440000003, false}, 422 {"potato/file2.jpg", 99, 1440000004, false}, 423 }) 424 assert.False(t, f.InActive()) 425 } 426 427 func TestNewFilterMaxAge(t *testing.T) { 428 f, err := NewFilter(nil) 429 require.NoError(t, err) 430 f.ModTimeFrom = time.Unix(1440000002, 0) 431 testInclude(t, f, []includeTest{ 432 {"file1.jpg", 100, 1440000000, false}, 433 {"file2.jpg", 101, 1440000001, false}, 434 {"file3.jpg", 102, 1440000002, true}, 435 {"potato/file1.jpg", 98, 1440000003, true}, 436 {"potato/file2.jpg", 99, 1440000004, true}, 437 }) 438 assert.False(t, f.InActive()) 439 } 440 441 func TestNewFilterMatches(t *testing.T) { 442 f, err := NewFilter(nil) 443 require.NoError(t, err) 444 add := func(s string) { 445 err := f.AddRule(s) 446 require.NoError(t, err) 447 } 448 add("+ cleared") 449 add("!") 450 add("- /file1.jpg") 451 add("+ /file2.png") 452 add("+ /*.jpg") 453 add("- /*.png") 454 add("- /potato") 455 add("+ /sausage1") 456 add("+ /sausage2*") 457 add("+ /sausage3**") 458 add("+ /a/*.jpg") 459 add("- *") 460 testInclude(t, f, []includeTest{ 461 {"cleared", 100, 0, false}, 462 {"file1.jpg", 100, 0, false}, 463 {"file2.png", 100, 0, true}, 464 {"FILE2.png", 100, 0, false}, 465 {"afile2.png", 100, 0, false}, 466 {"file3.jpg", 101, 0, true}, 467 {"file4.png", 101, 0, false}, 468 {"potato", 101, 0, false}, 469 {"sausage1", 101, 0, true}, 470 {"sausage1/potato", 101, 0, false}, 471 {"sausage2potato", 101, 0, true}, 472 {"sausage2/potato", 101, 0, false}, 473 {"sausage3/potato", 101, 0, true}, 474 {"a/one.jpg", 101, 0, true}, 475 {"a/one.png", 101, 0, false}, 476 {"unicorn", 99, 0, false}, 477 }) 478 testDirInclude(t, f, []includeDirTest{ 479 {"sausage1", false}, 480 {"sausage2", false}, 481 {"sausage2/sub", false}, 482 {"sausage2/sub/dir", false}, 483 {"sausage3", true}, 484 {"SAUSAGE3", false}, 485 {"sausage3/sub", true}, 486 {"sausage3/sub/dir", true}, 487 {"sausage4", false}, 488 {"a", true}, 489 }) 490 assert.False(t, f.InActive()) 491 } 492 493 func TestNewFilterMatchesIgnoreCase(t *testing.T) { 494 f, err := NewFilter(nil) 495 require.NoError(t, err) 496 f.Opt.IgnoreCase = true 497 add := func(s string) { 498 err := f.AddRule(s) 499 require.NoError(t, err) 500 } 501 add("+ /file2.png") 502 add("+ /sausage3**") 503 add("- *") 504 testInclude(t, f, []includeTest{ 505 {"file2.png", 100, 0, true}, 506 {"FILE2.png", 100, 0, true}, 507 }) 508 testDirInclude(t, f, []includeDirTest{ 509 {"sausage3", true}, 510 {"SAUSAGE3", true}, 511 }) 512 assert.False(t, f.InActive()) 513 } 514 515 func TestNewFilterMatchesRegexp(t *testing.T) { 516 f, err := NewFilter(nil) 517 require.NoError(t, err) 518 add := func(s string) { 519 err := f.AddRule(s) 520 require.NoError(t, err) 521 } 522 add(`+ /{{file\d+\.png}}`) 523 add(`+ *.{{(?i)jpg}}`) 524 add(`- *`) 525 testInclude(t, f, []includeTest{ 526 {"file2.png", 100, 0, true}, 527 {"sub/file2.png", 100, 0, false}, 528 {"file123.png", 100, 0, true}, 529 {"File123.png", 100, 0, false}, 530 {"something.jpg", 100, 0, true}, 531 {"deep/path/something.JPG", 100, 0, true}, 532 {"something.gif", 100, 0, false}, 533 }) 534 testDirInclude(t, f, []includeDirTest{ 535 {"anything at all", true}, 536 }) 537 assert.False(t, f.InActive()) 538 } 539 540 type includeTestMetadata struct { 541 in string 542 metadata fs.Metadata 543 want bool 544 } 545 546 func testIncludeMetadata(t *testing.T, f *Filter, tests []includeTestMetadata) { 547 for _, test := range tests { 548 got := f.Include(test.in, 0, time.Time{}, test.metadata) 549 assert.Equal(t, test.want, got, fmt.Sprintf("in=%q, metadata=%+v", test.in, test.metadata)) 550 } 551 } 552 553 func TestNewFilterMetadataInclude(t *testing.T) { 554 f, err := NewFilter(nil) 555 require.NoError(t, err) 556 add := func(s string) { 557 err := f.metaRules.AddRule(s) 558 require.NoError(t, err) 559 } 560 add(`+ t*=t*`) 561 add(`- *`) 562 testIncludeMetadata(t, f, []includeTestMetadata{ 563 {"nil", nil, false}, 564 {"empty", fs.Metadata{}, false}, 565 {"ok1", fs.Metadata{"thing": "thang"}, true}, 566 {"ok2", fs.Metadata{"thing1": "thang1"}, true}, 567 {"missing", fs.Metadata{"Thing1": "Thang1"}, false}, 568 }) 569 assert.False(t, f.InActive()) 570 } 571 572 func TestNewFilterMetadataExclude(t *testing.T) { 573 f, err := NewFilter(nil) 574 require.NoError(t, err) 575 add := func(s string) { 576 err := f.metaRules.AddRule(s) 577 require.NoError(t, err) 578 } 579 add(`- thing=thang`) 580 add(`+ *`) 581 testIncludeMetadata(t, f, []includeTestMetadata{ 582 {"nil", nil, true}, 583 {"empty", fs.Metadata{}, true}, 584 {"ok1", fs.Metadata{"thing": "thang"}, false}, 585 {"missing1", fs.Metadata{"thing1": "thang1"}, true}, 586 }) 587 assert.False(t, f.InActive()) 588 } 589 590 func TestFilterAddDirRuleOrFileRule(t *testing.T) { 591 for _, test := range []struct { 592 included bool 593 glob string 594 want string 595 }{ 596 { 597 false, 598 "potato", 599 `--- File filter rules --- 600 - (^|/)potato$ 601 --- Directory filter rules ---`, 602 }, 603 { 604 true, 605 "potato", 606 `--- File filter rules --- 607 + (^|/)potato$ 608 --- Directory filter rules --- 609 + ^.*$`, 610 }, 611 { 612 false, 613 "potato/", 614 `--- File filter rules --- 615 - (^|/)potato/.*$ 616 --- Directory filter rules --- 617 - (^|/)potato/.*$`, 618 }, 619 { 620 true, 621 "potato/", 622 `--- File filter rules --- 623 --- Directory filter rules --- 624 + (^|/)potato/$`, 625 }, 626 { 627 false, 628 "*", 629 `--- File filter rules --- 630 - (^|/)[^/]*$ 631 --- Directory filter rules --- 632 - ^.*$`, 633 }, 634 { 635 true, 636 "*", 637 `--- File filter rules --- 638 + (^|/)[^/]*$ 639 --- Directory filter rules --- 640 + ^.*$`, 641 }, 642 { 643 false, 644 ".*{,/**}", 645 `--- File filter rules --- 646 - (^|/)\.[^/]*(|/.*)$ 647 --- Directory filter rules --- 648 - (^|/)\.[^/]*(|/.*)$`, 649 }, 650 { 651 true, 652 "a/b/c/d", 653 `--- File filter rules --- 654 + (^|/)a/b/c/d$ 655 --- Directory filter rules --- 656 + (^|/)a/b/c/$ 657 + (^|/)a/b/$ 658 + (^|/)a/$`, 659 }, 660 } { 661 f, err := NewFilter(nil) 662 require.NoError(t, err) 663 err = f.Add(test.included, test.glob) 664 require.NoError(t, err) 665 got := f.DumpFilters() 666 assert.Equal(t, test.want, got, fmt.Sprintf("Add(%v, %q)", test.included, test.glob)) 667 } 668 } 669 670 func testFilterForEachLine(t *testing.T, useStdin, raw bool) { 671 file := testFile(t, `; comment 672 one 673 # another comment 674 675 676 two 677 # indented comment 678 three 679 four 680 five 681 six `) 682 defer func() { 683 err := os.Remove(file) 684 require.NoError(t, err) 685 }() 686 lines := []string{} 687 fileName := file 688 if useStdin { 689 in, err := os.Open(file) 690 require.NoError(t, err) 691 oldStdin := os.Stdin 692 os.Stdin = in 693 defer func() { 694 os.Stdin = oldStdin 695 _ = in.Close() 696 }() 697 fileName = "-" 698 } 699 err := forEachLine(fileName, raw, func(s string) error { 700 lines = append(lines, s) 701 return nil 702 }) 703 require.NoError(t, err) 704 if raw { 705 assert.Equal(t, "; comment,one,# another comment,,,two, # indented comment,three ,four ,five, six ", 706 strings.Join(lines, ",")) 707 } else { 708 assert.Equal(t, "one,two,three,four,five,six", strings.Join(lines, ",")) 709 } 710 } 711 712 func TestFilterForEachLine(t *testing.T) { 713 testFilterForEachLine(t, false, false) 714 } 715 716 func TestFilterForEachLineStdin(t *testing.T) { 717 testFilterForEachLine(t, true, false) 718 } 719 720 func TestFilterForEachLineWithRaw(t *testing.T) { 721 testFilterForEachLine(t, false, true) 722 } 723 724 func TestFilterForEachLineStdinWithRaw(t *testing.T) { 725 testFilterForEachLine(t, true, true) 726 } 727 728 func TestFilterMatchesFromDocs(t *testing.T) { 729 for _, test := range []struct { 730 glob string 731 included bool 732 file string 733 ignoreCase bool 734 }{ 735 {"file.jpg", true, "file.jpg", false}, 736 {"file.jpg", true, "directory/file.jpg", false}, 737 {"file.jpg", false, "afile.jpg", false}, 738 {"file.jpg", false, "directory/afile.jpg", false}, 739 {"/file.jpg", true, "file.jpg", false}, 740 {"/file.jpg", false, "afile.jpg", false}, 741 {"/file.jpg", false, "directory/file.jpg", false}, 742 {"*.jpg", true, "file.jpg", false}, 743 {"*.jpg", true, "directory/file.jpg", false}, 744 {"*.jpg", false, "file.jpg/anotherfile.png", false}, 745 {"dir/**", true, "dir/file.jpg", false}, 746 {"dir/**", true, "dir/dir1/dir2/file.jpg", false}, 747 {"dir/**", false, "directory/file.jpg", false}, 748 {"dir/**", false, "adir/file.jpg", false}, 749 {"l?ss", true, "less", false}, 750 {"l?ss", true, "lass", false}, 751 {"l?ss", false, "floss", false}, 752 {"h[ae]llo", true, "hello", false}, 753 {"h[ae]llo", true, "hallo", false}, 754 {"h[ae]llo", false, "hullo", false}, 755 {"{one,two}_potato", true, "one_potato", false}, 756 {"{one,two}_potato", true, "two_potato", false}, 757 {"{one,two}_potato", false, "three_potato", false}, 758 {"{one,two}_potato", false, "_potato", false}, 759 {"\\*.jpg", true, "*.jpg", false}, 760 {"\\\\.jpg", true, "\\.jpg", false}, 761 {"\\[one\\].jpg", true, "[one].jpg", false}, 762 {"potato", true, "potato", false}, 763 {"potato", false, "POTATO", false}, 764 {"potato", true, "potato", true}, 765 {"potato", true, "POTATO", true}, 766 } { 767 f, err := NewFilter(nil) 768 require.NoError(t, err) 769 if test.ignoreCase { 770 f.Opt.IgnoreCase = true 771 } 772 err = f.Add(true, test.glob) 773 require.NoError(t, err) 774 err = f.Add(false, "*") 775 require.NoError(t, err) 776 included := f.Include(test.file, 0, time.Unix(0, 0), nil) 777 if included != test.included { 778 t.Errorf("%q match %q: want %v got %v", test.glob, test.file, test.included, included) 779 } 780 } 781 } 782 783 func TestNewFilterUsesDirectoryFilters(t *testing.T) { 784 for i, test := range []struct { 785 rules []string 786 want bool 787 }{ 788 { 789 rules: []string{}, 790 want: false, 791 }, 792 { 793 rules: []string{ 794 "+ *", 795 }, 796 want: false, 797 }, 798 { 799 rules: []string{ 800 "+ *.jpg", 801 "- *", 802 }, 803 want: false, 804 }, 805 { 806 rules: []string{ 807 "- *.jpg", 808 }, 809 want: false, 810 }, 811 { 812 rules: []string{ 813 "- *.jpg", 814 "+ *", 815 }, 816 want: false, 817 }, 818 { 819 rules: []string{ 820 "+ dir/*.jpg", 821 "- *", 822 }, 823 want: true, 824 }, 825 { 826 rules: []string{ 827 "+ dir/**", 828 }, 829 want: true, 830 }, 831 { 832 rules: []string{ 833 "- dir/**", 834 }, 835 want: true, 836 }, 837 { 838 rules: []string{ 839 "- /dir/**", 840 }, 841 want: true, 842 }, 843 } { 844 what := fmt.Sprintf("#%d", i) 845 f, err := NewFilter(nil) 846 require.NoError(t, err) 847 for _, rule := range test.rules { 848 err := f.AddRule(rule) 849 require.NoError(t, err, what) 850 } 851 got := f.UsesDirectoryFilters() 852 assert.Equal(t, test.want, got, fmt.Sprintf("%s: %s", what, f.DumpFilters())) 853 } 854 } 855 856 func TestGetConfig(t *testing.T) { 857 ctx := context.Background() 858 859 // Check nil 860 //lint:ignore SA1012 false positive when running staticcheck, we want to test passing a nil Context and therefore ignore lint suggestion to use context.TODO 861 //nolint:staticcheck // Don't include staticcheck when running golangci-lint to avoid SA1012 862 config := GetConfig(nil) 863 assert.Equal(t, globalConfig, config) 864 865 // Check empty config 866 config = GetConfig(ctx) 867 assert.Equal(t, globalConfig, config) 868 869 // Check adding a config 870 ctx2, config2 := AddConfig(ctx) 871 require.NoError(t, config2.AddRule("+ *.jpg")) 872 assert.NotEqual(t, config2, config) 873 874 // Check can get config back 875 config2ctx := GetConfig(ctx2) 876 assert.Equal(t, config2, config2ctx) 877 878 // Check ReplaceConfig 879 f, err := NewFilter(nil) 880 require.NoError(t, err) 881 ctx3 := ReplaceConfig(ctx, f) 882 assert.Equal(t, globalConfig, GetConfig(ctx3)) 883 }