Files
ToastyFS/src/test_client.c
T
Claude cd3ecec786 Fix two bugs in read operation that caused test failure
1. Fix client not reading all server addresses in READ response
   The metadata server sends address info for ALL chunk servers holding
   a chunk, but the client only read info for ONE server. This caused
   message parsing to become misaligned for subsequent chunks.

   Added a loop to iterate through all num_servers and read each
   server's address list (using the first one, skipping the rest).

2. Fix tag value conflict between metadata and chunk server responses
   TAG_RETRIEVE_METADATA_FOR_READ was 1, which conflicted with chunk
   download range indices (0, 1, 2, 3...). When the second chunk
   download response came with range_idx=1, it was incorrectly treated
   as a metadata response.

   Changed TAG values from positive to negative integers since range
   indices are always non-negative.

Also updated the test to properly exit on success instead of assert(0).

https://claude.ai/code/session_01QYDQCNiUj1cjrsS6qRgkvd
2026-01-27 22:15:20 +00:00

188 lines
5.5 KiB
C

#ifdef MAIN_TEST
#define QUAKEY_ENABLE_MOCKS
#include <quakey.h>
#include <assert.h>
#include "tcp.h"
#include "test_client.h"
// Helper function to parse address and port from command line
static bool parse_server_addr(int argc, char **argv, char **addr, uint16_t *port)
{
// Default to metadata server
*addr = "127.0.0.1";
*port = 8080;
for (int i = 0; i < argc - 1; i++) {
if (!strcmp(argv[i], "--server") || !strcmp(argv[i], "-s")) {
*addr = argv[i + 1];
if (i + 2 < argc) {
errno = 0;
char *end;
long val = strtol(argv[i+2], &end, 10);
if (end == argv[i+2] || *end != '\0' || errno == ERANGE)
break;
if (val < 0 || val > UINT16_MAX)
break;
*port = (uint16_t) val;
return true;
}
}
}
return true;
}
int test_client_init(void *state_, int argc, char **argv,
void **ctxs, struct pollfd *pdata, int pcap, int *pnum,
int *timeout)
{
TestClient *client = state_;
char *addr;
uint16_t port;
parse_server_addr(argc, argv, &addr, &port);
client->toasty = toasty_connect((ToastyString) { addr, strlen(addr) }, port);
if (client->toasty == NULL)
return -1;
client->state = TEST_CLIENT_STATE_0;
client->tick = 0;
printf("Client set up (remote=%s:%d)\n", addr, port);
*timeout = -1;
if (pcap < TCP_POLL_CAPACITY)
return -1;
*pnum = toasty_process_events(client->toasty, ctxs, pdata, *pnum);
return 0;
}
static int random_in_range(int min, int max)
{
uint64_t n = quakey_random();
return min + n % (max - min + 1);
}
int test_client_tick(void *state_, void **ctxs,
struct pollfd *pdata, int pcap, int *pnum,
int *timeout)
{
TestClient *client = state_;
// Process any pending events from the network and get new poll descriptors
*pnum = toasty_process_events(client->toasty, ctxs, pdata, *pnum);
// This must be static as write operations will refer to this data
// after the function has returned
static char msg[] =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
"enim ad minim veniam, quis nostrud exercitation ullamco laboris "
"nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor "
"in reprehenderit in voluptate velit esse cillum dolore eu fugiat "
"nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
"sunt in culpa qui officia deserunt mollit anim id est laborum.";
ToastyString file_path = TOASTY_STR("some_file.txt");
switch (client->state) {
int ret;
ToastyResult result;
case TEST_CLIENT_STATE_0:
if (client->tick < 10) {
*timeout = 0;
client->tick++;
return 0;
}
client->handle = toasty_begin_create_file(client->toasty, file_path, 128);
if (client->handle == TOASTY_INVALID) {
assert(0); // TODO
}
printf("Create started\n");
client->state = TEST_CLIENT_STATE_1;
break;
case TEST_CLIENT_STATE_1:
ret = toasty_get_result(client->toasty, client->handle, &result);
if (ret < 0) {
assert(0); // TODO
}
if (ret == 1) {
break;
}
printf("Create completed\n");
assert(ret == 0);
if (result.type != TOASTY_RESULT_CREATE_SUCCESS) {
assert(0); // TODO
}
client->handle = toasty_begin_write(client->toasty, file_path, 0, msg, sizeof(msg)-1, TOASTY_VERSION_TAG_EMPTY, 0);
if (client->handle == TOASTY_INVALID) {
assert(0); // TODO
}
printf("Write started\n");
client->state = TEST_CLIENT_STATE_2;
break;
case TEST_CLIENT_STATE_2:
ret = toasty_get_result(client->toasty, client->handle, &result);
if (ret < 0) {
assert(0); // TODO
}
if (ret == 1) {
break;
}
printf("Write completed\n");
assert(ret == 0);
if (result.type != TOASTY_RESULT_WRITE_SUCCESS) {
assert(0); // TODO
}
client->handle = toasty_begin_read(client->toasty, file_path, 0, client->buf, sizeof(client->buf), TOASTY_VERSION_TAG_EMPTY);
if (client->handle == TOASTY_INVALID) {
assert(0); // TODO
}
printf("Read started\n");
client->state = TEST_CLIENT_STATE_3;
break;
case TEST_CLIENT_STATE_3:
ret = toasty_get_result(client->toasty, client->handle, &result);
if (ret < 0) {
assert(0); // TODO
}
if (ret == 1) {
break;
}
printf("Read completed\n");
assert(ret == 0);
if (result.type != TOASTY_RESULT_READ_SUCCESS) {
assert(0); // TODO
}
if (result.bytes_read != sizeof(msg)-1) {
assert(0); // TODO
}
if (memcmp(client->buf, msg, sizeof(msg)-1)) {
assert(0); // TODO
}
printf("Test PASSED: Read data matches written data\n");
exit(0); // Test completed successfully
}
*timeout = -1;
if (pcap < TCP_POLL_CAPACITY)
return -1;
*pnum = toasty_process_events(client->toasty, ctxs, pdata, 0);
return 0;
}
int test_client_free(void *state_)
{
TestClient *client = state_;
toasty_disconnect(client->toasty);
return 0;
}
#endif // MAIN_TEST