mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-25 08:53:45 +08:00
125 lines
5.5 KiB
Python
125 lines
5.5 KiB
Python
import subprocess
|
|
import sys
|
|
import types
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
# swaglog pulls in compiled msgq, unavailable in some checkouts; stub it
|
|
if "openpilot.common.swaglog" not in sys.modules:
|
|
try:
|
|
import openpilot.common.swaglog # noqa: F401
|
|
except ImportError:
|
|
stub = types.ModuleType("openpilot.common.swaglog")
|
|
stub.cloudlog = mock.MagicMock()
|
|
sys.modules["openpilot.common.swaglog"] = stub
|
|
|
|
from openpilot.system.ui.lib import tethering_nat
|
|
|
|
|
|
def _run_ok(cmd, **kwargs):
|
|
return subprocess.CompletedProcess(cmd, 0, "", "")
|
|
|
|
|
|
class TestTetheringNat(unittest.TestCase):
|
|
|
|
def test_subnet_cidr(self):
|
|
self.assertEqual(tethering_nat._subnet_cidr("10.42.0.1", 24), "10.42.0.0/24")
|
|
self.assertEqual(tethering_nat._subnet_cidr("192.168.43.1", 24), "192.168.43.0/24")
|
|
self.assertEqual(tethering_nat._subnet_cidr("10.99.62.240", 24), "10.99.62.0/24")
|
|
self.assertEqual(tethering_nat._subnet_cidr("172.16.5.9", 12), "172.16.0.0/12")
|
|
self.assertEqual(tethering_nat._subnet_cidr("8.8.8.8", 32), "8.8.8.8/32")
|
|
self.assertIsNone(tethering_nat._subnet_cidr("300.1.1.1", 24))
|
|
self.assertIsNone(tethering_nat._subnet_cidr("1.2.3", 24))
|
|
self.assertIsNone(tethering_nat._subnet_cidr("-1.2.3.4", 24))
|
|
self.assertIsNone(tethering_nat._subnet_cidr("1.2.three.4", 24))
|
|
self.assertIsNone(tethering_nat._subnet_cidr("1.2.3.4", True))
|
|
|
|
def test_interface_subnet_parses_ip_output(self):
|
|
result = subprocess.CompletedProcess(
|
|
[], 0, "11: wlan0 inet 10.42.0.1/24 brd 10.42.0.255 scope global wlan0\n", "")
|
|
with mock.patch.object(subprocess, "run", return_value=result):
|
|
self.assertEqual(tethering_nat._interface_subnet("wlan0"), "10.42.0.0/24")
|
|
|
|
def test_interface_subnet_no_address(self):
|
|
result = subprocess.CompletedProcess([], 0, "", "")
|
|
with mock.patch.object(subprocess, "run", return_value=result):
|
|
self.assertIsNone(tethering_nat._interface_subnet("wlan0"))
|
|
|
|
def test_interface_subnet_ignores_malformed_address_lines(self):
|
|
result = subprocess.CompletedProcess([], 0, "11: wlan0 inet\n11: wlan0 inet not-an-address\n", "")
|
|
with mock.patch.object(subprocess, "run", return_value=result):
|
|
self.assertIsNone(tethering_nat._interface_subnet("wlan0"))
|
|
|
|
def test_hotspot_subnets_live_first_then_candidates(self):
|
|
with mock.patch.object(tethering_nat, "_interface_subnet", return_value="10.42.0.0/24"):
|
|
self.assertEqual(tethering_nat.hotspot_subnets(),
|
|
("10.42.0.0/24", "192.168.43.0/24"))
|
|
with mock.patch.object(tethering_nat, "_interface_subnet", return_value=None):
|
|
self.assertEqual(tethering_nat.hotspot_subnets(),
|
|
("10.42.0.0/24", "192.168.43.0/24"))
|
|
with mock.patch.object(tethering_nat, "_interface_subnet", return_value="192.168.0.0/24"):
|
|
self.assertEqual(tethering_nat.hotspot_subnets(),
|
|
("192.168.0.0/24", "10.42.0.0/24", "192.168.43.0/24"))
|
|
|
|
def test_ensure_noops_without_iptables(self):
|
|
with mock.patch.object(tethering_nat.shutil, "which", return_value=None):
|
|
self.assertFalse(tethering_nat.ensure_tethering_nat())
|
|
|
|
def test_ensure_applies_rules_idempotently(self):
|
|
calls = []
|
|
|
|
def fake_run(cmd, **kwargs):
|
|
calls.append(cmd)
|
|
return subprocess.CompletedProcess(cmd, 0, "", "")
|
|
|
|
with mock.patch.object(tethering_nat.shutil, "which", return_value="/usr/sbin/iptables-legacy"), \
|
|
mock.patch.object(tethering_nat.subprocess, "run", side_effect=fake_run), \
|
|
mock.patch.object(tethering_nat, "_interface_subnet", return_value=None):
|
|
self.assertTrue(tethering_nat.ensure_tethering_nat())
|
|
|
|
sysctls = [c for c in calls if "sysctl" in c]
|
|
self.assertEqual(len(sysctls), 1)
|
|
self.assertIn("net.ipv4.ip_forward=1", sysctls[0])
|
|
|
|
# check-only passes: nothing re-added; 3 checks per subnet, 2 candidate subnets
|
|
ipt = [c for c in calls if "iptables-legacy" in c]
|
|
self.assertEqual(len(ipt), 6)
|
|
self.assertTrue(all("-C" in c for c in ipt))
|
|
|
|
def test_ensure_adds_when_check_fails(self):
|
|
calls = []
|
|
|
|
def fake_run(cmd, **kwargs):
|
|
calls.append(cmd)
|
|
# sudo checks fail (rule missing), sudo adds succeed
|
|
returncode = 1 if "-C" in cmd else 0
|
|
return subprocess.CompletedProcess(cmd, returncode, "", "")
|
|
|
|
with mock.patch.object(tethering_nat.shutil, "which", return_value="/usr/sbin/iptables-legacy"), \
|
|
mock.patch.object(tethering_nat.subprocess, "run", side_effect=fake_run), \
|
|
mock.patch.object(tethering_nat, "_interface_subnet", return_value=None):
|
|
self.assertTrue(tethering_nat.ensure_tethering_nat())
|
|
|
|
ipt = [c for c in calls if "iptables-legacy" in c]
|
|
self.assertEqual(len(ipt), 12) # check+add per rule, 3 rules x 2 subnets
|
|
self.assertEqual(len([c for c in ipt if "-A" in c]), 6)
|
|
|
|
def test_ensure_excludes_live_subnet_when_not_hotspot(self):
|
|
calls = []
|
|
|
|
def fake_run(cmd, **kwargs):
|
|
calls.append(cmd)
|
|
return subprocess.CompletedProcess(cmd, 0, "", "")
|
|
|
|
with mock.patch.object(tethering_nat.shutil, "which", return_value="/usr/sbin/iptables-legacy"), \
|
|
mock.patch.object(tethering_nat.subprocess, "run", side_effect=fake_run), \
|
|
mock.patch.object(tethering_nat, "_interface_subnet", return_value="10.99.62.0/24"):
|
|
self.assertTrue(tethering_nat.ensure_tethering_nat(include_live_subnet=False))
|
|
|
|
ipt = [c for c in calls if "iptables-legacy" in c]
|
|
self.assertFalse(any("10.99.62.0/24" in " ".join(c) for c in ipt))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|