github.com/mckael/restic@v0.8.3/cmd/restic/integration_fuse_test.go (about) 1 // +build !openbsd 2 // +build !windows 3 4 package main 5 6 import ( 7 "context" 8 "fmt" 9 "os" 10 "path/filepath" 11 "testing" 12 "time" 13 14 "github.com/restic/restic/internal/repository" 15 "github.com/restic/restic/internal/restic" 16 rtest "github.com/restic/restic/internal/test" 17 ) 18 19 const ( 20 mountWait = 20 21 mountSleep = 100 * time.Millisecond 22 mountTestSubdir = "snapshots" 23 ) 24 25 func snapshotsDirExists(t testing.TB, dir string) bool { 26 f, err := os.Open(filepath.Join(dir, mountTestSubdir)) 27 if err != nil && os.IsNotExist(err) { 28 return false 29 } 30 31 if err != nil { 32 t.Error(err) 33 } 34 35 if err := f.Close(); err != nil { 36 t.Error(err) 37 } 38 39 return true 40 } 41 42 // waitForMount blocks (max mountWait * mountSleep) until the subdir 43 // "snapshots" appears in the dir. 44 func waitForMount(t testing.TB, dir string) { 45 for i := 0; i < mountWait; i++ { 46 if snapshotsDirExists(t, dir) { 47 t.Log("mounted directory is ready") 48 return 49 } 50 51 time.Sleep(mountSleep) 52 } 53 54 t.Errorf("subdir %q of dir %s never appeared", mountTestSubdir, dir) 55 } 56 57 func testRunMount(t testing.TB, gopts GlobalOptions, dir string) { 58 opts := MountOptions{ 59 SnapshotTemplate: time.RFC3339, 60 } 61 rtest.OK(t, runMount(opts, gopts, []string{dir})) 62 } 63 64 func testRunUmount(t testing.TB, gopts GlobalOptions, dir string) { 65 var err error 66 for i := 0; i < mountWait; i++ { 67 if err = umount(dir); err == nil { 68 t.Logf("directory %v umounted", dir) 69 return 70 } 71 72 time.Sleep(mountSleep) 73 } 74 75 t.Errorf("unable to umount dir %v, last error was: %v", dir, err) 76 } 77 78 func listSnapshots(t testing.TB, dir string) []string { 79 snapshotsDir, err := os.Open(filepath.Join(dir, "snapshots")) 80 rtest.OK(t, err) 81 names, err := snapshotsDir.Readdirnames(-1) 82 rtest.OK(t, err) 83 rtest.OK(t, snapshotsDir.Close()) 84 return names 85 } 86 87 func checkSnapshots(t testing.TB, global GlobalOptions, repo *repository.Repository, mountpoint, repodir string, snapshotIDs restic.IDs, expectedSnapshotsInFuseDir int) { 88 t.Logf("checking for %d snapshots: %v", len(snapshotIDs), snapshotIDs) 89 90 go testRunMount(t, global, mountpoint) 91 waitForMount(t, mountpoint) 92 defer testRunUmount(t, global, mountpoint) 93 94 if !snapshotsDirExists(t, mountpoint) { 95 t.Fatal(`virtual directory "snapshots" doesn't exist`) 96 } 97 98 ids := listSnapshots(t, repodir) 99 t.Logf("found %v snapshots in repo: %v", len(ids), ids) 100 101 namesInSnapshots := listSnapshots(t, mountpoint) 102 t.Logf("found %v snapshots in fuse mount: %v", len(namesInSnapshots), namesInSnapshots) 103 rtest.Assert(t, 104 expectedSnapshotsInFuseDir == len(namesInSnapshots), 105 "Invalid number of snapshots: expected %d, got %d", expectedSnapshotsInFuseDir, len(namesInSnapshots)) 106 107 namesMap := make(map[string]bool) 108 for _, name := range namesInSnapshots { 109 namesMap[name] = false 110 } 111 112 // Is "latest" present? 113 if len(namesMap) != 0 { 114 _, ok := namesMap["latest"] 115 if !ok { 116 t.Errorf("Symlink latest isn't present in fuse dir") 117 } else { 118 namesMap["latest"] = true 119 } 120 } 121 122 for _, id := range snapshotIDs { 123 snapshot, err := restic.LoadSnapshot(context.TODO(), repo, id) 124 rtest.OK(t, err) 125 126 ts := snapshot.Time.Format(time.RFC3339) 127 present, ok := namesMap[ts] 128 if !ok { 129 t.Errorf("Snapshot %v (%q) isn't present in fuse dir", id.Str(), ts) 130 } 131 132 for i := 1; present; i++ { 133 ts = fmt.Sprintf("%s-%d", snapshot.Time.Format(time.RFC3339), i) 134 present, ok = namesMap[ts] 135 if !ok { 136 t.Errorf("Snapshot %v (%q) isn't present in fuse dir", id.Str(), ts) 137 } 138 139 if !present { 140 break 141 } 142 } 143 144 namesMap[ts] = true 145 } 146 147 for name, present := range namesMap { 148 rtest.Assert(t, present, "Directory %s is present in fuse dir but is not a snapshot", name) 149 } 150 } 151 152 func TestMount(t *testing.T) { 153 if !rtest.RunFuseTest { 154 t.Skip("Skipping fuse tests") 155 } 156 157 env, cleanup := withTestEnvironment(t) 158 defer cleanup() 159 160 testRunInit(t, env.gopts) 161 162 repo, err := OpenRepository(env.gopts) 163 rtest.OK(t, err) 164 165 // We remove the mountpoint now to check that cmdMount creates it 166 rtest.RemoveAll(t, env.mountpoint) 167 168 checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, []restic.ID{}, 0) 169 170 rtest.SetupTarTestFixture(t, env.testdata, filepath.Join("testdata", "backup-data.tar.gz")) 171 172 // first backup 173 testRunBackup(t, []string{env.testdata}, BackupOptions{}, env.gopts) 174 snapshotIDs := testRunList(t, "snapshots", env.gopts) 175 rtest.Assert(t, len(snapshotIDs) == 1, 176 "expected one snapshot, got %v", snapshotIDs) 177 178 checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, snapshotIDs, 2) 179 180 // second backup, implicit incremental 181 testRunBackup(t, []string{env.testdata}, BackupOptions{}, env.gopts) 182 snapshotIDs = testRunList(t, "snapshots", env.gopts) 183 rtest.Assert(t, len(snapshotIDs) == 2, 184 "expected two snapshots, got %v", snapshotIDs) 185 186 checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, snapshotIDs, 3) 187 188 // third backup, explicit incremental 189 bopts := BackupOptions{Parent: snapshotIDs[0].String()} 190 testRunBackup(t, []string{env.testdata}, bopts, env.gopts) 191 snapshotIDs = testRunList(t, "snapshots", env.gopts) 192 rtest.Assert(t, len(snapshotIDs) == 3, 193 "expected three snapshots, got %v", snapshotIDs) 194 195 checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, snapshotIDs, 4) 196 } 197 198 func TestMountSameTimestamps(t *testing.T) { 199 if !rtest.RunFuseTest { 200 t.Skip("Skipping fuse tests") 201 } 202 203 env, cleanup := withTestEnvironment(t) 204 defer cleanup() 205 206 rtest.SetupTarTestFixture(t, env.base, filepath.Join("testdata", "repo-same-timestamps.tar.gz")) 207 208 repo, err := OpenRepository(env.gopts) 209 rtest.OK(t, err) 210 211 ids := []restic.ID{ 212 restic.TestParseID("280303689e5027328889a06d718b729e96a1ce6ae9ef8290bff550459ae611ee"), 213 restic.TestParseID("75ad6cdc0868e082f2596d5ab8705e9f7d87316f5bf5690385eeff8dbe49d9f5"), 214 restic.TestParseID("5fd0d8b2ef0fa5d23e58f1e460188abb0f525c0f0c4af8365a1280c807a80a1b"), 215 } 216 217 checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, ids, 4) 218 }