mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-02-03 21:50:05 +08:00
414 lines
11 KiB
Lua
414 lines
11 KiB
Lua
|
pcall(require, "luacov")
|
||
|
|
||
|
local h = require("tests.helpers")
|
||
|
local Object = require("nui.object")
|
||
|
local spy = require("luassert.spy")
|
||
|
|
||
|
local function assert_class(Class, SuperClass, name)
|
||
|
h.eq(type(Class), "table")
|
||
|
|
||
|
h.eq(Class.super, SuperClass)
|
||
|
h.eq(Class.name, name)
|
||
|
h.eq(tostring(Class), "class " .. name)
|
||
|
|
||
|
h.eq(type(Class.new), "function")
|
||
|
h.eq(type(Class.extend), "function")
|
||
|
|
||
|
local is_callable = pcall(function()
|
||
|
return Class()
|
||
|
end)
|
||
|
h.eq(is_callable, true)
|
||
|
end
|
||
|
|
||
|
local function assert_instance(instance, Class)
|
||
|
h.eq(instance.class, Class)
|
||
|
h.eq(tostring(instance), "instance of class " .. Class.name)
|
||
|
|
||
|
h.eq(instance.name, nil)
|
||
|
h.eq(instance.super, nil)
|
||
|
h.eq(instance.static, nil)
|
||
|
|
||
|
h.eq(instance.new, nil)
|
||
|
h.eq(instance.extend, nil)
|
||
|
end
|
||
|
|
||
|
local function create_classes(...)
|
||
|
local by_name = {}
|
||
|
local classes = {}
|
||
|
|
||
|
for i, def in ipairs({ ... }) do
|
||
|
if type(def) == "string" then
|
||
|
local class = Object(def)
|
||
|
assert_class(class, nil, def)
|
||
|
by_name[def] = class
|
||
|
classes[i] = class
|
||
|
elseif type(def) == "table" then
|
||
|
local super = type(def[2]) == "table" and def[2] or (by_name[def[2]] and by_name[def[2]] or nil)
|
||
|
local class = super and super:extend(def[1]) or Object(def[1])
|
||
|
assert_class(class, super, def[1])
|
||
|
by_name[def[1]] = class
|
||
|
classes[i] = class
|
||
|
else
|
||
|
error("invalid argument")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return unpack(classes)
|
||
|
end
|
||
|
|
||
|
describe("nui.object", function()
|
||
|
describe("class", function()
|
||
|
it("can be created", function()
|
||
|
local Class = Object("Class")
|
||
|
assert_class(Class, nil, "Class")
|
||
|
end)
|
||
|
|
||
|
describe("static", function()
|
||
|
describe("method", function()
|
||
|
describe(":new", function()
|
||
|
it("is called when creating instance", function()
|
||
|
local Class = Object("Class")
|
||
|
|
||
|
spy.on(Class.static, "new")
|
||
|
Class()
|
||
|
assert.spy(Class.static.new).called_with(Class)
|
||
|
Class.static.new:revert()
|
||
|
|
||
|
spy.on(Class.static, "new")
|
||
|
Class:new()
|
||
|
assert.spy(Class.static.new).called_with(Class)
|
||
|
Class.static.new:revert()
|
||
|
end)
|
||
|
|
||
|
it("creates new instance", function()
|
||
|
local Class = Object("Class")
|
||
|
|
||
|
local instance = Class:new()
|
||
|
assert_instance(instance, Class)
|
||
|
end)
|
||
|
end)
|
||
|
|
||
|
describe(":extend", function()
|
||
|
it("creates subclass", function()
|
||
|
local Class = Object("Class")
|
||
|
|
||
|
local SubClass = Class:extend("SubClass")
|
||
|
assert_class(SubClass, Class, "SubClass")
|
||
|
end)
|
||
|
end)
|
||
|
|
||
|
describe(":is_subclass_of", function()
|
||
|
it("works", function()
|
||
|
local A, B, C = create_classes("A", { "B", "A" }, { "C", "B" })
|
||
|
|
||
|
for _, class in ipairs({ A, B, C }) do
|
||
|
h.eq(class.is_subclass_of, Object.is_subclass)
|
||
|
end
|
||
|
|
||
|
h.eq(A:is_subclass_of(A), false)
|
||
|
h.eq(A:is_subclass_of(B), false)
|
||
|
h.eq(A:is_subclass_of(C), false)
|
||
|
|
||
|
h.eq(B:is_subclass_of(A), true)
|
||
|
h.eq(B:is_subclass_of(B), false)
|
||
|
h.eq(B:is_subclass_of(C), false)
|
||
|
|
||
|
h.eq(C:is_subclass_of(A), true)
|
||
|
h.eq(C:is_subclass_of(B), true)
|
||
|
h.eq(C:is_subclass_of(C), false)
|
||
|
end)
|
||
|
end)
|
||
|
end)
|
||
|
|
||
|
local function define_static_say_level(A)
|
||
|
A.static.level = 1
|
||
|
function A.static.say_level(class)
|
||
|
return "Level: " .. class.level
|
||
|
end
|
||
|
|
||
|
h.eq(A.level, 1)
|
||
|
h.eq(A:say_level(), "Level: 1")
|
||
|
end
|
||
|
|
||
|
it("can be defined for class", function()
|
||
|
local A = create_classes("A")
|
||
|
define_static_say_level(A)
|
||
|
end)
|
||
|
|
||
|
it("is inherited by subclass", function()
|
||
|
local A, B = create_classes("A", { "B", "A" })
|
||
|
|
||
|
define_static_say_level(A)
|
||
|
|
||
|
h.eq(B.level, 1)
|
||
|
h.eq(B:say_level(), "Level: 1")
|
||
|
|
||
|
local C, D = create_classes({ "C", A }, { "D", B })
|
||
|
|
||
|
h.eq(C.level, 1)
|
||
|
h.eq(C:say_level(), "Level: 1")
|
||
|
|
||
|
h.eq(D.level, 1)
|
||
|
h.eq(D:say_level(), "Level: 1")
|
||
|
end)
|
||
|
|
||
|
it("can be redefined for subclass", function()
|
||
|
local A = create_classes("A")
|
||
|
define_static_say_level(A)
|
||
|
|
||
|
local B = create_classes({ "B", A })
|
||
|
|
||
|
B.static.level = 2
|
||
|
h.eq(B:say_level(), "Level: 2")
|
||
|
|
||
|
function B.static.say_level(class)
|
||
|
return "LEVEL: " .. class.level
|
||
|
end
|
||
|
h.eq(B:say_level(), "LEVEL: 2")
|
||
|
|
||
|
local C, D = create_classes({ "C", A }, { "D", B })
|
||
|
|
||
|
C.static.level = 2
|
||
|
h.eq(C:say_level(), "Level: 2")
|
||
|
|
||
|
D.static.level = 3
|
||
|
h.eq(D:say_level(), "LEVEL: 3")
|
||
|
end)
|
||
|
|
||
|
it("for subclass does not affect super", function()
|
||
|
local A = create_classes("A")
|
||
|
define_static_say_level(A)
|
||
|
|
||
|
local B = create_classes({ "B", A })
|
||
|
|
||
|
B.static.level = 2
|
||
|
function B.static.say_level(class)
|
||
|
return "LEVEL: " .. class.level
|
||
|
end
|
||
|
|
||
|
h.eq(A:say_level(), "Level: 1")
|
||
|
|
||
|
local C = create_classes({ "C", B })
|
||
|
|
||
|
function C.static.say_name(class)
|
||
|
return class.name
|
||
|
end
|
||
|
|
||
|
h.eq(C:say_name(), "C")
|
||
|
|
||
|
h.eq(type(C.say_name), "function")
|
||
|
h.eq(type(B.say_name), "nil")
|
||
|
h.eq(type(A.say_name), "nil")
|
||
|
end)
|
||
|
end)
|
||
|
|
||
|
describe("instance", function()
|
||
|
it("can be created", function()
|
||
|
local A = create_classes("A")
|
||
|
|
||
|
local a = A:new()
|
||
|
assert_instance(a, A)
|
||
|
end)
|
||
|
|
||
|
describe("method", function()
|
||
|
describe(":is_instance_of", function()
|
||
|
it("works", function()
|
||
|
local A, B, C, D = create_classes("A", { "B", "A" }, { "C", "B" }, "D")
|
||
|
|
||
|
local a, b, c, d = A:new(), B:new(), C:new(), D:new()
|
||
|
|
||
|
for _, instance in ipairs({ a, b, c, d }) do
|
||
|
h.eq(instance.is_instance_of, Object.is_instance)
|
||
|
end
|
||
|
|
||
|
h.eq(a:is_instance_of(A), true)
|
||
|
h.eq(a:is_instance_of(B), false)
|
||
|
h.eq(a:is_instance_of(C), false)
|
||
|
h.eq(a:is_instance_of(D), false)
|
||
|
|
||
|
h.eq(b:is_instance_of(A), true)
|
||
|
h.eq(b:is_instance_of(B), true)
|
||
|
h.eq(b:is_instance_of(C), false)
|
||
|
h.eq(b:is_instance_of(D), false)
|
||
|
|
||
|
h.eq(c:is_instance_of(A), true)
|
||
|
h.eq(c:is_instance_of(B), true)
|
||
|
h.eq(c:is_instance_of(C), true)
|
||
|
h.eq(c:is_instance_of(D), false)
|
||
|
|
||
|
h.eq(d:is_instance_of(A), false)
|
||
|
h.eq(d:is_instance_of(B), false)
|
||
|
h.eq(d:is_instance_of(C), false)
|
||
|
h.eq(d:is_instance_of(D), true)
|
||
|
end)
|
||
|
end)
|
||
|
|
||
|
it("can be defined", function()
|
||
|
local A = create_classes("A")
|
||
|
|
||
|
function A:before_instance_creation()
|
||
|
return "before " .. self.class.name .. " instance"
|
||
|
end
|
||
|
|
||
|
local a = A:new()
|
||
|
|
||
|
function A:after_instance_creation()
|
||
|
return "after " .. self.class.name .. " instance"
|
||
|
end
|
||
|
|
||
|
h.eq(a:before_instance_creation(), "before A instance")
|
||
|
h.eq(a:after_instance_creation(), "after A instance")
|
||
|
end)
|
||
|
|
||
|
it("can be inherited", function()
|
||
|
local A, B = create_classes("A", { "B", "A" })
|
||
|
|
||
|
function A:say_class_name()
|
||
|
return self.class.name
|
||
|
end
|
||
|
|
||
|
local a = A:new()
|
||
|
h.eq(a:say_class_name(), "A")
|
||
|
|
||
|
local b = B:new()
|
||
|
h.eq(b:say_class_name(), "B")
|
||
|
|
||
|
local C = create_classes({ "C", B })
|
||
|
|
||
|
local c = C:new()
|
||
|
h.eq(c:say_class_name(), "C")
|
||
|
end)
|
||
|
|
||
|
it("can be redefined", function()
|
||
|
local A, B = create_classes("A", { "B", "A" })
|
||
|
|
||
|
function A:say_class_name()
|
||
|
return self.class.name
|
||
|
end
|
||
|
|
||
|
local a = A:new()
|
||
|
h.eq(a:say_class_name(), "A")
|
||
|
|
||
|
function B:say_class_name()
|
||
|
return string.lower(self.class.name)
|
||
|
end
|
||
|
|
||
|
local b = B:new()
|
||
|
h.eq(b:say_class_name(), "b")
|
||
|
|
||
|
local C = create_classes({ "C", B })
|
||
|
|
||
|
local c = C:new()
|
||
|
h.eq(c:say_class_name(), "c")
|
||
|
|
||
|
function C:say_class_name()
|
||
|
return string.rep(self.class.name, 3)
|
||
|
end
|
||
|
|
||
|
h.eq(c:say_class_name(), "CCC")
|
||
|
|
||
|
C.say_class_name = nil
|
||
|
|
||
|
h.eq(c:say_class_name(), "c")
|
||
|
|
||
|
B.say_class_name = nil
|
||
|
|
||
|
h.eq(c:say_class_name(), "C")
|
||
|
end)
|
||
|
end)
|
||
|
|
||
|
describe("metamethod", function()
|
||
|
describe("__index", function()
|
||
|
it("can be set to table", function()
|
||
|
local A = create_classes("A")
|
||
|
|
||
|
function A:upper(str) -- luacheck: no unused args
|
||
|
return string.upper(str)
|
||
|
end
|
||
|
|
||
|
A.__index = {
|
||
|
upper = function(_, str)
|
||
|
return str
|
||
|
end,
|
||
|
lower = function(_, str)
|
||
|
return string.lower(str)
|
||
|
end,
|
||
|
}
|
||
|
|
||
|
local a = A()
|
||
|
|
||
|
h.eq(a:upper("y"), "Y")
|
||
|
|
||
|
h.eq(a:lower("Y"), "y")
|
||
|
|
||
|
A.__index = nil
|
||
|
|
||
|
h.eq(type(a.lower), "nil")
|
||
|
end)
|
||
|
|
||
|
it("can be set to function", function()
|
||
|
local A = create_classes("A")
|
||
|
|
||
|
function A:upper(str) -- luacheck: no unused args
|
||
|
return string.upper(str)
|
||
|
end
|
||
|
|
||
|
local index = {
|
||
|
upper = function(self, str) -- luacheck: no unused args
|
||
|
return str
|
||
|
end,
|
||
|
lower = function(self, str) -- luacheck: no unused args
|
||
|
return string.lower(str)
|
||
|
end,
|
||
|
}
|
||
|
|
||
|
A.__index = function(self, key) -- luacheck: no unused args
|
||
|
return index[key]
|
||
|
end
|
||
|
|
||
|
local a = A()
|
||
|
|
||
|
h.eq(a:upper("y"), "Y")
|
||
|
|
||
|
h.eq(a:lower("Y"), "y")
|
||
|
|
||
|
A.__index = nil
|
||
|
|
||
|
h.eq(type(a.lower), "nil")
|
||
|
end)
|
||
|
end)
|
||
|
|
||
|
describe("__tostring", function()
|
||
|
it("can be redefined", function()
|
||
|
local A, B = create_classes("A", { "B", "A" })
|
||
|
|
||
|
local a = A()
|
||
|
|
||
|
h.eq(tostring(a), "instance of class A")
|
||
|
|
||
|
function A:__tostring()
|
||
|
return "class " .. self.class.name .. "'s child"
|
||
|
end
|
||
|
|
||
|
h.eq(tostring(a), "class A's child")
|
||
|
|
||
|
local b = B()
|
||
|
|
||
|
h.eq(tostring(b), "class B's child")
|
||
|
|
||
|
function B:__tostring()
|
||
|
return "child of " .. self.class.name
|
||
|
end
|
||
|
|
||
|
h.eq(tostring(b), "child of B")
|
||
|
|
||
|
B.__tostring = nil
|
||
|
|
||
|
h.eq(tostring(b), "class B's child")
|
||
|
end)
|
||
|
end)
|
||
|
end)
|
||
|
end)
|
||
|
end)
|
||
|
end)
|