Skip to content

Commit

Permalink
handle when no cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
vysakh0 committed Jul 19, 2024
1 parent 4129059 commit b46de17
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 22 deletions.
27 changes: 19 additions & 8 deletions src/drd/cli/monitor/output_monitor.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import re
import threading
import time
import re
import select
import sys
from ...utils import print_info
from ...utils import print_info, print_error

MAX_RETRIES = 3


class OutputMonitor:
def __init__(self, monitor):
self.monitor = monitor
self.thread = None
self.last_output_time = time.time()
self.last_output_time = None
self.idle_prompt_shown = False
self.retry_count = 0

def start(self):
self.thread = threading.Thread(
Expand All @@ -29,15 +31,23 @@ def _check_for_errors(self, line, error_buffer):
def _monitor_output(self):
error_buffer = []
iteration = 0
self.last_output_time = time.time() # Initialize last_output_time here
self.last_output_time = time.time()
while not self.monitor.should_stop.is_set():
iteration += 1

if self.monitor.process.poll() is not None and not self.monitor.processing_input.is_set():
if not self.monitor.restart_requested.is_set():
print_info(
"Server process ended unexpectedly. Restarting...")
self.monitor.perform_restart()
print_info("Server process ended unexpectedly.")
if self.retry_count < MAX_RETRIES:
print_info(
f"Restarting... (Attempt {self.retry_count + 1}/{MAX_RETRIES})")
self.monitor.perform_restart()
self.retry_count += 1
else:
print_error(
f"Server failed to start after {MAX_RETRIES} attempts. Exiting.")
self.monitor.stop()
break
continue

ready, _, _ = select.select(
Expand All @@ -52,6 +62,7 @@ def _monitor_output(self):
error_buffer.pop(0)
self.last_output_time = time.time()
self.idle_prompt_shown = False
self.retry_count = 0 # Reset retry count on successful output

if not self.monitor.processing_input.is_set():
self._check_for_errors(line, error_buffer)
Expand Down
60 changes: 49 additions & 11 deletions src/drd/cli/monitor/server_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from queue import Queue
from .input_handler import InputHandler
from .output_monitor import OutputMonitor
from ...utils import print_info, print_success
from ...utils import print_info, print_success, print_error

MAX_RETRIES = 3


class DevServerMonitor:
Expand All @@ -17,25 +19,29 @@ def __init__(self, project_dir: str, error_handlers: dict, command: str):
self.restart_requested = threading.Event()
self.user_input_queue = Queue()
self.processing_input = threading.Event()

self.input_handler = InputHandler(self)
self.output_monitor = OutputMonitor(self)
self.retry_count = 0

def start(self):
self.should_stop.clear()
self.restart_requested.clear()

print_info(f"Starting server with command: {self.command}")
self.process = start_process(self.command, self.project_dir)

self.output_monitor.start()
self.input_handler.start()
try:
self.process = start_process(self.command, self.project_dir)
self.output_monitor.start()
self.input_handler.start()
except Exception as e:
print_error(f"Failed to start server process: {str(e)}")
self.stop()

def stop(self):
print_info("Stopping server monitor...")
self.should_stop.set()
if self.process:
self.process.terminate()
self.process.wait()
print_info("Server monitor stopped.")

def request_restart(self):
self.restart_requested.set()
Expand All @@ -45,10 +51,42 @@ def perform_restart(self):
if self.process:
self.process.terminate()
self.process.wait()
self.process = start_process(self.command, self.project_dir)
self.restart_requested.clear()
print_success("Server restarted successfully.")
print_info("Waiting for server output...")

try:
self.process = start_process(self.command, self.project_dir)
self.retry_count = 0
self.restart_requested.clear()
print_success("Server restarted successfully.")
print_info("Waiting for server output...")
except Exception as e:
print_error(f"Failed to restart server process: {str(e)}")
self.retry_count += 1
if self.retry_count >= MAX_RETRIES:
print_error(
f"Server failed to start after {MAX_RETRIES} attempts. Exiting.")
self.stop()
else:
print_info(
f"Retrying... (Attempt {self.retry_count + 1}/{MAX_RETRIES})")
self.request_restart()

def _start_process(self, command):
try:
return subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
stdin=subprocess.PIPE,
text=True,
bufsize=1,
universal_newlines=True,
shell=True,
cwd=self.project_dir
)
except Exception as e:
print_error(f"Failed to start server process: {str(e)}")
self.stop()
return None


def start_process(command, cwd):
Expand Down
31 changes: 28 additions & 3 deletions tests/cli/monitor/test_dev_server_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ def setUp(self):
@patch('drd.cli.monitor.server_monitor.start_process')
@patch('drd.cli.monitor.server_monitor.InputHandler')
@patch('drd.cli.monitor.server_monitor.OutputMonitor')
def test_start(self, mock_output_monitor, mock_input_handler, mock_start_process):
@patch('os.path.exists', return_value=True)
def test_start(self, mock_exists, mock_output_monitor, mock_input_handler, mock_start_process):
monitor = DevServerMonitor(
self.project_dir, self.error_handlers, self.test_command)
monitor.start()
Expand All @@ -41,7 +42,8 @@ def test_stop(self, mock_start_process):
mock_process.wait.assert_called_once()

@patch('drd.cli.monitor.server_monitor.start_process')
def test_perform_restart(self, mock_start_process):
@patch('os.path.exists', return_value=True)
def test_perform_restart(self, mock_exists, mock_start_process):
monitor = DevServerMonitor(
self.project_dir, self.error_handlers, self.test_command)
mock_process = MagicMock()
Expand All @@ -53,7 +55,6 @@ def test_perform_restart(self, mock_start_process):
mock_process.wait.assert_called_once()
mock_start_process.assert_called_once_with(
self.test_command, self.project_dir)
self.assertFalse(monitor.restart_requested.is_set())

@patch('subprocess.Popen')
def test_start_process(self, mock_popen):
Expand All @@ -69,3 +70,27 @@ def test_start_process(self, mock_popen):
shell=True,
cwd="/test/dir"
)

@patch('drd.cli.monitor.server_monitor.start_process')
@patch('os.path.exists', return_value=True)
def test_invalid_command_restart_limit(self, mock_exists, mock_start_process):
MAX_RETRIES = 3 # Assuming this is the value in your actual code
invalid_command = 'invalid_command'
monitor = DevServerMonitor(
self.project_dir, self.error_handlers, invalid_command)

# Simulate process failing to start
mock_start_process.side_effect = Exception("Failed to start process")

# Start the monitor
monitor.start()

# Simulate OutputMonitor behavior
for _ in range(MAX_RETRIES):
monitor.perform_restart()

# Check that start_process was called MAX_RETRIES + 1 times (initial + retries)
assert mock_start_process.call_count == MAX_RETRIES + 1

# Check that the monitor has stopped
assert monitor.should_stop.is_set()

0 comments on commit b46de17

Please sign in to comment.