github.com/jfrog/jfrog-cli-core/v2@v2.51.0/utils/lock/lock_test.go (about) 1 package lock 2 3 import ( 4 "math" 5 "os" 6 "path/filepath" 7 "testing" 8 "time" 9 10 "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" 11 "github.com/jfrog/jfrog-cli-core/v2/utils/log" 12 "github.com/jfrog/jfrog-cli-core/v2/utils/tests" 13 "github.com/jfrog/jfrog-client-go/utils/io/fileutils" 14 "github.com/stretchr/testify/assert" 15 ) 16 17 var testLockDirPath string 18 19 func init() { 20 log.SetDefaultLogger() 21 locksDirPath, err := coreutils.GetJfrogLocksDir() 22 if err != nil { 23 return 24 } 25 testLockDirPath = filepath.Join(locksDirPath, "test") 26 } 27 28 // The lock mechanism prefers earlier lock requests. If two locks requests have same time stamps, it'll take the one with the smaller PID first. 29 // Here we test the functionality of a real process with a real PID and a dummy process with MaxInt pid. 30 func TestLockSmallerPid(t *testing.T) { 31 // First creating the first lock object with special pid number that doesn't exist. 32 firstLock := getLock(math.MaxInt32, t) 33 // Creating a second lock object with the running PID 34 secondLock := getLock(os.Getpid(), t) 35 36 // Confirming that only two locks are located in the lock directory 37 files, err := fileutils.ListFiles(testLockDirPath, false) 38 assert.NoError(t, err) 39 assert.Len(t, files, 2) 40 41 // Performing lock. This should work since the first lock PID is not running. The lock() will remove it. 42 assert.NoError(t, secondLock.lock()) 43 44 // Unlocking to remove the lock file. 45 assert.NoError(t, secondLock.Unlock()) 46 47 // If timestamp equals, secondLock.lock() is not expected to delete first lock's file, since os.Getpid() < math.MaxInt32. 48 if firstLock.currentTime == secondLock.currentTime { 49 assert.NoError(t, firstLock.Unlock()) 50 } 51 52 // Confirming that no locks are located in the lock directory 53 files, err = fileutils.ListFiles(testLockDirPath, false) 54 assert.NoError(t, err) 55 assert.Empty(t, files) 56 } 57 58 // The lock mechanism prefers earlier lock requests. If two locks requests have same time stamps, it'll take the one with the smaller PID first. 59 // Here we test the functionality of a real process with a real PID and a dummy process with -1 pid. 60 func TestLockBiggerPid(t *testing.T) { 61 // First creating the first lock object with special pid number that doesn't exist. 62 getLock(-1, t) 63 // Creating a second lock object with the running PID 64 secondLock := getLock(os.Getpid(), t) 65 66 // Confirming that only two locks are located in the lock directory 67 files, err := fileutils.ListFiles(testLockDirPath, false) 68 assert.NoError(t, err) 69 assert.Len(t, files, 2) 70 71 // Performing lock. This should work since the first lock PID is not running. The lock() will remove it. 72 assert.NoError(t, secondLock.lock()) 73 74 // Unlocking to remove the lock file. 75 assert.NoError(t, secondLock.Unlock()) 76 77 // Confirming that no locks are located in the lock directory 78 files, err = fileutils.ListFiles(testLockDirPath, false) 79 assert.NoError(t, err) 80 assert.Empty(t, files) 81 } 82 83 func TestUnlock(t *testing.T) { 84 lock := new(Lock) 85 assert.NotZero(t, testLockDirPath, "An error occurred while initializing testLockDirPath") 86 err := lock.createNewLockFile(testLockDirPath) 87 assert.NoError(t, err) 88 89 exists, err := fileutils.IsFileExists(lock.fileName, false) 90 assert.NoError(t, err) 91 assert.Truef(t, exists, "File %s is missing", lock.fileName) 92 93 assert.NoError(t, lock.Unlock()) 94 exists, err = fileutils.IsFileExists(lock.fileName, false) 95 assert.NoError(t, err) 96 assert.Falsef(t, exists, "File %s exists, but it should have been removed by Unlock", lock.fileName) 97 } 98 99 func TestCreateFile(t *testing.T) { 100 pid := os.Getpid() 101 lock := getLock(pid, t) 102 103 exists, err := fileutils.IsFileExists(lock.fileName, false) 104 assert.NoError(t, err) 105 assert.True(t, exists, "Lock wan't created.") 106 107 files, err := fileutils.ListFiles(testLockDirPath, false) 108 assert.NoError(t, err) 109 assert.Lenf(t, files, 1, "Expected one file, got %d.", len(files)) 110 assert.Equalf(t, lock.fileName, files[0], "Expected filename %s, got %s", lock.fileName, files[0]) 111 112 // Removing the created lock file 113 assert.NoError(t, lock.Unlock()) 114 } 115 116 func TestGetLastLockTimestamp(t *testing.T) { 117 // Create an empty dir and make sure we get zero timestamp 118 tmpDir, createTempDirCallback := tests.CreateTempDirWithCallbackAndAssert(t) 119 defer createTempDirCallback() 120 timestamp, err := GetLastLockTimestamp(tmpDir) 121 assert.NoError(t, err) 122 assert.Zero(t, timestamp) 123 124 // Create a lock and make sure the timestamps are equal 125 lock := getLock(os.Getpid(), t) 126 timestamp, err = GetLastLockTimestamp(testLockDirPath) 127 assert.NoError(t, err) 128 assert.Equal(t, lock.currentTime, timestamp) 129 130 // Removing the created lock file 131 assert.NoError(t, lock.Unlock()) 132 } 133 134 func TestGetLastLockNotRunningTimestamp(t *testing.T) { 135 // Create an empty dir and make sure we get zero timestamp 136 tmpDir, createTempDirCallback := tests.CreateTempDirWithCallbackAndAssert(t) 137 defer createTempDirCallback() 138 timestamp, err := GetLastLockTimestamp(tmpDir) 139 assert.NoError(t, err) 140 assert.Zero(t, timestamp) 141 142 // Create a lock for a non-running process and make sure the timestamp is zero 143 lock := getLock(math.MaxInt-1, t) 144 timestamp, err = GetLastLockTimestamp(testLockDirPath) 145 assert.NoError(t, err) 146 assert.Zero(t, timestamp) 147 148 // Removing the created lock file 149 assert.NoError(t, lock.Unlock()) 150 } 151 152 func getLock(pid int, t *testing.T) Lock { 153 lock := Lock{pid: pid, currentTime: time.Now().UnixNano()} 154 assert.NotZero(t, testLockDirPath, "An error occurred while initializing testLockDirPath") 155 assert.NoError(t, fileutils.CreateDirIfNotExist(testLockDirPath)) 156 assert.NoError(t, lock.createFile(testLockDirPath)) 157 return lock 158 }