mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 06:08:41 +01:00
Reading files!
- File.close() - File.open() - File.read() - file.readBytes() And a few other little methods. Still lots more work to do, but it's a start.
This commit is contained in:
@ -7,7 +7,14 @@
|
||||
#include "scheduler.wren.inc"
|
||||
#include "timer.wren.inc"
|
||||
|
||||
extern void fileStartSize(WrenVM* vm);
|
||||
extern void fileAllocate(WrenVM* vm);
|
||||
extern void fileFinalize(WrenVM* vm);
|
||||
extern void fileOpen(WrenVM* vm);
|
||||
extern void fileSizePath(WrenVM* vm);
|
||||
extern void fileClose(WrenVM* vm);
|
||||
extern void fileDescriptor(WrenVM* vm);
|
||||
extern void fileReadBytes(WrenVM* vm);
|
||||
extern void fileSize(WrenVM* vm);
|
||||
extern void schedulerCaptureMethods(WrenVM* vm);
|
||||
extern void timerStartTimer(WrenVM* vm);
|
||||
|
||||
@ -19,7 +26,7 @@ extern void timerStartTimer(WrenVM* vm);
|
||||
// If you add a new method to the longest class below, make sure to bump this.
|
||||
// Note that it also includes an extra slot for the sentinel value indicating
|
||||
// the end of the list.
|
||||
#define MAX_METHODS_PER_CLASS 2
|
||||
#define MAX_METHODS_PER_CLASS 9
|
||||
|
||||
// The maximum number of foreign classes a single built-in module defines.
|
||||
//
|
||||
@ -75,7 +82,14 @@ static ModuleRegistry modules[] =
|
||||
{
|
||||
MODULE(io)
|
||||
CLASS(File)
|
||||
STATIC_METHOD("startSize_(_,_)", fileStartSize)
|
||||
STATIC_METHOD("<allocate>", fileAllocate)
|
||||
STATIC_METHOD("<finalize>", fileFinalize)
|
||||
STATIC_METHOD("open_(_,_)", fileOpen)
|
||||
STATIC_METHOD("sizePath_(_,_)", fileSizePath)
|
||||
METHOD("close_(_)", fileClose)
|
||||
METHOD("descriptor", fileDescriptor)
|
||||
METHOD("readBytes_(_,_)", fileReadBytes)
|
||||
METHOD("size_(_)", fileSize)
|
||||
END_CLASS
|
||||
END_MODULE
|
||||
|
||||
|
||||
10
src/cli/vm.c
10
src/cli/vm.c
@ -18,6 +18,9 @@ static uv_loop_t* loop;
|
||||
|
||||
static char const* rootDirectory = NULL;
|
||||
|
||||
// The exit code to use unless some other error overrides it.
|
||||
int defaultExitCode = 0;
|
||||
|
||||
// Reads the contents of the file at [path] and returns it as a heap allocated
|
||||
// string.
|
||||
//
|
||||
@ -220,6 +223,8 @@ void runFile(const char* path)
|
||||
// Exit with an error code if the script failed.
|
||||
if (result == WREN_RESULT_COMPILE_ERROR) exit(65); // EX_DATAERR.
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR) exit(70); // EX_SOFTWARE.
|
||||
|
||||
if (defaultExitCode != 0) exit(defaultExitCode);
|
||||
}
|
||||
|
||||
int runRepl()
|
||||
@ -262,6 +267,11 @@ uv_loop_t* getLoop()
|
||||
return loop;
|
||||
}
|
||||
|
||||
void setExitCode(int exitCode)
|
||||
{
|
||||
defaultExitCode = exitCode;
|
||||
}
|
||||
|
||||
void setTestCallbacks(WrenBindForeignMethodFn bindMethod,
|
||||
WrenBindForeignClassFn bindClass,
|
||||
WrenForeignMethodFn afterLoad)
|
||||
|
||||
@ -18,6 +18,9 @@ WrenVM* getVM();
|
||||
// Gets the event loop the VM is using.
|
||||
uv_loop_t* getLoop();
|
||||
|
||||
// Set the exit code the CLI should exit with when done.
|
||||
void setExitCode(int exitCode);
|
||||
|
||||
// Adds additional callbacks to use when binding foreign members from Wren.
|
||||
//
|
||||
// Used by the API test executable to let it wire up its own foreign functions.
|
||||
|
||||
171
src/module/io.c
171
src/module/io.c
@ -9,32 +9,171 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// Called by libuv when the stat call for size completes.
|
||||
static void sizeCallback(uv_fs_t* request)
|
||||
void fileAllocate(WrenVM* vm)
|
||||
{
|
||||
// Store the file descriptor in the foreign data, so that we can get to it
|
||||
// in the finalizer.
|
||||
int* fd = (int*)wrenAllocateForeign(vm, sizeof(int));
|
||||
*fd = (int)wrenGetArgumentDouble(vm, 1);
|
||||
}
|
||||
|
||||
void fileFinalize(WrenVM* vm)
|
||||
{
|
||||
int fd = *(int*)wrenGetArgumentForeign(vm, 0);
|
||||
|
||||
// Already closed.
|
||||
if (fd == -1) return;
|
||||
|
||||
uv_fs_t request;
|
||||
uv_fs_close(getLoop(), &request, fd, NULL);
|
||||
uv_fs_req_cleanup(&request);
|
||||
}
|
||||
|
||||
// If [request] failed with an error, sends the runtime error to the VM and
|
||||
// frees the request.
|
||||
//
|
||||
// Returns true if an error was reported.
|
||||
static bool handleRequestError(uv_fs_t* request)
|
||||
{
|
||||
if (request->result >= 0) return false;
|
||||
|
||||
WrenValue* fiber = (WrenValue*)request->data;
|
||||
schedulerResumeError(fiber, uv_strerror((int)request->result));
|
||||
uv_fs_req_cleanup(request);
|
||||
free(request);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allocates a new request that resumes [fiber] when it completes.
|
||||
uv_fs_t* createRequest(WrenValue* fiber)
|
||||
{
|
||||
uv_fs_t* request = (uv_fs_t*)malloc(sizeof(uv_fs_t));
|
||||
request->data = fiber;
|
||||
return request;
|
||||
}
|
||||
|
||||
// Releases resources used by [request].
|
||||
//
|
||||
// Returns the fiber that should be resumed after [request] completes.
|
||||
WrenValue* freeRequest(uv_fs_t* request)
|
||||
{
|
||||
WrenValue* fiber = (WrenValue*)request->data;
|
||||
|
||||
if (request->result != 0)
|
||||
{
|
||||
schedulerResumeString(fiber, uv_strerror((int)request->result));
|
||||
uv_fs_req_cleanup(request);
|
||||
return;
|
||||
}
|
||||
uv_fs_req_cleanup(request);
|
||||
free(request);
|
||||
|
||||
return fiber;
|
||||
}
|
||||
|
||||
static void openCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
|
||||
double fd = (double)request->result;
|
||||
WrenValue* fiber = freeRequest(request);
|
||||
|
||||
schedulerResumeDouble(fiber, fd);
|
||||
}
|
||||
|
||||
void fileOpen(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetArgumentString(vm, 1);
|
||||
uv_fs_t* request = createRequest(wrenGetArgumentValue(vm, 2));
|
||||
|
||||
// TODO: Allow controlling flags and modes.
|
||||
uv_fs_open(getLoop(), request, path, O_RDONLY, S_IRUSR, openCallback);
|
||||
}
|
||||
|
||||
// Called by libuv when the stat call for size completes.
|
||||
static void sizeCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
|
||||
double size = (double)request->statbuf.st_size;
|
||||
uv_fs_req_cleanup(request);
|
||||
WrenValue* fiber = freeRequest(request);
|
||||
|
||||
schedulerResumeDouble(fiber, size);
|
||||
}
|
||||
|
||||
void fileStartSize(WrenVM* vm)
|
||||
void fileSizePath(WrenVM* vm)
|
||||
{
|
||||
const char* path = wrenGetArgumentString(vm, 1);
|
||||
WrenValue* fiber = wrenGetArgumentValue(vm, 2);
|
||||
|
||||
// Store the fiber to resume when the request completes.
|
||||
uv_fs_t* request = (uv_fs_t*)malloc(sizeof(uv_fs_t));
|
||||
request->data = fiber;
|
||||
|
||||
uv_fs_t* request = createRequest(wrenGetArgumentValue(vm, 2));
|
||||
uv_fs_stat(getLoop(), request, path, sizeCallback);
|
||||
}
|
||||
|
||||
static void closeCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
|
||||
WrenValue* fiber = freeRequest(request);
|
||||
schedulerResume(fiber);
|
||||
}
|
||||
|
||||
void fileClose(WrenVM* vm)
|
||||
{
|
||||
int* foreign = (int*)wrenGetArgumentForeign(vm, 0);
|
||||
int fd = *foreign;
|
||||
|
||||
// If it's already closed, we're done.
|
||||
if (fd == -1)
|
||||
{
|
||||
wrenReturnBool(vm, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark it closed immediately.
|
||||
*foreign = -1;
|
||||
|
||||
uv_fs_t* request = createRequest(wrenGetArgumentValue(vm, 1));
|
||||
uv_fs_close(getLoop(), request, fd, closeCallback);
|
||||
wrenReturnBool(vm, false);
|
||||
}
|
||||
|
||||
void fileDescriptor(WrenVM* vm)
|
||||
{
|
||||
int* foreign = (int*)wrenGetArgumentForeign(vm, 0);
|
||||
int fd = *foreign;
|
||||
wrenReturnDouble(vm, fd);
|
||||
}
|
||||
|
||||
static void readBytesCallback(uv_fs_t* request)
|
||||
{
|
||||
if (handleRequestError(request)) return;
|
||||
|
||||
uv_buf_t buffer = request->bufs[0];
|
||||
WrenValue* fiber = freeRequest(request);
|
||||
|
||||
// TODO: Having to copy the bytes here is a drag. It would be good if Wren's
|
||||
// embedding API supported a way to *give* it bytes that were previously
|
||||
// allocated using Wren's own allocator.
|
||||
schedulerResumeBytes(fiber, buffer.base, buffer.len);
|
||||
|
||||
// TODO: Likewise, freeing this after we resume is lame.
|
||||
free(buffer.base);
|
||||
}
|
||||
|
||||
void fileReadBytes(WrenVM* vm)
|
||||
{
|
||||
uv_fs_t* request = createRequest(wrenGetArgumentValue(vm, 2));
|
||||
|
||||
int fd = *(int*)wrenGetArgumentForeign(vm, 0);
|
||||
// TODO: Assert fd != -1.
|
||||
|
||||
uv_buf_t buffer;
|
||||
buffer.len = (size_t)wrenGetArgumentDouble(vm, 1);
|
||||
buffer.base = (char*)malloc(buffer.len);
|
||||
|
||||
// TODO: Allow passing in offset.
|
||||
uv_fs_read(getLoop(), request, fd, &buffer, 1, 0, readBytesCallback);
|
||||
}
|
||||
|
||||
void fileSize(WrenVM* vm)
|
||||
{
|
||||
uv_fs_t* request = createRequest(wrenGetArgumentValue(vm, 1));
|
||||
|
||||
int fd = *(int*)wrenGetArgumentForeign(vm, 0);
|
||||
// TODO: Assert fd != -1.
|
||||
|
||||
uv_fs_fstat(getLoop(), request, fd, sizeCallback);
|
||||
}
|
||||
|
||||
@ -1,15 +1,69 @@
|
||||
import "scheduler" for Scheduler
|
||||
|
||||
class File {
|
||||
static size(path) {
|
||||
foreign class File {
|
||||
static open(path) {
|
||||
if (!(path is String)) Fiber.abort("Path must be a string.")
|
||||
|
||||
startSize_(path, Fiber.current)
|
||||
var result = Scheduler.runNextScheduled_()
|
||||
if (result is String) Fiber.abort(result)
|
||||
open_(path, Fiber.current)
|
||||
var fd = Scheduler.runNextScheduled_()
|
||||
return new_(fd)
|
||||
}
|
||||
|
||||
static open(path, fn) {
|
||||
var file = open(path)
|
||||
var fiber = Fiber.new { fn.call(file) }
|
||||
|
||||
// Poor man's finally. Can we make this more elegant?
|
||||
var result = fiber.try()
|
||||
file.close()
|
||||
|
||||
// TODO: Want something like rethrow since now the callstack ends here. :(
|
||||
if (fiber.error != null) Fiber.abort(fiber.error)
|
||||
return result
|
||||
}
|
||||
|
||||
foreign static startSize_(path, fiber)
|
||||
static read(path) {
|
||||
return File.open(path) {|file| file.readBytes(file.size) }
|
||||
}
|
||||
|
||||
static size(path) {
|
||||
if (!(path is String)) Fiber.abort("Path must be a string.")
|
||||
|
||||
sizePath_(path, Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
construct new_(fd) {}
|
||||
|
||||
close() {
|
||||
if (close_(Fiber.current)) return
|
||||
Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
isOpen { descriptor != -1 }
|
||||
|
||||
size {
|
||||
if (!isOpen) Fiber.abort("File is not open.")
|
||||
|
||||
size_(Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
readBytes(count) {
|
||||
if (!isOpen) Fiber.abort("File is not open.")
|
||||
if (!(count is Num)) Fiber.abort("Count must be an integer.")
|
||||
if (!count.isInteger) Fiber.abort("Count must be an integer.")
|
||||
if (count < 0) Fiber.abort("Count cannot be negative.")
|
||||
|
||||
readBytes_(count, Fiber.current)
|
||||
return Scheduler.runNextScheduled_()
|
||||
}
|
||||
|
||||
foreign static open_(path, fiber)
|
||||
foreign static sizePath_(path, fiber)
|
||||
|
||||
foreign close_(fiber)
|
||||
foreign descriptor
|
||||
foreign readBytes_(count, fiber)
|
||||
foreign size_(fiber)
|
||||
}
|
||||
|
||||
@ -2,16 +2,70 @@
|
||||
static const char* ioModuleSource =
|
||||
"import \"scheduler\" for Scheduler\n"
|
||||
"\n"
|
||||
"class File {\n"
|
||||
" static size(path) {\n"
|
||||
"foreign class File {\n"
|
||||
" static open(path) {\n"
|
||||
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
|
||||
"\n"
|
||||
" startSize_(path, Fiber.current)\n"
|
||||
" var result = Scheduler.runNextScheduled_()\n"
|
||||
" if (result is String) Fiber.abort(result)\n"
|
||||
" open_(path, Fiber.current)\n"
|
||||
" var fd = Scheduler.runNextScheduled_()\n"
|
||||
" return new_(fd)\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static open(path, fn) {\n"
|
||||
" var file = open(path)\n"
|
||||
" var fiber = Fiber.new { fn.call(file) }\n"
|
||||
"\n"
|
||||
" // Poor man's finally. Can we make this more elegant?\n"
|
||||
" var result = fiber.try()\n"
|
||||
" file.close()\n"
|
||||
"\n"
|
||||
" // TODO: Want something like rethrow since now the callstack ends here. :(\n"
|
||||
" if (fiber.error != null) Fiber.abort(fiber.error)\n"
|
||||
" return result\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static startSize_(path, fiber)\n"
|
||||
" static read(path) {\n"
|
||||
" return File.open(path) {|file| file.readBytes(file.size) }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" static size(path) {\n"
|
||||
" if (!(path is String)) Fiber.abort(\"Path must be a string.\")\n"
|
||||
"\n"
|
||||
" sizePath_(path, Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" construct new_(fd) {}\n"
|
||||
"\n"
|
||||
" close() {\n"
|
||||
" if (close_(Fiber.current)) return\n"
|
||||
" Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" isOpen { descriptor != -1 }\n"
|
||||
"\n"
|
||||
" size {\n"
|
||||
" if (!isOpen) Fiber.abort(\"File is not open.\")\n"
|
||||
"\n"
|
||||
" size_(Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" readBytes(count) {\n"
|
||||
" if (!isOpen) Fiber.abort(\"File is not open.\")\n"
|
||||
" if (!(count is Num)) Fiber.abort(\"Count must be an integer.\")\n"
|
||||
" if (!count.isInteger) Fiber.abort(\"Count must be an integer.\")\n"
|
||||
" if (count < 0) Fiber.abort(\"Count cannot be negative.\")\n"
|
||||
"\n"
|
||||
" readBytes_(count, Fiber.current)\n"
|
||||
" return Scheduler.runNextScheduled_()\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" foreign static open_(path, fiber)\n"
|
||||
" foreign static sizePath_(path, fiber)\n"
|
||||
"\n"
|
||||
" foreign close_(fiber)\n"
|
||||
" foreign descriptor\n"
|
||||
" foreign readBytes_(count, fiber)\n"
|
||||
" foreign size_(fiber)\n"
|
||||
"}\n";
|
||||
|
||||
@ -12,33 +12,63 @@
|
||||
// one.
|
||||
static WrenValue* resume;
|
||||
static WrenValue* resumeWithArg;
|
||||
static WrenValue* resumeError;
|
||||
|
||||
void schedulerCaptureMethods(WrenVM* vm)
|
||||
{
|
||||
resume = wrenGetMethod(vm, "scheduler", "Scheduler", "resume_(_)");
|
||||
resumeWithArg = wrenGetMethod(vm, "scheduler", "Scheduler", "resume_(_,_)");
|
||||
resumeError = wrenGetMethod(vm, "scheduler", "Scheduler", "resumeError_(_,_)");
|
||||
}
|
||||
|
||||
static void callResume(WrenValue* resumeMethod, WrenValue* fiber,
|
||||
const char* argTypes, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, argTypes);
|
||||
WrenInterpretResult result = wrenCallVarArgs(getVM(), resumeMethod, NULL,
|
||||
argTypes, args);
|
||||
va_end(args);
|
||||
|
||||
wrenReleaseValue(getVM(), fiber);
|
||||
|
||||
// If a runtime error occurs in response to an async operation and nothing
|
||||
// catches the error in the fiber, then exit the CLI.
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR)
|
||||
{
|
||||
uv_stop(getLoop());
|
||||
setExitCode(70); // EX_SOFTWARE.
|
||||
}
|
||||
}
|
||||
|
||||
void schedulerResume(WrenValue* fiber)
|
||||
{
|
||||
wrenCall(getVM(), resume, NULL, "v", fiber);
|
||||
wrenReleaseValue(getVM(), fiber);
|
||||
callResume(resume, fiber, "v", fiber);
|
||||
}
|
||||
|
||||
void schedulerResumeBytes(WrenValue* fiber, const char* bytes, size_t length)
|
||||
{
|
||||
callResume(resumeWithArg, fiber, "va", fiber, bytes, length);
|
||||
}
|
||||
|
||||
void schedulerResumeDouble(WrenValue* fiber, double value)
|
||||
{
|
||||
wrenCall(getVM(), resumeWithArg, NULL, "vd", fiber, value);
|
||||
wrenReleaseValue(getVM(), fiber);
|
||||
callResume(resumeWithArg, fiber, "vd", fiber, value);
|
||||
}
|
||||
|
||||
void schedulerResumeString(WrenValue* fiber, const char* text)
|
||||
{
|
||||
wrenCall(getVM(), resumeWithArg, NULL, "vs", fiber, text);
|
||||
wrenReleaseValue(getVM(), fiber);
|
||||
callResume(resumeWithArg, fiber, "vs", fiber, text);
|
||||
}
|
||||
|
||||
void schedulerResumeError(WrenValue* fiber, const char* error)
|
||||
{
|
||||
callResume(resumeError, fiber, "vs", fiber, error);
|
||||
}
|
||||
|
||||
void schedulerReleaseMethods()
|
||||
{
|
||||
if (resume != NULL) wrenReleaseValue(getVM(), resume);
|
||||
if (resumeWithArg != NULL) wrenReleaseValue(getVM(), resumeWithArg);
|
||||
if (resumeError != NULL) wrenReleaseValue(getVM(), resumeError);
|
||||
}
|
||||
|
||||
@ -4,8 +4,10 @@
|
||||
#include "wren.h"
|
||||
|
||||
void schedulerResume(WrenValue* fiber);
|
||||
void schedulerResumeBytes(WrenValue* fiber, const char* bytes, size_t length);
|
||||
void schedulerResumeDouble(WrenValue* fiber, double value);
|
||||
void schedulerResumeString(WrenValue* fiber, const char* text);
|
||||
void schedulerResumeError(WrenValue* fiber, const char* error);
|
||||
|
||||
void schedulerReleaseMethods();
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ class Scheduler {
|
||||
// Called by native code.
|
||||
static resume_(fiber) { fiber.transfer() }
|
||||
static resume_(fiber, arg) { fiber.transfer(arg) }
|
||||
static resumeError_(fiber, error) { fiber.transferError(error) }
|
||||
|
||||
static runNextScheduled_() {
|
||||
if (__scheduled == null || __scheduled.isEmpty) {
|
||||
|
||||
@ -13,6 +13,7 @@ static const char* schedulerModuleSource =
|
||||
" // Called by native code.\n"
|
||||
" static resume_(fiber) { fiber.transfer() }\n"
|
||||
" static resume_(fiber, arg) { fiber.transfer(arg) }\n"
|
||||
" static resumeError_(fiber, error) { fiber.transferError(error) }\n"
|
||||
"\n"
|
||||
" static runNextScheduled_() {\n"
|
||||
" if (__scheduled == null || __scheduled.isEmpty) {\n"
|
||||
|
||||
19
test/io/file/close.wren
Normal file
19
test/io/file/close.wren
Normal file
@ -0,0 +1,19 @@
|
||||
import "io" for File
|
||||
import "scheduler" for Scheduler
|
||||
|
||||
// See also: is_open.wren.
|
||||
|
||||
var file = File.open("test/io/file/close.wren")
|
||||
|
||||
System.print(file.close()) // expect: null
|
||||
|
||||
// Can call multiple times.
|
||||
file.close()
|
||||
|
||||
// If already closed, returns synchronously.
|
||||
Scheduler.add {
|
||||
System.print("does not print")
|
||||
}
|
||||
|
||||
file.close()
|
||||
System.print("sync") // expect: sync
|
||||
11
test/io/file/descriptor.wren
Normal file
11
test/io/file/descriptor.wren
Normal file
@ -0,0 +1,11 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/descriptor.wren")
|
||||
|
||||
// We can't test for a specific value since it's up to the OS, but it should be
|
||||
// a positive number.
|
||||
System.print(file.descriptor is Num) // expect: true
|
||||
System.print(file.descriptor > 0) // expect: true
|
||||
|
||||
file.close()
|
||||
System.print(file.descriptor) // expect: -1
|
||||
1
test/io/file/file.txt
Normal file
1
test/io/file/file.txt
Normal file
@ -0,0 +1 @@
|
||||
this is a text file
|
||||
6
test/io/file/is_open.wren
Normal file
6
test/io/file/is_open.wren
Normal file
@ -0,0 +1,6 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/is_open.wren")
|
||||
System.print(file.isOpen) // expect: true
|
||||
file.close()
|
||||
System.print(file.isOpen) // expect: false
|
||||
5
test/io/file/open.wren
Normal file
5
test/io/file/open.wren
Normal file
@ -0,0 +1,5 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/open.wren")
|
||||
System.print(file is File) // expect: true
|
||||
System.print(file.isOpen) // expect: true
|
||||
16
test/io/file/open_block.wren
Normal file
16
test/io/file/open_block.wren
Normal file
@ -0,0 +1,16 @@
|
||||
import "io" for File
|
||||
|
||||
var stash
|
||||
File.open("test/io/file/open_block.wren") {|file|
|
||||
System.print(file is File) // expect: true
|
||||
System.print(file.isOpen) // expect: true
|
||||
stash = file
|
||||
}
|
||||
|
||||
// Closes after block.
|
||||
System.print(stash.isOpen) // expect: false
|
||||
|
||||
// Returns null.
|
||||
System.print(File.open("test/io/file/open_block.wren") {|file|}) // expect: null
|
||||
|
||||
// TODO: Test a fiber aborting inside the block.
|
||||
3
test/io/file/open_block_nonexistent.wren
Normal file
3
test/io/file/open_block_nonexistent.wren
Normal file
@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.open("nonexistent") {|file|} // expect runtime error: no such file or directory
|
||||
4
test/io/file/open_block_wrong_block_type.wren
Normal file
4
test/io/file/open_block_wrong_block_type.wren
Normal file
@ -0,0 +1,4 @@
|
||||
import "io" for File
|
||||
|
||||
File.open("test/io/file/open_block_wrong_block_type.wren",
|
||||
"no callable") // expect runtime error: String does not implement 'call(_)'.
|
||||
3
test/io/file/open_block_wrong_path_type.wren
Normal file
3
test/io/file/open_block_wrong_path_type.wren
Normal file
@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.open(123) {|file|} // expect runtime error: Path must be a string.
|
||||
3
test/io/file/open_nonexistent.wren
Normal file
3
test/io/file/open_nonexistent.wren
Normal file
@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.open("nonexistent") // expect runtime error: no such file or directory
|
||||
3
test/io/file/open_wrong_arg_type.wren
Normal file
3
test/io/file/open_wrong_arg_type.wren
Normal file
@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.open(123) // expect runtime error: Path must be a string.
|
||||
8
test/io/file/read.wren
Normal file
8
test/io/file/read.wren
Normal file
@ -0,0 +1,8 @@
|
||||
import "io" for File
|
||||
|
||||
var text = File.read("test/io/file/file.txt")
|
||||
System.print(text) // expect: this is a text file
|
||||
System.print(text.count) // expect: 19
|
||||
|
||||
// TODO: A file containing line endings.
|
||||
// TODO: A file containing null bytes.
|
||||
13
test/io/file/read_bytes.wren
Normal file
13
test/io/file/read_bytes.wren
Normal file
@ -0,0 +1,13 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
|
||||
System.print(file.readBytes(3)) // expect: thi
|
||||
|
||||
// Always reads from the beginning.
|
||||
System.print(file.readBytes(7)) // expect: this is
|
||||
|
||||
// Allows zero.
|
||||
System.print(file.readBytes(0).bytes.count) // expect: 0
|
||||
|
||||
file.close()
|
||||
6
test/io/file/read_bytes_after_close.wren
Normal file
6
test/io/file/read_bytes_after_close.wren
Normal file
@ -0,0 +1,6 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
file.close()
|
||||
|
||||
file.readBytes(3) // expect runtime error: File is not open.
|
||||
4
test/io/file/read_bytes_count_not_num.wren
Normal file
4
test/io/file/read_bytes_count_not_num.wren
Normal file
@ -0,0 +1,4 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
file.readBytes("not num") // expect runtime error: Count must be an integer.
|
||||
4
test/io/file/read_bytes_negative.wren
Normal file
4
test/io/file/read_bytes_negative.wren
Normal file
@ -0,0 +1,4 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
file.readBytes(-1) // expect runtime error: Count cannot be negative.
|
||||
4
test/io/file/read_bytes_not_integer.wren
Normal file
4
test/io/file/read_bytes_not_integer.wren
Normal file
@ -0,0 +1,4 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
file.readBytes(1.2) // expect runtime error: Count must be an integer.
|
||||
3
test/io/file/read_nonexistent.wren
Normal file
3
test/io/file/read_nonexistent.wren
Normal file
@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.read("nonexistent") // expect runtime error: no such file or directory
|
||||
3
test/io/file/read_wrong_arg_type.wren
Normal file
3
test/io/file/read_wrong_arg_type.wren
Normal file
@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.read(123) // expect runtime error: Path must be a string.
|
||||
@ -1,7 +1,7 @@
|
||||
import "io" for File
|
||||
import "scheduler" for Scheduler
|
||||
|
||||
System.print(File.size("test/io/file/size.wren")) // expect: 401
|
||||
System.print(File.size("test/io/file/size.wren")) // expect: 270
|
||||
|
||||
// Runs asynchronously.
|
||||
Scheduler.add {
|
||||
@ -10,9 +10,4 @@ Scheduler.add {
|
||||
|
||||
System.print(File.size("test/io/file/size.wren"))
|
||||
// expect: async
|
||||
// expect: 401
|
||||
|
||||
var error = Fiber.new {
|
||||
System.print(File.size("nonexistent"))
|
||||
}.try()
|
||||
System.print(error) // expect: no such file or directory
|
||||
// expect: 270
|
||||
|
||||
6
test/io/file/size_after_close.wren
Normal file
6
test/io/file/size_after_close.wren
Normal file
@ -0,0 +1,6 @@
|
||||
import "io" for File
|
||||
|
||||
var file = File.open("test/io/file/file.txt")
|
||||
file.close()
|
||||
|
||||
file.size // expect runtime error: File is not open.
|
||||
3
test/io/file/size_nonexistent.wren
Normal file
3
test/io/file/size_nonexistent.wren
Normal file
@ -0,0 +1,3 @@
|
||||
import "io" for File
|
||||
|
||||
File.size("nonexistent") // expect runtime error: no such file or directory
|
||||
Reference in New Issue
Block a user