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()