Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: add a test which adds/deletes random values to/from grn_pat
- Loading branch information
Showing
2 changed files
with
298 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,296 @@ | ||
| /* -*- c-basic-offset: 2; coding: utf-8 -*- */ | ||
| /* | ||
| Copyright (C) 2015 Brazil | ||
| This library is free software; you can redistribute it and/or | ||
| modify it under the terms of the GNU Lesser General Public | ||
| License version 2.1 as published by the Free Software Foundation. | ||
| This library is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| Lesser General Public License for more details. | ||
| You should have received a copy of the GNU Lesser General Public | ||
| License along with this library; if not, write to the Free Software | ||
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| */ | ||
|
|
||
| #include <grn_pat.h> | ||
|
|
||
| #include <gcutter.h> | ||
| #include <glib/gstdio.h> | ||
|
|
||
| #include "../lib/grn-assertions.h" | ||
|
|
||
| /* | ||
| * N_EXECS (5) and N_OPS (1000) may not be enough to detect a rare bug, but it | ||
| * will take too long time if enough values are used, such as N_EXECS (100) and | ||
| * N_OPS (10000). | ||
| */ | ||
|
|
||
| #define N_EXECS 5 /* The number of execution in each test case. */ | ||
| #define N_OPS 1000 /* The number of operations in each execution. */ | ||
| #define N_RECS 8 /* The maximum number of records */ | ||
| /* This value is used as the buffer size. */ | ||
| #define MAX_LEN 8 /* The maximum value length. */ | ||
| /* This value is used as the buffer size. */ | ||
| #define MIN_LABEL '0' /* The minimum value label. */ | ||
| #define MAX_LABEL '7' /* The maximum value label. */ | ||
|
|
||
| /* Value. */ | ||
| typedef struct { | ||
| uint8_t buf[MAX_LEN]; | ||
| int len; | ||
| } val_t; | ||
|
|
||
| static int val_cmp(const val_t *lhs, const val_t *rhs) | ||
| { | ||
| int min_len = (lhs->len < rhs->len) ? lhs->len : rhs->len; | ||
| int cmp = memcmp(lhs->buf, rhs->buf, min_len); | ||
| return cmp ? cmp : lhs->len - rhs->len; | ||
| } | ||
|
|
||
| /* Operation type. */ | ||
| typedef enum { | ||
| OP_ADD, | ||
| OP_DEL | ||
| } op_type; | ||
|
|
||
| /* Operation. */ | ||
| typedef struct { | ||
| op_type type; | ||
| val_t val; | ||
| } op_t; | ||
|
|
||
| /* Record. */ | ||
| typedef struct { | ||
| grn_id id; | ||
| val_t key; | ||
| } rec_t; | ||
|
|
||
| static int rec_cmp(const void *lhs, const void *rhs) | ||
| { | ||
| const rec_t *lhs_rec = (const rec_t *)lhs; | ||
| const rec_t *rhs_rec = (const rec_t *)rhs; | ||
| return val_cmp(&lhs_rec->key, &rhs_rec->key); | ||
| } | ||
|
|
||
| grn_ctx *ctx; | ||
| grn_pat *pat; | ||
|
|
||
| int max_len; /* The maximum value length. */ | ||
| int max_n_recs; /* The maximum number of records. */ | ||
|
|
||
| op_t ops[N_OPS]; | ||
| int n_ops; | ||
| rec_t recs[N_RECS]; | ||
| int n_recs; | ||
|
|
||
| /* Random number generator (128-bit XorShift) */ | ||
| struct { | ||
| uint32_t x, y, z, w; | ||
| } rng; | ||
|
|
||
| static void rng_init(uint32_t seed) | ||
| { | ||
| rng.x = 123456789 ^ seed; | ||
| rng.y = 362436069; | ||
| rng.z = 521288629; | ||
| rng.w = 88675123; | ||
| } | ||
|
|
||
| static uint32_t rng_gen(void) | ||
| { | ||
| uint32_t t; | ||
| t = rng.x ^ (rng.x << 11); | ||
| rng.x = rng.y; | ||
| rng.y = rng.z; | ||
| rng.z = rng.w; | ||
| rng.w = (rng.w ^ (rng.w >> 19)) ^ (t ^ (t >> 8)); | ||
| return rng.w; | ||
| } | ||
|
|
||
| static int rng_gen_len(void) | ||
| { | ||
| return (rng_gen() % max_len) + 1; | ||
| } | ||
|
|
||
| static uint8_t rng_gen_label(void) | ||
| { | ||
| return (uint8_t)(MIN_LABEL + (rng_gen() % (MAX_LABEL - MIN_LABEL + 1))); | ||
| } | ||
|
|
||
| static val_t rng_gen_val(void) | ||
| { | ||
| int i; | ||
| val_t val; | ||
| val.len = rng_gen_len(); | ||
| for (i = 0; i < val.len; i++) { | ||
| val.buf[i] = rng_gen_label(); | ||
| } | ||
| return val; | ||
| } | ||
|
|
||
| static op_type rng_gen_op_type(void) | ||
| { | ||
| if (n_recs >= max_n_recs) { | ||
| return OP_DEL; | ||
| } | ||
| return (rng_gen() & 1) ? OP_ADD : OP_DEL; | ||
| } | ||
|
|
||
| static op_t rng_gen_op(void) | ||
| { | ||
| op_t op; | ||
| op.type = rng_gen_op_type(); | ||
| if (op.type == OP_ADD) { | ||
| op.val = rng_gen_val(); | ||
| } else { | ||
| if (n_recs && (rng_gen() & 1)) { | ||
| op.val = recs[rng_gen() % n_recs].key; | ||
| } else { | ||
| op.val = rng_gen_val(); | ||
| } | ||
| } | ||
| return op; | ||
| } | ||
|
|
||
| static void exec_add(op_t *op) | ||
| { | ||
| int i, added, added_expected = 1; | ||
| grn_id id, id_expected = 0; | ||
| for (i = 0; i < n_recs; i++) { | ||
| if (!val_cmp(&op->val, &recs[i].key)) { | ||
| id_expected = recs[i].id; | ||
| added_expected = 0; | ||
| break; | ||
| } | ||
| } | ||
| id = grn_pat_add(ctx, pat, op->val.buf, op->val.len, NULL, &added); | ||
| if (added_expected) { | ||
| cut_assert(id && added); | ||
| recs[n_recs].id = id; | ||
| recs[n_recs].key = op->val; | ||
| n_recs++; | ||
| } else { | ||
| cut_assert(id && !added); | ||
| } | ||
| } | ||
|
|
||
| static void exec_del(op_t *op) | ||
| { | ||
| int i, rec_id = -1; | ||
| grn_rc rc; | ||
| for (i = 0; i < n_recs; i++) { | ||
| if (!val_cmp(&op->val, &recs[i].key)) { | ||
| rec_id = i; | ||
| break; | ||
| } | ||
| } | ||
| rc = grn_pat_delete(ctx, pat, op->val.buf, op->val.len, NULL); | ||
| if (rec_id != -1) { | ||
| grn_test_assert_equal_rc(GRN_SUCCESS, rc); | ||
| n_recs--; | ||
| for (i = rec_id; i < n_recs; i++) { | ||
| recs[i] = recs[i + 1]; | ||
| } | ||
| } else { | ||
| grn_test_assert_equal_rc(GRN_INVALID_ARGUMENT, rc); | ||
| } | ||
| } | ||
|
|
||
| static void exec_check(void) | ||
| { | ||
| int i; | ||
| grn_pat_cursor *cursor; | ||
|
|
||
| /* Test grn_pat_get(). */ | ||
| for (i = 0; i < n_recs; i++) { | ||
| const val_t *key = &recs[i].key; | ||
| grn_id id = grn_pat_get(ctx, pat, key->buf, key->len, NULL); | ||
| cut_assert_equal_int(recs[i].id, id); | ||
| } | ||
|
|
||
| /* Test grn_pat_cursor (GRN_CURSOR_BY_KEY). */ | ||
| qsort(recs, n_recs, sizeof(rec_t), rec_cmp); | ||
| cursor = grn_pat_cursor_open(ctx, pat, NULL, 0, NULL, 0, 0, -1, | ||
| GRN_CURSOR_BY_KEY); | ||
| cut_assert_not_null(cursor); | ||
| for (i = 0; i < n_recs; i++) { | ||
| grn_id id = grn_pat_cursor_next(ctx, cursor); | ||
| cut_assert_equal_int(recs[i].id, id); | ||
| } | ||
| grn_id id = grn_pat_cursor_next(ctx, cursor); | ||
| cut_assert_equal_int(GRN_ID_NIL, id); | ||
| grn_pat_cursor_close(ctx, cursor); | ||
| } | ||
|
|
||
| static void exec_test(uint32_t seed) | ||
| { | ||
| rng_init(seed); | ||
| n_ops = 0; | ||
| n_recs = 0; | ||
| while (n_ops < N_OPS) { | ||
| op_t *op = &ops[n_ops]; | ||
| ops[n_ops++] = rng_gen_op(); | ||
| if (op->type == OP_ADD) { | ||
| exec_add(op); | ||
| } else { | ||
| exec_del(op); | ||
| } | ||
| exec_check(); | ||
| } | ||
| } | ||
|
|
||
| static void run_test(void) | ||
| { | ||
| int i; | ||
| for (i = 0; i < N_EXECS; i++) { | ||
| pat = grn_pat_create(ctx, NULL, GRN_PAT_MAX_KEY_SIZE, 0, | ||
| GRN_OBJ_KEY_VAR_SIZE | GRN_OBJ_TEMPORARY); | ||
| cut_assert_not_null(pat); | ||
|
|
||
| exec_test(i); | ||
|
|
||
| grn_pat_close(ctx, pat); | ||
| pat = NULL; | ||
| } | ||
| } | ||
|
|
||
| void cut_setup(void) | ||
| { | ||
| ctx = NULL; | ||
| pat = NULL; | ||
|
|
||
| ctx = grn_ctx_open(0); | ||
| cut_assert_not_null(ctx); | ||
| } | ||
|
|
||
| void cut_teardown(void) | ||
| { | ||
| if (ctx) { | ||
| if (pat) { | ||
| grn_pat_close(ctx, pat); | ||
| } | ||
| grn_ctx_fin(ctx); | ||
| } | ||
| } | ||
|
|
||
| #define GRN_TEST_DEFINE(_max_len, _max_n_recs)\ | ||
| void test_len ## _max_len ## _recs ## _max_n_recs(void)\ | ||
| {\ | ||
| max_len = _max_len;\ | ||
| max_n_recs = _max_n_recs;\ | ||
| run_test();\ | ||
| } | ||
| GRN_TEST_DEFINE(1, 1) | ||
| GRN_TEST_DEFINE(1, 2) | ||
| GRN_TEST_DEFINE(1, 3) | ||
| GRN_TEST_DEFINE(2, 1) | ||
| GRN_TEST_DEFINE(2, 2) | ||
| GRN_TEST_DEFINE(2, 3) | ||
| GRN_TEST_DEFINE(3, 1) | ||
| GRN_TEST_DEFINE(3, 2) | ||
| GRN_TEST_DEFINE(3, 3) | ||
| #undef GRN_TEST_DEFINE_TEST |