From 5155816b7b925dec5d5feb1568b1d7ceb00938b9 Mon Sep 17 00:00:00 2001 From: Mike Vink Date: Mon, 3 Feb 2025 21:29:42 +0100 Subject: fetch tarball --- spec/unit/build_spec.lua | 365 ++++++++++ spec/unit/deps_spec.lua | 143 ++++ spec/unit/dir_spec.lua | 70 ++ spec/unit/fetch_spec.lua | 486 +++++++++++++ spec/unit/fs_spec.lua | 1595 ++++++++++++++++++++++++++++++++++++++++++ spec/unit/fun_spec.lua | 128 ++++ spec/unit/loader_spec.lua | 18 + spec/unit/persist_spec.lua | 74 ++ spec/unit/rockspecs_spec.lua | 126 ++++ spec/unit/sysdetect_spec.lua | 79 +++ spec/unit/test_spec.lua | 173 +++++ spec/unit/tools_spec.lua | 251 +++++++ spec/unit/util_spec.lua | 160 +++++ 13 files changed, 3668 insertions(+) create mode 100644 spec/unit/build_spec.lua create mode 100644 spec/unit/deps_spec.lua create mode 100644 spec/unit/dir_spec.lua create mode 100644 spec/unit/fetch_spec.lua create mode 100644 spec/unit/fs_spec.lua create mode 100644 spec/unit/fun_spec.lua create mode 100644 spec/unit/loader_spec.lua create mode 100644 spec/unit/persist_spec.lua create mode 100644 spec/unit/rockspecs_spec.lua create mode 100644 spec/unit/sysdetect_spec.lua create mode 100644 spec/unit/test_spec.lua create mode 100644 spec/unit/tools_spec.lua create mode 100644 spec/unit/util_spec.lua (limited to 'spec/unit') 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 + #include + + 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) -- cgit v1.2.3