1
0
forked from Mirror/wren

Merge pull request #273 from munificent/api-test

Add infrastructure to test embedding API.
This commit is contained in:
Bob Nystrom
2015-05-24 09:29:25 -07:00
10 changed files with 152 additions and 21 deletions

View File

@ -34,6 +34,7 @@ clean:
# Run the tests against the debug build of Wren.
test: debug
@ $(MAKE) -f script/wren.mk MODE=debug test
@ ./script/test.py $(suite)
# Take the contents of the scripts under builtin/ and copy them into their

View File

@ -4,7 +4,7 @@ from __future__ import print_function
from collections import defaultdict
from os import listdir
from os.path import abspath, dirname, isdir, isfile, join, realpath, relpath, splitext
from os.path import abspath, basename, dirname, isdir, isfile, join, realpath, relpath, splitext
import re
from subprocess import Popen, PIPE
import sys
@ -12,6 +12,7 @@ import sys
# Runs the tests.
WREN_DIR = dirname(dirname(realpath(__file__)))
WREN_APP = join(WREN_DIR, 'bin', 'wrend')
TEST_APP = join(WREN_DIR, 'build', 'debug', 'test', 'wrend')
EXPECT_PATTERN = re.compile(r'// expect: (.*)')
EXPECT_ERROR_PATTERN = re.compile(r'// expect error')
@ -72,7 +73,7 @@ def print_line(line=None):
sys.stdout.flush()
def run_test(path, example=False):
def run_script(app, path, type):
global passed
global failed
global skipped
@ -155,7 +156,12 @@ def run_test(path, example=False):
input_bytes = "".join(input_lines).encode("utf-8")
# Invoke wren and run the test.
proc = Popen([WREN_APP, path], stdin=PIPE, stdout=PIPE, stderr=PIPE)
test_arg = path
if type == "api test":
# Just pass the suite name to API tests.
test_arg = basename(splitext(test_arg)[0])
proc = Popen([app, test_arg], stdin=PIPE, stdout=PIPE, stderr=PIPE)
(out, err) = proc.communicate(input_bytes)
fails = []
@ -223,7 +229,9 @@ def run_test(path, example=False):
for line in out_lines:
if sys.version_info < (3, 0):
line = line.encode('utf-8')
if example:
if type == "example":
# Ignore output from examples.
pass
elif expect_index >= len(expect_output):
fails.append('Got output "{0}" when none was expected.'.format(line))
@ -251,10 +259,21 @@ def run_test(path, example=False):
print(' ' + color.PINK + fail + color.DEFAULT)
print('')
def run_example(path):
return run_test(path, example=True)
walk(join(WREN_DIR, 'test'), run_test, ignored=['benchmark'])
def run_test(path, example=False):
run_script(WREN_APP, path, "test")
def run_api_test(path):
run_script(TEST_APP, path, "api test")
def run_example(path):
run_script(WREN_APP, path, "example")
walk(join(WREN_DIR, 'test'), run_test, ignored=['api', 'benchmark'])
walk(join(WREN_DIR, 'test', 'api'), run_api_test)
walk(join(WREN_DIR, 'example'), run_example)
print_line()

View File

@ -23,6 +23,7 @@ CLI_HEADERS := $(wildcard src/cli/*.h)
VM_HEADERS := $(wildcard src/vm/*.h)
CLI_SOURCES := $(wildcard src/cli/*.c)
VM_SOURCES := $(wildcard src/vm/*.c)
TEST_SOURCES := $(shell find test/api -name '*.c')
BUILD_DIR := build
C_WARNINGS := -Wall -Wextra -Werror -Wno-unused-parameter
@ -93,37 +94,58 @@ endif
CFLAGS := $(C_OPTIONS) $(C_WARNINGS)
CLI_OBJECTS := $(addprefix $(BUILD_DIR)/cli/, $(notdir $(CLI_SOURCES:.c=.o)))
VM_OBJECTS := $(addprefix $(BUILD_DIR)/vm/, $(notdir $(VM_SOURCES:.c=.o)))
CLI_OBJECTS := $(addprefix $(BUILD_DIR)/cli/, $(notdir $(CLI_SOURCES:.c=.o)))
VM_OBJECTS := $(addprefix $(BUILD_DIR)/vm/, $(notdir $(VM_SOURCES:.c=.o)))
TEST_OBJECTS := $(patsubst test/api/%.c, $(BUILD_DIR)/test/%.o, $(TEST_SOURCES))
# Targets ---------------------------------------------------------------------
all: prep bin/$(WREN) lib/lib$(WREN).a lib/lib$(WREN).$(SHARED_EXT)
# Builds the VM libraries and CLI interpreter.
all: bin/$(WREN) lib/lib$(WREN).a lib/lib$(WREN).$(SHARED_EXT)
prep:
@mkdir -p bin lib $(BUILD_DIR)/cli $(BUILD_DIR)/vm
# Builds the API test executable.
test: $(BUILD_DIR)/test/$(WREN)
# Command-line interpreter.
bin/$(WREN): $(CLI_OBJECTS) $(VM_OBJECTS)
@printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS)"
@$(CC) $(CFLAGS) -Isrc/include -o $@ $^ -lm
@mkdir -p bin
@$(CC) $(CFLAGS) -o $@ $^ -lm
# Static library.
lib/lib$(WREN).a: $(VM_OBJECTS)
@printf "%10s %-30s %s\n" $(AR) $@ "rcu"
@mkdir -p lib
@$(AR) rcu $@ $^
# Shared library.
lib/lib$(WREN).$(SHARED_EXT): $(VM_OBJECTS)
@printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS) $(SHARED_LIB_FLAGS)"
@mkdir -p lib
@$(CC) $(CFLAGS) -shared $(SHARED_LIB_FLAGS) -o $@ $^
# Test executable.
$(BUILD_DIR)/test/$(WREN): $(TEST_OBJECTS) $(VM_OBJECTS) $(BUILD_DIR)/cli/io.o $(BUILD_DIR)/cli/vm.o
@printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS)"
@mkdir -p $(BUILD_DIR)/test
@$(CC) $(CFLAGS) -o $@ $^ -lm
# CLI object files.
$(BUILD_DIR)/cli/%.o: src/cli/%.c $(CLI_HEADERS) $(VM_HEADERS)
@printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
@mkdir -p $(BUILD_DIR)/cli
@$(CC) -c $(CFLAGS) -Isrc/include -o $@ $(FILE_FLAG) $<
# VM object files.
$(BUILD_DIR)/vm/%.o: src/vm/%.c $(VM_HEADERS)
@printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
@mkdir -p $(BUILD_DIR)/vm
@$(CC) -c $(CFLAGS) -Isrc/include -o $@ $(FILE_FLAG) $<
# Test object files.
$(BUILD_DIR)/test/%.o: test/api/%.c $(VM_HEADERS)
@printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
@mkdir -p $(dir $@)
@$(CC) -c $(CFLAGS) -Isrc/include -Isrc/cli -o $@ $(FILE_FLAG) $<
.PHONY: all test

View File

@ -9,7 +9,7 @@
static int runRepl()
{
WrenVM* vm = createVM();
WrenVM* vm = createVM(NULL);
printf("\\\\/\"-\n");
printf(" \\_/ wren v0.0.0\n");
@ -50,7 +50,7 @@ int main(int argc, const char* argv[])
}
else if (argc == 2)
{
runFile(argv[1]);
runFile(NULL, argv[1]);
}
return 0;

View File

@ -4,11 +4,11 @@
#include "io.h"
#include "vm.h"
WrenVM* createVM()
WrenVM* createVM(WrenBindForeignMethodFn bindForeign)
{
WrenConfiguration config;
config.bindForeignMethodFn = NULL;
config.bindForeignMethodFn = bindForeign;
config.loadModuleFn = readModule;
// Since we're running in a standalone process, be generous with memory.
@ -22,7 +22,7 @@ WrenVM* createVM()
return wrenNewVM(&config);
}
void runFile(const char* path)
void runFile(WrenBindForeignMethodFn bindForeign, const char* path)
{
// Use the directory where the file is as the root to resolve imports
// relative to.
@ -42,7 +42,7 @@ void runFile(const char* path)
exit(66);
}
WrenVM* vm = createVM();
WrenVM* vm = createVM(bindForeign);
WrenInterpretResult result = wrenInterpret(vm, path, source);

View File

@ -4,11 +4,11 @@
#include "wren.h"
// Creates a new Wren VM with the CLI's module loader and other configuration.
WrenVM* createVM();
WrenVM* createVM(WrenBindForeignMethodFn bindForeign);
// Executes the Wren script at [path] in a new VM.
//
// Exits if the script failed or could not be loaded.
void runFile(const char* path);
void runFile(WrenBindForeignMethodFn bindForeign, const char* path);
#endif

58
test/api/main.c Normal file
View File

@ -0,0 +1,58 @@
#include <stdio.h>
#include <string.h>
#include "io.h"
#include "vm.h"
#include "wren.h"
#include "return_bool/return_bool.h"
// The name of the currently executing API test.
const char* testName;
static WrenForeignMethodFn bindForeign(
WrenVM* vm, const char* module, const char* className,
bool isStatic, const char* signature)
{
if (strcmp(module, "main") != 0) return NULL;
// For convenience, concatenate all of the method qualifiers into a single
// signature string.
char fullName[256];
fullName[0] = '\0';
if (isStatic) strcat(fullName, "static ");
strcat(fullName, className);
strcat(fullName, ".");
strcat(fullName, signature);
if (strcmp(testName, "return_bool") == 0)
{
return returnBoolBindForeign(fullName);
}
fprintf(stderr,
"Unknown foreign method '%s' for test '%s'\n", fullName, testName);
return NULL;
}
int main(int argc, const char* argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: wren <test>\n");
return 64; // EX_USAGE.
}
testName = argv[1];
// The test script is at "test/api/<test>/<test>.wren".
char testPath[256];
strcpy(testPath, "test/api/");
strcat(testPath, testName);
strcat(testPath, "/");
strcat(testPath, testName);
strcat(testPath, ".wren");
runFile(bindForeign, testPath);
return 0;
}

View File

@ -0,0 +1,21 @@
#include <string.h>
#include "wren.h"
static void returnTrue(WrenVM* vm)
{
wrenReturnBool(vm, true);
}
static void returnFalse(WrenVM* vm)
{
wrenReturnBool(vm, false);
}
WrenForeignMethodFn returnBoolBindForeign(const char* signature)
{
if (strcmp(signature, "static Api.returnTrue") == 0) return returnTrue;
if (strcmp(signature, "static Api.returnFalse") == 0) return returnFalse;
return NULL;
}

View File

@ -0,0 +1,3 @@
#include "wren.h"
WrenForeignMethodFn returnBoolBindForeign(const char* signature);

View File

@ -0,0 +1,7 @@
class Api {
foreign static returnTrue
foreign static returnFalse
}
IO.print(Api.returnTrue) // expect: true
IO.print(Api.returnFalse) // expect: false