[VOL-5272] - Fixed a problem with relative paths.

app-install.sh
Makefile
--------------
  o Add SPDX copyright tags.
    https://spdx.github.io/spdx-spec/v2.3/file-tags.
  o Create named test-* targets so 'make test' can be conditional.
  o No need to invoke tests/version-check for a non-release job.

app-install.sh
--------------
  o set -euo pipefail to detect shell problems.
  o Added ERR signal handler, display a stack trace for ($? != 0).
  o Added args --download and --install to begin weakening dependency
    on global variables.
  o Script now verbose when run.  Display banners, paths, artifacts, etc.
  o Replace cp with "rsync --checksum" everywhere for file copying.
  o Display contents of app.xml when unable to parse name=.
    Also display grep and sed output then fail hard.

test
test/bats/makefile
test/bats/display-help.sh
-------------------------
  o Add initial support for the bats testing harness (shell scripts).
  o Create a stub unit test directory to automate testing app-install.sh

Change-Id: I138fd2218a10121fbcce4fa8dbcf5776278d05ac
diff --git a/Makefile b/Makefile
index 75c0cc2..aac2d87 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 # -*- makefile -*-
 # -----------------------------------------------------------------------
-# Copyright 2016-2024 Open Networking Foundation (ONF) and the ONF Contributors
+# Copyright 2016-2024 Open Networking Foundation Contributors
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,6 +14,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 # -----------------------------------------------------------------------
+# SPDX-FileCopyrightText: 2017-2024 Open Networking Foundation Contributors
+# SPDX-License-Identifier: Apache-2.0
+# -----------------------------------------------------------------------
 
 .PHONY: help
 .DEFAULT_GOAL := help
@@ -49,6 +52,15 @@
 
 # For each makefile target, add ## <description> on the target line and it will be listed by 'make help'
 help :: ## Print help for each Makefile target
+	@echo "Usage: $(MAKE) [options] [target] ..."
+
+	@printf '  %-33.33s %s' 'test' \
+	  'Run repository based test suites (test=)'
+	@printf '  %-33.33s %s' 'test-bats' \
+	  'Invoke bats harness shell test suites (wip)'
+	@printf '  %-33.33s %s' 'test-release' \
+	  'Verify released VERSION does ont contain dev/SNAPSHOT apps'
+
 	@echo
 	@grep --no-filename '^[[:alpha:]_-]*:.* ##' $(MAKEFILE_LIST) \
 	    | sort \
@@ -86,15 +98,51 @@
 
 ## -----------------------------------------------------------------------
 ## -----------------------------------------------------------------------
-test: ## verify that if the version is released we're not pointing to SNAPSHOT apps
+test := $(null)
+
+ifdef TEST-BATS
+  test += test-bats
+endif
+
+ifdef RELEASE
+  test += test-release
+endif
+
+test :: $(test) ## verify that if the version is released we're not pointing to SNAPSHOT apps
+
+## -----------------------------------------------------------------------
+## Intent: Shell script testing with the bats test harness.
+##  Usage: make test TEST-BATS=1
+## -----------------------------------------------------------------------
+test-bats:
+	$(HIDE)$(MAKE) -C test/bats $@
+
+## -----------------------------------------------------------------------
+## Intent: Release based testing.
+## -----------------------------------------------------------------------
+## Usage:
+##   make test-release
+##   make test RELEASE=1
+## -----------------------------------------------------------------------
+## Legacy: VERSION validation has been defined as a default repository
+##         based test.  The target should be isolated and should only
+##         be required durring a release cycle.
+## -----------------------------------------------------------------------
+test-release :
 	bash tests/version-check.sh
 
+## -----------------------------------------------------------------------
+## -----------------------------------------------------------------------
 docker-push: ## push to docker registy: use DOCKER_REGISTRY, DOCKER_REPOSITORY and DOCKER_TAG to customize
 	docker push ${ONOS_IMAGENAME}
 
+## -----------------------------------------------------------------------
+## -----------------------------------------------------------------------
 clean :: ## clean the build environment
 	$(RM) -r local_imports
 
+## -----------------------------------------------------------------------
+## -----------------------------------------------------------------------
 sterile :: clean
 
 # end file
diff --git a/app-install.sh b/app-install.sh
index 59b102a..10effce 100755
--- a/app-install.sh
+++ b/app-install.sh
@@ -1,5 +1,6 @@
 #!/bin/bash
-# Copyright 2017-2024 Open Networking Foundation (ONF) and the ONF Contributors
+# -----------------------------------------------------------------------
+# Copyright 2017-2024 Open Networking Foundation Contributors
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,14 +13,51 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+# -----------------------------------------------------------------------
+# SPDX-FileCopyrightText: 2017-2024 Open Networking Foundation Contributors
+# SPDX-License-Identifier: Apache-2.0
+# -----------------------------------------------------------------------
 
 ##-------------------##
 ##---]  GLOBALS  [---##
 ##-------------------##
-# set -euo pipefail
+set -euo pipefail
 umask 022
 
 ## -----------------------------------------------------------------------
+## Intent: Display a stack trace on error
+## -----------------------------------------------------------------------
+function errexit()
+{
+    local err=$?
+    set +o xtrace
+    local code="${1:-1}"
+
+    local prefix="${BASH_SOURCE[1]}:${BASH_LINENO[1]}"
+    echo -e "\nOFFENDER: ${prefix}"
+    if [ $# -gt 0 ] && [ "$1" == '--stacktrace-quiet' ]; then
+        code=1
+    else
+        echo "ERROR: '${BASH_COMMAND}' exited with status $err"
+    fi
+
+    # Print out the stack trace described by $function_stack
+    if [ ${#FUNCNAME[@]} -gt 2 ]
+    then
+	    echo "Call tree:"
+	    for ((i=1;i<${#FUNCNAME[@]}-1;i++))
+	    do
+	        echo " $i: ${BASH_SOURCE[$i+1]}:${BASH_LINENO[$i]} ${FUNCNAME[$i]}(...)"
+	    done
+    fi
+
+    echo "Exiting with status ${code}"
+    echo
+    exit "${code}"
+}
+trap 'errexit' ERR
+
+## -----------------------------------------------------------------------
 ## Intent: Display an error mesage then exit with shell exit status
 ## -----------------------------------------------------------------------
 function error()
@@ -57,6 +95,8 @@
 ** $(declare -p APPS_ROOT)
 ** $(declare -p DOWNLOAD_ROOT)
 ** -----------------------------------------------------------------------
+** $(declare -p KARAF_M2)
+** -----------------------------------------------------------------------
 EOM
 
     return
@@ -82,19 +122,60 @@
     return
 }
 
-##----------------##
-##---]  MAIN  [---##
-##----------------##
+## -----------------------------------------------------------------------
+## Intent: Display program usage
+## -----------------------------------------------------------------------
+function usage()
+{
+    if [[ $# -gt 0 ]]; then
+        printf "\n** $@\n"
+    fi
+
+    cat <<__HELP__
+Usage: ${BASH_SOURCE[0]##*/}
+  --download                 Holding directory for downloaded oar files
+  --install                  Unpack and install app files into this directory
+
+[Test Helpers]
+  --fail                     Force script exit with status fail ($?==99)
+  --pass                     Force script exit with status pass ($?==0)
+
+  --debug                    Enable debug mode.
+  --help                     Display program usage.
+__HELP__
+
+    return
+}
+
+##-------------------##
+##---]  GETOPTS  [---##
+##-------------------##
 
 declare arg
 while [[ $# -gt 0 ]]; do
     arg="$1"; shift
     case "$arg" in
         '--debug') declare -g -i debug=1 ;;
+        '--down'*)
+            arg="$1"; shift
+            [[ ! -e "$arg" ]] && { error "--download $arg does not exist"; }
+            export DOWNLOAD_ROOT="$arg"
+            ;;
+        '--inst'*)
+            arg="$1"; shift
+            [[ ! -e "$arg" ]] && { error "--install $arg does not exist"; }
+            export APP_INSTALL_ROOT="$arg"
+            ;;
+        '--fail') exit 99 ;;
+        '--pass') exit 0 ;;
+        '--help') usage; exit 0 ;;
         *) echo "[SKIP] Unknown argument [$arg]" ;;
     esac
 done
 
+##----------------##
+##---]  MAIN  [---##
+##----------------##
 init
 
 declare -a oars
@@ -102,13 +183,15 @@
 
 for oar in "${oars[@]}"; do
 
-    app_xml="$APP_INSTALL_ROOT/app.xml"
+    # app_xml="$APP_INSTALL_ROOT/app.xml"
+    app_xml='app.xml'
     oar_basename="${oar##*/}"    # bash builtin
 
     cat <<EOF
 
 ** -----------------------------------------------------------------------
-** Artifact: ${oar##*/}
+** Artifact: ${oar}
+** Basename: ${oar_basename}
 ** -----------------------------------------------------------------------
 EOF
 
@@ -116,26 +199,40 @@
     rm -rf "$APP_INSTALL_ROOT"
     mkdir -p "$APP_INSTALL_ROOT"
 
+    rsync --checksum "$oar" "$APP_INSTALL_ROOT/."
+
     ## pushd()/popd(): cd $here && cd $root w/error checking
     pushd "$APP_INSTALL_ROOT" >/dev/null \
         || { error "pushd failed: $APP_INSTALL_ROOT"; }
 
     [[ -v debug ]] && { echo "** Installing: $oar"; }
 
-    set -x
-    rsync --checksum "$oar" "$APP_INSTALL_ROOT/."
-    unzip -oq -d . "$APP_INSTALL_ROOT/${oar_basename}"
-    set +x
+    unzip -oq -d . "${oar_basename}"
 
     # ------------------------------------------------------------
     # [IN]  <app name="org.opencord.kafka" origin="ONF" version="2.13.2"
     # [OUT] declare -a names=([0]="org.opencord.kafka")
     # ------------------------------------------------------------
     readarray -t names < <(grep 'name=' "$app_xml" \
-			                   | sed 's/<app name="//g;s/".*//g')
+                               | sed 's/<app name="//g;s/".*//g')
 
-    [[ ${#names[@]} -gt 0 ]] \
-        || { error "Detected invalid name gathering"; }
+    if [[ ${#names[@]} -eq 0 ]]; then
+        echo
+        echo "APP_XML FILE: $APP_INSTALL/${app_xml}"
+        cat "$app_xml"
+
+        cat <<ERR
+
+** -----------------------------------------------------------------------
+**  FILE: $APP_INSTALL/${app_xml}
+**  GREP: $(grep 'name=' "$app_xml")
+**   SED: $(grep 'name=' "$app_xml" | sed 's/<app name="//g;s/".*//g')
+** ERROR: Detected invalid app_xml=${app_xml} name gathering.
+** NAMES: $(declare -p names)
+** -----------------------------------------------------------------------
+ERR
+        exit 1
+    fi
 
     printf '** %s\n' "$(declare -p names)"
     name="${names[0]}"
@@ -148,13 +245,13 @@
 
     declare app_png="$APP_INSTALL_ROOT/app.png"
     [ -f "$app_png" ] && { rsync -v --checksum "$app_png" "${apps_name}/."; }
-    cp "${APP_INSTALL_ROOT}/${oar_basename}" "${apps_name}/${name}.oar"
-
-    rsync -rv --checksum "${APP_INSTALL_ROOT}/m2/." "$KARAF_M2/."
-    rm -rf "$APP_INSTALL_ROOT"
+    rsync -v "${oar_basename}" "${apps_name}/${name}.oar"
 
     popd >/dev/null || { error "popd failed: $APP_INSTALL_ROOT"; }
 
+    rsync -rv --checksum "$APP_INSTALL_ROOT/m2/." "$KARAF_M2/."
+    rm -rf "$APP_INSTALL_ROOT"
+
 done
 
 # [EOF]
diff --git a/test/bats/display-help.sh b/test/bats/display-help.sh
new file mode 100755
index 0000000..4edf068
--- /dev/null
+++ b/test/bats/display-help.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bats
+# -----------------------------------------------------------------------
+# Copyright 2024 Open Networking Foundation Contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------
+# SPDX-FileCopyrightText: 2024 Open Networking Foundation Contributors
+# SPDX-License-Identifier: Apache-2.0
+# -----------------------------------------------------------------------
+
+# load 'libs/bats-support/load'
+# load 'libs/bats-assert/load'
+
+@test "Verify option --help" {
+    run bash ../../app-install.sh --fail
+    [ "$status" -eq 99 ] # non-zero exit status
+
+    run bash ../../app-install.sh --pass
+    [ "$status" -eq 0 ]
+
+#    declare -i result="$(../../app-install.sh --pass)"
+#    assert_output -p "Usage app-install.sh"
+}
+
+# [SEE ALSO]
+# * https://opensource.com/article/19/2/te
+
+# [EOF]
diff --git a/test/bats/makefile b/test/bats/makefile
new file mode 100644
index 0000000..9da1e29
--- /dev/null
+++ b/test/bats/makefile
@@ -0,0 +1,26 @@
+# -*- makefile -*-
+# -----------------------------------------------------------------------
+# Copyright 2016-2024 Open Networking Foundation Contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------
+# SPDX-FileCopyrightText: 2017-2024 Open Networking Foundation Contributors
+# SPDX-License-Identifier: Apache-2.0
+# -----------------------------------------------------------------------
+
+all:
+
+test ::
+	$(HIDE)./display-help.sh
+
+# end file
diff --git a/test/bats/urls b/test/bats/urls
new file mode 100644
index 0000000..e010974
--- /dev/null
+++ b/test/bats/urls
@@ -0,0 +1,2 @@
+https://opensource.com/article/19/2/testing-bash-bats
+https://github.com/ztombol/bats-assert