11"""Tests for parsing utilities."""
22
3+ from unittest .mock import Mock
4+
35from rlm .core .types import CodeBlock , REPLResult , RLMIteration
6+ from rlm .environments .local_repl import LocalREPL
47from rlm .utils .parsing import (
58 convert_context_for_repl ,
69 find_code_blocks ,
@@ -81,12 +84,25 @@ class TestFindFinalAnswer:
8184 def test_final_answer (self ):
8285 text = "The answer is:\n FINAL(42)"
8386 result = find_final_answer (text )
84- assert result == ( "FINAL" , " 42")
87+ assert result == " 42"
8588
8689 def test_final_var_answer (self ):
8790 text = "Check the variable:\n FINAL_VAR(result)"
91+ # Create a mock environment that returns the variable value
92+ mock_env = Mock ()
93+ mock_env .execute_code .return_value = REPLResult (stdout = "42" , stderr = "" , locals = {})
94+ result = find_final_answer (text , environment = mock_env )
95+ assert result == "42"
96+ # Verify execute_code was called with the correct code
97+ mock_env .execute_code .assert_called_once ()
98+ call_args = mock_env .execute_code .call_args [0 ][0 ]
99+ assert "FINAL_VAR('result')" in call_args or 'FINAL_VAR("result")' in call_args
100+
101+ def test_final_var_without_environment (self ):
102+ text = "Check the variable:\n FINAL_VAR(result)"
103+ # Without environment, FINAL_VAR should return None
88104 result = find_final_answer (text )
89- assert result == ( "FINAL_VAR" , "result" )
105+ assert result is None
90106
91107 def test_no_final_answer (self ):
92108 text = "Still working on the problem..."
@@ -98,8 +114,8 @@ def test_final_with_multiline_content(self):
98114multiline answer)"""
99115 result = find_final_answer (text )
100116 assert result is not None
101- assert result [ 0 ] == "FINAL"
102- assert "multiline " in result [ 1 ]
117+ assert "multiline" in result
118+ assert "This is a " in result
103119
104120 def test_final_must_be_at_start_of_line (self ):
105121 # FINAL not at start of line should not match
@@ -110,7 +126,118 @@ def test_final_must_be_at_start_of_line(self):
110126 def test_final_with_whitespace (self ):
111127 text = " FINAL(answer with spaces)"
112128 result = find_final_answer (text )
113- assert result == ("FINAL" , "answer with spaces" )
129+ assert result == "answer with spaces"
130+
131+ def test_final_and_final_var_parsing (self ):
132+ """Test that both FINAL and FINAL_VAR patterns are parsed correctly."""
133+ # Test FINAL with various content types
134+ test_cases_final = [
135+ ("FINAL(42)" , "42" ),
136+ ("FINAL('hello world')" , "'hello world'" ),
137+ ('FINAL("test")' , '"test"' ),
138+ ("FINAL(123.45)" , "123.45" ),
139+ ("FINAL([1, 2, 3])" , "[1, 2, 3]" ),
140+ ]
141+
142+ for text , expected in test_cases_final :
143+ result = find_final_answer (text )
144+ assert result == expected , f"Failed for text: { text } "
145+
146+ # Test FINAL_VAR with environment
147+ mock_env = Mock ()
148+ mock_env .execute_code .return_value = REPLResult (
149+ stdout = "computed_result" , stderr = "" , locals = {}
150+ )
151+
152+ test_cases_final_var = [
153+ ("FINAL_VAR(result)" , "result" ),
154+ ("FINAL_VAR('my_var')" , "my_var" ),
155+ ('FINAL_VAR("another_var")' , "another_var" ),
156+ ("FINAL_VAR(answer)" , "answer" ),
157+ ]
158+
159+ for text , var_name in test_cases_final_var :
160+ result = find_final_answer (text , environment = mock_env )
161+ assert result == "computed_result" , f"Failed for text: { text } "
162+ # Verify the variable name was extracted correctly
163+ call_args = mock_env .execute_code .call_args [0 ][0 ]
164+ assert (
165+ var_name in call_args
166+ or f"'{ var_name } '" in call_args
167+ or f'"{ var_name } "' in call_args
168+ )
169+
170+ def test_final_var_takes_precedence_over_final (self ):
171+ """Test that FINAL_VAR is checked first and takes precedence over FINAL."""
172+ mock_env = Mock ()
173+ mock_env .execute_code .return_value = REPLResult (stdout = "var_value" , stderr = "" , locals = {})
174+
175+ # If both appear, FINAL_VAR should be found first (checked first in the function)
176+ text = "FINAL_VAR(result)\n FINAL(direct_answer)"
177+ result = find_final_answer (text , environment = mock_env )
178+ assert result == "var_value" # FINAL_VAR should be returned
179+
180+ # Without environment, FINAL_VAR pattern is found but returns None (no fallback to FINAL)
181+ # This is expected behavior - FINAL_VAR takes precedence when found
182+ result = find_final_answer (text )
183+ assert result is None # FINAL_VAR found but no environment, so returns None
184+
185+ # Test that FINAL alone works when FINAL_VAR is not present
186+ text_final_only = "FINAL(direct_answer)"
187+ result = find_final_answer (text_final_only )
188+ assert result == "direct_answer"
189+
190+ def test_final_var_retrieves_actual_variables_from_environment (self ):
191+ """Test that FINAL_VAR actually retrieves variables from a real code environment."""
192+ # Create a real LocalREPL environment
193+ env = LocalREPL ()
194+
195+ try :
196+ # Execute code to create variables with different types
197+ env .execute_code ("x = 42" )
198+ env .execute_code ("result = 'hello world'" )
199+ env .execute_code ("answer = [1, 2, 3, 4, 5]" )
200+ env .execute_code ("computed = 10 * 5 + 2" )
201+ env .execute_code ("nested = {'key': 'value', 'num': 123}" )
202+
203+ # Test retrieving integer variable
204+ text = "FINAL_VAR(x)"
205+ result = find_final_answer (text , environment = env )
206+ assert result == "42" , f"Expected '42', got '{ result } '"
207+
208+ # Test retrieving string variable
209+ text = "FINAL_VAR(result)"
210+ result = find_final_answer (text , environment = env )
211+ assert result == "hello world" , f"Expected 'hello world', got '{ result } '"
212+
213+ # Test retrieving list variable
214+ text = "FINAL_VAR(answer)"
215+ result = find_final_answer (text , environment = env )
216+ assert result == "[1, 2, 3, 4, 5]" , f"Expected '[1, 2, 3, 4, 5]', got '{ result } '"
217+
218+ # Test retrieving computed variable
219+ text = "FINAL_VAR(computed)"
220+ result = find_final_answer (text , environment = env )
221+ assert result == "52" , f"Expected '52', got '{ result } '"
222+
223+ # Test retrieving dictionary variable
224+ text = "FINAL_VAR(nested)"
225+ result = find_final_answer (text , environment = env )
226+ assert "'key': 'value'" in result or '"key": "value"' in result
227+ assert "'num': 123" in result or '"num": 123' in result
228+
229+ # Test that variable updates are reflected
230+ env .execute_code ("x = 100" )
231+ text = "FINAL_VAR(x)"
232+ result = find_final_answer (text , environment = env )
233+ assert result == "100" , f"Expected '100', got '{ result } '"
234+
235+ # Test that non-existent variable returns error message
236+ text = "FINAL_VAR(nonexistent)"
237+ result = find_final_answer (text , environment = env )
238+ assert "Error" in result or "not found" in result .lower ()
239+ finally :
240+ env .cleanup ()
114241
115242
116243class TestFormatExecutionResult :
0 commit comments