Update unit tests
This commit is contained in:
@@ -1,158 +1,156 @@
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
import os
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
from sia.background_entry import BackgroundEntry
|
||||
|
||||
class BackgroundEntryTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
"""Set up test cases with fixed id and timestamp"""
|
||||
self.test_id = "test-id-1234"
|
||||
self.test_timestamp = datetime(2024, 10, 31, 12, 0, 0)
|
||||
|
||||
# Create temporary files for testing
|
||||
self.test_filename = "test_output.txt"
|
||||
self.counter_file = "counter.txt"
|
||||
self.cleanup_files()
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test files"""
|
||||
self.cleanup_files()
|
||||
|
||||
def cleanup_files(self):
|
||||
"""Helper to remove test files"""
|
||||
for filename in [self.test_filename, self.counter_file]:
|
||||
if os.path.exists(filename):
|
||||
os.remove(filename)
|
||||
|
||||
def wait_for_condition(self, entry: BackgroundEntry, condition: callable, timeout: float = 1.0) -> bool:
|
||||
"""Wait for a condition on the entry context to be met"""
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
entry.update()
|
||||
context = entry.generate_context()
|
||||
if condition(context):
|
||||
return True
|
||||
time.sleep(0.1)
|
||||
return False
|
||||
|
||||
def test_initialization(self):
|
||||
"""Test entry initialization"""
|
||||
entry = BackgroundEntry(self.test_id, self.test_timestamp, "echo test")
|
||||
context = entry.generate_context()
|
||||
|
||||
# Initial context should have script but no pid or exit_code
|
||||
self.assertEqual(context.text, "echo test")
|
||||
self.assertIsNone(context.get("pid"))
|
||||
self.assertIsNone(context.get("exit_code"))
|
||||
|
||||
def test_short_running_process(self):
|
||||
"""Test execution of a quick process"""
|
||||
entry = BackgroundEntry(self.test_id, self.test_timestamp, "echo 'test'")
|
||||
|
||||
# Wait for process to complete
|
||||
def is_complete(ctx):
|
||||
return ctx.get("exit_code") == "0"
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, is_complete))
|
||||
|
||||
# Verify output
|
||||
context = entry.generate_context()
|
||||
self.assertEqual(context.find("stdout").text, "test\n")
|
||||
self.assertEqual(context.find("stderr").text, "")
|
||||
|
||||
def test_continuous_output(self):
|
||||
"""Test process that generates continuous output"""
|
||||
script = (
|
||||
'python3 -c "'
|
||||
'import sys\n'
|
||||
'for i in range(3):\n'
|
||||
' sys.stdout.write(f\\\"count {i+1}\\n\\\")\n'
|
||||
' sys.stdout.flush()\n'
|
||||
'"'
|
||||
)
|
||||
|
||||
entry = BackgroundEntry(self.test_id, self.test_timestamp, script)
|
||||
|
||||
# Wait for process to complete
|
||||
def has_all_output(ctx):
|
||||
stdout = ctx.find("stdout").text
|
||||
return all(f"count {i}" in stdout for i in range(1, 4))
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, has_all_output))
|
||||
|
||||
def test_process_failure(self):
|
||||
"""Test handling of process failures"""
|
||||
entry = BackgroundEntry(self.test_id, self.test_timestamp, "nonexistentcommand")
|
||||
|
||||
# Wait for process to fail
|
||||
def has_failed(ctx):
|
||||
return ctx.get("exit_code") is not None and ctx.get("exit_code") != "0"
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, has_failed))
|
||||
|
||||
# Verify error captured
|
||||
context = entry.generate_context()
|
||||
self.assertTrue(len(context.find("stderr").text) > 0)
|
||||
self.assertEqual(context.find("stdout").text, "")
|
||||
|
||||
def test_cleanup_running_process(self):
|
||||
"""Test cleanup of running process"""
|
||||
entry = BackgroundEntry(self.test_id, self.test_timestamp, "sleep 10")
|
||||
|
||||
# Wait for process to start
|
||||
def has_started(ctx):
|
||||
return ctx.get("pid") is not None
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, has_started))
|
||||
|
||||
# Get PID before cleanup
|
||||
context = entry.generate_context()
|
||||
pid = int(context.get("pid"))
|
||||
|
||||
# Clean up and verify process terminated
|
||||
entry.cleanup()
|
||||
|
||||
# Process should no longer exist
|
||||
time.sleep(0.1) # Give process time to terminate
|
||||
with self.assertRaises(ProcessLookupError):
|
||||
os.kill(pid, 0)
|
||||
|
||||
def test_cleanup_completed_process(self):
|
||||
"""Test cleanup of already completed process"""
|
||||
entry = BackgroundEntry(self.test_id, self.test_timestamp, "echo test")
|
||||
|
||||
# Wait for completion
|
||||
def is_complete(ctx):
|
||||
return ctx.get("exit_code") == "0"
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, is_complete))
|
||||
|
||||
# Cleanup should be safe
|
||||
entry.cleanup()
|
||||
|
||||
def test_multiple_cleanup_calls(self):
|
||||
"""Test that multiple cleanup calls are safe"""
|
||||
entry = BackgroundEntry(self.test_id, self.test_timestamp, "sleep 10")
|
||||
|
||||
# Wait for process to start
|
||||
def has_started(ctx):
|
||||
return ctx.get("pid") is not None
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, has_started))
|
||||
|
||||
# Get PID before cleanup
|
||||
context = entry.generate_context()
|
||||
pid = int(context.get("pid"))
|
||||
|
||||
# Multiple cleanups should be safe
|
||||
entry.cleanup()
|
||||
entry.cleanup()
|
||||
entry.cleanup()
|
||||
|
||||
# Process should no longer exist
|
||||
time.sleep(0.1)
|
||||
with self.assertRaises(ProcessLookupError):
|
||||
import unittest
|
||||
import os
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
from sia.entry.background_entry import BackgroundEntry
|
||||
|
||||
class BackgroundEntryTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
"""Set up test cases with fixed id"""
|
||||
self.test_id = "test-id-1234"
|
||||
|
||||
# Create temporary files for testing
|
||||
self.test_filename = "test_output.txt"
|
||||
self.counter_file = "counter.txt"
|
||||
self.cleanup_files()
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test files"""
|
||||
self.cleanup_files()
|
||||
|
||||
def cleanup_files(self):
|
||||
"""Helper to remove test files"""
|
||||
for filename in [self.test_filename, self.counter_file]:
|
||||
if os.path.exists(filename):
|
||||
os.remove(filename)
|
||||
|
||||
def wait_for_condition(self, entry: BackgroundEntry, condition: callable, timeout: float = 1.0) -> bool:
|
||||
"""Wait for a condition on the entry context to be met"""
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
entry.update()
|
||||
context = entry.generate_context()
|
||||
if condition(context):
|
||||
return True
|
||||
time.sleep(0.1)
|
||||
return False
|
||||
|
||||
def test_initialization(self):
|
||||
"""Test entry initialization"""
|
||||
entry = BackgroundEntry(self.test_id, os.getcwd(), "echo test")
|
||||
context = entry.generate_context()
|
||||
|
||||
# Initial context should have script but no pid or exit_code
|
||||
self.assertEqual(context.text, "echo test")
|
||||
self.assertIsNone(context.get("pid"))
|
||||
self.assertIsNone(context.get("exit_code"))
|
||||
|
||||
def test_short_running_process(self):
|
||||
"""Test execution of a quick process"""
|
||||
entry = BackgroundEntry(self.test_id, os.getcwd(), "echo 'test'")
|
||||
|
||||
# Wait for process to complete
|
||||
def is_complete(ctx):
|
||||
return ctx.get("exit_code") == "0"
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, is_complete))
|
||||
|
||||
# Verify output
|
||||
context = entry.generate_context()
|
||||
self.assertEqual(context.find("stdout").text, "test\n")
|
||||
self.assertEqual(context.find("stderr").text, "")
|
||||
|
||||
def test_continuous_output(self):
|
||||
"""Test process that generates continuous output"""
|
||||
script = (
|
||||
'python3 -c "'
|
||||
'import sys\n'
|
||||
'for i in range(3):\n'
|
||||
' sys.stdout.write(f\\\"count {i+1}\\n\\\")\n'
|
||||
' sys.stdout.flush()\n'
|
||||
'"'
|
||||
)
|
||||
|
||||
entry = BackgroundEntry(self.test_id, os.getcwd(), script)
|
||||
|
||||
# Wait for process to complete
|
||||
def has_all_output(ctx):
|
||||
stdout = ctx.find("stdout").text
|
||||
return all(f"count {i}" in stdout for i in range(1, 4))
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, has_all_output))
|
||||
|
||||
def test_process_failure(self):
|
||||
"""Test handling of process failures"""
|
||||
entry = BackgroundEntry(self.test_id, os.getcwd(), "nonexistentcommand")
|
||||
|
||||
# Wait for process to fail
|
||||
def has_failed(ctx):
|
||||
return ctx.get("exit_code") is not None and ctx.get("exit_code") != "0"
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, has_failed))
|
||||
|
||||
# Verify error captured
|
||||
context = entry.generate_context()
|
||||
self.assertTrue(len(context.find("stderr").text) > 0)
|
||||
self.assertEqual(context.find("stdout").text, "")
|
||||
|
||||
def test_cleanup_running_process(self):
|
||||
"""Test cleanup of running process"""
|
||||
entry = BackgroundEntry(self.test_id, os.getcwd(), "sleep 10")
|
||||
|
||||
# Wait for process to start
|
||||
def has_started(ctx):
|
||||
return ctx.get("pid") is not None
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, has_started))
|
||||
|
||||
# Get PID before cleanup
|
||||
context = entry.generate_context()
|
||||
pid = int(context.get("pid"))
|
||||
|
||||
# Clean up and verify process terminated
|
||||
entry.cleanup()
|
||||
|
||||
# Process should no longer exist
|
||||
time.sleep(0.1) # Give process time to terminate
|
||||
with self.assertRaises(ProcessLookupError):
|
||||
os.kill(pid, 0)
|
||||
|
||||
def test_cleanup_completed_process(self):
|
||||
"""Test cleanup of already completed process"""
|
||||
entry = BackgroundEntry(self.test_id, os.getcwd(), "echo test")
|
||||
|
||||
# Wait for completion
|
||||
def is_complete(ctx):
|
||||
return ctx.get("exit_code") == "0"
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, is_complete))
|
||||
|
||||
# Cleanup should be safe
|
||||
entry.cleanup()
|
||||
|
||||
def test_multiple_cleanup_calls(self):
|
||||
"""Test that multiple cleanup calls are safe"""
|
||||
entry = BackgroundEntry(self.test_id, os.getcwd(), "sleep 10")
|
||||
|
||||
# Wait for process to start
|
||||
def has_started(ctx):
|
||||
return ctx.get("pid") is not None
|
||||
|
||||
self.assertTrue(self.wait_for_condition(entry, has_started))
|
||||
|
||||
# Get PID before cleanup
|
||||
context = entry.generate_context()
|
||||
pid = int(context.get("pid"))
|
||||
|
||||
# Multiple cleanups should be safe
|
||||
entry.cleanup()
|
||||
entry.cleanup()
|
||||
entry.cleanup()
|
||||
|
||||
# Process should no longer exist
|
||||
time.sleep(0.1)
|
||||
with self.assertRaises(ProcessLookupError):
|
||||
os.kill(pid, 0)
|
||||
Reference in New Issue
Block a user