diff options
Diffstat (limited to 'tests/t_event.c')
| -rw-r--r-- | tests/t_event.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/tests/t_event.c b/tests/t_event.c new file mode 100644 index 0000000..5509f19 --- /dev/null +++ b/tests/t_event.c | |||
| @@ -0,0 +1,222 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. | ||
| 3 | |||
| 4 | $id$ */ | ||
| 5 | |||
| 6 | /* System */ | ||
| 7 | #include <string.h> | ||
| 8 | #include <sys/socket.h> | ||
| 9 | #include <sys/time.h> | ||
| 10 | #include <unistd.h> | ||
| 11 | |||
| 12 | /* Opentracker */ | ||
| 13 | #include "ot_event.h" | ||
| 14 | #include "test.h" | ||
| 15 | |||
| 16 | /* Against real socket pairs: only the kernel can say whether a descriptor is | ||
| 17 | ready, so there is nothing useful to mock here. */ | ||
| 18 | |||
| 19 | #define EVENTS 16 | ||
| 20 | |||
| 21 | static int sv[2]; | ||
| 22 | static ot_event ready[EVENTS]; | ||
| 23 | |||
| 24 | static void pair_open(void) { | ||
| 25 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) | ||
| 26 | sv[0] = sv[1] = -1; | ||
| 27 | } | ||
| 28 | |||
| 29 | static void pair_close(void) { | ||
| 30 | if (sv[0] != -1) | ||
| 31 | close(sv[0]); | ||
| 32 | if (sv[1] != -1) | ||
| 33 | close(sv[1]); | ||
| 34 | sv[0] = sv[1] = -1; | ||
| 35 | } | ||
| 36 | |||
| 37 | static int wait_for(int fd, int what, int timeout_ms, uint32_t *generation) { | ||
| 38 | int count = event_wait(ready, EVENTS, timeout_ms), i; | ||
| 39 | |||
| 40 | for (i = 0; i < count; ++i) | ||
| 41 | if (ready[i].fd == fd && (ready[i].what & what)) { | ||
| 42 | if (generation) | ||
| 43 | *generation = ready[i].generation; | ||
| 44 | return 1; | ||
| 45 | } | ||
| 46 | return 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | static unsigned long elapsed_ms(struct timeval *since) { | ||
| 50 | struct timeval now; | ||
| 51 | |||
| 52 | gettimeofday(&now, NULL); | ||
| 53 | return (unsigned long)((now.tv_sec - since->tv_sec) * 1000 + (now.tv_usec - since->tv_usec) / 1000); | ||
| 54 | } | ||
| 55 | |||
| 56 | static void case_readable(void) { | ||
| 57 | uint32_t generation = 0; | ||
| 58 | |||
| 59 | test_case("a descriptor with something to read is reported, with its generation"); | ||
| 60 | pair_open(); | ||
| 61 | CHECK(sv[0] != -1); | ||
| 62 | CHECK_INT(event_add(sv[0], EVENT_IN, 4711), 0); | ||
| 63 | CHECK_INT(write(sv[1], "x", 1), 1); | ||
| 64 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, &generation), 1); | ||
| 65 | CHECK_INT(generation, 4711); | ||
| 66 | |||
| 67 | test_case("and keeps being reported until it is read"); | ||
| 68 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, NULL), 1); | ||
| 69 | { | ||
| 70 | char scratch[4]; | ||
| 71 | CHECK_INT(read(sv[0], scratch, sizeof(scratch)), 1); | ||
| 72 | } | ||
| 73 | CHECK_INT(wait_for(sv[0], EVENT_IN, 50, NULL), 0); | ||
| 74 | |||
| 75 | CHECK_INT(event_del(sv[0]), 0); | ||
| 76 | pair_close(); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void case_writable(void) { | ||
| 80 | test_case("a descriptor with room to write is reported"); | ||
| 81 | pair_open(); | ||
| 82 | CHECK(sv[0] != -1); | ||
| 83 | CHECK_INT(event_add(sv[0], EVENT_OUT, 1), 0); | ||
| 84 | CHECK_INT(wait_for(sv[0], EVENT_OUT, 1000, NULL), 1); | ||
| 85 | |||
| 86 | test_case("and is not, once we say we no longer care"); | ||
| 87 | CHECK_INT(event_modify(sv[0], EVENT_IN, 1), 0); | ||
| 88 | CHECK_INT(wait_for(sv[0], EVENT_OUT, 50, NULL), 0); | ||
| 89 | |||
| 90 | CHECK_INT(event_del(sv[0]), 0); | ||
| 91 | pair_close(); | ||
| 92 | } | ||
| 93 | |||
| 94 | static void case_interest_is_separate(void) { | ||
| 95 | test_case("asking only about writing does not report reading"); | ||
| 96 | pair_open(); | ||
| 97 | CHECK(sv[0] != -1); | ||
| 98 | CHECK_INT(event_add(sv[0], EVENT_OUT, 1), 0); | ||
| 99 | CHECK_INT(write(sv[1], "x", 1), 1); | ||
| 100 | CHECK_INT(wait_for(sv[0], EVENT_IN, 50, NULL), 0); | ||
| 101 | |||
| 102 | test_case("until we ask about it"); | ||
| 103 | CHECK_INT(event_modify(sv[0], EVENT_IN | EVENT_OUT, 1), 0); | ||
| 104 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, NULL), 1); | ||
| 105 | |||
| 106 | CHECK_INT(event_del(sv[0]), 0); | ||
| 107 | pair_close(); | ||
| 108 | } | ||
| 109 | |||
| 110 | static void case_removed_is_silent(void) { | ||
| 111 | test_case("a removed descriptor is not reported again"); | ||
| 112 | pair_open(); | ||
| 113 | CHECK(sv[0] != -1); | ||
| 114 | CHECK_INT(event_add(sv[0], EVENT_IN, 1), 0); | ||
| 115 | CHECK_INT(write(sv[1], "x", 1), 1); | ||
| 116 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, NULL), 1); | ||
| 117 | CHECK_INT(event_del(sv[0]), 0); | ||
| 118 | CHECK_INT(wait_for(sv[0], EVENT_IN, 50, NULL), 0); | ||
| 119 | pair_close(); | ||
| 120 | } | ||
| 121 | |||
| 122 | static void case_generations_do_not_mix(void) { | ||
| 123 | int other[2]; | ||
| 124 | uint32_t generation = 0; | ||
| 125 | |||
| 126 | test_case("two descriptors carry their own generations"); | ||
| 127 | pair_open(); | ||
| 128 | CHECK(sv[0] != -1); | ||
| 129 | CHECK_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, other), 0); | ||
| 130 | CHECK_INT(event_add(sv[0], EVENT_IN, 100), 0); | ||
| 131 | CHECK_INT(event_add(other[0], EVENT_IN, 200), 0); | ||
| 132 | CHECK_INT(write(sv[1], "x", 1), 1); | ||
| 133 | CHECK_INT(write(other[1], "y", 1), 1); | ||
| 134 | |||
| 135 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, &generation), 1); | ||
| 136 | CHECK_INT(generation, 100); | ||
| 137 | CHECK_INT(wait_for(other[0], EVENT_IN, 1000, &generation), 1); | ||
| 138 | CHECK_INT(generation, 200); | ||
| 139 | |||
| 140 | test_case("and re-registering one changes only its own"); | ||
| 141 | CHECK_INT(event_modify(sv[0], EVENT_IN, 101), 0); | ||
| 142 | CHECK_INT(wait_for(sv[0], EVENT_IN, 1000, &generation), 1); | ||
| 143 | CHECK_INT(generation, 101); | ||
| 144 | CHECK_INT(wait_for(other[0], EVENT_IN, 1000, &generation), 1); | ||
| 145 | CHECK_INT(generation, 200); | ||
| 146 | |||
| 147 | event_del(sv[0]); | ||
| 148 | event_del(other[0]); | ||
| 149 | close(other[0]); | ||
| 150 | close(other[1]); | ||
| 151 | pair_close(); | ||
| 152 | } | ||
| 153 | |||
| 154 | /* Removing one of several has to leave the rest addressable: a backend that | ||
| 155 | keeps its descriptors packed moves the last one into the gap, and has to | ||
| 156 | remember where it went. */ | ||
| 157 | static void case_delete_from_the_middle(void) { | ||
| 158 | int pair[3][2]; | ||
| 159 | uint32_t generation = 0; | ||
| 160 | int i; | ||
| 161 | |||
| 162 | test_case("deleting one of three leaves the others reported correctly"); | ||
| 163 | for (i = 0; i < 3; ++i) { | ||
| 164 | CHECK_INT(socketpair(AF_UNIX, SOCK_STREAM, 0, pair[i]), 0); | ||
| 165 | CHECK_INT(event_add(pair[i][0], EVENT_IN, (uint32_t)(10 + i)), 0); | ||
| 166 | } | ||
| 167 | CHECK_INT(event_del(pair[1][0]), 0); | ||
| 168 | |||
| 169 | for (i = 0; i < 3; i += 2) { | ||
| 170 | CHECK_INT(write(pair[i][1], "x", 1), 1); | ||
| 171 | CHECK_INT(wait_for(pair[i][0], EVENT_IN, 1000, &generation), 1); | ||
| 172 | CHECK_INT(generation, 10 + i); | ||
| 173 | } | ||
| 174 | |||
| 175 | test_case("and the survivors can still be changed and removed"); | ||
| 176 | CHECK_INT(event_modify(pair[2][0], EVENT_IN, 99), 0); | ||
| 177 | CHECK_INT(wait_for(pair[2][0], EVENT_IN, 1000, &generation), 1); | ||
| 178 | CHECK_INT(generation, 99); | ||
| 179 | CHECK_INT(event_del(pair[0][0]), 0); | ||
| 180 | CHECK_INT(event_del(pair[2][0]), 0); | ||
| 181 | CHECK_INT(wait_for(pair[2][0], EVENT_IN, 50, NULL), 0); | ||
| 182 | |||
| 183 | for (i = 0; i < 3; ++i) | ||
| 184 | close(pair[i][0]), close(pair[i][1]); | ||
| 185 | } | ||
| 186 | |||
| 187 | static void case_timeout(void) { | ||
| 188 | struct timeval started; | ||
| 189 | |||
| 190 | test_case("waiting on nothing comes back empty when the time is up"); | ||
| 191 | gettimeofday(&started, NULL); | ||
| 192 | CHECK_INT(event_wait(ready, EVENTS, 200), 0); | ||
| 193 | { | ||
| 194 | unsigned long took = elapsed_ms(&started); | ||
| 195 | /* Scheduling makes the upper bound generous. The point is that it waited | ||
| 196 | rather than returning at once, and that it came back at all */ | ||
| 197 | CHECK(took >= 150); | ||
| 198 | CHECK(took < 5000); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | static void case_unknown_descriptor(void) { | ||
| 203 | test_case("changing or removing one that was never added is refused"); | ||
| 204 | CHECK_INT(event_modify(4096, EVENT_IN, 1), -1); | ||
| 205 | CHECK_INT(event_del(4096), -1); | ||
| 206 | } | ||
| 207 | |||
| 208 | void test_event(void) { | ||
| 209 | CHECK_INT(event_init(), 0); | ||
| 210 | CHECK(event_mechanism() != NULL); | ||
| 211 | |||
| 212 | case_readable(); | ||
| 213 | case_writable(); | ||
| 214 | case_interest_is_separate(); | ||
| 215 | case_removed_is_silent(); | ||
| 216 | case_generations_do_not_mix(); | ||
| 217 | case_delete_from_the_middle(); | ||
| 218 | case_timeout(); | ||
| 219 | case_unknown_descriptor(); | ||
| 220 | |||
| 221 | event_deinit(); | ||
| 222 | } | ||
