From 7c1e2f16fc37302cd2816dc8f8142ff6c38583f9 Mon Sep 17 00:00:00 2001
From: Evgeny Voropaev <evorop@gmail.com>
Date: Mon, 13 Apr 2026 20:18:42 +0800
Subject: [PATCH v11 2/5] Tests of vect and uniqsortvect containers and of
 bitpack algorithms.

Unit tests for the vect and the bitpack units are imlemented. Unit tests
are implemented as binary applications written in C language (ELF
executables) that support the TAP protocol and are run using the Prove
utility.

The new Makefile target, check-unit, is integrated into the PostgreSQL
build system and allows running the unit tests using the command 'make
check-unit'.

Author: Evgeny Voropaev <evgeny.voropaev@tantorlabs.com> <evorop@gmail.com>
Reviewed by: Andrey Borodin <x4mmm@yandex-team.ru>
---
 GNUmakefile.in                        |   1 +
 src/Makefile.global.in                |   2 +-
 src/test/Makefile                     |   1 +
 src/test/dfor/.gitignore              |   3 +
 src/test/dfor/Makefile                |  54 ++++
 src/test/dfor/meson.build             |  62 ++++
 src/test/dfor/test.h                  |  31 ++
 src/test/dfor/test_bitpack_u16.c      | 357 ++++++++++++++++++++++
 src/test/dfor/test_uniqsortvect_u16.c | 263 ++++++++++++++++
 src/test/dfor/test_vect_u16.c         | 168 ++++++++++
 src/test/libtap/.gitignore            |  13 +
 src/test/libtap/.travis.yml           |  13 +
 src/test/libtap/COPYING               | 165 ++++++++++
 src/test/libtap/INSTALL               |  41 +++
 src/test/libtap/Makefile              |  73 +++++
 src/test/libtap/Makefile.win          |  37 +++
 src/test/libtap/README.md             | 268 ++++++++++++++++
 src/test/libtap/tap.c                 | 421 ++++++++++++++++++++++++++
 src/test/libtap/tap.h                 | 115 +++++++
 src/test/meson.build                  |   1 +
 20 files changed, 2088 insertions(+), 1 deletion(-)
 create mode 100644 src/test/dfor/.gitignore
 create mode 100644 src/test/dfor/Makefile
 create mode 100644 src/test/dfor/meson.build
 create mode 100644 src/test/dfor/test.h
 create mode 100644 src/test/dfor/test_bitpack_u16.c
 create mode 100644 src/test/dfor/test_uniqsortvect_u16.c
 create mode 100644 src/test/dfor/test_vect_u16.c
 create mode 100644 src/test/libtap/.gitignore
 create mode 100644 src/test/libtap/.travis.yml
 create mode 100644 src/test/libtap/COPYING
 create mode 100644 src/test/libtap/INSTALL
 create mode 100644 src/test/libtap/Makefile
 create mode 100644 src/test/libtap/Makefile.win
 create mode 100644 src/test/libtap/README.md
 create mode 100644 src/test/libtap/tap.c
 create mode 100644 src/test/libtap/tap.h

diff --git a/GNUmakefile.in b/GNUmakefile.in
index cf6e759486e..3d9a42d6ad4 100644
--- a/GNUmakefile.in
+++ b/GNUmakefile.in
@@ -69,6 +69,7 @@ check check-tests installcheck installcheck-parallel installcheck-tests: submake
 	$(MAKE) -C src/test/regress $@
 
 $(call recurse,check-world,src/test src/pl src/interfaces contrib src/bin src/tools/pg_bsd_indent,check)
+$(call recurse,check-unit,src/test,check-unit)
 $(call recurse,checkprep,  src/test src/pl src/interfaces contrib src/bin)
 
 $(call recurse,installcheck-world,src/test src/pl src/interfaces contrib src/bin,installcheck)
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index cef1ad7f87d..0b6a22be18b 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -19,7 +19,7 @@
 #
 # Meta configuration
 
-standard_targets = all install installdirs uninstall clean distclean coverage check checkprep installcheck init-po update-po
+standard_targets = all install installdirs uninstall clean distclean coverage check checkprep installcheck check-unit init-po update-po
 # these targets should recurse even into subdirectories not being built:
 standard_always_targets = clean distclean
 
diff --git a/src/test/Makefile b/src/test/Makefile
index 3eb0a06abb4..aba8db1f483 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -14,6 +14,7 @@ include $(top_builddir)/src/Makefile.global
 
 SUBDIRS = \
 	authentication \
+	dfor \
 	isolation \
 	modules \
 	perl \
diff --git a/src/test/dfor/.gitignore b/src/test/dfor/.gitignore
new file mode 100644
index 00000000000..0d77a51216b
--- /dev/null
+++ b/src/test/dfor/.gitignore
@@ -0,0 +1,3 @@
+test_bitpack_u16
+test_uniqsortvect_u16
+test_vect_u16
diff --git a/src/test/dfor/Makefile b/src/test/dfor/Makefile
new file mode 100644
index 00000000000..4fc9f4bc1ba
--- /dev/null
+++ b/src/test/dfor/Makefile
@@ -0,0 +1,54 @@
+#-------------------------------------------------------------------------
+# File: src/test/dfor/Makefile
+#-------------------------------------------------------------------------
+
+subdir = src/test/dfor
+top_builddir = ../../..
+dfor_dir := $(top_builddir)/src/backend/lib
+
+include $(dfor_dir)/Makefile.dfor
+
+# Ensure dependency tracking works
+OBJS += $(OBJS_DFOR)
+
+include $(top_builddir)/src/Makefile.global
+
+# This fixes a problem in some CI jobs
+DEPDIR ?= ".deps"
+
+# Object files of vect, bitpack and dfor used by these unit-tests are in the same
+# directory as this Makefile. Tests don't use ones from src/backend/lib and compile
+# different ones for themselves.
+$(info Use OBJS_DFOR=$(OBJS_DFOR) from current directory $(subdir). \
+       They are built on sources from $(dfor_dir))
+
+$(OBJS_DFOR): %.o: $(dfor_dir)/%.c
+	@if test ! -d $(DEPDIR); then mkdir -p $(DEPDIR); fi
+	$(CC) $(CFLAGS) $(CPPFLAGS) -DFRONTEND \
+		-c $< \
+		-MMD -MP -MF $(DEPDIR)/$(notdir $<:.c=.Po) \
+		-o $@
+
+LIBTAP_OBJS = $(top_builddir)/src/test/libtap/tap.o
+
+TESTS= test_vect_u16 \
+       test_uniqsortvect_u16 \
+       test_bitpack_u16
+
+$(TESTS:%=%.o): CPPFLAGS += -I$(top_builddir)/src/test -DFRONTEND
+
+all: $(TESTS)
+
+$(TESTS): %: %.o $(LIBTAP_OBJS) $(OBJS_DFOR)
+	$(CC) $(CFLAGS) $(CPPFLAGS) $^ $(LDFLAGS) $(LIBS) -o $@$(X)
+
+check-unit: $(TESTS)
+	echo "# +++ Unit tests in $(subdir) +++" && \
+	cd $(top_builddir)/$(subdir) && \
+	   $(PROVE) $(PROVE_FLAGS) \
+	    -v $(addprefix ./,$(if $(PROVE_TESTS), $(PROVE_TESTS), $(TESTS)))
+
+check: check-unit
+
+clean distclean:
+	rm -rf $(TESTS) *.o
diff --git a/src/test/dfor/meson.build b/src/test/dfor/meson.build
new file mode 100644
index 00000000000..ce762c52430
--- /dev/null
+++ b/src/test/dfor/meson.build
@@ -0,0 +1,62 @@
+dfor_dir = join_paths(meson.project_source_root(), 'src/backend/lib')
+
+# Object files of vect, bitpack and dfor used by these unit-tests are in the same
+# directory as this meson.build. Tests don't use ones from src/backend/lib and
+# compile different ones for themselves. In Meson/Ninja build system we unite
+# them into a static library.
+
+dfor_sources = files(
+  join_paths(dfor_dir, 'vect_u16.c'),
+  join_paths(dfor_dir, 'bitpack_u16.c'),
+)
+
+dfor_test_lib = static_library(
+  'dfor_test_lib',
+  dfor_sources,
+  include_directories: [
+    include_directories('../../..'), # top_builddir
+    include_directories('../'),      # src/test
+    postgres_inc,                    # src/include here
+  ],
+  c_args: ['-DFRONTEND'],
+)
+
+# We also build libtap as a static library
+
+libtap = static_library(
+  'tap',
+  '../../test/libtap/tap.c',
+  include_directories:
+    include_directories('../../..'), # top_builddir
+)
+
+# Each test is an ELF executable
+
+test_names = [
+  'test_vect_u16',
+  'test_uniqsortvect_u16',
+  'test_bitpack_u16',
+]
+
+foreach t : test_names
+  exe = executable(
+    t,
+    t + '.c',
+    link_with: [
+      dfor_test_lib,
+      libtap,
+      common_static, # Provides pg_printf and other common utilities
+      pgport_static,    # Provides OS-portability functions
+    ],
+	dependencies: [os_deps, libintl],
+    include_directories: [
+      include_directories('../../..'), # top_builddir
+      include_directories('../'),      # src/test
+      postgres_inc,                    # src/include here
+    ],
+    # Backend code often requires these arguments to identify as backend
+    c_args: ['-DFRONTEND'],
+  )
+
+  test(t, exe, suite: 'dfor')
+endforeach
diff --git a/src/test/dfor/test.h b/src/test/dfor/test.h
new file mode 100644
index 00000000000..f6c54aad95f
--- /dev/null
+++ b/src/test/dfor/test.h
@@ -0,0 +1,31 @@
+
+/*
+ * test.h
+ */
+#ifndef _TEST_H_
+#define _TEST_H_
+
+#include <inttypes.h>
+#include <stdio.h>
+
+static inline void
+test_print_u8_array(size_t cnt, uint8_t arr[], const char *name)
+{
+	printf("%s(hex): { ", name);
+	for (size_t j = 0; j < cnt - 1; j++)
+		printf("%02" PRIx8 ", ", arr[j]);
+
+	printf("%02" PRIx8 " }\n", arr[cnt - 1]);
+}
+
+static inline void
+test_print_u16_array(size_t cnt, uint16_t arr[], const char *name)
+{
+	printf("%s(hex): { ", name);
+	for (size_t j = 0; j < cnt - 1; j++)
+		printf("%04" PRIx16 ", ", arr[j]);
+
+	printf("%04" PRIx16 "}\n", arr[cnt - 1]);
+}
+
+#endif /* _TEST_H_ */
\ No newline at end of file
diff --git a/src/test/dfor/test_bitpack_u16.c b/src/test/dfor/test_bitpack_u16.c
new file mode 100644
index 00000000000..da84bb2f22e
--- /dev/null
+++ b/src/test/dfor/test_bitpack_u16.c
@@ -0,0 +1,357 @@
+/*
+ * test_bitpack.c
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "lib/bitpack_u16.h"
+#include "lib/vect_u16.h"
+#include "libtap/tap.h"
+
+int
+main(void)
+{
+	plan(195);
+	printf("========================================\n");
+	printf("Test MASK AND WIDTH CALCULATION\n");
+	{
+		cmp_ok(1, "==", (uint16_t)width_u16_from_val(0x00),
+			   "Width of 00 is equal to 1 bit.");
+		cmp_ok(2, "==", (uint16_t)width_u16_from_val(0x02),
+			   "Width of 02 is equal to 2 bits.");
+		cmp_ok(2, "==", (uint16_t)width_u16_from_val(0x02),
+			   "Width of 02 is equal to 2 bits.");
+		cmp_ok(2, "==", (uint16_t)width_u16_from_val(0x03),
+			   "Width of 03 is equal to 2 bits.");
+		cmp_ok(3, "==", (uint16_t)width_u16_from_val(0x04),
+			   "Width of 04 is equal to 3 bits.");
+		cmp_ok(3, "==", (uint16_t)width_u16_from_val(0x05),
+			   "Width of 05 is equal to 3 bits.");
+		cmp_ok(4, "==", (uint16_t)width_u16_from_val(0x08),
+			   "Width of 08 is equal to 4 bits.");
+		cmp_ok(4, "==", (uint16_t)width_u16_from_val(0x0A),
+			   "Width of 10 is equal to 4 bits.");
+		cmp_ok(4, "==", (uint16_t)width_u16_from_val(0x0F),
+			   "Width of 15 is equal to 4 bits.");
+		cmp_ok(5, "==", (uint16_t)width_u16_from_val(0x10),
+			   "Width of 16 is equal to 5 bits.");
+		cmp_ok(5, "==", (uint16_t)width_u16_from_val(0x1F),
+			   "Width of 31 is equal to 5 bits.");
+		cmp_ok(6, "==", (uint16_t)width_u16_from_val(0x20),
+			   "Width of 32 is equal to 6 bits.");
+		cmp_ok(7, "==", (uint16_t)width_u16_from_val(0x40),
+			   "Width of 64 is equal to 7 bits.");
+		cmp_ok(8, "==", (uint16_t)width_u16_from_val(0x80),
+			   "Width of 128 is equal to 8 bits.");
+		cmp_ok(13, "==", (uint16_t)width_u16_from_val(0x1000),
+			   "Width of 0x01000 is equal to 13 bits.");
+		cmp_ok(16, "==", (uint16_t)width_u16_from_val(0x8ABC),
+			   "Width of 0x08ABC is equal to 15 bits.");
+
+		cmp_ok(0x1, "==", (uint16_t)width_u16_to_mask(1),
+			   "Mask from width 1 is 00000001(bin).");
+		cmp_ok(0x3, "==", (uint16_t)width_u16_to_mask(2),
+			   "Mask from width 2 is 00000011(bin).");
+		cmp_ok(0x7, "==", (uint16_t)width_u16_to_mask(3),
+			   "Mask from width 3 is 00000111(bin).");
+		cmp_ok(0x3FF, "==", (uint16_t)width_u16_to_mask(10),
+			   "Mask from width 10 is 0x3FF(bin).");
+		cmp_ok(0x7FFF, "==", (uint16_t)width_u16_to_mask(15),
+			   "Mask from width 15 is 0x7FFF(bin).");
+		cmp_ok(0xFFFF, "==", (uint16_t)width_u16_to_mask(16),
+			   "Mask from width 16 is 0xFFFF(bin).");
+	}
+	printf("Test MASK AND WIDTH CALCULATION PASSED\n");
+	printf("========================================\n\n");
+
+	printf("========================================\n");
+	printf("Test BITPACK PACKING\n");
+	{
+#define SIZEOFPACK 60U
+		uint8_t pack[SIZEOFPACK];
+		size_t caret = 0;
+		memset(pack, 0, SIZEOFPACK);
+		/*
+		 * Since we implemented the nulifying of bits according to a mask (see
+		 * the bitpack function), we can use even a pack not prepared in advance
+		 * and comprising garbage. But we want to check value of each byte of
+		 * the pack in this test and we simplify this task by using a zeroed
+		 * pack.
+		 */
+
+		for (int j = 0; j < 8; j++) {
+			caret = bitpack_u16_pack(pack, caret, 1, 1);
+			caret = bitpack_u16_pack(pack, caret, 0, 1);
+		}
+		cmp_ok(16, "==", caret, "Caret = 16.");
+		cmp_ok(0x55, "==", pack[0], "Saved bit-by-bit: first byte is 0x55.");
+		cmp_ok(0x55, "==", pack[1], "Saved bit-by-bit: second byte is 0x55.");
+
+		for (int j = 0; j < 8; j++) {
+			caret = bitpack_u16_pack(pack, caret, 0x02 /* 10(bin) */, 2);
+		}
+
+		cmp_ok(32, "==", caret, "Caret = 32.");
+		cmp_ok(0xAA, "==", pack[2],
+			   "Saved with two-bit width: first byte is 0xAA.");
+		cmp_ok(0xAA, "==", pack[3],
+			   "Saved with two-bit width: second byte is 0xAA.");
+
+		for (int j = 0; j < 8; j++) {
+			caret = bitpack_u16_pack(pack, caret, 0x05 /* 101(bin) */, 3);
+			caret = bitpack_u16_pack(pack, caret, 0x02 /* 010(bin) */, 3);
+		}
+
+		cmp_ok(80, "==", caret, "Caret = 80.");
+		cmp_ok(0x55, "==", pack[4],
+			   "Saved with three-bit width: first byte is 0x55.");
+		cmp_ok(0x55, "==", pack[5],
+			   "Saved with three-bit width: second byte is 0x55.");
+		cmp_ok(0x55, "==", pack[6],
+			   "Saved with three-bit width: second byte is 0x55.");
+		cmp_ok(0x55, "==", pack[7],
+			   "Saved with three-bit width: first byte is 0x55.");
+		cmp_ok(0x55, "==", pack[8],
+			   "Saved with three-bit width: second byte is 0x55.");
+		cmp_ok(0x55, "==", pack[9],
+			   "Saved with three-bit width: second byte is 0x55.");
+
+		caret = bitpack_u16_pack(pack, caret, 0x0B, 4);
+		caret = bitpack_u16_pack(pack, caret, 0x0C, 4);
+		caret = bitpack_u16_pack(pack, caret, 0x0D, 4);
+		caret = bitpack_u16_pack(pack, caret, 0x0E, 4);
+
+		cmp_ok(96, "==", caret, "Caret = 96.");
+		cmp_ok(0xCB, "==", pack[10],
+			   "Saved with four-bit width: first byte is 0xCB.");
+		cmp_ok(0xED, "==", pack[11],
+			   "Saved with four-bit width: second byte is 0xED.");
+
+		for (int j = 0; j < 8; j++) {
+			caret = bitpack_u16_pack(pack, caret, 0x07 /* 00111b */, 5);
+		}
+
+		cmp_ok(136, "==", caret, "Caret = 136.");
+		cmp_ok(0xE7, "==", pack[12],
+			   "Saved with five-bit width: first byte is 0xE7.");
+		cmp_ok(0x9C, "==", pack[13],
+			   "Saved with five-bit width: second byte is 0x9C.");
+		cmp_ok(0x73, "==", pack[14],
+			   "Saved with five-bit width: third byte is 0x73.");
+		cmp_ok(0xCE, "==", pack[15],
+			   "Saved with five-bit width: fourth byte is 0xCE.");
+		cmp_ok(0x39, "==", pack[16],
+			   "Saved with five-bit width: fifth byte is 0x39.");
+
+		for (int j = 0; j < 4; j++)
+			caret = bitpack_u16_pack(pack, caret, 0x07 /* 000111b */, 6);
+
+		cmp_ok(160, "==", caret, "Caret = 160.");
+		cmp_ok(0xC7, "==", pack[17],
+			   "Saved with six-bit width: first byte is 0xC7.");
+		cmp_ok(0x71, "==", pack[18],
+			   "Saved with six-bit width: second byte is 0x71.");
+		cmp_ok(0x1C, "==", pack[19],
+			   "Saved with six-bit width: third byte is 0x1C.");
+
+		for (int j = 0; j < 8; j++)
+			caret = bitpack_u16_pack(pack, caret, 0x57 /* 1010111b */, 7);
+
+		cmp_ok(216, "==", caret, "Caret = 216.");
+		cmp_ok(0xD7, "==", pack[20],
+			   "Saved with seven-bit width: byte1 is 0xD7.");
+		cmp_ok(0xEB, "==", pack[21],
+			   "Saved with seven-bit width: byte2 is 0xEB.");
+		cmp_ok(0xF5, "==", pack[22],
+			   "Saved with seven-bit width: byte3 is 0xF5.");
+		cmp_ok(0x7A, "==", pack[23],
+			   "Saved with seven-bit width: byte4 is 0x7A.");
+		cmp_ok(0xBD, "==", pack[24],
+			   "Saved with seven-bit width: byte5 is 0xBD.");
+		cmp_ok(0x5E, "==", pack[25],
+			   "Saved with seven-bit width: byte6 is 0x5E.");
+		cmp_ok(0xAF, "==", pack[26],
+			   "Saved with seven-bit width: byte7 is 0xAF.");
+
+		caret = bitpack_u16_pack(pack, caret, 0xBA, 8);
+		caret = bitpack_u16_pack(pack, caret, 0xDC, 8);
+		caret = bitpack_u16_pack(pack, caret, 0xFE, 8);
+
+		cmp_ok(240, "==", caret, "Caret = 240.");
+		cmp_ok(0xBA, "==", pack[27],
+			   "Saved with eight-bit width: byte1 is 0xBA.");
+		cmp_ok(0xDC, "==", pack[28],
+			   "Saved with eight-bit width: byte2 is 0xDC.");
+		cmp_ok(0xFE, "==", pack[29],
+			   "Saved with eight-bit width: byte3 is 0xFE.");
+
+		caret = bitpack_u16_pack(
+			pack, caret, 0x00,
+			4); /* move to 4 bit for fun and again continue with 8-bit width */
+		caret = bitpack_u16_pack(pack, caret, 0x32, 8);
+		caret = bitpack_u16_pack(pack, caret, 0x54, 8);
+		caret = bitpack_u16_pack(pack, caret, 0x76, 8);
+		caret = bitpack_u16_pack(pack, caret, 0x98, 8);
+
+		cmp_ok(276, "==", caret, "Caret = 276.");
+		cmp_ok(0x20, "==", pack[30],
+			   "Saved with eight-bit width but shifted by 4: is 0x20.");
+		cmp_ok(0x43, "==", pack[31],
+			   "Saved with eight-bit width but shifted by 4: is 0x43.");
+		cmp_ok(0x65, "==", pack[32],
+			   "Saved with eight-bit width but shifted by 4: is 0x65.");
+		cmp_ok(0x87, "==", pack[33],
+			   "Saved with eight-bit width but shifted by 4: is 0x87.");
+		cmp_ok(0x09, "==", pack[34],
+			   "Saved with eight-bit width but shifted by 4: is 0x09.");
+
+		caret = bitpack_u16_pack(
+			pack, caret, 0x00,
+			4); /* move to 4 bit for fun and again continue with 8-bit width */
+		cmp_ok(280, "==", caret, "Caret = 280.");
+		cmp_ok(0x09, "==", pack[34],
+			   "Added padding 0x0, width=4. Byte in pack is still 0x09.");
+
+		for (int j = 0; j < 3; j++)
+			caret = bitpack_u16_pack(pack, caret, 0x1671 /* 1011001110001b */,
+									 13);
+
+		cmp_ok(319, "==", caret, "Caret = 319.");
+		cmp_ok(0x71, "==", pack[35], "Saved with thirteen-bit width: is 0x71.");
+		cmp_ok(0x36, "==", pack[36], "Saved with thirteen-bit width: is 0x36.");
+		cmp_ok(0xCE, "==", pack[37], "Saved with thirteen-bit width: is 0xCE.");
+		cmp_ok(0xC6, "==", pack[38], "Saved with thirteen-bit width: is 0xC6.");
+		cmp_ok(0x59, "==", pack[39], "Saved with thirteen-bit width: is 0x59.");
+
+		caret = bitpack_u16_pack(pack, caret, 0x1 /* PADDING */, 1);
+		cmp_ok(320, "==", caret, "Caret = 320.");
+		cmp_ok(0xD9, "==", pack[39],
+			   "After padding with 0x01, w=1: 0x59 -> 0xD9.");
+
+		for (int j = 0; j < 5; j++)
+			caret = bitpack_u16_pack(pack, caret, 0xCDEF, 16);
+
+		cmp_ok(400, "==", caret, "Caret = 400.");
+		for (int i = 40; i < 50;) {
+			cmp_ok(0xEF, "==", pack[i++], "Packed with width=16. 0xEF.");
+			cmp_ok(0xCD, "==", pack[i++], "Packed with width=16. 0xC.");
+		}
+
+		caret = bitpack_u16_pack(pack, caret,
+								 0x0 /* PADDING in order to shift by 1 bit*/,
+								 1);
+		cmp_ok(401, "==", caret, "Caret = 401.");
+
+		for (int j = 0; j < 3; j++)
+			caret = bitpack_u16_pack(pack, caret, 0x5555, 16);
+
+		cmp_ok(449, "==", caret, "Caret = 401.");
+		for (int i = 50; i < 56;)
+			cmp_ok(0xAA, "==", pack[i++],
+				   "16-bit value saved with shift by 1 bit 0x55->0xAA.");
+
+		cmp_ok(0x0, "==", pack[56], "1 higher bit is alone .");
+
+		printf("Test BITPACK PACKING PASSED\n");
+		printf("========================================\n\n");
+
+		printf("========================================\n");
+		printf("Test BITPACK UNPACKING\n");
+
+		caret = 0;
+
+		for (int j = 0; j < 8; j++) {
+			cmp_ok(0x01, "==", bitpack_u16_unpack(pack, &caret, 1));
+			cmp_ok(0x00, "==", bitpack_u16_unpack(pack, &caret, 1));
+		}
+		cmp_ok(caret, "==", 16);
+
+		for (int j = 0; j < 8; j++)
+			cmp_ok(0x02, "==", bitpack_u16_unpack(pack, &caret, 2));
+
+		cmp_ok(32, "==", caret, "Caret = 32.");
+
+		for (int j = 0; j < 8; j++) {
+			cmp_ok(0x05, "==", bitpack_u16_unpack(pack, &caret, 3));
+			cmp_ok(0x02, "==", bitpack_u16_unpack(pack, &caret, 3));
+		}
+		cmp_ok(80, "==", caret, "Caret = 80.");
+
+		cmp_ok(0x0B, "==", bitpack_u16_unpack(pack, &caret, 4));
+		cmp_ok(0x0C, "==", bitpack_u16_unpack(pack, &caret, 4));
+		cmp_ok(0x0D, "==", bitpack_u16_unpack(pack, &caret, 4));
+		cmp_ok(0x0E, "==", bitpack_u16_unpack(pack, &caret, 4));
+
+		cmp_ok(96, "==", caret, "Caret = 96.");
+
+		for (int j = 0; j < 8; j++)
+			cmp_ok(0x07, "==", bitpack_u16_unpack(pack, &caret, 5),
+				   "width=5, val=00111b");
+
+		cmp_ok(136, "==", caret, "Caret = 136.");
+
+		for (int j = 0; j < 4; j++)
+			cmp_ok(0x07, "==", bitpack_u16_unpack(pack, &caret, 6),
+				   "width=6, val=000111b");
+
+		cmp_ok(160, "==", caret, "Caret = 160.");
+
+		for (int j = 0; j < 8; j++)
+			cmp_ok(0x57, "==", bitpack_u16_unpack(pack, &caret, 7),
+				   "width=7, val=1010111b (0x57)");
+
+		cmp_ok(216, "==", caret, "Caret = 216.");
+
+		cmp_ok(0xBA, "==", bitpack_u16_unpack(pack, &caret, 8),
+			   "width=8, val=0xBA");
+		cmp_ok(0xDC, "==", bitpack_u16_unpack(pack, &caret, 8),
+			   "width=8, val=0xDC");
+		cmp_ok(0xFE, "==", bitpack_u16_unpack(pack, &caret, 8),
+			   "width=8, val=0xFE");
+
+		cmp_ok(240, "==", caret, "Caret = 240.");
+
+		cmp_ok(0x0, "==", bitpack_u16_unpack(pack, &caret, 4),
+			   "width=4, val=0x0, for fun");
+
+		cmp_ok(0x32, "==", bitpack_u16_unpack(pack, &caret, 8),
+			   "width=8, val=0x32");
+		cmp_ok(0x54, "==", bitpack_u16_unpack(pack, &caret, 8),
+			   "width=8, val=0x54");
+		cmp_ok(0x76, "==", bitpack_u16_unpack(pack, &caret, 8),
+			   "width=8, val=0x76");
+		cmp_ok(0x98, "==", bitpack_u16_unpack(pack, &caret, 8),
+			   "width=8, val=0x98");
+
+		cmp_ok(0x0, "==", bitpack_u16_unpack(pack, &caret, 4),
+			   "width=4, val=0x0, for fun padding again");
+
+		for (int j = 0; j < 3; j++)
+			cmp_ok(0x1671, "==", bitpack_u16_unpack(pack, &caret, 13),
+				   "width=13, val=0x1671 (1011001110001b)");
+
+		cmp_ok(319, "==", caret, "Caret = 319.");
+
+		cmp_ok(0x1, "==", bitpack_u16_unpack(pack, &caret, 1),
+			   "width=1, val=0x1");
+		cmp_ok(320, "==", caret, "Caret = 320.");
+
+		for (int j = 0; j < 5; j++)
+			cmp_ok(0xCDEF, "==", bitpack_u16_unpack(pack, &caret, 16),
+				   "18-bit value alligned with bytes in pack.");
+
+		cmp_ok(400, "==", caret, "Caret = 400.");
+		cmp_ok(0x0, "==", bitpack_u16_unpack(pack, &caret, 1),
+			   "1-bit width value (padding for shift).");
+		cmp_ok(401, "==", caret, "Caret = 401.");
+
+		for (int j = 0; j < 3; j++)
+			cmp_ok(0x5555, "==", bitpack_u16_unpack(pack, &caret, 16),
+				   "16-bit width value shifted by 1 bit.");
+
+		cmp_ok(449, "==", caret, "Caret = 449.");
+	}
+
+	done_testing();
+}
diff --git a/src/test/dfor/test_uniqsortvect_u16.c b/src/test/dfor/test_uniqsortvect_u16.c
new file mode 100644
index 00000000000..4ddce8b0b3d
--- /dev/null
+++ b/src/test/dfor/test_uniqsortvect_u16.c
@@ -0,0 +1,263 @@
+/*
+ * test_uniqsortvect.c
+ */
+
+#include "lib/vect_u16.h"
+#include "libtap/tap.h"
+
+int test_usv(size_t src_cnt, uint16_t *src_in, size_t expected_cnt,
+			 uint16_t *expected_in);
+
+int
+test_usv(size_t src_cnt, uint16_t *src_in, size_t expected_cnt,
+		 uint16_t *expected_in)
+{
+	int result = -1;
+	vect_u16_t src;
+	vect_u16_t expected;
+	uniqsortvect_u16_t x;
+
+	vect_u16_init(&src, src_cnt, NULL);
+	vect_u16_fill(&src, src_cnt, src_in);
+
+	vect_u16_init(&expected, 0, NULL);
+	vect_u16_fill(&expected, expected_cnt, expected_in);
+
+	vect_u16_init(&x, 0, NULL);
+
+	for (size_t i = 0; i < src_cnt; i++)
+		usv_u16_insert(&x, src.m[i]);
+
+	result = vect_u16_compare(&x, &expected);
+
+	vect_u16_clear(&x);
+	vect_u16_clear(&expected);
+	vect_u16_clear(&src);
+	return result;
+}
+
+int
+main(void)
+{
+	plan(56);
+
+	printf("========================================\n");
+	printf("Test CREATE AND DESTROY SORTED UNIQUE VECTOR (EMPTY)\n");
+	{
+		uniqsortvect_u16_t usv;
+		size_t capacity = 10;
+		vect_u16_init(&usv, capacity, NULL);
+		cmp_ok(usv.cap, "==", capacity,
+			   "Vectors capacity is equal to requested one");
+		cmp_ok(usv.cnt, "==", 0, "No members in vector");
+		ok(usv.m != NULL, "Array for members is reserved");
+
+		vect_u16_clear(&usv);
+	}
+	printf("Test CREATE AND DESTROY SORTED UNIQUE VECTOR (EMPTY) PASSED\n");
+	printf("========================================\n\n");
+
+	printf("========================================\n");
+	printf("Test SEARCH IN UNIQUE SORT VECT\n");
+	{
+		uniqsortvect_u16_t usv;
+		usv_srch_res_t srch;
+		uint16_t input[4] = { 5, 10, 20, 30 };
+
+		vect_u16_init(&usv, 0, NULL);
+
+		for (size_t i = 0; i < 4; i++)
+			usv_u16_insert(&usv, input[i]);
+
+		srch = usv_u16_search(&usv, 0);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND_SMALLEST,
+			   "Not found, smallest");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		srch = usv_u16_search(&usv, 1);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND_SMALLEST,
+			   "Not found, smallest");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		srch = usv_u16_search(&usv, 5);
+		cmp_ok(srch.st, "==", USV_SRCH_FOUND, "Found");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		srch = usv_u16_search(&usv, 6);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND, "Not found");
+		cmp_ok(srch.pos, "==", 1, "Pos =1");
+
+		srch = usv_u16_search(&usv, 10);
+		cmp_ok(srch.st, "==", USV_SRCH_FOUND, "Found");
+		cmp_ok(srch.pos, "==", 1, "Pos =1");
+
+		srch = usv_u16_search(&usv, 15);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND, "Not found");
+		cmp_ok(srch.pos, "==", 2, "Pos =2");
+
+		srch = usv_u16_search(&usv, 20);
+		cmp_ok(srch.st, "==", USV_SRCH_FOUND, "Found");
+		cmp_ok(srch.pos, "==", 2, "Pos =2");
+
+		srch = usv_u16_search(&usv, 25);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND, "Not found");
+		cmp_ok(srch.pos, "==", 3, "Pos =3");
+
+		srch = usv_u16_search(&usv, 30);
+		cmp_ok(srch.st, "==", USV_SRCH_FOUND, "Found");
+		cmp_ok(srch.pos, "==", 3, "Pos =2");
+
+		srch = usv_u16_search(&usv, 45);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND_LARGEST, "Not found, largest");
+		cmp_ok(srch.pos, "==", 3, "Pos =3");
+
+		vect_u16_clear(&usv);
+	}
+	{
+		uniqsortvect_u16_t usv;
+		usv_srch_res_t srch;
+		uint16_t input[3] = { 5, 10, 20 };
+		uint16_t buf[3]; /* overindulge in testing the outer memory vector */
+
+		vect_u16_init(&usv, 3, buf);
+
+		for (size_t i = 0; i < 3; i++)
+			usv_u16_insert(&usv, input[i]);
+
+		srch = usv_u16_search(&usv, 0);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND_SMALLEST,
+			   "Not found, smallest");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		srch = usv_u16_search(&usv, 5);
+		cmp_ok(srch.st, "==", USV_SRCH_FOUND, "Found");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		srch = usv_u16_search(&usv, 6);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND, "Not found");
+		cmp_ok(srch.pos, "==", 1, "Pos =1");
+
+		srch = usv_u16_search(&usv, 10);
+		cmp_ok(srch.st, "==", USV_SRCH_FOUND, "Found");
+		cmp_ok(srch.pos, "==", 1, "Pos =1");
+
+		srch = usv_u16_search(&usv, 15);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND, "Not found");
+		cmp_ok(srch.pos, "==", 2, "Pos =2");
+
+		/*
+		 * When scopes l and g are neighbours (g-l=1) but
+		 * val==m[g] instead of val==m[l].
+		 */
+		srch = usv_u16_search(&usv, 20);
+		cmp_ok(srch.st, "==", USV_SRCH_FOUND, "Found");
+		cmp_ok(srch.pos, "==", 2, "Pos =2");
+
+		srch = usv_u16_search(&usv, 21);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND_LARGEST, "Not found, largest");
+		cmp_ok(srch.pos, "==", 2, "Pos =2");
+
+		vect_u16_clear(&usv);
+	}
+	{ /* single member*/
+
+		uniqsortvect_u16_t usv;
+		usv_srch_res_t srch;
+
+		vect_u16_init(&usv, 3, NULL);
+
+		usv_u16_insert(&usv, 5); /* The only item in list is 5 */
+
+		srch = usv_u16_search(&usv, 0);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND_SMALLEST,
+			   "Not found, smallest");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		srch = usv_u16_search(&usv, 5);
+		cmp_ok(srch.st, "==", USV_SRCH_FOUND, "Found");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		srch = usv_u16_search(&usv, 6);
+		cmp_ok(srch.st, "==", USV_SRCH_NOT_FOUND_LARGEST, "Not found, largest");
+		cmp_ok(srch.pos, "==", 0, "Pos =2");
+
+		vect_u16_clear(&usv);
+	}
+	{ /* empty vector*/
+
+		uniqsortvect_u16_t usv;
+		usv_srch_res_t srch;
+
+		vect_u16_init(&usv, 1, NULL);
+
+		srch = usv_u16_search(&usv, 0);
+		cmp_ok(srch.st, "==", USV_SRCH_EMPTY, "Vector is empty.");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		srch = usv_u16_search(&usv, 4);
+		cmp_ok(srch.st, "==", USV_SRCH_EMPTY, "Vector is empty.");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		srch = usv_u16_search(&usv, 5);
+		cmp_ok(srch.st, "==", USV_SRCH_EMPTY, "Vector is empty.");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		srch = usv_u16_search(&usv, 6);
+		cmp_ok(srch.st, "==", USV_SRCH_EMPTY, "Vector is empty.");
+		cmp_ok(srch.pos, "==", 0, "Pos =0");
+
+		vect_u16_clear(&usv);
+	}
+	printf("Test SEARCH IN UNIQUE SORT VECT PASSED.\n");
+	printf("========================================\n\n");
+
+	printf("========================================\n");
+	printf("Test ERROR ON NO VECTOR FOR SEARCHING\n");
+	{
+		usv_srch_res_t srch;
+		srch = usv_u16_search(NULL, 0);
+		cmp_ok(srch.st, "==", USV_SRCH_ERROR,
+			   "Error: no vector (empty pointer on vectror).");
+	}
+	{
+		uniqsortvect_u16_t usv;
+		usv_srch_res_t srch;
+		vect_u16_init(&usv, 0, NULL);
+
+		srch = usv_u16_search(&usv, 0);
+		cmp_ok(srch.st, "==", USV_SRCH_EMPTY,
+			   "Search in empty vector is not an error.");
+
+		vect_u16_clear(&usv);
+	}
+	printf("Test ERROR ON NO VECTOR FOR SEARCHING PASSED\n");
+	printf("========================================\n\n");
+
+	printf("========================================\n");
+	printf("Test CREATED VECTORS ARE SORTED AND UNIQUE\n");
+	{
+		cmp_ok(
+			0, "==",
+			test_usv(10, (uint16_t[]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, // src
+					 10,
+					 (uint16_t[]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }), // expected
+			"Unique sorted vector remains the same.");
+
+		cmp_ok(0, "==",
+			   test_usv(10,
+						(uint16_t[]) { 0, 1, 2, 3, 4, 5, 3, 7, 5, 9 }, // src
+						8, (uint16_t[]) { 0, 1, 2, 3, 4, 5, 7, 9 }), // expected
+			   "Duplicates are removed.");
+
+		cmp_ok(
+			0, "==",
+			test_usv(10, (uint16_t[]) { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, // src
+					 10,
+					 (uint16_t[]) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }), // expected
+			"Unsorted became sorted.");
+	}
+	printf("Test CREATED VECTORS ARE SORTED AND UNIQUE PASSED\n");
+	printf("========================================\n\n");
+
+	done_testing();
+}
diff --git a/src/test/dfor/test_vect_u16.c b/src/test/dfor/test_vect_u16.c
new file mode 100644
index 00000000000..00efe7dccbe
--- /dev/null
+++ b/src/test/dfor/test_vect_u16.c
@@ -0,0 +1,168 @@
+/*
+ * test_vect_u16.c
+ */
+
+#include "libtap/tap.h"
+#include "lib/vect_u16.h"
+
+int
+main(void)
+{
+	plan(35);
+
+	printf("========================================\n");
+	printf("Test INIT AND CLEAR VECTOR\n");
+	{
+		vect_u16_t v;
+		size_t capacity = 10;
+		cmp_ok(0, "==", vect_u16_init(&v, capacity, NULL),
+			   "Result of vect_init is 0");
+
+		cmp_ok(v.cap, "==", capacity,
+			   "Vectors capacity is equal to requested one");
+		cmp_ok(v.cnt, "==", 0, "No members in vector");
+		ok(v.m != NULL, "Array for members is reserved");
+		cmp_ok(v.mem_is_outer, "==", false, "Vector does not use outer memory");
+
+		vect_u16_clear(&v);
+
+		cmp_ok(v.cap, "==", 0, "Vectors capacity is 0 after cleanup");
+		cmp_ok(v.cnt, "==", 0, "No members in vector after cleanup");
+		ok(v.m == NULL, "Array for members is absent");
+		cmp_ok(v.mem_is_outer, "==", false, "Vector does not use outer memory");
+	}
+	printf("Test INIT AND CLEAR VECTOR PASSED\n");
+	printf("========================================\n\n");
+
+	printf("========================================\n");
+	printf("Test INIT AND FILL VECTOR\n");
+	{
+		vect_u16_t v;
+		size_t capacity = 10;
+		const uint16_t vals[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+		cmp_ok(0, "==", vect_u16_init(&v, capacity, NULL),
+			   "Result of vect_init is 0");
+		cmp_ok(0, "==", vect_u16_fill(&v, capacity, vals),
+			   "Result of vect_fill is 0");
+
+		cmp_ok(v.cap, "==", capacity,
+			   "Vectors capacity is equal to requested one");
+		ok(v.m != NULL, "Array for members is reserved");
+		cmp_ok(v.cnt, "==", capacity, "Members are in vector.");
+		{
+			int equal = 0;
+			for (size_t i = 0; i < capacity; i++) {
+				if (v.m[i] == i)
+					equal = equal + 1;
+				else
+					break;
+			}
+			cmp_ok(equal, "==", 10, "Members are correct");
+		}
+		vect_u16_clear(&v);
+	}
+	printf("Test INIT AND FILL VECTOR PASSED\n");
+	printf("========================================\n\n");
+
+	printf("========================================\n");
+	printf("Test INIT AND FILL VECTOR with zero capcaity\n");
+	{
+		vect_u16_t v;
+		size_t capacity = 0;
+
+		cmp_ok(0, "==", vect_u16_init(&v, capacity, NULL),
+			   "Result of vect_init is 0");
+		cmp_ok(0, "==", vect_u16_fill(&v, capacity, NULL),
+			   "Result of vect_fill is 0");
+
+		cmp_ok(v.cap, "==", 0, "Vector's capacity is zero");
+		ok(v.m == NULL,
+		   "Pointer to members is NULL (array for members is not reserved)");
+		ok(v.cnt == 0, "Counter of members is zero.");
+		vect_u16_clear(&v);
+	}
+	{
+		vect_u16_t v;
+		size_t capacity = 10;
+		const uint16_t vals[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+		cmp_ok(0, "==", vect_u16_init(&v, 0, NULL), "Result of vect_init is 0");
+		cmp_ok(0, "==", vect_u16_fill(&v, capacity, vals),
+			   "Result of vect_fill is 0");
+
+		cmp_ok(v.cap, "==", capacity,
+			   "Vector's capacity is not zero after filling");
+		ok(v.m != NULL,
+		   "Pointer to members is not NULL fater filling (array for members has been reserved)");
+		ok(v.cnt == capacity, "Counter of members is not zero after filling.");
+		vect_u16_clear(&v);
+	}
+	printf("Test INIT AND FILL VECTOR with zero capcaity is finished\n");
+	printf("========================================\n\n");
+
+	printf("========================================\n");
+	printf("Test COMPARE VECTORS\n");
+	{
+		vect_u16_t a, b, c, d;
+
+		uint16_t avals[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+		uint16_t bvals[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+		uint16_t cvals[] = { 1, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+		uint16_t dvals[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
+
+		vect_u16_init(&a, 0, NULL);
+		vect_u16_init(&b, 0, NULL);
+		vect_u16_init(&c, 0, NULL);
+		vect_u16_init(&d, 0, NULL);
+
+		vect_u16_fill(&a, 10, avals);
+		vect_u16_fill(&b, 10, bvals);
+		vect_u16_fill(&c, 10, cvals);
+		vect_u16_fill(&d, 9, dvals);
+
+		cmp_ok(0, "==", vect_u16_compare(&a, &b), "Vectors are equal");
+		cmp_ok(-1, "==", vect_u16_compare(&a, &c),
+			   "Vectors are not equal because of value of members");
+		cmp_ok(-1, "==", vect_u16_compare(&a, &d),
+			   "Vectors are not equal because of number of members");
+	}
+	printf("Test COMPARE VECTORS is finished. \n");
+	printf("========================================\n\n");
+
+	printf("========================================\n");
+	printf("Test VECTOR WITH OUTER MEMORY\n");
+	{
+#define VECT_CAP 100
+		vect_u16_t vect;
+		uint16_t buf[VECT_CAP]; /* uint16_t is the item's type */
+		vect_u16_init(&vect, VECT_CAP, buf);
+		cmp_ok(
+			vect.cap, "==", VECT_CAP,
+			"Initialisation of vector having external memory resulted in proper capacity.");
+		cmp_ok(
+			vect.cnt, "==", 0,
+			"Initialisation of vector having external memory resulted in proper number of items.");
+		ok(((void *)vect.m == (void *)buf),
+		   "Initialisation of vector having external memory set buf to vect->m.");
+		ok(vect.mem_is_outer,
+		   "Initialisation of vector having external memory set mem_is_outer flag.");
+
+		for (size_t i = 0; i < VECT_CAP; i++)
+		{
+			if (vect_u16_append(&vect, i) != 0)
+				fail(
+					"ERROR: New value can't be appended into vector having external memory.");
+		}
+		pass(
+			"All values have been appended into vector having external memory.");
+
+		cmp_ok(vect.cnt, "==", VECT_CAP, "Vector is full.");
+		cmp_ok(vect_u16_append(&vect, VECT_CAP), "==", -1,
+			   "Once vector is full, extra item can't be appended.");
+	}
+	printf("Test VECTOR WITH OUTER MEMORY is finished\n");
+	printf("========================================\n");
+
+	done_testing();
+}
diff --git a/src/test/libtap/.gitignore b/src/test/libtap/.gitignore
new file mode 100644
index 00000000000..2c95d046c7d
--- /dev/null
+++ b/src/test/libtap/.gitignore
@@ -0,0 +1,13 @@
+/t/*
+!/t/*.*
+/t/*.exe
+/t/*.got
+*.a
+*.lo
+*.o
+*.so
+*.pc
+usr/
+*.sw?
+/.deps
+/.dirstamp
diff --git a/src/test/libtap/.travis.yml b/src/test/libtap/.travis.yml
new file mode 100644
index 00000000000..6f9809e1b99
--- /dev/null
+++ b/src/test/libtap/.travis.yml
@@ -0,0 +1,13 @@
+language: c
+
+compiler:
+  - gcc
+  - clang
+
+before_install: sudo apt-get install -y libtest-differences-perl
+
+install: make CC=$CC install
+
+script: make CC=$CC test
+
+after_script: make uninstall
diff --git a/src/test/libtap/COPYING b/src/test/libtap/COPYING
new file mode 100644
index 00000000000..65c5ca88a67
--- /dev/null
+++ b/src/test/libtap/COPYING
@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/src/test/libtap/INSTALL b/src/test/libtap/INSTALL
new file mode 100644
index 00000000000..5b2c76df3d7
--- /dev/null
+++ b/src/test/libtap/INSTALL
@@ -0,0 +1,41 @@
+To install libtap on a Unix-like system:
+
+    $ make
+    $ make check
+    $ make install
+
+To compile with gcc -ansi, run:
+
+    $ ANSI=1 make
+
+To install to a different directory than /usr/local, supply the
+PREFIX variable to make:
+
+    $ PREFIX=/usr make install
+
+On Windows, the library can be created by first setting up the
+correct development environment variables. Usually this is done by
+running vcvars32.bat included in the Visual Studio distribution.
+You should also install gnu make which can be found at
+http://gnuwin32.sourceforge.net/packages/make.htm. Once this is
+done, you should be able to run the following:
+
+    > make -f Makefile.win
+
+If you want to use it directly in another project, you can copy tap.c
+and tap.h there and it shouldn't have a problem compiling.
+
+    $ ls
+    tap.c tap.h test.c
+    $ cat test.c
+    #include "tap.h"
+    int main () {
+        plan(1);
+        ok(50 + 5, "foo %s", "bar");
+        done_testing();
+    }
+    $ gcc test.c tap.c
+    $ a.out
+    1..1
+    ok 1 - foo bar
+
diff --git a/src/test/libtap/Makefile b/src/test/libtap/Makefile
new file mode 100644
index 00000000000..f020c2839a8
--- /dev/null
+++ b/src/test/libtap/Makefile
@@ -0,0 +1,73 @@
+CC ?= gcc
+CFLAGS += -Wall -I. -fPIC
+PREFIX ?= $(DESTDIR)/usr/local
+TESTS = $(patsubst %.c, %, $(wildcard t/*.c))
+
+ifdef ANSI
+	# -D_BSD_SOURCE for MAP_ANONYMOUS
+	CFLAGS += -ansi -D_BSD_SOURCE
+	LDLIBS += -lbsd-compat
+endif
+
+%:
+	$(CC) $(LDFLAGS) $(TARGET_ARCH) $(filter %.o %.a %.so, $^) $(LDLIBS) -o $@
+
+%.o:
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $(filter %.c, $^) $(LDLIBS) -o $@
+
+%.a:
+	$(AR) rcs $@ $(filter %.o, $^)
+
+%.so:
+	$(CC) -shared $(LDFLAGS) $(TARGET_ARCH) $(filter %.o, $^) $(LDLIBS) -o $@
+
+all: libtap.a libtap.so tap.pc tests
+
+tap.pc:
+	@echo generating tap.pc
+	@echo 'prefix='$(PREFIX) > tap.pc
+	@echo 'exec_prefix=$${prefix}' >> tap.pc
+	@echo 'libdir=$${prefix}/lib' >> tap.pc
+	@echo 'includedir=$${prefix}/include' >> tap.pc
+	@echo '' >> tap.pc
+	@echo 'Name: libtap' >> tap.pc
+	@echo 'Description: Write tests in C' >> tap.pc
+	@echo 'Version: 0.1.0' >> tap.pc
+	@echo 'URL: https://github.com/zorgnax/libtap' >> tap.pc
+	@echo 'Libs: -L$${libdir} -ltap' >> tap.pc
+	@echo 'Cflags: -I$${includedir}' >> tap.pc
+
+libtap.a: tap.o
+
+libtap.so: tap.o
+
+tap.o: tap.c tap.h
+
+tests: $(TESTS)
+
+$(TESTS): %: %.o libtap.a
+
+$(patsubst %, %.o, $(TESTS)): %.o: %.c tap.h
+	$(CC) $(CFLAGS) -O0 $(CPPFLAGS) $(TARGET_ARCH) -c $(filter %.c, $^) $(LDLIBS) -o $@
+
+clean:
+	rm -rf *.o t/*.o tap.pc libtap.a libtap.so $(TESTS)
+
+install: libtap.a tap.h libtap.so tap.pc
+	mkdir -p $(PREFIX)/lib $(PREFIX)/include $(PREFIX)/lib/pkgconfig
+	install -c libtap.a $(PREFIX)/lib
+	install -c libtap.so $(PREFIX)/lib
+	install -c tap.pc $(PREFIX)/lib/pkgconfig
+	install -c tap.h $(PREFIX)/include
+
+uninstall:
+	rm $(PREFIX)/lib/libtap.a $(PREFIX)/lib/libtap.so $(PREFIX)/include/tap.h
+
+dist:
+	rm libtap.zip
+	zip -r libtap *
+
+check test: all
+	./t/test
+
+.PHONY: all clean install uninstall dist check test tests
diff --git a/src/test/libtap/Makefile.win b/src/test/libtap/Makefile.win
new file mode 100644
index 00000000000..694d679a1b1
--- /dev/null
+++ b/src/test/libtap/Makefile.win
@@ -0,0 +1,37 @@
+CFLAGS = /Zi /Wall /wd4255 /wd4996 /wd4127 /wd4820 /wd4100 /wd4619 \
+		 /wd4514 /wd4668 /I.
+CC = cl /nologo
+TESTS = $(patsubst %.c, %.exe, $(wildcard t/*.c))
+
+%.exe:
+	$(CC) $(LDFLAGS) $(filter %.obj %.lib %.dll, $^) $(LDLIBS) /Fe $@
+
+%.o:
+	$(CC) $(CFLAGS) $(CPPFLAGS) /c $(filter %.c, $^) $(LDLIBS) /Fo $@
+
+%.lib:
+	lib /nologo /out:$@ $(filter %.obj, $^)
+
+%.dll:
+	lib /nologo /out:$@ $(filter %.obj, $^)
+
+all: tap.lib tests
+
+tap.lib: tap.obj
+
+tap.obj: tap.c tap.h
+
+tests: $(TESTS)
+
+$(TESTS): %.exe: %.obj tap.lib
+
+$(patsubst %.exe, %.obj, $(TESTS)): %.obj: %.c tap.h
+
+clean:
+	rm -rf *.obj t/*.obj tap.lib $(TESTS)
+
+check test: all
+	prove
+
+.PHONY: all clean check test tests
+
diff --git a/src/test/libtap/README.md b/src/test/libtap/README.md
new file mode 100644
index 00000000000..5332d526c08
--- /dev/null
+++ b/src/test/libtap/README.md
@@ -0,0 +1,268 @@
+NAME
+====
+
+libtap - Write tests in C
+
+SYNOPSIS
+========
+
+    #include <tap.h>
+
+    int main () {
+        plan(5);
+        int bronze = 1, silver = 2, gold = 3;
+        ok(bronze < silver, "bronze is less than silver");
+        ok(bronze > silver, "not quite");
+        is("gold", "gold", "gold is gold");
+        cmp_ok(silver, "<", gold, "%d <= %d", silver, gold);
+        like("platinum", ".*inum", "platinum matches .*inum");
+        done_testing();
+    }
+
+results in:
+
+    1..5
+    ok 1 - bronze is less than silver
+    not ok 2 - not quite
+    #   Failed test 'not quite'
+    #   at t/synopsis.c line 7.
+    ok 3 - gold is gold
+    ok 4 - 2 <= 3
+    ok 5 - platinum matches .*inum
+    # Looks like you failed 1 test of 5 run.
+
+DESCRIPTION
+===========
+
+tap is an easy to read and easy to write way of creating tests for
+your software. This library creates functions that can be used to
+generate it for your C programs. It is implemented using macros
+that include file and line info automatically, and makes it so that
+the format message of each test is optional. It is mostly based on
+the Test::More Perl module.
+
+INSTALL
+=======
+
+On **Unix** systems:
+
+    $ make
+    $ make install
+
+For more detailed installation instructions (eg, for **Windows**), see `INSTALL`.
+
+FUNCTIONS
+=========
+
+-   plan(tests)
+-   plan(NO_PLAN)
+-   plan(SKIP_ALL);
+-   plan(SKIP_ALL, fmt, ...)
+
+    Use this to start a series of tests. When you know how many tests there
+    will be, you can put a number as a number of tests you expect to run. If
+    you do not know how many tests there will be, you can use plan(NO_PLAN)
+    or not call this function. When you pass it a number of tests to run, a
+    message similar to the following will appear in the output:
+
+        1..5
+
+    If you pass it SKIP_ALL, the whole test will be skipped.
+
+-   ok(test)
+-   ok(test, fmt, ...)
+
+    Specify a test. the test can be any statement returning a true or false
+    value. You may optionally pass a format string describing the test.
+
+        ok(r = reader_new("Of Mice and Men"), "create a new reader");
+        ok(reader_go_to_page(r, 55), "can turn the page");
+        ok(r->page == 55, "page turned to the right one");
+
+    Should print out:
+
+        ok 1 - create a new reader
+        ok 2 - can turn the page
+        ok 3 - page turned to the right one
+
+    On failure, a diagnostic message will be printed out.
+
+        not ok 3 - page turned to the right one
+        #   Failed test 'page turned to the right one'
+        #   at reader.c line 13.
+
+-   is(got, expected)
+-   is(got, expected, fmt, ...)
+-   isnt(got, unexpected)
+-   isnt(got, unexpected, fmt, ...)
+
+    Tests that the string you got is what you expected. with isnt, it is the
+    reverse.
+
+        is("this", "that", "this is that");
+
+    prints:
+
+        not ok 1 - this is that
+        #   Failed test 'this is that'
+        #   at is.c line 6.
+        #          got: 'this'
+        #     expected: 'that'
+
+-   cmp_ok(a, op, b)
+-   cmp_ok(a, op, b, fmt, ...)
+
+    Compares two ints with any binary operator that doesn't require an lvalue.
+    This is nice to use since it provides a better error message than an
+    equivalent ok.
+
+        cmp_ok(420, ">", 666);
+
+    prints:
+
+        not ok 1
+        #   Failed test at cmpok.c line 5.
+        #     420
+        #         >
+        #     666
+
+-   cmp_mem(got, expected, n)
+-   cmp_mem(got, expected, n, fmt, ...)
+
+    Tests that the first n bytes of the memory you got is what you expected.
+    NULL pointers for got and expected are handled (if either is NULL,
+    the test fails), but you need to ensure n is not too large.
+
+        char *a = "foo";
+        char *b = "bar";
+        cmp_mem(a, b, 3)
+
+    prints
+
+        not ok 1
+        #   Failed test at t/cmp_mem.c line 9.
+        #     Difference starts at offset 0
+        #          got: 0x66
+        #     expected: 0x62
+
+-   like(got, expected)
+-   like(got, expected, fmt, ...)
+-   unlike(got, unexpected)
+-   unlike(got, unexpected, fmt, ...)
+
+    Tests that the string you got matches the expected extended POSIX regex.
+    unlike is the reverse. These macros are the equivalent of a skip on
+    Windows.
+
+        like("stranger", "^s.(r).*\\1$", "matches the regex");
+
+    prints:
+
+        ok 1 - matches the regex
+
+-   pass()
+-   pass(fmt, ...)
+-   fail()
+-   fail(fmt, ...)
+
+    Speciy that a test succeeded or failed. Use these when the statement is
+    longer than you can fit into the argument given to an ok() test.
+
+-   dies_ok(code)
+-   dies_ok(code, fmt, ...)
+-   lives_ok(code)
+-   lives_ok(code, fmt, ...)
+
+    Tests whether the given code causes your program to exit. The code gets
+    passed to a macro that will test it in a forked process. If the code
+    succeeds it will be executed in the parent process. You can test things
+    like passing a function a null pointer and make sure it doesnt
+    dereference it and crash.
+
+        dies_ok({abort();}, "abort does close your program");
+        dies_ok({int x = 0/0;}, "divide by zero crash");
+        lives_ok({pow(3.0, 5.0);}, "nothing wrong with taking 3**5");
+
+    On Windows, these macros are the equivalent of a skip.
+
+-   done_testing()
+
+    Summarizes the tests that occurred and exits the main function. If
+    there was no plan, it will print out the number of tests as.
+
+        1..5
+
+    It will also print a diagnostic message about how many
+    failures there were.
+
+        # Looks like you failed 2 tests of 3 run.
+
+    If all planned tests were successful, it will return 0. If any
+    test fails, it will return 1. If they all passed, but there
+    were missing tests, it will return 2.
+
+-   diag(fmt, ...)
+
+    print out a message to the tap output on stdout. Each line is
+    preceeded by a "# " so that you know its a diagnostic message.
+
+        diag("This is\na diag\nto describe\nsomething.");
+
+    prints:
+
+        # This is
+        # a diag
+        # to describe
+        # something
+
+    ok() and this function return an int so you can use it like:
+
+        ok(0) || diag("doh!");
+
+-   skip(test, n)
+-   skip(test, n, fmt, ...)
+-   end_skip
+
+    Skip a series of n tests if test is true. You may give a reason why you are
+    skipping them or not. The (possibly) skipped tests must occur between the
+    skip and end_skip macros.
+
+        skip(TRUE, 2);
+        ok(1);
+        ok(0);
+        end_skip;
+
+    prints:
+
+        ok 1 # skip
+        ok 2 # skip
+
+-   todo()
+-   todo(fmt, ...)
+-   end_todo
+
+    Specifies a series of tests that you expect to fail because they are not
+    yet implemented.
+
+        todo()
+        ok(0);
+        end_todo;
+
+    prints:
+
+        not ok 1 # TODO
+        #   Failed (TODO) test at todo.c line 7
+
+-   BAIL_OUT()
+-   BAIL_OUT(fmt, ...)
+
+    Immediately stops all testing.
+
+        BAIL_OUT("Can't go no further");
+
+    prints
+
+        Bail out!  Can't go no further
+
+    and exits with 255.
+
diff --git a/src/test/libtap/tap.c b/src/test/libtap/tap.c
new file mode 100644
index 00000000000..506e4000156
--- /dev/null
+++ b/src/test/libtap/tap.c
@@ -0,0 +1,421 @@
+/*
+libtap - Write tests in C
+Copyright 2012 Jake Gelbman <gelbman@gmail.com>
+This file is licensed under the LGPL
+*/
+
+#define _DEFAULT_SOURCE 1
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tap.h"
+
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <regex.h>
+
+#ifndef MAP_ANONYMOUS
+#ifdef MAP_ANON
+#define MAP_ANONYMOUS MAP_ANON
+#else
+#error "System does not support mapping anonymous pages"
+#endif
+#endif
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define PRINTF_ATTR(fmtarg, firstvararg) __attribute__((format(printf, fmtarg, firstvararg)))
+#else
+#define PRINTF_ATTR(fmtarg, firstvararg)
+#endif
+
+static int expected_tests = NO_PLAN;
+static int failed_tests;
+static int current_test;
+static char *todo_mesg;
+
+static char *vstrdupf(const char *fmt, va_list args) PRINTF_ATTR(1,0);
+
+void tap_plan(int tests, const char *fmt, ...) PRINTF_ATTR(2, 3);
+
+int vok_at_loc(const char *file, int line, int test, const char *fmt,
+			   va_list args) PRINTF_ATTR(4,0);
+
+int ok_at_loc(const char *file, int line, int test, const char *fmt, ...)
+	PRINTF_ATTR(4, 5);
+
+int is_at_loc(const char *file, int line, const char *got, const char *expected,
+			  const char *fmt, ...) PRINTF_ATTR(5, 6);
+
+int isnt_at_loc(const char *file, int line, const char *got,
+				const char *expected, const char *fmt, ...)
+	PRINTF_ATTR(5, 6);
+
+int cmp_ok_at_loc(const char *file, int line, int a, const char *op, int b,
+				  const char *fmt, ...)
+	PRINTF_ATTR(6, 7);
+
+int cmp_mem_at_loc(const char *file, int line, const void *got,
+				   const void *expected, size_t n, const char *fmt, ...)
+	PRINTF_ATTR(6, 7);
+
+int diag(const char *fmt, ...) PRINTF_ATTR(1, 2);
+
+int bail_out(int ignore, const char *fmt, ...)
+	PRINTF_ATTR(2, 3);
+
+void tap_skip(int n, const char *fmt, ...)
+	PRINTF_ATTR(2, 3);
+
+void tap_todo(int ignore, const char *fmt, ...)
+	PRINTF_ATTR(2, 3);
+
+int like_at_loc(int for_match, const char *file, int line, const char *got,
+				const char *expected, const char *fmt, ...)
+	PRINTF_ATTR(6, 7);
+
+static char *
+vstrdupf(const char *fmt, va_list args)
+{
+	char *str;
+	int size;
+	va_list args2;
+	va_copy(args2, args);
+	if (!fmt)
+		fmt = "";
+	size = vsnprintf(NULL, 0, fmt, args2) + 2;
+	str = malloc(size);
+	if (!str) {
+		perror("malloc error");
+		exit(1);
+	}
+	vsprintf(str, fmt, args);
+	va_end(args2);
+	return str;
+}
+
+void
+tap_plan(int tests, const char *fmt, ...)
+{
+	expected_tests = tests;
+	if (tests == SKIP_ALL) {
+		char *why;
+		va_list args;
+		va_start(args, fmt);
+		why = vstrdupf(fmt, args);
+		va_end(args);
+		printf("1..0 ");
+		diag("SKIP %s\n", why);
+		exit(0);
+	}
+	if (tests != NO_PLAN) {
+		printf("1..%d\n", tests);
+	}
+}
+
+int
+vok_at_loc(const char *file, int line, int test, const char *fmt, va_list args)
+{
+	char *name = vstrdupf(fmt, args);
+	if (!test) {
+		printf("not ");
+	}
+	printf("ok %d", ++current_test);
+	if (*name)
+		printf(" - %s", name);
+	if (todo_mesg) {
+		printf(" # TODO");
+		if (*todo_mesg)
+			printf(" %s", todo_mesg);
+	}
+	printf("\n");
+	if (!test) {
+		printf("#   Failed ");
+		if (todo_mesg)
+			printf("(TODO) ");
+		printf("test ");
+		if (*name)
+			printf("'%s'\n#   ", name);
+		printf("at %s line %d.\n", file, line);
+		if (!todo_mesg)
+			failed_tests++;
+	}
+	free(name);
+	return test;
+}
+
+int
+ok_at_loc(const char *file, int line, int test, const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	vok_at_loc(file, line, test, fmt, args);
+	va_end(args);
+	return test;
+}
+
+static int
+mystrcmp(const char *a, const char *b)
+{
+	return a == b ? 0 : !a ? -1 : !b ? 1 : strcmp(a, b);
+}
+
+#define eq(a, b) (!mystrcmp(a, b))
+#define ne(a, b) (mystrcmp(a, b))
+
+int
+is_at_loc(const char *file, int line, const char *got, const char *expected,
+		  const char *fmt, ...)
+{
+	int test = eq(got, expected);
+	va_list args;
+	va_start(args, fmt);
+	vok_at_loc(file, line, test, fmt, args);
+	va_end(args);
+	if (!test) {
+		diag("         got: '%s'", got);
+		diag("    expected: '%s'", expected);
+	}
+	return test;
+}
+
+int
+isnt_at_loc(const char *file, int line, const char *got, const char *expected,
+			const char *fmt, ...)
+{
+	int test = ne(got, expected);
+	va_list args;
+	va_start(args, fmt);
+	vok_at_loc(file, line, test, fmt, args);
+	va_end(args);
+	if (!test) {
+		diag("         got: '%s'", got);
+		diag("    expected: anything else");
+	}
+	return test;
+}
+
+int
+cmp_ok_at_loc(const char *file, int line, int a, const char *op, int b,
+			  const char *fmt, ...)
+{
+	int test = eq(op, "||") ? a || b :
+		eq(op, "&&")		? a && b :
+		eq(op, "|")			? a | b :
+		eq(op, "^")			? a ^ b :
+		eq(op, "&")			? a & b :
+		eq(op, "==")		? a == b :
+		eq(op, "!=")		? a != b :
+		eq(op, "<")			? a < b :
+		eq(op, ">")			? a > b :
+		eq(op, "<=")		? a <= b :
+		eq(op, ">=")		? a >= b :
+		eq(op, "<<")		? a << b :
+		eq(op, ">>")		? a >> b :
+		eq(op, "+")			? a + b :
+		eq(op, "-")			? a - b :
+		eq(op, "*")			? a * b :
+		eq(op, "/")			? a / b :
+		eq(op, "%")			? a % b :
+							  diag("unrecognized operator '%s'", op);
+	va_list args;
+	va_start(args, fmt);
+	vok_at_loc(file, line, test, fmt, args);
+	va_end(args);
+	if (!test) {
+		diag("    %d", a);
+		diag("        %s", op);
+		diag("    %d", b);
+	}
+	return test;
+}
+
+static int
+find_mem_diff(const char *a, const char *b, size_t n, size_t *offset)
+{
+	size_t i;
+	if (a == b)
+		return 0;
+	if (!a || !b)
+		return 2;
+	for (i = 0; i < n; i++) {
+		if (a[i] != b[i]) {
+			*offset = i;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+int
+cmp_mem_at_loc(const char *file, int line, const void *got,
+			   const void *expected, size_t n, const char *fmt, ...)
+{
+	size_t offset;
+	int diff = find_mem_diff(got, expected, n, &offset);
+	va_list args;
+	va_start(args, fmt);
+	vok_at_loc(file, line, !diff, fmt, args);
+	va_end(args);
+	if (diff == 1) {
+		diag("    Difference starts at offset %lu", offset);
+		diag("         got: 0x%02x", ((const unsigned char *)got)[offset]);
+		diag("    expected: 0x%02x", ((const unsigned char *)expected)[offset]);
+	} else if (diff == 2) {
+		diag("         got: %s", got ? "not NULL" : "NULL");
+		diag("    expected: %s", expected ? "not NULL" : "NULL");
+	}
+	return !diff;
+}
+
+int
+diag(const char *fmt, ...)
+{
+	va_list args;
+	char *mesg, *line;
+	int i;
+	va_start(args, fmt);
+	if (!fmt) {
+		va_end(args);
+		return 0;
+	}
+	mesg = vstrdupf(fmt, args);
+	line = mesg;
+	for (i = 0; *line; i++) {
+		char c = mesg[i];
+		if (!c || c == '\n') {
+			mesg[i] = '\0';
+			printf("# %s\n", line);
+			if (!c)
+				break;
+			mesg[i] = c;
+			line = mesg + i + 1;
+		}
+	}
+	free(mesg);
+	va_end(args);
+	return 0;
+}
+
+int
+exit_status(void)
+{
+	int retval = 0;
+	if (expected_tests == NO_PLAN) {
+		printf("1..%d\n", current_test);
+	} else if (current_test != expected_tests) {
+		diag("Looks like you planned %d test%s but ran %d.", expected_tests,
+			 expected_tests > 1 ? "s" : "", current_test);
+		retval = 2;
+	}
+	if (failed_tests) {
+		diag("Looks like you failed %d test%s of %d run.", failed_tests,
+			 failed_tests > 1 ? "s" : "", current_test);
+		retval = 1;
+	}
+	return retval;
+}
+
+int
+bail_out(int ignore, const char *fmt, ...)
+{
+	va_list args;
+	(void)ignore;
+	va_start(args, fmt);
+	printf("Bail out!  ");
+	vprintf(fmt, args);
+	printf("\n");
+	va_end(args);
+	exit(255);
+	return 0;
+}
+
+void
+tap_skip(int n, const char *fmt, ...)
+{
+	char *why;
+	va_list args;
+	va_start(args, fmt);
+	why = vstrdupf(fmt, args);
+	va_end(args);
+	while (n-- > 0) {
+		printf("ok %d ", ++current_test);
+		diag("skip %s\n", why);
+	}
+	free(why);
+}
+
+void
+tap_todo(int ignore, const char *fmt, ...)
+{
+	va_list args;
+	(void)ignore;
+	va_start(args, fmt);
+	todo_mesg = vstrdupf(fmt, args);
+	va_end(args);
+}
+
+void
+tap_end_todo(void)
+{
+	free(todo_mesg);
+	todo_mesg = NULL;
+}
+
+#ifndef _WIN32
+/* Create a shared memory int to keep track of whether a piece of code executed
+dies. to be used in the dies_ok and lives_ok macros.  */
+int
+tap_test_died(int status)
+{
+	static int *test_died = NULL;
+	int prev;
+	if (!test_died) {
+		test_died = mmap(0, sizeof(int), PROT_READ | PROT_WRITE,
+						 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+		*test_died = 0;
+	}
+	prev = *test_died;
+	*test_died = status;
+	return prev;
+}
+
+int
+like_at_loc(int for_match, const char *file, int line, const char *got,
+			const char *expected, const char *fmt, ...)
+{
+	int test;
+	regex_t re;
+	va_list args;
+	int err = regcomp(&re, expected, REG_EXTENDED);
+	if (err) {
+		char errbuf[256];
+		regerror(err, &re, errbuf, sizeof errbuf);
+		fprintf(stderr, "Unable to compile regex '%s': %s at %s line %d\n",
+				expected, errbuf, file, line);
+		exit(255);
+	}
+	err = regexec(&re, got, 0, NULL, 0);
+	regfree(&re);
+	test = for_match ? !err : err;
+	va_start(args, fmt);
+	vok_at_loc(file, line, test, fmt, args);
+	va_end(args);
+	if (!test) {
+		if (for_match) {
+			diag("                   '%s'", got);
+			diag("    doesn't match: '%s'", expected);
+		} else {
+			diag("                   '%s'", got);
+			diag("          matches: '%s'", expected);
+		}
+	}
+	return test;
+}
+#endif
diff --git a/src/test/libtap/tap.h b/src/test/libtap/tap.h
new file mode 100644
index 00000000000..e366a6affdc
--- /dev/null
+++ b/src/test/libtap/tap.h
@@ -0,0 +1,115 @@
+/*
+libtap - Write tests in C
+Copyright 2012 Jake Gelbman <gelbman@gmail.com>
+This file is licensed under the LGPL
+*/
+
+#ifndef __TAP_H__
+#define __TAP_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef va_copy
+#ifdef __va_copy
+#define va_copy __va_copy
+#else
+#define va_copy(d, s) ((d) = (s))
+#endif
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+int     vok_at_loc      (const char *file, int line, int test, const char *fmt,
+                         va_list args);
+int     ok_at_loc       (const char *file, int line, int test, const char *fmt,
+                         ...);
+int     is_at_loc       (const char *file, int line, const char *got,
+                         const char *expected, const char *fmt, ...);
+int     isnt_at_loc     (const char *file, int line, const char *got,
+                         const char *expected, const char *fmt, ...);
+int     cmp_ok_at_loc   (const char *file, int line, int a, const char *op,
+                         int b, const char *fmt, ...);
+int     cmp_mem_at_loc  (const char *file, int line, const void *got,
+                         const void *expected, size_t n, const char *fmt, ...);
+int     bail_out        (int ignore, const char *fmt, ...);
+void    tap_plan        (int tests, const char *fmt, ...);
+int     diag            (const char *fmt, ...);
+int     exit_status     (void);
+void    tap_skip        (int n, const char *fmt, ...);
+void    tap_todo        (int ignore, const char *fmt, ...);
+void    tap_end_todo    (void);
+
+#define NO_PLAN          -1
+#define SKIP_ALL         -2
+#define ok(...)          ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define is(...)          is_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define isnt(...)        isnt_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define cmp_ok(...)      cmp_ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define cmp_mem(...)     cmp_mem_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL)
+#define plan(...)        tap_plan(__VA_ARGS__, NULL)
+#define done_testing()   return exit_status()
+#define BAIL_OUT(...)    bail_out(0, "" __VA_ARGS__, NULL)
+#define pass(...)        ok(1, "" __VA_ARGS__)
+#define fail(...)        ok(0, "" __VA_ARGS__)
+
+#define skip(test, ...)  do {if (test) {tap_skip(__VA_ARGS__, NULL); break;}
+#define end_skip         } while (0)
+
+#define todo(...)        tap_todo(0, "" __VA_ARGS__, NULL)
+#define end_todo         tap_end_todo()
+
+#define dies_ok(...)     dies_ok_common(1, __VA_ARGS__)
+#define lives_ok(...)    dies_ok_common(0, __VA_ARGS__)
+
+#ifdef _WIN32
+#define like(...)        tap_skip(1, "like is not implemented on Windows")
+#define unlike(...)      tap_skip(1, "unlike is not implemented on Windows")
+#define dies_ok_common(...) \
+                         tap_skip(1, "Death detection is not supported on Windows")
+#else
+#define like(...)        like_at_loc(1, __FILE__, __LINE__, __VA_ARGS__, NULL)
+#define unlike(...)      like_at_loc(0, __FILE__, __LINE__, __VA_ARGS__, NULL)
+int     like_at_loc     (int for_match, const char *file, int line,
+                         const char *got, const char *expected,
+                         const char *fmt, ...);
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+int tap_test_died (int status);
+#define dies_ok_common(for_death, code, ...)                \
+    do {                                                    \
+        int cpid;                                           \
+        int it_died;                                        \
+        tap_test_died(1);                                   \
+        cpid = fork();                                      \
+        switch (cpid) {                                     \
+        case -1:                                            \
+            perror("fork error");                           \
+            exit(1);                                        \
+        case 0:                                             \
+            close(1);                                       \
+            close(2);                                       \
+            code                                            \
+            tap_test_died(0);                               \
+            exit(0);                                        \
+        }                                                   \
+        if (waitpid(cpid, NULL, 0) < 0) {                   \
+            perror("waitpid error");                        \
+            exit(1);                                        \
+        }                                                   \
+        it_died = tap_test_died(0);                         \
+        if (!it_died)                                       \
+            {code}                                          \
+        ok(for_death ? it_died : !it_died, "" __VA_ARGS__); \
+    } while (0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/test/meson.build b/src/test/meson.build
index cd45cbf57fb..64fa751a5a5 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -8,6 +8,7 @@ subdir('postmaster')
 subdir('recovery')
 subdir('subscription')
 subdir('modules')
+subdir('dfor')
 
 if ssl.found()
   subdir('ssl')
-- 
2.53.0

