Home | History | Annotate | Line # | Download | only in base
      1 /*	$NetBSD: test_base.c,v 1.2 2017/01/28 21:31:45 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2010-2016 Kungliga Tekniska Hgskolan
      5  * (Royal Institute of Technology, Stockholm, Sweden).
      6  * All rights reserved.
      7  *
      8  * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  *
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  *
     17  * 2. Redistributions in binary form must reproduce the above copyright
     18  *    notice, this list of conditions and the following disclaimer in the
     19  *    documentation and/or other materials provided with the distribution.
     20  *
     21  * 3. Neither the name of the Institute nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  */
     37 
     38 /*
     39  * This is a test of libheimbase functionality.  If you make any changes
     40  * to libheimbase or to this test you should run it under valgrind with
     41  * the following options:
     42  *
     43  *  -v --track-fds=yes --num-callers=30 --leak-check=full
     44  *
     45  * and make sure that there are no leaks that don't have
     46  * __heim_string_constant() or heim_db_register() in their stack trace.
     47  */
     48 
     49 #include <err.h>
     50 #include <errno.h>
     51 #include <stdio.h>
     52 #include <stdlib.h>
     53 #include <string.h>
     54 #include <sys/types.h>
     55 #include <sys/stat.h>
     56 #ifndef WIN32
     57 #include <sys/file.h>
     58 #endif
     59 #ifdef HAVE_IO_H
     60 #include <io.h>
     61 #endif
     62 #ifdef HAVE_UNISTD_H
     63 #include <unistd.h>
     64 #endif
     65 #include <fcntl.h>
     66 
     67 #include "baselocl.h"
     68 
     69 static void
     70 memory_free(heim_object_t obj)
     71 {
     72 }
     73 
     74 static int
     75 test_memory(void)
     76 {
     77     void *ptr;
     78 
     79     ptr = heim_alloc(10, "memory", memory_free);
     80 
     81     heim_retain(ptr);
     82     heim_release(ptr);
     83 
     84     heim_retain(ptr);
     85     heim_release(ptr);
     86 
     87     heim_release(ptr);
     88 
     89     ptr = heim_alloc(10, "memory", NULL);
     90     heim_release(ptr);
     91 
     92     return 0;
     93 }
     94 
     95 static int
     96 test_mutex(void)
     97 {
     98     HEIMDAL_MUTEX m = HEIMDAL_MUTEX_INITIALIZER;
     99 
    100     HEIMDAL_MUTEX_lock(&m);
    101     HEIMDAL_MUTEX_unlock(&m);
    102     HEIMDAL_MUTEX_destroy(&m);
    103 
    104     HEIMDAL_MUTEX_init(&m);
    105     HEIMDAL_MUTEX_lock(&m);
    106     HEIMDAL_MUTEX_unlock(&m);
    107     HEIMDAL_MUTEX_destroy(&m);
    108 
    109     return 0;
    110 }
    111 
    112 static int
    113 test_rwlock(void)
    114 {
    115     HEIMDAL_RWLOCK l = HEIMDAL_RWLOCK_INITIALIZER;
    116 
    117     HEIMDAL_RWLOCK_rdlock(&l);
    118     HEIMDAL_RWLOCK_unlock(&l);
    119     HEIMDAL_RWLOCK_wrlock(&l);
    120     HEIMDAL_RWLOCK_unlock(&l);
    121     if (HEIMDAL_RWLOCK_trywrlock(&l) != 0)
    122 	err(1, "HEIMDAL_RWLOCK_trywrlock() failed with lock not held");
    123     HEIMDAL_RWLOCK_unlock(&l);
    124     if (HEIMDAL_RWLOCK_tryrdlock(&l))
    125 	err(1, "HEIMDAL_RWLOCK_tryrdlock() failed with lock not held");
    126     HEIMDAL_RWLOCK_unlock(&l);
    127     HEIMDAL_RWLOCK_destroy(&l);
    128 
    129     HEIMDAL_RWLOCK_init(&l);
    130     HEIMDAL_RWLOCK_rdlock(&l);
    131     HEIMDAL_RWLOCK_unlock(&l);
    132     HEIMDAL_RWLOCK_wrlock(&l);
    133     HEIMDAL_RWLOCK_unlock(&l);
    134     if (HEIMDAL_RWLOCK_trywrlock(&l))
    135 	err(1, "HEIMDAL_RWLOCK_trywrlock() failed with lock not held");
    136     HEIMDAL_RWLOCK_unlock(&l);
    137     if (HEIMDAL_RWLOCK_tryrdlock(&l))
    138 	err(1, "HEIMDAL_RWLOCK_tryrdlock() failed with lock not held");
    139     HEIMDAL_RWLOCK_unlock(&l);
    140     HEIMDAL_RWLOCK_destroy(&l);
    141 
    142     return 0;
    143 }
    144 
    145 static int
    146 test_dict(void)
    147 {
    148     heim_dict_t dict;
    149     heim_number_t a1 = heim_number_create(1);
    150     heim_string_t a2 = heim_string_create("hejsan");
    151     heim_number_t a3 = heim_number_create(3);
    152     heim_string_t a4 = heim_string_create("foosan");
    153 
    154     dict = heim_dict_create(10);
    155 
    156     heim_dict_set_value(dict, a1, a2);
    157     heim_dict_set_value(dict, a3, a4);
    158 
    159     heim_dict_delete_key(dict, a3);
    160     heim_dict_delete_key(dict, a1);
    161 
    162     heim_release(a1);
    163     heim_release(a2);
    164     heim_release(a3);
    165     heim_release(a4);
    166 
    167     heim_release(dict);
    168 
    169     return 0;
    170 }
    171 
    172 static int
    173 test_auto_release(void)
    174 {
    175     heim_auto_release_t ar1, ar2;
    176     heim_number_t n1;
    177     heim_string_t s1;
    178 
    179     ar1 = heim_auto_release_create();
    180 
    181     s1 = heim_string_create("hejsan");
    182     heim_auto_release(s1);
    183 
    184     n1 = heim_number_create(1);
    185     heim_auto_release(n1);
    186 
    187     ar2 = heim_auto_release_create();
    188 
    189     n1 = heim_number_create(1);
    190     heim_auto_release(n1);
    191 
    192     heim_release(ar2);
    193     heim_release(ar1);
    194 
    195     return 0;
    196 }
    197 
    198 static int
    199 test_string(void)
    200 {
    201     heim_string_t s1, s2;
    202     const char *string = "hejsan";
    203 
    204     s1 = heim_string_create(string);
    205     s2 = heim_string_create(string);
    206 
    207     if (heim_cmp(s1, s2) != 0) {
    208 	printf("the same string is not the same\n");
    209 	exit(1);
    210     }
    211 
    212     heim_release(s1);
    213     heim_release(s2);
    214 
    215     return 0;
    216 }
    217 
    218 static int
    219 test_error(void)
    220 {
    221     heim_error_t e;
    222     heim_string_t s;
    223 
    224     e = heim_error_create(10, "foo: %s", "bar");
    225     heim_assert(heim_error_get_code(e) == 10, "error_code != 10");
    226 
    227     s = heim_error_copy_string(e);
    228     heim_assert(strcmp(heim_string_get_utf8(s), "foo: bar") == 0, "msg wrong");
    229 
    230     heim_release(s);
    231     heim_release(e);
    232 
    233     return 0;
    234 }
    235 
    236 static int
    237 test_json(void)
    238 {
    239     static char *j[] = {
    240 	"{ \"k1\" : \"s1\", \"k2\" : \"s2\" }",
    241 	"{ \"k1\" : [\"s1\", \"s2\", \"s3\"], \"k2\" : \"s3\" }",
    242 	"{ \"k1\" : {\"k2\":\"s1\",\"k3\":\"s2\",\"k4\":\"s3\"}, \"k5\" : \"s4\" }",
    243 	"[ \"v1\", \"v2\", [\"v3\",\"v4\",[\"v 5\",\" v 7 \"]], -123456789, "
    244 	    "null, true, false, 123456789, \"\"]",
    245 	" -1"
    246     };
    247     char *s;
    248     size_t i, k;
    249     heim_object_t o, o2;
    250     heim_string_t k1 = heim_string_create("k1");
    251 
    252     o = heim_json_create("\"string\"", 10, 0, NULL);
    253     heim_assert(o != NULL, "string");
    254     heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
    255     heim_assert(strcmp("string", heim_string_get_utf8(o)) == 0, "wrong string");
    256     heim_release(o);
    257 
    258     o = heim_json_create(" \"foo\\\"bar\" ]", 10, 0, NULL);
    259     heim_assert(o != NULL, "string");
    260     heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
    261     heim_assert(strcmp("foo\"bar", heim_string_get_utf8(o)) == 0, "wrong string");
    262     heim_release(o);
    263 
    264     o = heim_json_create(" { \"key\" : \"value\" }", 10, 0, NULL);
    265     heim_assert(o != NULL, "dict");
    266     heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
    267     heim_release(o);
    268 
    269     o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
    270 			 "{ \"k3\" : \"s4\" } : -1 }", 10, 0, NULL);
    271     heim_assert(o != NULL, "dict");
    272     heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
    273     heim_release(o);
    274 
    275     o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
    276 			 "{ \"k3\" : \"s4\" } : -1 }", 10,
    277 			 HEIM_JSON_F_STRICT_DICT, NULL);
    278     heim_assert(o == NULL, "dict");
    279 
    280     o = heim_json_create(" { \"k1\" : \"s1\", \"k2\" : \"s2\" }", 10, 0, NULL);
    281     heim_assert(o != NULL, "dict");
    282     heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
    283     o2 = heim_dict_copy_value(o, k1);
    284     heim_assert(heim_get_tid(o2) == heim_string_get_type_id(), "string-tid");
    285     heim_release(o2);
    286     heim_release(o);
    287 
    288     o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", 10, 0, NULL);
    289     heim_assert(o != NULL, "dict");
    290     heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
    291     o2 = heim_dict_copy_value(o, k1);
    292     heim_assert(heim_get_tid(o2) == heim_dict_get_type_id(), "dict-tid");
    293     heim_release(o2);
    294     heim_release(o);
    295 
    296     o = heim_json_create("{ \"k1\" : 1 }", 10, 0, NULL);
    297     heim_assert(o != NULL, "array");
    298     heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
    299     o2 = heim_dict_copy_value(o, k1);
    300     heim_assert(heim_get_tid(o2) == heim_number_get_type_id(), "number-tid");
    301     heim_release(o2);
    302     heim_release(o);
    303 
    304     o = heim_json_create("-10", 10, 0, NULL);
    305     heim_assert(o != NULL, "number");
    306     heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
    307     heim_release(o);
    308 
    309     o = heim_json_create("99", 10, 0, NULL);
    310     heim_assert(o != NULL, "number");
    311     heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
    312     heim_release(o);
    313 
    314     o = heim_json_create(" [ 1 ]", 10, 0, NULL);
    315     heim_assert(o != NULL, "array");
    316     heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
    317     heim_release(o);
    318 
    319     o = heim_json_create(" [ -1 ]", 10, 0, NULL);
    320     heim_assert(o != NULL, "array");
    321     heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
    322     heim_release(o);
    323 
    324     for (i = 0; i < (sizeof (j) / sizeof (j[0])); i++) {
    325 	o = heim_json_create(j[i], 10, 0, NULL);
    326 	if (o == NULL) {
    327 	    fprintf(stderr, "Failed to parse this JSON: %s\n", j[i]);
    328 	    return 1;
    329 	}
    330 	heim_release(o);
    331 	/* Simple fuzz test */
    332 	for (k = strlen(j[i]) - 1; k > 0; k--) {
    333 	    o = heim_json_create_with_bytes(j[i], k, 10, 0, NULL);
    334 	    if (o != NULL) {
    335 		fprintf(stderr, "Invalid JSON parsed: %.*s\n", (int)k, j[i]);
    336 		return EINVAL;
    337 	    }
    338 	}
    339 	/* Again, but this time make it so valgrind can find invalid accesses */
    340 	for (k = strlen(j[i]) - 1; k > 0; k--) {
    341 	    s = strndup(j[i], k);
    342 	    if (s == NULL)
    343 		return ENOMEM;
    344 	    o = heim_json_create(s, 10, 0, NULL);
    345 	    free(s);
    346 	    if (o != NULL) {
    347 		fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
    348 		return EINVAL;
    349 	    }
    350 	}
    351 	/* Again, but with no NUL termination */
    352 	for (k = strlen(j[i]) - 1; k > 0; k--) {
    353 	    s = malloc(k);
    354 	    if (s == NULL)
    355 		return ENOMEM;
    356 	    memcpy(s, j[i], k);
    357 	    o = heim_json_create_with_bytes(s, k, 10, 0, NULL);
    358 	    free(s);
    359 	    if (o != NULL) {
    360 		fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
    361 		return EINVAL;
    362 	    }
    363 	}
    364     }
    365 
    366     heim_release(k1);
    367 
    368     return 0;
    369 }
    370 
    371 static int
    372 test_path(void)
    373 {
    374     heim_dict_t dict = heim_dict_create(11);
    375     heim_string_t p1 = heim_string_create("abc");
    376     heim_string_t p2a = heim_string_create("def");
    377     heim_string_t p2b = heim_string_create("DEF");
    378     heim_number_t p3 = heim_number_create(0);
    379     heim_string_t p4a = heim_string_create("ghi");
    380     heim_string_t p4b = heim_string_create("GHI");
    381     heim_array_t a = heim_array_create();
    382     heim_number_t l1 = heim_number_create(42);
    383     heim_number_t l2 = heim_number_create(813);
    384     heim_number_t l3 = heim_number_create(1234);
    385     heim_string_t k1 = heim_string_create("k1");
    386     heim_string_t k2 = heim_string_create("k2");
    387     heim_string_t k3 = heim_string_create("k3");
    388     heim_string_t k2_1 = heim_string_create("k2-1");
    389     heim_string_t k2_2 = heim_string_create("k2-2");
    390     heim_string_t k2_3 = heim_string_create("k2-3");
    391     heim_string_t k2_4 = heim_string_create("k2-4");
    392     heim_string_t k2_5 = heim_string_create("k2-5");
    393     heim_string_t k2_5_1 = heim_string_create("k2-5-1");
    394     heim_object_t o;
    395     heim_object_t neg_num;
    396     int ret;
    397 
    398     if (!dict || !p1 || !p2a || !p2b || !p4a || !p4b)
    399 	return ENOMEM;
    400 
    401     ret = heim_path_create(dict, 11, a, NULL, p1, p2a, NULL);
    402     heim_release(a);
    403     if (ret)
    404 	return ret;
    405     ret = heim_path_create(dict, 11, l3, NULL, p1, p2b, NULL);
    406     if (ret)
    407 	return ret;
    408     o = heim_path_get(dict, NULL, p1, p2b, NULL);
    409     if (o != l3)
    410 	return 1;
    411     ret = heim_path_create(dict, 11, NULL, NULL, p1, p2a, p3, NULL);
    412     if (ret)
    413 	return ret;
    414     ret = heim_path_create(dict, 11, l1, NULL, p1, p2a, p3, p4a, NULL);
    415     if (ret)
    416 	return ret;
    417     ret = heim_path_create(dict, 11, l2, NULL, p1, p2a, p3, p4b, NULL);
    418     if (ret)
    419 	return ret;
    420 
    421     o = heim_path_get(dict, NULL, p1, p2a, p3, p4a, NULL);
    422     if (o != l1)
    423 	return 1;
    424     o = heim_path_get(dict, NULL, p1, p2a, p3, p4b, NULL);
    425     if (o != l2)
    426 	return 1;
    427 
    428     heim_release(dict);
    429 
    430     /* Test that JSON parsing works right by using heim_path_get() */
    431     dict = heim_json_create("{\"k1\":1,"
    432 			    "\"k2\":{\"k2-1\":21,"
    433 				    "\"k2-2\":null,"
    434 				    "\"k2-3\":true,"
    435 				    "\"k2-4\":false,"
    436 				    "\"k2-5\":[1,2,3,{\"k2-5-1\":-1},-2]},"
    437 			    "\"k3\":[true,false,0,42]}", 10, 0, NULL);
    438     heim_assert(dict != NULL, "dict");
    439     o = heim_path_get(dict, NULL, k1, NULL);
    440     if (heim_cmp(o, heim_number_create(1))) return 1;
    441     o = heim_path_get(dict, NULL, k2, NULL);
    442     if (heim_get_tid(o) != heim_dict_get_type_id()) return 1;
    443     o = heim_path_get(dict, NULL, k2, k2_1, NULL);
    444     if (heim_cmp(o, heim_number_create(21))) return 1;
    445     o = heim_path_get(dict, NULL, k2, k2_2, NULL);
    446     if (heim_cmp(o, heim_null_create())) return 1;
    447     o = heim_path_get(dict, NULL, k2, k2_3, NULL);
    448     if (heim_cmp(o, heim_bool_create(1))) return 1;
    449     o = heim_path_get(dict, NULL, k2, k2_4, NULL);
    450     if (heim_cmp(o, heim_bool_create(0))) return 1;
    451     o = heim_path_get(dict, NULL, k2, k2_5, NULL);
    452     if (heim_get_tid(o) != heim_array_get_type_id()) return 1;
    453     o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(0), NULL);
    454     if (heim_cmp(o, heim_number_create(1))) return 1;
    455     o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(1), NULL);
    456     if (heim_cmp(o, heim_number_create(2))) return 1;
    457     o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(3), k2_5_1, NULL);
    458     if (heim_cmp(o, neg_num = heim_number_create(-1))) return 1;
    459     heim_release(neg_num);
    460     o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(4), NULL);
    461     if (heim_cmp(o, neg_num = heim_number_create(-2))) return 1;
    462     heim_release(neg_num);
    463     o = heim_path_get(dict, NULL, k3, heim_number_create(3), NULL);
    464     if (heim_cmp(o, heim_number_create(42))) return 1;
    465 
    466     heim_release(dict);
    467     heim_release(p1);
    468     heim_release(p2a);
    469     heim_release(p2b);
    470     heim_release(p4a);
    471     heim_release(p4b);
    472     heim_release(k1);
    473     heim_release(k2);
    474     heim_release(k3);
    475     heim_release(k2_1);
    476     heim_release(k2_2);
    477     heim_release(k2_3);
    478     heim_release(k2_4);
    479     heim_release(k2_5);
    480     heim_release(k2_5_1);
    481 
    482     return 0;
    483 }
    484 
    485 typedef struct dict_db {
    486     heim_dict_t dict;
    487     int locked;
    488 } *dict_db_t;
    489 
    490 static int
    491 dict_db_open(void *plug, const char *dbtype, const char *dbname,
    492 	     heim_dict_t options, void **db, heim_error_t *error)
    493 {
    494     dict_db_t dictdb;
    495     heim_dict_t contents = NULL;
    496 
    497     if (error)
    498 	*error = NULL;
    499     if (dbtype && *dbtype && strcmp(dbtype, "dictdb"))
    500 	return EINVAL;
    501     if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0)
    502 	return EINVAL;
    503     dictdb = heim_alloc(sizeof (*dictdb), "dict_db", NULL);
    504     if (dictdb == NULL)
    505 	return ENOMEM;
    506 
    507     if (contents != NULL)
    508 	dictdb->dict = contents;
    509     else {
    510 	dictdb->dict = heim_dict_create(29);
    511 	if (dictdb->dict == NULL) {
    512 	    heim_release(dictdb);
    513 	    return ENOMEM;
    514 	}
    515     }
    516 
    517     *db = dictdb;
    518     return 0;
    519 }
    520 
    521 static int
    522 dict_db_close(void *db, heim_error_t *error)
    523 {
    524     dict_db_t dictdb = db;
    525 
    526     if (error)
    527 	*error = NULL;
    528     heim_release(dictdb->dict);
    529     heim_release(dictdb);
    530     return 0;
    531 }
    532 
    533 static int
    534 dict_db_lock(void *db, int read_only, heim_error_t *error)
    535 {
    536     dict_db_t dictdb = db;
    537 
    538     if (error)
    539 	*error = NULL;
    540     if (dictdb->locked)
    541 	return EWOULDBLOCK;
    542     dictdb->locked = 1;
    543     return 0;
    544 }
    545 
    546 static int
    547 dict_db_unlock(void *db, heim_error_t *error)
    548 {
    549     dict_db_t dictdb = db;
    550 
    551     if (error)
    552 	*error = NULL;
    553     dictdb->locked = 0;
    554     return 0;
    555 }
    556 
    557 static heim_data_t
    558 dict_db_copy_value(void *db, heim_string_t table, heim_data_t key,
    559 		  heim_error_t *error)
    560 {
    561     dict_db_t dictdb = db;
    562 
    563     if (error)
    564 	*error = NULL;
    565 
    566     return heim_retain(heim_path_get(dictdb->dict, error, table, key, NULL));
    567 }
    568 
    569 static int
    570 dict_db_set_value(void *db, heim_string_t table,
    571 		  heim_data_t key, heim_data_t value, heim_error_t *error)
    572 {
    573     dict_db_t dictdb = db;
    574 
    575     if (error)
    576 	*error = NULL;
    577 
    578     if (table == NULL)
    579 	table = HSTR("");
    580 
    581     return heim_path_create(dictdb->dict, 29, value, error, table, key, NULL);
    582 }
    583 
    584 static int
    585 dict_db_del_key(void *db, heim_string_t table, heim_data_t key,
    586 		heim_error_t *error)
    587 {
    588     dict_db_t dictdb = db;
    589 
    590     if (error)
    591 	*error = NULL;
    592 
    593     if (table == NULL)
    594 	table = HSTR("");
    595 
    596     heim_path_delete(dictdb->dict, error, table, key, NULL);
    597     return 0;
    598 }
    599 
    600 struct dict_db_iter_ctx {
    601     heim_db_iterator_f_t        iter_f;
    602     void                        *iter_ctx;
    603 };
    604 
    605 static void dict_db_iter_f(heim_object_t key, heim_object_t value, void *arg)
    606 {
    607     struct dict_db_iter_ctx *ctx = arg;
    608 
    609     ctx->iter_f((heim_object_t)key, (heim_object_t)value, ctx->iter_ctx);
    610 }
    611 
    612 static void
    613 dict_db_iter(void *db, heim_string_t table, void *iter_data,
    614 	     heim_db_iterator_f_t iter_f, heim_error_t *error)
    615 {
    616     dict_db_t dictdb = db;
    617     struct dict_db_iter_ctx ctx;
    618     heim_dict_t table_dict;
    619 
    620     if (error)
    621 	*error = NULL;
    622 
    623     if (table == NULL)
    624 	table = HSTR("");
    625 
    626     table_dict = heim_dict_copy_value(dictdb->dict, table);
    627     if (table_dict == NULL)
    628 	return;
    629 
    630     ctx.iter_ctx = iter_data;
    631     ctx.iter_f = iter_f;
    632 
    633     heim_dict_iterate_f(table_dict, &ctx, dict_db_iter_f);
    634     heim_release(table_dict);
    635 }
    636 
    637 static void
    638 test_db_iter(heim_data_t k, heim_data_t v, void *arg)
    639 {
    640     int *ret = arg;
    641     const void *kptr, *vptr;
    642     size_t klen, vlen;
    643 
    644     heim_assert(heim_get_tid(k) == heim_data_get_type_id(), "...");
    645 
    646     kptr = heim_data_get_ptr(k);
    647     klen = heim_data_get_length(k);
    648     vptr = heim_data_get_ptr(v);
    649     vlen = heim_data_get_length(v);
    650 
    651     if (klen == strlen("msg") && !strncmp(kptr, "msg", strlen("msg")) &&
    652 	vlen == strlen("abc") && !strncmp(vptr, "abc", strlen("abc")))
    653 	*ret &= ~(1);
    654     else if (klen == strlen("msg2") &&
    655 	!strncmp(kptr, "msg2", strlen("msg2")) &&
    656 	vlen == strlen("FooBar") && !strncmp(vptr, "FooBar", strlen("FooBar")))
    657 	*ret &= ~(2);
    658     else
    659 	*ret |= 4;
    660 }
    661 
    662 static struct heim_db_type dbt = {
    663     1, dict_db_open, NULL, dict_db_close,
    664     dict_db_lock, dict_db_unlock, NULL, NULL, NULL, NULL,
    665     dict_db_copy_value, dict_db_set_value,
    666     dict_db_del_key, dict_db_iter
    667 };
    668 
    669 static int
    670 test_db(const char *dbtype, const char *dbname)
    671 {
    672     heim_data_t k1, k2, v, v1, v2, v3;
    673     heim_db_t db;
    674     int ret;
    675 
    676     if (dbtype == NULL) {
    677 	ret = heim_db_register("dictdb", NULL, &dbt);
    678 	heim_assert(!ret, "...");
    679 	db = heim_db_create("dictdb", "foo", NULL, NULL);
    680 	heim_assert(!db, "...");
    681 	db = heim_db_create("foobar", "MEMORY", NULL, NULL);
    682 	heim_assert(!db, "...");
    683 	db = heim_db_create("dictdb", "MEMORY", NULL, NULL);
    684 	heim_assert(db, "...");
    685     } else {
    686 	heim_dict_t options;
    687 
    688 	options = heim_dict_create(11);
    689 	if (options == NULL) return ENOMEM;
    690 	if (heim_dict_set_value(options, HSTR("journal-filename"),
    691 				HSTR("json-journal")))
    692 	    return ENOMEM;
    693 	if (heim_dict_set_value(options, HSTR("create"), heim_null_create()))
    694 	    return ENOMEM;
    695 	if (heim_dict_set_value(options, HSTR("truncate"), heim_null_create()))
    696 	    return ENOMEM;
    697 	db = heim_db_create(dbtype, dbname, options, NULL);
    698 	heim_assert(db, "...");
    699 	heim_release(options);
    700     }
    701 
    702     k1 = heim_data_create("msg", strlen("msg"));
    703     k2 = heim_data_create("msg2", strlen("msg2"));
    704     v1 = heim_data_create("Hello world!", strlen("Hello world!"));
    705     v2 = heim_data_create("FooBar", strlen("FooBar"));
    706     v3 = heim_data_create("abc", strlen("abc"));
    707 
    708     ret = heim_db_set_value(db, NULL, k1, v1, NULL);
    709     heim_assert(!ret, "...");
    710 
    711     v = heim_db_copy_value(db, NULL, k1, NULL);
    712     heim_assert(v && !heim_cmp(v, v1), "...");
    713     heim_release(v);
    714 
    715     ret = heim_db_set_value(db, NULL, k2, v2, NULL);
    716     heim_assert(!ret, "...");
    717 
    718     v = heim_db_copy_value(db, NULL, k2, NULL);
    719     heim_assert(v && !heim_cmp(v, v2), "...");
    720     heim_release(v);
    721 
    722     ret = heim_db_set_value(db, NULL, k1, v3, NULL);
    723     heim_assert(!ret, "...");
    724 
    725     v = heim_db_copy_value(db, NULL, k1, NULL);
    726     heim_assert(v && !heim_cmp(v, v3), "...");
    727     heim_release(v);
    728 
    729     ret = 3;
    730     heim_db_iterate_f(db, NULL, &ret, test_db_iter, NULL);
    731     heim_assert(!ret, "...");
    732 
    733     ret = heim_db_begin(db, 0, NULL);
    734     heim_assert(!ret, "...");
    735 
    736     ret = heim_db_commit(db, NULL);
    737     heim_assert(!ret, "...");
    738 
    739     ret = heim_db_begin(db, 0, NULL);
    740     heim_assert(!ret, "...");
    741 
    742     ret = heim_db_rollback(db, NULL);
    743     heim_assert(!ret, "...");
    744 
    745     ret = heim_db_begin(db, 0, NULL);
    746     heim_assert(!ret, "...");
    747 
    748     ret = heim_db_set_value(db, NULL, k1, v1, NULL);
    749     heim_assert(!ret, "...");
    750 
    751     v = heim_db_copy_value(db, NULL, k1, NULL);
    752     heim_assert(v && !heim_cmp(v, v1), "...");
    753     heim_release(v);
    754 
    755     ret = heim_db_rollback(db, NULL);
    756     heim_assert(!ret, "...");
    757 
    758     v = heim_db_copy_value(db, NULL, k1, NULL);
    759     heim_assert(v && !heim_cmp(v, v3), "...");
    760     heim_release(v);
    761 
    762     ret = heim_db_begin(db, 0, NULL);
    763     heim_assert(!ret, "...");
    764 
    765     ret = heim_db_set_value(db, NULL, k1, v1, NULL);
    766     heim_assert(!ret, "...");
    767 
    768     v = heim_db_copy_value(db, NULL, k1, NULL);
    769     heim_assert(v && !heim_cmp(v, v1), "...");
    770     heim_release(v);
    771 
    772     ret = heim_db_commit(db, NULL);
    773     heim_assert(!ret, "...");
    774 
    775     v = heim_db_copy_value(db, NULL, k1, NULL);
    776     heim_assert(v && !heim_cmp(v, v1), "...");
    777     heim_release(v);
    778 
    779     ret = heim_db_begin(db, 0, NULL);
    780     heim_assert(!ret, "...");
    781 
    782     ret = heim_db_delete_key(db, NULL, k1, NULL);
    783     heim_assert(!ret, "...");
    784 
    785     v = heim_db_copy_value(db, NULL, k1, NULL);
    786     heim_assert(v == NULL, "...");
    787     heim_release(v);
    788 
    789     ret = heim_db_rollback(db, NULL);
    790     heim_assert(!ret, "...");
    791 
    792     v = heim_db_copy_value(db, NULL, k1, NULL);
    793     heim_assert(v && !heim_cmp(v, v1), "...");
    794     heim_release(v);
    795 
    796     if (dbtype != NULL) {
    797 	heim_data_t k3 = heim_data_create("value-is-a-dict", strlen("value-is-a-dict"));
    798 	heim_dict_t vdict = heim_dict_create(11);
    799 	heim_db_t db2;
    800 
    801 	heim_assert(k3 && vdict, "...");
    802 	ret = heim_dict_set_value(vdict, HSTR("vdict-k1"), heim_number_create(11));
    803 	heim_assert(!ret, "...");
    804 	ret = heim_dict_set_value(vdict, HSTR("vdict-k2"), heim_null_create());
    805 	heim_assert(!ret, "...");
    806 	ret = heim_dict_set_value(vdict, HSTR("vdict-k3"), HSTR("a value"));
    807 	heim_assert(!ret, "...");
    808 	ret = heim_db_set_value(db, NULL, k3, (heim_data_t)vdict, NULL);
    809 	heim_assert(!ret, "...");
    810 
    811 	heim_release(vdict);
    812 
    813 	db2 = heim_db_create(dbtype, dbname, NULL, NULL);
    814 	heim_assert(db2, "...");
    815 
    816 	vdict = (heim_dict_t)heim_db_copy_value(db2, NULL, k3, NULL);
    817 	heim_release(db2);
    818 	heim_release(k3);
    819 	heim_assert(vdict, "...");
    820 	heim_assert(heim_get_tid(vdict) == heim_dict_get_type_id(), "...");
    821 
    822 	v = heim_dict_copy_value(vdict, HSTR("vdict-k1"));
    823 	heim_assert(v && !heim_cmp(v, heim_number_create(11)), "...");
    824 	heim_release(v);
    825 
    826 	v = heim_dict_copy_value(vdict, HSTR("vdict-k2"));
    827 	heim_assert(v && !heim_cmp(v, heim_null_create()), "...");
    828 	heim_release(v);
    829 
    830 	v = heim_dict_copy_value(vdict, HSTR("vdict-k3"));
    831 	heim_assert(v && !heim_cmp(v, HSTR("a value")), "...");
    832 	heim_release(v);
    833 
    834 	heim_release(vdict);
    835     }
    836 
    837     heim_release(db);
    838     heim_release(k1);
    839     heim_release(k2);
    840     heim_release(v1);
    841     heim_release(v2);
    842     heim_release(v3);
    843 
    844     return 0;
    845 }
    846 
    847 struct test_array_iter_ctx {
    848     char buf[256];
    849 };
    850 
    851 static void test_array_iter(heim_object_t elt, void *arg, int *stop)
    852 {
    853     struct test_array_iter_ctx *iter_ctx = arg;
    854 
    855     strcat(iter_ctx->buf, heim_string_get_utf8((heim_string_t)elt));
    856 }
    857 
    858 static int
    859 test_array()
    860 {
    861     struct test_array_iter_ctx iter_ctx;
    862     heim_string_t s1 = heim_string_create("abc");
    863     heim_string_t s2 = heim_string_create("def");
    864     heim_string_t s3 = heim_string_create("ghi");
    865     heim_string_t s4 = heim_string_create("jkl");
    866     heim_string_t s5 = heim_string_create("mno");
    867     heim_string_t s6 = heim_string_create("pqr");
    868     heim_array_t a = heim_array_create();
    869 
    870     if (!s1 || !s2 || !s3 || !s4 || !s5 || !s6 || !a)
    871 	return ENOMEM;
    872 
    873     heim_array_append_value(a, s4);
    874     heim_array_append_value(a, s5);
    875     heim_array_insert_value(a, 0, s3);
    876     heim_array_insert_value(a, 0, s2);
    877     heim_array_append_value(a, s6);
    878     heim_array_insert_value(a, 0, s1);
    879 
    880     iter_ctx.buf[0] = '\0';
    881     heim_array_iterate_f(a, &iter_ctx, test_array_iter);
    882     if (strcmp(iter_ctx.buf, "abcdefghijklmnopqr") != 0)
    883 	return 1;
    884 
    885     iter_ctx.buf[0] = '\0';
    886     heim_array_delete_value(a, 2);
    887     heim_array_iterate_f(a, &iter_ctx, test_array_iter);
    888     if (strcmp(iter_ctx.buf, "abcdefjklmnopqr") != 0)
    889 	return 1;
    890 
    891     iter_ctx.buf[0] = '\0';
    892     heim_array_delete_value(a, 2);
    893     heim_array_iterate_f(a, &iter_ctx, test_array_iter);
    894     if (strcmp(iter_ctx.buf, "abcdefmnopqr") != 0)
    895 	return 1;
    896 
    897     iter_ctx.buf[0] = '\0';
    898     heim_array_delete_value(a, 0);
    899     heim_array_iterate_f(a, &iter_ctx, test_array_iter);
    900     if (strcmp(iter_ctx.buf, "defmnopqr") != 0)
    901 	return 1;
    902 
    903     iter_ctx.buf[0] = '\0';
    904     heim_array_delete_value(a, 2);
    905     heim_array_iterate_f(a, &iter_ctx, test_array_iter);
    906     if (strcmp(iter_ctx.buf, "defmno") != 0)
    907 	return 1;
    908 
    909     heim_array_insert_value(a, 0, s1);
    910     iter_ctx.buf[0] = '\0';
    911     heim_array_iterate_f(a, &iter_ctx, test_array_iter);
    912     if (strcmp(iter_ctx.buf, "abcdefmno") != 0)
    913 	return 1;
    914 
    915     heim_array_insert_value(a, 0, s2);
    916     iter_ctx.buf[0] = '\0';
    917     heim_array_iterate_f(a, &iter_ctx, test_array_iter);
    918     if (strcmp(iter_ctx.buf, "defabcdefmno") != 0)
    919 	return 1;
    920 
    921     heim_array_append_value(a, s3);
    922     iter_ctx.buf[0] = '\0';
    923     heim_array_iterate_f(a, &iter_ctx, test_array_iter);
    924     if (strcmp(iter_ctx.buf, "defabcdefmnoghi") != 0)
    925 	return 1;
    926 
    927     heim_array_append_value(a, s6);
    928     iter_ctx.buf[0] = '\0';
    929     heim_array_iterate_f(a, &iter_ctx, test_array_iter);
    930     if (strcmp(iter_ctx.buf, "defabcdefmnoghipqr") != 0)
    931 	return 1;
    932 
    933     heim_release(s1);
    934     heim_release(s2);
    935     heim_release(s3);
    936     heim_release(s4);
    937     heim_release(s5);
    938     heim_release(s6);
    939     heim_release(a);
    940 
    941     return 0;
    942 }
    943 
    944 int
    945 main(int argc, char **argv)
    946 {
    947     int res = 0;
    948 
    949     res |= test_memory();
    950     res |= test_mutex();
    951     res |= test_rwlock();
    952     res |= test_dict();
    953     res |= test_auto_release();
    954     res |= test_string();
    955     res |= test_error();
    956     res |= test_json();
    957     res |= test_path();
    958     res |= test_db(NULL, NULL);
    959     res |= test_db("json", argc > 1 ? argv[1] : "test_db.json");
    960     res |= test_array();
    961 
    962     return res ? 1 : 0;
    963 }
    964