191 lines
8.4 KiB
C++
191 lines
8.4 KiB
C++
/* Обновление настоящих INI: дубли, UTF-8, независимые секции и отказ записи. */
|
|
#include "../transport/config_updater.h"
|
|
#include <QtTest>
|
|
#include <QTemporaryDir>
|
|
#include <QFile>
|
|
extern "C" {
|
|
#include "debug_config.h"
|
|
}
|
|
#ifdef Q_OS_UNIX
|
|
#include <sys/resource.h>
|
|
#include <csignal>
|
|
#endif
|
|
|
|
class ConfigUpdaterTest : public QObject {
|
|
Q_OBJECT
|
|
QTemporaryDir directory;
|
|
|
|
QString path() const { return directory.filePath("node.cfg"); }
|
|
bool put(const QByteArray& data) {
|
|
QFile file(path());
|
|
return file.open(QIODevice::WriteOnly) && file.write(data) == data.size() && file.flush();
|
|
}
|
|
QByteArray contents() const {
|
|
QFile file(path());
|
|
if (!file.open(QIODevice::ReadOnly)) return {};
|
|
return file.readAll();
|
|
}
|
|
private slots:
|
|
void initTestCase() {
|
|
debug_config_init();
|
|
debug_enable_console(1);
|
|
debug_set_category_level(DEBUG_CATEGORY_CONFIG, DEBUG_LEVEL_DEBUG);
|
|
}
|
|
void init() { QVERIFY(directory.isValid()); QFile::remove(path()); }
|
|
|
|
void newFileAndSection() {
|
|
QVERIFY(config_update_option(path(), "global", "my_node_name", QString::fromUtf8("Тест")));
|
|
QVERIFY(config_update_option(path(), "gui", "language", "ru"));
|
|
QCOMPARE(contents(), QByteArray("[global]\nmy_node_name=") + QString::fromUtf8("Тест").toUtf8() +
|
|
"\n\n[gui]\nlanguage=ru\n");
|
|
}
|
|
|
|
void replacementUpdatesAllDuplicates() {
|
|
QVERIFY(put("# keep\n[global]\nmy_node_name=old\nmy_node_name=last\n"
|
|
"[control]\nport=9\n[GLOBAL]\nmy_node_name=override\n"));
|
|
QVERIFY(config_update_option(path(), "global", "my_node_name", "new"));
|
|
QCOMPARE(contents().count("my_node_name=new"), 3);
|
|
QVERIFY(!contents().contains("old"));
|
|
QVERIFY(!contents().contains("override"));
|
|
QVERIFY(contents().contains("# keep\n"));
|
|
QVERIFY(contents().contains("[control]\nport=9\n"));
|
|
}
|
|
|
|
void chatAliasUsesSameSection() {
|
|
QVERIFY(put("[ chat ]\nradio_auto_enabled=0\n[chatserver]\nradio_auto_enabled=0\n"));
|
|
QVERIFY(config_update_option(path(), "chatserver", "radio_auto_enabled", "1"));
|
|
QCOMPARE(contents().count("radio_auto_enabled=1"), 2);
|
|
}
|
|
|
|
void appendUsesLastMatchingSection() {
|
|
QStringList lines{"[client:peer]", "link=one", "[client:other]", "link=other",
|
|
"[client:peer]", "link=two", "[global]", "my_node_name=keep"};
|
|
QVERIFY(config_update_in_lines(lines, "client:peer", "link", "three", false));
|
|
QCOMPARE(lines.indexOf("link=three"), lines.indexOf("link=two") + 1);
|
|
QCOMPARE(lines.count("link=one"), 1);
|
|
QCOMPARE(lines.count("link=other"), 1);
|
|
}
|
|
|
|
void insertsMissingKey() {
|
|
QStringList lines{"[global]", "my_node_name=keep", "[gui]", "language=en"};
|
|
QVERIFY(config_update_in_lines(lines, "global", "auto_sockets", "yes", true));
|
|
QCOMPARE(lines, (QStringList{"[global]", "auto_sockets=yes", "my_node_name=keep", "[gui]", "language=en"}));
|
|
}
|
|
|
|
void controlAliasesAreUpdatedTogether() {
|
|
QStringList lines{"[control]", "port=1", "control_port=2", "ip=127.0.0.1", "control_ip=127.0.0.2"};
|
|
QVERIFY(config_update_in_lines(lines, "control", "port", "0", true));
|
|
QVERIFY(config_update_in_lines(lines, "control", "control_ip", "::1", true));
|
|
QCOMPARE(lines.count("port=0"), 2);
|
|
QCOMPARE(lines.count("control_ip=::1"), 2);
|
|
}
|
|
|
|
void batchReadKeepsOutputOnError() {
|
|
const QStringList original{"preserve caller buffer"};
|
|
auto lines = original;
|
|
QVERIFY(!config_read_lines(path(), lines));
|
|
QCOMPARE(lines, original);
|
|
QVERIFY(config_read_lines(path(), lines, true));
|
|
QVERIFY(lines.isEmpty());
|
|
QVERIFY(put("[gui]\nlanguage=ru\n"));
|
|
QVERIFY(config_read_lines(path(), lines));
|
|
QCOMPARE(lines, (QStringList{"[gui]", "language=ru"}));
|
|
}
|
|
|
|
void invalidArgumentsDoNotMutate() {
|
|
const QStringList original{"[global]", "my_node_name=keep"};
|
|
for (const auto& values : {QStringList{"", "name", "value"}, QStringList{"global", "x=y", "value"},
|
|
QStringList{"global", "name", "one\ntwo"}, QStringList{"global]", "name", "value"}}) {
|
|
auto lines = original;
|
|
QVERIFY(!config_update_in_lines(lines, values[0], values[1], values[2], true));
|
|
QCOMPARE(lines, original);
|
|
}
|
|
}
|
|
|
|
void rewritesOnlyServerSections() {
|
|
QVERIFY(put("[global]\nmy_node_name=keep\n\n[ SERVER : old ]\naddr=127.0.0.1:1\n"
|
|
"[client:peer]\nlink=old:127.0.0.1:2\n[server:other]\naddr=[::1]:3\n[gui]\nlanguage=ru\n"));
|
|
QVERIFY(config_rewrite_servers(path(), {{"lan", "127.0.0.1:10", "", "", ""},
|
|
{"lan6", "[::1]:10", "tcp", "local", "eth0"}}));
|
|
const auto text = contents();
|
|
QVERIFY(!text.contains("[ SERVER : old ]"));
|
|
QVERIFY(!text.contains("[server:other]"));
|
|
QVERIFY(text.contains("[server:lan]\naddr=127.0.0.1:10\ntransport=udp\ntype=public\n"));
|
|
QVERIFY(text.contains("[server:lan6]\naddr=[::1]:10\ntransport=tcp\ntype=local\nnetif=eth0\n"));
|
|
QVERIFY(text.indexOf("[server:lan]") < text.indexOf("[client:peer]"));
|
|
QVERIFY(text.contains("[client:peer]\nlink=old:127.0.0.1:2\n[gui]\nlanguage=ru\n"));
|
|
QVERIFY(config_rewrite_servers(path(), {}));
|
|
QVERIFY(!contents().contains("[server:"));
|
|
}
|
|
|
|
void readAndOpenFailures() {
|
|
QVERIFY(!config_update_option(directory.path(), "gui", "language", "en"));
|
|
QVERIFY(!config_rewrite_servers(directory.path(), {}));
|
|
QVERIFY(!config_update_option(directory.filePath("missing/node.cfg"), "gui", "language", "en"));
|
|
QVERIFY(!QFile::exists(directory.filePath("missing/node.cfg")));
|
|
}
|
|
|
|
void invalidTextIsNotRewritten() {
|
|
for (const QByteArray& original : {QByteArray("[global]\nname=bad\0tail\n", 23),
|
|
QByteArray("[global]\nname=\xff\n"), QByteArray("[global]\nname=") + QByteArray(1023, 'x') + '\n'}) {
|
|
QVERIFY(put(original));
|
|
QVERIFY(!config_update_option(path(), "global", "my_node_name", "new"));
|
|
QCOMPARE(contents(), original);
|
|
QVERIFY(!config_rewrite_servers(path(), {}));
|
|
QCOMPARE(contents(), original);
|
|
}
|
|
}
|
|
|
|
void invalidServerDoesNotReplaceFile() {
|
|
const QByteArray original("[server:keep]\naddr=127.0.0.1:1\n");
|
|
QVERIFY(put(original));
|
|
QVERIFY(!config_rewrite_servers(path(), {{"bad]\n[global", "127.0.0.1:2", "udp", "public", ""}}));
|
|
QCOMPARE(contents(), original);
|
|
}
|
|
|
|
void normalizesCrLf() {
|
|
QVERIFY(put("[global]\r\nmy_node_name=old\r\n# keep\r\n"));
|
|
QVERIFY(config_update_option(path(), "global", "my_node_name", "new"));
|
|
QCOMPARE(contents(), QByteArray("[global]\nmy_node_name=new\n# keep\n"));
|
|
}
|
|
|
|
void failedWritePreservesOriginal_data() {
|
|
QTest::addColumn<bool>("servers");
|
|
QTest::newRow("option") << false;
|
|
QTest::newRow("servers") << true;
|
|
}
|
|
void failedWritePreservesOriginal() {
|
|
#ifdef Q_OS_UNIX
|
|
QFETCH(bool, servers);
|
|
const QByteArray original("[global]\nmy_node_name=keep\n");
|
|
QVERIFY(put(original));
|
|
bool saved;
|
|
{
|
|
struct LimitGuard {
|
|
struct rlimit previous{};
|
|
using Handler = void (*)(int);
|
|
Handler handler = SIG_DFL;
|
|
bool active = false;
|
|
~LimitGuard() { if (active) { setrlimit(RLIMIT_FSIZE, &previous); std::signal(SIGXFSZ, handler); } }
|
|
} guard;
|
|
QVERIFY(getrlimit(RLIMIT_FSIZE, &guard.previous) == 0);
|
|
struct rlimit limited = guard.previous;
|
|
limited.rlim_cur = 64;
|
|
guard.handler = std::signal(SIGXFSZ, SIG_IGN);
|
|
guard.active = true;
|
|
QVERIFY(setrlimit(RLIMIT_FSIZE, &limited) == 0);
|
|
saved = servers ? config_rewrite_servers(path(), {{"lan", "127.0.0.1:10", "udp", "public", QString(200, 'x')}}) :
|
|
config_update_option(path(), "global", "my_node_name", QString(200, 'x'));
|
|
}
|
|
QVERIFY(!saved);
|
|
QCOMPARE(contents(), original);
|
|
QCOMPARE(QDir(directory.path()).entryList(QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot), QStringList{"node.cfg"});
|
|
#else
|
|
QSKIP("RLIMIT_FSIZE is available on Unix");
|
|
#endif
|
|
}
|
|
};
|
|
|
|
QTEST_GUILESS_MAIN(ConfigUpdaterTest)
|
|
#include "test_config_updater.moc"
|