fix zip archives paths and add zip files test

- create_zip from a subdirectory wrote entry names with a leading
  '/', so the mounted archive looked empty
- memory_ostream did not support tellp(), so create_zip into a memory
  device wrote invalid offsets (-1) to the archive
- ZipFileDevice added missing parent directories for the first entry
  only
- ZipFileDevice::exists returned false for the archive root
- MemoryDevice::mkdirs recreated existing directories, dropping their
  content (mkdir("a/b") cleared "a"); overwriting a file duplicated its
  name in the directory listing

Added dev/tests/zip_files.lua: builds archives with file.create_zip
from memory device root, subdirectories and the real filesystem,
mounts them and checks reading, listing and read-only access.
memory_filesystem.lua got checks for the MemoryDevice issues.

Closes #544
This commit is contained in:
ostermine 2026-10-10 15:54:28 +03:00
parent 1dd1af1a72
commit fa5466f675
5 changed files with 170 additions and 3 deletions

View file

@ -41,6 +41,17 @@ assert(entries[1] == "memtest:dir/subdir/a.txt")
assert(entries[2] == "memtest:dir/subdir/b.txt")
assert(entries[3] == "memtest:dir/subdir/other")
debug.log("create sibling directory")
file.mkdir("memtest:dir/subdir/sibling")
assert(file.isfile("memtest:dir/subdir/a.txt"))
assert(file.isdir("memtest:dir/subdir/other"))
asserts.equals(4, #file.list("memtest:dir/subdir"))
debug.log("overwrite file")
file.write("memtest:dir/subdir/a.txt", "overwritten")
asserts.equals("overwritten", file.read("memtest:dir/subdir/a.txt"))
asserts.equals(4, #file.list("memtest:dir/subdir"))
debug.log("remove tree")
file.remove_tree("memtest:dir")
assert(not file.isdir("memtest:dir"))

118
dev/tests/zip_files.lua Normal file
View file

@ -0,0 +1,118 @@
local function sorted_list(path)
local entries = file.list(path)
table.sort(entries)
return entries
end
local function check_list(expected, path)
local entries = sorted_list(path)
asserts.equals(#expected, #entries)
for i, name in ipairs(expected) do
asserts.equals(name, entries[i])
end
end
local function join(root, name)
if root:sub(-1) == ":" then
return root..name
end
return root.."/"..name
end
local bytes = {0xDE, 0xAD, 0x00, 0xC0, 0xDE}
local function create_tree(root)
file.mkdirs(join(root, "sub/deep"))
file.mkdir(join(root, "empty"))
file.write(join(root, "root.txt"), "root file")
file.write(join(root, "sub/a.txt"), "example, пример")
file.write_bytes(join(root, "sub/deep/binary"), bytes)
end
local function check_tree(m)
debug.log("check directories")
assert(file.exists(m..":"))
assert(file.isdir(m..":"))
assert(file.isdir(m..":sub"))
assert(file.isdir(m..":sub/deep"))
assert(file.isdir(m..":empty"))
assert(not file.isfile(m..":sub"))
debug.log("check files")
assert(file.isfile(m..":root.txt"))
assert(file.isfile(m..":sub/a.txt"))
assert(not file.isdir(m..":sub/a.txt"))
assert(not file.exists(m..":missing.txt"))
assert(not file.exists(m..":sub/missing.txt"))
debug.log("read files")
asserts.equals("root file", file.read(m..":root.txt"))
asserts.equals("example, пример", file.read(m..":sub/a.txt"))
asserts.equals(#"root file", file.length(m..":root.txt"))
local rbytes = file.read_bytes(m..":sub/deep/binary")
asserts.equals(#bytes, #rbytes)
for i, b in ipairs(bytes) do
asserts.equals(b, rbytes[i])
end
debug.log("read file with io_stream")
local stream = file.open(m..":root.txt", "r")
asserts.equals("root file", stream:read_line())
stream:close()
debug.log("list directories")
check_list({m..":empty", m..":root.txt", m..":sub"}, m..":")
check_list({m..":sub/a.txt", m..":sub/deep"}, m..":sub")
check_list({m..":sub/deep/binary"}, m..":sub/deep")
check_list({}, m..":empty")
debug.log("check read-only")
assert(not file.is_writeable(m..":"))
assert(not pcall(file.write, m..":new.txt", "text"))
local ok, created = pcall(file.mkdir, m..":newdir")
assert(not (ok and created))
assert(not file.exists(m..":newdir"))
end
local function check_zip(src, zipfile)
debug.log("create zip "..zipfile.." from "..src)
file.create_zip(src, zipfile)
assert(file.isfile(zipfile))
debug.log("mount "..zipfile)
local m = file.mount(zipfile)
check_tree(m)
debug.log("unmount "..m)
file.unmount(m)
assert(not pcall(file.read, m..":root.txt"))
assert(not pcall(file.unmount, m))
end
local mem = file.create_memory_device()
local out = file.create_memory_device()
debug.log("zip memory device root")
create_tree(mem..":")
check_zip(mem..":", out..":root.zip")
debug.log("zip memory device subdirectory")
create_tree(mem..":pack")
check_zip(mem..":pack", out..":pack.zip")
debug.log("zip nested subdirectory")
create_tree(mem..":a/b/pack")
check_zip(mem..":a/b/pack", out..":nested.zip")
debug.log("zip real filesystem directory")
if file.exists("config:ziptest") then
file.remove_tree("config:ziptest")
end
create_tree("config:ziptest/pack")
check_zip("config:ziptest/pack", "config:ziptest/pack.zip")
file.remove_tree("config:ziptest")
debug.log("mount non-zip file")
file.write(out..":text.txt", "not an archive")
assert(not pcall(file.mount, out..":text.txt"))