Files
admin-edu-space/tests/test_genetic_algorithm.py
T
2026-04-09 22:31:33 -03:00

219 lines
8.3 KiB
Python

import unittest
from datetime import datetime, timedelta
from app.models.genetic_algorithm import GeneticAlgorithm, ReservationOptimizer, ReservationRequest, Gene, Individual
from app.models.classroom import Classroom
from app.models.subject import Commission
class TestGeneticAlgorithm(unittest.TestCase):
"""Test cases for the Genetic Algorithm implementation"""
def setUp(self):
"""Set up test fixtures"""
self.ga = GeneticAlgorithm(population_size=10, generations=5)
# Mock classrooms
self.classrooms = [
Classroom(id=1, code="A101", building="A", capacity=30, is_active=True),
Classroom(id=2, code="A102", building="A", capacity=50, is_active=True),
Classroom(id=3, code="B101", building="B", capacity=25, is_active=True),
Classroom(id=4, code="B102", building="B", capacity=40, is_active=True),
]
# Mock reservation requests
start_time = datetime(2024, 1, 15, 9, 0, 0)
end_time = datetime(2024, 1, 15, 11, 0, 0)
self.requests = [
ReservationRequest(
commission_id=1,
expected_attendees=25,
purpose="Math Class",
preferred_start_time=start_time,
preferred_end_time=end_time,
priority=1,
flexibility_hours=2
),
ReservationRequest(
commission_id=2,
expected_attendees=35,
purpose="Physics Lab",
preferred_start_time=start_time,
preferred_end_time=end_time,
priority=1,
flexibility_hours=2
),
ReservationRequest(
commission_id=3,
expected_attendees=20,
purpose="Chemistry Class",
preferred_start_time=start_time,
preferred_end_time=end_time,
priority=1,
flexibility_hours=2
),
]
def test_individual_fitness_calculation(self):
"""Test fitness calculation for an individual"""
# Create an individual with some genes
genes = [
Gene(
commission_id=1,
classroom_id=1, # Capacity 30 for 25 students
start_time=datetime(2024, 1, 15, 9, 0, 0),
end_time=datetime(2024, 1, 15, 11, 0, 0),
expected_attendees=25,
purpose="Math Class"
),
Gene(
commission_id=2,
classroom_id=2, # Capacity 50 for 35 students
start_time=datetime(2024, 1, 15, 9, 0, 0),
end_time=datetime(2024, 1, 15, 11, 0, 0),
expected_attendees=35,
purpose="Physics Lab"
)
]
individual = Individual(genes)
classrooms_dict = {c.id: c for c in self.classrooms}
requests_dict = {r.commission_id: r for r in self.requests}
# Calculate fitness
fitness = individual.calculate_fitness(classrooms_dict, requests_dict)
# Fitness should be positive
self.assertGreater(fitness, 0)
self.assertEqual(individual.fitness, fitness)
def test_conflict_detection(self):
"""Test time conflict detection"""
genes = [
Gene(
commission_id=1,
classroom_id=1,
start_time=datetime(2024, 1, 15, 9, 0, 0),
end_time=datetime(2024, 1, 15, 11, 0, 0),
expected_attendees=25,
purpose="Math Class"
),
Gene(
commission_id=2,
classroom_id=1, # Same classroom
start_time=datetime(2024, 1, 15, 10, 0, 0), # Overlapping time
end_time=datetime(2024, 1, 15, 12, 0, 0),
expected_attendees=35,
purpose="Physics Lab"
)
]
individual = Individual(genes)
classrooms_dict = {c.id: c for c in self.classrooms}
requests_dict = {r.commission_id: r for r in self.requests[:2]}
# Calculate fitness - should detect conflicts
fitness = individual.calculate_fitness(classrooms_dict, requests_dict)
# Should have conflicts
self.assertGreater(len(individual.conflicts), 0)
self.assertLess(fitness, 2.0) # Lower fitness due to conflicts
def test_crossover_operation(self):
"""Test crossover operation"""
parent1_genes = [
Gene(1, 1, datetime.now(), datetime.now() + timedelta(hours=2), 25, "Class 1"),
Gene(2, 2, datetime.now(), datetime.now() + timedelta(hours=2), 30, "Class 2")
]
parent2_genes = [
Gene(3, 3, datetime.now(), datetime.now() + timedelta(hours=2), 20, "Class 3"),
Gene(4, 4, datetime.now(), datetime.now() + timedelta(hours=2), 35, "Class 4")
]
parent1 = Individual(parent1_genes)
parent2 = Individual(parent2_genes)
child1, child2 = self.ga._crossover(parent1, parent2)
# Children should have genes from both parents
self.assertGreater(len(child1.genes), 0)
self.assertGreater(len(child2.genes), 0)
# Total genes should equal sum of parents
total_genes = len(child1.genes) + len(child2.genes)
self.assertEqual(total_genes, len(parent1_genes) + len(parent2_genes))
def test_tournament_selection(self):
"""Test tournament selection"""
population = []
for i in range(5):
genes = [
Gene(i, 1, datetime.now(), datetime.now() + timedelta(hours=2), 25, f"Class {i}")
]
individual = Individual(genes)
individual.fitness = i * 0.1 # Different fitness values
population.append(individual)
selected = self.ga._tournament_selection(population)
# Should return an individual from the population
self.assertIn(selected, population)
def test_capacity_score_calculation(self):
"""Test capacity score calculation"""
# Perfect match
individual = Individual([])
score = individual._calculate_capacity_score(self.classrooms[0], 30) #capacity 30, students 30
self.assertEqual(score, 1.0)
# Underfilled but efficient (27/30 = 11.1% waste, goes to next category)
score = individual._calculate_capacity_score(self.classrooms[0], 27) # capacity 30, students 27
self.assertEqual(score, 0.8)
# Overfilled
score = individual._calculate_capacity_score(self.classrooms[0], 35) # capacity 30, students 35
self.assertEqual(score, 0.0)
def test_optimization_process(self):
"""Test the complete optimization process"""
result = self.ga.optimize_reservations(
self.requests,
self.classrooms,
datetime(2024, 1, 15),
datetime(2024, 1, 20)
)
# Should return best individual
self.assertIsInstance(result, Individual)
self.assertGreaterEqual(result.fitness, 0)
self.assertGreater(len(result.genes), 0)
def test_reservation_optimizer_interface(self):
"""Test the high-level optimizer interface"""
optimizer = ReservationOptimizer()
# Test the interface methods exist
self.assertTrue(hasattr(optimizer, 'optimize_schedule'))
self.assertTrue(hasattr(optimizer, 'apply_optimized_reservations'))
self.assertTrue(hasattr(optimizer, '_calculate_preferred_time'))
def test_reservation_request_validation(self):
"""Test reservation request validation"""
request = ReservationRequest(
commission_id=1,
expected_attendees=25,
purpose="Test Class",
preferred_start_time=datetime.now(),
preferred_end_time=datetime.now() + timedelta(hours=2)
)
# Should have required attributes
self.assertEqual(request.commission_id, 1)
self.assertEqual(request.expected_attendees, 25)
self.assertEqual(request.purpose, "Test Class")
self.assertEqual(request.priority, 1) # Default value
self.assertEqual(request.flexibility_hours, 2) # Default value
if __name__ == '__main__':
unittest.main()