diff options
| author | ruki <[email protected]> | 2026-08-09 23:34:58 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2026-08-09 23:34:58 +0800 |
| commit | c7172a3a0cce98e395567202e732ebb86b475360 (patch) | |
| tree | 1e719e5f6e3d8f34640d6b9c8d27c7f877a40d1f | |
| parent | d8459009d72b251f335544a3f25e5842e9184c43 (diff) | |
improve addon test
| -rw-r--r-- | tests/actions/addon/demo-addon-dep/rules/dep/xmake.lua | 10 | ||||
| -rw-r--r-- | tests/actions/addon/demo-addon/README.md | 5 | ||||
| -rw-r--r-- | tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/src/main.c | 3 | ||||
| -rw-r--r-- | tests/actions/addon/test.lua | 567 |
4 files changed, 145 insertions, 440 deletions
diff --git a/tests/actions/addon/demo-addon-dep/rules/dep/xmake.lua b/tests/actions/addon/demo-addon-dep/rules/dep/xmake.lua new file mode 100644 index 000000000..be4805e92 --- /dev/null +++ b/tests/actions/addon/demo-addon-dep/rules/dep/xmake.lua @@ -0,0 +1,10 @@ +rule("dep") + + -- depend on a rule of the other addon + add_deps("@addon/demo-addon/base") + + on_load(function (target) + -- import a module of the other addon + import("@addon.demo-addon.greeting") + print("demo-addon-dep: " .. greeting("dep")) + end) diff --git a/tests/actions/addon/demo-addon/README.md b/tests/actions/addon/demo-addon/README.md deleted file mode 100644 index 8ee255f64..000000000 --- a/tests/actions/addon/demo-addon/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# demo-addon - -A complete addon used by the addon tests, it provides all the payload kinds -and uses the optional `src` layout: only `src/` is installed, this README and -the `tests` directory are ours and must not be installed. diff --git a/tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/src/main.c b/tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/src/main.c index e5a9ccbdf..9b130982d 100644 --- a/tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/src/main.c +++ b/tests/actions/addon/demo-addon/src/templates/c/demoaddon/hello/src/main.c @@ -1,4 +1,3 @@ -int main(int argc, char** argv) -{ +int main(int argc, char** argv) { return 0; } diff --git a/tests/actions/addon/test.lua b/tests/actions/addon/test.lua index 1ead7acac..6a65d233c 100644 --- a/tests/actions/addon/test.lua +++ b/tests/actions/addon/test.lua @@ -1,185 +1,96 @@ import("core.base.global") --- the payloads of the mocked addons +-- the addon fixtures, @see tests/actions/addon/demo-addon -- --- the plugins and templates are not namespaced, so they are named with the addon name to keep them unique, --- the other payloads are always referenced with `@addon/<addon>/` or `@self/`, so they can use fixed names -local RULENAME = "flash" -local RULEBASENAME = "base" -local TOOLCHAINAME = "xtensa" -local MODULENAME = "sdkconfig" -local INCLUDESNAME = "check" +-- demo-addon: the `src` layout, it provides all the payload kinds +-- demo-addon-dep: the plain layout (payloads at the root), it depends on demo-addon +local ADDON = "demo-addon" +local ADDON_DEP = "demo-addon-dep" --- write a minimal plugin, it imports a module of its own addon with `@self` --- --- 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() - import("@self.%s") - print("%s: " .. %s()) -end -]], MODULENAME, name, MODULENAME)) -end - --- write a minimal template, 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 two rules, the main one depends on the other one of the same addon with `@self` --- --- e.g. <addondir>/rules/<name>/xmake.lua -function _write_rules(dir, name) - io.writefile(path.join(dir, "rules", RULEBASENAME, "xmake.lua"), string.format([[ -rule("%s") - on_load(function (target) - print("hello from rule %s") - end) -]], RULEBASENAME, RULEBASENAME)) - io.writefile(path.join(dir, "rules", RULENAME, "xmake.lua"), string.format([[ -rule("%s") - add_deps("@self/%s") - on_load(function (target) - import("@self.%s") - print("hello from rule %s of %s: " .. %s()) - end) -]], RULENAME, RULEBASENAME, MODULENAME, RULENAME, name, MODULENAME)) +function _addondir(name) + return path.join(os.scriptdir(), name) end --- write a minimal toolchain, e.g. <addondir>/toolchains/<name>/xmake.lua -function _write_toolchain(dir, name) - io.writefile(path.join(dir, "toolchains", TOOLCHAINAME, "xmake.lua"), string.format([[ -toolchain("%s") - set_kind("standalone") - set_description("hello from toolchain %s of %s") - on_load(function (toolchain) - toolchain:set("toolset", "cc", "gcc") - end) -]], TOOLCHAINAME, TOOLCHAINAME, name)) +function _installdir(name) + return path.join(global.directory(), "addons", name, "latest") end --- write a minimal module, e.g. <addondir>/modules/<name>.lua -function _write_module(dir, name) - io.writefile(path.join(dir, "modules", MODULENAME .. ".lua"), string.format([[ -function main() - return "hello from module %s of %s" -end -]], MODULENAME, name)) +function _remove(name) + try { function () os.runv("xmake", {"addon", "--remove", "--force", name}) end } end --- write a minimal includes file, e.g. <addondir>/includes/<name>/xmake.lua -function _write_includes(dir, name) - io.writefile(path.join(dir, "includes", INCLUDESNAME, "xmake.lua"), string.format([[ -print("hello from includes %s of %s") -]], INCLUDESNAME, name)) +-- install the given addon fixture from its local directory +function _install(name) + _remove(name) + os.runv("xmake", {"addon", "--install", _addondir(name)}) end --- write an addon payload directory, it provides all the supported payloads -function _write_addon(dir, name) - _write_plugin(path.join(dir, "plugins", name), name) - _write_template(dir, "c", name) - _write_rules(dir, name) - _write_toolchain(dir, name) - _write_module(dir, name) - _write_includes(dir, 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 --- --- @param opt the options, e.g. {deps = {"other-addon"}} -function _write_addon_package(dir, name, opt) - opt = opt or {} - local deps = "" - for _, depname in ipairs(opt.deps) do - deps = deps .. string.format("\n add_deps(\"%s\", {kind = \"addon\"})", depname) +-- run the given function with the addons installed, we always remove them again +function _with_addons(names, func) + for _, name in ipairs(names) do + _install(name) end - 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"))%s -]], name, name, deps)) - _write_addon(path.join(dir, "src"), name) + try + { + func, + finally + { + function () + for _, name in ipairs(names) do + _remove(name) + end + end + } + } end --- create a temporary addon repository (packages layout: addons/<first-letter>/<name>) and register it --- --- @param basenames the addon base names, e.g. {"hello", "world"} --- @param opt the options, e.g. {deps = {hello = {2}}}, the first addon depends on the second one +-- create a temporary repository which indexes the addon fixtures, and register it -- --- @return reponame, names, cleanup -function _mock_repo(basenames, opt) - opt = opt or {} +-- @note the recipes only point at the fixtures with `set_sourcedir`, we need not generate any payload +function _with_repo(func) 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 - table.insert(names, base .. "-" .. suffix) - end - for idx, base in ipairs(basenames) do - local name = names[idx] - local deps = {} - for _, depidx in ipairs(table.wrap((opt.deps or {})[base])) do - table.insert(deps, names[depidx]) - end - _write_addon_package(path.join(repodir, "addons", name:sub(1, 1), name), name, {deps = deps}) + local recipes = { + -- the addon itself + [ADDON] = ("set_sourcedir(%q)"):format(path.join(_addondir(ADDON), "src")), + -- the addon which depends on the addon above + [ADDON_DEP] = ("set_sourcedir(%q)\n add_deps(%q, {kind = \"addon\"})"):format(_addondir(ADDON_DEP), ADDON), + -- the addon which provides the same plugin and template names as demo-addon + ["demo-addon-clone"] = ("set_sourcedir(%q)"):format(path.join(_addondir(ADDON), "src")) + } + for name, body in pairs(recipes) do + io.writefile(path.join(repodir, "addons", name:sub(1, 1), name, "xmake.lua"), + ("package(%q)\n set_kind(\"addon\")\n set_description(\"the addon fixture of the tests\")\n %s\n"):format(name, body)) end - -- register the repository into the cache + -- register the repository and clear the quick search cache, it will be rebuilt on the next search 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 - try { function () os.runv("xmake", {"addon", "--remove", "--force", name}) end } - os.tryrm(path.join(global.directory(), "addons", name)) - 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, opt) - local reponame, names, cleanup = _mock_repo(basenames, opt) try { function () - func(reponame, names) + func(reponame) end, finally { - cleanup + function () + for name, _ in pairs(recipes) do + _remove(name) + 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 } } end @@ -193,15 +104,8 @@ function _config_project(content) local out, errors try { - function () - out = os.iorunv("xmake", {"config", "-y"}) - end, - catch - { - function (e) - errors = e - end - }, + function () out = os.iorunv("xmake", {"config", "-y"}) end, + catch { function (e) errors = e end }, finally { function () @@ -216,322 +120,119 @@ function _config_project(content) return out 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}) +-- only the payloads should be installed, our own files should not +function test_install(t) + _with_addons({ADDON}, function () + local installdir = _installdir(ADDON) + for _, payloaddir in ipairs({"plugins", "rules", "toolchains", "modules", "includes", "templates"}) do + t:require(os.isdir(path.join(installdir, payloaddir))) + end + for _, ourfile in ipairs({"src", "tests"}) do + t:require_not(os.exists(path.join(installdir, ourfile))) + end + t:require(os.iorunv("xmake", {"addon", "--list"}):find(ADDON, 1, true)) end) -end - --- an addon can reference its own payloads with `@self`, it never needs to know its installed name -function test_self_reference(t) - _with_repo({"hello"}, function (_, names) - local name = names[1] - os.runv("xmake", {"addon", "--install", "-y", name}) - -- the plugin imports its own module with `import("@self.<module>")` - local out = os.iorunv("xmake", {name}) - t:require(out:find("hello from module " .. MODULENAME .. " of " .. name, 1, true)) + -- it should not be runnable after removing it + t:require_not(try { function () os.runv("xmake", {"demo_hello"}); return true end }) +end - -- the rule depends on the other rule of the same addon with `add_deps("@self/<rule>")` - out = _config_project(string.format([[ -target("test") - set_kind("phony") - add_rules("@addon/%s/%s") -]], name, RULENAME)) - t:require(out:find("hello from rule " .. RULEBASENAME, 1, true)) - t:require(out:find("hello from rule " .. RULENAME .. " of " .. name, 1, true)) +-- all the payload kinds should be activated +function test_payloads(t) + _with_addons({ADDON}, function () - os.runv("xmake", {"addon", "--remove", name}) - end) -end + -- the plugin imports a module of its own addon with `@self` + t:require(os.iorunv("xmake", {"demo_hello", "-n", "xmake"}):find("hello from demo-addon: xmake", 1, true)) --- 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}) + -- the module can be imported with the addon name + local script = "import(\"@addon.demo-addon.greeting\"); print(greeting(\"module\"))" + t:require(os.iorunv("xmake", {"lua", "-c", script}):find("hello from demo-addon: module", 1, true)) - -- 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)) + -- the toolchain can be loaded with the addon name + local script2 = "import(\"core.tool.toolchain\"); print(toolchain.load(\"@addon/demo-addon/demo\"):get(\"description\"))" + t:require(os.iorunv("xmake", {"lua", "-c", script2}):find("the demo toolchain", 1, true)) - -- we can create a new project from it + -- the template should be listed and can create a project + t:require(os.iorunv("xmake", {"create", "--list", "-l", "c"}):find("demoaddon.hello", 1, true)) 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"))) + os.runv("xmake", {"create", "-l", "c", "-t", "demoaddon.hello", "-P", projectdir}) t:require(os.isfile(path.join(projectdir, "src", "main.c"))) os.tryrm(projectdir) - os.runv("xmake", {"addon", "--remove", name}) - end) -end - --- the rules of an installed addon can be used with the `@addon/<addon>/` prefix -function test_install_rules(t) - _with_repo({"hello"}, function (_, names) - local name = names[1] - os.runv("xmake", {"addon", "--install", "-y", name}) - - -- it should be found in the global rules - local script = string.format("import(\"core.project.rule\"); print(rule.rule(\"@addon/%s/%s\") ~= nil)", name, RULENAME) - t:require(os.iorunv("xmake", {"lua", "-c", script}):find("true", 1, true)) - - -- the addon name is always required - local script2 = string.format("import(\"core.project.rule\"); print(rule.rule(\"@addon/%s\"))", RULENAME) - t:require_not(try { function () os.iorunv("xmake", {"lua", "-c", script2}); return true end }) - - os.runv("xmake", {"addon", "--remove", name}) - end) -end - --- the includes of an installed addon can be used with the `@addon/<addon>/` prefix -function test_install_includes(t) - _with_repo({"hello"}, function (_, names) - local name = names[1] - os.runv("xmake", {"addon", "--install", "-y", name}) - - local out = _config_project(string.format([[ -includes("@addon/%s/%s") + -- the includes and the rules should work in a project, the app rule depends on + -- the other rule of the same addon with `add_deps("@self/base")` + local out = _config_project([[ +includes("@addon/demo-addon/check") target("test") set_kind("phony") -]], name, INCLUDESNAME)) - t:require(out:find("hello from includes " .. INCLUDESNAME .. " of " .. name, 1, true)) - - os.runv("xmake", {"addon", "--remove", name}) - end) -end - --- the toolchains of an installed addon can be loaded with the `@addon/<addon>/` prefix -function test_install_toolchains(t) - _with_repo({"hello"}, function (_, names) - local name = names[1] - os.runv("xmake", {"addon", "--install", "-y", name}) - - local script = string.format("import(\"core.tool.toolchain\"); print(toolchain.load(\"@addon/%s/%s\"):get(\"description\"))", name, TOOLCHAINAME) - t:require(os.iorunv("xmake", {"lua", "-c", script}):find("hello from toolchain " .. TOOLCHAINAME .. " of " .. name, 1, true)) - - -- it can also be bound to a package, e.g. "@addon/<addon>/clang@llvm" - local script2 = string.format("import(\"core.tool.toolchain\"); print(toolchain.load(\"@addon/%s/%s@llvm\"):config(\"packages\"))", name, TOOLCHAINAME) - t:require(os.iorunv("xmake", {"lua", "-c", script2}):find("llvm", 1, true)) - - -- it should not be found without the `@addon/<addon>/` prefix - local script3 = string.format("import(\"core.tool.toolchain\"); print(toolchain.load(\"%s\"))", TOOLCHAINAME) - t:require_not(try { function () os.iorunv("xmake", {"lua", "-c", script3}); return true end }) - - os.runv("xmake", {"addon", "--remove", name}) + add_rules("@addon/demo-addon/app") +]]) + t:require(out:find("demo-addon: includes check is loaded", 1, true)) + t:require(out:find("demo-addon: rule base is loaded", 1, true)) + t:require(out:find("demo-addon: rule app is loaded, hello from demo-addon: test", 1, true)) end) end --- the modules of an installed addon can be imported with the `@addon.<addon>.` prefix -function test_install_modules(t) - _with_repo({"hello"}, function (_, names) - local name = names[1] - os.runv("xmake", {"addon", "--install", "-y", name}) - - local script = string.format("import(\"@addon.%s.%s\"); print(%s())", name, MODULENAME, MODULENAME) - t:require(os.iorunv("xmake", {"lua", "-c", script}):find("hello from module " .. MODULENAME .. " of " .. name, 1, true)) +-- install the addons from a repository, by plain name and by repo@name +function test_install_from_repo(t) + _with_repo(function (reponame) + os.runv("xmake", {"addon", "--install", "-y", ADDON}) + t:require(os.iorunv("xmake", {"demo_hello"}):find("hello from demo-addon", 1, true)) - -- the addon name is always required - local script2 = string.format("import(\"@addon.%s\")", MODULENAME) - t:require_not(try { function () os.iorunv("xmake", {"lua", "-c", script2}); return true end }) + os.runv("xmake", {"addon", "--remove", ADDON}) + os.runv("xmake", {"addon", "--install", "-y", reponame .. "@" .. ADDON}) + t:require(os.iorunv("xmake", {"demo_hello"}):find("hello from demo-addon", 1, true)) - os.runv("xmake", {"addon", "--remove", name}) + -- it should be searchable, and the addons should not be found by the package search + t:require(os.iorunv("xmake", {"addon", "--search", ADDON}):find(ADDON, 1, true)) + t:require_not(os.iorunv("xrepo", {"search", ADDON}):find(ADDON, 1, true)) end) end --- an addon can depend on the other addons with `add_deps(name, {kind = "addon"})` +-- an addon can depend on the other addons, and use their rules and modules function test_addon_deps(t) - _with_repo({"hello", "world"}, function (_, names) - local name, depname = names[1], names[2] - - -- installing the first addon should install and activate its addon dependency - os.runv("xmake", {"addon", "--install", "-y", name}) - t:require(os.iorunv("xmake", {"addon", "--list"}):find(depname, 1, true)) + _with_repo(function () - -- the payloads of the dependency should be usable, e.g. its plugin - t:require(os.iorunv("xmake", {depname}):find(depname, 1, true)) + -- installing it should install and activate its addon dependency + os.runv("xmake", {"addon", "--install", "-y", ADDON_DEP}) + t:require(os.iorunv("xmake", {"addon", "--list"}):find(ADDON, 1, true)) -- we cannot remove the dependency, it's depended on by the other addon - t:require_not(try { function () os.runv("xmake", {"addon", "--remove", depname}); return true end }) - - os.runv("xmake", {"addon", "--remove", name}) - os.runv("xmake", {"addon", "--remove", depname}) - end, {deps = {hello = {2}}}) -end - --- the plugins and templates are not namespaced, the conflicts should be rejected when installing -function test_install_conflicts(t) - _with_repo({"hello"}, function (_, names) - local name = names[1] - os.runv("xmake", {"addon", "--install", "-y", name}) - - -- install another addon which provides the same plugin name - local othername = name .. "-other" - local dir = path.join(os.tmpfile() .. ".addon-conflict", othername) - _write_plugin(path.join(dir, "plugins", name), name) - _write_module(dir, name) - try - { - function () - -- it should be rejected - t:require_not(try { function () os.runv("xmake", {"addon", "--install", dir}); return true end }) - - -- and the other commands should still work - t:require(os.iorunv("xmake", {"addon", "--list"}):find(name, 1, true)) - t:require(os.iorunv("xmake", {name}):find(name, 1, true)) - end, - finally - { - function () - try { function () os.runv("xmake", {"addon", "--remove", "--force", othername}) end } - os.tryrm(path.directory(dir)) - end - } - } - - os.runv("xmake", {"addon", "--remove", name}) - end) -end - --- a complete addon with the optional `src` layout, @see tests/actions/addon/demo-addon --- --- it provides all the payload kinds and has its own files (README, tests) which must not be installed -function test_demo_addon(t) - local name = "demo-addon" - local addondir = path.join(os.scriptdir(), name) - try - { - function () - os.runv("xmake", {"addon", "--install", addondir}) + t:require_not(try { function () os.runv("xmake", {"addon", "--remove", ADDON}); return true end }) - -- only the payloads of the `src` directory should be installed - local installdir = path.join(global.directory(), "addons", name, "latest") - for _, payloaddir in ipairs({"plugins", "rules", "toolchains", "modules", "includes", "templates"}) do - t:require(os.isdir(path.join(installdir, payloaddir))) - end - for _, ourfile in ipairs({"src", "tests", "README.md"}) do - t:require_not(os.exists(path.join(installdir, ourfile))) - end - - -- the plugin imports a module of its own addon with `@self` - t:require(os.iorunv("xmake", {"demo_hello", "-n", "xmake"}):find("hello from demo-addon: xmake", 1, true)) - - -- the module can be imported with the addon name - local script = "import(\"@addon.demo-addon.greeting\"); print(greeting(\"module\"))" - t:require(os.iorunv("xmake", {"lua", "-c", script}):find("hello from demo-addon: module", 1, true)) - - -- the toolchain can be loaded with the addon name - local script2 = "import(\"core.tool.toolchain\"); print(toolchain.load(\"@addon/demo-addon/demo\"):get(\"description\"))" - t:require(os.iorunv("xmake", {"lua", "-c", script2}):find("the demo toolchain", 1, true)) - - -- the template should be listed - t:require(os.iorunv("xmake", {"create", "--list", "-l", "c"}):find("demoaddon.hello", 1, true)) - - -- the includes and the rules should work in a real project - local out = _config_project([[ -includes("@addon/demo-addon/check") + -- its rule depends on the rule of the other addon and imports its module + local out = _config_project([[ target("test") set_kind("phony") - add_rules("@addon/demo-addon/app") + add_rules("@addon/demo-addon-dep/dep") ]]) - t:require(out:find("demo-addon: includes check is loaded", 1, true)) - t:require(out:find("demo-addon: rule base is loaded", 1, true)) - t:require(out:find("demo-addon: rule app is loaded, hello from demo-addon: test", 1, true)) - end, - finally - { - function () - try { function () os.runv("xmake", {"addon", "--remove", "--force", name}) end } - 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) - try - { - function () - 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 }) - end, - finally - { - function () - try { function () os.runv("xmake", {"addon", "--remove", name}) end } - os.tryrm(path.directory(dir)) - end - } - } -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, rules, 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]}) + t:require(out:find("demo-addon: rule base is loaded", 1, true)) + t:require(out:find("demo-addon-dep: hello from demo-addon: dep", 1, true)) 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 plugins and the templates are not namespaced, the conflicts should be rejected when installing +function test_install_conflicts(t) + _with_repo(function () + os.runv("xmake", {"addon", "--install", "-y", ADDON}) + + -- this addon provides the same plugin and template names + t:require_not(try { function () os.runv("xmake", {"addon", "--install", "-y", "demo-addon-clone"}); return true end }) - -- 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)) + -- and the other commands should still work + t:require(os.iorunv("xmake", {"addon", "--list"}):find(ADDON, 1, true)) + t:require(os.iorunv("xmake", {"demo_hello"}):find("hello from demo-addon", 1, true)) end) end --- the `addon` package name is reserved for the addon references -function test_reserved_name(t) +-- the invalid installs should fail, and the `addon` name is reserved for the addon references +function test_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 }) t:require_not(try { function () _config_project([[ add_requires("addon") target("test") set_kind("phony") ]]); return 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 |
