from __future__ import annotations
import unittest
from mercury.config.edcs import ProcessingUnitConfig
from mercury.model.edcs.edc.r_manager.pu import ProcessingUnit
from mercury.msg.packet.app_packet.srv_packet import *

REQ_PERIOD = 10
REQ_DEADLINE = 5
SESS_DEADLINE = 5

pu_config: ProcessingUnitConfig | None = None


class TestProcessingUnit(unittest.TestCase):
    @staticmethod
    def prepare_scenario():
        global pu_config
        if pu_config is None:
            ServicesConfig.add_service('bad', REQ_DEADLINE, 'periodic', {'period': REQ_PERIOD}, 'constant', {}, 'periodic', {'period': REQ_PERIOD})
            ServicesConfig.add_service('req_1', REQ_DEADLINE, 'periodic', {'period': REQ_PERIOD}, 'constant', {}, 'periodic', {'period': REQ_PERIOD})
            ServicesConfig.add_service('req_2', REQ_DEADLINE, 'periodic', {'period': REQ_PERIOD}, 'constant', {}, 'periodic', {'period': REQ_PERIOD})
            ServicesConfig.add_service('sess_1', REQ_DEADLINE, 'periodic', {'period': REQ_PERIOD}, 'constant', {}, 'periodic', {'period': REQ_PERIOD})
            ServicesConfig.add_sess_config('sess_1', SESS_DEADLINE, True)
            ServicesConfig.add_service('sess_2', REQ_DEADLINE, 'periodic', {'period': REQ_PERIOD}, 'constant', {}, 'periodic', {'period': REQ_PERIOD})
            ServicesConfig.add_sess_config('sess_2', SESS_DEADLINE, False)
            pu_config = ProcessingUnitConfig('pu', 10, 5, 'fcfs', {}, 'constant', {'power': 100}, 'constant', {'temperature': 300})
            pu_config.add_service('req_1', 1, proc_t_id='constant', proc_t_config={'proc_t': 2}, power_id='constant', power_config={'power': 200})
            pu_config.add_service('req_2', 2, proc_t_id='round_robin', proc_t_config={'proc_t': 2}, power_id='constant', power_config={'power': 300})
            pu_config.add_service('sess_1', 1, proc_t_id='constant', proc_t_config={'proc_t': 1}, power_id='idle_active', power_config={'idle_power': 400, 'active_power': 450})
            pu_config.add_service('sess_2', 2, proc_t_id='constant', proc_t_config={'proc_t': [1, 2]}, power_id='idle_active', power_config={'idle_power': 500, 'active_power': 550})

    def test_pu_standby(self):
        self.prepare_scenario()
        pu = ProcessingUnit('edc', 'my_pu', pu_config, 298, False)
        self.assertFalse(pu.update)
        self.assertEqual(0, pu.last_t)
        self.assertEqual(pu.PHASE_OFF, pu.phase)
        self.assertIsNone(pu.service_id)
        self.assertEqual(pu.pu_config.t_on, pu.queue_time)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(0, pu.power)
        self.assertFalse(pu.busy)

        pu.update_t(10)
        self.assertFalse(pu.update)
        self.assertEqual(10, pu.last_t)
        self.assertEqual(pu.PHASE_OFF, pu.phase)
        self.assertIsNone(pu.service_id)
        self.assertEqual(pu.pu_config.t_on, pu.queue_time)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(0, pu.power)
        self.assertFalse(pu.busy)

        pu.set_standby(True)
        self.assertTrue(pu.update)
        pu.update_t(10)
        self.assertFalse(pu.update)
        self.assertEqual(10, pu.last_t)
        self.assertEqual(pu.PHASE_TO_ON, pu.phase)
        self.assertIsNone(pu.service_id)
        self.assertEqual(pu.next_t - pu.last_t, pu.queue_time)
        self.assertEqual(20, pu.next_t)
        self.assertEqual(100, pu.power)
        self.assertTrue(pu.busy)

        pu.set_standby(False)
        self.assertTrue(pu.update)
        pu.update_t(15)
        self.assertFalse(pu.update)
        self.assertEqual(15, pu.last_t)
        self.assertEqual(pu.PHASE_TO_ON, pu.phase)
        self.assertIsNone(pu.service_id)
        self.assertEqual(pu.next_t - pu.last_t, pu.queue_time)
        self.assertEqual(20, pu.next_t)
        self.assertEqual(100, pu.power)
        self.assertFalse(pu.busy)

        pu.update_t(21)
        self.assertEqual(21, pu.last_t)
        self.assertEqual(pu.PHASE_TO_OFF, pu.phase)
        self.assertIsNone(pu.service_id)
        self.assertEqual(pu.next_t - pu.last_t + pu.pu_config.t_on, pu.queue_time)
        self.assertEqual(26, pu.next_t)
        self.assertEqual(100, pu.power)
        self.assertFalse(pu.busy)

        pu.update_t(26)
        self.assertEqual(26, pu.last_t)
        self.assertEqual(pu.PHASE_OFF, pu.phase)
        self.assertIsNone(pu.service_id)
        self.assertEqual(pu.pu_config.t_on, pu.queue_time)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(0, pu.power)
        self.assertFalse(pu.busy)

        pu.set_standby(True, True)
        self.assertTrue(pu.update)
        pu.update_t(26)
        self.assertFalse(pu.update)
        self.assertEqual(26, pu.last_t)
        self.assertEqual(pu.PHASE_ON, pu.phase)
        self.assertIsNone(pu.service_id)
        self.assertEqual(0, pu.queue_time)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(100, pu.power)
        self.assertTrue(pu.busy)

        pu.set_standby(False, True)
        self.assertTrue(pu.update)
        pu.update_t(30)
        self.assertFalse(pu.update)
        self.assertEqual(30, pu.last_t)
        self.assertEqual(pu.PHASE_OFF, pu.phase)
        self.assertIsNone(pu.service_id)
        self.assertEqual(pu.pu_config.t_on, pu.queue_time)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(0, pu.power)
        self.assertFalse(pu.busy)

    def test_req(self):
        self.prepare_scenario()
        pu = ProcessingUnit('edc', 'my_pu', pu_config, 298, False)
        self.assertEqual(0, pu.last_t)
        self.assertEqual(pu.PHASE_OFF, pu.phase)
        self.assertIsNone(pu.service_id)
        self.assertEqual(pu.pu_config.t_on, pu.queue_time)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(0, pu.power)
        self.assertFalse(pu.busy)

        req_1 = SrvRequest('bad', 'client_1', 0, 'gateway', 'edc', 0)
        req_1.send(0)
        req_1.receive(1)
        proc_1 = SrvRequestProcess(req_1, req_1.t_rcv[-1])
        res_1 = pu.add_srv_request(proc_1)
        self.assertIsNotNone(res_1)
        self.assertFalse(res_1.response)
        self.assertFalse(pu.update)
        self.assertEqual('Bad PU mapping: it does not support this service', res_1.trace)

        req_1 = SrvRequest('req_1', 'client_1', 0, 'gateway', 'edc', 0)
        req_1.send(0)
        req_1.receive(1)
        proc_1 = SrvRequestProcess(req_1, req_1.t_rcv[-1])
        self.assertIsNone(pu.add_srv_request(proc_1))
        self.assertTrue(pu.update)
        self.assertEqual(1, len(pu.ready_srv_reqs))
        self.assertTrue(('req_1', 'client_1') in pu.ready_srv_reqs)
        self.assertEqual(proc_1, pu.ready_srv_reqs[('req_1', 'client_1')])
        self.assertEqual(0, pu.power)
        self.assertTrue(pu.busy)

        self.assertIsNone(pu.add_srv_request(proc_1))
        self.assertEqual(1, len(pu.ready_srv_reqs))
        self.assertTrue(('req_1', 'client_1') in pu.ready_srv_reqs)
        self.assertEqual(proc_1, pu.ready_srv_reqs[('req_1', 'client_1')])
        self.assertTrue(pu.busy)

        req_2 = SrvRequest('req_1', 'client_1', 1, 'gateway', 'edc', 0)
        req_2.send(0)
        req_2.receive(1)
        proc_2 = SrvRequestProcess(req_2, req_2.t_rcv[-1])
        res_2 = pu.add_srv_request(proc_2)
        self.assertIsNotNone(res_2)
        self.assertEqual(pu.pu_config.t_on + 2, pu.queue_time)
        self.assertEqual('PU is busy with a different request of the same client', res_2.trace)
        self.assertEqual(1, len(pu.ready_srv_reqs))
        self.assertTrue(('req_1', 'client_1') in pu.ready_srv_reqs)
        self.assertEqual(proc_1, pu.ready_srv_reqs[('req_1', 'client_1')])
        self.assertTrue(pu.busy)

        req_2 = SrvRequest('req_1', 'client_2', 1, 'gateway', 'edc', 1)
        req_2.send(1)
        req_2.receive(2)
        proc_2 = SrvRequestProcess(req_2, req_2.t_rcv[-1])
        self.assertIsNone(pu.add_srv_request(proc_2))
        self.assertEqual(pu.pu_config.t_on + 4, pu.queue_time)
        self.assertEqual(2, len(pu.ready_srv_reqs))
        self.assertTrue(('req_1', 'client_1') in pu.ready_srv_reqs)
        self.assertTrue(('req_1', 'client_2') in pu.ready_srv_reqs)
        self.assertEqual(proc_1, pu.ready_srv_reqs[('req_1', 'client_1')])
        self.assertEqual(proc_2, pu.ready_srv_reqs[('req_1', 'client_2')])
        self.assertTrue(pu.busy)

        self.assertTrue(pu.update)
        self.assertIsNone(pu.update_t(2))
        self.assertFalse(pu.update)
        self.assertEqual(ProcessingUnit.PHASE_TO_ON, pu.phase)
        self.assertEqual(2, pu.last_t)
        self.assertEqual(12, pu.next_t)
        self.assertEqual(100, pu.power)
        self.assertEqual(0, len(pu.running_processes))

        for res in pu.update_t(12):
            self.assertFalse(res)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(4, pu.queue_time)
        self.assertEqual(12, pu.last_t)
        self.assertEqual(14, pu.next_t)
        self.assertEqual(1, len(pu.running_processes))
        self.assertEqual(proc_1, pu.running_processes[0])
        self.assertEqual(14, proc_1.t_finish)
        self.assertEqual(200, pu.power)
        self.assertEqual(inf, proc_2.t_finish)

        for res in pu.update_t(13):
            self.assertFalse(res)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(3, pu.queue_time)
        self.assertEqual(13, pu.last_t)
        self.assertEqual(14, pu.next_t)
        self.assertEqual(1, len(pu.running_processes))
        self.assertEqual(proc_1, pu.running_processes[0])
        self.assertEqual(14, proc_1.t_finish)
        self.assertEqual(200, pu.power)
        self.assertEqual(inf, proc_2.t_finish)

        res_open, res_srv, res_close = pu.update_t(14)
        self.assertFalse(res_open)
        self.assertFalse(res_close)
        self.assertEqual(1, len(res_srv))
        self.assertEqual(req_1, res_srv[0].request)
        self.assertTrue(res_srv[0].response)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(2, pu.queue_time)
        self.assertEqual(14, pu.last_t)
        self.assertEqual(16, pu.next_t)
        self.assertFalse(('req_1', 'client_1') in pu.ready_srv_reqs)
        self.assertEqual(1, len(pu.running_processes))
        self.assertEqual(proc_2, pu.running_processes[0])
        self.assertTrue(proc_1.finished)
        self.assertEqual(200, pu.power)
        self.assertEqual(16, proc_2.t_finish)

        open_1 = OpenSessRequest('sess_1', 'client_1', 1, 'gateway', 'edc', 0)
        open_1.send(0)
        open_1.receive(1)
        res_open_1 = pu.add_open_session(open_1)
        self.assertIsNotNone(res_open_1)
        self.assertEqual(2, pu.queue_time)
        self.assertEqual('Bad PU mapping: PU has pending BE requests', res_open_1.trace)
        self.assertEqual(1, len(pu.ready_srv_reqs))
        self.assertTrue(('req_1', 'client_2') in pu.ready_srv_reqs)
        self.assertEqual(proc_2, pu.ready_srv_reqs[('req_1', 'client_2')])
        self.assertEqual(200, pu.power)
        self.assertTrue(pu.busy)

        req_3 = SrvRequest('sess_1', 'client_1', 1, 'gateway', 'edc', 0)
        req_3.send(pu.last_t)
        req_3.receive(pu.last_t)
        proc_3 = SrvRequestProcess(req_3, req_3.t_rcv[-1])
        res_3 = pu.add_srv_request(proc_3)
        self.assertIsNotNone(res_3)
        self.assertEqual(2, pu.queue_time)
        self.assertEqual('Bad PU mapping: required session does not exist', res_3.trace)
        self.assertEqual(1, len(pu.ready_srv_reqs))
        self.assertTrue(('req_1', 'client_2') in pu.ready_srv_reqs)
        self.assertEqual(proc_2, pu.ready_srv_reqs[('req_1', 'client_2')])
        self.assertEqual(200, pu.power)
        self.assertTrue(pu.busy)

        req_3 = SrvRequest('req_2', 'client_2', 1, 'gateway', 'edc', 1)
        req_3.send(1)
        req_3.receive(2)
        proc_3 = SrvRequestProcess(req_3, req_3.t_rcv[-1])
        self.assertFalse(pu.update)
        self.assertIsNone(pu.add_srv_request(proc_3))
        self.assertTrue(pu.update)
        self.assertEqual(4, pu.queue_time)
        self.assertEqual(2, len(pu.ready_srv_reqs))
        self.assertTrue(('req_1', 'client_2') in pu.ready_srv_reqs)
        self.assertTrue(('req_2', 'client_2') in pu.ready_srv_reqs)
        self.assertEqual(proc_2, pu.ready_srv_reqs[('req_1', 'client_2')])
        self.assertEqual(proc_3, pu.ready_srv_reqs[('req_2', 'client_2')])
        self.assertTrue(pu.busy)

        for res in pu.update_t(15):
            self.assertFalse(res)
        self.assertFalse(pu.update)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(3, pu.queue_time)
        self.assertEqual(15, pu.last_t)
        self.assertEqual(16, pu.next_t)
        self.assertEqual(1, len(pu.running_processes))
        self.assertEqual(proc_2, pu.running_processes[0])
        self.assertEqual(16, proc_2.t_finish)
        self.assertEqual(200, pu.power)
        self.assertEqual(inf, proc_3.t_finish)

        res_open, res_srv, res_close = pu.update_t(16)
        self.assertFalse(res_open)
        self.assertFalse(res_close)
        self.assertEqual(1, len(res_srv))
        self.assertEqual(req_2, res_srv[0].request)
        self.assertTrue(res_srv[0].response)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(2, pu.queue_time)
        self.assertEqual(16, pu.last_t)
        self.assertEqual(18, pu.next_t)
        self.assertFalse(('req_1', 'client_2') in pu.ready_srv_reqs)
        self.assertEqual(1, len(pu.running_processes))
        self.assertEqual(proc_3, pu.running_processes[0])
        self.assertTrue(proc_2.finished)
        self.assertEqual(300, pu.power)
        self.assertEqual(18, proc_3.t_finish)

        req_4 = SrvRequest('req_2', 'client_1', 1, 'gateway', 'edc', 1)
        req_4.send(1)
        req_4.receive(2)
        proc_4 = SrvRequestProcess(req_4, req_4.t_rcv[-1])
        self.assertFalse(pu.update)
        self.assertIsNone(pu.add_srv_request(proc_4))
        self.assertTrue(pu.update)
        self.assertEqual(4, pu.queue_time)
        self.assertEqual(2, len(pu.ready_srv_reqs))
        self.assertTrue(('req_2', 'client_2') in pu.ready_srv_reqs)
        self.assertTrue(('req_2', 'client_2') in pu.ready_srv_reqs)
        self.assertEqual(proc_4, pu.ready_srv_reqs[('req_2', 'client_1')])
        self.assertEqual(proc_3, pu.ready_srv_reqs[('req_2', 'client_2')])
        self.assertTrue(pu.busy)

        for res in pu.update_t(17):
            self.assertFalse(res)
        self.assertFalse(pu.update)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(3, pu.queue_time)
        self.assertEqual(17, pu.last_t)
        self.assertEqual(19, pu.next_t)
        self.assertEqual(2, len(pu.running_processes))
        self.assertEqual(proc_3, pu.running_processes[0])
        self.assertEqual(proc_4, pu.running_processes[1])
        self.assertEqual(19, proc_3.t_finish)  # 1 second is now 2 seconds
        self.assertEqual(21, proc_4.t_finish)  # 2 seconds is now 4 seconds
        self.assertEqual(300, pu.power)

        res_open, res_srv, res_close = pu.update_t(19)
        self.assertFalse(res_open)
        self.assertFalse(res_close)
        self.assertEqual(1, len(res_srv))
        self.assertEqual(req_3, res_srv[0].request)
        self.assertTrue(res_srv[0].response)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(1, pu.queue_time)
        self.assertEqual(19, pu.last_t)
        self.assertEqual(20, pu.next_t)  # two seconds are now one second
        self.assertFalse(('req_2', 'client_2') in pu.ready_srv_reqs)
        self.assertEqual(1, len(pu.running_processes))
        self.assertEqual(proc_4, pu.running_processes[0])
        self.assertTrue(proc_3.finished)
        self.assertEqual(300, pu.power)
        self.assertEqual(20, proc_4.t_finish)

        res_open, res_srv, res_close = pu.update_t(20)
        self.assertFalse(res_open)
        self.assertFalse(res_close)
        self.assertEqual(1, len(res_srv))
        self.assertEqual(req_4, res_srv[0].request)
        self.assertTrue(res_srv[0].response)
        self.assertEqual(ProcessingUnit.PHASE_TO_OFF, pu.phase)
        self.assertEqual(pu.pu_config.t_on + pu.pu_config.t_off, pu.queue_time)
        self.assertEqual(20, pu.last_t)
        self.assertEqual(pu.last_t + pu.pu_config.t_off, pu.next_t)
        self.assertFalse(pu.ready_srv_reqs)
        self.assertEqual(0, len(pu.running_processes))
        self.assertTrue(proc_4.finished)
        self.assertEqual(100, pu.power)

        pu.update_t(25)
        self.assertEqual(ProcessingUnit.PHASE_OFF, pu.phase)
        self.assertEqual(pu.pu_config.t_on, pu.queue_time)
        self.assertEqual(25, pu.last_t)
        self.assertEqual(inf, pu.next_t)
        self.assertFalse(pu.ready_srv_reqs)
        self.assertEqual(0, len(pu.running_processes))
        self.assertEqual(0, pu.power)

    def test_sess(self):
        self.prepare_scenario()
        pu = ProcessingUnit('edc', 'my_pu', pu_config, 298, False)
        self.assertFalse(pu.update)
        self.assertEqual(0, pu.last_t)
        self.assertEqual(pu.PHASE_OFF, pu.phase)
        self.assertIsNone(pu.service_id)
        self.assertEqual(pu.pu_config.t_on, pu.queue_time)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(0, pu.power)
        self.assertFalse(pu.busy)
        self.assertIsNone(pu.service_id)
        self.assertIsNone(pu.max_parallel_tasks)
        self.assertIsNone(pu.stream)

        close_req = CloseSessRequest('sess_1', 'client_1', 0, 'gateway', 'edc', 0)
        close_req.send(0)
        close_req.receive(0)
        close_res = pu.add_close_session(close_req)
        self.assertEqual(close_res.response, 0)
        self.assertFalse(pu.update)
        self.assertEqual(close_res.trace, 'Service session does not exist')

        open_1 = OpenSessRequest('sess_1', 'client_1', 0, 'gateway', 'edc', 0)
        open_1.send(0)
        open_1.receive(0)
        self.assertIsNone(pu.add_open_session(open_1))
        self.assertTrue(pu.update)
        self.assertEqual(1, len(pu.sessions))
        self.assertTrue('client_1' in pu.sessions)
        self.assertEqual(0, pu.sessions['client_1'])
        self.assertEqual(1, len(pu.ready_open_sess))
        self.assertTrue('client_1' in pu.ready_open_sess)
        self.assertEqual(open_1, pu.ready_open_sess['client_1'])
        self.assertEqual('sess_1', pu.service_id)
        self.assertEqual(1, pu.max_parallel_tasks)
        self.assertTrue(pu.stream)
        self.assertIsNone(pu.add_open_session(open_1))

        open_2 = OpenSessRequest('sess_2', 'client_1', 0, 'gateway', 'edc', 0)
        open_2.send(0)
        open_2.receive(0)
        open_2_res = pu.add_open_session(open_2)
        self.assertIsNotNone(open_2_res)
        self.assertIsNone(open_2_res.response)
        self.assertEqual(open_2_res.trace, 'Bad PU mapping: PU hosts sessions of different services')

        open_2 = OpenSessRequest('sess_1', 'client_2', 0, 'gateway', 'edc', 0)
        open_2.send(0)
        open_2.receive(0)
        open_2_res = pu.add_open_session(open_2)
        self.assertIsNotNone(open_2_res)
        self.assertIsNone(open_2_res.response)
        self.assertEqual(open_2_res.trace, 'Bad PU mapping: PU is full of service sessions')

        self.assertIsNone(pu.update_t(2))
        self.assertFalse(pu.update)
        self.assertEqual(ProcessingUnit.PHASE_TO_ON, pu.phase)
        self.assertEqual(2, pu.last_t)
        self.assertEqual(12, pu.next_t)
        self.assertEqual(100, pu.power)
        self.assertEqual(0, len(pu.running_processes))

        req_1 = SrvRequest('req_1', 'client_1', 0, 'gateway', 'edc', 2)
        req_1.send(2)
        req_1.receive(2)
        proc_1 = SrvRequestProcess(req_1, req_1.t_rcv[-1])
        res_1 = pu.add_srv_request(proc_1)
        self.assertIsNotNone(res_1)
        self.assertFalse(pu.update)
        self.assertFalse(res_1.response)
        self.assertEqual(res_1.trace, 'Bad PU mapping: PU hosts sessions of different services')

        req_1 = SrvRequest('sess_1', 'client_1', 0, 'gateway', 'edc', 2)
        req_1.send(2)
        req_1.receive(2)
        proc_1 = SrvRequestProcess(req_1, req_1.t_rcv[-1])
        res_1 = pu.add_srv_request(proc_1)
        self.assertIsNotNone(res_1)
        self.assertFalse(pu.update)
        self.assertFalse(res_1.response)
        self.assertEqual(res_1.trace, 'Session is being opened or removed')

        close_req = CloseSessRequest('sess_1', 'client_1', 0, 'gateway', 'edc', 2)
        close_req.send(2)
        close_req.receive(2)
        close_res = pu.add_close_session(close_req)
        self.assertFalse(pu.update)
        self.assertEqual(close_res.response, -1)
        self.assertEqual(close_res.trace, 'Service session is busy and cannot be closed')

        open_res, srv_res, close_res = pu.update_t(12)
        self.assertFalse(srv_res, close_res)
        self.assertEqual(1, len(open_res))
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(12, pu.last_t)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(450, pu.power)
        self.assertEqual(0, len(pu.running_processes))
        self.assertEqual(0, len(pu.ready_open_sess))
        pu_report = pu.pu_report()
        self.assertEqual(1, pu_report.n_sessions)

        req_1 = SrvRequest('sess_1', 'client_1', 0, 'gateway', 'edc', 13)
        req_1.send(13)
        req_1.receive(13)
        proc_1 = SrvRequestProcess(req_1, req_1.t_rcv[-1])
        self.assertIsNone(pu.add_srv_request(proc_1))
        self.assertTrue(pu.update)
        self.assertEqual(1, len(pu.sessions))
        self.assertEqual(1, len(pu.ready_srv_reqs))
        self.assertTrue(('sess_1', 'client_1') in pu.ready_srv_reqs)
        self.assertEqual(proc_1, pu.ready_srv_reqs[('sess_1', 'client_1')])
        self.assertEqual(450, pu.power)

        close_req = CloseSessRequest('sess_1', 'client_1', 0, 'gateway', 'edc', 13)
        close_req.send(13)
        close_req.receive(13)
        close_res = pu.add_close_session(close_req)
        self.assertEqual(close_res.response, -1)
        self.assertTrue(pu.update)
        self.assertEqual(close_res.trace, 'Service session is busy and cannot be closed')

        open_res, srv_res, close_res = pu.update_t(13)
        self.assertFalse(pu.update)
        self.assertFalse(open_res)
        self.assertFalse(srv_res)
        self.assertFalse(close_res)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(13, pu.last_t)
        self.assertEqual(14, pu.next_t)
        self.assertEqual(450, pu.power)
        self.assertEqual(1, len(pu.running_processes))
        self.assertEqual(proc_1, pu.running_processes[0])

        open_res, srv_res, close_res = pu.update_t(14)
        self.assertFalse(pu.update)
        self.assertFalse(open_res)
        self.assertFalse(close_res)
        self.assertEqual(1, len(srv_res))
        self.assertTrue(srv_res[0].response)
        self.assertEqual(req_1, srv_res[0].request)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(14, pu.last_t)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(450, pu.power)
        self.assertEqual(0, len(pu.running_processes))

        close_req = CloseSessRequest('sess_1', 'client_1', 0, 'gateway', 'edc', 14)
        close_req.send(14)
        close_req.receive(14)
        self.assertIsNone(pu.add_close_session(close_req))
        self.assertTrue(pu.update)
        self.assertEqual(1, len(pu.sessions))
        self.assertEqual(1, len(pu.ready_close_sess))
        self.assertTrue('client_1' in pu.ready_close_sess)

        req_1 = SrvRequest('sess_1', 'client_1', 0, 'gateway', 'edc', 14)
        req_1.send(14)
        req_1.receive(14)
        proc_1 = SrvRequestProcess(req_1, req_1.t_rcv[-1])
        res_1 = pu.add_srv_request(proc_1)
        self.assertIsNotNone(res_1)
        self.assertTrue(pu.update)
        self.assertFalse(res_1.response)
        self.assertEqual(res_1.trace, 'Session is being opened or removed')

        open_res, srv_res, close_res = pu.update_t(14)
        self.assertFalse(pu.update)
        self.assertFalse(open_res)
        self.assertFalse(srv_res)
        self.assertEqual(1, len(close_res))
        self.assertEqual(14, close_res[0].response)
        self.assertFalse(pu.sessions)
        self.assertFalse(pu.ready_close_sess)
        self.assertFalse(pu.ready_open_sess)
        self.assertFalse(pu.ready_srv_reqs)
        self.assertIsNone(pu.service_id)
        self.assertEqual(ProcessingUnit.PHASE_TO_OFF, pu.phase)
        self.assertEqual(14, pu.last_t)
        self.assertEqual(19, pu.next_t)
        self.assertEqual(100, pu.power)
        self.assertEqual(pu.pu_config.t_on + pu.pu_config.t_off, pu.queue_time)
        self.assertEqual(0, len(pu.running_processes))

        open_1 = OpenSessRequest('sess_2', 'client_1', 0, 'gateway', 'edc', 14)
        open_1.send(14)
        open_1.receive(14)
        self.assertIsNone(pu.add_open_session(open_1))
        self.assertTrue(pu.update)
        self.assertEqual(1, len(pu.sessions))
        self.assertTrue('client_1' in pu.sessions)
        t_sess_1 = pu.sessions['client_1']
        self.assertEqual(14, pu.sessions['client_1'])
        self.assertEqual(1, len(pu.ready_open_sess))
        self.assertTrue('client_1' in pu.ready_open_sess)
        self.assertEqual(open_1, pu.ready_open_sess['client_1'])
        self.assertEqual('sess_2', pu.service_id)
        self.assertEqual(2, pu.max_parallel_tasks)
        self.assertFalse(pu.stream)
        self.assertIsNone(pu.add_open_session(open_1))

        self.assertIsNone(pu.update_t(20))  # it could be 19
        self.assertFalse(pu.update)
        self.assertEqual('sess_2', pu.service_id)
        self.assertEqual(ProcessingUnit.PHASE_TO_ON, pu.phase)
        self.assertEqual(20, pu.last_t)
        self.assertEqual(30, pu.next_t)
        self.assertEqual(100, pu.power)
        self.assertEqual(pu.pu_config.t_on, pu.queue_time)
        self.assertEqual(0, len(pu.running_processes))

        open_res, srv_res, close_res = pu.update_t(30)
        self.assertFalse(pu.update)
        self.assertEqual(1, len(open_res))
        self.assertFalse(srv_res)
        self.assertFalse(close_res)
        self.assertEqual('sess_2', pu.service_id)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(30, pu.last_t)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(500, pu.power)
        self.assertEqual(0, pu.queue_time)
        self.assertEqual(0, len(pu.running_processes))

        req_1 = SrvRequest('sess_2', 'client_1', 0, 'gateway', 'edc', 30)
        req_1.send(30)
        req_1.receive(30)
        proc_1 = SrvRequestProcess(req_1, req_1.t_rcv[-1])
        self.assertIsNone(pu.add_srv_request(proc_1))
        self.assertTrue(pu.update)
        self.assertEqual(1, len(pu.ready_srv_reqs))

        open_2 = OpenSessRequest('sess_2', 'client_2', 0, 'gateway', 'edc', 30)
        open_2.send(30)
        open_2.receive(30)
        self.assertIsNone(pu.add_open_session(open_2))
        self.assertTrue(pu.update)
        self.assertEqual(2, len(pu.sessions))
        self.assertTrue('client_1' in pu.sessions)
        self.assertTrue('client_2' in pu.sessions)
        self.assertEqual(14, pu.sessions['client_1'])
        self.assertEqual(30, pu.sessions['client_2'])
        t_sess_2 = pu.sessions['client_2']
        self.assertEqual(1, len(pu.ready_open_sess))
        self.assertEqual(1, len(pu.ready_srv_reqs))
        self.assertTrue('client_2' in pu.ready_open_sess)
        self.assertEqual(open_2, pu.ready_open_sess['client_2'])
        self.assertEqual('sess_2', pu.service_id)
        self.assertFalse(pu.stream)

        open_res, srv_res, close_res = pu.update_t(30)
        self.assertFalse(pu.update)
        self.assertEqual(1, len(open_res))
        self.assertFalse(srv_res)
        self.assertFalse(close_res)
        self.assertEqual('sess_2', pu.service_id)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(30, pu.last_t)
        self.assertEqual(31, pu.next_t)
        self.assertEqual(550, pu.power)
        self.assertEqual(inf, pu.queue_time)
        self.assertEqual(1, len(pu.running_processes))
        self.assertEqual(proc_1, pu.running_processes[0])

        open_res, srv_res, close_res = pu.update_t(31)
        self.assertFalse(pu.update)
        self.assertFalse(open_res)
        self.assertFalse(close_res)
        self.assertEqual(1, len(srv_res))
        self.assertEqual(req_1, srv_res[0].request)
        self.assertEqual('sess_2', pu.service_id)
        self.assertEqual(ProcessingUnit.PHASE_ON, pu.phase)
        self.assertEqual(31, pu.last_t)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(500, pu.power)
        self.assertEqual(inf, pu.queue_time)
        self.assertEqual(0, len(pu.running_processes))

        close_req_1 = CloseSessRequest('sess_2', 'client_1', 0, 'gateway', 'edc', 31)
        close_req_1.send(31)
        close_req_1.receive(31)
        self.assertIsNone(pu.add_close_session(close_req_1))
        self.assertTrue(pu.update)
        self.assertEqual(2, len(pu.sessions))
        self.assertEqual(1, len(pu.ready_close_sess))
        self.assertTrue('client_1' in pu.ready_close_sess)

        close_req_2 = CloseSessRequest('sess_2', 'client_2', 0, 'gateway', 'edc', 31)
        close_req_2.send(31)
        close_req_2.receive(31)
        self.assertIsNone(pu.add_close_session(close_req_2))
        self.assertTrue(pu.update)
        self.assertEqual(2, len(pu.sessions))
        self.assertEqual(2, len(pu.ready_close_sess))
        self.assertTrue('client_1' in pu.ready_close_sess)
        self.assertTrue('client_2' in pu.ready_close_sess)

        open_res, srv_res, close_res = pu.update_t(31)
        self.assertFalse(pu.update)
        self.assertFalse(open_res)
        self.assertFalse(srv_res)
        self.assertEqual(2, len(close_res))
        self.assertFalse(open_res)
        self.assertEqual(close_req_1, close_res[0].request)
        self.assertEqual(close_res[0].response, 31 - t_sess_1)
        self.assertEqual(close_req_2, close_res[1].request)
        self.assertEqual(close_res[1].response, 31 - t_sess_2)
        self.assertIsNone(pu.service_id)
        self.assertEqual(ProcessingUnit.PHASE_TO_OFF, pu.phase)
        self.assertEqual(31, pu.last_t)
        self.assertEqual(36, pu.next_t)
        self.assertEqual(100, pu.power)
        self.assertEqual(pu.pu_config.t_on + pu.pu_config.t_off, pu.queue_time)
        self.assertEqual(0, len(pu.running_processes))

        self.assertIsNone(pu.update_t(36))
        self.assertFalse(pu.update)
        self.assertIsNone(pu.service_id)
        self.assertEqual(ProcessingUnit.PHASE_OFF, pu.phase)
        self.assertEqual(36, pu.last_t)
        self.assertEqual(inf, pu.next_t)
        self.assertEqual(0, pu.power)
        self.assertEqual(pu.pu_config.t_on, pu.queue_time)
        self.assertEqual(0, len(pu.running_processes))
        self.assertEqual(pu.pu_config.t_on, pu.queue_time)
        self.assertFalse(pu.ready_open_sess)
        self.assertFalse(pu.ready_close_sess)
        self.assertFalse(pu.ready_srv_reqs)
