diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/runner.c | 1 | ||||
| -rw-r--r-- | tests/stubs.c | 9 | ||||
| -rw-r--r-- | tests/t_clean.c | 140 | ||||
| -rw-r--r-- | tests/test.h | 1 |
4 files changed, 145 insertions, 6 deletions
diff --git a/tests/runner.c b/tests/runner.c index ae2565b..2398991 100644 --- a/tests/runner.c +++ b/tests/runner.c | |||
| @@ -27,6 +27,7 @@ static const struct module modules[] = {{"ot_urlencoded", test_urlencoded}, | |||
| 27 | {"ot_socket", test_socket}, | 27 | {"ot_socket", test_socket}, |
| 28 | {"ot_mutex", test_mutex}, | 28 | {"ot_mutex", test_mutex}, |
| 29 | {"trackerlogic", test_trackerlogic}, | 29 | {"trackerlogic", test_trackerlogic}, |
| 30 | {"clean", test_clean}, | ||
| 30 | {"ot_workqueue", test_workqueue}, | 31 | {"ot_workqueue", test_workqueue}, |
| 31 | {"ot_batch", test_batch}, | 32 | {"ot_batch", test_batch}, |
| 32 | {"ot_fdstate", test_fdstate}, | 33 | {"ot_fdstate", test_fdstate}, |
diff --git a/tests/stubs.c b/tests/stubs.c index f7ac679..fd47a68 100644 --- a/tests/stubs.c +++ b/tests/stubs.c | |||
| @@ -26,14 +26,11 @@ long io_trywrite(long d, const char *buf, long len) { | |||
| 26 | /* trackerlogic.c is under test, and starting it up starts every module it | 26 | /* trackerlogic.c is under test, and starting it up starts every module it |
| 27 | leans on. None of them is driven by these tests, so they are answered here | 27 | leans on. None of them is driven by these tests, so they are answered here |
| 28 | rather than linked in and left to run threads of their own. */ | 28 | rather than linked in and left to run threads of their own. */ |
| 29 | void clean_init(void) {} | 29 | /* ot_clean.c is in the unit build for t_clean, so its thread control is the |
| 30 | void clean_deinit(void) {} | 30 | real thing and only the stats hook it calls is stubbed here. */ |
| 31 | int clean_single_torrent(ot_torrent *torrent) { | ||
| 32 | (void)torrent; | ||
| 33 | return 0; | ||
| 34 | } | ||
| 35 | void fullscrape_init(void) {} | 31 | void fullscrape_init(void) {} |
| 36 | void fullscrape_deinit(void) {} | 32 | void fullscrape_deinit(void) {} |
| 33 | void stats_cleanup(void) {} | ||
| 37 | void livesync_init(void) {} | 34 | void livesync_init(void) {} |
| 38 | void livesync_deinit(void) {} | 35 | void livesync_deinit(void) {} |
| 39 | void livesync_tell(struct ot_workstruct *ws) { (void)ws; } | 36 | void livesync_tell(struct ot_workstruct *ws) { (void)ws; } |
diff --git a/tests/t_clean.c b/tests/t_clean.c new file mode 100644 index 0000000..7eca9aa --- /dev/null +++ b/tests/t_clean.c | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. */ | ||
| 3 | |||
| 4 | #include <arpa/inet.h> | ||
| 5 | #include <signal.h> | ||
| 6 | #include <pthread.h> | ||
| 7 | #include <string.h> | ||
| 8 | |||
| 9 | #include "ot_clean.h" | ||
| 10 | #include "ot_mutex.h" | ||
| 11 | #include "trackerlogic.h" | ||
| 12 | #include "test.h" | ||
| 13 | |||
| 14 | /* A clock stepped backwards used to leave peer_list->base in the future, so | ||
| 15 | the span the cleaner works from came out negative. It was then added to a | ||
| 16 | peer's age and stored in the single byte that age lives in, where -1 reads | ||
| 17 | as 255 and the next pass threw the whole swarm away. */ | ||
| 18 | |||
| 19 | static void announce(struct ot_workstruct *ws, ot_hash *hash, uint8_t last_octet, int seeding) { | ||
| 20 | static const uint8_t v4mapped[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; | ||
| 21 | |||
| 22 | /* The peer list is written into the reply, which workstruct_init leaves | ||
| 23 | for its caller to point somewhere */ | ||
| 24 | ws->reply = ws->outbuf; | ||
| 25 | |||
| 26 | memset(&ws->peer, 0, sizeof(ws->peer)); | ||
| 27 | memcpy(&ws->peer, v4mapped, sizeof(v4mapped)); | ||
| 28 | ((uint8_t *)&ws->peer)[15] = last_octet; | ||
| 29 | OT_SETPORT(&ws->peer, &(uint16_t){6881}); | ||
| 30 | if (seeding) | ||
| 31 | OT_PEERFLAG(ws->peer) |= PEER_FLAG_SEEDING; | ||
| 32 | ws->hash = hash; | ||
| 33 | add_peer_to_torrent_and_return_peers(FLAG_TCP, ws, 0); | ||
| 34 | } | ||
| 35 | |||
| 36 | static size_t peers_in(ot_hash *hash) { | ||
| 37 | char reply[1024]; | ||
| 38 | size_t len = return_udp_scrape_for_torrent(*hash, reply); | ||
| 39 | uint32_t seeders, leechers; | ||
| 40 | |||
| 41 | if (len < 12) | ||
| 42 | return 0; | ||
| 43 | memcpy(&seeders, reply, 4); | ||
| 44 | memcpy(&leechers, reply + 8, 4); | ||
| 45 | return (size_t)ntohl(seeders) + (size_t)ntohl(leechers); | ||
| 46 | } | ||
| 47 | |||
| 48 | static ot_torrent *torrent_of(ot_hash *hash) { | ||
| 49 | ot_vector *bucket = mutex_bucket_lock_by_hash(*hash); | ||
| 50 | ot_torrent *t = (ot_torrent *)bucket->data; | ||
| 51 | ot_torrent *found = NULL; | ||
| 52 | size_t i; | ||
| 53 | |||
| 54 | for (i = 0; i < bucket->size; ++i) | ||
| 55 | if (!memcmp(t[i].hash, hash, sizeof(ot_hash))) | ||
| 56 | found = t + i; | ||
| 57 | mutex_bucket_unlock_by_hash(*hash, 0); | ||
| 58 | return found; | ||
| 59 | } | ||
| 60 | |||
| 61 | /* What a clock stepped back by `minutes` leaves behind */ | ||
| 62 | static void step_clock_back(ot_torrent *torrent, int minutes) { | ||
| 63 | torrent->peer_list6->base += minutes; | ||
| 64 | torrent->peer_list4->base += minutes; | ||
| 65 | } | ||
| 66 | |||
| 67 | /* What the next minute of a well behaved clock looks like */ | ||
| 68 | static void age_by_a_minute(ot_torrent *torrent) { | ||
| 69 | torrent->peer_list6->base -= 1; | ||
| 70 | torrent->peer_list4->base -= 1; | ||
| 71 | } | ||
| 72 | |||
| 73 | static void case_a_clock_that_went_back_keeps_the_swarm(void) { | ||
| 74 | struct ot_workstruct ws; | ||
| 75 | ot_hash hash; | ||
| 76 | ot_torrent *torrent; | ||
| 77 | |||
| 78 | test_case("a swarm survives the clock being stepped backwards"); | ||
| 79 | memset(hash, 0x31, sizeof(hash)); | ||
| 80 | CHECK_INT(workstruct_init(&ws, G_INBUF_SIZE), 0); | ||
| 81 | |||
| 82 | announce(&ws, &hash, 1, 1); | ||
| 83 | announce(&ws, &hash, 2, 0); | ||
| 84 | CHECK_INT(peers_in(&hash), 2); | ||
| 85 | |||
| 86 | torrent = torrent_of(&hash); | ||
| 87 | CHECK(torrent != NULL); | ||
| 88 | step_clock_back(torrent, 5); | ||
| 89 | |||
| 90 | /* The pass that sees the step keeps everybody either way: unclamped it | ||
| 91 | writes a negative span into each peer's age byte, where it reads as 251 | ||
| 92 | rather than as a removal. */ | ||
| 93 | CHECK_INT(clean_single_torrent(torrent), 0); | ||
| 94 | CHECK_INT(peers_in(&hash), 2); | ||
| 95 | |||
| 96 | /* A minute later, and this is where a poisoned age byte took the swarm: | ||
| 97 | 251 plus one minute is past any timeout. */ | ||
| 98 | age_by_a_minute(torrent); | ||
| 99 | CHECK_INT(clean_single_torrent(torrent), 0); | ||
| 100 | CHECK_INT(peers_in(&hash), 2); | ||
| 101 | |||
| 102 | workstruct_deinit(&ws); | ||
| 103 | } | ||
| 104 | |||
| 105 | static void case_an_idle_swarm_still_ages_out(void) { | ||
| 106 | struct ot_workstruct ws; | ||
| 107 | ot_hash hash; | ||
| 108 | ot_torrent *torrent; | ||
| 109 | |||
| 110 | test_case("a swarm nobody touched is still cleaned away"); | ||
| 111 | memset(hash, 0x32, sizeof(hash)); | ||
| 112 | CHECK_INT(workstruct_init(&ws, G_INBUF_SIZE), 0); | ||
| 113 | |||
| 114 | announce(&ws, &hash, 3, 1); | ||
| 115 | CHECK_INT(peers_in(&hash), 1); | ||
| 116 | |||
| 117 | torrent = torrent_of(&hash); | ||
| 118 | CHECK(torrent != NULL); | ||
| 119 | step_clock_back(torrent, -OT_PEER_TIMEOUT); | ||
| 120 | |||
| 121 | CHECK_INT(clean_single_torrent(torrent), 0); | ||
| 122 | CHECK_INT(peers_in(&hash), 0); | ||
| 123 | |||
| 124 | workstruct_deinit(&ws); | ||
| 125 | } | ||
| 126 | |||
| 127 | void test_clean(void) { | ||
| 128 | sigset_t hup; | ||
| 129 | |||
| 130 | /* accesslist_deinit sends SIGHUP to its reader, trackerlogic_init starts | ||
| 131 | the accesslist, and the test process must not take it */ | ||
| 132 | sigemptyset(&hup); | ||
| 133 | sigaddset(&hup, SIGHUP); | ||
| 134 | pthread_sigmask(SIG_BLOCK, &hup, NULL); | ||
| 135 | |||
| 136 | trackerlogic_init(); | ||
| 137 | case_a_clock_that_went_back_keeps_the_swarm(); | ||
| 138 | case_an_idle_swarm_still_ages_out(); | ||
| 139 | trackerlogic_deinit(); | ||
| 140 | } | ||
diff --git a/tests/test.h b/tests/test.h index 82ec178..9b569c7 100644 --- a/tests/test.h +++ b/tests/test.h | |||
| @@ -86,6 +86,7 @@ void test_ip(void); | |||
| 86 | void test_socket(void); | 86 | void test_socket(void); |
| 87 | void test_mutex(void); | 87 | void test_mutex(void); |
| 88 | void test_trackerlogic(void); | 88 | void test_trackerlogic(void); |
| 89 | void test_clean(void); | ||
| 89 | void test_workqueue(void); | 90 | void test_workqueue(void); |
| 90 | void test_batch(void); | 91 | void test_batch(void); |
| 91 | void test_fdstate(void); | 92 | void test_fdstate(void); |
