Issue #174: Add -g option for Groot and integ tests for user namespace

This commit is contained in:
Filippo Squillace 2017-04-09 17:34:43 +01:00
parent fcb4a36f30
commit b817aa8445
15 changed files with 433 additions and 333 deletions

View file

@ -18,6 +18,7 @@ function oneTimeSetUp(){
function setUp(){
cwdSetUp
junestSetUp
init_mocks
}
function tearDown(){
@ -25,28 +26,42 @@ function tearDown(){
cwdTearDown
}
function test_run_env_as_root_different_arch(){
echo "JUNEST_ARCH=XXX" > ${JUNEST_HOME}/etc/junest/info
assertCommandFailOnStatus 104 run_env_as_root pwd
}
function _test_run_env_as_root() {
function init_mocks() {
chroot_cmd() {
[ "$JUNEST_ENV" != "1" ] && return 1
echo $@
echo "chroot_cmd $@"
}
assertCommandSuccess run_env_as_root $@
GROOT=chroot_cmd
}
function test_run_env_as_root_cmd(){
_test_run_env_as_root pwd
assertEquals "$JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)"
function test_run_env_as_groot_cmd(){
assertCommandSuccess run_env_as_groot "" pwd
assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)"
}
function test_run_env_as_classic_root_no_cmd(){
_test_run_env_as_root
assertEquals "$JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)"
function test_run_env_as_groot_no_cmd(){
assertCommandSuccess run_env_as_groot ""
assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)"
}
function test_run_env_as_groot_cmd_with_backend_args(){
assertCommandSuccess run_env_as_groot "-n -b /home/blah" pwd
assertEquals "chroot_cmd -b $HOME -b /tmp -b /proc -b /sys -b /dev -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)"
}
function test_run_env_as_chroot_cmd(){
assertCommandSuccess run_env_as_chroot "" pwd
assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)"
}
function test_run_env_as_chroot_no_cmd(){
assertCommandSuccess run_env_as_chroot ""
assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c /bin/sh --login" "$(cat $STDOUTF)"
}
function test_run_env_as_chroot_cmd_with_backend_args(){
assertCommandSuccess run_env_as_chroot "-n -b /home/blah" pwd
assertEquals "chroot_cmd -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat $STDOUTF)"
}
source $JUNEST_ROOT/tests/utils/shunit2

View file

@ -20,10 +20,20 @@ function oneTimeTearDown(){
}
function setUp(){
ld_exec() {
ld_exec_mock() {
echo "ld_exec $@"
}
LD_EXEC=ld_exec
ld_exec_mock_false() {
echo "ld_exec $@"
return 1
}
LD_EXEC=ld_exec_mock
unshare_mock() {
echo "unshare $@"
}
UNSHARE=unshare_mock
}
function test_ln(){
@ -117,26 +127,23 @@ function test_zgrep(){
}
function test_unshare(){
UNSHARE=echo assertCommandSuccess unshare_cmd new_program
assertEquals "new_program" "$(cat $STDOUTF)"
assertCommandSuccess unshare_cmd new_program
assertEquals "$(echo -e "ld_exec ${JUNEST_HOME}/usr/bin/$UNSHARE --user /bin/sh --login -c :\nld_exec ${JUNEST_HOME}/usr/bin/$UNSHARE new_program")" "$(cat $STDOUTF)"
UNSHARE=false assertCommandSuccess unshare_cmd new_program
assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false new_program" "$(cat $STDOUTF)"
LD_EXEC=ld_exec_mock_false assertCommandSuccess unshare_cmd new_program
assertEquals "$(echo -e "ld_exec ${JUNEST_HOME}/usr/bin/unshare_mock --user /bin/sh --login -c :\nunshare --user /bin/sh --login -c :\nunshare new_program")" "$(cat $STDOUTF)"
UNSHARE=false LD_EXEC=false assertCommandFail unshare_cmd new_program
}
function test_chroot(){
GROOT=echo assertCommandSuccess chroot_cmd root
CLASSIC_CHROOT=echo assertCommandSuccess chroot_cmd root
assertEquals "root" "$(cat $STDOUTF)"
GROOT=false CLASSIC_CHROOT=echo assertCommandSuccess chroot_cmd root
assertEquals "root" "$(cat $STDOUTF)"
GROOT=false CLASSIC_CHROOT=false assertCommandSuccess chroot_cmd root
CLASSIC_CHROOT=false assertCommandSuccess chroot_cmd root
assertEquals "ld_exec $JUNEST_HOME/usr/bin/false root" "$(cat $STDOUTF)"
GROOT=false CLASSIC_CHROOT=false LD_EXEC=false assertCommandFail chroot_cmd root
CLASSIC_CHROOT=false LD_EXEC=false assertCommandFail chroot_cmd root
}
function test_proot_cmd_compat(){
@ -187,11 +194,22 @@ function test_copy_passwd_and_group_failure(){
}
function test_nested_env(){
JUNEST_ENV=1 assertCommandFailOnStatus 106 bash -c "source $JUNEST_ROOT/lib/utils/utils.sh; source $JUNEST_ROOT/lib/core/common.sh"
JUNEST_ENV=1 assertCommandFailOnStatus 106 check_nested_env
}
function test_nested_env_not_set_variable(){
JUNEST_ENV=aaa assertCommandFailOnStatus 107 bash -c "source $JUNEST_ROOT/lib/utils/utils.sh; source $JUNEST_ROOT/lib/core/common.sh"
JUNEST_ENV=aaa assertCommandFailOnStatus 107 check_nested_env
}
function test_check_same_arch_not_same(){
echo "JUNEST_ARCH=XXX" > ${JUNEST_HOME}/etc/junest/info
assertCommandFailOnStatus 104 check_same_arch
}
function test_check_same_arch(){
echo "JUNEST_ARCH=$ARCH" > ${JUNEST_HOME}/etc/junest/info
assertCommandSuccess check_same_arch
}
source $JUNEST_ROOT/tests/utils/shunit2

View file

@ -79,39 +79,39 @@ function test_groot_mountpoint_exist(){
}
MOUNTPOINT=mountpoint_mock
assertCommandSuccess main chrootdir
assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_mountpoint_does_not_exist(){
assertCommandSuccess main chrootdir
assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind(){
assertCommandSuccess main -b /tmp chrootdir
[[ -d chrootdir/tmp ]]
assertEquals 0 $?
assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/tmp)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/tmp)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_file(){
touch file_src
assertCommandSuccess main -b ${PWD}/file_src:/file_src chrootdir
[[ -f chrootdir/file_src ]]
assertEquals 0 $?
assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind ${PWD}/file_src chrootdir/file_src)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind ${PWD}/file_src chrootdir/file_src)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_not_existing_node(){
assertCommandFailOnStatus $NOT_EXISTING_FILE main -b ${PWD}/file_src:/file_src chrootdir
assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)")" "$(cat $STDOUTF)"
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_not_absolute_path_node(){
touch file_src
assertCommandFailOnStatus $NOT_ABSOLUTE_PATH main -b file_src:/file_src chrootdir
assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)")" "$(cat $STDOUTF)"
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_guest_host(){
assertCommandSuccess main -b /tmp:/home/tmp chrootdir
[[ -d chrootdir/home/tmp ]]
assertEquals 0 $?
assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_multiple_bind(){
assertCommandSuccess main -b /tmp:/home/tmp -b /dev chrootdir
@ -119,11 +119,11 @@ function test_groot_with_multiple_bind(){
assertEquals 0 $?
[[ -d chrootdir/dev ]]
assertEquals 0 $?
assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nmount(--rbind /dev chrootdir/dev)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nmount(--rbind /dev chrootdir/dev)\nchroot(chrootdir)")" "$(cat $STDOUTF)"
}
function test_groot_with_command(){
assertCommandSuccess main chrootdir ls -la -h
assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir ls -la -h)")" "$(cat $STDOUTF)"
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nchroot(chrootdir ls -la -h)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_and_command(){
assertCommandSuccess main -b /tmp:/home/tmp -b /dev chrootdir ls -la -h
@ -131,7 +131,7 @@ function test_groot_with_bind_and_command(){
assertEquals 0 $?
[[ -d chrootdir/dev ]]
assertEquals 0 $?
assertEquals "$(echo -e "check_and_trap(chroot_teardown EXIT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nmount(--rbind /dev chrootdir/dev)\nchroot(chrootdir ls -la -h)")" "$(cat $STDOUTF)"
assertEquals "$(echo -e "check_and_trap(chroot_teardown QUIT EXIT ABRT KILL TERM INT)\nmountpoint(-q chrootdir)\nmount(--bind chrootdir chrootdir)\nmount(--rbind /tmp chrootdir/home/tmp)\nmount(--rbind /dev chrootdir/dev)\nchroot(chrootdir ls -la -h)")" "$(cat $STDOUTF)"
}
function test_groot_with_bind_no_umount(){
assertCommandSuccess main -n chrootdir

View file

@ -26,14 +26,7 @@ function version(){
}
function build_image_env(){
local disable_validation=$1
local skip_root_tests=$2
echo "build_image_env($disable_validation,$skip_root_tests)"
}
function check_env(){
local env_home=$1
local cmd_script=$2
local skip_root_tests=$3
echo "check_env($env_home,$cmd_script,$skip_root_tests)"
echo "build_image_env($disable_validation)"
}
function delete_env(){
echo "delete_env"
@ -49,23 +42,21 @@ function run_env_as_fakeroot(){
shift
echo "run_env_as_fakeroot($backend_args,$@)"
}
function run_env_as_root(){
echo "run_env_as_root $@"
function run_env_as_groot(){
echo "run_env_as_groot $@"
}
function run_env_as_chroot(){
echo "run_env_as_chroot $@"
}
function run_env_as_user(){
local backend_args="$1"
shift
echo "run_env_as_user($backend_args,$@)"
}
function run_env_as_fakeroot_with_namespace(){
function run_env_with_namespace(){
local backend_args="$1"
shift
echo "run_env_as_fakeroot_with_namespace($backend_args,$@)"
}
function run_env_as_user_with_namespace(){
local backend_args="$1"
shift
echo "run_env_as_user_with_namespace($backend_args,$@)"
echo "run_env_with_namespace($backend_args,$@)"
}
function test_help(){
@ -82,27 +73,13 @@ function test_version(){
}
function test_build_image_env(){
assertCommandSuccess main -b
assertEquals "build_image_env(false,false)" "$(cat $STDOUTF)"
assertEquals "build_image_env(false)" "$(cat $STDOUTF)"
assertCommandSuccess main --build-image
assertEquals "build_image_env(false,false)" "$(cat $STDOUTF)"
assertCommandSuccess main -b -s
assertEquals "build_image_env(false,true)" "$(cat $STDOUTF)"
assertEquals "build_image_env(false)" "$(cat $STDOUTF)"
assertCommandSuccess main -b -n
assertEquals "build_image_env(true,false)" "$(cat $STDOUTF)"
assertCommandSuccess main -b -n -s
assertEquals "build_image_env(true,true)" "$(cat $STDOUTF)"
assertCommandSuccess main --build-image --disable-validation --skip-root-tests
assertEquals "build_image_env(true,true)" "$(cat $STDOUTF)"
}
function test_check_env(){
assertCommandSuccess main -c myscript
assertEquals "check_env(${JUNEST_HOME},myscript,false)" "$(cat $STDOUTF)"
assertCommandSuccess main --check myscript
assertEquals "check_env(${JUNEST_HOME},myscript,false)" "$(cat $STDOUTF)"
assertCommandSuccess main -c myscript -s
assertEquals "check_env(${JUNEST_HOME},myscript,true)" "$(cat $STDOUTF)"
assertCommandSuccess main --check myscript --skip-root-tests
assertEquals "check_env(${JUNEST_HOME},myscript,true)" "$(cat $STDOUTF)"
assertEquals "build_image_env(true)" "$(cat $STDOUTF)"
assertCommandSuccess main --build-image --disable-validation
assertEquals "build_image_env(true)" "$(cat $STDOUTF)"
}
function test_delete_env(){
assertCommandSuccess main -d
@ -168,36 +145,31 @@ function test_run_env_as_user(){
assertCommandFail main -a "myarch" -- command -ls
}
function test_run_env_as_root(){
function test_run_env_as_groot(){
assertCommandSuccess main -g
assertEquals "run_env_as_groot " "$(cat $STDOUTF)"
assertCommandSuccess main -g command
assertEquals "run_env_as_groot command" "$(cat $STDOUTF)"
}
function test_run_env_as_chroot(){
assertCommandSuccess main -r
assertEquals "run_env_as_root " "$(cat $STDOUTF)"
assertEquals "run_env_as_chroot " "$(cat $STDOUTF)"
assertCommandSuccess main -r command
assertEquals "run_env_as_root command" "$(cat $STDOUTF)"
assertEquals "run_env_as_chroot command" "$(cat $STDOUTF)"
}
function test_run_env_as_fakeroot_with_namespace(){
function test_run_env_with_namespace(){
assertCommandSuccess main -u -f
assertEquals "run_env_as_fakeroot_with_namespace(,)" "$(cat $STDOUTF)"
assertCommandSuccess main --user-namespace --fakeroot
assertEquals "run_env_as_fakeroot_with_namespace(,)" "$(cat $STDOUTF)"
assertEquals "run_env_with_namespace(,)" "$(cat $STDOUTF)"
assertCommandSuccess main --namespace --fakeroot
assertEquals "run_env_with_namespace(,)" "$(cat $STDOUTF)"
assertCommandSuccess main -u -f -p "-b arg"
assertEquals "run_env_as_fakeroot_with_namespace(-b arg,)" "$(cat $STDOUTF)"
assertEquals "run_env_with_namespace(-b arg,)" "$(cat $STDOUTF)"
assertCommandSuccess main -u -f -p "-b arg" -- command -kv
assertEquals "run_env_as_fakeroot_with_namespace(-b arg,command -kv)" "$(cat $STDOUTF)"
assertEquals "run_env_with_namespace(-b arg,command -kv)" "$(cat $STDOUTF)"
assertCommandSuccess main -u -f command --as
assertEquals "run_env_as_fakeroot_with_namespace(,command --as)" "$(cat $STDOUTF)"
}
function test_run_env_as_user_with_namespace(){
assertCommandSuccess main -u
assertEquals "run_env_as_user_with_namespace(,)" "$(cat $STDOUTF)"
assertCommandSuccess main -u -p "-b arg"
assertEquals "run_env_as_user_with_namespace(-b arg,)" "$(cat $STDOUTF)"
assertCommandSuccess main -u -p "-b arg" -- command -ll
assertEquals "run_env_as_user_with_namespace(-b arg,command -ll)" "$(cat $STDOUTF)"
assertCommandSuccess main -u command -ls
assertEquals "run_env_as_user_with_namespace(,command -ls)" "$(cat $STDOUTF)"
assertEquals "run_env_with_namespace(,command --as)" "$(cat $STDOUTF)"
}
function test_check_cli(){

View file

@ -78,64 +78,36 @@ function test_is_user_namespace_enabled_with_config(){
assertCommandSuccess _is_user_namespace_enabled
}
function test_run_env_as_user_with_namespace() {
assertCommandSuccess run_env_as_user_with_namespace "" ""
function test_run_env_with_namespace() {
assertCommandSuccess run_env_with_namespace "" ""
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_user_with_namespace_with_bindings() {
assertCommandSuccess run_env_as_user_with_namespace "-b /usr -b /lib:/tmp/lib" ""
function test_run_env_with_namespace_with_bindings() {
assertCommandSuccess run_env_with_namespace "-b /usr -b /lib:/tmp/lib" ""
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_user_with_namespace_with_command() {
assertCommandSuccess run_env_as_user_with_namespace "" "ls -la"
function test_run_env_with_namespace_with_command() {
assertCommandSuccess run_env_with_namespace "" "ls -la"
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_user_with_namespace_with_bindings_and_command() {
assertCommandSuccess run_env_as_user_with_namespace "-b /usr -b /lib:/tmp/lib" "ls -la"
function test_run_env_with_namespace_with_bindings_and_command() {
assertCommandSuccess run_env_with_namespace "-b /usr -b /lib:/tmp/lib" "ls -la"
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_fakeroot_with_namespace() {
assertCommandSuccess run_env_as_fakeroot_with_namespace "" ""
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)"
_test_copy_common_files
}
function test_run_env_as_fakeroot_with_namespace_with_bindings() {
assertCommandSuccess run_env_as_fakeroot_with_namespace "-b /usr -b /lib:/tmp/lib" ""
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login" "$(cat $STDOUTF)"
_test_copy_common_files
}
function test_run_env_as_fakeroot_with_namespace_with_command() {
assertCommandSuccess run_env_as_fakeroot_with_namespace "" "ls -la"
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)"
_test_copy_common_files
}
function test_run_env_as_fakeroot_with_namespace_with_bindings_and_command() {
assertCommandSuccess run_env_as_fakeroot_with_namespace "-b /usr -b /lib:/tmp/lib" "ls -la"
assertEquals "unshare --mount --user --map-root-user $GROOT -n -b $HOME -b /tmp -b /proc -b /sys -b /dev -b /usr -b /lib:/tmp/lib $JUNEST_HOME /bin/sh --login -c \"ls -la\"" "$(cat $STDOUTF)"
_test_copy_common_files
}
source $JUNEST_ROOT/tests/utils/shunit2