package restic_test import ( "os" "testing" "time" "github.com/restic/restic" "github.com/restic/restic/backend" "github.com/restic/restic/repository" . "github.com/restic/restic/test" ) func TestLock(t *testing.T) { repo := SetupRepo() defer TeardownRepo(repo) lock, err := restic.NewLock(repo) OK(t, err) OK(t, lock.Unlock()) } func TestDoubleUnlock(t *testing.T) { repo := SetupRepo() defer TeardownRepo(repo) lock, err := restic.NewLock(repo) OK(t, err) OK(t, lock.Unlock()) err = lock.Unlock() Assert(t, err != nil, "double unlock didn't return an error, got %v", err) } func TestMultipleLock(t *testing.T) { repo := SetupRepo() defer TeardownRepo(repo) lock1, err := restic.NewLock(repo) OK(t, err) lock2, err := restic.NewLock(repo) OK(t, err) OK(t, lock1.Unlock()) OK(t, lock2.Unlock()) } func TestLockExclusive(t *testing.T) { repo := SetupRepo() defer TeardownRepo(repo) elock, err := restic.NewExclusiveLock(repo) OK(t, err) OK(t, elock.Unlock()) } func TestLockOnExclusiveLockedRepo(t *testing.T) { repo := SetupRepo() defer TeardownRepo(repo) elock, err := restic.NewExclusiveLock(repo) OK(t, err) lock, err := restic.NewLock(repo) Assert(t, err == restic.ErrAlreadyLocked, "create normal lock with exclusively locked repo didn't return an error") OK(t, lock.Unlock()) OK(t, elock.Unlock()) } func TestExclusiveLockOnLockedRepo(t *testing.T) { repo := SetupRepo() defer TeardownRepo(repo) elock, err := restic.NewLock(repo) OK(t, err) lock, err := restic.NewExclusiveLock(repo) Assert(t, err == restic.ErrAlreadyLocked, "create exclusive lock with locked repo didn't return an error") OK(t, lock.Unlock()) OK(t, elock.Unlock()) } func createFakeLock(repo *repository.Repository, t time.Time, pid int) (backend.ID, error) { newLock := &restic.Lock{Time: t, PID: pid} return repo.SaveJSONUnpacked(backend.Lock, &newLock) } func removeLock(repo *repository.Repository, id backend.ID) error { return repo.Backend().Remove(backend.Lock, id.String()) } var staleLockTests = []struct { timestamp time.Time stale bool pid int }{ { timestamp: time.Now(), stale: false, pid: os.Getpid(), }, { timestamp: time.Now().Add(-time.Hour), stale: true, pid: os.Getpid(), }, { timestamp: time.Now().Add(3 * time.Minute), stale: false, pid: os.Getpid(), }, { timestamp: time.Now(), stale: true, pid: os.Getpid() + 500, }, } func TestLockStale(t *testing.T) { for i, test := range staleLockTests { lock := restic.Lock{ Time: test.timestamp, PID: test.pid, } Assert(t, lock.Stale() == test.stale, "TestStaleLock: test %d failed: expected stale: %v, got %v", i, test.stale, !test.stale) } } func lockExists(repo *repository.Repository, t testing.TB, id backend.ID) bool { exists, err := repo.Backend().Test(backend.Lock, id.String()) OK(t, err) return exists } func TestLockWithStaleLock(t *testing.T) { repo := SetupRepo() defer TeardownRepo(repo) id1, err := createFakeLock(repo, time.Now().Add(-time.Hour), os.Getpid()) OK(t, err) id2, err := createFakeLock(repo, time.Now().Add(-time.Minute), os.Getpid()) OK(t, err) id3, err := createFakeLock(repo, time.Now().Add(-time.Minute), os.Getpid()+500) OK(t, err) OK(t, restic.RemoveStaleLocks(repo)) Assert(t, lockExists(repo, t, id1) == false, "stale lock still exists after RemoveStaleLocks was called") Assert(t, lockExists(repo, t, id2) == true, "non-stale lock was removed by RemoveStaleLocks") Assert(t, lockExists(repo, t, id3) == false, "stale lock still exists after RemoveStaleLocks was called") OK(t, removeLock(repo, id2)) }