From 05476fb102297cfbc2b5764899de7f270e85a6d3 Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 1 Oct 2026 18:56:57 +0300 Subject: [PATCH] Test Qt voice file validation and decoder cancellation --- tools/chatgui/CMakeLists.txt | 5 +++ tools/chatgui/tests/test_voice_file.cpp | 45 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 tools/chatgui/tests/test_voice_file.cpp diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index 71f67da8..df7b4333 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -262,6 +262,11 @@ add_executable(test_ptt_key_chord tests/test_ptt_key_chord.cpp) target_link_libraries(test_ptt_key_chord PRIVATE ${QT_CORE}) add_test(NAME test_ptt_key_chord COMMAND test_ptt_key_chord) +add_executable(test_voice_file tests/test_voice_file.cpp src/voiceplayback.cpp src/sound_manager.cpp transport/miniaudio_impl.c) +target_include_directories(test_voice_file PRIVATE ${UTUN_INCLUDE_DIRS}) +target_link_libraries(test_voice_file PRIVATE ${QT_LIBS} utun pthread ${CMAKE_DL_LIBS}) +add_test(NAME test_voice_file COMMAND test_voice_file) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") add_executable(test_ptt_portal tests/test_ptt_portal.cpp src/ptt_portal.cpp) target_link_libraries(test_ptt_portal PRIVATE ${QT_DBUS} ${QT_TEST_LIB} utun pthread) diff --git a/tools/chatgui/tests/test_voice_file.cpp b/tools/chatgui/tests/test_voice_file.cpp new file mode 100644 index 00000000..ce65547c --- /dev/null +++ b/tools/chatgui/tests/test_voice_file.cpp @@ -0,0 +1,45 @@ +/* Реальное воспроизведение: строгий формат и отмена асинхронного декодирования. */ +#include "../src/voiceplayback.h" +#include +#include +#include +#include +#include +extern "C" { +#include "../../../lib/debug_config.h" +#include "../../../src/media_async/voice_file.h" +} +#define REQUIRE(value) do { if (!(value)) { qFatal("voice file check line=%d", __LINE__); } } while (0) + +int main(int argc, char** argv) { + QCoreApplication app(argc, argv); + debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); + QTemporaryDir dir; REQUIRE(dir.isValid()); + QString path = dir.filePath("voice.opus"); + std::vector source(48000), decoded; + attachment_info info = {}; int rate = 0, channels = 0; float duration = 0; + REQUIRE(!voice_file_encode(source.data(), source.size(), 48000, 1, 1, qPrintable(path), &info)); + REQUIRE(VoicePlayer::decodeOpusFile(path, decoded, rate, channels, duration) == 50); + REQUIRE(decoded.size() == 48000 && rate == 48000 && channels == 1 && duration == 1); + QFile file(path); REQUIRE(file.open(QIODevice::ReadOnly)); + const auto valid = file.readAll(); file.close(); + auto reject = [&](const QByteArray& data) { + REQUIRE(file.open(QIODevice::WriteOnly | QIODevice::Truncate)); REQUIRE(file.write(data) == data.size()); file.close(); + REQUIRE(VoicePlayer::decodeOpusFile(path, decoded, rate, channels, duration) < 0 && duration == 0); + }; + reject(valid.left(10)); // Обрезанный заголовок. + auto bad = valid; bad[8] = 3; reject(bad); // Число каналов. + bad = valid; bad[10] = 0; bad[11] = 0; reject(bad); // Размер кадра. + reject(valid.left(valid.size() - 2)); // Нет end marker. + reject(valid + "x"); // Посторонние байты после marker. + reject(valid.left(14)); // Обрезанный пакет. + REQUIRE(file.open(QIODevice::WriteOnly | QIODevice::Truncate)); REQUIRE(file.write(valid) == valid.size()); file.close(); + bool called = false; + VoicePlayer::playOpusFile(path, &app, [&](float) { called = true; }); + REQUIRE(VoicePlayer::isPreparing()); VoicePlayer::cancelPending(); + QElapsedTimer deadline; deadline.start(); + while (VoicePlayer::isPreparing() && deadline.elapsed() < 5000) { app.processEvents(); QThread::msleep(1); } + REQUIRE(!VoicePlayer::isPreparing() && !called); + qInfo("test_voice_file: Opus round-trip, malformed files and asynchronous cancellation PASS"); + return 0; +}