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()