github.com/rigado/snapd@v2.42.5-go-mod+incompatible/osutil/flock_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package osutil_test 21 22 import ( 23 "bytes" 24 "os" 25 "os/exec" 26 "path/filepath" 27 "time" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/osutil" 32 ) 33 34 type flockSuite struct{} 35 36 var _ = Suite(&flockSuite{}) 37 38 // Test that opening and closing a lock works as expected. 39 func (s *flockSuite) TestNewFileLock(c *C) { 40 lock, err := osutil.NewFileLock(filepath.Join(c.MkDir(), "name")) 41 c.Assert(err, IsNil) 42 defer lock.Close() 43 44 _, err = os.Stat(lock.Path()) 45 c.Assert(err, IsNil) 46 } 47 48 func flockSupportsConflictExitCodeSwitch(c *C) bool { 49 output, err := exec.Command("flock", "--help").CombinedOutput() 50 c.Assert(err, IsNil) 51 return bytes.Contains(output, []byte("--conflict-exit-code")) 52 } 53 54 // Test that Lock and Unlock work as expected. 55 func (s *flockSuite) TestLockUnlockWorks(c *C) { 56 if os.Getenv("TRAVIS_BUILD_NUMBER") != "" { 57 c.Skip("Cannot use this under travis") 58 return 59 } 60 if !flockSupportsConflictExitCodeSwitch(c) { 61 c.Skip("flock too old for this test") 62 } 63 64 lock, err := osutil.NewFileLock(filepath.Join(c.MkDir(), "name")) 65 c.Assert(err, IsNil) 66 defer lock.Close() 67 68 // Run a flock command in another process, it should succeed because it can 69 // lock the lock as we didn't do it yet. 70 cmd := exec.Command("flock", "--exclusive", "--nonblock", lock.Path(), "true") 71 c.Assert(cmd.Run(), IsNil) 72 73 // Lock the lock. 74 c.Assert(lock.Lock(), IsNil) 75 76 // Run a flock command in another process, it should fail with the distinct 77 // error code because we hold the lock already and we asked it not to block. 78 cmd = exec.Command("flock", "--exclusive", "--nonblock", 79 "--conflict-exit-code", "2", lock.Path(), "true") 80 c.Assert(cmd.Run(), ErrorMatches, "exit status 2") 81 82 // Unlock the lock. 83 c.Assert(lock.Unlock(), IsNil) 84 85 // Run a flock command in another process, it should succeed because it can 86 // grab the lock again now. 87 cmd = exec.Command("flock", "--exclusive", "--nonblock", lock.Path(), "true") 88 c.Assert(cmd.Run(), IsNil) 89 } 90 91 // Test that locking a locked lock does nothing. 92 func (s *flockSuite) TestLockLocked(c *C) { 93 lock, err := osutil.NewFileLock(filepath.Join(c.MkDir(), "name")) 94 c.Assert(err, IsNil) 95 defer lock.Close() 96 97 // NOTE: technically this replaces the lock type but we only use LOCK_EX. 98 c.Assert(lock.Lock(), IsNil) 99 c.Assert(lock.Lock(), IsNil) 100 } 101 102 // Test that unlocking an unlocked lock does nothing. 103 func (s *flockSuite) TestUnlockUnlocked(c *C) { 104 lock, err := osutil.NewFileLock(filepath.Join(c.MkDir(), "name")) 105 c.Assert(err, IsNil) 106 defer lock.Close() 107 108 c.Assert(lock.Unlock(), IsNil) 109 } 110 111 // Test that locking or unlocking a closed lock fails. 112 func (s *flockSuite) TestUsingClosedLock(c *C) { 113 lock, err := osutil.NewFileLock(filepath.Join(c.MkDir(), "name")) 114 c.Assert(err, IsNil) 115 lock.Close() 116 117 c.Assert(lock.Lock(), ErrorMatches, "bad file descriptor") 118 c.Assert(lock.Unlock(), ErrorMatches, "bad file descriptor") 119 } 120 121 // Test that non-blocking locking reports error on pre-acquired lock. 122 func (s *flockSuite) TestLockUnlockNonblockingWorks(c *C) { 123 if os.Getenv("TRAVIS_BUILD_NUMBER") != "" { 124 c.Skip("Cannot use this under travis") 125 return 126 } 127 128 // Use the "flock" command to grab a lock for 9999 seconds in another process. 129 lockPath := filepath.Join(c.MkDir(), "lock") 130 cmd := exec.Command("flock", "--exclusive", lockPath, "sleep", "9999") 131 c.Assert(cmd.Start(), IsNil) 132 defer cmd.Process.Kill() 133 134 // Give flock some chance to create the lock file. 135 for i := 0; i < 10; i++ { 136 if osutil.FileExists(lockPath) { 137 break 138 } 139 time.Sleep(time.Millisecond * 300) 140 } 141 142 // Try to acquire the same lock file and see that it is busy. 143 lock, err := osutil.NewFileLock(lockPath) 144 c.Assert(err, IsNil) 145 c.Assert(lock, NotNil) 146 defer lock.Close() 147 148 c.Assert(lock.TryLock(), Equals, osutil.ErrAlreadyLocked) 149 }