///|
#cfg(target="native")
async test "open file for reading" {
let test_file = "_build/test_open_read.txt"
@fs.write_file(test_file, b"Hello, MoonBit!", create_mode=CreateOrTruncate)
let file = @fs.open(test_file, mode=ReadOnly)
defer file.close()
let content = file.read_all().text()
@fs.remove(test_file)
inspect(content, content="Hello, MoonBit!")
}
///|
#cfg(target="native")
async test "open file for writing" {
let test_file = "_build/test_open_write.txt"
let file = @fs.open(test_file, mode=WriteOnly, create_mode=CreateOrTruncate)
defer file.close()
file.write(b"Hello, World!")
@fs.remove(test_file)
}
///|
#cfg(target="native")
async test "open with append mode" {
let test_file = "_build/test_append.txt"
// Create initial file
@fs.write_file(test_file, b"First line\n", create_mode=CreateOrTruncate)
// Append to existing file
let file = @fs.open(test_file, mode=WriteOnly, append=true)
file.write(b"Second line\n")
file.close()
let content = @fs.read_file(test_file).text()
@fs.remove(test_file)
inspect(content, content="First line\nSecond line\n")
}///|
#cfg(target="native")
async test "create new file" {
let test_file = "_build/test_create.txt"
let file = @fs.create(test_file)
file.write(b"New file content")
file.close()
let exists = @fs.exists(test_file)
@fs.remove(test_file)
inspect(exists, content="true")
}///|
#cfg(target="native")
async test "read_file - read entire file" {
let test_file = "_build/test_read_file.txt"
@fs.write_file(test_file, b"Hello, MoonBit!", create_mode=CreateOrTruncate)
let content = @fs.read_file(test_file)
@fs.remove(test_file)
inspect(content.text(), content="Hello, MoonBit!")
}
///|
#cfg(target="native")
async test "read in chunks using File" {
let test_file = "_build/test_chunk_read.txt"
@fs.write_file(test_file, b"0123456789", create_mode=CreateOrTruncate)
let file = @fs.open(test_file, mode=ReadOnly)
defer file.close()
let buf = FixedArray::make(5, b'0')
let n = file.read(buf)
@fs.remove(test_file)
inspect(n, content="5")
inspect(@utf8.decode(buf.unsafe_reinterpret_as_bytes()), content="01234")
}
///|
#cfg(target="native")
async test "read_all from file" {
let test_file = "_build/test_read_all.txt"
@fs.write_file(test_file, b"Complete content", create_mode=CreateOrTruncate)
let file = @fs.open(test_file, mode=ReadOnly)
let data = file.read_all()
file.close()
@fs.remove(test_file)
inspect(data.text(), content="Complete content")
}
///|
#cfg(target="native")
async test "read_exactly specific bytes" {
let test_file = "_build/test_read_exact.txt"
@fs.write_file(test_file, b"1234567890", create_mode=CreateOrTruncate)
let file = @fs.open(test_file, mode=ReadOnly)
let bytes = file.read_exactly(5)
file.close()
@fs.remove(test_file)
inspect(@utf8.decode(bytes), content="12345")
}///|
#cfg(target="native")
async test "write_file - write entire file" {
let test_file = "_build/test_write.txt"
@fs.write_file(test_file, b"File content", create_mode=CreateOrTruncate)
let content = @fs.read_file(test_file).text()
@fs.remove(test_file)
inspect(content, content="File content")
}
///|
#cfg(target="native")
async test "write with sync modes" {
let test_file = "_build/test_sync.txt"
// Write with data sync
@fs.write_file(
test_file,
b"Synced data",
sync=Data,
create_mode=CreateOrTruncate,
)
let content = @fs.read_file(test_file).text()
@fs.remove(test_file)
inspect(content, content="Synced data")
}
///|
#cfg(target="native")
async test "write using File methods" {
let test_file = "_build/test_file_write.txt"
let file = @fs.create(test_file)
file.write(b"Line 1\n")
file.write(b"Line 2\n")
file.close()
let content = @fs.read_file(test_file).text()
@fs.remove(test_file)
inspect(content, content="Line 1\nLine 2\n")
}
///|
#cfg(target="native")
async test "write_once for single write operation" {
let test_file = "_build/test_write_once.txt"
let file = @fs.create(test_file)
let data : Bytes = b"Single write"
let written = file.write_once(data, offset=0, len=data.length())
file.close()
@fs.remove(test_file)
inspect(written, content="12")
}///|
#cfg(target="native")
async test "read at specific position" {
let test_file = "_build/read_at_test.txt"
@fs.write_file(test_file, b"0123456789", create_mode=CreateOrTruncate)
{
let file = @fs.open(test_file, mode=ReadOnly)
defer file.close()
// read 3 bytes at position 5
json_inspect(file.read_exactly_at(3, position=5), content="567")
// use `read_at` to handle EOF robustly
let buf = FixedArray::make(10, b'\x00')
let n = file.read_at(buf, position=5)
inspect(n, content="5")
json_inspect(buf.unsafe_reinterpret_as_bytes()[:n], content="56789")
}
@fs.remove(test_file)
}
///|
#cfg(target="native")
async test "write at specific position" {
let test_file = "_build/write_at_test.txt"
{
let file = @fs.open(test_file, mode=WriteOnly, create_mode=CreateOrTruncate)
defer file.close()
file.write("abcdef")
file.write_at(b"CD", position=2)
}
// read 3 bytes at position 5
inspect(@fs.read_file(test_file).text(), content="abCDef")
@fs.remove(test_file)
}
///|
#cfg(target="native")
async test "size - get file size" {
let test_file = "_build/test_size.txt"
@fs.write_file(test_file, b"Hello", create_mode=CreateOrTruncate)
let file = @fs.open(test_file, mode=ReadOnly)
let size = file.size()
file.close()
@fs.remove(test_file)
inspect(size, content="5")
}///|
#cfg(target="native")
async test "mkdir - create directory" {
let dir_path = "_build/test_mkdir"
@fs.mkdir(dir_path, permission=0o755)
let exists = @fs.exists(dir_path)
@fs.rmdir(dir_path)
inspect(exists, content="true")
}
///|
#cfg(target="native")
async test "mkdir - create with custom permissions" {
let dir_path = "_build/test_mkdir_perm"
@fs.mkdir(dir_path, permission=0o700)
let kind = @fs.kind(dir_path)
@fs.rmdir(dir_path)
debug_inspect(kind, content="Directory")
}///|
#cfg(target="native")
async test "readdir - read directory entries" {
let dir_path = "_build/test_readdir"
@fs.mkdir(dir_path, permission=0o755)
@fs.write_file("\{dir_path}/test1.txt", b"", create_mode=CreateOrTruncate)
@fs.write_file("\{dir_path}/test2.txt", b"", create_mode=CreateOrTruncate)
let entries = @fs.readdir(
dir_path,
include_hidden=false,
include_special=false,
)
// avoid platform inconsistent ordering
entries.sort()
@fs.rmdir(dir_path, recursive=true)
json_inspect(entries, content=["test1.txt", "test2.txt"])
}
///|
#cfg(target="native")
async test "readdir with sorting" {
let dir_path = "_build/test_readdir_sort"
@fs.mkdir(dir_path, permission=0o755)
@fs.write_file("\{dir_path}/c.txt", b"", create_mode=CreateOrTruncate)
@fs.write_file("\{dir_path}/a.txt", b"", create_mode=CreateOrTruncate)
@fs.write_file("\{dir_path}/b.txt", b"", create_mode=CreateOrTruncate)
let entries = @fs.readdir(dir_path, sort=true)
@fs.rmdir(dir_path, recursive=true)
json_inspect(entries, content=["a.txt", "b.txt", "c.txt"])
}
///|
#cfg(target="native")
async test "opendir and Directory::read_all" {
let dir_path = "_build/test_opendir"
@fs.mkdir(dir_path, permission=0o755)
@fs.write_file("\{dir_path}/file1.txt", b"test", create_mode=CreateOrTruncate)
@fs.write_file("\{dir_path}/file2.txt", b"test", create_mode=CreateOrTruncate)
let dir = @fs.opendir(dir_path)
let entries = dir
.read_all(include_hidden=false, include_special=false)
..sort()
dir.close()
@fs.rmdir(dir_path, recursive=true)
json_inspect(entries, content=["file1.txt", "file2.txt"])
}///|
#cfg(target="native")
async test "walk directory tree" {
let base = "_build/test_walk"
@fs.mkdir(base)
@fs.mkdir("\{base}/sub1")
@fs.mkdir("\{base}/sub2")
@fs.write_file("\{base}/file.txt", b"", create_mode=CreateOrTruncate)
@fs.write_file("\{base}/sub1/file1.txt", b"", create_mode=CreateOrTruncate)
let visited : Ref[Int] = Ref(0)
@fs.walk(base, fn(_path, _files) { visited.val = visited.val + 1 })
@fs.remove("\{base}/file.txt")
@fs.remove("\{base}/sub1/file1.txt")
@fs.rmdir("\{base}/sub1")
@fs.rmdir("\{base}/sub2")
@fs.rmdir(base)
inspect(visited.val >= 3, content="true")
}
///|
#cfg(target="native")
async test "walk with max_concurrency" {
let base = "_build/test_walk_concurrency"
@fs.mkdir(base, permission=0o755)
@fs.mkdir("\{base}/dir1", permission=0o755)
@fs.mkdir("\{base}/dir2", permission=0o755)
let count : Ref[Int] = Ref(0)
@fs.walk(
base,
fn(_path, _files) { count.val = count.val + 1 },
max_concurrency=1,
)
@fs.rmdir("\{base}/dir1")
@fs.rmdir("\{base}/dir2")
@fs.rmdir(base)
inspect(count.val >= 3, content="true")
}///|
#cfg(target="native")
async test "rmdir - remove empty directory" {
let dir_path = "_build/test_rmdir"
@fs.mkdir(dir_path)
@fs.rmdir(dir_path)
let exists = @fs.exists(dir_path)
inspect(exists, content="false")
}
///|
#cfg(target="native")
async test "rmdir recursive - remove directory tree" {
let base = "_build/test_rmdir_recursive"
@fs.mkdir(base)
@fs.mkdir("\{base}/subdir")
@fs.write_file("\{base}/file.txt", b"test", create_mode=CreateOrTruncate)
@fs.write_file(
"\{base}/subdir/nested.txt",
b"test",
create_mode=CreateOrTruncate,
)
@fs.rmdir(base, recursive=true)
let exists = @fs.exists(base)
inspect(exists, content="false")
}///|
#cfg(target="native")
async test "kind - regular file" {
let test_file = "_build/test_kind_file.txt"
@fs.write_file(test_file, b"test", create_mode=CreateOrTruncate)
let kind = @fs.kind(test_file)
@fs.remove(test_file)
debug_inspect(kind, content="Regular")
}
///|
#cfg(target="native")
async test "kind - directory" {
let dir_path = "_build/test_kind_dir"
@fs.mkdir(dir_path, permission=0o755)
let kind = @fs.kind(dir_path)
@fs.rmdir(dir_path)
debug_inspect(kind, content="Directory")
}
///|
#cfg(target="native")
async test "File::kind method" {
let test_file = "_build/test_file_kind.txt"
@fs.write_file(test_file, b"test", create_mode=CreateOrTruncate)
let file = @fs.open(test_file, mode=ReadOnly)
let kind = file.kind()
file.close()
@fs.remove(test_file)
debug_inspect(kind, content="Regular")
}///|
#cfg(all(target="native", not(platform="windows")))
async test "atime - access time" {
let test_file = "_build/test_atime.txt"
@fs.write_file(test_file, b"test", create_mode=CreateOrTruncate)
let (seconds, nanoseconds) = @fs.atime(test_file)
@fs.remove(test_file)
inspect(seconds > 0, content="true")
inspect(nanoseconds >= 0, content="true")
}
///|
#cfg(target="native")
async test "mtime - modification time" {
let test_file = "_build/test_mtime.txt"
@fs.write_file(test_file, b"test", create_mode=CreateOrTruncate)
let (seconds, nanoseconds) = @fs.mtime(test_file)
@fs.remove(test_file)
inspect(seconds > 0, content="true")
inspect(nanoseconds >= 0, content="true")
}
///|
#cfg(target="native")
async test "ctime - status change time" {
let test_file = "_build/test_ctime.txt"
@fs.write_file(test_file, b"test", create_mode=CreateOrTruncate)
let (seconds, nanoseconds) = @fs.ctime(test_file)
@fs.remove(test_file)
inspect(seconds > 0, content="true")
inspect(nanoseconds >= 0, content="true")
}
///|
#cfg(target="native")
async test "File timestamp methods" {
let test_file = "_build/test_file_times.txt"
@fs.write_file(test_file, b"test", create_mode=CreateOrTruncate)
let file = @fs.open(test_file, mode=ReadOnly)
let (atime_s, _) = file.atime()
let (mtime_s, _) = file.mtime()
let (ctime_s, _) = file.ctime()
file.close()
@fs.remove(test_file)
inspect(atime_s > 0, content="true")
inspect(mtime_s > 0, content="true")
inspect(ctime_s > 0, content="true")
}///|
#cfg(target="native")
async test "exists - check file existence" {
let test_file = "_build/test_exists.txt"
@fs.write_file(test_file, b"test", create_mode=CreateOrTruncate)
let exists = @fs.exists(test_file)
@fs.remove(test_file)
inspect(exists, content="true")
}
///|
#cfg(target="native")
async test "exists - non-existent file" {
let exists = @fs.exists("nonexistent_file_xyz.txt")
inspect(exists, content="false")
}
///|
#cfg(target="native")
async test "can_read - check read permission" {
let test_file = "_build/test_can_read.txt"
@fs.write_file(test_file, b"test", create_mode=CreateOrTruncate)
let can_read = @fs.can_read(test_file)
@fs.remove(test_file)
inspect(can_read, content="true")
}
///|
#cfg(target="native")
async test "can_write - check write permission" {
let test_file = "_build/test_can_write.txt"
@fs.write_file(test_file, b"test", create_mode=CreateOrTruncate)
let can_write = @fs.can_write(test_file)
@fs.remove(test_file)
inspect(can_write, content="true")
}
///|
#cfg(target="native")
async test "can_execute - check execute permission" {
let test_file = "_build/test_can_execute.txt"
@fs.write_file(test_file, b"test", create_mode=CreateNew, permission=0o755)
let can_execute = @fs.can_execute(test_file)
@fs.remove(test_file)
inspect(can_execute, content="true")
}///|
#cfg(target="native")
async test "realpath - resolve absolute path" {
let test_dir = "_build/test_realpath"
@fs.mkdir(test_dir)
let real_path = @fs.realpath(test_dir)
@fs.rmdir(test_dir)
@env.current_dir() is Some(cwd)
assert_true(real_path.has_prefix(cwd))
inspect(
// replace `\\` with `/` for Windows
real_path[cwd.length():].replace_all(old="\\", new="/"),
content="/_build/test_realpath",
)
}///|
#cfg(target="native")
async test "remove - delete file" {
let test_file = "_build/test_remove.txt"
@fs.write_file(test_file, b"test", create_mode=CreateOrTruncate)
@fs.remove(test_file)
let exists = @fs.exists(test_file)
inspect(exists, content="false")
}///|
#cfg(target="native")
async fn watch_project_sources() -> Unit {
let watcher = @fs.Watcher("src", ignored_paths=path => {
path.has_prefix("_build/")
})
defer watcher.close()
while watcher.wait() is events {
debug(events)
}
}///|
#cfg(target="native")
async test "error handling example" {
@test_util.assert_raise_async(() => @fs.read_file("nonexistent.txt"))
}pub(all) enum CreateMode {
OpenExisting
TruncateExisting
OpenOrCreate
CreateOrTruncate
CreateNew
}type Directoryasync fn Directory::next(dir : Directory, include_hidden? : Bool, include_special? : Bool) -> DirectoryEntry?type Filepub(all) enum Lock {
Shared
Exclusive
}pub(all) enum SyncMode {
NoSync
Data
Full
}type Watcherasync fn atime(path : StringView, follow_symlink? : Bool) -> (Int64, Int)async fn chmod(path : StringView, mode : Int) -> Unitasync fn ctime(path : StringView, follow_symlink? : Bool) -> (Int64, Int)async fn mkdir(path : StringView, permission? : Int, recursive? : Bool) -> Unitasync fn mtime(path : StringView, follow_symlink? : Bool) -> (Int64, Int)async fn open(filename : StringView, mode~ : Mode, sync? : SyncMode, append? : Bool, create_mode? : CreateMode, permission? : Int, create? : Int, truncate? : Bool) -> Fileasync fn readdir(path : StringView, include_hidden? : Bool, include_special? : Bool, sort? : Bool) -> Array[String]async fn realpath(path : StringView) -> Stringasync fn remove(path : StringView) -> Unitasync fn rename(old_path : StringView, new_path : StringView, replace? : Bool) -> Unitasync fn rmdir(path : StringView, recursive? : Bool) -> Unitasync fn symlink(target~ : StringView, path : StringView, force_symlink? : Bool) -> Unitasync fn tmpdir(prefix~ : StringView) -> Stringasync fn walk(path : StringView, f : async (String, Array[String]) -> Unit, exclude? : (String) -> Bool, max_concurrency? : Int, allow_failure? : Bool) -> Unitasync fn write_file(path : StringView, content : &Data, create_mode? : CreateMode, permission? : Int, sync? : SyncMode, append? : Bool, create? : Int, truncate? : Bool) -> UnitAsynchronous programming library for MoonBit