summaryrefslogtreecommitdiffstats
path: root/ctdb/tests/UNIT/cunit
diff options
context:
space:
mode:
Diffstat (limited to 'ctdb/tests/UNIT/cunit')
-rwxr-xr-xctdb/tests/UNIT/cunit/cluster_mutex_001.sh66
-rwxr-xr-xctdb/tests/UNIT/cunit/cluster_mutex_002.sh132
-rwxr-xr-xctdb/tests/UNIT/cunit/cluster_mutex_003.sh75
-rwxr-xr-xctdb/tests/UNIT/cunit/cmdline_test_001.sh98
-rwxr-xr-xctdb/tests/UNIT/cunit/comm_test_001.sh13
-rwxr-xr-xctdb/tests/UNIT/cunit/comm_test_002.sh24
-rwxr-xr-xctdb/tests/UNIT/cunit/conf_test_001.sh196
-rwxr-xr-xctdb/tests/UNIT/cunit/config_test_001.sh115
-rwxr-xr-xctdb/tests/UNIT/cunit/config_test_002.sh65
-rwxr-xr-xctdb/tests/UNIT/cunit/config_test_003.sh52
-rwxr-xr-xctdb/tests/UNIT/cunit/config_test_004.sh144
-rwxr-xr-xctdb/tests/UNIT/cunit/config_test_005.sh97
-rwxr-xr-xctdb/tests/UNIT/cunit/config_test_006.sh56
-rwxr-xr-xctdb/tests/UNIT/cunit/config_test_007.sh24
-rwxr-xr-xctdb/tests/UNIT/cunit/ctdb_io_test_001.sh10
-rwxr-xr-xctdb/tests/UNIT/cunit/db_hash_test_001.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/event_protocol_test_001.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/event_script_test_001.sh127
-rwxr-xr-xctdb/tests/UNIT/cunit/hash_count_test_001.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/line_test_001.sh90
-rwxr-xr-xctdb/tests/UNIT/cunit/path_tests_001.sh62
-rwxr-xr-xctdb/tests/UNIT/cunit/pidfile_test_001.sh8
-rwxr-xr-xctdb/tests/UNIT/cunit/pkt_read_001.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/pkt_write_001.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/porting_tests_001.sh15
-rwxr-xr-xctdb/tests/UNIT/cunit/protocol_test_001.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/protocol_test_002.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/protocol_test_012.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/protocol_test_101.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/protocol_test_111.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/protocol_test_201.sh6
-rwxr-xr-xctdb/tests/UNIT/cunit/rb_test_001.sh31
-rwxr-xr-xctdb/tests/UNIT/cunit/reqid_test_001.sh13
-rwxr-xr-xctdb/tests/UNIT/cunit/run_event_001.sh137
-rwxr-xr-xctdb/tests/UNIT/cunit/run_proc_001.sh159
-rwxr-xr-xctdb/tests/UNIT/cunit/sock_daemon_test_001.sh135
-rwxr-xr-xctdb/tests/UNIT/cunit/sock_io_test_001.sh9
-rwxr-xr-xctdb/tests/UNIT/cunit/srvid_test_001.sh7
-rwxr-xr-xctdb/tests/UNIT/cunit/system_socket_test_001.sh6
-rwxr-xr-xctdb/tests/UNIT/cunit/system_socket_test_002.sh68
-rwxr-xr-xctdb/tests/UNIT/cunit/system_socket_test_003.sh42
-rwxr-xr-xctdb/tests/UNIT/cunit/tmon_test_001.sh195
-rwxr-xr-xctdb/tests/UNIT/cunit/tmon_test_002.sh142
-rwxr-xr-xctdb/tests/UNIT/cunit/tunable_test_001.sh312
44 files changed, 2801 insertions, 0 deletions
diff --git a/ctdb/tests/UNIT/cunit/cluster_mutex_001.sh b/ctdb/tests/UNIT/cunit/cluster_mutex_001.sh
new file mode 100755
index 0000000..7976143
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/cluster_mutex_001.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+# This tests the fcntl helper, configured via a lock file
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+t="${CTDB_SCRIPTS_HELPER_BINDIR}/ctdb_mutex_fcntl_helper"
+export CTDB_CLUSTER_MUTEX_HELPER="$t"
+
+lockfile="${CTDB_TEST_TMP_DIR}/cluster_mutex.lockfile"
+trap 'rm -f ${lockfile}' 0
+
+test_case "No contention: lock, unlock"
+ok <<EOF
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-unlock "$lockfile"
+
+test_case "Contention: lock, lock, unlock"
+ok <<EOF
+LOCK
+CONTENTION
+NOLOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-lock-unlock "$lockfile"
+
+test_case "No contention: lock, unlock, lock, unlock"
+ok <<EOF
+LOCK
+UNLOCK
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-unlock-lock-unlock "$lockfile"
+
+test_case "Cancelled: unlock while lock still in progress"
+ok <<EOF
+CANCEL
+NOLOCK
+EOF
+unit_test cluster_mutex_test lock-cancel-check "$lockfile"
+
+test_case "Cancelled: unlock while lock still in progress, unlock again"
+ok <<EOF
+CANCEL
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-cancel-unlock "$lockfile"
+
+test_case "PPID doesn't go away: lock, wait, unlock"
+ok <<EOF
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-wait-unlock "$lockfile"
+
+test_case "PPID goes away: lock, wait, lock, unlock"
+ok <<EOF
+LOCK
+parent gone
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-ppid-gone-lock-unlock "$lockfile"
diff --git a/ctdb/tests/UNIT/cunit/cluster_mutex_002.sh b/ctdb/tests/UNIT/cunit/cluster_mutex_002.sh
new file mode 100755
index 0000000..c672eaf
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/cluster_mutex_002.sh
@@ -0,0 +1,132 @@
+#!/bin/sh
+
+# This tests the fcntl helper, externally configured via !
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+export CTDB_CLUSTER_MUTEX_HELPER="/bin/false"
+
+lockfile="${CTDB_TEST_TMP_DIR}/cluster_mutex.lockfile"
+trap 'rm ${lockfile}' 0
+
+t="${CTDB_SCRIPTS_HELPER_BINDIR}/ctdb_mutex_fcntl_helper"
+helper="!${t} ${lockfile}"
+
+test_case "No contention: lock, unlock"
+ok <<EOF
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-unlock "$helper"
+
+test_case "Contention: lock, lock, unlock"
+ok <<EOF
+LOCK
+CONTENTION
+NOLOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-lock-unlock "$helper"
+
+test_case "No contention: lock, unlock, lock, unlock"
+ok <<EOF
+LOCK
+UNLOCK
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-unlock-lock-unlock "$helper"
+
+test_case "Cancelled: unlock while lock still in progress"
+ok <<EOF
+CANCEL
+NOLOCK
+EOF
+unit_test cluster_mutex_test lock-cancel-check "$helper"
+
+test_case "Cancelled: unlock while lock still in progress, unlock again"
+ok <<EOF
+CANCEL
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-cancel-unlock "$helper"
+
+test_case "PPID doesn't go away: lock, wait, unlock"
+ok <<EOF
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-wait-unlock "$helper"
+
+test_case "PPID goes away: lock, wait, lock, unlock"
+ok <<EOF
+LOCK
+parent gone
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-ppid-gone-lock-unlock "$helper"
+
+test_case "Recheck off, lock file removed"
+ok <<EOF
+LOCK
+LOCK
+UNLOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-file-removed-no-recheck \
+ "$helper 0" "$lockfile"
+
+test_case "Recheck on, lock file not removed"
+ok <<EOF
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-file-wait-recheck-unlock \
+ "$helper 5" 10
+
+test_case "Recheck on, lock file removed"
+ok <<EOF
+LOCK
+ctdb_mutex_fcntl_helper: lock lost - lock file "${lockfile}" open failed (ret=2)
+LOST
+EOF
+unit_test cluster_mutex_test lock-file-removed "$helper 5" "$lockfile"
+
+test_case "Recheck on, lock file replaced"
+ok <<EOF
+LOCK
+ctdb_mutex_fcntl_helper: lock lost - lock file "${lockfile}" inode changed
+LOST
+EOF
+unit_test cluster_mutex_test lock-file-changed "$helper 10" "$lockfile"
+
+test_case "Recheck on, ping on, child isn't blocked"
+ok <<EOF
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-io-timeout "$helper 5 7" "$lockfile" 0 0
+
+test_case "Recheck on, ping on, child waits, child isn't blocked"
+ok <<EOF
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-io-timeout "$helper 5 3" "$lockfile" 7 0
+
+test_case "Recheck on, ping on, child waits, child blocks for short time"
+ok <<EOF
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-io-timeout "$helper 5 7" "$lockfile" 1 2
+
+
+test_case "Recheck on, ping on, child waits, child blocks causing ping timeout"
+ok <<EOF
+LOCK
+ctdb_mutex_fcntl_helper: ping timeout from lock test child
+LOST
+EOF
+unit_test cluster_mutex_test lock-io-timeout "$helper 5 3" "$lockfile" 1 7
diff --git a/ctdb/tests/UNIT/cunit/cluster_mutex_003.sh b/ctdb/tests/UNIT/cunit/cluster_mutex_003.sh
new file mode 100755
index 0000000..57319bd
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/cluster_mutex_003.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+# This tests a helper, externally configured via !
+
+# By default this is the fcntl helper, so this is a subset of test 002.
+# However, other helps can be tested by setting CTDB_TEST_MUTEX_HELPER.
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+export CTDB_CLUSTER_MUTEX_HELPER="/bin/false"
+
+lockfile="${CTDB_TEST_TMP_DIR}/cluster_mutex.lockfile"
+trap 'rm ${lockfile}' 0
+
+if [ -n "$CTDB_TEST_MUTEX_HELPER" ] ; then
+ helper="$CTDB_TEST_MUTEX_HELPER"
+else
+ t="${CTDB_SCRIPTS_HELPER_BINDIR}/ctdb_mutex_fcntl_helper"
+ helper="!${t} ${lockfile}"
+fi
+
+test_case "No contention: lock, unlock"
+ok <<EOF
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-unlock "$helper"
+
+test_case "Contention: lock, lock, unlock"
+ok <<EOF
+LOCK
+CONTENTION
+NOLOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-lock-unlock "$helper"
+
+test_case "No contention: lock, unlock, lock, unlock"
+ok <<EOF
+LOCK
+UNLOCK
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-unlock-lock-unlock "$helper"
+
+test_case "Cancelled: unlock while lock still in progress"
+ok <<EOF
+CANCEL
+NOLOCK
+EOF
+unit_test cluster_mutex_test lock-cancel-check "$helper"
+
+test_case "Cancelled: unlock while lock still in progress, unlock again"
+ok <<EOF
+CANCEL
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-cancel-unlock "$helper"
+
+test_case "PPID doesn't go away: lock, wait, unlock"
+ok <<EOF
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-wait-unlock "$helper"
+
+test_case "PPID goes away: lock, wait, lock, unlock"
+ok <<EOF
+LOCK
+parent gone
+LOCK
+UNLOCK
+EOF
+unit_test cluster_mutex_test lock-ppid-gone-lock-unlock "$helper"
diff --git a/ctdb/tests/UNIT/cunit/cmdline_test_001.sh b/ctdb/tests/UNIT/cunit/cmdline_test_001.sh
new file mode 100755
index 0000000..e959000
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/cmdline_test_001.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+unit_test cmdline_test 1
+
+ok <<EOF
+Command 'nofunc' has no implementation function
+Command 'nohelp' has no help msg
+Command 'really really long command with lots of words' is too long (85)
+Command 'longhelp' help too long (90)
+EOF
+unit_test cmdline_test 2
+
+ok <<EOF
+Option has no long name
+Option 'debug' has unsupported type
+Option 'debug' has invalid arg
+EOF
+unit_test cmdline_test 3
+
+ok <<EOF
+Usage: test4 [<options>] <command> [<args>]
+
+Help Options:
+ -h, --help Show this help message
+
+Options:
+ -c, --count=INT Option help of length thirty.
+ -v, --value=Value help of length 23 Short description
+
+Commands:
+ A really really long command <a long arguments message> This is a really long help message
+ short command <short arg msg> short msg for short command
+Usage: test4 [-h] [-h|--help] [-c|--count=INT]
+ [-v|--value=Value help of length 23] <command> [<args>]
+
+ short command <short arg msg> short msg for short command
+EOF
+unit_test cmdline_test 4
+
+ok <<EOF
+Usage: test5 [<options>] <command> [<args>]
+
+Help Options:
+ -h, --help Show this help message
+
+Action Commands:
+ action one action one help
+ action two action two help
+Usage: test5 [<options>] <command> [<args>]
+
+Help Options:
+ -h, --help Show this help message
+
+Action Commands:
+ action one action one help
+ action two action two help
+Usage: test5 [<options>] <command> [<args>]
+
+Help Options:
+ -h, --help Show this help message
+
+Action Commands:
+ action one action one help
+ action two action two help
+EOF
+unit_test cmdline_test 5
+
+ok <<EOF
+arg1
+EOF
+unit_test cmdline_test 6
+
+ok <<EOF
+Usage: test7 [<options>] <command> [<args>]
+
+Help Options:
+ -h, --help Show this help message
+
+Basic Commands:
+ cmd1 command one help
+ cmd2 command two help
+
+Advanced Commands:
+ cmd3 command three help
+ cmd4 command four help
+
+Ultimate Commands:
+ cmd5 command five help
+ cmd6 command six help
+
+one
+three
+six
+EOF
+unit_test cmdline_test 7
diff --git a/ctdb/tests/UNIT/cunit/comm_test_001.sh b/ctdb/tests/UNIT/cunit/comm_test_001.sh
new file mode 100755
index 0000000..ac09f5c
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/comm_test_001.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+
+ok_null
+unit_test comm_test 1
+
+ok_null
+unit_test comm_test 2
+
+ok "100 2048 500 4096 1024 8192 200 16384 300 32768 400 65536 1048576 "
+unit_test comm_test 3
diff --git a/ctdb/tests/UNIT/cunit/comm_test_002.sh b/ctdb/tests/UNIT/cunit/comm_test_002.sh
new file mode 100755
index 0000000..a2fbf51
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/comm_test_002.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+socket="${CTDB_TEST_TMP_DIR}/test_sock.$$"
+num_clients=10
+
+remove_socket ()
+{
+ rm -f "$socket"
+}
+
+test_cleanup remove_socket
+
+ok_null
+
+unit_test comm_server_test "$socket" $num_clients &
+pid=$!
+
+for i in $(seq 1 $num_clients) ; do
+ unit_test comm_client_test "$socket"
+done
+
+wait $pid
diff --git a/ctdb/tests/UNIT/cunit/conf_test_001.sh b/ctdb/tests/UNIT/cunit/conf_test_001.sh
new file mode 100755
index 0000000..188964e
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/conf_test_001.sh
@@ -0,0 +1,196 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+conffile="${CTDB_TEST_TMP_DIR}/config.$$"
+
+remove_files ()
+{
+ rm -f "$conffile"
+}
+
+test_cleanup remove_files
+
+ok_null
+unit_test conf_test 1
+
+ok <<EOF
+conf: unknown section [section1]
+EOF
+unit_test conf_test 2
+
+ok <<EOF
+conf: option "key1" already exists
+EOF
+unit_test conf_test 3
+
+ok <<EOF
+conf: option "key1" already exists
+EOF
+unit_test conf_test 4
+
+ok_null
+unit_test conf_test 5
+
+ok <<EOF
+[section1]
+ key1 = foobar # temporary
+ key2 = 20 # temporary
+ key3 = false # temporary
+EOF
+unit_test conf_test 6
+
+ok <<EOF
+conf: validation for option "key1" failed
+conf: validation for option "key2" failed
+conf: validation for option "key3" failed
+EOF
+unit_test conf_test 7
+
+cat > "$conffile" <<EOF
+[section1]
+EOF
+
+required_error EINVAL <<EOF
+conf: validation for section [section1] failed
+[section1]
+ # key1 = default
+EOF
+unit_test conf_test 8 "$conffile"
+
+cat > "$conffile" <<EOF
+[section1]
+ key1 = unknown
+EOF
+
+required_error EINVAL <<EOF
+conf: validation for section [section1] failed
+[section1]
+ # key1 = default
+EOF
+unit_test conf_test 8 "$conffile"
+
+cat > "$conffile" <<EOF
+[section1]
+ key1 =
+EOF
+
+required_error EINVAL <<EOF
+conf: empty value [section1] -> "key1"
+[section1]
+ # key1 = value1
+ # key2 = 10
+ key3 = false # temporary
+EOF
+unit_test conf_test 9 "$conffile"
+
+cat > "$conffile" <<EOF
+[section1]
+ key3 =
+EOF
+
+required_error EINVAL <<EOF
+conf: empty value [section1] -> "key3"
+[section1]
+ # key1 = value1
+ # key2 = 10
+ key3 = false # temporary
+EOF
+unit_test conf_test 9 "$conffile"
+
+cat > "$conffile" <<EOF
+
+[section1]
+ key1 = value2
+ key2 = 20 # comment
+key3 = false
+EOF
+
+ok <<EOF
+[section1]
+ key1 = value2
+ key2 = 20
+ # key3 = true
+EOF
+unit_test conf_test 9 "$conffile"
+
+cat > "$conffile" <<EOF
+[section1]
+key1 = value2
+EOF
+
+ok <<EOF
+[section1]
+ key1 = value2
+ # key2 = 10
+ # key3 = true
+EOF
+unit_test conf_test 9 "$conffile"
+
+cat > "$conffile" <<EOF
+[section2]
+ foo = bar
+EOF
+
+required_error EINVAL <<EOF
+conf: unknown section [section2]
+conf: unknown section for option "foo"
+[section1]
+ # key1 = value1
+ # key2 = 10
+ key3 = false # temporary
+EOF
+unit_test conf_test 10 "$conffile"
+
+cat > "$conffile" <<EOF
+[section1]
+ key1 = value2
+ foo = bar
+ key2 = 20
+EOF
+
+required_error EINVAL <<EOF
+conf: unknown option [section1] -> "foo"
+[section1]
+ # key1 = value1
+ # key2 = 10
+ key3 = false # temporary
+EOF
+unit_test conf_test 10 "$conffile"
+
+cat > "$conffile" <<EOF
+[section1]
+ key1 = value2
+ key2 = 20
+ key3 = false
+EOF
+
+touch "${conffile}.reload"
+
+ok <<EOF
+[section1]
+ # key1 = value1
+ # key2 = 10
+ # key3 = true
+EOF
+unit_test conf_test 11 "$conffile"
+
+cat > "$conffile" <<EOF
+[section1]
+ key1 = value2
+ key2 = 20
+ key3 = false
+EOF
+
+cat > "${conffile}.reload" <<EOF
+[section1]
+ key1 = value3
+EOF
+
+ok <<EOF
+[section1]
+ key1 = value3
+ # key2 = 10
+ # key3 = true
+EOF
+unit_test conf_test 11 "$conffile"
diff --git a/ctdb/tests/UNIT/cunit/config_test_001.sh b/ctdb/tests/UNIT/cunit/config_test_001.sh
new file mode 100755
index 0000000..70bf77f
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/config_test_001.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+PATH="$PATH:$CTDB_SCRIPTS_TOOLS_HELPER_DIR"
+
+setup_ctdb_base "${CTDB_TEST_TMP_DIR}" "ctdb-etc"
+
+conffile="${CTDB_BASE}/ctdb.conf"
+
+remove_files ()
+{
+ rm -f "$conffile"
+}
+
+test_cleanup remove_files
+
+# Get the default values that are dependent on install prefix
+logging_location=$(ctdb-config get "logging" "location")
+database_volatile_dbdir=$(ctdb-config get \
+ "database" \
+ "volatile database directory")
+database_persistent_dbdir=$(ctdb-config get \
+ "database" \
+ "persistent database directory")
+database_state_dbdir=$(ctdb-config get \
+ "database" \
+ "state database directory")
+
+ok <<EOF
+[logging]
+ # location = ${logging_location}
+ # log level = NOTICE
+[cluster]
+ # transport = tcp
+ # node address =
+ # cluster lock =
+ # recovery lock =
+ # leader timeout = 5
+ # leader capability = true
+[database]
+ # volatile database directory = ${database_volatile_dbdir}
+ # persistent database directory = ${database_persistent_dbdir}
+ # state database directory = ${database_state_dbdir}
+ # lock debug script =
+ # tdb mutexes = true
+[event]
+ # debug script =
+[failover]
+ # disabled = false
+[legacy]
+ # realtime scheduling = true
+ # lmaster capability = true
+ # start as stopped = false
+ # start as disabled = false
+ # script log level = ERROR
+EOF
+unit_test ctdb-config dump
+
+required_result 2 <<EOF
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+EOF
+
+ok_null
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[foobar]
+EOF
+
+required_result 22 <<EOF
+conf: unknown section [foobar]
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+foobar = cat
+EOF
+
+required_result 22 <<EOF
+conf: unknown section for option "foobar"
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+required_result 2 <<EOF
+Configuration option [section] -> "key" not defined
+EOF
+unit_test ctdb-config get section key
+
+# Confirm that an unknown key doesn't stop the rest of the file from
+# loading
+cat > "$conffile" <<EOF
+[database]
+ unknown key = 123
+
+[logging]
+ log level = debug
+EOF
+
+required_error EINVAL <<EOF
+conf: unknown option [database] -> "unknown key"
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+ok <<EOF
+debug
+EOF
+unit_test ctdb-config get "logging" "log level"
diff --git a/ctdb/tests/UNIT/cunit/config_test_002.sh b/ctdb/tests/UNIT/cunit/config_test_002.sh
new file mode 100755
index 0000000..23b0863
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/config_test_002.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+PATH="$PATH:$CTDB_SCRIPTS_TOOLS_HELPER_DIR"
+
+setup_ctdb_base "${CTDB_TEST_TMP_DIR}" "ctdb-etc"
+
+conffile="${CTDB_BASE}/ctdb.conf"
+
+remove_files ()
+{
+ rm -f "$conffile"
+}
+
+test_cleanup remove_files
+
+cat > "$conffile" <<EOF
+EOF
+
+ok <<EOF
+NOTICE
+EOF
+unit_test ctdb-config get "logging" "log level"
+
+cat > "$conffile" <<EOF
+[logging]
+ location = syslog:magic
+EOF
+
+required_result 22 <<EOF
+conf: validation for option "location" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[logging]
+ log level = high
+EOF
+
+required_result 22 <<EOF
+conf: validation for option "log level" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[logging]
+ location = syslog
+ log level = notice
+EOF
+
+ok_null
+unit_test ctdb-config validate
+
+ok <<EOF
+syslog
+EOF
+unit_test ctdb-config get "logging" "location"
+
+ok <<EOF
+notice
+EOF
+unit_test ctdb-config get "logging" "log level"
diff --git a/ctdb/tests/UNIT/cunit/config_test_003.sh b/ctdb/tests/UNIT/cunit/config_test_003.sh
new file mode 100755
index 0000000..4e8d553
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/config_test_003.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+PATH="$PATH:$CTDB_SCRIPTS_TOOLS_HELPER_DIR"
+
+setup_ctdb_base "${CTDB_TEST_TMP_DIR}" "ctdb-etc"
+
+conffile="${CTDB_BASE}/ctdb.conf"
+scriptfile="${CTDB_BASE}/debug-hung-script.sh"
+
+remove_files ()
+{
+ rm -f "$conffile"
+}
+
+test_cleanup remove_files
+
+cat > "$conffile" <<EOF
+EOF
+
+ok <<EOF
+EOF
+unit_test ctdb-config get "event" "debug script"
+
+cat > "$conffile" <<EOF
+[event]
+ debug script = debug-hung-script.sh
+EOF
+
+touch "$scriptfile"
+
+required_result 22 <<EOF
+debug script $scriptfile is not executable
+conf: validation for option "debug script" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+chmod +x "$scriptfile"
+
+ok_null
+unit_test ctdb-config validate
+
+rm -f "$scriptfile"
+
+required_result 22 <<EOF
+debug script $scriptfile does not exist
+conf: validation for option "debug script" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
diff --git a/ctdb/tests/UNIT/cunit/config_test_004.sh b/ctdb/tests/UNIT/cunit/config_test_004.sh
new file mode 100755
index 0000000..ebbc05b
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/config_test_004.sh
@@ -0,0 +1,144 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+PATH="$PATH:$CTDB_SCRIPTS_TOOLS_HELPER_DIR"
+
+setup_ctdb_base "${CTDB_TEST_TMP_DIR}" "ctdb-etc"
+
+conffile="$CTDB_BASE/ctdb.conf"
+
+remove_files ()
+{
+ rm -f "$conffile"
+}
+
+test_cleanup remove_files
+
+cat > "$conffile" <<EOF
+EOF
+
+ok <<EOF
+tcp
+EOF
+unit_test ctdb-config get "cluster" "transport"
+
+ok <<EOF
+EOF
+unit_test ctdb-config get "cluster" "node address"
+
+ok <<EOF
+EOF
+unit_test ctdb-config get "cluster" "cluster lock"
+
+ok <<EOF
+5
+EOF
+unit_test ctdb-config get "cluster" "leader timeout"
+
+ok <<EOF
+true
+EOF
+unit_test ctdb-config get "cluster" "leader capability"
+
+cat > "$conffile" <<EOF
+[cluster]
+ transport = invalid
+EOF
+
+required_result 22 <<EOF
+Invalid value for [cluster] -> transport = invalid
+conf: validation for option "transport" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[cluster]
+ node address = 10.1.2.3
+EOF
+
+ok <<EOF
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[cluster]
+ node address = fc00:10:1:2::123
+EOF
+
+ok <<EOF
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[cluster]
+ node address = 10.1.2.3:123
+EOF
+
+required_result 22 <<EOF
+Invalid value for [cluster] -> node address = 10.1.2.3:123
+conf: validation for option "node address" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[cluster]
+ cluster lock = /foo/bar
+EOF
+
+required_result 0 <<EOF
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[cluster]
+ recovery lock = /foo/bar
+EOF
+
+required_result 0 <<EOF
+Configuration option [cluster] -> recovery lock is deprecated
+EOF
+unit_test ctdb-config -d WARNING validate
+
+cat > "$conffile" <<EOF
+[cluster]
+ leader timeout = 10
+EOF
+
+required_result 0 <<EOF
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[cluster]
+ leader timeout = 0
+EOF
+
+required_result 22 <<EOF
+Invalid value for [cluster] -> leader timeout = 0
+conf: validation for option "leader timeout" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[cluster]
+ leader timeout = -5
+EOF
+
+required_result 22 <<EOF
+conf: invalid value [cluster] -> "leader timeout" = "-5"
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+cat > "$conffile" <<EOF
+[cluster]
+ leader capability = false
+EOF
+
+required_result 0 <<EOF
+EOF
+unit_test ctdb-config validate
diff --git a/ctdb/tests/UNIT/cunit/config_test_005.sh b/ctdb/tests/UNIT/cunit/config_test_005.sh
new file mode 100755
index 0000000..c16a43f
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/config_test_005.sh
@@ -0,0 +1,97 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+PATH="$PATH:$CTDB_SCRIPTS_HELPER_BINDIR"
+
+setup_ctdb_base "${CTDB_TEST_TMP_DIR}" "ctdb-etc"
+
+conffile="${CTDB_BASE}/ctdb.conf"
+scriptfile="${CTDB_BASE}/debug_locks.sh"
+dbdir="${CTDB_BASE}/dbdir"
+dbdir_volatile="${dbdir}/volatile"
+dbdir_persistent="${dbdir}/persistent"
+dbdir_state="${dbdir}/state"
+
+remove_files ()
+{
+ rm -f "$conffile" "$scriptfile"
+}
+
+test_cleanup remove_files
+
+cat > "$conffile" <<EOF
+[database]
+ volatile database directory = ${dbdir_volatile}
+ persistent database directory = ${dbdir_persistent}
+ state database directory = ${dbdir_state}
+EOF
+
+required_result 22 <<EOF
+volatile database directory "${dbdir_volatile}" does not exist
+conf: validation for option "volatile database directory" failed
+persistent database directory "${dbdir_persistent}" does not exist
+conf: validation for option "persistent database directory" failed
+state database directory "${dbdir_state}" does not exist
+conf: validation for option "state database directory" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+mkdir -p "$dbdir_volatile"
+
+required_result 22 <<EOF
+persistent database directory "${dbdir_persistent}" does not exist
+conf: validation for option "persistent database directory" failed
+state database directory "${dbdir_state}" does not exist
+conf: validation for option "state database directory" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+mkdir -p "$dbdir_persistent"
+
+required_result 22 <<EOF
+state database directory "${dbdir_state}" does not exist
+conf: validation for option "state database directory" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+mkdir -p "$dbdir_state"
+
+required_result 0 <<EOF
+EOF
+unit_test ctdb-config validate
+
+ok <<EOF
+EOF
+unit_test ctdb-config get "database" "lock debug script"
+
+cat > "$conffile" <<EOF
+[database]
+ lock debug script = $scriptfile
+EOF
+
+touch "$scriptfile"
+
+required_result 22 <<EOF
+lock debug script $scriptfile is not executable
+conf: validation for option "lock debug script" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
+
+chmod +x "$scriptfile"
+
+ok_null
+unit_test ctdb-config validate
+
+rm -f "$scriptfile"
+
+required_result 22 <<EOF
+lock debug script $scriptfile does not exist
+conf: validation for option "lock debug script" failed
+Failed to load config file $conffile
+EOF
+unit_test ctdb-config validate
diff --git a/ctdb/tests/UNIT/cunit/config_test_006.sh b/ctdb/tests/UNIT/cunit/config_test_006.sh
new file mode 100755
index 0000000..622fb66
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/config_test_006.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+PATH="$PATH:$CTDB_SCRIPTS_HELPER_BINDIR"
+
+setup_ctdb_base "${CTDB_TEST_TMP_DIR}" "ctdb-etc"
+
+conffile="${CTDB_BASE}/ctdb.conf"
+
+remove_files ()
+{
+ rm -f "$conffile"
+}
+
+test_cleanup remove_files
+
+cat > "$conffile" <<EOF
+EOF
+
+ok <<EOF
+true
+EOF
+unit_test ctdb-config get "legacy" "realtime scheduling"
+
+ok <<EOF
+true
+EOF
+unit_test ctdb-config get "legacy" "lmaster capability"
+
+ok <<EOF
+false
+EOF
+unit_test ctdb-config get "legacy" "start as stopped"
+
+ok <<EOF
+false
+EOF
+unit_test ctdb-config get "legacy" "start as disabled"
+
+ok <<EOF
+ERROR
+EOF
+unit_test ctdb-config get "legacy" "script log level"
+
+cat > "$conffile" <<EOF
+[legacy]
+ script log level = INVALID
+EOF
+
+required_result 22 <<EOF
+Invalid value for [legacy] -> script log level = INVALID
+conf: validation for option "script log level" failed
+Failed to load config file ${conffile}
+EOF
+unit_test ctdb-config validate
diff --git a/ctdb/tests/UNIT/cunit/config_test_007.sh b/ctdb/tests/UNIT/cunit/config_test_007.sh
new file mode 100755
index 0000000..8804448
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/config_test_007.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+PATH="$PATH:$CTDB_SCRIPTS_HELPER_BINDIR"
+
+setup_ctdb_base "${CTDB_TEST_TMP_DIR}" "ctdb-etc"
+
+conffile="${CTDB_BASE}/ctdb.conf"
+
+remove_files ()
+{
+ rm -f "$conffile"
+}
+
+test_cleanup remove_files
+
+cat > "$conffile" <<EOF
+EOF
+
+ok <<EOF
+false
+EOF
+unit_test ctdb-config get "failover" "disabled"
diff --git a/ctdb/tests/UNIT/cunit/ctdb_io_test_001.sh b/ctdb/tests/UNIT/cunit/ctdb_io_test_001.sh
new file mode 100755
index 0000000..b6d3bce
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/ctdb_io_test_001.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test ctdb_io_test 1
+unit_test ctdb_io_test 2
+unit_test ctdb_io_test 3
+unit_test ctdb_io_test 4
diff --git a/ctdb/tests/UNIT/cunit/db_hash_test_001.sh b/ctdb/tests/UNIT/cunit/db_hash_test_001.sh
new file mode 100755
index 0000000..76c38fe
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/db_hash_test_001.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test db_hash_test
diff --git a/ctdb/tests/UNIT/cunit/event_protocol_test_001.sh b/ctdb/tests/UNIT/cunit/event_protocol_test_001.sh
new file mode 100755
index 0000000..8d5f932
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/event_protocol_test_001.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test event_protocol_test 1 100
diff --git a/ctdb/tests/UNIT/cunit/event_script_test_001.sh b/ctdb/tests/UNIT/cunit/event_script_test_001.sh
new file mode 100755
index 0000000..0d6a38e
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/event_script_test_001.sh
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+scriptdir="${CTDB_TEST_TMP_DIR}/scriptdir"
+mkdir -p "${scriptdir}"
+
+scriptdir=$(cd "$scriptdir" && echo "$PWD")
+
+test_cleanup "rm -rf ${scriptdir}"
+
+# Invalid path
+invalid="${scriptdir}/notfound"
+ok <<EOF
+Script list ${invalid} failed with result=$(errcode ENOENT)
+EOF
+unit_test event_script_test list "${invalid}"
+
+# Empty directory
+ok <<EOF
+No scripts found
+EOF
+unit_test event_script_test list "$scriptdir"
+
+# Invalid script, doesn't end in ".script"
+touch "${scriptdir}/prog"
+
+ok <<EOF
+No scripts found
+EOF
+unit_test event_script_test list "$scriptdir"
+
+# Is not found because enabling "prog" actually looks for "prog.script"
+ok <<EOF
+Script enable ${scriptdir} prog completed with result=$(errcode ENOENT)
+EOF
+unit_test event_script_test enable "$scriptdir" "prog"
+
+required_result 1 <<EOF
+EOF
+unit_test test -x "${scriptdir}/prog"
+
+# Is not found because enabling "prog" actually looks for "prog.script"
+ok <<EOF
+Script disable ${scriptdir} prog completed with result=$(errcode ENOENT)
+EOF
+unit_test event_script_test disable "$scriptdir" "prog"
+
+# Valid script
+touch "$scriptdir/11.foo.script"
+
+ok <<EOF
+11.foo
+EOF
+unit_test event_script_test list "$scriptdir"
+
+ok <<EOF
+Script enable ${scriptdir} 11.foo completed with result=0
+EOF
+unit_test event_script_test enable "$scriptdir" "11.foo"
+
+ok <<EOF
+EOF
+unit_test test -x "${scriptdir}/11.foo.script"
+
+ok <<EOF
+Script disable ${scriptdir} 11.foo.script completed with result=0
+EOF
+unit_test event_script_test disable "$scriptdir" "11.foo.script"
+
+required_result 1 <<EOF
+EOF
+unit_test test -x "${scriptdir}/11.foo.script"
+
+# Multiple scripts
+touch "${scriptdir}/22.bar.script"
+
+ok <<EOF
+11.foo
+22.bar
+EOF
+unit_test event_script_test list "$scriptdir"
+
+# Symlink to existing file
+ln -s "${scriptdir}/prog" "${scriptdir}/33.link.script"
+
+ok <<EOF
+11.foo
+22.bar
+33.link
+EOF
+unit_test event_script_test list "$scriptdir"
+
+ok <<EOF
+Script enable ${scriptdir} 33.link completed with result=$(errcode EINVAL)
+EOF
+unit_test event_script_test enable "$scriptdir" "33.link"
+
+
+ok <<EOF
+Script disable ${scriptdir} 33.link.script completed with result=$(errcode EINVAL)
+EOF
+unit_test event_script_test disable "$scriptdir" "33.link.script"
+
+# Dangling symlink
+rm "${scriptdir}/33.link.script"
+ln -s "${scriptdir}/nosuchfile" "${scriptdir}/33.link.script"
+
+ok <<EOF
+11.foo
+22.bar
+33.link
+EOF
+unit_test event_script_test list "$scriptdir"
+
+ok <<EOF
+Script enable ${scriptdir} 33.link completed with result=$(errcode ENOENT)
+EOF
+unit_test event_script_test enable "$scriptdir" "33.link"
+
+
+ok <<EOF
+Script disable ${scriptdir} 33.link.script completed with result=$(errcode ENOENT)
+EOF
+unit_test event_script_test disable "$scriptdir" "33.link.script"
+
+exit 0
diff --git a/ctdb/tests/UNIT/cunit/hash_count_test_001.sh b/ctdb/tests/UNIT/cunit/hash_count_test_001.sh
new file mode 100755
index 0000000..3958706
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/hash_count_test_001.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test hash_count_test
diff --git a/ctdb/tests/UNIT/cunit/line_test_001.sh b/ctdb/tests/UNIT/cunit/line_test_001.sh
new file mode 100755
index 0000000..5676893
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/line_test_001.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+tfile="${CTDB_TEST_TMP_DIR}/line.$$"
+
+remove_files ()
+{
+ rm -f "$tfile"
+}
+
+test_cleanup remove_files
+
+> "$tfile"
+
+ok_null
+unit_test line_test "$tfile"
+
+printf "\0" > "$tfile"
+
+required_result 1 <<EOF
+
+EOF
+
+unit_test line_test "$tfile"
+
+echo -n "hello" > "$tfile"
+
+ok_null
+unit_test line_test "$tfile"
+
+cat <<EOF > "$tfile"
+hello
+world
+EOF
+
+required_result 2 << EOF
+hello
+world
+EOF
+unit_test line_test "$tfile"
+
+required_result 2 << EOF
+hello
+world
+EOF
+unit_test line_test "$tfile"
+
+cat <<EOF > "$tfile"
+This is a really long long line full of random words and hopefully it will be read properly by the line test program and identified as a single line
+EOF
+
+required_result 1 <<EOF
+This is a really long long line full of random words and hopefully it will be read properly by the line test program and identified as a single line
+EOF
+unit_test line_test "$tfile"
+
+cat <<EOF > "$tfile"
+line number one
+line number two
+line number one
+line number two
+line number one
+EOF
+
+required_result 5 <<EOF
+line number one
+line number two
+line number one
+line number two
+line number one
+EOF
+unit_test line_test "$tfile" 64
+
+cat <<EOF > "$tfile"
+this is line number one
+this is line number two
+this is line number three
+this is line number four
+this is line number five
+EOF
+
+required_result 5 <<EOF
+this is line number one
+this is line number two
+this is line number three
+this is line number four
+this is line number five
+EOF
+unit_test line_test "$tfile" 64
diff --git a/ctdb/tests/UNIT/cunit/path_tests_001.sh b/ctdb/tests/UNIT/cunit/path_tests_001.sh
new file mode 100755
index 0000000..5713fc8
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/path_tests_001.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+PATH="$PATH:$CTDB_SCRIPTS_TOOLS_HELPER_DIR"
+
+setup_ctdb_base "${CTDB_TEST_TMP_DIR}" "ctdb-etc"
+
+ok <<EOF
+$CTDB_BASE/ctdb.conf
+EOF
+unit_test ctdb-path config
+
+ok <<EOF
+$CTDB_BASE/run/foobar.pid
+EOF
+unit_test ctdb-path pidfile foobar
+
+ok <<EOF
+$CTDB_BASE/run/foobar.socket
+EOF
+unit_test ctdb-path socket foobar
+
+ok <<EOF
+$CTDB_BASE/share
+EOF
+unit_test ctdb-path datadir
+
+ok <<EOF
+$CTDB_BASE
+EOF
+unit_test ctdb-path etcdir
+
+ok <<EOF
+$CTDB_BASE/run
+EOF
+unit_test ctdb-path rundir
+
+ok <<EOF
+$CTDB_BASE/var
+EOF
+unit_test ctdb-path vardir
+
+ok <<EOF
+$CTDB_BASE/share/foobar
+EOF
+unit_test ctdb-path datadir append foobar
+
+ok <<EOF
+$CTDB_BASE/foobar
+EOF
+unit_test ctdb-path etcdir append foobar
+
+ok <<EOF
+$CTDB_BASE/run/foobar
+EOF
+unit_test ctdb-path rundir append foobar
+
+ok <<EOF
+$CTDB_BASE/var/foobar
+EOF
+unit_test ctdb-path vardir append foobar
diff --git a/ctdb/tests/UNIT/cunit/pidfile_test_001.sh b/ctdb/tests/UNIT/cunit/pidfile_test_001.sh
new file mode 100755
index 0000000..cf48403
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/pidfile_test_001.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+pidfile=$(TMPDIR="$CTDB_TEST_TMP_DIR" mktemp)
+
+ok_null
+unit_test pidfile_test $pidfile
diff --git a/ctdb/tests/UNIT/cunit/pkt_read_001.sh b/ctdb/tests/UNIT/cunit/pkt_read_001.sh
new file mode 100755
index 0000000..c951f39
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/pkt_read_001.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test pkt_read_test
diff --git a/ctdb/tests/UNIT/cunit/pkt_write_001.sh b/ctdb/tests/UNIT/cunit/pkt_write_001.sh
new file mode 100755
index 0000000..131af05
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/pkt_write_001.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test pkt_write_test
diff --git a/ctdb/tests/UNIT/cunit/porting_tests_001.sh b/ctdb/tests/UNIT/cunit/porting_tests_001.sh
new file mode 100755
index 0000000..bdb7fc5
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/porting_tests_001.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+socket="${CTDB_TEST_TMP_DIR}/test_sock.$$"
+
+remove_socket ()
+{
+ rm -f "$socket"
+}
+
+test_cleanup remove_socket
+
+ok_null
+unit_test porting_tests --socket="$socket"
diff --git a/ctdb/tests/UNIT/cunit/protocol_test_001.sh b/ctdb/tests/UNIT/cunit/protocol_test_001.sh
new file mode 100755
index 0000000..7f68c48
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/protocol_test_001.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test protocol_basic_test 1 1000
diff --git a/ctdb/tests/UNIT/cunit/protocol_test_002.sh b/ctdb/tests/UNIT/cunit/protocol_test_002.sh
new file mode 100755
index 0000000..51e0513
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/protocol_test_002.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test protocol_types_test 1 1000
diff --git a/ctdb/tests/UNIT/cunit/protocol_test_012.sh b/ctdb/tests/UNIT/cunit/protocol_test_012.sh
new file mode 100755
index 0000000..b9fd492
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/protocol_test_012.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test protocol_types_compat_test 1 1000
diff --git a/ctdb/tests/UNIT/cunit/protocol_test_101.sh b/ctdb/tests/UNIT/cunit/protocol_test_101.sh
new file mode 100755
index 0000000..f944c6b
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/protocol_test_101.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test protocol_ctdb_test 1 100
diff --git a/ctdb/tests/UNIT/cunit/protocol_test_111.sh b/ctdb/tests/UNIT/cunit/protocol_test_111.sh
new file mode 100755
index 0000000..28d190c
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/protocol_test_111.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test protocol_ctdb_compat_test 1 100
diff --git a/ctdb/tests/UNIT/cunit/protocol_test_201.sh b/ctdb/tests/UNIT/cunit/protocol_test_201.sh
new file mode 100755
index 0000000..012db90
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/protocol_test_201.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+unit_test protocol_util_test
diff --git a/ctdb/tests/UNIT/cunit/rb_test_001.sh b/ctdb/tests/UNIT/cunit/rb_test_001.sh
new file mode 100755
index 0000000..25d3ceb
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/rb_test_001.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+output="\
+testing trbt_insertarray32_callback
+traverse data:3
+traverse data:2
+traverse data:1
+
+deleting key4
+traverse data:3
+traverse data:2
+traverse data:1
+
+deleting key2
+traverse data:3
+traverse data:1
+
+deleting key3
+traverse data:3
+
+deleting key1
+
+run random insert and delete for 60 seconds
+
+deleting all entries"
+
+ok "$output"
+
+unit_test rb_test
diff --git a/ctdb/tests/UNIT/cunit/reqid_test_001.sh b/ctdb/tests/UNIT/cunit/reqid_test_001.sh
new file mode 100755
index 0000000..06259ba
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/reqid_test_001.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+output=$(
+for i in $(seq 0 1023) ; do
+ echo "WARNING: attempt to remove unset id $i in idtree"
+done
+)
+
+ok "$output"
+
+unit_test reqid_test
diff --git a/ctdb/tests/UNIT/cunit/run_event_001.sh b/ctdb/tests/UNIT/cunit/run_event_001.sh
new file mode 100755
index 0000000..4df3b4b
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/run_event_001.sh
@@ -0,0 +1,137 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+# Invalid path
+required_result 1 <<EOF
+run_event_init() failed, ret=2
+EOF
+unit_test run_event_test /a/b/c list
+
+scriptdir=$(TMPDIR="$CTDB_TEST_TMP_DIR" mktemp -d)
+
+# Empty directory
+ok <<EOF
+No event scripts found
+EOF
+unit_test run_event_test "$scriptdir" list
+
+cat > "$scriptdir/prog" <<EOF
+#!/bin/sh
+
+echo hello
+EOF
+
+# Invalid script, doesn't end in ".script"
+ok <<EOF
+No event scripts found
+EOF
+unit_test run_event_test "$scriptdir" list
+
+# Is not found because enabling "prog" actually looks for "prog.script"
+ok <<EOF
+Script enable prog completed with result=2
+EOF
+unit_test run_event_test "$scriptdir" enable prog
+
+required_result 1 <<EOF
+EOF
+unit_test test -x "${scriptdir}/prog"
+
+cat > "$scriptdir/11.foo.script" <<EOF
+#!/bin/sh
+
+echo hello
+EOF
+
+# Valid script
+ok <<EOF
+11.foo
+EOF
+unit_test run_event_test "$scriptdir" list
+
+ok <<EOF
+Script enable 11.foo completed with result=0
+EOF
+unit_test run_event_test "$scriptdir" enable 11.foo
+
+ok <<EOF
+EOF
+unit_test test -x "${scriptdir}/11.foo.script"
+
+ok <<EOF
+11.foo: hello
+Event monitor completed with result=0
+11.foo result=0
+EOF
+unit_test run_event_test "$scriptdir" run 10 monitor
+
+cat > "$scriptdir/22.bar.script" <<EOF
+#!/bin/sh
+
+exit 1
+EOF
+
+# Multiple scripts
+ok <<EOF
+11.foo
+22.bar
+EOF
+unit_test run_event_test "$scriptdir" list
+
+ok <<EOF
+Script enable 22.bar completed with result=0
+EOF
+unit_test run_event_test "$scriptdir" enable 22.bar
+
+ok <<EOF
+11.foo: hello
+Event monitor completed with result=1
+11.foo result=0
+22.bar result=1
+EOF
+unit_test run_event_test "$scriptdir" run 10 monitor
+
+# Disable script
+ok <<EOF
+Script disable 22.bar completed with result=0
+EOF
+unit_test run_event_test "$scriptdir" disable 22.bar
+
+required_result 1 <<EOF
+EOF
+unit_test test -x "${scriptdir}/22.bar.script"
+
+ok <<EOF
+11.foo: hello
+Event monitor completed with result=0
+11.foo result=0
+22.bar result=-$(errcode ENOEXEC)
+EOF
+unit_test run_event_test "$scriptdir" run 10 monitor
+
+cat > "$scriptdir/22.bar.script" <<EOF
+#!/bin/sh
+
+echo before sleep
+sleep 10
+echo after sleep
+EOF
+
+# Timed out script
+ok <<EOF
+Script enable 22.bar completed with result=0
+EOF
+unit_test run_event_test "$scriptdir" enable 22.bar
+
+ok <<EOF
+11.foo: hello
+22.bar: before sleep
+Event monitor completed with result=-$(errcode ETIMEDOUT)
+11.foo result=0
+22.bar result=-$(errcode ETIMEDOUT)
+EOF
+unit_test run_event_test "$scriptdir" run 5 monitor
+
+rm -rf "$scriptdir"
+exit 0
diff --git a/ctdb/tests/UNIT/cunit/run_proc_001.sh b/ctdb/tests/UNIT/cunit/run_proc_001.sh
new file mode 100755
index 0000000..3f48885
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/run_proc_001.sh
@@ -0,0 +1,159 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+# Invalid path
+ok <<EOF
+Process exited with error $(errcode ENOENT)
+EOF
+unit_test run_proc_test 0 -1 /a/b/c
+
+# Non-executable path
+prog=$(TMPDIR="$CTDB_TEST_TMP_DIR" mktemp)
+cat > "$prog" <<EOF
+echo hello
+EOF
+
+ok <<EOF
+Process exited with error $(errcode EACCES)
+EOF
+unit_test run_proc_test 0 -1 "$prog"
+
+# Executable path
+chmod +x "$prog"
+
+ok <<EOF
+Process exited with error $(errcode ENOEXEC)
+EOF
+unit_test run_proc_test 0 -1 "$prog"
+
+# Capture output
+cat > "$prog" <<EOF
+#!/bin/sh
+echo hello
+EOF
+
+ok <<EOF
+Process exited with status 0
+Output = (hello
+)
+EOF
+unit_test run_proc_test 0 -1 "$prog"
+
+# Specify timeout
+ok <<EOF
+Process exited with status 0
+Output = (hello
+)
+EOF
+unit_test run_proc_test 5 -1 "$prog"
+
+# Redirected output
+output=$(TMPDIR="$CTDB_TEST_TMP_DIR" mktemp)
+cat > "$prog" <<EOF
+#!/bin/sh
+exec >"$output" 2>&1
+echo hello
+EOF
+
+ok <<EOF
+Process exited with status 0
+EOF
+unit_test run_proc_test 0 -1 "$prog"
+
+ok <<EOF
+hello
+EOF
+unit_test cat "$output"
+
+# Exit with error
+cat > "$prog" <<EOF
+#!/bin/sh
+exit 1
+EOF
+
+ok <<EOF
+Process exited with status 1
+EOF
+unit_test run_proc_test 0 -1 "$prog"
+
+# Exit with signal
+cat > "$prog" <<EOF
+#!/bin/sh
+kill \$$
+EOF
+
+ok <<EOF
+Process exited with signal 15
+EOF
+unit_test run_proc_test 0 -1 "$prog"
+
+# Exit with timeout
+cat > "$prog" <<EOF
+#!/bin/sh
+echo "Sleeping for 5 seconds"
+sleep 5
+EOF
+
+result_filter ()
+{
+ _pid="[0-9][0-9]*"
+ sed -e "s|= ${_pid}|= PID|"
+}
+
+ok <<EOF
+Process exited with error $(errcode ETIMEDOUT)
+Child = PID
+Output = (Sleeping for 5 seconds
+)
+EOF
+unit_test run_proc_test 1 -1 "$prog"
+
+# No zombie processes
+pidfile=$(TMPDIR="$CTDB_TEST_TMP_DIR" mktemp)
+
+cat > "$prog" <<EOF
+#!/bin/sh
+echo \$$ > "$pidfile"
+sleep 10
+EOF
+
+ok <<EOF
+Process exited with error $(errcode ETIMEDOUT)
+Child = PID
+EOF
+unit_test run_proc_test 1 -1 "$prog"
+
+result_filter ()
+{
+ _header=" *PID *TTY *TIME *CMD"
+ _header2=" *PID *TT *STAT *TIME *COMMAND"
+ sed -e "s|^${_header}|HEADER|" -e "s|^${_header2}|HEADER|"
+}
+
+pid=$(cat "$pidfile")
+required_result 1 <<EOF
+HEADER
+EOF
+unit_test ps -p "$pid"
+
+# Redirect stdin
+cat > "$prog" <<EOF
+#!/bin/sh
+cat -
+EOF
+
+cat > "$output" <<EOF
+this is sample input
+EOF
+
+ok <<EOF
+Process exited with status 0
+Output = (this is sample input
+)
+EOF
+(unit_test run_proc_test 0 4 "$prog") 4<"$output"
+
+rm -f "$pidfile"
+rm -f "$output"
+rm -f "$prog"
diff --git a/ctdb/tests/UNIT/cunit/sock_daemon_test_001.sh b/ctdb/tests/UNIT/cunit/sock_daemon_test_001.sh
new file mode 100755
index 0000000..6f360f7
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/sock_daemon_test_001.sh
@@ -0,0 +1,135 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+pidfile="${CTDB_TEST_TMP_DIR}/sock_daemon_test.pid.$$"
+sockpath="${CTDB_TEST_TMP_DIR}/sock_daemon_test.sock.$$"
+
+remove_files ()
+{
+ rm -f "$pidfile"
+ rm -f "$sockpath"
+}
+
+test_cleanup remove_files
+
+result_filter ()
+{
+ _pid="[0-9][0-9]*"
+ sed -e "s|pid=${_pid}|pid=PID|" \
+ -e "s|PID ${_pid}|PID PID|"
+}
+
+
+ok <<EOF
+daemon started, pid=PID
+startup failed, ret=1
+daemon started, pid=PID
+startup failed, ret=2
+daemon started, pid=PID
+startup completed successfully
+listening on $sockpath
+Shutting down
+EOF
+unit_test sock_daemon_test "$pidfile" "$sockpath" 1
+
+ok <<EOF
+daemon started, pid=PID
+startup completed successfully
+listening on $sockpath
+Received signal $(sigcode SIGUSR1)
+reconfigure failed, ret=1
+Received signal $(sigcode SIGUSR1)
+reconfigure completed successfully
+Received signal 1
+reopen logs, ret=1
+Received signal 1
+reopen logs completed successfully
+Received signal $(sigcode SIGTERM)
+Shutting down
+daemon started, pid=PID
+startup completed successfully
+listening on $sockpath
+Received signal $(sigcode SIGUSR1)
+reconfigure failed, ret=2
+Received signal $(sigcode SIGUSR1)
+reconfigure completed successfully
+Received signal 1
+reopen logs failed, ret=2
+Received signal 1
+reopen logs completed successfully
+Received signal $(sigcode SIGTERM)
+Shutting down
+EOF
+unit_test sock_daemon_test "$pidfile" "$sockpath" 2
+
+ok <<EOF
+daemon started, pid=PID
+listening on $sockpath
+PID PID gone away, exiting
+Shutting down
+EOF
+unit_test sock_daemon_test "$pidfile" "$sockpath" 3
+
+ok <<EOF
+daemon started, pid=PID
+Shutting down
+EOF
+unit_test sock_daemon_test "$pidfile" "$sockpath" 4
+
+ok <<EOF
+daemon started, pid=PID
+listening on $sockpath
+Received signal $(sigcode SIGTERM)
+Shutting down
+EOF
+unit_test sock_daemon_test "$pidfile" "$sockpath" 5
+
+ok <<EOF
+daemon started, pid=PID
+listening on $sockpath
+Shutting down
+EOF
+unit_test sock_daemon_test "$pidfile" "$sockpath" 6
+
+ok <<EOF
+daemon started, pid=PID
+startup completed successfully
+Received signal $(sigcode SIGTERM)
+Shutting down
+EOF
+unit_test sock_daemon_test "$pidfile" "$sockpath" 7
+
+ok <<EOF
+daemon started, pid=PID
+startup completed successfully
+Received signal $(sigcode SIGTERM)
+Shutting down
+daemon started, pid=PID
+startup completed successfully
+Received signal $(sigcode SIGTERM)
+Shutting down
+EOF
+unit_test sock_daemon_test "$pidfile" "$sockpath" 8
+
+ok <<EOF
+daemon started, pid=PID
+startup completed successfully
+Received signal $(sigcode SIGTERM)
+Shutting down
+daemon started, pid=PID
+startup completed successfully
+Received signal $(sigcode SIGTERM)
+Shutting down
+EOF
+unit_test sock_daemon_test "$pidfile" "$sockpath" 9
+
+ok <<EOF
+daemon started, pid=PID
+listening on $sockpath
+daemon started, pid=PID
+listening on $sockpath
+Received signal $(sigcode SIGTERM)
+Shutting down
+EOF
+unit_test sock_daemon_test "$pidfile" "$sockpath" 10
diff --git a/ctdb/tests/UNIT/cunit/sock_io_test_001.sh b/ctdb/tests/UNIT/cunit/sock_io_test_001.sh
new file mode 100755
index 0000000..09a280c
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/sock_io_test_001.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+sockpath="${CTDB_TEST_TMP_DIR}/sock_daemon_test.sock.$$"
+
+ok_null
+
+unit_test sock_io_test "$sockpath"
diff --git a/ctdb/tests/UNIT/cunit/srvid_test_001.sh b/ctdb/tests/UNIT/cunit/srvid_test_001.sh
new file mode 100755
index 0000000..ed09535
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/srvid_test_001.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+
+unit_test srvid_test
diff --git a/ctdb/tests/UNIT/cunit/system_socket_test_001.sh b/ctdb/tests/UNIT/cunit/system_socket_test_001.sh
new file mode 100755
index 0000000..389cec6
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/system_socket_test_001.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ok_null
+unit_test system_socket_test types
diff --git a/ctdb/tests/UNIT/cunit/system_socket_test_002.sh b/ctdb/tests/UNIT/cunit/system_socket_test_002.sh
new file mode 100755
index 0000000..c20bcfe
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/system_socket_test_002.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+tcp_test ()
+{
+ unit_test system_socket_test tcp "$@"
+}
+
+test_case "ACK, IPv4, seq# 0, ack# 0"
+ok <<EOF
+000000 45 00 00 08 00 00 00 00 ff 06 00 00 c0 a8 01 19
+000010 c0 a8 02 4b 01 bd d4 31 00 00 00 00 00 00 00 00
+000020 50 10 04 d2 50 5f 00 00
+000028
+EOF
+tcp_test "192.168.1.25:445" "192.168.2.75:54321" 0 0 0
+
+test_case "RST, IPv4, seq# 0, ack# 0"
+ok <<EOF
+000000 45 00 00 08 00 00 00 00 ff 06 00 00 c0 a8 01 19
+000010 c0 a8 02 4b 01 bd d4 31 00 00 00 00 00 00 00 00
+000020 50 14 04 d2 50 5b 00 00
+000028
+EOF
+tcp_test "192.168.1.25:445" "192.168.2.75:54321" 0 0 1
+
+test_case "RST, IPv4, seq# 12345, ack# 23456"
+ok <<EOF
+000000 45 00 00 08 00 00 00 00 ff 06 00 00 c0 a8 01 19
+000010 c0 a8 02 4b 01 bd d4 31 39 30 00 00 a0 5b 00 00
+000020 50 14 04 d2 76 cf 00 00
+000028
+EOF
+tcp_test "192.168.1.25:445" "192.168.2.75:54321" 12345 23456 1
+
+test_case "ACK, IPv6, seq# 0, ack# 0"
+ok <<EOF
+000000 60 00 00 00 00 14 06 40 fe 80 00 00 00 00 00 00
+000010 6a f7 28 ff fe fa d1 36 fe 80 00 00 00 00 00 00
+000020 6a f7 28 ff fe fb d1 37 01 bd d4 31 00 00 00 00
+000030 00 00 00 00 50 10 04 d2 0f c0 00 00
+00003c
+EOF
+tcp_test "[fe80::6af7:28ff:fefa:d136]:445" \
+ "[fe80::6af7:28ff:fefb:d137]:54321" 0 0 0
+
+test_case "RST, IPv6, seq# 0, ack# 0"
+ok <<EOF
+000000 60 00 00 00 00 14 06 40 fe 80 00 00 00 00 00 00
+000010 6a f7 28 ff fe fa d1 36 fe 80 00 00 00 00 00 00
+000020 6a f7 28 ff fe fb d1 37 01 bd d4 31 00 00 00 00
+000030 00 00 00 00 50 14 04 d2 0f bc 00 00
+00003c
+EOF
+tcp_test "[fe80::6af7:28ff:fefa:d136]:445" \
+ "[fe80::6af7:28ff:fefb:d137]:54321" 0 0 1
+
+test_case "RST, IPv6, seq# 12345, ack# 23456"
+ok <<EOF
+000000 60 00 00 00 00 14 06 40 fe 80 00 00 00 00 00 00
+000010 6a f7 28 ff fe fa d1 36 fe 80 00 00 00 00 00 00
+000020 6a f7 28 ff fe fb d1 37 01 bd d4 31 39 30 00 00
+000030 a0 5b 00 00 50 14 04 d2 36 30 00 00
+00003c
+EOF
+tcp_test "[fe80::6af7:28ff:fefa:d136]:445" \
+ "[fe80::6af7:28ff:fefb:d137]:54321" 12345 23456 1
diff --git a/ctdb/tests/UNIT/cunit/system_socket_test_003.sh b/ctdb/tests/UNIT/cunit/system_socket_test_003.sh
new file mode 100755
index 0000000..c94ac30
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/system_socket_test_003.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+ctdb_test_check_supported_OS "Linux"
+
+arp_test ()
+{
+ unit_test system_socket_test arp "$@"
+}
+
+test_case "IPv4 ARP send"
+ok <<EOF
+000000 ff ff ff ff ff ff 12 34 56 78 9a bc 08 06 00 01
+000010 08 00 06 04 00 01 12 34 56 78 9a bc c0 a8 01 19
+000020 00 00 00 00 00 00 c0 a8 01 19 00 00 00 00 00 00
+000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+000040
+EOF
+arp_test "192.168.1.25" "12:34:56:78:9a:bc"
+
+test_case "IPv4 ARP reply"
+ok <<EOF
+000000 ff ff ff ff ff ff 12 34 56 78 9a bc 08 06 00 01
+000010 08 00 06 04 00 02 12 34 56 78 9a bc c0 a8 01 19
+000020 12 34 56 78 9a bc c0 a8 01 19 00 00 00 00 00 00
+000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+000040
+EOF
+arp_test "192.168.1.25" "12:34:56:78:9a:bc" reply
+
+test_case "IPv6 neighbor advertisement"
+ok <<EOF
+000000 33 33 00 00 00 01 12 34 56 78 9a bc 86 dd 60 00
+000010 00 00 00 20 3a ff fe 80 00 00 00 00 00 00 6a f7
+000020 28 ff fe fa d1 36 ff 02 00 00 00 00 00 00 00 00
+000030 00 00 00 00 00 01 88 00 8d e4 20 00 00 00 fe 80
+000040 00 00 00 00 00 00 6a f7 28 ff fe fa d1 36 02 01
+000050 12 34 56 78 9a bc
+000056
+EOF
+arp_test "fe80::6af7:28ff:fefa:d136" "12:34:56:78:9a:bc"
diff --git a/ctdb/tests/UNIT/cunit/tmon_test_001.sh b/ctdb/tests/UNIT/cunit/tmon_test_001.sh
new file mode 100755
index 0000000..96f706c
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/tmon_test_001.sh
@@ -0,0 +1,195 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+epipe=$(errcode EPIPE)
+eio=$(errcode EIO)
+etimedout=$(errcode ETIMEDOUT)
+
+test_case "No pings, only child monitors, so gets EPIPE"
+ok <<EOF
+parent: async wait start 5
+child: async wait start 10
+parent: async wait end
+child: pipe closed
+EOF
+unit_test tmon_ping_test false 0 5 0 0 false 0 10 0 "$epipe"
+
+test_case "No pings, only parent monitors, so gets EPIPE"
+ok <<EOF
+parent: async wait start 10
+child: async wait start 5
+child: async wait end
+parent: pipe closed
+EOF
+unit_test tmon_ping_test false 0 10 0 "$epipe" false 0 5 0 0
+
+test_case "No pings, Child exits first, parent notices"
+ok <<EOF
+parent: async wait start 10
+child: async wait start 1
+child: async wait end
+parent: pipe closed
+EOF
+unit_test tmon_ping_test false 0 10 0 "$epipe" false 0 1 0 0
+
+test_case "No pings, parent exits first, child notices"
+ok <<EOF
+parent: async wait start 1
+child: async wait start 10
+parent: async wait end
+child: pipe closed
+EOF
+unit_test tmon_ping_test false 0 1 0 0 false 0 10 0 "$epipe"
+
+test_case "Parent pings, child doesn't expect them, EIO"
+ok <<EOF
+parent: async wait start 5
+child: async wait start 5
+child: error ($eio)
+parent: pipe closed
+EOF
+unit_test tmon_ping_test true 0 5 0 "$epipe" false 0 5 0 "$eio"
+
+test_case "Child pings, parent doesn't expect them, EIO"
+ok <<EOF
+parent: async wait start 5
+child: async wait start 5
+parent: error ($eio)
+child: pipe closed
+EOF
+unit_test tmon_ping_test false 0 5 0 "$eio" true 0 5 0 "$epipe"
+
+test_case "Both ping, child doesn't expect them, EIO"
+ok <<EOF
+parent: async wait start 5
+child: async wait start 5
+child: error ($eio)
+parent: pipe closed
+EOF
+unit_test tmon_ping_test true 3 5 0 "$epipe" true 0 5 0 "$eio"
+
+test_case "Both ping, parent doesn't expect them, EIO"
+ok <<EOF
+parent: async wait start 5
+child: async wait start 5
+parent: error ($eio)
+child: pipe closed
+EOF
+unit_test tmon_ping_test true 0 5 0 "$eio" true 3 5 0 "$epipe"
+
+test_case "Child pings, no ping timeout error, child exits first"
+ok <<EOF
+parent: async wait start 10
+child: async wait start 5
+child: async wait end
+parent: pipe closed
+EOF
+unit_test tmon_ping_test false 3 10 0 "$epipe" true 0 5 0 0
+
+test_case "Parent pings, no ping timeout error, parent exits first"
+ok <<EOF
+parent: async wait start 5
+child: async wait start 10
+parent: async wait end
+child: pipe closed
+EOF
+unit_test tmon_ping_test true 0 5 0 0 false 3 10 0 "$epipe"
+
+test_case "Both ping, no ping timeout error, parent exits first"
+ok <<EOF
+parent: async wait start 5
+child: async wait start 10
+parent: async wait end
+child: pipe closed
+EOF
+unit_test tmon_ping_test true 3 5 0 0 true 3 10 0 "$epipe"
+
+test_case "Both ping, no ping timeout error, child exits first"
+ok <<EOF
+parent: async wait start 10
+child: async wait start 5
+child: async wait end
+parent: pipe closed
+EOF
+unit_test tmon_ping_test true 3 10 0 "$epipe" true 3 5 0 0
+
+test_case "Both ping, child blocks, parent ping timeout error"
+ok <<EOF
+parent: async wait start 20
+child: blocking sleep start 7
+parent: ping timeout
+child: blocking sleep end
+EOF
+unit_test tmon_ping_test true 3 20 0 "$etimedout" true 3 0 7 0
+
+test_case "Both ping, parent blocks, child ping timeout error"
+ok <<EOF
+parent: blocking sleep start 7
+child: async wait start 20
+child: ping timeout
+parent: blocking sleep end
+EOF
+unit_test tmon_ping_test true 3 0 7 0 true 3 20 0 "$etimedout"
+
+test_case "Both ping, child waits, child blocks, parent ping timeout error"
+ok <<EOF
+parent: async wait start 20
+child: async wait start 2
+child: async wait end
+child: blocking sleep start 7
+parent: ping timeout
+child: blocking sleep end
+EOF
+unit_test tmon_ping_test true 3 20 0 "$etimedout" true 3 2 7 0
+
+test_case "Both ping, parent waits, parent blocks, child ping timeout error"
+ok <<EOF
+parent: async wait start 2
+child: async wait start 20
+parent: async wait end
+parent: blocking sleep start 7
+child: ping timeout
+parent: blocking sleep end
+EOF
+unit_test tmon_ping_test true 3 2 7 0 true 3 20 0 "$etimedout"
+
+test_case "Both ping, child blocks for less than ping timeout"
+ok <<EOF
+parent: async wait start 20
+child: blocking sleep start 3
+child: blocking sleep end
+parent: pipe closed
+EOF
+unit_test tmon_ping_test true 7 20 0 "$epipe" true 7 0 3 0
+
+test_case "Both ping, parent blocks for less than ping timeout"
+ok <<EOF
+parent: blocking sleep start 3
+child: async wait start 20
+parent: blocking sleep end
+child: pipe closed
+EOF
+unit_test tmon_ping_test true 7 0 3 0 true 7 20 3 "$epipe"
+
+test_case "Both ping, child waits, child blocks for less than ping timeout"
+ok <<EOF
+parent: async wait start 20
+child: async wait start 2
+child: async wait end
+child: blocking sleep start 3
+child: blocking sleep end
+parent: pipe closed
+EOF
+unit_test tmon_ping_test true 7 20 0 "$epipe" true 7 2 3 0
+
+test_case "Both ping, parent waits, parent blocks for less than ping timeout"
+ok <<EOF
+parent: async wait start 2
+child: async wait start 20
+parent: async wait end
+parent: blocking sleep start 3
+parent: blocking sleep end
+child: pipe closed
+EOF
+unit_test tmon_ping_test true 7 2 3 0 true 7 20 0 "$epipe"
diff --git a/ctdb/tests/UNIT/cunit/tmon_test_002.sh b/ctdb/tests/UNIT/cunit/tmon_test_002.sh
new file mode 100755
index 0000000..e4118a3
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/tmon_test_002.sh
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+epipe=$(errcode EPIPE)
+etimedout=$(errcode ETIMEDOUT)
+edom=$(errcode EDOM)
+
+test_cases()
+{
+ test_case "no packets, sender exits, 3s timeout"
+ ok <<EOF
+WRITER OK
+READER ERR=$epipe
+EOF
+ unit_test tmon_test "" false 3 false
+
+ test_case "no packets, sender exits, 3s timeout, close ok"
+ ok <<EOF
+WRITER OK
+READER OK
+EOF
+ unit_test tmon_test "" true 3 false
+
+ test_case "Exit packet @ 1s, no timeout"
+ ok <<EOF
+READER OK
+WRITER OK
+EOF
+ unit_test tmon_test "0" false 0 false
+
+ test_case "errno 7 packet @ 1s, no timeout"
+ ok <<EOF
+READER ERR=7
+WRITER OK
+EOF
+ unit_test tmon_test "7" false 0 false
+
+ test_case "errno 110 packet @ 1s, no timeout"
+ ok <<EOF
+READER ERR=110
+WRITER OK
+EOF
+ unit_test tmon_test "#110" false 0 false
+
+ test_case "errno 0 error causes EDOM @ 1s, no timeout"
+ ok <<EOF
+WRITER ERR=$edom
+READER ERR=$epipe
+EOF
+ unit_test tmon_test "#0;" false 0 false
+
+ test_case "errno -1 error causes EDOM @ 1s, no timeout"
+ ok <<EOF
+WRITER ERR=$edom
+READER ERR=$epipe
+EOF
+ unit_test tmon_test "#-1;" false 0 false
+
+ test_case "errno 70000 error causes EDOM @ 1s, no timeout"
+ ok <<EOF
+WRITER ERR=$edom
+READER ERR=$epipe
+EOF
+ unit_test tmon_test "#70000;!0" false 0 false
+
+ test_case "Exit packet @ 3s, no timeout"
+ ok <<EOF
+READER OK
+WRITER OK
+EOF
+ unit_test tmon_test "..0" false 0 false
+
+ test_case "errno 7 packet @ 3s, no timeout"
+ ok <<EOF
+READER ERR=7
+WRITER OK
+EOF
+ unit_test tmon_test "..7" false 0 false
+
+ test_case "no packets for 5s, 3s timeout"
+ ok <<EOF
+READER ERR=$etimedout
+WRITER OK
+EOF
+ unit_test tmon_test "....." false 3 false
+
+ test_case "no packets for 5s, 3s timeout, timeout ok"
+ ok <<EOF
+READER OK
+WRITER OK
+EOF
+ unit_test tmon_test "....." false 3 true
+
+ test_case "4 pings then exit, 3s timeout"
+ ok <<EOF
+PING
+PING
+PING
+PING
+READER OK
+WRITER OK
+EOF
+ unit_test tmon_test "!!!!0" false 3 false
+
+ test_case "ASCII Hello, errno 7, 3s timeout"
+ ok <<EOF
+ASCII H
+ASCII e
+ASCII l
+ASCII l
+ASCII o
+READER ERR=7
+WRITER OK
+EOF
+ unit_test tmon_test "Hello7" false 3 false
+
+ test_case "Hi there! 3s timeout"
+ ok <<EOF
+ASCII H
+ASCII i
+CUSTOM 0x20
+ASCII t
+ASCII h
+ASCII e
+ASCII r
+ASCII e
+PING
+WRITER OK
+READER ERR=$epipe
+EOF
+ unit_test tmon_test "Hi there!" false 3 false
+}
+
+echo "PASS #1: Run test cases in default mode"
+test_cases
+
+echo
+echo "=================================================="
+
+echo "PASS #2: Run test cases in write-skip mode"
+CTDB_TEST_TMON_WRITE_SKIP_MODE=1 test_cases
diff --git a/ctdb/tests/UNIT/cunit/tunable_test_001.sh b/ctdb/tests/UNIT/cunit/tunable_test_001.sh
new file mode 100755
index 0000000..c68cd69
--- /dev/null
+++ b/ctdb/tests/UNIT/cunit/tunable_test_001.sh
@@ -0,0 +1,312 @@
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+tfile="${CTDB_TEST_TMP_DIR}/tunable.$$"
+
+remove_files ()
+{
+ rm -f "$tfile"
+}
+test_cleanup remove_files
+
+defaults="\
+SeqnumInterval=1000
+ControlTimeout=60
+TraverseTimeout=20
+KeepaliveInterval=5
+KeepaliveLimit=5
+RecoverTimeout=30
+RecoverInterval=1
+ElectionTimeout=3
+TakeoverTimeout=9
+MonitorInterval=15
+TickleUpdateInterval=20
+EventScriptTimeout=30
+MonitorTimeoutCount=20
+RecoveryGracePeriod=120
+RecoveryBanPeriod=300
+DatabaseHashSize=100001
+DatabaseMaxDead=5
+RerecoveryTimeout=10
+EnableBans=1
+NoIPFailback=0
+VerboseMemoryNames=0
+RecdPingTimeout=60
+RecdFailCount=10
+LogLatencyMs=0
+RecLockLatencyMs=1000
+RecoveryDropAllIPs=120
+VacuumInterval=10
+VacuumMaxRunTime=120
+RepackLimit=10000
+VacuumFastPathCount=60
+MaxQueueDropMsg=1000000
+AllowUnhealthyDBRead=0
+StatHistoryInterval=1
+DeferredAttachTO=120
+AllowClientDBAttach=1
+FetchCollapse=1
+HopcountMakeSticky=50
+StickyDuration=600
+StickyPindown=200
+NoIPTakeover=0
+DBRecordCountWarn=100000
+DBRecordSizeWarn=10000000
+DBSizeWarn=100000000
+PullDBPreallocation=10485760
+LockProcessesPerDB=200
+RecBufferSizeLimit=1000000
+QueueBufferSize=1024
+IPAllocAlgorithm=2
+AllowMixedVersions=0
+"
+
+ok_tunable_defaults ()
+{
+ ok "$defaults"
+}
+
+# Set required output to a version of $defaults where values for
+# tunables specified in $tfile replace the default values
+ok_tunable ()
+{
+ # Construct a version of $defaults prepended with a lowercase
+ # version of the tunable variable, to allow case-insensitive
+ # matching. This would be easier with the GNU sed
+ # case-insensitivity flag, but that is less portable. The $0
+ # condition in awk causes empty lines to be skipped, in case
+ # there are trailing empty lines in $defaults.
+ _map=$(echo "$defaults" |
+ awk -F= '$0 { printf "%s:%s=%s\n", tolower($1), $1, $2 }')
+
+ # Replace values for tunables set in $tfile
+ while IFS='= ' read -r _var _val ; do
+ case "$_var" in
+ \#* | "") continue ;;
+ esac
+ _decval=$((_val))
+ _vl=$(echo "$_var" | tr '[:upper:]' '[:lower:]')
+ _map=$(echo "$_map" |
+ sed -e "s|^\\(${_vl}:.*=\\).*\$|\\1${_decval}|")
+ done <"$tfile"
+
+ # Set result, stripping off lowercase tunable prefix
+ ok "$(echo "$_map" | awk -F: '{ print $2 }')"
+}
+
+test_case "Unreadable file"
+: >"$tfile"
+chmod a-r "$tfile"
+uid=$(id -u)
+# root can read unreadable files
+if [ "$uid" = 0 ]; then
+ ok_tunable_defaults
+else
+ required_error EINVAL <<EOF
+ctdb_tunable_load_file: Failed to open ${tfile}
+EOF
+fi
+unit_test tunable_test "$tfile"
+rm -f "$tfile"
+
+test_case "Invalid file, contains 1 word"
+echo "Hello" >"$tfile"
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Invalid line containing "Hello"
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "Invalid file, contains multiple words"
+echo "Hello world!" >"$tfile"
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Invalid line containing "Hello world!"
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "Invalid file, missing value"
+echo "EnableBans=" >"$tfile"
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Invalid line containing "EnableBans"
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "Invalid file, invalid value (not a number)"
+echo "EnableBans=value" >"$tfile"
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Invalid value "value" for tunable "EnableBans"
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "Invalid file, missing key"
+echo "=123" >"$tfile"
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Syntax error
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "Invalid file, missing key but space before ="
+cat >"$tfile" <<EOF
+ =0
+EOF
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Syntax error
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "Invalid file, unknown tunable"
+echo "HelloWorld=123" >"$tfile"
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Unknown tunable "HelloWorld"
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "Invalid file, obsolete tunable"
+echo "MaxRedirectCount=123" >"$tfile"
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Obsolete tunable "MaxRedirectCount"
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "Invalid file, trailing non-whitespace garbage"
+echo "EnableBans=0xgg" >"$tfile"
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Invalid value "0xgg" for tunable "EnableBans"
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "Invalid file, multiple errors"
+cat >"$tfile" <<EOF
+EnableBans=
+EnableBans=value
+=123
+HelloWorld=123
+MaxRedirectCount =123
+EOF
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Invalid line containing "EnableBans"
+ctdb_tunable_load_file: Invalid value "value" for tunable "EnableBans"
+ctdb_tunable_load_file: Syntax error
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "Invalid file, errors followed by valid"
+cat >"$tfile" <<EOF
+HelloWorld=123
+EnableBans=value
+EnableBans=0
+EOF
+required_error EINVAL <<EOF
+ctdb_tunable_load_file: Unknown tunable "HelloWorld"
+ctdb_tunable_load_file: Invalid value "value" for tunable "EnableBans"
+EOF
+unit_test tunable_test "$tfile"
+
+test_case "OK, missing file"
+rm -f "$tfile"
+ok_tunable_defaults
+unit_test tunable_test "$tfile"
+
+test_case "OK, empty file"
+: >"$tfile"
+ok_tunable_defaults
+unit_test tunable_test "$tfile"
+
+test_case "OK, comments and blanks only"
+cat >"$tfile" <<EOF
+# This is a comment
+
+# There are also some blank lines
+
+
+EOF
+ok_tunable_defaults
+unit_test tunable_test "$tfile"
+
+test_case "OK, 1 tunable"
+cat >"$tfile" <<EOF
+EnableBans=0
+EOF
+ok_tunable
+unit_test tunable_test "$tfile"
+
+test_case "OK, 1 tunable, hex"
+cat >"$tfile" <<EOF
+EnableBans=0xf
+EOF
+ok_tunable
+unit_test tunable_test "$tfile"
+
+test_case "OK, 1 tunable, octal"
+cat >"$tfile" <<EOF
+EnableBans=072
+EOF
+ok_tunable
+unit_test tunable_test "$tfile"
+
+test_case "OK, 1 tunable, tab before ="
+cat >"$tfile" <<EOF
+EnableBans =0
+EOF
+ok_tunable
+unit_test tunable_test "$tfile"
+
+test_case "OK, 1 tunable, space after ="
+cat >"$tfile" <<EOF
+EnableBans= 0
+EOF
+ok_tunable
+unit_test tunable_test "$tfile"
+
+test_case "OK, 2 tunables, multiple spaces around ="
+cat >"$tfile" <<EOF
+EnableBans = 0
+RecoverInterval = 10
+EOF
+ok_tunable
+unit_test tunable_test "$tfile"
+
+test_case "OK, 2 tunables, whitespace everywhere"
+cat >"$tfile" <<EOF
+ EnableBans = 0
+ RecoverInterval = 10
+EOF
+ok_tunable
+unit_test tunable_test "$tfile"
+
+test_case "OK, several tunables"
+cat >"$tfile" <<EOF
+EnableBans=0
+RecoverInterval=10
+ElectionTimeout=5
+EOF
+ok_tunable
+unit_test tunable_test "$tfile"
+
+test_case "OK, several tunables, varying case"
+cat >"$tfile" <<EOF
+enablebans=0
+ReCoVerInTeRvAl=10
+ELECTIONTIMEOUT=5
+EOF
+ok_tunable
+unit_test tunable_test "$tfile"
+
+test_case "OK, miscellaneous..."
+cat >"$tfile" <<EOF
+# Leading comment
+enablebans=0
+ReCoVerInTeRvAl = 10
+
+# Intermediate comment after a blank line
+ ELECTIONTIMEOUT=25
+
+
+# Final comment among blanks lines
+
+
+
+
+EOF
+ok_tunable
+unit_test tunable_test "$tfile"