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  }