Moved subjects to ./subjects, added subject testers

This commit is contained in:
Rui Ribeiro 2025-10-07 14:54:09 +01:00
parent 24da1f174c
commit 00a1df51bb
30 changed files with 3451 additions and 0 deletions

167
QUICK_START.sh Executable file
View File

@ -0,0 +1,167 @@
#!/bin/bash
# Quick Start Demo - Shows how to use the test suite
echo "╔════════════════════════════════════════════════════════╗"
echo "║ Philosophers Preparation - Test Suite Demo ║"
echo "╚════════════════════════════════════════════════════════╝"
echo ""
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
echo -e "${BLUE}📚 Este é um guia rápido de como usar os testers${NC}"
echo ""
echo "═══════════════════════════════════════════════════════"
echo ""
echo -e "${GREEN}1⃣ Setup Inicial${NC}"
echo " Execute uma vez para configurar:"
echo ""
echo -e " ${YELLOW}bash setup_testers.sh${NC}"
echo ""
echo " Isso irá:"
echo " - Tornar todos os scripts executáveis"
echo " - Verificar dependências (gcc, valgrind, etc.)"
echo ""
echo "═══════════════════════════════════════════════════════"
echo ""
echo -e "${GREEN}2⃣ Workflow de Desenvolvimento${NC}"
echo ""
echo " a) Implemente um exercício (ex: thread_basics.c)"
echo ""
echo " b) Compile:"
echo -e " ${YELLOW}gcc -Wall -Wextra -Werror -pthread thread_basics.c -o thread_basics${NC}"
echo ""
echo " c) Teste:"
echo -e " ${YELLOW}bash testers/test_1_thread_basics.sh${NC}"
echo ""
echo " d) Corrija erros e repita"
echo ""
echo "═══════════════════════════════════════════════════════"
echo ""
echo -e "${GREEN}3⃣ Testar Exercício Específico${NC}"
echo ""
echo " Opção 1 - Direto:"
echo -e " ${YELLOW}bash testers/test_1_thread_basics.sh${NC}"
echo ""
echo " Opção 2 - Via runner:"
echo -e " ${YELLOW}bash testers/run_all_tests.sh 1${NC}"
echo ""
echo "═══════════════════════════════════════════════════════"
echo ""
echo -e "${GREEN}4⃣ Testar Todos os Exercícios${NC}"
echo ""
echo -e " ${YELLOW}bash testers/run_all_tests.sh${NC}"
echo ""
echo "═══════════════════════════════════════════════════════"
echo ""
echo -e "${GREEN}5⃣ Exemplo Prático${NC}"
echo ""
echo " Vamos simular o workflow do Exercício 1:"
echo ""
# Check if thread_basics exists
if [ -f "./thread_basics" ]; then
echo -e " ${GREEN}${NC} Executável thread_basics encontrado!"
echo ""
echo " Executando teste..."
echo ""
bash testers/test_1_thread_basics.sh
else
echo -e " ${YELLOW}${NC} Executável thread_basics não encontrado"
echo ""
echo " Para testar de verdade:"
echo ""
echo " 1. Crie thread_basics.c"
echo " 2. Compile: gcc -Wall -Wextra -Werror -pthread thread_basics.c -o thread_basics"
echo " 3. Execute: bash testers/test_1_thread_basics.sh"
fi
echo ""
echo "═══════════════════════════════════════════════════════"
echo ""
echo -e "${GREEN}6⃣ Recursos Adicionais${NC}"
echo ""
echo " 📖 Documentação completa:"
echo -e " ${YELLOW}cat testers/README.md${NC}"
echo ""
echo " 📊 Cobertura de testes:"
echo -e " ${YELLOW}cat testers/TEST_COVERAGE.md${NC}"
echo ""
echo " 📝 Resumo geral:"
echo -e " ${YELLOW}cat TESTERS_SUMMARY.md${NC}"
echo ""
echo "═══════════════════════════════════════════════════════"
echo ""
echo -e "${GREEN}7⃣ Debugging${NC}"
echo ""
echo " Se um teste falhar:"
echo ""
echo " Memory Leaks:"
echo -e " ${YELLOW}valgrind --leak-check=full ./seu_programa${NC}"
echo ""
echo " Data Races:"
echo -e " ${YELLOW}valgrind --tool=helgrind ./seu_programa${NC}"
echo ""
echo " Thread Sanitizer:"
echo -e " ${YELLOW}gcc -fsanitize=thread -g programa.c -pthread -o programa_tsan${NC}"
echo -e " ${YELLOW}./programa_tsan${NC}"
echo ""
echo "═══════════════════════════════════════════════════════"
echo ""
echo -e "${GREEN}8⃣ Lista de Exercícios${NC}"
echo ""
echo " 1 - thread_basics (Threads básicas)"
echo " 2 - mutex_basics (Mutex e proteção)"
echo " 3 - precise_timing (Timing preciso)"
echo " 4 - state_monitor (Monitor de estados)"
echo " 5 - producer_consumer (Produtor-Consumidor)"
echo " 6 - deadlock_demo (Deadlock)"
echo " 7 - limited_resources (Recursos limitados)"
echo " 8 - simple_philosophers (Filósofos simples)"
echo " 9 - death_monitor (Monitor de morte)"
echo " 10 - philosophers_args (Filósofos completo)"
echo " 11 - race_detector (Detector de races)"
echo " 12 - philosophers_bonus (Bonus com processos)"
echo ""
echo "═══════════════════════════════════════════════════════"
echo ""
echo -e "${BLUE}💡 Dicas:${NC}"
echo ""
echo " • Comece pelos exercícios 1-3 (fundamentos)"
echo " • Leia os arquivos .txt de cada exercício"
echo " • Teste continuamente enquanto desenvolve"
echo " • Use valgrind e helgrind regularmente"
echo " • Exercícios 8-10 são os mais importantes"
echo ""
echo "═══════════════════════════════════════════════════════"
echo ""
echo -e "${GREEN}✨ Pronto para começar!${NC}"
echo ""
echo "Execute o setup:"
echo -e "${YELLOW}bash setup_testers.sh${NC}"
echo ""
echo "Boa sorte! 🚀"
echo ""

87
setup_testers.sh Executable file
View File

@ -0,0 +1,87 @@
#!/bin/bash
# Quick setup script for the test suite
# Makes all scripts executable and checks dependencies
echo "🔧 Setting up Philosophers Prep Test Suite..."
echo ""
# Make all test scripts executable
echo "Making test scripts executable..."
chmod +x testers/*.sh
if [ $? -eq 0 ]; then
echo "✓ Scripts are now executable"
else
echo "✗ Failed to make scripts executable"
exit 1
fi
echo ""
echo "Checking dependencies..."
echo ""
# Check for gcc
if command -v gcc &> /dev/null; then
GCC_VERSION=$(gcc --version | head -1)
echo "✓ gcc found: $GCC_VERSION"
else
echo "✗ gcc not found - REQUIRED"
echo " Install: sudo apt-get install gcc"
fi
# Check for valgrind
if command -v valgrind &> /dev/null; then
VALGRIND_VERSION=$(valgrind --version)
echo "✓ valgrind found: $VALGRIND_VERSION"
else
echo "⚠ valgrind not found - RECOMMENDED"
echo " Install: sudo apt-get install valgrind"
fi
# Check for pthread
echo -n "Checking pthread support... "
cat > /tmp/pthread_test.c << 'EOF'
#include <pthread.h>
int main() { return 0; }
EOF
gcc -pthread /tmp/pthread_test.c -o /tmp/pthread_test 2>/dev/null
if [ $? -eq 0 ]; then
echo "✓ pthread available"
rm -f /tmp/pthread_test /tmp/pthread_test.c
else
echo "✗ pthread not available"
fi
# Check thread sanitizer support
echo -n "Checking thread sanitizer... "
cat > /tmp/tsan_test.c << 'EOF'
int main() { return 0; }
EOF
gcc -fsanitize=thread /tmp/tsan_test.c -o /tmp/tsan_test 2>/dev/null
if [ $? -eq 0 ]; then
echo "✓ thread sanitizer available"
rm -f /tmp/tsan_test /tmp/tsan_test.c
else
echo "⚠ thread sanitizer not available"
fi
echo ""
echo "=========================================="
echo "Setup complete!"
echo "=========================================="
echo ""
echo "To run all tests:"
echo " bash testers/run_all_tests.sh"
echo ""
echo "To run a specific test:"
echo " bash testers/run_all_tests.sh [1-12]"
echo " Example: bash testers/run_all_tests.sh 1"
echo ""
echo "To run individual test directly:"
echo " bash testers/test_1_thread_basics.sh"
echo ""
echo "Read testers/README.md for more information."
echo ""

283
testers/README.md Normal file
View File

@ -0,0 +1,283 @@
# Philosophers Preparation - Test Suite
Este diretório contém testers automáticos para todos os exercícios preparatórios do projeto Philosophers da 42.
## 📋 Estrutura
Cada exercício tem seu próprio script de teste:
1. `test_1_thread_basics.sh` - Testa criação básica de threads
2. `test_2_mutex_basics.sh` - Testa proteção de variáveis com mutex
3. `test_3_precise_timing.sh` - Testa funções de timing preciso
4. `test_4_state_monitor.sh` - Testa monitoramento de estados
5. `test_5_producer_consumer.sh` - Testa padrão produtor-consumidor
6. `test_6_deadlock_demo.sh` - Testa demonstração e solução de deadlock
7. `test_7_limited_resources.sh` - Testa recursos limitados (semáforos)
8. `test_8_simple_philosophers.sh` - Testa filósofos simplificado
9. `test_9_death_monitor.sh` - Testa sistema de detecção de morte
10. `test_10_philosophers_args.sh` - Testa filósofos completo com argumentos
11. `test_11_race_detector.sh` - Testa detecção de data races
12. `test_12_philosophers_bonus.sh` - Testa versão bonus com processos
## 🚀 Como Usar
### Testar um exercício específico:
```bash
cd /home/ruiferna/Downloads/philosophers_prep
chmod +x testers/*.sh # Tornar todos os scripts executáveis
# Executar teste de um exercício específico
bash testers/test_1_thread_basics.sh
# Ou usando o runner principal
bash testers/run_all_tests.sh 1
```
### Testar todos os exercícios:
```bash
bash testers/run_all_tests.sh
```
## 📝 Pré-requisitos
### Obrigatório:
- `gcc` ou `cc` - Para compilar
- `bash` - Para executar os scripts
- Biblioteca pthread
### Opcional (para testes completos):
- `valgrind` - Para detectar memory leaks
- `gcc` com suporte a `-fsanitize=thread` - Para detectar data races
- `helgrind` (parte do valgrind) - Para análise avançada de threading
### Instalar ferramentas (Ubuntu/Debian):
```bash
sudo apt-get update
sudo apt-get install gcc valgrind build-essential
```
## 🎯 O que cada teste verifica
### Testes Comuns (na maioria dos exercícios):
- ✅ **Execução básica** - Programa roda sem crash
- ✅ **Funcionalidade** - Comportamento esperado
- ✅ **Output correto** - Mensagens e formato
- ✅ **Memory leaks** - Usando valgrind
- ✅ **Data races** - Usando thread sanitizer/helgrind
- ✅ **Edge cases** - Casos limite e argumentos inválidos
### Testes Específicos por Exercício:
#### 1. Thread Basics
- Criação de 2 threads
- Mensagens corretas de cada thread
- Quantidade correta de mensagens
- Join das threads
#### 2. Mutex Basics
- Valor final do contador = 4000
- Consistência através de múltiplas execuções
- Tempo de execução reportado
- Ausência de race conditions
#### 3. Precise Timing
- Sleep de 100ms com precisão ±5ms
- Consistência do timing
- Formato do output
#### 4. State Monitor
- 3 workers + 1 monitor
- Transições de estados (WORKING, RESTING, THINKING)
- Timestamps
- Detecção de workers travados
#### 5. Producer-Consumer
- 2 produtores + 2 consumidores
- Todos os items (1-100) produzidos e consumidos
- Sem duplicação de consumo
- Buffer limitado funcionando
#### 6. Deadlock Demo
- Modo 0: Demonstra deadlock (trava)
- Modo 1: Solução funciona (completa)
- Ambas as threads completam no modo solução
#### 7. Limited Resources
- 10 estudantes, 3 computadores
- Máximo 3 usuários simultâneos
- Todos eventualmente usam o recurso
- Sem starvation
#### 8. Simple Philosophers
- 3 filósofos, 3 garfos
- Programa roda por ~10 segundos
- Todos comem múltiplas vezes
- Sem deadlock
- Sem mortes
#### 9. Death Monitor
- 4 workers + monitor
- Detecção de "morte" (inatividade >3s)
- Atualização de last_activity_time
- Monitor verifica a cada 100ms
#### 10. Philosophers Args (Completo)
- Parsing de argumentos correto
- Caso básico: ninguém morre
- Detecção de morte quando esperado
- Limite de refeições funciona
- Filósofo sozinho morre
- Timestamps crescentes
#### 11. Race Detector
- Múltiplas iterações/estratégias testadas
- Estatísticas reportadas
- Testes com helgrind/drd
- Edge cases (ímpar/par, timing apertado)
#### 12. Philosophers Bonus
- Usa fork() (processos, não threads)
- Usa semáforos POSIX
- Cleanup de semáforos
- Sem processos zombie
- Signal handling
## 🔍 Interpretando Resultados
### Símbolos:
- ✓ **PASSED** (verde) - Teste passou
- ✗ **FAILED** (vermelho) - Teste falhou
- ⚠ **WARNING/PARTIAL** (amarelo) - Passou com avisos
- ⊘ **SKIPPED** (amarelo) - Teste não executado (ferramenta faltando)
### Exemplo de saída:
```
========================================
Testing: thread_basics
========================================
Test 1: Basic execution (1 message)... ✓ PASSED
Test 2: Multiple messages (5)... ✓ PASSED
Test 3: Large number (10)... ✓ PASSED
Test 4: Memory leak check... ✓ PASSED
========================================
Results: 4 passed, 0 failed
========================================
```
## 🐛 Debugging
### Se um teste falha:
1. **Veja o output detalhado**:
```bash
bash testers/test_X_exercise.sh 2>&1 | tee test_output.txt
```
2. **Execute seu programa manualmente**:
```bash
./exercise_name [args]
```
3. **Use valgrind para memory leaks**:
```bash
valgrind --leak-check=full ./exercise_name
```
4. **Use helgrind para data races**:
```bash
valgrind --tool=helgrind ./exercise_name
```
5. **Use thread sanitizer**:
```bash
gcc -fsanitize=thread -g exercise.c -pthread -o exercise_tsan
./exercise_tsan
```
## 📚 Dicas
### Compilação:
Sempre compile com flags de warning:
```bash
gcc -Wall -Wextra -Werror -pthread exercise.c -o exercise
```
### Para o projeto real Philosophers:
- Death detection deve ser < 10ms
- Nenhum data race é aceitável (nota 0)
- Memory leaks = pontos perdidos
- Teste com números ímpares e pares de filósofos
- Teste com `time_to_die` muito próximo de `time_to_eat`
### Casos importantes para testar:
```bash
./philo 1 800 200 200 # Deve morrer
./philo 4 410 200 200 # Caso difícil
./philo 4 310 200 100 # Muito apertado
./philo 5 800 200 200 # Número ímpar
./philo 5 800 200 200 7 # Com limite de refeições
```
## 🔧 Troubleshooting
### "Permission denied":
```bash
chmod +x testers/*.sh
```
### "Valgrind not found":
```bash
sudo apt-get install valgrind
```
### "Thread sanitizer não funciona":
```bash
# Verifique versão do gcc
gcc --version # Precisa >= 4.8
# Tente com g++ se gcc não funcionar
g++ -fsanitize=thread exercise.c -pthread -o exercise
```
### Semáforos não limpam (bonus):
```bash
# Liste semáforos
ls /dev/shm/sem.*
# Remova manualmente
rm /dev/shm/sem.*
```
## 📖 Recursos Adicionais
- [Pthread Tutorial](https://computing.llnl.gov/tutorials/pthreads/)
- [Valgrind Quick Start](https://valgrind.org/docs/manual/quick-start.html)
- [Thread Sanitizer](https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual)
- [Dining Philosophers Problem](https://en.wikipedia.org/wiki/Dining_philosophers_problem)
## 🤝 Contribuindo
Se encontrar bugs nos testers ou quiser adicionar mais testes:
1. Reporte o issue
2. Submeta um pull request
3. Compartilhe com outros estudantes da 42
## ✅ Checklist Final
Antes de submeter o projeto Philosophers:
- [ ] Todos os testers passam
- [ ] Valgrind: 0 memory leaks
- [ ] Helgrind: 0 data races
- [ ] Nenhum filósofo morre quando não deveria
- [ ] Death detection < 10ms após time_to_die
- [ ] Funciona com 1, 2, 3, 4, 5+ filósofos
- [ ] Funciona com timing apertado (310, 410ms)
- [ ] Meal limit funciona corretamente
- [ ] Código é norminette compliant
- [ ] Makefile correto (all, clean, fclean, re)
- [ ] Forbidden functions não são usadas
Boa sorte! 🍀

333
testers/TESTERS_SUMMARY.md Normal file
View File

@ -0,0 +1,333 @@
# 🧪 Sistema de Testes - Philosophers Preparation
## ✅ Testers Criados com Sucesso!
Analisei todos os 12 exercícios preparatórios do projeto Philosophers e criei um sistema completo de testes automáticos.
---
## 📦 O que foi criado:
### Diretório `testers/`
#### Scripts de Teste Individuais:
1. ✅ `test_1_thread_basics.sh` - Threads básicas
2. ✅ `test_2_mutex_basics.sh` - Mutex e proteção de variáveis
3. ✅ `test_3_precise_timing.sh` - Timing preciso
4. ✅ `test_4_state_monitor.sh` - Monitoramento de estados
5. ✅ `test_5_producer_consumer.sh` - Padrão produtor-consumidor
6. ✅ `test_6_deadlock_demo.sh` - Deadlock e solução
7. ✅ `test_7_limited_resources.sh` - Recursos limitados
8. ✅ `test_8_simple_philosophers.sh` - Filósofos simplificado
9. ✅ `test_9_death_monitor.sh` - Detecção de morte
10. ✅ `test_10_philosophers_args.sh` - Filósofos completo
11. ✅ `test_11_race_detector.sh` - Detecção de races
12. ✅ `test_12_philosophers_bonus.sh` - Versão bonus (processos)
#### Scripts Auxiliares:
- ✅ `run_all_tests.sh` - Runner principal para todos os testes
- ✅ `README.md` - Documentação completa
- ✅ `TEST_COVERAGE.md` - Cobertura detalhada de testes
#### No Diretório Raiz:
- ✅ `setup_testers.sh` - Script de setup e verificação de dependências
---
## 🚀 Como Usar:
### 1⃣ Setup Inicial:
```bash
cd /home/ruiferna/Downloads/philosophers_prep
bash setup_testers.sh
```
### 2⃣ Testar Todos os Exercícios:
```bash
bash testers/run_all_tests.sh
```
### 3⃣ Testar Exercício Específico:
```bash
# Opção 1: Pelo runner
bash testers/run_all_tests.sh 1
# Opção 2: Diretamente
bash testers/test_1_thread_basics.sh
```
---
## 🎯 O que cada Tester Verifica:
### Todos os Testers Incluem:
- ✅ **Funcionalidade básica** - Programa funciona como esperado
- ✅ **Output correto** - Formato e conteúdo das mensagens
- ✅ **Memory leaks** - Via valgrind (quando disponível)
- ✅ **Data races** - Via thread sanitizer/helgrind
- ✅ **Edge cases** - Casos limite e argumentos inválidos
- ✅ **Consistency** - Comportamento consistente em múltiplas execuções
### Testes Específicos por Exercício:
#### Exercícios 1-3 (Fundamentos):
- Thread creation e join
- Mutex protection e race conditions
- Timing precisão (±5ms)
#### Exercícios 4-7 (Padrões):
- State monitoring
- Producer-consumer com buffer limitado
- Deadlock demonstration e prevenção
- Semaphore implementation
#### Exercícios 8-10 (Philosophers):
- Dining philosophers básico
- Death detection system
- Argument parsing completo
- Meal counting
- Timestamp ordering
#### Exercícios 11-12 (Avançado):
- Multiple optimization strategies
- 100 iteration stress tests
- Process-based implementation
- POSIX semaphores
- Signal handling
---
## 📊 Estatísticas:
- **Total de Testers**: 12
- **Total de Testes**: ~99 testes automatizados
- **Categorias Testadas**:
- Funcionalidade: 100%
- Memory leaks: 100%
- Data races: 83%
- Edge cases: 83%
- Performance: 17%
---
## 🛠️ Ferramentas Utilizadas:
### Obrigatórias:
- ✅ `gcc` - Compilação
- ✅ `pthread` - Threading library
- ✅ `bash` - Shell scripting
### Recomendadas:
- ✅ `valgrind` - Memory leak detection
- ✅ `helgrind` - Advanced data race detection
- ✅ `gcc -fsanitize=thread` - Thread sanitizer
### Instalação (Ubuntu/Debian):
```bash
sudo apt-get update
sudo apt-get install gcc valgrind build-essential
```
---
## 📝 Estrutura de um Teste:
Cada tester segue este padrão:
```bash
1. Verificar se executável existe
2. Testes funcionais básicos
3. Testes de edge cases
4. Verificação de data races (thread sanitizer)
5. Verificação de memory leaks (valgrind)
6. Summary com cores (verde/vermelho/amarelo)
```
### Exemplo de Output:
```
========================================
Testing: thread_basics
========================================
Test 1: Basic execution (1 message)... ✓ PASSED
Test 2: Multiple messages (5)... ✓ PASSED
Test 3: Large number (10)... ✓ PASSED
Test 4: Memory leak check... ✓ PASSED
========================================
Results: 4 passed, 0 failed
========================================
```
---
## 🎨 Códigos de Cores:
- 🟢 **PASSED (verde)** - Teste passou completamente
- 🔴 **FAILED (vermelho)** - Teste falhou
- 🟡 **WARNING/PARTIAL (amarelo)** - Passou com avisos
- ⊘ **SKIPPED (amarelo)** - Ferramenta não disponível
---
## 🐛 Debugging:
Se um teste falhar:
```bash
# 1. Execute manualmente
./seu_programa [argumentos]
# 2. Verifique memory leaks
valgrind --leak-check=full ./seu_programa
# 3. Verifique data races
valgrind --tool=helgrind ./seu_programa
# 4. Use thread sanitizer
gcc -fsanitize=thread -g programa.c -pthread -o programa_tsan
./programa_tsan
# 5. Veja output detalhado do tester
bash testers/test_X_nome.sh 2>&1 | tee output.log
```
---
## 📚 Recursos Adicionais:
### Documentação:
- `testers/README.md` - Guia completo de uso
- `testers/TEST_COVERAGE.md` - Cobertura detalhada
- Comentários em cada script de teste
### Para o Projeto Real:
Testes importantes para Philosophers:
```bash
# Casos críticos
./philo 1 800 200 200 # Deve morrer (sem garfos)
./philo 4 410 200 200 # Caso difícil
./philo 4 310 200 100 # Timing apertado
./philo 5 800 200 200 # Número ímpar
./philo 5 800 200 200 7 # Com limite de refeições
# Verificação completa
valgrind --tool=helgrind ./philo 5 800 200 200
valgrind --leak-check=full ./philo 5 800 200 200 7
```
---
## ✨ Próximos Passos:
### 1. Complete os Exercícios:
Resolva cada exercício seguindo os subjects (arquivos .txt)
### 2. Teste Continuamente:
```bash
# Após implementar cada exercício
bash testers/test_X_nome.sh
```
### 3. Corrija Erros:
- Memory leaks → Use valgrind para encontrar
- Data races → Use helgrind/thread sanitizer
- Logic errors → Debug com gdb
### 4. Prepare para Philosophers:
- Todos os exercícios passando
- Entenda cada conceito
- Pratique casos edge
- Otimize performance
---
## 🎓 Conceitos Cobertos:
### Threading & Synchronization:
- [x] pthread_create, pthread_join
- [x] pthread_mutex (init, lock, unlock, destroy)
- [x] Race conditions e critical sections
- [x] Deadlock detection e prevention
### Timing:
- [x] gettimeofday
- [x] Precise sleep implementation
- [x] Timestamp management
### Philosophers-Specific:
- [x] Circular resource allocation
- [x] Death monitoring
- [x] State transitions
- [x] Fork management
- [x] Meal counting
### Bonus (Processes):
- [x] fork(), waitpid(), kill()
- [x] POSIX semaphores
- [x] Signal handling
- [x] Process cleanup
---
## ✅ Checklist de Qualidade:
Antes de submeter Philosophers, verifique:
- [ ] Todos os exercícios preparatórios passam
- [ ] Valgrind: 0 memory leaks
- [ ] Helgrind: 0 data races
- [ ] Death detection < 10ms
- [ ] Funciona com 1, 2, 3, 4, 5+ filósofos
- [ ] Timing apertado funciona (310ms, 410ms)
- [ ] Meal limit correto
- [ ] Norminette OK
- [ ] Makefile correto
- [ ] No forbidden functions
---
## 🤝 Suporte:
### Problemas Comuns:
**"Permission denied"**
```bash
chmod +x testers/*.sh
```
**"Valgrind not found"**
```bash
sudo apt-get install valgrind
```
**"Thread sanitizer não compila"**
```bash
# Precisa gcc >= 4.8
gcc --version
```
**Semáforos não limpam (bonus)**
```bash
ls /dev/shm/sem.*
rm /dev/shm/sem.*
```
---
## 🎉 Conclusão:
Sistema completo de testes criado com sucesso!
- ✅ 12 testers automáticos
- ✅ ~99 testes individuais
- ✅ Cobertura completa dos exercícios
- ✅ Documentação detalhada
- ✅ Scripts prontos para uso
**Boa sorte com os exercícios e o projeto Philosophers!** 🍀
---
*Criado em: Outubro 2025*
*Compatível com: 42 Philosophers Project*
*Testado em: Linux/Ubuntu*

408
testers/TEST_COVERAGE.md Normal file
View File

@ -0,0 +1,408 @@
# 📊 Test Coverage Summary
## Visão Geral dos Testers
Este documento resume a cobertura de testes para cada exercício preparatório do Philosophers.
---
## 📝 Exercício 1: Thread Basics
**Arquivo**: `test_1_thread_basics.sh`
### Testes Implementados:
1. ✅ Execução básica com 1 mensagem
2. ✅ Múltiplas mensagens (5)
3. ✅ Número grande de mensagens (10)
4. ✅ Verificação de memory leaks (valgrind)
### O que é verificado:
- Thread 1 e Thread 2 criam mensagens corretas
- Número correto de mensagens por thread
- Threads fazem join corretamente
---
## 🔒 Exercício 2: Mutex Basics
**Arquivo**: `test_2_mutex_basics.sh`
### Testes Implementados:
1. ✅ Valor do contador (deve ser 4000)
2. ✅ Teste de consistência (5 execuções)
3. ✅ Tempo de execução reportado
4. ✅ Detecção de data races (thread sanitizer)
5. ✅ Memory leaks
### O que é verificado:
- Mutex protege corretamente a variável compartilhada
- Resultado é sempre 4000 (sem race conditions)
- Thread sanitizer não detecta problemas
---
## ⏱️ Exercício 3: Precise Timing
**Arquivo**: `test_3_precise_timing.sh`
### Testes Implementados:
1. ✅ Programa executa com sucesso
2. ✅ Precisão do sleep (~100ms ±5ms)
3. ✅ Consistência através de 5 execuções
4. ✅ Formato do output
5. ✅ Memory leaks
### O que é verificado:
- Sleep preciso de 100ms
- Margem de erro aceitável (95-105ms)
- Funções de timing funcionam corretamente
---
## 📡 Exercício 4: State Monitor
**Arquivo**: `test_4_state_monitor.sh`
### Testes Implementados:
1. ✅ Execução do programa
2. ✅ Transições de estado (WORKING, RESTING, THINKING)
3. ✅ Timestamps presentes
4. ✅ 3 workers identificados
5. ✅ Duração de estados verificada
6. ✅ Monitor thread funcionando
7. ✅ Memory leaks
8. ✅ Data races
### O que é verificado:
- 3 workers + 1 monitor
- Estados mudam corretamente
- Monitor detecta workers travados
---
## 🏭 Exercício 5: Producer-Consumer
**Arquivo**: `test_5_producer_consumer.sh`
### Testes Implementados:
1. ✅ Programa completa execução
2. ✅ Produtores funcionando
3. ✅ Consumidores funcionando
4. ✅ Items 1-100 produzidos
5. ✅ Gestão de buffer
6. ✅ Sem duplicação de consumo
7. ✅ Data races
8. ✅ Memory leaks
### O que é verificado:
- 2 produtores + 2 consumidores
- Buffer de 10 items funciona
- Todos os 100 items são processados
- Sem race conditions
---
## 🔴 Exercício 6: Deadlock Demo
**Arquivo**: `test_6_deadlock_demo.sh`
### Testes Implementados:
1. ✅ Modo deadlock (deve travar)
2. ✅ Modo solução (deve completar)
3. ✅ Output da solução
4. ✅ Ambas threads completam
5. ✅ Consistência da solução (5 runs)
6. ✅ Uso de mutex
7. ✅ Handling de argumentos inválidos
8. ✅ Memory leaks
### O que é verificado:
- Deadlock é demonstrado no modo 0
- Solução funciona no modo 1
- Ordem consistente de locks previne deadlock
---
## 🎯 Exercício 7: Limited Resources
**Arquivo**: `test_7_limited_resources.sh`
### Testes Implementados:
1. ✅ Programa completa
2. ✅ 10 estudantes presentes
3. ✅ Mensagens de chegada
4. ✅ Obtenção de computadores
5. ✅ Mensagens de saída
6. ✅ Limite de 3 recursos simultâneos
7. ✅ Fairness (nenhum starved)
8. ✅ Data races
9. ✅ Memory leaks
### O que é verificado:
- Semáforo implementado com mutex
- Máximo 3 usuários simultâneos
- Todos os 10 estudantes usam o recurso
---
## 🍽️ Exercício 8: Simple Philosophers
**Arquivo**: `test_8_simple_philosophers.sh`
### Testes Implementados:
1. ✅ Duração do programa (~10s)
2. ✅ 3 filósofos presentes
3. ✅ Ações de pensar
4. ✅ Ações de comer
5. ✅ Timestamps
6. ✅ Todos os filósofos comem
7. ✅ Sem mortes
8. ✅ Data races
9. ✅ Sem deadlock (3 runs)
10. ✅ Memory leaks
### O que é verificado:
- 3 filósofos, 3 garfos
- Ciclo thinking → eating funciona
- Ordem de garfos previne deadlock
- Programa roda ~10 segundos
---
## ☠️ Exercício 9: Death Monitor
**Arquivo**: `test_9_death_monitor.sh`
### Testes Implementados:
1. ✅ Execução do programa
2. ✅ 4 workers presentes
3. ✅ Atividade dos workers
4. ✅ Monitor thread
5. ✅ Detecção de morte
6. ✅ Timestamps
7. ✅ Tracking de last_activity
8. ✅ Data races
9. ✅ Consistência (3 runs)
10. ✅ Memory leaks
### O que é verificado:
- Monitor verifica a cada 100ms
- Detecta "morte" após 3s de inatividade
- Mutex protege last_activity_time
---
## 🎓 Exercício 10: Philosophers Args (Completo)
**Arquivo**: `test_10_philosophers_args.sh`
### Testes Implementados:
1. ✅ Caso básico (5 800 200 200) - sem mortes
2. ✅ Cenário de morte (4 310 200 100)
3. ✅ Limite de refeições (5 800 200 200 7)
4. ✅ Filósofo sozinho (deve morrer)
5. ✅ Argumentos inválidos
6. ✅ IDs de filósofos válidos
7. ✅ Timestamps crescentes
8. ✅ Timing de detecção de morte
9. ✅ Data races
10. ✅ Número ímpar de filósofos
11. ✅ Memory leaks
### O que é verificado:
- Parsing completo de argumentos
- Detecção de morte funciona
- Limite de refeições funciona
- Casos edge (1 philo, timing apertado)
- Timestamps são monotônicos
---
## 🔍 Exercício 11: Race Detector
**Arquivo**: `test_11_race_detector.sh`
### Testes Implementados:
1. ✅ Execução básica
2. ✅ Múltiplas iterações
3. ✅ Relatório de estatísticas
4. ✅ Diferentes estratégias
5. ✅ Helgrind check
6. ✅ Número ímpar de filósofos
7. ✅ Timing apertado
8. ✅ Métricas de performance
9. ✅ Thread sanitizer
10. ✅ Memory leaks
### O que é verificado:
- Testa 100 iterações
- Compara estratégias A, B, C
- Reporta races, deaths, performance
- Helgrind/DRD detectam problemas
---
## 🌟 Exercício 12: Philosophers Bonus
**Arquivo**: `test_12_philosophers_bonus.sh`
### Testes Implementados:
1. ✅ Caso básico com processos
2. ✅ Usa fork() (não threads)
3. ✅ Usa semáforos POSIX
4. ✅ Cenário de morte
5. ✅ Limite de refeições
6. ✅ Filósofo sozinho
7. ✅ Argumentos inválidos
8. ✅ Cleanup de processos (no zombies)
9. ✅ Cleanup de semáforos
10. ✅ Estatísticas finais
11. ✅ Helgrind no processo pai
### O que é verificado:
- Usa processos (fork) em vez de threads
- Semáforos POSIX (sem_open, sem_wait, sem_post)
- Cleanup correto (sem_unlink)
- Signal handling (SIGINT, SIGTERM)
- Sem processos zombie
---
## 🎯 Estatísticas Gerais
### Total de Testes:
- **Exercício 1**: 4 testes
- **Exercício 2**: 5 testes
- **Exercício 3**: 5 testes
- **Exercício 4**: 8 testes
- **Exercício 5**: 8 testes
- **Exercício 6**: 8 testes
- **Exercício 7**: 9 testes
- **Exercício 8**: 10 testes
- **Exercício 9**: 10 testes
- **Exercício 10**: 11 testes
- **Exercício 11**: 10 testes
- **Exercício 12**: 11 testes
**TOTAL**: ~99 testes automatizados
### Categorias de Testes:
- ✅ Funcionalidade básica: 12/12 exercícios
- ✅ Memory leaks (valgrind): 12/12 exercícios
- ✅ Data races (sanitizer): 10/12 exercícios
- ✅ Edge cases: 10/12 exercícios
- ✅ Consistência: 8/12 exercícios
- ✅ Performance: 2/12 exercícios
### Ferramentas Utilizadas:
- `gcc` - Compilação e thread sanitizer
- `valgrind` - Memory leak detection
- `helgrind` - Data race detection (avançado)
- `timeout` - Detecção de deadlocks/hangs
- `grep/sed/awk` - Parsing de output
- `ps` - Verificação de processos zombie
---
## 🔑 Conceitos-Chave Testados
### Threading:
- [x] pthread_create
- [x] pthread_join
- [x] pthread_detach
- [x] Thread arguments
- [x] Multiple threads
### Synchronization:
- [x] pthread_mutex_init/destroy
- [x] pthread_mutex_lock/unlock
- [x] Critical sections
- [x] Race conditions
- [x] Deadlock prevention
### Timing:
- [x] gettimeofday
- [x] Precise sleep
- [x] Timestamp generation
- [x] Time difference calculation
### Processes (Bonus):
- [x] fork()
- [x] waitpid()
- [x] kill()
- [x] Signal handling
- [x] Process cleanup
### Semaphores (Bonus):
- [x] sem_open
- [x] sem_wait/sem_post
- [x] sem_close/sem_unlink
- [x] Named semaphores
### Philosophers-Specific:
- [x] Circular resource allocation
- [x] Death detection
- [x] Meal counting
- [x] State transitions
- [x] Monitor pattern
- [x] Fork ordering (deadlock prevention)
---
## 📈 Níveis de Dificuldade
### Iniciante (Exercícios 1-3):
- Thread Basics ⭐
- Mutex Basics ⭐
- Precise Timing ⭐⭐
### Intermediário (Exercícios 4-7):
- State Monitor ⭐⭐
- Producer-Consumer ⭐⭐⭐
- Deadlock Demo ⭐⭐
- Limited Resources ⭐⭐⭐
### Avançado (Exercícios 8-10):
- Simple Philosophers ⭐⭐⭐⭐
- Death Monitor ⭐⭐⭐⭐
- Philosophers Args ⭐⭐⭐⭐⭐
### Expert (Exercícios 11-12):
- Race Detector ⭐⭐⭐⭐⭐
- Philosophers Bonus ⭐⭐⭐⭐⭐
---
## ✨ Próximos Passos
Depois de completar todos os exercícios:
1. **Revise conceitos**:
- Data races e como evitá-los
- Deadlock e estratégias de prevenção
- Timing preciso e monitoramento
2. **Pratique casos edge**:
- 1 filósofo
- 2 filósofos
- Números grandes (200+ filósofos)
- Timing muito apertado
3. **Otimize**:
- Minimize tempo dentro de locks
- Balance entre precisão e performance
- Evite busy waiting
4. **Teste rigorosamente**:
```bash
valgrind --tool=helgrind ./philo 4 410 200 200
valgrind --leak-check=full ./philo 5 800 200 200 7
```
5. **Prepare para o projeto real**:
- Norminette
- Makefile correto
- Error handling robusto
- Código limpo e documentado
Boa sorte! 🚀

99
testers/run_all_tests.sh Executable file
View File

@ -0,0 +1,99 @@
#!/bin/bash
# Master test runner - runs all tests
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
TESTER_DIR="testers"
echo "========================================"
echo -e "${BLUE}Philosophers Preparation - Test Suite${NC}"
echo "========================================"
echo ""
TOTAL_PASSED=0
TOTAL_FAILED=0
# Function to run a test
run_test() {
local test_script=$1
local test_name=$2
if [ -f "$TESTER_DIR/$test_script" ]; then
echo ""
echo -e "${BLUE}▶ Running: $test_name${NC}"
echo "----------------------------------------"
bash "$TESTER_DIR/$test_script"
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}$test_name - ALL TESTS PASSED${NC}"
((TOTAL_PASSED++))
else
echo -e "${RED}$test_name - SOME TESTS FAILED${NC}"
((TOTAL_FAILED++))
fi
echo ""
else
echo -e "${YELLOW}⊘ Skipping $test_name (tester not found)${NC}"
echo ""
fi
}
# 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" ;;
*)
echo "Usage: $0 [exercise_number]"
echo "Example: $0 1 (to test only exercise 1)"
echo "Or run without arguments to test all exercises"
exit 1
;;
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"
fi
# Final summary
echo "========================================"
echo -e "${BLUE}FINAL RESULTS${NC}"
echo "========================================"
echo -e "Exercises passed: ${GREEN}$TOTAL_PASSED${NC}"
echo -e "Exercises failed: ${RED}$TOTAL_FAILED${NC}"
echo "========================================"
if [ $TOTAL_FAILED -eq 0 ] && [ $TOTAL_PASSED -gt 0 ]; then
echo -e "${GREEN}🎉 All tests passed! Great job!${NC}"
exit 0
else
echo -e "${YELLOW}Keep working on the failed exercises.${NC}"
exit 1
fi

View File

@ -0,0 +1,236 @@
#!/bin/bash
# Tester for philosophers_args exercise
# Tests complete philosophers implementation with arguments
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="philosophers_args"
PASSED=0
FAILED=0
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"
exit 1
fi
# 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
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
OUTPUT=$(cat /tmp/philo_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: 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
OUTPUT=$(cat /tmp/philo_test2.txt)
if echo "$OUTPUT" | grep -qiE "die"; then
echo -e "${GREEN}✓ PASSED${NC} (death detected)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "A philosopher should have died but didn't"
((FAILED++))
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
EXIT_CODE=$?
OUTPUT=$(cat /tmp/philo_test3.txt)
if [ $EXIT_CODE -eq 0 ]; then
# Check if program terminated (should stop after all ate 7 times)
if echo "$OUTPUT" | grep -qiE "(done|finish|complete|all.*eat)" || [ $(echo "$OUTPUT" | wc -l) -lt 1000 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${YELLOW}⚠ PARTIAL${NC} (program completed but unclear if meal limit worked)"
((PASSED++))
fi
else
echo -e "${RED}✗ FAILED${NC}"
((FAILED++))
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
OUTPUT=$(cat /tmp/philo_test4.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 (no fork available)"
((FAILED++))
fi
# Test 5: Invalid arguments
echo -n "Test 5: 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 abc def ghi > /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 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
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 -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!"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 11: Valgrind not installed, skipping${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

196
testers/test_11_race_detector.sh Executable file
View File

@ -0,0 +1,196 @@
#!/bin/bash
# Tester for race_detector exercise
# Tests data race detection and optimization strategies
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="race_detector"
PASSED=0
FAILED=0
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"
exit 1
fi
# 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
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED (timeout or crash)${NC}"
((FAILED++))
fi
OUTPUT=$(cat /tmp/race_detector_output.txt 2>/dev/null)
# Test 2: Multiple iterations/strategies tested
echo -n "Test 2: Multiple test iterations... "
if echo "$OUTPUT" | grep -qiE "(iteration|run|test|strategy)"; then
ITERATIONS=$(echo "$OUTPUT" | grep -ciE "(iteration|run|test)")
if [ "$ITERATIONS" -ge 3 ]; then
echo -e "${GREEN}✓ PASSED${NC} ($ITERATIONS iterations)"
((PASSED++))
else
echo -e "${YELLOW}⚠ PARTIAL${NC} (found $ITERATIONS iterations)"
((PASSED++))
fi
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected multiple test iterations"
((FAILED++))
fi
# Test 3: Reports statistics
echo -n "Test 3: Statistics reporting... "
HAS_STATS=false
if echo "$OUTPUT" | grep -qiE "(race|death|time|performance)"; then
HAS_STATS=true
fi
if [ "$HAS_STATS" = true ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected statistics about races, deaths, or performance"
((FAILED++))
fi
# Test 4: Different strategies mentioned
echo -n "Test 4: Multiple strategies... "
STRATEGY_COUNT=$(echo "$OUTPUT" | grep -ciE "(strategy|approach|method|optimization)")
if [ "$STRATEGY_COUNT" -ge 2 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC}"
echo "Expected different optimization strategies to be tested"
((PASSED++))
fi
# Test 5: Helgrind check (if available)
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
HELGRIND_OUTPUT=$(cat /tmp/helgrind_output.txt 2>/dev/null)
if echo "$HELGRIND_OUTPUT" | grep -q "ERROR SUMMARY: 0 errors"; then
echo -e "${GREEN}✓ PASSED (no races detected by helgrind)${NC}"
((PASSED++))
elif echo "$HELGRIND_OUTPUT" | grep -q "Possible data race"; then
echo -e "${RED}✗ FAILED${NC}"
echo "Helgrind detected data races!"
echo "$HELGRIND_OUTPUT" | grep -A 2 "Possible data race" | head -10
((FAILED++))
else
echo -e "${YELLOW}⚠ UNCLEAR${NC} (helgrind ran but results unclear)"
((PASSED++))
fi
rm -f /tmp/helgrind_output.txt
else
echo -e "${YELLOW}⊘ Test 5: Valgrind not installed, skipping helgrind${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
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 -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!"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 10: Valgrind not installed, skipping${NC}"
fi
# Cleanup
rm -f /tmp/race_detector_*.txt /tmp/race_detector_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

View File

@ -0,0 +1,235 @@
#!/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

111
testers/test_1_thread_basics.sh Executable file
View File

@ -0,0 +1,111 @@
#!/bin/bash
# Tester for thread_basics exercise
# Tests basic thread creation and joining
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
EXERCISE="thread_basics"
PASSED=0
FAILED=0
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"
exit 1
fi
# Test 1: Basic execution with 1 message
echo -n "Test 1: Basic execution (1 message)... "
OUTPUT=$(timeout 5 ./$EXERCISE 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
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected output to contain both thread messages"
echo "Got: $OUTPUT"
((FAILED++))
fi
else
echo -e "${RED}✗ FAILED (timeout or crash)${NC}"
((FAILED++))
fi
# Test 2: Multiple messages (5)
echo -n "Test 2: Multiple messages (5)... "
OUTPUT=$(timeout 5 ./$EXERCISE 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")
if [ "$COUNT_T1" -eq 5 ] && [ "$COUNT_T2" -eq 5 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected 5 messages from each thread"
echo "Got: Thread 1: $COUNT_T1, Thread 2: $COUNT_T2"
((FAILED++))
fi
else
echo -e "${RED}✗ FAILED (timeout or crash)${NC}"
((FAILED++))
fi
# Test 3: Large number (10)
echo -n "Test 3: Large number (10)... "
OUTPUT=$(timeout 5 ./$EXERCISE 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")
if [ "$COUNT_T1" -eq 10 ] && [ "$COUNT_T2" -eq 10 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected 10 messages from each thread"
echo "Got: Thread 1: $COUNT_T1, Thread 2: $COUNT_T2"
((FAILED++))
fi
else
echo -e "${RED}✗ FAILED (timeout or crash)${NC}"
((FAILED++))
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)
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 4: Valgrind not installed, skipping memory test${NC}"
fi
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
if [ $FAILED -eq 0 ]; then
exit 0
else
exit 1
fi

134
testers/test_2_mutex_basics.sh Executable file
View File

@ -0,0 +1,134 @@
#!/bin/bash
# Tester for mutex_basics exercise
# Tests mutex protection of shared variables
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="mutex_basics"
PASSED=0
FAILED=0
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"
exit 1
fi
# Test 1: Counter should be exactly 4000
echo -n "Test 1: Counter correctness (should be 4000)... "
OUTPUT=$(timeout 10 ./$EXERCISE 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
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)
fi
if [ "$COUNTER" = "4000" ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected counter = 4000, got: $COUNTER"
echo "Output: $OUTPUT"
((FAILED++))
fi
else
echo -e "${RED}✗ FAILED (timeout or crash)${NC}"
((FAILED++))
fi
# Test 2: Run multiple times to ensure consistency
echo -n "Test 2: Consistency check (5 runs)... "
CONSISTENT=true
for i in {1..5}; do
OUTPUT=$(timeout 10 ./$EXERCISE 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)
fi
if [ "$COUNTER" != "4000" ]; then
CONSISTENT=false
break
fi
done
if [ "$CONSISTENT" = true ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Counter value inconsistent across multiple runs"
echo "This suggests race conditions - mutex not working properly"
((FAILED++))
fi
# Test 3: Check execution time is reported
echo -n "Test 3: Execution time reported... "
OUTPUT=$(timeout 10 ./$EXERCISE 2>&1)
if echo "$OUTPUT" | grep -qiE "(time|ms|milliseconds|seconds|duration)"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Program should print the execution time"
((FAILED++))
fi
# Test 4: Thread sanitizer check
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
if [ $? -eq 0 ]; then
TSAN_OUTPUT=$(timeout 15 ./mutex_basics_tsan 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"
echo "$TSAN_OUTPUT" | head -20
((FAILED++))
else
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
fi
rm -f mutex_basics_tsan
else
echo -e "${YELLOW}⊘ Could not compile with thread sanitizer${NC}"
fi
else
echo -e "${YELLOW}⊘ Test 4: gcc not available, skipping sanitizer test${NC}"
fi
# Test 5: Memory leak check
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)
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}"
fi
# Summary
echo "========================================"
echo -e "Results: ${GREEN}$PASSED passed${NC}, ${RED}$FAILED failed${NC}"
echo "========================================"
[ $FAILED -eq 0 ] && exit 0 || exit 1

137
testers/test_3_precise_timing.sh Executable file
View File

@ -0,0 +1,137 @@
#!/bin/bash
# Tester for precise_timing exercise
# Tests timing functions accuracy
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="precise_timing"
PASSED=0
FAILED=0
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"
exit 1
fi
# Test 1: Basic execution
echo -n "Test 1: Program runs successfully... "
OUTPUT=$(timeout 5 ./$EXERCISE 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED (timeout or crash)${NC}"
((FAILED++))
fi
# Test 2: Check if time difference is approximately 100ms
echo -n "Test 2: Sleep precision (~100ms)... "
OUTPUT=$(timeout 5 ./$EXERCISE 2>&1)
# Extract number that should be around 100
TIME_DIFF=$(echo "$OUTPUT" | grep -oP "\d+" | tail -1)
if [ -n "$TIME_DIFF" ]; then
# Allow 5ms margin of error (95-105ms is acceptable)
if [ "$TIME_DIFF" -ge 95 ] && [ "$TIME_DIFF" -le 105 ]; then
echo -e "${GREEN}✓ PASSED${NC} (measured: ${TIME_DIFF}ms)"
((PASSED++))
elif [ "$TIME_DIFF" -ge 90 ] && [ "$TIME_DIFF" -le 110 ]; then
echo -e "${YELLOW}⚠ PARTIAL${NC} (measured: ${TIME_DIFF}ms, acceptable but not ideal)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected ~100ms, got: ${TIME_DIFF}ms"
echo "Output: $OUTPUT"
((FAILED++))
fi
else
echo -e "${RED}✗ FAILED${NC}"
echo "Could not extract time difference from output"
echo "Output: $OUTPUT"
((FAILED++))
fi
# Test 3: Consistency across multiple runs
echo -n "Test 3: Timing consistency (5 runs)... "
CONSISTENT=true
TOTAL=0
COUNT=0
for i in {1..5}; do
OUTPUT=$(timeout 5 ./$EXERCISE 2>&1)
TIME_DIFF=$(echo "$OUTPUT" | grep -oP "\d+" | tail -1)
if [ -n "$TIME_DIFF" ]; then
TOTAL=$((TOTAL + TIME_DIFF))
COUNT=$((COUNT + 1))
if [ "$TIME_DIFF" -lt 90 ] || [ "$TIME_DIFF" -gt 110 ]; then
CONSISTENT=false
fi
fi
done
if [ "$CONSISTENT" = true ] && [ "$COUNT" -eq 5 ]; then
AVG=$((TOTAL / COUNT))
echo -e "${GREEN}✓ PASSED${NC} (avg: ${AVG}ms)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
if [ "$COUNT" -eq 5 ]; then
AVG=$((TOTAL / COUNT))
echo "Timing inconsistent across runs (avg: ${AVG}ms)"
else
echo "Some runs failed to complete"
fi
((FAILED++))
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
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Program should clearly indicate the time difference"
echo "Output: $OUTPUT"
((FAILED++))
fi
# Test 5: Memory leak check
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)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
((FAILED++))
fi
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 "========================================"
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

155
testers/test_4_state_monitor.sh Executable file
View File

@ -0,0 +1,155 @@
#!/bin/bash
# Tester for state_monitor exercise
# Tests state monitoring and thread synchronization
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="state_monitor"
PASSED=0
FAILED=0
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"
exit 1
fi
# Test 1: Program runs for reasonable time
echo -n "Test 1: Program execution... "
timeout 15 ./$EXERCISE > /tmp/state_monitor_output.txt 2>&1 &
PID=$!
sleep 8
kill $PID 2>/dev/null
wait $PID 2>/dev/null
if [ -f /tmp/state_monitor_output.txt ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
((FAILED++))
fi
# Test 2: Check for state transitions
echo -n "Test 2: State transitions present... "
OUTPUT=$(cat /tmp/state_monitor_output.txt 2>/dev/null)
if echo "$OUTPUT" | grep -qi "WORKING" && \
echo "$OUTPUT" | grep -qi "RESTING" && \
echo "$OUTPUT" | grep -qi "THINKING"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected to see WORKING, RESTING, and THINKING states"
((FAILED++))
fi
# Test 3: Check for timestamps
echo -n "Test 3: Timestamps present... "
if echo "$OUTPUT" | grep -qE "\[[0-9]+\]"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected timestamps in format [timestamp]"
((FAILED++))
fi
# Test 4: Check for worker identification
echo -n "Test 4: Worker identification... "
WORKER_COUNT=$(echo "$OUTPUT" | grep -oE "Worker [0-9]+" | sort -u | wc -l)
if [ "$WORKER_COUNT" -ge 3 ]; then
echo -e "${GREEN}✓ PASSED${NC} (found $WORKER_COUNT workers)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected at least 3 workers, found: $WORKER_COUNT"
((FAILED++))
fi
# Test 5: Check state duration logic
echo -n "Test 5: State duration verification... "
# WORKING should last ~2s, RESTING ~1s, THINKING ~1.5s
# Extract timestamps and check intervals
WORKING_LINES=$(echo "$OUTPUT" | grep -i "WORKING" | head -10)
if [ -n "$WORKING_LINES" ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC} - Could not verify state durations"
((PASSED++))
fi
# Test 6: Monitor thread functionality
echo -n "Test 6: Monitor warnings (if applicable)... "
if echo "$OUTPUT" | grep -qi "WARNING"; then
echo -e "${GREEN}✓ PASSED${NC} (monitor detected stuck worker)"
((PASSED++))
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++))
fi
# Test 7: Memory leak check
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
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
((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}"
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

View File

@ -0,0 +1,161 @@
#!/bin/bash
# Tester for producer_consumer exercise
# Tests producer-consumer pattern with buffer
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="producer_consumer"
PASSED=0
FAILED=0
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"
exit 1
fi
# Test 1: Program runs to completion
echo -n "Test 1: Program execution... "
timeout 30 ./$EXERCISE > /tmp/producer_consumer_output.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED (timeout or crash)${NC}"
((FAILED++))
fi
OUTPUT=$(cat /tmp/producer_consumer_output.txt 2>/dev/null)
# Test 2: Check for producer activity
echo -n "Test 2: Producer threads working... "
PRODUCER_COUNT=$(echo "$OUTPUT" | grep -ciE "(produc|add|insert|put)")
if [ "$PRODUCER_COUNT" -gt 0 ]; then
echo -e "${GREEN}✓ PASSED${NC} ($PRODUCER_COUNT producer actions)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "No producer activity detected"
((FAILED++))
fi
# Test 3: Check for consumer activity
echo -n "Test 3: Consumer threads working... "
CONSUMER_COUNT=$(echo "$OUTPUT" | grep -ciE "(consum|take|remove|get|process)")
if [ "$CONSUMER_COUNT" -gt 0 ]; then
echo -e "${GREEN}✓ PASSED${NC} ($CONSUMER_COUNT consumer actions)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "No consumer activity detected"
((FAILED++))
fi
# Test 4: Check that items 1-100 are produced
echo -n "Test 4: Items 1-100 produced... "
ITEMS_FOUND=0
for i in {1..100}; do
if echo "$OUTPUT" | grep -q "\\b$i\\b"; then
((ITEMS_FOUND++))
fi
done
if [ "$ITEMS_FOUND" -ge 90 ]; then
echo -e "${GREEN}✓ PASSED${NC} (found $ITEMS_FOUND/100 items)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected 100 items, found: $ITEMS_FOUND"
((FAILED++))
fi
# Test 5: Check for buffer management
echo -n "Test 5: Buffer management... "
if echo "$OUTPUT" | grep -qiE "(buffer|full|empty)"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC}"
echo "No explicit buffer status messages (this may be OK)"
((PASSED++))
fi
# Test 6: Verify no duplicate processing
echo -n "Test 6: No duplicate consumption... "
# This is hard to test perfectly, but we can check the logic
DUPLICATE_FOUND=false
for i in {1..100}; do
COUNT=$(echo "$OUTPUT" | grep -ciE "(consum|process).*\\b$i\\b")
if [ "$COUNT" -gt 1 ]; then
DUPLICATE_FOUND=true
break
fi
done
if [ "$DUPLICATE_FOUND" = false ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Item $i was consumed multiple times - race condition!"
((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
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)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 8: Valgrind not installed, skipping${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

169
testers/test_6_deadlock_demo.sh Executable file
View File

@ -0,0 +1,169 @@
#!/bin/bash
# Tester for deadlock_demo exercise
# Tests deadlock demonstration and solution
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="deadlock_demo"
PASSED=0
FAILED=0
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"
exit 1
fi
# 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
EXIT_CODE=$?
if [ $EXIT_CODE -eq 124 ]; then
# Timeout occurred - this is expected for deadlock
echo -e "${GREEN}✓ PASSED${NC} (program deadlocked as expected)"
((PASSED++))
else
OUTPUT=$(cat /tmp/deadlock_output.txt 2>/dev/null)
if echo "$OUTPUT" | grep -qi "deadlock"; then
echo -e "${GREEN}✓ PASSED${NC} (deadlock detected/explained)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Program should demonstrate deadlock (hang) or explain it"
echo "Exit code: $EXIT_CODE"
((FAILED++))
fi
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
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}✓ PASSED${NC} (completed successfully)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Solution mode should complete without hanging"
echo "Exit code: $EXIT_CODE"
((FAILED++))
fi
# Test 3: Check solution output
echo -n "Test 3: Solution produces output... "
OUTPUT=$(cat /tmp/deadlock_solution.txt 2>/dev/null)
if [ -n "$OUTPUT" ] && [ $(echo "$OUTPUT" | wc -l) -gt 2 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected meaningful output from solution mode"
((FAILED++))
fi
# Test 4: Verify both threads complete in solution mode
echo -n "Test 4: Both threads complete (solution)... "
if echo "$OUTPUT" | grep -qiE "(thread 1|t1)" && \
echo "$OUTPUT" | grep -qiE "(thread 2|t2)"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC}"
echo "Could not clearly identify both threads in output"
echo "Output: $OUTPUT"
((PASSED++))
fi
# Test 5: Multiple runs of solution (should always work)
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
if [ $? -ne 0 ]; then
ALL_SUCCESS=false
break
fi
done
if [ "$ALL_SUCCESS" = true ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Solution mode failed on iteration $i"
((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)
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)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 8: Valgrind not installed, skipping${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

View File

@ -0,0 +1,168 @@
#!/bin/bash
# Tester for limited_resources exercise
# Tests semaphore-like behavior with mutexes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="limited_resources"
PASSED=0
FAILED=0
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"
exit 1
fi
# Test 1: Program runs to completion
echo -n "Test 1: Program execution... "
timeout 30 ./$EXERCISE > /tmp/limited_resources_output.txt 2>&1
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED (timeout or crash)${NC}"
((FAILED++))
fi
OUTPUT=$(cat /tmp/limited_resources_output.txt 2>/dev/null)
# Test 2: 10 students detected
echo -n "Test 2: 10 students present... "
STUDENT_COUNT=$(echo "$OUTPUT" | grep -oiE "student [0-9]+" | sort -u | wc -l)
if [ "$STUDENT_COUNT" -ge 10 ]; then
echo -e "${GREEN}✓ PASSED${NC} (found $STUDENT_COUNT students)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected 10 students, found: $STUDENT_COUNT"
((FAILED++))
fi
# Test 3: Check for arrival messages
echo -n "Test 3: Students arrive... "
ARRIVAL_COUNT=$(echo "$OUTPUT" | grep -ciE "(arrive|enter|library)")
if [ "$ARRIVAL_COUNT" -ge 10 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected arrival messages for 10 students"
((FAILED++))
fi
# Test 4: Check for computer acquisition
echo -n "Test 4: Getting computers... "
GET_COUNT=$(echo "$OUTPUT" | grep -ciE "(get|got|using|acquired|computer)")
if [ "$GET_COUNT" -ge 10 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "All students should get a computer eventually"
((FAILED++))
fi
# Test 5: Check for leaving/finishing messages
echo -n "Test 5: Students leave... "
LEAVE_COUNT=$(echo "$OUTPUT" | grep -ciE "(leave|finish|done|release)")
if [ "$LEAVE_COUNT" -ge 10 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected leaving messages for 10 students"
((FAILED++))
fi
# Test 6: Maximum 3 concurrent users
echo -n "Test 6: Resource limit (max 3)... "
# This is tricky to test - we need to check timestamps
# Extract lines with "get/using" and "finish/leave" and verify max 3 concurrent
# For simplicity, we'll check if the logic seems sound
if echo "$OUTPUT" | grep -qiE "(wait|waiting|full|limit)"; then
echo -e "${GREEN}✓ PASSED${NC} (waiting detected - limit enforced)"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC}"
echo "Could not verify resource limit enforcement"
echo "This is OK if program structure enforces it differently"
((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
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)
if [ $? -ne 42 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 9: Valgrind not installed, skipping${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

View File

@ -0,0 +1,192 @@
#!/bin/bash
# Tester for simple_philosophers exercise
# Tests basic philosophers implementation
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="simple_philosophers"
PASSED=0
FAILED=0
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"
exit 1
fi
# 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
EXIT_CODE=$?
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
if [ $EXIT_CODE -eq 0 ] && [ $DURATION -ge 9 ] && [ $DURATION -le 12 ]; then
echo -e "${GREEN}✓ PASSED${NC} (ran for ${DURATION}s)"
((PASSED++))
elif [ $EXIT_CODE -eq 0 ]; then
echo -e "${YELLOW}⚠ PARTIAL${NC} (ran for ${DURATION}s, expected ~10s)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Program should run for approximately 10 seconds"
((FAILED++))
fi
OUTPUT=$(cat /tmp/simple_philo_output.txt 2>/dev/null)
# Test 2: Three philosophers present
echo -n "Test 2: Three philosophers... "
PHILO_COUNT=$(echo "$OUTPUT" | grep -oE "Philosopher [0-9]+" | sort -u | wc -l)
if [ "$PHILO_COUNT" -eq 3 ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected 3 philosophers, found: $PHILO_COUNT"
((FAILED++))
fi
# Test 3: Thinking action present
echo -n "Test 3: Thinking actions... "
THINKING_COUNT=$(echo "$OUTPUT" | grep -ci "thinking")
if [ "$THINKING_COUNT" -gt 0 ]; then
echo -e "${GREEN}✓ PASSED${NC} ($THINKING_COUNT occurrences)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "No thinking actions detected"
((FAILED++))
fi
# Test 4: Eating action present
echo -n "Test 4: Eating actions... "
EATING_COUNT=$(echo "$OUTPUT" | grep -ci "eating")
if [ "$EATING_COUNT" -gt 0 ]; then
echo -e "${GREEN}✓ PASSED${NC} ($EATING_COUNT occurrences)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "No eating actions detected"
((FAILED++))
fi
# Test 5: Timestamps present
echo -n "Test 5: Timestamps... "
if echo "$OUTPUT" | grep -qE "\[[0-9]+\]"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected timestamps in format [timestamp]"
((FAILED++))
fi
# Test 6: Each philosopher eats multiple times
echo -n "Test 6: All philosophers eat... "
PHILO_0_EATS=$(echo "$OUTPUT" | grep -ciE "(philosopher 0|philo 0).*eat")
PHILO_1_EATS=$(echo "$OUTPUT" | grep -ciE "(philosopher 1|philo 1).*eat")
PHILO_2_EATS=$(echo "$OUTPUT" | grep -ciE "(philosopher 2|philo 2).*eat")
if [ "$PHILO_0_EATS" -gt 0 ] && [ "$PHILO_1_EATS" -gt 0 ] && [ "$PHILO_2_EATS" -gt 0 ]; then
echo -e "${GREEN}✓ PASSED${NC} (P0:$PHILO_0_EATS, P1:$PHILO_1_EATS, P2:$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"
((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
break
fi
done
if [ "$DEADLOCK_DETECTED" = false ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Program appears to deadlock on run $i"
((FAILED++))
fi
# Test 10: Memory leak check
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 -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!"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 10: Valgrind not installed, skipping${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

180
testers/test_9_death_monitor.sh Executable file
View File

@ -0,0 +1,180 @@
#!/bin/bash
# Tester for death_monitor exercise
# Tests death detection system
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
EXERCISE="death_monitor"
PASSED=0
FAILED=0
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"
exit 1
fi
# Test 1: Program runs
echo -n "Test 1: Program execution... "
timeout 15 ./$EXERCISE > /tmp/death_monitor_output.txt 2>&1 &
PID=$!
sleep 8
kill $PID 2>/dev/null
wait $PID 2>/dev/null
if [ -f /tmp/death_monitor_output.txt ]; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
((FAILED++))
fi
OUTPUT=$(cat /tmp/death_monitor_output.txt 2>/dev/null)
# Test 2: 4 worker threads
echo -n "Test 2: 4 workers present... "
WORKER_COUNT=$(echo "$OUTPUT" | grep -oiE "worker [0-9]+" | sort -u | wc -l)
if [ "$WORKER_COUNT" -ge 4 ]; then
echo -e "${GREEN}✓ PASSED${NC} (found $WORKER_COUNT workers)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected 4 workers, found: $WORKER_COUNT"
((FAILED++))
fi
# Test 3: Workers are active
echo -n "Test 3: Worker activity... "
ACTIVITY_COUNT=$(echo "$OUTPUT" | grep -ciE "(worker|work|activity|iteration)")
if [ "$ACTIVITY_COUNT" -gt 10 ]; then
echo -e "${GREEN}✓ PASSED${NC} ($ACTIVITY_COUNT activities)"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Not enough worker activity detected"
((FAILED++))
fi
# Test 4: Monitor thread exists
echo -n "Test 4: Monitor thread... "
if echo "$OUTPUT" | grep -qiE "(monitor|checking|detect)"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC}"
echo "Could not clearly identify monitor thread activity"
((PASSED++))
fi
# Test 5: Death detection
echo -n "Test 5: Death detection... "
if echo "$OUTPUT" | grep -qiE "(die|dead|death|warning)"; then
echo -e "${GREEN}✓ PASSED${NC} (death/warning detected)"
((PASSED++))
else
echo -e "${YELLOW}⚠ PARTIAL${NC}"
echo "No death detected (might be OK if worker didn't die during test)"
((PASSED++))
fi
# Test 6: Timestamps present
echo -n "Test 6: Timestamps... "
if echo "$OUTPUT" | grep -qE "\[[0-9]+\]"; then
echo -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Expected timestamps"
((FAILED++))
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 -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${YELLOW}⚠ WARNING${NC}"
echo "Could not verify activity time tracking"
((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
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 -e "${GREEN}✓ PASSED${NC}"
((PASSED++))
else
echo -e "${RED}✗ FAILED${NC}"
echo "Memory leaks detected!"
((FAILED++))
fi
else
echo -e "${YELLOW}⊘ Test 10: Valgrind not installed, skipping${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