"""Tests for CoSTEER feedback types and EvolvingItem.""" from __future__ import annotations import sys from pathlib import Path from unittest.mock import MagicMock import pytest PROJECT_ROOT = Path(__file__).parent.parent.parent sys.path.insert(0, str(PROJECT_ROOT)) # ============================================================================= # CoSTEERSingleFeedback # ============================================================================= class TestCoSTEERSingleFeedback: def test_construction_with_valid_fields(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback fb = CoSTEERSingleFeedback( execution="exec ok", return_checking="return ok", code="code ok", final_decision=True, ) assert fb.execution == "exec ok" assert fb.return_checking == "return ok" assert fb.code == "code ok" assert fb.final_decision is True def test_bool_returns_final_decision(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback fb_true = CoSTEERSingleFeedback(execution="x", return_checking="x", code="x", final_decision=True) fb_false = CoSTEERSingleFeedback(execution="x", return_checking="x", code="x", final_decision=False) assert bool(fb_true) is True assert bool(fb_false) is False def test_default_values(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback fb = CoSTEERSingleFeedback(execution="x", return_checking=None, code="x") assert fb.final_decision is None assert fb.raw_execution == "" assert fb.source_feedback == {} def test_val_and_update_init_dict_converts_boolean(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback assert CoSTEERSingleFeedback.val_and_update_init_dict( {"execution": "x", "return_checking": "y", "code": "z", "final_decision": "true"} )["final_decision"] is True assert CoSTEERSingleFeedback.val_and_update_init_dict( {"execution": "x", "return_checking": "y", "code": "z", "final_decision": "false"} )["final_decision"] is False assert CoSTEERSingleFeedback.val_and_update_init_dict( {"execution": "x", "return_checking": "y", "code": "z", "final_decision": "True"} )["final_decision"] is True assert CoSTEERSingleFeedback.val_and_update_init_dict( {"execution": "x", "return_checking": "y", "code": "z", "final_decision": "False"} )["final_decision"] is False def test_val_and_update_init_dict_rejects_non_boolean(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback with pytest.raises(ValueError): CoSTEERSingleFeedback.val_and_update_init_dict( {"execution": "x", "return_checking": "y", "code": "z", "final_decision": 42} ) def test_val_and_update_init_dict_missing_final_decision_raises(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback with pytest.raises(ValueError, match="final_decision"): CoSTEERSingleFeedback.val_and_update_init_dict( {"execution": "x", "return_checking": "y", "code": "z"} ) def test_val_and_update_init_dict_json_dumps_non_string_attrs(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback import json data = { "execution": {"key": "val"}, "return_checking": ["list"], "code": 123, "final_decision": True, } result = CoSTEERSingleFeedback.val_and_update_init_dict(data) for attr in ("execution", "return_checking", "code"): # Should have been converted to JSON string assert isinstance(result[attr], str) _ = json.loads(result[attr]) # valid JSON def test_merge_all_true_decisions(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback fb1 = CoSTEERSingleFeedback(execution="a", return_checking="ra", code="c1", final_decision=True) fb2 = CoSTEERSingleFeedback(execution="b", return_checking="rb", code="c2", final_decision=True) merged = CoSTEERSingleFeedback.merge([fb1, fb2]) assert merged.final_decision is True def test_merge_one_false_decision(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback fb1 = CoSTEERSingleFeedback(execution="a", return_checking="ra", code="c1", final_decision=True) fb2 = CoSTEERSingleFeedback(execution="b", return_checking="rb", code="c2", final_decision=False) merged = CoSTEERSingleFeedback.merge([fb1, fb2]) assert merged.final_decision is False def test_merge_concatenates_fields(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback fb1 = CoSTEERSingleFeedback(execution="A", return_checking="RA", code="C1", final_decision=True) fb2 = CoSTEERSingleFeedback(execution="B", return_checking="RB", code="C2", final_decision=True) merged = CoSTEERSingleFeedback.merge([fb1, fb2]) assert "A\n\nB" in merged.execution assert "RA\n\nRB" in merged.return_checking assert "C1\n\nC2" in merged.code def test_merge_skips_none_fields(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback fb1 = CoSTEERSingleFeedback(execution="A", return_checking="RA", code="C1", final_decision=True) fb2 = CoSTEERSingleFeedback(execution="B", return_checking=None, code="C2", final_decision=True) merged = CoSTEERSingleFeedback.merge([fb1, fb2]) assert merged.execution == "A\n\nB" assert merged.return_checking == "RA" assert merged.code == "C1\n\nC2" def test_merge_aggregates_source_feedback(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback fb1 = CoSTEERSingleFeedback(execution="a", return_checking="x", code="c", final_decision=True, source_feedback={"eval1": True}) fb2 = CoSTEERSingleFeedback(execution="b", return_checking="y", code="d", final_decision=True, source_feedback={"eval2": False}) merged = CoSTEERSingleFeedback.merge([fb1, fb2]) assert merged.source_feedback == {"eval1": True, "eval2": False} def test_str_contains_all_sections(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback fb = CoSTEERSingleFeedback(execution="exec", return_checking="ret", code="code", final_decision=True) s = str(fb) assert "Execution" in s assert "Return Checking" in s assert "Code" in s assert "Final Decision" in s assert "SUCCESS" in s def test_str_shows_fail_for_false(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback fb = CoSTEERSingleFeedback(execution="exec", return_checking="ret", code="code", final_decision=False) assert "FAIL" in str(fb) # ============================================================================= # CoSTEERSingleFeedbackDeprecated # ============================================================================= class TestCoSTEERSingleFeedbackDeprecated: def test_property_getters(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedbackDeprecated fb = CoSTEERSingleFeedbackDeprecated( execution_feedback="exec", code_feedback="code", value_feedback="val", shape_feedback="shape", final_decision=True, final_feedback="final", value_generated_flag=True, final_decision_based_on_gt=True, ) assert fb.execution == "exec" assert fb.code == "code" assert fb.final_decision is True assert fb.value_generated_flag is True assert fb.final_decision_based_on_gt is True def test_return_checking_when_value_generated(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedbackDeprecated fb = CoSTEERSingleFeedbackDeprecated( value_generated_flag=True, value_feedback="vals", shape_feedback="shapes", ) rc = fb.return_checking assert "vals" in rc assert "shapes" in rc def test_return_checking_when_no_value_generated(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedbackDeprecated fb = CoSTEERSingleFeedbackDeprecated(value_generated_flag=False) assert fb.return_checking is None def test_setters_work(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedbackDeprecated fb = CoSTEERSingleFeedbackDeprecated() fb.execution = "new_exec" fb.code = "new_code" fb.return_checking = "new_rc" assert fb.execution_feedback == "new_exec" assert fb.code_feedback == "new_code" assert fb.value_feedback == "new_rc" assert fb.shape_feedback == "new_rc" def test_str_contains_all_sections(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedbackDeprecated fb = CoSTEERSingleFeedbackDeprecated( execution_feedback="exec", shape_feedback="shape", code_feedback="code", value_feedback="val", final_feedback="final", final_decision=True, ) s = str(fb) for keyword in ("Execution", "Shape", "Code", "Value", "Final Decision", "SUCCESS"): assert keyword in s # ============================================================================= # CoSTEERMultiFeedback # ============================================================================= class TestCoSTEERMultiFeedback: def _make_fb(self, decision=True): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERSingleFeedback return CoSTEERSingleFeedback(execution="x", return_checking="x", code="x", final_decision=decision) def test_getitem(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERMultiFeedback fb1, fb2 = self._make_fb(True), self._make_fb(False) mf = CoSTEERMultiFeedback([fb1, fb2]) assert mf[0].final_decision is True assert mf[1].final_decision is False def test_len(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERMultiFeedback assert len(CoSTEERMultiFeedback([self._make_fb()])) == 1 assert len(CoSTEERMultiFeedback([])) == 0 def test_append(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERMultiFeedback mf = CoSTEERMultiFeedback([]) mf.append(self._make_fb(True)) assert len(mf) == 1 assert mf[0].final_decision is True def test_iter(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERMultiFeedback fbs = [self._make_fb(True), self._make_fb(True)] mf = CoSTEERMultiFeedback(fbs) assert list(mf) == fbs def test_finished_all_true(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERMultiFeedback mf = CoSTEERMultiFeedback([self._make_fb(True), self._make_fb(True)]) assert mf.finished() is True def test_finished_one_false(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERMultiFeedback mf = CoSTEERMultiFeedback([self._make_fb(True), self._make_fb(False)]) assert mf.finished() is False def test_finished_with_none_skips(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERMultiFeedback mf = CoSTEERMultiFeedback([self._make_fb(True), None]) assert mf.finished() is True # None = skipped = accepted def test_bool_all_true(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERMultiFeedback assert bool(CoSTEERMultiFeedback([self._make_fb(True), self._make_fb(True)])) is True def test_bool_one_false(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERMultiFeedback assert bool(CoSTEERMultiFeedback([self._make_fb(True), self._make_fb(False)])) is False def test_is_acceptable_delegates(self): from rdagent.components.coder.CoSTEER.evaluators import CoSTEERMultiFeedback mf = CoSTEERMultiFeedback([self._make_fb(True), self._make_fb(True)]) assert mf.is_acceptable() is True # ============================================================================= # EvolvingItem # ============================================================================= class TestEvolvingItem: def test_construction_without_gt(self): from rdagent.components.coder.CoSTEER.evolvable_subjects import EvolvingItem from rdagent.core.experiment import Task t1 = Task(name="task1") t2 = Task(name="task2") ei = EvolvingItem(sub_tasks=[t1, t2]) assert len(ei.sub_tasks) == 2 assert ei.sub_gt_implementations is None def test_construction_with_matching_gt(self): from rdagent.components.coder.CoSTEER.evolvable_subjects import EvolvingItem from rdagent.core.experiment import Task, FBWorkspace t1, t2 = Task(name="t1"), Task(name="t2") ws1, ws2 = FBWorkspace(), FBWorkspace() ei = EvolvingItem(sub_tasks=[t1, t2], sub_gt_implementations=[ws1, ws2]) assert ei.sub_gt_implementations == [ws1, ws2] def test_mismatched_gt_length_resets_to_none(self): from rdagent.components.coder.CoSTEER.evolvable_subjects import EvolvingItem from rdagent.core.experiment import Task, FBWorkspace t1, t2 = Task(name="t1"), Task(name="t2") ei = EvolvingItem(sub_tasks=[t1, t2], sub_gt_implementations=[FBWorkspace()]) assert ei.sub_gt_implementations is None def test_from_experiment(self): from rdagent.components.coder.CoSTEER.evolvable_subjects import EvolvingItem from rdagent.core.experiment import Experiment, Task exp = Experiment(sub_tasks=[Task(name="x")]) exp.based_experiments = ["base"] exp.experiment_workspace = "ws" ei = EvolvingItem.from_experiment(exp) assert len(ei.sub_tasks) == 1 assert ei.based_experiments == ["base"] assert ei.experiment_workspace == "ws" # ============================================================================= # CoSTEERQueriedKnowledge # ============================================================================= class TestCoSTEERQueriedKnowledge: def test_default_construction(self): from rdagent.components.coder.CoSTEER.knowledge_management import CoSTEERQueriedKnowledge qk = CoSTEERQueriedKnowledge() assert qk.success_task_to_knowledge_dict == {} assert qk.failed_task_info_set == set() def test_with_data(self): from rdagent.components.coder.CoSTEER.knowledge_management import CoSTEERQueriedKnowledge qk = CoSTEERQueriedKnowledge( success_task_to_knowledge_dict={"a": "knowledge_a"}, failed_task_info_set={"fail1", "fail2"}, ) assert qk.success_task_to_knowledge_dict["a"] == "knowledge_a" assert "fail1" in qk.failed_task_info_set assert "fail2" in qk.failed_task_info_set