summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozdevice/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/mozbase/mozdevice/tests
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--testing/mozbase/mozdevice/tests/conftest.py236
-rw-r--r--testing/mozbase/mozdevice/tests/manifest.ini6
-rw-r--r--testing/mozbase/mozdevice/tests/test_chown.py67
-rw-r--r--testing/mozbase/mozdevice/tests/test_escape_command_line.py21
-rw-r--r--testing/mozbase/mozdevice/tests/test_is_app_installed.py38
-rw-r--r--testing/mozbase/mozdevice/tests/test_socket_connection.py124
6 files changed, 492 insertions, 0 deletions
diff --git a/testing/mozbase/mozdevice/tests/conftest.py b/testing/mozbase/mozdevice/tests/conftest.py
new file mode 100644
index 0000000000..831090a428
--- /dev/null
+++ b/testing/mozbase/mozdevice/tests/conftest.py
@@ -0,0 +1,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
diff --git a/testing/mozbase/mozdevice/tests/manifest.ini b/testing/mozbase/mozdevice/tests/manifest.ini
new file mode 100644
index 0000000000..208fd06608
--- /dev/null
+++ b/testing/mozbase/mozdevice/tests/manifest.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+subsuite = mozbase
+[test_socket_connection.py]
+[test_is_app_installed.py]
+[test_chown.py]
+[test_escape_command_line.py]
diff --git a/testing/mozbase/mozdevice/tests/test_chown.py b/testing/mozbase/mozdevice/tests/test_chown.py
new file mode 100644
index 0000000000..1bbfcc5d8e
--- /dev/null
+++ b/testing/mozbase/mozdevice/tests/test_chown.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+import logging
+from unittest.mock import patch
+
+import mozunit
+import pytest
+
+
+@pytest.mark.parametrize("boolean_value", [True, False])
+def test_set_chown_r_attribute(
+ mock_adb_object, redirect_stdout_and_assert, boolean_value
+):
+ mock_adb_object._chown_R = boolean_value
+ assert mock_adb_object._chown_R == boolean_value
+
+
+def test_chown_path_internal(mock_adb_object, redirect_stdout_and_assert):
+ """Tests whether attempt to chown internal path is ignored"""
+ with patch.object(logging, "getLogger") as mock_log:
+ mock_adb_object._logger = mock_log
+
+ testing_parameters = {
+ "owner": "someuser",
+ "path": "internal_storage",
+ }
+ expected = "Ignoring attempt to chown external storage"
+ mock_adb_object.chown(**testing_parameters)
+ assert "".join(mock_adb_object._logger.method_calls[0][1]) != ""
+ assert "".join(mock_adb_object._logger.method_calls[0][1]) == expected
+
+
+def test_chown_one_path(mock_adb_object, redirect_stdout_and_assert):
+ """Tests the path where only one path is provided."""
+ # set up mock logging and self._chown_R attribute.
+ with patch.object(logging, "getLogger") as mock_log:
+ mock_adb_object._logger = mock_log
+ mock_adb_object._chown_R = True
+
+ testing_parameters = {
+ "owner": "someuser",
+ "path": "/system",
+ }
+ command = "chown {owner} {path}".format(**testing_parameters)
+ testing_parameters["text"] = command
+ redirect_stdout_and_assert(mock_adb_object.chown, **testing_parameters)
+
+
+def test_chown_one_path_with_group(mock_adb_object, redirect_stdout_and_assert):
+ """Tests the path where group is provided."""
+ # set up mock logging and self._chown_R attribute.
+ with patch.object(logging, "getLogger") as mock_log:
+ mock_adb_object._logger = mock_log
+ mock_adb_object._chown_R = True
+
+ testing_parameters = {
+ "owner": "someuser",
+ "path": "/system",
+ "group": "group_2",
+ }
+ command = "chown {owner}.{group} {path}".format(**testing_parameters)
+ testing_parameters["text"] = command
+ redirect_stdout_and_assert(mock_adb_object.chown, **testing_parameters)
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/testing/mozbase/mozdevice/tests/test_escape_command_line.py b/testing/mozbase/mozdevice/tests/test_escape_command_line.py
new file mode 100644
index 0000000000..112dd936c5
--- /dev/null
+++ b/testing/mozbase/mozdevice/tests/test_escape_command_line.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+import mozunit
+
+
+def test_escape_command_line(mock_adb_object, redirect_stdout_and_assert):
+ """Test _escape_command_line."""
+ cases = {
+ # expected output : test input
+ "adb shell ls -l": ["adb", "shell", "ls", "-l"],
+ "adb shell 'ls -l'": ["adb", "shell", "ls -l"],
+ "-e 'if (true)'": ["-e", "if (true)"],
+ "-e 'if (x === \"hello\")'": ["-e", 'if (x === "hello")'],
+ "-e 'if (x === '\"'\"'hello'\"'\"')'": ["-e", "if (x === 'hello')"],
+ }
+ for expected, input in cases.items():
+ assert mock_adb_object._escape_command_line(input) == expected
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/testing/mozbase/mozdevice/tests/test_is_app_installed.py b/testing/mozbase/mozdevice/tests/test_is_app_installed.py
new file mode 100644
index 0000000000..a51836bc02
--- /dev/null
+++ b/testing/mozbase/mozdevice/tests/test_is_app_installed.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+import mozunit
+import pytest
+from mozdevice import ADBError
+
+
+def test_is_app_installed(mock_adb_object):
+ """Tests that is_app_installed returns True if app is installed."""
+ assert mock_adb_object.is_app_installed("org.mozilla.geckoview_example")
+
+
+def test_is_app_installed_not_installed(mock_adb_object):
+ """Tests that is_app_installed returns False if provided app_name
+ does not resolve."""
+ assert not mock_adb_object.is_app_installed("some_random_name")
+
+
+def test_is_app_installed_partial_name(mock_adb_object):
+ """Tests that is_app_installed returns False if provided app_name
+ is only a partial match."""
+ assert not mock_adb_object.is_app_installed("fennec")
+
+
+def test_is_app_installed_package_manager_error(mock_adb_object):
+ """Tests that is_app_installed is able to raise an exception."""
+ with pytest.raises(ADBError):
+ mock_adb_object.is_app_installed("error")
+
+
+def test_is_app_installed_no_installed_package_found(mock_adb_object):
+ """Tests that is_app_installed is able to handle scenario
+ where no installed packages are found."""
+ assert not mock_adb_object.is_app_installed("none")
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/testing/mozbase/mozdevice/tests/test_socket_connection.py b/testing/mozbase/mozdevice/tests/test_socket_connection.py
new file mode 100644
index 0000000000..1182737546
--- /dev/null
+++ b/testing/mozbase/mozdevice/tests/test_socket_connection.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+
+import mozunit
+import pytest
+from conftest import random_tcp_port
+
+
+@pytest.fixture(params=["tcp:{}".format(random_tcp_port()) for _ in range(5)])
+def select_test_port(request):
+ """Generate a list of ports to be used for testing."""
+ yield request.param
+
+
+def test_list_socket_connections_reverse(mock_adb_object):
+ assert [("['reverse',", "'--list']")] == mock_adb_object.list_socket_connections(
+ "reverse"
+ )
+
+
+def test_list_socket_connections_forward(mock_adb_object):
+ assert [("['forward',", "'--list']")] == mock_adb_object.list_socket_connections(
+ "forward"
+ )
+
+
+def test_create_socket_connection_reverse(
+ mock_adb_object, select_test_port, redirect_stdout_and_assert
+):
+ _expected = "['reverse', '{0}', '{0}']".format(select_test_port)
+ redirect_stdout_and_assert(
+ mock_adb_object.create_socket_connection,
+ direction="reverse",
+ local=select_test_port,
+ remote=select_test_port,
+ text=_expected,
+ )
+
+
+def test_create_socket_connection_forward(
+ mock_adb_object, select_test_port, redirect_stdout_and_assert
+):
+ _expected = "['forward', '{0}', '{0}']".format(select_test_port)
+ redirect_stdout_and_assert(
+ mock_adb_object.create_socket_connection,
+ direction="forward",
+ local=select_test_port,
+ remote=select_test_port,
+ text=_expected,
+ )
+
+
+def test_create_socket_connection_forward_adb_assigned_port(
+ mock_adb_object, select_test_port
+):
+ result = mock_adb_object.create_socket_connection(
+ direction="forward", local="tcp:0", remote=select_test_port
+ )
+ assert isinstance(result, int) and result == 7777
+
+
+def test_remove_socket_connections_reverse(mock_adb_object, redirect_stdout_and_assert):
+ _expected = "['reverse', '--remove-all']"
+ redirect_stdout_and_assert(
+ mock_adb_object.remove_socket_connections, direction="reverse", text=_expected
+ )
+
+
+def test_remove_socket_connections_forward(mock_adb_object, redirect_stdout_and_assert):
+ _expected = "['forward', '--remove-all']"
+ redirect_stdout_and_assert(
+ mock_adb_object.remove_socket_connections, direction="forward", text=_expected
+ )
+
+
+def test_legacy_forward(mock_adb_object, select_test_port, redirect_stdout_and_assert):
+ _expected = "['forward', '{0}', '{0}']".format(select_test_port)
+ redirect_stdout_and_assert(
+ mock_adb_object.forward,
+ local=select_test_port,
+ remote=select_test_port,
+ text=_expected,
+ )
+
+
+def test_legacy_forward_adb_assigned_port(mock_adb_object, select_test_port):
+ result = mock_adb_object.forward(local="tcp:0", remote=select_test_port)
+ assert isinstance(result, int) and result == 7777
+
+
+def test_legacy_reverse(mock_adb_object, select_test_port, redirect_stdout_and_assert):
+ _expected = "['reverse', '{0}', '{0}']".format(select_test_port)
+ redirect_stdout_and_assert(
+ mock_adb_object.reverse,
+ local=select_test_port,
+ remote=select_test_port,
+ text=_expected,
+ )
+
+
+def test_validate_port_invalid_prefix(mock_adb_object):
+ with pytest.raises(ValueError):
+ mock_adb_object._validate_port("{}".format("invalid"), is_local=True)
+
+
+@pytest.mark.xfail
+def test_validate_port_non_numerical_port_identifier(mock_adb_object):
+ with pytest.raises(AttributeError):
+ mock_adb_object._validate_port(
+ "{}".format("tcp:this:is:not:a:number"), is_local=True
+ )
+
+
+def test_validate_port_identifier_length_short(mock_adb_object):
+ with pytest.raises(ValueError):
+ mock_adb_object._validate_port("{}".format("tcp"), is_local=True)
+
+
+def test_validate_direction(mock_adb_object):
+ with pytest.raises(ValueError):
+ mock_adb_object._validate_direction("{}".format("bad direction"))
+
+
+if __name__ == "__main__":
+ mozunit.main()