#! /bin/bash


#  unit.sh  version  20221217


set  -o errexit
set  +o xtrace


run  ()  {    #  --------------------------------------------------------  run

  #  usage:  run  expect  envN  lxrN  cmdN  arg...

  local  expect_raw="$1"
  local  -n  envN="$2"  lxrN="$3"  cmdN="$4"
  local  argv=(  "${@:5}"  )
  local  command=(  "${envN[@]}"  "${lxrN[@]}"  "${argv[@]}"  "${cmdN[@]}"  )

  local  stdout  ;  stdout="$(  "${command[@]}"  2>&1  )"  &&
    local  status="$?"  ||  local  status="$?"

  local  pretty="$status"

  case  "$expect_raw"  in
    ( [0-9]      )  local  expect="${expect_raw}"     ;;
    ( [0-9]-     )  local  expect="${expect_raw%-}"   ;;
    ( [0-9]'  '* )  local  expect="${expect_raw}"     ;;
    ( *          )  local  expect="0  ${expect_raw}"  ;;  esac

  case  "$expect_raw"  in
    ( [0-9]      )  local  actual="${status}${stdout:+  }${stdout}"  ;;
    ( [0-9]-     )  local  actual="${status}"                        ;;
    ( [0-9]'  '* )  local  actual="${status}  ${stdout}"             ;;
    ( *          )  local  actual="${status}  ${stdout}"             ;;  esac

  if  [ "$actual" = "$expect" ]  ;  then
    local  pretty2=(  "${lxrN[@]}"  "${argv[@]}"  "${cmdN[@]}"  )
    echo  "${BASH_LINENO[1]}  $pretty  ${pretty2[*]}"
    return  ;  fi

  clear

  #  rerun the failed command (without capturing its output)
  trace  "${command[@]}"  ||  true

  echo
  echo  '--------'
  echo
  echo  'unit.sh  test fail'
  echo
  echo  "  line       ${BASH_LINENO[1]}"
  echo
  echo  "  command    ${command[*]}"
  echo  "  status     $status"
  echo
  echo  "  expect     '$expect'"
  echo  "  actual     '$( echo "$actual" | head -n 1)'    length ${#actual}"

  local  f='  %-9s  %s\n'
  echo
  printf  "$f"  'cwd'      "$PWD"
  printf  "$f"  "$2"       "${envN[*]}"
  printf  "$f"  "$3"       "${lxrN[*]}"
  printf  "$f"  'argv'     "${argv[*]}"
  printf  "$f"  "$4"       "${cmdN[*]}"

  echo

  #  If status == segfault, then run lxroot in gdb.
  case  "$status"  in
    ( 139 )  run_gdb  ;  echo  ;  exit  1  ;;  esac

  exit  1  ;  }


run_gdb  ()  {    #  ------------------------------------------------  run_gdb
  gdb=(  /usr/bin/gdb  -q  -ex run  --args  )
  local  command=( "${envN[@]}" "${gdb[@]}" "${lxrN[@]}" "${argv[@]}" )
  trace  "${command[@]}"  ;  }


is_file   (){  test  -f  "$1"  ;}    #  -----------------------------  is_file
is_str    (){  test  -n  "$1"  ;}    #  ------------------------------  is_str

not       (){  !  "$@"             ;}    #  -----------------------------  not
not_file  (){  not  is_file  "$1"  ;}    #  ------------------------  not_file
not_str   (){  not  is_str   "$1"  ;}    #  -------------------------  not_str

die  ()  {  echo  "die  $*"  ;  exit  1  ;  }    #  ---------------------  die

popd   ()  {  builtin  popd   "$@"  >  /dev/null  ;  }    #  -----------  popd
pushd  ()  {  builtin  pushd  "$@"  >  /dev/null  ;  }    #  ----------  pushd

rm_soft     ()  { if  [ -e "$1" ];  then  rm     "$1"  ;  fi  ;  }    #  -----
rmdir_soft  ()  { if  [ -d "$1" ];  then  rmdir  "$1"  ;  fi  ;  }    #  -----

run1  ()  {  run  "$1"  env1  lxr1  cmd1  "${@:2}"  ;  }    #  ---------  run1
run2  ()  {  run  "$1"  env2  lxr2  cmd2  "${@:2}"  ;  }    #  ---------  run2
run3  ()  {  run  "$1"  env3  lxr3  cmd3  "${@:2}"  ;  }    #  ---------  run3


trace  ()  {   echo  "+  $@"  ;  "$@"  ;  }    #  ---------------------  trace


prepare  ()  {    #  ------------------------------------------------  prepare
  mkdir  -p  "$unit"/nr/tmp/.X11-unix
  echo  'unit.sh  done  prepare'  ;  }


prepare_users  ()  {    #  ------------------------------------  prepare_users
  pushd  "$unit"/nr/etc
  cp  -n  group   group-orig
  cp  -n  passwd  passwd-orig
  touch           group-none
  touch           passwd-none

  local  uid  ;  uid=`id  -u`
  local  gid  ;  gid=`id  -g`

  echo  "group2:x:$gid:"  >  group-user2
  echo  "user2:x:$uid:$gid::/home/user2:/bin/ash"  >  passwd-user2

  popd
  echo  'unit.sh  done  prepare_users'  ;  }


etc_passwd  ()  {    #  ------------------------------------------  etc_passwd

  #  usage:  etc_passwd  none     #  use an empty     /etc/passwd
  #          etc_passwd  orig     #  use the original /etc/passwd
  #          etc_passwd  user2    #  use a custom     /etc/passwd

  pushd  "$unit/nr/etc"
  cp  group-"$1"   group
  cp  passwd-"$1"  passwd
  popd  ;  }


home_make  ()  {    #  --------------------------------------------  home_make
  mkdir  -p  "$unit/nr/home/$USER"
  mkdir  -p  "$unit/nr/home/user1"
  mkdir  -p  "$unit/nr/home/user2/ape"
  mkdir  -p  "$unit/nr/home/user3"
  mkdir  -p  "$unit/nr/root"
  return  ;  }


home_remove  ()  {    #  ----------------------------------------  home_remove
  rm_soft     "$unit/nr/home/user2/foo"
  rm_soft     "$unit/nr/root/aaa/bbb"
  rm_soft     "$unit/nr/root/foo"
  rm_soft     "$unit/nr/home/$USER/.ash_history"
  rm_soft     "$unit/nr/home/user1/.ash_history"
  rm_soft     "$unit/nr/home/user2/.ash_history"
  rm_soft     "$unit/nr/home/user2/.Xauthority"
  rm_soft     "$unit/nr/home/user3/.ash_history"
  rmdir_soft  "$unit/nr/home/$USER"
  rmdir_soft  "$unit/nr/home/user1"
  rmdir_soft  "$unit/nr/home/user2/ape"
  rmdir_soft  "$unit/nr/home/user2"
  rmdir_soft  "$unit/nr/home/user3"
  rmdir_soft  "$unit/nr/root/aaa"
  rmdir_soft  "$unit/nr/root"
  return  ;  }


phase_one  ()  {    #  --------------------------------------------  phase_one

  #  no vars
  env1=(  env  -  )
  lxr1=(  ./lxr  nr  )
  cmd1=()

  #  only environment vars
  env2=(  env  -  HOME=/home/user1  LOGNAME=user1  USER=user1  )
  lxr2=(  ./lxr  nr  )
  cmd2=()

  #  explicit vars should override environment vars
  env3=(  env  -  HOME=/home/user1  LOGNAME=user1  USER=user1  )
  lxr3=(  ./lxr  nr
            HOME=/home/user3  LOGNAME=user3  USER=user3  SHELL=shell3  )
  cmd3=()

  return;  }


test_bind  ()  {    #  --------------------------------------------  test_bind

  echo  ;  echo  "-  test_bind"

  prepare_full_overlay

  env1=(  env  -  )
  lxr1=(  ./lxr  nr  bind  ape  f1/ape  )    #  a single bind
  cmd1=(  )

  run1  'one_ape'    --  cat  /ape/ape
  run1  '1-'         --  cat  /bug/ape

  lxr1=(  ./lxr  nr  bind  bug  f1/ape  )    #  a single bind

  run1  '1-'         --  cat  /ape/ape
  run1  'one_ape'    --  cat  /bug/ape

  lxr1=()  cmd1=(  touch  /home/user2/ape/foo  )    #  bind inside $HOME

  run1  ''           ./lxr  nr  bind      /home/user2/ape  f1/ape  --
  run1  ''           ./lxr  nr  bind      home/user2/ape   f1/ape  --
  run1  ''           ./lxr  nr  bind  ra  home/user2/ape   f1/ape  --
  run1  '1-'         ./lxr  nr  bind  ro  home/user2/ape   f1/ape  --
  run1  ''           ./lxr  nr  bind  rw  home/user2/ape   f1/ape  --

  lxr1=()  cmd1=()    #  attempting to bind to / is an error

  #  20210605
  #    The below test checks if lxroot dies on the over-binding of newroot.
  #    I believe this *should* be an error, but I have yet to implement it.
  #  run1  'err  1'  ./lxr  nr  bind  /  nr  --  /bin/sh -c 'exit 2'

  return  ;  }


test_chdir  ()  {    #  ------------------------------------------  test_chdir
  echo  ;  echo  "-  test_chdir"

  env1=(  env  -  )  lxr1=()  cmd1=()

  prepare_full_overlay
  home_remove

  #  test  cd & wd

  run1  '/ape'           ./lxr  nr  cd /ape                    --  pwd
  run1  '1-'             ./lxr  nr  cd /ape  cd /bug           --  pwd
  run1  '/ape'           ./lxr  nr  wd /ape                    --  pwd
  run1  '/bug'           ./lxr  nr  wd /ape  wd /bug           --  pwd
  run1  '/cow'           ./lxr  nr  wd /ape  wd /bug  cd /cow  --  pwd

  run1  '1-'             ./lxr     nr          --  touch /foo
  run1  '1-'             ./lxr     nr          --  touch /ape/foo
  run1  ''               ./lxr     nr          --  touch /tmp/foo
  run1  '/'              ./lxr     nr          --  pwd
  run1  '1-'             ./lxr     nr          --  touch foo

  run1  '1-'             ./lxr  ro nr          --  touch /foo
  run1  '1-'             ./lxr  ro nr          --  touch /ape/foo
  run1  '1-'             ./lxr  ro nr          --  touch /tmp/foo
  run1  '/'              ./lxr  ro nr          --  pwd
  run1  '1-'             ./lxr  ro nr          --  touch foo

  run1  '1-'             ./lxr     nr wd /ape  --  touch /foo
  run1  ''               ./lxr     nr wd /ape  --  touch /ape/foo
  run1  ''               ./lxr     nr wd /ape  --  touch /tmp/foo
  run1  '/ape'           ./lxr     nr wd /ape  --  pwd
  run1  ''               ./lxr     nr wd /ape  --  touch foo

  run1  '1-'             ./lxr  ro nr wd /ape  --  touch /foo

  run1  ''               ./lxr  ro nr wd /ape  --  touch /ape/foo
  run1  '1-'             ./lxr  ro nr wd /ape  --  touch /tmp/foo
  run1  '/ape'           ./lxr  ro nr wd /ape  --  pwd
  run1  ''               ./lxr  ro nr wd /ape  --  touch foo

  run1  '/tmp'           ./lxr  ro nr cd /tmp  --  pwd
  run1  '/tmp'           ./lxr  ro nr wd /tmp  --  pwd
  run1  '1-'             ./lxr  ro nr cd /tmp  --  touch foo
  run1  ''               ./lxr  ro nr wd /tmp  --  touch foo

  run1  '1-'             ./lxr  ro nr wd /tmp  --  touch /foo
  run1  '1-'             ./lxr  ro nr wd /tmp  --  touch /ape/foo
  run1  ''               ./lxr  ro nr wd /tmp  --  touch /tmp/foo

  return  ;  }


test_chdir_root  ()  {    #  --------------------------------  test_chdir_root

  #  env1=()  lxr1=()  cmd1=()    #  20220105
  env1=(  env  -  )  lxr1=()  cmd1=()

  mkdir  -p        nr/root/aaa
  echo   'ccc'  >  nr/root/aaa/bbb

  etc_passwd  orig    #  original inner /etc/passwd

  run1  '/root'          ./lxr  -r nr         --  /bin/sh -c 'echo $HOME'
  run1  '/root'          ./lxr  -r nr         --  pwd
  run1  '/root/aaa'      ./lxr  -r nr cd aaa  --  pwd
  run1  'ccc'            ./lxr  -r nr cd aaa  --  cat bbb
  run1  'ccc'            ./lxr  -r nr wd aaa  --  cat bbb

  etc_passwd  none    #  empty inner /etc/passwd

  run1  '/root'          ./lxr  -r nr         --  /bin/sh -c 'echo $HOME'
  run1  '/root'          ./lxr  -r nr         --  pwd
  run1  '/root/aaa'      ./lxr  -r nr cd aaa  --  pwd
  run1  'ccc'            ./lxr  -r nr cd aaa  --  cat bbb
  run1  'ccc'            ./lxr  -r nr wd aaa  --  cat bbb

  #  etc_passwd  user2  ;  test_chdir_2_inner

  return  ;  }


test_dev_shm  ()  {    #  --------------------------------------  test_dev_shm

  #  20211002  On my development system, /dev/shm exists.  Moreover,
  #            /dev/shm is mounted with the nosuid and nodev flags.
  #            Therefore, I can use /dev/shm to test read-only
  #            remounting of bind mounts with nosuid and nodev.

  [ ! -d /dev/shm ]  &&  return

  env1=(  env  -  )  lxr1=()  cmd1=()

  run1  'foo'    ./lxr  nr  bind  rw  /mnt  /dev/shm  --  echo  foo
  run1  'foo'    ./lxr  nr  bind  ra  /mnt  /dev/shm  --  echo  foo
  run1  'foo'    ./lxr  nr  bind  ro  /mnt  /dev/shm  --  echo  foo
  run1  'foo'    ./lxr  nr  bind      /mnt  /dev/shm  --  echo  foo

  return  ;  }


test_env  ()  {    #  ----------------------------------------------  test_env

  echo  ;  echo  "-  test_env"

  TZ='America/Los_Angeles'
  env1=(  env  -  'FOO=BAR2'  "TZ=$TZ"  )  lxr1=()  cmd1=()

  run1  'hello'    ./lxr      nr  --  /bin/sh  -c 'echo hello'
  run1  0          ./lxr      nr  --  /bin/sh  -c 'echo "$FOO"'
  run1  'BAR2'     ./lxr  -e  nr  --  /bin/sh  -c 'echo "$FOO"'
  run1  "$TZ"      ./lxr      nr  --  /bin/sh  -c 'echo "$TZ"'
  run1  "$TZ"      ./lxr  -e  nr  --  /bin/sh  -c 'echo "$TZ"'

  return  ;  }


test_fakeroot  ()  {    #  ------------------------------------  test_fakeroot

  echo  ;  echo  "-  test_fakeroot"

  not_file  nr/usr/bin/fakeroot  &&  {
    trace  env  -  ./lxr  -nr  nr  --  apk  update
    trace  env  -  ./lxr  -nr  nr  --  apk  add  fakeroot  ;  }

  env1=(  env  -  )  lxr1=()  cmd1=()

  run1  '0  1'     ./lxr      nr  --  /bin/sh -c 'echo $FAKEROOTDONTTRYCHOWN'
  run1  '0  1'     ./lxr  -w  nr  --  /bin/sh -c 'echo $FAKEROOTDONTTRYCHOWN'
  run1  '0  1'     ./lxr  -r  nr  --  /bin/sh -c 'echo $FAKEROOTDONTTRYCHOWN'

  run1  ''         ./lxr      nr  --  fakeroot  true
  run1  ''         ./lxr  -r  nr  --  fakeroot  true
  run1  '1'        ./lxr      nr  --  fakeroot  false
  run1  '1'        ./lxr  -r  nr  --  fakeroot  false

  run1  "0  $UID"  ./lxr  nr  --  id  -u
  run1  '0  0'     ./lxr  nr  --  fakeroot  id  -u

  return  ;  }


prepare_full_overlay  ()  {    #  ----------------------  prepare_full_overlay

  mkdir  -p  nr/ape   nr/bug  nr/cow
  mkdir  -p  f1/ape   f1/bug  f1/cow
  mkdir  -p  f2/ape   f2/bug  f2/cow  f2/dog
  mkdir  -p           f3/bug
  mkdir  -p  f4/home  f4/tmp  f4/var/tmp

  echo  'one_ape'  >  f1/ape/ape
  echo  'one_bug'  >  f1/bug/bug
  echo  'one_cow'  >  f1/cow/cow

  echo  'two_ape'  >  f2/ape/ape
  echo  'two_bug'  >  f2/bug/bug
  echo  'two_cow'  >  f2/cow/cow

  echo  'three_bug'  >  f3/bug/bug

  return  ;  }


test_full_overlay  ()  {    #  ----------------------------  test_full_overlay

  echo  ;  echo  '-  test_full_overlay'

  prepare_full_overlay
  env1=(  env  -  )  lxr1=()  cmd1=()

  #  a full overlay

  run1  'one_ape'      ./lxr  nr  f1       --  cat  /ape/ape
  run1  'one_bug'      ./lxr  nr  f1       --  cat  /bug/bug
  run1  'one_cow'      ./lxr  nr  f1       --  cat  /cow/cow

  #  a larger full overlay

  run1  'two_ape'      ./lxr  nr  f2       --  cat  /ape/ape
  run1  'two_bug'      ./lxr  nr  f2       --  cat  /bug/bug
  run1  'two_cow'      ./lxr  nr  f2       --  cat  /cow/cow
  run1  '1-'           ./lxr  nr  f2       --  cat  /dog/dog

  #  a smaller full overlay

  run1  '1-'           ./lxr  nr  f3       --  cat  /ape/ape
  run1  'three_bug'    ./lxr  nr  f3       --  cat  /bug/bug
  run1  '1-'           ./lxr  nr  f3       --  cat  /cow/cow

  #  two overlapping full overlays

  run1  'one_ape'      ./lxr  nr  f1  f3   --  cat  /ape/ape
  run1  'three_bug'    ./lxr  nr  f1  f3   --  cat  /bug/bug
  run1  'one_cow'      ./lxr  nr  f1  f3   --  cat  /cow/cow

  #  readauto on a full overlay

  run1  '1-'         ./lxr      nr     f1  --  touch  /ape/ape
  run1  '1-'         ./lxr      nr  ra f1  --  touch  /ape/ape
  run1  '1-'         ./lxr      nr  ro f1  --  touch  /ape/ape
  run1  ''           ./lxr      nr  rw f1  --  touch  /ape/ape

  run1  ''           ./lxr  -r  nr     f1  --  touch  /ape/ape
  run1  ''           ./lxr  -r  nr  ra f1  --  touch  /ape/ape
  run1  '1-'         ./lxr  -r  nr  ro f1  --  touch  /ape/ape
  run1  ''           ./lxr  -r  nr  rw f1  --  touch  /ape/ape

  run1  ''           ./lxr  -w  nr     f1  --  touch  /ape/ape
  run1  ''           ./lxr  -w  nr  ra f1  --  touch  /ape/ape
  run1  '1-'         ./lxr  -w  nr  ro f1  --  touch  /ape/ape
  run1  ''           ./lxr  -w  nr  rw f1  --  touch  /ape/ape

  #  readauto on /tmp in a full overlay

  run1  ''           ./lxr      nr     f4  --  touch  /tmp/foo
  run1  ''           ./lxr      nr  ra f4  --  touch  /tmp/foo
  run1  '1-'         ./lxr      nr  ro f4  --  touch  /tmp/foo
  run1  ''           ./lxr      nr  rw f4  --  touch  /tmp/foo

  run1  ''           ./lxr  -r  nr     f4  --  touch  /tmp/foo
  run1  ''           ./lxr  -r  nr  ra f4  --  touch  /tmp/foo
  run1  '1-'         ./lxr  -r  nr  ro f4  --  touch  /tmp/foo
  run1  ''           ./lxr  -r  nr  rw f4  --  touch  /tmp/foo

  run1  ''           ./lxr  -w  nr     f4  --  touch  /tmp/foo
  run1  ''           ./lxr  -w  nr  ra f4  --  touch  /tmp/foo
  run1  '1-'         ./lxr  -w  nr  ro f4  --  touch  /tmp/foo
  run1  ''           ./lxr  -w  nr  rw f4  --  touch  /tmp/foo

  #  readauto on /var/tmp in a full overlay

  run1  ''           ./lxr      nr     f4  --  touch  /var/tmp/foo
  run1  ''           ./lxr      nr  ra f4  --  touch  /var/tmp/foo
  run1  '1-'         ./lxr      nr  ro f4  --  touch  /var/tmp/foo
  run1  ''           ./lxr      nr  rw f4  --  touch  /var/tmp/foo

  run1  ''           ./lxr  -r  nr     f4  --  touch  /var/tmp/foo
  run1  ''           ./lxr  -r  nr  ra f4  --  touch  /var/tmp/foo
  run1  '1-'         ./lxr  -r  nr  ro f4  --  touch  /var/tmp/foo
  run1  ''           ./lxr  -r  nr  rw f4  --  touch  /var/tmp/foo

  run1  ''           ./lxr  -w  nr     f4  --  touch  /var/tmp/foo
  run1  ''           ./lxr  -w  nr  ra f4  --  touch  /var/tmp/foo
  run1  '1-'         ./lxr  -w  nr  ro f4  --  touch  /var/tmp/foo
  run1  ''           ./lxr  -w  nr  rw f4  --  touch  /var/tmp/foo

  #  20210604  todo  what additional tests should I add?

  return  ;  }


test_home  ()  {    #  --------------------------------------------  test_home

  echo  ;  echo  "-  test_home"

  etc_passwd  none    #  nr/etc/passwd miss
  home_make
  phase_one
      #  run1  enivornment variables  none
      #  run2  environment variables  fixed
      #  run3  environment variables  fixed, but overriden by n=v options

  #  20210624  The two below commented tests are too restrictive.

  #  run1  "$HOME"          -- /bin/sh -c 'echo $HOME'    #  host /etc/passwd
  run2  "/home/user1"    -- /bin/sh -c 'echo $HOME'       #  host env
  run3  '/home/user3'    -- /bin/sh -c 'echo $HOME'       #  explicit n=v
  #  run1  "$HOME"          -- pwd                        #  host /etc/passwd
  run2  '/home/user1'    -- pwd                           #  host env
  run3  '/home/user3'    -- pwd                           #  explicit n=v

  etc_passwd  user2    #  nr/etc/passwd hit

  run1  '/home/user2'    -- /bin/sh -c 'echo $HOME'       #  nr /etc/passwd
  run2  '/home/user2'    -- /bin/sh -c 'echo $HOME'       #  nr /etc/passwd
  run3  '/home/user3'    -- /bin/sh -c 'echo $HOME'       #  explicit n=v
  run1  "/home/user2"    -- pwd                           #  nr /etc/passwd
  run2  '/home/user2'    -- pwd                           #  nr /etc/passwd
  run3  '/home/user3'    -- pwd                           #  explicit n=v

  return  ;  }


test_home_root  ()  {    #  ----------------------------------  test_home_root

  home_make
  phase_one

  etc_passwd  orig    # use original /etc/passwd

  run1  '/root'          -r -- /bin/sh -c 'echo $HOME'    #  nr /etc/passwd
  run2  '/root'          -r -- /bin/sh -c 'echo $HOME'    #  nr /etc/passwd
  run3  '/home/user3'    -r -- /bin/sh -c 'echo $HOME'    #  explicit n=v
  run1  '/root'          -r -- pwd                        #  nr /etc/passwd
  run2  '/root'          -r -- pwd                        #  nr /etc/passwd
  run3  '/home/user3'    -r -- pwd                        #  explicit n=v

  etc_passwd  none    #  use empty /etc/passwd

  run1  '/root'          -r -- /bin/sh -c 'echo $HOME'    #  host /etc/passwd
  run2  '/root'          -r -- /bin/sh -c 'echo $HOME'    #  host /etc/passwd
  run3  '/home/user3'    -r -- /bin/sh -c 'echo $HOME'    #  explicit n=v
  run1  '/root'          -r -- pwd                        #  host /etc/passwd
  run2  '/root'          -r -- pwd                        #  host /etc/passwd
  run3  '/home/user3'    -r -- pwd                        #  explicit n=v

  return  ;  }


interact  ()  {    #  ----------------------------------------------  interact

  #  usage:  interact  expect  command  [arg ...]

  local  line="${BASH_LINENO[0]}"
  local  expect="$1"

  clear

  echo
  echo  "----  Interactive test:  $line  ----"


  #  run the command and capture its exit status
  "${@:2}"  &&  local  actual="$?"  ||  local  actual="$?"

  [ "$actual" = "$expect" ]  &&  {
    echo
    echo  '----  Test passed.  ----'
    echo
    printf  'line     %3d\n'  "$line"
    printf  'command  %s\n'   "${*:2}"
    printf  'expect   %d\n'   "$expect"
    printf  'actual   %d\n'   "$actual"

    [ -t 0 ]  &&  {    #  is stdin a terminal?
      echo
      echo  '----  Please press enter to continue.  ----'
      local  line  ;  read  line  ;  }

    return  0  ;  }

  echo
  echo
  echo  '----  Test failed.  ----'
  echo
  printf  'line     %3d\n'  "$line"
  printf  'command  %s\n'   "${*:2}"
  printf  'expect   %d\n'   "$expect"
  printf  'actual   %d\n'   "$actual"
  echo
  exit  1  ;  }


test_interactive_network  ()  {    #  --------------  test_interactive_network

  echo  ;  echo  '-  test_interactive_network'

  env1=()  lxr1=()  cmd1=()


  local  resolv='nr/etc/resolv.conf'
  local  cmd=(
    env  -  ./lxr  -n  nr  --  /bin/sh  -c 'test -f /etc/resolv.conf'  )

  trace  rm  -f  nr/etc/resolv.conf  nr/resolv.conf

  run1  0  [ ! -f "$resolv" ]  ;  run1      1  "${cmd[@]}"  < /dev/null
  run1  0  [ ! -f "$resolv" ]  ;  interact  0  "${cmd[@]}"
  run1  0  [   -f "$resolv" ]  ;  run1      0  "${cmd[@]}"  < /dev/null
  run1  0  [   -f "$resolv" ]

  trace  rm  -f  nr/etc/resolv.conf  nr/resolv.conf
  trace  ln  -s  ../resolv.conf  nr/etc/resolv.conf

  run1  0  [ ! -f "$resolv" ]  ;  run1  1  "${cmd[@]}"  < /dev/null
  run1  0  [ ! -f "$resolv" ]  ;  interact  0  "${cmd[@]}"
  run1  0  [   -f "$resolv" ]  ;  run1  0  "${cmd[@]}"  < /dev/null
  run1  0  [   -f "$resolv" ]

  return  ;  }


xset_enable  ()  {    #  ----------------------------------------  xset_enable

  is_file  nr/usr/bin/xset-backup  &&  {
    trace  cp  nr/usr/bin/xset-backup  nr/usr/bin/xset  ;  return  ;  }

  not_file  nr/usr/bin/xset  &&  {
    trace  cp  /etc/resolv.conf  nr/etc/
    trace  env  -  ./lxr  -nr  nr  --  apk  add  xset  ;  }

  return  ;  }


xset_disable  ()  {    #  --------------------------------------  xset_disable
  not_file  nr/usr/bin/xset  &&  return
  trace  cp  -n  nr/usr/bin/xset  nr/usr/bin/xset-backup
  trace  rm  nr/usr/bin/xset  ;  }


test_interactive_xorg  ()  {    #  --------------------  test_interactive_xorg

  echo  ;  echo  '-  test_interactive_xorg'

  not_str  "$DISPLAY"  &&  {
    echo  '$DISPLAY is not set.  skipping xorg test.'  ;  return  ;  }

  env1=(  env  -  HOME="$HOME"                      )    lxr1=()    cmd1=()
  env2=(  env  -  HOME="$HOME"  DISPLAY=''          )    lxr2=()    cmd2=()
  env3=(  env  -  HOME="$HOME"  DISPLAY="$DISPLAY"  )    lxr3=()    cmd3=()

  etc_passwd  user2

  trace  xset_disable
  trace  rm  -f  nr/home/user2/.Xauthority

  run1  'user2'        ./lxr      nr  --  /bin/sh  -c 'echo $USER'
  run1  '/home/user2'  ./lxr      nr  --  /bin/sh  -c 'echo $HOME'

  run1  '1  lxroot  error  $DISPLAY not set'  \
                       ./lxr  -x  nr  --  true

  run2  '1  lxroot  error  $DISPLAY not set'  \
                       ./lxr  -x  nr  --  true

  run3  '1  lxroot  error  file not found  /usr/bin/xset'  \
                       ./lxr  -x  nr  --  true

  mkdir  -p  nr/home/user2
  trace  xset_enable

  interact  1  "${env3[@]}"  ./lxr  -x  nr  --  true  < /dev/null
  interact  0  "${env3[@]}"  ./lxr  -x  nr  --  true

  return  ;  }


test_x11  ()  {    #  ----------------------------------------------  test_x11

  not_str  "$DISPLAY"  &&  return

  env1=()  lxr1=()  cmd1=(  /bin/sh -c  'echo $DISPLAY'  )

  run1  ''            env  -                      ./lxr         nr  --
  run1  '1-'          env  -                      ./lxr  -x     nr  --
  run1  '1-'          env  -                      ./lxr  --x11  nr  --

  run1  ''            env  -  DISPLAY="$DISPLAY"  ./lxr         nr  --
  run1  "$DISPLAY"    env  -  DISPLAY="$DISPLAY"  ./lxr  -x     nr  --
  run1  "$DISPLAY"    env  -  DISPLAY="$DISPLAY"  ./lxr  --x11  nr  --

  return  ;  }


test_no_home  ()  {    #  --------------------------------------  test_no_home

  echo  ;  echo  "-  test_no_home"

  etc_passwd  none
  home_remove
  phase_one

  #  test  echo and exit

  run1  'foo'                       --  echo  'foo'
  run1  '$foo'           'foo=bar'  --  echo  '$foo'
  run1  'bar'            'foo=bar'  --  /bin/sh  -c 'echo $foo'
  run1  '7'                         --  /bin/sh  -c 'exit 7'

  #  test  outside /etc/passwd

  #  we assume HOME, LOGNAME, and USER will match /etc/passwd

  #  20210624  these unit tests are too restrictive, at least at present.
  #
  #  run1  "$HOME"          --  /bin/sh  -c 'echo $HOME'
  #  run1  "$LOGNAME"       --  /bin/sh  -c 'echo $LOGNAME'
  #  run1  "$USER"          --  /bin/sh  -c 'echo $USER'
  #  run1  '/bin/ash'       --  /bin/sh  -c 'echo $SHELL'
  #  run1  '/'              --  pwd

  #  test  inherited environment variables

  run2  '/home/user1'    --  /bin/sh  -c 'echo $HOME'
  run2  'user1'          --  /bin/sh  -c 'echo $LOGNAME'
  run2  'user1'          --  /bin/sh  -c 'echo $USER'
  run2  '/bin/ash'       --  /bin/sh  -c 'echo $SHELL'
  run2  '/'              --  pwd

  #  test  $unit/nr/etc/passwd-user2

  etc_passwd  user2

  run2  '/home/user2'    --  /bin/sh  -c 'echo $HOME'
  run2  'user2'          --  /bin/sh  -c 'echo $LOGNAME'
  run2  'user2'          --  /bin/sh  -c 'echo $USER'
  run2  '/bin/ash'       --  /bin/sh  -c 'echo $SHELL'
  run2  '/'              --  pwd

  #  test  explicit environment variables

  etc_passwd  orig

  run3  '/home/user3'    -- /bin/sh -c 'echo $HOME'
  run3  'user3'          -- /bin/sh -c 'echo $LOGNAME'
  run3  'user3'          -- /bin/sh -c 'echo $USER'
  run3  'shell3'         -- /bin/sh -c 'echo $SHELL'
  run3  '/'              --  pwd

  #  test  various newroots
  lxr1=()

  run1  '0-'    ./lxr
  run1  '1-'    ./lxr  bad  --  true

  return  ;  }


test_partial_overlay  ()  {    #  ----------------------  test_partial_overlay

  echo  ;  echo  "-  test_partial_overlay"

  prepare_full_overlay
  env1=(  env  -  )  lxr1=()  cmd1=()

  lxr1=(  ./lxr  nr  src f1  ape  bug  cow  --  cat  )
  run1  'one_ape'    /ape/ape
  run1  'one_bug'    /bug/bug
  run1  'one_cow'    /cow/cow

  lxr1=(  ./lxr  nr  src f2  bug  --  cat  )
  run1  '1-'         /ape/ape
  run1  'two_bug'    /bug/bug
  run1  '1-'         /cow/cow

  lxr1=(  ./lxr  nr  src f1 ape cow  src f2 bug  --  cat  )
  run1  'one_ape'    /ape/ape
  run1  'two_bug'    /bug/bug
  run1  'one_cow'    /cow/cow

  #  20210618  todo  test readauto when dst is a descendant of $HOME

  #  20210618  todo  test mode inheritance

  return  ;  }


test_readauto  ()  {    #  ------------------------------------  test_readauto

  echo  ;  echo  "-  test_readauto"

  etc_passwd  user2
  home_make
  env1=(  env  -  )  lxr1=()  cmd1=()

  #  as non-root

  run1  ''      ./lxr  nr         --  touch  /home/user2/foo
  run1  '1-'    ./lxr  nr         --  touch  /root/foo
  run1  '1-'    ./lxr  nr         --  touch  /foo

  run1  ''      ./lxr  ra nr      --  touch  /home/user2/foo
  run1  '1-'    ./lxr  ra nr      --  touch  /root/foo
  run1  '1-'    ./lxr  ra nr      --  touch  /foo

  run1  '1-'    ./lxr  ro nr      --  touch  /home/user2/foo
  run1  '1-'    ./lxr  ro nr      --  touch  /root/foo
  run1  '1-'    ./lxr  ro nr      --  touch  /foo

  run1  ''      ./lxr  rw nr      --  touch  /home/user2/foo
  run1  ''      ./lxr  rw nr      --  touch  /root/foo
  run1  ''      ./lxr  rw nr      --  touch  /foo

  #  as root

  run1  ''      ./lxr  -r  nr     --  touch  /root/foo
  run1  ''      ./lxr  -r  nr     --  touch  /foo

  run1  ''      ./lxr  -r  ra nr  --  touch  /root/foo
  run1  ''      ./lxr  -r  ra nr  --  touch  /foo

  run1  '1-'    ./lxr  -r  ro nr  --  touch  /root/foo
  run1  '1-'    ./lxr  -r  ro nr  --  touch  /foo

  run1  ''      ./lxr  -r  rw nr  --  touch  /root/foo
  run1  ''      ./lxr  -r  rw nr  --  touch  /foo

  return  ;  }


test_options  ()  {    #  --------------------------------------  test_options

  echo  ;  echo  "-  test_options"

  etc_passwd  user2
  home_make

  env1=()  lxr1=()  cmd1=()

  run1  '1  lxroot  error  bad option  --foo'  env - ./lxr --foo -- true

  return  ;  }


test_no_newroot  ()  {    #  --------------------------------  test_no_newroot

  echo  ;  echo  "-  test_no_newroot"

  env1=(  env  -  )  lxr1=()  cmd1=()

  run1  ''        ./lxr      --  true
  run1  '1-'      ./lxr      --  nr
  run1  "$UID"    ./lxr      --  id  -u
  run1  '0'       ./lxr  -r  --  id  -u

  return  ;  }


test_trace  ()  {    #  ------------------------------------------  test_trace

  echo  ;  echo  '-  test_final'
  ./lxr  nr  --trace  --  echo  foo
  return  ;  }


main  ()  {    #  ------------------------------------------------------  main

  unit="$1"

  echo

  if  [ ! -x "$unit"/lxr ]  ;  then
    die  "unit.sh  error  executable not found  $unit/lxr"  ;  fi

  prepare
  prepare_users

  cd  "$unit"

  test_env
  test_no_home
  test_home
  test_home_root
  test_readauto
  test_full_overlay
  test_partial_overlay
  test_bind
  test_chdir
  test_chdir_root
  test_options
  test_dev_shm
  test_fakeroot

  test_interactive_network
  test_interactive_xorg

  #  20220822  now that lxroot run 'xset -q' test_x11 is obsolete?
  #  test_x11

  #  20220816  why was lxroot without a newroot ever valid?
  #  test_no_newroot

  #  20220811  fyi  test_trace is disabled due to its verbosity.
  #  test_trace

  echo  ;  echo  'unit.sh  done  all tests passed'  ;  exit  ;  }


main  "$@"
