]> git.ozlabs.org Git - ccan/blob - ccan/ntdb/test/run-04-basichash.c
ntdb: fix up tests.
[ccan] / ccan / ntdb / test / run-04-basichash.c
1 #include "ntdb-source.h"
2 #include "tap-interface.h"
3 #include "logging.h"
4 #include "helprun-external-agent.h"
5
6 /* We rig the hash so all records clash. */
7 static uint32_t clash(const void *key, size_t len, uint32_t seed, void *priv)
8 {
9         return *((const unsigned int *)key) << 20;
10 }
11
12 int main(int argc, char *argv[])
13 {
14         unsigned int i;
15         struct ntdb_context *ntdb;
16         unsigned int v;
17         struct ntdb_used_record rec;
18         NTDB_DATA key = { (unsigned char *)&v, sizeof(v) };
19         NTDB_DATA dbuf = { (unsigned char *)&v, sizeof(v) };
20         union ntdb_attribute hattr = { .hash = { .base = { NTDB_ATTRIBUTE_HASH },
21                                                 .fn = clash } };
22         int flags[] = { NTDB_INTERNAL, NTDB_DEFAULT, NTDB_NOMMAP,
23                         NTDB_INTERNAL|NTDB_CONVERT, NTDB_CONVERT,
24                         NTDB_NOMMAP|NTDB_CONVERT,
25         };
26
27         hattr.base.next = &tap_log_attr;
28
29         plan_tests(sizeof(flags) / sizeof(flags[0]) * 137 + 1);
30         for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
31                 struct hash_info h;
32                 ntdb_off_t new_off, new_off2, off;
33
34                 ntdb = ntdb_open("run-04-basichash.ntdb", flags[i]|MAYBE_NOSYNC,
35                                  O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr);
36                 ok1(ntdb);
37                 if (!ntdb)
38                         continue;
39
40                 v = 0;
41                 /* Should not find it. */
42                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
43                 /* Should have created correct hash. */
44                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
45                 /* Should have located space in top table, bucket 0. */
46                 ok1(h.table == NTDB_HASH_OFFSET);
47                 ok1(h.table_size == (1 << ntdb->hash_bits));
48                 ok1(h.bucket == 0);
49                 ok1(h.old_val == 0);
50
51                 /* Should have lock on bucket 0 */
52                 ok1(h.h == 0);
53                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
54                 ok1((ntdb->flags & NTDB_NOLOCK)
55                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
56                 /* FIXME: Check lock length */
57
58                 /* Allocate a new record. */
59                 new_off = alloc(ntdb, key.dsize, dbuf.dsize,
60                                 NTDB_USED_MAGIC, false);
61                 ok1(!NTDB_OFF_IS_ERR(new_off));
62
63                 /* We should be able to add it now. */
64                 ok1(add_to_hash(ntdb, &h, new_off) == 0);
65
66                 /* Make sure we fill it in for later finding. */
67                 off = new_off + sizeof(struct ntdb_used_record);
68                 ok1(!ntdb->io->twrite(ntdb, off, key.dptr, key.dsize));
69                 off += key.dsize;
70                 ok1(!ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize));
71
72                 /* We should be able to unlock that OK. */
73                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
74
75                 /* Database should be consistent. */
76                 ok1(ntdb_check(ntdb, NULL, NULL) == 0);
77
78                 /* Now, this should give a successful lookup. */
79                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == new_off);
80                 /* Should have created correct hash. */
81                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
82                 /* Should have located it in top table, bucket 0. */
83                 ok1(h.table == NTDB_HASH_OFFSET);
84                 ok1(h.table_size == (1 << ntdb->hash_bits));
85                 ok1(h.bucket == 0);
86
87                 /* Should have lock on bucket 0 */
88                 ok1(h.h == 0);
89                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
90                 ok1((ntdb->flags & NTDB_NOLOCK)
91                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
92                 /* FIXME: Check lock length */
93
94                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
95
96                 /* Database should be consistent. */
97                 ok1(ntdb_check(ntdb, NULL, NULL) == 0);
98
99                 /* Test expansion. */
100                 v = 1;
101                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
102                 /* Should have created correct hash. */
103                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
104                 /* Should have located clash in toplevel bucket 0. */
105                 ok1(h.table == NTDB_HASH_OFFSET);
106                 ok1(h.table_size == (1 << ntdb->hash_bits));
107                 ok1(h.bucket == 0);
108                 ok1((h.old_val & NTDB_OFF_MASK) == new_off);
109
110                 /* Should have lock on bucket 0 */
111                 ok1((h.h & ((1 << ntdb->hash_bits)-1)) == 0);
112                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
113                 ok1((ntdb->flags & NTDB_NOLOCK)
114                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
115                 /* FIXME: Check lock length */
116
117                 new_off2 = alloc(ntdb, key.dsize, dbuf.dsize,
118                                  NTDB_USED_MAGIC, false);
119                 ok1(!NTDB_OFF_IS_ERR(new_off2));
120
121                 off = new_off2 + sizeof(struct ntdb_used_record);
122                 ok1(!ntdb->io->twrite(ntdb, off, key.dptr, key.dsize));
123                 off += key.dsize;
124                 ok1(!ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize));
125
126                 /* We should be able to add it now. */
127                 ok1(add_to_hash(ntdb, &h, new_off2) == 0);
128                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
129
130                 /* Should be happy with expansion. */
131                 ok1(ntdb_check(ntdb, NULL, NULL) == 0);
132
133                 /* Should be able to find both. */
134                 v = 1;
135                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == new_off2);
136                 /* Should have created correct hash. */
137                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
138                 /* Should have located space in chain. */
139                 ok1(h.table > NTDB_HASH_OFFSET);
140                 ok1(h.table_size == 2);
141                 ok1(h.bucket == 1);
142                 /* Should have lock on bucket 0 */
143                 ok1((h.h & ((1 << ntdb->hash_bits)-1)) == 0);
144                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
145                 ok1((ntdb->flags & NTDB_NOLOCK)
146                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
147                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
148
149                 v = 0;
150                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == new_off);
151                 /* Should have created correct hash. */
152                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
153                 /* Should have located space in chain. */
154                 ok1(h.table > NTDB_HASH_OFFSET);
155                 ok1(h.table_size == 2);
156                 ok1(h.bucket == 0);
157
158                 /* Should have lock on bucket 0 */
159                 ok1((h.h & ((1 << ntdb->hash_bits)-1)) == 0);
160                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
161                 ok1((ntdb->flags & NTDB_NOLOCK)
162                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
163                 /* FIXME: Check lock length */
164
165                 /* Simple delete should work. */
166                 ok1(delete_from_hash(ntdb, &h) == 0);
167                 ok1(add_free_record(ntdb, new_off,
168                                     sizeof(struct ntdb_used_record)
169                                     + rec_key_length(&rec)
170                                     + rec_data_length(&rec)
171                                     + rec_extra_padding(&rec),
172                                     NTDB_LOCK_NOWAIT, false) == 0);
173                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
174                 ok1(ntdb_check(ntdb, NULL, NULL) == 0);
175
176                 /* Should still be able to find other record. */
177                 v = 1;
178                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == new_off2);
179                 /* Should have created correct hash. */
180                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
181                 /* Should have located space in chain. */
182                 ok1(h.table > NTDB_HASH_OFFSET);
183                 ok1(h.table_size == 2);
184                 ok1(h.bucket == 1);
185                 /* Should have lock on bucket 0 */
186                 ok1((h.h & ((1 << ntdb->hash_bits)-1)) == 0);
187                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
188                 ok1((ntdb->flags & NTDB_NOLOCK)
189                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
190                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
191
192                 /* Now should find empty space. */
193                 v = 0;
194                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
195                 /* Should have created correct hash. */
196                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
197                 /* Should have located space in chain, bucket 0. */
198                 ok1(h.table > NTDB_HASH_OFFSET);
199                 ok1(h.table_size == 2);
200                 ok1(h.bucket == 0);
201                 ok1(h.old_val == 0);
202
203                 /* Adding another record should work. */
204                 v = 2;
205                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
206                 /* Should have created correct hash. */
207                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
208                 /* Should have located space in chain, bucket 0. */
209                 ok1(h.table > NTDB_HASH_OFFSET);
210                 ok1(h.table_size == 2);
211                 ok1(h.bucket == 0);
212                 ok1(h.old_val == 0);
213
214                 /* Should have lock on bucket 0 */
215                 ok1((h.h & ((1 << ntdb->hash_bits)-1)) == 0);
216                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
217                 ok1((ntdb->flags & NTDB_NOLOCK)
218                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
219
220                 new_off = alloc(ntdb, key.dsize, dbuf.dsize,
221                                 NTDB_USED_MAGIC, false);
222                 ok1(!NTDB_OFF_IS_ERR(new_off2));
223                 ok1(add_to_hash(ntdb, &h, new_off) == 0);
224                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
225
226                 off = new_off + sizeof(struct ntdb_used_record);
227                 ok1(!ntdb->io->twrite(ntdb, off, key.dptr, key.dsize));
228                 off += key.dsize;
229                 ok1(!ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize));
230
231                 /* Adding another record should cause expansion. */
232                 v = 3;
233                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
234                 /* Should have created correct hash. */
235                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
236                 /* Should not have located space in chain. */
237                 ok1(h.table > NTDB_HASH_OFFSET);
238                 ok1(h.table_size == 2);
239                 ok1(h.bucket == 2);
240                 ok1(h.old_val != 0);
241
242                 /* Should have lock on bucket 0 */
243                 ok1((h.h & ((1 << ntdb->hash_bits)-1)) == 0);
244                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
245                 ok1((ntdb->flags & NTDB_NOLOCK)
246                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
247
248                 new_off = alloc(ntdb, key.dsize, dbuf.dsize,
249                                 NTDB_USED_MAGIC, false);
250                 ok1(!NTDB_OFF_IS_ERR(new_off2));
251                 off = new_off + sizeof(struct ntdb_used_record);
252                 ok1(!ntdb->io->twrite(ntdb, off, key.dptr, key.dsize));
253                 off += key.dsize;
254                 ok1(!ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize));
255                 ok1(add_to_hash(ntdb, &h, new_off) == 0);
256                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
257
258                 /* Retrieve it and check. */
259                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == new_off);
260                 /* Should have created correct hash. */
261                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
262                 /* Should have appended to chain, bucket 2. */
263                 ok1(h.table > NTDB_HASH_OFFSET);
264                 ok1(h.table_size == 3);
265                 ok1(h.bucket == 2);
266
267                 /* Should have lock on bucket 0 */
268                 ok1((h.h & ((1 << ntdb->hash_bits)-1)) == 0);
269                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
270                 ok1((ntdb->flags & NTDB_NOLOCK)
271                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
272                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
273
274                 /* YA record: relocation. */
275                 v = 4;
276                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == 0);
277                 /* Should have created correct hash. */
278                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
279                 /* Should not have located space in chain. */
280                 ok1(h.table > NTDB_HASH_OFFSET);
281                 ok1(h.table_size == 3);
282                 ok1(h.bucket == 3);
283                 ok1(h.old_val != 0);
284
285                 /* Should have lock on bucket 0 */
286                 ok1((h.h & ((1 << ntdb->hash_bits)-1)) == 0);
287                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
288                 ok1((ntdb->flags & NTDB_NOLOCK)
289                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
290
291                 new_off = alloc(ntdb, key.dsize, dbuf.dsize,
292                                 NTDB_USED_MAGIC, false);
293                 ok1(!NTDB_OFF_IS_ERR(new_off2));
294                 off = new_off + sizeof(struct ntdb_used_record);
295                 ok1(!ntdb->io->twrite(ntdb, off, key.dptr, key.dsize));
296                 off += key.dsize;
297                 ok1(!ntdb->io->twrite(ntdb, off, dbuf.dptr, dbuf.dsize));
298                 ok1(add_to_hash(ntdb, &h, new_off) == 0);
299                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
300
301                 /* Retrieve it and check. */
302                 ok1(find_and_lock(ntdb, key, F_WRLCK, &h, &rec, NULL) == new_off);
303                 /* Should have created correct hash. */
304                 ok1(h.h == ntdb_hash(ntdb, key.dptr, key.dsize));
305                 /* Should have appended to chain, bucket 2. */
306                 ok1(h.table > NTDB_HASH_OFFSET);
307                 ok1(h.table_size == 4);
308                 ok1(h.bucket == 3);
309
310                 /* Should have lock on bucket 0 */
311                 ok1((h.h & ((1 << ntdb->hash_bits)-1)) == 0);
312                 ok1((ntdb->flags & NTDB_NOLOCK) || ntdb->file->num_lockrecs == 1);
313                 ok1((ntdb->flags & NTDB_NOLOCK)
314                     || ntdb->file->lockrecs[0].off == NTDB_HASH_LOCK_START);
315                 ok1(ntdb_unlock_hash(ntdb, h.h, F_WRLCK) == 0);
316
317                 ntdb_close(ntdb);
318         }
319
320         ok1(tap_log_messages == 0);
321         return exit_status();
322 }