# -*- coding: utf-8 -*- import datetime import logging import os import sys import mock import pygame from pie_time import application as app_module from pie_time.application import logging as app_logging from pie_time.application import EVENT_QUIT, EVENT_CLICK_TO_UNBLANK,\ EVENT_CLICK_TO_PREV_CARD, EVENT_CLICK_TO_NEXT_CARD, RET_OK, RET_ERROR,\ PieTime from pie_time.card import AbstractCard class DummyCard(AbstractCard): def tick(self): pass def initialize(self): self._surface = mock.Mock(spec=pygame.surface.Surface) class FakeTimer(object): def __init__(self, step=1, start=None): if start is None: self.n = 0 - step else: self.n = start self._step = step def __call__(self): self.n += self._step return self.n def _now(*args): return datetime.datetime(*args) class Test_Application(object): def _dummy_deck(self): return [mock.Mock(spec=DummyCard)] def _dummy_blanker_schedule(self): return (datetime.timedelta(hours=1), datetime.timedelta(hours=2)) def _mocked_app(self, **kwargs): mocked_app = PieTime(self._dummy_deck(), **kwargs) mocked_app.init_pygame = mock.Mock() mocked_app.get_screen = mock.Mock( side_effect=lambda: mock.Mock(spec=pygame.Surface) ) mocked_app._should_blank = mock.Mock(return_value=False) mocked_app._blank = mock.Mock() mocked_app._unblank = mock.Mock() mocked_app.fill_screen = mock.Mock() mocked_app._clock = mock.Mock(spec=pygame.time.Clock) mocked_app.destroy_cards = mock.Mock() mocked_app.quit_pygame = mock.Mock() mocked_app._start_clock = mock.Mock() mocked_app._setup_output_stream = mock.Mock() mocked_app._setup_logging = mock.Mock() mocked_app._logger = mock.Mock(spec=logging.Logger) def init_cards(*args, **kwargs): PieTime.init_cards(mocked_app) mocked_app.init_cards = mock.Mock(side_effect=init_cards) def transition_cards(*args, **kwargs): mocked_app._current_card_idx = 0 mocked_app._current_card_time = 0 mocked_app._transition_cards = mock.Mock( side_effect=transition_cards ) return mocked_app def _make_app(self, *args, **kwargs): new_app = PieTime(*args, **kwargs) new_app._logger = mock.Mock(spec=logging.Logger) return new_app def test_init_default_settings(self): deck = self._dummy_deck() app = self._make_app(deck) assert app._deck == deck assert app.screen is None assert app.screen_size == (320, 240) assert app.events == [] assert app.log_path is None assert app._fps == 20 assert app._verbose is False assert app._blanker_schedule is None assert app._click_to_unblank_interval is None assert app._click_to_transition is True assert app._clock is None assert app._cards == [] assert app._is_blanked is False assert app._current_card_idx is None assert app._current_card_time is None assert app._should_quit is False assert len(app._internal_events) == 0 assert app._ctu_timer is None assert app._output_stream is None assert app._ctt_region_prev.x == 0 assert app._ctt_region_prev.y == 210 assert app._ctt_region_prev.width == 30 assert app._ctt_region_prev.height == 30 assert app._ctt_region_next.x == 290 assert app._ctt_region_next.y == 210 assert app._ctt_region_next.width == 30 assert app._ctt_region_next.height == 30 def test_init_override_settings(self): deck = self._dummy_deck() blanker_schedule = self._dummy_blanker_schedule() app = self._make_app( deck, screen_size=(640, 480), fps=60, verbose=True, blanker_schedule=blanker_schedule, click_to_unblank_interval=10, click_to_transition=False, log_path='/path/to/log_file.txt' ) assert app.screen_size == (640, 480) assert app.log_path == '/path/to/log_file.txt' assert app._fps == 60 assert app._verbose is True assert app._blanker_schedule == blanker_schedule assert app._click_to_unblank_interval == 10 assert app._click_to_transition is False def test_should_blank_scheduled(self): blanker_schedule = self._dummy_blanker_schedule() app = self._make_app(self._dummy_deck()) assert app._should_blank(now=_now(2014, 10, 15, 9)) is False app = self._make_app( self._dummy_deck(), blanker_schedule=blanker_schedule ) assert app._should_blank(now=_now(2014, 10, 15, 0, 0, 0)) is False assert app._should_blank(now=_now(2014, 10, 15, 1, 0, 0)) is True assert app._should_blank(now=_now(2014, 10, 15, 1, 30, 0)) is True assert app._should_blank(now=_now(2014, 10, 15, 1, 59, 59)) is True assert app._should_blank(now=_now(2014, 10, 15, 2, 0, 0)) is False assert app._should_blank(now=_now(2014, 10, 15, 9, 0, 0)) is False blanker_schedule = ( datetime.timedelta(hours=23), datetime.timedelta(hours=6) ) app = self._make_app( self._dummy_deck(), blanker_schedule=blanker_schedule ) assert app._should_blank(now=_now(2014, 10, 15, 0, 0, 0)) is True assert app._should_blank(now=_now(2014, 10, 15, 5, 59, 59)) is True assert app._should_blank(now=_now(2014, 10, 15, 6, 0, 0)) is False assert app._should_blank(now=_now(2014, 10, 15, 12, 0, 0)) is False assert app._should_blank(now=_now(2014, 10, 15, 22, 59, 59)) is False assert app._should_blank(now=_now(2014, 10, 15, 23, 0, 0)) is True def test_should_blank_ctu(self): ctu = 10 app = self._make_app(self._dummy_deck()) app._has_click_to_unblank_event = mock.Mock(return_value=True) assert app._should_blank() is False assert app._ctu_timer is None app = self._make_app( self._dummy_deck(), blanker_schedule=self._dummy_blanker_schedule(), click_to_unblank_interval=ctu ) app._is_blanked = True app._has_click_to_unblank_event = mock.Mock(return_value=True) assert app._should_blank(now=_now(2014, 10, 15, 1, 0, 0)) is False assert app._ctu_timer == ctu app = self._make_app( self._dummy_deck(), blanker_schedule=self._dummy_blanker_schedule(), click_to_unblank_interval=ctu ) app._is_blanked = True app._ctu_timer = ctu app._has_click_to_unblank_event = mock.Mock(return_value=False) app._clock = mock.Mock(spec=pygame.time.Clock) app._clock.get_time = mock.Mock(return_value=1 * 1000) assert app._should_blank(now=_now(2014, 10, 15, 1, 0, 0)) is False assert app._ctu_timer == ctu - 1 app = self._make_app( self._dummy_deck(), blanker_schedule=self._dummy_blanker_schedule(), click_to_unblank_interval=ctu ) app._is_blanked = True app._ctu_timer = ctu app._has_click_to_unblank_event = mock.Mock(return_value=False) app._clock = mock.Mock(spec=pygame.time.Clock) app._clock.get_time = mock.Mock(return_value=ctu * 1000) assert app._should_blank(now=_now(2014, 10, 15, 1, 0, 0)) is True assert app._ctu_timer is None def test_blank(self): app = self._make_app(self._dummy_deck()) app.screen = mock.Mock(spec=pygame.surface.Surface) app.will_blank = mock.Mock() app._blank() assert app._is_blanked is True assert app.will_blank.called is True app.screen.fill.assert_called_with(PieTime.BLANK_COLOR) def test_unblank(self): app = self._make_app(self._dummy_deck()) app.init_cards() app._is_blanked = True app.will_unblank = mock.Mock() app._unblank() assert app._is_blanked is False assert app.will_unblank.called is True assert app._cards[0][0].show.call_count == 1 def test_transition_cards(self): deck = [ (mock.Mock(spec=DummyCard), 1), (mock.Mock(spec=DummyCard), 1) ] app = self._make_app(self._dummy_deck()) app.init_cards() app._transition_cards() assert app._current_card_idx == 0 assert not app._cards[0][0].hide.called assert app._cards[0][0].show.call_count == 1 app = self._make_app(deck) app._clock = mock.Mock(spec=pygame.time.Clock) timer = FakeTimer(step=2, start=0) app._clock.get_time = mock.Mock( side_effect=lambda: timer() * 1000.0 ) app.init_cards() app._current_card_idx = 0 app._current_card_time = 0 app._transition_cards() assert app._current_card_idx == 1 assert app._cards[0][0].hide.call_count == 1 assert app._cards[1][0].show.call_count == 1 app._transition_cards() assert app._current_card_idx == 0 assert app._cards[0][0].show.call_count == 1 assert app._cards[1][0].hide.call_count == 1 def test_transition_cards_forced(self): deck = [ (mock.Mock(spec=DummyCard), 1), (mock.Mock(spec=DummyCard), 1) ] app = self._make_app(deck) app._clock = mock.Mock(spec=pygame.time.Clock) timer = FakeTimer(step=2, start=0) app._clock.get_time = mock.Mock( side_effect=lambda: timer() ) app.init_cards() app._current_card_idx = 0 app._current_card_time = 0 app._transition_cards(direction=1, force=True) assert app._current_card_idx == 1 app._transition_cards(direction=1, force=True) assert app._current_card_idx == 0 app._transition_cards(direction=-1, force=True) assert app._current_card_idx == 1 app._transition_cards(direction=-1, force=True) assert app._current_card_idx == 0 def test_get_events_quit_pygame(self): new_event_get = mock.Mock(return_value=[ pygame.event.Event(pygame.QUIT) ]) app = self._make_app(self._dummy_deck()) with mock.patch.object(app_module.pygame.event, 'get', new=new_event_get): app._get_events() assert app._internal_events == set([EVENT_QUIT]) def test_get_events_quit_key(self): new_event_get = mock.Mock(return_value=[ pygame.event.Event(pygame.KEYDOWN, key=PieTime.KEY_QUIT) ]) app = self._make_app(self._dummy_deck()) with mock.patch.object(app_module.pygame.event, 'get', new=new_event_get): app._get_events() assert app._internal_events == set([EVENT_QUIT]) def test_get_events_click_to_unblank(self): new_event_get = mock.Mock(return_value=[ pygame.event.Event(pygame.MOUSEBUTTONDOWN, pos=(160, 120)) ]) app = self._make_app(self._dummy_deck(), click_to_unblank_interval=10) with mock.patch.object(app_module.pygame.event, 'get', new=new_event_get): app._is_blanked = False app._get_events() assert app._internal_events == set() app._is_blanked = True app._get_events() assert app._internal_events == set([EVENT_CLICK_TO_UNBLANK]) def test_get_events_click_to_prev_card(self): new_event_get = mock.Mock(return_value=[ pygame.event.Event(pygame.MOUSEBUTTONDOWN, pos=(10, 230)) ]) app = self._make_app(self._dummy_deck(), click_to_transition=True) with mock.patch.object(app_module.pygame.event, 'get', new=new_event_get): app._is_blanked = False app._get_events() assert app._internal_events == set([EVENT_CLICK_TO_PREV_CARD]) app._is_blanked = True app._get_events() assert EVENT_CLICK_TO_PREV_CARD not in app._internal_events app._click_to_transition = False app._is_blanked = False app._get_events() assert EVENT_CLICK_TO_PREV_CARD not in app._internal_events def test_get_events_click_to_next_card(self): new_event_get = mock.Mock(return_value=[ pygame.event.Event(pygame.MOUSEBUTTONDOWN, pos=(310, 230)) ]) app = self._make_app(self._dummy_deck(), click_to_transition=True) with mock.patch.object(app_module.pygame.event, 'get', new=new_event_get): app._is_blanked = False app._get_events() assert app._internal_events == set([EVENT_CLICK_TO_NEXT_CARD]) app._is_blanked = True app._get_events() assert EVENT_CLICK_TO_NEXT_CARD not in app._internal_events app._click_to_transition = False app._is_blanked = False app._get_events() assert EVENT_CLICK_TO_NEXT_CARD not in app._internal_events def test_get_events_other_events(self): events = [ pygame.event.Event(pygame.MOUSEBUTTONDOWN, pos=(160, 120)), pygame.event.Event(pygame.KEYDOWN, key=pygame.K_RETURN) ] new_event_get = mock.Mock(return_value=events) app = self._make_app(self._dummy_deck(), click_to_transition=True) with mock.patch.object(app_module.pygame.event, 'get', new=new_event_get): app._is_blanked = False app._get_events() assert app._internal_events == set() assert app.events == events def test_has_quit_event(self): app = self._make_app(self._dummy_deck()) assert app._has_quit_event() is False app._internal_events.add(EVENT_QUIT) assert app._has_quit_event() is True def test_has_click_to_unblank_event(self): app = self._make_app(self._dummy_deck(), click_to_unblank_interval=10) assert app._has_click_to_unblank_event() is False app._internal_events.add(EVENT_CLICK_TO_UNBLANK) assert app._has_click_to_unblank_event() is True def test_start_clock(self): app = self._make_app(self._dummy_deck()) fake_clock = mock.Mock(spec=pygame.time.Clock) with mock.patch.object(app_module.pygame.time, 'Clock', return_value=fake_clock): app._start_clock() assert app_module.pygame.time.Clock.called is True assert app._clock == fake_clock def test_setup_output_stream_no_log_path(self): deck = self._dummy_deck() with mock.patch.object(PieTime, '_STREAM_FACTORY'): with mock.patch.object(PieTime, '_setup_logging'): app = self._make_app(deck, log_path=None) app._setup_output_stream() assert app._output_stream == PieTime._DEFAULT_OUTPUT_STREAM assert PieTime._STREAM_FACTORY.called is False def test_setup_output_stream_with_log_path(self): deck = self._dummy_deck() fake_file = mock.Mock(spec=file) with mock.patch.object(PieTime, '_STREAM_FACTORY', new=fake_file): with mock.patch.object(PieTime, '_setup_logging'): app = self._make_app(deck, log_path='/path/to/log_file.txt') app._setup_output_stream() PieTime._STREAM_FACTORY.assert_called_with(app.log_path, 'a') assert app._output_stream != fake_file def test_setup_logging_silent(self): deck = self._dummy_deck() fake_logger = mock.Mock(spec=logging.Logger) fake_requests_logger = mock.Mock(spec=logging.Logger) fake_handler = mock.Mock(spec=logging.StreamHandler) fake_formatter = mock.Mock(spec=logging.Formatter) fake_requests_logger.handlers = ['spam', 'eggs'] fake_requests_logger.removeHandler = mock.Mock() def fake_getLogger(name): if name == 'PieTime': return fake_logger elif name == 'requests': return fake_requests_logger else: return None with mock.patch.object(app_logging, 'getLogger', side_effect=fake_getLogger): with mock.patch.object(app_logging, 'StreamHandler', return_value=fake_handler): with mock.patch.object(app_logging, 'Formatter', return_value=fake_formatter): app = self._make_app(deck, verbose=False) app._output_stream = 'spam' app._setup_logging() fake_logger.setLevel.assert_called_with(logging.INFO) fake_requests_logger.setLevel.assert_called_with( logging.WARNING ) app_logging.StreamHandler.assert_called_with( app._output_stream ) assert app_logging.Formatter.called is True fake_handler.setFormatter.assert_called_with( fake_formatter ) fake_logger.addHandler.assert_called_with(fake_handler) assert fake_requests_logger.removeHandler.call_count == 2 fake_requests_logger.removeHandler.assert_any_call('spam') fake_requests_logger.removeHandler.assert_any_call('eggs') fake_requests_logger.addHandler.assert_called_with( fake_handler ) def test_setup_logging_verbose(self): deck = self._dummy_deck() fake_logger = mock.Mock(spec=logging.Logger) fake_requests_logger = mock.Mock(spec=logging.Logger) fake_requests_logger.handlers = [] def fake_getLogger(name): if name == 'PieTime': return fake_logger elif name == 'requests': return fake_requests_logger else: return None with mock.patch.object(app_logging, 'getLogger', side_effect=fake_getLogger): with mock.patch.object(app_logging, 'StreamHandler'): with mock.patch.object(app_logging, 'Formatter'): app = self._make_app(deck, verbose=True) app._output_stream = 'spam' app._setup_logging() fake_logger.setLevel.assert_called_with(logging.DEBUG) assert fake_requests_logger.setLevel.called is False def test_logger(self): app = self._make_app(self._dummy_deck()) assert app.logger is not None def test_init_pygame(self): app = self._make_app(self._dummy_deck()) with mock.patch.object(app_module.pygame, 'init'): with mock.patch.object(app_module.pygame.mouse, 'set_visible'): app.init_pygame() assert app_module.pygame.init.called app_module.pygame.mouse.set_visible.assert_called_with(False) assert app._clock is None def test_quit_pygame(self): app = self._make_app(self._dummy_deck()) with mock.patch.object(app_module.pygame, 'quit'): app.quit_pygame() assert app_module.pygame.quit.called assert app._clock is None def test_init_cards(self): deck = [ mock.Mock(spec=DummyCard), (mock.Mock(spec=DummyCard), 10), (mock.Mock(spec=DummyCard), 20, {'spam': 'eggs'}) ] app = self._make_app(deck) app.init_cards() assert len(app._cards) == 3 app._cards[0][0].set_app.assert_called_with(app) app._cards[0][0].set_settings.assert_called_with({}) assert app._cards[0][0].initialize.called assert not app._cards[0][0].show.called assert app._cards[0][1] == PieTime.CARD_INTERVAL app._cards[1][0].set_app.assert_called_with(app) app._cards[1][0].set_settings.assert_called_with({}) assert app._cards[1][0].initialize.called assert not app._cards[1][0].show.called assert app._cards[1][1] == 10 app._cards[2][0].set_app.assert_called_with(app) app._cards[2][0].set_settings.assert_called_with({'spam': 'eggs'}) assert app._cards[2][0].initialize.called assert not app._cards[2][0].show.called assert app._cards[2][1] == 20 assert app._current_card_idx is None assert app._current_card_time is None def test_destroy_cards(self): with mock.patch.object(DummyCard, 'quit'): app = self._make_app([DummyCard]) app.init_cards() app.destroy_cards() assert len(app._cards) == 0 assert DummyCard.quit.called def test_get_screen(self): with mock.patch.object(app_module.pygame.display, 'set_mode', return_value='spam'): app = self._make_app(self._dummy_deck()) screen = app.get_screen() assert screen == 'spam' app_module.pygame.display.set_mode.\ assert_called_with(app.screen_size) def test_fill_screen(self): app = self._make_app(self._dummy_deck()) app.screen = mock.Mock(pygame.Surface) app.fill_screen() app.screen.fill.assert_called_with(PieTime.BACKGROUND_COLOR) def test_run(self): app = self._mocked_app() def new_start_clock(*args, **kwargs): app._clock.tick = mock.Mock(side_effect=lambda x: app.quit()) app._start_clock = mock.Mock(side_effect=new_start_clock) new_event_get = mock.Mock(return_value=[]) with mock.patch.object(app_module.sys, 'exit'): with mock.patch.object(app_module.pygame.event, 'get', new=new_event_get): with mock.patch.object(app_module.pygame.display, 'flip'): app.run() assert app._setup_output_stream.called is True assert app._setup_logging.called is True assert app.init_pygame.called assert app.get_screen.called assert app.screen is not None assert app.init_cards.called assert app._start_clock.called assert app_module.pygame.event.get.called assert app._should_blank.called assert app._unblank.called assert not app._blank.called assert app._transition_cards.called assert app._cards[0][0].tick.called assert app.fill_screen.called app.screen.blit.assert_called_with( app._cards[0][0].surface, (0, 0, app._cards[0][0].width, app._cards[0][0].height) ) assert pygame.display.flip.called app._clock.tick.assert_called_with(app._fps) assert app.destroy_cards.called assert app.quit_pygame.called app_module.sys.exit.assert_called_with(RET_OK) def test_run_handling_exception(self): app = self._mocked_app() app._clock.tick = mock.Mock(side_effect=RuntimeError('spam')) new_event_get = mock.Mock(return_value=[]) with mock.patch.object(app_module.sys, 'exit'): with mock.patch.object(app_module.pygame.event, 'get', new=new_event_get): with mock.patch.object(app_module.pygame.display, 'flip'): app.run() sys.exit.assert_called_with(RET_ERROR) def test_run_handling_quit_event(self): app = self._mocked_app() app._get_events = mock.Mock() app._has_quit_event = mock.Mock(return_value=True) with mock.patch.object(app_module.sys, 'exit'): with mock.patch.object(app_module.pygame.display, 'flip'): app.run() sys.exit.assert_called_with(RET_OK) def test_run_blanking(self): app = self._mocked_app() timer = FakeTimer() def should_blank(*args, **kwargs): return (timer.n % 2 == 1) app._should_blank = mock.Mock(side_effect=should_blank) def clock_tick(*args, **kwargs): timer() if timer.n == 3: app.quit() app._clock.tick = mock.Mock(side_effect=clock_tick) new_event_get = mock.Mock(return_value=[]) with mock.patch.object(app_module.sys, 'exit'): with mock.patch.object(app_module.pygame.event, 'get', new=new_event_get): with mock.patch.object(app_module.pygame.display, 'flip'): app.run() assert app._unblank.call_count == 2 assert app._blank.call_count == 2 def test_run_handling_click_to_prev_event(self): app = self._mocked_app() app._internal_events = set([EVENT_CLICK_TO_PREV_CARD]) def clock_tick(*args, **kwargs): app.quit() app._clock.tick = mock.Mock(side_effect=clock_tick) app._get_events = mock.Mock() with mock.patch.object(app_module.sys, 'exit'): with mock.patch.object(app_module.pygame.display, 'flip'): app.run() app._transition_cards.assert_called_with( direction=-1, force=True ) def test_run_handling_click_to_next_event(self): app = self._mocked_app() app._internal_events = set([EVENT_CLICK_TO_PREV_CARD]) def clock_tick(*args, **kwargs): app.quit() app._clock.tick = mock.Mock(side_effect=clock_tick) app._get_events = mock.Mock() with mock.patch.object(app_module.sys, 'exit'): with mock.patch.object(app_module.pygame.display, 'flip'): app.run() app._transition_cards.assert_called_with( direction=-1, force=True ) def test_quit(self): app = self._mocked_app() app.quit() assert app._should_quit is True