#define _POSIX_C_SOURCE 200809L #define main dat_thang_program_main #include "../src/ddat_gui.c" #undef main #include #include #include #ifdef _WIN32 #include #include #define test_mkdir(path) _mkdir(path) #define test_setenv(name, value) _putenv_s((name), (value)) #define test_pid _getpid #define test_close_socket(fd) closesocket(fd) typedef int test_socklen_t; typedef SOCKET test_socket_t; #define test_socket_valid(fd) ((fd) != INVALID_SOCKET) #else #include #include #include #define test_mkdir(path) mkdir((path), 0700) #define test_setenv(name, value) setenv((name), (value), 1) #define test_pid getpid #define test_close_socket(fd) close(fd) typedef socklen_t test_socklen_t; typedef int test_socket_t; #define test_socket_valid(fd) ((fd) >= 0) #endif static uint16_t unused_udp_port(void) { test_socket_t fd = socket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in address; test_socklen_t length = sizeof(address); uint16_t port; assert(test_socket_valid(fd)); memset(&address, 0, sizeof(address)); address.sin_family = AF_INET; address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); assert(bind(fd, (struct sockaddr *)&address, sizeof(address)) == 0); assert(getsockname(fd, (struct sockaddr *)&address, &length) == 0); port = ntohs(address.sin_port); test_close_socket(fd); return port; } static void pause_one_millisecond(void) { SDL_Delay(1); } static void set_network_environment(uint16_t local_port, uint16_t bootstrap_port) { char value[32]; snprintf(value, sizeof(value), "%u", (unsigned)local_port); assert(test_setenv("DDAT_PORT", value) == 0); snprintf(value, sizeof(value), "127.0.0.1:%u", (unsigned)bootstrap_port); assert(test_setenv("DDAT_BOOTSTRAP", value) == 0); } static int peer_ready(const gui_state_t *state) { size_t i; if (!state->session_initialized) return 0; for (i = 0; i < state->session.peer_count; i++) if (state->session.peers[i].state == DAT_SESSION_PEER_READY) return 1; return 0; } #ifdef DAT_GUI_TEST_MEDIA static int test_fm_audio_is_playing(gui_state_t *state) { gui_fm_voice_t *voice = (gui_fm_voice_t *)state->fm_voice_ptr; int active; if (!voice || !state->audio_device) return 0; SDL_LockAudioDevice(state->audio_device); active = voice->active; SDL_UnlockAudioDevice(state->audio_device); return active; } #endif int main(void) { static gui_state_t left, right; char root[1024]; char left_home[1024], right_home[1024], file_path[1200], destination[1200]; char feed_directory[1024]; uint8_t content_key[32]; uint16_t left_port, right_port; Uint32 now; int elapsed; #ifdef _WIN32 WSADATA winsock; int fixture_attempt; assert(WSAStartup(MAKEWORD(2, 2), &winsock) == 0); for (fixture_attempt = 0; fixture_attempt < 1000; fixture_attempt++) { assert(snprintf(root, sizeof(root), "build/windows/test-gui-p2p-%ld-%d", (long)test_pid(), fixture_attempt) < (int)sizeof(root)); if (test_mkdir(root) == 0) break; } assert(fixture_attempt < 1000); #else strcpy(root, "/tmp/ddat-gui-p2p-XXXXXX"); #endif memset(&left, 0, sizeof(left)); memset(&right, 0, sizeof(right)); left_port = unused_udp_port(); right_port = unused_udp_port(); if (right_port == left_port) right_port = (uint16_t)(left_port + 1); #ifndef _WIN32 assert(mkdtemp(root) != NULL); #endif assert(snprintf(file_path, sizeof(file_path), "%s/payload.txt", root) < (int)sizeof(file_path)); { FILE *file = fopen(file_path, "wb"); assert(file != NULL); assert(fputs("a real GUI Cesspool to uTP to SLEEP block", file) >= 0); assert(fclose(file) == 0); } assert(snprintf(left_home, sizeof(left_home), "%s/left-home", root) < (int)sizeof(left_home)); assert(snprintf(right_home, sizeof(right_home), "%s/right-home", root) < (int)sizeof(right_home)); assert(test_mkdir(left_home) == 0); assert(test_mkdir(right_home) == 0); assert(snprintf(destination, sizeof(destination), "%s/download", root) < (int)sizeof(destination)); assert(test_mkdir(destination) == 0); assert(test_setenv("HOME", left_home) == 0); assert(test_setenv("USERPROFILE", left_home) == 0); #ifdef DAT_GUI_TEST_MEDIA assert(SDL_Init(SDL_INIT_TIMER | SDL_INIT_AUDIO) == 0); #else assert(SDL_Init(SDL_INIT_TIMER) == 0); #endif assert(dat_scheme_runtime_init(&left.metadata_scheme) == 0); strcpy(left.archive_path, root); strcpy(right.archive_path, destination); assert(prepare_archive(&left) == 0); assert(left.archive_open && left.version >= 1); left.selected_version = 0; assert(click_archive_version_bar(&left, 210, 80, 84)); assert(left.selected_version == 0); assert(!click_archive_version_bar(&left, 209, 80, 84)); assert(!click_archive_version_bar(&left, 210, 73, 84)); assert(!click_archive_version_bar(&left, 242, 80, 84)); assert(click_archive_version_bar(&left, 226, 80, 84)); assert(left.selected_version == 1); left.selected_version = left.version; assert(click_archive_version_bar(&left, 226, 80, 84)); assert(left.selected_version == left.version); assert(click_archive_version_bar(&left, 210, 80, 84)); assert(left.selected_version == left.version - 1); { dat_link_t link, parsed; char address[DAT_LINK_BYTES]; memset(&link, 0, sizeof(link)); memcpy(link.key, left.archive_key_bytes, sizeof(link.key)); link.has_version = 1; link.version = left.selected_version; assert(dat_link_format(&link, address, sizeof(address)) == 0); assert(dat_link_parse(address, &parsed) == 0); assert(parsed.has_version && parsed.version == left.selected_version); } { dat_archive_t source; char uri[DAT_URL_BYTES]; assert(dat_archive_open(&source, root) == 0); memcpy(right.remote_metadata_key, source.metadata_key, sizeof(right.remote_metadata_key)); right.remote_key_valid = 1; assert(dat_key_to_url(source.metadata_key, uri) == 0); strcpy(right.remote_address, uri); strcpy(right.remote_archive, uri + 6); strcpy(right.remote_site, "P2P TEST SOURCE"); strcpy(right.remote_version, "LATEST"); assert(strlen(destination) + sizeof("/.dat") <= sizeof(feed_directory)); strcpy(feed_directory, destination); strcat(feed_directory, "/.dat"); memcpy(content_key, source.content_key, sizeof(content_key)); dat_archive_close(&source); } assert(sleep_crypto_init() == 0); set_network_environment(left_port, right_port); network_start(&left); assert(left.network_requested && left.session_initialized); assert(!left.online); /* Reachability starts false until a response arrives. */ assert(test_setenv("HOME", right_home) == 0); assert(test_setenv("USERPROFILE", right_home) == 0); set_network_environment(right_port, left_port); network_start_remote_import(&right); assert(right.network_requested && right.session_initialized); assert(!right.online); assert(!memcmp(right.identity.pubkey, right.pool.net.peerid, 32)); for (elapsed = 0; elapsed < 12000; elapsed++) { now = SDL_GetTicks(); network_pump(&left, now); if (memcmp(right.identity.pubkey, right.pool.net.peerid, 32)) { fprintf(stderr, "left client pump overwrote right Cesspool identity at %d ms\\n", elapsed); abort(); } network_pump(&right, now); if (memcmp(right.identity.pubkey, right.pool.net.peerid, 32)) { size_t changed; for (changed = 0; changed < 32 && right.identity.pubkey[changed] == right.pool.net.peerid[changed]; changed++) {} fprintf(stderr, "right identity changed during P2P pump at %d ms: byte %lu %02x -> %02x pool=%d online=%d status=%s\n", elapsed, (unsigned long)changed, right.identity.pubkey[changed], right.pool.net.peerid[changed], right.pool_initialized, right.online, right.network_status); abort(); } if (peer_ready(&left) && peer_ready(&right) && right.session.content_key_known && sleep_feed_has(&right.content_feed, 1)) break; pause_one_millisecond(); } if (!peer_ready(&left) || !peer_ready(&right)) { fprintf(stderr, "peer setup timeout: L=%02x%02x R=%02x%02x left discovered=%lu peers=%lu ps=%d mask=%u rx=%lu tx=%llu/%llu status=%s; right discovered=%lu peers=%lu ps=%d mask=%u rx=%lu tx=%llu/%llu status=%s\n", left.identity.pubkey[0],left.identity.pubkey[1],right.identity.pubkey[0],right.identity.pubkey[1], (unsigned long)discovered_peer_count(&left), (unsigned long)left.session.peer_count, left.session.peers[0].state, left.session.peers[0].remote_feed_mask, (unsigned long)left.session.peers[0].rx_buffer_len, (unsigned long long)left.bytes_received, (unsigned long long)left.bytes_sent, left.network_status, (unsigned long)discovered_peer_count(&right), (unsigned long)right.session.peer_count, right.session.peers[0].state, right.session.peers[0].remote_feed_mask, (unsigned long)right.session.peers[0].rx_buffer_len, (unsigned long long)right.bytes_received, (unsigned long long)right.bytes_sent, right.network_status); for (size_t i = 0; i < GUI_TRANSPORT_MAX; i++) { if (left.transports[i].used) fprintf(stderr, "left transport %lu state=%d wait=%d auth=%d hello=%d/%d ack=%d/%d seq=%u recv=%u inflight=%lu\n", (unsigned long)i, left.transports[i].utp.state, left.transports[i].waiting_incoming, left.transports[i].authenticated, left.transports[i].hello_sent, left.transports[i].hello_received, left.transports[i].ack_sent, left.transports[i].ack_received, left.transports[i].utp.sequence, left.transports[i].utp.acknowledgement, (unsigned long)left.transports[i].utp.bytes_in_flight); if (right.transports[i].used) fprintf(stderr, "right transport %lu state=%d wait=%d auth=%d hello=%d/%d ack=%d/%d seq=%u recv=%u inflight=%lu\n", (unsigned long)i, right.transports[i].utp.state, right.transports[i].waiting_incoming, right.transports[i].authenticated, right.transports[i].hello_sent, right.transports[i].hello_received, right.transports[i].ack_sent, right.transports[i].ack_received, right.transports[i].utp.sequence, right.transports[i].utp.acknowledgement, (unsigned long)right.transports[i].utp.bytes_in_flight); } for (size_t i = 0; i < left.pool.peer_count; i++) fprintf(stderr, "left known peer %lu port=%u topic=%d\n", (unsigned long)i, ntohs(((struct sockaddr_in *)&left.pool.peers[i].address.addr)->sin_port), !memcmp(left.pool.peers[i].discovery_topic, left.discovery_topic, 32)); for (size_t i = 0; i < right.pool.peer_count; i++) fprintf(stderr, "right known peer %lu port=%u topic=%d\n", (unsigned long)i, ntohs(((struct sockaddr_in *)&right.pool.peers[i].address.addr)->sin_port), !memcmp(right.pool.peers[i].discovery_topic, right.discovery_topic, 32)); abort(); } assert(right.content_feed.length > 1); assert(left.session.peers[0].payload_bytes_sent > 0); assert(right.session.peers[0].payload_bytes_received > 0); assert(sleep_feed_has(&right.content_feed, 1)); if (!transport_by_id(&left, right.identity.pubkey)) { fprintf(stderr, "identity mismatch: L identity=%02x pool=%02x session=%02x; R identity=%02x pool=%02x session=%02x\n", left.identity.pubkey[0], left.pool.net.peerid[0], left.session.peer_id[0], right.identity.pubkey[0], right.pool.net.peerid[0], right.session.peer_id[0]); for (size_t i = 0; i < GUI_TRANSPORT_MAX; i++) fprintf(stderr, "left transport slot %lu used=%d state=%d auth=%d peer=%02x expected=%02x sessionpeer=%02x session=%d\n", (unsigned long)i, left.transports[i].used, left.transports[i].utp.state, left.transports[i].authenticated, left.transports[i].peer_id[0], right.identity.pubkey[0], left.session.peers[0].peer_id[0], left.session.peers[0].state); abort(); } assert(transport_by_id(&left, right.identity.pubkey)->authenticated); assert(transport_by_id(&right, left.identity.pubkey) != NULL); assert(transport_by_id(&right, left.identity.pubkey)->authenticated); transfer_disconnect(&left, 0, 0); assert(left.transfer_paused[0] && left.session.peers[0].paused); assert(peer_ready(&left) && peer_ready(&right)); transfer_resume(&left, 0); assert(!left.transfer_paused[0] && !left.session.peers[0].paused); for (elapsed = 0; elapsed < 5000; elapsed++) { now = SDL_GetTicks(); network_pump(&left, now); network_pump(&right, now); if (peer_ready(&left) && peer_ready(&right)) break; pause_one_millisecond(); } if (!peer_ready(&left) || !peer_ready(&right)) fprintf(stderr, "resume timeout IDs=%02x/%02x: left session=%d transport=%d/%d wait=%d auth=%d hello=%d/%d ack=%d/%d right session=%d transport=%d/%d wait=%d auth=%d hello=%d/%d ack=%d/%d statuses=%s | %s\n", left.identity.pubkey[0], right.identity.pubkey[0], left.session.peers[0].state, left.transports[0].used, left.transports[0].utp.state, left.transports[0].waiting_incoming, left.transports[0].authenticated, left.transports[0].hello_sent, left.transports[0].hello_received, left.transports[0].ack_sent, left.transports[0].ack_received, right.session.peers[0].state, right.transports[0].used, right.transports[0].utp.state, right.transports[0].waiting_incoming, right.transports[0].authenticated, right.transports[0].hello_sent, right.transports[0].hello_received, right.transports[0].ack_sent, right.transports[0].ack_received, left.network_status, right.network_status); assert(peer_ready(&left) && peer_ready(&right)); assert(left.online && right.online); memcpy(left.chat_peer_id,right.identity.pubkey,32); left.chat_open=1;strcpy(left.chat_input,"hello from cable over the authenticated peer link"); if (cable_chat_send(&left) != 0) { gui_transport_peer_t *chat_transport = transport_by_id(&left, left.chat_peer_id); fprintf(stderr, "chat after resume: transport=%p used=%d auth=%d utp=%d session=%d errno=%d\n", (void *)chat_transport, chat_transport ? chat_transport->used : 0, chat_transport ? chat_transport->authenticated : 0, chat_transport ? chat_transport->utp.state : 0, left.session.peers[0].state, errno); abort(); } for(elapsed=0;elapsed<3000;elapsed++){ now=SDL_GetTicks();network_pump(&left,now);network_pump(&right,now); if(right.chat_line_count)break; pause_one_millisecond(); } assert(right.chat_line_count==1); assert(!strcmp(right.chat_lines[0].text,"hello from cable over the authenticated peer link")); assert(!right.chat_lines[0].outgoing); { uint8_t *received = NULL; size_t received_length = 0; dat_archive_t downloaded; sleep_metadata_entry_t entry; const char expected[] = "a real GUI Cesspool to uTP to SLEEP block"; assert(dat_archive_open(&downloaded, destination) == 0); assert(dat_archive_find(&downloaded, "payload.txt", &entry) == 0); assert(dat_archive_read_file(&downloaded, &entry, &received, &received_length) == 0); assert(received_length == sizeof(expected) - 1); assert(!memcmp(received, expected, received_length)); free(received); received = NULL; dat_archive_close(&downloaded); assert(sleep_feed_read(&right.content_feed, 1, &received, &received_length) == 0); /* A content feed block is not the same thing as one source file: the * archive reader reconstructs files from metadata offsets and blocks. */ assert(received_length > 0); free(received); } sleep_feed_close(&right.content_feed); assert(sleep_feed_open_remote(&right.content_feed, feed_directory, "content", content_key) == 0); assert(sleep_feed_has(&right.content_feed, 1)); { sleep_feed_t restored; memset(&restored, 0, sizeof(restored)); strcpy(restored.directory, feed_directory); strcpy(restored.name, "content"); assert(sleep_feed_load_remote(&restored, content_key) == 0); assert(sleep_feed_has(&restored, 1)); sleep_feed_close(&restored); } #ifdef DAT_GUI_TEST_MEDIA left.audio_enabled = 1; fm_audio_start(&left); assert(left.audio_device != 0); audio_cue_broadcast(&right, 1); for (elapsed = 0; elapsed < 2000; elapsed++) { now = SDL_GetTicks(); network_pump(&left, now); network_pump(&right, now); if (((gui_fm_voice_t *)left.fm_voice_ptr)->dolphin && test_fm_audio_is_playing(&left)) break; pause_one_millisecond(); } assert(((gui_fm_voice_t *)left.fm_voice_ptr)->dolphin); assert(test_fm_audio_is_playing(&left)); fm_audio_play(&left); assert(test_fm_audio_is_playing(&left)); SDL_Delay(2100); assert(!test_fm_audio_is_playing(&left)); fm_audio_stop(&left); #endif network_stop(&right); network_stop(&left); dat_scheme_runtime_close(&left.metadata_scheme); SDL_Quit(); #ifdef _WIN32 WSACleanup(); #endif #ifdef DAT_GUI_TEST_MEDIA puts("dat-thang authenticated Cesspool transfer, chat, and dolphin cue: PASS"); #else puts("dat-thang authenticated Cesspool archive transfer, pause/resume, and chat: PASS"); #endif return 0; }