Make tun2socks the default TUN engine

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-10-06 23:56:39 +08:00
parent 30a202c833
commit 79ab0b146a
6 changed files with 31 additions and 14 deletions
+3 -3
View File
@@ -50,11 +50,11 @@ SYSTEM_PROXY_MODE_OPTIONS = (
)
registerAppSettings('VPNMode', isBinary=True)
# A missing preference, including upgrades from 0.8.2, deliberately selects
# sing-tun. VPNMode remains independent: choosing an engine does not enable TUN.
# A missing preference, including upgrades from 0.8.2, selects tun2socks.
# VPNMode remains independent: choosing an engine does not enable TUN.
# 0.8.2 ignores this key and continues using application-managed tun2socks.
registerAppSettings(
APPLICATION_TUN_BACKEND, validRange=list(TUN_BACKENDS), default='sing-tun'
APPLICATION_TUN_BACKEND, validRange=list(TUN_BACKENDS), default='tun2socks'
)
registerAppSettings(
APPLICATION_THEME_SETTING,
+1 -1
View File
@@ -141,7 +141,7 @@ if PLATFORM == 'Windows':
CORE_CHECK_ALIVE_INTERVAL = 2500
APPLICATION_TUN_BACKEND = 'ApplicationTUNBackend'
TUN_BACKENDS = ('sing-tun', 'tun2socks')
TUN_BACKENDS = ('tun2socks', 'sing-tun')
if PLATFORM == 'Windows':
APPLICATION_TUN2SOCKS_DEVICE_NAME = APPLICATION_NAME
+2 -2
View File
@@ -563,8 +563,8 @@ class _TUNBackendSettingsCard(_SettingsCard):
def __init__(self):
self.comboBox = AppQComboBox()
self.comboBox.setObjectName('SettingsComboBox')
self.comboBox.addItem('sing-tun', 'sing-tun')
self.comboBox.addItem('tun2socks', 'tun2socks')
self.comboBox.addItem('sing-tun', 'sing-tun')
super().__init__(
'diagram-3.svg',
@@ -954,8 +954,8 @@ class SettingsPage(Mixins.QTranslatable, QMainWindow):
)
self.tunSection.addCard(self.tunBackendCard)
self.tunSection.addCard(self.singTunSettingsCard)
self.tunSection.addCard(self.tunSettingsCard)
self.tunSection.addCard(self.singTunSettingsCard)
self.tunSection.setVisible(not SystemRuntime.flatpakID())
self.connectionSection.addCard(self.systemProxyCard)
+1 -1
View File
@@ -94,7 +94,7 @@ worker. Choose tests by the changed contract rather than by filename alone.
| [test_external_core.py](test_external_core.py) | Harmless real process launch/output/shutdown, partial thread-start rollback, non-finite timeout rejection, failed reap/thread-join retry, readiness/TUN metadata, Windows paths with spaces, subscription rejection of executable profiles, bounded DNS references. |
| [test_frozenlib.py](test_frozenlib.py) | Nested state guards, cleanup isolation, native peer destruction during connection/theme/translation/cleanup notifications, bounded caches/throttling, dual-stack probe selection, mocked proxy/DNS/routes/startup/session boundaries and failure handling. |
| [test_native_tun_semantics.py](test_native_tun_semantics.py) | Xray/Hysteria2 runtime-copy TUN preservation/replacement, managed-TUN failures, download-test stripping, prevention of a second tun2socks owner. |
| [test_sing_tun.py](test_sing_tun.py) | Engine choice/Go default, development-stack and congestion-control compatibility, independent backend settings and host defaults, selected-only snapshots, captured TUN log sources in both startup paths, staged settings and malformed storage, SOCKS authentication/UDP, harmless real spawn/status/stop, native readiness, cancellation, synchronous compatibility, mocked DNS/routing ownership including development IPv6 rule ordering and refused cleanup. |
| [test_sing_tun.py](test_sing_tun.py) | tun2socks engine default, saved engine choices, sing-tun Go stack default, development-stack and congestion-control compatibility, independent backend settings and host defaults, selected-only snapshots, captured TUN log sources in both startup paths, staged settings and malformed storage, SOCKS authentication/UDP, harmless real spawn/status/stop, native readiness, cancellation, synchronous compatibility, mocked DNS/routing ownership including development IPv6 rule ordering and refused cleanup. |
| [test_subscription_sync.py](test_subscription_sync.py) | Group-local preparation/commit, stable duplicate identity, atomic failure, preservation of newer local metadata, rejection of changed source state. |
| [test_subscription_manager.py](test_subscription_manager.py) | Stable request generations, provider metadata, batch/partial failures, timer policy, targeted updates, real Stop Updates input, worker responsiveness, cancellation and synchronous shutdown ownership. |
| [test_subscription_scalability.py](test_subscription_scalability.py) | Deterministic 1/3/8-group preparation and commit with 1,500 profiles per group and bounded workers; uses the offline benchmark helper. |
+16 -6
View File
@@ -484,8 +484,9 @@ class SingTUNUIAndStorageTest(unittest.TestCase):
card.comboBox.itemData(index)
for index in range(card.comboBox.count())
],
['sing-tun', 'tun2socks'],
['tun2socks', 'sing-tun'],
)
self.assertEqual(card.comboBox.currentData(), 'tun2socks')
for backend in ('tun2socks', 'sing-tun', 'tun2socks'):
card.comboBox.setCurrentIndex(card.comboBox.findData(backend))
@@ -797,20 +798,21 @@ class SingTUNUIAndStorageTest(unittest.TestCase):
with isolatedSettings(), mock.patch(
'Furious.Controllers.SettingsController.showMBoxNewChangesNextTime'
):
self.assertEqual(AppSettings.get('ApplicationTUNBackend'), 'sing-tun')
self.assertEqual(AppSettings.get('ApplicationTUNBackend'), 'tun2socks')
QtCore.QSettings().setValue('ApplicationTUNBackend', 'invalid')
self.assertEqual(AppSettings.get('ApplicationTUNBackend'), 'sing-tun')
self.assertEqual(AppSettings.get('ApplicationTUNBackend'), 'tun2socks')
controller = SettingsController()
changes = []
controller.tunBackendChanged.connect(changes.append)
controller.setTUNBackend('tun2socks')
controller.setTUNBackend('tun2socks')
controller.setTUNBackend('sing-tun')
controller.setTUNBackend('sing-tun')
with self.assertRaises(ValueError):
controller.setTUNBackend('invalid')
self.assertEqual(changes, ['tun2socks'])
self.assertEqual(changes, ['sing-tun'])
self.assertEqual(AppSettings.get('ApplicationTUNBackend'), 'sing-tun')
self.assertFalse(AppSettings.isStateON_('VPNMode'))
@@ -1556,6 +1558,8 @@ class SingTUNStartupTest(unittest.TestCase):
'getPluginRegistry',
return_value=_Registry([PreparedRuntime(primary)]),
):
AppSettings.set('ApplicationTUNBackend', 'sing-tun')
operation = manager.startAsync(_Configuration(), 'Global', deepcopy=False)
with mock.patch.object(
@@ -1605,6 +1609,8 @@ class SingTUNStartupTest(unittest.TestCase):
), mock.patch.object(
manager, '_createSingTUN', wraps=manager._createSingTUN
):
AppSettings.set('ApplicationTUNBackend', 'sing-tun')
succeeded = []
operation = manager.startAsync(_Configuration(), 'Global', deepcopy=False)
operation.succeeded.connect(succeeded.append)
@@ -1655,6 +1661,8 @@ class SingTUNStartupTest(unittest.TestCase):
), mock.patch.object(
module, 'SingTUNHostPlan', _Plan
):
AppSettings.set('ApplicationTUNBackend', 'sing-tun')
operation = manager.startAsync(_Configuration(), 'Global', deepcopy=False)
self.assertTrue(
waitFor(lambda: operation._tun is not None and operation._tun.alive)
@@ -1696,6 +1704,8 @@ class SingTUNStartupTest(unittest.TestCase):
), mock.patch.object(
_PreparedSing, 'ready', new_callable=mock.PropertyMock, return_value=True
):
AppSettings.set('ApplicationTUNBackend', 'sing-tun')
self.assertTrue(manager.start(_Configuration(), 'Global', deepcopy=False))
self.assertEqual(manager.runtimes[1]._hostPlan.applied, ['utun101'])
+8 -1
View File
@@ -667,12 +667,19 @@ class SettingsPageOrganizationTest(unittest.TestCase):
expectedTUNCards = [
page.tunBackendCard,
page.singTunSettingsCard,
page.tunSettingsCard,
page.singTunSettingsCard,
]
self.assertEqual(page.tunSection.titleLabel.text(), 'TUN')
self.assertEqual(page.tunSection.cards, expectedTUNCards)
self.assertEqual(
[
page.tunSection.layout.itemAt(index + 1).widget()
for index in range(len(expectedTUNCards))
],
expectedTUNCards,
)
self.assertFalse(page.tunSection.isHidden())
self.assertTrue(
all(card.parent() is page.tunSection for card in expectedTUNCards)