feat: bux test command + Std::Test module
- Add 'bux test' CLI command: discovers .bux files in tests/, compiles each into a temp package, runs them, reports pass/fail - Add Std::Test module with Assert, AssertEqInt, AssertTrue, AssertFalse, Fail - Add bux_exit and bux_assert C runtime primitives - Update README.md and docs/BuildAndTest.md
This commit is contained in:
@@ -198,7 +198,7 @@ func Main() -> int {
|
|||||||
| **CTFE** | `const func` — compile-time function execution |
|
| **CTFE** | `const func` — compile-time function execution |
|
||||||
| **Trait Bounds** | `func Max<T: Comparable>(a: T, b: T) -> T` |
|
| **Trait Bounds** | `func Max<T: Comparable>(a: T, b: T) -> T` |
|
||||||
| **Package Manager** | `bux add`, `bux install`, `bux.lock`, path + git deps |
|
| **Package Manager** | `bux add`, `bux install`, `bux.lock`, path + git deps |
|
||||||
| **Tooling** | `bux new`, `bux build`, `bux run`, `bux check` |
|
| **Tooling** | `bux new`, `bux build`, `bux run`, `bux test`, `bux check` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ Commands:
|
|||||||
install Resolve and install dependencies
|
install Resolve and install dependencies
|
||||||
build Build the current package
|
build Build the current package
|
||||||
run Build and run the current package
|
run Build and run the current package
|
||||||
|
test Run tests in tests/ directory
|
||||||
check Type-check the current package
|
check Type-check the current package
|
||||||
clean Remove build artifacts
|
clean Remove build artifacts
|
||||||
help Show this help message
|
help Show this help message
|
||||||
@@ -574,6 +575,46 @@ proc cmdClean*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
printInfo("clean: build directory removed", useColor)
|
printInfo("clean: build directory removed", useColor)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
proc cmdTest*(args: seq[string], opts: GlobalOptions): int =
|
||||||
|
let useColor = shouldUseColor(opts)
|
||||||
|
let root = getCurrentDir()
|
||||||
|
let testsDir = root / "tests"
|
||||||
|
var testFiles: seq[string] = @[]
|
||||||
|
if dirExists(testsDir):
|
||||||
|
for kind, path in walkDir(testsDir):
|
||||||
|
if kind == pcFile and path.endsWith(".bux"):
|
||||||
|
testFiles.add(path)
|
||||||
|
if testFiles.len == 0:
|
||||||
|
printError("no tests found in tests/ directory", useColor)
|
||||||
|
return 1
|
||||||
|
var passed = 0
|
||||||
|
var failed = 0
|
||||||
|
for testFile in testFiles:
|
||||||
|
let testName = splitFile(testFile).name
|
||||||
|
let tmpDir = getTempDir() / "bux_test_" & testName
|
||||||
|
removeDir(tmpDir)
|
||||||
|
createDir(tmpDir / "src")
|
||||||
|
copyFile(testFile, tmpDir / "src" / "Main.bux")
|
||||||
|
writeFile(tmpDir / "bux.toml", "[package]\nname = \"" & testName & "\"\nversion = \"0.1.0\"\n")
|
||||||
|
let buildRes = cmdBuild(@[tmpDir], opts)
|
||||||
|
if buildRes != 0:
|
||||||
|
printError(&" FAIL {testName} (build)", useColor)
|
||||||
|
failed += 1
|
||||||
|
continue
|
||||||
|
var execFile = tmpDir / "build" / testName
|
||||||
|
if not fileExists(execFile):
|
||||||
|
execFile = tmpDir / "build" / "bux_out"
|
||||||
|
let exitCode = execCmd(execFile)
|
||||||
|
if exitCode == 0:
|
||||||
|
printInfo(&" PASS {testName}", useColor)
|
||||||
|
passed += 1
|
||||||
|
else:
|
||||||
|
printError(&" FAIL {testName} (exit {exitCode})", useColor)
|
||||||
|
failed += 1
|
||||||
|
removeDir(tmpDir)
|
||||||
|
echo &"\nResults: {passed} passed, {failed} failed"
|
||||||
|
return if failed > 0: 1 else: 0
|
||||||
|
|
||||||
proc cmdVersion*(args: seq[string], opts: GlobalOptions): int =
|
proc cmdVersion*(args: seq[string], opts: GlobalOptions): int =
|
||||||
echo "bux 0.1.0 (bootstrap)"
|
echo "bux 0.1.0 (bootstrap)"
|
||||||
return 0
|
return 0
|
||||||
@@ -597,6 +638,7 @@ proc runCli*(args: seq[string]): int =
|
|||||||
of "build": return cmdBuild(cmdArgs, opts)
|
of "build": return cmdBuild(cmdArgs, opts)
|
||||||
of "run": return cmdRun(cmdArgs, opts)
|
of "run": return cmdRun(cmdArgs, opts)
|
||||||
of "check": return cmdCheck(cmdArgs, opts)
|
of "check": return cmdCheck(cmdArgs, opts)
|
||||||
|
of "test": return cmdTest(cmdArgs, opts)
|
||||||
of "clean": return cmdClean(cmdArgs, opts)
|
of "clean": return cmdClean(cmdArgs, opts)
|
||||||
of "version", "--version", "-v": return cmdVersion(cmdArgs, opts)
|
of "version", "--version", "-v": return cmdVersion(cmdArgs, opts)
|
||||||
of "help", "--help", "-h":
|
of "help", "--help", "-h":
|
||||||
|
|||||||
@@ -89,6 +89,9 @@ Output = "Bin"
|
|||||||
./buxc run
|
./buxc run
|
||||||
./buxc run ./myproject
|
./buxc run ./myproject
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
./buxc test
|
||||||
|
|
||||||
# Clean build artifacts
|
# Clean build artifacts
|
||||||
./buxc clean
|
./buxc clean
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1324,6 +1324,20 @@ const char* bux_socket_error(void) {
|
|||||||
return strerror(errno);
|
return strerror(errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Test / Assert primitives
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
void bux_exit(int code) {
|
||||||
|
exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bux_assert(int cond, const char* file, int line, const char* expr) {
|
||||||
|
if (!cond) {
|
||||||
|
fprintf(stderr, "ASSERT FAILED: %s at %s:%d\n", expr, file, line);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
* Cryptography primitives (OpenSSL)
|
* Cryptography primitives (OpenSSL)
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
module Std::Test {
|
||||||
|
|
||||||
|
extern func bux_exit(code: int);
|
||||||
|
extern func bux_assert(cond: int, file: String, line: int, expr: String);
|
||||||
|
|
||||||
|
func Test_Exit(code: int) {
|
||||||
|
bux_exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Assert(cond: bool) {
|
||||||
|
bux_assert(cond as int, "", 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_AssertEqInt(a: int, b: int) {
|
||||||
|
if a != b {
|
||||||
|
PrintLine("ASSERT_EQ FAILED:");
|
||||||
|
PrintInt(a);
|
||||||
|
PrintLine(" != ");
|
||||||
|
PrintInt(b);
|
||||||
|
bux_exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_AssertTrue(cond: bool) {
|
||||||
|
if !cond {
|
||||||
|
PrintLine("ASSERT_TRUE FAILED");
|
||||||
|
bux_exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_AssertFalse(cond: bool) {
|
||||||
|
if cond {
|
||||||
|
PrintLine("ASSERT_FALSE FAILED");
|
||||||
|
bux_exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Fail(msg: String) {
|
||||||
|
PrintLine("FAIL:");
|
||||||
|
PrintLine(msg);
|
||||||
|
bux_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user