diff --git a/QUICK_START.sh b/QUICK_START.sh new file mode 100755 index 0000000..42f5284 --- /dev/null +++ b/QUICK_START.sh @@ -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 "" diff --git a/setup_testers.sh b/setup_testers.sh new file mode 100755 index 0000000..1cc4287 --- /dev/null +++ b/setup_testers.sh @@ -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 +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 "" diff --git a/10_philosophers_args.txt b/subjects/10_philosophers_args.txt similarity index 100% rename from 10_philosophers_args.txt rename to subjects/10_philosophers_args.txt diff --git a/11_race_detector.txt b/subjects/11_race_detector.txt similarity index 100% rename from 11_race_detector.txt rename to subjects/11_race_detector.txt diff --git a/12_philosophers_bonus.txt b/subjects/12_philosophers_bonus.txt similarity index 100% rename from 12_philosophers_bonus.txt rename to subjects/12_philosophers_bonus.txt diff --git a/1_thread_basics.txt b/subjects/1_thread_basics.txt similarity index 100% rename from 1_thread_basics.txt rename to subjects/1_thread_basics.txt diff --git a/2_mutex_basics.txt b/subjects/2_mutex_basics.txt similarity index 100% rename from 2_mutex_basics.txt rename to subjects/2_mutex_basics.txt diff --git a/3_precise_timing.txt b/subjects/3_precise_timing.txt similarity index 100% rename from 3_precise_timing.txt rename to subjects/3_precise_timing.txt diff --git a/4_state_monitor.txt b/subjects/4_state_monitor.txt similarity index 100% rename from 4_state_monitor.txt rename to subjects/4_state_monitor.txt diff --git a/5_producer_consumer.txt b/subjects/5_producer_consumer.txt similarity index 100% rename from 5_producer_consumer.txt rename to subjects/5_producer_consumer.txt diff --git a/6_deadlock_demo.txt b/subjects/6_deadlock_demo.txt similarity index 100% rename from 6_deadlock_demo.txt rename to subjects/6_deadlock_demo.txt diff --git a/7_limited_resources.txt b/subjects/7_limited_resources.txt similarity index 100% rename from 7_limited_resources.txt rename to subjects/7_limited_resources.txt diff --git a/8_simple_philosophers.txt b/subjects/8_simple_philosophers.txt similarity index 100% rename from 8_simple_philosophers.txt rename to subjects/8_simple_philosophers.txt diff --git a/9_death_monitor.txt b/subjects/9_death_monitor.txt similarity index 100% rename from 9_death_monitor.txt rename to subjects/9_death_monitor.txt diff --git a/testers/README.md b/testers/README.md new file mode 100644 index 0000000..ed9a57d --- /dev/null +++ b/testers/README.md @@ -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! 🍀 diff --git a/testers/TESTERS_SUMMARY.md b/testers/TESTERS_SUMMARY.md new file mode 100644 index 0000000..9919286 --- /dev/null +++ b/testers/TESTERS_SUMMARY.md @@ -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* diff --git a/testers/TEST_COVERAGE.md b/testers/TEST_COVERAGE.md new file mode 100644 index 0000000..f1c0d7e --- /dev/null +++ b/testers/TEST_COVERAGE.md @@ -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! 🚀 diff --git a/testers/run_all_tests.sh b/testers/run_all_tests.sh new file mode 100755 index 0000000..f43771a --- /dev/null +++ b/testers/run_all_tests.sh @@ -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 diff --git a/testers/test_10_philosophers_args.sh b/testers/test_10_philosophers_args.sh new file mode 100755 index 0000000..a1f3f1a --- /dev/null +++ b/testers/test_10_philosophers_args.sh @@ -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 diff --git a/testers/test_11_race_detector.sh b/testers/test_11_race_detector.sh new file mode 100755 index 0000000..b76cae3 --- /dev/null +++ b/testers/test_11_race_detector.sh @@ -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 diff --git a/testers/test_12_philosophers_bonus.sh b/testers/test_12_philosophers_bonus.sh new file mode 100755 index 0000000..f45cd61 --- /dev/null +++ b/testers/test_12_philosophers_bonus.sh @@ -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 diff --git a/testers/test_1_thread_basics.sh b/testers/test_1_thread_basics.sh new file mode 100755 index 0000000..c48c872 --- /dev/null +++ b/testers/test_1_thread_basics.sh @@ -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 diff --git a/testers/test_2_mutex_basics.sh b/testers/test_2_mutex_basics.sh new file mode 100755 index 0000000..004fcce --- /dev/null +++ b/testers/test_2_mutex_basics.sh @@ -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 diff --git a/testers/test_3_precise_timing.sh b/testers/test_3_precise_timing.sh new file mode 100755 index 0000000..0ba9c0a --- /dev/null +++ b/testers/test_3_precise_timing.sh @@ -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 diff --git a/testers/test_4_state_monitor.sh b/testers/test_4_state_monitor.sh new file mode 100755 index 0000000..dc136c8 --- /dev/null +++ b/testers/test_4_state_monitor.sh @@ -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 diff --git a/testers/test_5_producer_consumer.sh b/testers/test_5_producer_consumer.sh new file mode 100755 index 0000000..5aa57f3 --- /dev/null +++ b/testers/test_5_producer_consumer.sh @@ -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 diff --git a/testers/test_6_deadlock_demo.sh b/testers/test_6_deadlock_demo.sh new file mode 100755 index 0000000..471b8f3 --- /dev/null +++ b/testers/test_6_deadlock_demo.sh @@ -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 diff --git a/testers/test_7_limited_resources.sh b/testers/test_7_limited_resources.sh new file mode 100755 index 0000000..751ce72 --- /dev/null +++ b/testers/test_7_limited_resources.sh @@ -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 diff --git a/testers/test_8_simple_philosophers.sh b/testers/test_8_simple_philosophers.sh new file mode 100755 index 0000000..e53100f --- /dev/null +++ b/testers/test_8_simple_philosophers.sh @@ -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 diff --git a/testers/test_9_death_monitor.sh b/testers/test_9_death_monitor.sh new file mode 100755 index 0000000..aba11b0 --- /dev/null +++ b/testers/test_9_death_monitor.sh @@ -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