456 lines
13 KiB
Lua
456 lines
13 KiB
Lua
require("tests")
|
|
local fs = require("fs")
|
|
|
|
-- Test data
|
|
local test_content = "Hello, filesystem!\nThis is a test file.\n"
|
|
local test_dir = "test_fs_dir"
|
|
local test_file = fs.join(test_dir, "test.txt")
|
|
|
|
-- Clean up function
|
|
local function cleanup()
|
|
if fs.exists(test_file) then fs.remove(test_file) end
|
|
if fs.exists(test_dir) then fs.rmdir(test_dir) end
|
|
end
|
|
|
|
-- ======================================================================
|
|
-- SETUP AND CLEANUP
|
|
-- ======================================================================
|
|
|
|
-- Clean up before tests
|
|
cleanup()
|
|
|
|
-- ======================================================================
|
|
-- BASIC FILE OPERATIONS
|
|
-- ======================================================================
|
|
|
|
test("File Write and Read", function()
|
|
fs.mkdir(test_dir)
|
|
|
|
fs.write(test_file, test_content)
|
|
assert_equal(fs.exists(test_file), true)
|
|
assert_equal(fs.is_file(test_file), true)
|
|
assert_equal(fs.is_dir(test_file), false)
|
|
|
|
local content = fs.read(test_file)
|
|
assert_equal(content, test_content)
|
|
end)
|
|
|
|
test("File Size", function()
|
|
local size = fs.size(test_file)
|
|
assert_equal(size, #test_content)
|
|
assert_equal(fs.size("nonexistent.txt"), nil)
|
|
end)
|
|
|
|
test("File Append", function()
|
|
local additional = "Appended content.\n"
|
|
fs.append(test_file, additional)
|
|
|
|
local content = fs.read(test_file)
|
|
assert_equal(content, test_content .. additional)
|
|
end)
|
|
|
|
test("File Copy", function()
|
|
local copy_file = fs.join(test_dir, "copy.txt")
|
|
fs.copy(test_file, copy_file)
|
|
|
|
assert_equal(fs.exists(copy_file), true)
|
|
assert_equal(fs.read(copy_file), fs.read(test_file))
|
|
|
|
fs.remove(copy_file)
|
|
end)
|
|
|
|
test("File Move", function()
|
|
local move_file = fs.join(test_dir, "moved.txt")
|
|
local original_content = fs.read(test_file)
|
|
|
|
fs.move(test_file, move_file)
|
|
|
|
assert_equal(fs.exists(test_file), false)
|
|
assert_equal(fs.exists(move_file), true)
|
|
assert_equal(fs.read(move_file), original_content)
|
|
|
|
-- Move back for other tests
|
|
fs.move(move_file, test_file)
|
|
end)
|
|
|
|
test("File Lines", function()
|
|
local lines = fs.lines(test_file)
|
|
assert_equal(type(lines), "table")
|
|
assert(#lines >= 2, "should have multiple lines")
|
|
assert(string.find(lines[1], "Hello"), "first line should contain 'Hello'")
|
|
end)
|
|
|
|
test("File Touch", function()
|
|
local touch_file = fs.join(test_dir, "touched.txt")
|
|
|
|
fs.touch(touch_file)
|
|
assert_equal(fs.exists(touch_file), true)
|
|
assert_equal(fs.size(touch_file), 0)
|
|
|
|
fs.remove(touch_file)
|
|
end)
|
|
|
|
test("File Modification Time", function()
|
|
local mtime = fs.mtime(test_file)
|
|
assert_equal(type(mtime), "number")
|
|
assert(mtime > 0, "mtime should be positive")
|
|
|
|
-- Touch should update mtime
|
|
local old_mtime = mtime
|
|
fs.touch(test_file)
|
|
local new_mtime = fs.mtime(test_file)
|
|
assert(new_mtime >= old_mtime, "mtime should be updated")
|
|
end)
|
|
|
|
-- ======================================================================
|
|
-- DIRECTORY OPERATIONS
|
|
-- ======================================================================
|
|
|
|
test("Directory Creation and Removal", function()
|
|
local nested_dir = fs.join(test_dir, "nested", "deep")
|
|
|
|
fs.mkdir(nested_dir)
|
|
assert_equal(fs.exists(nested_dir), true)
|
|
assert_equal(fs.is_dir(nested_dir), true)
|
|
|
|
-- Clean up nested directories
|
|
fs.rmdir(fs.join(test_dir, "nested"))
|
|
end)
|
|
|
|
test("Directory Listing", function()
|
|
-- Create some test files
|
|
fs.write(fs.join(test_dir, "file1.txt"), "content1")
|
|
fs.write(fs.join(test_dir, "file2.log"), "content2")
|
|
fs.mkdir(fs.join(test_dir, "subdir"))
|
|
|
|
local entries = fs.list(test_dir)
|
|
assert_equal(type(entries), "table")
|
|
assert(#entries >= 3, "should have at least 3 entries")
|
|
|
|
-- Check entry structure
|
|
local found_file = false
|
|
for _, entry in ipairs(entries) do
|
|
assert_equal(type(entry.name), "string")
|
|
assert_equal(type(entry.is_dir), "boolean")
|
|
if entry.name == "file1.txt" then
|
|
found_file = true
|
|
assert_equal(entry.is_dir, false)
|
|
assert_equal(type(entry.size), "number")
|
|
end
|
|
end
|
|
assert_equal(found_file, true)
|
|
|
|
-- Test filtered listings
|
|
local files = fs.list_files(test_dir)
|
|
local dirs = fs.list_dirs(test_dir)
|
|
local names = fs.list_names(test_dir)
|
|
|
|
assert_equal(type(files), "table")
|
|
assert_equal(type(dirs), "table")
|
|
assert_equal(type(names), "table")
|
|
assert(#files > 0, "should have files")
|
|
assert(#dirs > 0, "should have directories")
|
|
|
|
-- Clean up
|
|
fs.remove(fs.join(test_dir, "file1.txt"))
|
|
fs.remove(fs.join(test_dir, "file2.log"))
|
|
fs.rmdir(fs.join(test_dir, "subdir"))
|
|
end)
|
|
|
|
-- ======================================================================
|
|
-- PATH OPERATIONS
|
|
-- ======================================================================
|
|
|
|
test("Path Join", function()
|
|
local path = fs.join("a", "b", "c", "file.txt")
|
|
assert(string.find(path, "file.txt"), "should contain filename")
|
|
|
|
local empty_path = fs.join()
|
|
assert_equal(type(empty_path), "string")
|
|
end)
|
|
|
|
test("Path Components", function()
|
|
local test_path = fs.join("home", "user", "documents", "file.txt")
|
|
|
|
local dir = fs.dirname(test_path)
|
|
local base = fs.basename(test_path)
|
|
local ext = fs.ext(test_path)
|
|
|
|
assert_equal(base, "file.txt")
|
|
assert_equal(ext, ".txt")
|
|
assert(string.find(dir, "documents"), "dirname should contain 'documents'")
|
|
end)
|
|
|
|
test("Path Split Extension", function()
|
|
local test_path = fs.join("home", "user", "file.tar.gz")
|
|
local dir, name, ext = fs.splitext(test_path)
|
|
|
|
assert_equal(name, "file.tar")
|
|
assert_equal(ext, ".gz")
|
|
assert(string.find(dir, "user"), "dir should contain 'user'")
|
|
end)
|
|
|
|
test("Path Absolute", function()
|
|
local abs_path = fs.abs(".")
|
|
assert_equal(type(abs_path), "string")
|
|
assert(#abs_path > 1, "absolute path should not be empty")
|
|
end)
|
|
|
|
test("Path Clean", function()
|
|
local messy_path = "./test/../test/./file.txt"
|
|
local clean_path = fs.clean(messy_path)
|
|
assert_equal(type(clean_path), "string")
|
|
assert(not string.find(clean_path, "%.%."), "should not contain '..'")
|
|
end)
|
|
|
|
test("Path Split", function()
|
|
local test_path = fs.join("home", "user", "file.txt")
|
|
local dir, file = fs.split(test_path)
|
|
|
|
assert_equal(file, "file.txt")
|
|
assert(string.find(dir, "user"), "dir should contain 'user'")
|
|
end)
|
|
|
|
-- ======================================================================
|
|
-- WORKING DIRECTORY
|
|
-- ======================================================================
|
|
|
|
test("Working Directory", function()
|
|
local original_cwd = fs.getcwd()
|
|
assert_equal(type(original_cwd), "string")
|
|
assert(#original_cwd > 0, "cwd should not be empty")
|
|
|
|
-- Test directory change
|
|
fs.chdir(test_dir)
|
|
local new_cwd = fs.getcwd()
|
|
assert(string.find(new_cwd, test_dir), "cwd should contain test_dir")
|
|
|
|
-- Change back
|
|
fs.chdir(original_cwd)
|
|
assert_equal(fs.getcwd(), original_cwd)
|
|
end)
|
|
|
|
-- ======================================================================
|
|
-- TEMPORARY FILES
|
|
-- ======================================================================
|
|
|
|
test("Temporary Files", function()
|
|
local temp_file = fs.tempfile("test_")
|
|
local temp_dir = fs.tempdir("test_")
|
|
|
|
assert_equal(type(temp_file), "string")
|
|
assert_equal(type(temp_dir), "string")
|
|
assert_equal(fs.exists(temp_file), true)
|
|
assert_equal(fs.exists(temp_dir), true)
|
|
assert_equal(fs.is_dir(temp_dir), true)
|
|
|
|
-- Clean up
|
|
fs.remove(temp_file)
|
|
fs.rmdir(temp_dir)
|
|
end)
|
|
|
|
-- ======================================================================
|
|
-- PATTERN MATCHING
|
|
-- ======================================================================
|
|
|
|
test("Glob Patterns", function()
|
|
-- Create test files for globbing
|
|
fs.write(fs.join(test_dir, "test1.txt"), "content")
|
|
fs.write(fs.join(test_dir, "test2.txt"), "content")
|
|
fs.write(fs.join(test_dir, "other.log"), "content")
|
|
|
|
local pattern = fs.join(test_dir, "*.txt")
|
|
local matches = fs.glob(pattern)
|
|
|
|
assert_equal(type(matches), "table")
|
|
assert(#matches >= 2, "should match txt files")
|
|
|
|
-- Clean up
|
|
fs.remove(fs.join(test_dir, "test1.txt"))
|
|
fs.remove(fs.join(test_dir, "test2.txt"))
|
|
fs.remove(fs.join(test_dir, "other.log"))
|
|
end)
|
|
|
|
test("Walk Directory", function()
|
|
-- Create nested structure
|
|
fs.mkdir(fs.join(test_dir, "sub1"))
|
|
fs.mkdir(fs.join(test_dir, "sub2"))
|
|
fs.write(fs.join(test_dir, "root.txt"), "content")
|
|
fs.write(fs.join(test_dir, "sub1", "nested.txt"), "content")
|
|
|
|
local files = fs.walk(test_dir)
|
|
assert_equal(type(files), "table")
|
|
assert(#files > 3, "should find multiple files and directories")
|
|
|
|
-- Clean up
|
|
fs.remove(fs.join(test_dir, "root.txt"))
|
|
fs.remove(fs.join(test_dir, "sub1", "nested.txt"))
|
|
fs.rmdir(fs.join(test_dir, "sub1"))
|
|
fs.rmdir(fs.join(test_dir, "sub2"))
|
|
end)
|
|
|
|
-- ======================================================================
|
|
-- UTILITY FUNCTIONS
|
|
-- ======================================================================
|
|
|
|
test("File Extension Functions", function()
|
|
local path = "document.pdf"
|
|
assert_equal(fs.extension(path), "pdf")
|
|
|
|
local new_path = fs.change_ext(path, "txt")
|
|
assert_equal(new_path, "document.txt")
|
|
|
|
local new_path2 = fs.change_ext(path, ".docx")
|
|
assert_equal(new_path2, "document.docx")
|
|
end)
|
|
|
|
test("Ensure Directory", function()
|
|
local ensure_dir = fs.join(test_dir, "ensure_test")
|
|
|
|
fs.ensure_dir(ensure_dir)
|
|
assert_equal(fs.exists(ensure_dir), true)
|
|
assert_equal(fs.is_dir(ensure_dir), true)
|
|
|
|
-- Should not error if already exists
|
|
fs.ensure_dir(ensure_dir)
|
|
|
|
fs.rmdir(ensure_dir)
|
|
end)
|
|
|
|
test("Human Readable Size", function()
|
|
local small_file = fs.join(test_dir, "small.txt")
|
|
fs.write(small_file, "test")
|
|
|
|
local size_str = fs.size_human(small_file)
|
|
assert_equal(type(size_str), "string")
|
|
assert(string.find(size_str, "B"), "should contain byte unit")
|
|
|
|
fs.remove(small_file)
|
|
end)
|
|
|
|
test("Safe Path Check", function()
|
|
assert_equal(fs.is_safe_path("safe/path.txt"), true)
|
|
assert_equal(fs.is_safe_path("../dangerous"), false)
|
|
assert_equal(fs.is_safe_path("/absolute/path"), false)
|
|
assert_equal(fs.is_safe_path("~/home/path"), false)
|
|
end)
|
|
|
|
test("Copy Tree", function()
|
|
-- Create source structure
|
|
local src_dir = fs.join(test_dir, "src")
|
|
local dst_dir = fs.join(test_dir, "dst")
|
|
|
|
fs.mkdir(src_dir)
|
|
fs.mkdir(fs.join(src_dir, "subdir"))
|
|
fs.write(fs.join(src_dir, "file1.txt"), "content1")
|
|
fs.write(fs.join(src_dir, "subdir", "file2.txt"), "content2")
|
|
|
|
fs.copytree(src_dir, dst_dir)
|
|
|
|
assert_equal(fs.exists(dst_dir), true)
|
|
assert_equal(fs.exists(fs.join(dst_dir, "file1.txt")), true)
|
|
assert_equal(fs.exists(fs.join(dst_dir, "subdir", "file2.txt")), true)
|
|
assert_equal(fs.read(fs.join(dst_dir, "file1.txt")), "content1")
|
|
|
|
-- Clean up
|
|
fs.rmdir(src_dir)
|
|
fs.rmdir(dst_dir)
|
|
end)
|
|
|
|
test("Find Files", function()
|
|
-- Create test files
|
|
fs.write(fs.join(test_dir, "find1.txt"), "content")
|
|
fs.write(fs.join(test_dir, "find2.txt"), "content")
|
|
fs.write(fs.join(test_dir, "other.log"), "content")
|
|
fs.mkdir(fs.join(test_dir, "subdir"))
|
|
fs.write(fs.join(test_dir, "subdir", "find3.txt"), "content")
|
|
|
|
local txt_files = fs.find(test_dir, "%.txt$", true)
|
|
assert_equal(type(txt_files), "table")
|
|
assert(#txt_files >= 3, "should find txt files recursively")
|
|
|
|
local txt_files_flat = fs.find(test_dir, "%.txt$", false)
|
|
assert(#txt_files_flat < #txt_files, "non-recursive should find fewer files")
|
|
|
|
-- Clean up
|
|
fs.remove(fs.join(test_dir, "find1.txt"))
|
|
fs.remove(fs.join(test_dir, "find2.txt"))
|
|
fs.remove(fs.join(test_dir, "other.log"))
|
|
fs.remove(fs.join(test_dir, "subdir", "find3.txt"))
|
|
fs.rmdir(fs.join(test_dir, "subdir"))
|
|
end)
|
|
|
|
test("Directory Tree", function()
|
|
-- Create test structure
|
|
fs.mkdir(fs.join(test_dir, "tree_test"))
|
|
fs.write(fs.join(test_dir, "tree_test", "file.txt"), "content")
|
|
fs.mkdir(fs.join(test_dir, "tree_test", "subdir"))
|
|
|
|
local tree = fs.tree(test_dir)
|
|
assert_equal(type(tree), "table")
|
|
assert_equal(tree.is_dir, true)
|
|
assert_equal(type(tree.children), "table")
|
|
assert(#tree.children > 0, "should have children")
|
|
|
|
-- Clean up
|
|
fs.remove(fs.join(test_dir, "tree_test", "file.txt"))
|
|
fs.rmdir(fs.join(test_dir, "tree_test", "subdir"))
|
|
fs.rmdir(fs.join(test_dir, "tree_test"))
|
|
end)
|
|
|
|
-- ======================================================================
|
|
-- ERROR HANDLING
|
|
-- ======================================================================
|
|
|
|
test("Error Handling", function()
|
|
-- Reading non-existent file
|
|
local success, err = pcall(fs.read, "nonexistent.txt")
|
|
assert_equal(false, success)
|
|
|
|
-- Writing to invalid path
|
|
local success2, err2 = pcall(fs.write, "/invalid/path/file.txt", "content")
|
|
assert_equal(false, success2)
|
|
|
|
-- Listing non-existent directory
|
|
local success3, err3 = pcall(fs.list, "nonexistent_dir")
|
|
assert_equal(false, success3)
|
|
end)
|
|
|
|
-- ======================================================================
|
|
-- PERFORMANCE TESTS
|
|
-- ======================================================================
|
|
|
|
test("Performance Test", function()
|
|
local large_content = string.rep("performance test data\n", 1000)
|
|
local perf_file = fs.join(test_dir, "performance.txt")
|
|
|
|
local start = os.clock()
|
|
fs.write(perf_file, large_content)
|
|
local write_time = os.clock() - start
|
|
|
|
start = os.clock()
|
|
local read_content = fs.read(perf_file)
|
|
local read_time = os.clock() - start
|
|
|
|
start = os.clock()
|
|
local lines = fs.lines(perf_file)
|
|
local lines_time = os.clock() - start
|
|
|
|
print(string.format(" Write %d bytes: %.3fs", #large_content, write_time))
|
|
print(string.format(" Read %d bytes: %.3fs", #read_content, read_time))
|
|
print(string.format(" Parse %d lines: %.3fs", #lines, lines_time))
|
|
|
|
assert_equal(read_content, large_content)
|
|
assert_equal(#lines, 1000)
|
|
|
|
fs.remove(perf_file)
|
|
end)
|
|
|
|
-- ======================================================================
|
|
-- CLEANUP
|
|
-- ======================================================================
|
|
|
|
cleanup()
|
|
|
|
summary()
|
|
test_exit() |