summaryrefslogtreecommitdiff
path: root/spec/unit
diff options
context:
space:
mode:
Diffstat (limited to 'spec/unit')
-rw-r--r--spec/unit/build_spec.lua365
-rw-r--r--spec/unit/deps_spec.lua143
-rw-r--r--spec/unit/dir_spec.lua70
-rw-r--r--spec/unit/fetch_spec.lua486
-rw-r--r--spec/unit/fs_spec.lua1595
-rw-r--r--spec/unit/fun_spec.lua128
-rw-r--r--spec/unit/loader_spec.lua18
-rw-r--r--spec/unit/persist_spec.lua74
-rw-r--r--spec/unit/rockspecs_spec.lua126
-rw-r--r--spec/unit/sysdetect_spec.lua79
-rw-r--r--spec/unit/test_spec.lua173
-rw-r--r--spec/unit/tools_spec.lua251
-rw-r--r--spec/unit/util_spec.lua160
13 files changed, 3668 insertions, 0 deletions
diff --git a/spec/unit/build_spec.lua b/spec/unit/build_spec.lua
new file mode 100644
index 0000000..e8f1394
--- /dev/null
+++ b/spec/unit/build_spec.lua
@@ -0,0 +1,365 @@
+local test_env = require("spec.util.test_env")
+local lfs = require("lfs")
+local get_tmp_path = test_env.get_tmp_path
+local run = test_env.run
+local testing_paths = test_env.testing_paths
+local write_file = test_env.write_file
+local P = test_env.P
+
+test_env.setup_specs()
+local cfg = require("luarocks.core.cfg")
+local deps = require("luarocks.deps")
+local fs = require("luarocks.fs")
+local path = require("luarocks.path")
+local rockspecs = require("luarocks.rockspecs")
+local build_builtin = require("luarocks.build.builtin")
+
+local c_module_source = [[
+ #include <lua.h>
+ #include <lauxlib.h>
+
+ int luaopen_c_module(lua_State* L) {
+ lua_newtable(L);
+ lua_pushinteger(L, 1);
+ lua_setfield(L, -2, "c_module");
+ return 1;
+ }
+]]
+
+describe("LuaRocks build #unit", function()
+ local runner
+
+ lazy_setup(function()
+ runner = require("luacov.runner")
+ runner.init(testing_paths.testrun_dir .. "/luacov.config")
+ cfg.init()
+ fs.init()
+ deps.check_lua_incdir(cfg.variables)
+ deps.check_lua_libdir(cfg.variables)
+ end)
+
+ lazy_teardown(function()
+ runner.save_stats()
+ end)
+
+ describe("build.builtin", function()
+ it("builtin auto installs files in lua subdir", function()
+ test_env.run_in_tmp(function(tmpdir)
+ lfs.mkdir("lua")
+ write_file("lua_module-1.0-1.rockspec", [[
+ package = "lua_module"
+ version = "1.0-1"
+ source = {
+ url = "http://example.com/lua_module"
+ }
+ build = {
+ type = "builtin",
+ modules = {}
+ }
+ ]], finally)
+ write_file("lua/lua_module.lua", "return 123", finally)
+
+ assert.is_true(run.luarocks_bool("build"))
+ assert.match("[\\/]lua_module%.lua", run.luarocks("show lua_module"))
+ end, finally)
+ end)
+
+ describe("builtin.autodetect_modules", function()
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ tmpdir = get_tmp_path()
+ olddir = lfs.currentdir()
+ lfs.mkdir(tmpdir)
+ lfs.chdir(tmpdir)
+ fs.change_dir(tmpdir)
+ end)
+
+ after_each(function()
+ if olddir then
+ lfs.chdir(olddir)
+ fs.change_dir(olddir)
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ end
+ end
+ end)
+
+ local libs = { "foo1", "foo2" }
+ local incdirs = { "$(FOO1_INCDIR)", "$(FOO2_INCDIR)" }
+ local libdirs = { "$(FOO1_LIBDIR)", "$(FOO2_LIBDIR)" }
+
+ it("returns a table of the modules having as location the current directory", function()
+ write_file("module1.lua", "", finally)
+ write_file("module2.c", "", finally)
+ write_file("module3.c", "int luaopen_my_module()", finally)
+ write_file("test.lua", "", finally)
+ write_file("tests.lua", "", finally)
+
+ local modules = build_builtin.autodetect_modules(libs, incdirs, libdirs)
+ assert.same(modules, {
+ module1 = "module1.lua",
+ module2 = {
+ sources = "module2.c",
+ libraries = libs,
+ incdirs = incdirs,
+ libdirs = libdirs
+ },
+ my_module = {
+ sources = "module3.c",
+ libraries = libs,
+ incdirs = incdirs,
+ libdirs = libdirs
+ }
+ })
+ end)
+
+ local test_with_location = function(location)
+ lfs.mkdir(location)
+ lfs.mkdir(location .. "/dir1")
+ lfs.mkdir(location .. "/dir1/dir2")
+
+ write_file(location .. "/module1.lua", "", finally)
+ write_file(location .. "/dir1/module2.c", "", finally)
+ write_file(location .. "/dir1/dir2/module3.c", "int luaopen_my_module()", finally)
+ write_file(location .. "/test.lua", "", finally)
+ write_file(location .. "/tests.lua", "", finally)
+
+ local modules = build_builtin.autodetect_modules(libs, incdirs, libdirs)
+ assert.same(modules, {
+ module1 = P(location .. "/module1.lua"),
+ ["dir1.module2"] = {
+ sources = P(location .. "/dir1/module2.c"),
+ libraries = libs,
+ incdirs = incdirs,
+ libdirs = libdirs
+ },
+ my_module = {
+ sources = P(location .. "/dir1/dir2/module3.c"),
+ libraries = libs,
+ incdirs = incdirs,
+ libdirs = libdirs
+ }
+ })
+
+ lfs.rmdir(location .. "/dir1/dir2")
+ lfs.rmdir(location .. "/dir1")
+ lfs.rmdir(location)
+ end
+
+ it("returns a table of the modules having as location the src directory", function()
+ test_with_location("src")
+ end)
+
+ it("returns a table of the modules having as location the lua directory", function()
+ test_with_location("lua")
+ end)
+
+ it("returns as second and third argument tables of the bin files and copy directories", function()
+ lfs.mkdir("doc")
+ lfs.mkdir("docs")
+ lfs.mkdir("samples")
+ lfs.mkdir("tests")
+ lfs.mkdir("bin")
+ write_file("bin/binfile", "", finally)
+
+ local _, install, copy_directories = build_builtin.autodetect_modules({}, {}, {})
+ assert.same(install, { bin = { P"bin/binfile" } })
+ assert.same(copy_directories, { "doc", "docs", "samples", "tests" })
+
+ lfs.rmdir("doc")
+ lfs.rmdir("docs")
+ lfs.rmdir("samples")
+ lfs.rmdir("tests")
+ lfs.rmdir("bin")
+ end)
+ end)
+
+ describe("builtin.run", function()
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ tmpdir = get_tmp_path()
+ olddir = lfs.currentdir()
+ lfs.mkdir(tmpdir)
+ lfs.chdir(tmpdir)
+ fs.change_dir(tmpdir)
+ path.use_tree(lfs.currentdir())
+ end)
+
+ after_each(function()
+ if olddir then
+ lfs.chdir(olddir)
+ fs.change_dir(olddir)
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ end
+ end
+ end)
+
+ it("returns false if the rockspec has no build modules and its format does not support autoextraction", function()
+ local rockspec = {
+ package = "test",
+ version = "1.0-1",
+ source = {
+ url = "http://example.com/test"
+ },
+ build = {}
+ }
+
+ rockspecs.from_persisted_table("test-1.0-1.rockspec", rockspec)
+ assert.falsy(build_builtin.run(rockspec))
+ rockspec.rockspec_format = "1.0"
+ assert.falsy(build_builtin.run(rockspec))
+ end)
+
+ it("returns false if lua.h could not be found", function()
+ local rockspec = {
+ package = "c_module",
+ version = "1.0-1",
+ source = {
+ url = "http://example.com/c_module"
+ },
+ build = {
+ type = "builtin",
+ modules = {
+ c_module = "c_module.c"
+ }
+ }
+ }
+ write_file("c_module.c", c_module_source, finally)
+
+ rockspecs.from_persisted_table("c_module-1.0-1.rockspec", rockspec)
+ rockspec.variables = { LUA_INCDIR = "invalid" }
+ assert.falsy(build_builtin.run(rockspec))
+ end)
+
+ it("returns false if the build fails", function()
+ local rockspec = {
+ package = "c_module",
+ version = "1.0-1",
+ source = {
+ url = "http://example.com/c_module"
+ },
+ build = {
+ type = "builtin",
+ modules = {
+ c_module = "c_module.c"
+ }
+ }
+ }
+ write_file("c_module.c", c_module_source .. "invalid", finally)
+
+ rockspecs.from_persisted_table("c_module-1.0-1.rockspec", rockspec)
+ assert.falsy(build_builtin.run(rockspec))
+ end)
+
+ it("returns true if the build succeeds with C module", function()
+ local rockspec = {
+ package = "c_module",
+ version = "1.0-1",
+ source = {
+ url = "http://example.com/c_module"
+ },
+ build = {
+ type = "builtin",
+ modules = {
+ c_module = "c_module.c"
+ }
+ }
+ }
+ write_file("c_module.c", c_module_source, finally)
+
+ rockspecs.from_persisted_table("c_module-1.0-1.rockspec", rockspec)
+ assert.truthy(build_builtin.run(rockspec))
+ assert.truthy(lfs.attributes("lib/luarocks/rocks-" .. test_env.lua_version .. "/c_module/1.0-1/lib/c_module." .. test_env.lib_extension))
+ end)
+
+ it("returns true if the build succeeds with Lua module", function()
+ local rockspec = {
+ rockspec_format = "1.0",
+ package = "test",
+ version = "1.0-1",
+ source = {
+ url = "http://example.com/test"
+ },
+ build = {
+ type = "builtin",
+ modules = {
+ test = "test.lua"
+ }
+ }
+ }
+ write_file("test.lua", "return {}", finally)
+
+ rockspecs.from_persisted_table("test-1.0-1.rockspec", rockspec)
+ assert.truthy(build_builtin.run(rockspec))
+ assert.truthy(lfs.attributes("lib/luarocks/rocks-" .. test_env.lua_version .. "/test/1.0-1/lua/test.lua"))
+ end)
+
+ it("automatically extracts the modules and libraries if they are not given and builds against any external dependencies", function()
+ local fdir = testing_paths.fixtures_dir
+ if test_env.TEST_TARGET_OS == "windows" then
+ if test_env.MINGW then
+ os.execute("gcc -shared -o " .. fdir .. "/libfixturedep.dll -Wl,--out-implib," .. fdir .."/libfixturedep.a " .. fdir .. "/fixturedep.c")
+ else
+ os.execute("cl " .. fdir .. "\\fixturedep.c /link /export:fixturedep_fn /out:" .. fdir .. "\\fixturedep.dll /implib:" .. fdir .. "\\fixturedep.lib")
+ end
+ elseif test_env.TEST_TARGET_OS == "linux" then
+ os.execute("gcc -shared -o " .. fdir .. "/libfixturedep.so " .. fdir .. "/fixturedep.c")
+ elseif test_env.TEST_TARGET_OS == "osx" then
+ os.execute("cc -dynamiclib -o " .. fdir .. "/libfixturedep.dylib " .. fdir .. "/fixturedep.c")
+ end
+
+ local rockspec = {
+ rockspec_format = "3.0",
+ package = "c_module",
+ version = "1.0-1",
+ source = {
+ url = "http://example.com/c_module"
+ },
+ external_dependencies = {
+ FIXTUREDEP = {
+ library = "fixturedep"
+ }
+ },
+ build = {
+ type = "builtin"
+ }
+ }
+ write_file("c_module.c", c_module_source, finally)
+
+ rockspecs.from_persisted_table("c_module-1.0-1.rockspec", rockspec)
+ rockspec.variables["FIXTUREDEP_LIBDIR"] = testing_paths.fixtures_dir
+ assert.truthy(build_builtin.run(rockspec))
+ end)
+
+ it("returns false if any external dependency is missing", function()
+ local rockspec = {
+ rockspec_format = "3.0",
+ package = "c_module",
+ version = "1.0-1",
+ source = {
+ url = "https://example.com/c_module"
+ },
+ external_dependencies = {
+ EXTDEP = {
+ library = "missing"
+ }
+ },
+ build = {
+ type = "builtin"
+ }
+ }
+ write_file("c_module.c", c_module_source, finally)
+
+ rockspecs.from_persisted_table("c_module-1.0-1.rockspec", rockspec)
+ rockspec.variables["EXTDEP_INCDIR"] = lfs.currentdir()
+ rockspec.variables["EXTDEP_LIBDIR"] = lfs.currentdir()
+ assert.falsy(build_builtin.run(rockspec))
+ end)
+ end)
+ end)
+end)
diff --git a/spec/unit/deps_spec.lua b/spec/unit/deps_spec.lua
new file mode 100644
index 0000000..3be80b9
--- /dev/null
+++ b/spec/unit/deps_spec.lua
@@ -0,0 +1,143 @@
+local test_env = require("spec.util.test_env")
+local testing_paths = test_env.testing_paths
+
+local cfg = require("luarocks.core.cfg")
+local deps = require("luarocks.deps")
+local fs = require("luarocks.fs")
+
+describe("LuaRocks deps #unit", function()
+ local runner
+
+ lazy_setup(function()
+ cfg.init()
+ fs.init()
+ deps.check_lua_incdir(cfg.variables)
+ deps.check_lua_libdir(cfg.variables)
+
+ runner = require("luacov.runner")
+ runner.init(testing_paths.testrun_dir .. "/luacov.config")
+ end)
+
+ lazy_teardown(function()
+ runner.save_stats()
+ end)
+
+ describe("deps", function()
+ describe("deps.autodetect_external_dependencies", function()
+ it("returns false if the given build table has no external dependencies", function()
+ local build_table = {
+ type = "builtin"
+ }
+
+ assert.falsy(deps.autodetect_external_dependencies(build_table))
+ end)
+
+ it("returns a table of the external dependencies found in the given build table", function()
+ local build_table = {
+ type = "builtin",
+ modules = {
+ module1 = {
+ libraries = { "foo1", "foo2" },
+ },
+ module2 = {
+ libraries = "foo3"
+ },
+ }
+ }
+
+ local extdeps = deps.autodetect_external_dependencies(build_table)
+ assert.same(extdeps["FOO1"], { library = "foo1" })
+ assert.same(extdeps["FOO2"], { library = "foo2" })
+ assert.same(extdeps["FOO3"], { library = "foo3" })
+ end)
+
+ it("adds proper include and library dirs to the given build table", function()
+ local build_table
+
+ build_table = {
+ type = "builtin",
+ modules = {
+ module1 = {
+ libraries = "foo"
+ }
+ }
+ }
+ deps.autodetect_external_dependencies(build_table)
+ assert.same(build_table, {
+ type = "builtin",
+ modules = {
+ module1 = {
+ libraries = "foo",
+ incdirs = { "$(FOO_INCDIR)" },
+ libdirs = { "$(FOO_LIBDIR)" }
+ }
+ }
+ })
+
+ build_table = {
+ type = "builtin",
+ modules = {
+ module1 = {
+ libraries = "foo",
+ incdirs = { "INCDIRS" }
+ }
+ }
+ }
+ deps.autodetect_external_dependencies(build_table)
+ assert.same(build_table, {
+ type = "builtin",
+ modules = {
+ module1 = {
+ libraries = "foo",
+ incdirs = { "INCDIRS" },
+ libdirs = { "$(FOO_LIBDIR)" }
+ }
+ }
+ })
+
+ build_table = {
+ type = "builtin",
+ modules = {
+ module1 = {
+ libraries = "foo",
+ libdirs = { "LIBDIRS" }
+ }
+ }
+ }
+ deps.autodetect_external_dependencies(build_table)
+ assert.same(build_table, {
+ type = "builtin",
+ modules = {
+ module1 = {
+ libraries = "foo",
+ incdirs = { "$(FOO_INCDIR)" },
+ libdirs = { "LIBDIRS" }
+ }
+ }
+ })
+
+ build_table = {
+ type = "builtin",
+ modules = {
+ module1 = {
+ libraries = "foo",
+ incdirs = { "INCDIRS" },
+ libdirs = { "LIBDIRS" }
+ }
+ }
+ }
+ deps.autodetect_external_dependencies(build_table)
+ assert.same(build_table, {
+ type = "builtin",
+ modules = {
+ module1 = {
+ libraries = "foo",
+ incdirs = { "INCDIRS" },
+ libdirs = { "LIBDIRS" }
+ }
+ }
+ })
+ end)
+ end)
+ end)
+end)
diff --git a/spec/unit/dir_spec.lua b/spec/unit/dir_spec.lua
new file mode 100644
index 0000000..55dd6e0
--- /dev/null
+++ b/spec/unit/dir_spec.lua
@@ -0,0 +1,70 @@
+local test_env = require("spec.util.test_env")
+local testing_paths = test_env.testing_paths
+local P = test_env.P
+
+test_env.setup_specs()
+local dir = require("luarocks.dir")
+
+describe("luarocks.dir #unit", function()
+ local runner
+
+ lazy_setup(function()
+ runner = require("luacov.runner")
+ runner.init(testing_paths.testrun_dir .. "/luacov.config")
+ end)
+
+ lazy_teardown(function()
+ runner.save_stats()
+ end)
+
+ describe("dir.is_basic_protocol", function()
+ it("checks whether the arguments represent a valid protocol and returns the result of the check", function()
+ assert.truthy(dir.is_basic_protocol("http"))
+ assert.truthy(dir.is_basic_protocol("https"))
+ assert.truthy(dir.is_basic_protocol("ftp"))
+ assert.truthy(dir.is_basic_protocol("file"))
+ assert.falsy(dir.is_basic_protocol("git"))
+ assert.falsy(dir.is_basic_protocol("git+https"))
+ assert.falsy(dir.is_basic_protocol("invalid"))
+ end)
+ end)
+
+ describe("dir.deduce_base_dir", function()
+ it("deduces the base dir from archives", function()
+ assert.are.same("v0.3", dir.deduce_base_dir("https://example.com/hishamhm/lua-compat-5.2/archive/v0.3.zip"))
+ assert.are.same("lua-compat-5.2", dir.deduce_base_dir("https://example.com/hishamhm/lua-compat-5.2.zip"))
+ assert.are.same("lua-compat-5.2", dir.deduce_base_dir("https://example.com/hishamhm/lua-compat-5.2.tar.gz"))
+ assert.are.same("lua-compat-5.2", dir.deduce_base_dir("https://example.com/hishamhm/lua-compat-5.2.tar.bz2"))
+ end)
+ it("returns the basename when not given an archive", function()
+ assert.are.same("parser.moon", dir.deduce_base_dir("git://example.com/Cirru/parser.moon"))
+ assert.are.same("v0.3", dir.deduce_base_dir("https://example.com/hishamhm/lua-compat-5.2/archive/v0.3"))
+ end)
+ end)
+
+ describe("dir.normalize", function()
+ it("converts backslashes and removes trailing slashes", function()
+ assert.are.same(P"/foo/ovo", dir.normalize("\\foo\\ovo\\"))
+ assert.are.same(P"c:/some/dir", dir.normalize("c:\\..\\some\\foo\\..\\dir"))
+ assert.are.same("http://example.com/foo/ovo", dir.normalize("http://example.com/foo\\ovo\\"))
+ end)
+ it("strips unneeded /../ and /./", function()
+ assert.are.same(P"/some/dir/file.txt", dir.normalize("/../../../some/./foo/bar/.././../dir/bla/../file.txt"))
+ assert.are.same(P"/some/dir/file.txt", dir.normalize("/../../../some/./foo/bar/.././../dir/bla/../file.txt"))
+ assert.are.same(P"/some/dir", dir.normalize("/../../../some/./foo/bar/.././../dir/./some/subdir/../.."))
+ assert.are.same(P"/some/dir", dir.normalize("/../../../some/./foo/bar/.././../dir/./."))
+ end)
+ it("respects relative paths", function()
+ assert.are.same(P".", dir.normalize("."))
+ assert.are.same(P"boo", dir.normalize("./boo"))
+ assert.are.same(P"/boo", dir.normalize("/./boo"))
+ assert.are.same(P"../../../../boo", dir.normalize("../../../hello/world/../../../boo"))
+ end)
+ it("respects root directory", function()
+ assert.are.same(P"/", dir.normalize("/"))
+ assert.are.same(P"/", dir.normalize("/////"))
+ assert.are.same(P"/", dir.normalize("/a/b/.././../c/./../../"))
+ end)
+ end)
+
+end)
diff --git a/spec/unit/fetch_spec.lua b/spec/unit/fetch_spec.lua
new file mode 100644
index 0000000..bea50d7
--- /dev/null
+++ b/spec/unit/fetch_spec.lua
@@ -0,0 +1,486 @@
+local test_env = require("spec.util.test_env")
+
+test_env.setup_specs()
+local cfg = require("luarocks.core.cfg")
+local fetch = require("luarocks.fetch")
+local fs = require("luarocks.fs")
+local dir = require("luarocks.dir")
+local path = require("luarocks.path")
+local rockspecs = require("luarocks.rockspecs")
+local lfs = require("lfs")
+local get_tmp_path = test_env.get_tmp_path
+local testing_paths = test_env.testing_paths
+local write_file = test_env.write_file
+local P = test_env.P
+
+describe("luarocks fetch #unit", function()
+ local are_same_files = function(file1, file2)
+ return file1 == file2 or lfs.attributes(file1).ino == lfs.attributes(file2).ino
+ end
+
+ local runner
+
+ lazy_setup(function()
+ cfg.init()
+ fs.init()
+
+ -- mock network access
+ fs.download = function(url, destfile)
+ local mockfile = P(url:gsub("http://localhost:8080/file", testing_paths.fixtures_dir))
+ if not destfile then
+ destfile = dir.base_name(mockfile)
+ end
+ destfile = fs.absolute_name(destfile)
+
+ local fdr = io.open(mockfile, "rb")
+ if not fdr then
+ return nil, "mock failed opening for reading"
+ end
+
+ local fdw = io.open(destfile, "wb")
+ if not fdr then
+ return nil, "mock failed opening for writing"
+ end
+
+ local data = fdr:read("*a")
+ if not data then
+ return nil, "mock failed reading"
+ end
+
+ local ok = fdw:write(data)
+ if not ok then
+ return nil, "mock failed writing"
+ end
+
+ fdr:close()
+ fdw:close()
+
+ return true, destfile
+ end
+
+ runner = require("luacov.runner")
+ runner.init(testing_paths.testrun_dir .. "/luacov.config")
+ end)
+
+ lazy_teardown(function()
+ runner.save_stats()
+ end)
+
+
+ describe("fetch.fetch_url", function()
+
+ it("fetches the url argument and returns the absolute path of the fetched file", function()
+ test_env.run_in_tmp(function()
+ local fetchedfile, err = fetch.fetch_url("http://localhost:8080/file/a_rock.lua")
+ assert(fetchedfile, err)
+ assert.truthy(are_same_files(fetchedfile, lfs.currentdir() .. "/a_rock.lua"))
+ local fd = assert(io.open(fetchedfile, "r"))
+ local fetchedcontent = assert(fd:read("*a"))
+ fd:close()
+ fd = assert(io.open(testing_paths.fixtures_dir .. "/a_rock.lua", "r"))
+ local filecontent = assert(fd:read("*a"))
+ fd:close()
+ assert.same(fetchedcontent, filecontent)
+ end, finally)
+ end)
+
+ it("returns the absolute path of the filename argument if the url represents a file", function()
+ test_env.run_in_tmp(function()
+ write_file("test.lua", "return {}")
+
+ local file, err = fetch.fetch_url("file://test.lua")
+ assert.truthy(file, err)
+ assert.truthy(are_same_files(file, lfs.currentdir() .. "/test.lua"))
+ fs.pop_dir()
+ end, finally)
+ end)
+
+ it("fails if local path is invalid and returns a helpful hint for relative paths", function()
+ test_env.run_in_tmp(function()
+ local ok, err = fetch.fetch_url("file://boo.lua")
+ assert.falsy(ok)
+ assert.match("note that given path in rockspec is not absolute: file://boo.lua", err)
+ end, finally)
+ end)
+
+ it("returns false and does nothing if the url argument contains a nonexistent file", function()
+ assert.falsy(fetch.fetch_url("http://localhost:8080/file/nonexistent"))
+ end)
+
+ it("returns false and does nothing if the url argument is invalid", function()
+ assert.falsy(fetch.fetch_url("invalid://url", "file"))
+ end)
+ end)
+
+ describe("fetch.fetch_url_at_temp_dir", function()
+
+ it("returns the absolute path and the parent directory of the file specified by the url", function()
+ test_env.run_in_tmp(function(tmpdir)
+ local tmpfile = tmpdir .. "/tmpfile"
+ assert(io.open(tmpfile, "w"))
+ local pathname, dirname = fetch.fetch_url_at_temp_dir("file://" .. tmpfile, "test")
+ assert.truthy(are_same_files(tmpfile, pathname))
+ assert.truthy(are_same_files(tmpdir, dirname))
+ end, finally)
+ end)
+
+ it("returns true and fetches the url into a temporary dir", function()
+ test_env.run_in_tmp(function()
+ local fetchedfile, tmpdir = fetch.fetch_url_at_temp_dir("http://localhost:8080/file/a_rock.lua", "test")
+ assert(fetchedfile, tmpdir)
+ assert.truthy(are_same_files(fetchedfile, tmpdir .. "/a_rock.lua"))
+ local fd = assert(io.open(fetchedfile, "r"))
+ local fetchedcontent = assert(fd:read("*a"))
+ fd:close()
+ fd = assert(io.open(testing_paths.fixtures_dir .. "/a_rock.lua", "r"))
+ local filecontent = assert(fd:read("*a"))
+ fd:close()
+ assert.same(fetchedcontent, filecontent)
+ end, finally)
+ end)
+
+ it("returns true and fetches the url into a temporary dir with custom filename", function()
+ test_env.run_in_tmp(function()
+ local fetchedfile, tmpdir = fetch.fetch_url_at_temp_dir("http://localhost:8080/file/a_rock.lua", "test", "my_a_rock.lua")
+ assert(fetchedfile, tmpdir)
+ assert.truthy(are_same_files(fetchedfile, tmpdir .. "/my_a_rock.lua"))
+ assert.truthy(string.find(tmpdir, "test"))
+ local fd = assert(io.open(fetchedfile, "r"))
+ local fetchedcontent = assert(fd:read("*a"))
+ fd:close()
+ fd = assert(io.open(testing_paths.fixtures_dir .. "/a_rock.lua", "r"))
+ local filecontent = assert(fd:read("*a"))
+ fd:close()
+ assert.same(fetchedcontent, filecontent)
+ end, finally)
+ end)
+
+ it("returns false and does nothing if the file specified in the url is nonexistent", function()
+ assert.falsy(fetch.fetch_url_at_temp_dir("file://nonexistent", "test"))
+ assert.falsy(fetch.fetch_url_at_temp_dir("http://localhost:8080/file/nonexistent", "test"))
+ end)
+
+ it("returns false and does nothing if the url is invalid", function()
+ assert.falsy(fetch.fetch_url_at_temp_dir("url://invalid", "test"))
+ end)
+ end)
+
+ describe("fetch.find_base_dir", function()
+ it("extracts the archive given by the file argument and returns the inferred and the actual root directory in the archive", function()
+ test_env.run_in_tmp(function()
+ local url = "http://localhost:8080/file/an_upstream_tarball-0.1.tar.gz"
+ local file, tmpdir = assert(fetch.fetch_url_at_temp_dir(url, "test"))
+ local inferreddir, founddir = fetch.find_base_dir(file, tmpdir, url)
+ assert.truthy(are_same_files(inferreddir, founddir))
+ assert.truthy(lfs.attributes(tmpdir .. "/" .. founddir))
+ end, finally)
+ end)
+
+ it("extracts the archive given by the file argument with given base directory and returns the inferred and the actual root directory in the archive", function()
+ test_env.run_in_tmp(function()
+ local url = "http://localhost:8080/file/an_upstream_tarball-0.1.tar.gz"
+ local file, tmpdir = assert(fetch.fetch_url_at_temp_dir(url, "test"))
+ local inferreddir, founddir = fetch.find_base_dir(file, tmpdir, url, "basedir")
+ assert.truthy(are_same_files(inferreddir, "basedir"))
+ assert.truthy(are_same_files(founddir, "an_upstream_tarball-0.1"))
+ assert.truthy(lfs.attributes(tmpdir .. "/" .. founddir))
+ end, finally)
+ end)
+
+ it("returns false and does nothing if the temporary directory doesn't exist", function()
+ assert.falsy(fetch.find_base_dir("file", "nonexistent", "url"))
+ end)
+ end)
+
+ describe("fetch.fetch_and_unpack_rock", function()
+
+ it("unpacks the rock file from the url and returns its resulting temporary parent directory", function()
+ test_env.run_in_tmp(function()
+ local tmpdir = fetch.fetch_and_unpack_rock("http://localhost:8080/file/a_rock-1.0-1.src.rock")
+ assert.truthy(string.find(tmpdir, "a_rock%-1%.0%-1"))
+ assert.truthy(lfs.attributes(tmpdir .. "/a_rock-1.0-1.rockspec"))
+ assert.truthy(lfs.attributes(tmpdir .. "/a_rock.lua"))
+ end, finally)
+ end)
+
+ it("unpacks the rock file from the url with custom unpacking directory", function()
+ test_env.run_in_tmp(function()
+ local tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ local resultingdir = fetch.fetch_and_unpack_rock("http://localhost:8080/file/a_rock-1.0-1.src.rock", tmpdir)
+ assert.truthy(are_same_files(resultingdir, tmpdir))
+ assert.truthy(lfs.attributes(resultingdir .. "/a_rock-1.0-1.rockspec"))
+ assert.truthy(lfs.attributes(resultingdir .. "/a_rock.lua"))
+ end, finally)
+ end)
+
+ it("does nothing if the url doesn't represent a rock file", function()
+ assert.falsy(pcall(fetch.fetch_and_unpack_rock, "http://localhost:8080/file/a_rock.lua"))
+ end)
+
+ it("does nothing if the rock file url is invalid", function()
+ assert.falsy(pcall(fetch.fetch_and_unpack_rock, "url://invalid"))
+ end)
+
+ it("does nothing if the rock file url represents a nonexistent file", function()
+ assert.falsy(pcall(fetch.fetch_and_unpack_rock, "url://invalid"))
+ assert.falsy(pcall(fetch.fetch_and_unpack_rock, "http://localhost:8080/file/nonexistent"))
+ end)
+ end)
+
+ describe("fetch.load_local_rockspec", function()
+ it("returns a table representing the rockspec from the given file skipping some checks if the quick argument is enabled", function()
+ test_env.run_in_tmp(function()
+ local rockspec = fetch.load_local_rockspec(testing_paths.fixtures_dir .. "/a_rock-1.0-1.rockspec", true)
+ assert.same(rockspec.name, "a_rock")
+ assert.same(rockspec.version, "1.0-1")
+ assert.same(rockspec.source.url, "http://localhost:8080/file/a_rock.lua")
+ assert.same(rockspec.description, { summary = "An example rockspec" })
+
+ write_file("missing_mandatory_field-1.0-1.rockspec", [[
+ package="missing_mandatory_field"
+ version="1.0-1"
+ source = {
+ url = "http://example.com/foo.tar.gz"
+ }
+ ]])
+ rockspec = fetch.load_local_rockspec("missing_mandatory_field-1.0-1.rockspec", true)
+ assert.same(rockspec.name, "missing_mandatory_field")
+ assert.same(rockspec.version, "1.0-1")
+ assert.same(rockspec.source.url, "http://example.com/foo.tar.gz")
+
+ write_file("unknown_field-1.0-1.rockspec", [[
+ package="unknown_field"
+ version="1.0-1"
+ source = {
+ url = "http://example.com/foo.tar.gz"
+ }
+ unknown="foo"
+ ]])
+ rockspec = fetch.load_local_rockspec("unknown_field-1.0-1.rockspec", true)
+ assert.same(rockspec.name, "unknown_field")
+ assert.same(rockspec.version, "1.0-1")
+ assert.same(rockspec.source.url, "http://example.com/foo.tar.gz")
+
+ -- The previous calls fail if the detailed checking is done
+ path.use_tree(testing_paths.testing_tree)
+ assert.falsy(fetch.load_local_rockspec("missing_mandatory_field-1.0-1.rockspec"))
+ assert.falsy(fetch.load_local_rockspec("unknown_field-1.0-1.rockspec"))
+ end, finally)
+ end)
+
+ it("returns a table representing the rockspec from the given file", function()
+ test_env.run_in_tmp(function()
+ path.use_tree(testing_paths.testing_tree)
+ local rockspec = fetch.load_local_rockspec(testing_paths.fixtures_dir .. "/a_rock-1.0-1.rockspec")
+ assert.same(rockspec.name, "a_rock")
+ assert.same(rockspec.version, "1.0-1")
+ assert.same(rockspec.description, { summary = "An example rockspec" })
+ assert.same(rockspec.source.url, "http://localhost:8080/file/a_rock.lua")
+ end, finally)
+ end)
+
+ it("returns false if the rockspec in invalid", function()
+ assert.falsy(fetch.load_local_rockspec(testing_paths.fixtures_dir .. "/invalid_say-1.3-1.rockspec"))
+ end)
+
+ it("returns false if the rockspec version is not supported", function()
+ assert.falsy(fetch.load_local_rockspec("invalid_version.rockspec"))
+ end)
+
+ it("returns false if the rockspec doesn't pass the type checking", function()
+ test_env.run_in_tmp(function()
+ write_file("type_mismatch_string-1.0-1.rockspec", [[
+ package="type_mismatch_version"
+ version=1.0
+ ]])
+ assert.falsy(fetch.load_local_rockspec("type_mismatch_string-1.0-1.rockspec"))
+ end, finally)
+ end)
+
+ it("returns false if the rockspec file name is not right", function()
+ test_env.run_in_tmp(function()
+ write_file("invalid_rockspec_name.rockspec", [[
+ package="invalid_rockspec_name"
+ version="1.0-1"
+ source = {
+ url = "http://example.com/foo.tar.gz"
+ }
+ build = {
+
+ }
+ ]])
+ assert.falsy(fetch.load_local_rockspec("invalid_rockspec_name.rockspec"))
+ end, finally)
+ end)
+
+ it("returns false if the version in the rockspec file name doesn't match the version declared in the rockspec", function()
+ test_env.run_in_tmp(function()
+ write_file("inconsistent_versions-1.0-1.rockspec", [[
+ package="inconsistent_versions"
+ version="1.0-2"
+ source = {
+ url = "http://example.com/foo.tar.gz"
+ }
+ build = {
+
+ }
+ ]])
+ assert.falsy(fetch.load_local_rockspec("inconsistent_versions-1.0-1.rockspec"))
+ end, finally)
+ end)
+ end)
+
+ describe("fetch.load_rockspec", function()
+
+ it("returns a table containing the requested rockspec by downloading it into a temporary directory", function()
+ test_env.run_in_tmp(function()
+ path.use_tree(testing_paths.testing_tree)
+ local rockspec = fetch.load_rockspec("http://localhost:8080/file/a_rock-1.0-1.rockspec")
+ assert.same(rockspec.name, "a_rock")
+ assert.same(rockspec.version, "1.0-1")
+ assert.same(rockspec.description, { summary = "An example rockspec" })
+ assert.same(rockspec.source.url, "http://localhost:8080/file/a_rock.lua")
+ rockspec = fetch.load_rockspec(testing_paths.fixtures_dir .. "/a_rock-1.0-1.rockspec")
+ assert.same(rockspec.name, "a_rock")
+ assert.same(rockspec.version, "1.0-1")
+ assert.same(rockspec.description, { summary = "An example rockspec" })
+ assert.same(rockspec.source.url, "http://localhost:8080/file/a_rock.lua")
+ end, finally)
+ end)
+
+ it("returns a table containing the requested rockspec by downloading it into a given directory", function()
+ test_env.run_in_tmp(function()
+ local tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+
+ path.use_tree(testing_paths.testing_tree)
+ local rockspec = fetch.load_rockspec("http://localhost:8080/file/a_rock-1.0-1.rockspec", tmpdir)
+ assert.same(rockspec.name, "a_rock")
+ assert.same(rockspec.version, "1.0-1")
+ assert.same(rockspec.description, { summary = "An example rockspec" })
+ assert.same(rockspec.source.url, "http://localhost:8080/file/a_rock.lua")
+ assert.truthy(lfs.attributes(tmpdir .. "/a_rock-1.0-1.rockspec"))
+
+ lfs.rmdir(tmpdir)
+ end, finally)
+ end)
+
+ it("returns false if the given download directory doesn't exist", function()
+ assert.falsy(fetch.load_rockspec("http://localhost:8080/file/a_rock-1.0-1.rockspec", "nonexistent"))
+ end)
+
+ it("returns false if the given filename is not a valid rockspec name", function()
+ assert.falsy(fetch.load_rockspec("http://localhost:8080/file/a_rock.lua"))
+ end)
+ end)
+
+ describe("fetch.get_sources", function()
+
+ it("downloads the sources for building a rock and returns the resulting source filename and its parent directory", function()
+ test_env.run_in_tmp(function()
+ local rockspec = assert(fetch.load_rockspec("http://localhost:8080/file/a_rock-1.0-1.rockspec"))
+ local file, dirname = fetch.get_sources(rockspec, false)
+ assert.truthy(are_same_files(dirname .. "/a_rock.lua", file))
+ end, finally)
+ end)
+
+ it("downloads the sources for building a rock into a given directory and returns the resulting source filename and its parent directory", function()
+ test_env.run_in_tmp(function()
+ local tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ local rockspec = assert(fetch.load_rockspec("http://localhost:8080/file/a_rock-1.0-1.rockspec"))
+ local file, dirname = fetch.get_sources(rockspec, false, tmpdir)
+ assert.truthy(are_same_files(tmpdir, dirname))
+ assert.truthy(are_same_files(dirname .. "/a_rock.lua", file))
+ lfs.rmdir(tmpdir)
+ end, finally)
+ end)
+
+ it("downloads the sources for building a rock, extracts the downloaded tarball and returns the resulting source filename and its parent directory", function()
+ test_env.run_in_tmp(function()
+ local rockspec = assert(fetch.load_rockspec("http://localhost:8080/file/busted_project-0.1-1.rockspec"))
+ local file, dirname = fetch.get_sources(rockspec, true)
+ assert.truthy(are_same_files(dirname .. "/busted_project-0.1.tar.gz", file))
+ assert.truthy(lfs.attributes(dirname .. "/busted_project"))
+ assert.truthy(lfs.attributes(dirname .. "/busted_project/sum.lua"))
+ assert.truthy(lfs.attributes(dirname .. "/busted_project/spec/sum_spec.lua"))
+ end, finally)
+ end)
+
+ it("returns false and does nothing if the destination directory doesn't exist", function()
+ test_env.run_in_tmp(function()
+ local rockspec = assert(fetch.load_rockspec("http://localhost:8080/file/a_rock-1.0-1.rockspec"))
+ assert.falsy(fetch.get_sources(rockspec, false, "nonexistent"))
+ end, finally)
+ end)
+
+ it("returns false and does nothing if the rockspec source url is invalid", function()
+ test_env.run_in_tmp(function(tmpdir)
+ write_file(tmpdir .. "/invalid_url-1.0-1.rockspec", [[
+ package="invalid_url"
+ version="1.0-1"
+ source = {
+ url = "http://localhost:8080/file/nonexistent"
+ }
+ build = {
+
+ }
+ ]])
+ local rockspec = assert(fetch.load_rockspec(tmpdir .. "/invalid_url-1.0-1.rockspec"))
+ assert.falsy(fetch.get_sources(rockspec, false))
+ end, finally)
+ end)
+
+ it("returns false and does nothing if the downloaded rockspec has an invalid md5 checksum", function()
+ test_env.run_in_tmp(function()
+ write_file("invalid_checksum-1.0-1.rockspec", [[
+ package="invalid_checksum"
+ version="1.0-1"
+ source = {
+ url = "http://localhost:8080/file/a_rock.lua",
+ md5 = "invalid"
+ }
+ build = {
+
+ }
+ ]])
+ local rockspec = assert(fetch.load_rockspec("invalid_checksum-1.0-1.rockspec"))
+ assert.falsy(fetch.get_sources(rockspec, false))
+ end, finally)
+ end)
+ end)
+
+ describe("fetch_sources #unix #git", function()
+ local git_repo = require("spec.util.git_repo")
+
+ local git
+
+ setup(function()
+ git = git_repo.start()
+ end)
+
+ teardown(function()
+ if git then
+ git:stop()
+ end
+ end)
+
+ it("from #git", function()
+ local rockspec, err = rockspecs.from_persisted_table("testrock-dev-1.rockspec", {
+ rockspec_format = "3.0",
+ package = "testrock",
+ version = "dev-1",
+ source = {
+ url = "git://localhost/testrock",
+ },
+ }, nil)
+ assert.falsy(err)
+ local pathname, tmpdir = fetch.fetch_sources(rockspec, false)
+ assert.are.same("testrock", pathname)
+ assert.match("luarocks_testrock%-dev%-1%-", tmpdir)
+ assert.match("^%d%d%d%d%d%d%d%d.%d%d%d%d%d%d.%x+$", tostring(rockspec.source.identifier))
+ end)
+ end)
+
+end)
diff --git a/spec/unit/fs_spec.lua b/spec/unit/fs_spec.lua
new file mode 100644
index 0000000..c2a842b
--- /dev/null
+++ b/spec/unit/fs_spec.lua
@@ -0,0 +1,1595 @@
+local test_env = require("spec.util.test_env")
+
+test_env.setup_specs()
+local fs = require("luarocks.fs")
+local path = require("luarocks.path")
+local cfg = require("luarocks.core.cfg")
+local lfs = require("lfs")
+local is_win = test_env.TEST_TARGET_OS == "windows"
+local posix_ok = pcall(require, "posix")
+local testing_paths = test_env.testing_paths
+local get_tmp_path = test_env.get_tmp_path
+local write_file = test_env.write_file
+local P = test_env.P
+
+-- A chdir that works in both full and minimal mode, setting
+-- both the real process current dir and the LuaRocks internal stack in minimal mode
+local function chdir(d)
+ lfs.chdir(d)
+ fs.change_dir(d)
+end
+
+describe("luarocks.fs #unit", function()
+ local exists_file = function(path)
+ local ok, err, code = os.rename(path, path)
+ if not ok and code == 13 then
+ return true
+ end
+ return ok
+ end
+
+ local create_file = function(path, content)
+ local fd = assert(io.open(path, "w"))
+ if not content then
+ content = "foo"
+ end
+ assert(fd:write(content))
+ fd:close()
+ end
+
+ local make_unreadable = function(path)
+ if is_win then
+ fs.execute("icacls " .. fs.Q(path) .. " /inheritance:d /deny \"%USERNAME%\":(R)")
+ else
+ fs.execute("chmod -r " .. fs.Q(path))
+ end
+ end
+
+ local make_unwritable = function(path)
+ if is_win then
+ fs.execute("icacls " .. fs.Q(path) .. " /inheritance:d /deny \"%USERNAME%\":(W,M)")
+ else
+ fs.execute("chmod -w " .. fs.Q(path))
+ end
+ end
+
+ local make_unexecutable = function(path)
+ if is_win then
+ fs.execute("icacls " .. fs.Q(path) .. " /inheritance:d /deny \"%USERNAME%\":(X)")
+ else
+ fs.execute("chmod -x " .. fs.Q(path))
+ end
+ end
+
+ local runner
+
+ lazy_setup(function()
+ cfg.init()
+ fs.init()
+ runner = require("luacov.runner")
+ runner.init(testing_paths.testrun_dir .. "/luacov.config")
+ end)
+
+ lazy_teardown(function()
+ runner.save_stats()
+ end)
+
+ describe("fs.Q", function()
+ it("simple argument", function()
+ assert.are.same(is_win and '"foo"' or "'foo'", fs.Q("foo"))
+ end)
+
+ it("argument with quotes", function()
+ assert.are.same(is_win and [["it's \"quoting\""]] or [['it'\''s "quoting"']], fs.Q([[it's "quoting"]]))
+ end)
+
+ it("argument with special characters", function()
+ assert.are.same(is_win and [["\\"%" \\\\" \\\\\\"]] or [['\% \\" \\\']], fs.Q([[\% \\" \\\]]))
+ end)
+ end)
+
+ describe("fs.absolute_name", function()
+ it("unchanged if already absolute", function()
+ if is_win then
+ assert.are.same(P"c:\\foo\\bar", fs.absolute_name("\"c:\\foo\\bar\""))
+ assert.are.same(P"c:\\foo\\bar", fs.absolute_name("c:\\foo\\bar"))
+ assert.are.same(P"d:\\foo\\bar", fs.absolute_name("d:\\foo\\bar"))
+ assert.are.same(P"\\foo\\bar", fs.absolute_name("\\foo\\bar"))
+ else
+ assert.are.same(P"/foo/bar", fs.absolute_name("/foo/bar"))
+ end
+ end)
+
+ it("converts to absolute if relative", function()
+ local cur = fs.current_dir()
+ if is_win then
+ assert.are.same(P(cur .. "/foo\\bar"), fs.absolute_name("\"foo\\bar\""))
+ assert.are.same(P(cur .. "/foo\\bar"), fs.absolute_name("foo\\bar"))
+ else
+ assert.are.same(P(cur .. "/foo/bar"), fs.absolute_name("foo/bar"))
+ end
+ end)
+
+ it("converts a relative to specified base if given", function()
+ if is_win then
+ assert.are.same(P"c:\\bla/foo\\bar", fs.absolute_name("\"foo\\bar\"", "c:\\bla"))
+ assert.are.same(P"c:\\bla/foo\\bar", fs.absolute_name("foo/bar", "c:\\bla"))
+ assert.are.same(P"c:\\bla/foo\\bar", fs.absolute_name("foo\\bar", "c:\\bla\\"))
+ else
+ assert.are.same(P"/bla/foo/bar", fs.absolute_name("foo/bar", "/bla"))
+ assert.are.same(P"/bla/foo/bar", fs.absolute_name("foo/bar", "/bla/"))
+ end
+ end)
+ end)
+
+ describe("fs.execute_string", function()
+ local tmpdir
+
+ after_each(function()
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns the status code and runs the command given in the argument", function()
+ tmpdir = get_tmp_path()
+ assert.truthy(fs.execute_string("mkdir " .. fs.Q(tmpdir)))
+ assert.truthy(fs.is_dir(tmpdir))
+ assert.falsy(fs.execute_string("invalidcommand"))
+ end)
+ end)
+
+ describe("fs.dir_iterator", function()
+ local tmpfile1
+ local tmpfile2
+ local tmpdir
+ local intdir
+
+ after_each(function()
+ if tmpfile1 then
+ os.remove(tmpfile1)
+ tmpfile1 = nil
+ end
+ if tmpfile2 then
+ os.remove(tmpfile2)
+ tmpfile2 = nil
+ end
+ if intdir then
+ lfs.rmdir(intdir)
+ intdir = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("yields all files and directories in the directory given as argument during the iterations", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ tmpfile1 = tmpdir .. "/file1"
+ create_file(tmpfile1)
+ tmpfile2 = tmpdir .. "/file2"
+ create_file(tmpfile2)
+ intdir = tmpdir .. "/intdir"
+ lfs.mkdir(intdir)
+ local dirTable = {}
+ local dirCount = 0
+ local crt = coroutine.create(fs.dir_iterator)
+ while coroutine.status(crt) ~= "dead" do
+ local ok, val = coroutine.resume(crt, tmpdir)
+ if ok and val ~= nil then
+ dirTable[val] = true
+ dirCount = dirCount + 1
+ end
+ end
+ assert.same(dirCount, 3)
+ assert.is_not.same(dirTable["file1"], nil)
+ assert.is_not.same(dirTable["file2"], nil)
+ assert.is_not.same(dirTable["intdir"], nil)
+ dirCount = 0
+ crt = coroutine.create(fs.dir_iterator)
+ while coroutine.status(crt) ~= "dead" do
+ local ok, val = coroutine.resume(crt, intdir)
+ if ok and val ~= nil then
+ dirCount = dirCount + 1
+ end
+ end
+ assert.same(dirCount, 0)
+ end)
+
+ it("does nothing if the argument is a file", function()
+ tmpfile1 = get_tmp_path()
+ create_file(tmpfile1)
+ local crt = coroutine.create(fs.dir_iterator)
+ while coroutine.status(crt) ~= "dead" do
+ local ok, val = coroutine.resume(crt, tmpfile1)
+ assert.falsy(ok and res)
+ end
+ end)
+
+ it("does nothing if the argument is invalid", function()
+ local crt = coroutine.create(fs.dir_iterator)
+ while coroutine.status(crt) ~= "dead" do
+ local ok, val = coroutine.resume(crt, "/nonexistent")
+ assert.falsy(ok and res)
+ end
+ end)
+ end)
+
+ describe("fs.is_writable", function()
+ local tmpfile
+ local tmpdir
+
+ after_each(function()
+ if tmpfile then
+ os.remove(tmpfile)
+ tmpfile = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns true if the file given as argument is writable", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile)
+ assert.truthy(fs.is_writable(tmpfile))
+ end)
+
+ it("returns true if the directory given as argument is writable", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ assert.truthy(fs.is_writable(tmpdir))
+ tmpfile = tmpdir .. "/internalfile"
+ create_file(tmpfile)
+ make_unwritable(tmpfile)
+ assert.truthy(fs.is_writable(tmpdir))
+ end)
+
+ it("returns false if the file given as argument is not writable", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile)
+ make_unwritable(tmpfile)
+ assert.falsy(fs.is_writable(tmpfile))
+ end)
+
+ it("returns false if the directory given as argument is not writable", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ make_unwritable(tmpdir)
+ assert.falsy(fs.is_writable(tmpdir))
+ end)
+
+ it("returns false if the file or directory given as argument does not exist", function()
+ assert.falsy(fs.is_writable("/nonexistent"))
+ end)
+ end)
+
+ describe("fs.set_time #unix", function()
+ local tmpfile
+ local tmpdir
+ local intdir
+
+ after_each(function()
+ if tmpfile then
+ os.remove(tmpfile)
+ tmpfile = nil
+ end
+ if intdir then
+ os.remove(intdir)
+ intdir = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns true and modifies the access time of the file given as argument", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile)
+ local newtime = os.time() - 100
+ assert.truthy(fs.set_time(tmpfile, newtime))
+ assert.same(lfs.attributes(tmpfile, "access"), newtime)
+ assert.same(lfs.attributes(tmpfile, "modification"), newtime)
+ end)
+
+ it("returns true and modifies the access time of the directory given as argument", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ tmpfile = tmpdir .. "/internalfile"
+ create_file(tmpfile)
+ local newtime = os.time() - 100
+ assert.truthy(fs.set_time(tmpdir, newtime))
+ assert.same(lfs.attributes(tmpdir, "access"), newtime)
+ assert.same(lfs.attributes(tmpdir, "modification"), newtime)
+ assert.is_not.same(lfs.attributes(tmpfile, "access"), newtime)
+ assert.is_not.same(lfs.attributes(tmpfile, "modification"), newtime)
+ end)
+
+ it("returns false and does nothing if the file or directory given as arguments doesn't exist", function()
+ assert.falsy(fs.set_time("/nonexistent"))
+ end)
+ end)
+
+ describe("fs.set_permissions", function()
+ local readfile
+ local execfile
+ local tmpdir
+
+ after_each(function()
+ if readfile then
+ os.remove(readfile)
+ readfile = nil
+ end
+ if execfile then
+ os.remove(execfile)
+ execfile = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns true and sets the permissions of the argument accordingly", function()
+ readfile = get_tmp_path()
+ create_file(readfile)
+ make_unreadable(readfile)
+ assert.falsy(io.open(readfile, "r"))
+ assert.truthy(fs.set_permissions(readfile, "read", "user"))
+ assert.truthy(io.open(readfile, "r"))
+
+ if is_win then
+ execfile = get_tmp_path() .. ".exe"
+ create_file(execfile)
+ else
+ execfile = get_tmp_path() .. ".sh"
+ create_file(execfile, "#!/bin/bash")
+ end
+ make_unexecutable(execfile)
+ local fd = assert(io.popen(execfile .. " 2>&1"))
+ local result = assert(fd:read("*a"))
+ assert.truthy(result:match("denied"))
+ fd:close()
+ assert.truthy(fs.set_permissions(execfile, "exec", "user"))
+ fd = assert(io.popen(execfile .. " 2>&1"))
+ result = assert(fd:read("*a"))
+ assert.falsy(result:match("denied"))
+ fd:close()
+
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ make_unexecutable(tmpdir)
+ fd = assert(io.popen("cd " .. fs.Q(tmpdir) .. " 2>&1"))
+ result = assert(fd:read("*a"))
+ assert.truthy(result:match("denied") or result:match("can't cd"))
+ fd:close()
+ assert.truthy(fs.set_permissions(tmpdir, "exec", "user"))
+ fd = assert(io.popen("cd " .. fs.Q(tmpdir) .. " 2>&1"))
+ result = assert(fd:read("*a"))
+ assert.falsy(result:match("denied") or result:match("can't cd"))
+ fd:close()
+ end)
+
+ it("returns false and does nothing if the argument is nonexistent", function()
+ assert.falsy(fs.set_permissions("/nonexistent", "read", "user"))
+ end)
+ end)
+
+ describe("fs.is_file", function()
+ local tmpfile
+ local tmpdir
+
+ after_each(function()
+ if tmpfile then
+ os.remove(tmpfile)
+ tmpfile = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns true when the argument is a file", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile)
+ assert.same(true, fs.is_file(tmpfile))
+ end)
+
+ it("returns false when the argument does not exist", function()
+ assert.same(false, fs.is_file("/nonexistent"))
+ end)
+
+ it("returns false when the argument exists but is not a file", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ assert.same(false, fs.is_file("/nonexistent"))
+ end)
+
+ it("#unix returns false when the argument is a symlink to a directory", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ local linkname = tmpdir .. "/symlink"
+ finally(function() os.remove(linkname) end)
+ lfs.link(tmpdir, linkname, true)
+ assert.falsy(fs.is_file(linkname))
+ end)
+
+ it("#unix returns true when the argument is a symlink to a file", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile)
+ local linkname = tmpfile .. "_symlink"
+ finally(function() os.remove(linkname) end)
+ lfs.link(tmpfile, linkname, true)
+ assert.truthy(fs.is_file(linkname))
+ end)
+ end)
+
+ describe("fs.is_dir", function()
+ local tmpfile
+ local tmpdir
+
+ after_each(function()
+ if tmpfile then
+ os.remove(tmpfile)
+ tmpfile = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns true when the argument is a directory", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ assert.truthy(fs.is_dir(tmpdir))
+ end)
+
+ it("returns false when the argument is a file", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile)
+ assert.falsy(fs.is_dir(tmpfile))
+ end)
+
+ it("#unix returns true when the argument is a symlink to a directory", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ local linkname = tmpdir .. "/symlink"
+ finally(function() os.remove(linkname) end)
+ lfs.link(tmpdir, linkname, true)
+ assert.truthy(fs.is_dir(linkname))
+ end)
+
+ it("#unix returns false when the argument is a symlink to a file", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile)
+ local linkname = tmpfile .. "_symlink"
+ finally(function() os.remove(linkname) end)
+ lfs.link(tmpfile, linkname, true)
+ assert.falsy(fs.is_dir(linkname))
+ end)
+
+ it("returns false when the argument does not exist", function()
+ assert.falsy(fs.is_dir("/nonexistent"))
+ end)
+ end)
+
+ describe("fs.exists", function()
+ local tmpfile
+ local tmpdir
+
+ after_each(function()
+ if tmpfile then
+ os.remove(tmpfile)
+ tmpfile = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns true when the argument is a file", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile)
+ assert.truthy(fs.exists(tmpfile))
+ end)
+
+ it("returns true when the argument is a directory", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ assert.truthy(fs.exists(tmpdir))
+ end)
+
+ it("returns false when the argument does not exist", function()
+ assert.falsy(fs.exists("/nonexistent"))
+ end)
+ end)
+
+ describe("fs.current_dir", function()
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ olddir = lfs.currentdir()
+ end)
+
+ after_each(function()
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ if olddir then
+ chdir(olddir)
+ olddir = nil
+ end
+ end)
+
+ it("returns the current working directory", function()
+ local currentdir = lfs.currentdir()
+ assert.same(currentdir, fs.current_dir())
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ assert.truthy(fs.change_dir(tmpdir))
+ if is_win then
+ assert.same(tmpdir, fs.current_dir())
+ else
+ assert.same(lfs.attributes(tmpdir).ino, lfs.attributes((fs.current_dir())).ino)
+ end
+ end)
+ end)
+
+ describe("fs.change_dir", function()
+ local tmpfile
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ olddir = lfs.currentdir()
+ end)
+
+ after_each(function()
+ if tmpfile then
+ os.remove(tmpfile)
+ tmpfile = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ if olddir then
+ chdir(olddir)
+ olddir = nil
+ end
+ end)
+
+ it("returns true and changes the current working directory if the argument is a directory", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ assert.truthy(fs.change_dir(tmpdir))
+ if is_win then
+ assert.same(tmpdir, fs.current_dir())
+ else
+ assert.same(lfs.attributes(tmpdir).ino, lfs.attributes(fs.current_dir()).ino)
+ end
+ end)
+
+ it("returns false and does nothing when the argument is a file", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile)
+ assert.falsy(fs.change_dir(tmpfile))
+ assert.same(olddir, lfs.currentdir())
+ end)
+
+ it("returns false and does nothing when the argument does not exist", function()
+ assert.falsy(fs.change_dir("/nonexistent"))
+ assert.same(olddir, lfs.currentdir())
+ end)
+ end)
+
+ describe("fs.change_dir_to_root", function()
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ olddir = lfs.currentdir()
+ end)
+
+ after_each(function()
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ if olddir then
+ chdir(olddir)
+ end
+ end)
+
+ it("returns true and changes the current directory to root if the current directory is valid", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ assert.truthy(fs.change_dir(tmpdir))
+ assert.truthy(fs.change_dir_to_root())
+ if is_win then
+ local curr_dir = fs.current_dir()
+ assert.truthy(curr_dir == "C:\\" or curr_dir == P"/")
+ else
+ assert.same(P"/", fs.current_dir())
+ end
+ end)
+
+ it("returns false and does nothing if the current directory is not valid #unix", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ chdir(tmpdir)
+ lfs.rmdir(tmpdir)
+ assert.falsy(fs.change_dir_to_root())
+ assert.is_not.same("/", lfs.currentdir())
+ end)
+ end)
+
+ describe("fs.pop_dir", function()
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ olddir = lfs.currentdir()
+ end)
+
+ after_each(function()
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ if olddir then
+ chdir(olddir)
+ end
+ end)
+
+ it("returns true and changes the current directory to the previous one in the dir stack if the dir stack is not empty", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ assert.truthy(fs.change_dir(tmpdir))
+ assert.truthy(fs.pop_dir())
+ assert.same(olddir, lfs.currentdir())
+ end)
+ end)
+
+ describe("fs.make_dir", function()
+ local tmpfile
+ local tmpdir
+ local intdir
+
+ after_each(function()
+ if tmpfile then
+ os.remove(tmpfile)
+ tmpfile = nil
+ end
+ if intdir then
+ lfs.rmdir(intdir)
+ intdir = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns true and creates the directory specified by the argument", function()
+ tmpdir = get_tmp_path()
+ assert.truthy(fs.make_dir(tmpdir))
+ assert.same("directory", lfs.attributes(tmpdir, "mode"))
+ end)
+
+ it("returns true and creates the directory path specified by the argument", function()
+ tmpdir = get_tmp_path()
+ intdir = "/internaldir"
+ local dirpath = tmpdir .. intdir
+ assert.truthy(fs.make_dir(dirpath))
+ assert.same("directory", lfs.attributes(tmpdir, "mode"))
+ assert.same("directory", lfs.attributes(dirpath, "mode"))
+ end)
+
+ it("returns false and does nothing if the argument is not valid (file in the path)", function()
+ tmpfile = get_tmp_path()
+ local fd = assert(io.open(tmpfile, "w"))
+ assert(fd:write("foo"))
+ fd:close()
+ intdir = "/internaldir"
+ local dirpath = tmpfile .. intdir
+ assert.falsy(fs.make_dir(dirpath))
+ end)
+
+ it("returns false and does nothing if the argument already exists", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile)
+ assert.falsy(fs.make_dir(tmpfile))
+ end)
+ end)
+
+ describe("fs.remove_dir_if_empty", function()
+ local tmpfile
+ local tmpdir
+
+ after_each(function()
+ if tmpfile then
+ os.remove(tmpfile)
+ tmpfile = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("removes the directory specified by the argument if it is empty", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ fs.remove_dir_if_empty(tmpdir)
+ assert.falsy(exists_file(tmpdir))
+ end)
+
+ it("does nothing if the directory specified by the argument is not empty", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ tmpfile = "/internalfile"
+ local filepath = tmpdir .. tmpfile
+ create_file(filepath)
+ fs.remove_dir_if_empty(tmpdir)
+ assert.truthy(exists_file(tmpdir))
+ end)
+ end)
+
+ describe("fs.remove_dir_tree_if_empty", function()
+ local tmpfile
+ local tmpdir
+ local intdir
+
+ after_each(function()
+ if tmpfile then
+ os.remove(tmpfile)
+ tmpfile = nil
+ end
+ if intdir then
+ lfs.rmdir(intdir)
+ intdir = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("removes the directory path specified by the argument if it is empty", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ fs.remove_dir_tree_if_empty(tmpdir)
+ assert.falsy(exists_file(tmpdir))
+ end)
+
+ it("does nothing if the directory path specified by the argument is not empty", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ intdir = "/internaldir"
+ local dirpath = tmpdir .. intdir
+ lfs.mkdir(dirpath)
+ tmpfile = "/internalfile"
+ local filepath = dirpath .. tmpfile
+ fs.remove_dir_tree_if_empty(tmpdir)
+ assert.truthy(exists_file(dirpath))
+ assert.truthy(exists_file(tmpdir))
+ end)
+ end)
+
+ describe("fs.list_dir", function()
+ local intfile1
+ local intfile2
+ local intdir
+ local tmpdir
+
+ before_each(function()
+ if intfile1 then
+ os.remove(intfile1)
+ intfile1 = nil
+ end
+ if intfile2 then
+ os.remove(intfile2)
+ intfile2 = nil
+ end
+ if intdir then
+ lfs.rmdir(intdir)
+ intdir = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns a table with the contents of the given directory", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ intfile1 = tmpdir .. "/intfile1"
+ create_file(intfile1)
+ intdir = tmpdir .. "/intdir"
+ lfs.mkdir(intdir)
+ intfile2 = intdir .. "/intfile2"
+ create_file(intfile2)
+ local result = fs.list_dir(tmpdir)
+ assert.same(#result, 2)
+ assert.truthy(result[1] == "intfile1" or result[1] == "intdir")
+ assert.truthy(result[2] == "intfile1" or result[2] == "intdir")
+ assert.is_not.same(result[1], result[2])
+ end)
+
+ it("returns an empty table if the argument is a file", function()
+ intfile1 = get_tmp_path()
+ create_file(intfile1)
+ local result = fs.list_dir(intfile1)
+ assert.same(#result, 0)
+ end)
+
+ it("does nothing if the argument is nonexistent", function()
+ assert.same(fs.list_dir("/nonexistent"), {})
+ end)
+
+ it("does nothing if the argument doesn't have the proper permissions", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ make_unreadable(tmpdir)
+ assert.same(fs.list_dir(tmpdir), {})
+ end)
+ end)
+
+ describe("fs.copy", function()
+ local srcfile
+ local dstfile
+ local tmpdir
+
+ after_each(function()
+ if srcfile then
+ os.remove(srcfile)
+ srcfile = nil
+ end
+ if dstfile then
+ os.remove(dstfile)
+ dstfile = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns true and copies the contents and the permissions of the source file to the destination file", function()
+ srcfile = get_tmp_path()
+ create_file(srcfile, srccontent)
+ dstfile = get_tmp_path()
+ assert.truthy(fs.copy(srcfile, dstfile))
+ local fd = assert(io.open(dstfile, "r"))
+ local dstcontent = fd:read("*a")
+ assert.same("foo", dstcontent)
+ if posix_ok then
+ assert.same(lfs.attributes(srcfile, "permissions"), lfs.attributes(dstfile, "permissions"))
+ end
+ end)
+
+ it("returns true and copies contents of the source file to the destination file with custom permissions", function()
+ srcfile = get_tmp_path()
+ create_file(srcfile, srccontent)
+ dstfile = get_tmp_path()
+ assert.truthy(fs.copy(srcfile, dstfile, "exec"))
+ local fd = assert(io.open(dstfile, "r"))
+ local dstcontent = fd:read("*a")
+ assert.same("foo", dstcontent)
+ end)
+
+ it("returns false and does nothing if the source file does not exist", function()
+ srcfile = get_tmp_path()
+ dstfile = get_tmp_path()
+ local ok, err = fs.copy(srcfile, dstfile, nil)
+ assert.falsy(ok)
+ assert.not_match("are the same file", err)
+ assert.falsy(exists_file(dstfile))
+ end)
+
+ it("returns false and does nothing if the source file doesn't have the proper permissions", function()
+ srcfile = get_tmp_path()
+ create_file(srcfile)
+ make_unreadable(srcfile)
+ dstfile = get_tmp_path()
+ assert.falsy(fs.copy(srcfile, dstfile, nil))
+ assert.falsy(exists_file(dstfile))
+ end)
+
+ it("returns false and does nothing if the destination file directory doesn't have the proper permissions", function()
+ srcfile = get_tmp_path()
+ create_file(srcfile)
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ make_unwritable(tmpdir)
+ dstfile = tmpdir .. "/dstfile"
+ assert.falsy(fs.copy(srcfile, dstfile, nil))
+ assert(fs.set_permissions(tmpdir, "exec", "all"))
+ assert.falsy(exists_file(dstfile))
+ end)
+ end)
+
+ describe("fs.copy_contents", function()
+ local srcfile
+ local dstfile
+ local srcintdir
+ local dstintdir
+ local srcdir
+ local dstdir
+
+ after_each(function()
+ if srcfile then
+ os.remove(srcfile)
+ srcfile = nil
+ end
+ if dstfile then
+ os.remove(dstfile)
+ dstfile = nil
+ end
+ if srcintdir then
+ lfs.rmdir(srcintdir)
+ srcintdir = nil
+ end
+ if dstintdir then
+ lfs.rmdir(dstintdir)
+ dstintdir = nil
+ end
+ if srcdir then
+ lfs.rmdir(srcdir)
+ srcdir = nil
+ end
+ if dstdir then
+ lfs.rmdir(dstdir)
+ dstdir = nil
+ end
+ end)
+
+ local create_dir_tree = function()
+ srcdir = get_tmp_path()
+ lfs.mkdir(srcdir)
+ srcintdir = srcdir .. "/internaldir"
+ lfs.mkdir(srcintdir)
+ srcfile = srcintdir .. "/internalfile"
+ create_file(srcfile)
+ dstdir = get_tmp_path()
+ end
+
+ it("returns true and copies the contents (with their permissions) of the source dir to the destination dir", function()
+ create_dir_tree()
+ assert.truthy(fs.copy_contents(srcdir, dstdir))
+ assert.truthy(exists_file(dstdir))
+ dstintdir = dstdir .. "/internaldir"
+ assert.truthy(exists_file(dstintdir))
+ dstfile = dstdir .. "/internaldir/internalfile"
+ local fd = assert(io.open(dstfile, "r"))
+ local dstfilecontent = fd:read("*a")
+ assert.same("foo", dstfilecontent)
+ if posix_ok then
+ assert.same(lfs.attributes(srcfile, "permissions"), lfs.attributes(dstfile, "permissions"))
+ end
+ end)
+
+ it("returns true and copies the contents of the source dir to the destination dir with custom permissions", function()
+ create_dir_tree()
+ assert.truthy(fs.copy_contents(srcdir, dstdir, "read"))
+ assert.truthy(exists_file(dstdir))
+ dstintdir = dstdir .. "/internaldir"
+ assert.truthy(exists_file(dstintdir))
+ dstfile = dstdir .. "/internaldir/internalfile"
+ local fd = assert(io.open(dstfile, "r"))
+ local dstfilecontent = fd:read("*a")
+ assert.same("foo", dstfilecontent)
+ end)
+
+ it("returns false and does nothing if the source dir doesn't exist", function()
+ srcdir = get_tmp_path()
+ dstdir = get_tmp_path()
+ assert.falsy(fs.copy_contents(srcdir, dstdir))
+ assert.falsy(exists_file(dstdir))
+ end)
+
+ it("returns false if the source argument is a file", function()
+ srcdir = get_tmp_path()
+ create_file(srcdir)
+ dstdir = get_tmp_path()
+ assert.falsy(fs.copy_contents(srcdir, dstdir))
+ assert.falsy(exists_file(dstdir))
+ end)
+
+ it("returns false and does nothing if the source dir doesn't have the proper permissions", function()
+ create_dir_tree()
+ make_unreadable(srcdir)
+ assert.falsy(fs.copy_contents(srcdir, dstdir))
+ assert.falsy(exists_file(dstdir .. "/internaldir"))
+ assert.falsy(exists_file(dstdir .. "/internalfile"))
+ end)
+ end)
+
+ describe("fs.find", function()
+ local tmpdir
+ local intdir
+ local intfile1
+ local intfile2
+
+ after_each(function()
+ if intfile1 then
+ os.remove(intfile1)
+ intfile1 = nil
+ end
+ if intfile2 then
+ os.remove(intfile2)
+ intfile2 = nil
+ end
+ if intdir then
+ lfs.rmdir(intdir)
+ intdir = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ local create_dir_tree = function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ intfile1 = tmpdir .. "/intfile1"
+ create_file(intfile1)
+ intdir = tmpdir .. "/intdir"
+ lfs.mkdir(intdir)
+ intfile2 = intdir .. "/intfile2"
+ create_file(intfile2)
+ end
+
+ it("returns a table of all the contents in the directory given as argument", function()
+ create_dir_tree()
+ local contents = {}
+ local count = 0
+ for _, file in pairs(fs.find(tmpdir)) do
+ contents[file] = true
+ count = count + 1
+ end
+ assert.same(count, 3)
+ assert.is_not.same(contents[tmpdir], true)
+ assert.same(contents[P"intfile1"], true)
+ assert.same(contents[P"intdir"], true)
+ assert.same(contents[P"intdir/intfile2"], true)
+ end)
+
+ it("uses the current working directory if the argument is nil", function()
+ create_dir_tree()
+ local olddir = fs.current_dir()
+ fs.change_dir(intdir)
+ local contents = {}
+ local count = 0
+ for _, file in pairs(fs.find()) do
+ contents[file] = true
+ count = count + 1
+ end
+ assert.same(count, 1)
+ assert.is_not.same(contents["intfile1"], true)
+ assert.is_not.same(contents["intdir"], true)
+ assert.same(contents["intfile2"], true)
+ fs.change_dir(olddir)
+ end)
+
+ it("returns an empty table if the argument is nonexistent", function()
+ local contents = fs.find("/nonexistent")
+ local count = 0
+ for _, file in pairs(contents) do
+ count = count + 1
+ end
+ assert.same(count, 0)
+ end)
+
+ it("returns an empty table if the argument is a file", function()
+ intfile1 = get_tmp_path()
+ create_file(intfile1)
+ local contents = fs.find(intfile1)
+ local count = 0
+ for _, file in pairs(contents) do
+ count = count + 1
+ end
+ assert.same(count, 0)
+ end)
+
+ it("does nothing if the argument doesn't have the proper permissions", function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ make_unreadable(tmpdir)
+ assert.same(fs.find(tmpdir), {})
+ end)
+ end)
+
+ describe("fs.move", function()
+ local srcfile
+ local dstfile
+ local tmpdir
+
+ after_each(function()
+ if srcfile then
+ os.remove(srcfile)
+ srcfile = nil
+ end
+ if dstfile then
+ os.remove(dstfile)
+ dstfile = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ it("returns true and moves the source (together with its permissions) to the destination", function()
+ srcfile = get_tmp_path()
+ create_file(srcfile)
+ dstfile = get_tmp_path()
+ local oldperms = lfs.attributes(srcfile, "permissions")
+ assert.truthy(fs.move(srcfile, dstfile))
+ assert.truthy(fs.exists(dstfile))
+ assert.falsy(fs.exists(srcfile))
+ local fd = assert(io.open(dstfile, "r"))
+ local dstcontents = assert(fd:read("*a"))
+ assert.same(dstcontents, "foo")
+ if posix_ok then
+ assert.same(oldperms, lfs.attributes(dstfile, "permissions"))
+ end
+ end)
+
+ it("returns true and moves the source (with custom permissions) to the destination", function()
+ srcfile = get_tmp_path()
+ create_file(srcfile)
+ dstfile = get_tmp_path()
+ assert.truthy(fs.move(srcfile, dstfile, "read"))
+ assert.truthy(fs.exists(dstfile))
+ assert.falsy(fs.exists(srcfile))
+ local fd = assert(io.open(dstfile, "r"))
+ local dstcontents = assert(fd:read("*a"))
+ assert.same(dstcontents, "foo")
+ end)
+
+ it("returns false and does nothing if the source doesn't exist", function()
+ dstfile = get_tmp_path()
+ assert.falsy(fs.move("/nonexistent", dstfile))
+ assert.falsy(fs.exists(dstfile))
+ end)
+
+ it("returns false and does nothing if the destination already exists", function()
+ srcfile = get_tmp_path()
+ create_file(srcfile)
+ dstfile = get_tmp_path()
+ create_file(dstfile, "bar")
+ assert.falsy(fs.move(srcfile, dstfile))
+ assert.truthy(fs.exists(srcfile))
+ local fd = assert(io.open(dstfile, "r"))
+ local dstcontents = assert(fd:read("*a"))
+ assert.same(dstcontents, "bar")
+ end)
+
+ it("returns false and does nothing if the destination path doesn't have the proper permissions", function()
+ srcfile = get_tmp_path()
+ create_file(srcfile)
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ make_unwritable(tmpdir)
+ assert.falsy(fs.move(srcfile, tmpdir .. "/dstfile"))
+ assert.falsy(fs.exists(tmpdir .. "/dstfile"))
+ end)
+ end)
+
+ describe("fs.is_lua", function()
+ local tmpfile
+
+ after_each(function()
+ if tmpfile then
+ os.remove(tmpfile)
+ tmpfile = nil
+ end
+ end)
+
+ it("returns true if the argument is a valid lua script", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile, "print(\"foo\")")
+ assert.truthy(fs.is_lua(tmpfile))
+ end)
+
+ it("returns true if the argument is a valid lua script with shebang", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile, "#!/usr/bin/env lua\n\nprint(\"foo\")")
+ assert.truthy(fs.is_lua(tmpfile))
+ end)
+
+ it("returns false if the argument is not a valid lua script", function()
+ tmpfile = os.tmpname()
+ create_file(tmpfile)
+ assert.falsy(fs.is_lua(tmpfile))
+ end)
+
+ it("returns false if the argument is a valid lua script but doesn't have the proper permissions", function()
+ tmpfile = get_tmp_path()
+ create_file(tmpfile, "print(\"foo\")")
+ make_unreadable(tmpfile)
+ assert.falsy(fs.is_lua(tmpfile))
+ end)
+ end)
+
+ describe("fs.delete", function()
+ local tmpfile1
+ local tmpfile2
+ local tmpintdir
+ local tmpdir
+
+ after_each(function()
+ if tmpfile1 then
+ os.remove(tmpfile1)
+ tmpfile1 = nil
+ end
+ if tmpfile2 then
+ os.remove(tmpfile2)
+ tmpfile2 = nil
+ end
+ if tmpintdir then
+ lfs.rmdir(tmpintdir)
+ tmpintdir = nil
+ end
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ tmpdir = nil
+ end
+ end)
+
+ local create_dir_tree = function()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ tmpintdir = tmpdir .. "/internaldir"
+ lfs.mkdir(tmpintdir)
+ tmpfile1 = tmpdir .. "/internalfile1"
+ create_file(tmpfile1)
+ tmpfile2 = tmpdir .. "/internalfile2"
+ create_file(tmpfile2)
+ end
+
+ it("deletes the file specified by the argument", function()
+ tmpfile1 = get_tmp_path()
+ tmpfile2 = get_tmp_path()
+ fs.delete(tmpfile1)
+ fs.delete(tmpfile2)
+ assert.falsy(exists_file(tmpfile1))
+ assert.falsy(exists_file(tmpfile2))
+ end)
+
+ it("deletes the contents of the directory specified by the argument", function()
+ create_dir_tree()
+ fs.delete(tmpdir)
+ assert.falsy(exists_file(tmpfile2))
+ assert.falsy(exists_file(tmpintdir))
+ assert.falsy(exists_file(tmpfile1))
+ assert.falsy(exists_file(tmpdir))
+ end)
+ end)
+
+ describe("fs.zip", function()
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ olddir = lfs.currentdir()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ chdir(tmpdir)
+
+ write_file("file1", "content1", finally)
+ write_file("file2", "content2", finally)
+ lfs.mkdir("dir")
+ write_file("dir/file3", "content3", finally)
+ end)
+
+ after_each(function()
+ if olddir then
+ chdir(olddir)
+ if tmpdir then
+ lfs.rmdir(tmpdir .. "/dir")
+ lfs.rmdir(tmpdir)
+ end
+ end
+ end)
+
+ it("returns true and creates a zip archive of the given files", function()
+ assert.truthy(fs.zip("archive.zip", "file1", "file2", "dir"))
+ assert.truthy(exists_file("archive.zip"))
+ end)
+
+ it("returns false and does nothing if the files specified in the arguments are invalid", function()
+ assert.falsy(fs.zip("archive.zip", "nonexistent"))
+ assert.falsy(exists_file("nonexistent"))
+ end)
+ end)
+
+ describe("fs.bunzip2", function()
+
+ it("uncompresses a .bz2 file", function()
+ local input = testing_paths.fixtures_dir .. "/abc.bz2"
+ local output = os.tmpname()
+ assert.truthy(fs.bunzip2(input, output))
+ local fd = io.open(output, "r")
+ local content = fd:read("*a")
+ fd:close()
+ assert.same(300000, #content)
+ local abc = ("a"):rep(100000)..("b"):rep(100000)..("c"):rep(100000)
+ assert.same(abc, content)
+ end)
+
+ end)
+
+ describe("fs.unzip", function()
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ olddir = lfs.currentdir()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ chdir(tmpdir)
+
+ write_file("file1", "content1", finally)
+ write_file("file2", "content2", finally)
+ lfs.mkdir("dir")
+ write_file("dir/file3", "content3", finally)
+ end)
+
+ after_each(function()
+ if olddir then
+ chdir(olddir)
+ if tmpdir then
+ lfs.rmdir(tmpdir .. "/dir")
+ lfs.rmdir(tmpdir)
+ end
+ end
+ end)
+
+ it("returns true and unzips the given zip archive", function()
+ assert.truthy(fs.zip("archive.zip", "file1", "file2", "dir"))
+ os.remove("file1")
+ os.remove("file2")
+ lfs.rmdir("dir")
+
+ assert.truthy(fs.unzip("archive.zip"))
+ assert.truthy(exists_file("file1"))
+ assert.truthy(exists_file("file2"))
+ assert.truthy(exists_file("dir/file3"))
+
+ local fd
+
+ fd = assert(io.open("file1", "r"))
+ assert.same(fd:read("*a"), "content1")
+ fd:close()
+
+ fd = assert(io.open("file2", "r"))
+ assert.same(fd:read("*a"), "content2")
+ fd:close()
+
+ fd = assert(io.open("dir/file3", "r"))
+ assert.same(fd:read("*a"), "content3")
+ fd:close()
+ end)
+
+ it("does nothing if the given archive is invalid", function()
+ assert.falsy(fs.unzip("archive.zip"))
+ end)
+ end)
+
+ describe("fs.wrap_script", function()
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ olddir = lfs.currentdir()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ chdir(tmpdir)
+ end)
+
+ after_each(function()
+ if olddir then
+ chdir(olddir)
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ end
+ end
+ end)
+
+ it("produces a wrapper for a Lua script", function()
+ write_file("my_script", "io.write('Hello ' .. arg[1])", finally)
+ path.use_tree(testing_paths.testing_tree)
+ local wrapper_name = fs.absolute_name("wrapper") .. test_env.wrapper_extension
+ fs.wrap_script("my_script", wrapper_name, "one", nil, nil, "World")
+ local pd = assert(io.popen(wrapper_name))
+ local data = pd:read("*a")
+ pd:close()
+ assert.same("Hello World", data)
+ end)
+ end)
+
+ describe("fs.copy_binary", function()
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ olddir = lfs.currentdir()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ chdir(tmpdir)
+
+ write_file("test.exe", "", finally)
+ end)
+
+ after_each(function()
+ if olddir then
+ chdir(olddir)
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ end
+ end
+ end)
+
+ it("returns true and copies the given binary file to the file specified in the dest argument", function()
+ assert.truthy(fs.copy_binary("test.exe", lfs.currentdir() .. "/copy.exe"))
+ assert.truthy(exists_file("copy.exe"))
+ if is_win then
+ assert.truthy(exists_file("test.lua"))
+ local fd = assert(io.open("test.lua", "r"))
+ local content = assert(fd:read("*a"))
+ assert.truthy(content:find("package.path", 1, true))
+ assert.truthy(content:find("package.cpath", 1, true))
+ fd:close()
+ end
+ end)
+
+ it("returns false and does nothing if the source file is invalid", function()
+ assert.falsy(fs.copy_binary("invalid.exe", "copy.exe"))
+ end)
+ end)
+
+ describe("fs.modules", function()
+ local tmpdir
+ local olddir
+ local oldpath
+
+ before_each(function()
+ olddir = lfs.currentdir()
+ tmpdir = get_tmp_path()
+ lfs.mkdir(tmpdir)
+ chdir(tmpdir)
+ lfs.mkdir("lib")
+ write_file("lib/module1.lua", "", finally)
+ write_file("lib/module2.lua", "", finally)
+ write_file("lib/module1.LuA", "", finally)
+ write_file("lib/non_lua", "", finally)
+ lfs.mkdir("lib/internal")
+ write_file("lib/internal/module11.lua", "", finally)
+ write_file("lib/internal/module22.lua", "", finally)
+
+ oldpath = package.path
+ package.path = package.path .. tmpdir .. "/?.lua;"
+ end)
+
+ after_each(function()
+ if olddir then
+ chdir(olddir)
+ if tmpdir then
+ lfs.rmdir(tmpdir .. "/lib/internal")
+ lfs.rmdir(tmpdir .. "/lib")
+ lfs.rmdir(tmpdir)
+ end
+ end
+ if oldpath then
+ package.path = oldpath
+ end
+ end)
+
+ it("returns a table of the lua modules at a specific require path", function()
+ local result
+
+ result = fs.modules("lib")
+ assert.same(#result, 2)
+ assert.truthy(result[1] == "module1" or result[2] == "module1")
+ assert.truthy(result[1] == "module2" or result[2] == "module2")
+
+ result = fs.modules("lib.internal")
+ assert.same(#result, 2)
+ assert.truthy(result[1] == "module11" or result[2] == "module11")
+ assert.truthy(result[1] == "module22" or result[2] == "module22")
+ end)
+
+ it("returns an empty table if the modules couldn't be found in package.path", function()
+ package.path = ""
+ assert.same(fs.modules("lib"), {})
+ end)
+ end)
+
+ describe("#unix fs._unix_rwx_to_number", function()
+
+ it("converts permissions in rwx notation to numeric ones", function()
+ assert.same(tonumber("0644", 8), fs._unix_rwx_to_number("rw-r--r--"))
+ assert.same(tonumber("0755", 8), fs._unix_rwx_to_number("rwxr-xr-x"))
+ assert.same(tonumber("0000", 8), fs._unix_rwx_to_number("---------"))
+ assert.same(tonumber("0777", 8), fs._unix_rwx_to_number("rwxrwxrwx"))
+ assert.same(tonumber("0700", 8), fs._unix_rwx_to_number("rwx------"))
+ assert.same(tonumber("0600", 8), fs._unix_rwx_to_number("rw-------"))
+ end)
+
+ it("produces a negated mask if asked to", function()
+ assert.same(tonumber("0133", 8), fs._unix_rwx_to_number("rw-r--r--", true))
+ assert.same(tonumber("0022", 8), fs._unix_rwx_to_number("rwxr-xr-x", true))
+ assert.same(tonumber("0777", 8), fs._unix_rwx_to_number("---------", true))
+ assert.same(tonumber("0000", 8), fs._unix_rwx_to_number("rwxrwxrwx", true))
+ assert.same(tonumber("0077", 8), fs._unix_rwx_to_number("rwx------", true))
+ assert.same(tonumber("0177", 8), fs._unix_rwx_to_number("rw-------", true))
+ end)
+ end)
+
+ describe("fs.execute_env", function()
+ local tmpname
+ local tmplua
+ local LUA = "lua"
+
+ local function readfile(pathname)
+ local file = assert(io.open(pathname, "rb"))
+ local data = file:read "*a"
+ file:close()
+ return data
+ end
+
+ lazy_setup(function()
+ tmpname = os.tmpname()
+
+ tmplua = os.tmpname()
+ local f = assert(io.open(tmplua, 'wb'))
+ f:write [[
+ local out = io.open((...), 'wb')
+ out:write(os.getenv 'FOO')
+ out:close()
+ ]]
+ f:close()
+ LUA = test_env.testing_paths.lua
+ end)
+
+ after_each(function()
+ os.remove(tmpname)
+ end)
+
+ lazy_teardown(function()
+ os.remove(tmpname)
+ end)
+
+ it("passes variables w/o spaces correctly", function()
+ fs.execute_env({
+ FOO = "BAR",
+ }, LUA, tmplua, tmpname)
+ local data = readfile(tmpname)
+ assert.same("BAR", data)
+ end)
+
+ it("passes variables w/ spaces correctly", function()
+ fs.execute_env({
+ FOO = "BAR with spaces",
+ }, LUA, tmplua, tmpname)
+ local data = readfile(tmpname)
+ assert.same("BAR with spaces", data)
+ end)
+ end)
+
+end)
diff --git a/spec/unit/fun_spec.lua b/spec/unit/fun_spec.lua
new file mode 100644
index 0000000..bffb60d
--- /dev/null
+++ b/spec/unit/fun_spec.lua
@@ -0,0 +1,128 @@
+local test_env = require("spec.util.test_env")
+local testing_paths = test_env.testing_paths
+
+local fun = require("luarocks.fun")
+
+describe("luarocks.fun #unit", function()
+ local runner
+
+ lazy_setup(function()
+ runner = require("luacov.runner")
+ runner.init(testing_paths.testrun_dir .. "/luacov.config")
+ end)
+
+ lazy_teardown(function()
+ runner.save_stats()
+ end)
+
+ describe("fun.concat", function()
+ it("returns the concatenation of the two tables given as arguments", function()
+ local t1, t2
+
+ t1 = {1, 2, 3}
+ t2 = {4, 5, 6}
+ assert.same(fun.concat(t1, t2), {1, 2, 3, 4, 5, 6})
+ assert.same(fun.concat(t2, t1), {4, 5, 6, 1, 2, 3})
+ t1 = {1, 2, 3}
+ t2 = {}
+ assert.same(fun.concat(t1, t2), {1, 2, 3})
+ assert.same(fun.concat(t2, t1), {1, 2, 3})
+ t1 = {}
+ t2 = {}
+ assert.same(fun.concat(t1, t2), {})
+ end)
+ end)
+
+ describe("fun.contains", function()
+ it("checks whether a table contains a given value", function()
+ local t
+
+ t = {1, 2, 3}
+ assert.truthy(fun.contains(t, 1))
+ assert.falsy(fun.contains(t, 4))
+ t = {}
+ assert.falsy(fun.contains(t, 1))
+ end)
+ end)
+
+ local addOne = function(x) return x + 1 end
+
+ describe("fun.map", function()
+ it("applies a function to each element in the given table and returns the results in a new table", function()
+ local t
+
+ t = {1, 2, 3}
+ assert.same(fun.map(t, addOne), {2, 3, 4})
+ t = {}
+ assert.same(fun.map(t, addOne), {})
+ end)
+ end)
+
+ describe("fun.traverse", function()
+ it("recursively applies a function to each element in a given table and returns the results in a new table", function()
+ local t
+
+ t = {1, 2, {3, 4, {5, 6}}}
+ assert.same(fun.traverse(t, addOne), {2, 3, {4, 5, {6, 7}}})
+ t = {1, 2, {}, {1, {}, 2}}
+ assert.same(fun.traverse(t, addOne), {2, 3, {}, {2, {}, 3}})
+ end)
+ end)
+
+ describe("fun.filter", function()
+ it("filters the elements in the given table and returns the result in a new table", function()
+ local t
+
+ t = {1, 2, "a", "b", 3}
+ assert.same(fun.filter(t, function(x) return type(x) == "number" end), {1, 2, 3})
+ t = {2, 4, 7, 8}
+ assert.same(fun.filter(t, function(x) return x % 2 == 0 end), {2, 4, 8})
+ end)
+ end)
+
+ describe("fun.reverse_in", function()
+ it("reverses the elements in the given table and returns the result in a new table", function()
+ local t
+
+ t = {1, 2, 3, 4}
+ assert.same(fun.reverse_in(t), {4, 3, 2, 1})
+ t = {"a", "b", "c"}
+ assert.same(fun.reverse_in(t), {"c", "b", "a"})
+ end)
+ end)
+
+ describe("fun.sort_in", function()
+ it("sorts the elements in the given table and returns the result in a new table", function()
+ local t
+
+ t = {4, 2, 3, 1}
+ assert.same(fun.sort_in(t), {1, 2, 3, 4})
+ t = {"d", "a", "c", "b"}
+ assert.same(fun.sort_in(t), {"a", "b", "c", "d"})
+ end)
+ end)
+
+ describe("fun.flip", function()
+ it("returns a function behaving as the one given in the argument but with the arguments interchanged", function()
+ local a, b = fun.flip(function(a, b) return a, b end)(5, 6)
+ assert.same(a, 6)
+ assert.same(b, 5)
+ end)
+ end)
+
+ describe("fun.partial", function()
+ it("partially applies the given arguments to the given function and returns it", function()
+ local addOne = fun.partial(function(x, y) return x + y end, 1)
+ assert.same(addOne(1), 2)
+ assert.same(addOne(2), 3)
+
+ local addTwo = fun.partial(function(x, y, z) return x + y + z end, 1, 1)
+ assert.same(addTwo(1), 3)
+ assert.same(addTwo(2), 4)
+
+ local addThree = fun.partial(function(x, y, z, t, u) return x + y + z + t + u end, 1, 1, 1)
+ assert.same(addThree(1, 1), 5)
+ assert.same(addThree(1, 2), 6)
+ end)
+ end)
+end)
diff --git a/spec/unit/loader_spec.lua b/spec/unit/loader_spec.lua
new file mode 100644
index 0000000..cde3059
--- /dev/null
+++ b/spec/unit/loader_spec.lua
@@ -0,0 +1,18 @@
+local test_env = require("spec.util.test_env")
+local run = test_env.run
+
+test_env.setup_specs()
+
+describe("luarocks.loader", function()
+ describe("#unit", function()
+ it("starts", function()
+ assert(run.lua_bool([[-e "require 'luarocks.loader'; print(package.loaded['luarocks.loaded'])"]]))
+ end)
+
+ describe("which", function()
+ it("finds modules using package.path", function()
+ assert(run.lua_bool([[-e "loader = require 'luarocks.loader'; local x,y,z,p = loader.which('luarocks.loader', 'p'); assert(p == 'p')"]]))
+ end)
+ end)
+ end)
+end)
diff --git a/spec/unit/persist_spec.lua b/spec/unit/persist_spec.lua
new file mode 100644
index 0000000..ed78345
--- /dev/null
+++ b/spec/unit/persist_spec.lua
@@ -0,0 +1,74 @@
+local test_env = require("spec.util.test_env")
+local testing_paths = test_env.testing_paths
+
+local persist = require("luarocks.persist")
+
+describe("luarocks.persist #unit", function()
+ local runner
+
+ lazy_setup(function()
+ runner = require("luacov.runner")
+ runner.init(testing_paths.testrun_dir .. "/luacov.config")
+ end)
+
+ lazy_teardown(function()
+ runner.save_stats()
+ end)
+
+ describe("persist.save_from_table_to_string", function()
+ it("simple table", function()
+ assert.are.same([[
+bar = 1234
+foo = "string"
+]], persist.save_from_table_to_string({foo = "string", bar = 1234}))
+ end)
+
+ it("nested tables", function()
+ assert.are.same([[
+bar = {
+ baz = "string"
+}
+foo = {
+ 1, 2, 3, 4
+}
+]], persist.save_from_table_to_string({foo = {1, 2, 3, 4}, bar = {baz = "string"}}))
+ end)
+
+ it("table with a keyword key (#947)", function()
+ assert.are.same([[
+bar = {
+ ["function"] = "foo"
+}
+]], persist.save_from_table_to_string({bar = {["function"] = "foo"}}))
+ end)
+
+ it("strings with quotes", function()
+ assert.are.same([[
+bar = "a \\backslash?"
+foo = "a \"quote\"?"
+]], persist.save_from_table_to_string({foo = 'a "quote"?', bar = 'a \\backslash?'}))
+ end)
+
+ it("multiline strings", function()
+ assert.are.same([===[
+bar = [==[
+]]
+]=]]==]
+foo = [[
+First line
+Second line]]
+]===], persist.save_from_table_to_string({foo = "First line\nSecond line", bar = "]]\n]=]"}))
+ end)
+
+ it("multiline strings ending with brackets", function()
+ assert.are.same([===[
+bar = [==[
+]]
+]=]==]
+foo = [=[
+First line
+Second line [1]]=]
+]===], persist.save_from_table_to_string({foo = "First line\nSecond line [1]", bar = "]]\n]="}))
+ end)
+ end)
+end)
diff --git a/spec/unit/rockspecs_spec.lua b/spec/unit/rockspecs_spec.lua
new file mode 100644
index 0000000..7eb033c
--- /dev/null
+++ b/spec/unit/rockspecs_spec.lua
@@ -0,0 +1,126 @@
+
+local rockspecs = require("luarocks.rockspecs")
+local cfg = require("luarocks.core.cfg")
+local test_env = require("spec.util.test_env")
+local lfs = require("lfs")
+
+describe("luarocks.rockspecs #unit", function()
+
+ lazy_setup(function()
+ cfg.init()
+ end)
+
+ it("auto adds a build dependency for non-vendored build types", function()
+ local filename = "test-1.0-1.rockspec"
+ local rockspec = {
+ package = "test",
+ source = {
+ url = "",
+ },
+ build = {
+ type = "foo"
+ },
+ }
+ local globals = {}
+ local quick = true
+
+ local out = rockspecs.from_persisted_table(filename, rockspec, globals, quick)
+
+ assert(rockspec == out)
+ assert.same(rockspec.build_dependencies, {
+ { name = "luarocks-build-foo", constraints = {} },
+ })
+ end)
+
+ it("does not add a build dependency for non-vendored build type if it's already ther", function()
+ local filename = "test-1.0-1.rockspec"
+ local rockspec = {
+ package = "test",
+ source = {
+ url = "",
+ },
+ build_dependencies = {
+ "luarocks-build-cpp >= 1.0",
+ },
+ build = {
+ type = "cpp"
+ },
+ }
+ local globals = {}
+ local quick = true
+
+ local out = rockspecs.from_persisted_table(filename, rockspec, globals, quick)
+
+ assert(rockspec == out)
+
+ assert.same(rockspec.build_dependencies, {
+ { name = "luarocks-build-cpp", constraints = { { op = ">=", version = { string = "1.0", 1, 0 } } } },
+ })
+ end)
+
+ it("does not add a build dependency for 'none' build type", function()
+ local filename = "test-1.0-1.rockspec"
+ local rockspec = {
+ package = "test",
+ source = {
+ url = "",
+ },
+ build = {
+ type = "none"
+ },
+ }
+ local globals = {}
+ local quick = true
+
+ local out = rockspecs.from_persisted_table(filename, rockspec, globals, quick)
+
+ assert(rockspec == out)
+ assert.same(rockspec.build_dependencies, {})
+ end)
+
+ it("does not add a build dependency for 'module' build type", function()
+ local filename = "test-1.0-1.rockspec"
+ local rockspec = {
+ package = "test",
+ source = {
+ url = "",
+ },
+ build = {
+ type = "none"
+ },
+ }
+ local globals = {}
+ local quick = true
+
+ local out = rockspecs.from_persisted_table(filename, rockspec, globals, quick)
+
+ assert(rockspec == out)
+ assert.same(rockspec.build_dependencies, {})
+ end)
+
+ for d in lfs.dir(test_env.testing_paths.src_dir .. "/luarocks/build") do
+ local name = d:match("(.*)%.lua")
+ if name then
+ it("does not add a build dependency for vendored '" .. name .. "' type", function()
+ local filename = "test-1.0-1.rockspec"
+ local rockspec = {
+ package = "test",
+ source = {
+ url = "",
+ },
+ build = {
+ type = name
+ },
+ }
+ local globals = {}
+ local quick = true
+
+ local out = rockspecs.from_persisted_table(filename, rockspec, globals, quick)
+
+ assert(rockspec == out)
+ assert.same(rockspec.build_dependencies, {})
+ end)
+ end
+ end
+
+end)
diff --git a/spec/unit/sysdetect_spec.lua b/spec/unit/sysdetect_spec.lua
new file mode 100644
index 0000000..d3b1695
--- /dev/null
+++ b/spec/unit/sysdetect_spec.lua
@@ -0,0 +1,79 @@
+
+local sysdetect = require("luarocks.core.sysdetect")
+local lfs = require("lfs")
+
+describe("luarocks.core.sysdetect #unix #unit", function()
+
+ lazy_setup(function()
+ os.execute([=[
+ [ -e binary-samples ] || {
+ git clone --depth=1 https://github.com/hishamhm/binary-samples
+ ( cd binary-samples && git pull )
+ }
+ ]=])
+ end)
+
+ local files = {
+ ["."] = "ignore",
+ [".."] = "ignore",
+ ["README.md"] = "ignore",
+ [".git"] = "ignore",
+ ["MIT_LICENSE"] = "ignore",
+ ["anti-disassembler"] = "ignore",
+ ["elf-Linux-lib-x64.so"] = "ignore",
+ ["elf-Linux-lib-x86.so"] = "ignore",
+
+ ["elf-Linux-x64-bash"] = {"linux", "x86_64"},
+ ["elf-Linux-ia64-bash"] = {"linux", "ia_64"},
+ ["MachO-OSX-ppc-and-i386-bash"] = {"macosx", "x86"},
+ ["MachO-OSX-ppc-openssl-1.0.1h"] = {"macosx", "ppc"},
+ ["MachO-iOS-armv7-armv7s-arm64-Helloworld"] = {"macosx", "arm"},
+ ["pe-Windows-x64-cmd"] = {"windows", "x86_64"},
+ ["MachO-iOS-armv7s-Helloworld"] = {"macosx", "arm"},
+ ["elf-Linux-SparcV8-bash"] = {"linux", "sparcv8"},
+ ["elf-HPUX-ia64-bash"] = {"hpux", "ia_64"},
+ ["MachO-OSX-x64-ls"] = {"macosx", "x86_64"},
+ ["pe-Windows-ARMv7-Thumb2LE-HelloWorld"] = {"windows", "armv7l"},
+ ["elf-ARMv6-static-gofmt"] = {"sysv", "arm"},
+ ["elf-Linux-s390-bash"] = {"linux", "s390"},
+ ["elf-Linux-Alpha-bash"] = {"linux", "alpha"},
+ ["elf-Linux-hppa-bash"] = {"linux", "hppa"},
+ ["elf-Linux-x86_64-static-sln"] = {"linux", "x86_64"},
+ ["elf-Linux-Mips4-bash"] = {"linux", "mips"},
+ ["elf-ARMv6-dynamic-go"] = {"linux", "arm"},
+ ["elf-Linux-SuperH4-bash"] = {"linux", "superh"},
+ ["elf-Linux-x86-bash"] = {"linux", "x86"},
+ ["elf-Linux-PowerPC-bash"] = {"linux", "ppc"},
+ ["libSystem.B.dylib"] = {"macosx", "x86_64"},
+ ["MachO-iOS-arm1176JZFS-bash"] = {"macosx", "arm"},
+ ["pe-Windows-x86-cmd"] = {"windows", "x86"},
+ ["elf-Linux-ARMv7-ls"] = {"linux", "arm"},
+ ["elf-Linux-ARM64-bash"] = {"linux", "aarch64"},
+ ["MachO-OSX-x86-ls"] = {"macosx", "x86"},
+ ["elf-solaris-sparc-ls"] = {"solaris", "sparc"},
+ ["elf-solaris-x86-ls"] = {"solaris", "x86"},
+ ["pe-mingw32-strip.exe"] = {"windows", "x86"},
+ ["elf-OpenBSD-x86_64-sh"] = {"openbsd", "x86_64"},
+ ["elf-NetBSD-x86_64-echo"] = {"netbsd", "x86_64"},
+ ["elf-FreeBSD-x86_64-echo"] = {"freebsd", "x86_64"},
+ ["elf-Haiku-GCC2-ls"] = {"haiku", "x86"},
+ ["elf-Haiku-GCC7-WebPositive"] = {"haiku", "x86"},
+ ["pe-cygwin-ls.exe"] = {"cygwin", "x86"},
+ ["elf-DragonFly-x86_64-less"] = {"dragonfly", "x86_64"},
+
+ }
+
+ describe("detect_file", function()
+ it("detects system and processor", function()
+ for f in lfs.dir("binary-samples") do
+ if files[f] ~= "ignore" then
+ assert.table(files[f], "unknown binary " .. f)
+ local expected_s, expected_p = files[f][1], files[f][2]
+ local s, p = sysdetect.detect_file("binary-samples/" .. f)
+ assert.same(expected_s, s, "bad system for " .. f)
+ assert.same(expected_p, p, "bad processor for " .. f)
+ end
+ end
+ end)
+ end)
+end)
diff --git a/spec/unit/test_spec.lua b/spec/unit/test_spec.lua
new file mode 100644
index 0000000..4a31777
--- /dev/null
+++ b/spec/unit/test_spec.lua
@@ -0,0 +1,173 @@
+local test_env = require("spec.util.test_env")
+local lfs = require("lfs")
+local get_tmp_path = test_env.get_tmp_path
+local testing_paths = test_env.testing_paths
+local write_file = test_env.write_file
+
+local fs = require("luarocks.fs")
+local cfg = require("luarocks.core.cfg")
+local path = require("luarocks.path")
+local test = require("luarocks.test")
+local test_busted = require("luarocks.test.busted")
+local test_command = require("luarocks.test.command")
+
+describe("LuaRocks test #unit", function()
+ local runner
+
+ lazy_setup(function()
+ cfg.init()
+ fs.init()
+ runner = require("luacov.runner")
+ runner.init(testing_paths.testrun_dir .. "/luacov.config")
+ end)
+
+ lazy_teardown(function()
+ runner.save_stats()
+ end)
+
+ local tmpdir
+ local olddir
+
+ local create_tmp_dir = function()
+ tmpdir = get_tmp_path()
+ olddir = lfs.currentdir()
+ lfs.mkdir(tmpdir)
+ lfs.chdir(tmpdir)
+ fs.change_dir(tmpdir)
+ end
+
+ local destroy_tmp_dir = function()
+ if olddir then
+ lfs.chdir(olddir)
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ end
+ end
+ end
+
+ describe("test.command", function()
+ describe("command.detect_type", function()
+ before_each(function()
+ create_tmp_dir()
+ end)
+
+ after_each(function()
+ destroy_tmp_dir()
+ end)
+
+ it("returns true if test.lua exists", function()
+ write_file("test.lua", "", finally)
+ assert.truthy(test_command.detect_type())
+ end)
+
+ it("returns false if test.lua doesn't exist", function()
+ assert.falsy(test_command.detect_type())
+ end)
+ end)
+
+ describe("command.run_tests", function()
+ before_each(function()
+ create_tmp_dir()
+ end)
+
+ after_each(function()
+ destroy_tmp_dir()
+ end)
+
+ it("returns the result of the executed tests", function()
+ write_file("test.lua", "assert(1==1)", finally)
+ assert.truthy(test_command.run_tests(nil, {}))
+
+ write_file("test.lua", "assert(1==2)", finally)
+ assert.falsy(test_command.run_tests(nil, {}))
+ end)
+
+ it("returns the result of the executed tests with custom arguments and test command", function()
+ write_file("test.lua", "assert(1==1)", finally)
+
+ local test = {
+ script = "test.lua",
+ flags = {
+ arg1 = "1",
+ arg2 = "2"
+ },
+ command = fs.Q(testing_paths.lua)
+ }
+ assert.truthy(test_command.run_tests(test, {}))
+ end)
+
+ it("returns false and does nothing if the test script doesn't exist", function()
+ assert.falsy(test_command.run_tests(nil, {}))
+ end)
+ end)
+ end)
+
+ describe("test.busted", function()
+ describe("busted.detect_type", function()
+ before_each(function()
+ create_tmp_dir()
+ end)
+
+ after_each(function()
+ destroy_tmp_dir()
+ end)
+
+ it("returns true if .busted exists", function()
+ write_file(".busted", "", finally)
+ assert.truthy(test_busted.detect_type())
+ end)
+
+ it("returns false if .busted doesn't exist", function()
+ assert.falsy(test_busted.detect_type())
+ end)
+ end)
+
+ describe("busted.run_tests", function()
+ before_each(function()
+ path.use_tree(testing_paths.testing_sys_tree)
+ create_tmp_dir()
+ end)
+
+ after_each(function()
+ destroy_tmp_dir()
+ end)
+
+ pending("returns the result of the executed tests", function()
+ -- FIXME: busted issue
+ write_file("test_spec.lua", "assert(1==1)", finally)
+ assert.truthy(test_busted.run_tests(nil, {}))
+
+ write_file("test_spec.lua", "assert(1==2)", finally)
+ assert.falsy(test_busted.run_tests())
+ end)
+ end)
+ end)
+
+ describe("test", function()
+ describe("test.run_test_suite", function()
+ before_each(function()
+ create_tmp_dir()
+ end)
+
+ after_each(function()
+ destroy_tmp_dir()
+ end)
+
+ it("returns false if the given rockspec cannot be loaded", function()
+ assert.falsy(test.run_test_suite("invalid", nil, {}))
+ end)
+
+ it("returns false if no test type was detected", function()
+ assert.falsy(test.run_test_suite({ package = "test" }, nil, {}))
+ end)
+
+ it("returns the result of executing the tests specified in the given rockspec", function()
+ write_file("test.lua", "assert(1==1)", finally)
+ assert.truthy(test.run_test_suite({ test_dependencies = {} }, nil, {}))
+
+ write_file("test.lua", "assert(1==2)", finally)
+ assert.falsy(test.run_test_suite({ test_dependencies = {} }, nil, {}))
+ end)
+ end)
+ end)
+end)
diff --git a/spec/unit/tools_spec.lua b/spec/unit/tools_spec.lua
new file mode 100644
index 0000000..5b85c86
--- /dev/null
+++ b/spec/unit/tools_spec.lua
@@ -0,0 +1,251 @@
+local test_env = require("spec.util.test_env")
+local get_tmp_path = test_env.get_tmp_path
+local testing_paths = test_env.testing_paths
+local write_file = test_env.write_file
+
+local fs = require("luarocks.fs")
+local cfg = require("luarocks.core.cfg")
+local patch = require("luarocks.tools.patch")
+
+local lao =
+[[The Nameless is the origin of Heaven and Earth;
+The named is the mother of all things.
+
+Therefore let there always be non-being,
+ so we may see their subtlety,
+And let there always be being,
+ so we may see their outcome.
+The two are the same,
+But after they are produced,
+ they have different names.
+They both may be called deep and profound.
+Deeper and more profound,
+The door of all subtleties!]]
+
+local tzu =
+[[The Way that can be told of is not the eternal Way;
+The name that can be named is not the eternal name.
+The Nameless is the origin of Heaven and Earth;
+The Named is the mother of all things.
+Therefore let there always be non-being,
+ so we may see their subtlety,
+And let there always be being,
+ so we may see their outcome.
+The two are the same,
+But after they are produced,
+ they have different names.]]
+
+local valid_patch1 =
+[[--- lao 2002-02-21 23:30:39.942229878 -0800
++++ tzu 2002-02-21 23:30:50.442260588 -0800
+@@ -1,7 +1,6 @@
+-The Way that can be told of is not the eternal Way;
+-The name that can be named is not the eternal name.
+ The Nameless is the origin of Heaven and Earth;
+-The Named is the mother of all things.
++The named is the mother of all things.
++
+ Therefore let there always be non-being,
+ so we may see their subtlety,
+ And let there always be being,
+@@ -9,3 +8,6 @@
+ The two are the same,
+ But after they are produced,
+ they have different names.
++They both may be called deep and profound.
++Deeper and more profound,
++The door of all subtleties!]]
+
+local valid_patch2 =
+[[--- /dev/null 1969-02-21 23:30:39.942229878 -0800
++++ tzu 2002-02-21 23:30:50.442260588 -0800
+@@ -1,7 +1,6 @@
+-The Way that can be told of is not the eternal Way;
+-The name that can be named is not the eternal name.
+ The Nameless is the origin of Heaven and Earth;
+-The Named is the mother of all things.
++The named is the mother of all things.
++
+ Therefore let there always be non-being,
+ so we may see their subtlety,
+ And let there always be being,
+@@ -9,3 +8,6 @@
+ The two are the same,
+ But after they are produced,
+ they have different names.
++They both may be called deep and profound.
++Deeper and more profound,
++The door of all subtleties!]]
+
+local invalid_patch1 =
+[[--- lao 2002-02-21 23:30:39.942229878 -0800
++++ tzu 2002-02-21 23:30:50.442260588 -0800
+@@ -1,7 +1,6 @@
+-The Way that can be told of is not the eternal Way;
+-The name that can be named is not the eternal name.
+ The Nameless is the origin of Heaven and Earth;
+-The Named is the mother of all things.
+--- Extra
++The named is the mother of all things.
++
+ Therefore let there always be non-being,
+ so we may see their subtlety,
+ And let there always be being,
+--- Extra
+@@ -9,3 +8,7 @@
+ The two are the same,
+ But after they are produced,
+ they have different names.
++They both may be called deep and profound.
++Deeper and more profound,
++The door of all subtleties!]]
+
+local invalid_patch2 =
+[[--- lao 2002-02-21 23:30:39.942229878 -0800
++++ tzu 2002-02-21 23:30:50.442260588 -0800
+@@ -1,7 +1,6 @@
+-The Way that can be told of is not the eternal Way;
+-The name that can be named is not the eternal name.
+ The Nameless is the origin of Heaven and Earth;
+-The Named is the mother of all things.
++The named is the mother of all things.
++
+ Therefore let there always be non-being,
+ so we may see their subtlety,
+ And let there always be being,
+@@ -9,3 +8,6 @@
+ The two are the same,
+ But after they are produced,
+ they have different names.
++They both may be called deep and profound.
++Deeper and more profound,
+? ...
++The door of all subtleties!]]
+
+local invalid_patch3 =
+[[--- lao 2002-02-21 23:30:39.942229878 -0800
++++ tzu 2002-02-21 23:30:50.442260588 -0800
+@@ -1,7 +1,6 @@
+-The Way that can be told of is not the eternal Way;
+-The name that can be named is not the eternal name.
+ The Nameless is the origin of Heaven and Earth;
+-The Named is the mother of all things.
++The named is the mother of all things.
++
+ Therefore let there always be non-being,
+ so we may see their subtlety,
+ And let there always be being,
+@@ -9,3 +8,6 @@
+ The two are the same,
+ But after they are produced,
+ they have different names.
++They both may be called deep and profound.
++Deeper and more profound,
+? ...
++The door of all subtleties!]]
+
+describe("Luarocks patch test #unit", function()
+ local runner
+
+ lazy_setup(function()
+ cfg.init()
+ fs.init()
+ runner = require("luacov.runner")
+ runner.init(testing_paths.testrun_dir .. "/luacov.config")
+ end)
+
+ lazy_teardown(function()
+ runner.save_stats()
+ end)
+
+ describe("patch.read_patch", function()
+ it("returns a table with the patch file info and the result of parsing the file", function()
+ local t, result
+
+ write_file("test.patch", valid_patch1, finally)
+ t, result = patch.read_patch("test.patch")
+ assert.truthy(result)
+ assert.truthy(t)
+
+ write_file("test.patch", invalid_patch1, finally)
+ t, result = patch.read_patch("test.patch")
+ assert.falsy(result)
+ assert.truthy(t)
+
+ write_file("test.patch", invalid_patch2, finally)
+ t, result = patch.read_patch("test.patch")
+ assert.falsy(result)
+ assert.truthy(t)
+
+ write_file("test.patch", invalid_patch3, finally)
+ t, result = patch.read_patch("test.patch")
+ assert.falsy(result)
+ assert.truthy(t)
+ end)
+ end)
+
+ describe("patch.apply_patch", function()
+ local tmpdir
+ local olddir
+
+ before_each(function()
+ tmpdir = get_tmp_path()
+ olddir = lfs.currentdir()
+ lfs.mkdir(tmpdir)
+ lfs.chdir(tmpdir)
+
+ write_file("lao", tzu, finally)
+ write_file("tzu", lao, finally)
+ end)
+
+ after_each(function()
+ if olddir then
+ lfs.chdir(olddir)
+ if tmpdir then
+ lfs.rmdir(tmpdir)
+ end
+ end
+ end)
+
+ it("applies the given patch and returns the result of patching", function()
+ write_file("test.patch", valid_patch1, finally)
+ local p = patch.read_patch("test.patch")
+ local result = patch.apply_patch(p)
+ assert.truthy(result)
+ end)
+
+ it("applies the given patch with custom arguments and returns the result of patching", function()
+ write_file("test.patch", valid_patch2, finally)
+ local p = patch.read_patch("test.patch")
+ local result = patch.apply_patch(p, nil, true)
+ assert.truthy(result)
+ end)
+
+ it("fails if the patch file is invalid", function()
+ write_file("test.patch", invalid_patch1, finally)
+ local p = patch.read_patch("test.patch")
+ local result = pcall(patch.apply_patch, p)
+ assert.falsy(result)
+ end)
+
+ it("returns false if the files from the patch doesn't exist", function()
+ os.remove("lao")
+ os.remove("tzu")
+
+ write_file("test.patch", valid_patch1, finally)
+ local p = patch.read_patch("test.patch")
+ local result = patch.apply_patch(p)
+ assert.falsy(result)
+ end)
+
+ it("returns false if the target file was already patched", function()
+ write_file("test.patch", valid_patch1, finally)
+ local p = patch.read_patch("test.patch")
+ local result = patch.apply_patch(p)
+ assert.truthy(result)
+
+ result = patch.apply_patch(p)
+ assert.falsy(result)
+ end)
+ end)
+end)
diff --git a/spec/unit/util_spec.lua b/spec/unit/util_spec.lua
new file mode 100644
index 0000000..8b234b2
--- /dev/null
+++ b/spec/unit/util_spec.lua
@@ -0,0 +1,160 @@
+local test_env = require("spec.util.test_env")
+local testing_paths = test_env.testing_paths
+local P = test_env.P
+
+local util = require("luarocks.util")
+local core_util = require("luarocks.core.util")
+
+describe("luarocks.util #unit", function()
+ local runner
+
+ lazy_setup(function()
+ runner = require("luacov.runner")
+ runner.init(testing_paths.testrun_dir .. "/luacov.config")
+ end)
+
+ lazy_teardown(function()
+ runner.save_stats()
+ end)
+
+ describe("util.variable_substitutions", function()
+ it("replaces variables", function()
+ local t = {
+ ["hello"] = "$(KIND) world",
+ }
+ util.variable_substitutions(t, {
+ ["KIND"] = "happy",
+ })
+ assert.are.same({
+ ["hello"] = "happy world",
+ }, t)
+ end)
+
+ it("missing variables are empty", function()
+ local t = {
+ ["hello"] = "$(KIND) world",
+ }
+ util.variable_substitutions(t, {
+ })
+ assert.are.same({
+ ["hello"] = " world",
+ }, t)
+ end)
+ end)
+
+ describe("util.sortedpairs", function()
+ local function collect(iter, state, var)
+ local collected = {}
+
+ while true do
+ local returns = {iter(state, var)}
+
+ if returns[1] == nil then
+ return collected
+ else
+ table.insert(collected, returns)
+ var = returns[1]
+ end
+ end
+ end
+
+ it("default sort", function()
+ assert.are.same({}, collect(util.sortedpairs({})))
+ assert.are.same({
+ {1, "v1"},
+ {2, "v2"},
+ {3, "v3"},
+ {"bar", "v5"},
+ {"foo", "v4"}
+ }, collect(util.sortedpairs({"v1", "v2", "v3", foo = "v4", bar = "v5"})))
+ end)
+
+ it("sort by function", function()
+ local function compare(a, b) return a > b end
+ assert.are.same({}, collect(util.sortedpairs({}, compare)))
+ assert.are.same({
+ {3, "v3"},
+ {2, "v2"},
+ {1, "v1"}
+ }, collect(util.sortedpairs({"v1", "v2", "v3"}, compare)))
+ end)
+
+ it("sort by priority table", function()
+ assert.are.same({}, collect(util.sortedpairs({}, {"k1", "k2"})))
+ assert.are.same({
+ {"k3", "v3"},
+ {"k2", "v2", {"sub order"}},
+ {"k1", "v1"},
+ {"k4", "v4"},
+ {"k5", "v5"},
+ }, collect(util.sortedpairs({
+ k1 = "v1", k2 = "v2", k3 = "v3", k4 = "v4", k5 = "v5"
+ }, {"k3", {"k2", {"sub order"}}, "k1"})))
+ end)
+ end)
+
+ describe("core.util.show_table", function()
+ it("returns a pretty-printed string containing the representation of the given table", function()
+ local result
+
+ local t1 = {1, 2, 3}
+ result = core_util.show_table(t1)
+ assert.truthy(result:find("[1] = 1", 1, true))
+ assert.truthy(result:find("[2] = 2", 1, true))
+ assert.truthy(result:find("[3] = 3", 1, true))
+
+ local t2 = {a = 1, b = 2, c = 3}
+ result = core_util.show_table(t2)
+ assert.truthy(result:find("[\"a\"] = 1", 1, true))
+ assert.truthy(result:find("[\"b\"] = 2", 1, true))
+ assert.truthy(result:find("[\"c\"] = 3", 1, true))
+
+ local t3 = {a = 1, b = "2", c = {3}}
+ result = core_util.show_table(t3)
+ assert.truthy(result:find("[\"a\"] = 1", 1, true))
+ assert.truthy(result:find("[\"b\"] = \"2\"", 1, true))
+ assert.truthy(result:find("[\"c\"] = {", 1, true))
+ assert.truthy(result:find("[1] = 3", 1, true))
+
+ local t4 = {a = 1, b = {c = 2, d = {e = "4"}}}
+ result = core_util.show_table(t4)
+ assert.truthy(result:find("[\"a\"] = 1", 1, true))
+ assert.truthy(result:find("[\"b\"] = {", 1, true))
+ assert.truthy(result:find("[\"c\"] = 2", 1, true))
+ assert.truthy(result:find("[\"d\"] = {", 1, true))
+ assert.truthy(result:find("[\"e\"] = \"4\"", 1, true))
+ end)
+ end)
+
+ describe("core.util.cleanup_path", function()
+ it("does not change order of existing items of prepended path", function()
+ local sys_path = P'/usr/local/bin;/usr/bin'
+ local lr_path = P'/home/user/.luarocks/bin;/usr/bin'
+ local path = lr_path .. ';' .. sys_path
+
+ local result = core_util.cleanup_path(path, ';', '5.3', false)
+ assert.are.equal(P'/home/user/.luarocks/bin;/usr/local/bin;/usr/bin', result)
+ end)
+
+ it("does not change order of existing items of appended path", function()
+ local sys_path = P'/usr/local/bin;/usr/bin'
+ local lr_path = P'/home/user/.luarocks/bin;/usr/bin'
+ local path = sys_path .. ';' .. lr_path
+
+ local result = core_util.cleanup_path(path, ';', '5.3', true)
+ assert.are.equal(P'/usr/local/bin;/usr/bin;/home/user/.luarocks/bin', result)
+ end)
+
+ it("rewrites versions that do not match the provided version", function()
+ local expected = P'a/b/lua/5.3/?.lua;a/b/c/lua/5.3/?.lua'
+ local result = core_util.cleanup_path(P'a/b/lua/5.2/?.lua;a/b/c/lua/5.3/?.lua', ';', '5.3')
+ assert.are.equal(expected, result)
+ end)
+
+ it("does not rewrite versions for which the provided version is a substring", function()
+ local expected = P'a/b/lua/5.3/?.lua;a/b/c/lua/5.3.4/?.lua'
+ local result = core_util.cleanup_path(P'a/b/lua/5.2/?.lua;a/b/c/lua/5.3.4/?.lua', ';', '5.3')
+ assert.are.equal(expected, result)
+ end)
+ end)
+end)