1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
|
import("core.base.global")
-- write a minimal plugin that prints its name when run
--
-- the plugins of an addon are placed in its `plugins` payload directory,
-- e.g. <addondir>/plugins/<name>/xmake.lua
function _write_plugin(dir, name)
io.writefile(path.join(dir, "xmake.lua"), string.format([[
task("%s")
set_category("plugin")
on_run("main")
set_menu {usage = "xmake %s", description = "say hello from %s"}
]], name, name, name))
io.writefile(path.join(dir, "main.lua"), string.format([[function main() print("%s") end]], name))
end
-- write a minimal template into the `templates` payload directory of an addon,
-- e.g. <addondir>/templates/<language>/<templateid>/xmake.lua
function _write_template(dir, lang, templateid)
local templatedir = path.join(dir, "templates", lang, templateid)
io.writefile(path.join(templatedir, "xmake.lua"), [[
target("${TARGET_NAME}")
set_kind("binary")
add_files("src/*.c")
]])
io.writefile(path.join(templatedir, "src", "main.c"), [[
int main(int argc, char** argv) { return 0; }
]])
end
-- write an addon payload directory, it provides a plugin and a template
function _write_addon(dir, name)
_write_plugin(path.join(dir, "plugins", name), name)
_write_template(dir, "c", name)
end
-- write an addon package description, its payloads are placed in the `src` directory
--
-- addons in a repository are described as packages, e.g. <repodir>/addons/<first-letter>/<name>/xmake.lua
function _write_addon_package(dir, name)
io.writefile(path.join(dir, "xmake.lua"), string.format([[
package("%s")
set_kind("addon")
set_description("say hello from %s")
set_sourcedir(path.join(os.scriptdir(), "src"))
]], name, name))
_write_addon(path.join(dir, "src"), name)
end
-- create a temporary addon repository (packages layout: addons/<first-letter>/<name>) and register it
--
-- @return reponame, names, cleanup
function _mock_repo(basenames)
local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "")
local reponame = "addon-test-repo-" .. suffix
local repodir = os.tmpfile() .. ".addon-repo"
local names = {}
for _, base in ipairs(basenames) do
local name = base .. "-" .. suffix
_write_addon_package(path.join(repodir, "addons", name:sub(1, 1), name), name)
table.insert(names, name)
end
-- register the repository into the cache
local cachefile = path.join(global.cachedir(), "repository")
local cache = os.isfile(cachefile) and io.load(cachefile) or {}
cache.repositories = cache.repositories or {}
cache.repositories[reponame] = {repodir}
io.save(cachefile, cache)
-- we need to clear the quick search cache, it will be rebuilt on the next search
os.tryrm(path.join(global.cachedir(), "quick_search"))
local function cleanup()
for _, name in ipairs(names) do
os.tryrm(path.join(global.directory(), "addons", name))
try { function () os.runv("xmake", {"addon", "--remove", name}) end }
end
local cache = os.isfile(cachefile) and io.load(cachefile) or {}
if cache.repositories then
cache.repositories[reponame] = nil
end
io.save(cachefile, cache)
os.tryrm(path.join(global.cachedir(), "quick_search"))
os.tryrm(repodir)
end
return reponame, names, cleanup
end
-- run the given function with a mocked repository, we always clean it up even if the test fails
function _with_repo(basenames, func)
local reponame, names, cleanup = _mock_repo(basenames)
try
{
function ()
func(reponame, names)
end,
finally
{
cleanup
}
}
end
-- install an addon from a repository, by plain name and by repo@name
function test_install_from_repo(t)
_with_repo({"hello"}, function (reponame, names)
local name = names[1]
-- install by plain name (searched across all repositories)
os.runv("xmake", {"addon", "--install", "-y", name})
t:require(os.iorunv("xmake", {name}):find(name, 1, true))
-- reinstall by repo@name
os.runv("xmake", {"addon", "--remove", name})
os.runv("xmake", {"addon", "--install", "-y", reponame .. "@" .. name})
t:require(os.iorunv("xmake", {name}):find(name, 1, true))
os.runv("xmake", {"addon", "--remove", name})
end)
end
-- the templates of an installed addon can be used by `xmake create`
function test_install_templates(t)
_with_repo({"hello"}, function (_, names)
local name = names[1]
os.runv("xmake", {"addon", "--install", "-y", name})
-- this template should be listed and grouped by its addon name
local out = os.iorunv("xmake", {"create", "--list"})
t:require(out:find(name, 1, true))
-- we can create a new project from it
local projectdir = os.tmpfile() .. ".addon-project"
os.tryrm(projectdir)
os.runv("xmake", {"create", "-l", "c", "-t", name, "-P", projectdir})
t:require(os.isfile(path.join(projectdir, "xmake.lua")))
t:require(os.isfile(path.join(projectdir, "src", "main.c")))
os.tryrm(projectdir)
os.runv("xmake", {"addon", "--remove", name})
end)
end
-- install an addon from a local directory, then remove it
function test_install_from_local(t)
local suffix = path.filename(os.tmpfile()):gsub("[^%w]", "")
local name = "hello-local-" .. suffix
local dir = path.join(os.tmpfile() .. ".addon-local", name)
_write_addon(dir, name)
os.runv("xmake", {"addon", "--install", dir})
t:require(os.iorunv("xmake", {name}):find(name, 1, true))
-- the removed addon should no longer be runnable
os.runv("xmake", {"addon", "--remove", name})
t:require_not(try { function () os.runv("xmake", {name}); return true end })
os.tryrm(path.directory(dir))
end
-- --list shows the installed addons and their payloads
function test_list(t)
_with_repo({"hello", "world"}, function (_, names)
-- install the first addon, leave the second only available
os.runv("xmake", {"addon", "--install", "-y", names[1]})
local out = os.iorunv("xmake", {"addon", "--list"})
t:require(out:find("the installed addons:", 1, true))
t:require(out:find(names[1], 1, true))
-- the payloads of the installed addon should be shown, e.g. (plugins, templates)
t:require(out:find("plugins", 1, true))
t:require(out:find("templates", 1, true))
-- the addon which is not installed should be shown in the available addons
t:require(out:find("the available addons:", 1, true))
t:require(out:find(names[2], 1, true))
os.runv("xmake", {"addon", "--remove", names[1]})
end)
end
-- --search finds the addons in the repositories, it reuses `xrepo search --addon`
function test_search(t)
_with_repo({"hello"}, function (_, names)
local name = names[1]
local out = os.iorunv("xmake", {"addon", "--search", name})
t:require(out:find(name, 1, true))
-- the addons should not be found by the package search
local packages_out = os.iorunv("xrepo", {"search", name})
t:require_not(packages_out:find(name, 1, true))
end)
end
-- invalid installs should fail
function test_install_invalid(t)
t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "addon-test-missing"}); return true end })
t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "somerepo@.."}); return true end })
end
|