From 5469b7fb0783fb3fe460f69f4bcc6f0f92228433 Mon Sep 17 00:00:00 2001 From: Seamus Date: Mon, 23 Oct 2023 17:07:19 -0400 Subject: [PATCH 1/8] add docstrings & remove unused variables --- .vscode/settings.json | 10 ++++++++++ sadb.py | 24 ++++++++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0f52bf2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "cSpell.words": [ + "sadb", + "scrcpy", + "screencap", + "screenrecord", + "tcpip", + "wlan" + ] +} \ No newline at end of file diff --git a/sadb.py b/sadb.py index b075b22..bc65202 100644 --- a/sadb.py +++ b/sadb.py @@ -10,17 +10,20 @@ def split_get_devices(result): + """Split [adb devices] to gather each device's serial number""" lines = result.strip().split("\n")[1:] devices = [line.split()[0] for line in lines] return devices def get_devices(): + """Run [adb devices] to get all connected devices""" result = subprocess.run(["adb", "devices"], capture_output=True, text=True, check=False) return split_get_devices(result.stdout) def select_device(devices, allow_all=False): + """Allow the user to select a connected device or use the only device connected""" # If there are no devices found... if len(devices) == 0: print("No devices found") @@ -51,6 +54,7 @@ def select_device(devices, allow_all=False): def call_function_on_devices(selected_devices, func, *args): + """Run the command on the selected device(s)""" if isinstance(selected_devices, list): for device in selected_devices: func(device, *args) @@ -59,11 +63,13 @@ def call_function_on_devices(selected_devices, func, *args): def stop(device, package_name): + """Run [adb shell am force-stop com.package.name] to stop a process""" cmd = ["adb", "-s", device, "shell", "am", "force-stop", package_name] subprocess.run(cmd) def start(device, package_name): + """Start a package using adb shell monkey and the intent launcher""" cmd = ["adb", "-s", device, "shell", "monkey", "-p", package_name, "-c", "android.intent.category.LAUNCHER", "1"] with open("/dev/null", "w") as devnull: @@ -71,26 +77,31 @@ def start(device, package_name): def clear(device, package_name): + """Run [adb shell pm clear com.package.name] to clear storage""" cmd = ["adb", "-s", device, "shell", "pm", "clear", package_name] subprocess.run(cmd) def install(device, apk): + """Run [adb install your.apk] to install an APK""" cmd = ["adb", "-s", device, "install", apk] subprocess.run(cmd) def uninstall(device, package_name): + """Run [adb uninstall your.package.name] to uninstall a package""" cmd = ["adb", "-s", device, "uninstall", package_name] subprocess.run(cmd) def scrcpy(device): + """Run [scrcpy] to start screen copy""" cmd = ["scrcpy", "-s", device] subprocess.run(cmd) def get_ip(device): + """Run [adb shell ip addr show wlan0] to get the device's IP""" cmd = ["adb", "-s", device, "shell", "ip", "addr", "show", "wlan0"] result = subprocess.run(cmd, capture_output=True, text=True) lines = result.stdout.strip().split("\n") @@ -102,6 +113,7 @@ def get_ip(device): def screenshot(device, filename): + """Run [adb exec-out screencap -p] to capture a screenshot""" if not filename: filename = "screenshot.png" cmd = ["adb", "-s", device, "exec-out", "screencap", "-p"] @@ -112,6 +124,7 @@ def screenshot(device, filename): def record(device, filename): + """Run [adb shell screenrecord video.mp4] to perform a screen record""" if not filename: filename = "video.mp4" remote_path = "/data/local/tmp/screenrecord.mp4" @@ -139,6 +152,7 @@ def record(device, filename): def wifi(device): + """Start adb server in tcpip and connect to it via IP address""" ip_address = get_ip(device) if not ip_address: @@ -156,12 +170,14 @@ def wifi(device): def search(device, search_term): + """Search all packages for entered word""" cmd = ["adb", "-s", device, "shell", "pm", "list", "packages", "|", "grep", search_term] subprocess.run(cmd) def parse_args(): + """Parse all arguments with argparse""" parser = argparse.ArgumentParser( description="A wrapper for adb on multiple devices") subparsers = parser.add_subparsers(dest="command") @@ -193,7 +209,7 @@ def parse_args(): uninstall_parser.add_argument( "package_name", help="The name of the package to uninstall") - # Screencopy + # Screen Copy subparsers.add_parser("scrcpy", help="Start scrcpy on a device") # IP Address @@ -214,8 +230,7 @@ def parse_args(): help="The name of the file to save the screen recording as (default: video.mp4)") # WiFi - wifi_parser = subparsers.add_parser( - "wifi", help="Connect to a device via WiFi") + subparsers.add_parser("wifi", help="Connect to a device via WiFi") # Search search_parser = subparsers.add_parser( @@ -223,7 +238,7 @@ def parse_args(): search_parser.add_argument( "search_term", help="The name of the package to search for") - # R (Unwrapped) + # R (Raw) r_parser = subparsers.add_parser("r", help="Run an adb command") r_parser.add_argument( "args", help="Any argument you would normally pass through adb", nargs=argparse.REMAINDER) @@ -238,6 +253,7 @@ def parse_args(): def main(): + """Main function""" try: args = parse_args() devices = get_devices() From a4d77b7d07b132fa7e52e6b6157f610317bd0525 Mon Sep 17 00:00:00 2001 From: Seamus Date: Mon, 23 Oct 2023 17:10:11 -0400 Subject: [PATCH 2/8] remove unused imports & organize imports in tests --- tests/test_commands.py | 8 +++----- tests/test_get_devices.py | 6 +----- tests/test_select_device.py | 5 +---- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/tests/test_commands.py b/tests/test_commands.py index 3d90452..7c052c4 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -3,15 +3,13 @@ # Created by: Seamus Sloan # Last Edited: July 10, 2023 - -from unittest.mock import ANY, MagicMock, call, mock_open, patch import sys -sys.path.append(".") - +from unittest.mock import ANY, MagicMock, call, mock_open, patch +from conftest import DEVICE_IDS from sadb import stop, start, clear, install, uninstall from sadb import scrcpy, get_ip, screenshot, record, wifi, search -from conftest import DEVICE_IDS +sys.path.append(".") TEST_APK = "myApp.apk" TEST_PACKAGE = "com.example.app" diff --git a/tests/test_get_devices.py b/tests/test_get_devices.py index 84e1bd2..87f1b14 100644 --- a/tests/test_get_devices.py +++ b/tests/test_get_devices.py @@ -3,13 +3,9 @@ # Created by: Seamus Sloan # Last Edited: July 10, 2023 - -import os import sys -sys.path.append("..") - from sadb import split_get_devices - +sys.path.append("..") def test_get_devices_returns_correct_three_devices(testDevices): devices = split_get_devices(testDevices(3)) diff --git a/tests/test_select_device.py b/tests/test_select_device.py index ea9cea5..ce30fd4 100644 --- a/tests/test_select_device.py +++ b/tests/test_select_device.py @@ -4,12 +4,9 @@ # Last Edited: July 10, 2023 -import os import sys -sys.path.append("..") - from sadb import select_device - +sys.path.append("..") def test_select_one_device(monkeypatch, testDeviceList): monkeypatch.setattr('builtins.input', lambda _: 1) From 2b05deaef754acb6ed79e3c9d7de91f6740f9e2c Mon Sep 17 00:00:00 2001 From: Seamus Date: Mon, 23 Oct 2023 17:13:27 -0400 Subject: [PATCH 3/8] add pr template --- .github/pull_request_template.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..bf8f841 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,9 @@ +## What's New? + + +## Checklist +- [x] Tests pass? +- [x] App runs? + +## Notes + From c3f72c85d716a70e911b7aa72c85c6c570435184 Mon Sep 17 00:00:00 2001 From: Seamus Date: Mon, 23 Oct 2023 17:37:10 -0400 Subject: [PATCH 4/8] fix broken tests --- tests/test_commands.py | 3 +-- tests/test_get_devices.py | 2 +- tests/test_select_device.py | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/test_commands.py b/tests/test_commands.py index 7c052c4..38d2f56 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -6,11 +6,10 @@ import sys from unittest.mock import ANY, MagicMock, call, mock_open, patch from conftest import DEVICE_IDS +sys.path.append(".") from sadb import stop, start, clear, install, uninstall from sadb import scrcpy, get_ip, screenshot, record, wifi, search -sys.path.append(".") - TEST_APK = "myApp.apk" TEST_PACKAGE = "com.example.app" TEST_DEVICE = DEVICE_IDS[0] diff --git a/tests/test_get_devices.py b/tests/test_get_devices.py index 87f1b14..4c875ea 100644 --- a/tests/test_get_devices.py +++ b/tests/test_get_devices.py @@ -4,8 +4,8 @@ # Last Edited: July 10, 2023 import sys -from sadb import split_get_devices sys.path.append("..") +from sadb import split_get_devices def test_get_devices_returns_correct_three_devices(testDevices): devices = split_get_devices(testDevices(3)) diff --git a/tests/test_select_device.py b/tests/test_select_device.py index ce30fd4..ac8374c 100644 --- a/tests/test_select_device.py +++ b/tests/test_select_device.py @@ -3,10 +3,9 @@ # Created by: Seamus Sloan # Last Edited: July 10, 2023 - import sys -from sadb import select_device sys.path.append("..") +from sadb import select_device def test_select_one_device(monkeypatch, testDeviceList): monkeypatch.setattr('builtins.input', lambda _: 1) From 977c808de597f336163c9c8713f4735644de43c9 Mon Sep 17 00:00:00 2001 From: Seamus Date: Mon, 23 Oct 2023 17:46:50 -0400 Subject: [PATCH 5/8] fix linter errors & ignore unwanted errors in tests --- .vscode/settings.json | 1 + sadb.py | 3 +++ tests/conftest.py | 10 ++++++---- tests/test_commands.py | 3 +++ tests/test_get_devices.py | 19 +++++++++++-------- tests/test_select_device.py | 31 +++++++++++++++++-------------- 6 files changed, 41 insertions(+), 26 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 0f52bf2..c562fda 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "capsys", "sadb", "scrcpy", "screencap", diff --git a/sadb.py b/sadb.py index bc65202..d4b8aca 100644 --- a/sadb.py +++ b/sadb.py @@ -1,5 +1,8 @@ +"""Module for controlling multiple android devices at once with adb""" #!/usr/bin/python3 +#pylint: disable=subprocess-run-check, unspecified-encoding + # Created by: Seamus Sloan # Last Edited: July 10, 2023 diff --git a/tests/conftest.py b/tests/conftest.py index c5160c2..f742cc7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,7 @@ +"""Test all functions are sending the correct commands""" #!/usr/bin/python3 +#pylint: disable=missing-function-docstring, wrong-import-position # Created by: Seamus Sloan # Last Edited: July 10, 2023 @@ -9,14 +11,14 @@ DEVICE_IDS = ["FA79J1A00421", "ZY223TDZ43", "HT4CJ0203660", "R58M45YME1R", "emulator-5554"] @pytest.fixture -def testDeviceList(): +def test_device_list(): return DEVICE_IDS @pytest.fixture -def testDevices(): - def _testDevices(number_of_devices): +def test_devices(): + def _test_devices(number_of_devices): result = "List of devices attached\n" for i in range(number_of_devices): result += DEVICE_IDS[i] + "\tdevice\n" return result - return _testDevices \ No newline at end of file + return _test_devices \ No newline at end of file diff --git a/tests/test_commands.py b/tests/test_commands.py index 38d2f56..a9ef544 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1,5 +1,8 @@ +"""Test all functions are sending the correct commands""" #!/usr/bin/python3 +#pylint: disable=missing-function-docstring, wrong-import-position + # Created by: Seamus Sloan # Last Edited: July 10, 2023 diff --git a/tests/test_get_devices.py b/tests/test_get_devices.py index 4c875ea..1522f94 100644 --- a/tests/test_get_devices.py +++ b/tests/test_get_devices.py @@ -1,5 +1,8 @@ +"""Test get devices is returning the correct devices""" #!/usr/bin/python3 +#pylint: disable=missing-function-docstring, wrong-import-position + # Created by: Seamus Sloan # Last Edited: July 10, 2023 @@ -7,26 +10,26 @@ sys.path.append("..") from sadb import split_get_devices -def test_get_devices_returns_correct_three_devices(testDevices): - devices = split_get_devices(testDevices(3)) +def test_get_devices_returns_correct_three_devices(test_devices): + devices = split_get_devices(test_devices(3)) print(devices) assert devices == ['FA79J1A00421', 'ZY223TDZ43', 'HT4CJ0203660'] -def test_get_devices_returns_correct_five_devices(testDevices): - devices = split_get_devices(testDevices(5)) +def test_get_devices_returns_correct_five_devices(test_devices): + devices = split_get_devices(test_devices(5)) print(devices) assert devices == ["FA79J1A00421", "ZY223TDZ43", "HT4CJ0203660", "R58M45YME1R", "emulator-5554"] -def test_get_devices_returns_correct_single_device(testDevices): - devices = split_get_devices(testDevices(1)) +def test_get_devices_returns_correct_single_device(test_devices): + devices = split_get_devices(test_devices(1)) print(devices) assert devices == ['FA79J1A00421'] -def test_get_devices_returns_correct_no_devices(testDevices): - devices = split_get_devices(testDevices(0)) +def test_get_devices_returns_correct_no_devices(test_devices): + devices = split_get_devices(test_devices(0)) print(devices) assert devices == [] diff --git a/tests/test_select_device.py b/tests/test_select_device.py index ac8374c..c7cf509 100644 --- a/tests/test_select_device.py +++ b/tests/test_select_device.py @@ -1,5 +1,8 @@ +"""Test device selection is working as expected""" #!/usr/bin/python3 +#pylint: disable=missing-function-docstring, wrong-import-position + # Created by: Seamus Sloan # Last Edited: July 10, 2023 @@ -7,25 +10,25 @@ sys.path.append("..") from sadb import select_device -def test_select_one_device(monkeypatch, testDeviceList): +def test_select_one_device(monkeypatch, test_device_list): monkeypatch.setattr('builtins.input', lambda _: 1) - device = select_device(testDeviceList) + device = select_device(test_device_list) assert device == "FA79J1A00421" -def test_select_second_device(monkeypatch, testDeviceList): +def test_select_second_device(monkeypatch, test_device_list): monkeypatch.setattr('builtins.input', lambda _: 2) - device = select_device(testDeviceList) + device = select_device(test_device_list) assert device == "ZY223TDZ43" -def test_select_all_devices(monkeypatch, testDeviceList): - monkeypatch.setattr('builtins.input', lambda _: len(testDeviceList) + 1) +def test_select_all_devices(monkeypatch, test_device_list): + monkeypatch.setattr('builtins.input', lambda _: len(test_device_list) + 1) - device = select_device(testDeviceList, allow_all=True) - assert device == testDeviceList + device = select_device(test_device_list, allow_all=True) + assert device == test_device_list def test_display_of_no_devices(capsys): @@ -34,14 +37,14 @@ def test_display_of_no_devices(capsys): assert captured.out == "No devices found\n" -def test_display_of_one_device(testDeviceList): - devices = [testDeviceList[0]] +def test_display_of_one_device(test_device_list): + devices = [test_device_list[0]] assert select_device(devices) == "FA79J1A00421" -def test_select_device_multiple_devices(monkeypatch, capsys, testDeviceList): +def test_select_device_multiple_devices(monkeypatch, capsys, test_device_list): monkeypatch.setattr('builtins.input', lambda _: "1") - select_device(testDeviceList) + select_device(test_device_list) captured = capsys.readouterr() assert captured.out == """Select a device: 1. FA79J1A00421 @@ -52,9 +55,9 @@ def test_select_device_multiple_devices(monkeypatch, capsys, testDeviceList): """ -def test_display_of_multiple_devices_allow_all(monkeypatch, capsys, testDeviceList): +def test_display_of_multiple_devices_allow_all(monkeypatch, capsys, test_device_list): monkeypatch.setattr('builtins.input', lambda _: "1") - select_device(testDeviceList, allow_all=True) + select_device(test_device_list, allow_all=True) captured = capsys.readouterr() assert captured.out == """Select a device: 1. FA79J1A00421 From 1b4e96e6f6831c91d4bf4c5debbd8796cd507f65 Mon Sep 17 00:00:00 2001 From: Seamus Date: Mon, 23 Oct 2023 17:49:05 -0400 Subject: [PATCH 6/8] remove debugging string --- sadb.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sadb.py b/sadb.py index d4b8aca..76c3662 100644 --- a/sadb.py +++ b/sadb.py @@ -315,7 +315,6 @@ def main(): screenshot(device, args.filename) elif args.command == "record": - print(args.filename) device = select_device(devices) if device is None: return From cb94c9a6438d4a5d55983759a049ec08fe6671bc Mon Sep 17 00:00:00 2001 From: Seamus Date: Mon, 23 Oct 2023 18:08:31 -0400 Subject: [PATCH 7/8] make record wait 5 seconds before pulling --- sadb.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/sadb.py b/sadb.py index 76c3662..120f590 100644 --- a/sadb.py +++ b/sadb.py @@ -8,6 +8,7 @@ import argparse import sys +import os import subprocess import time @@ -130,7 +131,7 @@ def record(device, filename): """Run [adb shell screenrecord video.mp4] to perform a screen record""" if not filename: filename = "video.mp4" - remote_path = "/data/local/tmp/screenrecord.mp4" + remote_path = f"/data/local/tmp/{filename}" cmd = ["adb", "-s", device, "shell", f"screenrecord {remote_path}"] proc = subprocess.Popen(cmd) @@ -143,15 +144,19 @@ def record(device, filename): except KeyboardInterrupt: proc.terminate() + print("\nWaiting for recording to save to device...\n") + time.sleep(5) + cmd = ["adb", "-s", device, "pull", remote_path, filename] result = subprocess.run(cmd) if result.returncode == 0: - print(f"Screen recording saved to {filename}") - - cmd = ["adb", "-s", device, "shell", f"rm {remote_path}"] - subprocess.run(cmd) + print(f"Success! Screen recording saved to {os.getcwd()}/{filename}") + delete = input("\nDelete video from device? (Y/n): ") + if delete.lower() == 'y': + cmd = ["adb", "-s", device, "shell", f"rm {remote_path}"] + subprocess.run(cmd) def wifi(device): From f55a9e6b7042d4621e12da52c7acb1d277de9384 Mon Sep 17 00:00:00 2001 From: Seamus Date: Mon, 23 Oct 2023 18:42:19 -0400 Subject: [PATCH 8/8] fix record tests & add delete recording test --- tests/test_commands.py | 113 +++++++++++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 27 deletions(-) diff --git a/tests/test_commands.py b/tests/test_commands.py index a9ef544..67596c7 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -16,6 +16,8 @@ TEST_APK = "myApp.apk" TEST_PACKAGE = "com.example.app" TEST_DEVICE = DEVICE_IDS[0] +DEFAULT_VIDEO_NAME = "video.mp4" +DEFAULT_VIDEO_SAVE_LOC = "/data/local/tmp/" def test_stop(): @@ -90,38 +92,95 @@ def test_screenshot_custom_name(): stdout=mock_file.return_value) +def test_record_default_name(): + expected_file_location = f"{DEFAULT_VIDEO_SAVE_LOC}{DEFAULT_VIDEO_NAME}" + + with patch("sadb.subprocess") as mock_subprocess, \ + patch("time.sleep", side_effect=[None, KeyboardInterrupt, None]) as mock_sleep, \ + patch("sadb.input", return_value='y') as mock_input: + + mock_popen = MagicMock() + mock_subprocess.Popen.return_value = mock_popen + mock_subprocess.run.return_value = MagicMock(returncode=0) + + # Call the function + record(TEST_DEVICE, "") + + # Check if subprocess.Popen was called with correct arguments + mock_subprocess.Popen.assert_called_once_with(["adb", "-s", TEST_DEVICE, "shell", \ + f"screenrecord {expected_file_location}"]) + + # Check if KeyboardInterrupt was handled correctly + mock_popen.terminate.assert_called_once() + + # Check if subprocess.run was called with correct arguments for pulling the file + assert mock_subprocess.run.call_args_list[0] == \ + call(["adb", "-s", TEST_DEVICE, "pull", expected_file_location, DEFAULT_VIDEO_NAME]) + + # Check if subprocess.run was called with correct arguments for deleting the file + assert mock_subprocess.run.call_args_list[1] == \ + call(["adb", "-s", TEST_DEVICE, "shell", f"rm {expected_file_location}"]) + + def test_record_custom_name(): - filename = "custom.mp4" - remote_path = "/data/local/tmp/screenrecord.mp4" + custom_name = "customName.mp4" + expected_file_location = f"{DEFAULT_VIDEO_SAVE_LOC}{custom_name}" - mock_proc = MagicMock() - with patch("subprocess.Popen", return_value=mock_proc) as mock_popen, \ - patch("subprocess.run") as mock_run, \ - patch("time.sleep", side_effect=[None, None, KeyboardInterrupt]) as mock_sleep: - record(TEST_DEVICE, filename) - mock_popen.assert_called_once_with( - ["adb", "-s", TEST_DEVICE, "shell", f"screenrecord {remote_path}"]) - assert call(["adb", "-s", TEST_DEVICE, "pull", remote_path, - filename]) in mock_run.call_args_list - assert call(["adb", "-s", TEST_DEVICE, "shell", - f"rm {remote_path}"]) in mock_run.call_args_list + with patch("sadb.subprocess") as mock_subprocess, \ + patch("time.sleep", side_effect=[None, KeyboardInterrupt, None]) as mock_sleep, \ + patch("sadb.input", return_value='y') as mock_input: + mock_popen = MagicMock() + mock_subprocess.Popen.return_value = mock_popen + mock_subprocess.run.return_value = MagicMock(returncode=0) -def test_record_default_name(): - filename = "video.mp4" - remote_path = "/data/local/tmp/screenrecord.mp4" + # Call the function + record(TEST_DEVICE, custom_name) - mock_proc = MagicMock() - with patch("subprocess.Popen", return_value=mock_proc) as mock_popen, \ - patch("subprocess.run") as mock_run, \ - patch("time.sleep", side_effect=[None, None, KeyboardInterrupt]) as mock_sleep: - record(TEST_DEVICE, "") - mock_popen.assert_called_once_with( - ["adb", "-s", TEST_DEVICE, "shell", f"screenrecord {remote_path}"]) - assert call(["adb", "-s", TEST_DEVICE, "pull", remote_path, - filename]) in mock_run.call_args_list - assert call(["adb", "-s", TEST_DEVICE, "shell", - f"rm {remote_path}"]) in mock_run.call_args_list + # Check if subprocess.Popen was called with correct arguments + mock_subprocess.Popen.assert_called_once_with(["adb", "-s", TEST_DEVICE, "shell", \ + f"screenrecord {expected_file_location}"]) + + # Check if KeyboardInterrupt was handled correctly + mock_popen.terminate.assert_called_once() + + # Check if subprocess.run was called with correct arguments for pulling the file + assert mock_subprocess.run.call_args_list[0] == \ + call(["adb", "-s", TEST_DEVICE, "pull", expected_file_location, custom_name]) + + # Check if subprocess.run was called with correct arguments for deleting the file + assert mock_subprocess.run.call_args_list[1] == \ + call(["adb", "-s", TEST_DEVICE, "shell", f"rm {expected_file_location}"]) + + +def test_record_without_deleting_file(): + expected_file_location = f"{DEFAULT_VIDEO_SAVE_LOC}{DEFAULT_VIDEO_NAME}" + + with patch("sadb.subprocess") as mock_subprocess, \ + patch("time.sleep", side_effect=[None, KeyboardInterrupt, None]) as mock_sleep, \ + patch("sadb.input", return_value='n') as mock_input: + + mock_popen = MagicMock() + mock_subprocess.Popen.return_value = mock_popen + mock_subprocess.run.return_value = MagicMock(returncode=0) + + # Call the function + record(TEST_DEVICE, DEFAULT_VIDEO_NAME) + + # Check if subprocess.Popen was called with correct arguments + mock_subprocess.Popen.assert_called_once_with(["adb", "-s", TEST_DEVICE, "shell", \ + f"screenrecord {expected_file_location}"]) + + # Check if KeyboardInterrupt was handled correctly + mock_popen.terminate.assert_called_once() + + # Check if subprocess.run was called with correct arguments for pulling the file + assert mock_subprocess.run.call_args_list[0] == \ + call(["adb", "-s", TEST_DEVICE, "pull", "/data/local/tmp/video.mp4", "video.mp4"]) + + # Check if subprocess.run was not called with arguments for deleting the file + delete_call = call(["adb", "-s", TEST_DEVICE, "shell", f"rm {expected_file_location}"]) + assert delete_call not in mock_subprocess.run.call_args_list def test_wifi():