Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion render_machine/actions/fix_conformance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,41 @@
from render_machine.actions.base_action import BaseAction
from render_machine.implementation_code_helpers import ImplementationCodeHelpers
from render_machine.render_context import RenderContext
from render_machine.render_types import TestExecutionPhase
from render_machine.render_types import RenderError, TestExecutionPhase

MAX_CONFORMANCE_TEST_FIX_ATTEMPTS = 20
MAX_CONFORMANCE_TEST_RERENDER_ATTEMPTS = 1


class FixConformanceTest(BaseAction):
IMPLEMENTATION_CODE_NOT_UPDATED = "implementation_code_not_updated"
IMPLEMENTATION_CODE_UPDATED = "implementation_code_updated"
LIMIT_EXCEEDED_OUTCOME = "conformance_test_fix_limit_exceeded"
REGENERATE_CONFORMANCE_TESTS_OUTCOME = "regenerate_conformance_tests"

ISSUE_REASON_CODE_CONFORMANCE_TESTS = 0
ISSUE_REASON_CODE_IMPLEMENTATION_CODE = 1
ISSUE_REASON_CODE_CONFLICTING_REQUIREMENTS = 2
ISSUE_REASON_CODE_CONFLICTING_ACCEPTANCE_TESTS = 3

def execute(self, render_context: RenderContext, previous_action_payload: Any | None):
ctx = render_context.conformance_tests_running_context
ctx.fix_attempts += 1

if ctx.fix_attempts >= MAX_CONFORMANCE_TEST_FIX_ATTEMPTS:
if ctx.conformance_tests_render_attempts >= MAX_CONFORMANCE_TEST_RERENDER_ATTEMPTS:
error_msg = f"The renderer was unable to produce an implementation that passes conformance tests for functionality '{render_context.frid_context.frid}' after many attempts. Please review and rewrite the specification. (Render ID: {render_context.run_state.render_id})"
render_context.last_error_message = error_msg
return (
self.LIMIT_EXCEEDED_OUTCOME,
RenderError.encode(message=error_msg).to_payload(),
)
else:
ctx.regenerating_conformance_tests = True
return self.REGENERATE_CONFORMANCE_TESTS_OUTCOME, None

console.info(f"Running conformance tests attempt {ctx.fix_attempts + 1}.")

console.info(
f"Fixing conformance test for functionality {render_context.conformance_tests_running_context.current_testing_frid} in module {render_context.conformance_tests_running_context.current_testing_module_name}."
)
Expand Down
6 changes: 6 additions & 0 deletions render_machine/actions/prepare_testing_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ class PrepareTestingEnvironment(BaseAction):
FAILED_OUTCOME = "testing_environment_preparation_failed"

def execute(self, render_context: RenderContext, _previous_action_payload: Any | None):
if (
render_context.prepare_environment_script is None
or not render_context.conformance_tests_running_context.should_prepare_testing_environment
):
return self.SUCCESSFUL_OUTCOME, None

console.info(
f"Running testing environment preparation script {render_context.prepare_environment_script} for build folder {render_context.build_folder}."
)
Expand Down
18 changes: 18 additions & 0 deletions render_machine/actions/refactor_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,31 @@
from render_machine.actions.base_action import BaseAction
from render_machine.implementation_code_helpers import ImplementationCodeHelpers
from render_machine.render_context import RenderContext
from render_machine.render_types import RenderError

MAX_REFACTORING_ITERATIONS = 5


class RefactorCode(BaseAction):
SUCCESSFUL_OUTCOME = "refactoring_successful"
NO_FILES_REFACTORED_OUTCOME = "no_files_refactored"
ITERATION_LIMIT_EXCEEDED_OUTCOME = "refactoring_iteration_limit_exceeded"

def execute(self, render_context: RenderContext, _previous_action_payload: Any | None):
if render_context.frid_context.refactoring_iteration == 0:
console.info("Refactoring the generated code...")

render_context.frid_context.refactoring_iteration += 1

if render_context.frid_context.refactoring_iteration >= MAX_REFACTORING_ITERATIONS:
error_message = "Refactoring iterations limit of {MAX_REFACTORING_ITERATIONS} reached for functionality {render_context.frid_context.frid}."
render_context.last_error_message = error_message

return (
self.ITERATION_LIMIT_EXCEEDED_OUTCOME,
RenderError.encode(message=error_message).to_payload(),
)

existing_files, existing_files_content = ImplementationCodeHelpers.fetch_existing_files(
render_context.build_folder
)
Expand Down
14 changes: 14 additions & 0 deletions render_machine/actions/render_functional_requirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,22 @@
class RenderFunctionalRequirement(BaseAction):
SUCCESSFUL_OUTCOME = "code_and_unit_tests_generated"
FUNCTIONAL_REQUIREMENT_TOO_COMPLEX_OUTCOME = "functional_requirement_too_complex"
ITERATION_LIMIT_EXCEEDED_OUTCOME = "frid_iteration_limit_exceeded"

def execute(self, render_context: RenderContext, _previous_action_payload: Any | None):
if render_context.frid_context.functional_requirement_render_attempts >= MAX_CODE_GENERATION_RETRIES:
error_msg = f"Unittests could not be fixed after rendering the functionality {render_context.frid_context.frid} for the {MAX_CODE_GENERATION_RETRIES} times."
render_context.last_error_message = error_msg
return self.ITERATION_LIMIT_EXCEEDED_OUTCOME, None

render_context.frid_context.functional_requirement_render_attempts += 1

if render_context.frid_context.functional_requirement_render_attempts > 1:
console.info(
f"Unittests could not be fixed after rendering the functionality. "
f"Restarting rendering functionality {render_context.frid_context.frid} from scratch."
)

render_utils.revert_changes_for_frid(render_context)
existing_files, existing_files_content = ImplementationCodeHelpers.fetch_existing_files(
render_context.build_folder
Expand Down
55 changes: 0 additions & 55 deletions render_machine/render_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@
)

MAX_UNITTEST_FIX_ATTEMPTS = 20
MAX_CODE_GENERATION_RETRIES = 2
MAX_CONFORMANCE_TEST_RERENDER_ATTEMPTS = 1
MAX_REFACTORING_ITERATIONS = 5
MAX_CONFORMANCE_TEST_FIX_ATTEMPTS = 20
MAX_FUNCTIONAL_REQUIREMENT_RENDER_ATTEMPTS_FAILED_UNIT_DURING_CONFORMANCE_TESTS = 2


Expand Down Expand Up @@ -165,20 +161,6 @@ def start_implementing_frid(self):
self.run_state.current_frid = frid
return

def check_frid_iteration_limit(self):
if self.frid_context.functional_requirement_render_attempts >= MAX_CODE_GENERATION_RETRIES:
error_msg = f"Unittests could not be fixed after rendering the functionality {self.frid_context.frid} for the {MAX_CODE_GENERATION_RETRIES} times."
self.dispatch_error(error_msg)

self.frid_context.functional_requirement_render_attempts += 1

if self.frid_context.functional_requirement_render_attempts > 1:
# this if is intended just for logging
console.info(
f"Unittests could not be fixed after rendering the functionality. "
f"Restarting rendering the functionality {self.frid_context.frid} from scratch."
)

def has_next_frid(self) -> bool:
next_frid = plain_spec.get_next_frid(self.plain_source_tree, self.frid_context.frid)
if self.render_range is None or len(self.render_range) == 0:
Expand Down Expand Up @@ -310,26 +292,6 @@ def _on_unit_test_limit_exceeded_in_refactoring(self):
git_utils.revert_changes(self.build_folder)
self.machine.dispatch(triggers.START_NEW_REFACTORING_ITERATION)

def start_refactoring_code(self):

if self.frid_context.refactoring_iteration == 0:
console.info("Refactoring the generated code...")

self.frid_context.refactoring_iteration += 1

if self.frid_context.refactoring_iteration >= MAX_REFACTORING_ITERATIONS:
console.info(
f"Refactoring iterations limit of {MAX_REFACTORING_ITERATIONS} reached for functionality {self.frid_context.frid}."
)
self.machine.dispatch(triggers.PROCEED_FRID_PROCESSING)

def start_testing_environment_preparation(self):
if (
self.prepare_environment_script is None
or not self.conformance_tests_running_context.should_prepare_testing_environment
):
self.machine.dispatch(triggers.MARK_TESTING_ENVIRONMENT_PREPARED)

def start_conformance_tests_processing(self):
console.info("Implementing conformance tests...")
current_frid_specifications, _ = plain_spec.get_specifications_for_frid(
Expand Down Expand Up @@ -589,23 +551,6 @@ def start_conformance_tests_for_frid(self):
# Should never reach here
raise RuntimeError(f"Unexpected execution phase: {ctx.execution_phase}")

def start_fixing_conformance_tests(self):
self.conformance_tests_running_context.fix_attempts += 1

if self.conformance_tests_running_context.fix_attempts >= MAX_CONFORMANCE_TEST_FIX_ATTEMPTS:
if (
self.conformance_tests_running_context.conformance_tests_render_attempts
>= MAX_CONFORMANCE_TEST_RERENDER_ATTEMPTS
):
error_msg = f"The renderer was unable to produce an implementation that passes conformance tests for functionality '{self.frid_context.frid}' after many attempts. Please review and rewrite the specification. (Render ID: {self.run_state.render_id})"
self.dispatch_error(error_msg)
else:
self.conformance_tests_running_context.regenerating_conformance_tests = True
self.machine.dispatch(triggers.MARK_REGENERATION_OF_CONFORMANCE_TESTS)

def finish_fixing_conformance_tests(self):
console.info(f"Running conformance tests attempt {self.conformance_tests_running_context.fix_attempts + 1}.")

def start_render_completed(self):
self.run_state.set_render_succeeded(True)

Expand Down
21 changes: 7 additions & 14 deletions render_machine/state_machine_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,14 @@ def get_action_result_triggers_map(self) -> Dict[str, str]:
PrepareRepositories.SUCCESSFUL_OUTCOME: triggers.START_RENDER,
RenderFunctionalRequirement.SUCCESSFUL_OUTCOME: triggers.RENDER_FUNCTIONAL_REQUIREMENT,
RenderFunctionalRequirement.FUNCTIONAL_REQUIREMENT_TOO_COMPLEX_OUTCOME: triggers.HANDLE_ERROR,
RenderFunctionalRequirement.ITERATION_LIMIT_EXCEEDED_OUTCOME: triggers.HANDLE_ERROR,
RunUnitTests.SUCCESSFUL_OUTCOME: triggers.MARK_UNIT_TESTS_PASSED,
RunUnitTests.FAILED_OUTCOME: triggers.MARK_UNIT_TESTS_FAILED,
RunUnitTests.UNRECOVERABLE_ERROR_OUTCOME: triggers.HANDLE_ERROR,
FixUnitTests.SUCCESSFUL_OUTCOME: triggers.MARK_UNIT_TESTS_READY,
RefactorCode.SUCCESSFUL_OUTCOME: triggers.REFACTOR_CODE,
RefactorCode.NO_FILES_REFACTORED_OUTCOME: triggers.PROCEED_FRID_PROCESSING,
RefactorCode.ITERATION_LIMIT_EXCEEDED_OUTCOME: triggers.PROCEED_FRID_PROCESSING,
CommitImplementationCodeChanges.SUCCESSFUL_OUTCOME: triggers.PROCEED_FRID_PROCESSING,
FinishFunctionalRequirement.SUCCESSFUL_OUTCOME: triggers.PROCEED_FRID_PROCESSING,
CreateDist.SUCCESSFUL_OUTCOME: triggers.FINISH_RENDER,
Expand All @@ -90,6 +92,8 @@ def get_action_result_triggers_map(self) -> Dict[str, str]:
RunConformanceTests.UNRECOVERABLE_ERROR_OUTCOME: triggers.HANDLE_ERROR,
FixConformanceTest.IMPLEMENTATION_CODE_NOT_UPDATED: triggers.MARK_CONFORMANCE_TESTS_READY,
FixConformanceTest.IMPLEMENTATION_CODE_UPDATED: triggers.MARK_UNIT_TESTS_READY,
FixConformanceTest.LIMIT_EXCEEDED_OUTCOME: triggers.HANDLE_ERROR,
FixConformanceTest.REGENERATE_CONFORMANCE_TESTS_OUTCOME: triggers.MARK_REGENERATION_OF_CONFORMANCE_TESTS,
CommitConformanceTestsChanges.SUCCESSFUL_OUTCOME_IMPLEMENTATION_UPDATED: triggers.MARK_NEXT_CONFORMANCE_TESTS_POSTPROCESSING_STEP,
CommitConformanceTestsChanges.SUCCESSFUL_OUTCOME_IMPLEMENTATION_NOT_UPDATED: triggers.PROCEED_FRID_PROCESSING,
SummarizeConformanceTests.SUCCESSFUL_OUTCOME: triggers.MARK_NEXT_CONFORMANCE_TESTS_POSTPROCESSING_STEP,
Expand Down Expand Up @@ -135,16 +139,9 @@ def get_processing_conformance_tests_states(self, render_context: RenderContext)
"name": States.CONFORMANCE_TESTING_INITIALISED.value,
"on_enter": render_context.start_conformance_tests_for_frid,
},
{
"name": States.CONFORMANCE_TEST_GENERATED.value,
"on_enter": render_context.start_testing_environment_preparation,
},
States.CONFORMANCE_TEST_GENERATED.value,
States.CONFORMANCE_TEST_ENV_PREPARED.value,
{
"name": States.CONFORMANCE_TEST_FAILED.value,
"on_enter": render_context.start_fixing_conformance_tests,
"on_exit": render_context.finish_fixing_conformance_tests,
},
States.CONFORMANCE_TEST_FAILED.value,
self.get_processing_unit_tests_states(
render_context, render_context._on_unit_test_limit_exceeded_in_conformance_tests
),
Expand All @@ -164,7 +161,6 @@ def get_states(self, render_context: RenderContext) -> List[Any]:
refactoring_code_states = {
"name": States.REFACTORING_CODE.value,
"initial": States.READY_FOR_REFACTORING.value,
"on_enter": render_context.start_refactoring_code,
"children": [
States.READY_FOR_REFACTORING.value,
self.get_processing_unit_tests_states(
Expand All @@ -183,10 +179,7 @@ def get_states(self, render_context: RenderContext) -> List[Any]:
"on_exit": render_context.finish_implementing_frid,
"children": [
{"name": States.STEP_COMPLETED.value},
{
"name": States.READY_FOR_FRID_IMPLEMENTATION.value,
"on_enter": render_context.check_frid_iteration_limit,
},
States.READY_FOR_FRID_IMPLEMENTATION.value,
self.get_processing_unit_tests_states(
render_context, render_context._on_unit_test_limit_exceeded_in_implementation
),
Expand Down
Loading