Skip to content
Newer
Older
100644 517 lines (446 sloc) 14.5 KB
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #undef NDEBUG
6fcace5 @trondn Move to pandora build
trondn authored Aug 3, 2010
3 #include "config.h"
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 9, 2009
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <netdb.h>
7 #include <arpa/inet.h>
8 #include <netinet/in.h>
9 #include <netinet/tcp.h>
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <errno.h>
14 #include <assert.h>
15 #include <string.h>
16 #include <inttypes.h>
17 #include <stdbool.h>
18 #include <unistd.h>
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
19 #include <netinet/in.h>
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
20
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
21 #include "protocol_binary.h"
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
22 #include "config.h"
23 #include "cache.h"
24 #include "util.h"
25
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
26 #define TMP_TEMPLATE "/tmp/test_file.XXXXXXX"
27
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
28 enum test_return { TEST_SKIP, TEST_PASS, TEST_FAIL };
29
30 static enum test_return cache_create_test(void)
31 {
32 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
33 NULL, NULL);
34 assert(cache != NULL);
35 cache_destroy(cache);
36 return TEST_PASS;
37 }
38
39 const uint64_t constructor_pattern = 0xdeadcafebabebeef;
40
41 static int cache_constructor(void *buffer, void *notused1, int notused2) {
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
42 (void)buffer;
43 (void)notused1;
44 (void)notused2;
45
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
46 uint64_t *ptr = buffer;
47 *ptr = constructor_pattern;
48 return 0;
49 }
50
51 static enum test_return cache_constructor_test(void)
52 {
53 cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
54 cache_constructor, NULL);
55 assert(cache != NULL);
56 uint64_t *ptr = cache_alloc(cache);
57 uint64_t pattern = *ptr;
58 cache_free(cache, ptr);
59 cache_destroy(cache);
60 return (pattern == constructor_pattern) ? TEST_PASS : TEST_FAIL;
61 }
62
63 static int cache_fail_constructor(void *buffer, void *notused1, int notused2) {
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
64 (void)buffer;
65 (void)notused1;
66 (void)notused2;
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
67 return 1;
68 }
69
70 static enum test_return cache_fail_constructor_test(void)
71 {
72 enum test_return ret = TEST_PASS;
73
74 cache_t *cache = cache_create("test", sizeof(uint64_t), sizeof(uint64_t),
75 cache_fail_constructor, NULL);
76 assert(cache != NULL);
77 uint64_t *ptr = cache_alloc(cache);
78 if (ptr != NULL) {
79 ret = TEST_FAIL;
80 }
81 cache_destroy(cache);
82 return ret;
83 }
84
85 static void *destruct_data = 0;
86
87 static void cache_destructor(void *buffer, void *notused) {
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
88 (void)notused;
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
89 destruct_data = buffer;
90 }
91
92 static enum test_return cache_destructor_test(void)
93 {
94 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
95 NULL, cache_destructor);
96 assert(cache != NULL);
97 char *ptr = cache_alloc(cache);
98 cache_free(cache, ptr);
99 cache_destroy(cache);
100
101 return (ptr == destruct_data) ? TEST_PASS : TEST_FAIL;
102 }
103
104 static enum test_return cache_reuse_test(void)
105 {
106 int ii;
107 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
108 NULL, NULL);
109 char *ptr = cache_alloc(cache);
110 cache_free(cache, ptr);
111 for (ii = 0; ii < 100; ++ii) {
112 char *p = cache_alloc(cache);
113 assert(p == ptr);
114 cache_free(cache, ptr);
115 }
116 cache_destroy(cache);
117 return TEST_PASS;
118 }
119
120 static enum test_return cache_redzone_test(void)
121 {
122 #ifndef HAVE_UMEM_H
123 cache_t *cache = cache_create("test", sizeof(uint32_t), sizeof(char*),
124 NULL, NULL);
125
126 /* Ignore SIGABORT */
127 struct sigaction old_action;
128 struct sigaction action = { .sa_handler = SIG_IGN, .sa_flags = 0};
129 sigemptyset(&action.sa_mask);
130 sigaction(SIGABRT, &action, &old_action);
131
132 /* check memory debug.. */
133 char *p = cache_alloc(cache);
134 char old = *(p - 1);
135 *(p - 1) = 0;
136 cache_free(cache, p);
137 assert(cache_error == -1);
138 *(p - 1) = old;
139
140 p[sizeof(uint32_t)] = 0;
141 cache_free(cache, p);
142 assert(cache_error == 1);
143
144 /* restore signal handler */
145 sigaction(SIGABRT, &old_action, NULL);
146
147 cache_destroy(cache);
148
149 return TEST_PASS;
150 #else
151 return TEST_SKIP;
152 #endif
153 }
154
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
155 static enum test_return test_safe_strtoul(void) {
156 uint32_t val;
157 assert(safe_strtoul("123", &val));
158 assert(val == 123);
159 assert(safe_strtoul("+123", &val));
160 assert(val == 123);
161 assert(!safe_strtoul("", &val)); // empty
162 assert(!safe_strtoul("123BOGUS", &val)); // non-numeric
163 /* Not sure what it does, but this works with ICC :/
164 assert(!safe_strtoul("92837498237498237498029383", &val)); // out of range
165 */
166
167 // extremes:
168 assert(safe_strtoul("4294967295", &val)); // 2**32 - 1
169 assert(val == 4294967295L);
170 /* This actually works on 64-bit ubuntu
171 assert(!safe_strtoul("4294967296", &val)); // 2**32
172 */
173 assert(!safe_strtoul("-1", &val)); // negative
174 return TEST_PASS;
175 }
176
177
178 static enum test_return test_safe_strtoull(void) {
179 uint64_t val;
180 assert(safe_strtoull("123", &val));
181 assert(val == 123);
182 assert(safe_strtoull("+123", &val));
183 assert(val == 123);
184 assert(!safe_strtoull("", &val)); // empty
185 assert(!safe_strtoull("123BOGUS", &val)); // non-numeric
186 assert(!safe_strtoull("92837498237498237498029383", &val)); // out of range
187
188 // extremes:
189 assert(safe_strtoull("18446744073709551615", &val)); // 2**64 - 1
190 assert(val == 18446744073709551615ULL);
191 assert(!safe_strtoull("18446744073709551616", &val)); // 2**64
192 assert(!safe_strtoull("-1", &val)); // negative
193 return TEST_PASS;
194 }
195
196 static enum test_return test_safe_strtoll(void) {
197 int64_t val;
198 assert(safe_strtoll("123", &val));
199 assert(val == 123);
200 assert(safe_strtoll("+123", &val));
201 assert(val == 123);
202 assert(safe_strtoll("-123", &val));
203 assert(val == -123);
204 assert(!safe_strtoll("", &val)); // empty
205 assert(!safe_strtoll("123BOGUS", &val)); // non-numeric
206 assert(!safe_strtoll("92837498237498237498029383", &val)); // out of range
207
208 // extremes:
209 assert(!safe_strtoll("18446744073709551615", &val)); // 2**64 - 1
210 assert(safe_strtoll("9223372036854775807", &val)); // 2**63 - 1
211 assert(val == 9223372036854775807LL);
212 /*
213 assert(safe_strtoll("-9223372036854775808", &val)); // -2**63
214 assert(val == -9223372036854775808LL);
215 */
216 assert(!safe_strtoll("-9223372036854775809", &val)); // -2**63 - 1
217
218 // We'll allow space to terminate the string. And leading space.
219 assert(safe_strtoll(" 123 foo", &val));
220 assert(val == 123);
221 return TEST_PASS;
222 }
223
224 static enum test_return test_safe_strtol(void) {
225 int32_t val;
226 assert(safe_strtol("123", &val));
227 assert(val == 123);
228 assert(safe_strtol("+123", &val));
229 assert(val == 123);
230 assert(safe_strtol("-123", &val));
231 assert(val == -123);
232 assert(!safe_strtol("", &val)); // empty
233 assert(!safe_strtol("123BOGUS", &val)); // non-numeric
234 assert(!safe_strtol("92837498237498237498029383", &val)); // out of range
235
236 // extremes:
237 /* This actually works on 64-bit ubuntu
238 assert(!safe_strtol("2147483648", &val)); // (expt 2.0 31.0)
239 */
240 assert(safe_strtol("2147483647", &val)); // (- (expt 2.0 31) 1)
241 assert(val == 2147483647L);
242 /* This actually works on 64-bit ubuntu
243 assert(!safe_strtol("-2147483649", &val)); // (- (expt -2.0 31) 1)
244 */
245
246 // We'll allow space to terminate the string. And leading space.
247 assert(safe_strtol(" 123 foo", &val));
248 assert(val == 123);
249 return TEST_PASS;
250 }
251
252 /**
253 * Function to start the server and let it listen on a random port
254 *
255 * @param port_out where to store the TCP port number the server is
256 * listening on
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
257 * @param is_daemon set to true if you want to run the memcached server
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
258 * as a daemon process
259 * @return the pid of the memcached server
260 */
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
261 static pid_t start_server(in_port_t *port_out, bool is_daemon) {
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
262 char environment[80];
263 snprintf(environment, sizeof(environment),
264 "MEMCACHED_PORT_FILENAME=/tmp/ports.%lu", (long)getpid());
265 char *filename= environment + strlen("MEMCACHED_PORT_FILENAME=");
266 char pid_file[80];
267 snprintf(pid_file, sizeof(pid_file), "/tmp/pid.%lu", (long)getpid());
268
269 remove(filename);
270 remove(pid_file);
271
272 pid_t pid = fork();
273 assert(pid != -1);
274
275 if (pid == 0) {
276 /* Child */
277 char *argv[20];
278 int arg = 0;
279 putenv(environment);
280 #ifdef __sun
281 putenv("LD_PRELOAD=watchmalloc.so.1");
282 putenv("MALLOC_DEBUG=WATCH");
283 #endif
284
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
285 if (!is_daemon) {
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
286 argv[arg++] = "./timedrun";
287 argv[arg++] = "15";
288 }
289 argv[arg++] = "./memcached-debug";
290 argv[arg++] = "-p";
291 argv[arg++] = "-1";
292 argv[arg++] = "-U";
293 argv[arg++] = "0";
294 /* Handle rpmbuild and the like doing this as root */
295 if (getuid() == 0) {
296 argv[arg++] = "-u";
297 argv[arg++] = "root";
298 }
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
299 if (is_daemon) {
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
300 argv[arg++] = "-d";
301 argv[arg++] = "-P";
302 argv[arg++] = pid_file;
303 }
304 argv[arg++] = NULL;
305 assert(execv(argv[0], argv) != -1);
306 }
307
308 /* Yeah just let us "busy-wait" for the file to be created ;-) */
309 while (access(filename, F_OK) == -1) {
310 usleep(10);
311 }
312
313 FILE *fp = fopen(filename, "r");
314 if (fp == NULL) {
315 fprintf(stderr, "Failed to open the file containing port numbers: %s\n",
316 strerror(errno));
317 assert(false);
318 }
319
320 *port_out = (in_port_t)-1;
321 char buffer[80];
322 while ((fgets(buffer, sizeof(buffer), fp)) != NULL) {
323 if (strncmp(buffer, "TCP INET: ", 10) == 0) {
324 int32_t val;
325 assert(safe_strtol(buffer + 10, &val));
326 *port_out = (in_port_t)val;
327 }
328 }
b11bef1 @steveyen moxi skips test_issue_44
steveyen authored May 21, 2009
329
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
330 fclose(fp);
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
331 assert(remove(filename) == 0);
332
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
333 if (is_daemon) {
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
334 /* loop and wait for the pid file.. There is a potential race
335 * condition that the server just created the file but isn't
336 * finished writing the content, but I'll take the chance....
337 */
338 while (access(pid_file, F_OK) == -1) {
339 usleep(10);
340 }
341
342 fp = fopen(pid_file, "r");
343 if (fp == NULL) {
344 fprintf(stderr, "Failed to open pid file: %s\n",
345 strerror(errno));
346 assert(false);
347 }
348 assert(fgets(buffer, sizeof(buffer), fp) != NULL);
349 fclose(fp);
350
351 int32_t val;
352 assert(safe_strtol(buffer, &val));
353 pid = (pid_t)val;
354 }
355
356 return pid;
357 }
358
359 static enum test_return test_issue_44(void) {
360 in_port_t port;
361 pid_t pid = start_server(&port, true);
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
362 assert(kill(pid, SIGHUP) == 0);
363 sleep(1);
364 assert(kill(pid, SIGTERM) == 0);
365
366 return TEST_PASS;
367 }
368
a5962cb @steveyen passing whitespace.pl tests again
steveyen authored Aug 9, 2010
369 /*
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
370 * static struct addrinfo *lookuphost(const char *hostname, in_port_t port)
371 * {
372 * struct addrinfo *ai = 0;
373 * struct addrinfo hints = { .ai_family = AF_UNSPEC,
374 * .ai_protocol = IPPROTO_TCP,
375 * .ai_socktype = SOCK_STREAM };
376 * char service[NI_MAXSERV];
377 * int error;
a5962cb @steveyen passing whitespace.pl tests again
steveyen authored Aug 9, 2010
378 *
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
379 * (void)snprintf(service, NI_MAXSERV, "%d", port);
380 * if ((error = getaddrinfo(hostname, service, &hints, &ai)) != 0) {
381 * if (error != EAI_SYSTEM) {
382 * fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
383 * } else {
384 * perror("getaddrinfo()");
385 * }
386 * }
a5962cb @steveyen passing whitespace.pl tests again
steveyen authored Aug 9, 2010
387 *
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
388 * return ai;
389 * }
390 */
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
391
a5962cb @steveyen passing whitespace.pl tests again
steveyen authored Aug 9, 2010
392 /*
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
393 * static int connect_server(const char *hostname, in_port_t port)
394 * {
395 * struct addrinfo *ai = lookuphost(hostname, port);
396 * int sock = -1;
397 * if (ai != NULL) {
398 * if ((sock = socket(ai->ai_family, ai->ai_socktype,
399 * ai->ai_protocol)) != -1) {
400 * if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
401 * fprintf(stderr, "Failed to connect socket: %s\n",
402 * strerror(errno));
403 * close(sock);
404 * sock = -1;
405 * }
406 * } else {
407 * fprintf(stderr, "Failed to create socket: %s\n", strerror(errno));
408 * }
a5962cb @steveyen passing whitespace.pl tests again
steveyen authored Aug 9, 2010
409 *
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
410 * freeaddrinfo(ai);
411 * }
412 * return sock;
413 * }
414 */
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
415
416 static enum test_return test_vperror(void) {
417 int rv = 0;
418 int oldstderr = dup(STDERR_FILENO);
419 char tmpl[sizeof(TMP_TEMPLATE)+1];
420 strncpy(tmpl, TMP_TEMPLATE, sizeof(TMP_TEMPLATE)+1);
421
422 int newfile = mkstemp(tmpl);
423 assert(newfile > 0);
424 rv = dup2(newfile, STDERR_FILENO);
425 assert(rv == STDERR_FILENO);
426 rv = close(newfile);
427 assert(rv == 0);
428
429 errno = EIO;
430 vperror("Old McDonald had a farm. %s", "EI EIO");
431
432 /* Restore stderr */
433 rv = dup2(oldstderr, STDERR_FILENO);
434 assert(rv == STDERR_FILENO);
435
436
437 /* Go read the file */
438 char buf[80] = { 0 };
439 FILE *efile = fopen(tmpl, "r");
440 assert(efile);
441 char *prv = fgets(buf, sizeof(buf), efile);
442 assert(prv);
443 fclose(efile);
444
445 unlink(tmpl);
446
447 char expected[80] = { 0 };
448 snprintf(expected, sizeof(expected),
449 "Old McDonald had a farm. EI EIO: %s\n", strerror(EIO));
450
451 /*
452 fprintf(stderr,
453 "\nExpected: ``%s''"
454 "\nGot: ``%s''\n", expected, buf);
455 */
456
457 return strcmp(expected, buf) == 0 ? TEST_PASS : TEST_FAIL;
458 }
459
460
461 static enum test_return test_issue_72(void) {
5476b97 @steveyen moxi testapp.c skips test_issue_72
steveyen authored Jul 14, 2010
462 // SKIP: moxi doesn't handle MEMCACHED_PORT_FILENAME now.
463 return TEST_SKIP;
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
464 }
465
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
466 typedef enum test_return (*TEST_FUNC)(void);
467 struct testcase {
468 const char *description;
469 TEST_FUNC function;
470 };
471
472 struct testcase testcases[] = {
473 { "cache_create", cache_create_test },
474 { "cache_constructor", cache_constructor_test },
475 { "cache_constructor_fail", cache_fail_constructor_test },
476 { "cache_destructor", cache_destructor_test },
477 { "cache_reuse", cache_reuse_test },
478 { "cache_redzone", cache_redzone_test },
479 { "issue_44", test_issue_44 },
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
480 { "issue_72", test_issue_72 },
481 { "vperror", test_vperror },
482 { "safestrtoul", test_safe_strtoul },
483 { "safestrtoull", test_safe_strtoull },
484 { "safestrtoll", test_safe_strtoll },
485 { "safestrtol", test_safe_strtol },
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
486 { NULL, NULL }
487 };
488
e1ae2ef fixes tons of warnings produced on GNU/Linux
Aliaksey Kandratsenka authored Aug 4, 2010
489 int main(void)
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
490 {
491 int exitcode = 0;
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
492 int ii = 0, num_cases = 0;
493
494 for (num_cases = 0; testcases[num_cases].description; num_cases++) {
495 /* Just counting */
496 }
497
498 printf("1..%d\n", num_cases);
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
499
500 for (ii = 0; testcases[ii].description != NULL; ++ii) {
501 fflush(stdout);
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
502 alarm(60);
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
503 enum test_return ret = testcases[ii].function();
504 if (ret == TEST_SKIP) {
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
505 fprintf(stdout, "ok # SKIP %d - %s\n", ii + 1, testcases[ii].description);
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
506 } else if (ret == TEST_PASS) {
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
507 fprintf(stdout, "ok %d - %s\n", ii + 1, testcases[ii].description);
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
508 } else {
48eb885 @dustin Merged in 1.4.1
dustin authored Sep 10, 2009
509 fprintf(stdout, "not ok %d - %s\n", ii + 1, testcases[ii].description);
4c86fa5 Create a generic cache for objects of same size
Trond Norbye authored Mar 27, 2009
510 exitcode = 1;
511 }
512 fflush(stdout);
513 }
514
515 return exitcode;
516 }
Something went wrong with that request. Please try again.