def wait_for(signal: Signal, times_expected: List[float], delay_between: float, log: List[float] = []): for expected in times_expected: advance(delay_between) signal.wait() assert pytest.approx(expected) == now() log.append(now())
def __init__(self, sim: Simulator, num: str) -> None: self.num = num self._num_standing = 0 self._moment_empty = sim.now() self._time_empty = 0 self._travelers_waiting = Queue() self._traveler_ready = Signal() self._agents_working = Signal() sim.add(self._work_then_break) for name in ["alpha", "beta"]: sim.add(self._agent_accepting_travelers, name)
def test_signal_already_on(): sim = Simulator() signal = Signal().turn_on() log = [] expectation = [1.0] sim.add(wait_for, signal, expectation, 1.0, log) sim.run() assert expectation == log
def test_signal_wait_a_while(): sim = Simulator() signal = Signal().turn_off() log = [] expectation = [3.0, 4.0] sim.add(wait_for, signal, expectation, 1.0, log) sim._schedule(3.0, signal.turn_on) sim.run() assert expectation == log
def test_signal_toggling(): sim = Simulator() signal = Signal().turn_off() log = [] expectation = [3.0, 4.0, 10.0, 13.0, 14.0, 15.0] sim.add(wait_for, signal, expectation, 1.0, log) sim._schedule(3.0, signal.turn_on) sim._schedule(4.5, signal.turn_off) sim._schedule(10.0, signal.turn_on) sim._schedule(10.1, signal.turn_off) sim._schedule(13.0, signal.turn_on) sim.run() assert expectation == log
def test_auto_log_signal(auto_logger): def proc(sig): local.name = "the-process" sig.wait() advance(10) sig.wait() sim = Simulator() signal = Signal(name="the-signal").turn_off() sim.add(proc, signal) sim.run() signal.turn_on() sim.run() check_log(auto_logger, (logging.INFO, -1.0, "", "Signal", "the-signal", "turn-off", {}), (logging.INFO, 0.0, "", "Simulator", sim.name, "add", dict(fn=proc, args=(signal, ), kwargs={})), (logging.INFO, 0.0, "", "Simulator", sim.name, "run", dict(duration=inf)), (logging.INFO, 0.0, "the-process", "Signal", "the-signal", "wait", {}), (logging.INFO, 0.0, "the-process", "Queue", "the-signal-queue", "join", {}), (logging.INFO, 0.0, "the-process", "Process", "the-process", "pause", {}), (logging.INFO, 0.0, "", "Simulator", sim.name, "stop", {}), (logging.INFO, -1.0, "", "Signal", "the-signal", "turn-on", {}), (logging.INFO, -1.0, "", "Queue", "the-signal-queue", "pop", dict(process="the-process")), (logging.INFO, -1.0, "", "Process", "the-process", "resume", {}), (logging.INFO, 0.0, "", "Simulator", sim.name, "run", dict(duration=inf)), (logging.INFO, 0.0, "the-process", "Process", "the-process", "advance", dict(delay=10.0)), (logging.INFO, 10.0, "the-process", "Signal", "the-signal", "wait", {}), (logging.INFO, 10.0, "the-process", "Process", "the-process", "die-finish", {}), (logging.INFO, 10.0, "", "Simulator", sim.name, "stop", {}))
def test_select_one_on(): has_passed = False def selecter(sigs: List[Signal]): nonlocal has_passed select(*sigs) has_passed = True sim = Simulator() signals = [Signal().turn_off() for n in range(5)] sim.add(selecter, signals) sim.run() assert not has_passed signals[3].turn_on() sim.run() assert has_passed
def test_signal_waiter_turning_off(): def waiter_turning_off(signal: Signal, log: List[float]): signal.wait() signal.turn_off() log.append(now()) sim = Simulator() signal = Signal().turn_off() log_time = [] for n in range(5): sim.add(waiter_turning_off, signal, log_time) schedule_signal_on = [4.0, 9.0, 9.1, 200.0, 3000.0] for moment in schedule_signal_on: sim._schedule(moment, signal.turn_on) sim.run() assert schedule_signal_on == pytest.approx(log_time)
def test_log_additional_fields(logger): def ordeal(queue, signal, resource): logger.debug("debug") advance(10) logger.info("info") pause() logger.warning("warning") queue.join() logger.error("error", extra=dict(sim_process="the-process")) signal.wait() logger.critical("critical") resource.take() advance(10) logger.critical("finish", extra=dict(sim_time=1000.0)) resource.release() def do_resume(proc_ordeal): advance(15) proc_ordeal.resume() def do_pop(queue): advance(30) queue.pop() def do_open(signal): advance(50) signal.turn_on() sim = Simulator() queue = Queue() signal = Signal().turn_off() resource = Resource(1) proc_ordeal = sim.add(ordeal, queue, signal, resource) name_ordeal = proc_ordeal.local.name sim.add(do_resume, proc_ordeal) sim.add(do_pop, queue) sim.add(do_open, signal) sim.run() assert [(logging.DEBUG, 0.0, name_ordeal, "debug"), (logging.INFO, 10.0, name_ordeal, "info"), (logging.WARNING, 15.0, name_ordeal, "warning"), (logging.ERROR, 30.0, "the-process", "error"), (logging.CRITICAL, 50.0, name_ordeal, "critical"), (logging.CRITICAL, 1000.0, name_ordeal, "finish") ] == logger.handlers[0].log
def test_select_timeout_bad_parameter(): sigs = [Signal().turn_off() for n in range(5)] log = [] def selecter() -> None: try: select(*sigs, timeout="asdf") log.append(1) except Timeout: log.append(2) def turn_on() -> None: advance(25.0) sigs[0].turn_on() sim = Simulator() sim.add(selecter) sim.add(turn_on) with pytest.raises(ValueError): sim.run()
def test_select_multiple_turn_on(): def selecter(sigs: List[Signal], expected: List[bool]) -> None: signals_on = select(*sigs) for expd, sig in zip(expected, sigs): if expd: assert sig in signals_on else: assert sig not in signals_on def enabler(delay: float, sig: Signal) -> None: advance(delay) sig.turn_on() sim = Simulator() delays = [4.0, 1.0, 3.0, 1.0, 9.0] signals = [Signal().turn_off() for n in range(5)] for delay, signal in zip(delays, signals): sim.add(enabler, delay, signal) sim.add(selecter, signals, [delay < 2.0 for delay in delays]) sim.run()
def test_select_timeout(): sigs = [Signal().turn_off() for n in range(5)] log = [] def selecter() -> None: try: select(*sigs, timeout=10.0) log.append(1) except Timeout: log.append(2) def turn_on() -> None: advance(25.0) sigs[0].turn_on() sim = Simulator() sim.add(selecter) sim.add(turn_on) sim.run() assert log == [2]
def test_signal_timeout(): log = [] signal = Signal().turn_off() def wait_then_balk(name: str, delay_balk: float) -> None: try: signal.wait(delay_balk) log.append((name, "finish")) except Timeout: log.append((name, "balk")) def turn_on(): advance(20.0) signal.turn_on() sim = Simulator() sim.add(wait_then_balk, "a", 10.0) sim.add(wait_then_balk, "b", 30.0) sim.add(turn_on) sim.run() assert log == [("a", "balk"), ("b", "finish")]
advance(next(traveler_preparation)) self._num_standing -= 1 if self._num_standing == 0: debug(f"Belt {self.num} now empty") self._moment_empty = now() def order_traveler(counter: int) -> int: return counter + local.priority * 1000000000 # Simulation setup. sim = Simulator() main_queue = Queue(order_traveler) traveler_enters_main_queue = Signal() traveler_exits_belt = Signal() belts = [LuggageBodyScanner(sim, n + 1) for n in range(NUM_BELTS)] log_time_through_checkpoint = [] log_main_queue_empty = [] traveler_name = 0 # Journey of a traveler (hahaha) through the checkpoint. def traveler(): global traveler_name traveler_name += 1 name = traveler_name local.name = name
def enabler(delay: float, sig: Signal) -> None: advance(delay) sig.turn_on()
class LuggageBodyScanner(object): def __init__(self, sim: Simulator, num: str) -> None: self.num = num self._num_standing = 0 self._moment_empty = sim.now() self._time_empty = 0 self._travelers_waiting = Queue() self._traveler_ready = Signal() self._agents_working = Signal() sim.add(self._work_then_break) for name in ["alpha", "beta"]: sim.add(self._agent_accepting_travelers, name) def _work_then_break(self) -> None: while True: advance(next(agent_time_work)) info(f"Agents on belt {self.num} going on BREAK") self._agents_working.turn_off() advance(next(agent_time_break)) info(f"Agents on belt {self.num} coming back to work") self._agents_working.turn_on() def _agent_accepting_travelers(self, name) -> None: agent = Resource(1) # Models how the agent is busy processing a traveler. while True: # Are we on break yet? That coffee won't drink itself. self._agents_working.wait() info(f"Agent {name}/{self.num} ready") # Is anybody in there? if self._travelers_waiting.is_empty(): debug(f"Agent {name}/{self.num} waiting for travelers") self._traveler_ready.turn_off().wait() continue # Check back if we've gone on break while waiting for somebody. # Accept the next traveler traversing the checkpoint. traveler_next = self._travelers_waiting.peek() debug(f"Agent {name}/{self.num} about to process traveler {traveler_next.local.name}") traveler_next.local.agent = agent traveler_next.local.agent_name = f"{name}/{self.num}" self._travelers_waiting.pop() # Allow the next traveler to "use" this agent, so we may then wait until it's done traversing. advance(0.0) debug(f"Agent {name}/{self.num} doing the processing.") with agent.using(): debug(f"Agent {name}/{self.num} done with the processing.") @property def num_standing(self) -> int: return self._num_standing @property def time_empty(self) -> float: return self._time_empty def traverse(self) -> None: # Invoked by crossing passengers in order to get through their luggage and body scan. me = local.name if self._num_standing == 0: period_empty = now() - self._moment_empty self._time_empty += period_empty debug(f"Belt {self.num} -- Empty period: {period_empty:.1f} -- Time empty: {self.time_empty:.1f}") self._num_standing += 1 info(f"Traveler {me} entering belt {self.num}; now {self.num_standing} travelers here") # Prepare for check. advance(next(traveler_preparation)) # Wait for an agent to beckon. info(f"Traveler {me} (belt {self.num}) prepared and ready for processing") self._traveler_ready.turn_on() self._travelers_waiting.join() with local.agent.using(): # Make agent busy with me. # Administer scan or patdown. agent_name = Process.current().local.agent_name processing_type = next(traveler_processing_type) info(f"Traveler {me} processed by agent {agent_name}: {processing_type}") advance(next(traveler_processing_time[processing_type])) info(f"Traveler {me} (belt {self.num}) buckling back up") advance(next(traveler_preparation)) self._num_standing -= 1 if self._num_standing == 0: debug(f"Belt {self.num} now empty") self._moment_empty = now()
def turn_on(delay: float, signal: Signal) -> None: advance(delay) signal.turn_on()
def waiter_turning_off(signal: Signal, log: List[float]): signal.wait() signal.turn_off() log.append(now())