File size: 6,974 Bytes
3ea78ad
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
"""
Code Executor β€” Sandboxed Python execution for CodeReviewEnv.

Runs Python code + test cases in an isolated namespace.
Returns execution results, stdout, stderr, and test pass/fail.

This makes CodeReviewEnv a REAL tool server: agents run code,
see actual failures, and submit fixes that are verified by execution.
"""

import io
import sys
import traceback
from typing import Any, Dict, List, Optional, Tuple

# Maximum execution time (seconds) and output length
EXEC_TIMEOUT = 5
MAX_OUTPUT_LEN = 4096


def execute_code(code: str, test_code: str = "") -> Dict[str, Any]:
    """Execute Python code + test cases in an isolated namespace.

    Args:
        code: The source code to execute
        test_code: Test assertions to run after the code

    Returns:
        {
            "success": bool,
            "stdout": str,
            "stderr": str,
            "error": str or None,
            "tests_passed": int,
            "tests_failed": int,
            "test_results": [{"name": str, "passed": bool, "error": str}],
        }
    """
    namespace: Dict[str, Any] = {}
    stdout_capture = io.StringIO()
    stderr_capture = io.StringIO()

    result = {
        "success": False,
        "stdout": "",
        "stderr": "",
        "error": None,
        "tests_passed": 0,
        "tests_failed": 0,
        "test_results": [],
    }

    # Step 1: Execute the main code
    old_stdout, old_stderr = sys.stdout, sys.stderr
    try:
        sys.stdout = stdout_capture
        sys.stderr = stderr_capture
        exec(code, namespace)
        result["success"] = True
    except Exception as e:
        result["error"] = f"{type(e).__name__}: {e}"
        result["success"] = False
    finally:
        sys.stdout = old_stdout
        sys.stderr = old_stderr
        result["stdout"] = stdout_capture.getvalue()[:MAX_OUTPUT_LEN]
        result["stderr"] = stderr_capture.getvalue()[:MAX_OUTPUT_LEN]

    # Step 2: Run test cases
    if test_code and result["success"]:
        test_results = _run_tests(test_code, namespace)
        result["test_results"] = test_results
        result["tests_passed"] = sum(1 for t in test_results if t["passed"])
        result["tests_failed"] = sum(1 for t in test_results if not t["passed"])

    return result


def _run_tests(test_code: str, namespace: Dict) -> List[Dict[str, Any]]:
    """Run individual test assertions and collect results."""
    results = []

    # Split test code into individual assertions
    lines = test_code.strip().split('\n')
    test_blocks = []
    current_block = []

    for line in lines:
        stripped = line.strip()
        if stripped.startswith('# test:') or stripped.startswith('# Test:'):
            if current_block:
                test_blocks.append(('\n'.join(current_block), current_block[0].strip()))
                current_block = []
        current_block.append(line)

    if current_block:
        test_blocks.append(('\n'.join(current_block), current_block[0].strip()))

    # If no labeled blocks, treat each assert as a test
    if len(test_blocks) <= 1:
        test_blocks = []
        for line in lines:
            stripped = line.strip()
            if stripped and not stripped.startswith('#'):
                test_blocks.append((line, stripped[:60]))

    for code_block, name in test_blocks:
        try:
            exec(code_block, namespace)
            results.append({"name": name, "passed": True, "error": None})
        except AssertionError as e:
            results.append({"name": name, "passed": False, "error": f"AssertionError: {e}"})
        except Exception as e:
            results.append({"name": name, "passed": False, "error": f"{type(e).__name__}: {e}"})

    return results


def apply_fix_and_test(
    original_code: str,
    fix_code: str,
    test_code: str,
) -> Dict[str, Any]:
    """Apply agent's fix to the code and run tests.

    The fix_code can be either:
      1. Complete replacement code
      2. A patch description (we try to apply it)

    Returns execution results with test pass/fail.
    """
    # Try the fix as complete replacement first
    result = execute_code(fix_code, test_code)
    if result["tests_passed"] > 0:
        return result

    # If that didn't work, try prepending original code context
    combined = original_code + "\n\n" + fix_code
    result = execute_code(combined, test_code)
    return result


# ─── Test Case Templates ─────────────────────────────────────────────────────

# These map snippet names to test cases that validate correctness.
# Tests are written against the ORIGINAL (clean) code β€” they should
# PASS on clean code and FAIL on buggy code.

SNIPPET_TESTS: Dict[str, str] = {
    "binary_search": """\
assert binary_search([1,2,3,4,5], 3) == 2
assert binary_search([1,2,3,4,5], 1) == 0
assert binary_search([1,2,3,4,5], 5) == 4
assert binary_search([1,2,3,4,5], 6) == -1
assert binary_search([], 1) == -1
assert binary_search([1], 1) == 0
""",
    "fibonacci": """\
assert fibonacci(0) == 0
assert fibonacci(1) == 1
assert fibonacci(2) == 1
assert fibonacci(5) == 5
assert fibonacci(10) == 55
""",
    "max_subarray": """\
assert max_subarray([1, -2, 3, 4, -1]) == 7
assert max_subarray([-1, -2, -3]) == -1
assert max_subarray([5]) == 5
assert max_subarray([]) == 0
""",
    "is_palindrome": """\
assert is_palindrome("racecar") == True
assert is_palindrome("hello") == False
assert is_palindrome("A man a plan a canal Panama") == True
assert is_palindrome("") == True
""",
    "merge_sort": """\
assert merge_sort([3,1,4,1,5]) == [1,1,3,4,5]
assert merge_sort([]) == []
assert merge_sort([1]) == [1]
assert merge_sort([5,4,3,2,1]) == [1,2,3,4,5]
""",
    "flatten_dict": """\
assert flatten_dict({"a": 1, "b": {"c": 2}}) == {"a": 1, "b.c": 2}
assert flatten_dict({}) == {}
assert flatten_dict({"x": {"y": {"z": 1}}}) == {"x.y.z": 1}
""",
    "validate_email": """\
assert validate_email("user@example.com") == True
assert validate_email("bad") == False
assert validate_email("@example.com") == False
assert validate_email("user@.com") == False
assert validate_email("") == False
""",
    "group_by": """\
result = group_by([1,2,3,4,5], lambda x: x % 2)
assert result[0] == [2, 4]
assert result[1] == [1, 3, 5]
""",
    "matrix_multiply": """\
assert matrix_multiply([[1,2],[3,4]], [[5,6],[7,8]]) == [[19,22],[43,50]]
assert matrix_multiply([[1]], [[2]]) == [[2]]
""",
    "csv_parser": """\
assert parse_csv("a,b,c") == [["a", "b", "c"]]
assert parse_csv("1,2\\n3,4") == [["1", "2"], ["3", "4"]]
""",
    "topological_sort": """\
result = topological_sort({"a": ["b"], "b": ["c"], "c": []})
assert result.index("a") < result.index("b")
assert result.index("b") < result.index("c")
""",
    "lru_cache": """\
cache = LRUCache(2)
cache.put("a", 1)
cache.put("b", 2)
assert cache.get("a") == 1
cache.put("c", 3)
assert cache.get("b") == -1
""",
}