From c9743ee85de175d7c1770d55b7c1b6e6e3806d13 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 12 Oct 2026 02:29:08 +0200 Subject: [PATCH] Exercise Android stream queues and teardown against a live core --- tests/Makefile.am | 5 +++ tests/test_android_stream.c | 81 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 tests/test_android_stream.c diff --git a/tests/Makefile.am b/tests/Makefile.am index c2e46e3b..4e005b6b 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -98,6 +98,7 @@ endif check_PROGRAMS += \ test_stream_core \ test_stream_media \ + test_android_stream \ test_stream_service \ test_u_async_comprehensive \ test_nat_detection \ @@ -468,6 +469,10 @@ test_stream_core_LDFLAGS = $(test_clock_ldflags) test_stream_media_SOURCES = test_stream_media.c test_stream_media_LDADD = $(LIBUTUN) $(COMMON_LIBS) +test_android_stream_SOURCES = test_android_stream.c $(test_clock_sources) ../tools/chatgui-android/libutun_lite/stream_bridge.c +test_android_stream_LDADD = $(LIBUTUN) $(COMMON_LIBS) +test_android_stream_LDFLAGS = $(test_clock_ldflags) + test_stream_service_SOURCES = test_stream_service.c $(test_clock_sources) test_stream_service_LDADD = $(LIBUTUN) $(COMMON_LIBS) test_stream_service_LDFLAGS = $(test_clock_ldflags) -Wl,--wrap=uasync_get_load diff --git a/tests/test_android_stream.c b/tests/test_android_stream.c new file mode 100644 index 00000000..98225f50 --- /dev/null +++ b/tests/test_android_stream.c @@ -0,0 +1,81 @@ +/* Android actor: локальный preview, очередь, stop/FIN, отменённый open и detach concurrent producer. */ +#include +#include +#include + +#include "../lib/debug_config.h" +#include "../lib/mem.h" +#include "../lib/sqlite3.h" +#include "../src/chat/chat_core.h" +#include "../src/utun_instance.h" +#include "../tools/chatgui-android/libutun_lite/stream_bridge.h" +#include "../src/stream/stream_proto.h" +#include "test_clock.h" + +#define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "Android stream test line=%d: %s", __LINE__, #x); abort(); } } while (0) +static uint8_t video[9000]; +static struct UASYNC *ua; +static int producer_started, producer_stop; + +static uint8_t *wait_event(uint64_t id, int kind, size_t *size) { + for (unsigned i = 0; i < 1000; i++) { + uint8_t *data = android_stream_pull(id, size); + if (data && data[0] == kind) return data; + u_free(data); test_clock_poll(ua, 100); + } + CHECK(0); return NULL; +} +static void *producer(void *arg) { + (void)arg; + __atomic_store_n(&producer_started, 1, __ATOMIC_RELEASE); + uint64_t pts = 500000; + while (!__atomic_load_n(&producer_stop, __ATOMIC_ACQUIRE)) { + int result = android_stream_feed(3, 1, pts++, 1, video, sizeof(video)); + if (result < 0) break; + } + return NULL; +} + +int main(void) { + debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_STREAM, DEBUG_LEVEL_INFO); + size_t before = u_get_allocated_count(); test_clock_use_managed(); ua = uasync_create(); CHECK(ua); + struct UTUN_INSTANCE *inst = utun_instance_create_from_str(ua, + "[global]\nauto_sockets=no\n[chatserver]\ngroup_autoconnect=0\nstorage_autoload=0\nmember_files_enabled=0\n"); + CHECK(inst && !utun_core_start(inst) && !chat_service_start(inst)); + chat_core_create_channel_auto(inst, "Stream \"test\" 😀"); + sqlite3_stmt *statement = NULL; + CHECK(sqlite3_prepare_v2(chat_core_get_db(inst), "SELECT ch_id FROM channels LIMIT 1", -1, &statement, NULL) == SQLITE_OK); + CHECK(sqlite3_step(statement) == SQLITE_ROW); + uint64_t group = strtoull((const char *)sqlite3_column_text(statement, 0), NULL, 10); sqlite3_finalize(statement); CHECK(group); + uasync_mark_running(ua); android_stream_attach(inst); + CHECK(android_stream_open(1, group, 0, 1280, 720, 90, 1) == 0); + size_t size = 0; u_free(wait_event(1, 4, &size)); + char *sources = android_stream_sources(group); u_free(sources); test_clock_poll(ua, 100); + sources = android_stream_sources(group); CHECK(strstr(sources, "epoch") && strstr(sources, "name")); u_free(sources); + CHECK(android_stream_open(2, group, inst->node_id, 0, 0, 0, 1) == 0); u_free(wait_event(2, 4, &size)); + for (unsigned i = 0; i < sizeof(video); i++) video[i] = (uint8_t)i; + CHECK(android_stream_feed(1, 1, 100000, 0, video, sizeof(video)) == 0); + CHECK(android_stream_feed(1, 1, 100000, 1, video, sizeof(video)) == 1); + CHECK(android_stream_feed(1, 0, 100000, 0, (const uint8_t *)"ops", 3) == 1); + uint8_t *output = wait_event(2, 1, &size); + CHECK(size == sizeof(video) + 20 && stream_read_be(output + 8, 8) == 100000 && !memcmp(output + 20, video, sizeof(video))); + u_free(output); + output = wait_event(2, 2, &size); CHECK(size == 23 && !memcmp(output + 20, "ops", 3)); u_free(output); + android_stream_stop(1); u_free(wait_event(2, 5, &size)); android_stream_stop(2); u_free(wait_event(1, 5, &size)); + while ((output = android_stream_pull(2, &size))) u_free(output); + CHECK(android_stream_open(3, group, 0, 1280, 720, 0, 0) == 0); u_free(wait_event(3, 4, &size)); + pthread_t thread; CHECK(pthread_create(&thread, NULL, producer, NULL) == 0); + while (!__atomic_load_n(&producer_started, __ATOMIC_ACQUIRE)) {} + android_stream_detach(inst); + __atomic_store_n(&producer_stop, 1, __ATOMIC_RELEASE); CHECK(pthread_join(thread, NULL) == 0); + CHECK(android_stream_feed(3, 1, 900000, 1, video, sizeof(video)) == -1); + while ((output = android_stream_pull(3, &size))) u_free(output); + android_stream_attach(inst); + CHECK(android_stream_open(4, group, 0, 1280, 720, 0, 0) == 0); android_stream_stop(4); + u_free(wait_event(4, 5, &size)); + android_stream_detach(inst); uasync_mark_stopped(ua); utun_instance_destroy(inst); uasync_destroy(ua, 0); + CHECK(u_get_allocated_count() == before); + test_clock_use_real(); + DEBUG_INFO(DEBUG_CATEGORY_STREAM, "PASS Android stream: preview, source snapshot, FIN, cancellation, concurrent detach and no leaks"); + return 0; +}