1from __future__ import absolute_import, print_function 2 3import sys 4from random import randint, seed 5 6import mozdevice 7import pytest 8from unittest.mock import patch 9from six import StringIO 10 11# set up required module-level variables/objects 12seed(1488590) 13 14 15def random_tcp_port(): 16 """Returns a pseudo-random integer generated from a seed. 17 18 :returns: int: pseudo-randomly generated integer 19 """ 20 return randint(8000, 12000) 21 22 23@pytest.fixture(autouse=True) 24def mock_command_output(monkeypatch): 25 """Monkeypatches the ADBDevice.command_output() method call. 26 27 Instead of calling the concrete method implemented in adb.py::ADBDevice, 28 this method simply returns a string representation of the command that was 29 received. 30 31 As an exception, if the command begins with "forward tcp:0 ", this method 32 returns a mock port number. 33 34 :param object monkeypatch: pytest provided fixture for mocking. 35 """ 36 37 def command_output_wrapper(object, cmd, timeout): 38 """Actual monkeypatch implementation of the command_output method call. 39 40 :param object object: placeholder object representing ADBDevice 41 :param str cmd: command to be executed 42 :param timeout: unused parameter to represent timeout threshold 43 :returns: string - string representation of command to be executed 44 int - mock port number (only used when cmd begins with "forward tcp:0 ") 45 """ 46 47 if cmd[0] == "forward" and cmd[1] == "tcp:0": 48 return 7777 49 50 print(str(cmd)) 51 return str(cmd) 52 53 monkeypatch.setattr(mozdevice.ADBDevice, "command_output", command_output_wrapper) 54 55 56@pytest.fixture(autouse=True) 57def mock_shell_output(monkeypatch): 58 """Monkeypatches the ADBDevice.shell_output() method call. 59 60 Instead of returning the output of an adb call, this method will 61 return appropriate string output. Content of the string output is 62 in line with the calling method's expectations. 63 64 :param object monkeypatch: pytest provided fixture for mocking. 65 """ 66 67 def shell_output_wrapper( 68 object, cmd, env=None, cwd=None, timeout=None, enable_run_as=False 69 ): 70 """Actual monkeypatch implementation of the shell_output method call. 71 72 :param object object: placeholder object representing ADBDevice 73 :param str cmd: command to be executed 74 :param env: contains the environment variable 75 :type env: dict or None 76 :param cwd: The directory from which to execute. 77 :type cwd: str or None 78 :param timeout: unused parameter tp represent timeout threshold 79 :param enable_run_as: bool determining if run_as <app> is to be used 80 :returns: string - string representation of a simulated call to adb 81 """ 82 if "pm list package error" in cmd: 83 return "Error: Could not access the Package Manager" 84 elif "pm list package none" in cmd: 85 return "" 86 elif "pm list package" in cmd: 87 apps = ["org.mozilla.fennec", "org.mozilla.geckoview_example"] 88 return ("package:{}\n" * len(apps)).format(*apps) 89 else: 90 print(str(cmd)) 91 return str(cmd) 92 93 monkeypatch.setattr(mozdevice.ADBDevice, "shell_output", shell_output_wrapper) 94 95 96@pytest.fixture(autouse=True) 97def mock_is_path_internal_storage(monkeypatch): 98 """Monkeypatches the ADBDevice.is_path_internal_storage() method call. 99 100 Instead of returning the outcome of whether the path provided is 101 internal storage or external, this will always return True. 102 103 :param object monkeypatch: pytest provided fixture for mocking. 104 """ 105 106 def is_path_internal_storage_wrapper(object, path, timeout=None): 107 """Actual monkeypatch implementation of the is_path_internal_storage() call. 108 109 :param str path: The path to test. 110 :param timeout: The maximum time in 111 seconds for any spawned adb process to complete before 112 throwing an ADBTimeoutError. This timeout is per adb call. The 113 total time spent may exceed this value. If it is not 114 specified, the value set in the ADBDevice constructor is used. 115 :returns: boolean 116 117 :raises: * ADBTimeoutError 118 * ADBError 119 """ 120 if "internal_storage" in path: 121 return True 122 return False 123 124 monkeypatch.setattr( 125 mozdevice.ADBDevice, 126 "is_path_internal_storage", 127 is_path_internal_storage_wrapper, 128 ) 129 130 131@pytest.fixture(autouse=True) 132def mock_enable_run_as_for_path(monkeypatch): 133 """Monkeypatches the ADBDevice.enable_run_as_for_path(path) method. 134 135 Always return True 136 137 :param object monkeypatch: pytest provided fixture for mocking. 138 """ 139 140 def enable_run_as_for_path_wrapper(object, path): 141 """Actual monkeypatch implementation of the enable_run_as_for_path() call. 142 143 :param str path: The path to test. 144 :returns: boolean 145 """ 146 return True 147 148 monkeypatch.setattr( 149 mozdevice.ADBDevice, "enable_run_as_for_path", enable_run_as_for_path_wrapper 150 ) 151 152 153@pytest.fixture(autouse=True) 154def mock_shell_bool(monkeypatch): 155 """Monkeypatches the ADBDevice.shell_bool() method call. 156 157 Instead of returning the output of an adb call, this method will 158 return appropriate string output. Content of the string output is 159 in line with the calling method's expectations. 160 161 :param object monkeypatch: pytest provided fixture for mocking. 162 """ 163 164 def shell_bool_wrapper( 165 object, cmd, env=None, cwd=None, timeout=None, enable_run_as=False 166 ): 167 """Actual monkeypatch implementation of the shell_bool method call. 168 169 :param object object: placeholder object representing ADBDevice 170 :param str cmd: command to be executed 171 :param env: contains the environment variable 172 :type env: dict or None 173 :param cwd: The directory from which to execute. 174 :type cwd: str or None 175 :param timeout: unused parameter tp represent timeout threshold 176 :param enable_run_as: bool determining if run_as <app> is to be used 177 :returns: string - string representation of a simulated call to adb 178 """ 179 print(cmd) 180 return str(cmd) 181 182 monkeypatch.setattr(mozdevice.ADBDevice, "shell_bool", shell_bool_wrapper) 183 184 185@pytest.fixture(autouse=True) 186def mock_adb_object(): 187 """Patches the __init__ method call when instantiating ADBDevice. 188 189 ADBDevice normally requires instantiated objects in order to execute 190 its commands. 191 192 With a pytest-mock patch, we are able to mock the initialization of 193 the ADBDevice object. By yielding the instantiated mock object, 194 unit tests can be run that call methods that require an instantiated 195 object. 196 197 :yields: ADBDevice - mock instance of ADBDevice object 198 """ 199 with patch.object(mozdevice.ADBDevice, "__init__", lambda self: None): 200 yield mozdevice.ADBDevice() 201 202 203@pytest.fixture 204def redirect_stdout_and_assert(): 205 """Redirects the stdout pipe temporarily to a StringIO stream. 206 207 This is useful to assert on methods that do not return 208 a value, such as most ADBDevice methods. 209 210 The original stdout pipe is preserved throughout the process. 211 212 :returns: _wrapper method 213 """ 214 215 def _wrapper(func, **kwargs): 216 """Implements the stdout sleight-of-hand. 217 218 After preserving the original sys.stdout, it is switched 219 to use cStringIO.StringIO. 220 221 Method with no return value is called, and the stdout 222 pipe is switched back to the original sys.stdout. 223 224 The expected outcome is received as part of the kwargs. 225 This is asserted against a sanitized output from the method 226 under test. 227 228 :param object func: method under test 229 :param dict kwargs: dictionary of function parameters 230 """ 231 original_stdout = sys.stdout 232 sys.stdout = testing_stdout = StringIO() 233 expected_text = kwargs.pop("text") 234 func(**kwargs) 235 sys.stdout = original_stdout 236 assert expected_text in testing_stdout.getvalue().rstrip() 237 238 return _wrapper 239