| #!/usr/bin/env python3 |
| # |
| # Copyright 2016 - The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import logging |
| import time |
| import unittest |
| |
| import mock |
| |
| from acts import utils |
| from acts import signals |
| from acts.controllers.adb_lib.error import AdbError |
| from acts.controllers.android_device import AndroidDevice |
| from acts.controllers.fuchsia_device import FuchsiaDevice |
| from acts.controllers.utils_lib.ssh.connection import SshConnection |
| from acts.libs.proc import job |
| |
| PROVISIONED_STATE_GOOD = 1 |
| |
| MOCK_IP_ADDRESSES = """eno1 100.127.110.79 |
| eno1 2401:fa00:480:7a00:8d4f:85ff:cc5c:787e |
| eno1 2401:fa00:480:7a00:459:b993:fcbf:1419 |
| eno1 fe80::c66d:3c75:2cec:1d72 |
| enx00e04c000d06 192.168.42.220 |
| enx00e04c000d06 fe80::2c68:f1b7:eaaa:52e7""" |
| |
| MOCK_IFCONFIG_OUTPUT = """eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 |
| inet 100.127.110.79 netmask 255.255.255.0 broadcast 100.127.110.255 |
| inet6 2401:fa00:480:7a00:8d4f:85ff:cc5c:787e prefixlen 64 scopeid 0x0<global> |
| inet6 fe80::c66d:3c75:2cec:1d72 prefixlen 64 scopeid 0x20<link> |
| inet6 2401:fa00:480:7a00:459:b993:fcbf:1419 prefixlen 64 scopeid 0x0<global> |
| ether 54:b2:03:13:36:05 txqueuelen 1000 (Ethernet) |
| RX packets 32943262 bytes 13324306863 (13.3 GB) |
| RX errors 669 dropped 0 overruns 0 frame 669 |
| TX packets 4778580 bytes 3012041798 (3.0 GB) |
| TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 |
| device interrupt 16 memory 0xdf200000-df220000 |
| |
| enx00e04c000d06: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 |
| inet 192.168.42.220 netmask 255.255.255.0 broadcast 192.168.42.255 |
| inet6 fe80::2c68:f1b7:eaaa:52e7 prefixlen 64 scopeid 0x20<link> |
| ether 00:e0:4c:00:0d:06 txqueuelen 1000 (Ethernet) |
| RX packets 10212416 bytes 3204008175 (3.2 GB) |
| RX errors 0 dropped 0 overruns 0 frame 0 |
| TX packets 9868425 bytes 5641667955 (5.6 GB) |
| TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 |
| |
| lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 |
| inet 127.0.0.1 netmask 255.0.0.0 |
| inet6 ::1 prefixlen 128 scopeid 0x10<host> |
| loop txqueuelen 1000 (Local Loopback) |
| RX packets 42779835 bytes 6144028882 (6.1 GB) |
| RX errors 0 dropped 0 overruns 0 frame 0 |
| TX packets 42779835 bytes 6144028882 (6.1 GB) |
| TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 |
| |
| """ |
| |
| FUCHSIA_INTERFACES = { |
| 'id': |
| '1', |
| 'result': [{ |
| 'features': |
| 4, |
| 'filepath': |
| '[none]', |
| 'id': |
| 1, |
| 'ipv4_addresses': [[127, 0, 0, 1]], |
| 'ipv6_addresses': [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]], |
| 'is_administrative_status_enabled': |
| True, |
| 'is_physical_status_up': |
| True, |
| 'mac': [0, 0, 0, 0, 0, 0], |
| 'mtu': |
| 65536, |
| 'name': |
| 'lo', |
| 'topopath': |
| 'loopback' |
| }, { |
| 'features': |
| 0, |
| 'filepath': |
| '/dev/class/ethernet/000', |
| 'id': |
| 2, |
| 'ipv4_addresses': [[100, 127, 110, 79]], |
| 'ipv6_addresses': |
| [[254, 128, 0, 0, 0, 0, 0, 0, 198, 109, 60, 117, 44, 236, 29, 114], |
| [36, 1, 250, 0, 4, 128, 122, 0, 141, 79, 133, 255, 204, 92, 120, 126], |
| [36, 1, 250, 0, 4, 128, 122, 0, 4, 89, 185, 147, 252, 191, 20, 25]], |
| 'is_administrative_status_enabled': |
| True, |
| 'is_physical_status_up': |
| True, |
| 'mac': [0, 224, 76, 5, 76, 229], |
| 'mtu': |
| 1514, |
| 'name': |
| 'eno1', |
| 'topopath': |
| '@/dev/xhci/xhci/usb-bus/001/001/ifc-000/usb-cdc-ecm/ethernet' |
| }, { |
| 'features': |
| 1, |
| 'filepath': |
| '/dev/class/ethernet/001', |
| 'id': |
| 3, |
| 'ipv4_addresses': [], |
| 'ipv6_addresses': |
| [[254, 128, 0, 0, 0, 0, 0, 0, 96, 255, 93, 96, 52, 253, 253, 243], |
| [254, 128, 0, 0, 0, 0, 0, 0, 70, 7, 11, 255, 254, 118, 126, 192]], |
| 'is_administrative_status_enabled': |
| False, |
| 'is_physical_status_up': |
| False, |
| 'mac': [68, 7, 11, 118, 126, 192], |
| 'mtu': |
| 1500, |
| 'name': |
| 'wlanxc0', |
| 'topopath': |
| '@/dev/wifi/wlanphy/wlanif-client/wlan-ethernet/ethernet' |
| }], |
| 'error': |
| None |
| } |
| |
| CORRECT_FULL_IP_LIST = { |
| 'ipv4_private': [], |
| 'ipv4_public': ['100.127.110.79'], |
| 'ipv6_link_local': ['fe80::c66d:3c75:2cec:1d72'], |
| 'ipv6_private_local': [], |
| 'ipv6_public': [ |
| '2401:fa00:480:7a00:8d4f:85ff:cc5c:787e', |
| '2401:fa00:480:7a00:459:b993:fcbf:1419' |
| ] |
| } |
| |
| CORRECT_EMPTY_IP_LIST = { |
| 'ipv4_private': [], |
| 'ipv4_public': [], |
| 'ipv6_link_local': [], |
| 'ipv6_private_local': [], |
| 'ipv6_public': [] |
| } |
| |
| FUCHSIA_INIT_SERVER = ('acts.controllers.fuchsia_device.FuchsiaDevice.' |
| 'init_server_connection') |
| FUCHSIA_INIT_FFX = ('acts.controllers.fuchsia_device.FuchsiaDevice.' |
| 'init_ffx_connection') |
| FUCHSIA_SET_CONTROL_PATH_CONFIG = ('acts.controllers.fuchsia_device.' |
| 'FuchsiaDevice._set_control_path_config') |
| FUCHSIA_START_SERVICES = ('acts.controllers.fuchsia_device.FuchsiaDevice.' |
| 'start_services') |
| FUCHSIA_NETSTACK_LIST_INTERFACES = ( |
| 'acts.controllers.' |
| 'fuchsia_lib.netstack.netstack_lib.' |
| 'FuchsiaNetstackLib.netstackListInterfaces') |
| FUCHSIA_INIT_NETSTACK = ('acts.controllers.fuchsia_lib.netstack.' |
| 'netstack_lib.FuchsiaNetstackLib.init') |
| |
| |
| class ByPassSetupWizardTests(unittest.TestCase): |
| """This test class for unit testing acts.utils.bypass_setup_wizard.""" |
| |
| def test_start_standing_subproc(self): |
| with self.assertRaisesRegex(utils.ActsUtilsError, |
| 'Process .* has terminated'): |
| utils.start_standing_subprocess('sleep 0', check_health_delay=0.1) |
| |
| def test_stop_standing_subproc(self): |
| p = utils.start_standing_subprocess('sleep 0') |
| time.sleep(0.1) |
| with self.assertRaisesRegex(utils.ActsUtilsError, |
| 'Process .* has terminated'): |
| utils.stop_standing_subprocess(p) |
| |
| @mock.patch('time.sleep') |
| def test_bypass_setup_wizard_no_complications(self, _): |
| ad = mock.Mock() |
| ad.adb.shell.side_effect = [ |
| # Return value for SetupWizardExitActivity |
| BypassSetupWizardReturn.NO_COMPLICATIONS, |
| # Return value for device_provisioned |
| PROVISIONED_STATE_GOOD, |
| ] |
| ad.adb.return_state = BypassSetupWizardReturn.NO_COMPLICATIONS |
| self.assertTrue(utils.bypass_setup_wizard(ad)) |
| self.assertFalse( |
| ad.adb.root_adb.called, |
| 'The root command should not be called if there are no ' |
| 'complications.') |
| |
| @mock.patch('time.sleep') |
| def test_bypass_setup_wizard_unrecognized_error(self, _): |
| ad = mock.Mock() |
| ad.adb.shell.side_effect = [ |
| # Return value for SetupWizardExitActivity |
| BypassSetupWizardReturn.UNRECOGNIZED_ERR, |
| # Return value for device_provisioned |
| PROVISIONED_STATE_GOOD, |
| ] |
| with self.assertRaises(AdbError): |
| utils.bypass_setup_wizard(ad) |
| self.assertFalse( |
| ad.adb.root_adb.called, |
| 'The root command should not be called if we do not have a ' |
| 'codepath for recovering from the failure.') |
| |
| @mock.patch('time.sleep') |
| def test_bypass_setup_wizard_need_root_access(self, _): |
| ad = mock.Mock() |
| ad.adb.shell.side_effect = [ |
| # Return value for SetupWizardExitActivity |
| BypassSetupWizardReturn.ROOT_ADB_NO_COMP, |
| # Return value for rooting the device |
| BypassSetupWizardReturn.NO_COMPLICATIONS, |
| # Return value for device_provisioned |
| PROVISIONED_STATE_GOOD |
| ] |
| |
| utils.bypass_setup_wizard(ad) |
| |
| self.assertTrue( |
| ad.adb.root_adb_called, |
| 'The command required root access, but the device was never ' |
| 'rooted.') |
| |
| @mock.patch('time.sleep') |
| def test_bypass_setup_wizard_need_root_already_skipped(self, _): |
| ad = mock.Mock() |
| ad.adb.shell.side_effect = [ |
| # Return value for SetupWizardExitActivity |
| BypassSetupWizardReturn.ROOT_ADB_SKIPPED, |
| # Return value for SetupWizardExitActivity after root |
| BypassSetupWizardReturn.ALREADY_BYPASSED, |
| # Return value for device_provisioned |
| PROVISIONED_STATE_GOOD |
| ] |
| self.assertTrue(utils.bypass_setup_wizard(ad)) |
| self.assertTrue(ad.adb.root_adb_called) |
| |
| @mock.patch('time.sleep') |
| def test_bypass_setup_wizard_root_access_still_fails(self, _): |
| ad = mock.Mock() |
| ad.adb.shell.side_effect = [ |
| # Return value for SetupWizardExitActivity |
| BypassSetupWizardReturn.ROOT_ADB_FAILS, |
| # Return value for SetupWizardExitActivity after root |
| BypassSetupWizardReturn.UNRECOGNIZED_ERR, |
| # Return value for device_provisioned |
| PROVISIONED_STATE_GOOD |
| ] |
| |
| with self.assertRaises(AdbError): |
| utils.bypass_setup_wizard(ad) |
| self.assertTrue(ad.adb.root_adb_called) |
| |
| |
| class BypassSetupWizardReturn: |
| # No complications. Bypass works the first time without issues. |
| NO_COMPLICATIONS = ( |
| 'Starting: Intent { cmp=com.google.android.setupwizard/' |
| '.SetupWizardExitActivity }') |
| |
| # Fail with doesn't need to be skipped/was skipped already. |
| ALREADY_BYPASSED = AdbError('', 'ADB_CMD_OUTPUT:0', 'Error type 3\n' |
| 'Error: Activity class', 1) |
| # Fail with different error. |
| UNRECOGNIZED_ERR = AdbError('', 'ADB_CMD_OUTPUT:0', 'Error type 4\n' |
| 'Error: Activity class', 0) |
| # Fail, get root access, then no complications arise. |
| ROOT_ADB_NO_COMP = AdbError( |
| '', 'ADB_CMD_OUTPUT:255', 'Security exception: Permission Denial: ' |
| 'starting Intent { flg=0x10000000 ' |
| 'cmp=com.google.android.setupwizard/' |
| '.SetupWizardExitActivity } from null ' |
| '(pid=5045, uid=2000) not exported from uid ' |
| '10000', 0) |
| # Even with root access, the bypass setup wizard doesn't need to be skipped. |
| ROOT_ADB_SKIPPED = AdbError( |
| '', 'ADB_CMD_OUTPUT:255', 'Security exception: Permission Denial: ' |
| 'starting Intent { flg=0x10000000 ' |
| 'cmp=com.google.android.setupwizard/' |
| '.SetupWizardExitActivity } from null ' |
| '(pid=5045, uid=2000) not exported from ' |
| 'uid 10000', 0) |
| # Even with root access, the bypass setup wizard fails |
| ROOT_ADB_FAILS = AdbError( |
| '', 'ADB_CMD_OUTPUT:255', |
| 'Security exception: Permission Denial: starting Intent { ' |
| 'flg=0x10000000 cmp=com.google.android.setupwizard/' |
| '.SetupWizardExitActivity } from null (pid=5045, uid=2000) not ' |
| 'exported from uid 10000', 0) |
| |
| |
| class ConcurrentActionsTest(unittest.TestCase): |
| """Tests acts.utils.run_concurrent_actions and related functions.""" |
| |
| @staticmethod |
| def function_returns_passed_in_arg(arg): |
| return arg |
| |
| @staticmethod |
| def function_raises_passed_in_exception_type(exception_type): |
| raise exception_type |
| |
| def test_run_concurrent_actions_no_raise_returns_proper_return_values( |
| self): |
| """Tests run_concurrent_actions_no_raise returns in the correct order. |
| |
| Each function passed into run_concurrent_actions_no_raise returns the |
| values returned from each individual callable in the order passed in. |
| """ |
| ret_values = utils.run_concurrent_actions_no_raise( |
| lambda: self.function_returns_passed_in_arg( |
| 'ARG1'), lambda: self.function_returns_passed_in_arg('ARG2'), |
| lambda: self.function_returns_passed_in_arg('ARG3')) |
| |
| self.assertEqual(len(ret_values), 3) |
| self.assertEqual(ret_values[0], 'ARG1') |
| self.assertEqual(ret_values[1], 'ARG2') |
| self.assertEqual(ret_values[2], 'ARG3') |
| |
| def test_run_concurrent_actions_no_raise_returns_raised_exceptions(self): |
| """Tests run_concurrent_actions_no_raise returns raised exceptions. |
| |
| Instead of allowing raised exceptions to be raised in the main thread, |
| this function should capture the exception and return them in the slot |
| the return value should have been returned in. |
| """ |
| ret_values = utils.run_concurrent_actions_no_raise( |
| lambda: self.function_raises_passed_in_exception_type(IndexError), |
| lambda: self.function_raises_passed_in_exception_type(KeyError)) |
| |
| self.assertEqual(len(ret_values), 2) |
| self.assertEqual(ret_values[0].__class__, IndexError) |
| self.assertEqual(ret_values[1].__class__, KeyError) |
| |
| def test_run_concurrent_actions_returns_proper_return_values(self): |
| """Tests run_concurrent_actions returns in the correct order. |
| |
| Each function passed into run_concurrent_actions returns the values |
| returned from each individual callable in the order passed in. |
| """ |
| |
| ret_values = utils.run_concurrent_actions( |
| lambda: self.function_returns_passed_in_arg( |
| 'ARG1'), lambda: self.function_returns_passed_in_arg('ARG2'), |
| lambda: self.function_returns_passed_in_arg('ARG3')) |
| |
| self.assertEqual(len(ret_values), 3) |
| self.assertEqual(ret_values[0], 'ARG1') |
| self.assertEqual(ret_values[1], 'ARG2') |
| self.assertEqual(ret_values[2], 'ARG3') |
| |
| def test_run_concurrent_actions_raises_exceptions(self): |
| """Tests run_concurrent_actions raises exceptions from given actions.""" |
| with self.assertRaises(KeyError): |
| utils.run_concurrent_actions( |
| lambda: self.function_returns_passed_in_arg('ARG1'), lambda: |
| self.function_raises_passed_in_exception_type(KeyError)) |
| |
| def test_test_concurrent_actions_raises_non_test_failure(self): |
| """Tests test_concurrent_actions raises the given exception.""" |
| with self.assertRaises(KeyError): |
| utils.test_concurrent_actions( |
| lambda: self.function_raises_passed_in_exception_type(KeyError |
| ), |
| failure_exceptions=signals.TestFailure) |
| |
| def test_test_concurrent_actions_raises_test_failure(self): |
| """Tests test_concurrent_actions raises the given exception.""" |
| with self.assertRaises(signals.TestFailure): |
| utils.test_concurrent_actions( |
| lambda: self.function_raises_passed_in_exception_type(KeyError |
| ), |
| failure_exceptions=KeyError) |
| |
| |
| class SuppressLogOutputTest(unittest.TestCase): |
| """Tests SuppressLogOutput""" |
| |
| def test_suppress_log_output(self): |
| """Tests that the SuppressLogOutput context manager removes handlers |
| of the specified levels upon entry and re-adds handlers upon exit. |
| """ |
| handlers = [ |
| logging.NullHandler(level=lvl) |
| for lvl in (logging.DEBUG, logging.INFO, logging.ERROR) |
| ] |
| log = logging.getLogger('test_log') |
| for handler in handlers: |
| log.addHandler(handler) |
| with utils.SuppressLogOutput(log, [logging.INFO, logging.ERROR]): |
| self.assertTrue( |
| any(handler.level == logging.DEBUG |
| for handler in log.handlers)) |
| self.assertFalse( |
| any(handler.level in (logging.INFO, logging.ERROR) |
| for handler in log.handlers)) |
| self.assertCountEqual(handlers, log.handlers) |
| |
| |
| class IpAddressUtilTest(unittest.TestCase): |
| def test_positive_ipv4_normal_address(self): |
| ip_address = "192.168.1.123" |
| self.assertTrue(utils.is_valid_ipv4_address(ip_address)) |
| |
| def test_positive_ipv4_any_address(self): |
| ip_address = "0.0.0.0" |
| self.assertTrue(utils.is_valid_ipv4_address(ip_address)) |
| |
| def test_positive_ipv4_broadcast(self): |
| ip_address = "255.255.255.0" |
| self.assertTrue(utils.is_valid_ipv4_address(ip_address)) |
| |
| def test_negative_ipv4_with_ipv6_address(self): |
| ip_address = "fe80::f693:9fff:fef4:1ac" |
| self.assertFalse(utils.is_valid_ipv4_address(ip_address)) |
| |
| def test_negative_ipv4_with_invalid_string(self): |
| ip_address = "fdsafdsafdsafdsf" |
| self.assertFalse(utils.is_valid_ipv4_address(ip_address)) |
| |
| def test_negative_ipv4_with_invalid_number(self): |
| ip_address = "192.168.500.123" |
| self.assertFalse(utils.is_valid_ipv4_address(ip_address)) |
| |
| def test_positive_ipv6(self): |
| ip_address = 'fe80::f693:9fff:fef4:1ac' |
| self.assertTrue(utils.is_valid_ipv6_address(ip_address)) |
| |
| def test_positive_ipv6_link_local(self): |
| ip_address = 'fe80::' |
| self.assertTrue(utils.is_valid_ipv6_address(ip_address)) |
| |
| def test_negative_ipv6_with_ipv4_address(self): |
| ip_address = '192.168.1.123' |
| self.assertFalse(utils.is_valid_ipv6_address(ip_address)) |
| |
| def test_negative_ipv6_invalid_characters(self): |
| ip_address = 'fe80:jkyr:f693:9fff:fef4:1ac' |
| self.assertFalse(utils.is_valid_ipv6_address(ip_address)) |
| |
| def test_negative_ipv6_invalid_string(self): |
| ip_address = 'fdsafdsafdsafdsf' |
| self.assertFalse(utils.is_valid_ipv6_address(ip_address)) |
| |
| @mock.patch('acts.libs.proc.job.run') |
| def test_local_get_interface_ip_addresses_full(self, job_mock): |
| job_mock.side_effect = [ |
| job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'), |
| encoding='utf-8'), |
| job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'), |
| encoding='utf-8') |
| ] |
| self.assertEqual(utils.get_interface_ip_addresses(job, 'eno1'), |
| CORRECT_FULL_IP_LIST) |
| |
| @mock.patch('acts.libs.proc.job.run') |
| def test_local_get_interface_ip_addresses_empty(self, job_mock): |
| job_mock.side_effect = [ |
| job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'), |
| encoding='utf-8'), |
| job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'), |
| encoding='utf-8') |
| ] |
| self.assertEqual(utils.get_interface_ip_addresses(job, 'wlan1'), |
| CORRECT_EMPTY_IP_LIST) |
| |
| @mock.patch('acts.controllers.utils_lib.ssh.connection.SshConnection.run') |
| def test_ssh_get_interface_ip_addresses_full(self, ssh_mock): |
| ssh_mock.side_effect = [ |
| job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'), |
| encoding='utf-8'), |
| job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'), |
| encoding='utf-8') |
| ] |
| self.assertEqual( |
| utils.get_interface_ip_addresses(SshConnection('mock_settings'), |
| 'eno1'), CORRECT_FULL_IP_LIST) |
| |
| @mock.patch('acts.controllers.utils_lib.ssh.connection.SshConnection.run') |
| def test_ssh_get_interface_ip_addresses_empty(self, ssh_mock): |
| ssh_mock.side_effect = [ |
| job.Result(stdout=bytes(MOCK_IP_ADDRESSES, 'utf-8'), |
| encoding='utf-8'), |
| job.Result(stdout=bytes(MOCK_IFCONFIG_OUTPUT, 'utf-8'), |
| encoding='utf-8') |
| ] |
| self.assertEqual( |
| utils.get_interface_ip_addresses(SshConnection('mock_settings'), |
| 'wlan1'), CORRECT_EMPTY_IP_LIST) |
| |
| @mock.patch('acts.controllers.adb.AdbProxy') |
| @mock.patch.object(AndroidDevice, 'is_bootloader', return_value=True) |
| def test_android_get_interface_ip_addresses_full(self, is_bootloader, |
| adb_mock): |
| adb_mock().shell.side_effect = [ |
| MOCK_IP_ADDRESSES, MOCK_IFCONFIG_OUTPUT |
| ] |
| self.assertEqual( |
| utils.get_interface_ip_addresses(AndroidDevice(), 'eno1'), |
| CORRECT_FULL_IP_LIST) |
| |
| @mock.patch('acts.controllers.adb.AdbProxy') |
| @mock.patch.object(AndroidDevice, 'is_bootloader', return_value=True) |
| def test_android_get_interface_ip_addresses_empty(self, is_bootloader, |
| adb_mock): |
| adb_mock().shell.side_effect = [ |
| MOCK_IP_ADDRESSES, MOCK_IFCONFIG_OUTPUT |
| ] |
| self.assertEqual( |
| utils.get_interface_ip_addresses(AndroidDevice(), 'wlan1'), |
| CORRECT_EMPTY_IP_LIST) |
| |
| @mock.patch(FUCHSIA_INIT_SERVER) |
| @mock.patch(FUCHSIA_INIT_FFX) |
| @mock.patch(FUCHSIA_SET_CONTROL_PATH_CONFIG) |
| @mock.patch(FUCHSIA_START_SERVICES) |
| @mock.patch(FUCHSIA_NETSTACK_LIST_INTERFACES) |
| @mock.patch(FUCHSIA_INIT_NETSTACK) |
| def test_fuchsia_get_interface_ip_addresses_full( |
| self, init_mock, list_interfaces_mock, start_services_mock, |
| control_path_mock, ffx_mock, fuchsia_device_mock): |
| # Will never actually be created/used. |
| logging.log_path = '/tmp/unit_test_garbage' |
| |
| init_mock.return_value = None |
| list_interfaces_mock.return_value = FUCHSIA_INTERFACES |
| fuchsia_device_mock.return_value = None |
| self.assertEqual( |
| utils.get_interface_ip_addresses( |
| FuchsiaDevice({'ip': '192.168.1.1'}), 'eno1'), |
| CORRECT_FULL_IP_LIST) |
| |
| @mock.patch(FUCHSIA_INIT_SERVER) |
| @mock.patch(FUCHSIA_INIT_FFX) |
| @mock.patch(FUCHSIA_SET_CONTROL_PATH_CONFIG) |
| @mock.patch(FUCHSIA_START_SERVICES) |
| @mock.patch(FUCHSIA_NETSTACK_LIST_INTERFACES) |
| @mock.patch(FUCHSIA_INIT_NETSTACK) |
| def test_fuchsia_get_interface_ip_addresses_empty( |
| self, init_mock, list_interfaces_mock, start_services_mock, |
| control_path_mock, ffx_mock, fuchsia_device_mock): |
| # Will never actually be created/used. |
| logging.log_path = '/tmp/unit_test_garbage' |
| |
| init_mock.return_value = None |
| list_interfaces_mock.return_value = FUCHSIA_INTERFACES |
| fuchsia_device_mock.return_value = None |
| self.assertEqual( |
| utils.get_interface_ip_addresses( |
| FuchsiaDevice({'ip': '192.168.1.1'}), 'wlan1'), |
| CORRECT_EMPTY_IP_LIST) |
| |
| |
| class GetDeviceTest(unittest.TestCase): |
| class TestDevice: |
| def __init__(self, id, device_type=None) -> None: |
| self.id = id |
| if device_type: |
| self.device_type = device_type |
| |
| def test_get_device_none(self): |
| devices = [] |
| self.assertRaises(ValueError, utils.get_device, devices, 'DUT') |
| |
| def test_get_device_default_one(self): |
| devices = [self.TestDevice(0)] |
| self.assertEqual(utils.get_device(devices, 'DUT').id, 0) |
| |
| def test_get_device_default_many(self): |
| devices = [self.TestDevice(0), self.TestDevice(1)] |
| self.assertEqual(utils.get_device(devices, 'DUT').id, 0) |
| |
| def test_get_device_specified_one(self): |
| devices = [self.TestDevice(0), self.TestDevice(1, 'DUT')] |
| self.assertEqual(utils.get_device(devices, 'DUT').id, 1) |
| |
| def test_get_device_specified_many(self): |
| devices = [self.TestDevice(0, 'DUT'), self.TestDevice(1, 'DUT')] |
| self.assertRaises(ValueError, utils.get_device, devices, 'DUT') |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |