]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/apr-util/test/testredis.c
Upgrade to version 3.2.3
[FreeBSD/FreeBSD.git] / contrib / apr-util / test / testredis.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "testutil.h"
18 #include "apr.h"
19 #include "apu.h"
20 #include "apr_general.h"
21 #include "apr_strings.h"
22 #include "apr_hash.h"
23 #include "apr_redis.h"
24 #include "apr_network_io.h"
25
26 #include <stdio.h>
27 #if APR_HAVE_STDLIB_H
28 #include <stdlib.h>             /* for exit() */
29 #endif
30
31 #define HOST "localhost"
32 #define PORT 6379
33
34 /* the total number of items to use for set/get testing */
35 #define TDATA_SIZE 3000
36
37 /* some smaller subset of TDATA_SIZE used for multiget testing */
38 #define TDATA_SET 100
39
40 /* our custom hash function just returns this all the time */
41 #define HASH_FUNC_RESULT 510
42
43 /* all keys will be prefixed with this */
44 static const char prefix[] = "testredis";
45
46 /* text for values we store */
47 static const char txt[] =
48 "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis at"
49 "lacus in ligula hendrerit consectetuer. Vestibulum tristique odio"
50 "iaculis leo. In massa arcu, ultricies a, laoreet nec, hendrerit non,"
51 "neque. Nulla sagittis sapien ac risus. Morbi ligula dolor, vestibulum"
52 "nec, viverra id, placerat dapibus, arcu. Curabitur egestas feugiat"
53 "tellus. Donec dignissim. Nunc ante. Curabitur id lorem. In mollis"
54 "tortor sit amet eros auctor dapibus. Proin nulla sem, tristique in,"
55 "convallis id, iaculis feugiat cras amet.";
56
57 /*
58  * this datatype is for our custom server determination function. this might
59  * be useful if you don't want to rely on simply hashing keys to determine
60  * where a key belongs, but instead want to write something fancy, or use some
61  * other kind of configuration data, i.e. a hash plus some data about a 
62  * namespace, or whatever. see my_server_func, and test_redis_user_funcs
63  * for the examples.
64  */
65 typedef struct {
66   const char *someval;
67   apr_uint32_t which_server;
68 } my_hash_server_baton;
69
70
71 /* this could do something fancy and return some hash result. 
72  * for simplicity, just return the same value, so we can test it later on.
73  * if you wanted to use some external hashing library or functions for
74  * consistent hashing, for example, this would be a good place to do it.
75  */
76 static apr_uint32_t my_hash_func(void *baton, const char *data,
77                                  apr_size_t data_len)
78 {
79
80   return HASH_FUNC_RESULT;
81 }
82
83 /*
84  * a fancy function to determine which server to use given some kind of data
85  * and a hash value. this example actually ignores the hash value itself
86  * and pulls some number from the *baton, which is a struct that has some 
87  * kind of meaningful stuff in it.
88  */
89 static apr_redis_server_t *my_server_func(void *baton,
90                                              apr_redis_t *mc,
91                                              const apr_uint32_t hash)
92 {
93   apr_redis_server_t *ms = NULL;
94   my_hash_server_baton *mhsb = (my_hash_server_baton *)baton;
95
96   if(mc->ntotal == 0) {
97     return NULL;
98   } 
99
100   if(mc->ntotal < mhsb->which_server) {
101     return NULL;
102   }
103
104   ms = mc->live_servers[mhsb->which_server - 1];
105
106   return ms;
107 }
108
109 static apr_uint16_t firsttime = 0;
110 static int randval(apr_uint32_t high)
111 {
112     apr_uint32_t i = 0;
113     double d = 0;
114
115     if (firsttime == 0) {
116         srand((unsigned) (getpid()));
117         firsttime = 1;
118     }
119
120     d = (double) rand() / ((double) RAND_MAX + 1);
121     i = (int) (d * (high - 0 + 1));
122
123     return i > 0 ? i : 1;
124 }
125
126 /*
127  * general test to make sure we can create the redis struct and add
128  * some servers, but not more than we tell it we can add
129  */
130
131 static void test_redis_create(abts_case * tc, void *data)
132 {
133   apr_pool_t *pool = p;
134   apr_status_t rv;
135   apr_redis_t *redis;
136   apr_redis_server_t *server, *s;
137   apr_uint32_t max_servers = 10;
138   apr_uint32_t i;
139   apr_uint32_t hash;
140
141   rv = apr_redis_create(pool, max_servers, 0, &redis);
142   ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
143   
144   for (i = 1; i <= max_servers; i++) {
145     apr_port_t port;
146     
147     port = PORT + i;
148     rv =
149       apr_redis_server_create(pool, HOST, PORT + i, 0, 1, 1, 60, 60, &server);
150     ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
151     
152     rv = apr_redis_add_server(redis, server);
153     ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
154     
155     s = apr_redis_find_server(redis, HOST, port);
156     ABTS_PTR_EQUAL(tc, server, s);
157     
158     rv = apr_redis_disable_server(redis, s);
159     ABTS_ASSERT(tc, "server disable failed", rv == APR_SUCCESS);
160     
161     rv = apr_redis_enable_server(redis, s);
162     ABTS_ASSERT(tc, "server enable failed", rv == APR_SUCCESS);
163     
164     hash = apr_redis_hash(redis, prefix, strlen(prefix));
165     ABTS_ASSERT(tc, "hash failed", hash > 0);
166     
167     s = apr_redis_find_server_hash(redis, hash);
168     ABTS_PTR_NOTNULL(tc, s);
169   }
170
171   rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
172   ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
173   
174   rv = apr_redis_add_server(redis, server);
175   ABTS_ASSERT(tc, "server add should have failed", rv != APR_SUCCESS);
176   
177 }
178
179 /* install our own custom hashing and server selection routines. */
180
181 static int create_test_hash(apr_pool_t *p, apr_hash_t *h)
182 {
183   int i;
184   
185   for (i = 0; i < TDATA_SIZE; i++) {
186     char *k, *v;
187     
188     k = apr_pstrcat(p, prefix, apr_itoa(p, i), NULL);
189     v = apr_pstrndup(p, txt, randval((apr_uint32_t)strlen(txt)));
190     
191     apr_hash_set(h, k, APR_HASH_KEY_STRING, v);
192   }
193
194   return i;
195 }
196
197 static void test_redis_user_funcs(abts_case * tc, void *data)
198 {
199   apr_pool_t *pool = p;
200   apr_status_t rv;
201   apr_redis_t *redis;
202   apr_redis_server_t *found;
203   apr_uint32_t max_servers = 10;
204   apr_uint32_t hres;
205   apr_uint32_t i;
206   my_hash_server_baton *baton = 
207     apr_pcalloc(pool, sizeof(my_hash_server_baton));
208
209   rv = apr_redis_create(pool, max_servers, 0, &redis);
210   ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
211
212   /* as noted above, install our custom hash function, and call 
213    * apr_redis_hash. the return value should be our predefined number,
214    * and our function just ignores the other args, for simplicity.
215    */
216   redis->hash_func = my_hash_func;
217
218   hres = apr_redis_hash(redis, "whatever", sizeof("whatever") - 1);
219   ABTS_INT_EQUAL(tc, HASH_FUNC_RESULT, hres);
220   
221   /* add some servers */
222   for(i = 1; i <= 10; i++) {
223     apr_redis_server_t *ms;
224
225     rv = apr_redis_server_create(pool, HOST, i, 0, 1, 1, 60, 60, &ms);
226     ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
227     
228     rv = apr_redis_add_server(redis, ms);
229     ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
230   }
231
232   /* 
233    * set 'which_server' in our server_baton to find the third server 
234    * which should have the same port.
235    */
236   baton->which_server = 3;
237   redis->server_func = my_server_func;
238   redis->server_baton = baton;
239   found = apr_redis_find_server_hash(redis, 0);
240   ABTS_ASSERT(tc, "wrong server found", found->port == baton->which_server);
241 }
242
243 /* test non data related commands like stats and version */
244 static void test_redis_meta(abts_case * tc, void *data)
245 {
246     apr_pool_t *pool = p;
247     apr_redis_t *redis;
248     apr_redis_server_t *server;
249     apr_redis_stats_t *stats;
250     char *result;
251     apr_status_t rv;
252
253     rv = apr_redis_create(pool, 1, 0, &redis);
254     ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
255
256     rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
257     ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
258
259     rv = apr_redis_add_server(redis, server);
260     ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
261
262     rv = apr_redis_version(server, pool, &result);
263     ABTS_PTR_NOTNULL(tc, result);
264
265     rv = apr_redis_stats(server, p, &stats);
266     ABTS_PTR_NOTNULL(tc, stats);
267
268     /* 
269      * no way to know exactly what will be in most of these, so
270      * just make sure there is something.
271      */
272     ABTS_ASSERT(tc, "major", stats->major >= 1);
273     ABTS_ASSERT(tc, "minor", stats->minor >= 0);
274     ABTS_ASSERT(tc, "patch", stats->patch >= 0);
275     ABTS_ASSERT(tc, "process_id", stats->process_id >= 0);
276     ABTS_ASSERT(tc, "uptime_in_seconds", stats->uptime_in_seconds >= 0);
277     ABTS_ASSERT(tc, "arch_bits", stats->arch_bits >= 0);
278     ABTS_ASSERT(tc, "connected_clients", stats->connected_clients >= 0);
279     ABTS_ASSERT(tc, "blocked_clients", stats->blocked_clients >= 0);
280     ABTS_ASSERT(tc, "maxmemory", stats->maxmemory >= 0);
281     ABTS_ASSERT(tc, "used_memory", stats->used_memory >= 0);
282     ABTS_ASSERT(tc, "total_system_memory", stats->total_system_memory >= 0);
283     ABTS_ASSERT(tc, "total_connections_received", stats->total_connections_received >= 0);
284     ABTS_ASSERT(tc, "total_commands_processed", stats->total_commands_processed >= 0);
285     ABTS_ASSERT(tc, "total_net_input_bytes", stats->total_net_input_bytes >= 0);
286     ABTS_ASSERT(tc, "total_net_output_bytes", stats->total_net_output_bytes >= 0);
287     ABTS_ASSERT(tc, "keyspace_hits", stats->keyspace_hits >= 0);
288     ABTS_ASSERT(tc, "keyspace_misses", stats->keyspace_misses >= 0);
289     ABTS_ASSERT(tc, "role", stats->role >= 0);
290     ABTS_ASSERT(tc, "connected_slaves", stats->connected_slaves >= 0);
291     ABTS_ASSERT(tc, "used_cpu_sys", stats->used_cpu_sys >= 0);
292     ABTS_ASSERT(tc, "used_cpu_user", stats->used_cpu_user >= 0);
293     ABTS_ASSERT(tc, "cluster_enabled", stats->cluster_enabled >= 0);
294 }
295
296
297 /* basic tests of the increment and decrement commands */
298 static void test_redis_incrdecr(abts_case * tc, void *data)
299 {
300  apr_pool_t *pool = p;
301  apr_status_t rv;
302  apr_redis_t *redis;
303  apr_redis_server_t *server;
304  apr_uint32_t new;
305  char *result;
306  apr_size_t len;
307  apr_uint32_t i;
308
309   rv = apr_redis_create(pool, 1, 0, &redis);
310   ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
311   
312   rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
313   ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
314   
315   rv = apr_redis_add_server(redis, server);
316   ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
317
318   rv = apr_redis_set(redis, prefix, "271", sizeof("271") - 1, 27);
319   ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
320   
321   for( i = 1; i <= TDATA_SIZE; i++) {
322     apr_uint32_t expect;
323
324     rv = apr_redis_getp(redis, pool, prefix, &result, &len, NULL);
325     ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
326
327     expect = i + atoi(result);
328
329     rv = apr_redis_incr(redis, prefix, i, &new);
330     ABTS_ASSERT(tc, "incr failed", rv == APR_SUCCESS);
331
332     ABTS_INT_EQUAL(tc, expect, new);
333
334     rv = apr_redis_decr(redis, prefix, i, &new);
335     ABTS_ASSERT(tc, "decr failed", rv == APR_SUCCESS);
336
337     ABTS_INT_EQUAL(tc, atoi(result), new);
338
339   }
340
341   rv = apr_redis_getp(redis, pool, prefix, &result, &len, NULL);
342   ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
343
344   ABTS_INT_EQUAL(tc, 271, atoi(result));
345
346   rv = apr_redis_delete(redis, prefix, 0);
347   ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
348 }
349
350
351 /* test setting and getting */
352
353 static void test_redis_setget(abts_case * tc, void *data)
354 {
355     apr_pool_t *pool = p;
356     apr_status_t rv;
357     apr_redis_t *redis;
358     apr_redis_server_t *server;
359     apr_hash_t *tdata;
360     apr_hash_index_t *hi;
361     char *result;
362     apr_size_t len;
363
364     rv = apr_redis_create(pool, 1, 0, &redis);
365     ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
366
367     rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
368     ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
369
370     rv = apr_redis_add_server(redis, server);
371     ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
372
373     tdata = apr_hash_make(pool);
374
375     create_test_hash(pool, tdata);
376
377     for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
378         const void *k;
379         void *v;
380         const char *key;
381
382         apr_hash_this(hi, &k, NULL, &v);
383         key = k;
384
385         rv = apr_redis_set(redis, key, v, strlen(v), 27);
386         ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
387         rv = apr_redis_getp(redis, pool, key, &result, &len, NULL);
388         ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
389     }
390
391     rv = apr_redis_getp(redis, pool, "nothere3423", &result, &len, NULL);
392
393     ABTS_ASSERT(tc, "get should have failed", rv != APR_SUCCESS);
394
395     for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
396         const void *k;
397         const char *key;
398
399         apr_hash_this(hi, &k, NULL, NULL);
400         key = k;
401
402         rv = apr_redis_delete(redis, key, 0);
403         ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
404     }
405 }
406
407 /* test setting and getting */
408
409 static void test_redis_setexget(abts_case * tc, void *data)
410 {
411     apr_pool_t *pool = p;
412     apr_status_t rv;
413     apr_redis_t *redis;
414     apr_redis_server_t *server;
415     apr_hash_t *tdata;
416     apr_hash_index_t *hi;
417     char *result;
418     apr_size_t len;
419
420     rv = apr_redis_create(pool, 1, 0, &redis);
421     ABTS_ASSERT(tc, "redis create failed", rv == APR_SUCCESS);
422
423     rv = apr_redis_server_create(pool, HOST, PORT, 0, 1, 1, 60, 60, &server);
424     ABTS_ASSERT(tc, "server create failed", rv == APR_SUCCESS);
425
426     rv = apr_redis_add_server(redis, server);
427     ABTS_ASSERT(tc, "server add failed", rv == APR_SUCCESS);
428
429     tdata = apr_hash_make(pool);
430
431     create_test_hash(pool, tdata);
432
433     for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
434     const void *k;
435     void *v;
436         const char *key;
437
438     apr_hash_this(hi, &k, NULL, &v);
439         key = k;
440
441     rv = apr_redis_ping(server);
442     ABTS_ASSERT(tc, "ping failed", rv == APR_SUCCESS);
443     rv = apr_redis_setex(redis, key, v, strlen(v), 10, 27);
444     ABTS_ASSERT(tc, "set failed", rv == APR_SUCCESS);
445     rv = apr_redis_getp(redis, pool, key, &result, &len, NULL);
446     ABTS_ASSERT(tc, "get failed", rv == APR_SUCCESS);
447     }
448
449     rv = apr_redis_getp(redis, pool, "nothere3423", &result, &len, NULL);
450
451     ABTS_ASSERT(tc, "get should have failed", rv != APR_SUCCESS);
452
453     for (hi = apr_hash_first(p, tdata); hi; hi = apr_hash_next(hi)) {
454     const void *k;
455     const char *key;
456
457     apr_hash_this(hi, &k, NULL, NULL);
458     key = k;
459
460     rv = apr_redis_delete(redis, key, 0);
461     ABTS_ASSERT(tc, "delete failed", rv == APR_SUCCESS);
462     }
463 }
464
465 /* use apr_socket stuff to see if there is in fact a Redis server
466  * running on PORT.
467  */
468 static apr_status_t check_redis(void)
469 {
470   apr_pool_t *pool = p;
471   apr_status_t rv;
472   apr_socket_t *sock = NULL;
473   apr_sockaddr_t *sa;
474   struct iovec vec[2];
475   apr_size_t written;
476   char buf[128];
477   apr_size_t len;
478
479   rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, 0, pool);
480   if(rv != APR_SUCCESS) {
481     return rv;
482   }
483
484   rv = apr_sockaddr_info_get(&sa, HOST, APR_INET, PORT, 0, pool);
485   if(rv != APR_SUCCESS) {
486     return rv;
487   }
488
489   rv = apr_socket_timeout_set(sock, 1 * APR_USEC_PER_SEC);
490   if (rv != APR_SUCCESS) {
491     return rv;
492   }
493
494   rv = apr_socket_connect(sock, sa);
495   if (rv != APR_SUCCESS) {
496     return rv;
497   }
498
499   rv = apr_socket_timeout_set(sock, -1);
500   if (rv != APR_SUCCESS) {
501     return rv;
502   }
503
504   vec[0].iov_base = "PING";
505   vec[0].iov_len  = sizeof("PING") - 1;
506
507   vec[1].iov_base = "\r\n";
508   vec[1].iov_len  = sizeof("\r\n") -1;
509
510   rv = apr_socket_sendv(sock, vec, 2, &written);
511   if (rv != APR_SUCCESS) {
512     return rv;
513   }
514
515   len = sizeof(buf);
516   rv = apr_socket_recv(sock, buf, &len);
517   if(rv != APR_SUCCESS) {
518     return rv;
519   }
520   if(strncmp(buf, "+PONG", sizeof("+PONG")-1) != 0) {
521     rv = APR_EGENERAL;
522   }
523
524   apr_socket_close(sock);
525   return rv;
526 }
527
528 abts_suite *testredis(abts_suite * suite)
529 {
530     apr_status_t rv;
531     suite = ADD_SUITE(suite);
532     /* check for a running redis on the typical port before
533      * trying to run the tests. succeed if we don't find one.
534      */
535     rv = check_redis();
536     if (rv == APR_SUCCESS) {
537         abts_run_test(suite, test_redis_create, NULL);
538         abts_run_test(suite, test_redis_user_funcs, NULL);
539         abts_run_test(suite, test_redis_meta, NULL);
540         abts_run_test(suite, test_redis_setget, NULL);
541         abts_run_test(suite, test_redis_setexget, NULL);
542         /* abts_run_test(suite, test_redis_multiget, NULL); */
543         abts_run_test(suite, test_redis_incrdecr, NULL);
544     }
545     else {
546         abts_log_message("Error %d occurred attempting to reach Redis "
547                          "on %s:%d.  Skipping apr_redis tests...",
548                          rv, HOST, PORT);
549     }
550
551     return suite;
552 }