Moved subjects to ./subjects, added subject testers
This commit is contained in:
parent
24da1f174c
commit
00a1df51bb
167
QUICK_START.sh
Executable file
167
QUICK_START.sh
Executable 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
87
setup_testers.sh
Executable 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
283
testers/README.md
Normal 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
333
testers/TESTERS_SUMMARY.md
Normal 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
408
testers/TEST_COVERAGE.md
Normal 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
99
testers/run_all_tests.sh
Executable 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
|
||||
236
testers/test_10_philosophers_args.sh
Executable file
236
testers/test_10_philosophers_args.sh
Executable 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
196
testers/test_11_race_detector.sh
Executable 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
|
||||
235
testers/test_12_philosophers_bonus.sh
Executable file
235
testers/test_12_philosophers_bonus.sh
Executable 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
111
testers/test_1_thread_basics.sh
Executable 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
134
testers/test_2_mutex_basics.sh
Executable 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
137
testers/test_3_precise_timing.sh
Executable 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
155
testers/test_4_state_monitor.sh
Executable 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
|
||||
161
testers/test_5_producer_consumer.sh
Executable file
161
testers/test_5_producer_consumer.sh
Executable 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
169
testers/test_6_deadlock_demo.sh
Executable 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
|
||||
168
testers/test_7_limited_resources.sh
Executable file
168
testers/test_7_limited_resources.sh
Executable 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
|
||||
192
testers/test_8_simple_philosophers.sh
Executable file
192
testers/test_8_simple_philosophers.sh
Executable 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
180
testers/test_9_death_monitor.sh
Executable 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
|
||||
Loading…
Reference in New Issue
Block a user