github.com/mckael/restic@v0.8.3/cmd/restic/integration_helpers_test.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "runtime" 10 "testing" 11 12 "github.com/restic/restic/internal/options" 13 "github.com/restic/restic/internal/repository" 14 rtest "github.com/restic/restic/internal/test" 15 ) 16 17 type dirEntry struct { 18 path string 19 fi os.FileInfo 20 link uint64 21 } 22 23 func walkDir(dir string) <-chan *dirEntry { 24 ch := make(chan *dirEntry, 100) 25 26 go func() { 27 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 28 if err != nil { 29 fmt.Fprintf(os.Stderr, "error: %v\n", err) 30 return nil 31 } 32 33 name, err := filepath.Rel(dir, path) 34 if err != nil { 35 fmt.Fprintf(os.Stderr, "error: %v\n", err) 36 return nil 37 } 38 39 ch <- &dirEntry{ 40 path: name, 41 fi: info, 42 link: nlink(info), 43 } 44 45 return nil 46 }) 47 48 if err != nil { 49 fmt.Fprintf(os.Stderr, "Walk() error: %v\n", err) 50 } 51 52 close(ch) 53 }() 54 55 // first element is root 56 _ = <-ch 57 58 return ch 59 } 60 61 func isSymlink(fi os.FileInfo) bool { 62 mode := fi.Mode() & (os.ModeType | os.ModeCharDevice) 63 return mode == os.ModeSymlink 64 } 65 66 func sameModTime(fi1, fi2 os.FileInfo) bool { 67 switch runtime.GOOS { 68 case "darwin", "freebsd", "openbsd": 69 if isSymlink(fi1) && isSymlink(fi2) { 70 return true 71 } 72 } 73 74 same := fi1.ModTime().Equal(fi2.ModTime()) 75 if !same && (runtime.GOOS == "darwin" || runtime.GOOS == "openbsd") { 76 // Allow up to 1μs difference, because macOS <10.13 cannot restore 77 // with nanosecond precision and the current version of Go (1.9.2) 78 // does not yet support the new syscall. (#1087) 79 mt1 := fi1.ModTime() 80 mt2 := fi2.ModTime() 81 usecDiff := (mt1.Nanosecond()-mt2.Nanosecond())/1000 + (mt1.Second()-mt2.Second())*1000000 82 same = usecDiff <= 1 && usecDiff >= -1 83 } 84 return same 85 } 86 87 // directoriesEqualContents checks if both directories contain exactly the same 88 // contents. 89 func directoriesEqualContents(dir1, dir2 string) bool { 90 ch1 := walkDir(dir1) 91 ch2 := walkDir(dir2) 92 93 changes := false 94 95 var a, b *dirEntry 96 for { 97 var ok bool 98 99 if ch1 != nil && a == nil { 100 a, ok = <-ch1 101 if !ok { 102 ch1 = nil 103 } 104 } 105 106 if ch2 != nil && b == nil { 107 b, ok = <-ch2 108 if !ok { 109 ch2 = nil 110 } 111 } 112 113 if ch1 == nil && ch2 == nil { 114 break 115 } 116 117 if ch1 == nil { 118 fmt.Printf("+%v\n", b.path) 119 changes = true 120 } else if ch2 == nil { 121 fmt.Printf("-%v\n", a.path) 122 changes = true 123 } else if !a.equals(b) { 124 if a.path < b.path { 125 fmt.Printf("-%v\n", a.path) 126 changes = true 127 a = nil 128 continue 129 } else if a.path > b.path { 130 fmt.Printf("+%v\n", b.path) 131 changes = true 132 b = nil 133 continue 134 } else { 135 fmt.Printf("%%%v\n", a.path) 136 changes = true 137 } 138 } 139 140 a, b = nil, nil 141 } 142 143 if changes { 144 return false 145 } 146 147 return true 148 } 149 150 type dirStat struct { 151 files, dirs, other uint 152 size uint64 153 } 154 155 func isFile(fi os.FileInfo) bool { 156 return fi.Mode()&(os.ModeType|os.ModeCharDevice) == 0 157 } 158 159 // dirStats walks dir and collects stats. 160 func dirStats(dir string) (stat dirStat) { 161 for entry := range walkDir(dir) { 162 if isFile(entry.fi) { 163 stat.files++ 164 stat.size += uint64(entry.fi.Size()) 165 continue 166 } 167 168 if entry.fi.IsDir() { 169 stat.dirs++ 170 continue 171 } 172 173 stat.other++ 174 } 175 176 return stat 177 } 178 179 type testEnvironment struct { 180 base, cache, repo, mountpoint, testdata string 181 gopts GlobalOptions 182 } 183 184 // withTestEnvironment creates a test environment and returns a cleanup 185 // function which removes it. 186 func withTestEnvironment(t testing.TB) (env *testEnvironment, cleanup func()) { 187 if !rtest.RunIntegrationTest { 188 t.Skip("integration tests disabled") 189 } 190 191 repository.TestUseLowSecurityKDFParameters(t) 192 193 tempdir, err := ioutil.TempDir(rtest.TestTempDir, "restic-test-") 194 rtest.OK(t, err) 195 196 env = &testEnvironment{ 197 base: tempdir, 198 cache: filepath.Join(tempdir, "cache"), 199 repo: filepath.Join(tempdir, "repo"), 200 testdata: filepath.Join(tempdir, "testdata"), 201 mountpoint: filepath.Join(tempdir, "mount"), 202 } 203 204 rtest.OK(t, os.MkdirAll(env.mountpoint, 0700)) 205 rtest.OK(t, os.MkdirAll(env.testdata, 0700)) 206 rtest.OK(t, os.MkdirAll(env.cache, 0700)) 207 rtest.OK(t, os.MkdirAll(env.repo, 0700)) 208 209 env.gopts = GlobalOptions{ 210 Repo: env.repo, 211 Quiet: true, 212 CacheDir: env.cache, 213 ctx: context.Background(), 214 password: rtest.TestPassword, 215 stdout: os.Stdout, 216 stderr: os.Stderr, 217 extended: make(options.Options), 218 } 219 220 // always overwrite global options 221 globalOptions = env.gopts 222 223 cleanup = func() { 224 if !rtest.TestCleanupTempDirs { 225 t.Logf("leaving temporary directory %v used for test", tempdir) 226 return 227 } 228 rtest.RemoveAll(t, tempdir) 229 } 230 231 return env, cleanup 232 }