/* This software was written by Dirk Engling It is considered beerware. Prost. Skol. Cheers or whatever. $id$ */ /* System */ #include #include #include #include /* Opentracker */ #include "ot_event.h" #include "test.h" /* Against real socket pairs: only the kernel can say whether a descriptor is ready, so there is nothing useful to mock here. */ #define EVENTS 16 static int sv[2]; static ot_event ready[EVENTS]; static void pair_open(void) { if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) sv[0] = sv[1] = -1; } static void pair_close(void) { if (sv[0] != -1) close(sv[0]); if (sv[1] != -1) close(sv[1]); sv[0] = sv[1] = -1; } static int wait_for(int fd, int what, int timeout_ms, uint32_t *generation) { int count = event_wait(ready, EVENTS, timeout_ms), i; for (i = 0; i < count; ++i) if (ready[i].fd == fd && (ready[i].what & what)) { if (generation) *generation = ready[i].generation; return 1; } return 0; } static unsigned long elapsed_ms(struct timeval *since) { struct timeval now; gettimeofday(&now, NULL); return (unsigned long)((now.tv_sec - since->tv_sec) * 1000 + (now.tv_usec - since->tv_usec) / 1000); } static void case_readable(void) { uint32_t generation = 0; test_case("a descriptor with something to read is reported, with its generation"); pair_open(); CHECK(sv[0] != -1); CHECK_INT(event_add(sv[0], EVENT_READ, 4711), 0); CHECK_INT(write(sv[1], "x", 1), 1); CHECK_INT(wait_for(sv[0], EVENT_READ, 1000, &generation), 1); CHECK_INT(generation, 4711); test_case("and keeps being reported until it is read"); CHECK_INT(wait_for(sv[0], EVENT_READ, 1000, NULL), 1); { char scratch[4]; CHECK_INT(read(sv[0], scratch, sizeof(scratch)), 1); } CHECK_INT(wait_for(sv[0], EVENT_READ, 50, NULL), 0); CHECK_INT(event_del(sv[0]), 0); pair_close(); } static void case_writable(void) { test_case("a descriptor with room to write is reported"); pair_open(); CHECK(sv[0] != -1); CHECK_INT(event_add(sv[0], EVENT_WRITE, 1), 0); CHECK_INT(wait_for(sv[0], EVENT_WRITE, 1000, NULL), 1); test_case("and is not, once we say we no longer care"); CHECK_INT(event_modify(sv[0], EVENT_READ, 1), 0); CHECK_INT(wait_for(sv[0], EVENT_WRITE, 50, NULL), 0); CHECK_INT(event_del(sv[0]), 0); pair_close(); } static void case_interest_is_separate(void) { test_case("asking only about writing does not report reading"); pair_open(); CHECK(sv[0] != -1); CHECK_INT(event_add(sv[0], EVENT_WRITE, 1), 0); CHECK_INT(write(sv[1], "x", 1), 1); CHECK_INT(wait_for(sv[0], EVENT_READ, 50, NULL), 0); test_case("until we ask about it"); CHECK_INT(event_modify(sv[0], EVENT_READ | EVENT_WRITE, 1), 0); CHECK_INT(wait_for(sv[0], EVENT_READ, 1000, NULL), 1); CHECK_INT(event_del(sv[0]), 0); pair_close(); } static void case_removed_is_silent(void) { test_case("a removed descriptor is not reported again"); pair_open(); CHECK(sv[0] != -1); CHECK_INT(event_add(sv[0], EVENT_READ, 1), 0); CHECK_INT(write(sv[1], "x", 1), 1); CHECK_INT(wait_for(sv[0], EVENT_READ, 1000, NULL), 1); CHECK_INT(event_del(sv[0]), 0); CHECK_INT(wait_for(sv[0], EVENT_READ, 50, NULL), 0); pair_close(); } static void case_generations_do_not_mix(void) { int other[2]; uint32_t generation = 0; test_case("two descriptors carry their own generations"); pair_open(); CHECK(sv[0] != -1); CHECK_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, other), 0); CHECK_INT(event_add(sv[0], EVENT_READ, 100), 0); CHECK_INT(event_add(other[0], EVENT_READ, 200), 0); CHECK_INT(write(sv[1], "x", 1), 1); CHECK_INT(write(other[1], "y", 1), 1); CHECK_INT(wait_for(sv[0], EVENT_READ, 1000, &generation), 1); CHECK_INT(generation, 100); CHECK_INT(wait_for(other[0], EVENT_READ, 1000, &generation), 1); CHECK_INT(generation, 200); test_case("and re-registering one changes only its own"); CHECK_INT(event_modify(sv[0], EVENT_READ, 101), 0); CHECK_INT(wait_for(sv[0], EVENT_READ, 1000, &generation), 1); CHECK_INT(generation, 101); CHECK_INT(wait_for(other[0], EVENT_READ, 1000, &generation), 1); CHECK_INT(generation, 200); event_del(sv[0]); event_del(other[0]); close(other[0]); close(other[1]); pair_close(); } /* Removing one of several has to leave the rest addressable: a backend that keeps its descriptors packed moves the last one into the gap, and has to remember where it went. */ static void case_delete_from_the_middle(void) { int pair[3][2]; uint32_t generation = 0; int i; test_case("deleting one of three leaves the others reported correctly"); for (i = 0; i < 3; ++i) { CHECK_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, pair[i]), 0); CHECK_INT(event_add(pair[i][0], EVENT_READ, (uint32_t)(10 + i)), 0); } CHECK_INT(event_del(pair[1][0]), 0); for (i = 0; i < 3; i += 2) { CHECK_INT(write(pair[i][1], "x", 1), 1); CHECK_INT(wait_for(pair[i][0], EVENT_READ, 1000, &generation), 1); CHECK_INT(generation, 10 + i); } test_case("and the survivors can still be changed and removed"); CHECK_INT(event_modify(pair[2][0], EVENT_READ, 99), 0); CHECK_INT(wait_for(pair[2][0], EVENT_READ, 1000, &generation), 1); CHECK_INT(generation, 99); CHECK_INT(event_del(pair[0][0]), 0); CHECK_INT(event_del(pair[2][0]), 0); CHECK_INT(wait_for(pair[2][0], EVENT_READ, 50, NULL), 0); for (i = 0; i < 3; ++i) close(pair[i][0]), close(pair[i][1]); } static void case_timeout(void) { struct timeval started; test_case("waiting on nothing comes back empty when the time is up"); gettimeofday(&started, NULL); CHECK_INT(event_wait(ready, EVENTS, 200), 0); { unsigned long took = elapsed_ms(&started); /* Scheduling makes the upper bound generous. The point is that it waited rather than returning at once, and that it came back at all */ CHECK(took >= 150); CHECK(took < 5000); } } static void case_unknown_descriptor(void) { test_case("changing or removing one that was never added is refused"); CHECK_INT(event_modify(4096, EVENT_READ, 1), -1); CHECK_INT(event_del(4096), -1); } void test_event(void) { CHECK_INT(event_init(), 0); CHECK(event_mechanism() != NULL); case_readable(); case_writable(); case_interest_is_separate(); case_removed_is_silent(); case_generations_do_not_mix(); case_delete_from_the_middle(); case_timeout(); case_unknown_descriptor(); event_deinit(); }