From fc8a06547d35c916e6dc1a7fc4c0202534eabbf3 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 25 Oct 2026 16:27:10 +0100 Subject: [PATCH] Test automatic socket protocol configuration and Android port reuse --- tests/test_config_debug.c | 22 +++++++++ .../utun/chat/data/AutoSocketProtocolsTest.kt | 45 +++++++++++++++++++ tools/chatgui/tests/test_gui_language.cpp | 34 ++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 tools/chatgui-android/app/src/test/java/com/utun/chat/data/AutoSocketProtocolsTest.kt diff --git a/tests/test_config_debug.c b/tests/test_config_debug.c index f2617cc2..dd6b4cbf 100644 --- a/tests/test_config_debug.c +++ b/tests/test_config_debug.c @@ -210,6 +210,27 @@ static void test_debug_log_reopen(void) { DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Log reopen success/failure and append preservation: PASS"); } +/* Значение по умолчанию, все режимы и строгая проверка протоколов автосокетов. */ +static void test_auto_socket_protocols(void) { + static const struct { const char* value; int mask; } cases[] = { + {"", CFG_AUTO_SOCKET_BOTH}, {"tcp", CFG_AUTO_SOCKET_TCP}, {"udp", CFG_AUTO_SOCKET_UDP}, + {"tcp+udp", CFG_AUTO_SOCKET_BOTH}, {"TCP+UDP", CFG_AUTO_SOCKET_BOTH}, {"TCP", CFG_AUTO_SOCKET_TCP}, + {"udp+tcp", 0}, {"none", 0}, {"invalid", 0} + }; + for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + char text[128]; + int length = snprintf(text, sizeof(text), "[global]\nauto_sockets=yes\n%s%s\n", + cases[i].value[0] ? "auto_socket_protocols=" : "", cases[i].value); + struct utun_config* config = parse_config_from_buf(text, length, "auto-socket-protocols"); + if (!cases[i].mask) { assert(!config); continue; } + assert(config && config->global.auto_socket_protocols == cases[i].mask); + free_config(config); + } + const char* empty = "[global]\nauto_socket_protocols=\n"; + assert(!parse_config_from_buf(empty, strlen(empty), "empty-auto-socket-protocols")); + DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Auto socket protocols: defaults, valid values and rejection verified"); +} + int main() { // Initialize socket platform (required for getaddrinfo on Windows) if (socket_platform_init() != 0) { @@ -270,6 +291,7 @@ int main() { test_all_debug_categories(); test_debug_output_contract(); test_debug_log_reopen(); + test_auto_socket_protocols(); // Cleanup temp config file cleanup_temp_config(); diff --git a/tools/chatgui-android/app/src/test/java/com/utun/chat/data/AutoSocketProtocolsTest.kt b/tools/chatgui-android/app/src/test/java/com/utun/chat/data/AutoSocketProtocolsTest.kt new file mode 100644 index 00000000..0616d2d8 --- /dev/null +++ b/tools/chatgui-android/app/src/test/java/com/utun/chat/data/AutoSocketProtocolsTest.kt @@ -0,0 +1,45 @@ +package com.utun.chat.data + +import org.junit.Assert.* +import org.junit.Test + +class AutoSocketProtocolsTest { + @Test fun createsOnlySelectedProtocolsForBothFamilies() { + for (mode in AutoSocketProtocols.values()) { + for (ip in listOf("192.168.1.2", "fd42::2")) { + val entries = mode.createServers("wlan_0", ip, emptyList()) + assertEquals(mode.protocols, entries.map { it.protocol }) + assertTrue(entries.all { it.ifName == "wlan_0" && it.bindIp == ip && it.port in 2000..9999 }) + } + } + } + + @Test fun keepsPortsSeparateByInterfaceFamilyAndProtocol() { + val saved = listOf( + ServerEntry("1", "wlan0", "192.168.1.2", "tcp", 2001), + ServerEntry("2", "wlan0", "192.168.1.2", "udp", 2002), + ServerEntry("3", "wlan0", "fd42::2", "tcp", 2003), + ServerEntry("4", "wlan0", "fd42::2", "udp", 2004), + ServerEntry("5", "rmnet0", "192.168.1.2", "tcp", 2005) + ) + val v4 = AutoSocketProtocols.BOTH.createServers("wlan0", "192.168.2.3", saved) + val v6 = AutoSocketProtocols.BOTH.createServers("wlan0", "fd42::3", saved) + assertEquals(listOf(2001, 2002), v4.map { it.port }) + assertEquals(listOf(2003, 2004), v6.map { it.port }) + assertEquals(2005, AutoSocketProtocols.TCP.createServers("rmnet0", "10.0.0.2", saved).single().port) + assertEquals(4, (v4 + v6).map { it.sectionName }.toSet().size) + assertEquals(4, (v4 + v6).map { it.socketKey }.toSet().size) + } + + @Test fun restoresDisabledProtocolsAndMissingInterfaces() { + val saved = AutoSocketProtocols.BOTH.createServers("wlan0", "192.168.1.2", emptyList()) + + AutoSocketProtocols.BOTH.createServers("rmnet0", "10.0.0.2", emptyList()) + val active = AutoSocketProtocols.TCP.createServers("wlan0", "192.168.2.3", saved) + val activeKeys = active.map { it.socketKey }.toSet() + val inactive = saved.filter { it.socketKey !in activeKeys } + assertEquals(3, inactive.size) + val restored = AutoSocketProtocols.BOTH.createServers("wlan0", "192.168.3.4", active + inactive) + + AutoSocketProtocols.BOTH.createServers("rmnet0", "10.0.1.2", active + inactive) + assertEquals(saved.associate { it.socketKey to it.port }, restored.associate { it.socketKey to it.port }) + } +} diff --git a/tools/chatgui/tests/test_gui_language.cpp b/tools/chatgui/tests/test_gui_language.cpp index e0f69970..3c2e67a0 100644 --- a/tools/chatgui/tests/test_gui_language.cpp +++ b/tools/chatgui/tests/test_gui_language.cpp @@ -52,6 +52,40 @@ private slots: QCOMPARE(resolveGuiLanguage("en", {"ru-RU"}), QString("en")); } + void autoSocketProtocols() { + QTemporaryDir directory; + QVERIFY(directory.isValid()); + const QString path = directory.filePath("protocols.cfg"); + NodeConfig config(path); + QVERIFY(config.create("Protocol test", 1333)); + NetworkSettingsPage page; page.loadFromConfig(path); + auto* protocols = page.findChild("autoSocketProtocols"); + QVERIFY(protocols && protocols->isEnabled()); + QCOMPARE(protocols->currentData().toString(), QString("tcp+udp")); + for (const QString& value : {QString("tcp"), QString("udp"), QString("tcp+udp")}) { + protocols->setCurrentIndex(protocols->findData(value)); + QVERIFY(page.saveToConfig(path)); + QVERIFY(config.load()); + QVERIFY2(!config.hasErrors(), qPrintable(config.errors().join('\n'))); + QCOMPARE(config.autoSocketProtocols(), value); + QVERIFY(config.save()); + QVERIFY(config.load()); QCOMPARE(config.autoSocketProtocols(), value); + QVERIFY(config.saveFull()); + QVERIFY(config.load()); QCOMPARE(config.autoSocketProtocols(), value); + page.loadFromConfig(path); + QCOMPARE(protocols->currentData().toString(), value); + } + QVERIFY(config_update_option(path, "global", "auto_sockets", "no")); + page.loadFromConfig(path); QVERIFY(!protocols->isEnabled()); + QVERIFY(config_update_option(path, "global", "auto_socket_protocols", "invalid")); + QVERIFY(config.load()); QVERIFY(config.hasErrors()); + QFile file(path); QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Truncate)); + const QByteArray minimal("[global]\nauto_sockets=yes\n"); + QCOMPARE(file.write(minimal), qint64(minimal.size())); file.close(); + QVERIFY(config.load()); QCOMPARE(config.autoSocketProtocols(), QString("tcp+udp")); + page.loadFromConfig(path); QCOMPARE(protocols->currentData().toString(), QString("tcp+udp")); + } + void preferencePersistence() { QTemporaryDir directory; QVERIFY(directory.isValid());