github.com/advanderveer/restic@v0.8.1-0.20171209104529-42a8c19aaea6/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  	rtest.OK(t, runMount(opts, gopts, []string{dir}))
    60  }
    61  
    62  func testRunUmount(t testing.TB, gopts GlobalOptions, dir string) {
    63  	var err error
    64  	for i := 0; i < mountWait; i++ {
    65  		if err = umount(dir); err == nil {
    66  			t.Logf("directory %v umounted", dir)
    67  			return
    68  		}
    69  
    70  		time.Sleep(mountSleep)
    71  	}
    72  
    73  	t.Errorf("unable to umount dir %v, last error was: %v", dir, err)
    74  }
    75  
    76  func listSnapshots(t testing.TB, dir string) []string {
    77  	snapshotsDir, err := os.Open(filepath.Join(dir, "snapshots"))
    78  	rtest.OK(t, err)
    79  	names, err := snapshotsDir.Readdirnames(-1)
    80  	rtest.OK(t, err)
    81  	rtest.OK(t, snapshotsDir.Close())
    82  	return names
    83  }
    84  
    85  func checkSnapshots(t testing.TB, global GlobalOptions, repo *repository.Repository, mountpoint, repodir string, snapshotIDs restic.IDs, expectedSnapshotsInFuseDir int) {
    86  	t.Logf("checking for %d snapshots: %v", len(snapshotIDs), snapshotIDs)
    87  
    88  	go testRunMount(t, global, mountpoint)
    89  	waitForMount(t, mountpoint)
    90  	defer testRunUmount(t, global, mountpoint)
    91  
    92  	if !snapshotsDirExists(t, mountpoint) {
    93  		t.Fatal(`virtual directory "snapshots" doesn't exist`)
    94  	}
    95  
    96  	ids := listSnapshots(t, repodir)
    97  	t.Logf("found %v snapshots in repo: %v", len(ids), ids)
    98  
    99  	namesInSnapshots := listSnapshots(t, mountpoint)
   100  	t.Logf("found %v snapshots in fuse mount: %v", len(namesInSnapshots), namesInSnapshots)
   101  	rtest.Assert(t,
   102  		expectedSnapshotsInFuseDir == len(namesInSnapshots),
   103  		"Invalid number of snapshots: expected %d, got %d", expectedSnapshotsInFuseDir, len(namesInSnapshots))
   104  
   105  	namesMap := make(map[string]bool)
   106  	for _, name := range namesInSnapshots {
   107  		namesMap[name] = false
   108  	}
   109  
   110  	// Is "latest" present?
   111  	if len(namesMap) != 0 {
   112  		_, ok := namesMap["latest"]
   113  		if !ok {
   114  			t.Errorf("Symlink latest isn't present in fuse dir")
   115  		} else {
   116  			namesMap["latest"] = true
   117  		}
   118  	}
   119  
   120  	for _, id := range snapshotIDs {
   121  		snapshot, err := restic.LoadSnapshot(context.TODO(), repo, id)
   122  		rtest.OK(t, err)
   123  
   124  		ts := snapshot.Time.Format(time.RFC3339)
   125  		present, ok := namesMap[ts]
   126  		if !ok {
   127  			t.Errorf("Snapshot %v (%q) isn't present in fuse dir", id.Str(), ts)
   128  		}
   129  
   130  		for i := 1; present; i++ {
   131  			ts = fmt.Sprintf("%s-%d", snapshot.Time.Format(time.RFC3339), i)
   132  			present, ok = namesMap[ts]
   133  			if !ok {
   134  				t.Errorf("Snapshot %v (%q) isn't present in fuse dir", id.Str(), ts)
   135  			}
   136  
   137  			if !present {
   138  				break
   139  			}
   140  		}
   141  
   142  		namesMap[ts] = true
   143  	}
   144  
   145  	for name, present := range namesMap {
   146  		rtest.Assert(t, present, "Directory %s is present in fuse dir but is not a snapshot", name)
   147  	}
   148  }
   149  
   150  func TestMount(t *testing.T) {
   151  	if !rtest.RunFuseTest {
   152  		t.Skip("Skipping fuse tests")
   153  	}
   154  
   155  	env, cleanup := withTestEnvironment(t)
   156  	defer cleanup()
   157  
   158  	testRunInit(t, env.gopts)
   159  
   160  	repo, err := OpenRepository(env.gopts)
   161  	rtest.OK(t, err)
   162  
   163  	// We remove the mountpoint now to check that cmdMount creates it
   164  	rtest.RemoveAll(t, env.mountpoint)
   165  
   166  	checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, []restic.ID{}, 0)
   167  
   168  	rtest.SetupTarTestFixture(t, env.testdata, filepath.Join("testdata", "backup-data.tar.gz"))
   169  
   170  	// first backup
   171  	testRunBackup(t, []string{env.testdata}, BackupOptions{}, env.gopts)
   172  	snapshotIDs := testRunList(t, "snapshots", env.gopts)
   173  	rtest.Assert(t, len(snapshotIDs) == 1,
   174  		"expected one snapshot, got %v", snapshotIDs)
   175  
   176  	checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, snapshotIDs, 2)
   177  
   178  	// second backup, implicit incremental
   179  	testRunBackup(t, []string{env.testdata}, BackupOptions{}, env.gopts)
   180  	snapshotIDs = testRunList(t, "snapshots", env.gopts)
   181  	rtest.Assert(t, len(snapshotIDs) == 2,
   182  		"expected two snapshots, got %v", snapshotIDs)
   183  
   184  	checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, snapshotIDs, 3)
   185  
   186  	// third backup, explicit incremental
   187  	bopts := BackupOptions{Parent: snapshotIDs[0].String()}
   188  	testRunBackup(t, []string{env.testdata}, bopts, env.gopts)
   189  	snapshotIDs = testRunList(t, "snapshots", env.gopts)
   190  	rtest.Assert(t, len(snapshotIDs) == 3,
   191  		"expected three snapshots, got %v", snapshotIDs)
   192  
   193  	checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, snapshotIDs, 4)
   194  }
   195  
   196  func TestMountSameTimestamps(t *testing.T) {
   197  	if !rtest.RunFuseTest {
   198  		t.Skip("Skipping fuse tests")
   199  	}
   200  
   201  	env, cleanup := withTestEnvironment(t)
   202  	defer cleanup()
   203  
   204  	rtest.SetupTarTestFixture(t, env.base, filepath.Join("testdata", "repo-same-timestamps.tar.gz"))
   205  
   206  	repo, err := OpenRepository(env.gopts)
   207  	rtest.OK(t, err)
   208  
   209  	ids := []restic.ID{
   210  		restic.TestParseID("280303689e5027328889a06d718b729e96a1ce6ae9ef8290bff550459ae611ee"),
   211  		restic.TestParseID("75ad6cdc0868e082f2596d5ab8705e9f7d87316f5bf5690385eeff8dbe49d9f5"),
   212  		restic.TestParseID("5fd0d8b2ef0fa5d23e58f1e460188abb0f525c0f0c4af8365a1280c807a80a1b"),
   213  	}
   214  
   215  	checkSnapshots(t, env.gopts, repo, env.mountpoint, env.repo, ids, 4)
   216  }