arudradey's picture
download
raw
12.4 kB
/*
* Copyright 2023 The Emscripten Authors. All rights reserved.
* Emscripten is available under two separate licenses, the MIT license and the
* University of Illinois/NCSA Open Source License. Both these licenses can be
* found in the LICENSE file.
*/
#include <emscripten/emscripten.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <assert.h>
#include <fcntl.h>
EM_JS(void, test_fs_open, (), {
FS.writeFile('testfile', 'a=1\nb=2\n');
var readStream = FS.open('testfile', 'r');
var writeStream = FS.open('testfile', 'w');
var writePlusStream = FS.open('testfile', 'w+');
var appendStream = FS.open('testfile', 'a');
assert(readStream && readStream.fd >= 0);
assert(writeStream && writeStream.fd >= 0);
assert(writePlusStream && writePlusStream.fd >= 0);
assert(appendStream && appendStream.fd >= 0);
var ex;
try {
FS.open('filenothere', 'r');
} catch(err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 44 /* ENOENT */);
var createFileNotHere = FS.open('filenothere', 'w+');
assert(createFileNotHere && createFileNotHere.fd >= 0);
});
EM_JS(void, test_fs_readFile, (), {
// Reading existing files is already covered by other tests here, so
// just test the error conditions.
var ex;
try {
var output = FS.readFile("root/no-exist", {encoding : 'utf8'});
} catch (err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 44 /* ENOENT */);
});
// createPath should succeed when called on existing paths ( https://github.com/emscripten-core/emscripten/issues/23602 )
EM_JS(void, test_fs_createPath, (), {
FS.createPath('/', 'home', true, true);
FS.createPath('/', 'home', true, true); // Creating an already existing path
FS.createPath('/home', 'nested1', true, true);
FS.createPath('/home', 'nested2', true, true);
FS.writeFile('/home/nested1/test.txt', 'a=1\nb=2\n');
FS.writeFile('/home/nested2/test.txt', 'a=2\nb=4\n');
var read1 = FS.readFile('/home/nested1/test.txt', {encoding:'utf8'});
var read2 = FS.readFile('/home/nested2/test.txt', {encoding:'utf8'});
console.log("r1",read1);
console.log("r2",read2);
assert(read1 == 'a=1\nb=2\n');
assert(read2 == 'a=2\nb=4\n');
});
EM_JS(void, test_fs_rename, (), {
FS.mkdir('renamedir');
FS.writeFile('renamedir/renametestfile', "");
FS.rename('renamedir/renametestfile', 'renamedir/newname');
var newnameStream = FS.open('renamedir/newname', 'r');
assert(newnameStream);
var ex;
try {
FS.open('renamedir/renametestfile', 'r');
} catch (err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 44 /* ENOENT */);
try {
FS.rename('renamedir', 'renamedir/newdirname');
} catch (err) {
ex = err;
}
// The old path should not be an ancestor of the new path.
assert(ex.name === "ErrnoError" && ex.errno === 28 /* EINVAL */);
FS.writeFile('toplevelfile', "");
try {
FS.rename('renamedir', 'toplevelfile');
} catch (err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 54 /* ENOTDIR */);
});
EM_JS(void, test_fs_readlink,(), {
FS.writeFile('/readlinktestfile', "");
FS.symlink('/readlinktestfile', '/readlinksymlink');
var symlinkString = FS.readlink('readlinksymlink');
assert(symlinkString === '/readlinktestfile');
var ex;
try {
FS.readlink('nonexistent');
} catch (err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 44 /* ENOENT */);
try {
FS.readlink('readlinktestfile');
} catch (err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 28 /* EINVAL */);
});
EM_JS(void, test_fs_read, (), {
FS.writeFile("readtestfile", 'a=1_b=2_');
// Test read variant, checking that the file offset moves correctly.
var buf = new Uint8Array(8);
var stream = FS.open("readtestfile", "r");
var numRead = FS.read(stream, buf, 0, 4);
assert(numRead == 4);
assert((new TextDecoder().decode(buf.subarray(0, 4))) === "a=1_");
numRead = FS.read(stream, buf, 4, 4);
assert(numRead == 4);
assert((new TextDecoder().decode(buf)) == 'a=1_b=2_');
// Test pread variant.
stream = FS.open("readtestfile", "r");
var extraBuf = new Uint8Array(8);
numRead = FS.read(stream, extraBuf, 0, 4, 0);
assert(numRead == 4);
assert((new TextDecoder().decode(extraBuf.subarray(0, 4))) == 'a=1_');
// Check that pread did not move the file offset (Offset begins at 0).
var doubleBuf = new Uint8Array(8);
var firstNumRead = FS.read(stream, doubleBuf, 0, 4);
var secondNumRead = FS.read(stream, doubleBuf, 4, 4);
assert(firstNumRead == 4 && secondNumRead == 4);
assert((new TextDecoder().decode(doubleBuf)) == 'a=1_b=2_');
// Check that full read works.
stream = FS.open("readtestfile", "r");
var fullBuf = new Uint8Array(8);
numRead = FS.read(stream, fullBuf, 0, 8);
assert(numRead == 8);
assert((new TextDecoder().decode(fullBuf)) == 'a=1_b=2_');
FS.close(stream);
var ex;
try {
FS.read(stream, buf, 4, 4);
} catch (err) {
ex = err;
}
assert(ex.name === 'ErrnoError' && ex.errno == 8 /* EBADF */);
});
void test_fs_rmdir() {
EM_ASM(
// Create multiple directories
FS.mkdir('/dir1');
FS.mkdir('/dir2');
);
struct stat s;
stat("/dir1", &s);
assert(S_ISDIR(s.st_mode));
stat("/dir2", &s);
assert(S_ISDIR(s.st_mode));
EM_ASM(
// Remove the multiple directories
FS.rmdir('/dir1');
FS.rmdir('/dir2');
);
int err = open("/dir1", O_RDWR);
assert(err);
err = open("/dir2", O_RDWR);
assert(err);
EM_ASM(
// Create a directory with a file inside it
FS.mkdir('/test_dir');
FS.writeFile('/test_dir/file.txt', 'Hello World!');
// Attempt to remove the directory (should fail)
var ex;
try {
FS.rmdir('/test_dir');
} catch (err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 55 /* ENOTEMPTY */);
// Remove the file and then the directory
FS.unlink('/test_dir/file.txt');
FS.rmdir('/test_dir');
// Attempt to remove a non-existent directory (should fail)
try {
FS.rmdir('/non_existent_dir');
} catch (err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 44 /* ENOENT */);
);
}
EM_JS(void, test_fs_close, (), {
FS.writeFile("closetestfile", 'a=1\nb=2\n');
FS.mkdir("/testdir");
var file = FS.open("closetestfile", "r");
var error = FS.close(file);
assert(!error);
file = FS.open("/testdir", "r");
error = FS.close(file);
assert(!error);
var ex;
try {
FS.close(file);
} catch(err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 8 /* EBADF */);
});
void test_fs_mknod() {
EM_ASM(
FS.mknod("mknodtest", 0o100000 | 0o777 /* S_IFREG | S_RWXU | S_RWXG | S_RWXO */);
FS.create("createtest", 0o400 /* S_IRUSR */);
);
struct stat s;
stat("mknodtest", &s);
assert(S_ISREG(s.st_mode));
assert(s.st_mode & 0o777);
stat("createtest", &s);
assert(S_ISREG(s.st_mode));
assert(s.st_mode & 0o400);
remove("mknodtest");
remove("createtest");
}
void test_fs_truncate() {
EM_ASM(
FS.writeFile('truncatetest', 'a=1\nb=2\n');
);
struct stat s;
stat("truncatetest", &s);
assert(s.st_size == 8);
EM_ASM(
FS.truncate('truncatetest', 2);
);
stat("truncatetest", &s);
assert(s.st_size == 2);
EM_ASM(
FS.truncate('truncatetest', 10);
);
stat("truncatetest", &s);
assert(s.st_size == 10);
EM_ASM(
var truncateStream = FS.open('truncatetest', 'w');
FS.ftruncate(truncateStream.fd, 4);
);
stat("truncatetest", &s);
assert(s.st_size == 4);
EM_ASM(
var ex;
try {
FS.truncate('truncatetest', -10);
} catch(err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 28 /* EINVAL */);
try {
var truncateStream = FS.open('truncatetest', 'w');
FS.ftruncate(truncateStream.fd, -10);
} catch(err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 28 /* EINVAL */);
try {
FS.truncate('nonexistent', 10);
} catch(err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 44 /* ENOENT */);
var ex;
try {
FS.ftruncate(99, 10);
} catch(err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 8 /* EBADF */);
);
remove("truncatetest");
}
void test_fs_mmap() {
EM_ASM(
FS.writeFile('mmaptest', 'a=1_b=2_');
var stream = FS.open('mmaptest', 'r+');
assert(stream);
var mapped = FS.mmap(stream, 12, 0, 1 | 2 /* PROT_READ | PROT_WRITE */, 1 /* MAP_SHARED */);
var ret = new Uint8Array(HEAPU8.subarray(mapped.ptr, mapped.ptr + 12));
var fileContents = "";
for (var i = 0; i < 12; i++) {
fileContents += String.fromCharCode(ret[i]);
}
assert(fileContents === 'a=1_b=2_\0\0\0\0');
ret[8] = ':'.charCodeAt(0);
ret[9] = 'x'.charCodeAt(0);
ret[10] = 'y'.charCodeAt(0);
ret[11] = 'z'.charCodeAt(0);
HEAPU8.set(ret, mapped.ptr);
// The WasmFS msync syscall requires a pointer to the mapped memory, while the legacy JS API takes in any Uint8Array
// buffer to write to a file.
#if WASMFS
FS.msync(stream, mapped.ptr, 0, 12, 1 /* MAP_SHARED */);
var ex;
try {
FS.munmap(mapped.ptr, 4);
} catch (err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 28 /* EINVAL */);
FS.munmap(mapped.ptr, 12);
// WasmFS correctly handles unmapping, while the legacy JS API does not.
try {
FS.msync(stream, mapped.ptr, 0, 12, 1 /* MAP_SHARED */);
} catch (err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 28 /* EINVAL */);
#else
FS.msync(stream, new Uint8Array(ret), 0, 12, 1 /* MAP_SHARED */);
FS.munmap(stream);
#endif
);
FILE *fptr = fopen("mmaptest", "r");
char res[13];
fgets(res, 13, fptr);
assert(strcmp(res, "a=1_b=2_:xyz") == 0);
remove("mmaptest");
}
void test_fs_mkdirTree() {
EM_ASM(
FS.mkdirTree("/test1/test2/test3"); // Abs path
FS.mkdirTree("/readable", 0o400 /* S_IRUSR */);
);
struct stat s;
assert(stat("/test1", &s) == 0);
assert(S_ISDIR(s.st_mode));
assert(stat("/test1/test2", &s) == 0);
assert(S_ISDIR(s.st_mode));
assert(stat("/test1/test2/test3", &s) == 0);
assert(S_ISDIR(s.st_mode));
assert(stat("/readable", &s) == 0);
assert(s.st_mode & 0o400 /* S_IRUSR */);
EM_ASM(
var ex;
try {
FS.mkdirTree("/readable/forbidden");
} catch (err) {
ex = err;
}
assert(ex.name === "ErrnoError" && ex.errno === 2 /* EACCES */);
);
chdir("/test1");
EM_ASM(
FS.mkdirTree("foo/bar"); // Relative path
);
chdir("/");
assert(stat("/test1/foo", &s) == 0);
assert(S_ISDIR(s.st_mode));
assert(stat("/test1/foo/bar", &s) == 0);
assert(S_ISDIR(s.st_mode));
remove("/test1/test2/test3");
remove("/test1/test2");
remove("/test1/foo/bar");
remove("/test1/foo");
remove("/test1");
remove("/readable");
}
void test_fs_utime() {
EM_ASM(
FS.writeFile('utimetest', 'a=1\nb=2\n');
);
EM_ASM(
FS.utime('utimetest', 10500, 8000);
);
struct stat utimeStats;
stat("utimetest", &utimeStats);
assert(utimeStats.st_atime == 10);
assert(utimeStats.st_atim.tv_sec == 10);
assert(utimeStats.st_atim.tv_nsec == 500000000);
assert(utimeStats.st_mtime == 8);
assert(utimeStats.st_mtim.tv_sec == 8);
remove("utimetest");
}
#if !defined(NODERAWFS)
// NODERAWFS don't support absolute paths since we cannot write the
// actual root directory.
// In addition, abs paths testing is not really running correctly
// when we build run with NODEFS because the NODEFS filesystem is mounted
// at /nodefs in that case.
// TODO(sbc): Refactor these tests such that they test both relative
// and absolute paths, and don't depend on write access to the root
// directory.
#define ABS_PATH_OK
#endif
int main() {
#ifdef ABS_PATH_OK
test_fs_createPath();
test_fs_mkdirTree();
test_fs_close();
test_fs_readlink();
test_fs_rmdir();
#endif
test_fs_open();
test_fs_readFile();
test_fs_rename();
test_fs_read();
test_fs_mknod();
test_fs_truncate();
#if WASMFS
// TODO: Fix legacy API FS.mmap bug involving emscripten_builtin_memalign
test_fs_mmap();
#endif
test_fs_utime();
puts("success");
}

Xet Storage Details

Size:
12.4 kB
·
Xet hash:
2b4ffdf0b29c1b3e1b70ec54489b41fa20e58a0a4b9b3af92358ea156d2ecaab

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.