Files
utun2/tests/test_attachment_send.c
T

104 lines
5.8 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Общая подготовка: закреплённый target, реальный Opus, отмена и собственный cache. */
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include "../lib/mem.h"
#include "../lib/debug_config.h"
#include "../src/utun_instance.h"
#include "../src/chat/chat_core.h"
#include "../src/chat/chat_event.h"
#include "../src/dm/dm_core.h"
#include "../src/dm/dm_crypto.h"
#include "../src/media_async/attachment_send.h"
#include "test_utils.h"
#define REQUIRE(value) do { if (!(value)) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment send check line=%d", __LINE__); return 1; } } while (0)
struct test_state {
pthread_t event_thread;
char target[68];
int released, release_in_worker, starts, ready, failed;
int16_t pcm[48000];
};
static struct test_state test;
static void release_pcm(void* owner) {
struct test_state* state = owner;
state->released++; state->release_in_worker = !pthread_equal(pthread_self(), state->event_thread);
}
static void event(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int length) {
(void)inst;
if (type != CHAT_EVT_ATTACHMENT_PREPARATION) return;
if (length < 18 || length != 18 + data[0] || data[0] >= sizeof(test.target)) { test.failed++; return; }
memcpy(test.target, data + 1, data[0]); test.target[data[0]] = 0;
int state = data[length - 1];
if (state == 0) test.starts++;
else if (state == 1) test.ready++;
else test.failed++;
}
static int count(struct UTUN_INSTANCE* inst) {
sqlite3_stmt* statement = NULL;
if (sqlite3_prepare_v2(inst->topo_sqlite_db, "SELECT count(*) FROM dm_messages", -1, &statement, NULL)) return -1;
int result = sqlite3_step(statement) == SQLITE_ROW ? sqlite3_column_int(statement, 0) : -1;
sqlite3_finalize(statement); return result;
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR);
test.event_thread = pthread_self();
char dir[512] = "/tmp/utun_attachment_XXXXXX", config[1024], conv[64];
REQUIRE(!test_mkdtemp(dir));
snprintf(config, sizeof(config), "[global]\ndb_path=%s\n[chatserver]\ngroup_autoconnect=0\nstorage_autoload=0\n", dir);
struct UASYNC* ua = uasync_create(); REQUIRE(ua);
struct UTUN_INSTANCE* inst = utun_instance_create_from_str(ua, config); REQUIRE(inst);
REQUIRE(!utun_core_start(inst) && !chat_service_start(inst));
chat_event_set_handler(inst, event);
struct SC_MYKEYS peer; uint8_t ed[32];
REQUIRE(!sc_generate_keypair(&peer) && !sc_derive_ed25519_pubkey(peer.private_key, ed));
uint64_t peer_id = sc_derive_node_id_from_pubkey(peer.public_key);
REQUIRE(!dm_start(inst, peer_id, peer.public_key, ed, "test", NULL));
snprintf(conv, sizeof(conv), "%llu", (unsigned long long)dm_derive_conv_id(inst->node_id, peer_id));
REQUIRE(!sc_generate_keypair(&peer) && !sc_derive_ed25519_pubkey(peer.private_key, ed));
peer_id = sc_derive_node_id_from_pubkey(peer.public_key);
REQUIRE(!dm_start(inst, peer_id, peer.public_key, ed, "other", NULL));
char other[64]; snprintf(other, sizeof(other), "%llu", (unsigned long long)dm_derive_conv_id(inst->node_id, peer_id));
struct attachment_send_req* req = u_calloc(1, sizeof(*req)); REQUIRE(req);
req->inst = inst; req->is_dm = 1; req->info.kind = ATTACHMENT_VOICE; req->preset = 1;
snprintf(req->target, sizeof(req->target), "%s", conv);
req->pcm = test.pcm; req->pcm_count = 48000; req->pcm_owner = &test; req->pcm_release = release_pcm;
char active_target[64]; snprintf(active_target, sizeof(active_target), "%s", conv);
attachment_send_trampoline(req);
snprintf(active_target, sizeof(active_target), "%s", other); /* Выбор GUI изменился до completion. */
for (unsigned i = 0; i < 500 && count(inst) == 0; i++) uasync_poll(ua, 20);
REQUIRE(count(inst) == 1 && test.released == 1 && test.release_in_worker && test.starts == 2 && test.ready == 2 && !test.failed);
char expected[68], json[8192]; size_t length;
snprintf(expected, sizeof(expected), "dm:%s", conv);
REQUIRE(!strcmp(test.target, expected) && strcmp(active_target, conv));
REQUIRE(!dm_list_messages_json(inst, conv, 20, 0, json, sizeof(json), &length));
REQUIRE(strstr(json, "\"content_type\":\"audio/opus\"") && strstr(json, "\"duration_ms\":1000"));
REQUIRE(!dm_list_messages_json(inst, active_target, 20, 0, json, sizeof(json), &length) && !strcmp(json, "[]"));
/* Остановка до poll завершает preparation с CANCELLED, без регистрации сообщения. */
req = u_calloc(1, sizeof(*req)); REQUIRE(req);
req->inst = inst; req->is_dm = 1; req->info.kind = ATTACHMENT_VOICE; req->preset = 1;
snprintf(req->target, sizeof(req->target), "%s", conv);
req->pcm = test.pcm; req->pcm_count = 48000; req->pcm_owner = &test; req->pcm_release = release_pcm;
attachment_send_trampoline(req);
chat_service_stop(inst);
REQUIRE(test.released == 2 && test.failed == 1 && count(inst) == 1);
uasync_poll(ua, 0); REQUIRE(test.failed == 1);
/* Отклонение после stop удаляет только переданный собственный cache. */
req = u_calloc(1, sizeof(*req)); REQUIRE(req);
req->inst = inst; req->is_dm = 1; req->temporary_source = 1; snprintf(req->target, sizeof(req->target), "%s", conv);
snprintf(req->source, sizeof(req->source), "%s/cache.mp4", dir);
char cache[1024]; snprintf(cache, sizeof(cache), "%s", req->source);
FILE* file = fopen(cache, "wb"); REQUIRE(file); REQUIRE(fputc(42, file) != EOF && !fclose(file));
attachment_send_trampoline(req);
REQUIRE(test.failed == 2 && access(cache, F_OK) != 0);
utun_instance_destroy(inst); uasync_destroy(ua, 0);
printf("test_attachment_send: pinned PM target, worker PCM ownership, cancel and cache cleanup PASS\n");
return 0;
}