Verify media source admission, redirect bounds and transmit I/O failure cleanup

This commit is contained in:
evgeny committed 2026-10-22 19:37:28 +02:00
1 parent 47addfeffc
commit fabeb770a7
1 file changed
+60 -9
+60 -9
View File
@@ -122,8 +122,7 @@ static void test_identity_and_bounds(void) {
for (size_t length = 0; length < done.len; length++) {
struct packet bad = done; bad.len = length; unchanged(f.client, &bad);
}
for (int variant = 0; variant < 12; variant++) {
if (variant < 6) {
for (int variant = 0; variant < 6; variant++) {
struct packet bad = done;
struct media_pkt_block_done* d = (struct media_pkt_block_done*)bad.data;
switch (variant) {
@@ -135,14 +134,24 @@ static void test_identity_and_bounds(void) {
case 5: d->chunk++; break;
}
unchanged(f.client, &bad);
} else {
struct packet bad = {.from = good.from, .group = GROUP, .len = sizeof(struct media_pkt_block_overloaded)};
struct media_pkt_block_overloaded* ov = (struct media_pkt_block_overloaded*)bad.data;
ov->subcmd = MEDIA_SUBCMD_BLOCK_OVERLOADED; memcpy(ov->media_id, f.metadata.media_id, 16);
memcpy(ov->block_id, f.metadata.block_ids, 16); memset(ov->transfer_id, 0xFF, 16);
unchanged(f.client, &bad);
}
}
struct packet stale = {.from = good.from, .group = GROUP, .len = sizeof(struct media_pkt_block_overloaded)};
struct media_pkt_block_overloaded* ov = (struct media_pkt_block_overloaded*)stale.data;
ov->subcmd = MEDIA_SUBCMD_BLOCK_OVERLOADED; memcpy(ov->media_id, f.metadata.media_id, 16);
memcpy(ov->block_id, f.metadata.block_ids, 16); memset(ov->transfer_id, 0xFF, 16); unchanged(f.client, &stale);
struct packet redirect = {.from = good.from, .group = GROUP, .len = MEDIA_RELAY_FULL_HDR_SIZE + 8};
struct media_pkt_block_relay_full* full = (struct media_pkt_block_relay_full*)redirect.data;
full->subcmd = MEDIA_SUBCMD_BLOCK_RELAY_FULL; full->num_relay_nodes = 1;
memcpy(full->media_id, f.metadata.media_id, 16); memcpy(full->block_id, f.metadata.block_ids, 16);
memcpy(full->transfer_id, download(f.client)->blocks[0].transfer_id, 16);
uint64_t node = 0x300; memcpy(redirect.data + sizeof(*full), &node, 8);
for (size_t length = 0; length < redirect.len; length++) {
struct packet bad = redirect; bad.len = length; unchanged(f.client, &bad);
}
stale = redirect; stale.len++; unchanged(f.client, &stale);
stale = redirect; ((struct media_pkt_block_relay_full*)stale.data)->num_relay_nodes = UINT16_MAX;
unchanged(f.client, &stale);
stale = redirect; ((struct media_pkt_block_relay_full*)stale.data)->transfer_id[0]++; unchanged(f.client, &stale);
finish(&f); REQUIRE(done_count == 1 && done_err == -2);
puts("block identity, exact input bounds and duplicate without progress: PASS");
}
@@ -436,6 +445,47 @@ static void test_relay_limit_and_idle(void) {
puts("relay admission permits five readers, echoes redirect attempt and releases idle readers: PASS");
}
static void test_indexed_source(void) {
for (int variant = 0; variant < 4; variant++) {
struct fixture f = ready(4);
struct media_download* dl = download(f.client);
struct packet requests[4];
for (int i = 0; i < 4; i++) requests[i] = request_packet(dl, 0x300 + i);
struct packet chunk = chunk_packet(f.client, 0, 4), done = done_packet(f.client);
receive(f.client, &chunk); receive(f.client, &done); REQUIRE(done_count == 1 && !done_err);
capture_blocks = 1;
receive(f.client, &requests[0]); REQUIRE(f.client->md.active_streams == 1);
if (variant) {
const enum io_fault operations[] = {IO_NONE, IO_READ, IO_SEEK, IO_CLOSE};
fault = operations[variant]; poll_ready(); REQUIRE(fault_hits == 1 && !f.client->md.active_streams);
REQUIRE(!f.client->md.streams && !f.client->md.file_loads->head && packet_count == 1);
REQUIRE(packets[0].data[0] == MEDIA_SUBCMD_BLOCK_DONE
&& ((struct media_pkt_block_done*)packets[0].data)->status == MEDIA_BLOCK_UNAVAILABLE);
} else {
receive(f.client, &requests[0]); REQUIRE(f.client->md.active_streams == 1);
receive(f.client, &requests[1]); receive(f.client, &requests[2]); receive(f.client, &requests[3]);
REQUIRE(f.client->md.active_streams == 3 && packet_count == 1 && packets[0].data[0] == MEDIA_SUBCMD_BLOCK_RELAY_FULL);
const struct media_pkt_block_relay_full* full = (const struct media_pkt_block_relay_full*)packets[0].data;
REQUIRE(full->num_relay_nodes == 3 && !memcmp(full->transfer_id,
((struct media_pkt_block_req*)requests[3].data)->transfer_id, 16));
struct packet cancel = cancellation(&requests[2]); receive(f.client, &cancel);
struct md_file_load* load = md_file_load_find(&f.client->md, f.metadata.media_id);
REQUIRE(f.client->md.active_streams == 2 && load->active_downloads == 2
&& load->downloader_ids[0] == 0x300 && load->downloader_ids[1] == 0x301);
cancel = cancellation(&requests[0]); receive(f.client, &cancel);
REQUIRE(load->active_downloads == 1 && load->downloader_ids[0] == 0x301);
receive(f.client, &requests[3]); REQUIRE(f.client->md.active_streams == 2);
media_delivery_cancel_channel(f.client, GROUP);
REQUIRE(!f.client->md.streams && !f.client->md.active_streams && !f.client->md.file_loads->head);
poll_ready(); REQUIRE(packet_count == 1); // Отменённые pump не отправили DATA/DONE.
}
char path[512]; snprintf(path, sizeof(path), "%s/result.bin", directory);
uint8_t hash[32]; REQUIRE(!ma_sha256_file(path, hash) && !memcmp(hash, f.metadata.content_hash, 32));
finish(&f);
}
puts("indexed source: idempotency, source admission/redirect, exact cancellation counters and TX I/O failures: PASS");
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR);
if (getenv("UTUN_TEST_DEBUG")) debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_DEBUG);
@@ -450,6 +500,7 @@ int main(void) {
snprintf(directory, sizeof(directory), "/tmp/utun-blocks-XXXXXX"); REQUIRE(!test_mkdtemp(directory));
test_identity_and_bounds(); test_rejected_attempts(); test_io_failures(); test_reentrant_cancel();
test_growing_relay(); test_relay_cancel(); test_destinations(); test_registration_failure_and_keys(); test_relay_limit_and_idle();
test_indexed_source();
REQUIRE(!rmdir(directory)); uasync_destroy(ua, 0);
puts("media block regressions: PASS"); return 0;
}