Files
HeurAMS/tests/test_algorithms.py
2025-11-02 12:19:31 +08:00

206 lines
7.1 KiB
Python

"""
Unit tests for algorithm modules: BaseAlgorithm, SM2Algorithm
"""
import pytest
from datetime import datetime, timezone
from src.heurams.kernel.algorithms.base import BaseAlgorithm
from src.heurams.kernel.algorithms.sm2 import SM2Algorithm
from src.heurams.kernel.particles.electron import Electron
from src.heurams.kernel.particles.orbital import Orbital
class TestBaseAlgorithm:
"""Test cases for BaseAlgorithm class."""
def test_base_algorithm_abstract_methods(self):
"""Test that BaseAlgorithm cannot be instantiated directly."""
with pytest.raises(TypeError):
BaseAlgorithm()
class TestSM2Algorithm:
"""Test cases for SM2Algorithm class."""
def test_sm2_algorithm_creation(self):
"""Test SM2Algorithm creation."""
algorithm = SM2Algorithm()
assert algorithm.name == "sm2"
assert algorithm.version == "1.0"
def test_sm2_calculate_interval_learning_phase(self):
"""Test interval calculation in learning phase."""
algorithm = SM2Algorithm()
electron = Electron(repetitions=0)
orbital = Orbital(learning_steps=[1, 10])
interval = algorithm.calculate_interval(electron, orbital, quality=3)
assert interval == 1 # First learning step
def test_sm2_calculate_interval_graduation(self):
"""Test interval calculation when graduating."""
algorithm = SM2Algorithm()
electron = Electron(repetitions=1)
orbital = Orbital(learning_steps=[1, 10], graduating_interval=1)
interval = algorithm.calculate_interval(electron, orbital, quality=4)
assert interval == 1 # Graduating interval
def test_sm2_calculate_interval_review_phase(self):
"""Test interval calculation in review phase."""
algorithm = SM2Algorithm()
electron = Electron(ease=2.5, interval=10, repetitions=5)
orbital = Orbital()
interval = algorithm.calculate_interval(electron, orbital, quality=4)
# Should be: 10 * 2.5 = 25
assert interval == 25
def test_sm2_calculate_ease_increase(self):
"""Test ease calculation with increase."""
algorithm = SM2Algorithm()
electron = Electron(ease=2.5)
new_ease = algorithm.calculate_ease(electron, quality=5)
# Should be: 2.5 + 0.1 - 0.08 + 0.02 = 2.54
assert new_ease == pytest.approx(2.54)
def test_sm2_calculate_ease_decrease(self):
"""Test ease calculation with decrease."""
algorithm = SM2Algorithm()
electron = Electron(ease=2.5)
new_ease = algorithm.calculate_ease(electron, quality=2)
# Should be: 2.5 + 0.1 - 0.16 + 0.02 = 2.46
assert new_ease == pytest.approx(2.46)
def test_sm2_calculate_ease_minimum(self):
"""Test ease calculation with minimum bound."""
algorithm = SM2Algorithm()
electron = Electron(ease=1.3) # Very low ease
new_ease = algorithm.calculate_ease(electron, quality=0)
# Should be clamped to minimum 1.3
assert new_ease == 1.3
def test_sm2_calculate_repetitions_reset(self):
"""Test repetition calculation with reset."""
algorithm = SM2Algorithm()
electron = Electron(repetitions=5)
new_repetitions = algorithm.calculate_repetitions(electron, quality=1)
assert new_repetitions == 0 # Reset on failure
def test_sm2_calculate_repetitions_increment(self):
"""Test repetition calculation with increment."""
algorithm = SM2Algorithm()
electron = Electron(repetitions=2)
new_repetitions = algorithm.calculate_repetitions(electron, quality=3)
assert new_repetitions == 3 # Increment on success
def test_sm2_process_review_quality_1(self):
"""Test complete review process with quality 1."""
algorithm = SM2Algorithm()
electron = Electron(ease=2.5, interval=10, repetitions=5)
orbital = Orbital()
new_electron = algorithm.process_review(electron, orbital, 1)
assert new_electron.repetitions == 0
assert new_electron.interval == 1
assert new_electron.ease == 2.5
def test_sm2_process_review_quality_3(self):
"""Test complete review process with quality 3."""
algorithm = SM2Algorithm()
electron = Electron(ease=2.5, interval=1, repetitions=0)
orbital = Orbital(learning_steps=[1, 10])
new_electron = algorithm.process_review(electron, orbital, 3)
assert new_electron.repetitions == 1
assert new_electron.interval == 1
assert new_electron.ease == pytest.approx(2.54)
def test_sm2_process_review_quality_5(self):
"""Test complete review process with quality 5."""
algorithm = SM2Algorithm()
electron = Electron(ease=2.5, interval=10, repetitions=5)
orbital = Orbital()
new_electron = algorithm.process_review(electron, orbital, 5)
assert new_electron.repetitions == 6
assert new_electron.interval == 25 # 10 * 2.5
assert new_electron.ease == pytest.approx(2.54)
def test_sm2_get_next_review_date(self):
"""Test next review date calculation."""
algorithm = SM2Algorithm()
electron = Electron(interval=5)
# Mock current time
current_time = datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
next_review = algorithm.get_next_review_date(electron, current_time)
expected_date = datetime(2024, 1, 6, 12, 0, 0, tzinfo=timezone.utc)
assert next_review == expected_date
def test_sm2_get_next_review_date_no_interval(self):
"""Test next review date with zero interval."""
algorithm = SM2Algorithm()
electron = Electron(interval=0)
current_time = datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
next_review = algorithm.get_next_review_date(electron, current_time)
assert next_review == current_time
def test_sm2_algorithm_boundary_conditions(self):
"""Test boundary conditions for SM2 algorithm."""
algorithm = SM2Algorithm()
# Test with minimum ease
electron = Electron(ease=1.3)
orbital = Orbital()
new_electron = algorithm.process_review(electron, orbital, 0)
assert new_electron.ease == 1.3 # Should not go below minimum
# Test with maximum repetitions
electron = Electron(repetitions=100)
new_electron = algorithm.process_review(electron, orbital, 4)
assert new_electron.repetitions == 101 # Should continue incrementing
def test_sm2_algorithm_validation(self):
"""Test input validation for SM2 algorithm."""
algorithm = SM2Algorithm()
electron = Electron()
orbital = Orbital()
# Test invalid quality values
with pytest.raises(ValueError):
algorithm.process_review(electron, orbital, -1)
with pytest.raises(ValueError):
algorithm.process_review(electron, orbital, 6)
# Test with None electron
with pytest.raises(TypeError):
algorithm.process_review(None, orbital, 3)
# Test with None orbital
with pytest.raises(TypeError):
algorithm.process_review(electron, None, 3)