class TestKernel(unittest.TestCase):
    def setUp(self):
        self.memory = Memory(50)
        self.scheduler = Scheduler()
        self.scheduler.set_as_fifo()
        self.a_kernel = Kernel(None)
        self.pcb_table = PCBTable()
        self.a_kernel.set_pcb_table(self.pcb_table)
        interruption_manager = InterruptionHandler(self.a_kernel.cpu)
        self.a_kernel.set_interruption_handler(interruption_manager)
        load_in_interruption_manager = Handle_Loaders()
        load_in_interruption_manager.load_handlers(self, interruption_manager)

    def write_program(self,program,memory):
        pos = 0
        for instruction in program.obtain_instructions():
            memory.put(pos,instruction)

    def load_a_instruction_in_a_program(self):
        program = Program("SIN-IO")
        instruction = Instruction("Texto")
        program.addInstruction(instruction)
        program.addInstruction(instruction)
        self.write_program(program,self.memory)
        self.setup_load_of_a_program_in_memory(2,program,2)

    def load_a_io_instruction_in_a_program(self):
        program = Program("IO")
        self.a_kernel.set_scheduler(self.scheduler)
        instruction = InstructionIO()
        program.addInstruction(instruction)
        program.addInstruction(instruction)
        self.write_program(program, self.memory)
        self.setup_load_of_a_program_in_memory(2,program,2)

    def setup_load_of_a_program_in_memory(self, amount_instructions, program, pcb_id):
        block_holder = BlockHolder(program)
        block_holder.set_representation([0,1])
        pcb = PCB(amount_instructions, pcb_id, block_holder)
        self.pcb_table.add(pcb)
        self.scheduler.policy.add_pcb(pcb)
        memory_admin = ToyMemoryAdmin(self.memory)
        self.a_kernel.cpu.set_actual_pcb(pcb)
        self.a_kernel.cpu.set_memory_manager(memory_admin)

    def test_given_pcb_when_cpu_complete_instruction_cycle_then_increments_pc(self):
        '''
        Compare the initial state of PCB's PC with final state
        '''
        self.load_a_instruction_in_a_program()
        self.assertEqual(0, self.a_kernel.cpu.actual_pcb.get_pc())
        self.a_kernel.cpu.complete_instruction_cycle()
        self.assertEqual(1, self.a_kernel.cpu.actual_pcb.get_pc())

    def test_given_pcb_when_cpu_complete_instruction_cycle_then_IO_Manager_captures_interruption_and_increments_pc(self):
        self.load_a_io_instruction_in_a_program()
        self.assertEqual(0, self.a_kernel.cpu.actual_pcb.get_pc())
        self.a_kernel.cpu.complete_instruction_cycle()
        self.assertEqual(1, self.a_kernel.cpu.actual_pcb.get_pc())
class TestCPUWithClock(unittest.TestCase):
    def setUp(self):
        self.memory = Memory(50)
        self.a_kernel = Kernel(None)
        self.cpu = Cpu(self.a_kernel)
        self.scheduler = Scheduler()
        self.scheduler.set_as_fifo()
        self.clock = Clock(self.cpu)
        self.a_kernel.clock = self.clock
        self.pcb_table = PCBTable()

    def test_the_clock_makes_a_tick_and_the_cpu_fetch_a_single_instruction_to_decode(self):
        CpuArrangements().load_a_instruction_in_a_program(self.a_kernel,self.scheduler,self.pcb_table,self.cpu,self.memory)
        self.a_kernel.clock.tick()
        self.assertTrue(self.cpu.get_actual_pcb().get_pc() == 1)
class TestSchedulerInteractions(unittest.TestCase):
    '''
    Testing Scheduler and CPU interactions...
    '''

    def setUp(self):
        self.pcb1 = PCB(5, 20, 4)
        self.pcb2 = PCB(4, 21, 3)
        self.a_kernel = Kernel(None)
        self.scheduler = Scheduler(None)
        self.a_kernel.set_scheduler(self.scheduler)
        self.cpu = Cpu(self.a_kernel)

    def test_when_given_scheduler_with_priority_and_cpu_then_scheduler_sends_next_process_to_cpu(self):
        queue = PriorityQueue()
        scheduler_priority = Scheduler(None, ready_queue=queue)
        self.a_kernel.set_scheduler(scheduler_priority)
        scheduler_priority.set_cpu(self.cpu)
        scheduler_priority.set_as_priority()
        self.pcb1.set_priority(1)
        scheduler_priority.push_to_queue(self.pcb1)
        self.pcb2.set_priority(2)
        scheduler_priority.push_to_queue(self.pcb2)
        expected_pcb = scheduler_priority.next_process()
        scheduler_priority.send_next_to_cpu()
        self.assertEqual(self.pcb2, self.cpu.get_actual_pcb())
        
    def test_when_given_scheduler_with_round_robin_and_a_cpu_then_scheduler_sends_next_process_to_cpu(self):
        self.scheduler.set_cpu(self.cpu)
        quantum = 1
        self.scheduler.set_as_round_robin(quantum)
        self.scheduler.push_to_queue(self.pcb1)
        self.scheduler.send_next_to_cpu()
        self.assertEqual(self.pcb1, self.cpu.get_actual_pcb())

    def test_when_given_scheduler_with_fifo_and_a_cpu_then_scheduler_sends_next_process_to_cpu(self):
        self.scheduler.set_cpu(self.cpu)
        self.scheduler.set_as_fifo()
        self.scheduler.push_to_queue(self.pcb1)
        self.scheduler.send_next_to_cpu()
        self.assertEqual(self.pcb1, self.cpu.get_actual_pcb())