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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include "apr_general.h"
21 #include "apr_strings.h"
23 #include "apr_memcache.h"
24 #include "apr_network_io.h"
27 #include <stdlib.h> /* for exit() */
30 #define HOST "localhost"
33 /* the total number of items to use for set/get testing */
34 #define TDATA_SIZE 3000
36 /* some smaller subset of TDATA_SIZE used for multiget testing */
39 /* our custom hash function just returns this all the time */
40 #define HASH_FUNC_RESULT 510
42 /* all keys will be prefixed with this */
43 const char prefix
[] = "testmemcache";
45 /* text for values we store */
47 "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis at"
48 "lacus in ligula hendrerit consectetuer. Vestibulum tristique odio"
49 "iaculis leo. In massa arcu, ultricies a, laoreet nec, hendrerit non,"
50 "neque. Nulla sagittis sapien ac risus. Morbi ligula dolor, vestibulum"
51 "nec, viverra id, placerat dapibus, arcu. Curabitur egestas feugiat"
52 "tellus. Donec dignissim. Nunc ante. Curabitur id lorem. In mollis"
53 "tortor sit amet eros auctor dapibus. Proin nulla sem, tristique in,"
54 "convallis id, iaculis feugiat cras amet.";
57 * this datatype is for our custom server determination function. this might
58 * be useful if you don't want to rely on simply hashing keys to determine
59 * where a key belongs, but instead want to write something fancy, or use some
60 * other kind of configuration data, i.e. a hash plus some data about a
61 * namespace, or whatever. see my_server_func, and test_memcache_user_funcs
66 apr_uint32_t which_server
;
67 } my_hash_server_baton
;
70 /* this could do something fancy and return some hash result.
71 * for simplicity, just return the same value, so we can test it later on.
72 * if you wanted to use some external hashing library or functions for
73 * consistent hashing, for example, this would be a good place to do it.
75 apr_uint32_t
my_hash_func(void *baton
, const char *data
,
79 return HASH_FUNC_RESULT
;
83 * a fancy function to determine which server to use given some kind of data
84 * and a hash value. this example actually ignores the hash value itself
85 * and pulls some number from the *baton, which is a struct that has some
86 * kind of meaningful stuff in it.
88 apr_memcache_server_t
*my_server_func(void *baton
,
90 const apr_uint32_t hash
)
92 apr_memcache_server_t
*ms
= NULL
;
93 my_hash_server_baton
*mhsb
= (my_hash_server_baton
*)baton
;
99 if(mc
->ntotal
< mhsb
->which_server
) {
103 ms
= mc
->live_servers
[mhsb
->which_server
- 1];
108 apr_uint16_t firsttime
= 0;
109 int randval(apr_uint32_t high
)
114 if (firsttime
== 0) {
115 srand((unsigned) (getpid()));
119 d
= (double) rand() / ((double) RAND_MAX
+ 1);
120 i
= (int) (d
* (high
- 0 + 1));
122 return i
> 0 ? i
: 1;
126 * general test to make sure we can create the memcache struct and add
127 * some servers, but not more than we tell it we can add
130 static void test_memcache_create(abts_case
* tc
, void *data
)
132 apr_pool_t
*pool
= p
;
134 apr_memcache_t
*memcache
;
135 apr_memcache_server_t
*server
, *s
;
136 apr_uint32_t max_servers
= 10;
140 rv
= apr_memcache_create(pool
, max_servers
, 0, &memcache
);
141 ABTS_ASSERT(tc
, "memcache create failed", rv
== APR_SUCCESS
);
143 for (i
= 1; i
<= max_servers
; i
++) {
148 apr_memcache_server_create(pool
, HOST
, PORT
+ i
, 0, 1, 1, 60, &server
);
149 ABTS_ASSERT(tc
, "server create failed", rv
== APR_SUCCESS
);
151 rv
= apr_memcache_add_server(memcache
, server
);
152 ABTS_ASSERT(tc
, "server add failed", rv
== APR_SUCCESS
);
154 s
= apr_memcache_find_server(memcache
, HOST
, port
);
155 ABTS_PTR_EQUAL(tc
, server
, s
);
157 rv
= apr_memcache_disable_server(memcache
, s
);
158 ABTS_ASSERT(tc
, "server disable failed", rv
== APR_SUCCESS
);
160 rv
= apr_memcache_enable_server(memcache
, s
);
161 ABTS_ASSERT(tc
, "server enable failed", rv
== APR_SUCCESS
);
163 hash
= apr_memcache_hash(memcache
, prefix
, strlen(prefix
));
164 ABTS_ASSERT(tc
, "hash failed", hash
> 0);
166 s
= apr_memcache_find_server_hash(memcache
, hash
);
167 ABTS_PTR_NOTNULL(tc
, s
);
170 rv
= apr_memcache_server_create(pool
, HOST
, PORT
, 0, 1, 1, 60, &server
);
171 ABTS_ASSERT(tc
, "server create failed", rv
== APR_SUCCESS
);
173 rv
= apr_memcache_add_server(memcache
, server
);
174 ABTS_ASSERT(tc
, "server add should have failed", rv
!= APR_SUCCESS
);
178 /* install our own custom hashing and server selection routines. */
180 int create_test_hash(apr_pool_t
*p
, apr_hash_t
*h
)
184 for (i
= 0; i
< TDATA_SIZE
; i
++) {
187 k
= apr_pstrcat(p
, prefix
, apr_itoa(p
, i
), NULL
);
188 v
= apr_pstrndup(p
, txt
, randval((apr_uint32_t
)strlen(txt
)));
190 apr_hash_set(h
, k
, APR_HASH_KEY_STRING
, v
);
196 static void test_memcache_user_funcs(abts_case
* tc
, void *data
)
198 apr_pool_t
*pool
= p
;
200 apr_memcache_t
*memcache
;
201 apr_memcache_server_t
*found
;
202 apr_uint32_t max_servers
= 10;
205 my_hash_server_baton
*baton
=
206 apr_pcalloc(pool
, sizeof(my_hash_server_baton
));
208 rv
= apr_memcache_create(pool
, max_servers
, 0, &memcache
);
209 ABTS_ASSERT(tc
, "memcache create failed", rv
== APR_SUCCESS
);
211 /* as noted above, install our custom hash function, and call
212 * apr_memcache_hash. the return value should be our predefined number,
213 * and our function just ignores the other args, for simplicity.
215 memcache
->hash_func
= my_hash_func
;
217 hres
= apr_memcache_hash(memcache
, "whatever", sizeof("whatever") - 1);
218 ABTS_INT_EQUAL(tc
, HASH_FUNC_RESULT
, hres
);
220 /* add some servers */
221 for(i
= 1; i
<= 10; i
++) {
222 apr_memcache_server_t
*ms
;
224 rv
= apr_memcache_server_create(pool
, HOST
, i
, 0, 1, 1, 60, &ms
);
225 ABTS_ASSERT(tc
, "server create failed", rv
== APR_SUCCESS
);
227 rv
= apr_memcache_add_server(memcache
, ms
);
228 ABTS_ASSERT(tc
, "server add failed", rv
== APR_SUCCESS
);
232 * set 'which_server' in our server_baton to find the third server
233 * which should have the same port.
235 baton
->which_server
= 3;
236 memcache
->server_func
= my_server_func
;
237 memcache
->server_baton
= baton
;
238 found
= apr_memcache_find_server_hash(memcache
, 0);
239 ABTS_ASSERT(tc
, "wrong server found", found
->port
== baton
->which_server
);
242 /* test non data related commands like stats and version */
243 static void test_memcache_meta(abts_case
* tc
, void *data
)
245 apr_pool_t
*pool
= p
;
246 apr_memcache_t
*memcache
;
247 apr_memcache_server_t
*server
;
248 apr_memcache_stats_t
*stats
;
252 rv
= apr_memcache_create(pool
, 1, 0, &memcache
);
253 ABTS_ASSERT(tc
, "memcache create failed", rv
== APR_SUCCESS
);
255 rv
= apr_memcache_server_create(pool
, HOST
, PORT
, 0, 1, 1, 60, &server
);
256 ABTS_ASSERT(tc
, "server create failed", rv
== APR_SUCCESS
);
258 rv
= apr_memcache_add_server(memcache
, server
);
259 ABTS_ASSERT(tc
, "server add failed", rv
== APR_SUCCESS
);
261 rv
= apr_memcache_version(server
, pool
, &result
);
262 ABTS_PTR_NOTNULL(tc
, result
);
264 rv
= apr_memcache_stats(server
, p
, &stats
);
265 ABTS_PTR_NOTNULL(tc
, stats
);
267 ABTS_STR_NEQUAL(tc
, stats
->version
, result
, 5);
270 * no way to know exactly what will be in most of these, so
271 * just make sure there is something.
274 ABTS_ASSERT(tc
, "pid", stats
->pid
>= 0);
275 ABTS_ASSERT(tc
, "time", stats
->time
>= 0);
276 /* ABTS_ASSERT(tc, "pointer_size", stats->pointer_size >= 0); */
277 ABTS_ASSERT(tc
, "rusage_user", stats
->rusage_user
>= 0);
278 ABTS_ASSERT(tc
, "rusage_system", stats
->rusage_system
>= 0);
280 ABTS_ASSERT(tc
, "curr_items", stats
->curr_items
>= 0);
281 ABTS_ASSERT(tc
, "total_items", stats
->total_items
>= 0);
282 ABTS_ASSERT(tc
, "bytes", stats
->bytes
>= 0);
284 ABTS_ASSERT(tc
, "curr_connections", stats
->curr_connections
>= 0);
285 ABTS_ASSERT(tc
, "total_connections", stats
->total_connections
>= 0);
286 ABTS_ASSERT(tc
, "connection_structures",
287 stats
->connection_structures
>= 0);
289 ABTS_ASSERT(tc
, "cmd_get", stats
->cmd_get
>= 0);
290 ABTS_ASSERT(tc
, "cmd_set", stats
->cmd_set
>= 0);
291 ABTS_ASSERT(tc
, "get_hits", stats
->get_hits
>= 0);
292 ABTS_ASSERT(tc
, "get_misses", stats
->get_misses
>= 0);
294 /* ABTS_ASSERT(tc, "evictions", stats->evictions >= 0); */
296 ABTS_ASSERT(tc
, "bytes_read", stats
->bytes_read
>= 0);
297 ABTS_ASSERT(tc
, "bytes_written", stats
->bytes_written
>= 0);
298 ABTS_ASSERT(tc
, "limit_maxbytes", stats
->limit_maxbytes
>= 0);
300 /* ABTS_ASSERT(tc, "threads", stats->threads >= 0); */
303 /* test add and replace calls */
305 static void test_memcache_addreplace(abts_case
* tc
, void *data
)
307 apr_pool_t
*pool
= p
;
309 apr_memcache_t
*memcache
;
310 apr_memcache_server_t
*server
;
312 apr_hash_index_t
*hi
;
316 rv
= apr_memcache_create(pool
, 1, 0, &memcache
);
317 ABTS_ASSERT(tc
, "memcache create failed", rv
== APR_SUCCESS
);
319 rv
= apr_memcache_server_create(pool
, HOST
, PORT
, 0, 1, 1, 60, &server
);
320 ABTS_ASSERT(tc
, "server create failed", rv
== APR_SUCCESS
);
322 rv
= apr_memcache_add_server(memcache
, server
);
323 ABTS_ASSERT(tc
, "server add failed", rv
== APR_SUCCESS
);
325 tdata
= apr_hash_make(p
);
326 create_test_hash(pool
, tdata
);
328 for (hi
= apr_hash_first(p
, tdata
); hi
; hi
= apr_hash_next(hi
)) {
333 apr_hash_this(hi
, &k
, NULL
, &v
);
336 /* doesn't exist yet, fail */
337 rv
= apr_memcache_replace(memcache
, key
, v
, strlen(v
) - 1, 0, 27);
338 ABTS_ASSERT(tc
, "replace should have failed", rv
!= APR_SUCCESS
);
340 /* doesn't exist yet, succeed */
341 rv
= apr_memcache_add(memcache
, key
, v
, strlen(v
), 0, 27);
342 ABTS_ASSERT(tc
, "add failed", rv
== APR_SUCCESS
);
344 /* exists now, succeed */
345 rv
= apr_memcache_replace(memcache
, key
, "new", sizeof("new") - 1, 0, 27);
346 ABTS_ASSERT(tc
, "replace failed", rv
== APR_SUCCESS
);
348 /* make sure its different */
349 rv
= apr_memcache_getp(memcache
, pool
, key
, &result
, &len
, NULL
);
350 ABTS_ASSERT(tc
, "get failed", rv
== APR_SUCCESS
);
351 ABTS_STR_NEQUAL(tc
, result
, "new", 3);
353 /* exists now, fail */
354 rv
= apr_memcache_add(memcache
, key
, v
, strlen(v
), 0, 27);
355 ABTS_ASSERT(tc
, "add should have failed", rv
!= APR_SUCCESS
);
358 rv
= apr_memcache_delete(memcache
, key
, 0);
359 ABTS_ASSERT(tc
, "delete failed", rv
== APR_SUCCESS
);
363 /* basic tests of the increment and decrement commands */
364 static void test_memcache_incrdecr(abts_case
* tc
, void *data
)
366 apr_pool_t
*pool
= p
;
368 apr_memcache_t
*memcache
;
369 apr_memcache_server_t
*server
;
375 rv
= apr_memcache_create(pool
, 1, 0, &memcache
);
376 ABTS_ASSERT(tc
, "memcache create failed", rv
== APR_SUCCESS
);
378 rv
= apr_memcache_server_create(pool
, HOST
, PORT
, 0, 1, 1, 60, &server
);
379 ABTS_ASSERT(tc
, "server create failed", rv
== APR_SUCCESS
);
381 rv
= apr_memcache_add_server(memcache
, server
);
382 ABTS_ASSERT(tc
, "server add failed", rv
== APR_SUCCESS
);
384 rv
= apr_memcache_set(memcache
, prefix
, "271", sizeof("271") - 1, 0, 27);
385 ABTS_ASSERT(tc
, "set failed", rv
== APR_SUCCESS
);
387 for( i
= 1; i
<= TDATA_SIZE
; i
++) {
390 rv
= apr_memcache_getp(memcache
, pool
, prefix
, &result
, &len
, NULL
);
391 ABTS_ASSERT(tc
, "get failed", rv
== APR_SUCCESS
);
393 expect
= i
+ atoi(result
);
395 rv
= apr_memcache_incr(memcache
, prefix
, i
, &new);
396 ABTS_ASSERT(tc
, "incr failed", rv
== APR_SUCCESS
);
398 ABTS_INT_EQUAL(tc
, expect
, new);
400 rv
= apr_memcache_decr(memcache
, prefix
, i
, &new);
401 ABTS_ASSERT(tc
, "decr failed", rv
== APR_SUCCESS
);
402 ABTS_INT_EQUAL(tc
, atoi(result
), new);
406 rv
= apr_memcache_getp(memcache
, pool
, prefix
, &result
, &len
, NULL
);
407 ABTS_ASSERT(tc
, "get failed", rv
== APR_SUCCESS
);
409 ABTS_INT_EQUAL(tc
, 271, atoi(result
));
411 rv
= apr_memcache_delete(memcache
, prefix
, 0);
412 ABTS_ASSERT(tc
, "delete failed", rv
== APR_SUCCESS
);
415 /* test the multiget functionality */
416 static void test_memcache_multiget(abts_case
* tc
, void *data
)
418 apr_pool_t
*pool
= p
;
421 apr_memcache_t
*memcache
;
422 apr_memcache_server_t
*server
;
423 apr_hash_t
*tdata
, *values
;
424 apr_hash_index_t
*hi
;
427 rv
= apr_memcache_create(pool
, 1, 0, &memcache
);
428 ABTS_ASSERT(tc
, "memcache create failed", rv
== APR_SUCCESS
);
430 rv
= apr_memcache_server_create(pool
, HOST
, PORT
, 0, 1, 1, 60, &server
);
431 ABTS_ASSERT(tc
, "server create failed", rv
== APR_SUCCESS
);
433 rv
= apr_memcache_add_server(memcache
, server
);
434 ABTS_ASSERT(tc
, "server add failed", rv
== APR_SUCCESS
);
436 values
= apr_hash_make(p
);
437 tdata
= apr_hash_make(p
);
439 create_test_hash(pool
, tdata
);
441 for (hi
= apr_hash_first(p
, tdata
); hi
; hi
= apr_hash_next(hi
)) {
446 apr_hash_this(hi
, &k
, NULL
, &v
);
449 rv
= apr_memcache_set(memcache
, key
, v
, strlen(v
), 0, 27);
450 ABTS_ASSERT(tc
, "set failed", rv
== APR_SUCCESS
);
453 rv
= apr_pool_create(&tmppool
, pool
);
454 for (i
= 0; i
< TDATA_SET
; i
++)
455 apr_memcache_add_multget_key(pool
,
456 apr_pstrcat(pool
, prefix
,
457 apr_itoa(pool
, i
), NULL
),
460 rv
= apr_memcache_multgetp(memcache
,
465 ABTS_ASSERT(tc
, "multgetp failed", rv
== APR_SUCCESS
);
466 ABTS_ASSERT(tc
, "multgetp returned too few results",
467 apr_hash_count(values
) == TDATA_SET
);
469 for (hi
= apr_hash_first(p
, tdata
); hi
; hi
= apr_hash_next(hi
)) {
473 apr_hash_this(hi
, &k
, NULL
, NULL
);
476 rv
= apr_memcache_delete(memcache
, key
, 0);
477 ABTS_ASSERT(tc
, "delete failed", rv
== APR_SUCCESS
);
482 /* test setting and getting */
484 static void test_memcache_setget(abts_case
* tc
, void *data
)
486 apr_pool_t
*pool
= p
;
488 apr_memcache_t
*memcache
;
489 apr_memcache_server_t
*server
;
490 apr_hash_t
*tdata
, *values
;
491 apr_hash_index_t
*hi
;
495 rv
= apr_memcache_create(pool
, 1, 0, &memcache
);
496 ABTS_ASSERT(tc
, "memcache create failed", rv
== APR_SUCCESS
);
498 rv
= apr_memcache_server_create(pool
, HOST
, PORT
, 0, 1, 1, 60, &server
);
499 ABTS_ASSERT(tc
, "server create failed", rv
== APR_SUCCESS
);
501 rv
= apr_memcache_add_server(memcache
, server
);
502 ABTS_ASSERT(tc
, "server add failed", rv
== APR_SUCCESS
);
504 tdata
= apr_hash_make(pool
);
505 values
= apr_hash_make(pool
);
507 create_test_hash(pool
, tdata
);
509 for (hi
= apr_hash_first(p
, tdata
); hi
; hi
= apr_hash_next(hi
)) {
514 apr_hash_this(hi
, &k
, NULL
, &v
);
517 rv
= apr_memcache_set(memcache
, key
, v
, strlen(v
), 0, 27);
518 ABTS_ASSERT(tc
, "set failed", rv
== APR_SUCCESS
);
519 rv
= apr_memcache_getp(memcache
, pool
, key
, &result
, &len
, NULL
);
520 ABTS_ASSERT(tc
, "get failed", rv
== APR_SUCCESS
);
523 rv
= apr_memcache_getp(memcache
, pool
, "nothere3423", &result
, &len
, NULL
);
525 ABTS_ASSERT(tc
, "get should have failed", rv
!= APR_SUCCESS
);
527 for (hi
= apr_hash_first(p
, tdata
); hi
; hi
= apr_hash_next(hi
)) {
531 apr_hash_this(hi
, &k
, NULL
, NULL
);
534 rv
= apr_memcache_delete(memcache
, key
, 0);
535 ABTS_ASSERT(tc
, "delete failed", rv
== APR_SUCCESS
);
539 /* use apr_socket stuff to see if there is in fact a memcached server
542 apr_status_t
check_mc(void)
544 apr_pool_t
*pool
= p
;
546 apr_socket_t
*sock
= NULL
;
553 rv
= apr_socket_create(&sock
, APR_INET
, SOCK_STREAM
, 0, pool
);
554 if(rv
!= APR_SUCCESS
) {
558 rv
= apr_sockaddr_info_get(&sa
, HOST
, APR_INET
, PORT
, 0, pool
);
559 if(rv
!= APR_SUCCESS
) {
563 rv
= apr_socket_timeout_set(sock
, 1 * APR_USEC_PER_SEC
);
564 if (rv
!= APR_SUCCESS
) {
568 rv
= apr_socket_connect(sock
, sa
);
569 if (rv
!= APR_SUCCESS
) {
573 rv
= apr_socket_timeout_set(sock
, -1);
574 if (rv
!= APR_SUCCESS
) {
578 vec
[0].iov_base
= "version";
579 vec
[0].iov_len
= sizeof("version") - 1;
581 vec
[1].iov_base
= "\r\n";
582 vec
[1].iov_len
= sizeof("\r\n") -1;
584 rv
= apr_socket_sendv(sock
, vec
, 2, &written
);
585 if (rv
!= APR_SUCCESS
) {
590 rv
= apr_socket_recv(sock
, buf
, &len
);
591 if(rv
!= APR_SUCCESS
) {
595 if(strncmp(buf
, "VERSION", sizeof("VERSION")-1) != 0) {
599 apr_socket_close(sock
);
603 abts_suite
*testmemcache(abts_suite
* suite
)
606 suite
= ADD_SUITE(suite
);
607 /* check for a running memcached on the typical port before
608 * trying to run the tests. succeed silently if we don't find one.
611 if(rv
== APR_SUCCESS
) {
612 abts_run_test(suite
, test_memcache_create
, NULL
);
613 abts_run_test(suite
, test_memcache_user_funcs
, NULL
);
614 abts_run_test(suite
, test_memcache_meta
, NULL
);
615 abts_run_test(suite
, test_memcache_setget
, NULL
);
616 abts_run_test(suite
, test_memcache_multiget
, NULL
);
617 abts_run_test(suite
, test_memcache_addreplace
, NULL
);
618 abts_run_test(suite
, test_memcache_incrdecr
, NULL
);