#!/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