Increased testers funcionality: now testers checks for executables on rendu folder

This commit is contained in:
Rui Ribeiro 2025-10-07 20:27:39 +01:00
parent 00a1df51bb
commit f30d1e67b5
13 changed files with 572 additions and 723 deletions

View File

@ -8,7 +8,8 @@ YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
TESTER_DIR="testers"
# Get the directory where this script is located, which is the testers directory.
TESTER_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
echo "========================================"
echo -e "${BLUE}Philosophers Preparation - Test Suite${NC}"
@ -23,11 +24,11 @@ run_test() {
local test_script=$1
local test_name=$2
if [ -f "$TESTER_DIR/$test_script" ]; then
if [ -f "$test_script" ]; then
echo ""
echo -e "${BLUE}▶ Running: $test_name${NC}"
echo "----------------------------------------"
bash "$TESTER_DIR/$test_script"
bash "$test_script"
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
@ -39,7 +40,7 @@ run_test() {
fi
echo ""
else
echo -e "${YELLOW}⊘ Skipping $test_name (tester not found)${NC}"
echo -e "${YELLOW}⊘ Skipping $test_name (tester not found at $test_script)${NC}"
echo ""
fi
}
@ -47,18 +48,18 @@ run_test() {
# Check if specific test is requested
if [ $# -eq 1 ]; then
case $1 in
1) run_test "test_1_thread_basics.sh" "Exercise 1: Thread Basics" ;;
2) run_test "test_2_mutex_basics.sh" "Exercise 2: Mutex Basics" ;;
3) run_test "test_3_precise_timing.sh" "Exercise 3: Precise Timing" ;;
4) run_test "test_4_state_monitor.sh" "Exercise 4: State Monitor" ;;
5) run_test "test_5_producer_consumer.sh" "Exercise 5: Producer-Consumer" ;;
6) run_test "test_6_deadlock_demo.sh" "Exercise 6: Deadlock Demo" ;;
7) run_test "test_7_limited_resources.sh" "Exercise 7: Limited Resources" ;;
8) run_test "test_8_simple_philosophers.sh" "Exercise 8: Simple Philosophers" ;;
9) run_test "test_9_death_monitor.sh" "Exercise 9: Death Monitor" ;;
10) run_test "test_10_philosophers_args.sh" "Exercise 10: Philosophers Args" ;;
11) run_test "test_11_race_detector.sh" "Exercise 11: Race Detector" ;;
12) run_test "test_12_philosophers_bonus.sh" "Exercise 12: Philosophers Bonus" ;;
1) run_test "$TESTER_DIR/test_1_thread_basics.sh" "Exercise 1: Thread Basics" ;;
2) run_test "$TESTER_DIR/test_2_mutex_basics.sh" "Exercise 2: Mutex Basics" ;;
3) run_test "$TESTER_DIR/test_3_precise_timing.sh" "Exercise 3: Precise Timing" ;;
4) run_test "$TESTER_DIR/test_4_state_monitor.sh" "Exercise 4: State Monitor" ;;
5) run_test "$TESTER_DIR/test_5_producer_consumer.sh" "Exercise 5: Producer-Consumer" ;;
6) run_test "$TESTER_DIR/test_6_deadlock_demo.sh" "Exercise 6: Deadlock Demo" ;;
7) run_test "$TESTER_DIR/test_7_limited_resources.sh" "Exercise 7: Limited Resources" ;;
8) run_test "$TESTER_DIR/test_8_simple_philosophers.sh" "Exercise 8: Simple Philosophers" ;;
9) run_test "$TESTER_DIR/test_9_death_monitor.sh" "Exercise 9: Death Monitor" ;;
10) run_test "$TESTER_DIR/test_10_philosophers_args.sh" "Exercise 10: Philosophers Args" ;;
11) run_test "$TESTER_DIR/test_11_race_detector.sh" "Exercise 11: Race Detector" ;;
12) run_test "$TESTER_DIR/test_12_philosophers_bonus.sh" "Exercise 12: Philosophers Bonus" ;;
*)
echo "Usage: $0 [exercise_number]"
echo "Example: $0 1 (to test only exercise 1)"
@ -68,18 +69,18 @@ if [ $# -eq 1 ]; then
esac
else
# Run all tests
run_test "test_1_thread_basics.sh" "Exercise 1: Thread Basics"
run_test "test_2_mutex_basics.sh" "Exercise 2: Mutex Basics"
run_test "test_3_precise_timing.sh" "Exercise 3: Precise Timing"
run_test "test_4_state_monitor.sh" "Exercise 4: State Monitor"
run_test "test_5_producer_consumer.sh" "Exercise 5: Producer-Consumer"
run_test "test_6_deadlock_demo.sh" "Exercise 6: Deadlock Demo"
run_test "test_7_limited_resources.sh" "Exercise 7: Limited Resources"
run_test "test_8_simple_philosophers.sh" "Exercise 8: Simple Philosophers"
run_test "test_9_death_monitor.sh" "Exercise 9: Death Monitor"
run_test "test_10_philosophers_args.sh" "Exercise 10: Philosophers Args"
run_test "test_11_race_detector.sh" "Exercise 11: Race Detector"
run_test "test_12_philosophers_bonus.sh" "Exercise 12: Philosophers Bonus"
run_test "$TESTER_DIR/test_1_thread_basics.sh" "Exercise 1: Thread Basics"
run_test "$TESTER_DIR/test_2_mutex_basics.sh" "Exercise 2: Mutex Basics"
run_test "$TESTER_DIR/test_3_precise_timing.sh" "Exercise 3: Precise Timing"
run_test "$TESTER_DIR/test_4_state_monitor.sh" "Exercise 4: State Monitor"
run_test "$TESTER_DIR/test_5_producer_consumer.sh" "Exercise 5: Producer-Consumer"
run_test "$TESTER_DIR/test_6_deadlock_demo.sh" "Exercise 6: Deadlock Demo"
run_test "$TESTER_DIR/test_7_limited_resources.sh" "Exercise 7: Limited Resources"
run_test "$TESTER_DIR/test_8_simple_philosophers.sh" "Exercise 8: Simple Philosophers"
run_test "$TESTER_DIR/test_9_death_monitor.sh" "Exercise 9: Death Monitor"
run_test "$TESTER_DIR/test_10_philosophers_args.sh" "Exercise 10: Philosophers Args"
run_test "$TESTER_DIR/test_11_race_detector.sh" "Exercise 11: Race Detector"
run_test "$TESTER_DIR/test_12_philosophers_bonus.sh" "Exercise 12: Philosophers Bonus"
fi
# Final summary

View File

@ -9,22 +9,34 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="philosophers_args"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE (Complete Philosophers)"
echo "========================================"
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile: gcc -Wall -Wextra -Werror -pthread philosophers_args.c -o philosophers_args"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread philosophers_args.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Basic execution (no one should die)
echo -n "Test 1: Basic case - 5 800 200 200... "
timeout 10 ./$EXERCISE 5 800 200 200 > /tmp/philo_test1.txt 2>&1
timeout 10 "$EXE_PATH" 5 800 200 200 > /tmp/philo_test1.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
@ -44,7 +56,7 @@ fi
# Test 2: Death scenario (should die)
echo -n "Test 2: Death detection - 4 310 200 100... "
timeout 5 ./$EXERCISE 4 310 200 100 > /tmp/philo_test2.txt 2>&1
timeout 5 "$EXE_PATH" 4 310 200 100 > /tmp/philo_test2.txt 2>&1
OUTPUT=$(cat /tmp/philo_test2.txt)
if echo "$OUTPUT" | grep -qiE "die"; then
@ -58,7 +70,7 @@ fi
# Test 3: Meals limit
echo -n "Test 3: Meal limit - 5 800 200 200 7... "
timeout 15 ./$EXERCISE 5 800 200 200 7 > /tmp/philo_test3.txt 2>&1
timeout 15 "$EXE_PATH" 5 800 200 200 7 > /tmp/philo_test3.txt 2>&1
EXIT_CODE=$?
OUTPUT=$(cat /tmp/philo_test3.txt)
@ -78,7 +90,7 @@ fi
# Test 4: Single philosopher (should die)
echo -n "Test 4: Single philosopher - 1 800 200 200... "
timeout 3 ./$EXERCISE 1 800 200 200 > /tmp/philo_test4.txt 2>&1
timeout 3 "$EXE_PATH" 1 800 200 200 > /tmp/philo_test4.txt 2>&1
OUTPUT=$(cat /tmp/philo_test4.txt)
if echo "$OUTPUT" | grep -qiE "die"; then
@ -92,11 +104,11 @@ fi
# Test 5: Invalid arguments
echo -n "Test 5: Invalid argument handling... "
timeout 2 ./$EXERCISE 0 800 200 200 > /dev/null 2>&1
timeout 2 "$EXE_PATH" 0 800 200 200 > /dev/null 2>&1
EXIT1=$?
timeout 2 ./$EXERCISE 5 -100 200 200 > /dev/null 2>&1
timeout 2 "$EXE_PATH" 5 -100 200 200 > /dev/null 2>&1
EXIT2=$?
timeout 2 ./$EXERCISE abc def ghi > /dev/null 2>&1
timeout 2 "$EXE_PATH" abc def ghi > /dev/null 2>&1
EXIT3=$?
if [ $EXIT1 -ne 0 ] && [ $EXIT2 -ne 0 ] && [ $EXIT3 -ne 0 ]; then
@ -108,129 +120,68 @@ else
((FAILED++))
fi
# Test 6: No data races
echo -n "Test 6: All actions for correct philosopher... "
OUTPUT=$(cat /tmp/philo_test1.txt)
# Check that philosopher IDs are valid (0 to 4 for 5 philosophers)
INVALID_ID=$(echo "$OUTPUT" | grep -oE "Philosopher [0-9]+" | grep -vE "Philosopher [0-4]" | wc -l)
if [ "$INVALID_ID" -eq 0 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Found invalid philosopher IDs"
((FAILED++))
fi
# Test 7: Timestamp ordering
echo -n "Test 7: Timestamps are increasing... "
TIMESTAMPS=$(echo "$OUTPUT" | grep -oE "\[[0-9]+\]" | tr -d '[]' | head -20)
DECREASING=false
PREV=0
for TS in $TIMESTAMPS; do
if [ "$TS" -lt "$PREV" ]; then
DECREASING=true
break
fi
PREV=$TS
done
if [ "$DECREASING" = false ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Timestamps should be monotonically increasing"
((FAILED++))
fi
# Test 8: Death detection timing
echo -n "Test 8: Death detection is timely... "
timeout 5 ./$EXERCISE 4 410 200 200 > /tmp/philo_test8.txt 2>&1
OUTPUT=$(cat /tmp/philo_test8.txt)
DEATH_LINE=$(echo "$OUTPUT" | grep -iE "die" | head -1)
if [ -n "$DEATH_LINE" ]; then
DEATH_TIME=$(echo "$DEATH_LINE" | grep -oE "\[[0-9]+\]" | tr -d '[]')
if [ "$DEATH_TIME" -le 430 ]; then
echo -e "${GREEN}✓ PASSED${NC} (died at ${DEATH_TIME}ms)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Death detected too late: ${DEATH_TIME}ms (should be ~410ms)"
((FAILED++))
fi
else
echo -e "${YELLOW}⚠ UNCLEAR${NC} (no death detected)"
((PASSED++))
fi
# Test 9: Thread sanitizer
if command -v gcc &> /dev/null; then
echo -n "Test 9: Data race detection... "
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g philosophers_args.c -o philosophers_args_tsan 2>/dev/null
if [ $? -eq 0 ]; then
TSAN_OUTPUT=$(timeout 8 ./philosophers_args_tsan 5 800 200 200 5 2>&1)
if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Data race detected!"
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f philosophers_args_tsan
else
echo -e "${YELLOW}⊘ Could not compile with thread sanitizer${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 9: gcc not available, skipping${NC}"
fi
# Test 10: Stress test - odd number
echo -n "Test 10: Odd philosophers (3) - 3 410 200 200... "
timeout 8 ./$EXERCISE 3 410 200 200 > /tmp/philo_test10.txt 2>&1
OUTPUT=$(cat /tmp/philo_test10.txt)
if ! echo "$OUTPUT" | grep -qiE "die"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Philosophers died when they shouldn't (check timing logic)"
((FAILED++))
fi
# Test 11: Memory leak check
# Test 6: Check for memory leaks with valgrind
if command -v valgrind &> /dev/null; then
echo -n "Test 11: Memory leak check... "
VALGRIND_OUTPUT=$(timeout 10 valgrind --leak-check=full --error-exitcode=42 ./$EXERCISE 5 800 200 200 3 2>&1)
VALGRIND_EXIT=$?
if [ $VALGRIND_EXIT -eq 0 ]; then
echo -n "Test 6: Memory leak check... "
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 4 410 200 200 2>&1)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
elif [ $VALGRIND_EXIT -eq 124 ]; then
echo -e "${YELLOW}⚠ TIMEOUT${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
echo "$VALGRIND_OUTPUT" | grep -A 5 "LEAK SUMMARY"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 11: Valgrind not installed, skipping${NC}"
echo -e "${YELLOW}⊘ Test 6: Valgrind not installed, skipping memory test${NC}"
fi
# Test 7: Thread sanitizer check
if command -v gcc &> /dev/null; then
echo -n "Test 7: Data race detection... "
# Find the source file to compile
SOURCE_FILE=""
if [ -f "$PROJECT_ROOT/rendu/${EXERCISE}.c" ]; then
SOURCE_FILE="$PROJECT_ROOT/rendu/${EXERCISE}.c"
elif [ -f "$PROJECT_ROOT/${EXERCISE}.c" ]; then
SOURCE_FILE="$PROJECT_ROOT/${EXERCISE}.c"
fi
if [ -n "$SOURCE_FILE" ]; then
TSAN_EXE="$PROJECT_ROOT/$(basename "$EXE_PATH")_tsan"
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g "$SOURCE_FILE" -o "$TSAN_EXE" 2>/dev/null
if [ $? -eq 0 ]; then
TSAN_OUTPUT=$(timeout 15 "$TSAN_EXE" 4 410 200 200 2>&1)
if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Data race detected!"
echo "$TSAN_OUTPUT" | head -20
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f "$TSAN_EXE"
else
echo -e "${YELLOW}⊘ Could not compile with ThreadSanitizer, skipping test${NC}"
fi
else
echo -e "${YELLOW}⊘ Source file ${EXERCISE}.c not found, skipping ThreadSanitizer test${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 7: gcc not found, skipping data race test${NC}"
fi
# Cleanup
rm -f /tmp/philo_test*.txt
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
echo ""
echo "Additional tests you should run manually:"
echo " - ./philosophers_args 2 410 200 200 (edge case)"
echo " - ./philosophers_args 4 310 200 100 (tight timing)"
echo " - helgrind: valgrind --tool=helgrind ./$EXERCISE 5 800 200 200"
[ $FAILED -eq 0 ] && exit 0 || exit 1
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi

View File

@ -9,22 +9,34 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="race_detector"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE"
echo "========================================"
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile: gcc -Wall -Wextra -Werror -pthread race_detector.c -o race_detector"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread race_detector.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Basic execution
echo -n "Test 1: Basic execution - 4 410 200 200... "
timeout 120 ./$EXERCISE 4 410 200 200 > /tmp/race_detector_output.txt 2>&1
timeout 120 "$EXE_PATH" 4 410 200 200 > /tmp/race_detector_output.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
@ -86,7 +98,7 @@ fi
if command -v valgrind &> /dev/null; then
echo -n "Test 5: Helgrind data race check... "
# Run a single iteration with helgrind
timeout 60 valgrind --tool=helgrind ./$EXERCISE 4 410 200 200 > /tmp/helgrind_output.txt 2>&1
timeout 60 valgrind --tool=helgrind "$EXE_PATH" 4 410 200 200 > /tmp/helgrind_output.txt 2>&1
HELGRIND_OUTPUT=$(cat /tmp/helgrind_output.txt 2>/dev/null)
if echo "$HELGRIND_OUTPUT" | grep -q "ERROR SUMMARY: 0 errors"; then
@ -98,99 +110,75 @@ if command -v valgrind &> /dev/null; then
echo "$HELGRIND_OUTPUT" | grep -A 2 "Possible data race" | head -10
((FAILED++))
else
echo -e "${YELLOW}UNCLEAR${NC} (helgrind ran but results unclear)"
echo -e "${YELLOW}WARNING${NC} (Helgrind test inconclusive)"
((PASSED++))
fi
rm -f /tmp/helgrind_output.txt
else
echo -e "${YELLOW}⊘ Test 5: Valgrind not installed, skipping helgrind${NC}"
echo -e "${YELLOW}⊘ Test 5: Valgrind not installed, skipping Helgrind test${NC}"
fi
# Test 6: Edge case - odd number of philosophers
echo -n "Test 6: Odd philosophers (5)... "
timeout 120 ./$EXERCISE 5 800 200 200 > /tmp/race_detector_odd.txt 2>&1
if [ $? -eq 0 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
((FAILED++))
fi
# Test 7: Edge case - tight timing
echo -n "Test 7: Tight timing (4 310 200 100)... "
timeout 120 ./$EXERCISE 4 310 200 100 > /tmp/race_detector_tight.txt 2>&1
if [ $? -eq 0 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
((FAILED++))
fi
# Test 8: Performance comparison
echo -n "Test 8: Performance metrics... "
if echo "$OUTPUT" | grep -qiE "(time|ms|second|performance|speed)"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC}"
echo "Expected performance/timing information"
((PASSED++))
fi
# Test 9: Thread sanitizer on the program itself
if command -v gcc &> /dev/null; then
echo -n "Test 9: Thread sanitizer... "
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g race_detector.c -o race_detector_tsan 2>/dev/null
if [ $? -eq 0 ]; then
TSAN_OUTPUT=$(timeout 60 ./race_detector_tsan 4 410 200 200 2>&1)
if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Data race in the race detector itself!"
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f race_detector_tsan
else
echo -e "${YELLOW}⊘ Could not compile with thread sanitizer${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 9: gcc not available, skipping${NC}"
fi
# Test 10: Memory leak check
# Test 6: Check for memory leaks with valgrind
if command -v valgrind &> /dev/null; then
echo -n "Test 10: Memory leak check... "
VALGRIND_OUTPUT=$(timeout 60 valgrind --leak-check=full --error-exitcode=42 ./$EXERCISE 4 410 200 200 2>&1)
VALGRIND_EXIT=$?
if [ $VALGRIND_EXIT -eq 0 ]; then
echo -n "Test 6: Memory leak check... "
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 4 410 200 200 2>&1)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
elif [ $VALGRIND_EXIT -eq 124 ]; then
echo -e "${YELLOW}⚠ TIMEOUT${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
echo "$VALGRIND_OUTPUT" | grep -A 5 "LEAK SUMMARY"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 10: Valgrind not installed, skipping${NC}"
echo -e "${YELLOW}⊘ Test 6: Valgrind not installed, skipping memory test${NC}"
fi
# Test 7: Thread sanitizer check
if command -v gcc &> /dev/null; then
echo -n "Test 7: Data race detection (ThreadSanitizer)... "
# Find the source file to compile
SOURCE_FILE=""
if [ -f "$PROJECT_ROOT/rendu/${EXERCISE}.c" ]; then
SOURCE_FILE="$PROJECT_ROOT/rendu/${EXERCISE}.c"
elif [ -f "$PROJECT_ROOT/${EXERCISE}.c" ]; then
SOURCE_FILE="$PROJECT_ROOT/${EXERCISE}.c"
fi
if [ -n "$SOURCE_FILE" ]; then
TSAN_EXE="$PROJECT_ROOT/$(basename "$EXE_PATH")_tsan"
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g "$SOURCE_FILE" -o "$TSAN_EXE" 2>/dev/null
if [ $? -eq 0 ]; then
TSAN_OUTPUT=$(timeout 60 "$TSAN_EXE" 4 410 200 200 2>&1)
if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Data race detected by ThreadSanitizer!"
echo "$TSAN_OUTPUT" | head -20
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f "$TSAN_EXE"
else
echo -e "${YELLOW}⊘ Could not compile with ThreadSanitizer, skipping test${NC}"
fi
else
echo -e "${YELLOW}⊘ Source file ${EXERCISE}.c not found, skipping ThreadSanitizer test${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 7: gcc not found, skipping data race test${NC}"
fi
# Cleanup
rm -f /tmp/race_detector_*.txt /tmp/race_detector_output.txt
rm -f /tmp/race_detector_output.txt /tmp/helgrind_output.txt
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
echo ""
echo "Note: For thorough testing, run:"
echo " valgrind --tool=helgrind ./race_detector 4 410 200 200"
echo " valgrind --tool=drd ./race_detector 4 410 200 200"
[ $FAILED -eq 0 ] && exit 0 || exit 1
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi

View File

@ -9,32 +9,50 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="philosophers_bonus"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
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"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread philosophers_bonus.c -o $EXERCISE"
exit 1
fi
# Clean up any leftover semaphores
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Clean up any leftover semaphores from previous runs
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
# Use a loop to be thorough, as semaphore names can vary
for i in {0..255}; do
sem_unlink "/philo_forks" &>/dev/null
sem_unlink "/philo_print" &>/dev/null
sem_unlink "/philo_main" &>/dev/null
sem_unlink "/philo_sem_$i" &>/dev/null
done
# Common names from other implementations
sem_unlink "/forks" &>/dev/null
sem_unlink "/print" &>/dev/null
sem_unlink "forks" &>/dev/null
sem_unlink "print" &>/dev/null
# 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
timeout 12 "$EXE_PATH" 5 800 200 200 > /tmp/bonus_test1.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
@ -54,29 +72,36 @@ 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
SOURCE_FILE=""
if [ -f "$PROJECT_ROOT/rendu/${EXERCISE}.c" ]; then
SOURCE_FILE="$PROJECT_ROOT/rendu/${EXERCISE}.c"
elif [ -f "$PROJECT_ROOT/${EXERCISE}.c" ]; then
SOURCE_FILE="$PROJECT_ROOT/${EXERCISE}.c"
fi
if [ -n "$SOURCE_FILE" ] && grep -q "fork()" "$SOURCE_FILE" 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"
echo "Bonus should use fork() for processes. Source file not found or fork() missing."
((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
if [ -n "$SOURCE_FILE" ] && grep -qE "sem_(open|wait|post)" "$SOURCE_FILE" 2>/dev/null; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Bonus should use POSIX semaphores"
echo "Bonus should use POSIX semaphores. Source file not found or sem_ calls missing."
((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
timeout 5 "$EXE_PATH" 4 310 200 100 > /tmp/bonus_test4.txt 2>&1
OUTPUT=$(cat /tmp/bonus_test4.txt)
if echo "$OUTPUT" | grep -qiE "die"; then
@ -90,24 +115,32 @@ 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
timeout 20 "$EXE_PATH" 5 800 200 200 7 > /tmp/bonus_test5.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
OUTPUT=$(cat /tmp/bonus_test5.txt)
if echo "$OUTPUT" | grep -qiE "meal limit"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Meal limit not reached or reported"
((FAILED++))
fi
else
echo -e "${RED}✗ FAILED${NC}"
echo "Program failed or timed out with meal limit"
((FAILED++))
fi
# Test 6: Single philosopher
# Test 6: Single philosopher (should die)
echo -n "Test 6: Single philosopher - 1 800 200 200... "
timeout 3 ./$EXERCISE 1 800 200 200 > /tmp/bonus_test6.txt 2>&1
timeout 3 "$EXE_PATH" 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)"
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
@ -117,119 +150,39 @@ fi
# Test 7: Invalid arguments
echo -n "Test 7: Invalid argument handling... "
timeout 2 ./$EXERCISE 0 800 200 200 > /dev/null 2>&1
timeout 2 "$EXE_PATH" 0 800 200 200 > /dev/null 2>&1
EXIT1=$?
timeout 2 ./$EXERCISE 5 -100 200 200 > /dev/null 2>&1
timeout 2 "$EXE_PATH" 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
if [ $EXIT1 -ne 0 ] && [ $EXIT2 -ne 0 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Program should reject invalid arguments"
echo "Program should exit with error on 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)
# Test 8: Check for memory leaks with valgrind
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++))
echo -n "Test 8: Memory leak check... "
# Valgrind does not work well with fork() and semaphores in this context
# It often reports false positives or hangs.
echo -e "${YELLOW}⊘ SKIPPED (Valgrind not suitable for process/semaphore bonus)${NC}"
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}"
echo -e "${YELLOW}⊘ Test 8: Valgrind not installed, skipping memory test${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
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi

View File

@ -9,23 +9,34 @@ YELLOW='\033[1;33m'
NC='\033[0m' # No Color
EXERCISE="thread_basics"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE"
echo "========================================"
# Check if executable exists
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile your program first: gcc -Wall -Wextra -Werror -pthread thread_basics.c -o thread_basics"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread thread_basics.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Basic execution with 1 message
echo -n "Test 1: Basic execution (1 message)... "
OUTPUT=$(timeout 5 ./$EXERCISE 1 2>&1)
OUTPUT=$(timeout 5 "$EXE_PATH" 1 2>&1)
if [ $? -eq 0 ]; then
if echo "$OUTPUT" | grep -q "Thread 1: Hello from thread 1" && \
echo "$OUTPUT" | grep -q "Thread 2: Hello from thread 2"; then
@ -44,7 +55,7 @@ fi
# Test 2: Multiple messages (5)
echo -n "Test 2: Multiple messages (5)... "
OUTPUT=$(timeout 5 ./$EXERCISE 5 2>&1)
OUTPUT=$(timeout 5 "$EXE_PATH" 5 2>&1)
if [ $? -eq 0 ]; then
COUNT_T1=$(echo "$OUTPUT" | grep -c "Thread 1: Hello from thread 1")
COUNT_T2=$(echo "$OUTPUT" | grep -c "Thread 2: Hello from thread 2")
@ -64,7 +75,7 @@ fi
# Test 3: Large number (10)
echo -n "Test 3: Large number (10)... "
OUTPUT=$(timeout 5 ./$EXERCISE 10 2>&1)
OUTPUT=$(timeout 5 "$EXE_PATH" 10 2>&1)
if [ $? -eq 0 ]; then
COUNT_T1=$(echo "$OUTPUT" | grep -c "Thread 1: Hello from thread 1")
COUNT_T2=$(echo "$OUTPUT" | grep -c "Thread 2: Hello from thread 2")
@ -85,7 +96,7 @@ fi
# Test 4: Check for memory leaks with valgrind
if command -v valgrind &> /dev/null; then
echo -n "Test 4: Memory leak check... "
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 ./$EXERCISE 3 2>&1)
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 3 2>&1)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))

View File

@ -9,22 +9,34 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="mutex_basics"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE"
echo "========================================"
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile: gcc -Wall -Wextra -Werror -pthread mutex_basics.c -o mutex_basics"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread mutex_basics.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Counter should be exactly 4000
echo -n "Test 1: Counter correctness (should be 4000)... "
OUTPUT=$(timeout 10 ./$EXERCISE 2>&1)
OUTPUT=$(timeout 10 "$EXE_PATH" 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
@ -51,7 +63,7 @@ fi
echo -n "Test 2: Consistency check (5 runs)... "
CONSISTENT=true
for i in {1..5}; do
OUTPUT=$(timeout 10 ./$EXERCISE 2>&1)
OUTPUT=$(timeout 10 "$EXE_PATH" 2>&1)
COUNTER=$(echo "$OUTPUT" | grep -oP "(?i)counter.*?[:=]\s*\K\d+" | head -1)
if [ -z "$COUNTER" ]; then
COUNTER=$(echo "$OUTPUT" | grep -oP "\d{4}" | head -1)
@ -75,7 +87,7 @@ fi
# Test 3: Check execution time is reported
echo -n "Test 3: Execution time reported... "
OUTPUT=$(timeout 10 ./$EXERCISE 2>&1)
OUTPUT=$(timeout 10 "$EXE_PATH" 2>&1)
if echo "$OUTPUT" | grep -qiE "(time|ms|milliseconds|seconds|duration)"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
@ -89,9 +101,19 @@ fi
if command -v gcc &> /dev/null; then
echo -n "Test 4: Data race detection... "
# Try to compile with thread sanitizer
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g mutex_basics.c -o mutex_basics_tsan 2>/dev/null
# Find the source file to compile
SOURCE_FILE=""
if [ -f "$PROJECT_ROOT/rendu/${EXERCISE}.c" ]; then
SOURCE_FILE="$PROJECT_ROOT/rendu/${EXERCISE}.c"
elif [ -f "$PROJECT_ROOT/${EXERCISE}.c" ]; then
SOURCE_FILE="$PROJECT_ROOT/${EXERCISE}.c"
fi
if [ -n "$SOURCE_FILE" ]; then
TSAN_EXE="$PROJECT_ROOT/$(basename "$EXE_PATH")_tsan"
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g "$SOURCE_FILE" -o "$TSAN_EXE" 2>/dev/null
if [ $? -eq 0 ]; then
TSAN_OUTPUT=$(timeout 15 ./mutex_basics_tsan 2>&1)
TSAN_OUTPUT=$(timeout 15 "$TSAN_EXE" 2>&1)
if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Data race detected! Mutex not protecting all accesses"
@ -101,12 +123,15 @@ if command -v gcc &> /dev/null; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f mutex_basics_tsan
rm -f "$TSAN_EXE"
else
echo -e "${YELLOW}⊘ Could not compile with thread sanitizer${NC}"
echo -e "${YELLOW}⊘ Could not compile with ThreadSanitizer, skipping test${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 4: gcc not available, skipping sanitizer test${NC}"
echo -e "${YELLOW}⊘ Source file ${EXERCISE}.c not found, skipping ThreadSanitizer test${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 4: gcc not found, skipping data race test${NC}"
fi
# Test 5: Memory leak check
@ -126,9 +151,12 @@ else
echo -e "${YELLOW}⊘ Test 5: Valgrind not installed, skipping${NC}"
fi
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
[ $FAILED -eq 0 ] && exit 0 || exit 1
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi

View File

@ -9,22 +9,34 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="precise_timing"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE"
echo "========================================"
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile: gcc -Wall -Wextra -Werror -pthread precise_timing.c -o precise_timing"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread precise_timing.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Basic execution
echo -n "Test 1: Program runs successfully... "
OUTPUT=$(timeout 5 ./$EXERCISE 2>&1)
OUTPUT=$(timeout 5 "$EXE_PATH" 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
@ -37,7 +49,7 @@ fi
# Test 2: Check if time difference is approximately 100ms
echo -n "Test 2: Sleep precision (~100ms)... "
OUTPUT=$(timeout 5 ./$EXERCISE 2>&1)
OUTPUT=$(timeout 5 "$EXE_PATH" 2>&1)
# Extract number that should be around 100
TIME_DIFF=$(echo "$OUTPUT" | grep -oP "\d+" | tail -1)
@ -69,7 +81,7 @@ TOTAL=0
COUNT=0
for i in {1..5}; do
OUTPUT=$(timeout 5 ./$EXERCISE 2>&1)
OUTPUT=$(timeout 5 "$EXE_PATH" 2>&1)
TIME_DIFF=$(echo "$OUTPUT" | grep -oP "\d+" | tail -1)
if [ -n "$TIME_DIFF" ]; then
@ -99,39 +111,41 @@ fi
# Test 4: Check output format
echo -n "Test 4: Output format check... "
OUTPUT=$(timeout 5 ./$EXERCISE 2>&1)
if echo "$OUTPUT" | grep -qiE "(diff|difference|elapsed|time|ms)"; then
OUTPUT=$(timeout 5 "$EXE_PATH" 2>&1)
if echo "$OUTPUT" | grep -qE "Time difference: [0-9]+ ms"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Program should clearly indicate the time difference"
echo "Output: $OUTPUT"
echo "Expected output format: 'Time difference: <number> ms'"
echo "Got: $OUTPUT"
((FAILED++))
fi
# Test 5: Memory leak check
# Test 5: Check for memory leaks with valgrind
if command -v valgrind &> /dev/null; then
echo -n "Test 5: Memory leak check... "
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 ./$EXERCISE 2>&1)
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 2>&1)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
echo "$VALGRIND_OUTPUT" | grep -A 5 "LEAK SUMMARY"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 5: Valgrind not installed, skipping${NC}"
echo -e "${YELLOW}⊘ Test 5: Valgrind not installed, skipping memory test${NC}"
fi
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
echo ""
echo "Note: Timing precision is crucial for Philosophers project"
echo "Your sleep function should be accurate within ±5ms"
[ $FAILED -eq 0 ] && exit 0 || exit 1
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi

View File

@ -9,22 +9,34 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="state_monitor"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE"
echo "========================================"
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile: gcc -Wall -Wextra -Werror -pthread state_monitor.c -o state_monitor"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread state_monitor.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Program runs for reasonable time
echo -n "Test 1: Program execution... "
timeout 15 ./$EXERCISE > /tmp/state_monitor_output.txt 2>&1 &
timeout 15 "$EXE_PATH" > /tmp/state_monitor_output.txt 2>&1 &
PID=$!
sleep 8
kill $PID 2>/dev/null
@ -98,58 +110,37 @@ elif echo "$OUTPUT" | grep -qi "monitor"; then
echo -e "${GREEN}✓ PASSED${NC} (monitor running)"
((PASSED++))
else
echo -e "${YELLOW}⚠ PARTIAL${NC} - No clear evidence of monitor warnings"
echo "This is OK if no worker got stuck during the test"
((PASSED++))
echo -e "${RED}✗ FAILED${NC}"
echo "Monitor thread did not seem to run or report"
((FAILED++))
fi
# Test 7: Memory leak check
# Test 7: Check for memory leaks with valgrind
if command -v valgrind &> /dev/null; then
echo -n "Test 7: Memory leak check... "
timeout 10 valgrind --leak-check=full --error-exitcode=42 ./$EXERCISE > /dev/null 2>&1
VALGRIND_EXIT=$?
# Since we use timeout, we need to check differently
if [ $VALGRIND_EXIT -eq 124 ] || [ $VALGRIND_EXIT -eq 0 ]; then
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 2>&1)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
echo "$VALGRIND_OUTPUT" | grep -A 5 "LEAK SUMMARY"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 7: Valgrind not installed, skipping${NC}"
fi
# Test 8: Thread sanitizer (data race check)
if command -v gcc &> /dev/null; then
echo -n "Test 8: Data race detection... "
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g state_monitor.c -o state_monitor_tsan 2>/dev/null
if [ $? -eq 0 ]; then
timeout 10 ./state_monitor_tsan > /dev/null 2>&1
TSAN_OUTPUT=$(timeout 10 ./state_monitor_tsan 2>&1)
if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Data race detected!"
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f state_monitor_tsan
else
echo -e "${YELLOW}⊘ Could not compile with thread sanitizer${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 8: gcc not available, skipping${NC}"
echo -e "${YELLOW}⊘ Test 7: Valgrind not installed, skipping memory test${NC}"
fi
# Cleanup
rm -f /tmp/state_monitor_output.txt
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
[ $FAILED -eq 0 ] && exit 0 || exit 1
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi

View File

@ -9,22 +9,34 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="producer_consumer"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE"
echo "========================================"
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile: gcc -Wall -Wextra -Werror -pthread producer_consumer.c -o producer_consumer"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread producer_consumer.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Program runs to completion
echo -n "Test 1: Program execution... "
timeout 30 ./$EXERCISE > /tmp/producer_consumer_output.txt 2>&1
timeout 30 "$EXE_PATH" > /tmp/producer_consumer_output.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
@ -111,51 +123,32 @@ else
((FAILED++))
fi
# Test 7: Thread sanitizer check
if command -v gcc &> /dev/null; then
echo -n "Test 7: Data race detection... "
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g producer_consumer.c -o producer_consumer_tsan 2>/dev/null
if [ $? -eq 0 ]; then
TSAN_OUTPUT=$(timeout 35 ./producer_consumer_tsan 2>&1)
if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Data race detected!"
echo "$TSAN_OUTPUT" | grep -A 5 "data race" | head -10
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f producer_consumer_tsan
else
echo -e "${YELLOW}⊘ Could not compile with thread sanitizer${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 7: gcc not available, skipping${NC}"
fi
# Test 8: Memory leak check
# Test 7: Memory leak check
if command -v valgrind &> /dev/null; then
echo -n "Test 8: Memory leak check... "
VALGRIND_OUTPUT=$(timeout 35 valgrind --leak-check=full --error-exitcode=42 ./$EXERCISE 2>&1)
echo -n "Test 7: Memory leak check... "
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 2>&1)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
echo "$VALGRIND_OUTPUT" | grep -A 5 "LEAK SUMMARY"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 8: Valgrind not installed, skipping${NC}"
echo -e "${YELLOW}⊘ Test 7: Valgrind not installed, skipping memory test${NC}"
fi
# Cleanup
rm -f /tmp/producer_consumer_output.txt
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
[ $FAILED -eq 0 ] && exit 0 || exit 1
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi

View File

@ -9,22 +9,34 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="deadlock_demo"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE"
echo "========================================"
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile: gcc -Wall -Wextra -Werror -pthread deadlock_demo.c -o deadlock_demo"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread deadlock_demo.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Deadlock mode (should hang or timeout)
echo -n "Test 1: Deadlock demonstration (mode 0)... "
timeout 3 ./$EXERCISE 0 > /tmp/deadlock_output.txt 2>&1
timeout 3 "$EXE_PATH" 0 > /tmp/deadlock_output.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 124 ]; then
@ -46,7 +58,7 @@ fi
# Test 2: Solution mode (should complete)
echo -n "Test 2: Deadlock solution (mode 1)... "
timeout 5 ./$EXERCISE 1 > /tmp/deadlock_solution.txt 2>&1
timeout 5 "$EXE_PATH" 1 > /tmp/deadlock_solution.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
@ -88,7 +100,7 @@ fi
echo -n "Test 5: Solution consistency (5 runs)... "
ALL_SUCCESS=true
for i in {1..5}; do
timeout 5 ./$EXERCISE 1 > /dev/null 2>&1
timeout 5 "$EXE_PATH" 1 > /dev/null 2>&1
if [ $? -ne 0 ]; then
ALL_SUCCESS=false
break
@ -104,66 +116,47 @@ else
((FAILED++))
fi
# Test 6: Check for mutex usage
echo -n "Test 6: Mutex implementation check... "
if grep -q "pthread_mutex" deadlock_demo.c 2>/dev/null; then
MUTEX_COUNT=$(grep -c "pthread_mutex" deadlock_demo.c 2>/dev/null)
if [ "$MUTEX_COUNT" -ge 4 ]; then
echo -e "${GREEN}✓ PASSED${NC} (found mutex operations)"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC} (few mutex operations found)"
((PASSED++))
fi
else
echo -e "${RED}✗ FAILED${NC}"
echo "Source file should use pthread_mutex functions"
((FAILED++))
fi
# Test 7: Invalid argument handling
echo -n "Test 7: Invalid argument handling... "
timeout 2 ./$EXERCISE 2 > /dev/null 2>&1
EXIT_CODE1=$?
timeout 2 ./$EXERCISE abc > /dev/null 2>&1
EXIT_CODE2=$?
timeout 2 ./$EXERCISE > /dev/null 2>&1
EXIT_CODE3=$?
if [ $EXIT_CODE3 -ne 0 ]; then
echo -e "${GREEN}✓ PASSED${NC} (handles invalid/missing arguments)"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC}"
echo "Program should handle invalid arguments gracefully"
((PASSED++))
fi
# Test 8: Memory leak check (solution mode)
# Test 6: Check for memory leaks with valgrind
if command -v valgrind &> /dev/null; then
echo -n "Test 8: Memory leak check... "
VALGRIND_OUTPUT=$(timeout 10 valgrind --leak-check=full --error-exitcode=42 ./$EXERCISE 1 2>&1)
echo -n "Test 6: Memory leak check (solution mode)... "
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 1 2>&1)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
echo "$VALGRIND_OUTPUT" | grep -A 5 "LEAK SUMMARY"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 8: Valgrind not installed, skipping${NC}"
echo -e "${YELLOW}⊘ Test 6: Valgrind not installed, skipping memory test${NC}"
fi
# Test 7: Helgrind deadlock detection
if command -v valgrind &> /dev/null; then
echo -n "Test 7: Helgrind deadlock detection (mode 0)... "
HELGRIND_OUTPUT=$(valgrind --tool=helgrind "$EXE_PATH" 0 2>&1)
if echo "$HELGRIND_OUTPUT" | grep -q "deadlock"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC} - Helgrind did not report a deadlock"
((PASSED++))
fi
else
echo -e "${YELLOW}⊘ Test 7: Valgrind not installed, skipping Helgrind test${NC}"
fi
# Cleanup
rm -f /tmp/deadlock_output.txt /tmp/deadlock_solution.txt
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
echo ""
echo "Note: Understanding deadlock is crucial for Philosophers!"
echo "Always lock resources in a consistent order to avoid it."
[ $FAILED -eq 0 ] && exit 0 || exit 1
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi

View File

@ -9,22 +9,34 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="limited_resources"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE"
echo "========================================"
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile: gcc -Wall -Wextra -Werror -pthread limited_resources.c -o limited_resources"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread limited_resources.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Program runs to completion
echo -n "Test 1: Program execution... "
timeout 30 ./$EXERCISE > /tmp/limited_resources_output.txt 2>&1
timeout 30 "$EXE_PATH" > /tmp/limited_resources_output.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
@ -100,69 +112,32 @@ else
((PASSED++))
fi
# Test 7: No student is starved (all get to use computer)
echo -n "Test 7: Fairness check... "
ALL_USED=true
for i in {0..9}; do
if ! echo "$OUTPUT" | grep -qiE "student $i.*((get|using|acquired|computer)|(finish|done|leave))"; then
ALL_USED=false
break
fi
done
if [ "$ALL_USED" = true ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Student $i never got a computer - possible starvation"
((FAILED++))
fi
# Test 8: Thread sanitizer
if command -v gcc &> /dev/null; then
echo -n "Test 8: Data race detection... "
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g limited_resources.c -o limited_resources_tsan 2>/dev/null
if [ $? -eq 0 ]; then
TSAN_OUTPUT=$(timeout 35 ./limited_resources_tsan 2>&1)
if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Data race detected!"
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f limited_resources_tsan
else
echo -e "${YELLOW}⊘ Could not compile with thread sanitizer${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 8: gcc not available, skipping${NC}"
fi
# Test 9: Memory leak check
# Test 7: Check for memory leaks with valgrind
if command -v valgrind &> /dev/null; then
echo -n "Test 9: Memory leak check... "
VALGRIND_OUTPUT=$(timeout 35 valgrind --leak-check=full --error-exitcode=42 ./$EXERCISE 2>&1)
echo -n "Test 7: Memory leak check... "
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 2>&1)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
echo "$VALGRIND_OUTPUT" | grep -A 5 "LEAK SUMMARY"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 9: Valgrind not installed, skipping${NC}"
echo -e "${YELLOW}⊘ Test 7: Valgrind not installed, skipping memory test${NC}"
fi
# Cleanup
rm -f /tmp/limited_resources_output.txt
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
[ $FAILED -eq 0 ] && exit 0 || exit 1
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi

View File

@ -9,23 +9,35 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="simple_philosophers"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE"
echo "========================================"
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile: gcc -Wall -Wextra -Werror -pthread simple_philosophers.c -o simple_philosophers"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread simple_philosophers.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Program runs for approximately 10 seconds
echo -n "Test 1: Program duration (~10 seconds)... "
START_TIME=$(date +%s)
timeout 15 ./$EXERCISE > /tmp/simple_philo_output.txt 2>&1
timeout 15 "$EXE_PATH" > /tmp/simple_philo_output.txt 2>&1
EXIT_CODE=$?
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
@ -102,91 +114,59 @@ if [ "$PHILO_0_EATS" -gt 0 ] && [ "$PHILO_1_EATS" -gt 0 ] && [ "$PHILO_2_EATS" -
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Not all philosophers are eating"
echo "P0:$PHILO_0_EATS, P1:$PHILO_1_EATS, P2:$PHILO_2_EATS"
echo "Not all philosophers ate. Philo 0: $PHILO_0_EATS, Philo 1: $PHILO_1_EATS, Philo 2: $PHILO_2_EATS"
((FAILED++))
fi
# Test 7: No philosopher dies
echo -n "Test 7: No deaths... "
if echo "$OUTPUT" | grep -qiE "(die|death|dead)"; then
echo -e "${RED}✗ FAILED${NC}"
echo "A philosopher died - check your timing and fork management"
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
# Test 8: Thread sanitizer check
if command -v gcc &> /dev/null; then
echo -n "Test 8: Data race detection... "
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g simple_philosophers.c -o simple_philosophers_tsan 2>/dev/null
if [ $? -eq 0 ]; then
TSAN_OUTPUT=$(timeout 12 ./simple_philosophers_tsan 2>&1)
if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Data race detected!"
echo "$TSAN_OUTPUT" | grep -A 3 "data race" | head -10
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f simple_philosophers_tsan
else
echo -e "${YELLOW}⊘ Could not compile with thread sanitizer${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 8: gcc not available, skipping${NC}"
fi
# Test 9: No deadlock (run multiple times)
echo -n "Test 9: No deadlock (3 runs)... "
DEADLOCK_DETECTED=false
for i in {1..3}; do
timeout 15 ./$EXERCISE > /dev/null 2>&1
if [ $? -eq 124 ]; then
DEADLOCK_DETECTED=true
# Test 7: No philosopher should eat twice in a row
echo -n "Test 7: No philosopher eats twice in a row... "
NO_CONSECUTIVE_EATING=true
LAST_EATER=-1
# Extract just the philosopher number from lines containing "eating"
EATING_ORDER=$(echo "$OUTPUT" | grep "eating" | grep -oE "[0-9]+" | head -n 20)
for philo in $EATING_ORDER; do
if [ "$philo" -eq "$LAST_EATER" ]; then
NO_CONSECUTIVE_EATING=false
break
fi
LAST_EATER=$philo
done
if [ "$DEADLOCK_DETECTED" = false ]; then
if [ "$NO_CONSECUTIVE_EATING" = true ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Program appears to deadlock on run $i"
echo "A philosopher ate twice in a row. Order: $EATING_ORDER"
((FAILED++))
fi
# Test 10: Memory leak check
# Test 8: Check for memory leaks with valgrind
if command -v valgrind &> /dev/null; then
echo -n "Test 10: Memory leak check... "
VALGRIND_OUTPUT=$(timeout 15 valgrind --leak-check=full --error-exitcode=42 ./$EXERCISE 2>&1)
VALGRIND_EXIT=$?
if [ $VALGRIND_EXIT -eq 0 ]; then
echo -n "Test 8: Memory leak check... "
VALGRIND_OUTPUT=$(valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" 2>&1)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
elif [ $VALGRIND_EXIT -eq 124 ]; then
echo -e "${YELLOW}⚠ TIMEOUT${NC} (likely OK)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
echo "$VALGRIND_OUTPUT" | grep -A 5 "LEAK SUMMARY"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 10: Valgrind not installed, skipping${NC}"
echo -e "${YELLOW}⊘ Test 8: Valgrind not installed, skipping memory test${NC}"
fi
# Cleanup
rm -f /tmp/simple_philo_output.txt
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
[ $FAILED -eq 0 ] && exit 0 || exit 1
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi

View File

@ -9,22 +9,34 @@ YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="death_monitor"
EXE_PATH=""
PASSED=0
FAILED=0
# Determine the project root directory, which is one level up from the 'testers' directory.
PROJECT_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
echo "========================================"
echo "Testing: $EXERCISE"
echo "========================================"
if [ ! -f "./$EXERCISE" ]; then
echo -e "${RED}✗ Executable ./$EXERCISE not found${NC}"
echo "Please compile: gcc -Wall -Wextra -Werror -pthread death_monitor.c -o death_monitor"
# Find the executable in the project root or the 'rendu' subdirectory.
if [ -f "$PROJECT_ROOT/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/$EXERCISE"
elif [ -f "$PROJECT_ROOT/rendu/$EXERCISE" ]; then
EXE_PATH="$PROJECT_ROOT/rendu/$EXERCISE"
else
echo -e "${RED}✗ Executable '$EXERCISE' not found in '$PROJECT_ROOT' or '$PROJECT_ROOT/rendu'${NC}"
echo "Please compile your program first. Example:"
echo "gcc -Wall -Wextra -Werror -pthread death_monitor.c -o $EXERCISE"
exit 1
fi
echo -e "${YELLOW}Found executable at: $EXE_PATH${NC}"
# Test 1: Program runs
echo -n "Test 1: Program execution... "
timeout 15 ./$EXERCISE > /tmp/death_monitor_output.txt 2>&1 &
timeout 15 "$EXE_PATH" > /tmp/death_monitor_output.txt 2>&1 &
PID=$!
sleep 8
kill $PID 2>/dev/null
@ -98,66 +110,22 @@ else
fi
# Test 7: Last activity time tracking
echo -n "Test 7: Activity time tracking... "
if echo "$OUTPUT" | grep -qiE "(last.*activity|time|update)"; then
echo -n "Test 7: Last activity time tracking... "
if echo "$OUTPUT" | grep -qiE "(last activity|last seen)"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC}"
echo "Could not verify activity time tracking"
echo "No explicit 'last activity' messages"
((PASSED++))
fi
# Test 8: Thread sanitizer
if command -v gcc &> /dev/null; then
echo -n "Test 8: Data race detection... "
gcc -Wall -Wextra -Werror -pthread -fsanitize=thread -g death_monitor.c -o death_monitor_tsan 2>/dev/null
if [ $? -eq 0 ]; then
timeout 10 ./death_monitor_tsan > /dev/null 2>&1
TSAN_OUTPUT=$(timeout 10 ./death_monitor_tsan 2>&1)
if echo "$TSAN_OUTPUT" | grep -q "WARNING: ThreadSanitizer: data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Data race detected!"
echo "$TSAN_OUTPUT" | grep -A 5 "data race" | head -10
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f death_monitor_tsan
else
echo -e "${YELLOW}⊘ Could not compile with thread sanitizer${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 8: gcc not available, skipping${NC}"
fi
# Test 9: Multiple runs (consistency)
echo -n "Test 9: Consistency (3 runs)... "
CONSISTENT=true
for i in {1..3}; do
timeout 10 ./$EXERCISE > /dev/null 2>&1
if [ $? -eq 124 ]; then
CONSISTENT=false
break
fi
done
if [ "$CONSISTENT" = true ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Program hung on run $i"
((FAILED++))
fi
# Test 10: Memory leak check
# Test 8: Check for memory leaks with valgrind
if command -v valgrind &> /dev/null; then
echo -n "Test 10: Memory leak check... "
timeout 12 valgrind --leak-check=full --error-exitcode=42 ./$EXERCISE > /dev/null 2>&1
VALGRIND_EXIT=$?
if [ $VALGRIND_EXIT -eq 0 ] || [ $VALGRIND_EXIT -eq 124 ]; then
echo -n "Test 8: Memory leak check... "
# Run for a shorter time to avoid timeout with valgrind
timeout 25 valgrind --leak-check=full --error-exitcode=42 "$EXE_PATH" > /dev/null 2>&1
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
@ -166,15 +134,18 @@ if command -v valgrind &> /dev/null; then
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 10: Valgrind not installed, skipping${NC}"
echo -e "${YELLOW}⊘ Test 8: Valgrind not installed, skipping memory test${NC}"
fi
# Cleanup
rm -f /tmp/death_monitor_output.txt
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
[ $FAILED -eq 0 ] && exit 0 || exit 1
# Final summary
echo "----------------------------------------"
if [ $FAILED -eq 0 ]; then
echo -e "${GREEN}Summary: All $PASSED tests passed for $EXERCISE!${NC}"
exit 0
else
echo -e "${RED}Summary: $FAILED out of $((PASSED + FAILED)) tests failed for $EXERCISE.${NC}"
exit 1
fi