summaryrefslogtreecommitdiff
path: root/lib/lua-repl/t
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-12-13 16:10:08 +1100
committerjacqueline <me@jacqueline.id.au>2023-12-13 16:10:08 +1100
commit64b106c13e18c33be0f2b0de532054e0ed3f731d (patch)
treeb54b1c90d941bc456b4d51e864970720bdf2d648 /lib/lua-repl/t
parent5a2f0b08e0e3f20cda977b510b680d5843ae7283 (diff)
downloadtangara-fw-64b106c13e18c33be0f2b0de532054e0ed3f731d.tar.gz
add a cool lua repl
Diffstat (limited to 'lib/lua-repl/t')
-rw-r--r--lib/lua-repl/t/abstract-repl-tests.lua33
-rw-r--r--lib/lua-repl/t/clone-repl-tests.lua119
-rw-r--r--lib/lua-repl/t/lib/test-utils.lua56
-rw-r--r--lib/lua-repl/t/plugin-after-tests.lua202
-rw-r--r--lib/lua-repl/t/plugin-around-tests.lua218
-rw-r--r--lib/lua-repl/t/plugin-basic-tests.lua206
-rw-r--r--lib/lua-repl/t/plugin-before-tests.lua201
-rw-r--r--lib/lua-repl/t/plugin-feature-tests.lua132
-rw-r--r--lib/lua-repl/t/plugin-override-tests.lua201
-rw-r--r--lib/lua-repl/t/plugin-repl-tests.lua75
-rw-r--r--lib/lua-repl/t/sync-repl-tests.lua52
11 files changed, 1495 insertions, 0 deletions
diff --git a/lib/lua-repl/t/abstract-repl-tests.lua b/lib/lua-repl/t/abstract-repl-tests.lua
new file mode 100644
index 00000000..50d4b07c
--- /dev/null
+++ b/lib/lua-repl/t/abstract-repl-tests.lua
@@ -0,0 +1,33 @@
+-- vim:foldmethod=marker
+local repl = require 'repl'
+pcall(require, 'luarocks.loader')
+require 'Test.More'
+
+plan(8)
+
+do -- getprompt tests {{{
+ is(repl:getprompt(1), '>')
+ is(repl:getprompt(2), '>>')
+end -- }}}
+
+do -- prompt abstract tests {{{
+ error_like(function()
+ repl:prompt(1)
+ end, 'You must implement the showprompt method')
+
+ error_like(function()
+ repl:prompt(2)
+ end, 'You must implement the showprompt method')
+end -- }}}
+
+do -- name tests {{{
+ is(repl:name(), 'REPL')
+end -- }}}
+
+do -- handleline abstract tests {{{
+ is(_G.testresult, nil)
+ error_like(function()
+ repl:handleline '_G.testresult = 17'
+ end, 'You must implement the displayresults method')
+ is(_G.testresult, 17)
+end -- }}}
diff --git a/lib/lua-repl/t/clone-repl-tests.lua b/lib/lua-repl/t/clone-repl-tests.lua
new file mode 100644
index 00000000..3ec82f92
--- /dev/null
+++ b/lib/lua-repl/t/clone-repl-tests.lua
@@ -0,0 +1,119 @@
+-- vim:foldmethod=marker
+local repl = require 'repl'
+pcall(require, 'luarocks.loader')
+require 'Test.More'
+
+plan(45)
+
+local clone = repl:clone()
+local prompt
+local results
+local errmsg
+
+isnt(type(clone), 'nil')
+
+function clone:showprompt(p)
+ prompt = p
+end
+
+function clone:displayresults(r)
+ results = r
+end
+
+function clone:displayerror(err)
+ errmsg = err
+end
+
+do -- prompt tests {{{
+ lives_ok(function()
+ clone:prompt(1)
+ end)
+
+ is(prompt, '>')
+
+ lives_ok(function()
+ clone:prompt(2)
+ end)
+
+ is(prompt, '>>')
+end -- }}}
+
+do -- handleline tests {{{
+ is(_G.testresult, nil)
+ is(results, nil)
+
+ lives_ok(function()
+ clone:handleline '_G.testresult = 18'
+ end)
+
+ is(_G.testresult, 18)
+
+ is(type(results), 'table')
+ is(results.n, 0)
+ is(#results, 0)
+
+ lives_ok(function()
+ clone:handleline 'return 19'
+ end)
+
+ is(type(results), 'table')
+ is(results.n, 1)
+ is(#results, 1)
+ is(results[1], 19)
+
+ lives_ok(function()
+ clone:handleline 'return 20, 21, 22'
+ end)
+
+ is(type(results), 'table')
+ is(results.n, 3)
+ is(#results, 3)
+ is(results[1], 20)
+ is(results[2], 21)
+ is(results[3], 22)
+
+ lives_ok(function()
+ clone:handleline 'return 1, nil, nil, nil, nil, nil, nil, 2'
+ end)
+
+ is(type(results), 'table')
+ is(results.n, 8)
+ is(results[1], 1)
+ for i = 2, 7 do
+ is(results[i], nil)
+ end
+ is(results[8], 2)
+end -- }}}
+
+do -- error handling tests {{{
+ lives_ok(function()
+ clone:handleline '3 4'
+ end)
+
+ isnt(type(errmsg), 'nil')
+
+ errmsg = nil
+
+ lives_ok(function()
+ clone:handleline 'error "foo"'
+ end)
+
+ like(errmsg, 'foo')
+end -- }}}
+
+do -- multi-line input tests {{{
+ errmsg = nil
+ _G.t = {}
+
+ lives_ok(function()
+ clone:handleline 'for i = 1, 3 do'
+ clone:handleline ' table.insert(_G.t, i)'
+ clone:handleline 'end'
+ end)
+
+ is(errmsg, nil)
+ is(#_G.t, 3)
+ is(_G.t[1], 1)
+ is(_G.t[2], 2)
+ is(_G.t[3], 3)
+end -- }}}
diff --git a/lib/lua-repl/t/lib/test-utils.lua b/lib/lua-repl/t/lib/test-utils.lua
new file mode 100644
index 00000000..a0bb16ac
--- /dev/null
+++ b/lib/lua-repl/t/lib/test-utils.lua
@@ -0,0 +1,56 @@
+local _M = {}
+
+function _M.next_line_number()
+ local info = debug.getinfo(2, 'l')
+ return info.currentline + 1 -- doesn't work with whitespace
+end
+
+function _M.cmp_tables(lhs, rhs)
+ local ok = true
+ local got
+ local expected
+ local failing_k
+
+ for k, v in pairs(lhs) do
+ local rv = rhs[k]
+
+ if v ~= rv then
+ ok = false
+ failing_k = k
+ got = v
+ expected = rv
+ break
+ end
+ end
+
+ if ok then
+ for k, v in pairs(rhs) do
+ local lv = lhs[k]
+
+ if v ~= lv then
+ ok = false
+ failing_k = k
+ got = lv
+ expected = v
+ break
+ end
+ end
+ end
+
+ if ok then
+ pass()
+ else
+ fail 'value mismatch'
+ diag(string.format(' got[%q]: %s', tostring(failing_k), tostring(got)))
+ diag(string.format('expected[%q]: %s', tostring(failing_k), tostring(expected)))
+ end
+end
+
+function _M.gather_results(...)
+ return {
+ n = select('#', ...),
+ ...,
+ }
+end
+
+return _M
diff --git a/lib/lua-repl/t/plugin-after-tests.lua b/lib/lua-repl/t/plugin-after-tests.lua
new file mode 100644
index 00000000..15a24c11
--- /dev/null
+++ b/lib/lua-repl/t/plugin-after-tests.lua
@@ -0,0 +1,202 @@
+-- vim:foldmethod=marker
+local repl = require 'repl'
+pcall(require, 'luarocks.loader')
+require 'Test.More'
+local utils = require 'test-utils'
+
+plan(26)
+
+local clone = repl:clone()
+
+do -- basic tests {{{
+ local with_plugin = clone:clone()
+
+ local has_called_normal
+ local has_called_after
+
+ function with_plugin:foo()
+ has_called_normal = true
+ end
+
+ with_plugin:loadplugin(function()
+ function after:foo()
+ has_called_after = true
+ ok(has_called_normal)
+ end
+ end)
+
+ with_plugin:foo()
+ ok(has_called_normal)
+ ok(has_called_after)
+
+ local line_no
+
+ local _, err = pcall(function()
+ with_plugin:loadplugin(function()
+ line_no = utils.next_line_number()
+ function after:nonexistent()
+ end
+ end)
+ end)
+
+ like(err, string.format("%d: The 'nonexistent' method does not exist", line_no))
+
+ _, err = pcall(function()
+ with_plugin:loadplugin(function()
+ line_no = utils.next_line_number()
+ after.foo = 17
+ end)
+ end)
+
+ like(err, string.format('%d: 17 is not a function', line_no))
+end -- }}}
+
+do -- arguments tests {{{
+ local with_plugin = clone:clone()
+ local orig_args
+ local got_args
+
+ function with_plugin:foo(...)
+ orig_args = {
+ n = select('#', ...),
+ ...,
+ }
+ end
+
+ with_plugin:loadplugin(function()
+ function after:foo(...)
+ got_args = {
+ n = select('#', ...),
+ ...,
+ }
+ end
+ end)
+
+ with_plugin:foo()
+ is(got_args.n, 0)
+ utils.cmp_tables(orig_args, got_args)
+
+ with_plugin:foo(1, 2, 3)
+ is(got_args.n, 3)
+ is(got_args[1], 1)
+ is(got_args[2], 2)
+ is(got_args[3], 3)
+ utils.cmp_tables(orig_args, got_args)
+
+ with_plugin:foo(1, nil, nil, nil, 5)
+ is(got_args.n, 5)
+ is(got_args[1], 1)
+ is(got_args[2], nil)
+ is(got_args[3], nil)
+ is(got_args[4], nil)
+ is(got_args[5], 5)
+ utils.cmp_tables(orig_args, got_args)
+end -- }}}
+
+do -- exception tests {{{
+ local with_plugin = clone:clone()
+
+ local has_called_original
+
+ function with_plugin:foo()
+ has_called_original = true
+ end
+
+ with_plugin:loadplugin(function()
+ function after:foo()
+ error 'uh-oh'
+ end
+ end)
+
+ local _, err = pcall(with_plugin.foo, with_plugin)
+
+ like(err, 'uh%-oh')
+ ok(has_called_original)
+end -- }}}
+
+do -- return value tests {{{
+ local with_plugin = clone:clone()
+
+ function with_plugin:foo()
+ return 17
+ end
+
+ function with_plugin:bar()
+ return 18, 19, 20
+ end
+
+ function with_plugin:baz()
+ return 1, nil, nil, nil, 5
+ end
+
+ with_plugin:loadplugin(function()
+ function after:foo()
+ return 18
+ end
+
+ function after:bar()
+ return 18
+ end
+
+ function after:baz()
+ return 18
+ end
+ end)
+
+ local result = with_plugin:foo()
+ is(result, 17)
+
+ local results = utils.gather_results(with_plugin:bar())
+ utils.cmp_tables(results, { n = 3, 18, 19, 20 })
+
+ results = utils.gather_results(with_plugin:baz())
+ utils.cmp_tables(results, { n = 5, 1, nil, nil, nil, 5 })
+end -- }}}
+
+do -- multiple advice, multiple plugins {{{
+ local with_plugin = clone:clone()
+ local calls = {}
+
+ function with_plugin:foo()
+ calls[#calls + 1] = 'original'
+ end
+
+ with_plugin:loadplugin(function()
+ function after:foo()
+ calls[#calls + 1] = 'first'
+ end
+ end)
+
+ with_plugin:loadplugin(function()
+ function after:foo()
+ calls[#calls + 1] = 'second'
+ end
+ end)
+
+ with_plugin:foo()
+
+ utils.cmp_tables(calls, { 'original', 'first', 'second' })
+end -- }}}
+
+do -- multiple advice, single plugin {{{
+ local with_plugin = clone:clone()
+ local calls = {}
+
+ function with_plugin:foo()
+ calls[#calls + 1] = 'original'
+ end
+
+ with_plugin:loadplugin(function()
+ function after:foo()
+ calls[#calls + 1] = 'first'
+ end
+
+ function after:foo()
+ calls[#calls + 1] = 'second'
+ end
+ end)
+
+ with_plugin:foo()
+
+ utils.cmp_tables(calls, { 'original', 'first', 'second' })
+end -- }}}
diff --git a/lib/lua-repl/t/plugin-around-tests.lua b/lib/lua-repl/t/plugin-around-tests.lua
new file mode 100644
index 00000000..df0ea073
--- /dev/null
+++ b/lib/lua-repl/t/plugin-around-tests.lua
@@ -0,0 +1,218 @@
+-- vim:foldmethod=marker
+local repl = require 'repl'
+local compat = require 'repl.compat'
+pcall(require, 'luarocks.loader')
+require 'Test.More'
+local utils = require 'test-utils'
+
+plan(27)
+
+local clone = repl:clone()
+
+do -- basic tests {{{
+ local with_plugin = clone:clone()
+
+ local has_called_normal
+ local has_called_around
+
+ function with_plugin:foo()
+ has_called_normal = true
+ end
+
+ with_plugin:loadplugin(function()
+ function around:foo(orig, ...)
+ has_called_around = true
+ ok(not has_called_normal)
+ local return_values = utils.gather_results(orig(self, ...))
+ ok(has_called_normal)
+ return compat.unpack(return_values, 1, return_values.n)
+ end
+ end)
+
+ with_plugin:foo()
+ ok(has_called_normal)
+ ok(has_called_around)
+
+ local line_no
+
+ local _, err = pcall(function()
+ with_plugin:loadplugin(function()
+ line_no = utils.next_line_number()
+ function around:nonexistent()
+ end
+ end)
+ end)
+
+ like(err, string.format("%d: The 'nonexistent' method does not exist", line_no))
+
+ _, err = pcall(function()
+ with_plugin:loadplugin(function()
+ line_no = utils.next_line_number()
+ around.foo = 17
+ end)
+ end)
+
+ like(err, string.format('%d: 17 is not a function', line_no))
+end -- }}}
+
+do -- arguments tests {{{
+ local with_plugin = clone:clone()
+ local orig_args
+ local got_args
+
+ function with_plugin:foo(...)
+ orig_args = {
+ n = select('#', ...),
+ ...,
+ }
+ end
+
+ with_plugin:loadplugin(function()
+ function around:foo(orig, ...)
+ got_args = {
+ n = select('#', ...),
+ ...,
+ }
+
+ return orig(self, ...)
+ end
+ end)
+
+ with_plugin:foo()
+ is(got_args.n, 0)
+ utils.cmp_tables(orig_args, got_args)
+
+ with_plugin:foo(1, 2, 3)
+ is(got_args.n, 3)
+ is(got_args[1], 1)
+ is(got_args[2], 2)
+ is(got_args[3], 3)
+ utils.cmp_tables(orig_args, got_args)
+
+ with_plugin:foo(1, nil, nil, nil, 5)
+ is(got_args.n, 5)
+ is(got_args[1], 1)
+ is(got_args[2], nil)
+ is(got_args[3], nil)
+ is(got_args[4], nil)
+ is(got_args[5], 5)
+ utils.cmp_tables(orig_args, got_args)
+end -- }}}
+
+do -- exception tests {{{
+ local with_plugin = clone:clone()
+
+ local has_called_original
+
+ function with_plugin:foo()
+ has_called_original = true
+ end
+
+ with_plugin:loadplugin(function()
+ function around:foo()
+ error 'uh-oh'
+ end
+ end)
+
+ local _, err = pcall(with_plugin.foo, with_plugin)
+
+ like(err, 'uh%-oh')
+ ok(not has_called_original)
+end -- }}}
+
+do -- return value tests {{{
+ local with_plugin = clone:clone()
+
+ function with_plugin:foo()
+ return 17
+ end
+
+ function with_plugin:bar()
+ return 18, 19, 20
+ end
+
+ function with_plugin:baz()
+ return 1, nil, nil, nil, 5
+ end
+
+ with_plugin:loadplugin(function()
+ function around:foo()
+ return 18
+ end
+
+ function around:bar()
+ return 19, 20, 21
+ end
+
+ function around:baz()
+ return 1, nil, nil, 4
+ end
+ end)
+
+ local result = with_plugin:foo()
+ is(result, 18)
+
+ local results = utils.gather_results(with_plugin:bar())
+ utils.cmp_tables(results, { n = 3, 19, 20, 21 })
+
+ results = utils.gather_results(with_plugin:baz())
+ utils.cmp_tables(results, { n = 4, 1, nil, nil, 4 })
+end -- }}}
+
+do -- multiple advice, multiple plugins {{{
+ local with_plugin = clone:clone()
+ local calls = {}
+
+ function with_plugin:foo()
+ calls[#calls + 1] = 'original'
+ end
+
+ with_plugin:loadplugin(function()
+ function around:foo(orig)
+ calls[#calls + 1] = 'before_one'
+ orig()
+ calls[#calls + 1] = 'after_one'
+ end
+ end)
+
+ with_plugin:loadplugin(function()
+ function around:foo(orig)
+ calls[#calls + 1] = 'before_two'
+ orig()
+ calls[#calls + 1] = 'after_two'
+ end
+ end)
+
+ with_plugin:foo()
+
+ utils.cmp_tables(calls, { 'before_two', 'before_one', 'original',
+ 'after_one', 'after_two' })
+end -- }}}
+
+do -- multiple advice, single plugin {{{
+ local with_plugin = clone:clone()
+ local calls = {}
+
+ function with_plugin:foo()
+ calls[#calls + 1] = 'original'
+ end
+
+ with_plugin:loadplugin(function()
+ function around:foo(orig)
+ calls[#calls + 1] = 'before_one'
+ orig()
+ calls[#calls + 1] = 'after_one'
+ end
+
+ function around:foo(orig)
+ calls[#calls + 1] = 'before_two'
+ orig()
+ calls[#calls + 1] = 'after_two'
+ end
+ end)
+
+ with_plugin:foo()
+
+ utils.cmp_tables(calls, { 'before_two', 'before_one', 'original',
+ 'after_one', 'after_two' })
+end -- }}}
diff --git a/lib/lua-repl/t/plugin-basic-tests.lua b/lib/lua-repl/t/plugin-basic-tests.lua
new file mode 100644
index 00000000..21283ef8
--- /dev/null
+++ b/lib/lua-repl/t/plugin-basic-tests.lua
@@ -0,0 +1,206 @@
+-- vim:foldmethod=marker
+local repl = require 'repl'
+local utils = require 'test-utils'
+pcall(require, 'luarocks.loader')
+require 'Test.More'
+
+plan(28)
+
+local clone = repl:clone()
+
+do -- basic tests {{{
+ local loaded
+
+ clone:loadplugin(function()
+ loaded = true
+ end)
+
+ ok(loaded)
+
+ error_like(function()
+ clone:loadplugin(function()
+ error 'uh-oh'
+ end)
+ end, 'uh%-oh')
+
+end -- }}}
+
+do -- loading the same plugin twice {{{
+ local function plugin()
+ end
+
+ local line_no
+
+ clone:loadplugin(plugin)
+ local _, err = pcall(function()
+ line_no = utils.next_line_number()
+ clone:loadplugin(plugin)
+ end)
+ like(err, tostring(line_no) .. ': plugin "function:%s+%S+" has already been loaded')
+
+ _, err = pcall(function()
+ line_no = utils.next_line_number()
+ clone:clone():loadplugin(plugin)
+ end)
+ like(err, tostring(line_no) .. ': plugin "function:%s+%S+" has already been loaded')
+
+ repl:clone():loadplugin(plugin)
+ repl:clone():loadplugin(plugin)
+end -- }}}
+
+do -- loading plugins by name {{{
+ local loaded
+
+ package.preload['repl.plugins.test'] = function()
+ loaded = true
+ end
+
+ clone:clone():loadplugin 'test'
+
+ ok(loaded)
+ loaded = false
+
+ clone:clone():loadplugin 'test'
+
+ ok(loaded, 'loading a plugin twice should initialize it twice')
+
+ package.preload['repl.plugins.test'] = function()
+ error 'uh-oh'
+ end
+
+ error_like(function()
+ clone:clone():loadplugin 'test'
+ end, 'uh%-oh')
+
+ package.preload['repl.plugins.test'] = nil
+
+ local line_no
+
+ local _, err = pcall(function()
+ line_no = utils.next_line_number()
+ clone:clone():loadplugin 'test'
+ end)
+ like(err, tostring(line_no) .. ': unable to locate plugin')
+end -- }}}
+
+do -- hasplugin tests {{{
+ local child = repl:clone()
+
+ local plugin = function()
+ end
+
+ child:loadplugin(plugin)
+
+ local grandchild = child:clone()
+
+ ok(not repl:hasplugin(plugin))
+ ok(child:hasplugin(plugin))
+ ok(grandchild:hasplugin(plugin))
+
+ plugin = function()
+ end
+
+ child:loadplugin(plugin)
+
+ ok(not repl:hasplugin(plugin))
+ ok(child:hasplugin(plugin))
+ ok(not grandchild:hasplugin(plugin))
+end -- }}}
+
+do -- global tests {{{
+ local clone = repl:clone()
+ local line_no
+
+ local _, err = pcall(function()
+ clone:loadplugin(function()
+ line_no = utils.next_line_number()
+ foo = 17
+ end)
+ end)
+
+ like(err, tostring(line_no) .. ': global environment is read%-only %(key = "foo"%)')
+
+ _, err = pcall(function()
+ clone:loadplugin(function()
+ line_no = utils.next_line_number()
+ _G.foo = 17
+ end)
+ end)
+
+ like(err, tostring(line_no) .. ': global environment is read%-only %(key = "foo"%)')
+end -- }}}
+
+do -- ifplugin tests {{{
+ local clone = repl:clone()
+ local has_run
+
+ package.preload['repl.plugins.test'] = function()
+ end
+
+ clone:ifplugin('test', function()
+ has_run = true
+ end)
+
+ ok(not has_run)
+
+ clone:loadplugin 'test'
+
+ ok(has_run)
+
+ has_run = false
+
+ clone:ifplugin('test', function()
+ has_run = true
+ end)
+
+ ok(has_run)
+end -- }}}
+
+do -- ifplugin multiple times {{{
+ local clone = repl:clone()
+ local has_run
+ local has_run2
+
+ package.preload['repl.plugins.test'] = function()
+ end
+
+ clone:ifplugin('test', function()
+ has_run = true
+ end)
+
+ clone:ifplugin('test', function()
+ has_run2 = true
+ end)
+
+ clone:loadplugin 'test'
+
+ ok(has_run)
+ ok(has_run2)
+end -- }}}
+
+do -- plugin return value {{{
+ local clone = repl:clone()
+
+ local result = clone:loadplugin(function()
+ return 17
+ end)
+
+ local result2 = clone:loadplugin(function()
+ end)
+
+ local result3, result4 = clone:loadplugin(function()
+ return 18, 19
+ end)
+
+ local result5, result6, result7 = clone:loadplugin(function()
+ return 20, nil, 21
+ end)
+
+ is(result, 17)
+ is(result2, nil)
+ is(result3, 18)
+ is(result4, 19)
+ is(result5, 20)
+ is(result6, nil)
+ is(result7, 21)
+end -- }}}
diff --git a/lib/lua-repl/t/plugin-before-tests.lua b/lib/lua-repl/t/plugin-before-tests.lua
new file mode 100644
index 00000000..cc23ba78
--- /dev/null
+++ b/lib/lua-repl/t/plugin-before-tests.lua
@@ -0,0 +1,201 @@
+-- vim:foldmethod=marker
+local repl = require 'repl'
+pcall(require, 'luarocks.loader')
+require 'Test.More'
+local utils = require 'test-utils'
+
+plan(26)
+
+local clone = repl:clone()
+
+do -- basic tests {{{
+ local with_plugin = clone:clone()
+
+ local has_called_normal
+ local has_called_before
+
+ function with_plugin:foo()
+ has_called_normal = true
+ end
+
+ with_plugin:loadplugin(function()
+ function before:foo()
+ has_called_before = true
+ ok(not has_called_normal)
+ end
+ end)
+
+ with_plugin:foo()
+ ok(has_called_normal)
+ ok(has_called_before)
+
+ local line_no
+
+ local _, err = pcall(function()
+ with_plugin:loadplugin(function()
+ line_no = utils.next_line_number()
+ function before:nonexistent()
+ end
+ end)
+ end)
+
+ like(err, string.format("%d: The 'nonexistent' method does not exist", line_no))
+
+ _, err = pcall(function()
+ with_plugin:loadplugin(function()
+ line_no = utils.next_line_number()
+ before.foo = 17
+ end)
+ end)
+
+ like(err, string.format('%d: 17 is not a function', line_no))
+end -- }}}
+
+do -- arguments tests {{{
+ local with_plugin = clone:clone()
+ local orig_args
+ local got_args
+
+ function with_plugin:foo(...)
+ orig_args = {
+ n = select('#', ...),
+ ...,
+ }
+ end
+
+ with_plugin:loadplugin(function()
+ function before:foo(...)
+ got_args = {
+ n = select('#', ...),
+ ...,
+ }
+ end
+ end)
+
+ with_plugin:foo()
+ is(got_args.n, 0)
+ utils.cmp_tables(orig_args, got_args)
+
+ with_plugin:foo(1, 2, 3)
+ is(got_args.n, 3)
+ is(got_args[1], 1)
+ is(got_args[2], 2)
+ is(got_args[3], 3)
+ utils.cmp_tables(orig_args, got_args)
+
+ with_plugin:foo(1, nil, nil, nil, 5)
+ is(got_args.n, 5)
+ is(got_args[1], 1)
+ is(got_args[2], nil)
+ is(got_args[3], nil)
+ is(got_args[4], nil)
+ is(got_args[5], 5)
+ utils.cmp_tables(orig_args, got_args)
+end -- }}}
+
+do -- exception tests {{{
+ local with_plugin = clone:clone()
+
+ local has_called_original
+
+ function with_plugin:foo()
+ has_called_original = true
+ end
+
+ with_plugin:loadplugin(function()
+ function before:foo()
+ error 'uh-oh'
+ end
+ end)
+
+ local _, err = pcall(with_plugin.foo, with_plugin)
+
+ like(err, 'uh%-oh')
+ ok(not has_called_original)
+end -- }}}
+
+do -- return value tests {{{
+ local with_plugin = clone:clone()
+
+ function with_plugin:foo()
+ return 17
+ end
+
+ function with_plugin:bar()
+ return 18, 19, 20
+ end
+
+ function with_plugin:baz()
+ return 1, nil, nil, nil, 5
+ end
+
+ with_plugin:loadplugin(function()
+ function before:foo()
+ return 18
+ end
+
+ function before:bar()
+ return 18
+ end
+
+ function before:baz()
+ end
+ end)
+
+ local result = with_plugin:foo()
+ is(result, 17)
+
+ local results = utils.gather_results(with_plugin:bar())
+ utils.cmp_tables(results, { n = 3, 18, 19, 20 })
+
+ results = utils.gather_results(with_plugin:baz())
+ utils.cmp_tables(results, { n = 5, 1, nil, nil, nil, 5 })
+end -- }}}
+
+do -- multiple advice, multiple plugins {{{
+ local with_plugin = clone:clone()
+ local calls = {}
+
+ function with_plugin:foo()
+ calls[#calls + 1] = 'original'
+ end
+
+ with_plugin:loadplugin(function()
+ function before:foo()
+ calls[#calls + 1] = 'first'
+ end
+ end)
+
+ with_plugin:loadplugin(function()
+ function before:foo()
+ calls[#calls + 1] = 'second'
+ end
+ end)
+
+ with_plugin:foo()
+
+ utils.cmp_tables(calls, { 'second', 'first', 'original' })
+end -- }}}
+
+do -- multiple advice, single plugin {{{
+ local with_plugin = clone:clone()
+ local calls = {}
+
+ function with_plugin:foo()
+ calls[#calls + 1] = 'original'
+ end
+
+ with_plugin:loadplugin(function()
+ function before:foo()
+ calls[#calls + 1] = 'first'
+ end
+
+ function before:foo()
+ calls[#calls + 1] = 'second'
+ end
+ end)
+
+ with_plugin:foo()
+
+ utils.cmp_tables(calls, { 'second', 'first', 'original' })
+end -- }}}
diff --git a/lib/lua-repl/t/plugin-feature-tests.lua b/lib/lua-repl/t/plugin-feature-tests.lua
new file mode 100644
index 00000000..4c74a4c1
--- /dev/null
+++ b/lib/lua-repl/t/plugin-feature-tests.lua
@@ -0,0 +1,132 @@
+-- vim:foldmethod=marker
+local repl = require 'repl'
+local utils = require 'test-utils'
+pcall(require, 'luarocks.loader')
+require 'Test.More'
+
+plan(19)
+
+do -- basic tests {{{
+ local clone = repl:clone()
+
+ clone:loadplugin(function()
+ features = 'foo'
+ end)
+
+ ok(clone:hasfeature 'foo')
+ ok(not clone:hasfeature 'bar')
+ ok(not clone:hasfeature 'baz')
+
+ clone:loadplugin(function()
+ features = { 'bar', 'baz' }
+ end)
+
+ ok(clone:hasfeature 'foo')
+ ok(clone:hasfeature 'bar')
+ ok(clone:hasfeature 'baz')
+end -- }}}
+
+do -- requirefeature {{{
+ local clone = repl:clone()
+
+ clone:loadplugin(function()
+ features = 'foo'
+ end)
+
+ clone:requirefeature 'foo'
+
+ local line_no
+ local _, err = pcall(function()
+ line_no = utils.next_line_number()
+ clone:requirefeature 'bar'
+ end)
+
+ like(err, tostring(line_no) .. ': required feature "bar" not present')
+end -- }}}
+
+do -- conflicts {{{
+ local clone = repl:clone()
+ local line_no
+
+ clone:loadplugin(function()
+ features = 'foo'
+ end)
+
+ local _, err = pcall(function()
+ line_no = utils.next_line_number()
+ clone:loadplugin(function()
+ features = 'foo'
+ end)
+ end)
+ like(err, tostring(line_no) .. ': feature "foo" already present')
+
+ -- XXX what about methods injected into the object?
+end -- }}}
+
+do -- clone:hasfeature {{{
+ local child = repl:clone()
+
+ child:loadplugin(function()
+ features = 'foo'
+ end)
+
+ local grandchild = child:clone()
+
+ ok(not repl:hasfeature 'foo')
+ ok(child:hasfeature 'foo')
+ ok(grandchild:hasfeature 'foo')
+
+ child:loadplugin(function()
+ features = 'bar'
+ end)
+
+ ok(not repl:hasfeature 'bar')
+ ok(child:hasfeature 'bar')
+ ok(not grandchild:hasfeature 'bar')
+end -- }}}
+
+do -- iffeature tests {{{
+ local clone = repl:clone()
+ local has_run
+
+ clone:iffeature('foo', function()
+ has_run = true
+ end)
+
+ ok(not has_run)
+
+ clone:loadplugin(function()
+ features = 'foo'
+ end)
+
+ ok(has_run)
+
+ has_run = false
+
+ clone:iffeature('foo', function()
+ has_run = true
+ end)
+
+ ok(has_run)
+end -- }}}
+
+do -- iffeature multiple times {{{
+ local clone = repl:clone()
+ local has_run
+ local has_run2
+
+ clone:iffeature('foo', function()
+ has_run = true
+ end)
+
+ clone:iffeature('foo', function()
+ has_run2 = true
+ end)
+
+ clone:loadplugin(function()
+ features = 'foo'
+ end)
+
+ ok(has_run)
+ ok(has_run2)
+end -- }}}
diff --git a/lib/lua-repl/t/plugin-override-tests.lua b/lib/lua-repl/t/plugin-override-tests.lua
new file mode 100644
index 00000000..6ea11d8b
--- /dev/null
+++ b/lib/lua-repl/t/plugin-override-tests.lua
@@ -0,0 +1,201 @@
+-- vim:foldmethod=marker
+local repl = require 'repl'
+pcall(require, 'luarocks.loader')
+require 'Test.More'
+local utils = require 'test-utils'
+
+plan(25)
+
+local clone = repl:clone()
+
+do -- basic tests {{{
+ local with_plugin = clone:clone()
+
+ local has_called_normal
+ local has_called_override
+
+ function with_plugin:foo()
+ has_called_normal = true
+ end
+
+ with_plugin:loadplugin(function()
+ function override:foo()
+ has_called_override = true
+ end
+ end)
+
+ with_plugin:foo()
+ ok(not has_called_normal)
+ ok(has_called_override)
+
+ local line_no
+
+ local _, err = pcall(function()
+ with_plugin:loadplugin(function()
+ line_no = utils.next_line_number()
+ function override:nonexistent()
+ end
+ end)
+ end)
+
+ like(err, string.format("%d: The 'nonexistent' method does not exist", line_no))
+
+ _, err = pcall(function()
+ with_plugin:loadplugin(function()
+ line_no = utils.next_line_number()
+ override.foo = 17
+ end)
+ end)
+
+ like(err, string.format('%d: 17 is not a function', line_no))
+end -- }}}
+
+do -- arguments tests {{{
+ local with_plugin = clone:clone()
+ local orig_args
+ local got_args
+
+ function with_plugin:foo(...)
+ orig_args = {
+ n = select('#', ...),
+ ...,
+ }
+ end
+
+ with_plugin:loadplugin(function()
+ function override:foo(...)
+ got_args = {
+ n = select('#', ...),
+ ...,
+ }
+ end
+ end)
+
+ with_plugin:foo()
+ is(got_args.n, 0)
+ ok(not orig_args)
+
+ with_plugin:foo(1, 2, 3)
+ is(got_args.n, 3)
+ is(got_args[1], 1)
+ is(got_args[2], 2)
+ is(got_args[3], 3)
+ ok(not orig_args)
+
+ with_plugin:foo(1, nil, nil, nil, 5)
+ is(got_args.n, 5)
+ is(got_args[1], 1)
+ is(got_args[2], nil)
+ is(got_args[3], nil)
+ is(got_args[4], nil)
+ is(got_args[5], 5)
+ ok(not orig_args)
+end -- }}}
+
+do -- exception tests {{{
+ local with_plugin = clone:clone()
+
+ local has_called_original
+
+ function with_plugin:foo()
+ has_called_original = true
+ end
+
+ with_plugin:loadplugin(function()
+ function override:foo()
+ error 'uh-oh'
+ end
+ end)
+
+ local _, err = pcall(with_plugin.foo, with_plugin)
+
+ like(err, 'uh%-oh')
+ ok(not has_called_original)
+end -- }}}
+
+do -- return value tests {{{
+ local with_plugin = clone:clone()
+
+ function with_plugin:foo()
+ return 17
+ end
+
+ function with_plugin:bar()
+ return 18, 19, 20
+ end
+
+ function with_plugin:baz()
+ return 1, nil, nil, nil, 5
+ end
+
+ with_plugin:loadplugin(function()
+ function override:foo()
+ return 18
+ end
+
+ function override:bar()
+ return 19, 20, 21
+ end
+
+ function override:baz()
+ return 1, nil, nil, 4
+ end
+ end)
+
+ local result = with_plugin:foo()
+ is(result, 18)
+
+ local results = utils.gather_results(with_plugin:bar())
+ utils.cmp_tables(results, { n = 3, 19, 20, 21 })
+
+ results = utils.gather_results(with_plugin:baz())
+ utils.cmp_tables(results, { n = 4, 1, nil, nil, 4 })
+end -- }}}
+
+do -- multiple advice, multiple plugins {{{
+ local with_plugin = clone:clone()
+ local calls = {}
+
+ function with_plugin:foo()
+ calls[#calls + 1] = 'original'
+ end
+
+ with_plugin:loadplugin(function()
+ function override:foo()
+ calls[#calls + 1] = 'first'
+ end
+ end)
+
+ with_plugin:loadplugin(function()
+ function override:foo()
+ calls[#calls + 1] = 'second'
+ end
+ end)
+
+ with_plugin:foo()
+
+ utils.cmp_tables(calls, { 'second' })
+end
+
+do -- multiple advice, single plugin {{{
+ local with_plugin = clone:clone()
+ local calls = {}
+
+ function with_plugin:foo()
+ calls[#calls + 1] = 'original'
+ end
+
+ with_plugin:loadplugin(function()
+ function override:foo()
+ calls[#calls + 1] = 'first'
+ end
+
+ function override:foo()
+ calls[#calls + 1] = 'second'
+ end
+ end)
+
+ with_plugin:foo()
+
+ utils.cmp_tables(calls, { 'second' })
+end
diff --git a/lib/lua-repl/t/plugin-repl-tests.lua b/lib/lua-repl/t/plugin-repl-tests.lua
new file mode 100644
index 00000000..b3aa7665
--- /dev/null
+++ b/lib/lua-repl/t/plugin-repl-tests.lua
@@ -0,0 +1,75 @@
+-- vim:foldmethod=marker
+local r = require 'repl' -- we don't call it 'repl' so we don't shadow
+ -- repl in the plugin environment
+pcall(require, 'luarocks.loader')
+require 'Test.More'
+local utils = require 'test-utils'
+
+plan(5)
+
+local clone = r:clone()
+
+do -- basic tests {{{
+ local with_plugin = clone:clone()
+
+ function with_plugin:foo()
+ end
+
+ local line_no
+
+ local _, err = pcall(function()
+ with_plugin:loadplugin(function()
+ line_no = utils.next_line_number()
+ function repl:foo()
+ end
+ end)
+ end)
+
+ like(err, string.format("%d: The 'foo' method already exists", line_no))
+
+ with_plugin:loadplugin(function()
+ function repl:bar()
+ return 17
+ end
+ end)
+
+ is(with_plugin:bar(), 17)
+
+ with_plugin:loadplugin(function()
+ repl.baz = 18
+ end)
+
+ is(with_plugin.baz, 18)
+end -- }}}
+
+do -- conflict tests {{{
+ local clone = r:clone()
+ local line_no
+
+ clone:loadplugin(function()
+ function repl:foo()
+ end
+ end)
+
+ local _, err = pcall(function()
+ clone:loadplugin(function()
+ line_no = utils.next_line_number()
+ function repl:foo()
+ end
+ end)
+ end)
+
+ like(err, tostring(line_no) .. ": The 'foo' method already exists")
+end -- }}}
+
+do -- proxy tests {{{
+ local clone = r:clone()
+
+ clone:loadplugin(function()
+ features = 'foo'
+ end)
+
+ clone:loadplugin(function()
+ ok(repl:hasfeature 'foo')
+ end)
+end -- }}}
diff --git a/lib/lua-repl/t/sync-repl-tests.lua b/lib/lua-repl/t/sync-repl-tests.lua
new file mode 100644
index 00000000..d5249ec2
--- /dev/null
+++ b/lib/lua-repl/t/sync-repl-tests.lua
@@ -0,0 +1,52 @@
+-- vim:foldmethod=marker
+local sync = require 'repl.sync'
+pcall(require, 'luarocks.loader')
+require 'Test.More'
+
+plan(13)
+
+local clone = sync:clone()
+local resultlist = {}
+
+function clone:lines()
+ local index = 0
+ local function iterator(s)
+ index = index + 1
+ return s[index]
+ end
+
+ return iterator, {
+ 'return foo',
+ 'return 1',
+ 'return "bar"',
+ 'return {}',
+ 'return 1, 2, 3',
+ }
+end
+
+function clone:showprompt()
+end
+
+function clone:displayresults(results)
+ resultlist[#resultlist + 1] = results
+end
+
+clone:run()
+
+is(#resultlist, 5)
+is(resultlist[1].n, 1)
+is(resultlist[1][1], nil)
+
+is(resultlist[2].n, 1)
+is(resultlist[2][1], 1)
+
+is(resultlist[3].n, 1)
+is(resultlist[3][1], 'bar')
+
+is(resultlist[4].n, 1)
+is(type(resultlist[4][1]), 'table')
+
+is(resultlist[5].n, 3)
+is(resultlist[5][1], 1)
+is(resultlist[5][2], 2)
+is(resultlist[5][3], 3)