philosophers_prep/testers/test_12_philosophers_bonus.sh

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