summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozdevice/tests/conftest.py
blob: 831090a4280d71a3bf0ba242b260184c79baf952 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
import sys
from random import randint, seed
from unittest.mock import patch

import mozdevice
import pytest
from six import StringIO

# set up required module-level variables/objects
seed(1488590)


def random_tcp_port():
    """Returns a pseudo-random integer generated from a seed.

    :returns: int: pseudo-randomly generated integer
    """
    return randint(8000, 12000)


@pytest.fixture(autouse=True)
def mock_command_output(monkeypatch):
    """Monkeypatches the ADBDevice.command_output() method call.

    Instead of calling the concrete method implemented in adb.py::ADBDevice,
    this method simply returns a string representation of the command that was
    received.

    As an exception, if the command begins with "forward tcp:0 ", this method
    returns a mock port number.

    :param object monkeypatch: pytest provided fixture for mocking.
    """

    def command_output_wrapper(object, cmd, timeout):
        """Actual monkeypatch implementation of the command_output method call.

        :param object object: placeholder object representing ADBDevice
        :param str cmd: command to be executed
        :param timeout: unused parameter to represent timeout threshold
        :returns: string - string representation of command to be executed
                  int - mock port number (only used when cmd begins with "forward tcp:0 ")
        """

        if cmd[0] == "forward" and cmd[1] == "tcp:0":
            return 7777

        print(str(cmd))
        return str(cmd)

    monkeypatch.setattr(mozdevice.ADBDevice, "command_output", command_output_wrapper)


@pytest.fixture(autouse=True)
def mock_shell_output(monkeypatch):
    """Monkeypatches the ADBDevice.shell_output() method call.

    Instead of returning the output of an adb call, this method will
    return appropriate string output. Content of the string output is
    in line with the calling method's expectations.

    :param object monkeypatch: pytest provided fixture for mocking.
    """

    def shell_output_wrapper(
        object, cmd, env=None, cwd=None, timeout=None, enable_run_as=False
    ):
        """Actual monkeypatch implementation of the shell_output method call.

        :param object object: placeholder object representing ADBDevice
        :param str cmd: command to be executed
        :param env: contains the environment variable
        :type env: dict or None
        :param cwd: The directory from which to execute.
        :type cwd: str or None
        :param timeout: unused parameter tp represent timeout threshold
        :param enable_run_as: bool determining if run_as <app> is to be used
        :returns: string - string representation of a simulated call to adb
        """
        if "pm list package error" in cmd:
            return "Error: Could not access the Package Manager"
        elif "pm list package none" in cmd:
            return ""
        elif "pm list package" in cmd:
            apps = ["org.mozilla.fennec", "org.mozilla.geckoview_example"]
            return ("package:{}\n" * len(apps)).format(*apps)
        else:
            print(str(cmd))
            return str(cmd)

    monkeypatch.setattr(mozdevice.ADBDevice, "shell_output", shell_output_wrapper)


@pytest.fixture(autouse=True)
def mock_is_path_internal_storage(monkeypatch):
    """Monkeypatches the ADBDevice.is_path_internal_storage() method call.

    Instead of returning the outcome of whether the path provided is
    internal storage or external, this will always return True.

    :param object monkeypatch: pytest provided fixture for mocking.
    """

    def is_path_internal_storage_wrapper(object, path, timeout=None):
        """Actual monkeypatch implementation of the is_path_internal_storage() call.

        :param str path: The path to test.
        :param timeout: The maximum time in
            seconds for any spawned adb process to complete before
            throwing an ADBTimeoutError.  This timeout is per adb call. The
            total time spent may exceed this value. If it is not
            specified, the value set in the ADBDevice constructor is used.
        :returns: boolean

        :raises: * ADBTimeoutError
                 * ADBError
        """
        if "internal_storage" in path:
            return True
        return False

    monkeypatch.setattr(
        mozdevice.ADBDevice,
        "is_path_internal_storage",
        is_path_internal_storage_wrapper,
    )


@pytest.fixture(autouse=True)
def mock_enable_run_as_for_path(monkeypatch):
    """Monkeypatches the ADBDevice.enable_run_as_for_path(path) method.

    Always return True

    :param object monkeypatch: pytest provided fixture for mocking.
    """

    def enable_run_as_for_path_wrapper(object, path):
        """Actual monkeypatch implementation of the enable_run_as_for_path() call.

        :param str path: The path to test.
        :returns: boolean
        """
        return True

    monkeypatch.setattr(
        mozdevice.ADBDevice, "enable_run_as_for_path", enable_run_as_for_path_wrapper
    )


@pytest.fixture(autouse=True)
def mock_shell_bool(monkeypatch):
    """Monkeypatches the ADBDevice.shell_bool() method call.

    Instead of returning the output of an adb call, this method will
    return appropriate string output. Content of the string output is
    in line with the calling method's expectations.

    :param object monkeypatch: pytest provided fixture for mocking.
    """

    def shell_bool_wrapper(
        object, cmd, env=None, cwd=None, timeout=None, enable_run_as=False
    ):
        """Actual monkeypatch implementation of the shell_bool method call.

        :param object object: placeholder object representing ADBDevice
        :param str cmd: command to be executed
        :param env: contains the environment variable
        :type env: dict or None
        :param cwd: The directory from which to execute.
        :type cwd: str or None
        :param timeout: unused parameter tp represent timeout threshold
        :param enable_run_as: bool determining if run_as <app> is to be used
        :returns: string - string representation of a simulated call to adb
        """
        print(cmd)
        return str(cmd)

    monkeypatch.setattr(mozdevice.ADBDevice, "shell_bool", shell_bool_wrapper)


@pytest.fixture(autouse=True)
def mock_adb_object():
    """Patches the __init__ method call when instantiating ADBDevice.

    ADBDevice normally requires instantiated objects in order to execute
    its commands.

    With a pytest-mock patch, we are able to mock the initialization of
    the ADBDevice object. By yielding the instantiated mock object,
    unit tests can be run that call methods that require an instantiated
    object.

    :yields: ADBDevice - mock instance of ADBDevice object
    """
    with patch.object(mozdevice.ADBDevice, "__init__", lambda self: None):
        yield mozdevice.ADBDevice()


@pytest.fixture
def redirect_stdout_and_assert():
    """Redirects the stdout pipe temporarily to a StringIO stream.

    This is useful to assert on methods that do not return
    a value, such as most ADBDevice methods.

    The original stdout pipe is preserved throughout the process.

    :returns: _wrapper method
    """

    def _wrapper(func, **kwargs):
        """Implements the stdout sleight-of-hand.

        After preserving the original sys.stdout, it is switched
        to use cStringIO.StringIO.

        Method with no return value is called, and the stdout
        pipe is switched back to the original sys.stdout.

        The expected outcome is received as part of the kwargs.
        This is asserted against a sanitized output from the method
        under test.

        :param object func: method under test
        :param dict kwargs: dictionary of function parameters
        """
        original_stdout = sys.stdout
        sys.stdout = testing_stdout = StringIO()
        expected_text = kwargs.pop("text")
        func(**kwargs)
        sys.stdout = original_stdout
        assert expected_text in testing_stdout.getvalue().rstrip()

    return _wrapper