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 "apr.h"
18#include "apr_general.h"
19#include "apr_pools.h"
20#include "apr_errno.h"
21#include "apr_dbm.h"
22#include "apr_uuid.h"
23#include "apr_strings.h"
24#include "abts.h"
25#include "testutil.h"
26
27#define NUM_TABLE_ROWS  1024
28
29typedef struct {
30    apr_datum_t key;
31    apr_datum_t val;
32    int deleted;
33    int visited;
34} dbm_table_t;
35
36static dbm_table_t *generate_table(void)
37{
38    unsigned int i;
39    apr_uuid_t uuid;
40    dbm_table_t *table = apr_pcalloc(p, sizeof(*table) * NUM_TABLE_ROWS);
41
42    for (i = 0; i < NUM_TABLE_ROWS/2; i++) {
43        apr_uuid_get(&uuid);
44        table[i].key.dptr = apr_pmemdup(p, uuid.data, sizeof(uuid.data));
45        table[i].key.dsize = sizeof(uuid.data);
46        table[i].val.dptr = apr_palloc(p, APR_UUID_FORMATTED_LENGTH);
47        table[i].val.dsize = APR_UUID_FORMATTED_LENGTH;
48        apr_uuid_format(table[i].val.dptr, &uuid);
49    }
50
51    for (; i < NUM_TABLE_ROWS; i++) {
52        apr_uuid_get(&uuid);
53        table[i].val.dptr = apr_pmemdup(p, uuid.data, sizeof(uuid.data));
54        table[i].val.dsize = sizeof(uuid.data);
55        table[i].key.dptr = apr_palloc(p, APR_UUID_FORMATTED_LENGTH);
56        table[i].key.dsize = APR_UUID_FORMATTED_LENGTH;
57        apr_uuid_format(table[i].key.dptr, &uuid);
58    }
59
60    return table;
61}
62
63static void test_dbm_store(abts_case *tc, apr_dbm_t *db, dbm_table_t *table)
64{
65    apr_status_t rv;
66    unsigned int i = NUM_TABLE_ROWS - 1;
67
68    for (; i >= NUM_TABLE_ROWS/2; i--) {
69        rv = apr_dbm_store(db, table[i].key, table[i].val);
70        ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
71        table[i].deleted = FALSE;
72    }
73
74    for (i = 0; i < NUM_TABLE_ROWS/2; i++) {
75        rv = apr_dbm_store(db, table[i].key, table[i].val);
76        ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
77        table[i].deleted = FALSE;
78    }
79}
80
81static void test_dbm_fetch(abts_case *tc, apr_dbm_t *db, dbm_table_t *table)
82{
83    apr_status_t rv;
84    unsigned int i;
85    apr_datum_t val;
86
87    for (i = 0; i < NUM_TABLE_ROWS; i++) {
88        memset(&val, 0, sizeof(val));
89        rv = apr_dbm_fetch(db, table[i].key, &val);
90        if (!table[i].deleted) {
91            ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
92            ABTS_INT_EQUAL(tc, table[i].val.dsize, val.dsize);
93            ABTS_INT_EQUAL(tc, 0, memcmp(table[i].val.dptr, val.dptr, val.dsize));
94            apr_dbm_freedatum(db, val);
95        } else {
96            ABTS_INT_EQUAL(tc, 0, val.dsize);
97        }
98    }
99}
100
101static void test_dbm_delete(abts_case *tc, apr_dbm_t *db, dbm_table_t *table)
102{
103    apr_status_t rv;
104    unsigned int i;
105
106    for (i = 0; i < NUM_TABLE_ROWS; i++) {
107        /* XXX: random */
108        if (i & 1)
109            continue;
110        rv = apr_dbm_delete(db, table[i].key);
111        ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
112        table[i].deleted = TRUE;
113    }
114}
115
116static void test_dbm_exists(abts_case *tc, apr_dbm_t *db, dbm_table_t *table)
117{
118    unsigned int i;
119    int cond;
120
121    for (i = 0; i < NUM_TABLE_ROWS; i++) {
122        cond = apr_dbm_exists(db, table[i].key);
123        if (table[i].deleted) {
124            ABTS_TRUE(tc, cond == 0);
125        } else {
126            ABTS_TRUE(tc, cond != 0);
127        }
128    }
129}
130
131static void test_dbm_traversal(abts_case *tc, apr_dbm_t *db, dbm_table_t *table)
132{
133    apr_status_t rv;
134    unsigned int i;
135    apr_datum_t key;
136
137    rv = apr_dbm_firstkey(db, &key);
138    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
139
140    do {
141        if (key.dptr == NULL || key.dsize == 0)
142            break;
143
144        for (i = 0; i < NUM_TABLE_ROWS; i++) {
145            if (table[i].key.dsize != key.dsize)
146                continue;
147            if (memcmp(table[i].key.dptr, key.dptr, key.dsize))
148                continue;
149            ABTS_INT_EQUAL(tc, 0, table[i].deleted);
150            ABTS_INT_EQUAL(tc, 0, table[i].visited);
151            table[i].visited++;
152        }
153
154        rv = apr_dbm_nextkey(db, &key);
155        ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
156    } while (1);
157
158    for (i = 0; i < NUM_TABLE_ROWS; i++) {
159        if (table[i].deleted)
160            continue;
161        ABTS_INT_EQUAL(tc, 1, table[i].visited);
162        table[i].visited = 0;
163    }
164}
165
166static void test_dbm(abts_case *tc, void *data)
167{
168    apr_dbm_t *db;
169    apr_status_t rv;
170    dbm_table_t *table;
171    const char *type = data;
172    const char *file = apr_pstrcat(p, "data/test-", type, NULL);
173
174    rv = apr_dbm_open_ex(&db, type, file, APR_DBM_RWCREATE, APR_OS_DEFAULT, p);
175    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
176
177    if (rv != APR_SUCCESS)
178        return;
179
180    table = generate_table();
181
182    test_dbm_store(tc, db, table);
183    test_dbm_fetch(tc, db, table);
184    test_dbm_delete(tc, db, table);
185    test_dbm_exists(tc, db, table);
186    test_dbm_traversal(tc, db, table);
187
188    apr_dbm_close(db);
189
190    rv = apr_dbm_open_ex(&db, type, file, APR_DBM_READONLY, APR_OS_DEFAULT, p);
191    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
192
193    if (rv != APR_SUCCESS)
194        return;
195
196    test_dbm_exists(tc, db, table);
197    test_dbm_traversal(tc, db, table);
198    test_dbm_fetch(tc, db, table);
199
200    apr_dbm_close(db);
201}
202
203abts_suite *testdbm(abts_suite *suite)
204{
205    suite = ADD_SUITE(suite);
206
207#if APU_HAVE_GDBM
208    abts_run_test(suite, test_dbm, "gdbm");
209#endif
210#if APU_HAVE_NDBM
211    abts_run_test(suite, test_dbm, "ndbm");
212#endif
213#if APU_HAVE_SDBM
214    abts_run_test(suite, test_dbm, "sdbm");
215#endif
216#if APU_HAVE_DB
217    abts_run_test(suite, test_dbm, "db");
218#endif
219
220    return suite;
221}
222