4 changed files with 140 additions and 4 deletions
@ -0,0 +1,122 @@
|
||||
/* Настоящий desktop worker и Qt bridge: нажатие иконки → uasync → SQLite → цвет и результат в GUI. */ |
||||
#include "../transport/utun_node.h" |
||||
#include "../transport/gui_bridge.h" |
||||
#include "../src/accountdelegate.h" |
||||
#include <QtTest> |
||||
#include <QFile> |
||||
#include <QTemporaryDir> |
||||
#include <QStandardItemModel> |
||||
#include <QListView> |
||||
|
||||
extern "C" { |
||||
#include "utun_instance.h" |
||||
#include "chat/chat_core.h" |
||||
#include "chat/chat_member.h" |
||||
#include "routing_layer/topo_group.h" |
||||
#include "routing_layer/topo_node_sqlite.h" |
||||
#include "u_async.h" |
||||
#include "debug_config.h" |
||||
} |
||||
|
||||
struct Probe { |
||||
QObject* receiver = nullptr; |
||||
QStandardItemModel* model = nullptr; |
||||
QString channel; |
||||
bool ready = false, setup_ok = false, polling_running = false; |
||||
int updates = 0; |
||||
QList<bool> results; |
||||
}; |
||||
static Probe* current; |
||||
|
||||
static void prepare_channel(void* arg) { |
||||
auto* probe = static_cast<Probe*>(arg); |
||||
debug_set_category_level(DEBUG_CATEGORY_RADIO, DEBUG_LEVEL_DEBUG); debug_enable_console(1); |
||||
UTUN_INSTANCE* inst = gui_bridge_get_inst(); |
||||
chat_core_create_channel_auto(inst, "radio mute bridge"); |
||||
TOPO_GROUP* channel = nullptr; |
||||
for (ll_entry* e = inst->topo_groups->group_list->head; e; e = e->next) { |
||||
auto* group = reinterpret_cast<TOPO_GROUP*>(e); |
||||
if (group->group_type == TOPO_GROUP_TYPE_CHAT) channel = group; |
||||
} |
||||
const uint8_t key[32] = {1}; |
||||
const bool ok = channel && topo_node_sqlite_member_placeholder_put(inst->topo_sqlite_db, channel->channel_id, 42, key, key) == 0; |
||||
const QString id = channel ? QString::fromUtf8(channel->channel_id) : QString(); |
||||
const int running = uasync_is_running(inst->ua); |
||||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "radio: desktop bridge probe setup=%d poll_running=%d inst=%p ua=%p ch=%s", |
||||
ok, running, (void*)inst, (void*)inst->ua, qPrintable(id)); |
||||
QMetaObject::invokeMethod(probe->receiver, [probe, ok, running, id] { |
||||
probe->setup_ok = ok; probe->polling_running = running; probe->channel = id; probe->ready = true; |
||||
}, Qt::QueuedConnection); |
||||
} |
||||
|
||||
static void member_updated(const uint8_t* data, int len) { |
||||
if (!current || !data || len < 2 || len != data[0] + 2 + CHAT_MEMBER_DISPLAY_SIZE) return; |
||||
chat_member_display member{}; |
||||
if (chat_core_deserialize_member(data + data[0] + 2, &member) < 0 || member.node_id != 42) return; |
||||
++current->updates; |
||||
current->model->setData(current->model->index(0, 0), member.flags, Qt::UserRole + 3); |
||||
} |
||||
|
||||
static void mute_result(const uint8_t* data, int len) { |
||||
if (current && len == 17) current->results.append(data[16] != 0); |
||||
} |
||||
|
||||
class RadioMuteBridgeTest : public QObject { |
||||
Q_OBJECT |
||||
private slots: |
||||
void cleanup() { |
||||
gui_bridge_set_member_updated_cb(nullptr); gui_bridge_set_radio_mute_result_cb(nullptr); |
||||
gui_bridge_set_inst(nullptr); gui_bridge_set_uasync(nullptr); current = nullptr; |
||||
debug_disable_file_output(); |
||||
} |
||||
|
||||
void desktopWorkerAcceptsIconToggleAndReturnsConfirmedState() { |
||||
QTemporaryDir directory; QVERIFY(directory.isValid()); |
||||
const QString path = directory.filePath("node.cfg"); |
||||
QFile config(path); QVERIFY(config.open(QIODevice::WriteOnly)); |
||||
const QByteArray text = "[global]\nmy_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n" |
||||
"my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" |
||||
"[chatserver]\ngroup_autoconnect=0\nstorage_autoload=0\n[ntp]\nenabled=no\n"; |
||||
QCOMPARE(config.write(text), text.size()); config.close(); |
||||
gui_bridge_init(); |
||||
QStandardItemModel model; auto* member = new QStandardItem("other"); |
||||
member->setData(false, Qt::UserRole + 2); member->setData(0, Qt::UserRole + 3); model.appendRow(member); |
||||
Probe probe; probe.receiver = this; probe.model = &model; current = &probe; |
||||
gui_bridge_set_member_updated_cb(member_updated); gui_bridge_set_radio_mute_result_cb(mute_result); |
||||
UtunNode node; node.setDbPath(directory.filePath("db")); |
||||
node.setDebugFile(qEnvironmentVariable("UTUN_TEST_DIAGNOSTIC_LOG", directory.filePath("diagnostic.log"))); |
||||
node.setDebugLevel("warn"); node.setDebugCategories("radio=debug,general=debug"); |
||||
QVERIFY(node.start(path)); QTRY_VERIFY_WITH_TIMEOUT(gui_bridge_is_uasync_ready(), 3000); |
||||
gui_bridge_post_uasync_fn(prepare_channel, &probe); |
||||
QTRY_VERIFY_WITH_TIMEOUT(probe.ready, 3000); QVERIFY(probe.setup_ok); |
||||
QListView view; AccountDelegate delegate; |
||||
view.setModel(&model); view.setItemDelegate(&delegate); view.resize(280, 100); view.show(); QTest::qWait(30); |
||||
int accepted = -2; |
||||
connect(&delegate, &AccountDelegate::radioMuteRequested, &view, [&](const QModelIndex&) { |
||||
accepted = gui_bridge_toggle_member_radio_mute(probe.channel.toUtf8().constData(), 42); |
||||
}); |
||||
const QRect row = view.visualRect(model.index(0, 0)); const QPoint button(row.right() - 50, row.center().y()); |
||||
auto iconHasColor = [&](const QColor& color) { |
||||
const QImage image = view.viewport()->grab().toImage(); |
||||
for (int y = row.top(); y <= row.bottom(); ++y) |
||||
for (int x = row.right() - 63; x < row.right() - 35; ++x) if (image.pixelColor(x, y) == color) return true; |
||||
return false; |
||||
}; |
||||
QVERIFY(iconHasColor(QColor("#4CAF50"))); |
||||
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, button); |
||||
QCOMPARE(accepted, 0); QVERIFY(probe.polling_running); |
||||
QTRY_COMPARE_WITH_TIMEOUT(probe.results.size(), 1, 3000); QVERIFY(probe.results[0]); |
||||
QCOMPARE(probe.updates, 1); QVERIFY(model.index(0, 0).data(Qt::UserRole + 3).toInt() & CHAT_MEMBER_FLAG_RADIO_MUTED); |
||||
QVERIFY(iconHasColor(QColor("#EF5350"))); QVERIFY(!iconHasColor(QColor("#4CAF50"))); |
||||
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, button); |
||||
QCOMPARE(accepted, 0); QTRY_COMPARE_WITH_TIMEOUT(probe.results.size(), 2, 3000); QVERIFY(probe.results[1]); |
||||
QCOMPARE(probe.updates, 2); QVERIFY(!(model.index(0, 0).data(Qt::UserRole + 3).toInt() & CHAT_MEMBER_FLAG_RADIO_MUTED)); |
||||
QVERIFY(iconHasColor(QColor("#4CAF50"))); QVERIFY(!iconHasColor(QColor("#EF5350"))); |
||||
node.stop(); QVERIFY(!gui_bridge_is_uasync_ready()); |
||||
QCOMPARE(gui_bridge_toggle_member_radio_mute(probe.channel.toUtf8().constData(), 42), -1); |
||||
gui_bridge_set_member_updated_cb(nullptr); gui_bridge_set_radio_mute_result_cb(nullptr); current = nullptr; |
||||
debug_disable_file_output(); |
||||
} |
||||
}; |
||||
QTEST_MAIN(RadioMuteBridgeTest) |
||||
#include "test_radio_mute_bridge.moc" |
||||
Loading…
Reference in new issue