github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/internal/restic/lock_test.go (about) 1 package restic_test 2 3 import ( 4 "context" 5 "os" 6 "testing" 7 "time" 8 9 "github.com/restic/restic/internal/repository" 10 "github.com/restic/restic/internal/restic" 11 rtest "github.com/restic/restic/internal/test" 12 ) 13 14 func TestLock(t *testing.T) { 15 repo, cleanup := repository.TestRepository(t) 16 defer cleanup() 17 18 lock, err := restic.NewLock(context.TODO(), repo) 19 rtest.OK(t, err) 20 21 rtest.OK(t, lock.Unlock()) 22 } 23 24 func TestDoubleUnlock(t *testing.T) { 25 repo, cleanup := repository.TestRepository(t) 26 defer cleanup() 27 28 lock, err := restic.NewLock(context.TODO(), repo) 29 rtest.OK(t, err) 30 31 rtest.OK(t, lock.Unlock()) 32 33 err = lock.Unlock() 34 rtest.Assert(t, err != nil, 35 "double unlock didn't return an error, got %v", err) 36 } 37 38 func TestMultipleLock(t *testing.T) { 39 repo, cleanup := repository.TestRepository(t) 40 defer cleanup() 41 42 lock1, err := restic.NewLock(context.TODO(), repo) 43 rtest.OK(t, err) 44 45 lock2, err := restic.NewLock(context.TODO(), repo) 46 rtest.OK(t, err) 47 48 rtest.OK(t, lock1.Unlock()) 49 rtest.OK(t, lock2.Unlock()) 50 } 51 52 func TestLockExclusive(t *testing.T) { 53 repo, cleanup := repository.TestRepository(t) 54 defer cleanup() 55 56 elock, err := restic.NewExclusiveLock(context.TODO(), repo) 57 rtest.OK(t, err) 58 rtest.OK(t, elock.Unlock()) 59 } 60 61 func TestLockOnExclusiveLockedRepo(t *testing.T) { 62 repo, cleanup := repository.TestRepository(t) 63 defer cleanup() 64 65 elock, err := restic.NewExclusiveLock(context.TODO(), repo) 66 rtest.OK(t, err) 67 68 lock, err := restic.NewLock(context.TODO(), repo) 69 rtest.Assert(t, err != nil, 70 "create normal lock with exclusively locked repo didn't return an error") 71 rtest.Assert(t, restic.IsAlreadyLocked(err), 72 "create normal lock with exclusively locked repo didn't return the correct error") 73 74 rtest.OK(t, lock.Unlock()) 75 rtest.OK(t, elock.Unlock()) 76 } 77 78 func TestExclusiveLockOnLockedRepo(t *testing.T) { 79 repo, cleanup := repository.TestRepository(t) 80 defer cleanup() 81 82 elock, err := restic.NewLock(context.TODO(), repo) 83 rtest.OK(t, err) 84 85 lock, err := restic.NewExclusiveLock(context.TODO(), repo) 86 rtest.Assert(t, err != nil, 87 "create normal lock with exclusively locked repo didn't return an error") 88 rtest.Assert(t, restic.IsAlreadyLocked(err), 89 "create normal lock with exclusively locked repo didn't return the correct error") 90 91 rtest.OK(t, lock.Unlock()) 92 rtest.OK(t, elock.Unlock()) 93 } 94 95 func createFakeLock(repo restic.Repository, t time.Time, pid int) (restic.ID, error) { 96 hostname, err := os.Hostname() 97 if err != nil { 98 return restic.ID{}, err 99 } 100 101 newLock := &restic.Lock{Time: t, PID: pid, Hostname: hostname} 102 return repo.SaveJSONUnpacked(context.TODO(), restic.LockFile, &newLock) 103 } 104 105 func removeLock(repo restic.Repository, id restic.ID) error { 106 h := restic.Handle{Type: restic.LockFile, Name: id.String()} 107 return repo.Backend().Remove(context.TODO(), h) 108 } 109 110 var staleLockTests = []struct { 111 timestamp time.Time 112 stale bool 113 staleOnOtherHost bool 114 pid int 115 }{ 116 { 117 timestamp: time.Now(), 118 stale: false, 119 staleOnOtherHost: false, 120 pid: os.Getpid(), 121 }, 122 { 123 timestamp: time.Now().Add(-time.Hour), 124 stale: true, 125 staleOnOtherHost: true, 126 pid: os.Getpid(), 127 }, 128 { 129 timestamp: time.Now().Add(3 * time.Minute), 130 stale: false, 131 staleOnOtherHost: false, 132 pid: os.Getpid(), 133 }, 134 { 135 timestamp: time.Now(), 136 stale: true, 137 staleOnOtherHost: false, 138 pid: os.Getpid() + 500000, 139 }, 140 } 141 142 func TestLockStale(t *testing.T) { 143 hostname, err := os.Hostname() 144 rtest.OK(t, err) 145 146 otherHostname := "other-" + hostname 147 148 for i, test := range staleLockTests { 149 lock := restic.Lock{ 150 Time: test.timestamp, 151 PID: test.pid, 152 Hostname: hostname, 153 } 154 155 rtest.Assert(t, lock.Stale() == test.stale, 156 "TestStaleLock: test %d failed: expected stale: %v, got %v", 157 i, test.stale, !test.stale) 158 159 lock.Hostname = otherHostname 160 rtest.Assert(t, lock.Stale() == test.staleOnOtherHost, 161 "TestStaleLock: test %d failed: expected staleOnOtherHost: %v, got %v", 162 i, test.staleOnOtherHost, !test.staleOnOtherHost) 163 } 164 } 165 166 func lockExists(repo restic.Repository, t testing.TB, id restic.ID) bool { 167 h := restic.Handle{Type: restic.LockFile, Name: id.String()} 168 exists, err := repo.Backend().Test(context.TODO(), h) 169 rtest.OK(t, err) 170 171 return exists 172 } 173 174 func TestLockWithStaleLock(t *testing.T) { 175 repo, cleanup := repository.TestRepository(t) 176 defer cleanup() 177 178 id1, err := createFakeLock(repo, time.Now().Add(-time.Hour), os.Getpid()) 179 rtest.OK(t, err) 180 181 id2, err := createFakeLock(repo, time.Now().Add(-time.Minute), os.Getpid()) 182 rtest.OK(t, err) 183 184 id3, err := createFakeLock(repo, time.Now().Add(-time.Minute), os.Getpid()+500000) 185 rtest.OK(t, err) 186 187 rtest.OK(t, restic.RemoveStaleLocks(context.TODO(), repo)) 188 189 rtest.Assert(t, lockExists(repo, t, id1) == false, 190 "stale lock still exists after RemoveStaleLocks was called") 191 rtest.Assert(t, lockExists(repo, t, id2) == true, 192 "non-stale lock was removed by RemoveStaleLocks") 193 rtest.Assert(t, lockExists(repo, t, id3) == false, 194 "stale lock still exists after RemoveStaleLocks was called") 195 196 rtest.OK(t, removeLock(repo, id2)) 197 } 198 199 func TestRemoveAllLocks(t *testing.T) { 200 repo, cleanup := repository.TestRepository(t) 201 defer cleanup() 202 203 id1, err := createFakeLock(repo, time.Now().Add(-time.Hour), os.Getpid()) 204 rtest.OK(t, err) 205 206 id2, err := createFakeLock(repo, time.Now().Add(-time.Minute), os.Getpid()) 207 rtest.OK(t, err) 208 209 id3, err := createFakeLock(repo, time.Now().Add(-time.Minute), os.Getpid()+500000) 210 rtest.OK(t, err) 211 212 rtest.OK(t, restic.RemoveAllLocks(context.TODO(), repo)) 213 214 rtest.Assert(t, lockExists(repo, t, id1) == false, 215 "lock still exists after RemoveAllLocks was called") 216 rtest.Assert(t, lockExists(repo, t, id2) == false, 217 "lock still exists after RemoveAllLocks was called") 218 rtest.Assert(t, lockExists(repo, t, id3) == false, 219 "lock still exists after RemoveAllLocks was called") 220 } 221 222 func TestLockRefresh(t *testing.T) { 223 repo, cleanup := repository.TestRepository(t) 224 defer cleanup() 225 226 lock, err := restic.NewLock(context.TODO(), repo) 227 rtest.OK(t, err) 228 229 var lockID *restic.ID 230 for id := range repo.List(context.TODO(), restic.LockFile) { 231 if lockID != nil { 232 t.Error("more than one lock found") 233 } 234 lockID = &id 235 } 236 237 rtest.OK(t, lock.Refresh(context.TODO())) 238 239 var lockID2 *restic.ID 240 for id := range repo.List(context.TODO(), restic.LockFile) { 241 if lockID2 != nil { 242 t.Error("more than one lock found") 243 } 244 lockID2 = &id 245 } 246 247 rtest.Assert(t, !lockID.Equal(*lockID2), 248 "expected a new ID after lock refresh, got the same") 249 rtest.OK(t, lock.Unlock()) 250 }