tests: Avoid verbose output for 'make check V=0'
[openafs.git] / tests / opr / cache-t.c
1 /*
2  * Copyright (c) 2019 Sine Nomine Associates. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <afsconfig.h>
26 #include <afs/param.h>
27
28 #include <errno.h>
29 #include <string.h>
30
31 #include <tests/tap/basic.h>
32 #include <afs/opr.h>
33
34 #include <stdlib.h>
35 #include <time.h>
36
37 struct {
38     char *key;
39     int key_len;
40
41     char *val;
42     int val_len;
43 } items[] = {
44
45 #define TCASE(key, len, val) { (key), (len), (val), sizeof(val)-1 }
46
47     TCASE("foo\0\0", 6, "one"),
48     TCASE("bar\0\0", 6, "two"),
49     TCASE("baz\0\0", 6, "three"),
50     TCASE("quux\0",  6, "four"),
51     TCASE("pants",   6, "five"),
52
53     TCASE("foo\0\0", 4, "six"),
54     TCASE("bar\0\0", 4, "seven"),
55     TCASE("baz\0\0", 4, "eight"),
56     TCASE("quux\0",  5, "nine"),
57
58     TCASE("foo1\0", 6, "ten"),
59     TCASE("bar1\0", 6, "eleven"),
60     TCASE("baz1\0", 6, "twelve"),
61     TCASE("quux1",  6, "thirteen"),
62
63     TCASE("f\xf3\x0a", 5, "value \x01"),
64     TCASE("ba\xffr", 5, "\x01\x02\x03"),
65     TCASE("ba\xffz", 5, "\0\0\0\0"),
66
67 #undef TCASE
68
69 };
70 static const int n_items = sizeof(items)/sizeof(items[0]);
71
72 static void
73 run_seed(int seed)
74 {
75     struct opr_cache_opts opts;
76     struct opr_cache *cache = NULL;
77     int item_i;
78     int missing;
79     int code;
80     char val[1024];
81     size_t val_len;
82
83     srand(seed);
84
85     val_len = sizeof(val);
86     code = opr_cache_get(cache, NULL, 0, val, &val_len);
87     is_int(ENOENT, code,
88        "Looking up in a NULL cache fails with ENOENT");
89
90     opr_cache_put(cache, NULL, 0, NULL, 0);
91     ok(1,
92        "Storing in a NULL cache does nothing");
93
94     memset(&opts, 0, sizeof(opts));
95
96     opts.n_buckets = 2;
97     opts.max_entries = 100;
98     ok(opr_cache_init(&opts, &cache) != 0,
99        "Initializing a cache with a tiny n_buckets fails");
100
101     opts.n_buckets = 0x40000000;
102     ok(opr_cache_init(&opts, &cache) != 0,
103        "Initializing a cache with a huge n_buckets fails");
104
105     opts.n_buckets = 1024*1024 + 1;
106     ok(opr_cache_init(&opts, &cache) != 0,
107        "Initializing a cache with 1024*1024+1 n_buckets fails");
108
109     opts.n_buckets = 1024*1024;
110     code = opr_cache_init(&opts, &cache);
111     is_int(0, code,
112        "Initializing a cache with 1024*1024 n_buckets succeeds");
113     opr_cache_free(&cache);
114
115     opts.n_buckets = 1024*1024 - 1;
116     code = opr_cache_init(&opts, &cache);
117     is_int(0, code,
118        "Initializing a cache with 1024*1024-1 n_buckets succeeds");
119     opr_cache_free(&cache);
120
121     opts.n_buckets = 23;
122     code = opr_cache_init(&opts, &cache);
123     is_int(0, code,
124        "Initializing a cache with non-power-of-2 n_buckets succeeds");
125     opr_cache_free(&cache);
126
127     opts.n_buckets = 64;
128     opts.max_entries = 1;
129     ok(opr_cache_init(&opts, &cache) != 0,
130        "Initializing a cache with a tiny max_entries fails");
131
132     opts.max_entries = 0x7fffffff;
133     ok(opr_cache_init(&opts, &cache) != 0,
134        "Initializing a cache with a huge max_entries fails");
135
136     opts.n_buckets = 8;
137     opts.max_entries = 12;
138
139     code = opr_cache_init(&opts, &cache);
140     is_int(0, code,
141        "Initializing a reasonably-sized cache succeeds");
142
143     ok(cache != NULL,
144        "Initializing a cache gives us a cache");
145
146     for (item_i = 0; item_i < n_items; item_i++) {
147         val_len = sizeof(val);
148         code = opr_cache_get(cache, items[item_i].key,
149                              items[item_i].key_len,
150                              val, &val_len);
151         is_int(ENOENT, code,
152            "[item %d] Looking up in an empty cache fails with ENOENT", item_i);
153     }
154
155     for (item_i = 0; item_i < 12; item_i++) {
156         opr_cache_put(cache, items[item_i].key, items[item_i].key_len,
157                       items[item_i].val, items[item_i].val_len);
158     }
159     ok(1, "Cache filled successfully");
160
161     for (item_i = 0; item_i < 12; item_i++) {
162         val_len = sizeof(val);
163         code = opr_cache_get(cache, items[item_i].key, items[item_i].key_len,
164                              val, &val_len);
165         is_int(0, code, "[item %d] Lookup succeeds", item_i);
166         is_int(items[item_i].val_len, val_len,
167            "[item %d] Lookup returns correct val_len %d",
168            item_i, items[item_i].val_len);
169
170         ok(memcmp(val, items[item_i].val, val_len) == 0,
171            "[item %d] Lookup returns correct val", item_i);
172     }
173
174     val_len = sizeof(val);
175     code = opr_cache_get(cache, NULL, 5, val, &val_len);
176     is_int(ENOENT, code,
177         "Looking up NULL key fails with ENOENT");
178
179     code = opr_cache_get(cache, val, 0, val, &val_len);
180     is_int(ENOENT, code,
181         "Looking up 0-length key fails with ENOENT");
182
183     opr_cache_put(cache, NULL, 0, val, val_len);
184     opr_cache_put(cache, NULL, 5, val, val_len);
185     opr_cache_put(cache, val, 0, val, val_len);
186     opr_cache_put(cache, val, val_len, NULL, 0);
187     opr_cache_put(cache, val, val_len, NULL, 5);
188     opr_cache_put(cache, val, val_len, val, 0);
189     opr_cache_put(cache, NULL, 0, NULL, 0);
190     opr_cache_put(cache, NULL, 5, NULL, 5);
191     opr_cache_put(cache, val, 0, val, 0);
192     ok(1, "Storing NULL/0-length entries does nothing");
193
194     code = opr_cache_get(cache, "123", 3, val, &val_len);
195     is_int(ENOENT, code, "Cache lookup fails for nonexistent item");
196
197     memcpy(val, "replace", 7);
198     val_len = 7;
199     opr_cache_put(cache, items[0].key, items[0].key_len, val, val_len);
200     ok(1, "Replacement store succeeds");
201
202     val_len = 1;
203     code = opr_cache_get(cache, items[0].key, items[0].key_len, val, &val_len);
204     is_int(ENOSPC, code, "Small lookup returns ENOSPC");
205
206     val_len = sizeof(val);
207     code = opr_cache_get(cache, items[0].key, items[0].key_len, val, &val_len);
208     is_int(0, code, "Replacement lookup succeeds");
209     is_int(7, val_len, "Lookup trims val_len");
210     ok(memcmp(val, "replace", 7) == 0,
211         "Replacement lookup returns correct value");
212
213     /* Set items[0] back to the original value. */
214     opr_cache_put(cache, items[0].key, items[0].key_len, items[0].val,
215                   items[0].val_len);
216
217     for (item_i = 12; item_i < n_items; item_i++) {
218         opr_cache_put(cache, items[item_i].key, items[item_i].key_len,
219                       items[item_i].val, items[item_i].val_len);
220     }
221     ok(1, "[seed %d] Cache over-filled successfully", seed);
222
223     missing = 0;
224     for (item_i = 0; item_i < n_items; item_i++) {
225         val_len = sizeof(val);
226         code = opr_cache_get(cache, items[item_i].key, items[item_i].key_len,
227                              val, &val_len);
228         if (code == ENOENT) {
229             missing++;
230             continue;
231         }
232         is_int(0, code, "[item %d] Lookup succeeds", item_i);
233         is_int(items[item_i].val_len, val_len,
234            "[item %d] Lookup returns correct val_len %d",
235            item_i, items[item_i].val_len);
236
237         ok(memcmp(val, items[item_i].val, val_len) == 0,
238             "[item %d] Lookup returns correct val", item_i);
239     }
240
241     is_int(4, missing,
242         "[seed %d] Cache lookup fails for %d items", seed, missing);
243
244     opr_cache_free(&cache);
245     ok(1, "Cache free succeeds");
246     ok(cache == NULL, "Cache free NULLs arg");
247
248     opr_cache_free(&cache);
249     ok(1, "Double-free is noop");
250     ok(cache == NULL, "Cache is still NULL after double-free");
251 }
252
253 int
254 main(void)
255 {
256     int seed;
257
258     plan(116 * 32);
259
260     for (seed = 0; seed < 32; seed++) {
261         run_seed(seed);
262     }
263
264     return 0;
265 }