aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2024-05-03 09:13:00 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2024-05-03 09:13:00 +0200
commit064717fcc800d9ca94366745610305d12a5532a4 (patch)
tree538e14ed2bccd4d176c163a22f709def397df9a1
parent6bce026b0c664ba9686a37e614330cb84be3a5e6 (diff)
downloadvis-lockfiles-064717fcc800d9ca94366745610305d12a5532a4.tar.gz
vis-lockfiles-064717fcc800d9ca94366745610305d12a5532a4.zip
split lock file functions into separate file to enable testing
-rw-r--r--.gitlab-ci.yml7
-rw-r--r--Makefile4
-rw-r--r--init.lua87
-rw-r--r--lockfiles.lua84
-rwxr-xr-xlockfiles_test.lua12
5 files changed, 120 insertions, 74 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 555786b..ce373b1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,6 +2,7 @@ image: muhq/lua-dev:0.1
stages:
- check
+ - test
check-luacheck:
stage: check
@@ -14,3 +15,9 @@ check-format:
script:
- luarocks install --server=https://luarocks.org/dev luaformatter
- make check-format
+
+test:
+ stage: test
+ script:
+ - luarocks install lunatest
+ - make test
diff --git a/Makefile b/Makefile
index 816596e..855b34b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,7 @@
.PHONY: check format check-luacheck check-format
LUA_FILES := $(shell find -name "*.lua")
+TEST_FILES := $(shell find -name "*_test.lua")
check: check-luacheck check-format
@@ -12,3 +13,6 @@ check-format:
format:
lua-format -i $(LUA_FILES)
+
+test:
+ set -e; for tf in $(TEST_FILES); do "$$tf"; done
diff --git a/init.lua b/init.lua
index 25735bb..5c85818 100644
--- a/init.lua
+++ b/init.lua
@@ -12,51 +12,23 @@
-- You should have received a copy of the GNU General Public License along with
-- vis-lockfiles found in the LICENSE file.
-- If not, see <https://www.gnu.org/licenses/>.
+local source_str = debug.getinfo(1, 'S').source:sub(2)
+local script_path = source_str:match('(.*/)')
+
+local lockfiles = dofile(script_path .. 'lockfiles.lua')
+
local M = {}
M.directory = '${XDG_STATE_HOME:-${HOME}/.local/state}/vis/locks'
M.directory_expanded = nil
--- Helper to execute a command and capture its output
-local function capture_cmd(cmd)
- local p = assert(io.popen(cmd, 'r'))
- local s = assert(p:read('*a'))
- local success, _, status = p:close()
- if not success then
- local err = cmd .. ' failed with exit code: ' .. status
- vis:info('vis-lockfiles: ' .. err)
- end
- return s
-end
-
local function get_lock_dir()
if M.directory_expanded ~= M.directory then
- M.directory = capture_cmd('printf %s "' .. M.directory .. '"')
+ M.directory = lockfiles.capture_cmd('printf %s "' .. M.directory .. '"')
M.directory_expanded = M.directory
end
return M.directory
end
-local function lock_file_path(path)
- -- escape '%' first
- local filename = path:gsub('%%', '%%25'):gsub('/', '%%2F')
- return get_lock_dir() .. '/' .. filename .. '.lock'
-end
-
--- determine vis' pid
-local vis_pid
-do
- local vis_proc_file = io.open('/proc/self/stat', 'r')
- if vis_proc_file then
- vis_pid = vis_proc_file:read('*n')
- vis_proc_file:close()
-
- else -- fallback if /proc/self/stat is not available
- local out = capture_cmd('sh -c "echo $PPID"')
- vis_pid = tonumber(out)
- end
-end
-assert(vis_pid)
-
-- get a user confirmation
-- return true if user selected yes, false otherwise
local function confirm(path)
@@ -82,49 +54,15 @@ local function confirm(path)
end
local created_locks = {}
-local function create_lock_file(lfile_path)
- local lock_file = io.open(lfile_path)
- if lock_file then
- lock_file:close()
- return false
- end
-
- -- create the lock file with our pid
- if not io.open(lfile_path, 'w') then
- assert(os.execute('mkdir -p "' .. get_lock_dir() .. '"'))
- end
- lock_file = assert(io.open(lfile_path, 'w'))
- lock_file:write(vis_pid)
- lock_file:close()
-
- -- detect a possible race
- lock_file = assert(io.open(lfile_path))
- local lock_pid = lock_file:read('*n')
- lock_file:close()
- local locked = tonumber(lock_pid) == vis_pid
-
- -- remember we created the lock file
- if locked then
- created_locks[lfile_path] = true
- end
-
- return locked
-end
-
-local function delete_lock_file(lfile_path)
- local success, err = os.remove(lfile_path)
- if not success then
- vis:info('vis-lockfiles: ' .. err)
- end
- created_locks[lfile_path] = nil
-end
vis.events.subscribe(vis.events.FILE_OPEN, function(file)
if not file.path then
return
end
- local lfile_path = lock_file_path(file.path)
- if not create_lock_file(lfile_path) and not confirm(lfile_path) then
+ local lfile_path = lockfiles.lock_file_path(get_lock_dir(), file.path)
+ if lockfiles.create_lock_file(get_lock_dir(), lfile_path) then
+ created_locks[lfile_path] = true
+ elseif not confirm(lfile_path) then
os.exit(1)
end
end)
@@ -133,9 +71,10 @@ vis.events.subscribe(vis.events.FILE_CLOSE, function(file)
if not file.path then
return
end
- local lfile_path = lock_file_path(file.path)
+ local lfile_path = lockfiles.lock_file_path(get_lock_dir(), file.path)
if created_locks[lfile_path] then
- delete_lock_file(lfile_path)
+ lockfiles.delete_lock_file(lfile_path)
+ created_locks[lfile_path] = nil
end
end)
diff --git a/lockfiles.lua b/lockfiles.lua
new file mode 100644
index 0000000..9e120bd
--- /dev/null
+++ b/lockfiles.lua
@@ -0,0 +1,84 @@
+-- Copyright (c) 2024 Florian Fischer. All rights reserved.
+--
+-- vis-lockfiles is free software: you can redistribute it and/or modify it under
+-- the terms of the GNU General Public License as published by the Free Software
+-- Foundation, either version 3 of the License, or (at your option) any later
+-- version.
+--
+-- vis-lockfiles is distributed in the hope that it will be useful, but WITHOUT
+-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+-- FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License along with
+-- vis-lockfiles found in the LICENSE file.
+-- If not, see <https://www.gnu.org/licenses/>.
+-- Helper to execute a command and capture its output
+local function capture_cmd(cmd)
+ local p = assert(io.popen(cmd, 'r'))
+ local s = assert(p:read('*a'))
+ local success, _, status = p:close()
+ if not success then
+ local err = cmd .. ' failed with exit code: ' .. status
+ vis:info('vis-lockfiles: ' .. err)
+ end
+ return s
+end
+
+local function lock_file_path(lock_dir, path)
+ -- escape '%' first
+ local filename = path:gsub('%%', '%%25'):gsub('/', '%%2F')
+ return lock_dir .. '/' .. filename .. '.lock'
+end
+
+-- determine vis' pid
+local vis_pid
+do
+ local vis_proc_file = io.open('/proc/self/stat', 'r')
+ if vis_proc_file then
+ vis_pid = vis_proc_file:read('*n')
+ vis_proc_file:close()
+
+ else -- fallback if /proc/self/stat is not available
+ local out = capture_cmd('sh -c "echo $PPID"')
+ vis_pid = tonumber(out)
+ end
+end
+assert(vis_pid)
+
+local function create_lock_file(lock_dir, lfile_path)
+ local lock_file = io.open(lfile_path)
+ if lock_file then
+ lock_file:close()
+ return false
+ end
+
+ -- create the lock file with our pid
+ if not io.open(lfile_path, 'w') then
+ assert(os.execute('mkdir -p "' .. lock_dir .. '"'))
+ end
+ lock_file = assert(io.open(lfile_path, 'w'))
+ lock_file:write(vis_pid)
+ lock_file:close()
+
+ -- detect a possible race
+ lock_file = assert(io.open(lfile_path))
+ local lock_pid = lock_file:read('*n')
+ lock_file:close()
+ local locked = tonumber(lock_pid) == vis_pid
+
+ return locked
+end
+
+local function delete_lock_file(lfile_path)
+ local success, err = os.remove(lfile_path)
+ if not success then
+ vis:info('vis-lockfiles: ' .. err)
+ end
+end
+
+return {
+ capture_cmd = capture_cmd,
+ lock_file_path = lock_file_path,
+ delete_lock_file = delete_lock_file,
+ create_lock_file = create_lock_file,
+}
diff --git a/lockfiles_test.lua b/lockfiles_test.lua
new file mode 100755
index 0000000..a393f60
--- /dev/null
+++ b/lockfiles_test.lua
@@ -0,0 +1,12 @@
+#!/usr/bin/env lua5.4
+
+local lockfiles = dofile('lockfiles.lua')
+
+local lunatest = require('lunatest')
+function test_percentage_escape() -- luacheck: ignore 111
+ local lock_file_path_with_percent = lockfiles.lock_file_path('.', 'foo%bar')
+ local lock_file_path_with_slash = lockfiles.lock_file_path('.', 'foo/bar')
+ lunatest.assert_not_equal(lock_file_path_with_percent, lock_file_path_with_slash)
+end
+
+lunatest.run()