# -*- coding: utf-8 -*- import threading import mock import pygame import requests from pie_time.application import PieTime from pie_time.cards import weather as card_module class Test_WeatherCard(object): def _dummy_card(self, city='Wroclaw,PL', api_key='API key', **settings): app = PieTime(None, screen_size=(320, 240)) app._logger = mock.Mock() app.path_for_resource = mock.Mock( side_effect=lambda resource: resource ) card = card_module.WeatherCard() card.set_app(app) if city: settings['city'] = city if api_key: settings['api_key'] = api_key card.set_settings(settings) return card def _ok_payload(self): return { "base": "cmc stations", "clouds": { "all": 20 }, "cod": 200, "coord": { "lat": 51.1, "lon": 17.03 }, "dt": 1413815400, "id": 3081368, "main": { "humidity": 59, "pressure": 1013, "temp": 17, "temp_max": 17, "temp_min": 17 }, "name": "Wroclaw", "sys": { "country": "PL", "id": 5375, "message": 0.1316, "sunrise": 1413782666, "sunset": 1413820124, "type": 1 }, "weather": [ { "description": "few clouds", "icon": "02d", "id": 801, "main": "Clouds" } ], "wind": { "deg": 280, "speed": 6.2 } } def test_initialize_no_city(self): card = self._dummy_card(city=None) try: card.initialize() except Exception as exc: assert isinstance(exc, AssertionError) assert exc.args[0] == 'Configuration error: missing city' else: assert False, 'Nothing was raised :(' def test_initialize_no_api_key(self): card = self._dummy_card(api_key=None) try: card.initialize() except Exception as exc: assert isinstance(exc, AssertionError) assert exc.args[0] == 'Configuration error: missing API key' else: assert False, 'Nothing was raised :(' def test_initialize(self): with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card = self._dummy_card() card._refresh_conditions = mock.Mock() card.initialize() assert card._text_font is not None assert card._temp_font is not None assert card._icon_font is not None assert card._timer is None assert card._current_conditions is None assert card._refresh_conditions.called def test_refresh_conditions_requests_error(self): fake_timer = mock.Mock(spec=threading.Timer) fake_timer.start = mock.Mock() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): with mock.patch.object(card_module.requests, 'get', side_effect=RuntimeError('TODO')): with mock.patch.object(card_module, 'Timer', return_value=fake_timer): card = self._dummy_card() card.initialize(refresh=False) card._refresh_conditions() assert card_module.requests.get.called assert card._current_conditions is None assert card._timer is not None card_module.Timer.assert_called_with( card.REFRESH_INTERVAL, card._refresh_conditions ) assert card._timer.start.called def test_refresh_conditions_bad_status_code(self): fake_response = mock.Mock(spec=requests.Response) fake_response.status_code = 404 fake_timer = mock.Mock(spec=threading.Timer) fake_timer.start = mock.Mock() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): with mock.patch.object(card_module.requests, 'get', return_value=fake_response): with mock.patch.object(card_module, 'Timer', return_value=fake_timer): card = self._dummy_card() card.initialize(refresh=False) card._refresh_conditions() assert card_module.requests.get.called assert card._current_conditions is None assert card._timer is not None card_module.Timer.assert_called_with( card.REFRESH_INTERVAL, card._refresh_conditions ) assert card._timer.start.called def test_refresh_conditions_missing_weather_object(self): payload = self._ok_payload() payload['weather'] = [] fake_response = mock.Mock(spec=requests.Response) fake_response.json = mock.Mock(return_value=payload) fake_response.status_code = 200 fake_timer = mock.Mock(spec=threading.Timer) fake_timer.start = mock.Mock() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): with mock.patch.object(card_module.requests, 'get', return_value=fake_response): with mock.patch.object(card_module, 'Timer', return_value=fake_timer): card = self._dummy_card() card.initialize(refresh=False) card._refresh_conditions() assert card_module.requests.get.called assert card._current_conditions is None assert card._timer is not None card_module.Timer.assert_called_with( card.REFRESH_INTERVAL, card._refresh_conditions ) assert card._timer.start.called def test_refresh_conditions_missing_conditions(self): payload = self._ok_payload() payload['weather'][0].pop('main') fake_response = mock.Mock(spec=requests.Response) fake_response.json = mock.Mock(return_value=payload) fake_response.status_code = 200 fake_timer = mock.Mock(spec=threading.Timer) fake_timer.start = mock.Mock() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): with mock.patch.object(card_module.requests, 'get', return_value=fake_response): with mock.patch.object(card_module, 'Timer', return_value=fake_timer): card = self._dummy_card() card.initialize(refresh=False) card._refresh_conditions() assert card_module.requests.get.called assert card._current_conditions is None assert card._timer is not None card_module.Timer.assert_called_with( card.REFRESH_INTERVAL, card._refresh_conditions ) assert card._timer.start.called def test_refresh_conditions_missing_temp(self): payload = self._ok_payload() payload['main'].pop('temp') fake_response = mock.Mock(spec=requests.Response) fake_response.json = mock.Mock(return_value=payload) fake_response.status_code = 200 fake_timer = mock.Mock(spec=threading.Timer) fake_timer.start = mock.Mock() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): with mock.patch.object(card_module.requests, 'get', return_value=fake_response): with mock.patch.object(card_module, 'Timer', return_value=fake_timer): card = self._dummy_card() card.initialize(refresh=False) card._refresh_conditions() assert card_module.requests.get.called assert card._current_conditions is None assert card._timer is not None card_module.Timer.assert_called_with( card.REFRESH_INTERVAL, card._refresh_conditions ) assert card._timer.start.called def test_refresh_conditions(self): payload = self._ok_payload() fake_response = mock.Mock(spec=requests.Response) fake_response.json = mock.Mock(return_value=payload) fake_response.status_code = 200 fake_timer = mock.Mock(spec=threading.Timer) fake_timer.start = mock.Mock() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): with mock.patch.object(card_module.requests, 'get', return_value=fake_response): with mock.patch.object(card_module, 'Timer', return_value=fake_timer): card = self._dummy_card() card.initialize(refresh=False) card._refresh_conditions() assert card_module.requests.get.called assert card._current_conditions == { 'conditions': payload['weather'][0]['main'], 'icon': payload['weather'][0]['icon'], 'temperature': payload['main']['temp'], } assert card._timer is not None card_module.Timer.assert_called_with( card.REFRESH_INTERVAL, card._refresh_conditions ) assert card._timer.start.called def test_refresh_conditions_no_icon(self): payload = self._ok_payload() payload['weather'][0].pop('icon') fake_response = mock.Mock(spec=requests.Response) fake_response.json = mock.Mock(return_value=payload) fake_response.status_code = 200 fake_timer = mock.Mock(spec=threading.Timer) fake_timer.start = mock.Mock() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): with mock.patch.object(card_module.requests, 'get', return_value=fake_response): with mock.patch.object(card_module, 'Timer', return_value=fake_timer): card = self._dummy_card() card.initialize(refresh=False) card._refresh_conditions() assert card_module.requests.get.called assert card._current_conditions == { 'conditions': payload['weather'][0]['main'], 'icon': None, 'temperature': payload['main']['temp'], } assert card._timer is not None card_module.Timer.assert_called_with( card.REFRESH_INTERVAL, card._refresh_conditions ) assert card._timer.start.called def test_render_city(self): card = self._dummy_card() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) card._text_font.render = mock.Mock( return_value=mock.Mock(spec=pygame.surface.Surface) ) surface = card._render_city() card._text_font.render.assert_called_with( card._settings['city'], True, card.WHITE ) assert isinstance(surface, pygame.surface.Surface) def test_render_city_override_color(self): card = self._dummy_card(city_color=(255, 0, 0)) with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) card._text_font.render = mock.Mock( return_value=mock.Mock(spec=pygame.surface.Surface) ) surface = card._render_city() card._text_font.render.assert_called_with( card._settings['city'], True, (255, 0, 0) ) assert isinstance(surface, pygame.surface.Surface) def test_render_conditions(self): card = self._dummy_card() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) card._current_conditions = { 'conditions': 'clouds' } card._text_font.render = mock.Mock( return_value=mock.Mock(spec=pygame.surface.Surface) ) surface = card._render_conditions() card._text_font.render.assert_called_with( 'Clouds', True, card.WHITE ) assert isinstance(surface, pygame.surface.Surface) def test_render_conditions_override_color(self): card = self._dummy_card(conditions_color=(255, 0, 0)) with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) card._current_conditions = { 'conditions': 'clouds' } card._text_font.render = mock.Mock( return_value=mock.Mock(spec=pygame.surface.Surface) ) surface = card._render_conditions() card._text_font.render.assert_called_with( 'Clouds', True, (255, 0, 0) ) assert isinstance(surface, pygame.surface.Surface) def test_render_icon(self): card = self._dummy_card() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) icon_code = '01d' card._current_conditions = {'icon': icon_code} card._text_font.render = mock.Mock( return_value=mock.Mock(spec=pygame.surface.Surface) ) surface = card._render_icon() card._icon_font.render.assert_called_with( card.WEATHER_CODE_TO_ICON[icon_code], True, card.WHITE ) assert isinstance(surface, pygame.surface.Surface) def test_render_icon_override_color(self): card = self._dummy_card(icon_color=(255, 0, 0)) with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) icon_code = '01d' card._current_conditions = {'icon': icon_code} card._text_font.render = mock.Mock( return_value=mock.Mock(spec=pygame.surface.Surface) ) surface = card._render_icon() card._icon_font.render.assert_called_with( card.WEATHER_CODE_TO_ICON[icon_code], True, (255, 0, 0) ) assert isinstance(surface, pygame.surface.Surface) def test_render_icon_no_icon(self): card = self._dummy_card() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) icon_code = None card._current_conditions = {'icon': icon_code} card._text_font.render = mock.Mock( return_value=mock.Mock(spec=pygame.surface.Surface) ) surface = card._render_icon() assert card._icon_font.render.called is False assert surface is None def test_render_icon_unknown_icon(self): card = self._dummy_card() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) icon_code = 'spam' card._current_conditions = {'icon': icon_code} card._text_font.render = mock.Mock( return_value=mock.Mock(spec=pygame.surface.Surface) ) surface = card._render_icon() assert card._icon_font.render.called is False assert surface is None def test_render_temperature(self): card = self._dummy_card() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) card._current_conditions = { 'temperature': 17 } card._text_font.render = mock.Mock( return_value=mock.Mock(spec=pygame.surface.Surface) ) surface = card._render_temperature() card._text_font.render.assert_called_with( u'17°', True, card.WHITE ) assert isinstance(surface, pygame.surface.Surface) def test_render_temperature_override_color(self): card = self._dummy_card(temperature_color=(255, 0, 0)) with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) card._current_conditions = { 'temperature': 17 } card._text_font.render = mock.Mock( return_value=mock.Mock(spec=pygame.surface.Surface) ) surface = card._render_temperature() card._text_font.render.assert_called_with( u'17°', True, (255, 0, 0) ) assert isinstance(surface, pygame.surface.Surface) def test_quit(self): card = self._dummy_card() card._timer = None card.quit() card = self._dummy_card() card._timer = mock.Mock(spec=threading.Timer) card._timer.cancel = mock.Mock() card.quit() assert card._timer.cancel.called def test_tick_redraw(self): card = self._dummy_card() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) card._current_conditions = { 'conditions': 'Clouds', 'icon': '01d', 'temperature': 17 } card._surface = mock.Mock(spec=pygame.surface.Surface) city_surface = mock.Mock(spec=pygame.surface.Surface) city_surface.get_size = mock.Mock(return_value=(120, 40)) card._render_city = mock.Mock(return_value=city_surface) conditions_surface = mock.Mock(spec=pygame.surface.Surface) conditions_surface.get_size = mock.Mock(return_value=(200, 40)) card._render_conditions = mock.Mock( return_value=conditions_surface ) icon_surface = mock.Mock(spec=pygame.surface.Surface) icon_surface.get_size = mock.Mock(return_value=(128, 128)) card._render_icon = mock.Mock(return_value=icon_surface) temperature_surface = mock.Mock(spec=pygame.surface.Surface) temperature_surface.get_size = mock.Mock(return_value=(100, 82)) card._render_temperature = mock.Mock( return_value=temperature_surface ) card.tick() card.surface.blit.assert_any_call( city_surface, (100, 0, 120, 40) ) card.surface.blit.assert_any_call( conditions_surface, (60, 200, 200, 40) ) card.surface.blit.assert_any_call( icon_surface, (34, 56, 128, 128) ) card.surface.blit.assert_any_call( temperature_surface, (186, 79, 100, 82) ) assert card._should_redraw is False def test_tick_dont_redraw(self): card = self._dummy_card() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) card._should_redraw = False card._surface = mock.Mock(spec=pygame.surface.Surface) city_surface = mock.Mock(spec=pygame.surface.Surface) city_surface.get_size = mock.Mock(return_value=(120, 40)) card._render_city = mock.Mock(return_value=city_surface) conditions_surface = mock.Mock(spec=pygame.surface.Surface) conditions_surface.get_size = mock.Mock(return_value=(200, 40)) card._render_conditions = mock.Mock( return_value=conditions_surface ) icon_surface = mock.Mock(spec=pygame.surface.Surface) icon_surface.get_size = mock.Mock(return_value=(128, 128)) card._render_icon = mock.Mock(return_value=icon_surface) temperature_surface = mock.Mock(spec=pygame.surface.Surface) temperature_surface.get_size = mock.Mock(return_value=(100, 82)) card._render_temperature = mock.Mock( return_value=temperature_surface ) card.tick() assert card._render_city.called is False assert card._render_conditions.called is False assert card._render_icon.called is False assert card._render_temperature.called is False assert card.surface.blit.called is False assert card._should_redraw is False def test_tick_without_icon(self): card = self._dummy_card() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) card._current_conditions = { 'conditions': 'Clouds', 'icon': '01d', 'temperature': 17 } card._surface = mock.Mock(spec=pygame.surface.Surface) city_surface = mock.Mock(spec=pygame.surface.Surface) city_surface.get_size = mock.Mock(return_value=(120, 40)) card._render_city = mock.Mock(return_value=city_surface) conditions_surface = mock.Mock(spec=pygame.surface.Surface) conditions_surface.get_size = mock.Mock(return_value=(200, 40)) card._render_conditions = mock.Mock( return_value=conditions_surface ) card._render_icon = mock.Mock(return_value=None) temperature_surface = mock.Mock(spec=pygame.surface.Surface) temperature_surface.get_size = mock.Mock(return_value=(100, 82)) card._render_temperature = mock.Mock( return_value=temperature_surface ) card.tick() card.surface.blit.assert_any_call( city_surface, (100, 0, 120, 40) ) card.surface.blit.assert_any_call( conditions_surface, (60, 200, 200, 40) ) card.surface.blit.assert_any_call( temperature_surface, (110, 79, 100, 82) ) assert card._should_redraw is False def test_tick_without_conditions(self): card = self._dummy_card() with mock.patch.object(card_module.pygame.font, 'Font', spec=pygame.font.Font): card.initialize(refresh=False) card._surface = mock.Mock(spec=pygame.surface.Surface) city_surface = mock.Mock(spec=pygame.surface.Surface) city_surface.get_size = mock.Mock(return_value=(120, 40)) card._render_city = mock.Mock(return_value=city_surface) card._render_conditions = mock.Mock() card._render_icon = mock.Mock() card._render_temperature = mock.Mock() card.tick() assert card.surface.blit.call_count == 1 card.surface.blit.assert_called_with( city_surface, (100, 0, 120, 40) ) assert card._render_conditions.called is False assert card._render_icon.called is False assert card._render_temperature.called is False assert card._should_redraw is False