from __future__ import annotations import importlib.util import json from pathlib import Path import tempfile import unittest from unittest.mock import patch SPEC = importlib.util.spec_from_file_location("registration", Path(__file__).with_name("prepare_test_device.py")) registration = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(registration) def valid_device(): return {"alias": "android-test-a", "serial": "FAKE-UNIT-SERIAL", "model": "Fixture", "android_release": "13", "api_level": 33, "connection": "usb", "dedicated_test_device": True, "automation_allowed": True, "visual_check_requires_user_instruction": True} class RegistrationTests(unittest.TestCase): def test_windows_usb_without_listing_marker(self): with patch.object(registration, "run_adb", side_effect=["FAKE-USB", "List of devices attached\nFAKE-USB device product:test transport_id:1\n"]): self.assertEqual(registration.list_devices("adb"), [("FAKE-USB", "device", True)]) def test_ambiguous_usb_does_not_guess_from_serial_shape(self): with patch.object(registration, "run_adb", side_effect=[registration.RegistrationError("ambiguous"), "List of devices attached\nFAKE-USB device transport_id:1\n"]): self.assertEqual(registration.list_devices("adb"), [("FAKE-USB", "device", False)]) def test_blank_copy_blocks_and_preserves_existing(self): with tempfile.TemporaryDirectory() as temporary: directory = Path(temporary) / "中文 空格" directory.mkdir() payload = Path(__file__).with_name(registration.EXAMPLE_NAME).read_bytes() (directory / registration.EXAMPLE_NAME).write_bytes(payload) with self.assertRaises(registration.RegistrationError): registration.load_registration(directory) private = directory / registration.PRIVATE_NAME self.assertEqual(private.read_bytes(), payload) with self.assertRaises(registration.RegistrationError): registration.load_registration(directory) self.assertEqual(private.read_bytes(), payload) def test_existing_file_never_overwritten_even_if_corrupt(self): with tempfile.TemporaryDirectory() as temporary: directory = Path(temporary) private = directory / registration.PRIVATE_NAME private.write_bytes(b"not-json") with patch.object(registration, "list_devices") as listing: with self.assertRaises(registration.RegistrationError): registration.register_connected("adb", "android-test-a", directory) listing.assert_not_called() self.assertEqual(private.read_bytes(), b"not-json") def test_ambiguous_unauthorized_and_wireless_registration_refused(self): for rows in ([], [("A", "device", True), ("B", "device", True)], [("A", "unauthorized", True)], [("A", "device", False)]): with self.subTest(rows=rows), tempfile.TemporaryDirectory() as temporary: with patch.object(registration, "list_devices", return_value=rows): with self.assertRaises(registration.RegistrationError): registration.register_connected("adb", "android-test-a", Path(temporary)) self.assertFalse((Path(temporary) / registration.PRIVATE_NAME).exists()) def test_private_creation_and_no_device_values_in_errors(self): device = valid_device() with tempfile.TemporaryDirectory() as temporary: directory = Path(temporary) with patch.object(registration, "list_devices", return_value=[(device["serial"], "device", True)]), \ patch.object(registration, "read_identity", return_value={k: device[k] for k in ("serial", "model", "android_release", "api_level")}): registration.register_connected("adb", device["alias"], directory) self.assertEqual(registration.load_registration(directory), [device]) with patch.object(registration, "list_devices", return_value=[("DIFFERENT-PRIVATE-SERIAL", "device", True)]): with self.assertRaises(registration.RegistrationError) as caught: registration.check_connected("adb", device) self.assertNotIn(device["serial"], str(caught.exception)) self.assertNotIn("DIFFERENT-PRIVATE-SERIAL", str(caught.exception)) def test_authorization_visual_gate_duplicates_and_placeholders(self): for key, value in (("automation_allowed", False), ("visual_check_requires_user_instruction", False), ("serial", ""), ("api_level", True)): device = valid_device() device[key] = value with self.subTest(key=key), self.assertRaises(registration.RegistrationError): registration.validate_document({"schema_version": 1, "devices": [device]}) with self.assertRaises(registration.RegistrationError): registration.validate_document({"schema_version": 1, "devices": [valid_device(), valid_device()]}) def test_system_change_blocks_without_modifying_document(self): device = valid_device() before = json.dumps(device) with patch.object(registration, "list_devices", return_value=[(device["serial"], "device", True)]), \ patch.object(registration, "read_identity", return_value={**device, "api_level": 34}): with self.assertRaises(registration.RegistrationError): registration.check_connected("adb", device) self.assertEqual(json.dumps(device), before) if __name__ == "__main__": unittest.main()