feat: add integration tests and lint script for examples
- Add 30 integration tests that verify all examples and projects
parse and type-check correctly
- Add scripts/lint-examples.sh for quick pre-commit validation
- Tests will catch regressions like missing `run ... with {}` syntax
or broken escape sequences
Run with: cargo test example_tests
Or quick check: ./scripts/lint-examples.sh
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
71
scripts/lint-examples.sh
Executable file
71
scripts/lint-examples.sh
Executable file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Lint all Lux example and project files
|
||||||
|
# Run this before committing to catch parse/type errors early
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
LUX="${ROOT_DIR}/target/release/lux"
|
||||||
|
|
||||||
|
# Build if needed
|
||||||
|
if [ ! -f "$LUX" ]; then
|
||||||
|
echo "Building lux..."
|
||||||
|
cd "$ROOT_DIR" && cargo build --release
|
||||||
|
fi
|
||||||
|
|
||||||
|
FAILED=0
|
||||||
|
PASSED=0
|
||||||
|
|
||||||
|
check_file() {
|
||||||
|
local file="$1"
|
||||||
|
local output
|
||||||
|
# Run the file and capture output, timeout after 10s
|
||||||
|
if output=$(timeout 10 "$LUX" "$file" 2>&1); then
|
||||||
|
echo -e "${GREEN}OK${NC}: $file"
|
||||||
|
((PASSED++)) || true
|
||||||
|
else
|
||||||
|
# Check if it's just a runtime error vs parse/type error
|
||||||
|
if echo "$output" | grep -q "Parse error\|Type error\|Type Error\|Type Mismatch"; then
|
||||||
|
echo -e "${RED}FAIL${NC}: $file"
|
||||||
|
echo "$output" | head -20
|
||||||
|
echo ""
|
||||||
|
((FAILED++)) || true
|
||||||
|
else
|
||||||
|
# Runtime errors are OK for linting (file parses and type checks)
|
||||||
|
echo -e "${GREEN}OK${NC}: $file (runtime error expected)"
|
||||||
|
((PASSED++)) || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "=== Linting Lux Examples ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
for file in "$ROOT_DIR"/examples/*.lux; do
|
||||||
|
check_file "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Linting Lux Projects ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
for file in "$ROOT_DIR"/projects/*/main.lux; do
|
||||||
|
check_file "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Summary ==="
|
||||||
|
echo "Passed: $PASSED"
|
||||||
|
echo "Failed: $FAILED"
|
||||||
|
|
||||||
|
if [ $FAILED -gt 0 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "All files pass parse and type checking!"
|
||||||
173
src/main.rs
173
src/main.rs
@@ -2814,4 +2814,177 @@ c")"#;
|
|||||||
assert_eq!(eval(source).unwrap(), "42");
|
assert_eq!(eval(source).unwrap(), "42");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Integration tests for example files
|
||||||
|
mod example_tests {
|
||||||
|
use super::*;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn check_file(path: &str) -> Result<(), String> {
|
||||||
|
let source = fs::read_to_string(path)
|
||||||
|
.map_err(|e| format!("Failed to read {}: {}", path, e))?;
|
||||||
|
|
||||||
|
let program = Parser::parse_source(&source)
|
||||||
|
.map_err(|e| format!("Parse error in {}: {}", path, e))?;
|
||||||
|
|
||||||
|
let mut checker = TypeChecker::new();
|
||||||
|
checker.check_program(&program).map_err(|errors| {
|
||||||
|
format!("Type errors in {}:\n{}", path,
|
||||||
|
errors.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("\n"))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_hello() {
|
||||||
|
check_file("examples/hello.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_factorial() {
|
||||||
|
check_file("examples/factorial.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_functional() {
|
||||||
|
check_file("examples/functional.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_pipelines() {
|
||||||
|
check_file("examples/pipelines.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_tailcall() {
|
||||||
|
check_file("examples/tailcall.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_effects() {
|
||||||
|
check_file("examples/effects.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_handlers() {
|
||||||
|
check_file("examples/handlers.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_builtin_effects() {
|
||||||
|
check_file("examples/builtin_effects.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_datatypes() {
|
||||||
|
check_file("examples/datatypes.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_generics() {
|
||||||
|
check_file("examples/generics.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_traits() {
|
||||||
|
check_file("examples/traits.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_interpolation() {
|
||||||
|
check_file("examples/interpolation.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_behavioral() {
|
||||||
|
check_file("examples/behavioral.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_behavioral_types() {
|
||||||
|
check_file("examples/behavioral_types.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_versioning() {
|
||||||
|
check_file("examples/versioning.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_schema_evolution() {
|
||||||
|
check_file("examples/schema_evolution.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_file_io() {
|
||||||
|
check_file("examples/file_io.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_json() {
|
||||||
|
check_file("examples/json.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_random() {
|
||||||
|
check_file("examples/random.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_statemachine() {
|
||||||
|
check_file("examples/statemachine.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_shell() {
|
||||||
|
check_file("examples/shell.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_http() {
|
||||||
|
check_file("examples/http.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_http_server() {
|
||||||
|
check_file("examples/http_server.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_example_jit_test() {
|
||||||
|
check_file("examples/jit_test.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_project_json_parser() {
|
||||||
|
check_file("projects/json-parser/main.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_project_markdown_converter() {
|
||||||
|
check_file("projects/markdown-converter/main.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_project_todo_app() {
|
||||||
|
check_file("projects/todo-app/main.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_project_mini_interpreter() {
|
||||||
|
check_file("projects/mini-interpreter/main.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_project_guessing_game() {
|
||||||
|
check_file("projects/guessing-game/main.lux").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_project_rest_api() {
|
||||||
|
check_file("projects/rest-api/main.lux").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user