Updated tests and testing built in modules

This commit is contained in:
Bobby Lucero 2025-08-12 16:50:54 -04:00
parent 7f7c6e438d
commit 6e3379b5b8
20 changed files with 241 additions and 172 deletions

View File

@ -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

View File

@ -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()`

View File

@ -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`)

View File

@ -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!');");

View File

@ -300,7 +300,7 @@
"Random Number": {
"prefix": "random",
"body": [
"var randomValue = random();"
"import rand; var randomValue = rand.random();"
],
"description": "Generate random number"
},

View File

@ -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({

View File

@ -27,7 +27,9 @@ void registerPathModule(Interpreter& interpreter) {
m.fn("splitext", [](std::vector<Value> 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>{ 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>{ Value(basePath.generic_string()), Value(ext) });
});
m.fn("normalize", [](std::vector<Value> a, int, int) -> Value {
if (a.size()!=1 || !a[0].isString()) return NONE_VALUE;

View File

@ -8,6 +8,18 @@
#include <cstring>
#include <vector>
// Platform-specific includes for memoryUsage()
#if defined(__APPLE__) && defined(__MACH__)
#include <mach/mach.h>
#elif defined(__linux__)
#include <fstream>
#include <sstream>
#elif defined(_WIN32)
#define NOMINMAX
#include <windows.h>
#include <psapi.h>
#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<Value> 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<double>(memoryBytes) / (1024.0 * 1024.0);
return Value(memoryMB);
});
m.fn("exit", [](std::vector<Value> a, int, int) -> Value {
int code = 0; if (!a.empty() && a[0].isNumber()) code = static_cast<int>(a[0].asNumber());
std::exit(code);

View File

@ -290,24 +290,7 @@ void BobStdLib::addToEnvironment(std::shared_ptr<Environment> 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<BuiltinFunction>("exit",
[](std::vector<Value> args, int line, int column) -> Value {
int exitCode = 0; // Default exit code
if (args.size() > 0) {
if (args[0].isNumber()) {
exitCode = static_cast<int>(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<Environment> env, Interpreter&
env->define("functions", Value(functionsFunc));
interpreter.addBuiltinFunction(functionsFunc);
// Create a built-in random function
auto randomFunc = std::make_shared<BuiltinFunction>("random",
[errorReporter](std::vector<Value> 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<unsigned int>(time(nullptr)));
seeded = true;
}
return Value(static_cast<double>(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<Environment> env, Interpreter&
// (file I/O moved to io module)
// Create a built-in memoryUsage function (platform-specific, best effort)
auto memoryUsageFunc = std::make_shared<BuiltinFunction>("memoryUsage",
[errorReporter](std::vector<Value> 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<double>(memoryBytes) / (1024.0 * 1024.0);
return Value(memoryMB);
});
env->define("memoryUsage", Value(memoryUsageFunc));
interpreter.addBuiltinFunction(memoryUsageFunc);
// memoryUsage moved to sys module
}

View File

@ -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.");

11
tests/test_base64.bob Normal file
View File

@ -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");

5
tests/test_hash.bob Normal file
View File

@ -0,0 +1,5 @@
print("\n--- Test: hash module ---");
// Placeholder: implement when hash module is added
print("hash: SKIPPED");

View File

@ -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;

15
tests/test_math.bob Normal file
View File

@ -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");

15
tests/test_path.bob Normal file
View File

@ -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");

14
tests/test_rand.bob Normal file
View File

@ -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");

5
tests/test_re.bob Normal file
View File

@ -0,0 +1,5 @@
print("\n--- Test: re module ---");
// Placeholder: implement when re module is added
print("re: SKIPPED");

14
tests/test_time.bob Normal file
View File

@ -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");

View File

@ -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");

5
tests/test_uuid.bob Normal file
View File

@ -0,0 +1,5 @@
print("\n--- Test: uuid module ---");
// Placeholder: implement when uuid module is added
print("uuid: SKIPPED");