import unittest
import time

# Mock the actual frequency scaling and load calculation functions
class MockFrequencyScaler:
    def __init__(self, initial_frequency):
        self.current_frequency = initial_frequency

    def set_frequency(self, frequency):
        self.current_frequency = frequency

    def get_frequency(self):
        return self.current_frequency

class MockLoadCalculator:
    def __init__(self, initial_load):
        self.current_load = initial_load

    def get_load(self):
        return self.current_load

    def set_load(self, load):
        self.current_load = load

# The actual frequency scaling logic (replace with your actual implementation)
class FrequencyScaler:
    def __init__(self, initial_frequency, load_calculator, frequency_scaler, high_threshold, low_threshold, hysteresis_duration):
        self.load_calculator = load_calculator
        self.frequency_scaler = frequency_scaler
        self.high_threshold = high_threshold
        self.low_threshold = low_threshold
        self.hysteresis_duration = hysteresis_duration
        self.last_high_threshold_cross_time = None
        self.last_low_threshold_cross_time = None
        self.target_frequency = initial_frequency # Initialize target frequency

    def update_frequency(self):
        load = self.load_calculator.get_load()
        current_frequency = self.frequency_scaler.get_frequency()

        if load > self.high_threshold:
            if self.last_high_threshold_cross_time is None:
                self.last_high_threshold_cross_time = time.time()
            elif time.time() - self.last_high_threshold_cross_time > self.hysteresis_duration:
                self.target_frequency *= 1.1  # Scale up by 10% (example)
                self.frequency_scaler.set_frequency(self.target_frequency)
                self.last_high_threshold_cross_time = None  # Reset timer
                self.last_low_threshold_cross_time = None

        elif load < self.low_threshold:
            if self.last_low_threshold_cross_time is None:
                self.last_low_threshold_cross_time = time.time()
            elif time.time() - self.last_low_threshold_cross_time > self.hysteresis_duration:
                self.target_frequency *= 0.9  # Scale down by 10% (example)
                self.frequency_scaler.set_frequency(self.target_frequency)
                self.last_low_threshold_cross_time = None  # Reset timer
                self.last_high_threshold_cross_time = None

        else:
            # Reset timers if load is between thresholds
            self.last_high_threshold_cross_time = None
            self.last_low_threshold_cross_time = None


class TestFrequencyScaling(unittest.TestCase):

    def test_no_scaling_below_threshold(self):
        initial_frequency = 1000
        load_calculator = MockLoadCalculator(50)
        frequency_scaler = MockFrequencyScaler(initial_frequency)
        scaler = FrequencyScaler(initial_frequency, load_calculator, frequency_scaler, high_threshold=80, low_threshold=20, hysteresis_duration=0.5)

        scaler.update_frequency()
        self.assertEqual(frequency_scaler.get_frequency(), initial_frequency)

        time.sleep(0.6) # wait for longer than hysteresis duration
        scaler.update_frequency()
        self.assertEqual(frequency_scaler.get_frequency(), initial_frequency)

    def test_scaling_up_above_threshold(self):
        initial_frequency = 1000
        load_calculator = MockLoadCalculator(90)
        frequency_scaler = MockFrequencyScaler(initial_frequency)
        scaler = FrequencyScaler(initial_frequency, load_calculator, frequency_scaler, high_threshold=80, low_threshold=20, hysteresis_duration=0.5)

        scaler.update_frequency()
        self.assertEqual(frequency_scaler.get_frequency(), initial_frequency)

        time.sleep(0.6) # wait for longer than hysteresis duration
        scaler.update_frequency()
        self.assertAlmostEqual(frequency_scaler.get_frequency(), 1100, delta=0.01)

    def test_scaling_down_below_threshold(self):
        initial_frequency = 1000
        load_calculator = MockLoadCalculator(10)
        frequency_scaler = MockFrequencyScaler(initial_frequency)
        scaler = FrequencyScaler(initial_frequency, load_calculator, frequency_scaler, high_threshold=80, low_threshold=20, hysteresis_duration=0.5)

        scaler.update_frequency()
        self.assertEqual(frequency_scaler.get_frequency(), initial_frequency)

        time.sleep(0.6) # wait for longer than hysteresis duration
        scaler.update_frequency()
        self.assertAlmostEqual(frequency_scaler.get_frequency(), 900, delta=0.01)

    def test_fluctuation_around_threshold(self):
        initial_frequency = 1000
        load_calculator = MockLoadCalculator(75)
        frequency_scaler = MockFrequencyScaler(initial_frequency)
        scaler = FrequencyScaler(initial_frequency, load_calculator, frequency_scaler, high_threshold=80, low_threshold=20, hysteresis_duration=0.5)

        # Load goes above threshold, but not for long enough
        load_calculator.set_load(85)
        scaler.update_frequency()
        time.sleep(0.2)
        load_calculator.set_load(75)
        scaler.update_frequency()
        time.sleep(0.4)
        scaler.update_frequency()
        self.assertEqual(frequency_scaler.get_frequency(), initial_frequency)

        # Load stays above threshold long enough
        load_calculator.set_load(85)
        scaler.update_frequency()
        time.sleep(0.6)
        scaler.update_frequency()
        self.assertAlmostEqual(frequency_scaler.get_frequency(), 1100, delta=0.01)

    def test_different_thresholds_and_durations(self):
        initial_frequency = 1000
        load_calculator = MockLoadCalculator(95)
        frequency_scaler = MockFrequencyScaler(initial_frequency)
        scaler = FrequencyScaler(initial_frequency, load_calculator, frequency_scaler, high_threshold=90, low_threshold=10, hysteresis_duration=1.0)

        scaler.update_frequency()
        time.sleep(0.5)
        scaler.update_frequency()
        self.assertEqual(frequency_scaler.get_frequency(), initial_frequency)

        time.sleep(0.6)
        scaler.update_frequency()
        self.assertAlmostEqual(frequency_scaler.get_frequency(), 1100, delta=0.01)

if __name__ == '__main__':
    unittest.main()