| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | package archiver | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/google/go-cmp/cmp" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/fs" | 
					
						
							|  |  |  | 	restictest "github.com/restic/restic/internal/test" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestScanner(t *testing.T) { | 
					
						
							|  |  |  | 	var tests = []struct { | 
					
						
							|  |  |  | 		name  string | 
					
						
							|  |  |  | 		src   TestDir | 
					
						
							|  |  |  | 		want  map[string]ScanStats | 
					
						
							|  |  |  | 		selFn SelectFunc | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "include-all", | 
					
						
							|  |  |  | 			src: TestDir{ | 
					
						
							|  |  |  | 				"other": TestFile{Content: "another file"}, | 
					
						
							|  |  |  | 				"work": TestDir{ | 
					
						
							|  |  |  | 					"foo":     TestFile{Content: "foo"}, | 
					
						
							|  |  |  | 					"foo.txt": TestFile{Content: "foo text file"}, | 
					
						
							|  |  |  | 					"subdir": TestDir{ | 
					
						
							|  |  |  | 						"other":   TestFile{Content: "other in subdir"}, | 
					
						
							|  |  |  | 						"bar.txt": TestFile{Content: "bar.txt in subdir"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			want: map[string]ScanStats{ | 
					
						
							| 
									
										
										
										
											2020-10-05 23:07:23 +02:00
										 |  |  | 				filepath.FromSlash("other"):               {Files: 1, Bytes: 12}, | 
					
						
							|  |  |  | 				filepath.FromSlash("work/foo"):            {Files: 2, Bytes: 15}, | 
					
						
							|  |  |  | 				filepath.FromSlash("work/foo.txt"):        {Files: 3, Bytes: 28}, | 
					
						
							|  |  |  | 				filepath.FromSlash("work/subdir/bar.txt"): {Files: 4, Bytes: 45}, | 
					
						
							|  |  |  | 				filepath.FromSlash("work/subdir/other"):   {Files: 5, Bytes: 60}, | 
					
						
							|  |  |  | 				filepath.FromSlash("work/subdir"):         {Files: 5, Dirs: 1, Bytes: 60}, | 
					
						
							|  |  |  | 				filepath.FromSlash("work"):                {Files: 5, Dirs: 2, Bytes: 60}, | 
					
						
							| 
									
										
										
										
											2021-01-28 21:30:06 +01:00
										 |  |  | 				filepath.FromSlash(""):                    {Files: 5, Dirs: 2, Bytes: 60}, | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "select-txt", | 
					
						
							|  |  |  | 			src: TestDir{ | 
					
						
							|  |  |  | 				"other": TestFile{Content: "another file"}, | 
					
						
							|  |  |  | 				"work": TestDir{ | 
					
						
							|  |  |  | 					"foo":     TestFile{Content: "foo"}, | 
					
						
							|  |  |  | 					"foo.txt": TestFile{Content: "foo text file"}, | 
					
						
							|  |  |  | 					"subdir": TestDir{ | 
					
						
							|  |  |  | 						"other":   TestFile{Content: "other in subdir"}, | 
					
						
							|  |  |  | 						"bar.txt": TestFile{Content: "bar.txt in subdir"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			selFn: func(item string, fi os.FileInfo) bool { | 
					
						
							|  |  |  | 				if fi.IsDir() { | 
					
						
							|  |  |  | 					return true | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if filepath.Ext(item) == ".txt" { | 
					
						
							|  |  |  | 					return true | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return false | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			want: map[string]ScanStats{ | 
					
						
							| 
									
										
										
										
											2020-10-05 23:07:23 +02:00
										 |  |  | 				filepath.FromSlash("work/foo.txt"):        {Files: 1, Bytes: 13}, | 
					
						
							|  |  |  | 				filepath.FromSlash("work/subdir/bar.txt"): {Files: 2, Bytes: 30}, | 
					
						
							|  |  |  | 				filepath.FromSlash("work/subdir"):         {Files: 2, Dirs: 1, Bytes: 30}, | 
					
						
							|  |  |  | 				filepath.FromSlash("work"):                {Files: 2, Dirs: 2, Bytes: 30}, | 
					
						
							| 
									
										
										
										
											2021-01-28 21:30:06 +01:00
										 |  |  | 				filepath.FromSlash(""):                    {Files: 2, Dirs: 2, Bytes: 30}, | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, test := range tests { | 
					
						
							|  |  |  | 		t.Run(test.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			ctx, cancel := context.WithCancel(context.Background()) | 
					
						
							|  |  |  | 			defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-09 13:42:33 +01:00
										 |  |  | 			tempdir := restictest.TempDir(t) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 			TestCreateFiles(t, tempdir, test.src) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 13:24:09 +01:00
										 |  |  | 			back := restictest.Chdir(t, tempdir) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 			defer back() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			cur, err := os.Getwd() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.Fatal(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-05 08:04:55 -04:00
										 |  |  | 			sc := NewScanner(fs.Track{FS: fs.Local{}}) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 			if test.selFn != nil { | 
					
						
							|  |  |  | 				sc.Select = test.selFn | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			results := make(map[string]ScanStats) | 
					
						
							|  |  |  | 			sc.Result = func(item string, s ScanStats) { | 
					
						
							|  |  |  | 				var p string | 
					
						
							|  |  |  | 				var err error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if item != "" { | 
					
						
							|  |  |  | 					p, err = filepath.Rel(cur, item) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						panic(err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				results[p] = s | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			err = sc.Scan(ctx, []string{"."}) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.Fatal(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if !cmp.Equal(test.want, results) { | 
					
						
							|  |  |  | 				t.Error(cmp.Diff(test.want, results)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestScannerError(t *testing.T) { | 
					
						
							|  |  |  | 	var tests = []struct { | 
					
						
							|  |  |  | 		name    string | 
					
						
							|  |  |  | 		unix    bool | 
					
						
							|  |  |  | 		src     TestDir | 
					
						
							|  |  |  | 		result  ScanStats | 
					
						
							|  |  |  | 		selFn   SelectFunc | 
					
						
							| 
									
										
										
										
											2022-05-21 00:31:26 +02:00
										 |  |  | 		errFn   func(t testing.TB, item string, err error) error | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 		resFn   func(t testing.TB, item string, s ScanStats) | 
					
						
							|  |  |  | 		prepare func(t testing.TB) | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "no-error", | 
					
						
							|  |  |  | 			src: TestDir{ | 
					
						
							|  |  |  | 				"other": TestFile{Content: "another file"}, | 
					
						
							|  |  |  | 				"work": TestDir{ | 
					
						
							|  |  |  | 					"foo":     TestFile{Content: "foo"}, | 
					
						
							|  |  |  | 					"foo.txt": TestFile{Content: "foo text file"}, | 
					
						
							|  |  |  | 					"subdir": TestDir{ | 
					
						
							|  |  |  | 						"other":   TestFile{Content: "other in subdir"}, | 
					
						
							|  |  |  | 						"bar.txt": TestFile{Content: "bar.txt in subdir"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2021-01-28 21:30:06 +01:00
										 |  |  | 			result: ScanStats{Files: 5, Dirs: 2, Bytes: 60}, | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "unreadable-dir", | 
					
						
							|  |  |  | 			unix: true, | 
					
						
							|  |  |  | 			src: TestDir{ | 
					
						
							|  |  |  | 				"other": TestFile{Content: "another file"}, | 
					
						
							|  |  |  | 				"work": TestDir{ | 
					
						
							|  |  |  | 					"foo":     TestFile{Content: "foo"}, | 
					
						
							|  |  |  | 					"foo.txt": TestFile{Content: "foo text file"}, | 
					
						
							|  |  |  | 					"subdir": TestDir{ | 
					
						
							|  |  |  | 						"other":   TestFile{Content: "other in subdir"}, | 
					
						
							|  |  |  | 						"bar.txt": TestFile{Content: "bar.txt in subdir"}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2021-01-28 21:30:06 +01:00
										 |  |  | 			result: ScanStats{Files: 3, Dirs: 1, Bytes: 28}, | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 			prepare: func(t testing.TB) { | 
					
						
							|  |  |  | 				err := os.Chmod(filepath.Join("work", "subdir"), 0000) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					t.Fatal(err) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-05-21 00:31:26 +02:00
										 |  |  | 			errFn: func(t testing.TB, item string, err error) error { | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 				if item == filepath.FromSlash("work/subdir") { | 
					
						
							|  |  |  | 					return nil | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "removed-item", | 
					
						
							|  |  |  | 			src: TestDir{ | 
					
						
							|  |  |  | 				"bar":   TestFile{Content: "bar"}, | 
					
						
							|  |  |  | 				"baz":   TestFile{Content: "baz"}, | 
					
						
							|  |  |  | 				"foo":   TestFile{Content: "foo"}, | 
					
						
							|  |  |  | 				"other": TestFile{Content: "other"}, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2021-01-28 21:30:06 +01:00
										 |  |  | 			result: ScanStats{Files: 3, Dirs: 0, Bytes: 11}, | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 			resFn: func(t testing.TB, item string, s ScanStats) { | 
					
						
							|  |  |  | 				if item == "bar" { | 
					
						
							|  |  |  | 					err := os.Remove("foo") | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						t.Fatal(err) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-05-21 00:31:26 +02:00
										 |  |  | 			errFn: func(t testing.TB, item string, err error) error { | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 				if item == "foo" { | 
					
						
							|  |  |  | 					t.Logf("ignoring error for %v: %v", item, err) | 
					
						
							|  |  |  | 					return nil | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, test := range tests { | 
					
						
							|  |  |  | 		t.Run(test.name, func(t *testing.T) { | 
					
						
							|  |  |  | 			if test.unix && runtime.GOOS == "windows" { | 
					
						
							|  |  |  | 				t.Skipf("skip on windows") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			ctx, cancel := context.WithCancel(context.Background()) | 
					
						
							|  |  |  | 			defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-09 13:42:33 +01:00
										 |  |  | 			tempdir := restictest.TempDir(t) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 			TestCreateFiles(t, tempdir, test.src) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 13:24:09 +01:00
										 |  |  | 			back := restictest.Chdir(t, tempdir) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 			defer back() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			cur, err := os.Getwd() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.Fatal(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if test.prepare != nil { | 
					
						
							|  |  |  | 				test.prepare(t) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-05 08:04:55 -04:00
										 |  |  | 			sc := NewScanner(fs.Track{FS: fs.Local{}}) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 			if test.selFn != nil { | 
					
						
							|  |  |  | 				sc.Select = test.selFn | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var stats ScanStats | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			sc.Result = func(item string, s ScanStats) { | 
					
						
							|  |  |  | 				if item == "" { | 
					
						
							|  |  |  | 					stats = s | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if test.resFn != nil { | 
					
						
							|  |  |  | 					p, relErr := filepath.Rel(cur, item) | 
					
						
							|  |  |  | 					if relErr != nil { | 
					
						
							|  |  |  | 						panic(relErr) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					test.resFn(t, p, s) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if test.errFn != nil { | 
					
						
							| 
									
										
										
										
											2022-05-21 00:31:26 +02:00
										 |  |  | 				sc.Error = func(item string, err error) error { | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 					p, relErr := filepath.Rel(cur, item) | 
					
						
							|  |  |  | 					if relErr != nil { | 
					
						
							|  |  |  | 						panic(relErr) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-21 00:31:26 +02:00
										 |  |  | 					return test.errFn(t, p, err) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			err = sc.Scan(ctx, []string{"."}) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.Fatal(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if stats != test.result { | 
					
						
							|  |  |  | 				t.Errorf("wrong final result, want\n  %#v\ngot:\n  %#v", test.result, stats) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestScannerCancel(t *testing.T) { | 
					
						
							|  |  |  | 	src := TestDir{ | 
					
						
							|  |  |  | 		"bar":   TestFile{Content: "bar"}, | 
					
						
							|  |  |  | 		"baz":   TestFile{Content: "baz"}, | 
					
						
							|  |  |  | 		"foo":   TestFile{Content: "foo"}, | 
					
						
							|  |  |  | 		"other": TestFile{Content: "other"}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-28 21:30:06 +01:00
										 |  |  | 	result := ScanStats{Files: 2, Dirs: 0, Bytes: 6} | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ctx, cancel := context.WithCancel(context.Background()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-09 13:42:33 +01:00
										 |  |  | 	tempdir := restictest.TempDir(t) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 	TestCreateFiles(t, tempdir, src) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-17 13:24:09 +01:00
										 |  |  | 	back := restictest.Chdir(t, tempdir) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 	defer back() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cur, err := os.Getwd() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-05 08:04:55 -04:00
										 |  |  | 	sc := NewScanner(fs.Track{FS: fs.Local{}}) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 	var lastStats ScanStats | 
					
						
							|  |  |  | 	sc.Result = func(item string, s ScanStats) { | 
					
						
							|  |  |  | 		lastStats = s | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if item == filepath.Join(cur, "baz") { | 
					
						
							|  |  |  | 			t.Logf("found baz") | 
					
						
							|  |  |  | 			cancel() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = sc.Scan(ctx, []string{"."}) | 
					
						
							| 
									
										
										
										
											2018-09-08 18:35:49 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Errorf("unexpected error %v found", err) | 
					
						
							| 
									
										
										
										
											2018-03-30 22:43:18 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if lastStats != result { | 
					
						
							|  |  |  | 		t.Errorf("wrong final result, want\n  %#v\ngot:\n  %#v", result, lastStats) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |