From be7e4ddd189a4e4125e9adc4c5faf6f592271ffd Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Tue, 12 Nov 2024 22:04:07 +1100 Subject: [PATCH] Reworked the tests, read more I've brought the tests up to scratch, except for compounds im the parser, because I'm too damn tired to do that over SSH. It looks like collections are right-recursive, whixh was unintended but still works just fine. I've also added the '--verbose' flag to the repl to control the debugging output. Several obscure bugs have been fixed, and comments have been tweaked. Mustfail tests are still needed, but that's a low priority. See #142. Fixed #151 --- .../workflows/continuous-integration-v2.yml | 2 - README.md | 9 +- makefile | 8 +- source/toy_array.h | 3 +- source/toy_bytecode.h | 1 - source/toy_lexer.c | 2 +- source/toy_opcodes.h | 3 +- source/toy_parser.c | 4 +- source/toy_value.c | 2 - source/toy_vm.c | 2 +- source/toy_vm.h | 2 +- tests/README.md | 36 ++++ tests/cases/test_ast.c | 180 ++++++++++++------ tests/cases/test_parser.c | 17 +- tests/cases/test_vm.c | 153 ++++++++++++++- tests/integrations/makefile | 4 +- tests/integrations/test_expressions.toy | 3 - tests/integrations/test_keyword_assert.toy | 14 ++ ...{test_print.toy => test_keyword_print.toy} | 4 +- tests/integrations/test_scopes.toy | 10 +- tests/integrations/test_variables.toy | 7 +- .../standalone}/standalone_tests.yml | 0 22 files changed, 364 insertions(+), 102 deletions(-) create mode 100644 tests/README.md delete mode 100644 tests/integrations/test_expressions.toy create mode 100644 tests/integrations/test_keyword_assert.toy rename tests/integrations/{test_print.toy => test_keyword_print.toy} (79%) rename {.github/workflows => tests/standalone}/standalone_tests.yml (100%) diff --git a/.github/workflows/continuous-integration-v2.yml b/.github/workflows/continuous-integration-v2.yml index 9c9e7c2..86657db 100644 --- a/.github/workflows/continuous-integration-v2.yml +++ b/.github/workflows/continuous-integration-v2.yml @@ -60,5 +60,3 @@ jobs: - name: run the tests if: (matrix.commands.gdb == true && matrix.platforms.gdb_enabled == false) != true run: ${{ matrix.commands.exec }} - - diff --git a/README.md b/README.md index 02cae71..f65a6db 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,8 @@ This repository holds the reference implementation for Toy version 2.x, written //print is a built-in keyword, that can handle complex expressions print 6 * 7; -//strings can be concatenated with the .. operator -print "Hello" .. "world!"; +//strings can be concatenated with the .. operator, and substringed with the [] operator +print "Hello" .. "world!"[3, 3]; //[index, length] - this prints "low" //variables are declared easily var foobar = 42; @@ -40,7 +40,10 @@ var foobar = 42; //the types default to 'any' but can be specified if needed (same with constants) var immutable: string const = "Foobar"; -//more examples to be added as the features are implemented +//the assert keyword can check an expression, and takes an optional second parameter +assert immutable == "Fizzbuzz", "This message is sent to the terminal by default"; + +//NOTE: This section will be expanded as more features are implemented ``` # Building diff --git a/makefile b/makefile index 04e27ff..5355d7a 100644 --- a/makefile +++ b/makefile @@ -27,8 +27,8 @@ repl: source .PHONY: tests tests: clean test-cases test-integrations -.PHONY: test-all -test-all: clean test-cases test-integrations +#.PHONY: test-all +#test-all: clean test-cases test-integrations .PHONY: test-cases test-cases: @@ -39,8 +39,8 @@ test-integrations: $(MAKE) -C $(TOY_INTEGRATIONSDIR) -k #same as above, but with GDB -.PHONY: test-gdb -test-gdb: clean test-cases-gdb test-integrations-gdb +.PHONY: tests-gdb +tests-gdb: clean test-cases-gdb test-integrations-gdb .PHONY: test-cases-gdb test-cases-gdb: diff --git a/source/toy_array.h b/source/toy_array.h index 0971964..02398ec 100644 --- a/source/toy_array.h +++ b/source/toy_array.h @@ -33,11 +33,10 @@ TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, unsigned int capacity); //one line to expand the array #ifndef TOY_ARRAY_EXPAND -#define TOY_ARRAY_EXPAND(array) (array = (array != NULL && (array)->count + 1 > (array)->capacity ? Toy_resizeArray(array, (array)-> capacity * TOY_ARRAY_EXPANSION_RATE) : array)) +#define TOY_ARRAY_EXPAND(array) (array = (array != NULL && (array)->count + 1 > (array)->capacity ? Toy_resizeArray(array, (array)->capacity * TOY_ARRAY_EXPANSION_RATE) : array)) #endif //quick push back #ifndef TOY_ARRAY_PUSHBACK #define TOY_ARRAY_PUSHBACK(array, value) (TOY_ARRAY_EXPAND(array),(array)->data[(array)->count++] = (value)) #endif - diff --git a/source/toy_bytecode.h b/source/toy_bytecode.h index 632b898..5fd4855 100644 --- a/source/toy_bytecode.h +++ b/source/toy_bytecode.h @@ -11,4 +11,3 @@ typedef struct Toy_Bytecode { TOY_API Toy_Bytecode Toy_compileBytecode(Toy_Ast* ast); TOY_API void Toy_freeBytecode(Toy_Bytecode bc); - diff --git a/source/toy_lexer.c b/source/toy_lexer.c index 4b1ef73..9f8ab43 100644 --- a/source/toy_lexer.c +++ b/source/toy_lexer.c @@ -23,7 +23,7 @@ const Toy_KeywordTypeTuple keywordTuples[] = { {TOY_TOKEN_TYPE_STRING, "string"}, {TOY_TOKEN_TYPE_ARRAY, "array"}, {TOY_TOKEN_TYPE_TABLE, "table"}, - {TOY_TOKEN_TYPE_FUNCTION, "function"}, //TODO: type?? + {TOY_TOKEN_TYPE_FUNCTION, "function"}, {TOY_TOKEN_TYPE_OPAQUE, "opaque"}, {TOY_TOKEN_TYPE_ANY, "any"}, diff --git a/source/toy_opcodes.h b/source/toy_opcodes.h index 9f19cf3..641e730 100644 --- a/source/toy_opcodes.h +++ b/source/toy_opcodes.h @@ -41,11 +41,10 @@ typedef enum Toy_OpcodeType { TOY_OPCODE_PRINT, TOY_OPCODE_CONCAT, TOY_OPCODE_INDEX, - //TODO: clear the program stack? + //TODO: clear the program stack - much needed //meta instructions TOY_OPCODE_PASS, TOY_OPCODE_ERROR, TOY_OPCODE_EOF = 255, } Toy_OpcodeType; - diff --git a/source/toy_parser.c b/source/toy_parser.c index ec77d9c..1293117 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -563,7 +563,7 @@ static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A advance(parser); if (parser->previous.type == TOY_TOKEN_OPERATOR_COMMA) { - parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); + parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //NOT +1, as compounds are right-recursive return TOY_AST_FLAG_COMPOUND_COLLECTION; } else if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_LEFT) { @@ -848,4 +848,4 @@ void Toy_resetParser(Toy_Parser* parser) { void Toy_configureParser(Toy_Parser* parser, bool removeAssert) { parser->removeAssert = removeAssert; -} \ No newline at end of file +} diff --git a/source/toy_value.c b/source/toy_value.c index b4f2d46..1bafa20 100644 --- a/source/toy_value.c +++ b/source/toy_value.c @@ -272,8 +272,6 @@ void Toy_stringifyValue(Toy_Value value, Toy_callbackType callback) { case TOY_VALUE_STRING: { Toy_String* str = TOY_VALUE_AS_STRING(value); - - //TODO: decide on how long strings, etc. live for in memory if (str->type == TOY_STRING_NODE) { char* buffer = Toy_getStringRawBuffer(str); callback(buffer); diff --git a/source/toy_vm.c b/source/toy_vm.c index 0abbbc4..3631f7c 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -189,7 +189,7 @@ static void processAccess(Toy_VM* vm) { //find and push the value Toy_Value value = Toy_accessScope(vm->scope, TOY_VALUE_AS_STRING(name)); - Toy_pushStack(&vm->stack, value); + Toy_pushStack(&vm->stack, Toy_copyValue(value)); //cleanup Toy_freeValue(name); diff --git a/source/toy_vm.h b/source/toy_vm.h index 1ada0ca..529bc4f 100644 --- a/source/toy_vm.h +++ b/source/toy_vm.h @@ -49,4 +49,4 @@ TOY_API void Toy_freeVM(Toy_VM* vm); TOY_API void Toy_resetVM(Toy_VM* vm); //prepares for another run without deleting stack, scope and memory -//TODO: inject extra data +//TODO: inject extra data (hook system for external libraries) diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..bdcf5c1 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,36 @@ +# Test Instructions + +To run these tests, execute the following commands from the repo's root: + +`make tests` +`make test-cases` +`make test-integrations` + +Alternatively, to run these tests under GDB, execute the following commands from the repo's root: + +`make tests-gdb` +`make test-cases-gdb` +`make test-integrations-gdb` + +Remember that `make clean` will remove the build artifacts after testing, and `make tests` and `make-tests-gdb` automatically invoke `make clean` before they begin. + +## Benchmarks + +For testing and comparing different potential solutions. This may be left in an incomplete state, so it might not work out of the box. + +## Cases + +For testing individual pieces of the source code in isolation. These are essentially the unit tests. + +## Integrations + +This compiles the source and repl files into a library and executable, then runs each `*.toy` file through the repl to ensure the Toy code works in practice. These are essentially integration tests. + +## Mustfails + +These have situations which will raise errors of some kind, to ensure that common user errors are handled gracefully. This is not yet implemented. + +## Standalone + +These are one-file programs that are not intended to test the source directly. Instead, these can cover a number of situations, such as the exact behavior of GitHub's workflow runners, or to generate repetitive code predictably, etc. + diff --git a/tests/cases/test_ast.c b/tests/cases/test_ast.c index cee2bce..5c7c981 100644 --- a/tests/cases/test_ast.c +++ b/tests/cases/test_ast.c @@ -185,7 +185,71 @@ int test_type_emission(Toy_Bucket** bucketHandle) { } } - //emit print keyword + //emit compound + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstCompound(bucketHandle, &ast, TOY_AST_FLAG_COMPOUND_COLLECTION, right); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_COMPOUND || + + ast->compound.left == NULL || + ast->compound.left->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->compound.left->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->compound.left->value.value) != 42 || + + ast->compound.right == NULL || + ast->compound.right->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->compound.right->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->compound.right->value.value) != 69 || + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a compound as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit keyword assert + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* child = NULL; + Toy_Ast* msg = NULL; + + Toy_private_emitAstValue(bucketHandle, &child, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &msg, TOY_VALUE_FROM_INTEGER(69)); + + Toy_private_emitAstAssert(bucketHandle, &ast, child, msg); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_ASSERT || + ast->assert.child == NULL || + ast->assert.child->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->assert.child->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->assert.child->value.value) != 42 || + + ast->assert.message == NULL || + ast->assert.message->type != TOY_AST_VALUE || + TOY_VALUE_IS_INTEGER(ast->assert.message->value.value) != true || + TOY_VALUE_AS_INTEGER(ast->assert.message->value.value) != 69 || + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'assert' as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit keyword print { //build the AST Toy_Ast* ast = NULL; @@ -207,54 +271,11 @@ int test_type_emission(Toy_Bucket** bucketHandle) { ast->print.child->binary.right->type != TOY_AST_VALUE || TOY_VALUE_AS_INTEGER(ast->print.child->binary.right->value.value) != 69) { - fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a print as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'print' as 'Toy_Ast', state unknown\n" TOY_CC_RESET); return -1; } } - //emit and append blocks of code - { - //initialize the root block - Toy_Ast* block = NULL; - Toy_private_initAstBlock(bucketHandle, &block); - - //loop over the ast emissions, appending each one as you go - for (int i = 0; i < 5; i++) { - //build the AST - Toy_Ast* ast = NULL; - Toy_Ast* right = NULL; - Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); - Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); - Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right); - Toy_private_emitAstGroup(bucketHandle, &ast); - - Toy_private_appendAstBlock(bucketHandle, block, ast); - } - - //check if it worked - Toy_Ast* iter = block; - - while(iter != NULL) { - if ( - iter->type != TOY_AST_BLOCK || - iter->block.child == NULL || - iter->block.child->type != TOY_AST_GROUP || - iter->block.child->group.child == NULL || - iter->block.child->group.child->type != TOY_AST_BINARY || - iter->block.child->group.child->binary.flag != TOY_AST_FLAG_ADD || - iter->block.child->group.child->binary.left->type != TOY_AST_VALUE || - TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.left->value.value) != 42 || - iter->block.child->group.child->binary.right->type != TOY_AST_VALUE || - TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.right->value.value) != 69) - { - fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a block as 'Toy_Ast', state unknown\n" TOY_CC_RESET); - return -1; - } - - iter = iter->block.next; - } - } - //emit var declare { //build the AST @@ -331,6 +352,48 @@ int test_type_emission(Toy_Bucket** bucketHandle) { } } + //emit and append blocks of code (at the bottom of this test function, so everything else is checked first) + { + //initialize the root block + Toy_Ast* block = NULL; + Toy_private_initAstBlock(bucketHandle, &block); + + //loop over the ast emissions, appending each one as you go + for (int i = 0; i < 5; i++) { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right); + Toy_private_emitAstGroup(bucketHandle, &ast); + + Toy_private_appendAstBlock(bucketHandle, block, ast); + } + + //check if it worked + Toy_Ast* iter = block; + + while(iter != NULL) { + if ( + iter->type != TOY_AST_BLOCK || + iter->block.child == NULL || + iter->block.child->type != TOY_AST_GROUP || + iter->block.child->group.child == NULL || + iter->block.child->group.child->type != TOY_AST_BINARY || + iter->block.child->group.child->binary.flag != TOY_AST_FLAG_ADD || + iter->block.child->group.child->binary.left->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.left->value.value) != 42 || + iter->block.child->group.child->binary.right->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.right->value.value) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a block as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + + iter = iter->block.next; + } + } return 0; } @@ -339,25 +402,24 @@ int main() { //run each test set, returning the total errors given int total = 0, res = 0; - + { #if TOY_BITNESS == 64 - res = test_sizeof_ast_64bit(); - total += res; - - if (res == 0) { - printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); - } + res = test_sizeof_ast_64bit(); #elif TOY_BITNESS == 32 - res = test_sizeof_ast_32bit(); - total += res; - - if (res == 0) { - printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); - } + res = test_sizeof_ast_32bit(); #else - fprintf(stderr, TOY_CC_WARN "WARNING: Skipping test_sizeof_ast_*bit(); Can't determine the 'bitness' of this platform (seems to be %d)\n" TOY_CC_RESET, TOY_BITNESS); + res = -1; + fprintf(stderr, TOY_CC_WARN "WARNING: Skipping test_sizeof_ast_**bit(); Can't determine the 'bitness' of this platform (seems to be %d)\n" TOY_CC_RESET, TOY_BITNESS); #endif + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + else if (res > 0) { + total += res; + } + } + { Toy_Bucket* bucketHandle = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_type_emission(&bucketHandle); diff --git a/tests/cases/test_parser.c b/tests/cases/test_parser.c index 787fc49..4aa5371 100644 --- a/tests/cases/test_parser.c +++ b/tests/cases/test_parser.c @@ -455,6 +455,11 @@ int test_binary(Toy_Bucket** bucketHandle) { return 0; } +int test_compound(Toy_Bucket** bucketHandle) { + //TODO: fix test_compound() + return 0; +} + int test_precedence(Toy_Bucket** bucketHandle) { //test term-factor precedence { @@ -624,8 +629,6 @@ int main() { total += res; } - //TODO: assign & compare? - { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_values(&bucket); @@ -656,6 +659,16 @@ int main() { total += res; } + { + Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); + res = test_compound(&bucket); + Toy_freeBucket(&bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_precedence(&bucket); diff --git a/tests/cases/test_vm.c b/tests/cases/test_vm.c index 127d919..bb1c74c 100644 --- a/tests/cases/test_vm.c +++ b/tests/cases/test_vm.c @@ -175,7 +175,136 @@ static void callbackUtil(const char* msg) { } } -int test_keywords(Toy_Bucket** bucketHandle) { +int test_keyword_assert(Toy_Bucket** bucketHandle) { + //test assert true + { + //setup + Toy_setAssertFailureCallback(callbackUtil); + const char* source = "assert true;"; + + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); + Toy_Bytecode bc = Toy_compileBytecode(ast); + + Toy_VM vm; + Toy_initVM(&vm); + Toy_bindVM(&vm, bc.ptr); + + //run + Toy_runVM(&vm); + + //check + if (callbackUtilReceived != NULL) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected assert message '%s' found, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); + + //cleanup and return + Toy_resetAssertFailureCallback(); + free(callbackUtilReceived); + Toy_freeVM(&vm); + return -1; + } + + //teadown + Toy_resetAssertFailureCallback(); + free(callbackUtilReceived); + callbackUtilReceived = NULL; + Toy_freeVM(&vm); + } + + //test assert false + { + //setup + Toy_setAssertFailureCallback(callbackUtil); + const char* source = "assert false;"; + + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); + Toy_Bytecode bc = Toy_compileBytecode(ast); + + Toy_VM vm; + Toy_initVM(&vm); + Toy_bindVM(&vm, bc.ptr); + + //run + Toy_runVM(&vm); + + //check + if (callbackUtilReceived == NULL || + strcmp(callbackUtilReceived, "assertion failed") != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected assert failure message '%s' found, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); + + //cleanup and return + Toy_resetAssertFailureCallback(); + free(callbackUtilReceived); + Toy_freeVM(&vm); + return -1; + } + + //teadown + Toy_resetAssertFailureCallback(); + free(callbackUtilReceived); + callbackUtilReceived = NULL; + Toy_freeVM(&vm); + } + + //test assert false with message + { + //setup + Toy_setAssertFailureCallback(callbackUtil); + const char* source = "assert false, \"You passed a false to assert\";"; + + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); + Toy_Bytecode bc = Toy_compileBytecode(ast); + + Toy_VM vm; + Toy_initVM(&vm); + Toy_bindVM(&vm, bc.ptr); + + //run + Toy_runVM(&vm); + + //check + if (callbackUtilReceived == NULL || + strcmp(callbackUtilReceived, "You passed a false to assert") != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected assert failure message '%s' found, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source); + + //cleanup and return + Toy_resetAssertFailureCallback(); + free(callbackUtilReceived); + Toy_freeVM(&vm); + return -1; + } + + //teadown + Toy_resetAssertFailureCallback(); + free(callbackUtilReceived); + callbackUtilReceived = NULL; + Toy_freeVM(&vm); + } + + return 0; +} + +int test_keyword_print(Toy_Bucket** bucketHandle) { //test print { //setup @@ -198,7 +327,7 @@ int test_keywords(Toy_Bucket** bucketHandle) { //run Toy_runVM(&vm); - //check the final state of the stack + //check if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "42") != 0) { @@ -240,7 +369,7 @@ int test_keywords(Toy_Bucket** bucketHandle) { //run Toy_runVM(&vm); - //check the final state of the stack + //check if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "Hello world!") != 0) { @@ -282,7 +411,7 @@ int test_keywords(Toy_Bucket** bucketHandle) { //run Toy_runVM(&vm); - //check the final state of the stack + //check if (callbackUtilReceived == NULL || strcmp(callbackUtilReceived, "Helloworld!") != 0) { @@ -329,7 +458,7 @@ int test_scope(Toy_Bucket** bucketHandle) { //run Toy_runVM(&vm); - //check the final state of the stack + //check Toy_String* key = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_ANY, false); if (vm.stack == NULL || @@ -376,7 +505,7 @@ int test_scope(Toy_Bucket** bucketHandle) { //run Toy_runVM(&vm); - //check the final state of the stack + //check Toy_String* key = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_UNKNOWN, false); if (vm.stack == NULL || @@ -512,7 +641,17 @@ int main() { { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); - res = test_keywords(&bucket); + res = test_keyword_print(&bucket); + Toy_freeBucket(&bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); + res = test_keyword_assert(&bucket); Toy_freeBucket(&bucket); if (res == 0) { printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); diff --git a/tests/integrations/makefile b/tests/integrations/makefile index 969cab5..e30bc40 100644 --- a/tests/integrations/makefile +++ b/tests/integrations/makefile @@ -33,7 +33,7 @@ all: source repl run run: $(TEST_SCRIPTFILES:.toy=.toy-run) %.toy-run: %.toy - $(TEST_OUTDIR)/$(TEST_REPLNAME) -f ../$< + $(TEST_OUTDIR)/$(TEST_REPLNAME) -f ../$< --verbose #same as above, but with gdb gdb: source repl run-gdb @@ -41,7 +41,7 @@ gdb: source repl run-gdb run-gdb: $(TEST_SCRIPTFILES:.toy=.toy-gdb) %.toy-gdb: %.toy - gdb $(TEST_OUTDIR)/$(TEST_REPLNAME) -ix gdb_init -ex=run --batch --return-child-result --args "$(TEST_OUTDIR)/$(TEST_REPLNAME)" "-f" "../$<" + gdb $(TEST_OUTDIR)/$(TEST_REPLNAME) -ix gdb_init -ex=run --batch --return-child-result --args "$(TEST_OUTDIR)/$(TEST_REPLNAME)" "-f" "../$<" "--verbose" #compile the source and repl first source: $(TEST_OBJDIR) $(TEST_OUTDIR) diff --git a/tests/integrations/test_expressions.toy b/tests/integrations/test_expressions.toy deleted file mode 100644 index 6a60545..0000000 --- a/tests/integrations/test_expressions.toy +++ /dev/null @@ -1,3 +0,0 @@ -//basic expressions with no side effects (other than debug stack dumps) -(1 + 2) * (3 + 4); - diff --git a/tests/integrations/test_keyword_assert.toy b/tests/integrations/test_keyword_assert.toy new file mode 100644 index 0000000..4ea5482 --- /dev/null +++ b/tests/integrations/test_keyword_assert.toy @@ -0,0 +1,14 @@ +//NOTE: these tests are all passing - failing tests can be found under the 'mustfails' directory + +//basic assert statement +assert true; + +//assert on a string (tests for it's truthiness) +assert "Hello world"; + +//assert on a condition +assert 1 < 2; + +//assert with an optional message +assert true, "Assertion message"; + diff --git a/tests/integrations/test_print.toy b/tests/integrations/test_keyword_print.toy similarity index 79% rename from tests/integrations/test_print.toy rename to tests/integrations/test_keyword_print.toy index 62aedf7..6020bec 100644 --- a/tests/integrations/test_print.toy +++ b/tests/integrations/test_keyword_print.toy @@ -13,7 +13,9 @@ print "Hello" .. "world!"; //print with escaped characters print "\tHello\nworld"; -//print from a leaf string +//print from a substring string print "Hello world"[0,5]; +//print from a substring, after a concat print ("hello" .. "world")[2,6]; + diff --git a/tests/integrations/test_scopes.toy b/tests/integrations/test_scopes.toy index 1525a1f..985267b 100644 --- a/tests/integrations/test_scopes.toy +++ b/tests/integrations/test_scopes.toy @@ -2,8 +2,8 @@ var answer = 42; print answer; //42 { - var answer = 7; - print answer; //7 + var answer = 7; + print answer; //7 } print answer; //42 @@ -11,9 +11,7 @@ print answer; //42 var question = 42; print question; //42 { - var question = question; - print question; //42 + var question = question; + print question; //42 } print question; //42 - -//TODO: scope test case diff --git a/tests/integrations/test_variables.toy b/tests/integrations/test_variables.toy index 03248b6..d570489 100644 --- a/tests/integrations/test_variables.toy +++ b/tests/integrations/test_variables.toy @@ -50,7 +50,7 @@ print !true; //false print !false; //true //precedence -print true && false || true; //TODO: a warning is needed for this +print true && false || true; //TODO: a grouping warning is needed for this //types var a: int; @@ -67,4 +67,9 @@ var c: int const = 42; print c; +//indexing +var s = "Hello" .. "world!"; + +print s[3, 3]; + //TODO: type casting diff --git a/.github/workflows/standalone_tests.yml b/tests/standalone/standalone_tests.yml similarity index 100% rename from .github/workflows/standalone_tests.yml rename to tests/standalone/standalone_tests.yml