Skip to content

Adding more tests#8

Open
LowLevelLore wants to merge 2 commits into
mainfrom
comprehensive-testing
Open

Adding more tests#8
LowLevelLore wants to merge 2 commits into
mainfrom
comprehensive-testing

Conversation

@LowLevelLore

@LowLevelLore LowLevelLore commented May 30, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

Add tooling and fixtures to expand runtime test coverage across conditionals, functions, loops, operations, strings, types, and variables.

Enhancements:

  • Introduce a Python helper script to compile, run, and regenerate expected outputs for runtime tests.

Tests:

  • Add new runtime test cases and corresponding expected outputs for complex conditionals, loop behaviors, arithmetic and floating‑point operations, strings, types, and variable semantics.
  • Regenerate or update expected exit codes and outputs for existing runtime tests to align with current behavior.

@sourcery-ai

sourcery-ai Bot commented May 30, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR adds a Python helper script to automatically generate expected runtime test outputs and introduces a broad set of new runtime tests (with corresponding expected stdout/exitcode files) for conditionals, functions, loops, operations, strings, types, and variables, while wiring them into the existing expected-results structure.

Sequence diagram for generate_expected_outputs runtime flow

sequenceDiagram
    actor Developer
    participant Script as generate_expected_outputs_py
    participant Zpiler as ZPILER
    participant As as as
    participant Gcc as gcc
    participant Exe as test_executable
    participant FS as Filesystem

    Developer->>Script: main()
    Script->>FS: RUNTIME_DIR.rglob
    loop for each test_file
        Script->>Script: generate_outputs_for_test(test_file)
        Script->>Zpiler: run_command(ZPILER ...)
        alt compile ok
            Script->>As: run_command(as asm_file -o obj_file)
            alt assemble ok
                Script->>Gcc: run_command(gcc obj_file -o exe_file)
                alt link ok
                    Script->>Exe: run_command(exe_file)
                    Script->>FS: write_bytes(.stdout/.stderr/.exitcode)
                else link failed
                    Script-->>Script: return False
                end
            else assemble failed
                Script-->>Script: return False
            end
        else compile failed
            Script-->>Script: return False
        end
    end
    Script->>Developer: print summary of success_count/fail_count
Loading

File-Level Changes

Change Details Files
Add a Python utility to auto-generate expected outputs for all runtime .zz tests.
  • Introduce generate_expected_outputs.py that compiles each runtime test with zpiler, assembles and links it to a native executable, runs it, and captures stdout, stderr, and exit code.
  • Use fixed absolute paths for the compiler binary and tests/expected directories and walk tests/runtime recursively to find .zz files.
  • Write .stdout, .stderr, and .exitcode files under tests/expected/runtime mirroring the runtime test directory structure, with basic success/failure reporting.
generate_expected_outputs.py
Add new runtime tests for conditionals, functions, loops, operations, strings, types, and variables with matching expected outputs.
  • Create new .zz test programs covering boolean logic, complex and nested conditionals, function composition and sequences, various loop shapes (including nesting, control flow, and accumulation), arithmetic and comparison operations (including floating point and mixed types), basic string behavior, type boundaries and enumerations, and variable behaviors like sequential assignment and shadowing.
  • Add corresponding .stdout and .exitcode expected files under tests/expected/runtime mirroring the new test names and directories.
  • Ensure newly added tests integrate with the existing runtime/expected layout so they are picked up by the current test harness.
tests/runtime/conditionals/boolean_logic.zz
tests/runtime/conditionals/complex_bool.zz
tests/runtime/conditionals/nested.zz
tests/runtime/functions/composition.zz
tests/runtime/functions/sequences.zz
tests/runtime/loops/break_continue.zz
tests/runtime/loops/increment_patterns.zz
tests/runtime/loops/increments.zz
tests/runtime/loops/nested.zz
tests/runtime/loops/nested_control.zz
tests/runtime/loops/sums.zz
tests/runtime/loops/with_verify.zz
tests/runtime/operations/basic_comparisons.zz
tests/runtime/operations/factorials.zz
tests/runtime/operations/floating_point.zz
tests/runtime/operations/increment_decrement.zz
tests/runtime/operations/mixed_types.zz
tests/runtime/strings/basic.zz
tests/runtime/types/all_types.zz
tests/runtime/types/boundaries.zz
tests/runtime/variables/sequential_assign.zz
tests/runtime/variables/shadowing.zz
tests/expected/runtime/conditionals/boolean_logic.exitcode
tests/expected/runtime/conditionals/boolean_logic.stdout
tests/expected/runtime/conditionals/complex_bool.exitcode
tests/expected/runtime/conditionals/complex_bool.stdout
tests/expected/runtime/conditionals/nested.exitcode
tests/expected/runtime/conditionals/nested.stdout
tests/expected/runtime/functions/composition.exitcode
tests/expected/runtime/functions/composition.stdout
tests/expected/runtime/functions/sequences.exitcode
tests/expected/runtime/functions/sequences.stdout
tests/expected/runtime/loops/break_continue.exitcode
tests/expected/runtime/loops/break_continue.stdout
tests/expected/runtime/loops/increment_patterns.exitcode
tests/expected/runtime/loops/increment_patterns.stdout
tests/expected/runtime/loops/increments.exitcode
tests/expected/runtime/loops/increments.stdout
tests/expected/runtime/loops/nested.exitcode
tests/expected/runtime/loops/nested.stdout
tests/expected/runtime/loops/nested_control.exitcode
tests/expected/runtime/loops/nested_control.stdout
tests/expected/runtime/loops/sums.exitcode
tests/expected/runtime/loops/sums.stdout
tests/expected/runtime/loops/with_verify.exitcode
tests/expected/runtime/loops/with_verify.stdout
tests/expected/runtime/operations/basic_comparisons.exitcode
tests/expected/runtime/operations/basic_comparisons.stdout
tests/expected/runtime/operations/factorials.exitcode
tests/expected/runtime/operations/factorials.stdout
tests/expected/runtime/operations/floating_point.exitcode
tests/expected/runtime/operations/floating_point.stdout
tests/expected/runtime/operations/increment_decrement.exitcode
tests/expected/runtime/operations/increment_decrement.stdout
tests/expected/runtime/operations/mixed_types.exitcode
tests/expected/runtime/operations/mixed_types.stdout
tests/expected/runtime/strings/basic.exitcode
tests/expected/runtime/strings/basic.stdout
tests/expected/runtime/types/all_types.exitcode
tests/expected/runtime/types/all_types.stdout
tests/expected/runtime/types/boundaries.exitcode
tests/expected/runtime/types/boundaries.stdout
tests/expected/runtime/variables/sequential_assign.exitcode
tests/expected/runtime/variables/sequential_assign.stdout
tests/expected/runtime/variables/shadowing.exitcode
tests/expected/runtime/variables/shadowing.stdout

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 security issue, 2 other issues, and left some high level feedback:

Security issues:

  • Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. (link)

General comments:

  • The hard-coded absolute paths for ZPILER, TESTS_DIR, and related dirs in generate_expected_outputs.py make the script environment-specific; consider deriving these from the repository root or accepting them via CLI args so it can run on different machines/CI environments.
  • In generate_expected_outputs.py, run_command takes a check flag but still exits the whole script on failure when check=True; for more flexible use (e.g., partial regeneration), you might want to have it raise an exception or return error details instead of calling sys.exit(1) directly.
  • The script currently emits only a generic 'FAILED to compile/assemble/link' message; including the underlying stderr from those failing subprocesses in those branches would make it much easier to debug failing test generation.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The hard-coded absolute paths for `ZPILER`, `TESTS_DIR`, and related dirs in `generate_expected_outputs.py` make the script environment-specific; consider deriving these from the repository root or accepting them via CLI args so it can run on different machines/CI environments.
- In `generate_expected_outputs.py`, `run_command` takes a `check` flag but still exits the whole script on failure when `check=True`; for more flexible use (e.g., partial regeneration), you might want to have it raise an exception or return error details instead of calling `sys.exit(1)` directly.
- The script currently emits only a generic 'FAILED to compile/assemble/link' message; including the underlying `stderr` from those failing subprocesses in those branches would make it much easier to debug failing test generation.

## Individual Comments

### Comment 1
<location path="generate_expected_outputs.py" line_range="5-11" />
<code_context>
+import tempfile
+import shutil
+
+ZPILER = Path("/home/xzist/zust/build/zpiler")
+TESTS_DIR = Path("/home/xzist/zust/tests")
+RUNTIME_DIR = TESTS_DIR / "runtime"
+EXPECTED_DIR = TESTS_DIR / "expected" / "runtime"
+
+def run_command(cmd, check=True):
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Avoid hardcoded absolute paths to make the script portable within the repo

Using `/home/xzist/...` makes this script unusable outside your machine and likely to fail in CI. Instead, derive these paths relative to this file (e.g. via `Path(__file__).resolve()` to the repo root, then into `build/zpiler` and `tests`) so it works for all environments.

```suggestion
import tempfile
import shutil

# Derive repository-root-relative paths so the script is portable
SCRIPT_PATH = Path(__file__).resolve()
REPO_ROOT = SCRIPT_PATH
while not ((REPO_ROOT / "build").is_dir() and (REPO_ROOT / "tests").is_dir()):
    if REPO_ROOT.parent == REPO_ROOT:
        raise RuntimeError(
            "Could not locate repository root containing 'build' and 'tests' directories"
        )
    REPO_ROOT = REPO_ROOT.parent

ZPILER = REPO_ROOT / "build" / "zpiler"
TESTS_DIR = REPO_ROOT / "tests"
RUNTIME_DIR = TESTS_DIR / "runtime"
EXPECTED_DIR = TESTS_DIR / "expected" / "runtime"
```
</issue_to_address>

### Comment 2
<location path="generate_expected_outputs.py" line_range="39-48" />
<code_context>
+        
+        # Compile
+        print(f"Compiling {rel_path}...", end=" ")
+        result = run_command([str(ZPILER), "--format", "x86_64-linux", "-o", str(asm_file), str(test_file)], check=False)
+        if result.returncode != 0:
+            print(f"FAILED to compile")
+            return False
+        
+        # Assemble
+        result = run_command(["as", str(asm_file), "-o", str(obj_file)], check=False)
+        if result.returncode != 0:
+            print(f"FAILED to assemble")
+            return False
+        
+        # Link
+        result = run_command(["gcc", str(obj_file), "-o", str(exe_file)], check=False)
+        if result.returncode != 0:
+            print(f"FAILED to link")
+            return False
+        
</code_context>
<issue_to_address>
**suggestion:** Surface more diagnostic info when compile/assemble/link steps fail

Because `check=False` is used, failures here only show a generic `FAILED to ...` message and hide the underlying error output, which makes debugging test failures difficult. Please either use `check=True` so `run_command` prints stdout/stderr on failure, or explicitly log `result.stdout` and `result.stderr` in these non‑zero return cases.

Suggested implementation:

```python
        # Compile
        print(f"Compiling {rel_path}...", end=" ")
        result = run_command(
            [str(ZPILER), "--format", "x86_64-linux", "-o", str(asm_file), str(test_file)],
            check=True,
        )
        if result.returncode != 0:
            print("FAILED to compile")
            return False

        # Assemble
        result = run_command(["as", str(asm_file), "-o", str(obj_file)], check=True)
        if result.returncode != 0:
            print("FAILED to assemble")
            return False

        # Link
        result = run_command(["gcc", str(obj_file), "-o", str(exe_file)], check=True)
        if result.returncode != 0:
            print("FAILED to link")
            return False

```

```python
def run_command(cmd, check=True):
    result = subprocess.run(cmd, capture_output=True, text=False)
    if check and result.returncode != 0:
        print(f"Command failed: {' '.join(cmd)}")
        if result.stdout:
            print(f"stdout:\n{result.stdout.decode(errors='replace')}")
        if result.stderr:
            print(f"stderr:\n{result.stderr.decode(errors='replace')}")

```

If `subprocess` is not already imported at the top of `generate_expected_outputs.py`, you should add:

```python
import subprocess
```

near the other imports.
</issue_to_address>

### Comment 3
<location path="generate_expected_outputs.py" line_range="14" />
<code_context>
    result = subprocess.run(cmd, capture_output=True, text=False)
</code_context>
<issue_to_address>
**security (python.lang.security.audit.dangerous-subprocess-use-audit):** Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

*Source: opengrep*
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +5 to +11
import tempfile
import shutil

ZPILER = Path("/home/xzist/zust/build/zpiler")
TESTS_DIR = Path("/home/xzist/zust/tests")
RUNTIME_DIR = TESTS_DIR / "runtime"
EXPECTED_DIR = TESTS_DIR / "expected" / "runtime"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Avoid hardcoded absolute paths to make the script portable within the repo

Using /home/xzist/... makes this script unusable outside your machine and likely to fail in CI. Instead, derive these paths relative to this file (e.g. via Path(__file__).resolve() to the repo root, then into build/zpiler and tests) so it works for all environments.

Suggested change
import tempfile
import shutil
ZPILER = Path("/home/xzist/zust/build/zpiler")
TESTS_DIR = Path("/home/xzist/zust/tests")
RUNTIME_DIR = TESTS_DIR / "runtime"
EXPECTED_DIR = TESTS_DIR / "expected" / "runtime"
import tempfile
import shutil
# Derive repository-root-relative paths so the script is portable
SCRIPT_PATH = Path(__file__).resolve()
REPO_ROOT = SCRIPT_PATH
while not ((REPO_ROOT / "build").is_dir() and (REPO_ROOT / "tests").is_dir()):
if REPO_ROOT.parent == REPO_ROOT:
raise RuntimeError(
"Could not locate repository root containing 'build' and 'tests' directories"
)
REPO_ROOT = REPO_ROOT.parent
ZPILER = REPO_ROOT / "build" / "zpiler"
TESTS_DIR = REPO_ROOT / "tests"
RUNTIME_DIR = TESTS_DIR / "runtime"
EXPECTED_DIR = TESTS_DIR / "expected" / "runtime"

Comment on lines +39 to +48
result = run_command([str(ZPILER), "--format", "x86_64-linux", "-o", str(asm_file), str(test_file)], check=False)
if result.returncode != 0:
print(f"FAILED to compile")
return False

# Assemble
result = run_command(["as", str(asm_file), "-o", str(obj_file)], check=False)
if result.returncode != 0:
print(f"FAILED to assemble")
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Surface more diagnostic info when compile/assemble/link steps fail

Because check=False is used, failures here only show a generic FAILED to ... message and hide the underlying error output, which makes debugging test failures difficult. Please either use check=True so run_command prints stdout/stderr on failure, or explicitly log result.stdout and result.stderr in these non‑zero return cases.

Suggested implementation:

        # Compile
        print(f"Compiling {rel_path}...", end=" ")
        result = run_command(
            [str(ZPILER), "--format", "x86_64-linux", "-o", str(asm_file), str(test_file)],
            check=True,
        )
        if result.returncode != 0:
            print("FAILED to compile")
            return False

        # Assemble
        result = run_command(["as", str(asm_file), "-o", str(obj_file)], check=True)
        if result.returncode != 0:
            print("FAILED to assemble")
            return False

        # Link
        result = run_command(["gcc", str(obj_file), "-o", str(exe_file)], check=True)
        if result.returncode != 0:
            print("FAILED to link")
            return False
def run_command(cmd, check=True):
    result = subprocess.run(cmd, capture_output=True, text=False)
    if check and result.returncode != 0:
        print(f"Command failed: {' '.join(cmd)}")
        if result.stdout:
            print(f"stdout:\n{result.stdout.decode(errors='replace')}")
        if result.stderr:
            print(f"stderr:\n{result.stderr.decode(errors='replace')}")

If subprocess is not already imported at the top of generate_expected_outputs.py, you should add:

import subprocess

near the other imports.

EXPECTED_DIR = TESTS_DIR / "expected" / "runtime"

def run_command(cmd, check=True):
result = subprocess.run(cmd, capture_output=True, text=False)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security (python.lang.security.audit.dangerous-subprocess-use-audit): Detected subprocess function 'run' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'.

Source: opengrep

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant