def test_loading_file(hass, test_client): """Test that it loads image from disk.""" hass.allow_pool = True @mock.patch('os.path.isfile', mock.Mock(return_value=True)) @mock.patch('os.access', mock.Mock(return_value=True)) def setup_platform(): """Setup platform inside callback.""" assert setup_component( hass, 'camera', { 'camera': { 'name': 'config_test', 'platform': 'local_file', 'file_path': 'mock.file', } }) yield from hass.loop.run_in_executor(None, setup_platform) client = yield from test_client(hass.http.app) m_open = MockOpen(read_data=b'hello') with mock.patch('homeassistant.components.camera.local_file.open', m_open, create=True): resp = yield from client.get('/api/camera_proxy/camera.config_test') assert resp.status == 200 body = yield from resp.text() assert body == 'hello'
def test_get_part_of_log_next_line(offset, json, valid): m = MockOpen() m[LOG_FILE_PATH].read_data = json with mock.patch('__builtin__.open', new=m): assert get_part_of_log(offset, 1)['next_offset'] == valid assert get_part_of_log(offset, 1)['total_size'] == len(LINE_FOR_TEST)
def test_loading_file(hass, aiohttp_client): """Test that it loads image from disk.""" mock_registry(hass) with mock.patch('os.path.isfile', mock.Mock(return_value=True)), \ mock.patch('os.access', mock.Mock(return_value=True)): yield from async_setup_component( hass, 'camera', { 'camera': { 'name': 'config_test', 'platform': 'local_file', 'file_path': 'mock.file', } }) client = yield from aiohttp_client(hass.http.app) m_open = MockOpen(read_data=b'hello') with mock.patch('homeassistant.components.camera.local_file.open', m_open, create=True): resp = yield from client.get('/api/camera_proxy/camera.config_test') assert resp.status == 200 body = yield from resp.text() assert body == 'hello'
def test_camera_content_type(hass, test_client): """Test local_file camera content_type.""" cam_config_jpg = { 'name': 'test_jpg', 'platform': 'local_file', 'file_path': '/path/to/image.jpg', } cam_config_png = { 'name': 'test_png', 'platform': 'local_file', 'file_path': '/path/to/image.png', } cam_config_svg = { 'name': 'test_svg', 'platform': 'local_file', 'file_path': '/path/to/image.svg', } cam_config_noext = { 'name': 'test_no_ext', 'platform': 'local_file', 'file_path': '/path/to/image', } yield from async_setup_component(hass, 'camera', { 'camera': [cam_config_jpg, cam_config_png, cam_config_svg, cam_config_noext]}) client = yield from test_client(hass.http.app) image = 'hello' m_open = MockOpen(read_data=image.encode()) with mock.patch('homeassistant.components.camera.local_file.open', m_open, create=True): resp_1 = yield from client.get('/api/camera_proxy/camera.test_jpg') resp_2 = yield from client.get('/api/camera_proxy/camera.test_png') resp_3 = yield from client.get('/api/camera_proxy/camera.test_svg') resp_4 = yield from client.get('/api/camera_proxy/camera.test_no_ext') assert resp_1.status == 200 assert resp_1.content_type == 'image/jpeg' body = yield from resp_1.text() assert body == image assert resp_2.status == 200 assert resp_2.content_type == 'image/png' body = yield from resp_2.text() assert body == image assert resp_3.status == 200 assert resp_3.content_type == 'image/svg+xml' body = yield from resp_3.text() assert body == image # default mime type assert resp_4.status == 200 assert resp_4.content_type == 'image/jpeg' body = yield from resp_4.text() assert body == image
def test_setup(self): """Test that sensor can be setup.""" config = {"sensor": {"platform": "fail2ban", "jails": ["jail_one"]}} mock_fh = MockOpen() with patch("homeassistant.components.fail2ban.sensor.open", mock_fh, create=True): assert setup_component(self.hass, "sensor", config) self.hass.block_till_done() assert_setup_component(1, "sensor")
def _mock_call(self, path: str, mode: str = "r", *args, **kws): original_side_effect = self._mock_side_effect if path not in self._MockOpen__files: self._mock_side_effect = FileNotFoundError child = MockOpen._mock_call(self, path, mode, *args, **kws) self._mock_side_effect = original_side_effect return child
def test_setup(self): """Test that sensor can be setup.""" config = {'sensor': {'platform': 'fail2ban', 'jails': ['jail_one']}} mock_fh = MockOpen() with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, create=True): assert setup_component(self.hass, 'sensor', config) self.hass.block_till_done() assert_setup_component(1, 'sensor')
def test_get_part_of_log_read(offset, lines, json, valid): """ Первые две цифры кортежа задают offset и lines offset == 0 lines == 1 читаем первую строчку offset == 2 lines == 10 так как смещение указаывает не на первый байт в новой строке, возвращаем следующую строку. """ m = MockOpen() m[LOG_FILE_PATH].read_data = json with mock.patch('__builtin__.open', new=m): assert get_part_of_log(offset, lines)['messages'] == valid
def _check_bank_splitting(expected, contents, is_fresh, is_done): """Compare two bank splits by their structure.""" (expected_header, expected_feature, expected_scenarios) = expected mocked_open = MockOpen() mocked_open[BANK_PATH_1].read_data = contents with patch("bddbot.bank.open", mocked_open): bank = Bank(BANK_PATH_1) mocked_open.assert_called_once_with(BANK_PATH_1, "r") mocked_open[BANK_PATH_1].read.assert_called_once_with() assert_equal(is_fresh, bank.is_fresh()) assert_equal(is_done, bank.is_done()) assert_equal(FEATURE_PATH_1, bank.output_path) assert_multi_line_equal(expected_header, bank.header) assert_multi_line_equal(expected_feature, bank.feature) all_scenarios = list(iter(bank.get_next_scenario, None)) for (expected_scenario, actual_scenario) in zip(expected_scenarios, all_scenarios): assert_multi_line_equal(expected_scenario, actual_scenario)
def test_file_value(self): """Test the File sensor.""" config = { "sensor": {"platform": "file", "name": "file1", "file_path": "mock.file1"} } m_open = MockOpen(read_data="43\n45\n21") with patch("homeassistant.components.file.sensor.open", m_open, create=True): assert setup_component(self.hass, "sensor", config) self.hass.block_till_done() state = self.hass.states.get("sensor.file1") assert state.state == "21"
def test_file_empty(self): """Test the File sensor with an empty file.""" config = { "sensor": {"platform": "file", "name": "file3", "file_path": "mock.file"} } m_open = MockOpen(read_data="") with patch("homeassistant.components.file.sensor.open", m_open, create=True): assert setup_component(self.hass, "sensor", config) self.hass.block_till_done() state = self.hass.states.get("sensor.file3") assert state.state == STATE_UNKNOWN
def test_ipv6_ban(self): """Test that log is parsed correctly for IPV6 bans.""" log_parser = BanLogParser("/test/fail2ban.log") sensor = BanSensor("fail2ban", "jail_one", log_parser) assert sensor.name == "fail2ban jail_one" mock_fh = MockOpen(read_data=fake_log("ipv6_ban")) with patch( "homeassistant.components.fail2ban.sensor.open", mock_fh, create=True ): sensor.update() assert sensor.state == "2607:f0d0:1002:51::4" assert sensor.state_attributes[STATE_CURRENT_BANS] == ["2607:f0d0:1002:51::4"] assert sensor.state_attributes[STATE_ALL_BANS] == ["2607:f0d0:1002:51::4"]
def test_unban_all(self): """Test that log is parsed correctly when unbanning.""" log_parser = BanLogParser('/tmp') sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('unban_all')) with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, create=True): sensor.update() assert sensor.state == 'None' assert sensor.state_attributes[STATE_CURRENT_BANS] == [] assert sensor.state_attributes[STATE_ALL_BANS] == \ ['111.111.111.111', '222.222.222.222']
def test_single_ban(self): """Test that log is parsed correctly for single ban.""" log_parser = BanLogParser("/tmp") sensor = BanSensor("fail2ban", "jail_one", log_parser) assert sensor.name == "fail2ban jail_one" mock_fh = MockOpen(read_data=fake_log("single_ban")) with patch( "homeassistant.components.fail2ban.sensor.open", mock_fh, create=True ): sensor.update() assert sensor.state == "111.111.111.111" assert sensor.state_attributes[STATE_CURRENT_BANS] == ["111.111.111.111"] assert sensor.state_attributes[STATE_ALL_BANS] == ["111.111.111.111"]
def test_ban_active_after_update(self): """Test that ban persists after subsequent update.""" log_parser = BanLogParser('/tmp') sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('single_ban')) with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, create=True): sensor.update() assert sensor.state == '111.111.111.111' sensor.update() assert sensor.state == '111.111.111.111' assert \ sensor.state_attributes[STATE_CURRENT_BANS] == ['111.111.111.111'] assert sensor.state_attributes[STATE_ALL_BANS] == ['111.111.111.111']
def test_multiple_ban(self): """Test that log is parsed correctly for multiple ban.""" log_parser = BanLogParser(timedelta(seconds=-1), '/tmp') sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('multi_ban')) with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, create=True): sensor.update() assert sensor.state == '222.222.222.222' assert sensor.state_attributes[STATE_CURRENT_BANS] == \ ['111.111.111.111', '222.222.222.222'] assert sensor.state_attributes[STATE_ALL_BANS] == \ ['111.111.111.111', '222.222.222.222']
def test_single_ban(self): """Test that log is parsed correctly for single ban.""" log_parser = BanLogParser('/tmp') sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('single_ban')) with patch('homeassistant.components.fail2ban.sensor.open', mock_fh, create=True): sensor.update() assert sensor.state == '111.111.111.111' assert \ sensor.state_attributes[STATE_CURRENT_BANS] == ['111.111.111.111'] assert \ sensor.state_attributes[STATE_ALL_BANS] == ['111.111.111.111']
def test_ban_active_after_update(self): """Test that ban persists after subsequent update.""" log_parser = BanLogParser("/test/fail2ban.log") sensor = BanSensor("fail2ban", "jail_one", log_parser) assert sensor.name == "fail2ban jail_one" mock_fh = MockOpen(read_data=fake_log("single_ban")) with patch( "homeassistant.components.fail2ban.sensor.open", mock_fh, create=True ): sensor.update() assert sensor.state == "111.111.111.111" sensor.update() assert sensor.state == "111.111.111.111" assert sensor.state_attributes[STATE_CURRENT_BANS] == ["111.111.111.111"] assert sensor.state_attributes[STATE_ALL_BANS] == ["111.111.111.111"]
def test_unban_all(self): """Test that log is parsed correctly when unbanning.""" log_parser = BanLogParser(timedelta(seconds=-1), '/tmp') sensor = BanSensor('fail2ban', 'jail_one', log_parser) self.assertEqual(sensor.name, 'fail2ban jail_one') mock_fh = MockOpen(read_data=fake_log('unban_all')) with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, create=True): sensor.update() self.assertEqual(sensor.state, 'None') self.assertEqual(sensor.state_attributes[STATE_CURRENT_BANS], []) self.assertEqual(sensor.state_attributes[STATE_ALL_BANS], ['111.111.111.111', '222.222.222.222'])
def test_ipv6_ban(self): """Test that log is parsed correctly for IPV6 bans.""" log_parser = BanLogParser('/tmp') sensor = BanSensor('fail2ban', 'jail_one', log_parser) assert sensor.name == 'fail2ban jail_one' mock_fh = MockOpen(read_data=fake_log('ipv6_ban')) with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, create=True): sensor.update() assert sensor.state == '2607:f0d0:1002:51::4' assert \ sensor.state_attributes[STATE_CURRENT_BANS] == \ ['2607:f0d0:1002:51::4'] assert \ sensor.state_attributes[STATE_ALL_BANS] == ['2607:f0d0:1002:51::4']
def test_ban_active_after_update(self): """Test that ban persists after subsequent update.""" log_parser = BanLogParser(timedelta(seconds=-1), '/tmp') sensor = BanSensor('fail2ban', 'jail_one', log_parser) self.assertEqual(sensor.name, 'fail2ban jail_one') mock_fh = MockOpen(read_data=fake_log('single_ban')) with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, create=True): sensor.update() self.assertEqual(sensor.state, '111.111.111.111') sensor.update() self.assertEqual(sensor.state, '111.111.111.111') self.assertEqual(sensor.state_attributes[STATE_CURRENT_BANS], ['111.111.111.111']) self.assertEqual(sensor.state_attributes[STATE_ALL_BANS], ['111.111.111.111'])
def test_unban_all(self): """Test that log is parsed correctly when unbanning.""" log_parser = BanLogParser("/test/fail2ban.log") sensor = BanSensor("fail2ban", "jail_one", log_parser) assert sensor.name == "fail2ban jail_one" mock_fh = MockOpen(read_data=fake_log("unban_all")) with patch("homeassistant.components.fail2ban.sensor.open", mock_fh, create=True): sensor.update() assert sensor.state == "None" assert sensor.state_attributes[STATE_CURRENT_BANS] == [] assert sensor.state_attributes[STATE_ALL_BANS] == [ "111.111.111.111", "222.222.222.222", ]
def test_file_value(self): """Test the File sensor.""" config = { 'sensor': { 'platform': 'file', 'name': 'file1', 'file_path': 'mock.file1', } } m_open = MockOpen(read_data='43\n45\n21') with patch('homeassistant.components.sensor.file.open', m_open, create=True): assert setup_component(self.hass, 'sensor', config) self.hass.block_till_done() state = self.hass.states.get('sensor.file1') self.assertEqual(state.state, '21')
def test_file_empty(self): """Test the File sensor with an empty file.""" config = { 'sensor': { 'platform': 'file', 'name': 'file3', 'file_path': 'mock.file', } } m_open = MockOpen(read_data='') with patch('homeassistant.components.sensor.file.open', m_open, create=True): assert setup_component(self.hass, 'sensor', config) self.hass.block_till_done() state = self.hass.states.get('sensor.file3') self.assertEqual(state.state, STATE_UNKNOWN)
def test_multi_jail(self): """Test that log is parsed correctly when using multiple jails.""" log_parser = BanLogParser("/tmp") sensor1 = BanSensor("fail2ban", "jail_one", log_parser) sensor2 = BanSensor("fail2ban", "jail_two", log_parser) assert sensor1.name == "fail2ban jail_one" assert sensor2.name == "fail2ban jail_two" mock_fh = MockOpen(read_data=fake_log("multi_jail")) with patch( "homeassistant.components.fail2ban.sensor.open", mock_fh, create=True ): sensor1.update() sensor2.update() assert sensor1.state == "111.111.111.111" assert sensor1.state_attributes[STATE_CURRENT_BANS] == ["111.111.111.111"] assert sensor1.state_attributes[STATE_ALL_BANS] == ["111.111.111.111"] assert sensor2.state == "222.222.222.222" assert sensor2.state_attributes[STATE_CURRENT_BANS] == ["222.222.222.222"] assert sensor2.state_attributes[STATE_ALL_BANS] == ["222.222.222.222"]
def test_multi_jail(self): """Test that log is parsed correctly when using multiple jails.""" log_parser = BanLogParser('/tmp') sensor1 = BanSensor('fail2ban', 'jail_one', log_parser) sensor2 = BanSensor('fail2ban', 'jail_two', log_parser) assert sensor1.name == 'fail2ban jail_one' assert sensor2.name == 'fail2ban jail_two' mock_fh = MockOpen(read_data=fake_log('multi_jail')) with patch('homeassistant.components.sensor.fail2ban.open', mock_fh, create=True): sensor1.update() sensor2.update() assert sensor1.state == '111.111.111.111' assert \ sensor1.state_attributes[STATE_CURRENT_BANS] == ['111.111.111.111'] assert sensor1.state_attributes[STATE_ALL_BANS] == ['111.111.111.111'] assert sensor2.state == '222.222.222.222' assert \ sensor2.state_attributes[STATE_CURRENT_BANS] == ['222.222.222.222'] assert sensor2.state_attributes[STATE_ALL_BANS] == ['222.222.222.222']
def test_file_value_template(self): """Test the File sensor with JSON entries.""" config = { 'sensor': { 'platform': 'file', 'name': 'file2', 'file_path': 'mock.file2', 'value_template': '{{ value_json.temperature }}', } } data = '{"temperature": 29, "humidity": 31}\n' \ '{"temperature": 26, "humidity": 36}' m_open = MockOpen(read_data=data) with patch('homeassistant.components.sensor.file.open', m_open, create=True): assert setup_component(self.hass, 'sensor', config) self.hass.block_till_done() state = self.hass.states.get('sensor.file2') self.assertEqual(state.state, '26')
def test_file_value_template(self): """Test the File sensor with JSON entries.""" config = { "sensor": { "platform": "file", "name": "file2", "file_path": "mock.file2", "value_template": "{{ value_json.temperature }}", } } data = ( '{"temperature": 29, "humidity": 31}\n' '{"temperature": 26, "humidity": 36}' ) m_open = MockOpen(read_data=data) with patch("homeassistant.components.file.sensor.open", m_open, create=True): assert setup_component(self.hass, "sensor", config) self.hass.block_till_done() state = self.hass.states.get("sensor.file2") assert state.state == "26"
def __init__(self): super(BaseDealerTest, self).__init__() self.dealer = None self.mocked_open = MockOpen() self.mocked_popen = create_autospec(Popen) self.mocked_mkdir = Mock()
async def test_service_exists(hass, caplog): """Test importing a pyscript module.""" conf_dir = hass.config.path(FOLDER) file_contents = { f"{conf_dir}/hello.py": """ import xyz2 from xyz2 import f_minus @service def func1(): pyscript.done = [xyz2.f_add(1, 2), xyz2.f_mult(3, 4), xyz2.f_add(10, 20), f_minus(50, 20)] """, # # this will fail to load since import doesn't exist # f"{conf_dir}/bad_import.py": """ import no_such_package @service def func10(): pass """, # # this will fail to load since import has a syntax error # f"{conf_dir}/bad_import2.py": """ import bad_module @service def func11(): pass """, # # This will load, since there is an apps/world config entry # f"{conf_dir}/apps/world.py": """ from xyz2 import * @service def func2(): pyscript.done = [get_x(), get_name(), other_name(), f_add(1, 5), f_mult(3, 6), f_add(10, 30), f_minus(50, 30)] """, # # This will not load, since there is no apps/world2 config entry # f"{conf_dir}/apps/world2.py": """ from xyz2 import * @service def func10(): pass """, f"{conf_dir}/modules/xyz2/__init__.py": """ from .other import f_minus, other_name log.info(f"modules/xyz2 global_ctx={pyscript.get_global_ctx()};") x = 99 def f_add(a, b): return a + b def f_mult(a, b): return a * b def get_x(): return x def get_name(): return __name__ """, f"{conf_dir}/modules/xyz2/other.py": """ def f_minus(a, b): return a - b def other_name(): return __name__ """, # # this module has a syntax error (missing :) # f"{conf_dir}/modules/bad_module.py": """ def func12() pass """, # # this script file should auto-load # f"{conf_dir}/scripts/func13.py": """ @service def func13(): pass """, # # this script file should auto-load # f"{conf_dir}/scripts/a/b/c/d/func14.py": """ @service def func14(): pass log.info(f"func14 global_ctx={pyscript.get_global_ctx()};") """, # # this script file should not auto-load # f"{conf_dir}/scripts/a/b/c/d/#func15.py": """ @service def func15(): pass """, # # this script file should not auto-load # f"{conf_dir}/scripts/#a/b/c/d/func15.py": """ @service def func15(): pass """, } mock_open = MockOpen() for key, value in file_contents.items(): mock_open[key].read_data = value def isfile_side_effect(arg): return arg in file_contents def glob_side_effect(path, recursive=None): result = [] path_re = path.replace("*", "[^/]*").replace(".", "\\.") path_re = path_re.replace("[^/]*[^/]*/", ".*") for this_path in file_contents: if re.match(path_re, this_path): result.append(this_path) return result conf = {"apps": {"world": {}}} with patch( "custom_components.pyscript.os.path.isdir", return_value=True ), patch("custom_components.pyscript.glob.iglob") as mock_glob, patch( "custom_components.pyscript.global_ctx.open", mock_open ), patch("custom_components.pyscript.open", mock_open), patch( "homeassistant.config.load_yaml_config_file", return_value={"pyscript": conf} ), patch("custom_components.pyscript.watchdog_start", return_value=None), patch( "custom_components.pyscript.os.path.getmtime", return_value=1000), patch( "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), patch( "custom_components.pyscript.os.path.isfile" ) as mock_isfile: mock_isfile.side_effect = isfile_side_effect mock_glob.side_effect = glob_side_effect assert await async_setup_component(hass, "pyscript", {DOMAIN: conf}) notify_q = asyncio.Queue(0) async def state_changed(event): var_name = event.data["entity_id"] if var_name != "pyscript.done": return value = event.data["new_state"].state await notify_q.put(value) hass.bus.async_listen(EVENT_STATE_CHANGED, state_changed) assert not hass.services.has_service("pyscript", "func10") assert not hass.services.has_service("pyscript", "func11") assert hass.services.has_service("pyscript", "func13") assert hass.services.has_service("pyscript", "func14") assert not hass.services.has_service("pyscript", "func15") await hass.services.async_call("pyscript", "func1", {}) ret = await wait_until_done(notify_q) assert literal_eval(ret) == [1 + 2, 3 * 4, 10 + 20, 50 - 20] await hass.services.async_call("pyscript", "func2", {}) ret = await wait_until_done(notify_q) assert literal_eval(ret) == [ 99, "xyz2", "xyz2.other", 1 + 5, 3 * 6, 10 + 30, 50 - 30 ] assert "modules/xyz2 global_ctx=modules.xyz2;" in caplog.text assert "func14 global_ctx=scripts.a.b.c.d.func14;" in caplog.text assert "ModuleNotFoundError: import of no_such_package not allowed" in caplog.text assert "SyntaxError: invalid syntax (bad_module.py, line 2)" in caplog.text
class BaseDealerTest(BankMockerTest): # pylint: disable=too-few-public-methods """A container for utility classes common when testing the Dealer class.""" FEATURES = { BANK_PATH_1: (FEATURE_PATH_1, "", FEATURE_1 + "\n"), BANK_PATH_2: (FEATURE_PATH_2, "", FEATURE_2 + "\n"), } def __init__(self): super(BaseDealerTest, self).__init__() self.dealer = None self.mocked_open = MockOpen() self.mocked_popen = create_autospec(Popen) self.mocked_mkdir = Mock() def teardown(self): super(BaseDealerTest, self).teardown() patch.stopall() # Reset dealer instance. self.dealer = None def _mock_dealer_functions(self): """Mock out standard library functions used by the dealer module.""" self._reset_mocks() patcher = patch.multiple( "bddbot.dealer", open = self.mocked_open, Bank = self.mock_bank_class, RemoteBank = self.mock_bank_class, Popen = self.mocked_popen, mkdir = self.mocked_mkdir) patcher.start() def _create_dealer(self, banks, tests, name = ""): """Create a new dealer instance without loading state.""" if tests is None: tests = DEFAULT_TEST_COMMANDS self.mocked_open[STATE_PATH].side_effect = IOError() self.dealer = Dealer(banks, tests, name = name) self.mocked_open.assert_called_once_with(STATE_PATH, "rb") self._reset_mocks() def _load_dealer(self, banks = None, tests = None, name = ""): """Simulate a call to load() and verify success.""" if banks is None: banks = [BANK_PATH_1, ] if not tests: tests = DEFAULT_TEST_COMMANDS if self.dealer is None: self._create_dealer(banks, tests, name = name) # pylint: disable=bad-continuation with patch.multiple("bddbot.dealer", Bank = self.mock_bank_class, RemoteBank = self.mock_bank_class): self.dealer.load() # Verify calls to mocks. self.mocked_open.assert_not_called() self.mocked_popen.assert_not_called() for path in banks: if not path.startswith("@"): self.mock_bank_class.assert_any_call(path) else: (host, port) = path[1:].split(":") self.mock_bank_class.assert_called_with(name, host, int(port)) self._reset_mocks() def _deal(self, expected_feature, expected_scenario, bank_path = None, feature_path = None): # pylint: disable=too-many-arguments """Simulate dealing a scenario and verify success. If `expected_feature` is specified, simulate the first time a scenario is dealt from the features bank. Otherwise, simulate a consecutive deal from a previous bank. Return the commands passed to `Popen()` to verify outside of this function. """ if not bank_path: bank_path = BANK_PATH_1 if not feature_path: feature_path = bank_path.replace("bank", "feature") self.mocked_popen.return_value.returncode = 0 self.mocked_popen.return_value.communicate.return_value = ("", "") self.dealer.deal() # If feature is specified, simulate the first deal from the features bank. if expected_feature is not None: self.mocked_open.assert_called_once_with(feature_path, "w") self._assert_writes( ["", expected_feature + "\n", expected_scenario, ], path = feature_path) self.mocked_mkdir.assert_called_once_with(dirname(feature_path)) # If feature isn't specified, simulate a consecutive deal. # Note that calls to Popen should be verified outside of this function in this case. else: self.mocked_open.assert_called_once_with(feature_path, "ab") self.mocked_open[feature_path].write.assert_called_once_with(expected_scenario) self.mocked_popen.return_value.communicate.assert_called_with() self.mocked_mkdir.assert_not_called() self.mock_banks[bank_path].is_fresh.assert_called_with() self.mock_banks[bank_path].is_done.assert_called_with() self.mock_banks[bank_path].get_next_scenario.assert_called_once_with() # We return the commands because we reset the mocks at the end of the function. # The stdout/stderr values aren't important, we only care about the commands. popen_calls = [command for ((command, ), _) in self.mocked_popen.call_args_list] # Reset mocks. self._reset_mocks() return popen_calls def _assert_writes(self, chunks, path = FEATURE_PATH_1): """Verify all calls to write().""" assert_equal([call(chunk) for chunk in chunks], self.mocked_open[path].write.mock_calls) def _reset_mocks(self): """Reset all mocks.""" self.mocked_open.reset_mock() self.mocked_popen.reset_mock() self.mock_bank_class.reset_mock() self.mocked_mkdir.reset_mock() for mock_bank in self.mock_banks.itervalues(): mock_bank.reset_mock()
def test_valid_upload_recording(self): """Test UserAPI.upload_recordings ( parameters passed: groupname, devicename, filename, props) to mocked CacophonyServer object. # Range of expectedResult 'expectedResult':{'outcome':'success', 'validator':lambda test: test}, 'expectedResult':{'outcome':'failurePreRequest', 'validator':lambda test: test}, 'expectedResult':{'outcome':'failureOnRequest', 'validator':lambda test: test}, """ # TODO: Construct the data to pass in the post mock_apiPath = lambda devicename, groupname: "/api/v1/recordings/device/{devicename}/group/{groupname}".format( devicename=devicename, groupname=groupname) # TODO: construct the success validatior # TODO: construct the failure validtor testcases = [ { "filename": "test1.cptv", "mock_file.side_effect": None, "devicename": "deviceTEST1234", "groupname": "groupTEST1234", "prop": None, "expectedProp": { "type": "thermalRaw" }, "expectedResult": { "outcome": "success", "validator": lambda test: test, }, "mockRequestJsonResponse": { "success": True }, "mockRequestStatusCode": 200, }, # TODO: adjust test mock/asserts to handle IOError # {'filename':"test1.cptv", # 'mock_file.side_effect':IOError(), # 'devicename':'deviceTEST1234', # 'groupname':'groupTEST1234', # 'prop':None, # 'expectedProp':{'type':'thermalRaw'}, # 'mockRequestStatusCode':200 # }, { "filename": "test2.mp3", "mock_file.side_effect": None, "devicename": "deviceTEST1234", "groupname": "groupTEST1234", "prop": None, "expectedProp": { "type": "audio" }, "expectedResult": { "outcome": "success", "validator": lambda test: test, }, "mockRequestJsonResponse": { "success": True }, "mockRequestStatusCode": 200, }, # TODO: what assert/mock to do for unknown file type { "filename": "test3.xyz", "mock_file.side_effect": None, "devicename": "deviceTEST1234", "groupname": "groupTEST1234", "prop": None, "expectedProp": None, "expectedResult": { "outcome": "failurePreRequest", "validator": lambda test: test, }, "mockRequestJsonResponse": None, "mockRequestStatusCode": None, }, ] for tc in testcases: # TODO: pretty print print(tc) """ This Test is tough to design/debug made easier by using mock-open module https://github.com/nivbend/mock-open mock the file to upload Last, you also need to mock os.fstat() so that permissions are correct (Clues in https://gist.github.com/dmerejkowsky/d11e3c68be6a96387dea3d8b6a409b40#file-test_netrc-py-L17) """ mock_open = MockOpen(read_data="0123456789012345678901234") # mock_open = MockOpen(read_data='',side_effect=IOError('no file')) mock_open[os.path.join( os.path.dirname(__file__), tc["filename"])].read_data = "0123456789012345678901234" mock_open[os.path.join( os.path.dirname(__file__), tc["filename"])].side_effect = tc["mock_file.side_effect"] fake_stat = mock.MagicMock("fstat") # fake_stat.st_uid = # os.getuid() fake_stat.st_mode = 0o600 fake_stat.st_size = 25 with patch("builtins.open", mock_open), patch("os.fstat") as mock_fstat: mock_fstat.return_value = fake_stat with requests_mock.Mocker() as m: m.register_uri( requests_mock.POST, "{apiURL}{apiPath}".format( apiURL=defaults["apiURL"], apiPath=mock_apiPath(tc["devicename"], tc["groupname"]), ), json=tc["mockRequestJsonResponse"], status_code=tc["mockRequestStatusCode"], ) if tc["expectedResult"]["outcome"] == "success": # ----------------------- API call UNDERTEST ------------------ result = self.cli.upload_recording( tc["groupname"], tc["devicename"], tc["filename"], tc["prop"], ) # ---------------------------------------------------------------- print(result) # TODO: FILL in the TEST assertions in place of the following self.assertEqual(result, tc["mockRequestJsonResponse"]) self.assertTrue( tc["expectedResult"]["validator"](True)) elif tc["expectedResult"][ "outcome"] == "failurePreRequest": with self.assertRaises(ValueError): # ----------------------- API call UNDERTEST ------------------ result = self.cli.upload_recording( tc["groupname"], tc["devicename"], tc["filename"], tc["prop"], ) # ---------------------------------------------------------------- else: # TODO: Check the mockrequest was called, and mockfile self.assertTrue( False, "Something not being checked ----------------------------", )
async def test_reload(hass, caplog): """Test reload a pyscript module.""" conf_dir = hass.config.path(FOLDER) file_contents = { f"{conf_dir}/hello.py": """ import xyz2 from xyz2 import xyz # # ensure a regular script doesn't have pyscript.app_config set # try: x = pyscript.app_config assert False except NameError: pass log.info(f"{__name__} global_ctx={pyscript.get_global_ctx()} xyz={xyz} xyz2.xyz={xyz2.xyz}") @service def func1(): pass """, # # This will load, since there is an apps/world config entry # f"{conf_dir}/apps/world.py": """ from xyz2 import * log.info(f"{__name__} global_ctx={pyscript.get_global_ctx()} xyz={xyz}") @service def func2(): pass """, # # This will load, since there is an apps/world2 config entry # f"{conf_dir}/apps/world2/__init__.py": """ from .other import * assert pyscript.config['apps']['world2'] == pyscript.app_config log.info(f"{__name__} global_ctx={pyscript.get_global_ctx()} var1={pyscript.config['apps']['world2']['var1']}, other_abc={other_abc}") @service def func3(): pass """, f"{conf_dir}/apps/world2/other.py": """ other_abc = 987 log.info(f"{__name__} global_ctx={pyscript.get_global_ctx()}") # # ensure a sub file in the app doesn't have pyscript.app_config set # try: x = pyscript.app_config assert False except NameError: pass @time_trigger("shutdown") def shutdown(trigger_time=None): log.info(f"{__name__} global_ctx={pyscript.get_global_ctx()} shutdown trigger_time={trigger_time}") """, f"{conf_dir}/modules/xyz2/__init__.py": """ from .other import xyz # # ensure a module doesn't have pyscript.app_config set # try: x = pyscript.app_config assert False except NameError: pass log.info(f"modules/xyz2 global_ctx={pyscript.get_global_ctx()};") """, f"{conf_dir}/modules/xyz2/other.py": """ log.info(f"modules/xyz2/other global_ctx={pyscript.get_global_ctx()};") xyz = 123 """, # # these shouldn't load since the package takes precedence # f"{conf_dir}/modules/xyz2.py": """ log.info(f"BOTCH shouldn't load {__name__}") """, f"{conf_dir}/apps/world2.py": """ log.info(f"BOTCH shouldn't load {__name__}") """, } mock_open = MockOpen() for key, value in file_contents.items(): mock_open[key].read_data = value def isfile_side_effect(arg): return arg in file_contents def glob_side_effect(path, recursive=None): result = [] path_re = path.replace("*", "[^/]*").replace(".", "\\.") path_re = path_re.replace("[^/]*[^/]*/", ".*") for this_path in file_contents: if re.match(path_re, this_path): result.append(this_path) return result conf = {"apps": {"world": {}, "world2": {"var1": 100}}} with patch( "custom_components.pyscript.os.path.isdir", return_value=True ), patch("custom_components.pyscript.glob.iglob") as mock_glob, patch( "custom_components.pyscript.global_ctx.open", mock_open ), patch("custom_components.pyscript.open", mock_open), patch( "homeassistant.util.yaml.loader.open", mock_open ), patch("homeassistant.config.load_yaml_config_file", return_value={"pyscript": conf}), patch( "custom_components.pyscript.watchdog_start", return_value=None), patch( "custom_components.pyscript.os.path.getmtime", return_value=1000 ), patch( "custom_components.pyscript.global_ctx.os.path.getmtime", return_value=1000), patch( "custom_components.pyscript.os.path.isfile" ) as mock_isfile: mock_isfile.side_effect = isfile_side_effect mock_glob.side_effect = glob_side_effect assert await async_setup_component(hass, "pyscript", {DOMAIN: conf}) notify_q = asyncio.Queue(0) async def state_changed(event): var_name = event.data["entity_id"] if var_name != "pyscript.done": return value = event.data["new_state"].state await notify_q.put(value) hass.bus.async_listen(EVENT_STATE_CHANGED, state_changed) assert hass.services.has_service("pyscript", "func1") assert hass.services.has_service("pyscript", "func2") assert hass.services.has_service("pyscript", "func3") assert "modules/xyz2 global_ctx=modules.xyz2;" in caplog.text assert "modules/xyz2/other global_ctx=modules.xyz2.other;" in caplog.text assert "hello global_ctx=file.hello xyz=123 xyz2.xyz=123" in caplog.text assert "world2.other global_ctx=apps.world2.other" in caplog.text assert "world2 global_ctx=apps.world2 var1=100, other_abc=987" in caplog.text # # add a new script file # file_contents[f"{conf_dir}/hello2.py"] = """ log.info(f"{__name__} global_ctx={pyscript.get_global_ctx()};") @service def func20(): pass """ mock_open[f"{conf_dir}/hello2.py"].read_data = file_contents[ f"{conf_dir}/hello2.py"] # # should not load the new script if we reload something else # await hass.services.async_call("pyscript", "reload", {"global_ctx": "file.hello"}, blocking=True) assert not hass.services.has_service("pyscript", "func20") assert "hello2 global_ctx=file.hello2;" not in caplog.text # # should load new file # await hass.services.async_call("pyscript", "reload", {}, blocking=True) assert hass.services.has_service("pyscript", "func20") assert "hello2 global_ctx=file.hello2;" in caplog.text # # delete the script file # del file_contents[f"{conf_dir}/hello2.py"] # # should not delete the script file if we reload something else # await hass.services.async_call("pyscript", "reload", {"global_ctx": "file.hello"}, blocking=True) assert hass.services.has_service("pyscript", "func20") # # should delete the script file # await hass.services.async_call("pyscript", "reload", {}, blocking=True) assert not hass.services.has_service("pyscript", "func20") # # change a module file and confirm the parent script is reloaded too # file_contents[f"{conf_dir}/modules/xyz2/other.py"] = """ log.info(f"modules/xyz2/other global_ctx={pyscript.get_global_ctx()};") xyz = 456 """ mock_open[ f"{conf_dir}/modules/xyz2/other.py"].read_data = file_contents[ f"{conf_dir}/modules/xyz2/other.py"] await hass.services.async_call("pyscript", "reload", {}, blocking=True) assert "hello global_ctx=file.hello xyz=456 xyz2.xyz=456" in caplog.text # # change the app config # conf["apps"]["world2"]["var1"] = 200 await hass.services.async_call("pyscript", "reload", {}, blocking=True) assert "world2 global_ctx=apps.world2 var1=200, other_abc=987" in caplog.text assert "world2.other global_ctx=apps.world2.other shutdown trigger_time=shutdown" in caplog.text # # change a module inside an app # file_contents[f"{conf_dir}/apps/world2/other.py"] = """ other_abc = 654 log.info(f"{__name__} global_ctx={pyscript.get_global_ctx()}") @time_trigger("shutdown") def shutdown(trigger_time=None): log.info(f"{__name__} global_ctx={pyscript.get_global_ctx()} shutdown_new trigger_time={trigger_time}") """ mock_open[ f"{conf_dir}/apps/world2/other.py"].read_data = file_contents[ f"{conf_dir}/apps/world2/other.py"] await hass.services.async_call("pyscript", "reload", {}, blocking=True) assert "world2 global_ctx=apps.world2 var1=200, other_abc=654" in caplog.text assert "world2.other global_ctx=apps.world2.other shutdown trigger_time=shutdown" in caplog.text # # now confirm certain files reloaded the correct number of times, # and reload everything a few times # for i in range(3): assert caplog.text.count( "world global_ctx=apps.world xyz=") == 2 + i assert caplog.text.count( "world2 global_ctx=apps.world2 var1=") == 3 + i assert caplog.text.count( "hello global_ctx=file.hello xyz=") == 4 + i assert caplog.text.count( "modules/xyz2/other global_ctx=modules.xyz2.other") == 2 + i assert caplog.text.count( "modules/xyz2 global_ctx=modules.xyz2") == 2 + i assert (caplog.text.count( "world2.other global_ctx=apps.world2.other shutdown trigger_time=shutdown" ) == 2) assert (caplog.text.count( "world2.other global_ctx=apps.world2.other shutdown_new trigger_time=shutdown" ) == i) if i < 2: await hass.services.async_call("pyscript", "reload", {"global_ctx": "*"}, blocking=True) # # make sure files that shouldn't load were not loaded # assert "BOTCH shouldn't load" not in caplog.text