236 lines
6.6 KiB
Bash
Executable File
236 lines
6.6 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Tester for philosophers_bonus exercise
|
|
# Tests process-based philosophers with semaphores
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
EXERCISE="philosophers_bonus"
|
|
PASSED=0
|
|
FAILED=0
|
|
|
|
echo "========================================"
|
|
echo "Testing: $EXERCISE (Bonus)"
|
|
echo "========================================"
|
|
|
|
if [ ! -f "./$EXERCISE" ]; then
|
|
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
|
|
echo "Please compile: gcc -Wall -Wextra -Werror -pthread philosophers_bonus.c -o philosophers_bonus"
|
|
exit 1
|
|
fi
|
|
|
|
# Clean up any leftover semaphores
|
|
echo "Cleaning up old semaphores..."
|
|
for i in {0..10}; do
|
|
sem_unlink "/philo_sem_$i" 2>/dev/null
|
|
sem_unlink "/fork_$i" 2>/dev/null
|
|
sem_unlink "/forks" 2>/dev/null
|
|
sem_unlink "/print" 2>/dev/null
|
|
sem_unlink "/philo_forks" 2>/dev/null
|
|
done
|
|
|
|
# Test 1: Basic execution (no death)
|
|
echo -n "Test 1: Basic case - 5 800 200 200... "
|
|
timeout 12 ./$EXERCISE 5 800 200 200 > /tmp/bonus_test1.txt 2>&1
|
|
EXIT_CODE=$?
|
|
|
|
if [ $EXIT_CODE -eq 0 ]; then
|
|
OUTPUT=$(cat /tmp/bonus_test1.txt)
|
|
if ! echo "$OUTPUT" | grep -qiE "die"; then
|
|
echo -e "${GREEN}✓ PASSED${NC}"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${RED}✗ FAILED${NC}"
|
|
echo "A philosopher died when they shouldn't have"
|
|
((FAILED++))
|
|
fi
|
|
else
|
|
echo -e "${RED}✗ FAILED (timeout or crash)${NC}"
|
|
((FAILED++))
|
|
fi
|
|
|
|
# Test 2: Check for process creation (not threads)
|
|
echo -n "Test 2: Uses processes (not threads)... "
|
|
if grep -q "fork()" philosophers_bonus.c 2>/dev/null; then
|
|
echo -e "${GREEN}✓ PASSED${NC} (fork() found in source)"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${RED}✗ FAILED${NC}"
|
|
echo "Bonus should use fork() for processes"
|
|
((FAILED++))
|
|
fi
|
|
|
|
# Test 3: Check for semaphore usage
|
|
echo -n "Test 3: Uses semaphores... "
|
|
if grep -qE "sem_(open|wait|post)" philosophers_bonus.c 2>/dev/null; then
|
|
echo -e "${GREEN}✓ PASSED${NC}"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${RED}✗ FAILED${NC}"
|
|
echo "Bonus should use POSIX semaphores"
|
|
((FAILED++))
|
|
fi
|
|
|
|
# Test 4: Death scenario
|
|
echo -n "Test 4: Death detection - 4 310 200 100... "
|
|
timeout 5 ./$EXERCISE 4 310 200 100 > /tmp/bonus_test4.txt 2>&1
|
|
OUTPUT=$(cat /tmp/bonus_test4.txt)
|
|
|
|
if echo "$OUTPUT" | grep -qiE "die"; then
|
|
echo -e "${GREEN}✓ PASSED${NC}"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${RED}✗ FAILED${NC}"
|
|
echo "Expected a philosopher to die"
|
|
((FAILED++))
|
|
fi
|
|
|
|
# Test 5: Meal limit functionality
|
|
echo -n "Test 5: Meal limit - 5 800 200 200 7... "
|
|
timeout 20 ./$EXERCISE 5 800 200 200 7 > /tmp/bonus_test5.txt 2>&1
|
|
EXIT_CODE=$?
|
|
|
|
if [ $EXIT_CODE -eq 0 ]; then
|
|
echo -e "${GREEN}✓ PASSED${NC}"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${RED}✗ FAILED${NC}"
|
|
((FAILED++))
|
|
fi
|
|
|
|
# Test 6: Single philosopher
|
|
echo -n "Test 6: Single philosopher - 1 800 200 200... "
|
|
timeout 3 ./$EXERCISE 1 800 200 200 > /tmp/bonus_test6.txt 2>&1
|
|
OUTPUT=$(cat /tmp/bonus_test6.txt)
|
|
|
|
if echo "$OUTPUT" | grep -qiE "die"; then
|
|
echo -e "${GREEN}✓ PASSED${NC} (died as expected)"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${RED}✗ FAILED${NC}"
|
|
echo "Single philosopher should die"
|
|
((FAILED++))
|
|
fi
|
|
|
|
# Test 7: Invalid arguments
|
|
echo -n "Test 7: Invalid argument handling... "
|
|
timeout 2 ./$EXERCISE 0 800 200 200 > /dev/null 2>&1
|
|
EXIT1=$?
|
|
timeout 2 ./$EXERCISE 5 -100 200 200 > /dev/null 2>&1
|
|
EXIT2=$?
|
|
timeout 2 ./$EXERCISE > /dev/null 2>&1
|
|
EXIT3=$?
|
|
|
|
if [ $EXIT1 -ne 0 ] && [ $EXIT2 -ne 0 ] && [ $EXIT3 -ne 0 ]; then
|
|
echo -e "${GREEN}✓ PASSED${NC}"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${RED}✗ FAILED${NC}"
|
|
echo "Program should reject invalid arguments"
|
|
((FAILED++))
|
|
fi
|
|
|
|
# Test 8: Process cleanup (no zombie processes)
|
|
echo -n "Test 8: Process cleanup... "
|
|
./$EXERCISE 5 800 200 200 3 > /dev/null 2>&1 &
|
|
TEST_PID=$!
|
|
sleep 5
|
|
ZOMBIE_COUNT=$(ps aux | grep defunct | grep -v grep | wc -l)
|
|
kill $TEST_PID 2>/dev/null
|
|
wait $TEST_PID 2>/dev/null
|
|
sleep 1
|
|
ZOMBIE_AFTER=$(ps aux | grep defunct | grep -v grep | wc -l)
|
|
|
|
if [ "$ZOMBIE_AFTER" -eq 0 ]; then
|
|
echo -e "${GREEN}✓ PASSED${NC}"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${RED}✗ FAILED${NC}"
|
|
echo "Zombie processes detected - check waitpid() usage"
|
|
((FAILED++))
|
|
fi
|
|
|
|
# Test 9: Semaphore cleanup
|
|
echo -n "Test 9: Semaphore cleanup... "
|
|
# Run program and interrupt it
|
|
timeout 3 ./$EXERCISE 5 800 200 200 > /dev/null 2>&1 &
|
|
TEST_PID=$!
|
|
sleep 1
|
|
kill -SIGINT $TEST_PID 2>/dev/null
|
|
wait $TEST_PID 2>/dev/null
|
|
sleep 1
|
|
|
|
# Check if semaphores are cleaned up (this is OS-dependent)
|
|
# On Linux, check /dev/shm
|
|
SEM_COUNT=0
|
|
if [ -d /dev/shm ]; then
|
|
SEM_COUNT=$(ls /dev/shm/sem.* 2>/dev/null | wc -l)
|
|
fi
|
|
|
|
if [ "$SEM_COUNT" -eq 0 ]; then
|
|
echo -e "${GREEN}✓ PASSED${NC}"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${YELLOW}⚠ WARNING${NC}"
|
|
echo "Found $SEM_COUNT semaphores in /dev/shm (may need manual cleanup)"
|
|
echo "Use sem_unlink() and signal handlers for proper cleanup"
|
|
((PASSED++))
|
|
fi
|
|
|
|
# Test 10: Statistics/final report
|
|
echo -n "Test 10: Final statistics... "
|
|
OUTPUT=$(cat /tmp/bonus_test5.txt)
|
|
if echo "$OUTPUT" | grep -qiE "(statistic|report|summary|total|finish)"; then
|
|
echo -e "${GREEN}✓ PASSED${NC}"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${YELLOW}⚠ OPTIONAL${NC}"
|
|
echo "Final statistics are optional but recommended"
|
|
((PASSED++))
|
|
fi
|
|
|
|
# Test 11: No data races (helgrind on parent process)
|
|
if command -v valgrind &> /dev/null; then
|
|
echo -n "Test 11: Parent process race check... "
|
|
timeout 15 valgrind --tool=helgrind --trace-children=no ./$EXERCISE 5 800 200 200 3 > /tmp/helgrind_bonus.txt 2>&1
|
|
HELGRIND_OUTPUT=$(cat /tmp/helgrind_bonus.txt 2>/dev/null)
|
|
|
|
if echo "$HELGRIND_OUTPUT" | grep -q "ERROR SUMMARY: 0 errors"; then
|
|
echo -e "${GREEN}✓ PASSED${NC}"
|
|
((PASSED++))
|
|
else
|
|
echo -e "${YELLOW}⚠ WARNING${NC}"
|
|
echo "Helgrind reported issues (check /tmp/helgrind_bonus.txt)"
|
|
((PASSED++))
|
|
fi
|
|
rm -f /tmp/helgrind_bonus.txt
|
|
else
|
|
echo -e "${YELLOW}⊘ Test 11: Valgrind not installed, skipping${NC}"
|
|
fi
|
|
|
|
# Cleanup
|
|
rm -f /tmp/bonus_test*.txt
|
|
killall philosophers_bonus 2>/dev/null
|
|
|
|
# Clean up semaphores again
|
|
for i in {0..10}; do
|
|
sem_unlink "/philo_sem_$i" 2>/dev/null
|
|
sem_unlink "/fork_$i" 2>/dev/null
|
|
sem_unlink "/forks" 2>/dev/null
|
|
sem_unlink "/print" 2>/dev/null
|
|
sem_unlink "/philo_forks" 2>/dev/null
|
|
done
|
|
|
|
# Summary
|
|
echo "========================================"
|
|
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
|
|
echo "========================================"
|
|
echo ""
|
|
echo "Note: Bonus uses processes and semaphores instead of threads and mutexes"
|
|
echo "Make sure to properly clean up semaphores and handle signals!"
|
|
|
|
[ $FAILED -eq 0 ] && exit 0 || exit 1
|