Meh I'll figure out submodules later
This commit is contained in:
parent
4ca9d44a90
commit
8cb281f436
352 changed files with 66107 additions and 0 deletions
|
@ -0,0 +1,44 @@
|
|||
require("plenary.reload").reload_module "plenary"
|
||||
|
||||
local Job = require "plenary.job"
|
||||
local profiler = require "plenary.profile.lua_profiler"
|
||||
|
||||
profiler.start()
|
||||
|
||||
local start = vim.fn.reltime()
|
||||
local finish = nil
|
||||
|
||||
local results = {}
|
||||
|
||||
local j = Job:new {
|
||||
command = "fdfind",
|
||||
|
||||
cwd = "~/plugins/",
|
||||
|
||||
enable_handlers = false,
|
||||
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
|
||||
-- on_exit = vim.schedule_wrap(function()
|
||||
-- finish = vim.fn.reltime(start)
|
||||
-- end),
|
||||
}
|
||||
|
||||
pcall(function()
|
||||
j:sync(2000, 5)
|
||||
end)
|
||||
finish = vim.fn.reltime(start)
|
||||
|
||||
profiler.stop()
|
||||
profiler.report "/home/tj/tmp/temp.txt"
|
||||
|
||||
if finish == nil then
|
||||
print "Did not finish :'("
|
||||
else
|
||||
print("finished in:", vim.fn.reltimestr(finish))
|
||||
end
|
||||
|
||||
collectgarbage()
|
||||
print(collectgarbage "count")
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
set rtp+=.
|
||||
runtime plugin/plenary.vim
|
||||
|
||||
nnoremap ,,x :luafile %<CR>
|
|
@ -0,0 +1,53 @@
|
|||
require("plenary.async").tests.add_to_env()
|
||||
|
||||
describe("async", function()
|
||||
a.it("void functions can call wrapped functions", function()
|
||||
local stat = 0
|
||||
local saved_arg
|
||||
|
||||
local wrapped = a.wrap(function(inc, callback)
|
||||
stat = stat + inc
|
||||
callback()
|
||||
end, 2)
|
||||
|
||||
local voided = a.void(function(arg)
|
||||
wrapped(1)
|
||||
wrapped(2)
|
||||
wrapped(3)
|
||||
stat = stat + 1
|
||||
saved_arg = arg
|
||||
end)
|
||||
|
||||
voided "hello"
|
||||
|
||||
assert(stat == 7)
|
||||
assert(saved_arg == "hello")
|
||||
end)
|
||||
|
||||
a.it("void functions can call wrapped functions with ignored arguments", function()
|
||||
local stat = 0
|
||||
local saved_arg
|
||||
|
||||
local wrapped = a.wrap(function(inc, nil1, nil2, callback)
|
||||
assert(type(inc) == "number")
|
||||
assert(nil1 == nil)
|
||||
assert(nil2 == nil)
|
||||
assert(type(callback) == "function")
|
||||
stat = stat + inc
|
||||
callback()
|
||||
end, 4)
|
||||
|
||||
local voided = a.void(function(arg)
|
||||
wrapped(1)
|
||||
wrapped(2, nil)
|
||||
wrapped(3, nil, nil)
|
||||
stat = stat + 1
|
||||
saved_arg = arg
|
||||
end)
|
||||
|
||||
voided "hello"
|
||||
|
||||
assert(stat == 7)
|
||||
assert(saved_arg == "hello")
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,219 @@
|
|||
require("plenary.async").tests.add_to_env()
|
||||
local channel = a.control.channel
|
||||
local eq = assert.are.same
|
||||
local apcall = a.util.apcall
|
||||
|
||||
describe("channel", function()
|
||||
describe("oneshot", function()
|
||||
a.it("should work when rx is used first", function()
|
||||
local tx, rx = channel.oneshot()
|
||||
|
||||
a.run(function()
|
||||
local got = rx()
|
||||
|
||||
eq("sent value", got)
|
||||
end)
|
||||
|
||||
tx "sent value"
|
||||
end)
|
||||
|
||||
a.it("should work when tx is used first", function()
|
||||
local tx, rx = channel.oneshot()
|
||||
|
||||
tx "sent value"
|
||||
|
||||
local got = rx()
|
||||
|
||||
eq("sent value", got)
|
||||
end)
|
||||
|
||||
a.it("should work with multiple returns", function()
|
||||
local tx, rx = channel.oneshot()
|
||||
|
||||
a.run(function()
|
||||
local got, got2 = rx()
|
||||
eq("sent value", got)
|
||||
eq("another sent value", got2)
|
||||
end)
|
||||
|
||||
tx("sent value", "another sent value")
|
||||
end)
|
||||
|
||||
a.it("should work when sending a falsey value", function()
|
||||
local tx, rx = channel.oneshot()
|
||||
|
||||
tx(false)
|
||||
|
||||
local res = rx()
|
||||
eq(res, false)
|
||||
|
||||
local stat, ret = apcall(rx)
|
||||
eq(stat, false)
|
||||
local stat, ret = apcall(rx)
|
||||
eq(stat, false)
|
||||
end)
|
||||
|
||||
a.it("should work when sending a nil value", function()
|
||||
local tx, rx = channel.oneshot()
|
||||
|
||||
tx(nil)
|
||||
|
||||
local res = rx()
|
||||
eq(res, nil)
|
||||
|
||||
local stat, ret = apcall(rx)
|
||||
eq(stat, false)
|
||||
local stat, ret = apcall(rx)
|
||||
eq(stat, false)
|
||||
end)
|
||||
|
||||
a.it("should error when sending mulitple times", function()
|
||||
local tx, rx = channel.oneshot()
|
||||
|
||||
tx()
|
||||
local stat = pcall(tx)
|
||||
eq(stat, false)
|
||||
end)
|
||||
|
||||
a.it("should block receiving multiple times", function()
|
||||
local tx, rx = channel.oneshot()
|
||||
tx(true)
|
||||
rx()
|
||||
local stat = apcall(rx)
|
||||
eq(stat, false)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("mpsc", function()
|
||||
a.it("should wait multiple recv before any send", function()
|
||||
local sender, receiver = channel.mpsc()
|
||||
|
||||
local expected_count = 10
|
||||
|
||||
a.run(function()
|
||||
for i = 1, expected_count do
|
||||
a.util.sleep(250)
|
||||
sender.send(i)
|
||||
end
|
||||
end)
|
||||
|
||||
local receive_count = 0
|
||||
while receive_count < expected_count do
|
||||
receive_count = receive_count + 1
|
||||
local i = receiver.recv()
|
||||
eq(receive_count, i)
|
||||
end
|
||||
end)
|
||||
|
||||
a.it("should queues multiple sends before any read", function()
|
||||
local sender, receiver = channel.mpsc()
|
||||
|
||||
local counter = 0
|
||||
|
||||
a.run(function()
|
||||
counter = counter + 1
|
||||
sender.send(10)
|
||||
|
||||
counter = counter + 1
|
||||
sender.send(20)
|
||||
end)
|
||||
|
||||
a.util.sleep(1000)
|
||||
|
||||
eq(10, receiver.recv())
|
||||
eq(20, receiver.recv())
|
||||
eq(2, counter)
|
||||
end)
|
||||
|
||||
a.it("should queues multiple sends from multiple producers before any read", function()
|
||||
local sender, receiver = channel.mpsc()
|
||||
|
||||
local counter = 0
|
||||
|
||||
a.run(function()
|
||||
counter = counter + 1
|
||||
sender.send(10)
|
||||
|
||||
counter = counter + 1
|
||||
sender.send(20)
|
||||
end)
|
||||
|
||||
a.run(function()
|
||||
counter = counter + 1
|
||||
sender.send(30)
|
||||
|
||||
counter = counter + 1
|
||||
sender.send(40)
|
||||
end)
|
||||
|
||||
a.util.sleep(1000)
|
||||
|
||||
local read_counter = 0
|
||||
a.util.block_on(function()
|
||||
for _ = 1, 4 do
|
||||
receiver.recv()
|
||||
read_counter = read_counter + 1
|
||||
end
|
||||
end, 1000)
|
||||
eq(4, counter)
|
||||
eq(4, read_counter)
|
||||
end)
|
||||
|
||||
a.it("should read only the last value", function()
|
||||
local sender, receiver = channel.mpsc()
|
||||
|
||||
local counter = 0
|
||||
|
||||
a.run(function()
|
||||
counter = counter + 1
|
||||
sender.send(10)
|
||||
|
||||
counter = counter + 1
|
||||
sender.send(20)
|
||||
end)
|
||||
|
||||
a.util.sleep(1000)
|
||||
|
||||
eq(20, receiver.last())
|
||||
eq(2, counter)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("counter", function()
|
||||
a.it("should work", function()
|
||||
local tx, rx = channel.counter()
|
||||
|
||||
tx.send()
|
||||
tx.send()
|
||||
tx.send()
|
||||
|
||||
local counter = 0
|
||||
|
||||
a.run(function()
|
||||
for i = 1, 3 do
|
||||
rx.recv()
|
||||
counter = counter + 1
|
||||
end
|
||||
end)
|
||||
|
||||
eq(counter, 3)
|
||||
end)
|
||||
|
||||
a.it("should work when getting last", function()
|
||||
local tx, rx = channel.counter()
|
||||
|
||||
tx.send()
|
||||
tx.send()
|
||||
tx.send()
|
||||
|
||||
local counter = 0
|
||||
|
||||
a.run(function()
|
||||
rx.last()
|
||||
counter = counter + 1
|
||||
end)
|
||||
|
||||
eq(counter, 1)
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,158 @@
|
|||
require("plenary.async").tests.add_to_env()
|
||||
local Condvar = a.control.Condvar
|
||||
local eq = assert.are.same
|
||||
local join, run_all = a.util.join, a.util.run_all
|
||||
|
||||
describe("condvar", function()
|
||||
a.it("should allow blocking", function()
|
||||
local var = false
|
||||
|
||||
local condvar = Condvar.new()
|
||||
|
||||
a.run(function()
|
||||
condvar:wait()
|
||||
var = true
|
||||
end)
|
||||
|
||||
eq(var, false)
|
||||
|
||||
condvar:notify_one()
|
||||
|
||||
eq(var, true)
|
||||
end)
|
||||
|
||||
a.it("should be able to notify one when running", function()
|
||||
local counter = 0
|
||||
|
||||
local condvar = Condvar.new()
|
||||
|
||||
local first = function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
local second = function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
local third = function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
a.run(function()
|
||||
join { first, second, third }
|
||||
end)
|
||||
|
||||
eq(0, counter)
|
||||
|
||||
condvar:notify_one()
|
||||
|
||||
eq(1, counter)
|
||||
|
||||
condvar:notify_one()
|
||||
|
||||
eq(counter, 2)
|
||||
|
||||
condvar:notify_one()
|
||||
|
||||
eq(counter, 3)
|
||||
end)
|
||||
|
||||
a.it("should allow notify_one to work when using await_all", function()
|
||||
local counter = 0
|
||||
|
||||
local condvar = Condvar.new()
|
||||
|
||||
local first = function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
local second = function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
local third = function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
run_all { first, second, third }
|
||||
|
||||
eq(0, counter)
|
||||
|
||||
condvar:notify_one()
|
||||
|
||||
eq(1, counter)
|
||||
|
||||
condvar:notify_one()
|
||||
|
||||
eq(counter, 2)
|
||||
|
||||
condvar:notify_one()
|
||||
|
||||
eq(counter, 3)
|
||||
end)
|
||||
|
||||
a.it("should notify_all", function()
|
||||
local counter = 0
|
||||
|
||||
local condvar = Condvar.new()
|
||||
|
||||
local first = function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
local second = function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
local third = function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end
|
||||
|
||||
run_all { first, second, third }
|
||||
|
||||
eq(0, counter)
|
||||
|
||||
condvar:notify_all()
|
||||
|
||||
eq(3, counter)
|
||||
end)
|
||||
|
||||
a.it("notify all works multiple times", function()
|
||||
local condvar = Condvar.new()
|
||||
local counter = 0
|
||||
|
||||
a.run(function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end)
|
||||
|
||||
a.run(function()
|
||||
condvar:wait()
|
||||
counter = counter + 1
|
||||
end)
|
||||
|
||||
eq(0, counter)
|
||||
|
||||
condvar:notify_all()
|
||||
|
||||
eq(2, counter)
|
||||
|
||||
a.run(function()
|
||||
condvar:wait()
|
||||
counter = 0
|
||||
end)
|
||||
|
||||
condvar:notify_all()
|
||||
|
||||
eq(0, counter)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,91 @@
|
|||
local Deque = require("plenary.async.structs").Deque
|
||||
local eq = assert.are.same
|
||||
|
||||
-- just a helper to create the test deque
|
||||
local function new_deque()
|
||||
local deque = Deque.new()
|
||||
eq(deque:len(), 0)
|
||||
|
||||
deque:pushleft(1)
|
||||
eq(deque:len(), 1)
|
||||
|
||||
deque:pushleft(2)
|
||||
eq(deque:len(), 2)
|
||||
|
||||
deque:pushright(3)
|
||||
eq(deque:len(), 3)
|
||||
|
||||
deque:pushright(4)
|
||||
eq(deque:len(), 4)
|
||||
|
||||
deque:pushright(5)
|
||||
eq(deque:len(), 5)
|
||||
|
||||
return deque
|
||||
end
|
||||
|
||||
describe("deque", function()
|
||||
it("should allow pushing and popping and finding len", function()
|
||||
new_deque()
|
||||
end)
|
||||
|
||||
it("should be able to iterate from left", function()
|
||||
local deque = new_deque()
|
||||
|
||||
local iter = deque:ipairs_left()
|
||||
|
||||
local i, v = iter()
|
||||
eq(i, -2)
|
||||
eq(v, 2)
|
||||
|
||||
i, v = iter()
|
||||
eq(i, -1)
|
||||
eq(v, 1)
|
||||
|
||||
i, v = iter()
|
||||
eq(i, 0)
|
||||
eq(v, 3)
|
||||
|
||||
i, v = iter()
|
||||
eq(i, 1)
|
||||
eq(v, 4)
|
||||
|
||||
i, v = iter()
|
||||
eq(i, 2)
|
||||
eq(v, 5)
|
||||
end)
|
||||
|
||||
it("should be able to iterate from right", function()
|
||||
local deque = new_deque()
|
||||
|
||||
local iter = deque:ipairs_right()
|
||||
|
||||
local i, v = iter()
|
||||
eq(i, 2)
|
||||
eq(v, 5)
|
||||
|
||||
i, v = iter()
|
||||
eq(i, 1)
|
||||
eq(v, 4)
|
||||
|
||||
i, v = iter()
|
||||
eq(i, 0)
|
||||
eq(v, 3)
|
||||
|
||||
i, v = iter()
|
||||
eq(i, -1)
|
||||
eq(v, 1)
|
||||
|
||||
i, v = iter()
|
||||
eq(i, -2)
|
||||
eq(v, 2)
|
||||
end)
|
||||
|
||||
it("should allow clearing", function()
|
||||
local deque = new_deque()
|
||||
|
||||
deque:clear()
|
||||
|
||||
assert(deque:is_empty())
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,58 @@
|
|||
require("plenary.async").tests.add_to_env()
|
||||
local Semaphore = a.control.Semaphore
|
||||
|
||||
local eq = assert.are.same
|
||||
|
||||
describe("semaphore", function()
|
||||
a.it("should validate arguments", function()
|
||||
local status = pcall(Semaphore.new, -1)
|
||||
eq(status, false)
|
||||
|
||||
local status = pcall(Semaphore.new)
|
||||
eq(status, false)
|
||||
end)
|
||||
|
||||
a.it("should acquire a permit if available", function()
|
||||
local sem = Semaphore.new(1)
|
||||
local permit = sem:acquire()
|
||||
assert(permit ~= nil)
|
||||
end)
|
||||
|
||||
a.it("should block if no permit is available", function()
|
||||
local sem = Semaphore.new(1)
|
||||
sem:acquire()
|
||||
|
||||
local completed = false
|
||||
local blocking = function()
|
||||
sem:acquire()
|
||||
completed = true
|
||||
end
|
||||
a.run(blocking)
|
||||
|
||||
eq(completed, false)
|
||||
end)
|
||||
|
||||
a.it("should give another permit when an acquired permit is released", function()
|
||||
local sem = Semaphore.new(1)
|
||||
local permit = sem:acquire()
|
||||
permit:forget()
|
||||
local next_permit = sem:acquire()
|
||||
assert(next_permit ~= nil)
|
||||
end)
|
||||
|
||||
a.it("should permit the next waiting client when a permit is released", function()
|
||||
local sem = Semaphore.new(1)
|
||||
local permit = sem:acquire()
|
||||
|
||||
local completed = false
|
||||
local blocking = function()
|
||||
sem:acquire()
|
||||
completed = true
|
||||
end
|
||||
|
||||
a.run(blocking)
|
||||
permit:forget()
|
||||
|
||||
eq(completed, true)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,58 @@
|
|||
require("plenary.async").tests.add_to_env()
|
||||
|
||||
a.describe("a.before_each", function()
|
||||
local counter = 0
|
||||
|
||||
local set_counter_to_one = a.wrap(function(callback)
|
||||
a.util.sleep(5)
|
||||
counter = 1
|
||||
end, 1)
|
||||
|
||||
a.before_each(a.void(function()
|
||||
set_counter_to_one()
|
||||
end))
|
||||
|
||||
a.it("should run in async context", function()
|
||||
counter = counter + 1
|
||||
assert.are.same(counter, 2)
|
||||
end)
|
||||
|
||||
a.it("should run for all tests", function()
|
||||
counter = counter + 2
|
||||
assert.are.same(counter, 3)
|
||||
end)
|
||||
end)
|
||||
|
||||
a.describe("a.after_each", function()
|
||||
local counter = 0
|
||||
|
||||
local set_counter_to_one = a.wrap(function(callback)
|
||||
a.util.sleep(5)
|
||||
counter = 1
|
||||
end, 1)
|
||||
|
||||
a.after_each(a.void(function()
|
||||
set_counter_to_one()
|
||||
end))
|
||||
|
||||
a.it("should not run before first test", function()
|
||||
counter = counter + 1
|
||||
assert.are.same(counter, 1)
|
||||
end)
|
||||
|
||||
a.it("should run before the second test", function()
|
||||
counter = counter + 2
|
||||
assert.are.same(counter, 3)
|
||||
end)
|
||||
|
||||
a.it("should run before the third test", function()
|
||||
counter = counter + 3
|
||||
assert.are.same(counter, 4)
|
||||
end)
|
||||
end)
|
||||
|
||||
a.describe("a.pending", function()
|
||||
a.pending("This test is disabled", function()
|
||||
assert(false, "Should not run")
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,77 @@
|
|||
require("plenary.async").tests.add_to_env()
|
||||
local block_on = a.util.block_on
|
||||
local eq = assert.are.same
|
||||
local id = a.util.id
|
||||
|
||||
describe("async await util", function()
|
||||
describe("block_on", function()
|
||||
a.it("should block_on", function()
|
||||
local fn = function()
|
||||
a.util.sleep(100)
|
||||
return "hello"
|
||||
end
|
||||
|
||||
local res = fn()
|
||||
eq(res, "hello")
|
||||
end)
|
||||
|
||||
a.it("should work even when failing", function()
|
||||
local nonleaf = function()
|
||||
eq(true, false)
|
||||
end
|
||||
|
||||
local stat = pcall(block_on, nonleaf)
|
||||
eq(stat, false)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("protect", function()
|
||||
a.it("should be able to protect a non-leaf future", function()
|
||||
local nonleaf = function()
|
||||
error "This should error"
|
||||
return "return"
|
||||
end
|
||||
|
||||
local stat, ret = pcall(nonleaf)
|
||||
eq(false, stat)
|
||||
assert(ret:match "This should error")
|
||||
end)
|
||||
|
||||
a.it("should be able to protect a non-leaf future that doesnt fail", function()
|
||||
local nonleaf = function()
|
||||
return "didnt fail"
|
||||
end
|
||||
|
||||
local stat, ret = pcall(nonleaf)
|
||||
eq(stat, true)
|
||||
eq(ret, "didnt fail")
|
||||
end)
|
||||
end)
|
||||
|
||||
local function sleep(msec)
|
||||
return function()
|
||||
a.util.sleep(msec)
|
||||
return msec
|
||||
end
|
||||
end
|
||||
|
||||
describe("race", function()
|
||||
a.it("should return the first result", function()
|
||||
local funcs = vim.tbl_map(sleep, { 300, 400, 100, 200 })
|
||||
local result = a.util.race(funcs)
|
||||
eq(result, 100)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("run_first", function()
|
||||
a.it("should return the first result", function()
|
||||
local async_functions = vim.tbl_map(function(num)
|
||||
return function(callback)
|
||||
return a.run(sleep(num), callback)
|
||||
end
|
||||
end, { 300, 400, 100, 200 })
|
||||
local result = a.util.run_first(async_functions)
|
||||
eq(result, 100)
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,75 @@
|
|||
local Job = require "plenary.job"
|
||||
|
||||
local Timing = {}
|
||||
|
||||
function Timing:log(name)
|
||||
self[name] = vim.loop.uptime()
|
||||
end
|
||||
|
||||
function Timing:check(from, to, min_elapsed)
|
||||
assert(self[from], "did not log " .. from)
|
||||
assert(self[to], "did not log " .. to)
|
||||
local elapsed = self[to] - self[from]
|
||||
assert(
|
||||
min_elapsed <= elapsed,
|
||||
string.format("only took %s to get from %s to %s - expected at least %s", elapsed, from, to, min_elapsed)
|
||||
)
|
||||
end
|
||||
|
||||
describe("Async test", function()
|
||||
it("can resume testing with vim.defer_fn", function()
|
||||
local co = coroutine.running()
|
||||
assert(co, "not running inside a coroutine")
|
||||
|
||||
local timing = setmetatable({}, { __index = Timing })
|
||||
|
||||
vim.defer_fn(function()
|
||||
coroutine.resume(co)
|
||||
end, 200)
|
||||
timing:log "before"
|
||||
coroutine.yield()
|
||||
timing:log "after"
|
||||
timing:check("before", "after", 0.1)
|
||||
end)
|
||||
|
||||
it("can resume testing from job callback", function()
|
||||
local co = coroutine.running()
|
||||
assert(co, "not running inside a coroutine")
|
||||
|
||||
local timing = setmetatable({}, { __index = Timing })
|
||||
|
||||
Job:new({
|
||||
command = "bash",
|
||||
args = {
|
||||
"-ce",
|
||||
[[
|
||||
sleep 0.2
|
||||
echo hello
|
||||
sleep 0.2
|
||||
echo world
|
||||
sleep 0.2
|
||||
exit 42
|
||||
]],
|
||||
},
|
||||
on_stdout = function(_, data)
|
||||
timing:log(data)
|
||||
end,
|
||||
on_exit = function(_, exit_status)
|
||||
timing:log "exit"
|
||||
--This is required so that the rest of the test will run in a proper context
|
||||
vim.schedule(function()
|
||||
coroutine.resume(co, exit_status)
|
||||
end)
|
||||
end,
|
||||
}):start()
|
||||
timing:log "job started"
|
||||
local exit_status = coroutine.yield()
|
||||
timing:log "job finished"
|
||||
assert.are.equal(exit_status, 42)
|
||||
|
||||
timing:check("job started", "job finished", 0.3)
|
||||
timing:check("job started", "hello", 0.1)
|
||||
timing:check("hello", "world", 0.1)
|
||||
timing:check("world", "job finished", 0.1)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,203 @@
|
|||
local context_manager = require "plenary.context_manager"
|
||||
local debug_utils = require "plenary.debug_utils"
|
||||
local Path = require "plenary.path"
|
||||
|
||||
local with = context_manager.with
|
||||
local open = context_manager.open
|
||||
|
||||
local README_STR_PATH = vim.fn.fnamemodify(debug_utils.sourced_filepath(), ":h:h:h") .. "/README.md"
|
||||
local README_FIRST_LINE = "# plenary.nvim"
|
||||
|
||||
describe("context_manager", function()
|
||||
it("works with objects", function()
|
||||
local obj_manager = {
|
||||
enter = function(self)
|
||||
self.result = 10
|
||||
return self.result
|
||||
end,
|
||||
|
||||
exit = function() end,
|
||||
}
|
||||
|
||||
local result = with(obj_manager, function(obj)
|
||||
return obj
|
||||
end)
|
||||
|
||||
assert.are.same(10, result)
|
||||
assert.are.same(obj_manager.result, result)
|
||||
end)
|
||||
|
||||
it("works with coroutine", function()
|
||||
local co = function()
|
||||
coroutine.yield(10)
|
||||
end
|
||||
|
||||
local result = with(co, function(obj)
|
||||
return obj
|
||||
end)
|
||||
|
||||
assert.are.same(10, result)
|
||||
end)
|
||||
|
||||
it("does not work with coroutine with extra yields", function()
|
||||
local co = function()
|
||||
coroutine.yield(10)
|
||||
|
||||
-- Can't yield twice. That'd be bad and wouldn't make any sense.
|
||||
coroutine.yield(10)
|
||||
end
|
||||
|
||||
assert.has.error_match(function()
|
||||
with(co, function(obj)
|
||||
return obj
|
||||
end)
|
||||
end, "Should not yield anymore, otherwise that would make things complicated")
|
||||
end)
|
||||
|
||||
it("reads from files with open", function()
|
||||
local result = with(open(README_STR_PATH), function(reader)
|
||||
return reader:read()
|
||||
end)
|
||||
|
||||
assert.are.same(result, README_FIRST_LINE)
|
||||
end)
|
||||
|
||||
it("reads from Paths with open", function()
|
||||
local p = Path:new(README_STR_PATH)
|
||||
|
||||
local result = with(open(p), function(reader)
|
||||
return reader:read()
|
||||
end)
|
||||
|
||||
assert.are.same(result, README_FIRST_LINE)
|
||||
end)
|
||||
|
||||
it("calls exit on error with objects", function()
|
||||
local entered = false
|
||||
local exited = false
|
||||
local obj_manager = {
|
||||
enter = function(self)
|
||||
entered = true
|
||||
end,
|
||||
|
||||
exit = function(self)
|
||||
exited = true
|
||||
end,
|
||||
}
|
||||
|
||||
assert.has.error_match(function()
|
||||
with(obj_manager, function(obj)
|
||||
assert(false, "failed in callback")
|
||||
end)
|
||||
end, "failed in callback")
|
||||
|
||||
assert.is["true"](entered)
|
||||
assert.is["true"](exited)
|
||||
end)
|
||||
|
||||
it("calls exit on error with coroutines", function()
|
||||
local entered = false
|
||||
local exited = false
|
||||
local co = function()
|
||||
entered = true
|
||||
coroutine.yield(nil)
|
||||
|
||||
exited = true
|
||||
end
|
||||
|
||||
assert.has.error_match(function()
|
||||
with(co, function(obj)
|
||||
assert(false, "failed in callback")
|
||||
end)
|
||||
end, "failed in callback")
|
||||
|
||||
assert.is["true"](entered)
|
||||
assert.is["true"](exited)
|
||||
end)
|
||||
|
||||
it("fails from enter error with objects", function()
|
||||
local exited = false
|
||||
local obj_manager = {
|
||||
enter = function(self)
|
||||
assert(false, "failed in enter")
|
||||
end,
|
||||
|
||||
exit = function(self)
|
||||
exited = true
|
||||
end,
|
||||
}
|
||||
|
||||
local ran_callback = false
|
||||
assert.has.error_match(function()
|
||||
with(obj_manager, function(obj)
|
||||
ran_callback = true
|
||||
end)
|
||||
end, "failed in enter")
|
||||
|
||||
assert.is["false"](ran_callback)
|
||||
assert.is["false"](exited)
|
||||
end)
|
||||
|
||||
it("fails from enter error with coroutines", function()
|
||||
local exited = false
|
||||
local co = function()
|
||||
assert(false, "failed in enter")
|
||||
coroutine.yield(nil)
|
||||
|
||||
exited = true
|
||||
end
|
||||
|
||||
local ran_callback = false
|
||||
assert.has.error_match(function()
|
||||
with(co, function(obj)
|
||||
ran_callback = true
|
||||
end)
|
||||
end, "Should have yielded in coroutine.")
|
||||
|
||||
assert.is["false"](ran_callback)
|
||||
assert.is["false"](exited)
|
||||
end)
|
||||
|
||||
it("fails from exit error with objects", function()
|
||||
local entered = false
|
||||
local obj_manager = {
|
||||
enter = function(self)
|
||||
entered = true
|
||||
end,
|
||||
|
||||
exit = function(self)
|
||||
assert(false, "failed in exit")
|
||||
end,
|
||||
}
|
||||
|
||||
local ran_callback = false
|
||||
assert.has.error_match(function()
|
||||
with(obj_manager, function(obj)
|
||||
ran_callback = true
|
||||
end)
|
||||
end, "failed in exit")
|
||||
|
||||
assert.is["true"](entered)
|
||||
assert.is["true"](ran_callback)
|
||||
end)
|
||||
|
||||
it("fails from exit error with coroutines", function()
|
||||
local entered = false
|
||||
local co = function()
|
||||
entered = true
|
||||
coroutine.yield(nil)
|
||||
|
||||
assert(false, "failed in exit")
|
||||
end
|
||||
|
||||
local ran_callback = false
|
||||
assert.has.error_match(function()
|
||||
with(co, function(obj)
|
||||
ran_callback = true
|
||||
end)
|
||||
end, "Should be done")
|
||||
|
||||
assert.is["true"](entered)
|
||||
assert.is["true"](ran_callback)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,221 @@
|
|||
local curl = require "plenary.curl"
|
||||
local eq = assert.are.same
|
||||
local incl = function(p, s)
|
||||
return (nil ~= string.find(s, p))
|
||||
end
|
||||
|
||||
describe("CURL Wrapper:", function()
|
||||
describe("request", function() -----------------------------------------------
|
||||
it("sends and returns table.", function()
|
||||
eq(
|
||||
"table",
|
||||
type(curl.request {
|
||||
url = "https://postman-echo.com/get",
|
||||
method = "get",
|
||||
accept = "application/json",
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("should accept the url as first argument.", function()
|
||||
local res = curl.get("https://postman-echo.com/get", {
|
||||
accept = "application/json",
|
||||
})
|
||||
eq(200, res.status)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("GET", function() --------------------------------------------------
|
||||
it("sends and returns table.", function()
|
||||
eq(
|
||||
"table",
|
||||
type(curl.get {
|
||||
url = "https://postman-echo.com/get",
|
||||
accept = "application/json",
|
||||
})
|
||||
)
|
||||
end)
|
||||
|
||||
it("should accept the url as first argument.", function()
|
||||
local res = curl.get("https://postman-echo.com/get", {
|
||||
accept = "application/json",
|
||||
})
|
||||
eq(200, res.status) -- table has response status
|
||||
end)
|
||||
|
||||
it("sends encoded URL query params.", function()
|
||||
local query = { name = "john Doe", key = "123456" }
|
||||
local response = curl.get("https://postman-echo.com/get", {
|
||||
query = query,
|
||||
})
|
||||
|
||||
eq(200, response.status)
|
||||
eq(query, vim.fn.json_decode(response.body).args)
|
||||
end)
|
||||
|
||||
it("downloads files to opts.output synchronously", function()
|
||||
local file = "https://media2.giphy.com/media/bEMcuOG3hXVnihvB7x/giphy.gif"
|
||||
local loc = "/tmp/giphy2.gif"
|
||||
local res = curl.get(file, { output = loc })
|
||||
|
||||
eq(1, vim.fn.filereadable(loc), "should exists")
|
||||
eq(200, res.status, "should return 200")
|
||||
eq(0, res.exit, "should have exit code of 0")
|
||||
vim.fn.delete(loc)
|
||||
end)
|
||||
|
||||
it("downloads files to to opts.output asynchronous", function()
|
||||
local res = nil
|
||||
local succ = nil
|
||||
local done = false
|
||||
local file = "https://media2.giphy.com/media/notvalid.gif"
|
||||
local loc = "/tmp/notvalid.gif"
|
||||
|
||||
curl.get(file, {
|
||||
output = loc,
|
||||
callback = function(out)
|
||||
done = true
|
||||
succ = out.status == 200
|
||||
res = out
|
||||
end,
|
||||
})
|
||||
|
||||
vim.wait(60000, function()
|
||||
return done
|
||||
end)
|
||||
|
||||
eq(403, res.status, "It should return 403")
|
||||
assert(not succ, "It should fail")
|
||||
|
||||
vim.fn.delete(loc)
|
||||
end)
|
||||
|
||||
it("sends with basic-auth as string", function()
|
||||
local url = "https://postman-echo.com/basic-auth"
|
||||
local auth, res
|
||||
|
||||
auth = "postman:password"
|
||||
res = curl.get(url, { auth = auth })
|
||||
assert(incl("authenticated.*true", res.body))
|
||||
eq(200, res.status)
|
||||
|
||||
auth = "tami5:123456"
|
||||
res = curl.get(url, { auth = auth })
|
||||
assert(not incl("authenticated.*true", res.body), "it should fail")
|
||||
eq(401, res.status)
|
||||
end)
|
||||
|
||||
it("sends with basic-auth as table", function()
|
||||
local url = "https://postman-echo.com/basic-auth"
|
||||
local res = curl.get(url, { auth = { postman = "password" } })
|
||||
assert(incl("authenticated.*true", res.body))
|
||||
eq(200, res.status)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("POST", function() --------------------------------------------------
|
||||
it("sends raw string", function()
|
||||
local res = curl.post("https://postman-echo.com/post", {
|
||||
body = "John Doe",
|
||||
})
|
||||
assert(incl("John", res.body))
|
||||
eq(200, res.status)
|
||||
end)
|
||||
|
||||
it("sends lua table", function()
|
||||
local res = curl.post("https://jsonplaceholder.typicode.com/posts", {
|
||||
body = {
|
||||
title = "Hello World",
|
||||
body = "...",
|
||||
},
|
||||
})
|
||||
eq(201, res.status)
|
||||
end)
|
||||
|
||||
it("sends file", function()
|
||||
local res = curl.post("https://postman-echo.com/post", {
|
||||
body = "./README.md",
|
||||
}).body
|
||||
|
||||
assert(incl("plenary.test_harness", res))
|
||||
end)
|
||||
|
||||
it("sends and recives json body.", function()
|
||||
local json = { title = "New", name = "YORK" }
|
||||
local res = curl.post("https://postman-echo.com/post", {
|
||||
body = vim.fn.json_encode(json),
|
||||
headers = {
|
||||
content_type = "application/json",
|
||||
},
|
||||
}).body
|
||||
eq(json, vim.fn.json_decode(res).json)
|
||||
end)
|
||||
|
||||
it("should not include the body twice", function()
|
||||
local json = { title = "New", name = "YORK" }
|
||||
local body = vim.fn.json_encode(json)
|
||||
local res = curl.post("https://postman-echo.com/post", {
|
||||
body = body,
|
||||
headers = {
|
||||
content_type = "application/json",
|
||||
},
|
||||
dry_run = true,
|
||||
})
|
||||
local joined_response = table.concat(res, " ")
|
||||
local first_index = joined_response:find(body)
|
||||
|
||||
eq(nil, joined_response:find(body, first_index + 1))
|
||||
end)
|
||||
end)
|
||||
describe("PUT", function() --------------------------------------------------
|
||||
it("sends changes and get be back the new version.", function()
|
||||
local cha = { title = "New Title" }
|
||||
local res = curl.put("https://jsonplaceholder.typicode.com/posts/8", {
|
||||
body = cha,
|
||||
})
|
||||
eq(cha.title, vim.fn.json_decode(res.body).title)
|
||||
eq(200, res.status)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("PATCH", function() ------------------------------------------------
|
||||
it("sends changes and get be back the new version.", function()
|
||||
local cha = { title = "New Title" }
|
||||
local res = curl.patch("https://jsonplaceholder.typicode.com/posts/8", {
|
||||
body = cha,
|
||||
})
|
||||
eq(cha.title, vim.fn.json_decode(res.body).title)
|
||||
eq(200, res.status)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("DELETE", function() ------------------------------------------------
|
||||
it("sends delete request", function()
|
||||
local res = curl.delete "https://jsonplaceholder.typicode.com/posts/8"
|
||||
eq(200, res.status)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("DEBUG", function() --------------------------------------------------
|
||||
it("dry_run return the curl command to be ran.", function()
|
||||
local res = curl.delete("https://jsonplaceholder.typicode.com/posts/8", { dry_run = true })
|
||||
assert(type(res) == "table")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("Issue #601", function() --------------------------------------------
|
||||
it("should not use URL from previous call", function()
|
||||
local url = "https://example.com"
|
||||
local opts = { dry_run = true, dump = "" } -- dump would be random each time
|
||||
local first = curl.get(url, opts)
|
||||
eq(table.remove(first, #first), url, "expected url last")
|
||||
|
||||
local success, second = pcall(curl.get, opts)
|
||||
if success then
|
||||
eq(first, second, "should be same, but without url")
|
||||
else
|
||||
-- Failure is also acceptable
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,93 @@
|
|||
local Enum = require "plenary.enum"
|
||||
|
||||
local function should_fail(fun)
|
||||
local stat = pcall(fun)
|
||||
assert(not stat, "Function should fail")
|
||||
end
|
||||
|
||||
describe("Enum", function()
|
||||
it("should be able to define specific values for members", function()
|
||||
local E = Enum {
|
||||
{ "Foo", 2 },
|
||||
{ "Bar", 4 },
|
||||
"Qux",
|
||||
"Baz",
|
||||
{ "Another", 11 },
|
||||
}
|
||||
|
||||
assert(E.Foo.value == 2)
|
||||
assert(E.Bar.value == 4)
|
||||
assert(E.Qux.value == 5)
|
||||
assert(E.Baz.value == 6)
|
||||
assert(E.Another.value == 11)
|
||||
|
||||
assert(E[2] == "Foo")
|
||||
assert(E[4] == "Bar")
|
||||
assert(E[5] == "Qux")
|
||||
assert(E[6] == "Baz")
|
||||
assert(E[11] == "Another")
|
||||
end)
|
||||
it("should compare with itself", function()
|
||||
local E1 = Enum {
|
||||
"Foo",
|
||||
{ "Qux", 11 },
|
||||
"Bar",
|
||||
"Baz",
|
||||
}
|
||||
|
||||
local E2 = Enum {
|
||||
"Foo",
|
||||
"Bar",
|
||||
"Baz",
|
||||
}
|
||||
|
||||
assert(E1.Foo < E1.Qux)
|
||||
assert(E1.Baz > E1.Bar)
|
||||
|
||||
assert(not (E1.Foo == E2.Foo))
|
||||
|
||||
should_fail(function()
|
||||
return E1.Foo > E2.Foo
|
||||
end)
|
||||
|
||||
should_fail(function()
|
||||
return E2.Bar >= E1.Foo
|
||||
end)
|
||||
end)
|
||||
it("should error when accessing invalid field", function()
|
||||
local E = Enum {
|
||||
"Foo",
|
||||
"Bar",
|
||||
"Baz",
|
||||
}
|
||||
|
||||
should_fail(function()
|
||||
return E.foo
|
||||
end)
|
||||
|
||||
should_fail(function()
|
||||
return E.bar
|
||||
end)
|
||||
end)
|
||||
it("should fail if there is name or index clashing", function()
|
||||
should_fail(function()
|
||||
return Enum {
|
||||
"Foo",
|
||||
"Foo",
|
||||
}
|
||||
end)
|
||||
should_fail(function()
|
||||
return Enum {
|
||||
"Foo",
|
||||
{ "Bar", 1 },
|
||||
}
|
||||
end)
|
||||
end)
|
||||
it("should fail if there is a key that starts with lowercase", function()
|
||||
should_fail(function()
|
||||
return Enum {
|
||||
"foo",
|
||||
}
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,139 @@
|
|||
local filetype = require "plenary.filetype"
|
||||
|
||||
describe("filetype", function()
|
||||
describe("_get_extension_parts", function()
|
||||
it("should find stuff with underscores", function()
|
||||
assert.are.same({ "py" }, filetype._get_extension_parts "__init__.py")
|
||||
end)
|
||||
|
||||
it("should find all possibilities", function()
|
||||
assert.are.same({ "rst.txt", "txt" }, filetype._get_extension_parts "example.rst.txt")
|
||||
assert.are.same({ "emacs.desktop", "desktop" }, filetype._get_extension_parts "example.emacs.desktop")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("detect_from_extension", function()
|
||||
it("should work for md", function()
|
||||
assert.are.same("markdown", filetype.detect_from_extension "Readme.md")
|
||||
end)
|
||||
|
||||
it("should work for CMakeList.txt", function()
|
||||
assert.are.same("text", filetype.detect_from_extension "CMakeLists.txt")
|
||||
end)
|
||||
|
||||
it("should work with extensions with dot", function()
|
||||
assert.are.same("rst", filetype.detect_from_extension "example.rst.txt")
|
||||
assert.are.same("rst", filetype.detect_from_extension "example.rest.txt")
|
||||
assert.are.same("yaml", filetype.detect_from_extension "example.yaml.sed")
|
||||
assert.are.same("yaml", filetype.detect_from_extension "example.yml.mysql")
|
||||
assert.are.same("erlang", filetype.detect_from_extension "asdf/example.app.src")
|
||||
assert.are.same("cmake", filetype.detect_from_extension "/asdf/example.cmake.in")
|
||||
assert.are.same("desktop", filetype.detect_from_extension "/asdf/asdf.desktop.in")
|
||||
assert.are.same("xml", filetype.detect_from_extension "example.dll.config")
|
||||
assert.are.same("haml", filetype.detect_from_extension "example.haml.deface")
|
||||
assert.are.same("html", filetype.detect_from_extension "example.html.hl")
|
||||
assert.are.same("yaml", filetype.detect_from_extension "example.model.lkml")
|
||||
assert.are.same("rust", filetype.detect_from_extension "example.rs.in")
|
||||
assert.are.same("sh", filetype.detect_from_extension "example.sh.in")
|
||||
assert.are.same("json", filetype.detect_from_extension "example.tfstate.backup")
|
||||
assert.are.same("yaml", filetype.detect_from_extension "example.view.lkml")
|
||||
assert.are.same("xml", filetype.detect_from_extension "example.xml.dist")
|
||||
assert.are.same("xml", filetype.detect_from_extension "example.xsp.metadata")
|
||||
end)
|
||||
|
||||
it("should work for ext==ft even without a table value", function()
|
||||
assert.are.same("bib", filetype.detect_from_extension "file.bib")
|
||||
assert.are.same("bst", filetype.detect_from_extension "file.bst")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("detect_from_name", function()
|
||||
it("should work for common filenames, like makefile", function()
|
||||
assert.are.same("make", filetype.detect_from_name "Makefile")
|
||||
assert.are.same("make", filetype.detect_from_name "makefile")
|
||||
end)
|
||||
|
||||
it("should work for CMakeList.txt", function()
|
||||
assert.are.same("cmake", filetype.detect_from_name "CMakeLists.txt")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("detect_from_modeline", function()
|
||||
it("should work for modeline 2", function()
|
||||
assert.are.same("help", filetype._parse_modeline " vim:tw=78:ts=8:noet:ft=help:norl:")
|
||||
end)
|
||||
|
||||
it("should return nothing if ft not found in modeline", function()
|
||||
assert.are.same("", filetype._parse_modeline "/* vim: set ts=8 sw=4 tw=0 noet : */")
|
||||
end)
|
||||
|
||||
it("should return nothing for random line", function()
|
||||
assert.are.same("", filetype._parse_modeline "return filetype")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("detect_from_shebang", function()
|
||||
it("should work for shell", function()
|
||||
assert.are.same("sh", filetype._parse_shebang "#!/bin/sh")
|
||||
end)
|
||||
|
||||
it("should work for bash", function()
|
||||
assert.are.same("sh", filetype._parse_shebang "#!/bin/bash")
|
||||
end)
|
||||
|
||||
it("should work for usr/bin/env shell", function()
|
||||
assert.are.same("sh", filetype._parse_shebang "#!/usr/bin/env sh")
|
||||
end)
|
||||
|
||||
it("should work for env shell", function()
|
||||
assert.are.same("sh", filetype._parse_shebang "#!/bin/env sh")
|
||||
end)
|
||||
|
||||
it("should work for python", function()
|
||||
assert.are.same("python", filetype._parse_shebang "#!/bin/python")
|
||||
end)
|
||||
|
||||
it("should work for /usr/bin/python3", function()
|
||||
assert.are.same("python", filetype._parse_shebang "#!/usr/bin/python3")
|
||||
end)
|
||||
|
||||
it("should work for python3", function()
|
||||
assert.are.same("python", filetype._parse_shebang "#!/bin/python3")
|
||||
end)
|
||||
|
||||
it("should work for env python", function()
|
||||
assert.are.same("python", filetype._parse_shebang "#!/bin/env python")
|
||||
end)
|
||||
|
||||
it("should not work for random line", function()
|
||||
assert.are.same("", filetype._parse_shebang 'local path = require"plenary.path"')
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("detect", function()
|
||||
it("should work for common filetypes, like python", function()
|
||||
assert.are.same("python", filetype.detect "__init__.py")
|
||||
end)
|
||||
|
||||
it("should work for common filenames, like makefile", function()
|
||||
assert.are.same("make", filetype.detect "Makefile")
|
||||
assert.are.same("make", filetype.detect "makefile")
|
||||
end)
|
||||
|
||||
it("should work for CMakeList.txt", function()
|
||||
assert.are.same("cmake", filetype.detect "CMakeLists.txt")
|
||||
end)
|
||||
|
||||
it("should work for common files, even with .s, like .bashrc", function()
|
||||
assert.are.same("sh", filetype.detect ".bashrc")
|
||||
end)
|
||||
|
||||
it("should work fo custom filetypes, like fennel", function()
|
||||
assert.are.same("fennel", filetype.detect "init.fnl")
|
||||
end)
|
||||
|
||||
it("should work for custom filenames, like Cakefile", function()
|
||||
assert.are.same("coffee", filetype.detect "Cakefile")
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,18 @@
|
|||
local f = require "plenary.functional"
|
||||
|
||||
describe("functional", function()
|
||||
describe("partial", function()
|
||||
local function args(...)
|
||||
assert.is.equal(4, select("#", ...))
|
||||
return table.concat({ ... }, ",")
|
||||
end
|
||||
it("should bind correct parameters", function()
|
||||
local expected = args(1, 2, 3, 4)
|
||||
assert.is.equal(expected, f.partial(args)(1, 2, 3, 4))
|
||||
assert.is.equal(expected, f.partial(args, 1)(2, 3, 4))
|
||||
assert.is.equal(expected, f.partial(args, 1, 2)(3, 4))
|
||||
assert.is.equal(expected, f.partial(args, 1, 2, 3)(4))
|
||||
assert.is.equal(expected, f.partial(args, 1, 2, 3, 4)())
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,149 @@
|
|||
local i = require "plenary.iterators"
|
||||
local f = require "plenary.functional"
|
||||
local eq = assert.are.same
|
||||
|
||||
local function check_keys(tbl, keys)
|
||||
for _, key in ipairs(keys) do
|
||||
if not tbl[key] then
|
||||
error("Key " .. key .. " was not found")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe("iterators", function()
|
||||
it("should be able to create iterator from table", function()
|
||||
local tbl = { first = 1, second = 2, third = 3, fourth = 4 }
|
||||
local results = i.iter(tbl):tolist()
|
||||
eq(#results, 4)
|
||||
check_keys(tbl, { "first", "second", "third", "fourth" })
|
||||
results = {
|
||||
{ "first", 1 },
|
||||
{ "second", 2 },
|
||||
{ "third", 3 },
|
||||
{ "fourth", 4 },
|
||||
}
|
||||
end)
|
||||
|
||||
it("should be able to create iterator from array", function()
|
||||
local tbl = { 1, 2, 3, 4 }
|
||||
local results = i.iter(tbl):tolist()
|
||||
eq(#results, 4)
|
||||
check_keys(tbl, { 1, 2, 3, 4 })
|
||||
end)
|
||||
|
||||
it("should be able to fold", function()
|
||||
local numbers = { 1, 2, 3, 4 }
|
||||
local result = i.iter(numbers):fold(0, function(a, v)
|
||||
return a + v
|
||||
end)
|
||||
eq(result, 10)
|
||||
|
||||
local strings = { "hello", "world", "this", "is", "a", "test" }
|
||||
result = i.iter(strings):fold("", function(a, v)
|
||||
return a .. v
|
||||
end)
|
||||
eq(result, "helloworldthisisatest")
|
||||
end)
|
||||
|
||||
it("should be able to enumerate", function()
|
||||
local tbl = { 1, 2, 3, 4 }
|
||||
local results = i.iter(tbl)
|
||||
:enumerate()
|
||||
:map(function(idx, v)
|
||||
return { idx, v }
|
||||
end)
|
||||
:tolist()
|
||||
eq(#results, 4)
|
||||
eq(results[1], { 1, 1 })
|
||||
eq(results[2], { 2, 2 })
|
||||
eq(results[3], { 3, 3 })
|
||||
eq(results[4], { 4, 4 })
|
||||
end)
|
||||
|
||||
it("should be able to find", function()
|
||||
local tbl = { 1, 2, 3, 4 }
|
||||
local tbl_iter = i.iter(tbl)
|
||||
local res = tbl_iter:find(2)
|
||||
eq(res, 2)
|
||||
res = tbl_iter:find "will not find this"
|
||||
assert(not res)
|
||||
|
||||
tbl = { 1, 2, 3, 4, "some random string", 6 }
|
||||
eq(
|
||||
i.iter(tbl):find(function(x)
|
||||
return type(x) == "string"
|
||||
end),
|
||||
"some random string"
|
||||
)
|
||||
end)
|
||||
|
||||
it("should be table to chain", function()
|
||||
local first = i.iter { 1, 2, 3 }
|
||||
local second = i.iter { 4, 5, 6, 7 }
|
||||
local third = i.iter { 8, 9, 10 }
|
||||
local res = (first .. second .. third):tolist()
|
||||
eq(res, i.range(10):tolist())
|
||||
end)
|
||||
|
||||
it("should make a range", function()
|
||||
eq({ 1, 2, 3, 4, 5 }, i.range(5):tolist())
|
||||
end)
|
||||
|
||||
it("should be able to make a stateful", function()
|
||||
local iter = i.range(3):stateful()
|
||||
eq(iter(), 1)
|
||||
eq(iter(), 2)
|
||||
eq(iter(), 3)
|
||||
assert(not iter())
|
||||
assert(not iter())
|
||||
assert(not iter())
|
||||
|
||||
local iter = i.range(3):stateful()
|
||||
-- dump(iter:tolist())
|
||||
eq(iter:tolist(), { 1, 2, 3 })
|
||||
end)
|
||||
|
||||
it("should be able to flatten", function()
|
||||
local iter = i.range(3)
|
||||
:map(function(_)
|
||||
return i.iter { 5, 7, 9 }
|
||||
end)
|
||||
:flatten()
|
||||
:stateful()
|
||||
eq(iter(), 5)
|
||||
eq(iter(), 7)
|
||||
eq(iter(), 9)
|
||||
eq(iter(), 5)
|
||||
eq(iter(), 7)
|
||||
eq(iter(), 9)
|
||||
eq(iter(), 5)
|
||||
eq(iter(), 7)
|
||||
eq(iter(), 9)
|
||||
local iter = i.range(3)
|
||||
:map(function(_)
|
||||
return i.iter { 5, 7, 9 }
|
||||
end)
|
||||
:flatten()
|
||||
eq(iter:tolist(), { 5, 7, 9, 5, 7, 9, 5, 7, 9 })
|
||||
end)
|
||||
|
||||
it("should be able to flatten very nested stuff", function()
|
||||
local iter = i.iter({ 5, 6, i.iter { i.iter { 5, 5 }, 7, 8 }, i.iter { 9, 10, i.iter { 1, 2 } } }):flatten()
|
||||
eq(iter:tolist(), { 5, 6, 5, 5, 7, 8, 9, 10, 1, 2 })
|
||||
end)
|
||||
|
||||
it("chaining nil should work", function()
|
||||
local iter = i.iter(""):chain(i.iter { 5, 7, 9 })
|
||||
eq(#iter:tolist(), 3)
|
||||
end)
|
||||
|
||||
describe("generators", function()
|
||||
it("should be able to split", function()
|
||||
local iter = i.split(" hello person world ", " ")
|
||||
eq(iter:tolist(), { "", "hello", "person", "world", "" })
|
||||
|
||||
iter = i.split("\n\n\nfirst\nsecond\nthird\n\n", "\n")
|
||||
eq(iter:tolist(), { "", "", "", "first", "second", "third", "", "" })
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,28 @@
|
|||
local Job = require "plenary.job"
|
||||
|
||||
describe("Job Validation", function()
|
||||
it("does not require command when called with array method", function()
|
||||
local ok, j = pcall(Job.new, Job, { "ls" })
|
||||
assert(ok, "Accepts positional arguments")
|
||||
assert(j.command == "ls")
|
||||
end)
|
||||
|
||||
it("cannot use command and array syntax", function()
|
||||
local ok = pcall(Job.new, Job, { "ls", command = "ls" })
|
||||
assert(not ok, "cannot use command and array syntax")
|
||||
end)
|
||||
|
||||
it("can parse command and args from array syntax", function()
|
||||
local ok, j = pcall(Job.new, Job, { "ls", "-al" })
|
||||
assert(ok, "Accepts positional arguments")
|
||||
assert(j.command == "ls")
|
||||
assert.are.same({ "-al" }, j.args)
|
||||
end)
|
||||
|
||||
it("can parse command and multiple args from array syntax", function()
|
||||
local ok, j = pcall(Job.new, Job, { "ls", "-al", "~" })
|
||||
assert(ok, "Accepts positional arguments")
|
||||
assert(j.command == "ls")
|
||||
assert.are.same({ "-al", "~" }, j.args)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,864 @@
|
|||
local Job = require "plenary.job"
|
||||
|
||||
local has_all_executables = function(execs)
|
||||
for _, e in ipairs(execs) do
|
||||
if vim.fn.executable(e) == 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local tables_equal = function(t1, t2)
|
||||
if #t1 ~= #t2 then
|
||||
return false
|
||||
end
|
||||
for i, t1_v in ipairs(t1) do
|
||||
if t2[i] ~= t1_v then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local wait_for_result = function(job, result)
|
||||
if type(result) == "string" then
|
||||
result = { result }
|
||||
end
|
||||
vim.wait(1000, function()
|
||||
return tables_equal(job:result(), result)
|
||||
end)
|
||||
end
|
||||
|
||||
describe("Job", function()
|
||||
describe("> cat manually >", function()
|
||||
it("should split simple stdin", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "cat",
|
||||
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:start()
|
||||
job:send "hello\n"
|
||||
job:send "world\n"
|
||||
|
||||
wait_for_result(job, { "hello", "world" })
|
||||
job:shutdown()
|
||||
|
||||
assert.are.same(job:result(), { "hello", "world" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should allow empty strings", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "cat",
|
||||
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:start()
|
||||
job:send "hello\n"
|
||||
job:send "\n"
|
||||
job:send "world\n"
|
||||
job:send "\n"
|
||||
|
||||
wait_for_result(job, { "hello", "", "world", "" })
|
||||
job:shutdown()
|
||||
|
||||
assert.are.same(job:result(), { "hello", "", "world", "" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should split stdin across newlines", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
-- writer = "hello\nword\nthis is\ntj",
|
||||
command = "cat",
|
||||
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:start()
|
||||
job:send "hello\nwor"
|
||||
job:send "ld\n"
|
||||
|
||||
wait_for_result(job, { "hello", "world" })
|
||||
job:shutdown()
|
||||
|
||||
assert.are.same(job:result(), { "hello", "world" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should split stdin across newlines with no ending newline", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
-- writer = "hello\nword\nthis is\ntj",
|
||||
command = "cat",
|
||||
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:start()
|
||||
job:send "hello\nwor"
|
||||
job:send "ld"
|
||||
|
||||
wait_for_result(job, { "hello", "world" })
|
||||
job:shutdown()
|
||||
|
||||
assert.are.same(job:result(), { "hello", "world" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should return last line when there is ending newline", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "printf",
|
||||
|
||||
args = { "test1\ntest2\n" },
|
||||
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
|
||||
assert.are.same(job:result(), { "test1", "test2" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should return last line when there is no ending newline", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "printf",
|
||||
|
||||
args = { "test1\ntest2" },
|
||||
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
|
||||
assert.are.same(job:result(), { "test1", "test2" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("env", function()
|
||||
it("should be possible to set one env variable with an array", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "env",
|
||||
env = { "A=100" },
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
|
||||
assert.are.same(job:result(), { "A=100" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should be possible to set multiple env variables with an array", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "env",
|
||||
env = { "A=100", "B=test" },
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
|
||||
assert.are.same(job:result(), { "A=100", "B=test" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should be possible to set one env variable with a map", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "env",
|
||||
env = { "A=100" },
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
|
||||
assert.are.same(job:result(), { "A=100" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should be possible to set one env variable with spaces", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "env",
|
||||
env = { "A=This is a long env var" },
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
|
||||
assert.are.same(job:result(), { "A=This is a long env var" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should be possible to set one env variable with spaces and a map", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "env",
|
||||
env = { ["A"] = "This is a long env var" },
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
|
||||
assert.are.same(job:result(), { "A=This is a long env var" })
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should be possible to set multiple env variables with a map", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "env",
|
||||
env = { ["A"] = 100, ["B"] = "test" },
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
|
||||
local expected = { "A=100", "B=test" }
|
||||
local found = { false, false }
|
||||
for k, v in ipairs(job:result()) do
|
||||
for _, w in ipairs(expected) do
|
||||
if v == w then
|
||||
found[k] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert.are.same({ true, true }, found)
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
|
||||
it("should be possible to set multiple env variables with both, array and map", function()
|
||||
local results = {}
|
||||
local job = Job:new {
|
||||
command = "env",
|
||||
env = { ["A"] = 100, "B=test" },
|
||||
on_stdout = function(_, data)
|
||||
table.insert(results, data)
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
|
||||
local expected = { "A=100", "B=test" }
|
||||
local found = { false, false }
|
||||
for k, v in ipairs(job:result()) do
|
||||
for _, w in ipairs(expected) do
|
||||
if v == w then
|
||||
found[k] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert.are.same({ true, true }, found)
|
||||
assert.are.same(job:result(), results)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("> simple ls >", function()
|
||||
it("should match systemlist", function()
|
||||
local ls_results = vim.fn.systemlist "ls -l"
|
||||
|
||||
local job = Job:new {
|
||||
command = "ls",
|
||||
args = { "-l" },
|
||||
}
|
||||
|
||||
job:sync()
|
||||
assert.are.same(job:result(), ls_results)
|
||||
end)
|
||||
|
||||
it("should match larger systemlist", function()
|
||||
local results = vim.fn.systemlist "find ."
|
||||
local stdout_results = {}
|
||||
|
||||
local job = Job:new {
|
||||
command = "find",
|
||||
args = { "." },
|
||||
|
||||
on_stdout = function(_, line)
|
||||
table.insert(stdout_results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
assert.are.same(job:result(), results)
|
||||
assert.are.same(job:result(), stdout_results)
|
||||
end)
|
||||
|
||||
it("should not timeout when completing fast jobs", function()
|
||||
local start = vim.loop.hrtime()
|
||||
|
||||
local job = Job:new { command = "ls" }
|
||||
|
||||
job:sync()
|
||||
|
||||
assert((vim.loop.hrtime() - start) / 1e9 < 1, "Should not take one second to complete")
|
||||
end)
|
||||
|
||||
it("should return the return code as well", function()
|
||||
local job = Job:new { command = "false" }
|
||||
local _, ret = job:sync()
|
||||
|
||||
assert.are.same(1, job.code)
|
||||
assert.are.same(1, ret)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("chain", function()
|
||||
it("should always run the next job when using and_then", function()
|
||||
local results = {}
|
||||
|
||||
local first_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["a"] = "1" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local second_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["b"] = "2" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local third_job = Job:new { command = "false" }
|
||||
|
||||
local fourth_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["c"] = "3" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
first_job:and_then(second_job)
|
||||
second_job:and_then(third_job)
|
||||
third_job:and_then(fourth_job)
|
||||
|
||||
first_job:sync()
|
||||
second_job:wait()
|
||||
third_job:wait()
|
||||
fourth_job:wait()
|
||||
|
||||
assert.are.same({ "a=1", "b=2", "c=3" }, results)
|
||||
assert.are.same({ "a=1" }, first_job:result())
|
||||
assert.are.same({ "b=2" }, second_job:result())
|
||||
assert.are.same(1, third_job.code)
|
||||
assert.are.same({ "c=3" }, fourth_job:result())
|
||||
end)
|
||||
|
||||
it("should only run the next job on success when using and_then_on_success", function()
|
||||
local results = {}
|
||||
|
||||
local first_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["a"] = "1" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local second_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["b"] = "2" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local third_job = Job:new { command = "false" }
|
||||
|
||||
local fourth_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["c"] = "3" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
first_job:and_then_on_success(second_job)
|
||||
second_job:and_then_on_success(third_job)
|
||||
third_job:and_then_on_success(fourth_job)
|
||||
|
||||
first_job:sync()
|
||||
second_job:wait()
|
||||
third_job:wait()
|
||||
|
||||
assert.are.same({ "a=1", "b=2" }, results)
|
||||
assert.are.same({ "a=1" }, first_job:result())
|
||||
assert.are.same({ "b=2" }, second_job:result())
|
||||
assert.are.same(1, third_job.code)
|
||||
assert.are.same(nil, fourth_job.handle, "Job never started")
|
||||
end)
|
||||
|
||||
it("should only run the next job on failure when using and_then_on_failure", function()
|
||||
local results = {}
|
||||
|
||||
local first_job = Job:new {
|
||||
command = "false",
|
||||
}
|
||||
|
||||
local second_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["a"] = "1" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local third_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["b"] = "2" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
first_job:and_then_on_failure(second_job)
|
||||
second_job:and_then_on_failure(third_job)
|
||||
|
||||
local _, ret = first_job:sync()
|
||||
second_job:wait()
|
||||
|
||||
assert.are.same(1, first_job.code)
|
||||
assert.are.same(1, ret)
|
||||
assert.are.same({ "a=1" }, results)
|
||||
assert.are.same({ "a=1" }, second_job:result())
|
||||
assert.are.same(nil, third_job.handle, "Job never started")
|
||||
end)
|
||||
|
||||
it("should run all normal functions when using after", function()
|
||||
local results = {}
|
||||
local code = 0
|
||||
|
||||
local first_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["a"] = "1" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local second_job = Job:new { command = "false" }
|
||||
|
||||
first_job
|
||||
:after(function()
|
||||
code = code + 10
|
||||
end)
|
||||
:and_then(second_job)
|
||||
second_job:after(function(_, c)
|
||||
code = code + c
|
||||
end)
|
||||
|
||||
first_job:sync()
|
||||
second_job:wait()
|
||||
|
||||
assert.are.same({ "a=1" }, results)
|
||||
assert.are.same({ "a=1" }, first_job:result())
|
||||
assert.are.same(1, second_job.code)
|
||||
assert.are.same(11, code)
|
||||
end)
|
||||
|
||||
it("should run only on success normal functions when using after_success", function()
|
||||
local results = {}
|
||||
local code = 0
|
||||
|
||||
local first_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["a"] = "1" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local second_job = Job:new { command = "false" }
|
||||
local third_job = Job:new { command = "true" }
|
||||
|
||||
first_job:after_success(function()
|
||||
code = code + 10
|
||||
end)
|
||||
first_job:and_then_on_success(second_job)
|
||||
second_job:after_success(function(_, c)
|
||||
code = code + c
|
||||
end)
|
||||
second_job:and_then_on_success(third_job)
|
||||
|
||||
first_job:sync()
|
||||
second_job:wait()
|
||||
|
||||
assert.are.same({ "a=1" }, results)
|
||||
assert.are.same({ "a=1" }, first_job:result())
|
||||
assert.are.same(1, second_job.code)
|
||||
assert.are.same(10, code)
|
||||
assert.are.same(nil, third_job.handle)
|
||||
end)
|
||||
|
||||
it("should run only on failure normal functions when using after_failure", function()
|
||||
local results = {}
|
||||
local code = 0
|
||||
|
||||
local first_job = Job:new { command = "false" }
|
||||
|
||||
local second_job = Job:new {
|
||||
command = "env",
|
||||
env = { ["a"] = "1" },
|
||||
on_stdout = function(_, line)
|
||||
table.insert(results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local third_job = Job:new { command = "true" }
|
||||
|
||||
first_job:after_failure(function(_, c)
|
||||
code = code + c
|
||||
end)
|
||||
first_job:and_then_on_failure(second_job)
|
||||
second_job:after_failure(function()
|
||||
code = code + 10
|
||||
end)
|
||||
second_job:and_then_on_failure(third_job)
|
||||
|
||||
local _, ret = first_job:sync()
|
||||
second_job:wait()
|
||||
|
||||
assert.are.same({ "a=1" }, results)
|
||||
assert.are.same({ "a=1" }, second_job:result())
|
||||
assert.are.same(1, ret)
|
||||
assert.are.same(1, first_job.code)
|
||||
assert.are.same(1, code)
|
||||
assert.are.same(0, second_job.code)
|
||||
assert.are.same(nil, third_job.handle)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(".writer", function()
|
||||
pending("should allow using things like fzf", function()
|
||||
if not has_all_executables { "fzf", "fdfind" } then
|
||||
return
|
||||
end
|
||||
|
||||
local stdout_results = {}
|
||||
|
||||
local fzf = Job:new {
|
||||
writer = Job:new {
|
||||
command = "fdfind",
|
||||
cwd = vim.fn.expand "~/plugins/plenary.nvim/",
|
||||
},
|
||||
|
||||
command = "fzf",
|
||||
args = { "--filter", "job.lua" },
|
||||
|
||||
cwd = vim.fn.expand "~/plugins/plenary.nvim/",
|
||||
|
||||
on_stdout = function(_, line)
|
||||
table.insert(stdout_results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local results = fzf:sync()
|
||||
assert.are.same(results, stdout_results)
|
||||
|
||||
-- 'job.lua' should be the best file from fzf.
|
||||
-- So make sure we're processing correctly.
|
||||
assert.are.same("lua/plenary/job.lua", results[1])
|
||||
end)
|
||||
|
||||
it("should work with a table", function()
|
||||
if not has_all_executables { "fzf" } then
|
||||
return
|
||||
end
|
||||
|
||||
local stdout_results = {}
|
||||
|
||||
local fzf = Job:new {
|
||||
writer = { "hello", "world", "job.lua" },
|
||||
|
||||
command = "fzf",
|
||||
args = { "--filter", "job.lua" },
|
||||
|
||||
on_stdout = function(_, line)
|
||||
table.insert(stdout_results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local results = fzf:sync()
|
||||
assert.are.same(results, stdout_results)
|
||||
|
||||
-- 'job.lua' should be the best file from fzf.
|
||||
-- So make sure we're processing correctly.
|
||||
assert.are.same("job.lua", results[1])
|
||||
assert.are.same(1, #results)
|
||||
end)
|
||||
|
||||
it("should work with a string", function()
|
||||
if not has_all_executables { "fzf" } then
|
||||
return
|
||||
end
|
||||
|
||||
local stdout_results = {}
|
||||
|
||||
local fzf = Job:new {
|
||||
writer = "hello\nworld\njob.lua",
|
||||
|
||||
command = "fzf",
|
||||
args = { "--filter", "job.lua" },
|
||||
|
||||
on_stdout = function(_, line)
|
||||
table.insert(stdout_results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
local results = fzf:sync()
|
||||
assert.are.same(results, stdout_results)
|
||||
|
||||
-- 'job.lua' should be the best file from fzf.
|
||||
-- So make sure we're processing correctly.
|
||||
assert.are.same("job.lua", results[1])
|
||||
assert.are.same(1, #results)
|
||||
end)
|
||||
|
||||
it("should work with a pipe", function()
|
||||
if not has_all_executables { "fzf" } then
|
||||
return
|
||||
end
|
||||
|
||||
local input_pipe = vim.loop.new_pipe(false)
|
||||
|
||||
local stdout_results = {}
|
||||
local fzf = Job:new {
|
||||
writer = input_pipe,
|
||||
|
||||
command = "fzf",
|
||||
args = { "--filter", "job.lua" },
|
||||
|
||||
on_stdout = function(_, line)
|
||||
table.insert(stdout_results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
fzf:start()
|
||||
|
||||
input_pipe:write "hello\n"
|
||||
input_pipe:write "world\n"
|
||||
input_pipe:write "job.lua\n"
|
||||
input_pipe:close()
|
||||
|
||||
wait_for_result(fzf, "job.lua")
|
||||
fzf:shutdown()
|
||||
|
||||
local results = fzf:result()
|
||||
assert.are.same(results, stdout_results)
|
||||
|
||||
-- 'job.lua' should be the best file from fzf.
|
||||
-- So make sure we're processing correctly.
|
||||
assert.are.same("job.lua", results[1])
|
||||
assert.are.same(1, #results)
|
||||
end)
|
||||
|
||||
it("should work with a pipe, but no final newline", function()
|
||||
if not has_all_executables { "fzf" } then
|
||||
return
|
||||
end
|
||||
|
||||
local input_pipe = vim.loop.new_pipe(false)
|
||||
|
||||
local stdout_results = {}
|
||||
local fzf = Job:new {
|
||||
writer = input_pipe,
|
||||
|
||||
command = "fzf",
|
||||
args = { "--filter", "job.lua" },
|
||||
|
||||
on_stdout = function(_, line)
|
||||
table.insert(stdout_results, line)
|
||||
end,
|
||||
}
|
||||
|
||||
fzf:start()
|
||||
|
||||
input_pipe:write "hello\n"
|
||||
input_pipe:write "world\n"
|
||||
input_pipe:write "job.lua"
|
||||
input_pipe:close()
|
||||
|
||||
wait_for_result(fzf, "job.lua")
|
||||
fzf:shutdown()
|
||||
|
||||
local results = fzf:result()
|
||||
assert.are.same(results, stdout_results)
|
||||
|
||||
-- 'job.lua' should be the best file from fzf.
|
||||
-- So make sure we're processing correctly.
|
||||
assert.are.same("job.lua", results[1])
|
||||
assert.are.same(1, #results)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(":wait()", function()
|
||||
it("should respect timeout", function()
|
||||
local j = Job:new {
|
||||
command = "sleep",
|
||||
args = { "10" },
|
||||
}
|
||||
|
||||
local ok = pcall(j.sync, j, 500)
|
||||
assert(not ok, "Job should fail")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("enable_.*", function()
|
||||
it("should not add things to results when disabled", function()
|
||||
local job = Job:new {
|
||||
command = "ls",
|
||||
args = { "-l" },
|
||||
|
||||
enable_recording = false,
|
||||
}
|
||||
|
||||
local res = job:sync()
|
||||
assert(res == nil, "No results should exist")
|
||||
assert(job._stdout_results == nil, "No result table")
|
||||
end)
|
||||
|
||||
it("should not call callbacks when disabled", function()
|
||||
local was_called = false
|
||||
local job = Job:new {
|
||||
command = "ls",
|
||||
args = { "-l" },
|
||||
|
||||
enable_handlers = false,
|
||||
|
||||
on_stdout = function()
|
||||
was_called = true
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
assert(not was_called, "Should not be called.")
|
||||
assert(job._stdout_results == nil, "No result table")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("enable_.*", function()
|
||||
it("should not add things to results when disabled", function()
|
||||
local job = Job:new {
|
||||
command = "ls",
|
||||
args = { "-l" },
|
||||
|
||||
enable_recording = false,
|
||||
}
|
||||
|
||||
local res = job:sync()
|
||||
assert(res == nil, "No results should exist")
|
||||
assert(job._stdout_results == nil, "No result table")
|
||||
end)
|
||||
|
||||
it("should not call callbacks when disbaled", function()
|
||||
local was_called = false
|
||||
local job = Job:new {
|
||||
command = "ls",
|
||||
args = { "-l" },
|
||||
|
||||
enable_handlers = false,
|
||||
|
||||
on_stdout = function()
|
||||
was_called = true
|
||||
end,
|
||||
}
|
||||
|
||||
job:sync()
|
||||
assert(not was_called, "Should not be called.")
|
||||
assert(job._stdout_results == nil, "No result table")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("validation", function()
|
||||
it("requires options", function()
|
||||
local ok = pcall(Job.new, { command = "ls" })
|
||||
assert(not ok, "Requires options")
|
||||
end)
|
||||
|
||||
it("requires command", function()
|
||||
local ok = pcall(Job.new, Job, { cmd = "ls" })
|
||||
assert(not ok, "Requires command")
|
||||
end)
|
||||
|
||||
it("will not spawn jobs with invalid commands", function()
|
||||
local ok = pcall(Job.new, Job, { command = "dasowlwl" })
|
||||
assert(not ok, "Should not allow invalid executables")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("on_exit", function()
|
||||
it("should only be called once for wait", function()
|
||||
local count = 0
|
||||
|
||||
local job = Job:new {
|
||||
command = "ls",
|
||||
on_exit = function(...)
|
||||
count = count + 1
|
||||
end,
|
||||
}
|
||||
job:start()
|
||||
job:wait()
|
||||
|
||||
assert.are.same(count, 1)
|
||||
end)
|
||||
|
||||
it("should only be called once for shutdown", function()
|
||||
local count = 0
|
||||
|
||||
local job = Job:new {
|
||||
command = "ls",
|
||||
on_exit = function(...)
|
||||
count = count + 1
|
||||
end,
|
||||
}
|
||||
job:start()
|
||||
job:shutdown()
|
||||
|
||||
assert.are.same(count, 1)
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,87 @@
|
|||
local json = require "plenary.json"
|
||||
local eq = assert.are.same
|
||||
|
||||
describe("json", function()
|
||||
it("replace comments with whitespace", function()
|
||||
eq(json.json_strip_comments '//comment\n{"a":"b"}', ' \n{"a":"b"}')
|
||||
eq(json.json_strip_comments '/*//comment*/{"a":"b"}', ' {"a":"b"}')
|
||||
eq(json.json_strip_comments '{"a":"b"//comment\n}', '{"a":"b" \n}')
|
||||
eq(json.json_strip_comments '{"a":"b"/*comment*/}', '{"a":"b" }')
|
||||
eq(json.json_strip_comments '{"a"/*\n\n\ncomment\r\n*/:"b"}', '{"a" \n\n\n \r\n :"b"}')
|
||||
eq(json.json_strip_comments '/*!\n * comment\n */\n{"a":"b"}', ' \n \n \n{"a":"b"}')
|
||||
eq(json.json_strip_comments '{/*comment*/"a":"b"}', '{ "a":"b"}')
|
||||
end)
|
||||
|
||||
it("remove comments", function()
|
||||
local options = { whitespace = false }
|
||||
eq(json.json_strip_comments('//comment\n{"a":"b"}', options), '\n{"a":"b"}')
|
||||
eq(json.json_strip_comments('/*//comment*/{"a":"b"}', options), '{"a":"b"}')
|
||||
eq(json.json_strip_comments('{"a":"b"//comment\n}', options), '{"a":"b"\n}')
|
||||
eq(json.json_strip_comments('{"a":"b"/*comment*/}', options), '{"a":"b"}')
|
||||
eq(json.json_strip_comments('{"a"/*\n\n\ncomment\r\n*/:"b"}', options), '{"a":"b"}')
|
||||
eq(json.json_strip_comments('/*!\n * comment\n */\n{"a":"b"}', options), '\n{"a":"b"}')
|
||||
eq(json.json_strip_comments('{/*comment*/"a":"b"}', options), '{"a":"b"}')
|
||||
end)
|
||||
|
||||
it("doesn't strip comments inside strings", function()
|
||||
eq(json.json_strip_comments '{"a":"b//c"}', '{"a":"b//c"}')
|
||||
eq(json.json_strip_comments '{"a":"b/*c*/"}', '{"a":"b/*c*/"}')
|
||||
eq(json.json_strip_comments '{"/*a":"b"}', '{"/*a":"b"}')
|
||||
eq(json.json_strip_comments '{"\\"/*a":"b"}', '{"\\"/*a":"b"}')
|
||||
end)
|
||||
|
||||
it("consider escaped slashes when checking for escaped string quote", function()
|
||||
eq(json.json_strip_comments '{"\\\\":"https://foobar.com"}', '{"\\\\":"https://foobar.com"}')
|
||||
eq(json.json_strip_comments '{"foo\\"":"https://foobar.com"}', '{"foo\\"":"https://foobar.com"}')
|
||||
end)
|
||||
|
||||
it("line endings - no comments", function()
|
||||
eq(json.json_strip_comments '{"a":"b"\n}', '{"a":"b"\n}')
|
||||
eq(json.json_strip_comments '{"a":"b"\r\n}', '{"a":"b"\r\n}')
|
||||
end)
|
||||
|
||||
it("line endings - single line comment", function()
|
||||
eq(json.json_strip_comments '{"a":"b"//c\n}', '{"a":"b" \n}')
|
||||
eq(json.json_strip_comments '{"a":"b"//c\r\n}', '{"a":"b" \r\n}')
|
||||
end)
|
||||
|
||||
it("line endings - single line block comment", function()
|
||||
eq(json.json_strip_comments '{"a":"b"/*c*/\n}', '{"a":"b" \n}')
|
||||
eq(json.json_strip_comments '{"a":"b"/*c*/\r\n}', '{"a":"b" \r\n}')
|
||||
end)
|
||||
|
||||
it("line endings - multi line block comment", function()
|
||||
eq(json.json_strip_comments '{"a":"b",/*c\nc2*/"x":"y"\n}', '{"a":"b", \n "x":"y"\n}')
|
||||
eq(json.json_strip_comments '{"a":"b",/*c\r\nc2*/"x":"y"\r\n}', '{"a":"b", \r\n "x":"y"\r\n}')
|
||||
end)
|
||||
|
||||
it("line endings - works at EOF", function()
|
||||
local options = { whitespace = false }
|
||||
eq(json.json_strip_comments '{\r\n\t"a":"b"\r\n} //EOF', '{\r\n\t"a":"b"\r\n} ')
|
||||
eq(json.json_strip_comments('{\r\n\t"a":"b"\r\n} //EOF', options), '{\r\n\t"a":"b"\r\n} ')
|
||||
end)
|
||||
|
||||
it("handles weird escaping", function()
|
||||
eq(
|
||||
json.json_strip_comments [[{"x":"x \"sed -e \\\"s/^.\\\\{46\\\\}T//\\\" -e \\\"s/#033/\\\\x1b/g\\\"\""}]],
|
||||
[[{"x":"x \"sed -e \\\"s/^.\\\\{46\\\\}T//\\\" -e \\\"s/#033/\\\\x1b/g\\\"\""}]]
|
||||
)
|
||||
end)
|
||||
|
||||
it("trailing commas", function()
|
||||
eq(json.json_strip_comments '{"a":"b",}', '{"a":"b"}')
|
||||
eq(json.json_strip_comments '{"a":{"b":"c",},}', '{"a":{"b":"c"}}')
|
||||
eq(json.json_strip_comments '{"a":["b","c",],}', '{"a":["b","c"]}')
|
||||
end)
|
||||
|
||||
it("trailing commas - ignored in strings and comments", function()
|
||||
eq(json.json_strip_comments '{"a":"b,}"}', '{"a":"b,}"}')
|
||||
end)
|
||||
|
||||
it("trailing commas - left when disabled in options", function()
|
||||
local options = { trailing_commas = true }
|
||||
eq(json.json_strip_comments('{"a":"b",}', options), '{"a":"b",}')
|
||||
eq(json.json_strip_comments('{"a":{"b":"c",},}', options), '{"a":{"b":"c",},}')
|
||||
eq(json.json_strip_comments('{"a":["b","c",],}', options), '{"a":["b","c",],}')
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,762 @@
|
|||
local Path = require "plenary.path"
|
||||
local path = Path.path
|
||||
local compat = require "plenary.compat"
|
||||
|
||||
describe("Path", function()
|
||||
it("should find valid files", function()
|
||||
local p = Path:new "README.md"
|
||||
assert(p.filename == "README.md", p.filename)
|
||||
assert.are.same(p.filename, "README.md")
|
||||
end)
|
||||
|
||||
describe("absolute", function()
|
||||
it(".absolute()", function()
|
||||
local p = Path:new { "README.md", sep = "/" }
|
||||
assert.are.same(p:absolute(), vim.fn.fnamemodify("README.md", ":p"))
|
||||
end)
|
||||
|
||||
it("can determine absolute paths", function()
|
||||
local p = Path:new { "/home/asdfasdf/", sep = "/" }
|
||||
assert(p:is_absolute(), "Is absolute")
|
||||
assert(p:absolute() == p.filename)
|
||||
end)
|
||||
|
||||
it("can determine non absolute paths", function()
|
||||
local p = Path:new { "./home/tj/", sep = "/" }
|
||||
assert(not p:is_absolute(), "Is absolute")
|
||||
end)
|
||||
|
||||
it("will normalize the path", function()
|
||||
local p = Path:new { "lua", "..", "README.md", sep = "/" }
|
||||
assert.are.same(p:absolute(), vim.fn.fnamemodify("README.md", ":p"))
|
||||
end)
|
||||
end)
|
||||
|
||||
it("can join paths by constructor or join path", function()
|
||||
assert.are.same(Path:new("lua", "plenary"), Path:new("lua"):joinpath "plenary")
|
||||
end)
|
||||
|
||||
it("can join paths with /", function()
|
||||
assert.are.same(Path:new("lua", "plenary"), Path:new "lua" / "plenary")
|
||||
end)
|
||||
|
||||
it("can join paths with paths", function()
|
||||
assert.are.same(Path:new("lua", "plenary"), Path:new("lua", Path:new "plenary"))
|
||||
end)
|
||||
|
||||
it("inserts slashes", function()
|
||||
assert.are.same("lua" .. path.sep .. "plenary", Path:new("lua", "plenary").filename)
|
||||
end)
|
||||
|
||||
describe(".exists()", function()
|
||||
it("finds files that exist", function()
|
||||
assert.are.same(true, Path:new("README.md"):exists())
|
||||
end)
|
||||
|
||||
it("returns false for files that do not exist", function()
|
||||
assert.are.same(false, Path:new("asdf.md"):exists())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(".is_dir()", function()
|
||||
it("should find directories that exist", function()
|
||||
assert.are.same(true, Path:new("lua"):is_dir())
|
||||
end)
|
||||
|
||||
it("should return false when the directory does not exist", function()
|
||||
assert.are.same(false, Path:new("asdf"):is_dir())
|
||||
end)
|
||||
|
||||
it("should not show files as directories", function()
|
||||
assert.are.same(false, Path:new("README.md"):is_dir())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(".is_file()", function()
|
||||
it("should not allow directories", function()
|
||||
assert.are.same(true, not Path:new("lua"):is_file())
|
||||
end)
|
||||
|
||||
it("should return false when the file does not exist", function()
|
||||
assert.are.same(true, not Path:new("asdf"):is_file())
|
||||
end)
|
||||
|
||||
it("should show files as file", function()
|
||||
assert.are.same(true, Path:new("README.md"):is_file())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(":new", function()
|
||||
it("can be called with or without colon", function()
|
||||
-- This will work, cause we used a colon
|
||||
local with_colon = Path:new "lua"
|
||||
local no_colon = Path.new "lua"
|
||||
|
||||
assert.are.same(with_colon, no_colon)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(":make_relative", function()
|
||||
it("can take absolute paths and make them relative to the cwd", function()
|
||||
local p = Path:new { "lua", "plenary", "path.lua" }
|
||||
local absolute = vim.loop.cwd() .. path.sep .. p.filename
|
||||
local relative = Path:new(absolute):make_relative()
|
||||
assert.are.same(relative, p.filename)
|
||||
end)
|
||||
|
||||
it("can take absolute paths and make them relative to a given path", function()
|
||||
local root = path.sep == "\\" and "c:\\" or "/"
|
||||
local r = Path:new { root, "home", "prime" }
|
||||
local p = Path:new { "aoeu", "agen.lua" }
|
||||
local absolute = r.filename .. path.sep .. p.filename
|
||||
local relative = Path:new(absolute):make_relative(r.filename)
|
||||
assert.are.same(relative, p.filename)
|
||||
end)
|
||||
|
||||
it("can take double separator absolute paths and make them relative to the cwd", function()
|
||||
local p = Path:new { "lua", "plenary", "path.lua" }
|
||||
local absolute = vim.loop.cwd() .. path.sep .. path.sep .. p.filename
|
||||
local relative = Path:new(absolute):make_relative()
|
||||
assert.are.same(relative, p.filename)
|
||||
end)
|
||||
|
||||
it("can take double separator absolute paths and make them relative to a given path", function()
|
||||
local root = path.sep == "\\" and "c:\\" or "/"
|
||||
local r = Path:new { root, "home", "prime" }
|
||||
local p = Path:new { "aoeu", "agen.lua" }
|
||||
local absolute = r.filename .. path.sep .. path.sep .. p.filename
|
||||
local relative = Path:new(absolute):make_relative(r.filename)
|
||||
assert.are.same(relative, p.filename)
|
||||
end)
|
||||
|
||||
it("can take absolute paths and make them relative to a given path with trailing separator", function()
|
||||
local root = path.sep == "\\" and "c:\\" or "/"
|
||||
local r = Path:new { root, "home", "prime" }
|
||||
local p = Path:new { "aoeu", "agen.lua" }
|
||||
local absolute = r.filename .. path.sep .. p.filename
|
||||
local relative = Path:new(absolute):make_relative(r.filename .. path.sep)
|
||||
assert.are.same(relative, p.filename)
|
||||
end)
|
||||
|
||||
it("can take absolute paths and make them relative to the root directory", function()
|
||||
local root = path.sep == "\\" and "c:\\" or "/"
|
||||
local p = Path:new { "home", "prime", "aoeu", "agen.lua" }
|
||||
local absolute = root .. p.filename
|
||||
local relative = Path:new(absolute):make_relative(root)
|
||||
assert.are.same(relative, p.filename)
|
||||
end)
|
||||
|
||||
it("can take absolute paths and make them relative to themselves", function()
|
||||
local root = path.sep == "\\" and "c:\\" or "/"
|
||||
local p = Path:new { root, "home", "prime", "aoeu", "agen.lua" }
|
||||
local relative = Path:new(p.filename):make_relative(p.filename)
|
||||
assert.are.same(relative, ".")
|
||||
end)
|
||||
|
||||
it("should not truncate if path separator is not present after cwd", function()
|
||||
local cwd = "tmp" .. path.sep .. "foo"
|
||||
local p = Path:new { "tmp", "foo_bar", "fileb.lua" }
|
||||
local relative = Path:new(p.filename):make_relative(cwd)
|
||||
assert.are.same(p.filename, relative)
|
||||
end)
|
||||
|
||||
it("should not truncate if path separator is not present after cwd and cwd ends in path sep", function()
|
||||
local cwd = "tmp" .. path.sep .. "foo" .. path.sep
|
||||
local p = Path:new { "tmp", "foo_bar", "fileb.lua" }
|
||||
local relative = Path:new(p.filename):make_relative(cwd)
|
||||
assert.are.same(p.filename, relative)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(":normalize", function()
|
||||
it("can take path that has one character directories", function()
|
||||
local orig = "/home/j/./p//path.lua"
|
||||
local final = Path:new(orig):normalize()
|
||||
assert.are.same(final, "/home/j/p/path.lua")
|
||||
end)
|
||||
|
||||
it("can take paths with double separators change them to single separators", function()
|
||||
local orig = "/lua//plenary/path.lua"
|
||||
local final = Path:new(orig):normalize()
|
||||
assert.are.same(final, "/lua/plenary/path.lua")
|
||||
end)
|
||||
-- this may be redundant since normalize just calls make_relative which is tested above
|
||||
it("can take absolute paths with double seps" .. "and make them relative with single seps", function()
|
||||
local orig = "/lua//plenary/path.lua"
|
||||
local final = Path:new(orig):normalize()
|
||||
assert.are.same(final, "/lua/plenary/path.lua")
|
||||
end)
|
||||
|
||||
it("can remove the .. in paths", function()
|
||||
local orig = "/lua//plenary/path.lua/foo/bar/../.."
|
||||
local final = Path:new(orig):normalize()
|
||||
assert.are.same(final, "/lua/plenary/path.lua")
|
||||
end)
|
||||
|
||||
it("can normalize relative paths", function()
|
||||
assert.are.same(Path:new("lua/plenary/path.lua"):normalize(), "lua/plenary/path.lua")
|
||||
end)
|
||||
|
||||
it("can normalize relative paths containing ..", function()
|
||||
assert.are.same(Path:new("lua/plenary/path.lua/../path.lua"):normalize(), "lua/plenary/path.lua")
|
||||
end)
|
||||
|
||||
it("can normalize relative paths with initial ..", function()
|
||||
local p = Path:new "../lua/plenary/path.lua"
|
||||
p._cwd = "/tmp/lua"
|
||||
assert.are.same("lua/plenary/path.lua", p:normalize())
|
||||
end)
|
||||
|
||||
it("can normalize relative paths to absolute when initial .. count matches cwd parts", function()
|
||||
local p = Path:new "../../tmp/lua/plenary/path.lua"
|
||||
p._cwd = "/tmp/lua"
|
||||
assert.are.same("/tmp/lua/plenary/path.lua", p:normalize())
|
||||
end)
|
||||
|
||||
it("can normalize ~ when file is within home directory (trailing slash)", function()
|
||||
local home = "/home/test/"
|
||||
local p = Path:new { home, "./test_file" }
|
||||
p.path.home = home
|
||||
p._cwd = "/tmp/lua"
|
||||
assert.are.same("~/test_file", p:normalize())
|
||||
end)
|
||||
|
||||
it("can normalize ~ when file is within home directory (no trailing slash)", function()
|
||||
local home = "/home/test"
|
||||
local p = Path:new { home, "./test_file" }
|
||||
p.path.home = home
|
||||
p._cwd = "/tmp/lua"
|
||||
assert.are.same("~/test_file", p:normalize())
|
||||
end)
|
||||
|
||||
it("handles usernames with a dash at the end", function()
|
||||
local home = "/home/mattr-"
|
||||
local p = Path:new { home, "test_file" }
|
||||
p.path.home = home
|
||||
p._cwd = "/tmp/lua"
|
||||
assert.are.same("~/test_file", p:normalize())
|
||||
end)
|
||||
|
||||
it("handles filenames with the same prefix as the home directory", function()
|
||||
local p = Path:new "/home/test.old/test_file"
|
||||
p.path.home = "/home/test"
|
||||
assert.are.same("/home/test.old/test_file", p:normalize())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(":shorten", function()
|
||||
it("can shorten a path", function()
|
||||
local long_path = "/this/is/a/long/path"
|
||||
local short_path = Path:new(long_path):shorten()
|
||||
assert.are.same(short_path, "/t/i/a/l/path")
|
||||
end)
|
||||
|
||||
it("can shorten a path's components to a given length", function()
|
||||
local long_path = "/this/is/a/long/path"
|
||||
local short_path = Path:new(long_path):shorten(2)
|
||||
assert.are.same(short_path, "/th/is/a/lo/path")
|
||||
|
||||
-- without the leading /
|
||||
long_path = "this/is/a/long/path"
|
||||
short_path = Path:new(long_path):shorten(3)
|
||||
assert.are.same(short_path, "thi/is/a/lon/path")
|
||||
|
||||
-- where len is greater than the length of the final component
|
||||
long_path = "this/is/an/extremely/long/path"
|
||||
short_path = Path:new(long_path):shorten(5)
|
||||
assert.are.same(short_path, "this/is/an/extre/long/path")
|
||||
end)
|
||||
|
||||
it("can shorten a path's components when excluding parts", function()
|
||||
local long_path = "/this/is/a/long/path"
|
||||
local short_path = Path:new(long_path):shorten(nil, { 1, -1 })
|
||||
assert.are.same(short_path, "/this/i/a/l/path")
|
||||
|
||||
-- without the leading /
|
||||
long_path = "this/is/a/long/path"
|
||||
short_path = Path:new(long_path):shorten(nil, { 1, -1 })
|
||||
assert.are.same(short_path, "this/i/a/l/path")
|
||||
|
||||
-- where excluding positions greater than the number of parts
|
||||
long_path = "this/is/an/extremely/long/path"
|
||||
short_path = Path:new(long_path):shorten(nil, { 2, 4, 6, 8 })
|
||||
assert.are.same(short_path, "t/is/a/extremely/l/path")
|
||||
|
||||
-- where excluding positions less than the negation of the number of parts
|
||||
long_path = "this/is/an/extremely/long/path"
|
||||
short_path = Path:new(long_path):shorten(nil, { -2, -4, -6, -8 })
|
||||
assert.are.same(short_path, "this/i/an/e/long/p")
|
||||
end)
|
||||
|
||||
it("can shorten a path's components to a given length and exclude positions", function()
|
||||
local long_path = "/this/is/a/long/path"
|
||||
local short_path = Path:new(long_path):shorten(2, { 1, -1 })
|
||||
assert.are.same(short_path, "/this/is/a/lo/path")
|
||||
|
||||
long_path = "this/is/a/long/path"
|
||||
short_path = Path:new(long_path):shorten(3, { 2, -2 })
|
||||
assert.are.same(short_path, "thi/is/a/long/pat")
|
||||
|
||||
long_path = "this/is/an/extremely/long/path"
|
||||
short_path = Path:new(long_path):shorten(5, { 3, -3 })
|
||||
assert.are.same(short_path, "this/is/an/extremely/long/path")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("mkdir / rmdir", function()
|
||||
it("can create and delete directories", function()
|
||||
local p = Path:new "_dir_not_exist"
|
||||
|
||||
p:rmdir()
|
||||
assert(not p:exists(), "After rmdir, it should not exist")
|
||||
|
||||
p:mkdir()
|
||||
assert(p:exists())
|
||||
|
||||
p:rmdir()
|
||||
assert(not p:exists())
|
||||
end)
|
||||
|
||||
it("fails when exists_ok is false", function()
|
||||
local p = Path:new "lua"
|
||||
assert(not pcall(p.mkdir, p, { exists_ok = false }))
|
||||
end)
|
||||
|
||||
it("fails when parents is not passed", function()
|
||||
local p = Path:new("impossible", "dir")
|
||||
assert(not pcall(p.mkdir, p, { parents = false }))
|
||||
assert(not p:exists())
|
||||
end)
|
||||
|
||||
it("can create nested directories", function()
|
||||
local p = Path:new("impossible", "dir")
|
||||
assert(pcall(p.mkdir, p, { parents = true }))
|
||||
assert(p:exists())
|
||||
|
||||
p:rmdir()
|
||||
Path:new("impossible"):rmdir()
|
||||
assert(not p:exists())
|
||||
assert(not Path:new("impossible"):exists())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("touch", function()
|
||||
it("can create and delete new files", function()
|
||||
local p = Path:new "test_file.lua"
|
||||
assert(pcall(p.touch, p))
|
||||
assert(p:exists())
|
||||
|
||||
p:rm()
|
||||
assert(not p:exists())
|
||||
end)
|
||||
|
||||
it("does not effect already created files but updates last access", function()
|
||||
local p = Path:new "README.md"
|
||||
local last_atime = p:_stat().atime.sec
|
||||
local last_mtime = p:_stat().mtime.sec
|
||||
|
||||
local lines = p:readlines()
|
||||
|
||||
assert(pcall(p.touch, p))
|
||||
print(p:_stat().atime.sec > last_atime)
|
||||
print(p:_stat().mtime.sec > last_mtime)
|
||||
assert(p:exists())
|
||||
|
||||
assert.are.same(lines, p:readlines())
|
||||
end)
|
||||
|
||||
it("does not create dirs if nested in none existing dirs and parents not set", function()
|
||||
local p = Path:new { "nested", "nested2", "test_file.lua" }
|
||||
assert(not pcall(p.touch, p, { parents = false }))
|
||||
assert(not p:exists())
|
||||
end)
|
||||
|
||||
it("does create dirs if nested in none existing dirs", function()
|
||||
local p1 = Path:new { "nested", "nested2", "test_file.lua" }
|
||||
local p2 = Path:new { "nested", "asdf", ".hidden" }
|
||||
local d1 = Path:new { "nested", "dir", ".hidden" }
|
||||
assert(pcall(p1.touch, p1, { parents = true }))
|
||||
assert(pcall(p2.touch, p2, { parents = true }))
|
||||
assert(pcall(d1.mkdir, d1, { parents = true }))
|
||||
assert(p1:exists())
|
||||
assert(p2:exists())
|
||||
assert(d1:exists())
|
||||
|
||||
Path:new({ "nested" }):rm { recursive = true }
|
||||
assert(not p1:exists())
|
||||
assert(not p2:exists())
|
||||
assert(not d1:exists())
|
||||
assert(not Path:new({ "nested" }):exists())
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("rename", function()
|
||||
it("can rename a file", function()
|
||||
local p = Path:new "a_random_filename.lua"
|
||||
assert(pcall(p.touch, p))
|
||||
assert(p:exists())
|
||||
|
||||
assert(pcall(p.rename, p, { new_name = "not_a_random_filename.lua" }))
|
||||
assert.are.same("not_a_random_filename.lua", p.filename)
|
||||
|
||||
p:rm()
|
||||
end)
|
||||
|
||||
it("can handle an invalid filename", function()
|
||||
local p = Path:new "some_random_filename.lua"
|
||||
assert(pcall(p.touch, p))
|
||||
assert(p:exists())
|
||||
|
||||
assert(not pcall(p.rename, p, { new_name = "" }))
|
||||
assert(not pcall(p.rename, p))
|
||||
assert.are.same("some_random_filename.lua", p.filename)
|
||||
|
||||
p:rm()
|
||||
end)
|
||||
|
||||
it("can move to parent dir", function()
|
||||
local p = Path:new "some_random_filename.lua"
|
||||
assert(pcall(p.touch, p))
|
||||
assert(p:exists())
|
||||
|
||||
assert(pcall(p.rename, p, { new_name = "../some_random_filename.lua" }))
|
||||
assert.are.same(vim.loop.fs_realpath(Path:new("../some_random_filename.lua"):absolute()), p:absolute())
|
||||
|
||||
p:rm()
|
||||
end)
|
||||
|
||||
it("cannot rename to an existing filename", function()
|
||||
local p1 = Path:new "a_random_filename.lua"
|
||||
local p2 = Path:new "not_a_random_filename.lua"
|
||||
assert(pcall(p1.touch, p1))
|
||||
assert(pcall(p2.touch, p2))
|
||||
assert(p1:exists())
|
||||
assert(p2:exists())
|
||||
|
||||
assert(not pcall(p1.rename, p1, { new_name = "not_a_random_filename.lua" }))
|
||||
assert.are.same(p1.filename, "a_random_filename.lua")
|
||||
|
||||
p1:rm()
|
||||
p2:rm()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("copy", function()
|
||||
it("can copy a file", function()
|
||||
local p1 = Path:new "a_random_filename.rs"
|
||||
local p2 = Path:new "not_a_random_filename.rs"
|
||||
assert(pcall(p1.touch, p1))
|
||||
assert(p1:exists())
|
||||
|
||||
assert(pcall(p1.copy, p1, { destination = "not_a_random_filename.rs" }))
|
||||
assert.are.same(p1.filename, "a_random_filename.rs")
|
||||
assert.are.same(p2.filename, "not_a_random_filename.rs")
|
||||
|
||||
p1:rm()
|
||||
p2:rm()
|
||||
end)
|
||||
|
||||
it("can copy to parent dir", function()
|
||||
local p = Path:new "some_random_filename.lua"
|
||||
assert(pcall(p.touch, p))
|
||||
assert(p:exists())
|
||||
|
||||
assert(pcall(p.copy, p, { destination = "../some_random_filename.lua" }))
|
||||
assert(pcall(p.exists, p))
|
||||
|
||||
p:rm()
|
||||
Path:new(vim.loop.fs_realpath "../some_random_filename.lua"):rm()
|
||||
end)
|
||||
|
||||
it("cannot copy an existing file if override false", function()
|
||||
local p1 = Path:new "a_random_filename.rs"
|
||||
local p2 = Path:new "not_a_random_filename.rs"
|
||||
assert(pcall(p1.touch, p1))
|
||||
assert(pcall(p2.touch, p2))
|
||||
assert(p1:exists())
|
||||
assert(p2:exists())
|
||||
|
||||
assert(pcall(p1.copy, p1, { destination = "not_a_random_filename.rs", override = false }))
|
||||
assert.are.same(p1.filename, "a_random_filename.rs")
|
||||
assert.are.same(p2.filename, "not_a_random_filename.rs")
|
||||
|
||||
p1:rm()
|
||||
p2:rm()
|
||||
end)
|
||||
|
||||
it("fails when copying folders non-recursively", function()
|
||||
local src_dir = Path:new "src"
|
||||
src_dir:mkdir()
|
||||
src_dir:joinpath("file1.lua"):touch()
|
||||
|
||||
local trg_dir = Path:new "trg"
|
||||
local status = xpcall(function()
|
||||
src_dir:copy { destination = trg_dir, recursive = false }
|
||||
end, function() end)
|
||||
-- failed as intended
|
||||
assert(status == false)
|
||||
|
||||
src_dir:rm { recursive = true }
|
||||
end)
|
||||
|
||||
it("can copy directories recursively", function()
|
||||
-- vim.tbl_flatten doesn't work here as copy doesn't return a list
|
||||
local flatten
|
||||
flatten = function(ret, t)
|
||||
for _, v in pairs(t) do
|
||||
if type(v) == "table" then
|
||||
flatten(ret, v)
|
||||
else
|
||||
table.insert(ret, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- setup directories
|
||||
local src_dir = Path:new "src"
|
||||
local trg_dir = Path:new "trg"
|
||||
src_dir:mkdir()
|
||||
|
||||
-- set up sub directory paths for creation and testing
|
||||
local sub_dirs = { "sub_dir1", "sub_dir1/sub_dir2" }
|
||||
local src_dirs = { src_dir }
|
||||
local trg_dirs = { trg_dir }
|
||||
-- {src, trg}_dirs is a table with all directory levels by {src, trg}
|
||||
for _, dir in ipairs(sub_dirs) do
|
||||
table.insert(src_dirs, src_dir:joinpath(dir))
|
||||
table.insert(trg_dirs, trg_dir:joinpath(dir))
|
||||
end
|
||||
|
||||
-- generate {file}_{level}.lua on every directory level in src
|
||||
-- src
|
||||
-- ├── file1_1.lua
|
||||
-- ├── file2_1.lua
|
||||
-- ├── .file3_1.lua
|
||||
-- └── sub_dir1
|
||||
-- ├── file1_2.lua
|
||||
-- ├── file2_2.lua
|
||||
-- ├── .file3_2.lua
|
||||
-- └── sub_dir2
|
||||
-- ├── file1_3.lua
|
||||
-- ├── file2_3.lua
|
||||
-- └── .file3_3.lua
|
||||
local files = { "file1", "file2", ".file3" }
|
||||
for _, file in ipairs(files) do
|
||||
for level, dir in ipairs(src_dirs) do
|
||||
local p = dir:joinpath(file .. "_" .. level .. ".lua")
|
||||
assert(pcall(p.touch, p, { parents = true, exists_ok = true }))
|
||||
assert(p:exists())
|
||||
end
|
||||
end
|
||||
|
||||
for _, hidden in ipairs { true, false } do
|
||||
-- override = `false` should NOT copy as it was copied beforehand
|
||||
for _, override in ipairs { true, false } do
|
||||
local success = src_dir:copy { destination = trg_dir, recursive = true, override = override, hidden = hidden }
|
||||
-- the files are already created because we iterate first with `override=true`
|
||||
-- hence, we test here that no file ops have been committed: any value in tbl of tbls should be false
|
||||
if not override then
|
||||
local file_ops = {}
|
||||
flatten(file_ops, success)
|
||||
-- 3 layers with at at least 2 and at most 3 files (`hidden = true`)
|
||||
local num_files = not hidden and 6 or 9
|
||||
assert(#file_ops == num_files)
|
||||
for _, op in ipairs(file_ops) do
|
||||
assert(op == false)
|
||||
end
|
||||
else
|
||||
for _, file in ipairs(files) do
|
||||
for level, dir in ipairs(trg_dirs) do
|
||||
local p = dir:joinpath(file .. "_" .. level .. ".lua")
|
||||
-- file 3 is hidden
|
||||
if not (file == files[3]) then
|
||||
assert(p:exists())
|
||||
else
|
||||
assert(p:exists() == hidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- only clean up once we tested that we dont want to copy
|
||||
-- if `override=true`
|
||||
if not override then
|
||||
trg_dir:rm { recursive = true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
src_dir:rm { recursive = true }
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("parents", function()
|
||||
it("should extract the ancestors of the path", function()
|
||||
local p = Path:new(vim.loop.cwd())
|
||||
local parents = p:parents()
|
||||
assert(compat.islist(parents))
|
||||
for _, parent in pairs(parents) do
|
||||
assert.are.same(type(parent), "string")
|
||||
end
|
||||
end)
|
||||
it("should return itself if it corresponds to path.root", function()
|
||||
local p = Path:new(Path.path.root(vim.loop.cwd()))
|
||||
assert.are.same(p:parent(), p)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("read parts", function()
|
||||
it("should read head of file", function()
|
||||
local p = Path:new "LICENSE"
|
||||
local data = p:head()
|
||||
local should = [[MIT License
|
||||
|
||||
Copyright (c) 2020 TJ DeVries
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:]]
|
||||
assert.are.same(should, data)
|
||||
end)
|
||||
|
||||
it("should read the first line of file", function()
|
||||
local p = Path:new "LICENSE"
|
||||
local data = p:head(1)
|
||||
local should = [[MIT License]]
|
||||
assert.are.same(should, data)
|
||||
end)
|
||||
|
||||
it("head should max read whole file", function()
|
||||
local p = Path:new "LICENSE"
|
||||
local data = p:head(1000)
|
||||
local should = [[MIT License
|
||||
|
||||
Copyright (c) 2020 TJ DeVries
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.]]
|
||||
assert.are.same(should, data)
|
||||
end)
|
||||
|
||||
it("should read tail of file", function()
|
||||
local p = Path:new "LICENSE"
|
||||
local data = p:tail()
|
||||
local should = [[The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.]]
|
||||
assert.are.same(should, data)
|
||||
end)
|
||||
|
||||
it("should read the last line of file", function()
|
||||
local p = Path:new "LICENSE"
|
||||
local data = p:tail(1)
|
||||
local should = [[SOFTWARE.]]
|
||||
assert.are.same(should, data)
|
||||
end)
|
||||
|
||||
it("tail should max read whole file", function()
|
||||
local p = Path:new "LICENSE"
|
||||
local data = p:tail(1000)
|
||||
local should = [[MIT License
|
||||
|
||||
Copyright (c) 2020 TJ DeVries
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.]]
|
||||
assert.are.same(should, data)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("readbyterange", function()
|
||||
it("should read bytes at given offset", function()
|
||||
local p = Path:new "LICENSE"
|
||||
local data = p:readbyterange(13, 10)
|
||||
local should = "Copyright "
|
||||
assert.are.same(should, data)
|
||||
end)
|
||||
|
||||
it("supports negative offset", function()
|
||||
local p = Path:new "LICENSE"
|
||||
local data = p:readbyterange(-10, 10)
|
||||
local should = "SOFTWARE.\n"
|
||||
assert.are.same(should, data)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe(":find_upwards", function()
|
||||
it("finds files that exist", function()
|
||||
local p = Path:new(debug.getinfo(1, "S").source:sub(2))
|
||||
local found = p:find_upwards "README.md"
|
||||
assert.are.same(found:absolute(), Path:new("README.md"):absolute())
|
||||
end)
|
||||
|
||||
it("finds files that exist at the root", function()
|
||||
local p = Path:new(debug.getinfo(1, "S").source:sub(2))
|
||||
|
||||
-- Temporarily set path.root to the root of this repository
|
||||
local root = p.path.root
|
||||
p.path.root = function(_)
|
||||
return p:parent():parent():parent().filename
|
||||
end
|
||||
|
||||
local found = p:find_upwards "README.md"
|
||||
assert.are.same(found:absolute(), Path:new("README.md"):absolute())
|
||||
p.path.root = root
|
||||
end)
|
||||
|
||||
it("returns nil if no file is found", function()
|
||||
local p = Path:new(debug.getinfo(1, "S").source:sub(2))
|
||||
local found = p:find_upwards "MISSINGNO.md"
|
||||
assert.are.same(found, nil)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
-- function TestPath:testIsDir()
|
||||
-- end
|
||||
|
||||
-- function TestPath:testCanBeCalledWithoutColon()
|
||||
-- end
|
||||
|
||||
-- -- @sideeffect
|
||||
-- function TestPath:testMkdir()
|
||||
-- end
|
|
@ -0,0 +1,42 @@
|
|||
local scandir = require "plenary.scandir"
|
||||
local Path = require "plenary.path"
|
||||
|
||||
local eq = assert.are.same
|
||||
|
||||
describe("plenary.popup", function()
|
||||
local allowed_imports = {
|
||||
["plenary.window"] = true,
|
||||
["plenary.window.border"] = true,
|
||||
}
|
||||
|
||||
local matches_any_import = function(line)
|
||||
local matched = string.match(line, [[require."(.*)"]])
|
||||
if matched and not vim.startswith(matched, "plenary.popup") then
|
||||
if not allowed_imports[matched] then
|
||||
return true, string.format("Not an allowed import for popup: %s. Line: %s", matched, line)
|
||||
end
|
||||
end
|
||||
|
||||
return false, nil
|
||||
end
|
||||
|
||||
-- Tests to make sure that we're matching both types of requires
|
||||
it("should match these kinds of patterns", function()
|
||||
eq(true, matches_any_import [[local x = require "plenary.other"]])
|
||||
eq(true, matches_any_import [[local x = require("plenary.module").something]])
|
||||
end)
|
||||
|
||||
it("must not require anything other than Window and Border from plenary", function()
|
||||
local result = scandir.scan_dir("./lua/plenary/popup", { depth = 1 })
|
||||
|
||||
for _, file in ipairs(result) do
|
||||
local popup_file = Path:new(file)
|
||||
local lines = popup_file:readlines()
|
||||
|
||||
for _, line in ipairs(lines) do
|
||||
local matches, msg = matches_any_import(line)
|
||||
eq(false, matches, msg)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,162 @@
|
|||
local popup = require "plenary.popup"
|
||||
|
||||
local eq = assert.are.same
|
||||
|
||||
describe("plenary.popup", function()
|
||||
before_each(function()
|
||||
vim.cmd [[highlight PopupColor1 ctermbg=lightblue guibg=lightblue]]
|
||||
vim.cmd [[highlight PopupColor2 ctermbg=lightcyan guibg=lightcyan]]
|
||||
end)
|
||||
|
||||
-- TODO: Probably want to clear all the popups between iterations
|
||||
-- after_each(function() end)
|
||||
|
||||
it("can create a very simple window", function()
|
||||
local win_id = popup.create("hello there", {
|
||||
line = 1,
|
||||
col = 1,
|
||||
width = 20,
|
||||
})
|
||||
|
||||
local win_config = vim.api.nvim_win_get_config(win_id)
|
||||
eq(20, win_config.width)
|
||||
end)
|
||||
|
||||
it("can create a very simple window after 'set nomodifiable'", function()
|
||||
vim.o.modifiable = false
|
||||
local win_id = popup.create("hello there", {
|
||||
line = 1,
|
||||
col = 1,
|
||||
width = 20,
|
||||
})
|
||||
|
||||
local win_config = vim.api.nvim_win_get_config(win_id)
|
||||
eq(20, win_config.width)
|
||||
vim.o.modifiable = true
|
||||
end)
|
||||
|
||||
it("can apply a highlight", function()
|
||||
local win_id = popup.create("hello there", {
|
||||
highlight = "PopupColor1",
|
||||
})
|
||||
|
||||
eq("Normal:PopupColor1,EndOfBuffer:PopupColor1", vim.api.nvim_win_get_option(win_id, "winhl"))
|
||||
end)
|
||||
|
||||
it("can create a border", function()
|
||||
local win_id, config = popup.create("hello border", {
|
||||
line = 2,
|
||||
col = 3,
|
||||
border = {},
|
||||
})
|
||||
|
||||
eq(true, vim.api.nvim_win_is_valid(win_id))
|
||||
|
||||
local border_id = config.border.win_id
|
||||
assert(border_id, "Has a border win id")
|
||||
eq(true, vim.api.nvim_win_is_valid(border_id))
|
||||
end)
|
||||
|
||||
it("can apply a border highlight", function()
|
||||
local _, opts = popup.create("hello there", {
|
||||
border = true,
|
||||
borderhighlight = "PopupColor2",
|
||||
})
|
||||
|
||||
local border_win_id = opts.border.win_id
|
||||
eq("Normal:PopupColor2", vim.api.nvim_win_get_option(border_win_id, "winhl"))
|
||||
end)
|
||||
|
||||
it("can ignore border highlight with no border", function()
|
||||
local _ = popup.create("hello there", {
|
||||
border = false,
|
||||
borderhighlight = "PopupColor3",
|
||||
})
|
||||
end)
|
||||
|
||||
it("can do basic padding", function()
|
||||
local win_id = popup.create("12345", {
|
||||
line = 1,
|
||||
col = 1,
|
||||
padding = {},
|
||||
})
|
||||
|
||||
local bufnr = vim.api.nvim_win_get_buf(win_id)
|
||||
eq({ "", " 12345 ", "" }, vim.api.nvim_buf_get_lines(bufnr, 0, -1, false))
|
||||
end)
|
||||
|
||||
it("can do padding and border", function()
|
||||
local win_id, config = popup.create("hello border", {
|
||||
line = 2,
|
||||
col = 2,
|
||||
border = {},
|
||||
padding = {},
|
||||
})
|
||||
|
||||
local bufnr = vim.api.nvim_win_get_buf(win_id)
|
||||
eq({ "", " hello border ", "" }, vim.api.nvim_buf_get_lines(bufnr, 0, -1, false))
|
||||
|
||||
local border_id = config.border.win_id
|
||||
assert(border_id, "Has a border win id")
|
||||
eq(true, vim.api.nvim_win_is_valid(border_id))
|
||||
end)
|
||||
|
||||
describe("borderchars", function()
|
||||
local test_border = function(name, borderchars, expected)
|
||||
it(name, function()
|
||||
local _, config = popup.create("all the plus signs", {
|
||||
line = 8,
|
||||
col = 55,
|
||||
padding = { 0, 3, 0, 3 },
|
||||
borderchars = borderchars,
|
||||
})
|
||||
|
||||
local border_id = config.border.win_id
|
||||
local border_bufnr = vim.api.nvim_win_get_buf(border_id)
|
||||
eq(expected, vim.api.nvim_buf_get_lines(border_bufnr, 0, -1, false))
|
||||
end)
|
||||
end
|
||||
|
||||
test_border("can support multiple border patterns", { "+" }, {
|
||||
"++++++++++++++++++++++++++",
|
||||
"+ +",
|
||||
"++++++++++++++++++++++++++",
|
||||
})
|
||||
|
||||
test_border("can support multiple patterns inside the borderchars", { "-", "+" }, {
|
||||
"+------------------------+",
|
||||
"- -",
|
||||
"+------------------------+",
|
||||
})
|
||||
end)
|
||||
|
||||
describe("what", function()
|
||||
it("can be an existing bufnr", function()
|
||||
local bufnr = vim.api.nvim_create_buf(false, false)
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, { "pass bufnr 1", "pass bufnr 2" })
|
||||
local win_id = popup.create(bufnr, {
|
||||
line = 8,
|
||||
col = 55,
|
||||
minwidth = 20,
|
||||
})
|
||||
|
||||
eq(bufnr, vim.api.nvim_win_get_buf(win_id))
|
||||
eq({ "pass bufnr 1", "pass bufnr 2" }, vim.api.nvim_buf_get_lines(bufnr, 0, -1, false))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("cursor", function()
|
||||
pending("not yet tested", function()
|
||||
popup.create({ "option 1", "option 2" }, {
|
||||
line = "cursor+2",
|
||||
col = "cursor+2",
|
||||
border = { 1, 1, 1, 1 },
|
||||
enter = true,
|
||||
cursorline = true,
|
||||
callback = function(win_id, sel)
|
||||
print(sel)
|
||||
end,
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,60 @@
|
|||
local List = require "plenary.collections.py_list"
|
||||
local Iter = require "plenary.iterators"
|
||||
|
||||
describe("List", function()
|
||||
it("should detect whether a value is an instance of List", function()
|
||||
local l = List { 1, 2 }
|
||||
local n = 42
|
||||
assert(List.is_list(l))
|
||||
assert(not List.is_list(n))
|
||||
end)
|
||||
it("should be equal if all elements are equal", function()
|
||||
local l1 = List { 1, 2, 3 }
|
||||
local l2 = List { 1, 2, 3 }
|
||||
local l3 = List { 4, 5, 6 }
|
||||
assert.are.equal(l1, l2)
|
||||
assert.are_not.equal(l1, l3)
|
||||
end)
|
||||
it("can be concatenated to other list-like tables", function()
|
||||
local l1 = List { 1, 2, 3 } .. { 4 }
|
||||
assert.are.equal(l1, List { 1, 2, 3, 4 })
|
||||
end)
|
||||
it("can create a copy of itself with equal elements", function()
|
||||
local l1 = List { 1, 2, 3 }
|
||||
local l2 = l1:copy()
|
||||
assert.are.equal(l1, l2)
|
||||
end)
|
||||
it("can create a slice between two indices", function()
|
||||
local l1 = List { 1, 2, 3, 4 }
|
||||
local l2 = l1:slice(2, 4)
|
||||
assert.are.equal(l2, List { 2, 3, 4 })
|
||||
end)
|
||||
it("can reverse itself in place", function()
|
||||
local l = List { 1, 2, 3, 4 }
|
||||
l:reverse()
|
||||
assert.are.equal(l, List { 4, 3, 2, 1 })
|
||||
end)
|
||||
it("can push elements to itself", function()
|
||||
local l = List { 1, 2, 3 }
|
||||
l:push(4)
|
||||
assert.are.equal(l, List { 1, 2, 3, 4 })
|
||||
end)
|
||||
it("can pop the n-th element from itself (last one by default)", function()
|
||||
local l = List { 1, 2, 3, 4 }
|
||||
local n = l:pop()
|
||||
assert.are.equal(l, List { 1, 2, 3 })
|
||||
assert.are.equal(n, 4)
|
||||
end)
|
||||
it("can create a list from an iterable", function()
|
||||
local l = List.from_iter(Iter.range(5, 10))
|
||||
assert.are.equal(l, List { 5, 6, 7, 8, 9, 10 })
|
||||
end)
|
||||
it("can be partitioned based on a predicate", function()
|
||||
local l = List.from_iter(Iter.range(1, 10))
|
||||
local evens, odds = l:iter():partition(function(e)
|
||||
return e % 2 == 0
|
||||
end)
|
||||
assert.are.equal(evens, List { 2, 4, 6, 8, 10 })
|
||||
assert.are.equal(odds, List { 1, 3, 5, 7, 9 })
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,33 @@
|
|||
local rotate = require("plenary.vararg").rotate
|
||||
|
||||
local eq = function(a, b)
|
||||
assert.is["true"](vim.deep_equal(a, b), true)
|
||||
end
|
||||
|
||||
describe("rotate", function()
|
||||
it("should return as many values, as the first argument", function()
|
||||
local args = {}
|
||||
for _ = 0, 20 do
|
||||
local n = select("#", unpack(args))
|
||||
assert.is.equal(n, select("#", rotate(n, unpack(args))))
|
||||
args[#args + 1] = n
|
||||
end
|
||||
end)
|
||||
|
||||
it("should rotate varargs", function()
|
||||
eq({ rotate(3, 1, 2, 3) }, { 2, 3, 1 })
|
||||
eq({ rotate(9, 1, 2, 3, 4, 5, 6, 7, 8, 9) }, { 2, 3, 4, 5, 6, 7, 8, 9, 1 })
|
||||
end)
|
||||
|
||||
it("should rotate zero", function()
|
||||
assert.is.equal(0, select("#", rotate(0)))
|
||||
end)
|
||||
|
||||
it("should rotate none", function()
|
||||
assert.is.equal(0, select("#", rotate()))
|
||||
end)
|
||||
|
||||
it("should rotate one", function()
|
||||
eq({ rotate(1, 1) }, { 1 })
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,249 @@
|
|||
local scan = require "plenary.scandir"
|
||||
local mock = require "luassert.mock"
|
||||
local stub = require "luassert.stub"
|
||||
local eq = assert.are.same
|
||||
|
||||
local contains = function(tbl, str)
|
||||
for _, v in ipairs(tbl) do
|
||||
if v == str then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local contains_match = function(tbl, str)
|
||||
for _, v in ipairs(tbl) do
|
||||
if v:match(str) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
describe("scandir", function()
|
||||
describe("can list all files recursive", function()
|
||||
it("with cwd", function()
|
||||
local dirs = scan.scan_dir "."
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./README.md"))
|
||||
eq(true, contains(dirs, "./LICENSE"))
|
||||
eq(true, contains(dirs, "./lua/plenary/job.lua"))
|
||||
eq(false, contains(dirs, "./asdf/asdf/adsf.lua"))
|
||||
end)
|
||||
|
||||
it("and callback gets called for each entry", function()
|
||||
local count = 0
|
||||
local dirs = scan.scan_dir(".", {
|
||||
on_insert = function()
|
||||
count = count + 1
|
||||
end,
|
||||
})
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./README.md"))
|
||||
eq(true, contains(dirs, "./LICENSE"))
|
||||
eq(true, contains(dirs, "./lua/plenary/job.lua"))
|
||||
eq(false, contains(dirs, "./asdf/asdf/adsf.lua"))
|
||||
eq(count, #dirs)
|
||||
end)
|
||||
|
||||
it("with multiple paths", function()
|
||||
local dirs = scan.scan_dir { "./lua", "./tests" }
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./lua/say.lua"))
|
||||
eq(true, contains(dirs, "./lua/plenary/job.lua"))
|
||||
eq(true, contains(dirs, "./tests/plenary/scandir_spec.lua"))
|
||||
eq(false, contains(dirs, "./asdf/asdf/adsf.lua"))
|
||||
end)
|
||||
|
||||
it("with hidden files", function()
|
||||
local dirs = scan.scan_dir(".", { hidden = true })
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./README.md"))
|
||||
eq(true, contains(dirs, "./lua/plenary/job.lua"))
|
||||
eq(true, contains(dirs, "./.gitignore"))
|
||||
eq(false, contains(dirs, "./asdf/asdf/adsf.lua"))
|
||||
end)
|
||||
|
||||
it("with add directories", function()
|
||||
local dirs = scan.scan_dir(".", { add_dirs = true })
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./README.md"))
|
||||
eq(true, contains(dirs, "./lua/plenary/job.lua"))
|
||||
eq(true, contains(dirs, "./lua"))
|
||||
eq(true, contains(dirs, "./tests"))
|
||||
eq(false, contains(dirs, "./asdf/asdf/adsf.lua"))
|
||||
end)
|
||||
|
||||
it("with only directories", function()
|
||||
local dirs = scan.scan_dir(".", { only_dirs = true })
|
||||
eq("table", type(dirs))
|
||||
eq(false, contains(dirs, "./README.md"))
|
||||
eq(false, contains(dirs, "./lua/plenary/job.lua"))
|
||||
eq(true, contains(dirs, "./lua"))
|
||||
eq(true, contains(dirs, "./tests"))
|
||||
eq(false, contains(dirs, "./asdf/asdf/adsf.lua"))
|
||||
end)
|
||||
|
||||
it("until depth 1 is reached", function()
|
||||
local dirs = scan.scan_dir(".", { depth = 1 })
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./README.md"))
|
||||
eq(false, contains(dirs, "./lua"))
|
||||
eq(false, contains(dirs, "./lua/say.lua"))
|
||||
eq(false, contains(dirs, "./lua/plenary/job.lua"))
|
||||
eq(false, contains(dirs, "./asdf/asdf/adsf.lua"))
|
||||
end)
|
||||
|
||||
it("until depth 1 is reached and with directories", function()
|
||||
local dirs = scan.scan_dir(".", { depth = 1, add_dirs = true })
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./README.md"))
|
||||
eq(true, contains(dirs, "./lua"))
|
||||
eq(false, contains(dirs, "./lua/say.lua"))
|
||||
eq(false, contains(dirs, "./lua/plenary/job.lua"))
|
||||
eq(false, contains(dirs, "./asdf/asdf/adsf.lua"))
|
||||
end)
|
||||
|
||||
it("until depth 2 is reached", function()
|
||||
local dirs = scan.scan_dir(".", { depth = 2 })
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./README.md"))
|
||||
eq(true, contains(dirs, "./lua/say.lua"))
|
||||
eq(false, contains(dirs, "./lua/plenary/job.lua"))
|
||||
eq(false, contains(dirs, "./asdf/asdf/adsf.lua"))
|
||||
end)
|
||||
|
||||
it("with respect_gitignore", function()
|
||||
vim.cmd ":silent !touch lua/test.so"
|
||||
local dirs = scan.scan_dir(".", { respect_gitignore = true })
|
||||
vim.cmd ":silent !rm lua/test.so"
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./README.md"))
|
||||
eq(true, contains(dirs, "./LICENSE"))
|
||||
eq(true, contains(dirs, "./lua/plenary/job.lua"))
|
||||
eq(false, contains(dirs, "./lua/test.so"))
|
||||
eq(false, contains(dirs, "./asdf/asdf/adsf.lua"))
|
||||
end)
|
||||
|
||||
it("with search pattern", function()
|
||||
local dirs = scan.scan_dir(".", { search_pattern = "filetype" })
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./scripts/update_filetypes_from_github.lua"))
|
||||
eq(true, contains(dirs, "./lua/plenary/filetype.lua"))
|
||||
eq(true, contains(dirs, "./tests/plenary/filetype_spec.lua"))
|
||||
eq(true, contains(dirs, "./data/plenary/filetypes/base.lua"))
|
||||
eq(true, contains(dirs, "./data/plenary/filetypes/builtin.lua"))
|
||||
eq(false, contains(dirs, "./README.md"))
|
||||
end)
|
||||
|
||||
it("with callback search pattern", function()
|
||||
local dirs = scan.scan_dir(".", {
|
||||
search_pattern = function(entry)
|
||||
return entry:match "filetype"
|
||||
end,
|
||||
})
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains(dirs, "./scripts/update_filetypes_from_github.lua"))
|
||||
eq(true, contains(dirs, "./lua/plenary/filetype.lua"))
|
||||
eq(true, contains(dirs, "./tests/plenary/filetype_spec.lua"))
|
||||
eq(true, contains(dirs, "./data/plenary/filetypes/base.lua"))
|
||||
eq(true, contains(dirs, "./data/plenary/filetypes/builtin.lua"))
|
||||
eq(false, contains(dirs, "./README.md"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("gitignore", function()
|
||||
local Path = require "plenary.path"
|
||||
local mock_path, mock_gitignore
|
||||
before_each(function()
|
||||
mock_path = {
|
||||
exists = stub.new().returns(true),
|
||||
iter = function()
|
||||
local i = 0
|
||||
local n = table.getn(mock_gitignore)
|
||||
return function()
|
||||
i = i + 1
|
||||
if i <= n then
|
||||
return mock_gitignore[i]
|
||||
end
|
||||
end
|
||||
end,
|
||||
}
|
||||
Path.new = stub.new().returns(mock_path)
|
||||
end)
|
||||
after_each(function()
|
||||
Path.new:revert()
|
||||
end)
|
||||
|
||||
describe("ignores path", function()
|
||||
it("when path matches pattern exactly", function()
|
||||
mock_gitignore = { "ignored.txt" }
|
||||
local should_add = scan.__make_gitignore { "path" }
|
||||
eq(false, should_add({ "path" }, "./path/ignored.txt"))
|
||||
end)
|
||||
it("when path matches * pattern", function()
|
||||
mock_gitignore = { "*.txt" }
|
||||
local should_add = scan.__make_gitignore { "path" }
|
||||
eq(false, should_add({ "path" }, "./path/dir/ignored.txt"))
|
||||
end)
|
||||
it("when path matches leading ** pattern", function()
|
||||
mock_gitignore = { "**/ignored.txt" }
|
||||
local should_add = scan.__make_gitignore { "path" }
|
||||
eq(false, should_add({ "path" }, "./path/dir/subdir/ignored.txt"))
|
||||
end)
|
||||
it("when path matches trailing ** pattern", function()
|
||||
mock_gitignore = { "/dir/**" }
|
||||
local should_add = scan.__make_gitignore { "path" }
|
||||
eq(false, should_add({ "path" }, "./path/dir/subdir/ignored.txt"))
|
||||
end)
|
||||
it("when path matches ? pattern", function()
|
||||
mock_gitignore = { "ignore?.txt" }
|
||||
local should_add = scan.__make_gitignore { "path" }
|
||||
eq(false, should_add({ "path" }, "./path/ignored.txt"))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("does not ignore path", function()
|
||||
it("when path does not match", function()
|
||||
mock_gitignore = { "ignored.txt" }
|
||||
local should_add = scan.__make_gitignore { "path" }
|
||||
eq(true, should_add({ "path" }, "./path/ok.txt"))
|
||||
end)
|
||||
it("when path is negated", function()
|
||||
mock_gitignore = { "*.txt", "!ok.txt" }
|
||||
local should_add = scan.__make_gitignore { "path" }
|
||||
eq(true, should_add({ "path" }, "./path/ok.txt"))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("ls", function()
|
||||
it("works for cwd", function()
|
||||
local dirs = scan.ls "."
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains_match(dirs, "LICENSE"))
|
||||
eq(true, contains_match(dirs, "README.md"))
|
||||
eq(true, contains_match(dirs, "lua"))
|
||||
eq(false, contains_match(dirs, "%.git$"))
|
||||
end)
|
||||
|
||||
it("works for another directory", function()
|
||||
local dirs = scan.ls "./lua"
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains_match(dirs, "luassert"))
|
||||
eq(true, contains_match(dirs, "plenary"))
|
||||
eq(true, contains_match(dirs, "say.lua"))
|
||||
eq(false, contains_match(dirs, "README.md"))
|
||||
end)
|
||||
|
||||
it("works with opts.hidden for cwd", function()
|
||||
local dirs = scan.ls(".", { hidden = true })
|
||||
eq("table", type(dirs))
|
||||
eq(true, contains_match(dirs, "README.md"))
|
||||
eq(true, contains_match(dirs, "LICENSE"))
|
||||
eq(true, contains_match(dirs, "lua"))
|
||||
eq(true, contains_match(dirs, "%.git$"))
|
||||
end)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,206 @@
|
|||
local eq = assert.are.same
|
||||
|
||||
local tester_function = function()
|
||||
error(7)
|
||||
end
|
||||
|
||||
describe("busted specs", function()
|
||||
describe("nested", function()
|
||||
it("should work", function()
|
||||
assert(true)
|
||||
end)
|
||||
end)
|
||||
|
||||
it("should not nest", function()
|
||||
assert(true)
|
||||
end)
|
||||
|
||||
it("should not fail unless we unpcall this", function()
|
||||
pcall(tester_function)
|
||||
end)
|
||||
|
||||
pending("other thing pending", function()
|
||||
error()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("before each", function()
|
||||
local a = 2
|
||||
local b = 3
|
||||
it("is not cleared", function()
|
||||
eq(2, a)
|
||||
eq(3, b)
|
||||
a = a + 1
|
||||
b = b + 1
|
||||
end)
|
||||
describe("nested", function()
|
||||
before_each(function()
|
||||
a = 0
|
||||
end)
|
||||
it("should clear a but not b", function()
|
||||
eq(0, a)
|
||||
eq(4, b)
|
||||
a = a + 1
|
||||
b = b + 1
|
||||
end)
|
||||
describe("nested nested", function()
|
||||
before_each(function()
|
||||
b = 0
|
||||
end)
|
||||
it("should clear b as well", function()
|
||||
eq(0, a)
|
||||
eq(0, b)
|
||||
a = a + 1
|
||||
b = b + 1
|
||||
end)
|
||||
end)
|
||||
it("should only clear a", function()
|
||||
eq(0, a)
|
||||
eq(1, b)
|
||||
a = a + 1
|
||||
b = b + 1
|
||||
end)
|
||||
end)
|
||||
it("should clear nothing", function()
|
||||
eq(1, a)
|
||||
eq(2, b)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("before_each ordering", function()
|
||||
local order = ""
|
||||
before_each(function()
|
||||
order = order .. "1,"
|
||||
end)
|
||||
before_each(function()
|
||||
order = order .. "2,"
|
||||
end)
|
||||
describe("nested 1 deep", function()
|
||||
before_each(function()
|
||||
order = order .. "3,"
|
||||
end)
|
||||
before_each(function()
|
||||
order = order .. "4,"
|
||||
end)
|
||||
describe("nested 2 deep", function()
|
||||
before_each(function()
|
||||
order = order .. "5,"
|
||||
end)
|
||||
it("runs before_each`s in order", function()
|
||||
eq("1,2,3,4,5,", order)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe("adjacent nested 1 deep", function()
|
||||
before_each(function()
|
||||
order = order .. "3a,"
|
||||
end)
|
||||
before_each(function()
|
||||
order = order .. "4a,"
|
||||
end)
|
||||
describe("nested 2 deep", function()
|
||||
before_each(function()
|
||||
order = order .. "5a,"
|
||||
end)
|
||||
it("runs before_each`s in order", function()
|
||||
eq("1,2,3,4,5,1,2,3a,4a,5a,", order)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("after each", function()
|
||||
local a = 2
|
||||
local b = 3
|
||||
it("is not cleared", function()
|
||||
eq(2, a)
|
||||
eq(3, b)
|
||||
a = a + 1
|
||||
b = b + 1
|
||||
end)
|
||||
describe("nested", function()
|
||||
after_each(function()
|
||||
a = 0
|
||||
end)
|
||||
it("should not clear any at this point", function()
|
||||
eq(3, a)
|
||||
eq(4, b)
|
||||
a = a + 1
|
||||
b = b + 1
|
||||
end)
|
||||
describe("nested nested", function()
|
||||
after_each(function()
|
||||
b = 0
|
||||
end)
|
||||
it("should have cleared a", function()
|
||||
eq(0, a)
|
||||
eq(5, b)
|
||||
a = a + 1
|
||||
b = b + 1
|
||||
end)
|
||||
end)
|
||||
it("should have cleared a and b", function()
|
||||
eq(0, a)
|
||||
eq(0, b)
|
||||
a = a + 1
|
||||
b = b + 1
|
||||
end)
|
||||
end)
|
||||
it("should only have cleared a", function()
|
||||
eq(0, a)
|
||||
eq(1, b)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("after_each ordering", function()
|
||||
local order = ""
|
||||
describe("1st describe having after_each", function()
|
||||
after_each(function()
|
||||
order = order .. "1,"
|
||||
end)
|
||||
after_each(function()
|
||||
order = order .. "2,"
|
||||
end)
|
||||
describe("nested 1 deep", function()
|
||||
after_each(function()
|
||||
order = order .. "3,"
|
||||
end)
|
||||
after_each(function()
|
||||
order = order .. "4,"
|
||||
end)
|
||||
describe("nested 2 deep", function()
|
||||
after_each(function()
|
||||
order = order .. "5,"
|
||||
end)
|
||||
it("a test to trigger the after_each`s", function()
|
||||
assert(true)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
describe("adjacent nested 1 deep", function()
|
||||
after_each(function()
|
||||
order = order .. "3a,"
|
||||
end)
|
||||
after_each(function()
|
||||
order = order .. "4a,"
|
||||
end)
|
||||
describe("nested 2 deep", function()
|
||||
after_each(function()
|
||||
order = order .. "5a,"
|
||||
end)
|
||||
it("a test to trigger the adjacent after_each`s", function()
|
||||
assert(true)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
it("ran after_each`s in order", function()
|
||||
eq("1,2,3,4,5,1,2,3a,4a,5a,", order)
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("another top level describe test", function()
|
||||
it("should work", function()
|
||||
eq(1, 1)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,305 @@
|
|||
local strings = require "plenary.strings"
|
||||
local eq = assert.are.same
|
||||
|
||||
describe("strings", function()
|
||||
describe("strdisplaywidth", function()
|
||||
for _, case in ipairs {
|
||||
{ str = "abcde", expected = { single = 5, double = 5 } },
|
||||
-- This space below is a tab (U+0009)
|
||||
{ str = "abc de", expected = { single = 10, double = 10 } },
|
||||
{ str = "アイウエオ", expected = { single = 10, double = 10 } },
|
||||
{ str = "├─┤", expected = { single = 3, double = 6 } },
|
||||
{ str = 123, expected = { single = 3, double = 3 } },
|
||||
} do
|
||||
for _, ambiwidth in ipairs { "single", "double" } do
|
||||
local item = type(case.str) == "string" and '"%s"' or "%s"
|
||||
local msg = ("ambiwidth = %s, " .. item .. " -> %d"):format(ambiwidth, case.str, case.expected[ambiwidth])
|
||||
local original = vim.o.ambiwidth
|
||||
vim.o.ambiwidth = ambiwidth
|
||||
it("lua: " .. msg, function()
|
||||
eq(case.expected[ambiwidth], strings.strdisplaywidth(case.str))
|
||||
end)
|
||||
it("vim: " .. msg, function()
|
||||
eq(case.expected[ambiwidth], vim.fn.strdisplaywidth(case.str))
|
||||
end)
|
||||
vim.o.ambiwidth = original
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
describe("strcharpart", function()
|
||||
for _, case in ipairs {
|
||||
{ args = { "abcde", 2 }, expected = "cde" },
|
||||
{ args = { "abcde", 2, 2 }, expected = "cd" },
|
||||
{ args = { "アイウエオ", 2, 2 }, expected = "ウエ" },
|
||||
{ args = { "├───┤", 2, 2 }, expected = "──" },
|
||||
} do
|
||||
local msg = ('("%s", %d, %s) -> "%s"'):format(case.args[1], case.args[2], tostring(case.args[3]), case.expected)
|
||||
it("lua: " .. msg, function()
|
||||
eq(case.expected, strings.strcharpart(unpack(case.args)))
|
||||
end)
|
||||
it("vim: " .. msg, function()
|
||||
eq(case.expected, vim.fn.strcharpart(unpack(case.args)))
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
describe("truncate", function()
|
||||
for _, case in ipairs {
|
||||
-- truncations from the right
|
||||
{ args = { "abcde", 6, nil, 1 }, expected = { single = "abcde", double = "abcde" } },
|
||||
{ args = { "abcde", 5, nil, 1 }, expected = { single = "abcde", double = "abcde" } },
|
||||
{ args = { "abcde", 4, nil, 1 }, expected = { single = "abc…", double = "ab…" } },
|
||||
{
|
||||
args = { "アイウエオ", 11, nil, 1 },
|
||||
expected = { single = "アイウエオ", double = "アイウエオ" },
|
||||
},
|
||||
{
|
||||
args = { "アイウエオ", 10, nil, 1 },
|
||||
expected = { single = "アイウエオ", double = "アイウエオ" },
|
||||
},
|
||||
{
|
||||
args = { "アイウエオ", 9, nil, 1 },
|
||||
expected = { single = "アイウエ…", double = "アイウ…" },
|
||||
},
|
||||
{ args = { "アイウエオ", 8, nil, 1 }, expected = { single = "アイウ…", double = "アイウ…" } },
|
||||
{ args = { "├─┤", 7, nil, 1 }, expected = { single = "├─┤", double = "├─┤" } },
|
||||
{ args = { "├─┤", 6, nil, 1 }, expected = { single = "├─┤", double = "├─┤" } },
|
||||
{ args = { "├─┤", 5, nil, 1 }, expected = { single = "├─┤", double = "├…" } },
|
||||
{ args = { "├─┤", 4, nil, 1 }, expected = { single = "├─┤", double = "├…" } },
|
||||
{ args = { "├─┤", 3, nil, 1 }, expected = { single = "├─┤", double = "…" } },
|
||||
{ args = { "├─┤", 2, nil, 1 }, expected = { single = "├…", double = "…" } },
|
||||
-- truncations from the left
|
||||
{ args = { "abcde", 6, nil, -1 }, expected = { single = "abcde", double = "abcde" } },
|
||||
{ args = { "abcde", 5, nil, -1 }, expected = { single = "abcde", double = "abcde" } },
|
||||
{ args = { "abcde", 4, nil, -1 }, expected = { single = "…cde", double = "…de" } },
|
||||
{
|
||||
args = { "アイウエオ", 11, nil, -1 },
|
||||
expected = { single = "アイウエオ", double = "アイウエオ" },
|
||||
},
|
||||
{
|
||||
args = { "アイウエオ", 10, nil, -1 },
|
||||
expected = { single = "アイウエオ", double = "アイウエオ" },
|
||||
},
|
||||
{
|
||||
args = { "アイウエオ", 9, nil, -1 },
|
||||
expected = { single = "…イウエオ", double = "…ウエオ" },
|
||||
},
|
||||
{ args = { "アイウエオ", 8, nil, -1 }, expected = { single = "…ウエオ", double = "…ウエオ" } },
|
||||
{ args = { "├─┤", 7, nil, -1 }, expected = { single = "├─┤", double = "├─┤" } },
|
||||
{ args = { "├─┤", 6, nil, -1 }, expected = { single = "├─┤", double = "├─┤" } },
|
||||
{ args = { "├─┤", 5, nil, -1 }, expected = { single = "├─┤", double = "…┤" } },
|
||||
{ args = { "├─┤", 4, nil, -1 }, expected = { single = "├─┤", double = "…┤" } },
|
||||
{ args = { "├─┤", 3, nil, -1 }, expected = { single = "├─┤", double = "…" } },
|
||||
{ args = { "├─┤", 2, nil, -1 }, expected = { single = "…┤", double = "…" } },
|
||||
-- truncations from the middle
|
||||
{ args = { "abcde", 6, nil, 0 }, expected = { single = "abcde", double = "abcde" } },
|
||||
{ args = { "abcde", 5, nil, 0 }, expected = { single = "abcde", double = "abcde" } },
|
||||
{ args = { "abcde", 4, nil, 0 }, expected = { single = "a…de", double = "a…e" } },
|
||||
{
|
||||
args = { "アイウエオ", 11, nil, 0 },
|
||||
expected = { single = "アイウエオ", double = "アイウエオ" },
|
||||
},
|
||||
{
|
||||
args = { "アイウエオ", 10, nil, 0 },
|
||||
expected = { single = "アイウエオ", double = "アイウエオ" },
|
||||
},
|
||||
{
|
||||
args = { "アイウエオ", 9, nil, 0 },
|
||||
expected = { single = "アイ…エオ", double = "ア…エオ" },
|
||||
},
|
||||
{ args = { "アイウエオ", 8, nil, 0 }, expected = { single = "ア…エオ", double = "ア…エオ" } },
|
||||
{ args = { "├─┤", 7, nil, 0 }, expected = { single = "├─┤", double = "├─┤" } },
|
||||
{ args = { "├─┤", 6, nil, 0 }, expected = { single = "├─┤", double = "├─┤" } },
|
||||
{ args = { "├─┤", 5, nil, 0 }, expected = { single = "├─┤", double = "…┤" } },
|
||||
{ args = { "├─┤", 4, nil, 0 }, expected = { single = "├─┤", double = "…┤" } },
|
||||
{ args = { "├─┤", 3, nil, 0 }, expected = { single = "├─┤", double = "…" } },
|
||||
{ args = { "├─┤", 2, nil, 0 }, expected = { single = "…┤", double = "…" } },
|
||||
} do
|
||||
for _, ambiwidth in ipairs { "single", "double" } do
|
||||
local msg = ("ambiwidth = %s, direction = %s, [%s, %d] -> %s"):format(
|
||||
ambiwidth,
|
||||
(case.args[4] > 0) and "right" or (case.args[4] < 0) and "left" or "middle",
|
||||
case.args[1],
|
||||
case.args[2],
|
||||
case.expected[ambiwidth]
|
||||
)
|
||||
it(msg, function()
|
||||
local original = vim.o.ambiwidth
|
||||
vim.o.ambiwidth = ambiwidth
|
||||
eq(case.expected[ambiwidth], strings.truncate(unpack(case.args)))
|
||||
vim.o.ambiwidth = original
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
describe("align_str", function()
|
||||
for _, case in ipairs {
|
||||
{ args = { "abcde", 8 }, expected = { single = "abcde ", double = "abcde " } },
|
||||
{ args = { "アイウ", 8 }, expected = { single = "アイウ ", double = "アイウ " } },
|
||||
{ args = { "├─┤", 8 }, expected = { single = "├─┤ ", double = "├─┤ " } },
|
||||
{ args = { "abcde", 8, true }, expected = { single = " abcde", double = " abcde" } },
|
||||
{ args = { "アイウ", 8, true }, expected = { single = " アイウ", double = " アイウ" } },
|
||||
{ args = { "├─┤", 8, true }, expected = { single = " ├─┤", double = " ├─┤" } },
|
||||
} do
|
||||
for _, ambiwidth in ipairs { "single", "double" } do
|
||||
local msg = ('ambiwidth = %s, [%s, %d, %s] -> "%s"'):format(
|
||||
ambiwidth,
|
||||
case.args[1],
|
||||
case.args[2],
|
||||
tostring(case.args[3]),
|
||||
case.expected[ambiwidth]
|
||||
)
|
||||
it(msg, function()
|
||||
local original = vim.o.ambiwidth
|
||||
vim.o.ambiwidth = ambiwidth
|
||||
eq(case.expected[ambiwidth], strings.align_str(unpack(case.args)))
|
||||
vim.o.ambiwidth = original
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
describe("dedent", function()
|
||||
local function lines(t)
|
||||
return table.concat(t, "\n")
|
||||
end
|
||||
for _, case in ipairs {
|
||||
{
|
||||
msg = "empty string",
|
||||
tabstop = 8,
|
||||
args = { "" },
|
||||
expected = "",
|
||||
},
|
||||
{
|
||||
msg = "in case tabs are longer than spaces",
|
||||
tabstop = 8,
|
||||
args = {
|
||||
lines {
|
||||
" <Tab><Tab> -> 13 spaces",
|
||||
" 5 spaces -> 0 space",
|
||||
},
|
||||
},
|
||||
expected = lines {
|
||||
" <Tab><Tab> -> 13 spaces",
|
||||
"5 spaces -> 0 space",
|
||||
},
|
||||
},
|
||||
{
|
||||
msg = "in case tabs are shorter than spaces",
|
||||
tabstop = 2,
|
||||
args = {
|
||||
lines {
|
||||
" <Tab><Tab> -> 0 space",
|
||||
" 5spaces -> 1 space",
|
||||
},
|
||||
},
|
||||
expected = lines {
|
||||
"<Tab><Tab> -> 0 space",
|
||||
" 5spaces -> 1 space",
|
||||
},
|
||||
},
|
||||
{
|
||||
msg = "ignores empty lines",
|
||||
tabstop = 2,
|
||||
args = {
|
||||
lines {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
" 8 spaces -> 3 spaces",
|
||||
"",
|
||||
"",
|
||||
" 5 spaces -> 0 space",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
},
|
||||
},
|
||||
expected = lines {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
" 8 spaces -> 3 spaces",
|
||||
"",
|
||||
"",
|
||||
"5 spaces -> 0 space",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
},
|
||||
},
|
||||
{
|
||||
msg = "no indent",
|
||||
tabstop = 2,
|
||||
args = {
|
||||
lines {
|
||||
" <Tab> -> 2 spaces",
|
||||
"Here is no indent.",
|
||||
" 4 spaces will remain",
|
||||
},
|
||||
},
|
||||
expected = lines {
|
||||
" <Tab> -> 2 spaces",
|
||||
"Here is no indent.",
|
||||
" 4 spaces will remain",
|
||||
},
|
||||
},
|
||||
{
|
||||
msg = "leave_indent = 4",
|
||||
tabstop = 2,
|
||||
args = {
|
||||
lines {
|
||||
" <Tab> -> 6 spaces",
|
||||
"0 indent -> 4 spaces",
|
||||
" 4 spaces -> 8 spaces",
|
||||
},
|
||||
4,
|
||||
},
|
||||
expected = lines {
|
||||
" <Tab> -> 6 spaces",
|
||||
" 0 indent -> 4 spaces",
|
||||
" 4 spaces -> 8 spaces",
|
||||
},
|
||||
},
|
||||
{
|
||||
msg = "typical usecase: <Tab> to 5 spaces",
|
||||
tabstop = 4,
|
||||
args = {
|
||||
lines {
|
||||
"",
|
||||
" Chapter 1",
|
||||
"",
|
||||
" Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed",
|
||||
" do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
"",
|
||||
" Ut enim ad minim veniam, quis nostrud exercitation ullamco",
|
||||
" laboris nisi ut aliquip ex ea commodo consequat.",
|
||||
"",
|
||||
},
|
||||
5,
|
||||
},
|
||||
expected = lines {
|
||||
"",
|
||||
" Chapter 1",
|
||||
"",
|
||||
" Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed",
|
||||
" do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
||||
"",
|
||||
" Ut enim ad minim veniam, quis nostrud exercitation ullamco",
|
||||
" laboris nisi ut aliquip ex ea commodo consequat.",
|
||||
"",
|
||||
},
|
||||
},
|
||||
} do
|
||||
local msg = ("tabstop = %d, %s"):format(case.tabstop, case.msg)
|
||||
it(msg, function()
|
||||
local original = vim.bo.tabstop
|
||||
vim.bo.tabstop = case.tabstop
|
||||
eq(case.expected, strings.dedent(unpack(case.args)))
|
||||
vim.bo.tabstop = original
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,27 @@
|
|||
local tbl = require "plenary.tbl"
|
||||
|
||||
local function should_fail(fun)
|
||||
local stat = pcall(fun)
|
||||
assert(not stat, "Function should have errored")
|
||||
end
|
||||
|
||||
describe("tbl utilities", function()
|
||||
it("should be able to freeze a table", function()
|
||||
local t = { 1, 2, 3 }
|
||||
local frozen = tbl.freeze(t)
|
||||
assert(t[1] == frozen[1])
|
||||
assert(t[2] == frozen[2])
|
||||
assert(t[3] == frozen[3])
|
||||
|
||||
should_fail(function()
|
||||
frozen[4] = "thisthis"
|
||||
end)
|
||||
|
||||
should_fail(function()
|
||||
frozen.hello = "asdfasdf"
|
||||
end)
|
||||
|
||||
assert(not frozen[5])
|
||||
assert(not frozen.hello)
|
||||
end)
|
||||
end)
|
|
@ -0,0 +1,5 @@
|
|||
describe("simple nvim test", function()
|
||||
it("should work", function()
|
||||
vim.cmd "let g:val = v:true"
|
||||
end)
|
||||
end)
|
Loading…
Add table
Add a link
Reference in a new issue