Exemple #1
0
class Simulator(object):
    def __init__(self, jobs, num_processors, scheduler):
        self.event_queue = EventQueue()
        self.machine = ValidatingMachine(num_processors=num_processors, event_queue=self.event_queue)
        self.scheduler = scheduler

        self.event_queue.add_handler(JobSubmissionEvent, self.handle_submission_event)
        self.event_queue.add_handler(JobTerminationEvent, self.handle_termination_event)

        for job in jobs:
            self.event_queue.add_event(
                    JobSubmissionEvent(timestamp = job.submit_time, job = job)
                )

    def run(self):
        while not self.event_queue.is_empty:
            self.event_queue.advance()

    def handle_submission_event(self, event):
        assert isinstance(event, JobSubmissionEvent)
        newEvents = self.scheduler.handleSubmissionOfJobEvent(event.job, event.timestamp)
        for event in newEvents:
            self.event_queue.add_event(event)

    def handle_termination_event(self, event):
        assert isinstance(event, JobTerminationEvent)
        newEvents = self.scheduler.handleTerminationOfJobEvent(event.job, event.timestamp)
        for event in newEvents:
            self.event_queue.add_event(event)
            
    def handle_prediction_is_over_event(self, event):
        assert isinstance(event, JobPredictionIsOverEvent)
        newEvents = self.scheduler.handlePredictionIsOverEvent(event.job, event.timestamp)
        for event in newEvents:
            self.event_queue.add_event(event)
class Simulator(object):
    def __init__(self, jobs, num_processors, scheduler):
        self.event_queue = EventQueue()
        self.machine = ValidatingMachine(num_processors=num_processors,
                                         event_queue=self.event_queue)
        self.scheduler = scheduler

        self.event_queue.add_handler(JobSubmissionEvent,
                                     self.handle_submission_event)
        self.event_queue.add_handler(JobTerminationEvent,
                                     self.handle_termination_event)

        for job in jobs:
            self.event_queue.add_event(
                JobSubmissionEvent(timestamp=job.submit_time, job=job))

    def run(self):
        while not self.event_queue.is_empty:
            self.event_queue.advance()

    def handle_submission_event(self, event):
        assert isinstance(event, JobSubmissionEvent)
        newEvents = self.scheduler.handleSubmissionOfJobEvent(
            event.job, event.timestamp)
        for event in newEvents:
            self.event_queue.add_event(event)

    def handle_termination_event(self, event):
        assert isinstance(event, JobTerminationEvent)
        newEvents = self.scheduler.handleTerminationOfJobEvent(
            event.job, event.timestamp)
        for event in newEvents:
            self.event_queue.add_event(event)

    def handle_prediction_is_over_event(self, event):
        assert isinstance(event, JobPredictionIsOverEvent)
        newEvents = self.scheduler.handlePredictionIsOverEvent(
            event.job, event.timestamp)
        for event in newEvents:
            self.event_queue.add_event(event)
class test_EventQueue(TestCase):
    def setUp(self):
        self.queue = EventQueue()
        self.event = prototype.JobEvent(timestamp=0, job=None)
        self.events = [
                prototype.JobEvent(timestamp=i, job=None)
                for i in xrange(10)
            ]

        self.handler = _create_handler()

    def tearDown(self):
        del self.queue, self.event, self.events, self.handler

    def test_len_empty(self):
        self.assertEqual( 0, len(self.queue) )

    def test_len_nonempty(self):
        for event in self.events:
            self.queue.add_event(event)
        self.assertEqual( len(self.events), len(self.queue) )

    def test_add_event_sanity(self):
        self.queue.add_event( self.event )

    def test_add_event_single_event(self):
        self.queue.add_event(self.event)
        self.assertEqual( [self.event], self.queue.sorted_events )

    def test_add_same_event_fails(self):
        self.queue.add_event(self.event)
        self.assertRaises(Exception, self.queue.add_event, self.event)

    def test_add_event_simple(self):
        for event in self.events:
            self.queue.add_event(event)
        self.assertEqual( self.events, list(self.queue.sorted_events) )

    def test_add_event_sorting(self):
        random_events = _gen_random_timestamp_events()
        for event in random_events:
            self.queue.add_event(event)
        self.assertEqual( sorted(random_events), self.queue.sorted_events )

    def test_remove_event_fails_on_empty(self):
        self.assertRaises(Exception, self.queue.remove_event, self.event)

    def test_remove_event_fails_on_missing_event(self):
        event1 = prototype.JobEvent(0, 0)
        event2 = prototype.JobEvent(0, 1)
        assert event1 != event2 # different events
        self.queue.add_event(event1)
        self.assertRaises(Exception, self.queue.remove_event, event2)

    def test_remove_event_succeeds(self):
        self.queue.add_event(self.event)
        self.queue.remove_event(self.event)
        self.failUnless( self.queue.is_empty )

    def test_pop_one_job(self):
        self.queue.add_event( self.event )
        assert self.queue.pop() is self.event

    def test_pop_many_jobs(self):
        for event in self.events:
            self.queue.add_event(event)
        for event in self.events:
            assert self.queue.pop() is event

    def test_pop_empty(self):
        self.assertRaises(AssertionError, self.queue.pop)

    def test_empty_true(self):
        self.failUnless( self.queue.is_empty )

    def test_empty_false(self):
        self.queue.add_event( self.event )
        self.failIf( self.queue.is_empty )

    def test_add_handler_sanity(self):
        self.queue.add_handler(prototype.JobEvent, self.handler)
        self.queue.add_event(self.event)
        self.failIf( self.handler.called )

    def test_get_event_handlers_empty(self):
        self.assertEqual(
            0, len(self.queue._get_event_handlers( prototype.JobEvent ))
        )

    def test_get_event_handlers_nonempty(self):
        self.queue.add_handler(prototype.JobEvent, self.handler)
        self.assertEqual(
            1, len(self.queue._get_event_handlers( prototype.JobEvent ))
        )

    def test_advance_empty_queue(self):
        self.assertRaises(AssertionError, self.queue.advance)

    def test_advance_eats_event(self):
        self._add_event_and_advance(self.event)
        self.failUnless(self.queue.is_empty)

    def test_add_event_earlier_event_after_later_advance(self):
        # after handling an event with a later timestamp, adding an event with
        # an older timestamp should fail
        self._add_event_and_advance(prototype.JobEvent(timestamp=2, job="x"))
        self.assertRaises(Exception, self.queue.add_event, prototype.JobEvent(timestamp=1, job="x"))

    def test_add_event_same_timestamp_after_advance(self):
        # same timestamp should succeed even after an event has been handled
        self._add_event_and_advance(prototype.JobEvent(timestamp=2, job="x"))
        self.queue.add_event(prototype.JobEvent(timestamp=2, job="y"))

    def test_advance_one_handler_handles(self):
        self.queue.add_handler(prototype.JobEvent, self.handler)
        self._add_event_and_advance(self.event)

        self.failUnless( self.handler.called )

    def test_advance_one_handler_doesnt_handle(self):
        self.queue.add_handler(prototype.JobStartEvent, self.handler)
        self._add_event_and_advance(self.event) # JobEvent, different type

        self.failIf( self.handler.called )

    def test_advance_many_handlers(self):
        matching_handlers = [ _create_handler() for i in xrange(5) ]
        nonmatching_handlers = [ _create_handler() for i in xrange(5) ]

        # register handlers that should run
        for handler in matching_handlers:
            self.queue.add_handler(prototype.JobEvent, handler)

        # register handlers that shouldn't run with a different event type
        for handler in nonmatching_handlers:
            self.queue.add_handler(prototype.JobStartEvent, handler)

        self._add_event_and_advance(self.event)

        for handler in matching_handlers:
            self.failUnless( handler.called )

        for handler in nonmatching_handlers:
            self.failIf( handler.called )

    def test_sometimes_relevant_handler(self):
        self.queue.add_handler(prototype.JobEvent, self.handler)
        self._add_event_and_advance(prototype.JobEvent(timestamp=0, job="x"))
        self.failUnless(self.handler.called)
        self.handler.called = False
        self._add_event_and_advance(prototype.JobStartEvent(timestamp=1, job="x"))
        self.failIf(self.handler.called)
        self._add_event_and_advance(prototype.JobEvent(timestamp=2, job="x"))
        self.failUnless(self.handler.called)

    def _add_event_and_advance(self, event):
        self.queue.add_event(event)
        self.queue.advance()
class test_ValidatingMachine(TestCase):
    def setUp(self):
        self.event_queue = EventQueue()
        self.machine = prototype.ValidatingMachine(50, self.event_queue)
        self.unique_numbers = unique_numbers()

    def tearDown(self):
        del self.event_queue, self.machine, self.unique_numbers

    def _unique_job(self, user_estimated_run_time=100, actual_run_time=60, num_required_processors=20):
        return prototype.Job(
                id = self.unique_numbers.next(),
                user_estimated_run_time = user_estimated_run_time,
                actual_run_time = actual_run_time,
                num_required_processors = num_required_processors
            )

    def test_no_jobs_on_init(self):
        self.assertEqual(0, len(self.machine.jobs))

    def test_add_job(self):
        job = self._unique_job()
        self.machine._add_job(job, current_timestamp=0)
        assert job in self.machine.jobs

    def test_add_several_jobs_success(self):
        for i in xrange(5):
            self.machine._add_job( self._unique_job(num_required_processors=5), current_timestamp=0 )

    def test_add_job_too_big(self):
        self.assertRaises(Exception, self.machine._add_job, self._unique_job(num_required_processors=100), current_timestamp=0)

    def test_add_second_job_too_big(self):
        self.machine._add_job( self._unique_job(num_required_processors=40), current_timestamp=0 )
        self.assertRaises(Exception, self.machine._add_job, self._unique_job(num_required_processors=40), current_timestamp=0 )

    def test_free_processors_empty(self):
        self.assertEqual(50, self.machine.free_processors)

    def test_free_processors_nonempty(self):
        for i in xrange(10):
            self.machine._add_job(self._unique_job(num_required_processors=3), current_timestamp=0)
        self.assertEqual(20, self.machine.free_processors)

    def test_free_processors_full(self):
        self.machine._add_job(self._unique_job(num_required_processors=50), current_timestamp=0)
        self.assertEqual(0, self.machine.free_processors)

    def test_busy_processors_empty(self):
        self.assertEqual(0, self.machine.busy_processors)

    def test_busy_processors_nonempty(self):
        for i in xrange(10):
            self.machine._add_job(self._unique_job(num_required_processors=3), current_timestamp=0)
        self.assertEqual(30, self.machine.busy_processors)

    def test_busy_processors_full(self):
        self.machine._add_job(self._unique_job(num_required_processors=50), current_timestamp=0)
        self.assertEqual(50, self.machine.busy_processors)

    def test_add_job_adds_job_end_event(self):
        self.machine._add_job(self._unique_job(), current_timestamp=0)
        self.failIf(self.event_queue.is_empty)

    def test_job_done_removed(self):
        self.machine._add_job(self._unique_job(), current_timestamp=0)
        self.event_queue.advance()
        self.assertEqual(0, self.machine.busy_processors)

    def test_add_job_different_current_timestamp(self):
        self.machine._add_job(current_timestamp=100, job=self._unique_job(actual_run_time=20, num_required_processors=10))
        self.machine._add_job(current_timestamp=120, job=self._unique_job(actual_run_time=40, num_required_processors=5))
        self.event_queue.advance()
        self.assertEqual(5, self.machine.busy_processors)

    def test_add_job_different_current_timestamp2(self):
        self.machine._add_job(current_timestamp=110, job=self._unique_job(actual_run_time=20, num_required_processors=10))
        self.machine._add_job(current_timestamp=100, job=self._unique_job(actual_run_time=40, num_required_processors=5))
        self.event_queue.advance()
        self.assertEqual(5, self.machine.busy_processors)

    def test_start_job_handler(self):
        job = self._unique_job()

        self.event_queue.add_event(
            prototype.JobStartEvent( timestamp=0, job=job )
        )
        self.event_queue.advance()

        assert job in self.machine.jobs
class test_EventQueue(TestCase):
    def setUp(self):
        self.queue = EventQueue()
        self.event = prototype.JobEvent(timestamp=0, job=None)
        self.events = [
            prototype.JobEvent(timestamp=i, job=None) for i in xrange(10)
        ]

        self.handler = _create_handler()

    def tearDown(self):
        del self.queue, self.event, self.events, self.handler

    def test_len_empty(self):
        self.assertEqual(0, len(self.queue))

    def test_len_nonempty(self):
        for event in self.events:
            self.queue.add_event(event)
        self.assertEqual(len(self.events), len(self.queue))

    def test_add_event_sanity(self):
        self.queue.add_event(self.event)

    def test_add_event_single_event(self):
        self.queue.add_event(self.event)
        self.assertEqual([self.event], self.queue.sorted_events)

    def test_add_same_event_fails(self):
        self.queue.add_event(self.event)
        self.assertRaises(Exception, self.queue.add_event, self.event)

    def test_add_event_simple(self):
        for event in self.events:
            self.queue.add_event(event)
        self.assertEqual(self.events, list(self.queue.sorted_events))

    def test_add_event_sorting(self):
        random_events = _gen_random_timestamp_events()
        for event in random_events:
            self.queue.add_event(event)
        self.assertEqual(sorted(random_events), self.queue.sorted_events)

    def test_remove_event_fails_on_empty(self):
        self.assertRaises(Exception, self.queue.remove_event, self.event)

    def test_remove_event_fails_on_missing_event(self):
        event1 = prototype.JobEvent(0, 0)
        event2 = prototype.JobEvent(0, 1)
        assert event1 != event2  # different events
        self.queue.add_event(event1)
        self.assertRaises(Exception, self.queue.remove_event, event2)

    def test_remove_event_succeeds(self):
        self.queue.add_event(self.event)
        self.queue.remove_event(self.event)
        self.failUnless(self.queue.is_empty)

    def test_pop_one_job(self):
        self.queue.add_event(self.event)
        assert self.queue.pop() is self.event

    def test_pop_many_jobs(self):
        for event in self.events:
            self.queue.add_event(event)
        for event in self.events:
            assert self.queue.pop() is event

    def test_pop_empty(self):
        self.assertRaises(AssertionError, self.queue.pop)

    def test_empty_true(self):
        self.failUnless(self.queue.is_empty)

    def test_empty_false(self):
        self.queue.add_event(self.event)
        self.failIf(self.queue.is_empty)

    def test_add_handler_sanity(self):
        self.queue.add_handler(prototype.JobEvent, self.handler)
        self.queue.add_event(self.event)
        self.failIf(self.handler.called)

    def test_get_event_handlers_empty(self):
        self.assertEqual(
            0, len(self.queue._get_event_handlers(prototype.JobEvent)))

    def test_get_event_handlers_nonempty(self):
        self.queue.add_handler(prototype.JobEvent, self.handler)
        self.assertEqual(
            1, len(self.queue._get_event_handlers(prototype.JobEvent)))

    def test_advance_empty_queue(self):
        self.assertRaises(AssertionError, self.queue.advance)

    def test_advance_eats_event(self):
        self._add_event_and_advance(self.event)
        self.failUnless(self.queue.is_empty)

    def test_add_event_earlier_event_after_later_advance(self):
        # after handling an event with a later timestamp, adding an event with
        # an older timestamp should fail
        self._add_event_and_advance(prototype.JobEvent(timestamp=2, job="x"))
        self.assertRaises(Exception, self.queue.add_event,
                          prototype.JobEvent(timestamp=1, job="x"))

    def test_add_event_same_timestamp_after_advance(self):
        # same timestamp should succeed even after an event has been handled
        self._add_event_and_advance(prototype.JobEvent(timestamp=2, job="x"))
        self.queue.add_event(prototype.JobEvent(timestamp=2, job="y"))

    def test_advance_one_handler_handles(self):
        self.queue.add_handler(prototype.JobEvent, self.handler)
        self._add_event_and_advance(self.event)

        self.failUnless(self.handler.called)

    def test_advance_one_handler_doesnt_handle(self):
        self.queue.add_handler(prototype.JobStartEvent, self.handler)
        self._add_event_and_advance(self.event)  # JobEvent, different type

        self.failIf(self.handler.called)

    def test_advance_many_handlers(self):
        matching_handlers = [_create_handler() for i in xrange(5)]
        nonmatching_handlers = [_create_handler() for i in xrange(5)]

        # register handlers that should run
        for handler in matching_handlers:
            self.queue.add_handler(prototype.JobEvent, handler)

        # register handlers that shouldn't run with a different event type
        for handler in nonmatching_handlers:
            self.queue.add_handler(prototype.JobStartEvent, handler)

        self._add_event_and_advance(self.event)

        for handler in matching_handlers:
            self.failUnless(handler.called)

        for handler in nonmatching_handlers:
            self.failIf(handler.called)

    def test_sometimes_relevant_handler(self):
        self.queue.add_handler(prototype.JobEvent, self.handler)
        self._add_event_and_advance(prototype.JobEvent(timestamp=0, job="x"))
        self.failUnless(self.handler.called)
        self.handler.called = False
        self._add_event_and_advance(
            prototype.JobStartEvent(timestamp=1, job="x"))
        self.failIf(self.handler.called)
        self._add_event_and_advance(prototype.JobEvent(timestamp=2, job="x"))
        self.failUnless(self.handler.called)

    def _add_event_and_advance(self, event):
        self.queue.add_event(event)
        self.queue.advance()
class test_ValidatingMachine(TestCase):
    def setUp(self):
        self.event_queue = EventQueue()
        self.machine = prototype.ValidatingMachine(50, self.event_queue)
        self.unique_numbers = unique_numbers()

    def tearDown(self):
        del self.event_queue, self.machine, self.unique_numbers

    def _unique_job(self,
                    user_estimated_run_time=100,
                    actual_run_time=60,
                    num_required_processors=20):
        return prototype.Job(id=self.unique_numbers.next(),
                             user_estimated_run_time=user_estimated_run_time,
                             actual_run_time=actual_run_time,
                             num_required_processors=num_required_processors)

    def test_no_jobs_on_init(self):
        self.assertEqual(0, len(self.machine.jobs))

    def test_add_job(self):
        job = self._unique_job()
        self.machine._add_job(job, current_timestamp=0)
        assert job in self.machine.jobs

    def test_add_several_jobs_success(self):
        for i in xrange(5):
            self.machine._add_job(self._unique_job(num_required_processors=5),
                                  current_timestamp=0)

    def test_add_job_too_big(self):
        self.assertRaises(Exception,
                          self.machine._add_job,
                          self._unique_job(num_required_processors=100),
                          current_timestamp=0)

    def test_add_second_job_too_big(self):
        self.machine._add_job(self._unique_job(num_required_processors=40),
                              current_timestamp=0)
        self.assertRaises(Exception,
                          self.machine._add_job,
                          self._unique_job(num_required_processors=40),
                          current_timestamp=0)

    def test_free_processors_empty(self):
        self.assertEqual(50, self.machine.free_processors)

    def test_free_processors_nonempty(self):
        for i in xrange(10):
            self.machine._add_job(self._unique_job(num_required_processors=3),
                                  current_timestamp=0)
        self.assertEqual(20, self.machine.free_processors)

    def test_free_processors_full(self):
        self.machine._add_job(self._unique_job(num_required_processors=50),
                              current_timestamp=0)
        self.assertEqual(0, self.machine.free_processors)

    def test_busy_processors_empty(self):
        self.assertEqual(0, self.machine.busy_processors)

    def test_busy_processors_nonempty(self):
        for i in xrange(10):
            self.machine._add_job(self._unique_job(num_required_processors=3),
                                  current_timestamp=0)
        self.assertEqual(30, self.machine.busy_processors)

    def test_busy_processors_full(self):
        self.machine._add_job(self._unique_job(num_required_processors=50),
                              current_timestamp=0)
        self.assertEqual(50, self.machine.busy_processors)

    def test_add_job_adds_job_end_event(self):
        self.machine._add_job(self._unique_job(), current_timestamp=0)
        self.failIf(self.event_queue.is_empty)

    def test_job_done_removed(self):
        self.machine._add_job(self._unique_job(), current_timestamp=0)
        self.event_queue.advance()
        self.assertEqual(0, self.machine.busy_processors)

    def test_add_job_different_current_timestamp(self):
        self.machine._add_job(current_timestamp=100,
                              job=self._unique_job(actual_run_time=20,
                                                   num_required_processors=10))
        self.machine._add_job(current_timestamp=120,
                              job=self._unique_job(actual_run_time=40,
                                                   num_required_processors=5))
        self.event_queue.advance()
        self.assertEqual(5, self.machine.busy_processors)

    def test_add_job_different_current_timestamp2(self):
        self.machine._add_job(current_timestamp=110,
                              job=self._unique_job(actual_run_time=20,
                                                   num_required_processors=10))
        self.machine._add_job(current_timestamp=100,
                              job=self._unique_job(actual_run_time=40,
                                                   num_required_processors=5))
        self.event_queue.advance()
        self.assertEqual(5, self.machine.busy_processors)

    def test_start_job_handler(self):
        job = self._unique_job()

        self.event_queue.add_event(
            prototype.JobStartEvent(timestamp=0, job=job))
        self.event_queue.advance()

        assert job in self.machine.jobs