Add sound settings request ownership regressions
This commit is contained in:
1 parent
a6ab797659
commit
821f1b8a92
2 files changed
+187
-1
No files matched your search
@@ -490,6 +490,17 @@ endif()
|
||||
add_test(NAME test_channel_settings COMMAND test_channel_settings)
|
||||
set_tests_properties(test_channel_settings PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 30)
|
||||
|
||||
add_executable(test_sound_settings tests/test_sound_settings.cpp src/soundsettingspage.cpp src/sound_manager.cpp
|
||||
src/audio_device.cpp src/voice_audio_io.cpp db/db_manager.cpp transport/gui_bridge_impl.cpp
|
||||
transport/miniaudio_impl.c transport/audio_diagnostics.cpp)
|
||||
target_link_libraries(test_sound_settings PRIVATE ${QT_LIBS} ${QT_TEST_LIB} utun_voice utun pthread ${CMAKE_DL_LIBS})
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_compile_definitions(test_sound_settings PRIVATE UTUN_TEST_SETTINGS_ALLOC_FAILURES)
|
||||
target_link_options(test_sound_settings PRIVATE "-Wl,--wrap=u_malloc_impl" "-Wl,--wrap=u_calloc_impl")
|
||||
endif()
|
||||
add_test(NAME test_sound_settings COMMAND test_sound_settings)
|
||||
set_tests_properties(test_sound_settings PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 30)
|
||||
|
||||
add_executable(test_single_instance tests/test_single_instance.cpp src/single_instance.cpp)
|
||||
target_link_libraries(test_single_instance PRIVATE ${QT_LIBS} ${QT_TEST_LIB} utun pthread ${CMAKE_DL_LIBS})
|
||||
if(WIN32)
|
||||
@@ -872,7 +883,8 @@ add_test(NAME test_voice_file COMMAND test_voice_file)
|
||||
|
||||
if(WIN32)
|
||||
# AudioDevice закрывает неисправный WASAPI в worker с собственным COM apartment.
|
||||
foreach(audio_target vibechat test_file_drop test_invite_links test_video_engine test_stream_view test_stream_publish test_voice_file)
|
||||
foreach(audio_target vibechat test_file_drop test_invite_links test_video_engine test_stream_view test_stream_publish
|
||||
test_voice_file test_sound_settings)
|
||||
target_link_libraries(${audio_target} PRIVATE ole32)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/* Настоящие SoundSettingsPage/bridge/SQLite: выполнение, отказ и отмена сохранения без утечек. */
|
||||
#include "../src/soundsettingspage.h"
|
||||
#include "../src/sound_manager.h"
|
||||
#include "../db/db_manager.h"
|
||||
#include "../transport/gui_bridge.h"
|
||||
#include <QtTest>
|
||||
#include <QCheckBox>
|
||||
#include <QSlider>
|
||||
#include <QScopeGuard>
|
||||
#include <cstring>
|
||||
extern "C" {
|
||||
#include "utun_instance.h"
|
||||
#include "mem.h"
|
||||
#include "debug_config.h"
|
||||
}
|
||||
|
||||
#ifdef UTUN_TEST_SETTINGS_ALLOC_FAILURES
|
||||
static int failAllocation;
|
||||
static const char* allocationFile;
|
||||
static bool rejectAllocation(const char* where) {
|
||||
if (!failAllocation || !strstr(where, allocationFile) || --failAllocation) return false;
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "[INJECT] sound settings allocation failure at %s", where);
|
||||
return true;
|
||||
}
|
||||
extern "C" void* __real_u_malloc_impl(uint32_t, const char*);
|
||||
extern "C" void* __wrap_u_malloc_impl(uint32_t bytes, const char* where) {
|
||||
return rejectAllocation(where) ? nullptr : __real_u_malloc_impl(bytes, where);
|
||||
}
|
||||
extern "C" void* __real_u_calloc_impl(uint32_t, uint32_t, const char*);
|
||||
extern "C" void* __wrap_u_calloc_impl(uint32_t count, uint32_t bytes, const char* where) {
|
||||
return rejectAllocation(where) ? nullptr : __real_u_calloc_impl(count, bytes, where);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct SoundSettingsCore {
|
||||
UASYNC* ua = uasync_create();
|
||||
UTUN_INSTANCE* inst = nullptr;
|
||||
DbManager db;
|
||||
~SoundSettingsCore() {
|
||||
gui_bridge_set_inst(nullptr); gui_bridge_set_uasync(nullptr);
|
||||
if (ua) uasync_mark_stopped(ua);
|
||||
db.setDb(nullptr);
|
||||
if (inst) utun_instance_destroy(inst);
|
||||
if (ua) uasync_destroy(ua, 0);
|
||||
}
|
||||
bool start() {
|
||||
if (!ua) return false;
|
||||
const char* config = "[global]\n"
|
||||
"my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n"
|
||||
"my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n"
|
||||
"[chatserver]\ngroup_autoconnect=0\nmember_files_enabled=0\nstorage_autoload=0\n";
|
||||
inst = utun_instance_create_from_str(ua, config);
|
||||
if (!inst || utun_core_start(inst) || chat_service_start(inst)) return false;
|
||||
db.setDb(inst->topo_sqlite_db);
|
||||
gui_bridge_set_uasync(ua); gui_bridge_set_inst(inst);
|
||||
uasync_poll(ua, 0);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static const char* events[] = {"msg_incoming", "msg_outgoing", "connect", "disconnect",
|
||||
"call_incoming", "call_connecting", "call_ringing", "radio_incoming"};
|
||||
|
||||
static QMap<QString, QString> expectedSettings(int revision) {
|
||||
QMap<QString, QString> values{{"sound_enabled", QString::number(revision % 2)},
|
||||
{"sound_volume", QString::number(37 + revision)}};
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
values.insert(QString("sound_%1_on").arg(events[i]), QString::number((i + revision) % 2));
|
||||
values.insert(QString("sound_%1_vol").arg(events[i]), QString::number((i * 17 + revision) % 101));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
class SoundSettingsTest : public QObject {
|
||||
Q_OBJECT
|
||||
void editPage(SoundSettingsPage& page, const QMap<QString, QString>& values) {
|
||||
const auto checks = page.findChildren<QCheckBox*>(); QCOMPARE(checks.size(), 9);
|
||||
const auto sliders = page.findChildren<QSlider*>(); QCOMPARE(sliders.size(), 9);
|
||||
checks[0]->setChecked(values["sound_enabled"].toInt()); sliders[0]->setValue(values["sound_volume"].toInt());
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
checks[i + 1]->setChecked(values[QString("sound_%1_on").arg(events[i])].toInt());
|
||||
sliders[i + 1]->setValue(values[QString("sound_%1_vol").arg(events[i])].toInt());
|
||||
}
|
||||
}
|
||||
void checkLocalSettings(const QMap<QString, QString>& values) {
|
||||
auto* manager = SoundManager::instance();
|
||||
QCOMPARE(manager->isEnabled(), bool(values["sound_enabled"].toInt()));
|
||||
QCOMPARE(manager->volume(), values["sound_volume"].toInt() / 100.0f);
|
||||
for (const auto* event : events) {
|
||||
const auto settings = manager->eventSettings(event);
|
||||
QCOMPARE(settings.enabled, bool(values[QString("sound_%1_on").arg(event)].toInt()));
|
||||
QCOMPARE(settings.volume, values[QString("sound_%1_vol").arg(event)].toInt() / 100.0f);
|
||||
}
|
||||
}
|
||||
private slots:
|
||||
void initTestCase() {
|
||||
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
|
||||
debug_set_category_level(DEBUG_CATEGORY_CONFIG, DEBUG_LEVEL_DEBUG);
|
||||
debug_set_category_level(DEBUG_CATEGORY_GENERAL, DEBUG_LEVEL_DEBUG);
|
||||
debug_set_category_level(DEBUG_CATEGORY_CHAT, DEBUG_LEVEL_DEBUG);
|
||||
gui_bridge_init(); SoundManager::instance(); // Настройки не требуют открытия звукового устройства.
|
||||
}
|
||||
void unavailableCoreReleasesRequests() {
|
||||
SoundSettingsPage page(nullptr);
|
||||
const auto baseline = u_get_allocated_count();
|
||||
for (int revision : {1, 2}) {
|
||||
const auto values = expectedSettings(revision); editPage(page, values);
|
||||
page.applyAndSave(); checkLocalSettings(values);
|
||||
QCOMPARE(u_get_allocated_count(), baseline);
|
||||
}
|
||||
}
|
||||
void detachingCoreReleasesPendingRequests() {
|
||||
SoundSettingsCore core; QVERIFY(core.start());
|
||||
SoundSettingsPage page(&core.db);
|
||||
const auto values = expectedSettings(1); editPage(page, values);
|
||||
const auto baseline = u_get_allocated_count();
|
||||
page.applyAndSave(); checkLocalSettings(values);
|
||||
QVERIFY(core.ua->posted_tasks_head);
|
||||
for (auto it = values.cbegin(); it != values.cend(); ++it) QVERIFY(core.db.getUiState(it.key()).isEmpty());
|
||||
gui_bridge_set_inst(nullptr);
|
||||
QCOMPARE(u_get_allocated_count(), baseline);
|
||||
uasync_poll(core.ua, 0);
|
||||
for (auto it = values.cbegin(); it != values.cend(); ++it) QVERIFY(core.db.getUiState(it.key()).isEmpty());
|
||||
}
|
||||
void savesSnapshotAfterPageDestruction() {
|
||||
SoundSettingsCore core; QVERIFY(core.start());
|
||||
const auto baseline = u_get_allocated_count();
|
||||
for (int revision : {1, 2}) {
|
||||
const auto values = expectedSettings(revision);
|
||||
{
|
||||
SoundSettingsPage page(&core.db); editPage(page, values);
|
||||
page.applyAndSave(); checkLocalSettings(values);
|
||||
}
|
||||
// Очередь хранит снимок значений независимо от времени жизни формы.
|
||||
QVERIFY(core.ua->posted_tasks_head);
|
||||
uasync_poll(core.ua, 0);
|
||||
for (auto it = values.cbegin(); it != values.cend(); ++it) QCOMPARE(core.db.getUiState(it.key()), it.value());
|
||||
QCOMPARE(u_get_allocated_count(), baseline);
|
||||
}
|
||||
}
|
||||
#ifdef UTUN_TEST_SETTINGS_ALLOC_FAILURES
|
||||
void allocationFailure_data() {
|
||||
QTest::addColumn<QByteArray>("source"); QTest::addColumn<int>("position"); QTest::addColumn<bool>("cancel");
|
||||
for (const auto* source : {"soundsettingspage.cpp", "gui_bridge_impl.cpp"})
|
||||
for (int position : {1, 9, 18})
|
||||
for (bool cancel : {false, true})
|
||||
QTest::newRow(qPrintable(QString("%1-%2-%3").arg(source).arg(position).arg(cancel ? "cancel" : "execute")))
|
||||
<< QByteArray(source) << position << cancel;
|
||||
}
|
||||
void allocationFailure() {
|
||||
QFETCH(QByteArray, source); QFETCH(int, position); QFETCH(bool, cancel);
|
||||
SoundSettingsCore core; QVERIFY(core.start());
|
||||
SoundSettingsPage page(&core.db);
|
||||
const auto values = expectedSettings(1); editPage(page, values);
|
||||
const auto baseline = u_get_allocated_count();
|
||||
allocationFile = source.constData(); failAllocation = position;
|
||||
auto reset = qScopeGuard([] { failAllocation = 0; allocationFile = nullptr; });
|
||||
page.applyAndSave(); QCOMPARE(failAllocation, 0); checkLocalSettings(values);
|
||||
if (cancel) gui_bridge_set_inst(nullptr);
|
||||
else uasync_poll(core.ua, 0);
|
||||
QCOMPARE(u_get_allocated_count(), baseline);
|
||||
int saved = 0;
|
||||
for (auto it = values.cbegin(); it != values.cend(); ++it) {
|
||||
const auto stored = core.db.getUiState(it.key());
|
||||
if (stored.isEmpty()) continue;
|
||||
QCOMPARE(stored, it.value()); ++saved;
|
||||
}
|
||||
QCOMPARE(saved, cancel ? 0 : 17);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
QTEST_MAIN(SoundSettingsTest)
|
||||
#include "test_sound_settings.moc"
|
||||
Reference in new issue
Block a user