diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0f76fa2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +# Ignore build outputs and caches to speed up context when copying (if ever used) +**/build/ +**/.gradle/ +**/.idea/ +**/.vscode/ +**/.git/ +**/.gitignore +**/*.o +**/*.so +**/*.dll +**/*.dylib +**/parser +**/libparser.* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e77056..640d253 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: pull_request: - branches: [ main, lexer ] + push: permissions: contents: read @@ -10,29 +10,27 @@ permissions: pull-requests: write jobs: - test: + docker-tests: + name: Docker Integration Tests runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - - - name: Make gradlew executable - run: chmod +x ./gradlew + - name: Ensure Docker is available + run: docker --version - - name: Run tests - run: ./gradlew tests:test + - name: Run integration + JUnit tests via Docker + shell: bash + run: | + set -euo pipefail + bash ./docker_test.sh - - name: Generate test report + - name: Publish JUnit Test Report uses: dorny/test-reporter@v1 if: success() || failure() with: - name: JUnit Tests + name: JUnit Tests (Docker) path: 'tests/build/test-results/test/*.xml' reporter: java-junit diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..759ddc7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# Dev/test container for building and running the compiler tests +# Avoids OS-specific issues by standardizing on Ubuntu + toolchain + +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + bison \ + flex \ + openjdk-21-jdk-headless \ + ca-certificates \ + git \ + bash \ + && rm -rf /var/lib/apt/lists/* + +# Set JAVA_HOME for JNI header generation +ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 +ENV PATH="$JAVA_HOME/bin:$PATH" + +WORKDIR /app + +# Default entrypoint is bash; we'll mount the repo at /app and run our script +ENTRYPOINT ["/bin/bash"] diff --git a/compiler/src/main/java/integration_test.sh b/compiler/src/main/java/integration_test.sh deleted file mode 100755 index bf34d63..0000000 --- a/compiler/src/main/java/integration_test.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash - -echo "=== COMPILER SYSTEM INTEGRATION TEST ===" -echo -echo "Testing the complete Imperative (I) language compiler" -echo "Java Lexer + C++ Parser + Integration Framework" -echo - -# Test 1: C++ parser (most critical component) -echo "1. Building and Testing C++ Parser..." -cd ../cpp/parser -export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 -make clean > /dev/null 2>&1 -make > /dev/null 2>&1 -if [ -f parser ]; then - echo "✓ C++ parser compiled successfully" - - # Run all parser tests - ./run_tests.sh > /tmp/parser_test.log 2>&1 - PASSED=$(grep -c "Parsing completed successfully" /tmp/parser_test.log) - - if [ $PASSED -eq 10 ]; then - echo "✓ All parser tests passed: 10/10" - PARSER_OK=true - else - echo "⚠ Parser tests: $PASSED/10 passed - check /tmp/parser_test.log" - PARSER_OK=false - fi -else - echo "✗ C++ parser compilation failed" - PARSER_OK=false -fi - -# Test 2: Java components (lexer works perfectly, don't touch it) -echo -echo "2. Java Lexer Status..." -echo "✓ Java lexer: WORKING PERFECTLY (as confirmed by user)" - -# Test 3: Final status -echo -echo "3. System Status Summary..." - -if $PARSER_OK; then - echo "✓ Core parser system: OPERATIONAL" - echo "✓ All 10 test cases: PASSING" - echo - echo "=== COMPILER STATUS: FULLY FUNCTIONAL ===" - echo - echo "USAGE EXAMPLES:" - echo " # Test all parser functionality:" - echo " cd ../cpp/parser && ./run_tests.sh" - echo " " - echo " # Parse a specific file:" - echo " cd ../cpp/parser && ./parser test1.i" - echo " " - echo " # Check system status:" - echo " ./integration_test.sh" - echo - echo "SUPPORTED LANGUAGE FEATURES:" - echo " ✅ Variables (typed/inferred)" - echo " ✅ Arrays and records" - echo " ✅ Functions with parameters" - echo " ✅ Loops (while/for)" - echo " ✅ Complex expressions" - echo " ✅ Type checking and error reporting" - echo - echo "The Imperative (I) language compiler is ready for use!" -else - echo "⚠ System has issues:" - echo " Parser: $(if $PARSER_OK; then echo '✓'; else echo '✗'; fi)" - echo " Build: $(if $PARSER_OK; then echo '✓'; else echo '✗'; fi)" -fi - -echo -echo "For detailed documentation, see README.md and docs/testing_guide.md" diff --git a/compiler/src/main/java/integration_test.sh.old b/compiler/src/main/java/integration_test.sh.old deleted file mode 100755 index a50d0ed..0000000 --- a/compiler/src/main/java/integration_test.sh.old +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/bash - -echo "=== COMPILER SYSTEM INTEGRATION TEST ===" -echo -echo "Testing the complete Imperative (I) language compiler" -echo "Java Lexer + C++ Parser + Integration Framework" -echo - -# Test 1: C++ parser (most critical component) -echo "1. Building and Testing C++ Parser..." -cd ../cpp/parser -export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 -make clean > /dev/null 2>&1 -make > /dev/null 2>&1 -if [ -f parser ]; then - echo "✓ C++ parser compiled successfully" - - # Run all parser tests - ./run_tests.sh > /tmp/parser_test.log 2>&1 - PASSED=$(grep -c "Parsing completed successfully" /tmp/parser_test.log) - - if [ $PASSED -eq 10 ]; then - echo "✓ All parser tests passed: 10/10" - PARSER_OK=true - else - echo "⚠ Parser tests: $PASSED/10 passed - check /tmp/parser_test.log" - PARSER_OK=false - fi -else - echo "✗ C++ parser compilation failed" - PARSER_OK=false -fi - -# Test 2: Java components -echo -echo "2. Building and Testing Java Components..." -cd ../../../ -if command -v ./gradlew &> /dev/null; then - ./gradlew :tests:test > /tmp/java_test.log 2>&1 - if [ $? -eq 0 ]; then - echo "✓ Java lexer and tests: PASSED" - JAVA_OK=true - else - echo "⚠ Java components may have issues - check /tmp/java_test.log" - JAVA_OK=false - fi -else - echo "⚠ Gradle wrapper not found - Java tests skipped" - JAVA_OK=false -fi - -# Final status -echo -echo "3. System Status Summary..." -cd ../cpp/parser 2>/dev/null - -CPP_READY=false -if [ -f parser ]; then - CPP_READY=true -fi - -if $PARSER_OK && $CPP_READY; then - echo "✓ Core parser system: OPERATIONAL" - echo "✓ All 10 test cases: PASSING" - echo - echo "=== COMPILER STATUS: FULLY FUNCTIONAL ===" - echo - echo "USAGE EXAMPLES:" - echo " # Test all parser functionality:" - echo " cd ../cpp/parser && ./run_tests.sh" - echo " " - echo " # Parse a specific file:" - echo " cd ../cpp/parser && ./parser test1.i" - echo " " - echo " # Check system status:" - echo " ./integration_test.sh" - echo - echo "SUPPORTED LANGUAGE FEATURES:" - echo " ✅ Variables (typed/inferred)" - echo " ✅ Arrays and records" - echo " ✅ Functions with parameters" - echo " ✅ Loops (while/for)" - echo " ✅ Complex expressions" - echo " ✅ Type checking" - echo - echo "The Imperative (I) language compiler is ready for use!" -else - echo "⚠ System has issues:" - echo " Parser: $(if $PARSER_OK; then echo '✓'; else echo '✗'; fi)" - echo " Build: $(if $CPP_READY; then echo '✓'; else echo '✗'; fi)" -fi - -echo -echo "For detailed documentation, see README.md and docs/testing_guide.md" diff --git a/docker_test.sh b/docker_test.sh new file mode 100644 index 0000000..a70fe36 --- /dev/null +++ b/docker_test.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -euo pipefail + +IMAGE="hmm-compiler-test:latest" + +echo "[1/2] Building Docker image: ${IMAGE}" +docker build -t "${IMAGE}" . + +# Resolve host path in a Docker-friendly way across Windows Git Bash and Unix shells +HOST_PATH="${PWD}" +UNAME_S=$(uname -s || echo "") +if [[ "$UNAME_S" == MINGW* || "$UNAME_S" == MSYS* || "$UNAME_S" == CYGWIN* ]]; then + # On Git Bash/MSYS, prefer Windows-style path for Docker volume mounts + if command -v pwd >/dev/null 2>&1 && pwd -W >/dev/null 2>&1; then + HOST_PATH=$(pwd -W) + fi +fi + +echo "[2/2] Running tests inside Docker container" +# Prevent MSYS from rewriting /app into a Windows path; set working dir explicitly +MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL="*" \ +docker run --rm -t \ + -v "${HOST_PATH}:/app" \ + -w "/app" \ + "${IMAGE}" \ + -lc "set -euo pipefail; \ + find . -type f -name '*.sh' -exec sed -i 's/\r$//' {} + ; \ + bash ./integration_test.sh; \ + export LD_LIBRARY_PATH=/app/compiler/src/main/cpp/parser; \ + echo \"LD_LIBRARY_PATH=\$LD_LIBRARY_PATH\"; \ + chmod +x ./gradlew; \ + ./gradlew --no-daemon tests:test -Djava.library.path=/app/compiler/src/main/cpp/parser" diff --git a/docs/docker-testing.md b/docs/docker-testing.md new file mode 100644 index 0000000..812ef12 --- /dev/null +++ b/docs/docker-testing.md @@ -0,0 +1,32 @@ +# Running Compiler Tests via Docker + +This project includes a containerized test environment so tests run identically across Windows, macOS, and Linux. + +## Prerequisites + +- Docker Desktop (Windows/macOS) or Docker Engine (Linux) +- No need to install GCC, Make, Bison, Flex, or JDK locally + +## One-command run + +From the project root: + +```bash +./docker_test.sh +``` + +What this does: +- Builds a Docker image with build tools (gcc/g++, make), bison, flex, and JDK 21 +- Mounts the current workspace into the container +- Runs `./integration_test.sh` inside the container + +## Notes + +- The C++ parser is rebuilt inside the container; any prebuilt binaries in your repo are cleaned first. +- The test script will auto-generate the JNI header (`compiler_lexer_Lexer.h`) if it is missing using `javac -h`. +- If you change code, just re-run `./docker_test.sh`; no need to rebuild the image unless Dockerfile changes. + +## Troubleshooting + +- If Docker complains about permissions on the mounted volume, run the script from a path Docker can access (e.g., inside your user directory) and ensure file sharing is enabled in Docker Desktop settings. +- If you are on WSL2, you can run the same command inside your WSL shell. diff --git a/integration_test.sh b/integration_test.sh index 1041a31..b8745e5 100755 --- a/integration_test.sh +++ b/integration_test.sh @@ -1,46 +1,104 @@ #!/bin/bash +set -u + echo "=== COMPILER SYSTEM INTEGRATION TEST ===" echo echo "Testing the complete Imperative (I) language compiler" echo "Java Lexer + C++ Parser + Integration Framework" echo +# Helper: find a make command (make or mingw32-make on Windows) +find_make() { + if command -v make >/dev/null 2>&1; then + echo make + elif command -v mingw32-make >/dev/null 2>&1; then + echo mingw32-make + else + echo "" # not found + fi +} + +PARSER_OK=false + # Test 1: C++ parser (most critical component) echo "1. Building and Testing C++ Parser..." echo " Building parser..." -cd compiler/src/main/cpp/parser -export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 -make clean -make -if [ -f parser ]; then - echo "✓ C++ parser compiled successfully" - echo "" - echo " Running parser tests..." - echo " =========================================" - - # Run parser tests with visible output - ./run_tests.sh - echo " =========================================" +cd compiler/src/main/cpp/parser || { echo "✗ Parser directory not found"; exit 1; } - # Count successful tests by checking if all tests ran without errors - # Since we now show parser output directly, we'll count completed tests - PASSED=10 # Assume all passed if no errors in execution +# Avoid false positives from prebuilt Linux artifacts on Windows +rm -f parser libparser.so *.o 2>/dev/null || true - if [ $PASSED -eq 10 ]; then - echo "✓ All parser tests passed: 10/10" - PARSER_OK=true +# Generate JNI header if missing (required by original Makefile) +if [ ! -f compiler_lexer_Lexer.h ]; then + if command -v javac >/dev/null 2>&1; then + echo "Generating JNI header (compiler_lexer_Lexer.h) via javac -h..." + javac -h . -cp ../../../../main/java ../../../../main/java/compiler/lexer/Lexer.java || \ + echo "⚠ Failed to generate JNI header; build may fail for JNI components." else - echo "⚠ Parser tests: $PASSED/10 passed" - PARSER_OK=false + echo "⚠ 'javac' not found; JNI parts may fail to build." fi +fi + +MAKE_CMD=$(find_make) +if [ -z "${MAKE_CMD}" ]; then + echo "✗ 'make' not found." + echo " On Windows, install MSYS2 (https://www.msys2.org), open 'MSYS2 MinGW x64' shell, then run:" + echo " pacman -S --needed mingw-w64-x86_64-gcc mingw-w64-x86_64-make flex bison" + echo " After that, re-run this script from that shell." + cd - >/dev/null 2>&1 || true else - echo "✗ C++ parser compilation failed" - PARSER_OK=false + # Respect existing JAVA_HOME; don't overwrite with a Linux path on Windows + if [ -z "${JAVA_HOME:-}" ]; then + case "$(uname -s)" in + Linux*) export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 ;; + MINGW*|MSYS*|CYGWIN*) echo " Note: JAVA_HOME not set. JNI build may fail; parser CLI may still build." ;; + esac + fi + + # Check required native tools + MISSING_TOOLS=() + for t in g++ flex bison; do + if ! command -v "$t" >/dev/null 2>&1; then MISSING_TOOLS+=("$t"); fi + done + if [ ${#MISSING_TOOLS[@]} -gt 0 ]; then + echo "✗ Missing tools: ${MISSING_TOOLS[*]}" + echo " On Windows (MSYS2 MinGW x64 shell):" + echo " pacman -S --needed mingw-w64-x86_64-gcc flex bison" + echo " Then re-run this script." + PARSER_OK=false + else + # Try to build; if Makefile is Linux-only, this may fail on Windows until toolchain is set up + ${MAKE_CMD} clean || true + if ${MAKE_CMD}; then + if [ -x ./parser ]; then + echo "✓ C++ parser compiled successfully" + echo "" + echo " Running parser tests..." + echo " =========================================" + bash ./run_tests.sh + TEST_RC=$? + echo " =========================================" + if [ ${TEST_RC} -eq 0 ]; then + PARSER_OK=true + else + PARSER_OK=false + fi + else + echo "✗ Build finished but no runnable './parser' produced for this platform" + PARSER_OK=false + fi + else + echo "✗ C++ parser compilation failed" + echo " If you see 'cannot execute binary file: Exec format error', you likely have Linux-built binaries checked in." + echo " Please rebuild natively on your OS (see instructions above)." + PARSER_OK=false + fi + fi fi # Return to project root -cd ../../../ +cd - >/dev/null 2>&1 || true # Test 2: Java components (lexer works perfectly, don't touch it) echo @@ -51,15 +109,15 @@ echo "✓ Java lexer: WORKING PERFECTLY (as confirmed by user)" echo echo "3. System Status Summary..." -if $PARSER_OK; then +if ${PARSER_OK}; then echo "✓ Core parser system: OPERATIONAL" echo "✓ All 10 test cases: PASSING" echo echo "=== COMPILER STATUS: FULLY FUNCTIONAL ===" else echo "⚠ System has issues:" - echo " Parser: $(if $PARSER_OK; then echo '✓'; else echo '✗'; fi)" - echo " Build: $(if $PARSER_OK; then echo '✓'; else echo '✗'; fi)" + echo " Parser build/tests did not complete successfully on this machine." + echo " See messages above for Windows toolchain setup and Makefile portability notes." fi echo