diff options
Diffstat (limited to 'test/unit')
-rw-r--r-- | test/unit/Makefile | 65 | ||||
-rw-r--r-- | test/unit/Makefile.in | 33 | ||||
-rw-r--r-- | test/unit/bcache_t.c | 1036 | ||||
-rw-r--r-- | test/unit/bcache_utils_t.c | 474 | ||||
-rw-r--r-- | test/unit/bitset_t.c | 99 | ||||
-rw-r--r-- | test/unit/config_t.c | 154 | ||||
-rw-r--r-- | test/unit/dmlist_t.c | 49 | ||||
-rw-r--r-- | test/unit/dmstatus_t.c | 84 | ||||
-rw-r--r-- | test/unit/framework.c | 66 | ||||
-rw-r--r-- | test/unit/framework.h | 51 | ||||
-rw-r--r-- | test/unit/io_engine_t.c | 229 | ||||
-rw-r--r-- | test/unit/matcher_t.c | 122 | ||||
-rw-r--r-- | test/unit/percent_t.c | 102 | ||||
-rw-r--r-- | test/unit/radix_tree_t.c | 852 | ||||
-rw-r--r-- | test/unit/rt_case1.c | 1669 | ||||
-rw-r--r-- | test/unit/run.c | 335 | ||||
-rw-r--r-- | test/unit/string_t.c | 72 | ||||
-rw-r--r-- | test/unit/unit-test.sh | 21 | ||||
-rw-r--r-- | test/unit/units.h | 55 | ||||
-rw-r--r-- | test/unit/vdo_t.c | 325 |
20 files changed, 5662 insertions, 231 deletions
diff --git a/test/unit/Makefile b/test/unit/Makefile new file mode 100644 index 0000000..05e2501 --- /dev/null +++ b/test/unit/Makefile @@ -0,0 +1,65 @@ +# Copyright (C) 2011-2018 Red Hat, Inc. All rights reserved. +# +# This file is part of LVM2. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions +# of the GNU General Public License v.2. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# NOTE: this Makefile only works as 'include' for toplevel Makefile +# which defined all top_* variables + +UNIT_SOURCE=\ + device_mapper/vdo/status.c \ + \ + test/unit/bcache_t.c \ + test/unit/bcache_utils_t.c \ + test/unit/bitset_t.c \ + test/unit/config_t.c \ + test/unit/dmlist_t.c \ + test/unit/dmstatus_t.c \ + test/unit/framework.c \ + test/unit/io_engine_t.c \ + test/unit/matcher_t.c \ + test/unit/percent_t.c \ + test/unit/radix_tree_t.c \ + test/unit/run.c \ + test/unit/string_t.c \ + test/unit/vdo_t.c + +test/unit/radix_tree_t.o: test/unit/rt_case1.c + +UNIT_TARGET = test/unit/unit-test +UNIT_DEPENDS = $(UNIT_SOURCE:%.c=%.d) +UNIT_OBJECTS = $(UNIT_SOURCE:%.c=%.o) +CLEAN_TARGETS += $(UNIT_DEPENDS) $(UNIT_OBJECTS) \ + $(UNIT_SOURCE:%.c=%.gcda) \ + $(UNIT_SOURCE:%.c=%.gcno) \ + $(UNIT_TARGET) + +lib/liblvm-internal.a: lib +libdaemon/client/libdaemonclient.a: libdaemon + +$(UNIT_TARGET): $(UNIT_OBJECTS) $(LVMINTERNAL_LIBS) + @echo " [LD] $@" + $(Q) $(CC) $(CFLAGS) $(LDFLAGS) $(EXTRA_EXEC_LDFLAGS) \ + -o $@ $+ $(LVMLIBS) + +.PHONY: run-unit-test unit-test +unit-test: $(UNIT_TARGET) +run-unit-test: $(UNIT_TARGET) + @echo "Running unit tests" + test -n "$$LVM_TEST_DIR" || LVM_TEST_DIR=$${TMPDIR:-/tmp} ;\ + TESTDIR=$$(mktemp -d -t -p "$$LVM_TEST_DIR" "LVMTEST.XXXXXXXXXX") ;\ + cd "$$TESTDIR" ;\ + LD_LIBRARY_PATH=$(abs_top_builddir)/libdm:$(abs_top_builddir)/daemons/dmeventd $(abs_top_builddir)/$(UNIT_TARGET) run ;\ + cd $$OLDPWD ;\ + $(RM) -r "$${TESTDIR:?}" + +ifeq ("$(DEPENDS)","yes") +-include $(UNIT_SOURCE:%.c=%.d) +endif diff --git a/test/unit/Makefile.in b/test/unit/Makefile.in deleted file mode 100644 index 740eb14..0000000 --- a/test/unit/Makefile.in +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (C) 2011-2012 Red Hat, Inc. All rights reserved. -# -# This file is part of LVM2. -# -# This copyrighted material is made available to anyone wishing to use, -# modify, copy, or redistribute it subject to the terms and conditions -# of the GNU General Public License v.2. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -top_builddir = @top_builddir@ - -VPATH = $(srcdir) -ifeq ("@TESTING@", "yes") -SOURCES = bitset_t.c matcher_t.c config_t.c string_t.c run.c -TARGETS = run -endif - -include $(top_builddir)/make.tmpl -ifeq ("$(TESTING)", "yes") -LDLIBS += -ldevmapper @CUNIT_LIBS@ -CFLAGS += @CUNIT_CFLAGS@ - -check: unit - -unit: $(TARGETS) - @echo Running unit tests - LD_LIBRARY_PATH=$(top_builddir)/libdm ./$(TARGETS) -endif diff --git a/test/unit/bcache_t.c b/test/unit/bcache_t.c new file mode 100644 index 0000000..2668d3f --- /dev/null +++ b/test/unit/bcache_t.c @@ -0,0 +1,1036 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "lib/device/bcache.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#define SHOW_MOCK_CALLS 0 + +/*---------------------------------------------------------------- + * Mock engine + *--------------------------------------------------------------*/ +struct mock_engine { + struct io_engine e; + struct dm_list expected_calls; + struct dm_list issued_io; + unsigned max_io; + sector_t block_size; +}; + +enum method { + E_DESTROY, + E_ISSUE, + E_WAIT, + E_MAX_IO +}; + +struct mock_call { + struct dm_list list; + enum method m; + + bool match_args; + enum dir d; + int di; + block_address b; + bool issue_r; + bool wait_r; +}; + +struct mock_io { + struct dm_list list; + int di; + sector_t sb; + sector_t se; + void *data; + void *context; + bool r; +}; + +static const char *_show_method(enum method m) +{ + switch (m) { + case E_DESTROY: + return "destroy()"; + case E_ISSUE: + return "issue()"; + case E_WAIT: + return "wait()"; + case E_MAX_IO: + return "max_io()"; + } + + return "<unknown>"; +} + +static void _expect(struct mock_engine *e, enum method m) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + + T_ASSERT(mc); + mc->m = m; + mc->match_args = false; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_read(struct mock_engine *e, int di, block_address b) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + + T_ASSERT(mc); + mc->m = E_ISSUE; + mc->match_args = true; + mc->d = DIR_READ; + mc->di = di; + mc->b = b; + mc->issue_r = true; + mc->wait_r = true; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_read_any(struct mock_engine *e) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + + T_ASSERT(mc); + mc->m = E_ISSUE; + mc->match_args = false; + mc->issue_r = true; + mc->wait_r = true; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_write(struct mock_engine *e, int di, block_address b) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + + T_ASSERT(mc); + mc->m = E_ISSUE; + mc->match_args = true; + mc->d = DIR_WRITE; + mc->di = di; + mc->b = b; + mc->issue_r = true; + mc->wait_r = true; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_read_bad_issue(struct mock_engine *e, int di, block_address b) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + + T_ASSERT(mc); + mc->m = E_ISSUE; + mc->match_args = true; + mc->d = DIR_READ; + mc->di = di; + mc->b = b; + mc->issue_r = false; + mc->wait_r = true; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_write_bad_issue(struct mock_engine *e, int di, block_address b) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + + T_ASSERT(mc); + mc->m = E_ISSUE; + mc->match_args = true; + mc->d = DIR_WRITE; + mc->di = di; + mc->b = b; + mc->issue_r = false; + mc->wait_r = true; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_read_bad_wait(struct mock_engine *e, int di, block_address b) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + + T_ASSERT(mc); + mc->m = E_ISSUE; + mc->match_args = true; + mc->d = DIR_READ; + mc->di = di; + mc->b = b; + mc->issue_r = true; + mc->wait_r = false; + dm_list_add(&e->expected_calls, &mc->list); +} + +static void _expect_write_bad_wait(struct mock_engine *e, int di, block_address b) +{ + struct mock_call *mc = malloc(sizeof(*mc)); + + T_ASSERT(mc); + mc->m = E_ISSUE; + mc->match_args = true; + mc->d = DIR_WRITE; + mc->di = di; + mc->b = b; + mc->issue_r = true; + mc->wait_r = false; + dm_list_add(&e->expected_calls, &mc->list); +} + +static struct mock_call *_match_pop(struct mock_engine *e, enum method m) +{ + + struct mock_call *mc; + + if (dm_list_empty(&e->expected_calls)) + test_fail("unexpected call to method %s\n", _show_method(m)); + + mc = dm_list_item(e->expected_calls.n, struct mock_call); + dm_list_del(&mc->list); + + if (mc->m != m) + test_fail("expected %s, but got %s\n", _show_method(mc->m), _show_method(m)); +#if SHOW_MOCK_CALLS + else + fprintf(stderr, "%s called (expected)\n", _show_method(m)); +#endif + + return mc; +} + +static void _match(struct mock_engine *e, enum method m) +{ + free(_match_pop(e, m)); +} + +static void _no_outstanding_expectations(struct mock_engine *e) +{ + struct mock_call *mc; + + if (!dm_list_empty(&e->expected_calls)) { + fprintf(stderr, "unsatisfied expectations:\n"); + dm_list_iterate_items (mc, &e->expected_calls) + fprintf(stderr, " %s\n", _show_method(mc->m)); + } + T_ASSERT(dm_list_empty(&e->expected_calls)); +} + +static struct mock_engine *_to_mock(struct io_engine *e) +{ + return container_of(e, struct mock_engine, e); +} + +static void _mock_destroy(struct io_engine *e) +{ + struct mock_engine *me = _to_mock(e); + + _match(me, E_DESTROY); + T_ASSERT(dm_list_empty(&me->issued_io)); + T_ASSERT(dm_list_empty(&me->expected_calls)); + free(_to_mock(e)); +} + +static bool _mock_issue(struct io_engine *e, enum dir d, int di, + sector_t sb, sector_t se, void *data, void *context) +{ + bool r, wait_r; + struct mock_io *io; + struct mock_call *mc; + struct mock_engine *me = _to_mock(e); + + mc = _match_pop(me, E_ISSUE); + if (mc->match_args) { + T_ASSERT(d == mc->d); + T_ASSERT(di == mc->di); + T_ASSERT(sb == mc->b * me->block_size); + T_ASSERT(se == (mc->b + 1) * me->block_size); + } + r = mc->issue_r; + wait_r = mc->wait_r; + free(mc); + + if (r) { + io = malloc(sizeof(*io)); + if (!io) + abort(); + + io->di = di; + io->sb = sb; + io->se = se; + io->data = data; + io->context = context; + io->r = wait_r; + + dm_list_add(&me->issued_io, &io->list); + } + + return r; +} + +static bool _mock_wait(struct io_engine *e, io_complete_fn fn) +{ + struct mock_io *io; + struct mock_engine *me = _to_mock(e); + _match(me, E_WAIT); + + // FIXME: provide a way to control how many are completed and whether + // they error. + T_ASSERT(!dm_list_empty(&me->issued_io)); + io = dm_list_item(me->issued_io.n, struct mock_io); + dm_list_del(&io->list); + fn(io->context, io->r ? 0 : -EIO); + free(io); + + return true; +} + +static unsigned _mock_max_io(struct io_engine *e) +{ + struct mock_engine *me = _to_mock(e); + _match(me, E_MAX_IO); + return me->max_io; +} + +static struct mock_engine *_mock_create(unsigned max_io, sector_t block_size) +{ + struct mock_engine *m = malloc(sizeof(*m)); + + T_ASSERT(m); + + m->e.destroy = _mock_destroy; + m->e.issue = _mock_issue; + m->e.wait = _mock_wait; + m->e.max_io = _mock_max_io; + + m->max_io = max_io; + m->block_size = block_size; + dm_list_init(&m->expected_calls); + dm_list_init(&m->issued_io); + + return m; +} + +/*---------------------------------------------------------------- + * Fixtures + *--------------------------------------------------------------*/ +struct fixture { + struct mock_engine *me; + struct bcache *cache; +}; + +static struct fixture *_fixture_init(sector_t block_size, unsigned nr_cache_blocks) +{ + struct fixture *f = malloc(sizeof(*f)); + + T_ASSERT(f); + + f->me = _mock_create(16, block_size); + T_ASSERT(f->me); + + _expect(f->me, E_MAX_IO); + f->cache = bcache_create(block_size, nr_cache_blocks, &f->me->e); + T_ASSERT(f->cache); + + return f; +} + +static void _fixture_exit(struct fixture *f) +{ + if (f) { + _expect(f->me, E_DESTROY); + bcache_destroy(f->cache); + + free(f); + } +} + +static void *_small_fixture_init(void) +{ + return _fixture_init(128, 16); +} + +static void _small_fixture_exit(void *context) +{ + _fixture_exit(context); +} + +static void *_large_fixture_init(void) +{ + return _fixture_init(128, 1024); +} + +static void _large_fixture_exit(void *context) +{ + _fixture_exit(context); +} + +/*---------------------------------------------------------------- + * Tests + *--------------------------------------------------------------*/ +#define MEG 2048 +#define SECTOR_SHIFT 9 +#define PAGE_SIZE_SECTORS ((PAGE_SIZE) >> SECTOR_SHIFT) + +static void good_create(sector_t block_size, unsigned nr_cache_blocks) +{ + struct bcache *cache; + struct mock_engine *me = _mock_create(16, 128); + + _expect(me, E_MAX_IO); + cache = bcache_create(block_size, nr_cache_blocks, &me->e); + T_ASSERT(cache); + + _expect(me, E_DESTROY); + bcache_destroy(cache); +} + +static void bad_create(sector_t block_size, unsigned nr_cache_blocks) +{ + struct bcache *cache; + struct mock_engine *me = _mock_create(16, 128); + + _expect(me, E_MAX_IO); + cache = bcache_create(block_size, nr_cache_blocks, &me->e); + T_ASSERT(!cache); + + _expect(me, E_DESTROY); + me->e.destroy(&me->e); +} + +static void test_create(void *fixture) +{ + good_create(PAGE_SIZE_SECTORS, 16); +} + +static void test_nr_cache_blocks_must_be_positive(void *fixture) +{ + bad_create(PAGE_SIZE_SECTORS, 0); +} + +static void test_block_size_must_be_positive(void *fixture) +{ + bad_create(0, 16); +} + +static void test_block_size_must_be_multiple_of_page_size(void *fixture) +{ + static unsigned _bad_examples[] = {3, 9, 13, 1025}; + + unsigned i; + + for (i = 0; i < DM_ARRAY_SIZE(_bad_examples); i++) + bad_create(_bad_examples[i], 16); + + for (i = 1; i < 100; i++) + good_create(i * PAGE_SIZE_SECTORS, 16); +} + +static void test_get_triggers_read(void *context) +{ + struct fixture *f = context; + + int di = 17; // arbitrary key + struct block *b; + + _expect_read(f->me, di, 0); + _expect(f->me, E_WAIT); + T_ASSERT(bcache_get(f->cache, di, 0, 0, &b)); + bcache_put(b); + + _expect_read(f->me, di, 1); + _expect(f->me, E_WAIT); + T_ASSERT(bcache_get(f->cache, di, 1, GF_DIRTY, &b)); + _expect_write(f->me, di, 1); + _expect(f->me, E_WAIT); + bcache_put(b); +} + +static void test_repeated_reads_are_cached(void *context) +{ + struct fixture *f = context; + + int di = 17; // arbitrary key + unsigned i; + struct block *b; + + _expect_read(f->me, di, 0); + _expect(f->me, E_WAIT); + for (i = 0; i < 100; i++) { + T_ASSERT(bcache_get(f->cache, di, 0, 0, &b)); + bcache_put(b); + } +} + +static void test_block_gets_evicted_with_many_reads(void *context) +{ + struct fixture *f = context; + + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + const unsigned nr_cache_blocks = 16; + + int di = 17; // arbitrary key + unsigned i; + struct block *b; + + for (i = 0; i < nr_cache_blocks; i++) { + _expect_read(me, di, i); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, i, 0, &b)); + bcache_put(b); + } + + // Not enough cache blocks to hold this one + _expect_read(me, di, nr_cache_blocks); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, nr_cache_blocks, 0, &b)); + bcache_put(b); + + // Now if we run through we should find one block has been + // evicted. We go backwards because the oldest is normally + // evicted first. + _expect_read_any(me); + _expect(me, E_WAIT); + for (i = nr_cache_blocks; i; i--) { + T_ASSERT(bcache_get(cache, di, i - 1, 0, &b)); + bcache_put(b); + } +} + +static void test_prefetch_issues_a_read(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + const unsigned nr_cache_blocks = 16; + + int di = 17; // arbitrary key + unsigned i; + struct block *b; + + for (i = 0; i < nr_cache_blocks; i++) { + // prefetch should not wait + _expect_read(me, di, i); + bcache_prefetch(cache, di, i); + } + _no_outstanding_expectations(me); + + for (i = 0; i < nr_cache_blocks; i++) { + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, i, 0, &b)); + bcache_put(b); + } +} + +static void test_too_many_prefetches_does_not_trigger_a_wait(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + + const unsigned nr_cache_blocks = 16; + int di = 17; // arbitrary key + unsigned i; + + for (i = 0; i < 10 * nr_cache_blocks; i++) { + // prefetch should not wait + if (i < nr_cache_blocks) + _expect_read(me, di, i); + bcache_prefetch(cache, di, i); + } + + // Destroy will wait for any in flight IO triggered by prefetches. + for (i = 0; i < nr_cache_blocks; i++) + _expect(me, E_WAIT); +} + +static void test_dirty_data_gets_written_back(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + + int di = 17; // arbitrary key + struct block *b; + + // Expect the read + _expect_read(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, 0, GF_DIRTY, &b)); + bcache_put(b); + + // Expect the write + _expect_write(me, di, 0); + _expect(me, E_WAIT); +} + +static void test_zeroed_data_counts_as_dirty(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + + int di = 17; // arbitrary key + struct block *b; + + // No read + T_ASSERT(bcache_get(cache, di, 0, GF_ZERO, &b)); + bcache_put(b); + + // Expect the write + _expect_write(me, di, 0); + _expect(me, E_WAIT); +} + +static void test_flush_waits_for_all_dirty(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + + const unsigned count = 16; + int di = 17; // arbitrary key + unsigned i; + struct block *b; + + for (i = 0; i < count; i++) { + if (i % 2) { + T_ASSERT(bcache_get(cache, di, i, GF_ZERO, &b)); + } else { + _expect_read(me, di, i); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, i, 0, &b)); + } + bcache_put(b); + } + + for (i = 0; i < count; i++) { + if (i % 2) + _expect_write(me, di, i); + } + + for (i = 0; i < count; i++) { + if (i % 2) + _expect(me, E_WAIT); + } + + T_ASSERT(bcache_flush(cache)); + _no_outstanding_expectations(me); +} + +static void test_multiple_files(void *context) +{ + static int _dis[] = {1, 128, 345, 678, 890}; + + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + unsigned i; + + for (i = 0; i < DM_ARRAY_SIZE(_dis); i++) { + _expect_read(me, _dis[i], 0); + _expect(me, E_WAIT); + + T_ASSERT(bcache_get(cache, _dis[i], 0, 0, &b)); + bcache_put(b); + } +} + +static void test_read_bad_issue(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + + _expect_read_bad_issue(me, 17, 0); + T_ASSERT(!bcache_get(cache, 17, 0, 0, &b)); +} + +static void test_read_bad_issue_intermittent(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + _expect_read_bad_issue(me, di, 0); + T_ASSERT(!bcache_get(cache, di, 0, 0, &b)); + + _expect_read(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, 0, 0, &b)); + bcache_put(b); +} + +static void test_read_bad_wait(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + _expect_read_bad_wait(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(!bcache_get(cache, di, 0, 0, &b)); +} + +static void test_read_bad_wait_intermittent(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + _expect_read_bad_wait(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(!bcache_get(cache, di, 0, 0, &b)); + + _expect_read(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, 0, 0, &b)); + bcache_put(b); +} + +static void test_write_bad_issue_stops_flush(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + T_ASSERT(bcache_get(cache, di, 0, GF_ZERO, &b)); + _expect_write_bad_issue(me, di, 0); + bcache_put(b); + T_ASSERT(!bcache_flush(cache)); + + // we'll let it succeed the second time + _expect_write(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_flush(cache)); +} + +static void test_write_bad_io_stops_flush(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + T_ASSERT(bcache_get(cache, di, 0, GF_ZERO, &b)); + _expect_write_bad_wait(me, di, 0); + _expect(me, E_WAIT); + bcache_put(b); + T_ASSERT(!bcache_flush(cache)); + + // we'll let it succeed the second time + _expect_write(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_flush(cache)); +} + +static void test_invalidate_not_present(void *context) +{ + struct fixture *f = context; + struct bcache *cache = f->cache; + int di = 17; + + T_ASSERT(bcache_invalidate(cache, di, 0)); +} + +static void test_invalidate_present(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + _expect_read(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, 0, 0, &b)); + bcache_put(b); + + T_ASSERT(bcache_invalidate(cache, di, 0)); +} + +static void test_invalidate_after_read_error(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + _expect_read_bad_issue(me, di, 0); + T_ASSERT(!bcache_get(cache, di, 0, 0, &b)); + T_ASSERT(bcache_invalidate(cache, di, 0)); +} + +static void test_invalidate_after_write_error(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + T_ASSERT(bcache_get(cache, di, 0, GF_ZERO, &b)); + bcache_put(b); + + // invalidate should fail if the write fails + _expect_write_bad_wait(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(!bcache_invalidate(cache, di, 0)); + + // and should succeed if the write does + _expect_write(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_invalidate(cache, di, 0)); + + // a read is not required to get the block + _expect_read(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, 0, 0, &b)); + bcache_put(b); +} + +static void test_invalidate_held_block(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + T_ASSERT(bcache_get(cache, di, 0, GF_ZERO, &b)); + + T_ASSERT(!bcache_invalidate(cache, di, 0)); + + _expect_write(me, di, 0); + _expect(me, E_WAIT); + bcache_put(b); +} + +//---------------------------------------------------------------- +// abort tests + +static void test_abort_no_blocks(void *context) +{ + struct fixture *f = context; + struct bcache *cache = f->cache; + int di = 17; + + // We have no expectations + bcache_abort_di(cache, di); +} + +static void test_abort_single_block(void *context) +{ + struct fixture *f = context; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + T_ASSERT(bcache_get(cache, di, 0, GF_ZERO, &b)); + bcache_put(b); + + bcache_abort_di(cache, di); + + // no write should be issued + T_ASSERT(bcache_flush(cache)); +} + +static void test_abort_forces_reread(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di = 17; + + _expect_read(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, 0, GF_DIRTY, &b)); + bcache_put(b); + + bcache_abort_di(cache, di); + T_ASSERT(bcache_flush(cache)); + + // Check the block is re-read + _expect_read(me, di, 0); + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, di, 0, 0, &b)); + bcache_put(b); +} + +static void test_abort_only_specific_di(void *context) +{ + struct fixture *f = context; + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + struct block *b; + int di1 = 17, di2 = 18; + + T_ASSERT(bcache_get(cache, di1, 0, GF_ZERO, &b)); + bcache_put(b); + + T_ASSERT(bcache_get(cache, di1, 1, GF_ZERO, &b)); + bcache_put(b); + + T_ASSERT(bcache_get(cache, di2, 0, GF_ZERO, &b)); + bcache_put(b); + + T_ASSERT(bcache_get(cache, di2, 1, GF_ZERO, &b)); + bcache_put(b); + + bcache_abort_di(cache, di2); + + // writes for di1 should still be issued + _expect_write(me, di1, 0); + _expect_write(me, di1, 1); + + _expect(me, E_WAIT); + _expect(me, E_WAIT); + + T_ASSERT(bcache_flush(cache)); +} + +//---------------------------------------------------------------- +// Chasing a bug reported by dct + +static void _cycle(struct fixture *f, unsigned nr_cache_blocks) +{ + struct mock_engine *me = f->me; + struct bcache *cache = f->cache; + + unsigned i; + struct block *b; + + for (i = 0; i < nr_cache_blocks; i++) { + // prefetch should not wait + _expect_read(me, i, 0); + bcache_prefetch(cache, i, 0); + } + + // This double checks the reads occur in response to the prefetch + _no_outstanding_expectations(me); + + for (i = 0; i < nr_cache_blocks; i++) { + _expect(me, E_WAIT); + T_ASSERT(bcache_get(cache, i, 0, 0, &b)); + bcache_put(b); + } + + _no_outstanding_expectations(me); +} + +static void test_concurrent_reads_after_invalidate(void *context) +{ + struct fixture *f = context; + unsigned i, nr_cache_blocks = 16; + + _cycle(f, nr_cache_blocks); + for (i = 0; i < nr_cache_blocks; i++) + bcache_invalidate_di(f->cache, i); + _cycle(f, nr_cache_blocks); +} + +/*---------------------------------------------------------------- + * Top level + *--------------------------------------------------------------*/ +#define T(path, desc, fn) register_test(ts, "/base/device/bcache/" path, desc, fn) + +static struct test_suite *_tiny_tests(void) +{ + struct test_suite *ts = test_suite_create(NULL, NULL); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("create-destroy", "simple create/destroy", test_create); + T("cache-blocks-positive", "nr cache blocks must be positive", test_nr_cache_blocks_must_be_positive); + T("block-size-positive", "block size must be positive", test_block_size_must_be_positive); + T("block-size-multiple-page", "block size must be a multiple of page size", test_block_size_must_be_multiple_of_page_size); + + return ts; +} + +static struct test_suite *_small_tests(void) +{ + struct test_suite *ts = test_suite_create(_small_fixture_init, _small_fixture_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("get-reads", "bcache_get() triggers read", test_get_triggers_read); + T("reads-cached", "repeated reads are cached", test_repeated_reads_are_cached); + T("blocks-get-evicted", "block get evicted with many reads", test_block_gets_evicted_with_many_reads); + T("prefetch-reads", "prefetch issues a read", test_prefetch_issues_a_read); + T("prefetch-never-waits", "too many prefetches does not trigger a wait", test_too_many_prefetches_does_not_trigger_a_wait); + T("writeback-occurs", "dirty data gets written back", test_dirty_data_gets_written_back); + T("zero-flag-dirties", "zeroed data counts as dirty", test_zeroed_data_counts_as_dirty); + T("read-multiple-files", "read from multiple files", test_multiple_files); + T("read-bad-issue", "read fails if io engine unable to issue", test_read_bad_issue); + T("read-bad-issue-intermittent", "failed issue, followed by succes", test_read_bad_issue_intermittent); + T("read-bad-io", "read issued ok, but io fails", test_read_bad_wait); + T("read-bad-io-intermittent", "failed io, followed by success", test_read_bad_wait_intermittent); + T("write-bad-issue-stops-flush", "flush fails temporarily if any block fails to write", test_write_bad_issue_stops_flush); + T("write-bad-io-stops-flush", "flush fails temporarily if any block fails to write", test_write_bad_io_stops_flush); + T("invalidate-not-present", "invalidate a block that isn't in the cache", test_invalidate_not_present); + T("invalidate-present", "invalidate a block that is in the cache", test_invalidate_present); + T("invalidate-read-error", "invalidate a block that errored", test_invalidate_after_read_error); + T("invalidate-write-error", "invalidate a block that errored", test_invalidate_after_write_error); + T("invalidate-fails-in-held", "invalidating a held block fails", test_invalidate_held_block); + + T("abort-with-no-blocks", "you can call abort, even if there are no blocks in the cache", test_abort_no_blocks); + T("abort-single-block", "single block get silently discarded", test_abort_single_block); + T("abort-forces-read", "if a block has been discarded then another read is necc.", test_abort_forces_reread); + T("abort-specific-di", "abort doesn't effect other dis", test_abort_only_specific_di); + + T("concurrent-reads-after-invalidate", "prefetch should still issue concurrent reads after invalidate", + test_concurrent_reads_after_invalidate); + + return ts; +} + +static struct test_suite *_large_tests(void) +{ + struct test_suite *ts = test_suite_create(_large_fixture_init, _large_fixture_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("flush-waits", "flush waits for all dirty", test_flush_waits_for_all_dirty); + + return ts; +} + +void bcache_tests(struct dm_list *all_tests) +{ + dm_list_add(all_tests, &_tiny_tests()->list); + dm_list_add(all_tests, &_small_tests()->list); + dm_list_add(all_tests, &_large_tests()->list); +} diff --git a/test/unit/bcache_utils_t.c b/test/unit/bcache_utils_t.c new file mode 100644 index 0000000..f052924 --- /dev/null +++ b/test/unit/bcache_utils_t.c @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "lib/device/bcache.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/statvfs.h> + +//---------------------------------------------------------------- + +#define T_BLOCK_SIZE (PAGE_SIZE) +#define NR_BLOCKS 64 +#define INIT_PATTERN 123 + +struct fixture { + int fd; + int di; + char fname[32]; + struct bcache *cache; +}; + +static inline uint8_t _pattern_at(uint8_t pat, uint8_t byte) +{ + return pat + byte; +} + +static uint64_t byte(block_address b, uint64_t offset) +{ + return b * T_BLOCK_SIZE + offset; +} + +static void *_fix_init(struct io_engine *engine) +{ + uint8_t buffer[T_BLOCK_SIZE]; + struct fixture *f = malloc(sizeof(*f)); + unsigned b, i; + static int _runs_is_tmpfs = -1; + + memset(buffer, 0, sizeof(buffer)); + T_ASSERT(f); + + if (_runs_is_tmpfs == -1) { + snprintf(f->fname, sizeof(f->fname), "unit-test-XXXXXX"); + /* coverity[secure_temp] don't care */ + f->fd = mkstemp(f->fname); + T_ASSERT(f->fd >= 0); + (void) close(f->fd); + // test if we can reopen with O_DIRECT + if ((f->fd = open(f->fname, O_RDWR | O_DIRECT)) >= 0) { + _runs_is_tmpfs = 0; + (void) close(f->fd); + } else { + _runs_is_tmpfs = 1; // likely running on tmpfs + printf(" Running test in tmpfs, *NOT* using O_DIRECT\n"); + } + (void) unlink(f->fname); + } + + snprintf(f->fname, sizeof(f->fname), "unit-test-XXXXXX"); + /* coverity[secure_temp] don't care */ + f->fd = mkstemp(f->fname); + T_ASSERT(f->fd >= 0); + + for (b = 0; b < NR_BLOCKS; b++) { + for (i = 0; i < sizeof(buffer); i++) + buffer[i] = _pattern_at(INIT_PATTERN, byte(b, i)); + T_ASSERT(write(f->fd, buffer, T_BLOCK_SIZE) > 0); + } + + if (!_runs_is_tmpfs) { + (void) close(f->fd); + // reopen with O_DIRECT + f->fd = open(f->fname, O_RDWR | O_DIRECT); + T_ASSERT(f->fd >= 0); + } + + f->cache = bcache_create(T_BLOCK_SIZE / 512, NR_BLOCKS, engine); + T_ASSERT(f->cache); + + f->di = bcache_set_fd(f->fd); + + return f; +} + +static void *_async_init(void) +{ + struct io_engine *e = create_async_io_engine(); + T_ASSERT(e); + return _fix_init(e); +} + +static void *_sync_init(void) +{ + struct io_engine *e = create_sync_io_engine(); + T_ASSERT(e); + return _fix_init(e); +} + +static void _fix_exit(void *fixture) +{ + struct fixture *f = fixture; + + if (f) { + bcache_destroy(f->cache); + (void) close(f->fd); + bcache_clear_fd(f->di); + (void) unlink(f->fname); + free(f); + } +} + +//---------------------------------------------------------------- + +static void _verify_bytes(struct block *b, uint64_t base, + uint64_t offset, uint64_t len, uint8_t pat) +{ + unsigned i; + + for (i = 0; i < len; i++) + T_ASSERT_EQUAL(((uint8_t *) b->data)[offset + i], _pattern_at(pat, base + offset + i)); +} + +static uint64_t _min(uint64_t lhs, uint64_t rhs) +{ + return rhs < lhs ? rhs : lhs; +} + +static void _verify(struct fixture *f, uint64_t byte_b, uint64_t byte_e, uint8_t pat) +{ + struct block *b; + block_address bb = byte_b / T_BLOCK_SIZE; + block_address be = (byte_e + T_BLOCK_SIZE - 1) / T_BLOCK_SIZE; + uint64_t offset = byte_b % T_BLOCK_SIZE; + uint64_t blen, len = byte_e - byte_b; + + // Verify via bcache_read_bytes + { + unsigned i; + size_t len2 = byte_e - byte_b; + uint8_t *buffer = malloc(len2); + + T_ASSERT(buffer); + memset(buffer, 0, len2); + + T_ASSERT(bcache_read_bytes(f->cache, f->di, byte_b, len2, buffer)); + for (i = 0; i < len; i++) + T_ASSERT_EQUAL(buffer[i], _pattern_at(pat, byte_b + i)); + free(buffer); + } + + // Verify again, driving bcache directly + for (; bb != be; bb++) { + T_ASSERT(bcache_get(f->cache, f->di, bb, 0, &b)); + + blen = _min(T_BLOCK_SIZE - offset, len); + _verify_bytes(b, bb * T_BLOCK_SIZE, offset, blen, pat); + + offset = 0; + len -= blen; + + bcache_put(b); + } +} + +static void _verify_set(struct fixture *f, uint64_t byte_b, uint64_t byte_e, uint8_t val) +{ + unsigned i; + struct block *b; + block_address bb = byte_b / T_BLOCK_SIZE; + block_address be = (byte_e + T_BLOCK_SIZE - 1) / T_BLOCK_SIZE; + uint64_t offset = byte_b % T_BLOCK_SIZE; + uint64_t blen, len = byte_e - byte_b; + + for (; bb != be; bb++) { + T_ASSERT(bcache_get(f->cache, f->di, bb, 0, &b)); + + blen = _min(T_BLOCK_SIZE - offset, len); + for (i = 0; i < blen; i++) + T_ASSERT(((uint8_t *) b->data)[offset + i] == val); + + offset = 0; + len -= blen; + + bcache_put(b); + } +} + +static void _verify_zeroes(struct fixture *f, uint64_t byte_b, uint64_t byte_e) +{ + _verify_set(f, byte_b, byte_e, 0); +} + +static void _do_write(struct fixture *f, uint64_t byte_b, uint64_t byte_e, uint8_t pat) +{ + unsigned i; + size_t len = byte_e - byte_b; + uint8_t *buffer = malloc(len); + + T_ASSERT(buffer); + memset(buffer, 0, len); + + for (i = 0; i < len; i++) + buffer[i] = _pattern_at(pat, byte_b + i); + + T_ASSERT(bcache_write_bytes(f->cache, f->di, byte_b, byte_e - byte_b, buffer)); + free(buffer); +} + +static void _do_zero(struct fixture *f, uint64_t byte_b, uint64_t byte_e) +{ + T_ASSERT(bcache_zero_bytes(f->cache, f->di, byte_b, byte_e - byte_b)); +} + +static void _do_set(struct fixture *f, uint64_t byte_b, uint64_t byte_e, uint8_t val) +{ + T_ASSERT(bcache_set_bytes(f->cache, f->di, byte_b, byte_e - byte_b, val)); +} + +static void _reopen(struct fixture *f) +{ + struct io_engine *engine; + + bcache_destroy(f->cache); + engine = create_async_io_engine(); + T_ASSERT(engine); + + f->cache = bcache_create(T_BLOCK_SIZE / 512, NR_BLOCKS, engine); + T_ASSERT(f->cache); + + f->di = bcache_set_fd(f->fd); +} + +//---------------------------------------------------------------- + +static uint8_t _random_pattern(void) +{ + /* coverity[dont_call] don't care */ + return random(); +} + +static uint64_t _max_byte(void) +{ + return T_BLOCK_SIZE * NR_BLOCKS; +} + +static void _rwv_cycle(struct fixture *f, uint64_t b, uint64_t e) +{ + uint8_t pat = _random_pattern(); + + _verify(f, b, e, INIT_PATTERN); + _do_write(f, b, e, pat); + _reopen(f); + _verify(f, b < 128 ? 0 : b - 128, b, INIT_PATTERN); + _verify(f, b, e, pat); + _verify(f, e, _min(e + 128, _max_byte()), INIT_PATTERN); +} + +static void _test_rw_first_block(void *fixture) +{ + _rwv_cycle(fixture, byte(0, 0), byte(0, T_BLOCK_SIZE)); +} + +static void _test_rw_last_block(void *fixture) +{ + uint64_t last_block = NR_BLOCKS - 1; + _rwv_cycle(fixture, byte(last_block, 0), + byte(last_block, T_BLOCK_SIZE)); +} + +static void _test_rw_several_whole_blocks(void *fixture) +{ + _rwv_cycle(fixture, byte(5, 0), byte(10, 0)); +} + +static void _test_rw_within_single_block(void *fixture) +{ + _rwv_cycle(fixture, byte(7, 3), byte(7, T_BLOCK_SIZE / 2)); +} + +static void _test_rw_cross_one_boundary(void *fixture) +{ + _rwv_cycle(fixture, byte(13, 43), byte(14, 43)); +} + +static void _test_rw_many_boundaries(void *fixture) +{ + _rwv_cycle(fixture, byte(13, 13), byte(23, 13)); +} + +//---------------------------------------------------------------- + +static void _zero_cycle(struct fixture *f, uint64_t b, uint64_t e) +{ + _verify(f, b, e, INIT_PATTERN); + _do_zero(f, b, e); + _reopen(f); + _verify(f, b < 128 ? 0 : b - 128, b, INIT_PATTERN); + _verify_zeroes(f, b, e); + _verify(f, e, _min(e + 128, _max_byte()), INIT_PATTERN); +} + +static void _test_zero_first_block(void *fixture) +{ + _zero_cycle(fixture, byte(0, 0), byte(0, T_BLOCK_SIZE)); +} + +static void _test_zero_last_block(void *fixture) +{ + uint64_t last_block = NR_BLOCKS - 1; + _zero_cycle(fixture, byte(last_block, 0), byte(last_block, T_BLOCK_SIZE)); +} + +static void _test_zero_several_whole_blocks(void *fixture) +{ + _zero_cycle(fixture, byte(5, 0), byte(10, 0)); +} + +static void _test_zero_within_single_block(void *fixture) +{ + _zero_cycle(fixture, byte(7, 3), byte(7, T_BLOCK_SIZE / 2)); +} + +static void _test_zero_cross_one_boundary(void *fixture) +{ + _zero_cycle(fixture, byte(13, 43), byte(14, 43)); +} + +static void _test_zero_many_boundaries(void *fixture) +{ + _zero_cycle(fixture, byte(13, 13), byte(23, 13)); +} + +//---------------------------------------------------------------- + +static void _set_cycle(struct fixture *f, uint64_t b, uint64_t e) +{ + uint8_t val = _random_pattern(); + + _verify(f, b, e, INIT_PATTERN); + _do_set(f, b, e, val); + _reopen(f); + _verify(f, b < 128 ? 0 : b - 128, b, INIT_PATTERN); + _verify_set(f, b, e, val); + _verify(f, e, _min(e + 128, _max_byte()), INIT_PATTERN); +} + +static void _test_set_first_block(void *fixture) +{ + _set_cycle(fixture, byte(0, 0), byte(0, T_BLOCK_SIZE)); +} + +static void _test_set_last_block(void *fixture) +{ + uint64_t last_block = NR_BLOCKS - 1; + _set_cycle(fixture, byte(last_block, 0), byte(last_block, T_BLOCK_SIZE)); +} + +static void _test_set_several_whole_blocks(void *fixture) +{ + _set_cycle(fixture, byte(5, 0), byte(10, 0)); +} + +static void _test_set_within_single_block(void *fixture) +{ + _set_cycle(fixture, byte(7, 3), byte(7, T_BLOCK_SIZE / 2)); +} + +static void _test_set_cross_one_boundary(void *fixture) +{ + _set_cycle(fixture, byte(13, 43), byte(14, 43)); +} + +static void _test_set_many_boundaries(void *fixture) +{ + _set_cycle(fixture, byte(13, 13), byte(23, 13)); +} + +//---------------------------------------------------------------- + +#define T(path, desc, fn) register_test(ts, "/base/device/bcache/utils/async/" path, desc, fn) + +static struct test_suite *_async_tests(void) +{ + struct test_suite *ts = test_suite_create(_async_init, _fix_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + +#define T(path, desc, fn) register_test(ts, "/base/device/bcache/utils/async/" path, desc, fn) + T("rw-first-block", "read/write/verify the first block", _test_rw_first_block); + T("rw-last-block", "read/write/verify the last block", _test_rw_last_block); + T("rw-several-blocks", "read/write/verify several whole blocks", _test_rw_several_whole_blocks); + T("rw-within-single-block", "read/write/verify within single block", _test_rw_within_single_block); + T("rw-cross-one-boundary", "read/write/verify across one boundary", _test_rw_cross_one_boundary); + T("rw-many-boundaries", "read/write/verify many boundaries", _test_rw_many_boundaries); + + T("zero-first-block", "zero the first block", _test_zero_first_block); + T("zero-last-block", "zero the last block", _test_zero_last_block); + T("zero-several-blocks", "zero several whole blocks", _test_zero_several_whole_blocks); + T("zero-within-single-block", "zero within single block", _test_zero_within_single_block); + T("zero-cross-one-boundary", "zero across one boundary", _test_zero_cross_one_boundary); + T("zero-many-boundaries", "zero many boundaries", _test_zero_many_boundaries); + + T("set-first-block", "set the first block", _test_set_first_block); + T("set-last-block", "set the last block", _test_set_last_block); + T("set-several-blocks", "set several whole blocks", _test_set_several_whole_blocks); + T("set-within-single-block", "set within single block", _test_set_within_single_block); + T("set-cross-one-boundary", "set across one boundary", _test_set_cross_one_boundary); + T("set-many-boundaries", "set many boundaries", _test_set_many_boundaries); +#undef T + + return ts; +} + + +static struct test_suite *_sync_tests(void) +{ + struct test_suite *ts = test_suite_create(_sync_init, _fix_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + +#define T(path, desc, fn) register_test(ts, "/base/device/bcache/utils/sync/" path, desc, fn) + T("rw-first-block", "read/write/verify the first block", _test_rw_first_block); + T("rw-last-block", "read/write/verify the last block", _test_rw_last_block); + T("rw-several-blocks", "read/write/verify several whole blocks", _test_rw_several_whole_blocks); + T("rw-within-single-block", "read/write/verify within single block", _test_rw_within_single_block); + T("rw-cross-one-boundary", "read/write/verify across one boundary", _test_rw_cross_one_boundary); + T("rw-many-boundaries", "read/write/verify many boundaries", _test_rw_many_boundaries); + + T("zero-first-block", "zero the first block", _test_zero_first_block); + T("zero-last-block", "zero the last block", _test_zero_last_block); + T("zero-several-blocks", "zero several whole blocks", _test_zero_several_whole_blocks); + T("zero-within-single-block", "zero within single block", _test_zero_within_single_block); + T("zero-cross-one-boundary", "zero across one boundary", _test_zero_cross_one_boundary); + T("zero-many-boundaries", "zero many boundaries", _test_zero_many_boundaries); + + T("set-first-block", "set the first block", _test_set_first_block); + T("set-last-block", "set the last block", _test_set_last_block); + T("set-several-blocks", "set several whole blocks", _test_set_several_whole_blocks); + T("set-within-single-block", "set within single block", _test_set_within_single_block); + T("set-cross-one-boundary", "set across one boundary", _test_set_cross_one_boundary); + T("set-many-boundaries", "set many boundaries", _test_set_many_boundaries); +#undef T + + return ts; +} + +void bcache_utils_tests(struct dm_list *all_tests) +{ + dm_list_add(all_tests, &_async_tests()->list); + dm_list_add(all_tests, &_sync_tests()->list); +} + diff --git a/test/unit/bitset_t.c b/test/unit/bitset_t.c index 499de32..1e74e12 100644 --- a/test/unit/bitset_t.c +++ b/test/unit/bitset_t.c @@ -9,38 +9,43 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libdevmapper.h" -#include <CUnit/CUnit.h> - -int bitset_init(void); -int bitset_fini(void); +#include "units.h" +#include "device_mapper/all.h" enum { NR_BITS = 137 }; -static struct dm_pool *mem; +static void *_mem_init(void) { + struct dm_pool *mem = dm_pool_create("bitset test", 1024); + if (!mem) { + fprintf(stderr, "out of memory\n"); + exit(1); + } -int bitset_init(void) { - mem = dm_pool_create("bitset test", 1024); - return mem == NULL; + return mem; } -int bitset_fini(void) { - dm_pool_destroy(mem); - return 0; +static void _mem_exit(void *mem) +{ + if (mem) + dm_pool_destroy(mem); } -static void test_get_next(void) +static void test_get_next(void *fixture) { + struct dm_pool *mem = fixture; + int i, j, last = 0, first; dm_bitset_t bs = dm_bitset_create(mem, NR_BITS); + T_ASSERT(bs); + for (i = 0; i < NR_BITS; i++) - CU_ASSERT(!dm_bit(bs, i)); + T_ASSERT(!dm_bit(bs, i)); for (i = 0, j = 1; i < NR_BITS; i += j, j++) dm_bit_set(bs, i); @@ -53,10 +58,10 @@ static void test_get_next(void) } else last = dm_bit_get_next(bs, last); - CU_ASSERT(last == i); + T_ASSERT(last == i); } - CU_ASSERT(dm_bit_get_next(bs, last) == -1); + T_ASSERT(dm_bit_get_next(bs, last) == -1); } static void bit_flip(dm_bitset_t bs, int bit) @@ -68,37 +73,48 @@ static void bit_flip(dm_bitset_t bs, int bit) dm_bit_set(bs, bit); } -static void test_equal(void) +static void test_equal(void *fixture) { + struct dm_pool *mem = fixture; dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS); dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS); - int i, j; + int i, j; + + T_ASSERT(bs1); + T_ASSERT(bs2); + for (i = 0, j = 1; i < NR_BITS; i += j, j++) { dm_bit_set(bs1, i); dm_bit_set(bs2, i); } - CU_ASSERT(dm_bitset_equal(bs1, bs2)); - CU_ASSERT(dm_bitset_equal(bs2, bs1)); + T_ASSERT(dm_bitset_equal(bs1, bs2)); + T_ASSERT(dm_bitset_equal(bs2, bs1)); for (i = 0; i < NR_BITS; i++) { bit_flip(bs1, i); - CU_ASSERT(!dm_bitset_equal(bs1, bs2)); - CU_ASSERT(!dm_bitset_equal(bs2, bs1)); + T_ASSERT(!dm_bitset_equal(bs1, bs2)); + T_ASSERT(!dm_bitset_equal(bs2, bs1)); - CU_ASSERT(dm_bitset_equal(bs1, bs1)); /* comparing with self */ + T_ASSERT(dm_bitset_equal(bs1, bs1)); /* comparing with self */ bit_flip(bs1, i); } } -static void test_and(void) +static void test_and(void *fixture) { + struct dm_pool *mem = fixture; dm_bitset_t bs1 = dm_bitset_create(mem, NR_BITS); dm_bitset_t bs2 = dm_bitset_create(mem, NR_BITS); dm_bitset_t bs3 = dm_bitset_create(mem, NR_BITS); - int i, j; + int i, j; + + T_ASSERT(bs1); + T_ASSERT(bs2); + T_ASSERT(bs3); + for (i = 0, j = 1; i < NR_BITS; i += j, j++) { dm_bit_set(bs1, i); dm_bit_set(bs2, i); @@ -106,9 +122,9 @@ static void test_and(void) dm_bit_and(bs3, bs1, bs2); - CU_ASSERT(dm_bitset_equal(bs1, bs2)); - CU_ASSERT(dm_bitset_equal(bs1, bs3)); - CU_ASSERT(dm_bitset_equal(bs2, bs3)); + T_ASSERT(dm_bitset_equal(bs1, bs2)); + T_ASSERT(dm_bitset_equal(bs1, bs3)); + T_ASSERT(dm_bitset_equal(bs2, bs3)); dm_bit_clear_all(bs1); dm_bit_clear_all(bs2); @@ -122,12 +138,23 @@ static void test_and(void) dm_bit_and(bs3, bs1, bs2); for (i = 0; i < NR_BITS; i++) - CU_ASSERT(!dm_bit(bs3, i)); + T_ASSERT(!dm_bit(bs3, i)); +} + +#define T(path, desc, fn) register_test(ts, "/base/data-struct/bitset/" path, desc, fn) + +void bitset_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(_mem_init, _mem_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("get_next", "get next set bit", test_get_next); + T("equal", "equality", test_equal); + T("and", "and all bits", test_and); + + dm_list_add(all_tests, &ts->list); } -CU_TestInfo bitset_list[] = { - { (char*)"get_next", test_get_next }, - { (char*)"equal", test_equal }, - { (char*)"and", test_and }, - CU_TEST_INFO_NULL -}; diff --git a/test/unit/config_t.c b/test/unit/config_t.c index 9a8b693..cd539ab 100644 --- a/test/unit/config_t.c +++ b/test/unit/config_t.c @@ -9,25 +9,26 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libdevmapper.h" -#include <CUnit/CUnit.h> +#include "units.h" +#include "device_mapper/all.h" -int config_init(void); -int config_fini(void); - -static struct dm_pool *mem; +static void *_mem_init(void) +{ + struct dm_pool *mem = dm_pool_create("config test", 1024); + if (!mem) { + fprintf(stderr, "out of memory\n"); + exit(1); + } -int config_init(void) { - mem = dm_pool_create("config test", 1024); - return mem == NULL; + return mem; } -int config_fini(void) { +static void _mem_exit(void *mem) +{ dm_pool_destroy(mem); - return 0; } static const char *conf = @@ -60,97 +61,120 @@ static const char *overlay = " }\n" "}\n"; -static void test_parse(void) +static void test_parse(void *fixture) { struct dm_config_tree *tree = dm_config_from_string(conf); const struct dm_config_value *value; - CU_ASSERT((long) tree); - CU_ASSERT(dm_config_has_node(tree->root, "id")); - CU_ASSERT(dm_config_has_node(tree->root, "physical_volumes")); - CU_ASSERT(dm_config_has_node(tree->root, "physical_volumes/pv0")); - CU_ASSERT(dm_config_has_node(tree->root, "physical_volumes/pv0/id")); + T_ASSERT((long) tree); + T_ASSERT(dm_config_has_node(tree->root, "id")); + T_ASSERT(dm_config_has_node(tree->root, "physical_volumes")); + T_ASSERT(dm_config_has_node(tree->root, "physical_volumes/pv0")); + T_ASSERT(dm_config_has_node(tree->root, "physical_volumes/pv0/id")); - CU_ASSERT(!strcmp(dm_config_find_str(tree->root, "id", "foo"), "yada-yada")); - CU_ASSERT(!strcmp(dm_config_find_str(tree->root, "idt", "foo"), "foo")); + T_ASSERT(!strcmp(dm_config_find_str(tree->root, "id", "foo"), "yada-yada")); + T_ASSERT(!strcmp(dm_config_find_str(tree->root, "idt", "foo"), "foo")); - CU_ASSERT(!strcmp(dm_config_find_str(tree->root, "physical_volumes/pv0/bb", "foo"), "foo")); - CU_ASSERT(!strcmp(dm_config_find_str(tree->root, "physical_volumes/pv0/id", "foo"), "abcd-efgh")); + T_ASSERT(!strcmp(dm_config_find_str(tree->root, "physical_volumes/pv0/bb", "foo"), "foo")); + T_ASSERT(!strcmp(dm_config_find_str(tree->root, "physical_volumes/pv0/id", "foo"), "abcd-efgh")); - CU_ASSERT(!dm_config_get_uint32(tree->root, "id", NULL)); - CU_ASSERT(dm_config_get_uint32(tree->root, "extent_size", NULL)); + T_ASSERT(!dm_config_get_uint32(tree->root, "id", NULL)); + T_ASSERT(dm_config_get_uint32(tree->root, "extent_size", NULL)); /* FIXME: Currently everything parses as a list, even if it's not */ - // CU_ASSERT(!dm_config_get_list(tree->root, "id", NULL)); - // CU_ASSERT(!dm_config_get_list(tree->root, "extent_size", NULL)); + // T_ASSERT(!dm_config_get_list(tree->root, "id", NULL)); + // T_ASSERT(!dm_config_get_list(tree->root, "extent_size", NULL)); - CU_ASSERT(dm_config_get_list(tree->root, "flags", &value)); - CU_ASSERT(value->next == NULL); /* an empty list */ - CU_ASSERT(dm_config_get_list(tree->root, "status", &value)); - CU_ASSERT(value->next != NULL); /* a non-empty list */ + T_ASSERT(dm_config_get_list(tree->root, "flags", &value)); + T_ASSERT(value->next == NULL); /* an empty list */ + T_ASSERT(dm_config_get_list(tree->root, "status", &value)); + T_ASSERT(value->next != NULL); /* a non-empty list */ dm_config_destroy(tree); } -static void test_clone(void) +static void test_clone(void *fixture) { struct dm_config_tree *tree = dm_config_from_string(conf); - struct dm_config_node *n = dm_config_clone_node(tree, tree->root, 1); + struct dm_config_node *n; const struct dm_config_value *value; + T_ASSERT(tree); + + n = dm_config_clone_node(tree, tree->root, 1); + + T_ASSERT(n); + /* Check that the nodes are actually distinct. */ - CU_ASSERT(n != tree->root); - CU_ASSERT(n->sib != tree->root->sib); - CU_ASSERT(dm_config_find_node(n, "physical_volumes") != NULL); - CU_ASSERT(dm_config_find_node(tree->root, "physical_volumes") != NULL); - CU_ASSERT(dm_config_find_node(n, "physical_volumes") != dm_config_find_node(tree->root, "physical_volumes")); + T_ASSERT(n != tree->root); + T_ASSERT(n->sib != tree->root->sib); + T_ASSERT(dm_config_find_node(n, "physical_volumes") != NULL); + T_ASSERT(dm_config_find_node(tree->root, "physical_volumes") != NULL); + T_ASSERT(dm_config_find_node(n, "physical_volumes") != dm_config_find_node(tree->root, "physical_volumes")); - CU_ASSERT(dm_config_has_node(n, "id")); - CU_ASSERT(dm_config_has_node(n, "physical_volumes")); - CU_ASSERT(dm_config_has_node(n, "physical_volumes/pv0")); - CU_ASSERT(dm_config_has_node(n, "physical_volumes/pv0/id")); + T_ASSERT(dm_config_has_node(n, "id")); + T_ASSERT(dm_config_has_node(n, "physical_volumes")); + T_ASSERT(dm_config_has_node(n, "physical_volumes/pv0")); + T_ASSERT(dm_config_has_node(n, "physical_volumes/pv0/id")); - CU_ASSERT(!strcmp(dm_config_find_str(n, "id", "foo"), "yada-yada")); - CU_ASSERT(!strcmp(dm_config_find_str(n, "idt", "foo"), "foo")); + T_ASSERT(!strcmp(dm_config_find_str(n, "id", "foo"), "yada-yada")); + T_ASSERT(!strcmp(dm_config_find_str(n, "idt", "foo"), "foo")); - CU_ASSERT(!strcmp(dm_config_find_str(n, "physical_volumes/pv0/bb", "foo"), "foo")); - CU_ASSERT(!strcmp(dm_config_find_str(n, "physical_volumes/pv0/id", "foo"), "abcd-efgh")); + T_ASSERT(!strcmp(dm_config_find_str(n, "physical_volumes/pv0/bb", "foo"), "foo")); + T_ASSERT(!strcmp(dm_config_find_str(n, "physical_volumes/pv0/id", "foo"), "abcd-efgh")); - CU_ASSERT(!dm_config_get_uint32(n, "id", NULL)); - CU_ASSERT(dm_config_get_uint32(n, "extent_size", NULL)); + T_ASSERT(!dm_config_get_uint32(n, "id", NULL)); + T_ASSERT(dm_config_get_uint32(n, "extent_size", NULL)); /* FIXME: Currently everything parses as a list, even if it's not */ - // CU_ASSERT(!dm_config_get_list(tree->root, "id", NULL)); - // CU_ASSERT(!dm_config_get_list(tree->root, "extent_size", NULL)); + // T_ASSERT(!dm_config_get_list(tree->root, "id", NULL)); + // T_ASSERT(!dm_config_get_list(tree->root, "extent_size", NULL)); - CU_ASSERT(dm_config_get_list(n, "flags", &value)); - CU_ASSERT(value->next == NULL); /* an empty list */ - CU_ASSERT(dm_config_get_list(n, "status", &value)); - CU_ASSERT(value->next != NULL); /* a non-empty list */ + T_ASSERT(dm_config_get_list(n, "flags", &value)); + T_ASSERT(value->next == NULL); /* an empty list */ + T_ASSERT(dm_config_get_list(n, "status", &value)); + T_ASSERT(value->next != NULL); /* a non-empty list */ dm_config_destroy(tree); } -static void test_cascade(void) +static void test_cascade(void *fixture) { struct dm_config_tree *t1 = dm_config_from_string(conf), *t2 = dm_config_from_string(overlay), - *tree = dm_config_insert_cascaded_tree(t2, t1); + *tree; + + T_ASSERT(t1); + T_ASSERT(t2); - CU_ASSERT(!strcmp(dm_config_tree_find_str(tree, "id", "foo"), "yoda-soda")); - CU_ASSERT(!strcmp(dm_config_tree_find_str(tree, "idt", "foo"), "foo")); + tree = dm_config_insert_cascaded_tree(t2, t1); - CU_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv0/bb", "foo"), "foo")); - CU_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv1/id", "foo"), "hgfe-dcba")); - CU_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv3/id", "foo"), "dbcd-efgh")); + T_ASSERT(tree); + + T_ASSERT(!strcmp(dm_config_tree_find_str(tree, "id", "foo"), "yoda-soda")); + T_ASSERT(!strcmp(dm_config_tree_find_str(tree, "idt", "foo"), "foo")); + + T_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv0/bb", "foo"), "foo")); + T_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv1/id", "foo"), "hgfe-dcba")); + T_ASSERT(!strcmp(dm_config_tree_find_str(tree, "physical_volumes/pv3/id", "foo"), "dbcd-efgh")); dm_config_destroy(t1); dm_config_destroy(t2); } -CU_TestInfo config_list[] = { - { (char*)"parse", test_parse }, - { (char*)"clone", test_clone }, - { (char*)"cascade", test_cascade }, - CU_TEST_INFO_NULL +#define T(path, desc, fn) register_test(ts, "/metadata/config/" path, desc, fn) + +void config_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(_mem_init, _mem_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("parse", "parsing various", test_parse); + T("clone", "duplicating a config tree", test_clone); + T("cascade", "cascade", test_cascade); + + dm_list_add(all_tests, &ts->list); }; diff --git a/test/unit/dmlist_t.c b/test/unit/dmlist_t.c new file mode 100644 index 0000000..0e33385 --- /dev/null +++ b/test/unit/dmlist_t.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "device_mapper/all.h" + +static void test_dmlist_splice(void *fixture) +{ + unsigned i; + struct dm_list a[10]; + struct dm_list list1; + struct dm_list list2; + + dm_list_init(&list1); + dm_list_init(&list2); + + for (i = 0; i < DM_ARRAY_SIZE(a); i++) + dm_list_add(&list1, &a[i]); + + dm_list_splice(&list2, &list1); + T_ASSERT(dm_list_size(&list1) == 0); + T_ASSERT(dm_list_size(&list2) == 10); +} + +#define T(path, desc, fn) register_test(ts, "/base/data-struct/list/" path, desc, fn) + +void dm_list_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(NULL, NULL); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("splice", "joining lists together", test_dmlist_splice); + + dm_list_add(all_tests, &ts->list); +} diff --git a/test/unit/dmstatus_t.c b/test/unit/dmstatus_t.c new file mode 100644 index 0000000..ac2015e --- /dev/null +++ b/test/unit/dmstatus_t.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "device_mapper/all.h" + +static void *_mem_init(void) +{ + struct dm_pool *mem = dm_pool_create("dmstatus test", 1024); + if (!mem) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + return mem; +} + +static void _mem_exit(void *mem) +{ + dm_pool_destroy(mem); +} + +static void _test_mirror_status(void *fixture) +{ + struct dm_pool *mem = fixture; + struct dm_status_mirror *s = NULL; + + T_ASSERT(dm_get_status_mirror(mem, + "2 253:1 253:2 80/81 1 AD 3 disk 253:0 A", + &s)); + if (s) { + T_ASSERT_EQUAL(s->total_regions, 81); + T_ASSERT_EQUAL(s->insync_regions, 80); + T_ASSERT_EQUAL(s->dev_count, 2); + T_ASSERT_EQUAL(s->devs[0].health, 'A'); + T_ASSERT_EQUAL(s->devs[0].major, 253); + T_ASSERT_EQUAL(s->devs[0].minor, 1); + T_ASSERT_EQUAL(s->devs[1].health, 'D'); + T_ASSERT_EQUAL(s->devs[1].major, 253); + T_ASSERT_EQUAL(s->devs[1].minor, 2); + T_ASSERT_EQUAL(s->log_count, 1); + T_ASSERT_EQUAL(s->logs[0].major, 253); + T_ASSERT_EQUAL(s->logs[0].minor, 0); + T_ASSERT_EQUAL(s->logs[0].health, 'A'); + T_ASSERT(!strcmp(s->log_type, "disk")); + } + + T_ASSERT(dm_get_status_mirror(mem, + "4 253:1 253:2 253:3 253:4 10/10 1 ADFF 1 core", + &s)); + if (s) { + T_ASSERT_EQUAL(s->total_regions, 10); + T_ASSERT_EQUAL(s->insync_regions, 10); + T_ASSERT_EQUAL(s->dev_count, 4); + T_ASSERT_EQUAL(s->devs[3].minor, 4); + T_ASSERT_EQUAL(s->devs[3].health, 'F'); + T_ASSERT_EQUAL(s->log_count, 0); + T_ASSERT(!strcmp(s->log_type, "core")); + } +} + +void dm_status_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(_mem_init, _mem_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + register_test(ts, "/device-mapper/mirror/status", "parsing mirror status", _test_mirror_status); + dm_list_add(all_tests, &ts->list); +} + diff --git a/test/unit/framework.c b/test/unit/framework.c new file mode 100644 index 0000000..de9a8b1 --- /dev/null +++ b/test/unit/framework.c @@ -0,0 +1,66 @@ +#include "framework.h" + +/*---------------------------------------------------------------- + * Assertions + *--------------------------------------------------------------*/ + +jmp_buf test_k; +#define TEST_FAILED 1 + +void test_fail(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + + longjmp(test_k, TEST_FAILED); +} + +struct test_suite *test_suite_create(void *(*fixture_init)(void), + void (*fixture_exit)(void *)) +{ + struct test_suite *ts = malloc(sizeof(*ts)); + if (ts) { + ts->fixture_init = fixture_init; + ts->fixture_exit = fixture_exit; + dm_list_init(&ts->tests); + } + + return ts; +} + +void test_suite_destroy(struct test_suite *ts) +{ + struct test_details *td, *tmp; + + dm_list_iterate_items_safe (td, tmp, &ts->tests) { + dm_list_del(&td->list); + free(td); + } + + free(ts); +} + +bool register_test(struct test_suite *ts, + const char *path, const char *desc, + void (*fn)(void *)) +{ + struct test_details *t = malloc(sizeof(*t)); + if (!t) { + fprintf(stderr, "out of memory\n"); + return false; + } + + t->parent = ts; + t->path = path; + t->desc = desc; + t->fn = fn; + dm_list_add(&ts->tests, &t->list); + + return true; +} + +//----------------------------------------------------------------- diff --git a/test/unit/framework.h b/test/unit/framework.h new file mode 100644 index 0000000..f7f5b5b --- /dev/null +++ b/test/unit/framework.h @@ -0,0 +1,51 @@ +#ifndef TEST_UNIT_FRAMEWORK_H +#define TEST_UNIT_FRAMEWORK_H + +#include "device_mapper/all.h" + +#include <stdbool.h> +#include <stdint.h> +#include <setjmp.h> + +//----------------------------------------------------------------- + +// A test suite gathers a set of tests with a common fixture together. +struct test_suite { + struct dm_list list; + + void *(*fixture_init)(void); + void (*fixture_exit)(void *); + struct dm_list tests; +}; + +struct test_details { + struct test_suite *parent; + struct dm_list list; + + const char *path; + const char *desc; + void (*fn)(void *); +}; + +struct test_suite *test_suite_create(void *(*fixture_init)(void), + void (*fixture_exit)(void *)); +void test_suite_destroy(struct test_suite *ts); + +bool register_test(struct test_suite *ts, + const char *path, const char *desc, void (*fn)(void *)); + +void test_fail(const char *fmt, ...) + __attribute__((noreturn, format (printf, 1, 2))); + +#define T_ASSERT(e) do {if (!(e)) {test_fail("assertion failed: '%s'", # e);} } while(0) +#define T_ASSERT_EQUAL(x, y) T_ASSERT((x) == (y)) +#define T_ASSERT_NOT_EQUAL(x, y) T_ASSERT((x) != (y)) + +extern jmp_buf test_k; +#define TEST_FAILED 1 + +#define PAGE_SIZE ({ int ps = sysconf(_SC_PAGESIZE); (ps > 0) ? ps : 4096 ; }) + +//----------------------------------------------------------------- + +#endif diff --git a/test/unit/io_engine_t.c b/test/unit/io_engine_t.c new file mode 100644 index 0000000..2f6ea5b --- /dev/null +++ b/test/unit/io_engine_t.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "lib/device/bcache.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +//---------------------------------------------------------------- + +#define SECTOR_SHIFT 9 +#define SECTOR_SIZE 512 +#define BLOCK_SIZE_SECTORS 8 +#define PAGE_SIZE_SECTORS ((PAGE_SIZE) >> SECTOR_SHIFT) +#define NR_BLOCKS 64 + +struct fixture { + struct io_engine *e; + uint8_t *data; + + char fname[64]; + int fd; + int di; +}; + +static void _fill_buffer(uint8_t *buffer, uint8_t seed, size_t count) +{ + unsigned i; + uint8_t b = seed; + + for (i = 0; i < count; i++) { + buffer[i] = b; + b = ((b << 5) + b) + i; + } +} + +static void _check_buffer(uint8_t *buffer, uint8_t seed, size_t count) +{ + unsigned i; + uint8_t b = seed; + + for (i = 0; i < count; i++) { + T_ASSERT_EQUAL(buffer[i], b); + b = ((b << 5) + b) + i; + } +} + +static void _print_buffer(const char *name, uint8_t *buffer, size_t count) +{ + unsigned col; + + fprintf(stderr, "%s:\n", name); + while (count) { + for (col = 0; count && col < 20; col++) { + fprintf(stderr, "%x, ", (unsigned) *buffer); + col++; + buffer++; + count--; + } + fprintf(stderr, "\n"); + } +} + +static void *_fix_init(void) +{ + struct fixture *f = malloc(sizeof(*f)); + + T_ASSERT(f); + f->e = create_async_io_engine(); + T_ASSERT(f->e); + if (posix_memalign((void **) &f->data, PAGE_SIZE, SECTOR_SIZE * BLOCK_SIZE_SECTORS)) + test_fail("posix_memalign failed"); + + snprintf(f->fname, sizeof(f->fname), "unit-test-XXXXXX"); + /* coverity[secure_temp] don't care */ + f->fd = mkstemp(f->fname); + T_ASSERT(f->fd >= 0); + + _fill_buffer(f->data, 123, SECTOR_SIZE * BLOCK_SIZE_SECTORS); + + T_ASSERT(write(f->fd, f->data, SECTOR_SIZE * BLOCK_SIZE_SECTORS) > 0); + T_ASSERT(lseek(f->fd, 0, SEEK_SET) != -1); + + return f; +} + +static void _fix_exit(void *fixture) +{ + struct fixture *f = fixture; + + if (f) { + (void) close(f->fd); + bcache_clear_fd(f->di); + (void) unlink(f->fname); + free(f->data); + if (f->e) + f->e->destroy(f->e); + free(f); + } +} + +static void _test_create(void *fixture) +{ + // empty +} + +struct io { + bool completed; + int error; +}; + +static void _io_init(struct io *io) +{ + io->completed = false; + io->error = 0; +} + +static void _complete_io(void *context, int io_error) +{ + struct io *io = context; + io->completed = true; + io->error = io_error; +} + +static void _test_read(void *fixture) +{ + struct fixture *f = fixture; + struct io io; + struct bcache *cache = bcache_create(PAGE_SIZE_SECTORS, BLOCK_SIZE_SECTORS, f->e); + T_ASSERT(cache); + + f->di = bcache_set_fd(f->fd); + + T_ASSERT(f->di >= 0); + + _io_init(&io); + T_ASSERT(f->e->issue(f->e, DIR_READ, f->di, 0, BLOCK_SIZE_SECTORS, f->data, &io)); + T_ASSERT(f->e->wait(f->e, _complete_io)); + T_ASSERT(io.completed); + T_ASSERT(!io.error); + + _check_buffer(f->data, 123, SECTOR_SIZE * BLOCK_SIZE_SECTORS); +} + +static void _test_write(void *fixture) +{ + struct fixture *f = fixture; + struct io io; + struct bcache *cache = bcache_create(PAGE_SIZE_SECTORS, BLOCK_SIZE_SECTORS, f->e); + T_ASSERT(cache); + + f->di = bcache_set_fd(f->fd); + + T_ASSERT(f->di >= 0); + + _io_init(&io); + T_ASSERT(f->e->issue(f->e, DIR_WRITE, f->di, 0, BLOCK_SIZE_SECTORS, f->data, &io)); + T_ASSERT(f->e->wait(f->e, _complete_io)); + T_ASSERT(io.completed); + T_ASSERT(!io.error); +} + +static void _test_write_bytes(void *fixture) +{ + struct fixture *f = fixture; + + unsigned offset = 345; + char buf_out[32]; + char buf_in[32]; + struct bcache *cache = bcache_create(PAGE_SIZE_SECTORS, BLOCK_SIZE_SECTORS, f->e); + T_ASSERT(cache); + + f->di = bcache_set_fd(f->fd); + + // T_ASSERT(bcache_read_bytes(cache, f->di, offset, sizeof(buf_in), buf_in)); + _fill_buffer((uint8_t *) buf_out, 234, sizeof(buf_out)); + T_ASSERT(bcache_write_bytes(cache, f->di, offset, sizeof(buf_out), buf_out)); + T_ASSERT(bcache_read_bytes(cache, f->di, offset, sizeof(buf_in), buf_in)); + + _print_buffer("buf_out", (uint8_t *) buf_out, sizeof(buf_out)); + _print_buffer("buf_in", (uint8_t *) buf_in, sizeof(buf_in)); + T_ASSERT(!memcmp(buf_out, buf_in, sizeof(buf_out))); + + bcache_destroy(cache); + f->e = NULL; // already destroyed +} + +//---------------------------------------------------------------- + +#define T(path, desc, fn) register_test(ts, "/base/device/bcache/io-engine/" path, desc, fn) + +static struct test_suite *_tests(void) +{ + struct test_suite *ts = test_suite_create(_fix_init, _fix_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("create-destroy", "simple create/destroy", _test_create); + T("read", "read sanity check", _test_read); + T("write", "write sanity check", _test_write); + T("bcache-write-bytes", "test the utility fns", _test_write_bytes); + + return ts; +} + +void io_engine_tests(struct dm_list *all_tests) +{ + dm_list_add(all_tests, &_tests()->list); +} + diff --git a/test/unit/matcher_t.c b/test/unit/matcher_t.c index 7331a82..89b2988 100644 --- a/test/unit/matcher_t.c +++ b/test/unit/matcher_t.c @@ -10,76 +10,120 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libdevmapper.h" -#include "log.h" +#include "units.h" +#include "device_mapper/all.h" -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/mman.h> - -#include <CUnit/CUnit.h> #include "matcher_data.h" -int regex_init(void); -int regex_fini(void); - -static struct dm_pool *mem = NULL; +static void *_mem_init(void) +{ + struct dm_pool *mem = dm_pool_create("bitset test", 1024); + if (!mem) { + fprintf(stderr, "out of memory"); + exit(1); + } -int regex_init(void) { - mem = dm_pool_create("bitset test", 1024); - return mem == NULL; + return mem; } -int regex_fini(void) { +static void _mem_exit(void *mem) +{ dm_pool_destroy(mem); - return 0; } -static struct dm_regex *make_scanner(const char **rx) +static struct dm_regex *make_scanner(struct dm_pool *mem, const char **rx) { struct dm_regex *scanner; int nrx = 0; for (; rx[nrx]; ++nrx); scanner = dm_regex_create(mem, rx, nrx); - CU_ASSERT_FATAL(scanner != NULL); + T_ASSERT(scanner != NULL); return scanner; } -static void test_fingerprints(void) { +static void test_fingerprints(void *fixture) +{ + struct dm_pool *mem = fixture; struct dm_regex *scanner; - scanner = make_scanner(dev_patterns); - CU_ASSERT_EQUAL(dm_regex_fingerprint(scanner), 0x7f556c09); + scanner = make_scanner(mem, dev_patterns); + T_ASSERT_EQUAL(dm_regex_fingerprint(scanner), 0x7f556c09); - scanner = make_scanner(random_patterns); - CU_ASSERT_EQUAL(dm_regex_fingerprint(scanner), 0x9f11076c); + scanner = make_scanner(mem, random_patterns); + T_ASSERT_EQUAL(dm_regex_fingerprint(scanner), 0x9f11076c); } -static void test_matching(void) { +static void test_matching(void *fixture) +{ + struct dm_pool *mem = fixture; struct dm_regex *scanner; int i; - scanner = make_scanner(dev_patterns); + scanner = make_scanner(mem, dev_patterns); for (i = 0; devices[i].str; ++i) - CU_ASSERT_EQUAL(dm_regex_match(scanner, devices[i].str), devices[i].expected - 1); + T_ASSERT_EQUAL(dm_regex_match(scanner, devices[i].str), devices[i].expected - 1); - scanner = make_scanner(nonprint_patterns); + scanner = make_scanner(mem, nonprint_patterns); for (i = 0; nonprint[i].str; ++i) - CU_ASSERT_EQUAL(dm_regex_match(scanner, nonprint[i].str), nonprint[i].expected - 1); + T_ASSERT_EQUAL(dm_regex_match(scanner, nonprint[i].str), nonprint[i].expected - 1); } -CU_TestInfo regex_list[] = { - { (char*)"fingerprints", test_fingerprints }, - { (char*)"matching", test_matching }, - CU_TEST_INFO_NULL -}; +static void test_kabi_query(void *fixture) +{ + // Remember, matches regexes from last to first. + static const char *_patterns[] = { + ".*", ".*/dev/md.*", "loop" + }; + + static struct { + const char *input; + int r; + } _cases[] = { + {"foo", 0}, + {"/dev/mapper/vg-lvol1", 0}, + {"/dev/mapper/vglvol1", 0}, + {"/dev/md1", 1}, + {"loop", 2}, + }; + + int r; + unsigned i; + struct dm_pool *mem = fixture; + struct dm_regex *scanner; + + scanner = dm_regex_create(mem, _patterns, DM_ARRAY_SIZE(_patterns)); + T_ASSERT(scanner != NULL); + + for (i = 0; i < DM_ARRAY_SIZE(_cases); i++) { + r = dm_regex_match(scanner, _cases[i].input); + if (r != _cases[i].r) { + test_fail("'%s' expected to match '%s', but matched %s", + _cases[i].input, + _cases[i].r >= DM_ARRAY_SIZE(_patterns) ? "<nothing>" : _patterns[_cases[i].r], + r >= DM_ARRAY_SIZE(_patterns) ? "<nothing>" : _patterns[r]); + } + } + +} + +#define T(path, desc, fn) register_test(ts, "/base/regex/" path, desc, fn) + +void regex_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(_mem_init, _mem_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("fingerprints", "not sure", test_fingerprints); + T("matching", "test the matcher with a variety of regexes", test_matching); + T("kabi-query", "test the matcher with some specific patterns", test_kabi_query); + + dm_list_add(all_tests, &ts->list); +} diff --git a/test/unit/percent_t.c b/test/unit/percent_t.c new file mode 100644 index 0000000..3575e12 --- /dev/null +++ b/test/unit/percent_t.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "device_mapper/all.h" + +#include <stdio.h> +#include <string.h> + +static void test_percent_100(void *fixture) +{ + char buf[32]; + + /* Check 100% is shown only for DM_PERCENT_100*/ + dm_percent_t p_100 = dm_make_percent(100, 100); + dm_percent_t p1_100 = dm_make_percent(100000, 100000); + dm_percent_t n_100 = dm_make_percent(999999, 1000000); + + T_ASSERT_EQUAL(p_100, DM_PERCENT_100); + T_ASSERT_EQUAL(p1_100, DM_PERCENT_100); + T_ASSERT_NOT_EQUAL(n_100, DM_PERCENT_100); + + (void) dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(p_100)); + T_ASSERT_EQUAL(strcmp(buf, "100.00"), 0); + + (void) dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(p1_100)); + T_ASSERT_EQUAL(strcmp(buf, "100.00"), 0); + + (void) dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(n_100)); + T_ASSERT_NOT_EQUAL(strcmp(buf, "99.99"), 0); /* Would like to gett */ + + (void) dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_round_float(n_100, 2)); + T_ASSERT_EQUAL(strcmp(buf, "99.99"), 0); + + (void) dm_snprintf(buf, sizeof(buf), "%.3f", dm_percent_to_round_float(n_100, 3)); + T_ASSERT_EQUAL(strcmp(buf, "99.999"), 0); + + (void) dm_snprintf(buf, sizeof(buf), "%.4f", dm_percent_to_round_float(n_100, 4)); + T_ASSERT_EQUAL(strcmp(buf, "99.9999"), 0); + + (void) dm_snprintf(buf, sizeof(buf), "%d", (int)dm_percent_to_round_float(n_100, 0)); + T_ASSERT_EQUAL(strcmp(buf, "99"), 0); +} + +static void test_percent_0(void *fixture) +{ + char buf[32]; + + /* Check 0% is shown only for DM_PERCENT_0 */ + dm_percent_t p_0 = dm_make_percent(0, 100); + dm_percent_t p1_0 = dm_make_percent(0, 100000); + dm_percent_t n_0 = dm_make_percent(1, 1000000); + + T_ASSERT_EQUAL(p_0, DM_PERCENT_0); + T_ASSERT_EQUAL(p1_0, DM_PERCENT_0); + T_ASSERT_NOT_EQUAL(n_0, DM_PERCENT_0); + + (void) dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(p_0)); + T_ASSERT_EQUAL(strcmp(buf, "0.00"), 0); + + (void) dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(p1_0)); + T_ASSERT_EQUAL(strcmp(buf, "0.00"), 0); + + (void) dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_float(n_0)); + T_ASSERT_NOT_EQUAL(strcmp(buf, "0.01"), 0); + + (void) dm_snprintf(buf, sizeof(buf), "%.2f", dm_percent_to_round_float(n_0, 2)); + T_ASSERT_EQUAL(strcmp(buf, "0.01"), 0); + + (void) dm_snprintf(buf, sizeof(buf), "%.3f", dm_percent_to_round_float(n_0, 3)); + T_ASSERT_EQUAL(strcmp(buf, "0.001"), 0); + + (void) dm_snprintf(buf, sizeof(buf), "%d", (int)dm_percent_to_round_float(n_0, 0)); + T_ASSERT_EQUAL(strcmp(buf, "1"), 0); +} + +#define T(path, desc, fn) register_test(ts, "/base/formatting/percent/" path, desc, fn) + +void percent_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(NULL, NULL); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("100", "Pretty printing of percentages near 100%", test_percent_100); + T("0", "Pretty printing of percentages near 0%", test_percent_0); + + dm_list_add(all_tests, &ts->list); +} diff --git a/test/unit/radix_tree_t.c b/test/unit/radix_tree_t.c new file mode 100644 index 0000000..b80e4f8 --- /dev/null +++ b/test/unit/radix_tree_t.c @@ -0,0 +1,852 @@ +// Copyright (C) 2018 Red Hat, Inc. All rights reserved. +// +// This file is part of LVM2. +// +// This copyrighted material is made available to anyone wishing to use, +// modify, copy, or redistribute it subject to the terms and conditions +// of the GNU Lesser General Public License v.2.1. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#include "units.h" +#include "base/data-struct/radix-tree.h" +#include "base/memory/container_of.h" + +#include <stdio.h> +#include <stdlib.h> + +//---------------------------------------------------------------- + +static void *rt_init(void) +{ + struct radix_tree *rt = radix_tree_create(NULL, NULL); + T_ASSERT(rt); + return rt; +} + +static void rt_exit(void *fixture) +{ + if (fixture) + radix_tree_destroy(fixture); +} + +static void test_create_destroy(void *fixture) +{ + T_ASSERT(fixture); +} + +static void test_insert_one(void *fixture) +{ + struct radix_tree *rt = fixture; + union radix_value v; + unsigned char k = 'a'; + v.n = 65; + T_ASSERT(radix_tree_insert(rt, &k, &k + 1, v)); + T_ASSERT(radix_tree_is_well_formed(rt)); + v.n = 0; + T_ASSERT(radix_tree_lookup(rt, &k, &k + 1, &v)); + T_ASSERT_EQUAL(v.n, 65); +} + +static void test_single_byte_keys(void *fixture) +{ + unsigned i, count = 256; + struct radix_tree *rt = fixture; + union radix_value v; + uint8_t k; + + for (i = 0; i < count; i++) { + k = i; + v.n = 100 + i; + T_ASSERT(radix_tree_insert(rt, &k, &k + 1, v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + + for (i = 0; i < count; i++) { + k = i; + T_ASSERT(radix_tree_lookup(rt, &k, &k + 1, &v)); + T_ASSERT_EQUAL(v.n, 100 + i); + } +} + +static void test_overwrite_single_byte_keys(void *fixture) +{ + unsigned i, count = 256; + struct radix_tree *rt = fixture; + union radix_value v; + uint8_t k; + + for (i = 0; i < count; i++) { + k = i; + v.n = 100 + i; + T_ASSERT(radix_tree_insert(rt, &k, &k + 1, v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + + for (i = 0; i < count; i++) { + k = i; + v.n = 1000 + i; + T_ASSERT(radix_tree_insert(rt, &k, &k + 1, v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + + for (i = 0; i < count; i++) { + k = i; + T_ASSERT(radix_tree_lookup(rt, &k, &k + 1, &v)); + T_ASSERT_EQUAL(v.n, 1000 + i); + } +} + +static void test_16_bit_keys(void *fixture) +{ + unsigned i, count = 1 << 16; + struct radix_tree *rt = fixture; + union radix_value v; + uint8_t k[2]; + + for (i = 0; i < count; i++) { + k[0] = i / 256; + k[1] = i % 256; + v.n = 100 + i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + + for (i = 0; i < count; i++) { + k[0] = i / 256; + k[1] = i % 256; + T_ASSERT(radix_tree_lookup(rt, k, k + sizeof(k), &v)); + T_ASSERT_EQUAL(v.n, 100 + i); + } +} + +static void test_prefix_keys(void *fixture) +{ + struct radix_tree *rt = fixture; + union radix_value v; + uint8_t k[2]; + + k[0] = 100; + k[1] = 200; + v.n = 1024; + T_ASSERT(radix_tree_insert(rt, k, k + 1, v)); + T_ASSERT(radix_tree_is_well_formed(rt)); + v.n = 2345; + T_ASSERT(radix_tree_insert(rt, k, k + 2, v)); + T_ASSERT(radix_tree_is_well_formed(rt)); + T_ASSERT(radix_tree_lookup(rt, k, k + 1, &v)); + T_ASSERT_EQUAL(v.n, 1024); + T_ASSERT(radix_tree_lookup(rt, k, k + 2, &v)); + T_ASSERT_EQUAL(v.n, 2345); +} + +static void test_prefix_keys_reversed(void *fixture) +{ + struct radix_tree *rt = fixture; + union radix_value v; + uint8_t k[2]; + + k[0] = 100; + k[1] = 200; + v.n = 1024; + T_ASSERT(radix_tree_insert(rt, k, k + 2, v)); + T_ASSERT(radix_tree_is_well_formed(rt)); + v.n = 2345; + T_ASSERT(radix_tree_insert(rt, k, k + 1, v)); + T_ASSERT(radix_tree_is_well_formed(rt)); + T_ASSERT(radix_tree_lookup(rt, k, k + 2, &v)); + T_ASSERT_EQUAL(v.n, 1024); + T_ASSERT(radix_tree_lookup(rt, k, k + 1, &v)); + T_ASSERT_EQUAL(v.n, 2345); +} + +static void _gen_key(uint8_t *b, uint8_t *e) +{ + for (; b != e; b++) + /* coverity[dont_call] don't care */ + *b = rand() % 256; +} + +static void test_sparse_keys(void *fixture) +{ + unsigned n; + struct radix_tree *rt = fixture; + union radix_value v; + uint8_t k[32]; + + for (n = 0; n < 100000; n++) { + _gen_key(k, k + sizeof(k)); + v.n = 1234; + T_ASSERT(radix_tree_insert(rt, k, k + 32, v)); + // FIXME: remove + //T_ASSERT(radix_tree_is_well_formed(rt)); + } + T_ASSERT(radix_tree_is_well_formed(rt)); +} + +static void test_remove_one(void *fixture) +{ + struct radix_tree *rt = fixture; + uint8_t k[4]; + union radix_value v; + + _gen_key(k, k + sizeof(k)); + v.n = 1234; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + T_ASSERT(radix_tree_is_well_formed(rt)); + T_ASSERT(radix_tree_remove(rt, k, k + sizeof(k))); + T_ASSERT(radix_tree_is_well_formed(rt)); + T_ASSERT(!radix_tree_lookup(rt, k, k + sizeof(k), &v)); +} + +static void test_remove_one_byte_keys(void *fixture) +{ + struct radix_tree *rt = fixture; + unsigned i, j; + uint8_t k[1]; + union radix_value v; + + for (i = 0; i < 256; i++) { + k[0] = i; + v.n = i + 1000; + T_ASSERT(radix_tree_insert(rt, k, k + 1, v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + for (i = 0; i < 256; i++) { + k[0] = i; + T_ASSERT(radix_tree_remove(rt, k, k + 1)); + T_ASSERT(radix_tree_is_well_formed(rt)); + + for (j = i + 1; j < 256; j++) { + k[0] = j; + T_ASSERT(radix_tree_lookup(rt, k, k + 1, &v)); + if (v.n != j + 1000) + test_fail("v.n (%u) != j + 1000 (%u)\n", + (unsigned) v.n, + (unsigned) j + 1000); + } + } + + for (i = 0; i < 256; i++) { + k[0] = i; + T_ASSERT(!radix_tree_lookup(rt, k, k + 1, &v)); + } +} + +static void test_remove_one_byte_keys_reversed(void *fixture) +{ + struct radix_tree *rt = fixture; + unsigned i, j; + uint8_t k[1]; + union radix_value v; + + for (i = 0; i < 256; i++) { + k[0] = i; + v.n = i + 1000; + T_ASSERT(radix_tree_insert(rt, k, k + 1, v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + for (i = 256; i; i--) { + k[0] = i - 1; + T_ASSERT(radix_tree_remove(rt, k, k + 1)); + T_ASSERT(radix_tree_is_well_formed(rt)); + + for (j = 0; j < i - 1; j++) { + k[0] = j; + T_ASSERT(radix_tree_lookup(rt, k, k + 1, &v)); + if (v.n != j + 1000) + test_fail("v.n (%u) != j + 1000 (%u)\n", + (unsigned) v.n, + (unsigned) j + 1000); + } + } + + for (i = 0; i < 256; i++) { + k[0] = i; + T_ASSERT(!radix_tree_lookup(rt, k, k + 1, &v)); + } +} +static void test_remove_prefix_keys(void *fixture) +{ + struct radix_tree *rt = fixture; + unsigned i, j; + uint8_t k[32]; + union radix_value v; + + _gen_key(k, k + sizeof(k)); + + for (i = 0; i < 32; i++) { + v.n = i; + T_ASSERT(radix_tree_insert(rt, k, k + i, v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + for (i = 0; i < 32; i++) { + T_ASSERT(radix_tree_remove(rt, k, k + i)); + T_ASSERT(radix_tree_is_well_formed(rt)); + for (j = i + 1; j < 32; j++) { + T_ASSERT(radix_tree_lookup(rt, k, k + j, &v)); + T_ASSERT_EQUAL(v.n, j); + } + } + + for (i = 0; i < 32; i++) + T_ASSERT(!radix_tree_lookup(rt, k, k + i, &v)); +} + +static void test_remove_prefix_keys_reversed(void *fixture) +{ + struct radix_tree *rt = fixture; + unsigned i, j; + uint8_t k[32]; + union radix_value v; + + _gen_key(k, k + sizeof(k)); + + for (i = 0; i < 32; i++) { + v.n = i; + T_ASSERT(radix_tree_insert(rt, k, k + i, v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + for (i = 0; i < 32; i++) { + T_ASSERT(radix_tree_remove(rt, k, k + (31 - i))); + T_ASSERT(radix_tree_is_well_formed(rt)); + for (j = 0; j < 31 - i; j++) { + T_ASSERT(radix_tree_lookup(rt, k, k + j, &v)); + T_ASSERT_EQUAL(v.n, j); + } + } + + for (i = 0; i < 32; i++) + T_ASSERT(!radix_tree_lookup(rt, k, k + i, &v)); +} + +static void test_remove_prefix(void *fixture) +{ + struct radix_tree *rt = fixture; + unsigned i, count = 0; + uint8_t k[4]; + union radix_value v; + + // populate some random 32bit keys + for (i = 0; i < 100000; i++) { + _gen_key(k, k + sizeof(k)); + if (k[0] == 21) + count++; + v.n = i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + + // remove keys in a sub range + k[0] = 21; + T_ASSERT_EQUAL(radix_tree_remove_prefix(rt, k, k + 1), count); + T_ASSERT(radix_tree_is_well_formed(rt)); +} + +static void test_remove_prefix_single(void *fixture) +{ + struct radix_tree *rt = fixture; + uint8_t k[4]; + union radix_value v; + + _gen_key(k, k + sizeof(k)); + v.n = 1234; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + T_ASSERT(radix_tree_is_well_formed(rt)); + T_ASSERT_EQUAL(radix_tree_remove_prefix(rt, k, k + 2), 1); + T_ASSERT(radix_tree_is_well_formed(rt)); +} + +static void test_size(void *fixture) +{ + struct radix_tree *rt = fixture; + unsigned i, dup_count = 0; + uint8_t k[2]; + union radix_value v; + + // populate some random 16bit keys + for (i = 0; i < 10000; i++) { + _gen_key(k, k + sizeof(k)); + if (radix_tree_lookup(rt, k, k + sizeof(k), &v)) + dup_count++; + v.n = i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + } + + T_ASSERT_EQUAL(radix_tree_size(rt), 10000 - dup_count); + T_ASSERT(radix_tree_is_well_formed(rt)); +} + +struct visitor { + struct radix_tree_iterator it; + unsigned count; +}; + +static bool _visit(struct radix_tree_iterator *it, + uint8_t *kb, uint8_t *ke, union radix_value v) +{ + struct visitor *vt = container_of(it, struct visitor, it); + vt->count++; + return true; +} + +static void test_iterate_all(void *fixture) +{ + struct radix_tree *rt = fixture; + unsigned i; + uint8_t k[4]; + union radix_value v; + struct visitor vt; + + // populate some random 32bit keys + for (i = 0; i < 100000; i++) { + _gen_key(k, k + sizeof(k)); + v.n = i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + vt.count = 0; + vt.it.visit = _visit; + radix_tree_iterate(rt, NULL, NULL, &vt.it); + T_ASSERT_EQUAL(vt.count, radix_tree_size(rt)); +} + +static void test_iterate_subset(void *fixture) +{ + struct radix_tree *rt = fixture; + unsigned i, subset_count = 0; + uint8_t k[3]; + union radix_value v; + struct visitor vt; + + // populate some random 32bit keys + for (i = 0; i < 100000; i++) { + _gen_key(k, k + sizeof(k)); + if (k[0] == 21 && k[1] == 12) + subset_count++; + v.n = i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + vt.count = 0; + vt.it.visit = _visit; + k[0] = 21; + k[1] = 12; + radix_tree_iterate(rt, k, k + 2, &vt.it); + T_ASSERT_EQUAL(vt.count, subset_count); +} + +static void test_iterate_single(void *fixture) +{ + struct radix_tree *rt = fixture; + uint8_t k[6]; + union radix_value v; + struct visitor vt; + + _gen_key(k, k + sizeof(k)); + v.n = 1234; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + + T_ASSERT(radix_tree_is_well_formed(rt)); + vt.count = 0; + vt.it.visit = _visit; + radix_tree_iterate(rt, k, k + 3, &vt.it); + T_ASSERT_EQUAL(vt.count, 1); +} + +static void test_iterate_vary_middle(void *fixture) +{ + struct radix_tree *rt = fixture; + unsigned i; + uint8_t k[6]; + union radix_value v; + struct visitor vt; + + _gen_key(k, k + sizeof(k)); + for (i = 0; i < 16; i++) { + k[3] = i; + v.n = i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + vt.it.visit = _visit; + for (i = 0; i < 16; i++) { + vt.count = 0; + k[3] = i; + radix_tree_iterate(rt, k, k + 4, &vt.it); + T_ASSERT_EQUAL(vt.count, 1); + } +} + +//---------------------------------------------------------------- + +#define DTR_COUNT 100 + +struct counter { + unsigned c; + uint8_t present[DTR_COUNT]; +}; + +static void _counting_dtr(void *context, union radix_value v) +{ + struct counter *c = context; + c->c++; + T_ASSERT(v.n < DTR_COUNT); + c->present[v.n] = 0; +} + +static void test_remove_calls_dtr(void *fixture) +{ + struct counter c; + struct radix_tree *rt = radix_tree_create(_counting_dtr, &c); + T_ASSERT(rt); + + // Bug hunting, so I need the keys to be deterministic + srand(0); + + c.c = 0; + memset(c.present, 1, sizeof(c.present)); + + { + unsigned i; + uint8_t keys[DTR_COUNT * 3]; + union radix_value v; + + // generate and insert a lot of keys + for (i = 0; i < DTR_COUNT; i++) { + bool found = false; + do { + v.n = i; + uint8_t *k = keys + (i * 3); + _gen_key(k, k + 3); + if (!radix_tree_lookup(rt, k, k + 3, &v)) { + T_ASSERT(radix_tree_insert(rt, k, k + 3, v)); + found = true; + } + + } while (!found); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + + // double check + for (i = 0; i < DTR_COUNT; i++) { + uint8_t *k = keys + (i * 3); + T_ASSERT(radix_tree_lookup(rt, k, k + 3, &v)); + } + + for (i = 0; i < DTR_COUNT; i++) { + uint8_t *k = keys + (i * 3); + // FIXME: check the values get passed to the dtr + T_ASSERT(radix_tree_remove(rt, k, k + 3)); + } + + T_ASSERT(c.c == DTR_COUNT); + for (i = 0; i < DTR_COUNT; i++) + T_ASSERT(!c.present[i]); + } + + radix_tree_destroy(rt); +} + +static void test_destroy_calls_dtr(void *fixture) +{ + unsigned i; + struct counter c; + struct radix_tree *rt = radix_tree_create(_counting_dtr, &c); + T_ASSERT(rt); + + // Bug hunting, so I need the keys to be deterministic + srand(0); + + c.c = 0; + memset(c.present, 1, sizeof(c.present)); + + { + uint8_t keys[DTR_COUNT * 3]; + union radix_value v; + + // generate and insert a lot of keys + for (i = 0; i < DTR_COUNT; i++) { + bool found = false; + do { + v.n = i; + uint8_t *k = keys + (i * 3); + _gen_key(k, k + 3); + if (!radix_tree_lookup(rt, k, k + 3, &v)) { + T_ASSERT(radix_tree_insert(rt, k, k + 3, v)); + found = true; + } + + } while (!found); + } + + T_ASSERT(radix_tree_is_well_formed(rt)); + } + + radix_tree_destroy(rt); + T_ASSERT(c.c == DTR_COUNT); + for (i = 0; i < DTR_COUNT; i++) + T_ASSERT(!c.present[i]); +} + +//---------------------------------------------------------------- + +static void test_bcache_scenario(void *fixture) +{ + struct radix_tree *rt = fixture; + + unsigned i; + uint8_t k[6]; + union radix_value v; + + memset(k, 0, sizeof(k)); + + for (i = 0; i < 3; i++) { + // it has to be the 4th byte that varies to + // trigger the bug. + k[4] = i; + v.n = i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + } + T_ASSERT(radix_tree_is_well_formed(rt)); + + k[4] = 0; + T_ASSERT(radix_tree_remove(rt, k, k + sizeof(k))); + T_ASSERT(radix_tree_is_well_formed(rt)); + + k[4] = i; + v.n = i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + T_ASSERT(radix_tree_is_well_formed(rt)); +} + +//---------------------------------------------------------------- + +static void _bcs2_step1(struct radix_tree *rt) +{ + unsigned i; + uint8_t k[12]; + union radix_value v; + + memset(k, 0, sizeof(k)); + for (i = 0x6; i < 0x69; i++) { + k[0] = i; + v.n = i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + } + T_ASSERT(radix_tree_is_well_formed(rt)); +} + +static void _bcs2_step2(struct radix_tree *rt) +{ + unsigned i; + uint8_t k[12]; + + memset(k, 0, sizeof(k)); + for (i = 0x6; i < 0x69; i++) { + k[0] = i; + radix_tree_remove_prefix(rt, k, k + 4); + } + T_ASSERT(radix_tree_is_well_formed(rt)); +} + +static void test_bcache_scenario2(void *fixture) +{ + unsigned i; + struct radix_tree *rt = fixture; + uint8_t k[12]; + union radix_value v; + + _bcs2_step1(rt); + _bcs2_step2(rt); + + memset(k, 0, sizeof(k)); + for (i = 0; i < 50; i++) { + k[0] = 0x6; + v.n = 0x6; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + radix_tree_remove_prefix(rt, k, k + 4); + } + T_ASSERT(radix_tree_is_well_formed(rt)); + + _bcs2_step1(rt); + _bcs2_step2(rt); + _bcs2_step1(rt); + _bcs2_step2(rt); + + memset(k, 0, sizeof(k)); + for(i = 0x6; i < 0x37; i++) { + k[0] = i; + k[4] = 0xf; + k[5] = 0x1; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + k[4] = 0; + k[5] = 0; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + } + T_ASSERT(radix_tree_is_well_formed(rt)); + + memset(k, 0, sizeof(k)); + for (i = 0x38; i < 0x69; i++) { + k[0] = i - 0x32; + k[4] = 0xf; + k[5] = 1; + T_ASSERT(radix_tree_remove(rt, k, k + sizeof(k))); + + k[0] = i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + + k[0] = i - 0x32; + k[4] = 0; + k[5] = 0; + T_ASSERT(radix_tree_remove(rt, k, k + sizeof(k))); + + k[0] = i; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + } + T_ASSERT(radix_tree_is_well_formed(rt)); + + memset(k, 0, sizeof(k)); + k[0] = 0x6; + radix_tree_remove_prefix(rt, k, k + 4); + T_ASSERT(radix_tree_is_well_formed(rt)); + + k[0] = 0x38; + k[4] = 0xf; + k[5] = 0x1; + T_ASSERT(radix_tree_remove(rt, k, k + sizeof(k))); + T_ASSERT(radix_tree_is_well_formed(rt)); + + memset(k, 0, sizeof(k)); + k[0] = 0x6; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + T_ASSERT(radix_tree_is_well_formed(rt)); + + k[0] = 0x7; + radix_tree_remove_prefix(rt, k, k + 4); + T_ASSERT(radix_tree_is_well_formed(rt)); + + k[0] = 0x38; + T_ASSERT(radix_tree_remove(rt, k, k + sizeof(k))); + T_ASSERT(radix_tree_is_well_formed(rt)); + + k[0] = 7; + T_ASSERT(radix_tree_insert(rt, k, k + sizeof(k), v)); + T_ASSERT(radix_tree_is_well_formed(rt)); +} + +//---------------------------------------------------------------- + +struct key_parts { + uint32_t fd; + uint64_t b; +} __attribute__ ((packed)); + +union key { + struct key_parts parts; + uint8_t bytes[12]; +}; + +static void __lookup_matches(struct radix_tree *rt, int fd, uint64_t b, uint64_t expected) +{ + union key k; + union radix_value v; + + k.parts.fd = fd; + k.parts.b = b; + T_ASSERT(radix_tree_lookup(rt, k.bytes, k.bytes + sizeof(k.bytes), &v)); + T_ASSERT(v.n == expected); +} + +static void __lookup_fails(struct radix_tree *rt, int fd, uint64_t b) +{ + union key k; + union radix_value v; + + k.parts.fd = fd; + k.parts.b = b; + T_ASSERT(!radix_tree_lookup(rt, k.bytes, k.bytes + sizeof(k.bytes), &v)); +} + +static void __insert(struct radix_tree *rt, int fd, uint64_t b, uint64_t n) +{ + union key k; + union radix_value v; + + k.parts.fd = fd; + k.parts.b = b; + v.n = n; + T_ASSERT(radix_tree_insert(rt, k.bytes, k.bytes + sizeof(k.bytes), v)); +} + +static void __invalidate(struct radix_tree *rt, int fd) +{ + union key k; + + k.parts.fd = fd; + radix_tree_remove_prefix(rt, k.bytes, k.bytes + sizeof(k.parts.fd)); + T_ASSERT(radix_tree_is_well_formed(rt)); +} + +static void test_bcache_scenario3(void *fixture) +{ + struct radix_tree *rt = fixture; + + #include "test/unit/rt_case1.c" +} + +//---------------------------------------------------------------- +#define T(path, desc, fn) register_test(ts, "/base/data-struct/radix-tree/" path, desc, fn) + +void radix_tree_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(rt_init, rt_exit); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("create-destroy", "create and destroy an empty tree", test_create_destroy); + T("insert-one", "insert one trivial trivial key", test_insert_one); + T("insert-single-byte-keys", "inserts many single byte keys", test_single_byte_keys); + T("overwrite-single-byte-keys", "overwrite many single byte keys", test_overwrite_single_byte_keys); + T("insert-16-bit-keys", "insert many 16bit keys", test_16_bit_keys); + T("prefix-keys", "prefixes of other keys are valid keys", test_prefix_keys); + T("prefix-keys-reversed", "prefixes of other keys are valid keys", test_prefix_keys_reversed); + T("sparse-keys", "see what the memory usage is for sparsely distributed keys", test_sparse_keys); + T("remove-one", "remove one entry", test_remove_one); + T("remove-one-byte-keys", "remove many one byte keys", test_remove_one_byte_keys); + T("remove-one-byte-keys-reversed", "remove many one byte keys reversed", test_remove_one_byte_keys_reversed); + T("remove-prefix-keys", "remove a set of keys that have common prefixes", test_remove_prefix_keys); + T("remove-prefix-keys-reversed", "remove a set of keys that have common prefixes (reversed)", test_remove_prefix_keys_reversed); + T("remove-prefix", "remove a subrange", test_remove_prefix); + T("remove-prefix-single", "remove a subrange with a single entry", test_remove_prefix_single); + T("size-spots-duplicates", "duplicate entries aren't counted twice", test_size); + T("iterate-all", "iterate all entries in tree", test_iterate_all); + T("iterate-subset", "iterate a subset of entries in tree", test_iterate_subset); + T("iterate-single", "iterate a subset that contains a single entry", test_iterate_single); + T("iterate-vary-middle", "iterate keys that vary in the middle", test_iterate_vary_middle); + T("remove-calls-dtr", "remove should call the dtr for the value", test_remove_calls_dtr); + T("destroy-calls-dtr", "destroy should call the dtr for all values", test_destroy_calls_dtr); + T("bcache-scenario", "A specific series of keys from a bcache scenario", test_bcache_scenario); + T("bcache-scenario-2", "A second series of keys from a bcache scenario", test_bcache_scenario2); + T("bcache-scenario-3", "A third series of keys from a bcache scenario", test_bcache_scenario3); + + dm_list_add(all_tests, &ts->list); +} +//---------------------------------------------------------------- diff --git a/test/unit/rt_case1.c b/test/unit/rt_case1.c new file mode 100644 index 0000000..c1677d1 --- /dev/null +++ b/test/unit/rt_case1.c @@ -0,0 +1,1669 @@ + __lookup_fails(rt, 6, 0); + __insert(rt, 6, 0, 0); + __lookup_fails(rt, 7, 0); + __insert(rt, 7, 0, 1); + __lookup_fails(rt, 8, 0); + __insert(rt, 8, 0, 2); + __lookup_fails(rt, 9, 0); + __insert(rt, 9, 0, 3); + __lookup_fails(rt, 10, 0); + __insert(rt, 10, 0, 4); + __lookup_fails(rt, 11, 0); + __insert(rt, 11, 0, 5); + __lookup_fails(rt, 12, 0); + __insert(rt, 12, 0, 6); + __lookup_fails(rt, 13, 0); + __insert(rt, 13, 0, 7); + __lookup_fails(rt, 14, 0); + __insert(rt, 14, 0, 8); + __lookup_fails(rt, 15, 0); + __insert(rt, 15, 0, 9); + __lookup_fails(rt, 16, 0); + __insert(rt, 16, 0, 10); + __lookup_fails(rt, 17, 0); + __insert(rt, 17, 0, 11); + __lookup_fails(rt, 18, 0); + __insert(rt, 18, 0, 12); + __lookup_fails(rt, 19, 0); + __insert(rt, 19, 0, 13); + __lookup_fails(rt, 20, 0); + __insert(rt, 20, 0, 14); + __lookup_fails(rt, 21, 0); + __insert(rt, 21, 0, 15); + __lookup_fails(rt, 22, 0); + __insert(rt, 22, 0, 16); + __lookup_fails(rt, 23, 0); + __insert(rt, 23, 0, 17); + __lookup_fails(rt, 24, 0); + __insert(rt, 24, 0, 18); + __lookup_fails(rt, 25, 0); + __insert(rt, 25, 0, 19); + __lookup_fails(rt, 26, 0); + __insert(rt, 26, 0, 20); + __lookup_fails(rt, 27, 0); + __insert(rt, 27, 0, 21); + __lookup_fails(rt, 28, 0); + __insert(rt, 28, 0, 22); + __lookup_fails(rt, 29, 0); + __insert(rt, 29, 0, 23); + __lookup_fails(rt, 30, 0); + __insert(rt, 30, 0, 24); + __lookup_fails(rt, 31, 0); + __insert(rt, 31, 0, 25); + __lookup_fails(rt, 32, 0); + __insert(rt, 32, 0, 26); + __lookup_fails(rt, 33, 0); + __insert(rt, 33, 0, 27); + __lookup_fails(rt, 34, 0); + __insert(rt, 34, 0, 28); + __lookup_fails(rt, 35, 0); + __insert(rt, 35, 0, 29); + __lookup_fails(rt, 36, 0); + __insert(rt, 36, 0, 30); + __lookup_fails(rt, 37, 0); + __insert(rt, 37, 0, 31); + __lookup_fails(rt, 38, 0); + __insert(rt, 38, 0, 32); + __lookup_fails(rt, 39, 0); + __insert(rt, 39, 0, 33); + __lookup_fails(rt, 40, 0); + __insert(rt, 40, 0, 34); + __lookup_fails(rt, 41, 0); + __insert(rt, 41, 0, 35); + __lookup_fails(rt, 42, 0); + __insert(rt, 42, 0, 36); + __lookup_fails(rt, 43, 0); + __insert(rt, 43, 0, 37); + __lookup_fails(rt, 44, 0); + __insert(rt, 44, 0, 38); + __lookup_fails(rt, 45, 0); + __insert(rt, 45, 0, 39); + __lookup_fails(rt, 46, 0); + __insert(rt, 46, 0, 40); + __lookup_fails(rt, 47, 0); + __insert(rt, 47, 0, 41); + __lookup_fails(rt, 48, 0); + __insert(rt, 48, 0, 42); + __lookup_fails(rt, 49, 0); + __insert(rt, 49, 0, 43); + __lookup_fails(rt, 50, 0); + __insert(rt, 50, 0, 44); + __lookup_fails(rt, 51, 0); + __insert(rt, 51, 0, 45); + __lookup_fails(rt, 52, 0); + __insert(rt, 52, 0, 46); + __lookup_fails(rt, 53, 0); + __insert(rt, 53, 0, 47); + __lookup_fails(rt, 54, 0); + __insert(rt, 54, 0, 48); + __lookup_fails(rt, 55, 0); + __insert(rt, 55, 0, 49); + __lookup_fails(rt, 56, 0); + __insert(rt, 56, 0, 50); + __lookup_fails(rt, 57, 0); + __insert(rt, 57, 0, 51); + __lookup_fails(rt, 58, 0); + __insert(rt, 58, 0, 52); + __lookup_fails(rt, 59, 0); + __insert(rt, 59, 0, 53); + __lookup_fails(rt, 60, 0); + __insert(rt, 60, 0, 54); + __lookup_fails(rt, 61, 0); + __insert(rt, 61, 0, 55); + __lookup_fails(rt, 62, 0); + __insert(rt, 62, 0, 56); + __lookup_fails(rt, 63, 0); + __insert(rt, 63, 0, 57); + __lookup_fails(rt, 64, 0); + __insert(rt, 64, 0, 58); + __lookup_fails(rt, 65, 0); + __insert(rt, 65, 0, 59); + __lookup_fails(rt, 66, 0); + __insert(rt, 66, 0, 60); + __lookup_fails(rt, 67, 0); + __insert(rt, 67, 0, 61); + __lookup_fails(rt, 68, 0); + __insert(rt, 68, 0, 62); + __lookup_fails(rt, 69, 0); + __insert(rt, 69, 0, 63); + __lookup_fails(rt, 70, 0); + __insert(rt, 70, 0, 64); + __lookup_fails(rt, 71, 0); + __insert(rt, 71, 0, 65); + __lookup_fails(rt, 72, 0); + __insert(rt, 72, 0, 66); + __lookup_fails(rt, 73, 0); + __insert(rt, 73, 0, 67); + __lookup_fails(rt, 74, 0); + __insert(rt, 74, 0, 68); + __lookup_fails(rt, 75, 0); + __insert(rt, 75, 0, 69); + __lookup_fails(rt, 76, 0); + __insert(rt, 76, 0, 70); + __lookup_fails(rt, 77, 0); + __insert(rt, 77, 0, 71); + __lookup_fails(rt, 78, 0); + __insert(rt, 78, 0, 72); + __lookup_fails(rt, 79, 0); + __insert(rt, 79, 0, 73); + __lookup_fails(rt, 80, 0); + __insert(rt, 80, 0, 74); + __lookup_fails(rt, 81, 0); + __insert(rt, 81, 0, 75); + __lookup_fails(rt, 82, 0); + __insert(rt, 82, 0, 76); + __lookup_fails(rt, 83, 0); + __insert(rt, 83, 0, 77); + __lookup_fails(rt, 84, 0); + __insert(rt, 84, 0, 78); + __lookup_fails(rt, 85, 0); + __insert(rt, 85, 0, 79); + __lookup_fails(rt, 86, 0); + __insert(rt, 86, 0, 80); + __lookup_fails(rt, 87, 0); + __insert(rt, 87, 0, 81); + __lookup_fails(rt, 88, 0); + __insert(rt, 88, 0, 82); + __lookup_fails(rt, 89, 0); + __insert(rt, 89, 0, 83); + __lookup_fails(rt, 90, 0); + __insert(rt, 90, 0, 84); + __lookup_fails(rt, 91, 0); + __insert(rt, 91, 0, 85); + __lookup_fails(rt, 92, 0); + __insert(rt, 92, 0, 86); + __lookup_fails(rt, 93, 0); + __insert(rt, 93, 0, 87); + __lookup_fails(rt, 94, 0); + __insert(rt, 94, 0, 88); + __lookup_fails(rt, 95, 0); + __insert(rt, 95, 0, 89); + __lookup_fails(rt, 96, 0); + __insert(rt, 96, 0, 90); + __lookup_fails(rt, 97, 0); + __insert(rt, 97, 0, 91); + __lookup_fails(rt, 98, 0); + __insert(rt, 98, 0, 92); + __lookup_fails(rt, 99, 0); + __insert(rt, 99, 0, 93); + __lookup_fails(rt, 100, 0); + __insert(rt, 100, 0, 94); + __lookup_fails(rt, 101, 0); + __insert(rt, 101, 0, 95); + __lookup_fails(rt, 102, 0); + __insert(rt, 102, 0, 96); + __lookup_fails(rt, 103, 0); + __insert(rt, 103, 0, 97); + __lookup_fails(rt, 104, 0); + __insert(rt, 104, 0, 98); + __lookup_fails(rt, 105, 0); + __insert(rt, 105, 0, 99); + __lookup_fails(rt, 106, 0); + __insert(rt, 106, 0, 100); + __lookup_fails(rt, 107, 0); + __insert(rt, 107, 0, 101); + __lookup_fails(rt, 108, 0); + __insert(rt, 108, 0, 102); + __lookup_fails(rt, 109, 0); + __insert(rt, 109, 0, 103); + __lookup_fails(rt, 110, 0); + __insert(rt, 110, 0, 104); + __lookup_fails(rt, 111, 0); + __insert(rt, 111, 0, 105); + __lookup_fails(rt, 112, 0); + __insert(rt, 112, 0, 106); + __lookup_fails(rt, 113, 0); + __insert(rt, 113, 0, 107); + __lookup_fails(rt, 114, 0); + __insert(rt, 114, 0, 108); + __lookup_fails(rt, 115, 0); + __insert(rt, 115, 0, 109); + __lookup_fails(rt, 116, 0); + __insert(rt, 116, 0, 110); + __lookup_fails(rt, 117, 0); + __insert(rt, 117, 0, 111); + __lookup_fails(rt, 118, 0); + __insert(rt, 118, 0, 112); + __lookup_fails(rt, 119, 0); + __insert(rt, 119, 0, 113); + __lookup_fails(rt, 120, 0); + __insert(rt, 120, 0, 114); + __lookup_fails(rt, 121, 0); + __insert(rt, 121, 0, 115); + __lookup_fails(rt, 122, 0); + __insert(rt, 122, 0, 116); + __lookup_fails(rt, 123, 0); + __insert(rt, 123, 0, 117); + __lookup_fails(rt, 124, 0); + __insert(rt, 124, 0, 118); + __lookup_fails(rt, 125, 0); + __insert(rt, 125, 0, 119); + __lookup_fails(rt, 126, 0); + __insert(rt, 126, 0, 120); + __lookup_fails(rt, 127, 0); + __insert(rt, 127, 0, 121); + __lookup_fails(rt, 128, 0); + __insert(rt, 128, 0, 122); + __lookup_fails(rt, 129, 0); + __insert(rt, 129, 0, 123); + __lookup_fails(rt, 130, 0); + __insert(rt, 130, 0, 124); + __lookup_fails(rt, 131, 0); + __insert(rt, 131, 0, 125); + __lookup_fails(rt, 132, 0); + __insert(rt, 132, 0, 126); + __lookup_fails(rt, 133, 0); + __insert(rt, 133, 0, 127); + __lookup_fails(rt, 134, 0); + __insert(rt, 134, 0, 128); + __lookup_fails(rt, 135, 0); + __insert(rt, 135, 0, 129); + __lookup_fails(rt, 136, 0); + __insert(rt, 136, 0, 130); + __lookup_fails(rt, 137, 0); + __insert(rt, 137, 0, 131); + __lookup_fails(rt, 138, 0); + __insert(rt, 138, 0, 132); + __lookup_fails(rt, 139, 0); + __insert(rt, 139, 0, 133); + __lookup_fails(rt, 140, 0); + __insert(rt, 140, 0, 134); + __lookup_fails(rt, 141, 0); + __insert(rt, 141, 0, 135); + __lookup_fails(rt, 142, 0); + __insert(rt, 142, 0, 136); + __lookup_fails(rt, 143, 0); + __insert(rt, 143, 0, 137); + __lookup_fails(rt, 144, 0); + __insert(rt, 144, 0, 138); + __lookup_fails(rt, 145, 0); + __insert(rt, 145, 0, 139); + __lookup_fails(rt, 146, 0); + __insert(rt, 146, 0, 140); + __lookup_fails(rt, 147, 0); + __insert(rt, 147, 0, 141); + __lookup_fails(rt, 148, 0); + __insert(rt, 148, 0, 142); + __lookup_fails(rt, 149, 0); + __insert(rt, 149, 0, 143); + __lookup_fails(rt, 150, 0); + __insert(rt, 150, 0, 144); + __lookup_fails(rt, 151, 0); + __insert(rt, 151, 0, 145); + __lookup_fails(rt, 152, 0); + __insert(rt, 152, 0, 146); + __lookup_fails(rt, 153, 0); + __insert(rt, 153, 0, 147); + __lookup_fails(rt, 154, 0); + __insert(rt, 154, 0, 148); + __lookup_fails(rt, 155, 0); + __insert(rt, 155, 0, 149); + __lookup_fails(rt, 156, 0); + __insert(rt, 156, 0, 150); + __lookup_fails(rt, 157, 0); + __insert(rt, 157, 0, 151); + __lookup_fails(rt, 158, 0); + __insert(rt, 158, 0, 152); + __lookup_fails(rt, 159, 0); + __insert(rt, 159, 0, 153); + __lookup_fails(rt, 160, 0); + __insert(rt, 160, 0, 154); + __lookup_fails(rt, 161, 0); + __insert(rt, 161, 0, 155); + __lookup_fails(rt, 162, 0); + __insert(rt, 162, 0, 156); + __lookup_fails(rt, 163, 0); + __insert(rt, 163, 0, 157); + __lookup_fails(rt, 164, 0); + __insert(rt, 164, 0, 158); + __lookup_fails(rt, 165, 0); + __insert(rt, 165, 0, 159); + __lookup_fails(rt, 166, 0); + __insert(rt, 166, 0, 160); + __lookup_fails(rt, 167, 0); + __insert(rt, 167, 0, 161); + __lookup_fails(rt, 168, 0); + __insert(rt, 168, 0, 162); + __lookup_fails(rt, 169, 0); + __insert(rt, 169, 0, 163); + __lookup_fails(rt, 170, 0); + __insert(rt, 170, 0, 164); + __lookup_fails(rt, 171, 0); + __insert(rt, 171, 0, 165); + __lookup_fails(rt, 172, 0); + __insert(rt, 172, 0, 166); + __lookup_fails(rt, 173, 0); + __insert(rt, 173, 0, 167); + __lookup_fails(rt, 174, 0); + __insert(rt, 174, 0, 168); + __lookup_fails(rt, 175, 0); + __insert(rt, 175, 0, 169); + __lookup_fails(rt, 176, 0); + __insert(rt, 176, 0, 170); + __lookup_fails(rt, 177, 0); + __insert(rt, 177, 0, 171); + __lookup_fails(rt, 178, 0); + __insert(rt, 178, 0, 172); + __lookup_fails(rt, 179, 0); + __insert(rt, 179, 0, 173); + __lookup_fails(rt, 180, 0); + __insert(rt, 180, 0, 174); + __lookup_fails(rt, 181, 0); + __insert(rt, 181, 0, 175); + __lookup_fails(rt, 182, 0); + __insert(rt, 182, 0, 176); + __lookup_fails(rt, 183, 0); + __insert(rt, 183, 0, 177); + __lookup_fails(rt, 184, 0); + __insert(rt, 184, 0, 178); + __lookup_fails(rt, 185, 0); + __insert(rt, 185, 0, 179); + __lookup_fails(rt, 186, 0); + __insert(rt, 186, 0, 180); + __lookup_fails(rt, 187, 0); + __insert(rt, 187, 0, 181); + __lookup_fails(rt, 188, 0); + __insert(rt, 188, 0, 182); + __lookup_fails(rt, 189, 0); + __insert(rt, 189, 0, 183); + __lookup_fails(rt, 190, 0); + __insert(rt, 190, 0, 184); + __lookup_fails(rt, 191, 0); + __insert(rt, 191, 0, 185); + __lookup_fails(rt, 192, 0); + __insert(rt, 192, 0, 186); + __lookup_fails(rt, 193, 0); + __insert(rt, 193, 0, 187); + __lookup_fails(rt, 194, 0); + __insert(rt, 194, 0, 188); + __lookup_fails(rt, 195, 0); + __insert(rt, 195, 0, 189); + __lookup_fails(rt, 196, 0); + __insert(rt, 196, 0, 190); + __lookup_fails(rt, 197, 0); + __insert(rt, 197, 0, 191); + __lookup_fails(rt, 198, 0); + __insert(rt, 198, 0, 192); + __lookup_fails(rt, 199, 0); + __insert(rt, 199, 0, 193); + __lookup_fails(rt, 200, 0); + __insert(rt, 200, 0, 194); + __lookup_fails(rt, 201, 0); + __insert(rt, 201, 0, 195); + __lookup_fails(rt, 202, 0); + __insert(rt, 202, 0, 196); + __lookup_fails(rt, 203, 0); + __insert(rt, 203, 0, 197); + __lookup_fails(rt, 204, 0); + __insert(rt, 204, 0, 198); + __lookup_fails(rt, 205, 0); + __insert(rt, 205, 0, 199); + __lookup_matches(rt, 6, 0, 0); + __invalidate(rt, 6); + __lookup_matches(rt, 7, 0, 1); + __invalidate(rt, 7); + __lookup_matches(rt, 8, 0, 2); + __invalidate(rt, 8); + __lookup_matches(rt, 9, 0, 3); + __invalidate(rt, 9); + __lookup_matches(rt, 10, 0, 4); + __invalidate(rt, 10); + __lookup_matches(rt, 11, 0, 5); + __invalidate(rt, 11); + __lookup_matches(rt, 12, 0, 6); + __lookup_matches(rt, 13, 0, 7); + __invalidate(rt, 13); + __lookup_matches(rt, 14, 0, 8); + __invalidate(rt, 14); + __lookup_matches(rt, 15, 0, 9); + __invalidate(rt, 15); + __lookup_matches(rt, 16, 0, 10); + __invalidate(rt, 16); + __lookup_matches(rt, 17, 0, 11); + __invalidate(rt, 17); + __lookup_matches(rt, 18, 0, 12); + __invalidate(rt, 18); + __lookup_matches(rt, 19, 0, 13); + __invalidate(rt, 19); + __lookup_matches(rt, 20, 0, 14); + __invalidate(rt, 20); + __lookup_matches(rt, 21, 0, 15); + __invalidate(rt, 21); + __lookup_matches(rt, 22, 0, 16); + __invalidate(rt, 22); + __lookup_matches(rt, 23, 0, 17); + __invalidate(rt, 23); + __lookup_matches(rt, 24, 0, 18); + __invalidate(rt, 24); + __lookup_matches(rt, 25, 0, 19); + __invalidate(rt, 25); + __lookup_matches(rt, 26, 0, 20); + __invalidate(rt, 26); + __lookup_matches(rt, 27, 0, 21); + __invalidate(rt, 27); + __lookup_matches(rt, 28, 0, 22); + __invalidate(rt, 28); + __lookup_matches(rt, 29, 0, 23); + __invalidate(rt, 29); + __lookup_matches(rt, 30, 0, 24); + __invalidate(rt, 30); + __lookup_matches(rt, 31, 0, 25); + __invalidate(rt, 31); + __lookup_matches(rt, 32, 0, 26); + __invalidate(rt, 32); + __lookup_matches(rt, 33, 0, 27); + __invalidate(rt, 33); + __lookup_matches(rt, 34, 0, 28); + __invalidate(rt, 34); + __lookup_matches(rt, 35, 0, 29); + __invalidate(rt, 35); + __lookup_matches(rt, 36, 0, 30); + __invalidate(rt, 36); + __lookup_matches(rt, 37, 0, 31); + __invalidate(rt, 37); + __lookup_matches(rt, 38, 0, 32); + __invalidate(rt, 38); + __lookup_matches(rt, 39, 0, 33); + __invalidate(rt, 39); + __lookup_matches(rt, 40, 0, 34); + __invalidate(rt, 40); + __lookup_matches(rt, 41, 0, 35); + __invalidate(rt, 41); + __lookup_matches(rt, 42, 0, 36); + __invalidate(rt, 42); + __lookup_matches(rt, 43, 0, 37); + __invalidate(rt, 43); + __lookup_matches(rt, 44, 0, 38); + __invalidate(rt, 44); + __lookup_matches(rt, 45, 0, 39); + __invalidate(rt, 45); + __lookup_matches(rt, 46, 0, 40); + __lookup_fails(rt, 46, 5); + __insert(rt, 46, 5, 200); + __lookup_matches(rt, 46, 5, 200); + __lookup_fails(rt, 46, 6); + __insert(rt, 46, 6, 201); + __lookup_fails(rt, 46, 7); + __insert(rt, 46, 7, 202); + __lookup_fails(rt, 46, 8); + __insert(rt, 46, 8, 203); + __lookup_matches(rt, 46, 5, 200); + __lookup_matches(rt, 46, 6, 201); + __lookup_matches(rt, 46, 7, 202); + __lookup_matches(rt, 46, 8, 203); + __lookup_matches(rt, 47, 0, 41); + __invalidate(rt, 47); + __lookup_matches(rt, 48, 0, 42); + __invalidate(rt, 48); + __lookup_matches(rt, 49, 0, 43); + __invalidate(rt, 49); + __lookup_matches(rt, 50, 0, 44); + __invalidate(rt, 50); + __lookup_matches(rt, 51, 0, 45); + __invalidate(rt, 51); + __lookup_matches(rt, 52, 0, 46); + __invalidate(rt, 52); + __lookup_matches(rt, 53, 0, 47); + __invalidate(rt, 53); + __lookup_matches(rt, 54, 0, 48); + __invalidate(rt, 54); + __lookup_matches(rt, 55, 0, 49); + __invalidate(rt, 55); + __lookup_matches(rt, 56, 0, 50); + __invalidate(rt, 56); + __lookup_matches(rt, 57, 0, 51); + __invalidate(rt, 57); + __lookup_matches(rt, 58, 0, 52); + __invalidate(rt, 58); + __lookup_matches(rt, 59, 0, 53); + __invalidate(rt, 59); + __lookup_matches(rt, 60, 0, 54); + __invalidate(rt, 60); + __lookup_matches(rt, 61, 0, 55); + __invalidate(rt, 61); + __lookup_matches(rt, 62, 0, 56); + __invalidate(rt, 62); + __lookup_matches(rt, 63, 0, 57); + __invalidate(rt, 63); + __lookup_matches(rt, 64, 0, 58); + __invalidate(rt, 64); + __lookup_matches(rt, 65, 0, 59); + __lookup_fails(rt, 65, 1); + __insert(rt, 65, 1, 204); + __lookup_fails(rt, 65, 2); + __insert(rt, 65, 2, 205); + __lookup_fails(rt, 65, 3); + __insert(rt, 65, 3, 206); + __lookup_fails(rt, 65, 4); + __insert(rt, 65, 4, 207); + __lookup_matches(rt, 65, 0, 59); + __lookup_matches(rt, 65, 1, 204); + __lookup_matches(rt, 65, 2, 205); + __lookup_matches(rt, 65, 3, 206); + __lookup_matches(rt, 65, 4, 207); + __lookup_matches(rt, 66, 0, 60); + __invalidate(rt, 66); + __lookup_matches(rt, 67, 0, 61); + __invalidate(rt, 67); + __lookup_matches(rt, 68, 0, 62); + __invalidate(rt, 68); + __lookup_matches(rt, 69, 0, 63); + __invalidate(rt, 69); + __lookup_matches(rt, 70, 0, 64); + __invalidate(rt, 70); + __lookup_matches(rt, 71, 0, 65); + __invalidate(rt, 71); + __lookup_matches(rt, 72, 0, 66); + __invalidate(rt, 72); + __lookup_matches(rt, 73, 0, 67); + __invalidate(rt, 73); + __lookup_matches(rt, 74, 0, 68); + __invalidate(rt, 74); + __lookup_matches(rt, 75, 0, 69); + __invalidate(rt, 75); + __lookup_matches(rt, 76, 0, 70); + __invalidate(rt, 76); + __lookup_matches(rt, 77, 0, 71); + __invalidate(rt, 77); + __lookup_matches(rt, 78, 0, 72); + __invalidate(rt, 78); + __lookup_matches(rt, 79, 0, 73); + __invalidate(rt, 79); + __lookup_matches(rt, 80, 0, 74); + __invalidate(rt, 80); + __lookup_matches(rt, 81, 0, 75); + __invalidate(rt, 81); + __lookup_matches(rt, 82, 0, 76); + __invalidate(rt, 82); + __lookup_matches(rt, 83, 0, 77); + __invalidate(rt, 83); + __lookup_matches(rt, 84, 0, 78); + __invalidate(rt, 84); + __lookup_matches(rt, 85, 0, 79); + __invalidate(rt, 85); + __lookup_matches(rt, 86, 0, 80); + __invalidate(rt, 86); + __lookup_matches(rt, 87, 0, 81); + __invalidate(rt, 87); + __lookup_matches(rt, 88, 0, 82); + __invalidate(rt, 88); + __lookup_matches(rt, 89, 0, 83); + __invalidate(rt, 89); + __lookup_matches(rt, 90, 0, 84); + __invalidate(rt, 90); + __lookup_matches(rt, 91, 0, 85); + __invalidate(rt, 91); + __lookup_matches(rt, 92, 0, 86); + __invalidate(rt, 92); + __lookup_matches(rt, 93, 0, 87); + __invalidate(rt, 93); + __lookup_matches(rt, 94, 0, 88); + __invalidate(rt, 94); + __lookup_matches(rt, 95, 0, 89); + __invalidate(rt, 95); + __lookup_matches(rt, 96, 0, 90); + __lookup_matches(rt, 97, 0, 91); + __invalidate(rt, 97); + __lookup_matches(rt, 98, 0, 92); + __invalidate(rt, 98); + __lookup_matches(rt, 99, 0, 93); + __invalidate(rt, 99); + __lookup_matches(rt, 100, 0, 94); + __invalidate(rt, 100); + __lookup_matches(rt, 101, 0, 95); + __invalidate(rt, 101); + __lookup_matches(rt, 102, 0, 96); + __invalidate(rt, 102); + __lookup_matches(rt, 103, 0, 97); + __invalidate(rt, 103); + __lookup_matches(rt, 104, 0, 98); + __invalidate(rt, 104); + __lookup_matches(rt, 105, 0, 99); + __invalidate(rt, 105); + __lookup_matches(rt, 106, 0, 100); + __invalidate(rt, 106); + __lookup_matches(rt, 107, 0, 101); + __invalidate(rt, 107); + __lookup_matches(rt, 108, 0, 102); + __invalidate(rt, 108); + __lookup_matches(rt, 109, 0, 103); + __invalidate(rt, 109); + __lookup_matches(rt, 110, 0, 104); + __invalidate(rt, 110); + __lookup_matches(rt, 111, 0, 105); + __invalidate(rt, 111); + __lookup_matches(rt, 112, 0, 106); + __invalidate(rt, 112); + __lookup_matches(rt, 113, 0, 107); + __invalidate(rt, 113); + __lookup_matches(rt, 114, 0, 108); + __invalidate(rt, 114); + __lookup_matches(rt, 115, 0, 109); + __invalidate(rt, 115); + __lookup_matches(rt, 116, 0, 110); + __invalidate(rt, 116); + __lookup_matches(rt, 117, 0, 111); + __invalidate(rt, 117); + __lookup_matches(rt, 118, 0, 112); + __invalidate(rt, 118); + __lookup_matches(rt, 119, 0, 113); + __invalidate(rt, 119); + __lookup_matches(rt, 120, 0, 114); + __invalidate(rt, 120); + __lookup_matches(rt, 121, 0, 115); + __invalidate(rt, 121); + __lookup_matches(rt, 122, 0, 116); + __invalidate(rt, 122); + __lookup_matches(rt, 123, 0, 117); + __invalidate(rt, 123); + __lookup_matches(rt, 124, 0, 118); + __invalidate(rt, 124); + __lookup_matches(rt, 125, 0, 119); + __invalidate(rt, 125); + __lookup_matches(rt, 126, 0, 120); + __invalidate(rt, 126); + __lookup_matches(rt, 127, 0, 121); + __invalidate(rt, 127); + __lookup_matches(rt, 128, 0, 122); + __invalidate(rt, 128); + __lookup_matches(rt, 129, 0, 123); + __invalidate(rt, 129); + __lookup_matches(rt, 130, 0, 124); + __invalidate(rt, 130); + __lookup_matches(rt, 131, 0, 125); + __invalidate(rt, 131); + __lookup_matches(rt, 132, 0, 126); + __invalidate(rt, 132); + __lookup_matches(rt, 133, 0, 127); + __invalidate(rt, 133); + __lookup_matches(rt, 134, 0, 128); + __invalidate(rt, 134); + __lookup_matches(rt, 135, 0, 129); + __invalidate(rt, 135); + __lookup_matches(rt, 136, 0, 130); + __invalidate(rt, 136); + __lookup_matches(rt, 137, 0, 131); + __invalidate(rt, 137); + __lookup_matches(rt, 138, 0, 132); + __invalidate(rt, 138); + __lookup_matches(rt, 139, 0, 133); + __invalidate(rt, 139); + __lookup_matches(rt, 140, 0, 134); + __invalidate(rt, 140); + __lookup_matches(rt, 141, 0, 135); + __invalidate(rt, 141); + __lookup_matches(rt, 142, 0, 136); + __invalidate(rt, 142); + __lookup_matches(rt, 143, 0, 137); + __invalidate(rt, 143); + __lookup_matches(rt, 144, 0, 138); + __invalidate(rt, 144); + __lookup_matches(rt, 145, 0, 139); + __invalidate(rt, 145); + __lookup_matches(rt, 146, 0, 140); + __invalidate(rt, 146); + __lookup_matches(rt, 147, 0, 141); + __invalidate(rt, 147); + __lookup_matches(rt, 148, 0, 142); + __invalidate(rt, 148); + __lookup_matches(rt, 149, 0, 143); + __invalidate(rt, 149); + __lookup_matches(rt, 150, 0, 144); + __invalidate(rt, 150); + __lookup_matches(rt, 151, 0, 145); + __invalidate(rt, 151); + __lookup_matches(rt, 152, 0, 146); + __invalidate(rt, 152); + __lookup_matches(rt, 153, 0, 147); + __invalidate(rt, 153); + __lookup_matches(rt, 154, 0, 148); + __invalidate(rt, 154); + __lookup_matches(rt, 155, 0, 149); + __invalidate(rt, 155); + __lookup_matches(rt, 156, 0, 150); + __invalidate(rt, 156); + __lookup_matches(rt, 157, 0, 151); + __invalidate(rt, 157); + __lookup_matches(rt, 158, 0, 152); + __invalidate(rt, 158); + __lookup_matches(rt, 159, 0, 153); + __invalidate(rt, 159); + __lookup_matches(rt, 160, 0, 154); + __invalidate(rt, 160); + __lookup_matches(rt, 161, 0, 155); + __invalidate(rt, 161); + __lookup_matches(rt, 162, 0, 156); + __invalidate(rt, 162); + __lookup_matches(rt, 163, 0, 157); + __lookup_matches(rt, 164, 0, 158); + __invalidate(rt, 164); + __lookup_matches(rt, 165, 0, 159); + __invalidate(rt, 165); + __lookup_matches(rt, 166, 0, 160); + __invalidate(rt, 166); + __lookup_matches(rt, 167, 0, 161); + __invalidate(rt, 167); + __lookup_matches(rt, 168, 0, 162); + __invalidate(rt, 168); + __lookup_matches(rt, 169, 0, 163); + __invalidate(rt, 169); + __lookup_matches(rt, 170, 0, 164); + __invalidate(rt, 170); + __lookup_matches(rt, 171, 0, 165); + __invalidate(rt, 171); + __lookup_matches(rt, 172, 0, 166); + __invalidate(rt, 172); + __lookup_matches(rt, 173, 0, 167); + __invalidate(rt, 173); + __lookup_matches(rt, 174, 0, 168); + __invalidate(rt, 174); + __lookup_matches(rt, 175, 0, 169); + __invalidate(rt, 175); + __lookup_matches(rt, 176, 0, 170); + __invalidate(rt, 176); + __lookup_matches(rt, 177, 0, 171); + __invalidate(rt, 177); + __lookup_matches(rt, 178, 0, 172); + __invalidate(rt, 178); + __lookup_matches(rt, 179, 0, 173); + __invalidate(rt, 179); + __lookup_matches(rt, 180, 0, 174); + __invalidate(rt, 180); + __lookup_matches(rt, 181, 0, 175); + __invalidate(rt, 181); + __lookup_matches(rt, 182, 0, 176); + __invalidate(rt, 182); + __lookup_matches(rt, 183, 0, 177); + __invalidate(rt, 183); + __lookup_matches(rt, 184, 0, 178); + __invalidate(rt, 184); + __lookup_matches(rt, 185, 0, 179); + __invalidate(rt, 185); + __lookup_matches(rt, 186, 0, 180); + __invalidate(rt, 186); + __lookup_matches(rt, 187, 0, 181); + __invalidate(rt, 187); + __lookup_matches(rt, 188, 0, 182); + __invalidate(rt, 188); + __lookup_matches(rt, 189, 0, 183); + __invalidate(rt, 189); + __lookup_matches(rt, 190, 0, 184); + __invalidate(rt, 190); + __lookup_matches(rt, 191, 0, 185); + __invalidate(rt, 191); + __lookup_matches(rt, 192, 0, 186); + __invalidate(rt, 192); + __lookup_matches(rt, 193, 0, 187); + __invalidate(rt, 193); + __lookup_matches(rt, 194, 0, 188); + __invalidate(rt, 194); + __lookup_matches(rt, 195, 0, 189); + __invalidate(rt, 195); + __lookup_matches(rt, 196, 0, 190); + __invalidate(rt, 196); + __lookup_matches(rt, 197, 0, 191); + __invalidate(rt, 197); + __lookup_matches(rt, 198, 0, 192); + __invalidate(rt, 198); + __lookup_matches(rt, 199, 0, 193); + __invalidate(rt, 199); + __lookup_matches(rt, 200, 0, 194); + __invalidate(rt, 200); + __lookup_matches(rt, 201, 0, 195); + __invalidate(rt, 201); + __lookup_matches(rt, 202, 0, 196); + __invalidate(rt, 202); + __lookup_matches(rt, 203, 0, 197); + __invalidate(rt, 203); + __lookup_matches(rt, 204, 0, 198); + __invalidate(rt, 204); + __lookup_matches(rt, 205, 0, 199); + __invalidate(rt, 205); + __lookup_fails(rt, 6, 0); + __insert(rt, 6, 0, 208); + __lookup_fails(rt, 7, 0); + __insert(rt, 7, 0, 209); + __lookup_fails(rt, 8, 0); + __insert(rt, 8, 0, 210); + __lookup_fails(rt, 9, 0); + __insert(rt, 9, 0, 211); + __lookup_fails(rt, 10, 0); + __insert(rt, 10, 0, 212); + __lookup_fails(rt, 11, 0); + __insert(rt, 11, 0, 213); + __lookup_fails(rt, 13, 0); + __insert(rt, 13, 0, 214); + __lookup_fails(rt, 14, 0); + __insert(rt, 14, 0, 215); + __lookup_fails(rt, 15, 0); + __insert(rt, 15, 0, 216); + __lookup_fails(rt, 16, 0); + __insert(rt, 16, 0, 217); + __lookup_fails(rt, 17, 0); + __insert(rt, 17, 0, 218); + __lookup_fails(rt, 18, 0); + __insert(rt, 18, 0, 219); + __lookup_fails(rt, 19, 0); + __insert(rt, 19, 0, 220); + __lookup_fails(rt, 20, 0); + __insert(rt, 20, 0, 221); + __lookup_fails(rt, 21, 0); + __insert(rt, 21, 0, 222); + __lookup_fails(rt, 22, 0); + __insert(rt, 22, 0, 223); + __lookup_fails(rt, 23, 0); + __insert(rt, 23, 0, 224); + __lookup_fails(rt, 24, 0); + __insert(rt, 24, 0, 225); + __lookup_fails(rt, 25, 0); + __insert(rt, 25, 0, 226); + __lookup_fails(rt, 26, 0); + __insert(rt, 26, 0, 227); + __lookup_fails(rt, 27, 0); + __insert(rt, 27, 0, 228); + __lookup_fails(rt, 28, 0); + __insert(rt, 28, 0, 229); + __lookup_fails(rt, 29, 0); + __insert(rt, 29, 0, 230); + __lookup_fails(rt, 30, 0); + __insert(rt, 30, 0, 231); + __lookup_fails(rt, 31, 0); + __insert(rt, 31, 0, 232); + __lookup_fails(rt, 32, 0); + __insert(rt, 32, 0, 233); + __lookup_fails(rt, 33, 0); + __insert(rt, 33, 0, 234); + __lookup_fails(rt, 34, 0); + __insert(rt, 34, 0, 235); + __lookup_fails(rt, 35, 0); + __insert(rt, 35, 0, 236); + __lookup_fails(rt, 36, 0); + __insert(rt, 36, 0, 237); + __lookup_fails(rt, 37, 0); + __insert(rt, 37, 0, 238); + __lookup_fails(rt, 38, 0); + __insert(rt, 38, 0, 239); + __lookup_fails(rt, 39, 0); + __insert(rt, 39, 0, 240); + __lookup_fails(rt, 40, 0); + __insert(rt, 40, 0, 241); + __lookup_fails(rt, 41, 0); + __insert(rt, 41, 0, 242); + __lookup_fails(rt, 42, 0); + __insert(rt, 42, 0, 243); + __lookup_fails(rt, 43, 0); + __insert(rt, 43, 0, 244); + __lookup_fails(rt, 44, 0); + __insert(rt, 44, 0, 245); + __lookup_fails(rt, 45, 0); + __insert(rt, 45, 0, 246); + __lookup_fails(rt, 47, 0); + __insert(rt, 47, 0, 247); + __lookup_fails(rt, 48, 0); + __insert(rt, 48, 0, 248); + __lookup_fails(rt, 49, 0); + __insert(rt, 49, 0, 249); + __lookup_fails(rt, 50, 0); + __insert(rt, 50, 0, 250); + __lookup_fails(rt, 51, 0); + __insert(rt, 51, 0, 251); + __lookup_fails(rt, 52, 0); + __insert(rt, 52, 0, 252); + __lookup_fails(rt, 53, 0); + __insert(rt, 53, 0, 253); + __lookup_fails(rt, 54, 0); + __insert(rt, 54, 0, 254); + __lookup_fails(rt, 55, 0); + __insert(rt, 55, 0, 255); + __lookup_fails(rt, 56, 0); + __insert(rt, 56, 0, 256); + __lookup_fails(rt, 57, 0); + __insert(rt, 57, 0, 257); + __lookup_fails(rt, 58, 0); + __insert(rt, 58, 0, 258); + __lookup_fails(rt, 59, 0); + __insert(rt, 59, 0, 259); + __lookup_fails(rt, 60, 0); + __insert(rt, 60, 0, 260); + __lookup_fails(rt, 61, 0); + __insert(rt, 61, 0, 261); + __lookup_fails(rt, 62, 0); + __insert(rt, 62, 0, 262); + __lookup_fails(rt, 63, 0); + __insert(rt, 63, 0, 263); + __lookup_fails(rt, 64, 0); + __insert(rt, 64, 0, 264); + __lookup_fails(rt, 66, 0); + __insert(rt, 66, 0, 265); + __lookup_fails(rt, 67, 0); + __insert(rt, 67, 0, 266); + __lookup_fails(rt, 68, 0); + __insert(rt, 68, 0, 267); + __lookup_fails(rt, 69, 0); + __insert(rt, 69, 0, 268); + __lookup_fails(rt, 70, 0); + __insert(rt, 70, 0, 269); + __lookup_fails(rt, 71, 0); + __insert(rt, 71, 0, 270); + __lookup_fails(rt, 72, 0); + __insert(rt, 72, 0, 271); + __lookup_fails(rt, 73, 0); + __insert(rt, 73, 0, 272); + __lookup_fails(rt, 74, 0); + __insert(rt, 74, 0, 273); + __lookup_fails(rt, 75, 0); + __insert(rt, 75, 0, 274); + __lookup_fails(rt, 76, 0); + __insert(rt, 76, 0, 275); + __lookup_fails(rt, 77, 0); + __insert(rt, 77, 0, 276); + __lookup_fails(rt, 78, 0); + __insert(rt, 78, 0, 277); + __lookup_fails(rt, 79, 0); + __insert(rt, 79, 0, 278); + __lookup_fails(rt, 80, 0); + __insert(rt, 80, 0, 279); + __lookup_fails(rt, 81, 0); + __insert(rt, 81, 0, 280); + __lookup_fails(rt, 82, 0); + __insert(rt, 82, 0, 281); + __lookup_fails(rt, 83, 0); + __insert(rt, 83, 0, 282); + __lookup_fails(rt, 84, 0); + __insert(rt, 84, 0, 283); + __lookup_fails(rt, 85, 0); + __insert(rt, 85, 0, 284); + __lookup_fails(rt, 86, 0); + __insert(rt, 86, 0, 285); + __lookup_fails(rt, 87, 0); + __insert(rt, 87, 0, 286); + __lookup_fails(rt, 88, 0); + __insert(rt, 88, 0, 287); + __lookup_fails(rt, 89, 0); + __insert(rt, 89, 0, 288); + __lookup_fails(rt, 90, 0); + __insert(rt, 90, 0, 289); + __lookup_fails(rt, 91, 0); + __insert(rt, 91, 0, 290); + __lookup_fails(rt, 92, 0); + __insert(rt, 92, 0, 291); + __lookup_fails(rt, 93, 0); + __insert(rt, 93, 0, 292); + __lookup_fails(rt, 94, 0); + __insert(rt, 94, 0, 293); + __lookup_fails(rt, 95, 0); + __insert(rt, 95, 0, 294); + __lookup_fails(rt, 97, 0); + __insert(rt, 97, 0, 295); + __lookup_fails(rt, 98, 0); + __insert(rt, 98, 0, 296); + __lookup_fails(rt, 99, 0); + __insert(rt, 99, 0, 297); + __lookup_fails(rt, 100, 0); + __insert(rt, 100, 0, 298); + __lookup_fails(rt, 101, 0); + __insert(rt, 101, 0, 299); + __lookup_fails(rt, 102, 0); + __insert(rt, 102, 0, 300); + __lookup_fails(rt, 103, 0); + __insert(rt, 103, 0, 301); + __lookup_fails(rt, 104, 0); + __insert(rt, 104, 0, 302); + __lookup_fails(rt, 105, 0); + __insert(rt, 105, 0, 303); + __lookup_fails(rt, 106, 0); + __insert(rt, 106, 0, 304); + __lookup_fails(rt, 107, 0); + __insert(rt, 107, 0, 305); + __lookup_fails(rt, 108, 0); + __insert(rt, 108, 0, 306); + __lookup_fails(rt, 109, 0); + __insert(rt, 109, 0, 307); + __lookup_fails(rt, 110, 0); + __insert(rt, 110, 0, 308); + __lookup_fails(rt, 111, 0); + __insert(rt, 111, 0, 309); + __lookup_fails(rt, 112, 0); + __insert(rt, 112, 0, 310); + __lookup_fails(rt, 113, 0); + __insert(rt, 113, 0, 311); + __lookup_fails(rt, 114, 0); + __insert(rt, 114, 0, 312); + __lookup_fails(rt, 115, 0); + __insert(rt, 115, 0, 313); + __lookup_fails(rt, 116, 0); + __insert(rt, 116, 0, 314); + __lookup_fails(rt, 117, 0); + __insert(rt, 117, 0, 315); + __lookup_fails(rt, 118, 0); + __insert(rt, 118, 0, 316); + __lookup_fails(rt, 119, 0); + __insert(rt, 119, 0, 317); + __lookup_fails(rt, 120, 0); + __insert(rt, 120, 0, 318); + __lookup_fails(rt, 121, 0); + __insert(rt, 121, 0, 319); + __lookup_fails(rt, 122, 0); + __insert(rt, 122, 0, 320); + __lookup_fails(rt, 123, 0); + __insert(rt, 123, 0, 321); + __lookup_fails(rt, 124, 0); + __insert(rt, 124, 0, 322); + __lookup_fails(rt, 125, 0); + __insert(rt, 125, 0, 323); + __lookup_fails(rt, 126, 0); + __insert(rt, 126, 0, 324); + __lookup_fails(rt, 127, 0); + __insert(rt, 127, 0, 325); + __lookup_fails(rt, 128, 0); + __insert(rt, 128, 0, 326); + __lookup_fails(rt, 129, 0); + __insert(rt, 129, 0, 327); + __lookup_fails(rt, 130, 0); + __insert(rt, 130, 0, 328); + __lookup_fails(rt, 131, 0); + __insert(rt, 131, 0, 329); + __lookup_fails(rt, 132, 0); + __insert(rt, 132, 0, 330); + __lookup_fails(rt, 133, 0); + __insert(rt, 133, 0, 331); + __lookup_fails(rt, 134, 0); + __insert(rt, 134, 0, 332); + __lookup_fails(rt, 135, 0); + __insert(rt, 135, 0, 333); + __lookup_fails(rt, 136, 0); + __insert(rt, 136, 0, 334); + __lookup_fails(rt, 137, 0); + __insert(rt, 137, 0, 335); + __lookup_fails(rt, 138, 0); + __insert(rt, 138, 0, 336); + __lookup_fails(rt, 139, 0); + __insert(rt, 139, 0, 337); + __lookup_fails(rt, 140, 0); + __insert(rt, 140, 0, 338); + __lookup_fails(rt, 141, 0); + __insert(rt, 141, 0, 339); + __lookup_fails(rt, 142, 0); + __insert(rt, 142, 0, 340); + __lookup_fails(rt, 143, 0); + __insert(rt, 143, 0, 341); + __lookup_fails(rt, 144, 0); + __insert(rt, 144, 0, 342); + __lookup_fails(rt, 145, 0); + __insert(rt, 145, 0, 343); + __lookup_fails(rt, 146, 0); + __insert(rt, 146, 0, 344); + __lookup_fails(rt, 147, 0); + __insert(rt, 147, 0, 345); + __lookup_fails(rt, 148, 0); + __insert(rt, 148, 0, 346); + __lookup_fails(rt, 149, 0); + __insert(rt, 149, 0, 347); + __lookup_fails(rt, 150, 0); + __insert(rt, 150, 0, 348); + __lookup_fails(rt, 151, 0); + __insert(rt, 151, 0, 349); + __lookup_fails(rt, 152, 0); + __insert(rt, 152, 0, 350); + __lookup_fails(rt, 153, 0); + __insert(rt, 153, 0, 351); + __lookup_fails(rt, 154, 0); + __insert(rt, 154, 0, 352); + __lookup_fails(rt, 155, 0); + __insert(rt, 155, 0, 353); + __lookup_fails(rt, 156, 0); + __insert(rt, 156, 0, 354); + __lookup_fails(rt, 157, 0); + __insert(rt, 157, 0, 355); + __lookup_fails(rt, 158, 0); + __insert(rt, 158, 0, 356); + __lookup_fails(rt, 159, 0); + __insert(rt, 159, 0, 357); + __lookup_fails(rt, 160, 0); + __insert(rt, 160, 0, 358); + __lookup_fails(rt, 161, 0); + __insert(rt, 161, 0, 359); + __lookup_fails(rt, 162, 0); + __insert(rt, 162, 0, 360); + __lookup_fails(rt, 164, 0); + __insert(rt, 164, 0, 361); + __lookup_fails(rt, 165, 0); + __insert(rt, 165, 0, 362); + __lookup_fails(rt, 166, 0); + __insert(rt, 166, 0, 363); + __lookup_fails(rt, 167, 0); + __insert(rt, 167, 0, 364); + __lookup_fails(rt, 168, 0); + __insert(rt, 168, 0, 365); + __lookup_fails(rt, 169, 0); + __insert(rt, 169, 0, 366); + __lookup_fails(rt, 170, 0); + __insert(rt, 170, 0, 367); + __lookup_fails(rt, 171, 0); + __insert(rt, 171, 0, 368); + __lookup_fails(rt, 172, 0); + __insert(rt, 172, 0, 369); + __lookup_fails(rt, 173, 0); + __insert(rt, 173, 0, 370); + __lookup_fails(rt, 174, 0); + __insert(rt, 174, 0, 371); + __lookup_fails(rt, 175, 0); + __insert(rt, 175, 0, 372); + __lookup_fails(rt, 176, 0); + __insert(rt, 176, 0, 373); + __lookup_fails(rt, 177, 0); + __insert(rt, 177, 0, 374); + __lookup_fails(rt, 178, 0); + __insert(rt, 178, 0, 375); + __lookup_fails(rt, 179, 0); + __insert(rt, 179, 0, 376); + __lookup_fails(rt, 180, 0); + __insert(rt, 180, 0, 377); + __lookup_fails(rt, 181, 0); + __insert(rt, 181, 0, 378); + __lookup_fails(rt, 182, 0); + __insert(rt, 182, 0, 379); + __lookup_fails(rt, 183, 0); + __insert(rt, 183, 0, 380); + __lookup_fails(rt, 184, 0); + __insert(rt, 184, 0, 381); + __lookup_fails(rt, 185, 0); + __insert(rt, 185, 0, 382); + __lookup_fails(rt, 186, 0); + __insert(rt, 186, 0, 383); + __lookup_fails(rt, 187, 0); + __insert(rt, 187, 0, 384); + __lookup_fails(rt, 188, 0); + __insert(rt, 188, 0, 385); + __lookup_fails(rt, 189, 0); + __insert(rt, 189, 0, 386); + __lookup_fails(rt, 190, 0); + __insert(rt, 190, 0, 387); + __lookup_fails(rt, 191, 0); + __insert(rt, 191, 0, 388); + __lookup_fails(rt, 192, 0); + __insert(rt, 192, 0, 389); + __lookup_fails(rt, 193, 0); + __insert(rt, 193, 0, 390); + __lookup_fails(rt, 194, 0); + __insert(rt, 194, 0, 391); + __lookup_fails(rt, 195, 0); + __insert(rt, 195, 0, 392); + __lookup_fails(rt, 196, 0); + __insert(rt, 196, 0, 393); + __lookup_fails(rt, 197, 0); + __insert(rt, 197, 0, 394); + __lookup_fails(rt, 198, 0); + __insert(rt, 198, 0, 395); + __lookup_fails(rt, 199, 0); + __insert(rt, 199, 0, 396); + __lookup_fails(rt, 200, 0); + __insert(rt, 200, 0, 397); + __lookup_fails(rt, 201, 0); + __insert(rt, 201, 0, 398); + __lookup_fails(rt, 202, 0); + __insert(rt, 202, 0, 399); + __lookup_fails(rt, 203, 0); + __insert(rt, 203, 0, 400); + __lookup_fails(rt, 204, 0); + __insert(rt, 204, 0, 401); + __lookup_fails(rt, 205, 0); + __insert(rt, 205, 0, 402); + __lookup_fails(rt, 206, 0); + __insert(rt, 206, 0, 403); + __lookup_fails(rt, 207, 0); + __insert(rt, 207, 0, 404); + __lookup_fails(rt, 208, 0); + __insert(rt, 208, 0, 405); + __lookup_fails(rt, 209, 0); + __insert(rt, 209, 0, 406); + __lookup_fails(rt, 210, 0); + __insert(rt, 210, 0, 407); + __lookup_matches(rt, 6, 0, 208); + __invalidate(rt, 6); + __lookup_matches(rt, 7, 0, 209); + __invalidate(rt, 7); + __lookup_matches(rt, 8, 0, 210); + __invalidate(rt, 8); + __lookup_matches(rt, 9, 0, 211); + __invalidate(rt, 9); + __lookup_matches(rt, 10, 0, 212); + __invalidate(rt, 10); + __lookup_matches(rt, 11, 0, 213); + __invalidate(rt, 11); + __lookup_matches(rt, 13, 0, 214); + __invalidate(rt, 13); + __lookup_matches(rt, 14, 0, 215); + __invalidate(rt, 14); + __lookup_matches(rt, 15, 0, 216); + __invalidate(rt, 15); + __lookup_matches(rt, 16, 0, 217); + __invalidate(rt, 16); + __lookup_matches(rt, 17, 0, 218); + __invalidate(rt, 17); + __lookup_matches(rt, 18, 0, 219); + __invalidate(rt, 18); + __lookup_matches(rt, 19, 0, 220); + __invalidate(rt, 19); + __lookup_matches(rt, 20, 0, 221); + __invalidate(rt, 20); + __lookup_matches(rt, 21, 0, 222); + __invalidate(rt, 21); + __lookup_matches(rt, 22, 0, 223); + __invalidate(rt, 22); + __lookup_matches(rt, 23, 0, 224); + __invalidate(rt, 23); + __lookup_matches(rt, 24, 0, 225); + __invalidate(rt, 24); + __lookup_matches(rt, 25, 0, 226); + __invalidate(rt, 25); + __lookup_matches(rt, 26, 0, 227); + __invalidate(rt, 26); + __lookup_matches(rt, 27, 0, 228); + __invalidate(rt, 27); + __lookup_matches(rt, 28, 0, 229); + __invalidate(rt, 28); + __lookup_matches(rt, 29, 0, 230); + __invalidate(rt, 29); + __lookup_matches(rt, 30, 0, 231); + __invalidate(rt, 30); + __lookup_matches(rt, 31, 0, 232); + __invalidate(rt, 31); + __lookup_matches(rt, 32, 0, 233); + __invalidate(rt, 32); + __lookup_matches(rt, 33, 0, 234); + __invalidate(rt, 33); + __lookup_matches(rt, 34, 0, 235); + __invalidate(rt, 34); + __lookup_matches(rt, 35, 0, 236); + __invalidate(rt, 35); + __lookup_matches(rt, 36, 0, 237); + __invalidate(rt, 36); + __lookup_matches(rt, 37, 0, 238); + __invalidate(rt, 37); + __lookup_matches(rt, 38, 0, 239); + __invalidate(rt, 38); + __lookup_matches(rt, 39, 0, 240); + __invalidate(rt, 39); + __lookup_matches(rt, 40, 0, 241); + __invalidate(rt, 40); + __lookup_matches(rt, 41, 0, 242); + __invalidate(rt, 41); + __lookup_matches(rt, 42, 0, 243); + __invalidate(rt, 42); + __lookup_matches(rt, 43, 0, 244); + __invalidate(rt, 43); + __lookup_matches(rt, 44, 0, 245); + __invalidate(rt, 44); + __lookup_matches(rt, 45, 0, 246); + __invalidate(rt, 45); + __lookup_matches(rt, 47, 0, 247); + __invalidate(rt, 47); + __lookup_matches(rt, 48, 0, 248); + __invalidate(rt, 48); + __lookup_matches(rt, 49, 0, 249); + __invalidate(rt, 49); + __lookup_matches(rt, 50, 0, 250); + __invalidate(rt, 50); + __lookup_matches(rt, 51, 0, 251); + __invalidate(rt, 51); + __lookup_matches(rt, 52, 0, 252); + __invalidate(rt, 52); + __lookup_matches(rt, 53, 0, 253); + __invalidate(rt, 53); + __lookup_matches(rt, 54, 0, 254); + __invalidate(rt, 54); + __lookup_matches(rt, 55, 0, 255); + __invalidate(rt, 55); + __lookup_matches(rt, 56, 0, 256); + __invalidate(rt, 56); + __lookup_matches(rt, 57, 0, 257); + __invalidate(rt, 57); + __lookup_matches(rt, 58, 0, 258); + __invalidate(rt, 58); + __lookup_matches(rt, 59, 0, 259); + __invalidate(rt, 59); + __lookup_matches(rt, 60, 0, 260); + __invalidate(rt, 60); + __lookup_matches(rt, 61, 0, 261); + __invalidate(rt, 61); + __lookup_matches(rt, 62, 0, 262); + __invalidate(rt, 62); + __lookup_matches(rt, 63, 0, 263); + __invalidate(rt, 63); + __lookup_matches(rt, 64, 0, 264); + __invalidate(rt, 64); + __lookup_matches(rt, 66, 0, 265); + __invalidate(rt, 66); + __lookup_matches(rt, 67, 0, 266); + __invalidate(rt, 67); + __lookup_matches(rt, 68, 0, 267); + __invalidate(rt, 68); + __lookup_matches(rt, 69, 0, 268); + __invalidate(rt, 69); + __lookup_matches(rt, 70, 0, 269); + __invalidate(rt, 70); + __lookup_matches(rt, 71, 0, 270); + __invalidate(rt, 71); + __lookup_matches(rt, 72, 0, 271); + __invalidate(rt, 72); + __lookup_matches(rt, 73, 0, 272); + __lookup_matches(rt, 74, 0, 273); + __invalidate(rt, 74); + __lookup_matches(rt, 75, 0, 274); + __invalidate(rt, 75); + __lookup_matches(rt, 76, 0, 275); + __invalidate(rt, 76); + __lookup_matches(rt, 77, 0, 276); + __invalidate(rt, 77); + __lookup_matches(rt, 78, 0, 277); + __invalidate(rt, 78); + __lookup_matches(rt, 79, 0, 278); + __invalidate(rt, 79); + __lookup_matches(rt, 80, 0, 279); + __invalidate(rt, 80); + __lookup_matches(rt, 81, 0, 280); + __invalidate(rt, 81); + __lookup_matches(rt, 82, 0, 281); + __invalidate(rt, 82); + __lookup_matches(rt, 83, 0, 282); + __invalidate(rt, 83); + __lookup_matches(rt, 84, 0, 283); + __invalidate(rt, 84); + __lookup_matches(rt, 85, 0, 284); + __invalidate(rt, 85); + __lookup_matches(rt, 86, 0, 285); + __invalidate(rt, 86); + __lookup_matches(rt, 87, 0, 286); + __invalidate(rt, 87); + __lookup_matches(rt, 88, 0, 287); + __invalidate(rt, 88); + __lookup_matches(rt, 89, 0, 288); + __invalidate(rt, 89); + __lookup_matches(rt, 90, 0, 289); + __invalidate(rt, 90); + __lookup_matches(rt, 91, 0, 290); + __invalidate(rt, 91); + __lookup_matches(rt, 92, 0, 291); + __invalidate(rt, 92); + __lookup_matches(rt, 93, 0, 292); + __invalidate(rt, 93); + __lookup_matches(rt, 94, 0, 293); + __invalidate(rt, 94); + __lookup_matches(rt, 95, 0, 294); + __invalidate(rt, 95); + __lookup_matches(rt, 97, 0, 295); + __invalidate(rt, 97); + __lookup_matches(rt, 98, 0, 296); + __invalidate(rt, 98); + __lookup_matches(rt, 99, 0, 297); + __invalidate(rt, 99); + __lookup_matches(rt, 100, 0, 298); + __invalidate(rt, 100); + __lookup_matches(rt, 101, 0, 299); + __invalidate(rt, 101); + __lookup_matches(rt, 102, 0, 300); + __invalidate(rt, 102); + __lookup_matches(rt, 103, 0, 301); + __invalidate(rt, 103); + __lookup_matches(rt, 104, 0, 302); + __invalidate(rt, 104); + __lookup_matches(rt, 105, 0, 303); + __invalidate(rt, 105); + __lookup_matches(rt, 106, 0, 304); + __invalidate(rt, 106); + __lookup_matches(rt, 107, 0, 305); + __invalidate(rt, 107); + __lookup_matches(rt, 108, 0, 306); + __invalidate(rt, 108); + __lookup_matches(rt, 109, 0, 307); + __invalidate(rt, 109); + __lookup_matches(rt, 110, 0, 308); + __invalidate(rt, 110); + __lookup_matches(rt, 111, 0, 309); + __invalidate(rt, 111); + __lookup_matches(rt, 112, 0, 310); + __invalidate(rt, 112); + __lookup_matches(rt, 113, 0, 311); + __invalidate(rt, 113); + __lookup_matches(rt, 114, 0, 312); + __invalidate(rt, 114); + __lookup_matches(rt, 115, 0, 313); + __invalidate(rt, 115); + __lookup_matches(rt, 116, 0, 314); + __invalidate(rt, 116); + __lookup_matches(rt, 117, 0, 315); + __invalidate(rt, 117); + __lookup_matches(rt, 118, 0, 316); + __invalidate(rt, 118); + __lookup_matches(rt, 119, 0, 317); + __invalidate(rt, 119); + __lookup_matches(rt, 120, 0, 318); + __invalidate(rt, 120); + __lookup_matches(rt, 121, 0, 319); + __invalidate(rt, 121); + __lookup_matches(rt, 122, 0, 320); + __invalidate(rt, 122); + __lookup_matches(rt, 123, 0, 321); + __invalidate(rt, 123); + __lookup_matches(rt, 124, 0, 322); + __invalidate(rt, 124); + __lookup_matches(rt, 125, 0, 323); + __invalidate(rt, 125); + __lookup_matches(rt, 126, 0, 324); + __invalidate(rt, 126); + __lookup_matches(rt, 127, 0, 325); + __invalidate(rt, 127); + __lookup_matches(rt, 128, 0, 326); + __invalidate(rt, 128); + __lookup_matches(rt, 129, 0, 327); + __invalidate(rt, 129); + __lookup_matches(rt, 130, 0, 328); + __invalidate(rt, 130); + __lookup_matches(rt, 131, 0, 329); + __invalidate(rt, 131); + __lookup_matches(rt, 132, 0, 330); + __invalidate(rt, 132); + __lookup_matches(rt, 133, 0, 331); + __invalidate(rt, 133); + __lookup_matches(rt, 134, 0, 332); + __invalidate(rt, 134); + __lookup_matches(rt, 135, 0, 333); + __invalidate(rt, 135); + __lookup_matches(rt, 136, 0, 334); + __invalidate(rt, 136); + __lookup_matches(rt, 137, 0, 335); + __invalidate(rt, 137); + __lookup_matches(rt, 138, 0, 336); + __invalidate(rt, 138); + __lookup_matches(rt, 139, 0, 337); + __invalidate(rt, 139); + __lookup_matches(rt, 140, 0, 338); + __invalidate(rt, 140); + __lookup_matches(rt, 141, 0, 339); + __invalidate(rt, 141); + __lookup_matches(rt, 142, 0, 340); + __invalidate(rt, 142); + __lookup_matches(rt, 143, 0, 341); + __invalidate(rt, 143); + __lookup_matches(rt, 144, 0, 342); + __invalidate(rt, 144); + __lookup_matches(rt, 145, 0, 343); + __invalidate(rt, 145); + __lookup_matches(rt, 146, 0, 344); + __invalidate(rt, 146); + __lookup_matches(rt, 147, 0, 345); + __invalidate(rt, 147); + __lookup_matches(rt, 148, 0, 346); + __invalidate(rt, 148); + __lookup_matches(rt, 149, 0, 347); + __invalidate(rt, 149); + __lookup_matches(rt, 150, 0, 348); + __invalidate(rt, 150); + __lookup_matches(rt, 151, 0, 349); + __invalidate(rt, 151); + __lookup_matches(rt, 152, 0, 350); + __invalidate(rt, 152); + __lookup_matches(rt, 153, 0, 351); + __invalidate(rt, 153); + __lookup_matches(rt, 154, 0, 352); + __invalidate(rt, 154); + __lookup_matches(rt, 155, 0, 353); + __invalidate(rt, 155); + __lookup_matches(rt, 156, 0, 354); + __invalidate(rt, 156); + __lookup_matches(rt, 157, 0, 355); + __invalidate(rt, 157); + __lookup_matches(rt, 158, 0, 356); + __invalidate(rt, 158); + __lookup_matches(rt, 159, 0, 357); + __invalidate(rt, 159); + __lookup_matches(rt, 160, 0, 358); + __invalidate(rt, 160); + __lookup_matches(rt, 161, 0, 359); + __invalidate(rt, 161); + __lookup_matches(rt, 162, 0, 360); + __invalidate(rt, 162); + __lookup_matches(rt, 164, 0, 361); + __invalidate(rt, 164); + __lookup_matches(rt, 165, 0, 362); + __invalidate(rt, 165); + __lookup_matches(rt, 166, 0, 363); + __invalidate(rt, 166); + __lookup_matches(rt, 167, 0, 364); + __invalidate(rt, 167); + __lookup_matches(rt, 168, 0, 365); + __invalidate(rt, 168); + __lookup_matches(rt, 169, 0, 366); + __invalidate(rt, 169); + __lookup_matches(rt, 170, 0, 367); + __invalidate(rt, 170); + __lookup_matches(rt, 171, 0, 368); + __invalidate(rt, 171); + __lookup_matches(rt, 172, 0, 369); + __invalidate(rt, 172); + __lookup_matches(rt, 173, 0, 370); + __invalidate(rt, 173); + __lookup_matches(rt, 174, 0, 371); + __invalidate(rt, 174); + __lookup_matches(rt, 175, 0, 372); + __invalidate(rt, 175); + __lookup_matches(rt, 176, 0, 373); + __invalidate(rt, 176); + __lookup_matches(rt, 177, 0, 374); + __invalidate(rt, 177); + __lookup_matches(rt, 178, 0, 375); + __invalidate(rt, 178); + __lookup_matches(rt, 179, 0, 376); + __invalidate(rt, 179); + __lookup_matches(rt, 180, 0, 377); + __invalidate(rt, 180); + __lookup_matches(rt, 181, 0, 378); + __invalidate(rt, 181); + __lookup_matches(rt, 182, 0, 379); + __invalidate(rt, 182); + __lookup_matches(rt, 183, 0, 380); + __invalidate(rt, 183); + __lookup_matches(rt, 184, 0, 381); + __invalidate(rt, 184); + __lookup_matches(rt, 185, 0, 382); + __invalidate(rt, 185); + __lookup_matches(rt, 186, 0, 383); + __invalidate(rt, 186); + __lookup_matches(rt, 187, 0, 384); + __invalidate(rt, 187); + __lookup_matches(rt, 188, 0, 385); + __invalidate(rt, 188); + __lookup_matches(rt, 189, 0, 386); + __invalidate(rt, 189); + __lookup_matches(rt, 190, 0, 387); + __invalidate(rt, 190); + __lookup_matches(rt, 191, 0, 388); + __invalidate(rt, 191); + __lookup_matches(rt, 192, 0, 389); + __invalidate(rt, 192); + __lookup_matches(rt, 193, 0, 390); + __invalidate(rt, 193); + __lookup_matches(rt, 194, 0, 391); + __invalidate(rt, 194); + __lookup_matches(rt, 195, 0, 392); + __invalidate(rt, 195); + __lookup_matches(rt, 196, 0, 393); + __invalidate(rt, 196); + __lookup_matches(rt, 197, 0, 394); + __invalidate(rt, 197); + __lookup_matches(rt, 198, 0, 395); + __invalidate(rt, 198); + __lookup_matches(rt, 199, 0, 396); + __invalidate(rt, 199); + __lookup_matches(rt, 200, 0, 397); + __invalidate(rt, 200); + __lookup_matches(rt, 201, 0, 398); + __invalidate(rt, 201); + __lookup_matches(rt, 202, 0, 399); + __invalidate(rt, 202); + __lookup_matches(rt, 203, 0, 400); + __invalidate(rt, 203); + __lookup_matches(rt, 204, 0, 401); + __invalidate(rt, 204); + __lookup_matches(rt, 205, 0, 402); + __invalidate(rt, 205); + __lookup_matches(rt, 206, 0, 403); + __invalidate(rt, 206); + __lookup_matches(rt, 207, 0, 404); + __invalidate(rt, 207); + __lookup_matches(rt, 208, 0, 405); + __invalidate(rt, 208); + __lookup_matches(rt, 209, 0, 406); + __invalidate(rt, 209); + __lookup_matches(rt, 210, 0, 407); + __invalidate(rt, 210); + __lookup_fails(rt, 6, 0); + __insert(rt, 6, 0, 408); + __lookup_fails(rt, 7, 0); + __insert(rt, 7, 0, 409); + __lookup_fails(rt, 8, 0); + __insert(rt, 8, 0, 410); + __lookup_fails(rt, 9, 0); + __insert(rt, 9, 0, 411); + __lookup_fails(rt, 10, 0); + __insert(rt, 10, 0, 412); + __lookup_fails(rt, 11, 0); + __insert(rt, 11, 0, 413); + __lookup_fails(rt, 13, 0); + __insert(rt, 13, 0, 414); + __lookup_fails(rt, 14, 0); + __insert(rt, 14, 0, 415); + __lookup_fails(rt, 15, 0); + __insert(rt, 15, 0, 416); + __lookup_fails(rt, 16, 0); + __insert(rt, 16, 0, 417); + __lookup_fails(rt, 17, 0); + __insert(rt, 17, 0, 418); + __lookup_fails(rt, 18, 0); + __insert(rt, 18, 0, 419); + __lookup_fails(rt, 19, 0); + __insert(rt, 19, 0, 420); + __lookup_fails(rt, 20, 0); + __insert(rt, 20, 0, 421); + __lookup_fails(rt, 21, 0); + __insert(rt, 21, 0, 422); + __lookup_fails(rt, 22, 0); + __insert(rt, 22, 0, 423); + __lookup_fails(rt, 23, 0); + __insert(rt, 23, 0, 424); + __lookup_matches(rt, 6, 0, 408); + __invalidate(rt, 6); + __lookup_matches(rt, 7, 0, 409); + __invalidate(rt, 7); + __lookup_matches(rt, 8, 0, 410); + __invalidate(rt, 8); + __lookup_matches(rt, 9, 0, 411); + __invalidate(rt, 9); + __lookup_matches(rt, 10, 0, 412); + __invalidate(rt, 10); + __lookup_matches(rt, 11, 0, 413); + __invalidate(rt, 11); + __lookup_matches(rt, 13, 0, 414); + __invalidate(rt, 13); + __lookup_matches(rt, 14, 0, 415); diff --git a/test/unit/run.c b/test/unit/run.c index 482498a..84b867b 100644 --- a/test/unit/run.c +++ b/test/unit/run.c @@ -1,29 +1,312 @@ -#include <CUnit/CUnit.h> -#include <CUnit/Basic.h> - -#define DECL(n) \ - extern CU_TestInfo n ## _list[]; \ - int n ## _init(void); \ - int n ## _fini(void); -#define USE(n) { (char*) #n, n##_init, n##_fini, n##_list } - -DECL(bitset); -DECL(regex); -DECL(config); -DECL(string); - -CU_SuiteInfo suites[] = { - USE(bitset), - USE(regex), - USE(config), - USE(string), - CU_SUITE_INFO_NULL +#include "units.h" + +#include <getopt.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <setjmp.h> +#include <unistd.h> + +//----------------------------------------------------------------- + +#define MAX_COMPONENTS 16 + +struct token { + const char *b, *e; }; -int main(int argc, char **argv) { - CU_initialize_registry(); - CU_register_suites(suites); - CU_basic_set_mode(CU_BRM_VERBOSE); - CU_basic_run_tests(); - return CU_get_number_of_failures() != 0; +static bool _pop_component(const char *path, struct token *result) +{ + const char *b, *e; + + while (*path && *path == '/') + path++; + + b = path; + while (*path && (*path != '/')) + path++; + e = path; + + if (b == e) + return false; + + result->b = b; + result->e = e; + return true; +} + +static unsigned _split_components(const char *path, struct token *toks, unsigned len) +{ + unsigned count = 0; + struct token tok; + tok.e = path; + + while (len && _pop_component(tok.e, &tok)) { + *toks = tok; + toks++; + count++; + len--; + } + + return count; +} + +static void _indent(FILE *stream, unsigned count) +{ + unsigned i; + + for (i = 0; i < count; i++) + fprintf(stream, " "); +} + +static void _print_token(FILE *stream, struct token *t) +{ + const char *ptr; + + for (ptr = t->b; ptr != t->e; ptr++) + fprintf(stream, "%c", *ptr); +} + +static int _char_cmp(char l, char r) +{ + if (l < r) + return -1; + + else if (r < l) + return 1; + + else + return 0; +} + +static int _tok_cmp(struct token *lhs, struct token *rhs) +{ + const char *l = lhs->b, *le = lhs->e; + const char *r = rhs->b, *re = rhs->e; + + while ((l != le) && (r != re) && (*l == *r)) { + l++; + r++; + } + + if ((l != le) && (r != re)) + return _char_cmp(*l, *r); + + else if (r != re) + return -1; + + else if (l != le) + return 1; + + else + return 0; +} + +static void _print_path_delta(FILE *stream, + struct token *old, unsigned old_len, + struct token *new, unsigned new_len, + const char *desc) +{ + unsigned i, common_prefix = 0, len, d; + unsigned max_prefix = old_len < new_len ? old_len : new_len; + + for (i = 0; i < max_prefix; i++) { + if (_tok_cmp(old + i, new + i)) + break; + else + common_prefix++; + } + + for (; i < new_len; i++) { + _indent(stream, common_prefix); + _print_token(stream, new + i); + common_prefix++; + if (i < new_len - 1) + fprintf(stream, "\n"); + } + + len = (new_len > 0) ? common_prefix * 2 + (new[new_len - 1].e - new[new_len - 1].b) : 0; + + fprintf(stream, " "); + for (d = len; d < 60; d++) + fprintf(stream, "."); + fprintf(stream, " "); + fprintf(stream, "%s", desc); + fprintf(stream, "\n"); +} + +typedef struct token comp_t[MAX_COMPONENTS]; + +static void _list_tests(struct test_details **tests, unsigned nr) +{ + unsigned i, current = 0, current_len, last_len = 0; + + comp_t components[2]; + + for (i = 0; i < nr; i++) { + struct test_details *t = tests[i]; + current_len = _split_components(t->path, components[current], MAX_COMPONENTS); + _print_path_delta(stderr, components[!current], last_len, + components[current], current_len, t->desc); + + last_len = current_len; + current = !current; + } +} + +static void _destroy_tests(struct dm_list *suites) +{ + struct test_suite *ts, *tmp; + + dm_list_iterate_items_safe (ts, tmp, suites) + test_suite_destroy(ts); +} + +static const char *red(bool c) +{ + return c ? "\x1B[31m" : ""; +} + +static const char *green(bool c) +{ + return c ? "\x1B[32m" : ""; +} + +static const char *normal(bool c) +{ + return c ? "\x1B[0m" : ""; +} + +static void _run_test(struct test_details *t, bool use_colour, unsigned *passed, unsigned *total) +{ + void *fixture; + struct test_suite *ts = t->parent; + fprintf(stderr, "[RUN ] %s\n", t->path); + + (*total)++; + if (setjmp(test_k)) + fprintf(stderr, "%s[ FAIL]%s %s\n", red(use_colour), normal(use_colour), t->path); + else { + if (ts->fixture_init) + fixture = ts->fixture_init(); + else + fixture = NULL; + + t->fn(fixture); + + if (ts->fixture_exit) + ts->fixture_exit(fixture); + + (*passed)++; + fprintf(stderr, "%s[ OK]%s\n", green(use_colour), normal(use_colour)); + /* coverity[leaked_storage] fixture released by fixture_exit */ + } } + +static bool _run_tests(struct test_details **tests, unsigned nr) +{ + bool use_colour = isatty(fileno(stderr)); + unsigned i, passed = 0, total = 0; + + for (i = 0; i < nr; i++) + _run_test(tests[i], use_colour, &passed, &total); + + fprintf(stderr, "\n%u/%u tests passed\n", passed, total); + + return passed == total; +} + +static void _usage(void) +{ + fprintf(stderr, "Usage: unit-test <list|run> [pattern]\n"); +} + +static int _cmp_paths(const void *lhs, const void *rhs) +{ + struct test_details *l = *((struct test_details **) lhs); + struct test_details *r = *((struct test_details **) rhs); + + return strcmp(l->path, r->path); +} + +static unsigned _filter(const char *pattern, struct test_details **tests, unsigned nr) +{ + unsigned i, found = 0; + regex_t rx; + + if (regcomp(&rx, pattern, 0)) { + fprintf(stderr, "couldn't compile regex '%s'\n", pattern); + exit(1); + } + + for (i = 0; i < nr; i++) + if (tests[i] && !regexec(&rx, tests[i]->path, 0, NULL, 0)) + tests[found++] = tests[i]; + + regfree(&rx); + + return found; +} + +int main(int argc, char **argv) +{ + int r; + unsigned i, nr_tests; + struct test_suite *ts; + struct test_details *t, **t_array; + struct dm_list suites; + + dm_list_init(&suites); + register_all_tests(&suites); + + // count all tests + nr_tests = 0; + dm_list_iterate_items (ts, &suites) + dm_list_iterate_items (t, &ts->tests) + nr_tests++; + + // stick them in an array + t_array = malloc(sizeof(*t_array) * nr_tests); + if (!t_array) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + memset(t_array, 0, sizeof(*t_array) * nr_tests); + + i = 0; + dm_list_iterate_items (ts, &suites) + dm_list_iterate_items (t, &ts->tests) + t_array[i++] = t; + + // filter + if (argc == 3) + nr_tests = _filter(argv[2], t_array, nr_tests); + + // sort + qsort(t_array, nr_tests, sizeof(*t_array), _cmp_paths); + + // run or list them + if (argc == 1) + r = !_run_tests(t_array, nr_tests); + else { + const char *cmd = argv[1]; + if (!strcmp(cmd, "run")) + r = !_run_tests(t_array, nr_tests); + + else if (!strcmp(cmd, "list")) { + _list_tests(t_array, nr_tests); + r = 0; + + } else { + _usage(); + r = 1; + } + } + + free(t_array); + _destroy_tests(&suites); + + return r; +} + +//----------------------------------------------------------------- diff --git a/test/unit/string_t.c b/test/unit/string_t.c index df72505..82c6448 100644 --- a/test/unit/string_t.c +++ b/test/unit/string_t.c @@ -9,50 +9,48 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libdevmapper.h" +#include "units.h" +#include "device_mapper/all.h" #include <stdio.h> #include <string.h> -#include <CUnit/CUnit.h> - -int string_init(void); -int string_fini(void); - -static struct dm_pool *mem = NULL; - -int string_init(void) +#if 0 +static int _mem_init(void) { - mem = dm_pool_create("string test", 1024); + struct dm_pool *mem = dm_pool_create("string test", 1024); + if (!mem) { + fprintf(stderr, "out of memory\n"); + exit(1); + } - return (mem == NULL); + return mem; } -int string_fini(void) +static void _mem_exit(void *mem) { dm_pool_destroy(mem); - - return 0; } /* TODO: Add more string unit tests here */ +#endif -static void test_strncpy(void) +static void test_strncpy(void *fixture) { const char st[] = "1234567890"; char buf[sizeof(st)]; - CU_ASSERT_EQUAL(dm_strncpy(buf, st, sizeof(buf)), 1); - CU_ASSERT_EQUAL(strcmp(buf, st), 0); + T_ASSERT_EQUAL(dm_strncpy(buf, st, sizeof(buf)), 1); + T_ASSERT_EQUAL(strcmp(buf, st), 0); - CU_ASSERT_EQUAL(dm_strncpy(buf, st, sizeof(buf) - 1), 0); - CU_ASSERT_EQUAL(strlen(buf) + 1, sizeof(buf) - 1); + T_ASSERT_EQUAL(dm_strncpy(buf, st, sizeof(buf) - 1), 0); + T_ASSERT_EQUAL(strlen(buf) + 1, sizeof(buf) - 1); } -static void test_asprint(void) +static void test_asprint(void *fixture) { const char st0[] = ""; const char st1[] = "12345678901"; @@ -61,23 +59,33 @@ static void test_asprint(void) int a; a = dm_asprintf(&buf, "%s", st0); - CU_ASSERT_EQUAL(strcmp(buf, st0), 0); - CU_ASSERT_EQUAL(a, sizeof(st0)); + T_ASSERT_EQUAL(strcmp(buf, st0), 0); + T_ASSERT_EQUAL(a, sizeof(st0)); free(buf); a = dm_asprintf(&buf, "%s", st1); - CU_ASSERT_EQUAL(strcmp(buf, st1), 0); - CU_ASSERT_EQUAL(a, sizeof(st1)); + T_ASSERT_EQUAL(strcmp(buf, st1), 0); + T_ASSERT_EQUAL(a, sizeof(st1)); free(buf); a = dm_asprintf(&buf, "%s", st2); - CU_ASSERT_EQUAL(a, sizeof(st2)); - CU_ASSERT_EQUAL(strcmp(buf, st2), 0); + T_ASSERT_EQUAL(a, sizeof(st2)); + T_ASSERT_EQUAL(strcmp(buf, st2), 0); free(buf); } -CU_TestInfo string_list[] = { - { (char*)"asprint", test_asprint }, - { (char*)"strncpy", test_strncpy }, - CU_TEST_INFO_NULL -}; +#define T(path, desc, fn) register_test(ts, "/base/data-struct/string/" path, desc, fn) + +void string_tests(struct dm_list *all_tests) +{ + struct test_suite *ts = test_suite_create(NULL, NULL); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + + T("asprint", "tests asprint", test_asprint); + T("strncpy", "tests string copying", test_strncpy); + + dm_list_add(all_tests, &ts->list); +} diff --git a/test/unit/unit-test.sh b/test/unit/unit-test.sh new file mode 100644 index 0000000..f545f14 --- /dev/null +++ b/test/unit/unit-test.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# Copyright (C) 2018 Red Hat, Inc. All rights reserved. +# +# This file is part of LVM2. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions +# of the GNU General Public License v.2. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +SKIP_WITH_LVMLOCKD=1 +SKIP_WITH_LVMPOLLD=1 + +SKIP_ROOT_DM_CHECK=1 + +. lib/inittest + +aux unittest run diff --git a/test/unit/units.h b/test/unit/units.h new file mode 100644 index 0000000..d7ac6ad --- /dev/null +++ b/test/unit/units.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef TEST_UNIT_UNITS_H +#define TEST_UNIT_UNITS_H + +#include "framework.h" + +//----------------------------------------------------------------- + +// Declare the function that adds tests suites here ... +void bcache_tests(struct dm_list *suites); +void bcache_utils_tests(struct dm_list *suites); +void bitset_tests(struct dm_list *suites); +void config_tests(struct dm_list *suites); +void dm_list_tests(struct dm_list *suites); +void dm_status_tests(struct dm_list *suites); +void io_engine_tests(struct dm_list *suites); +void percent_tests(struct dm_list *suites); +void radix_tree_tests(struct dm_list *suites); +void regex_tests(struct dm_list *suites); +void string_tests(struct dm_list *suites); +void vdo_tests(struct dm_list *suites); + +// ... and call it in here. +static inline void register_all_tests(struct dm_list *suites) +{ + bcache_tests(suites); + bcache_utils_tests(suites); + bitset_tests(suites); + config_tests(suites); + dm_list_tests(suites); + dm_status_tests(suites); + io_engine_tests(suites); + percent_tests(suites); + radix_tree_tests(suites); + regex_tests(suites); + string_tests(suites); + vdo_tests(suites); +} + +//----------------------------------------------------------------- + +#endif diff --git a/test/unit/vdo_t.c b/test/unit/vdo_t.c new file mode 100644 index 0000000..27ad26b --- /dev/null +++ b/test/unit/vdo_t.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2018 Red Hat, Inc. All rights reserved. + * + * This file is part of LVM2. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "units.h" +#include "device_mapper/vdo/target.h" + +//---------------------------------------------------------------- + +static bool _status_eq(struct dm_vdo_status *lhs, struct dm_vdo_status *rhs) +{ + return !strcmp(lhs->device, rhs->device) && + (lhs->operating_mode == rhs->operating_mode) && + (lhs->recovering == rhs->recovering) && + (lhs->index_state == rhs->index_state) && + (lhs->compression_state == rhs->compression_state) && + (lhs->used_blocks == rhs->used_blocks) && + (lhs->total_blocks == rhs->total_blocks); +} + +#if 0 +static const char *_op_mode(enum dm_vdo_operating_mode m) +{ + switch (m) { + case DM_VDO_MODE_RECOVERING: + return "recovering"; + case DM_VDO_MODE_READ_ONLY: + return "read-only"; + case DM_VDO_MODE_NORMAL: + return "normal"; + } + + return "<unknown>"; +} + +static const char *_index_state(enum dm_vdo_index_state is) +{ + switch (is) { + case DM_VDO_INDEX_ERROR: + return "error"; + case DM_VDO_INDEX_CLOSED: + return "closed"; + case DM_VDO_INDEX_OPENING: + return "opening"; + case DM_VDO_INDEX_CLOSING: + return "closing"; + case DM_VDO_INDEX_OFFLINE: + return "offline"; + case DM_VDO_INDEX_ONLINE: + return "online"; + case DM_VDO_INDEX_UNKNOWN: + return "unknown"; + } + + return "<unknown>"; +} + +static void _print_status(FILE *stream, struct dm_vdo_status *s) +{ + fprintf(stream, "<status| %s ", s->device); + fprintf(stream, "%s ", _op_mode(s->operating_mode)); + fprintf(stream, "%s ", s->recovering ? "recovering" : "-"); + fprintf(stream, "%s ", _index_state(s->index_state)); + fprintf(stream, "%s ", s->compression_state == DM_VDO_COMPRESSION_ONLINE ? "online" : "offline"); + fprintf(stream, "%llu ", (unsigned long long) s->used_blocks); + fprintf(stream, "%llu", (unsigned long long) s->total_blocks); + fprintf(stream, ">"); +} +#endif + +struct example_good { + const char *input; + struct dm_vdo_status status; +}; + +static void _check_good(struct example_good *es, unsigned count) +{ + unsigned i; + + for (i = 0; i < count; i++) { + struct example_good *e = es + i; + struct dm_vdo_status_parse_result pr; + + T_ASSERT(dm_vdo_status_parse(NULL, e->input, &pr)); +#if 0 + _print_status(stderr, pr.status); + fprintf(stderr, "\n"); + _print_status(stderr, &e->status); + fprintf(stderr, "\n"); +#endif + T_ASSERT(_status_eq(&e->status, pr.status)); + free(pr.status); + } +} + +struct example_bad { + const char *input; + const char *reason; +}; + +static void _check_bad(struct example_bad *es, unsigned count) +{ + unsigned i; + + for (i = 0; i < count; i++) { + struct example_bad *e = es + i; + struct dm_vdo_status_parse_result pr; + + T_ASSERT(!dm_vdo_status_parse(NULL, e->input, &pr)); + T_ASSERT(!strcmp(e->reason, pr.error)); + } +} + +static void _test_device_names_good(void *fixture) +{ + static struct example_good _es[] = { + {"foo1234 read-only - error online 0 1234", + {(char *) "foo1234", DM_VDO_MODE_READ_ONLY, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"f read-only - error online 0 1234", + {(char *) "f", DM_VDO_MODE_READ_ONLY, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + }; + + _check_good(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_operating_mode_good(void *fixture) +{ + static struct example_good _es[] = { + {"device-name recovering - error online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name read-only - error online 0 1234", + {(char *) "device-name", DM_VDO_MODE_READ_ONLY, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name normal - error online 0 1234", + {(char *) "device-name", DM_VDO_MODE_NORMAL, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + }; + + _check_good(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_operating_mode_bad(void *fixture) +{ + static struct example_bad _es[] = { + {"device-name investigating - error online 0 1234", + "couldn't parse 'operating mode'"}}; + + _check_bad(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_recovering_good(void *fixture) +{ + static struct example_good _es[] = { + {"device-name recovering - error online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name read-only recovering error online 0 1234", + {(char *) "device-name", DM_VDO_MODE_READ_ONLY, true, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + }; + + _check_good(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_recovering_bad(void *fixture) +{ + static struct example_bad _es[] = { + {"device-name normal fish error online 0 1234", + "couldn't parse 'recovering'"}}; + + _check_bad(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_index_state_good(void *fixture) +{ + static struct example_good _es[] = { + {"device-name recovering - error online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name recovering - closed online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_CLOSED, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name recovering - opening online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_OPENING, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name recovering - closing online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_CLOSING, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name recovering - offline online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_OFFLINE, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name recovering - online online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_ONLINE, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name recovering - unknown online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_UNKNOWN, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + }; + + _check_good(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_index_state_bad(void *fixture) +{ + static struct example_bad _es[] = { + {"device-name normal - fish online 0 1234", + "couldn't parse 'index state'"}}; + + _check_bad(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_compression_state_good(void *fixture) +{ + static struct example_good _es[] = { + {"device-name recovering - error online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name read-only - error offline 0 1234", + {(char *) "device-name", DM_VDO_MODE_READ_ONLY, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_OFFLINE, 0, 1234}}, + }; + + _check_good(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_compression_state_bad(void *fixture) +{ + static struct example_bad _es[] = { + {"device-name normal - error fish 0 1234", + "couldn't parse 'compression state'"}}; + + _check_bad(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_used_blocks_good(void *fixture) +{ + static struct example_good _es[] = { + {"device-name recovering - error online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name read-only - error offline 1 1234", + {(char *) "device-name", DM_VDO_MODE_READ_ONLY, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_OFFLINE, 1, 1234}}, + {"device-name read-only - error offline 12 1234", + {(char *) "device-name", DM_VDO_MODE_READ_ONLY, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_OFFLINE, 12, 1234}}, + {"device-name read-only - error offline 3456 1234", + {(char *) "device-name", DM_VDO_MODE_READ_ONLY, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_OFFLINE, 3456, 1234}}, + }; + + _check_good(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_used_blocks_bad(void *fixture) +{ + static struct example_bad _es[] = { + {"device-name normal - error online fish 1234", + "couldn't parse 'used blocks'"}}; + + _check_bad(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_total_blocks_good(void *fixture) +{ + static struct example_good _es[] = { + {"device-name recovering - error online 0 1234", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1234}}, + {"device-name recovering - error online 0 1", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 1}}, + {"device-name recovering - error online 0 0", + {(char *) "device-name", DM_VDO_MODE_RECOVERING, false, DM_VDO_INDEX_ERROR, DM_VDO_COMPRESSION_ONLINE, 0, 0}}, + }; + + _check_good(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_total_blocks_bad(void *fixture) +{ + static struct example_bad _es[] = { + {"device-name normal - error online 0 fish", + "couldn't parse 'total blocks'"}}; + + _check_bad(_es, DM_ARRAY_SIZE(_es)); +} + +static void _test_status_bad(void *fixture) +{ + struct example_bad _bad[] = { + {"", "couldn't get token for device"}, + {"device-name read-only - error online 0 1000 lksd", "too many tokens"} + }; + + _check_bad(_bad, DM_ARRAY_SIZE(_bad)); +} + +//---------------------------------------------------------------- + +#define T(path, desc, fn) register_test(ts, "/device-mapper/vdo/status/" path, desc, fn) + +static struct test_suite *_tests(void) +{ + struct test_suite *ts = test_suite_create(NULL, NULL); + if (!ts) { + fprintf(stderr, "out of memory\n"); + exit(1); + }; + + T("device-names", "parse various device names", _test_device_names_good); + T("operating-mode-good", "operating mode, good examples", _test_operating_mode_good); + T("operating-mode-bad", "operating mode, bad examples", _test_operating_mode_bad); + T("recovering-good", "recovering, good examples", _test_recovering_good); + T("recovering-bad", "recovering, bad examples", _test_recovering_bad); + T("index-state-good", "index state, good examples", _test_index_state_good); + T("index-state-bad", "index state, bad examples", _test_index_state_bad); + T("compression-state-good", "compression state, good examples", _test_compression_state_good); + T("compression-state-bad", "compression state, bad examples", _test_compression_state_bad); + T("used-blocks-good", "used blocks, good examples", _test_used_blocks_good); + T("used-blocks-bad", "used blocks, bad examples", _test_used_blocks_bad); + T("total-blocks-good", "total blocks, good examples", _test_total_blocks_good); + T("total-blocks-bad", "total blocks, bad examples", _test_total_blocks_bad); + T("bad", "parse various badly formed vdo status lines", _test_status_bad); + + return ts; +} + +void vdo_tests(struct dm_list *all_tests) +{ + dm_list_add(all_tests, &_tests()->list); +} + +//---------------------------------------------------------------- |