206 lines
7.1 KiB
Python
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) |