From 6e3379b5b8e975906ac99eaabfadc9b8293dfc14 Mon Sep 17 00:00:00 2001 From: Bobby Lucero Date: Tue, 12 Aug 2025 16:50:54 -0400 Subject: [PATCH] Updated tests and testing built in modules --- Reference/BOB_LANGUAGE_REFERENCE.md | 4 +- Reference/ROADMAP.md | 2 +- bob-language-extension/README.md | 2 +- bob-language-extension/example.bob | 2 +- bob-language-extension/snippets/bob.json | 2 +- leakTests/leaktest_builtin.bob | 5 +- src/sources/builtinModules/path.cpp | 4 +- src/sources/builtinModules/sys.cpp | 46 ++++++ src/sources/stdlib/BobStdLib.cpp | 93 +----------- test_bob_language.bob | 158 ++++++++++++--------- tests/test_base64.bob | 11 ++ tests/test_hash.bob | 5 + tests/test_imports_basic.bob | 3 +- tests/test_math.bob | 15 ++ tests/test_path.bob | 15 ++ tests/test_rand.bob | 14 ++ tests/test_re.bob | 5 + tests/test_time.bob | 14 ++ tests/test_try_catch_loop_interactions.bob | 8 +- tests/test_uuid.bob | 5 + 20 files changed, 241 insertions(+), 172 deletions(-) create mode 100644 tests/test_base64.bob create mode 100644 tests/test_hash.bob create mode 100644 tests/test_math.bob create mode 100644 tests/test_path.bob create mode 100644 tests/test_rand.bob create mode 100644 tests/test_re.bob create mode 100644 tests/test_time.bob create mode 100644 tests/test_uuid.bob diff --git a/Reference/BOB_LANGUAGE_REFERENCE.md b/Reference/BOB_LANGUAGE_REFERENCE.md index b8d0bc2..ed9beef 100644 --- a/Reference/BOB_LANGUAGE_REFERENCE.md +++ b/Reference/BOB_LANGUAGE_REFERENCE.md @@ -260,7 +260,7 @@ toInt(3.9); // 3 (global) assert(condition, "message"); // Testing time(); // Current time in microseconds sleep(1.5); // Sleep for 1.5 seconds -random(); // Random number 0-1 +rand.random(); // Random number 0-1 eval("print('Hello');"); // Execute string as code exit(0); // Exit program ``` @@ -297,7 +297,7 @@ The following built-ins are available by default. Unless specified, functions th - fileExists(path): boolean - time(): microseconds since Unix epoch - sleep(seconds): pauses execution -- random(): float in [0,1) +- rand.random(): float in [0,1) - eval(code): executes code string in current environment - exit(code?): terminates the program diff --git a/Reference/ROADMAP.md b/Reference/ROADMAP.md index b598a3a..caa7ad7 100644 --- a/Reference/ROADMAP.md +++ b/Reference/ROADMAP.md @@ -56,7 +56,7 @@ Bob is a mature, working programming language with a modern architecture and com - **Type System**: `type()`, `toString()`, `toNumber()`, `toInt()`, `toBoolean()` - **Testing**: `assert()` with custom error messages - **Timing**: `time()` (microsecond precision), `sleep()` -- **Utility**: `random()` (properly seeded), `eval()`, `exit()` +- **Utility**: `rand.random()` (properly seeded), `eval.eval()`, `sys.exit()` - **Data Structure**: `len()`, `push()`, `pop()`, `keys()`, `values()`, `has()` - **File I/O**: `readFile()`, `writeFile()`, `readLines()`, `fileExists()` diff --git a/bob-language-extension/README.md b/bob-language-extension/README.md index 2cde502..62e0e7a 100644 --- a/bob-language-extension/README.md +++ b/bob-language-extension/README.md @@ -24,7 +24,7 @@ This extension provides syntax highlighting and language support for the Bob pro - `print()`, `assert()`, `input()`, `type()`, `toString()`, `toNumber()`, `toInt()`, `time()`, `sleep()`, `printRaw()` - Arrays/Dictionaries (preferred method style): `arr.len()`, `arr.push(...)`, `arr.pop()`, `dict.len()`, `dict.keys()`, `dict.values()`, `dict.has()` - Global forms still available: `len(x)`, `push(arr, ...)`, `pop(arr)`, `keys(dict)`, `values(dict)`, `has(dict, key)` -- Misc: `random()`, `eval()` +- Misc: `rand.random()`, `eval.eval()` ### Data Types - Numbers (integers, floats, binary `0b1010`, hex `0xFF`) diff --git a/bob-language-extension/example.bob b/bob-language-extension/example.bob index 9acb4e8..8421642 100644 --- a/bob-language-extension/example.bob +++ b/bob-language-extension/example.bob @@ -91,7 +91,7 @@ value *= 2; value -= 3; // New built-in functions -var randomValue = random(); +import rand; var randomValue = rand.random(); sleep(100); // Sleep for 100ms printRaw("No newline here"); eval("print('Dynamic code execution!');"); diff --git a/bob-language-extension/snippets/bob.json b/bob-language-extension/snippets/bob.json index 15c3dbe..e5e6e5a 100644 --- a/bob-language-extension/snippets/bob.json +++ b/bob-language-extension/snippets/bob.json @@ -300,7 +300,7 @@ "Random Number": { "prefix": "random", "body": [ - "var randomValue = random();" + "import rand; var randomValue = rand.random();" ], "description": "Generate random number" }, diff --git a/leakTests/leaktest_builtin.bob b/leakTests/leaktest_builtin.bob index 0e474a6..7b6c27e 100644 --- a/leakTests/leaktest_builtin.bob +++ b/leakTests/leaktest_builtin.bob @@ -151,8 +151,9 @@ input("File data cleared. Check memory usage..."); print("Test 6: Random number stress"); var randomData = []; for (var i = 0; i < 200000; i++) { - var rand1 = random(); - var rand2 = random(); + import rand as RLeak; + var rand1 = RLeak.random(); + var rand2 = RLeak.random(); var sum = rand1 + rand2; randomData.push({ diff --git a/src/sources/builtinModules/path.cpp b/src/sources/builtinModules/path.cpp index 11f03ce..bdb5eb9 100644 --- a/src/sources/builtinModules/path.cpp +++ b/src/sources/builtinModules/path.cpp @@ -27,7 +27,9 @@ void registerPathModule(Interpreter& interpreter) { m.fn("splitext", [](std::vector a, int, int) -> Value { if (a.size()!=1 || !a[0].isString()) return NONE_VALUE; fs::path p(a[0].asString()); - return Value(std::vector{ Value(p.replace_extension("").generic_string()), Value(p.extension().generic_string()) }); + std::string ext = p.has_extension() ? p.extension().generic_string() : std::string(""); + fs::path basePath = p.has_extension() ? (p.parent_path() / p.stem()) : p; + return Value(std::vector{ Value(basePath.generic_string()), Value(ext) }); }); m.fn("normalize", [](std::vector a, int, int) -> Value { if (a.size()!=1 || !a[0].isString()) return NONE_VALUE; diff --git a/src/sources/builtinModules/sys.cpp b/src/sources/builtinModules/sys.cpp index b0f1c48..a546ead 100644 --- a/src/sources/builtinModules/sys.cpp +++ b/src/sources/builtinModules/sys.cpp @@ -8,6 +8,18 @@ #include #include +// Platform-specific includes for memoryUsage() +#if defined(__APPLE__) && defined(__MACH__) +#include +#elif defined(__linux__) +#include +#include +#elif defined(_WIN32) +#define NOMINMAX +#include +#include +#endif + void registerSysModule(Interpreter& interpreter) { interpreter.registerModule("sys", [](Interpreter::ModuleBuilder& m) { Interpreter& I = m.interpreterRef; @@ -37,6 +49,40 @@ void registerSysModule(Interpreter& interpreter) { auto snapshot = I.getModuleCacheSnapshot(); return Value(snapshot); }); + // memoryUsage(): process RSS in MB (best effort per-platform) + m.fn("memoryUsage", [](std::vector a, int, int) -> Value { + if (!a.empty()) return NONE_VALUE; + size_t memoryBytes = 0; +#if defined(__APPLE__) && defined(__MACH__) + // macOS + struct mach_task_basic_info info; + mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; + if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) == KERN_SUCCESS) { + memoryBytes = info.resident_size; + } +#elif defined(__linux__) + // Linux + std::ifstream statusFile("/proc/self/status"); + std::string line; + while (std::getline(statusFile, line)) { + if (line.substr(0, 6) == "VmRSS:") { + std::istringstream iss(line); + std::string label, value, unit; + iss >> label >> value >> unit; + memoryBytes = std::stoull(value) * 1024; // KB -> bytes + break; + } + } +#elif defined(_WIN32) + // Windows + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { + memoryBytes = pmc.WorkingSetSize; + } +#endif + double memoryMB = static_cast(memoryBytes) / (1024.0 * 1024.0); + return Value(memoryMB); + }); m.fn("exit", [](std::vector a, int, int) -> Value { int code = 0; if (!a.empty() && a[0].isNumber()) code = static_cast(a[0].asNumber()); std::exit(code); diff --git a/src/sources/stdlib/BobStdLib.cpp b/src/sources/stdlib/BobStdLib.cpp index 210f981..87c289f 100644 --- a/src/sources/stdlib/BobStdLib.cpp +++ b/src/sources/stdlib/BobStdLib.cpp @@ -290,24 +290,7 @@ void BobStdLib::addToEnvironment(std::shared_ptr env, Interpreter& // Store the shared_ptr in the interpreter to keep it alive interpreter.addBuiltinFunction(toBooleanFunc); - // Create a built-in exit function to terminate the program - auto exitFunc = std::make_shared("exit", - [](std::vector args, int line, int column) -> Value { - int exitCode = 0; // Default exit code - - if (args.size() > 0) { - if (args[0].isNumber()) { - exitCode = static_cast(args[0].asNumber()); - } - // If not a number, just use default exit code 0 - } - - std::exit(exitCode); - }); - env->define("exit", Value(exitFunc)); - - // Store the shared_ptr in the interpreter to keep it alive - interpreter.addBuiltinFunction(exitFunc); + // exit moved to sys module // sleep moved into builtin time module @@ -353,30 +336,7 @@ void BobStdLib::addToEnvironment(std::shared_ptr env, Interpreter& env->define("functions", Value(functionsFunc)); interpreter.addBuiltinFunction(functionsFunc); - // Create a built-in random function - auto randomFunc = std::make_shared("random", - [errorReporter](std::vector args, int line, int column) -> Value { - if (args.size() != 0) { - if (errorReporter) { - errorReporter->reportError(line, column, "StdLib Error", - "Expected 0 arguments but got " + std::to_string(args.size()) + ".", "", true); - } - throw std::runtime_error("Expected 0 arguments but got " + std::to_string(args.size()) + "."); - } - - // Seed the random number generator if not already done - static bool seeded = false; - if (!seeded) { - srand(static_cast(time(nullptr))); - seeded = true; - } - - return Value(static_cast(rand()) / RAND_MAX); - }); - env->define("random", Value(randomFunc)); - - // Store the shared_ptr in the interpreter to keep it alive - interpreter.addBuiltinFunction(randomFunc); + // random moved to rand module // (eval and evalFile moved to eval module) @@ -384,53 +344,6 @@ void BobStdLib::addToEnvironment(std::shared_ptr env, Interpreter& // (file I/O moved to io module) - // Create a built-in memoryUsage function (platform-specific, best effort) - auto memoryUsageFunc = std::make_shared("memoryUsage", - [errorReporter](std::vector args, int line, int column) -> Value { - if (args.size() != 0) { - if (errorReporter) { - errorReporter->reportError(line, column, "StdLib Error", - "Expected 0 arguments but got " + std::to_string(args.size()) + ".", "", true); - } - throw std::runtime_error("Expected 0 arguments but got " + std::to_string(args.size()) + "."); - } - - // Platform-specific memory usage detection - size_t memoryBytes = 0; - -#if defined(__APPLE__) && defined(__MACH__) - // macOS - struct mach_task_basic_info info; - mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; - if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) == KERN_SUCCESS) { - memoryBytes = info.resident_size; - } -#elif defined(__linux__) - // Linux - read from /proc/self/status - std::ifstream statusFile("/proc/self/status"); - std::string line; - while (std::getline(statusFile, line)) { - if (line.substr(0, 6) == "VmRSS:") { - std::istringstream iss(line); - std::string label, value, unit; - iss >> label >> value >> unit; - memoryBytes = std::stoull(value) * 1024; // Convert KB to bytes - break; - } - } -#elif defined(_WIN32) - // Windows - PROCESS_MEMORY_COUNTERS pmc; - if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) { - memoryBytes = pmc.WorkingSetSize; - } -#endif - - // Return memory usage in MB for readability - double memoryMB = static_cast(memoryBytes) / (1024.0 * 1024.0); - return Value(memoryMB); - }); - env->define("memoryUsage", Value(memoryUsageFunc)); - interpreter.addBuiltinFunction(memoryUsageFunc); + // memoryUsage moved to sys module } \ No newline at end of file diff --git a/test_bob_language.bob b/test_bob_language.bob index d7adea4..932b959 100644 --- a/test_bob_language.bob +++ b/test_bob_language.bob @@ -786,9 +786,9 @@ print("Comprehensive number tests: PASS"); // TEST 29: TIME FUNCTION // ======================================== print("\n--- Test 29: Time Function ---"); - -var start_time = time(); -var end_time = time(); +import time; +var start_time = time.now(); +var end_time = time.now(); var duration = end_time - start_time; assert(start_time > 0, "Start time should be positive"); assert(end_time >= start_time, "End time should be >= start time"); @@ -2416,15 +2416,17 @@ print("Array built-in functions: PASS"); print("\n--- Test 52: New Built-in Functions ---"); // sleep() function -var startTime = time(); -//sleep(0.001); // Sleep for 1ms (much shorter for testing) -var endTime = time(); +import time as T; +var startTime = T.now(); +T.sleep(0.01); // Sleep briefly to ensure elapsed time +var endTime = T.now(); assert(endTime > startTime, "sleep() - time elapsed"); // random() function - test proper seeding -var random1 = random(); -var random2 = random(); -var random3 = random(); +import rand as R; +var random1 = R.random(); +var random2 = R.random(); +var random3 = R.random(); assert(random1 >= 0 && random1 <= 1, "random() - range check 1"); assert(random2 >= 0 && random2 <= 1, "random() - range check 2"); assert(random3 >= 0 && random3 <= 1, "random() - range check 3"); @@ -2432,30 +2434,44 @@ assert(random3 >= 0 && random3 <= 1, "random() - range check 3"); assert(random1 != random2 || random2 != random3 || random1 != random3, "random() - different values"); // Test random number generation in different ranges -var randomRange1 = random() * 10; -var randomRange2 = random() * 100; +var randomRange1 = R.random() * 10; +var randomRange2 = R.random() * 100; assert(randomRange1 >= 0 && randomRange1 <= 10, "random() - range 0-10"); assert(randomRange2 >= 0 && randomRange2 <= 100, "random() - range 0-100"); -// eval() function -eval("var evalVar = 42;"); +// eval() function (now via eval module) +import eval as E; +E.eval("var evalVar = 42;"); assert(evalVar == 42, "eval() - variable creation"); -eval("print(\"eval test\");"); // Should print "eval test" +E.eval("print(\"eval test\");"); // Should print "eval test" -var evalResult = eval("2 + 2;"); +var evalResult = E.eval("2 + 2;"); // eval() currently returns none, so we just test it doesn't crash // Test eval with complex expressions -eval("var complexVar = [1, 2, 3];"); +E.eval("var complexVar = [1, 2, 3];"); assert(complexVar.len() == 3, "eval() - complex expression"); // Test eval with function definitions -eval("func evalFunc(x) { return x * 2; }"); +E.eval("func evalFunc(x) { return x * 2; }"); assert(evalFunc(5) == 10, "eval() - function definition"); print("New built-in functions: PASS"); +// Module tests +import io as IO; +var p1 = IO.exists("tests/test_path.bob") ? "tests/test_path.bob" : "../tests/test_path.bob"; +E.evalFile(p1); +var p2 = IO.exists("tests/test_base64.bob") ? "tests/test_base64.bob" : "../tests/test_base64.bob"; +E.evalFile(p2); +var p3 = IO.exists("tests/test_math.bob") ? "tests/test_math.bob" : "../tests/test_math.bob"; +E.evalFile(p3); +var p4 = IO.exists("tests/test_rand.bob") ? "tests/test_rand.bob" : "../tests/test_rand.bob"; +E.evalFile(p4); +var p5 = IO.exists("tests/test_time.bob") ? "tests/test_time.bob" : "../tests/test_time.bob"; +E.evalFile(p5); + // ======================================== // TEST 52.5: EXIT FUNCTION // ======================================== @@ -2718,14 +2734,14 @@ print("\n--- Test 54: Array Performance ---"); // Test large array operations var perfArray = []; -var startTime = time(); +import time as T; var startTime = T.now(); // Create large array for (var i = 0; i < 1000; i = i + 1) { perfArray.push(i); } -var midTime = time(); +var midTime = T.now(); assert(perfArray.len() == 1000, "Array performance - creation"); // Access elements @@ -2734,7 +2750,7 @@ for (var j = 0; j < 100; j = j + 1) { assert(value == j * 10, "Array performance - access"); } -var endTime = time(); +var endTime = T.now(); assert(endTime > startTime, "Array performance - time check"); print("Array performance: PASS"); @@ -2852,7 +2868,7 @@ print("\n--- Test 57: Array Stress Testing ---"); // Large array creation and manipulation var stressArray = []; -var startTime = time(); +import time as T2; var startTime = T2.now(); // Create large array with mixed types for (var i = 0; i < 500; i = i + 1) { @@ -2865,7 +2881,7 @@ for (var i = 0; i < 500; i = i + 1) { } } -var midTime = time(); +var midTime = T2.now(); assert(stressArray.len() == 500, "Stress test - array creation"); // Access and modify elements @@ -2876,7 +2892,7 @@ for (var j = 0; j < 100; j = j + 1) { } } -var endTime = time(); +var endTime = T2.now(); assert(endTime > startTime, "Stress test - time validation"); // Verify modifications @@ -2992,7 +3008,8 @@ assert(toInt(-0.0) == 0, "toInt(-0.0) should be 0"); // Test with random numbers for(var i = 0; i < 5; i++) { - var randomFloat = random() * 10; + import rand as Rn; + var randomFloat = Rn.random() * 10; var randomInt = toInt(randomFloat); assert(randomInt >= 0 && randomInt <= 9, "toInt(random) should be in range 0-9"); } @@ -3273,58 +3290,59 @@ print(" * Error reporting in both modes"); // Additional Tests: Classes and Extensions print("\n--- Additional Tests: Classes and Extensions ---"); -var path1 = fileExists("tests/test_method_calls.bob") ? "tests/test_method_calls.bob" : "../tests/test_method_calls.bob"; -evalFile(path1); -var path2 = fileExists("tests/test_class_basic.bob") ? "tests/test_class_basic.bob" : "../tests/test_class_basic.bob"; -evalFile(path2); -var path3 = fileExists("tests/test_class_with_this.bob") ? "tests/test_class_with_this.bob" : "../tests/test_class_with_this.bob"; -evalFile(path3); -var path4 = fileExists("tests/test_class_init.bob") ? "tests/test_class_init.bob" : "../tests/test_class_init.bob"; -evalFile(path4); -var path5 = fileExists("tests/test_class_extension_user.bob") ? "tests/test_class_extension_user.bob" : "../tests/test_class_extension_user.bob"; -evalFile(path5); -var path6 = fileExists("tests/test_extension_methods.bob") ? "tests/test_extension_methods.bob" : "../tests/test_extension_methods.bob"; -evalFile(path6); -var path7 = fileExists("tests/test_class_inheritance.bob") ? "tests/test_class_inheritance.bob" : "../tests/test_class_inheritance.bob"; -evalFile(path7); -var path8 = fileExists("tests/test_classes_comprehensive.bob") ? "tests/test_classes_comprehensive.bob" : "../tests/test_classes_comprehensive.bob"; -evalFile(path8); -var path9 = fileExists("tests/test_class_super.bob") ? "tests/test_class_super.bob" : "../tests/test_class_super.bob"; -evalFile(path9); -var path10 = fileExists("tests/test_classes_extensive.bob") ? "tests/test_classes_extensive.bob" : "../tests/test_classes_extensive.bob"; -evalFile(path10); -var path11 = fileExists("tests/test_class_edge_cases.bob") ? "tests/test_class_edge_cases.bob" : "../tests/test_class_edge_cases.bob"; -evalFile(path11); -var path12 = fileExists("tests/test_polymorphism.bob") ? "tests/test_polymorphism.bob" : "../tests/test_polymorphism.bob"; -evalFile(path12); -var path13 = fileExists("tests/test_polymorphism_practical.bob") ? "tests/test_polymorphism_practical.bob" : "../tests/test_polymorphism_practical.bob"; -evalFile(path13); +import io as IO; // for file existence checks +var path1 = IO.exists("tests/test_method_calls.bob") ? "tests/test_method_calls.bob" : "../tests/test_method_calls.bob"; +E.evalFile(path1); +var path2 = IO.exists("tests/test_class_basic.bob") ? "tests/test_class_basic.bob" : "../tests/test_class_basic.bob"; +E.evalFile(path2); +var path3 = IO.exists("tests/test_class_with_this.bob") ? "tests/test_class_with_this.bob" : "../tests/test_class_with_this.bob"; +E.evalFile(path3); +var path4 = IO.exists("tests/test_class_init.bob") ? "tests/test_class_init.bob" : "../tests/test_class_init.bob"; +E.evalFile(path4); +var path5 = IO.exists("tests/test_class_extension_user.bob") ? "tests/test_class_extension_user.bob" : "../tests/test_class_extension_user.bob"; +E.evalFile(path5); +var path6 = IO.exists("tests/test_extension_methods.bob") ? "tests/test_extension_methods.bob" : "../tests/test_extension_methods.bob"; +E.evalFile(path6); +var path7 = IO.exists("tests/test_class_inheritance.bob") ? "tests/test_class_inheritance.bob" : "../tests/test_class_inheritance.bob"; +E.evalFile(path7); +var path8 = IO.exists("tests/test_classes_comprehensive.bob") ? "tests/test_classes_comprehensive.bob" : "../tests/test_classes_comprehensive.bob"; +E.evalFile(path8); +var path9 = IO.exists("tests/test_class_super.bob") ? "tests/test_class_super.bob" : "../tests/test_class_super.bob"; +E.evalFile(path9); +var path10 = IO.exists("tests/test_classes_extensive.bob") ? "tests/test_classes_extensive.bob" : "../tests/test_classes_extensive.bob"; +E.evalFile(path10); +var path11 = IO.exists("tests/test_class_edge_cases.bob") ? "tests/test_class_edge_cases.bob" : "../tests/test_class_edge_cases.bob"; +E.evalFile(path11); +var path12 = IO.exists("tests/test_polymorphism.bob") ? "tests/test_polymorphism.bob" : "../tests/test_polymorphism.bob"; +E.evalFile(path12); +var path13 = IO.exists("tests/test_polymorphism_practical.bob") ? "tests/test_polymorphism_practical.bob" : "../tests/test_polymorphism_practical.bob"; +E.evalFile(path13); -var path14 = fileExists("tests/test_builtin_methods_style.bob") ? "tests/test_builtin_methods_style.bob" : "../tests/test_builtin_methods_style.bob"; -evalFile(path14); +var path14 = IO.exists("tests/test_builtin_methods_style.bob") ? "tests/test_builtin_methods_style.bob" : "../tests/test_builtin_methods_style.bob"; +E.evalFile(path14); -var path15 = fileExists("tests/test_try_catch.bob") ? "tests/test_try_catch.bob" : "../tests/test_try_catch.bob"; -evalFile(path15); -var path15a = fileExists("tests/test_try_catch_runtime.bob") ? "tests/test_try_catch_runtime.bob" : "../tests/test_try_catch_runtime.bob"; -evalFile(path15a); -var path15b = fileExists("tests/test_try_catch_extensive.bob") ? "tests/test_try_catch_extensive.bob" : "../tests/test_try_catch_extensive.bob"; -evalFile(path15b); -var path15c = fileExists("tests/test_try_catch_edge_cases2.bob") ? "tests/test_try_catch_edge_cases2.bob" : "../tests/test_try_catch_edge_cases2.bob"; -evalFile(path15c); -var path15d = fileExists("tests/test_try_catch_cross_function.bob") ? "tests/test_try_catch_cross_function.bob" : "../tests/test_try_catch_cross_function.bob"; -evalFile(path15d); -var path15e = fileExists("tests/test_try_catch_loop_interactions.bob") ? "tests/test_try_catch_loop_interactions.bob" : "../tests/test_try_catch_loop_interactions.bob"; -evalFile(path15e); +var path15 = IO.exists("tests/test_try_catch.bob") ? "tests/test_try_catch.bob" : "../tests/test_try_catch.bob"; +E.evalFile(path15); +var path15a = IO.exists("tests/test_try_catch_runtime.bob") ? "tests/test_try_catch_runtime.bob" : "../tests/test_try_catch_runtime.bob"; +E.evalFile(path15a); +var path15b = IO.exists("tests/test_try_catch_extensive.bob") ? "tests/test_try_catch_extensive.bob" : "../tests/test_try_catch_extensive.bob"; +E.evalFile(path15b); +var path15c = IO.exists("tests/test_try_catch_edge_cases2.bob") ? "tests/test_try_catch_edge_cases2.bob" : "../tests/test_try_catch_edge_cases2.bob"; +E.evalFile(path15c); +var path15d = IO.exists("tests/test_try_catch_cross_function.bob") ? "tests/test_try_catch_cross_function.bob" : "../tests/test_try_catch_cross_function.bob"; +E.evalFile(path15d); +var path15e = IO.exists("tests/test_try_catch_loop_interactions.bob") ? "tests/test_try_catch_loop_interactions.bob" : "../tests/test_try_catch_loop_interactions.bob"; +E.evalFile(path15e); // Modules: basic imports suite -var pathMods = fileExists("tests/test_imports_basic.bob") ? "tests/test_imports_basic.bob" : "../tests/test_imports_basic.bob"; -evalFile(pathMods); +var pathMods = IO.exists("tests/test_imports_basic.bob") ? "tests/test_imports_basic.bob" : "../tests/test_imports_basic.bob"; +E.evalFile(pathMods); -var pathModsB = fileExists("tests/test_imports_builtin.bob") ? "tests/test_imports_builtin.bob" : "../tests/test_imports_builtin.bob"; -evalFile(pathModsB); +var pathModsB = IO.exists("tests/test_imports_builtin.bob") ? "tests/test_imports_builtin.bob" : "../tests/test_imports_builtin.bob"; +E.evalFile(pathModsB); -var pathOs = fileExists("tests/test_os_basic.bob") ? "tests/test_os_basic.bob" : "../tests/test_os_basic.bob"; -evalFile(pathOs); +var pathOs = IO.exists("tests/test_os_basic.bob") ? "tests/test_os_basic.bob" : "../tests/test_os_basic.bob"; +E.evalFile(pathOs); print("\nAll tests passed."); print("Test suite complete."); \ No newline at end of file diff --git a/tests/test_base64.bob b/tests/test_base64.bob new file mode 100644 index 0000000..ff0e1a8 --- /dev/null +++ b/tests/test_base64.bob @@ -0,0 +1,11 @@ +print("\n--- Test: base64 module ---"); +import base64; + +var s = "hello world"; +var enc = base64.encode(s); +assert(enc == "aGVsbG8gd29ybGQ=", "base64.encode"); +var dec = base64.decode(enc); +assert(dec == s, "base64.decode roundtrip"); +print("base64: PASS"); + + diff --git a/tests/test_hash.bob b/tests/test_hash.bob new file mode 100644 index 0000000..7674c11 --- /dev/null +++ b/tests/test_hash.bob @@ -0,0 +1,5 @@ +print("\n--- Test: hash module ---"); +// Placeholder: implement when hash module is added +print("hash: SKIPPED"); + + diff --git a/tests/test_imports_basic.bob b/tests/test_imports_basic.bob index a098653..e08cf72 100644 --- a/tests/test_imports_basic.bob +++ b/tests/test_imports_basic.bob @@ -28,7 +28,8 @@ var after = mod_hello.X; assert(before == after, "module executed once (cached)"); // Cross-file visibility in same interpreter: eval user file after importing here -evalFile("tests/import_user_of_mod_hello.bob"); +import eval as E; +E.evalFile("tests/import_user_of_mod_hello.bob"); // Immutability: cannot reassign module binding var immFail = false; diff --git a/tests/test_math.bob b/tests/test_math.bob new file mode 100644 index 0000000..3e43113 --- /dev/null +++ b/tests/test_math.bob @@ -0,0 +1,15 @@ +print("\n--- Test: math module ---"); +import math; + +assert(math.sqrt(9) == 3, "math.sqrt"); +assert(math.round(3.6) == 4, "math.round"); +assert(math.floor(3.6) == 3, "math.floor"); +assert(math.ceil(3.1) == 4, "math.ceil"); +assert(math.abs(-5) == 5, "math.abs"); +assert(math.pow(2,8) == 256, "math.pow"); +assert(math.min(1, 2, 3) == 1, "math.min"); +assert(math.max(1, 2, 3) == 3, "math.max"); +assert(math.pi > 3.14 && math.pi < 3.15, "math.pi range"); +print("math: PASS"); + + diff --git a/tests/test_path.bob b/tests/test_path.bob new file mode 100644 index 0000000..e42a08b --- /dev/null +++ b/tests/test_path.bob @@ -0,0 +1,15 @@ +print("\n--- Test: path module ---"); +import path; + +assert(path.join("a","b","c.txt") == "a/b/c.txt", "path.join"); +assert(path.dirname("/a/b/c.txt") == "/a/b", "path.dirname"); +assert(path.basename("/a/b/c.txt") == "c.txt", "path.basename"); +var sp = path.splitext("c.txt"); +assert(sp[0] == "c" && sp[1] == ".txt", "path.splitext"); +assert(path.normalize("a/./b/../c") == "a/c", "path.normalize"); +assert(path.isabs("/a/b") == true, "path.isabs"); +var rp = path.relpath("/a/b/c", "/a"); +assert(rp == "b/c" || rp == "../a/b/c", "path.relpath platform tolerance"); +print("path: PASS"); + + diff --git a/tests/test_rand.bob b/tests/test_rand.bob new file mode 100644 index 0000000..c84c761 --- /dev/null +++ b/tests/test_rand.bob @@ -0,0 +1,14 @@ +print("\n--- Test: rand module ---"); +import rand; + +rand.seed(1234); +var r1 = rand.random(); +var r2 = rand.random(); +assert(r1 >= 0 && r1 < 1 && r2 >= 0 && r2 < 1, "rand.random range"); +var i = rand.randint(1, 10); +assert(i >= 1 && i <= 10, "rand.randint range"); +var choice = rand.choice(["a","b","c"]); +assert(choice == "a" || choice == "b" || choice == "c", "rand.choice"); +print("rand: PASS"); + + diff --git a/tests/test_re.bob b/tests/test_re.bob new file mode 100644 index 0000000..ba0d192 --- /dev/null +++ b/tests/test_re.bob @@ -0,0 +1,5 @@ +print("\n--- Test: re module ---"); +// Placeholder: implement when re module is added +print("re: SKIPPED"); + + diff --git a/tests/test_time.bob b/tests/test_time.bob new file mode 100644 index 0000000..00aefd6 --- /dev/null +++ b/tests/test_time.bob @@ -0,0 +1,14 @@ +print("\n--- Test: time module ---"); +import time; + +var t1 = time.now(); +var t2 = time.now(); +assert(t2 >= t1, "time.now monotonic-ish"); + +var m1 = time.monotonic(); +time.sleep(0.01); +var m2 = time.monotonic(); +assert(m2 > m1, "time.monotonic increases"); +print("time: PASS"); + + diff --git a/tests/test_try_catch_loop_interactions.bob b/tests/test_try_catch_loop_interactions.bob index 686c762..d202ffc 100644 --- a/tests/test_try_catch_loop_interactions.bob +++ b/tests/test_try_catch_loop_interactions.bob @@ -15,7 +15,7 @@ while (true) { } assert(i == 2 && seq.len() == 2, "continue/break in finally"); -// Throw in loop body caught outside +// Throw in loop body caught and handled without extra reporter noise var hits = []; for (var j = 0; j < 3; j = j + 1) { try { @@ -25,7 +25,11 @@ for (var j = 0; j < 3; j = j + 1) { hits.push("caught:" + e.message); } } -assert(hits.len() == 4 && hits[1] == "caught:bang", "loop catch per-iteration"); +// Strictly check sequence; avoid out-of-bounds checks +assert(hits.len() == 3, "loop catch sequence length"); +assert(hits[0] == 0, "loop catch sequence item0"); +assert(hits[1] == "caught:bang", "loop catch sequence item1"); +assert(hits[2] == 2, "loop catch sequence item2"); print("try/catch with loops: PASS"); diff --git a/tests/test_uuid.bob b/tests/test_uuid.bob new file mode 100644 index 0000000..8c0c7df --- /dev/null +++ b/tests/test_uuid.bob @@ -0,0 +1,5 @@ +print("\n--- Test: uuid module ---"); +// Placeholder: implement when uuid module is added +print("uuid: SKIPPED"); + +