From 7084d6bfd5138855be86ad19e97c34e0ce5c1914 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Sun, 24 May 2015 09:23:30 -0700 Subject: [PATCH] Add infrastructure to test embedding API. --- Makefile | 1 + script/test.py | 33 +++++++++++---- script/wren.mk | 34 +++++++++++++--- src/cli/main.c | 4 +- src/cli/vm.c | 8 ++-- src/cli/vm.h | 4 +- test/api/main.c | 58 +++++++++++++++++++++++++++ test/api/return_bool/return_bool.c | 21 ++++++++++ test/api/return_bool/return_bool.h | 3 ++ test/api/return_bool/return_bool.wren | 7 ++++ 10 files changed, 152 insertions(+), 21 deletions(-) create mode 100644 test/api/main.c create mode 100644 test/api/return_bool/return_bool.c create mode 100644 test/api/return_bool/return_bool.h create mode 100644 test/api/return_bool/return_bool.wren diff --git a/Makefile b/Makefile index e31b44f7..0fadf053 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/script/test.py b/script/test.py index 134a5de8..31d7b71f 100755 --- a/script/test.py +++ b/script/test.py @@ -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() diff --git a/script/wren.mk b/script/wren.mk index a1402ea3..0b3e0fb3 100644 --- a/script/wren.mk +++ b/script/wren.mk @@ -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 diff --git a/src/cli/main.c b/src/cli/main.c index b04e9f94..2b8e5d61 100644 --- a/src/cli/main.c +++ b/src/cli/main.c @@ -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; diff --git a/src/cli/vm.c b/src/cli/vm.c index 4008d58d..3a62d0f7 100644 --- a/src/cli/vm.c +++ b/src/cli/vm.c @@ -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); diff --git a/src/cli/vm.h b/src/cli/vm.h index 32520ded..90fa491a 100644 --- a/src/cli/vm.h +++ b/src/cli/vm.h @@ -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 diff --git a/test/api/main.c b/test/api/main.c new file mode 100644 index 00000000..a7de5aa0 --- /dev/null +++ b/test/api/main.c @@ -0,0 +1,58 @@ +#include +#include + +#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 \n"); + return 64; // EX_USAGE. + } + + testName = argv[1]; + + // The test script is at "test/api//.wren". + char testPath[256]; + strcpy(testPath, "test/api/"); + strcat(testPath, testName); + strcat(testPath, "/"); + strcat(testPath, testName); + strcat(testPath, ".wren"); + + runFile(bindForeign, testPath); + return 0; +} diff --git a/test/api/return_bool/return_bool.c b/test/api/return_bool/return_bool.c new file mode 100644 index 00000000..e30ae061 --- /dev/null +++ b/test/api/return_bool/return_bool.c @@ -0,0 +1,21 @@ +#include + +#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; +} diff --git a/test/api/return_bool/return_bool.h b/test/api/return_bool/return_bool.h new file mode 100644 index 00000000..f20d4986 --- /dev/null +++ b/test/api/return_bool/return_bool.h @@ -0,0 +1,3 @@ +#include "wren.h" + +WrenForeignMethodFn returnBoolBindForeign(const char* signature); diff --git a/test/api/return_bool/return_bool.wren b/test/api/return_bool/return_bool.wren new file mode 100644 index 00000000..6e33ac8d --- /dev/null +++ b/test/api/return_bool/return_bool.wren @@ -0,0 +1,7 @@ +class Api { + foreign static returnTrue + foreign static returnFalse +} + +IO.print(Api.returnTrue) // expect: true +IO.print(Api.returnFalse) // expect: false