mirror of
https://github.com/clearlinux/swupd-client.git
synced 2026-09-08 06:31:50 +00:00
1542 lines
44 KiB
Bash
1542 lines
44 KiB
Bash
#!bin/bash
|
|
|
|
FUNC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
TEST_FILENAME=$(basename "$BATS_TEST_FILENAME")
|
|
TEST_NAME=${TEST_FILENAME%.bats}
|
|
THEME_DIRNAME="$BATS_TEST_DIRNAME"
|
|
|
|
export TEST_NAME
|
|
export THEME_DIRNAME
|
|
export FUNC_DIR
|
|
export SWUPD_DIR="$FUNC_DIR/../.."
|
|
export SWUPD="$SWUPD_DIR/swupd"
|
|
|
|
# Error codes
|
|
export EBUNDLE_MISMATCH=2 # at least one local bundle mismatches from MoM
|
|
export EBUNDLE_REMOVE=3 # cannot delete local bundle filename
|
|
export EMOM_NOTFOUND=4 # MoM cannot be loaded into memory (this could imply network issue)
|
|
export ETYPE_CHANGED_FILE_RM=5 # do_staging() couldn't delete a file which must be deleted
|
|
export EDIR_OVERWRITE=6 # do_staging() couldn't overwrite a directory
|
|
export EDOTFILE_WRITE=7 # do_staging() couldn't create a dotfile
|
|
export ERECURSE_MANIFEST=8 # error while recursing a manifest
|
|
export ELOCK_FILE=9 # cannot get the lock
|
|
export ECURL_INIT=11 # cannot initialize curl agent
|
|
export EINIT_GLOBALS=12 # cannot initialize globals
|
|
export EBUNDLE_NOT_TRACKED=13 # bundle is not tracked on the system
|
|
export EMANIFEST_LOAD=14 # cannot load manifest into memory
|
|
export EINVALID_OPTION=15 # invalid command option
|
|
export ENOSWUPDSERVER=16 # no net connection to swupd server
|
|
export EFULLDOWNLOAD=17 # full_download problem
|
|
export ENET404=404 # download 404'd
|
|
export EBUNDLE_INSTALL=18 # Cannot install bundles
|
|
export EREQUIRED_DIRS=19 # Cannot create required dirs
|
|
export ECURRENT_VERSION=20 # Cannot determine current OS version
|
|
export ESIGNATURE=21 # Cannot initialize signature verification
|
|
export EBADTIME=22 # System time is bad
|
|
export EDOWNLOADPACKS=23 # Pack download failed
|
|
export EBADCERT=24 # unable to verify server SSL certificate
|
|
|
|
|
|
generate_random_content() {
|
|
|
|
local bottom_range=${1:-5}
|
|
local top_range=${2:-100}
|
|
local range=$((top_range - bottom_range + 1))
|
|
local number_of_lines=$((RANDOM%range+$bottom_range))
|
|
< /dev/urandom tr -dc 'a-zA-Z0-9-_!@#$%^&*()_+{}|:<>?=' | fold -w 100 | head -n $number_of_lines
|
|
|
|
}
|
|
|
|
generate_random_name() {
|
|
|
|
local prefix=${1:-test-}
|
|
local uuid
|
|
|
|
# generate random 8 character alphanumeric string (lowercase only)
|
|
uuid=$(< /dev/urandom tr -dc 'a-f0-9' | fold -w 8 | head -n 1)
|
|
echo "$prefix$uuid"
|
|
|
|
}
|
|
|
|
print_stack() {
|
|
|
|
echo "An error occurred"
|
|
echo "Function stack (most recent on top):"
|
|
for func in ${FUNCNAME[*]}; do
|
|
if [ "$func" != "print_stack" ] && [ "$func" != "terminate" ]; then
|
|
echo -e "\\t$func"
|
|
fi
|
|
done
|
|
|
|
}
|
|
|
|
terminate() {
|
|
|
|
# since the library could be sourced and run from an interactive shell
|
|
# not only from a script, we cannot use exit in interactive shells since it
|
|
# would terminate the shell, so we are using parameter expansion with a fake
|
|
# parameter "param" to force an error when running interactive shells
|
|
local msg=$1
|
|
local param
|
|
case "$-" in
|
|
*i*) print_stack
|
|
: "${param:?"$msg, exiting..."}" ;;
|
|
*) print_stack
|
|
echo "$msg, exiting..."
|
|
exit 1;;
|
|
esac
|
|
|
|
}
|
|
|
|
validate_path() {
|
|
|
|
local path=$1
|
|
if [ -z "$path" ] || [ ! -d "$path" ]; then
|
|
terminate "Please provide a valid path"
|
|
fi
|
|
|
|
}
|
|
|
|
validate_item() {
|
|
|
|
local vfile=$1
|
|
if [ -z "$vfile" ] || [ ! -e "$vfile" ]; then
|
|
terminate "Please provide a valid file"
|
|
fi
|
|
|
|
}
|
|
|
|
validate_param() {
|
|
|
|
local param=$1
|
|
if [ -z "$param" ]; then
|
|
terminate "Mandatory parameter missing"
|
|
fi
|
|
|
|
}
|
|
|
|
# Writes to a file that is owned by root
|
|
# Parameters:
|
|
# - "-a": if set, the text will be appeneded to the file,
|
|
# otherwise will be overwritten
|
|
# - FILE: the path to the file to write to
|
|
# - STREAM: the content to be written
|
|
write_to_protected_file() {
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
write_to_protected_file [-a] <file> <stream>
|
|
|
|
Options:
|
|
-a If used the text will be appended to the file, otherwise it will be overwritten
|
|
EOM
|
|
return
|
|
fi
|
|
local arg
|
|
[ "$1" = "-a" ] && { arg=-a ; shift ; }
|
|
local file=${1?Missing output file in write_to_protected_file}
|
|
shift
|
|
printf "$@" | sudo tee $arg "$file" >/dev/null
|
|
|
|
}
|
|
|
|
# Exports environment variables that are dependent on the test environment
|
|
# Parameters:
|
|
# - ENV_NAME: the name of the test environment
|
|
set_env_variables() {
|
|
|
|
local env_name=$1
|
|
local path
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
set_env_variables <environment_name>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_path "$env_name"
|
|
path=$(dirname "$(realpath "$env_name")")
|
|
|
|
export SWUPD_OPTS="-S $path/$env_name/state -p $path/$env_name/target-dir -F staging -u file://$path/$env_name/web-dir -C $FUNC_DIR/Swupd_Root.pem -I"
|
|
export SWUPD_OPTS_NO_CERT="-S $path/$env_name/state -p $path/$env_name/target-dir -F staging -u file://$path/$env_name/web-dir"
|
|
export SWUPD_OPTS_MIRROR="-p $path/$env_name/target-dir"
|
|
export TEST_DIRNAME="$path"/"$env_name"
|
|
|
|
}
|
|
|
|
# Creates a directory with a hashed name in the specified path, if a directory
|
|
# already exists it returns the name
|
|
# Parameters:
|
|
# - PATH: the path where the directory will be created
|
|
create_dir() {
|
|
|
|
local path=$1
|
|
local hashed_name
|
|
local directory
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
create_dir <path>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_path "$path"
|
|
|
|
# most directories have the same hash, so we only need one directory
|
|
# in the files directory, if there is already one just return the path/name
|
|
directory=$(find "$path"/* -type d 2> /dev/null)
|
|
if [ ! "$directory" ]; then
|
|
sudo mkdir "$path"/testdir
|
|
hashed_name=$(sudo "$SWUPD" hashdump "$path"/testdir 2> /dev/null)
|
|
sudo mv "$path"/testdir "$path"/"$hashed_name"
|
|
# since tar is all we use, create a tar for the new dir
|
|
create_tar "$path"/"$hashed_name"
|
|
directory="$path"/"$hashed_name"
|
|
fi
|
|
echo "$directory"
|
|
|
|
}
|
|
|
|
# Generates a file with a hashed name in the specified path
|
|
# Parameters:
|
|
# - PATH: the path where the file will be created
|
|
create_file() {
|
|
|
|
local path=$1
|
|
local hashed_name
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
create_file <path>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_path "$path"
|
|
|
|
generate_random_content | sudo tee "$path/testfile" > /dev/null
|
|
hashed_name=$(sudo "$SWUPD" hashdump "$path"/testfile 2> /dev/null)
|
|
sudo mv "$path"/testfile "$path"/"$hashed_name"
|
|
# since tar is all we use, create a tar for the new file
|
|
create_tar "$path"/"$hashed_name"
|
|
echo "$path/$hashed_name"
|
|
|
|
}
|
|
|
|
# Creates a symbolic link with a hashed name to the specified file in the specified path.
|
|
# If no existing file is specified to point to, a new file will be created and pointed to
|
|
# by the link.
|
|
# If a file is provided but doesn't exist, then a dangling file will be created
|
|
# Parameters:
|
|
# - PATH: the path where the symbolic link will be created
|
|
# - FILE: the path to the file to point to
|
|
create_link() {
|
|
|
|
local path=$1
|
|
local pfile=$2
|
|
local hashed_name
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
create_link <path> [file_to_point_to]
|
|
EOM
|
|
return
|
|
fi
|
|
validate_path "$path"
|
|
|
|
# if no file is specified, create one
|
|
if [ -z "$pfile" ]; then
|
|
pfile=$(create_file "$path")
|
|
fi
|
|
sudo ln -rs "$pfile" "$path"/testlink
|
|
hashed_name=$(sudo "$SWUPD" hashdump "$path"/testlink 2> /dev/null)
|
|
sudo mv "$path"/testlink "$path"/"$hashed_name"
|
|
create_tar --skip-validation "$path"/"$hashed_name"
|
|
echo "$path/$hashed_name"
|
|
|
|
}
|
|
|
|
# Creates a tar for the specified item in the same location
|
|
# Parameters:
|
|
# - --skip-validation: if this flag is set (as first parameter) the other parameter
|
|
# is not validated, so use this option carefully
|
|
# - ITEM: the relative path to the item (file, directory, link, manifest)
|
|
create_tar() {
|
|
|
|
local path
|
|
local item_name
|
|
local skip_param_validation=false
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
create_tar [--skip-validation] <item>
|
|
|
|
Options:
|
|
--skip-validation If set, the function parameters will not be validated
|
|
EOM
|
|
return
|
|
fi
|
|
[ "$1" = "--skip-validation" ] && { skip_param_validation=true ; shift ; }
|
|
local item=$1
|
|
|
|
if [ "$skip_param_validation" = false ]; then
|
|
validate_item "$item"
|
|
fi
|
|
|
|
path=$(dirname "$(realpath "$item")")
|
|
item_name=$(basename "$item")
|
|
# if the item is a directory exclude its content when taring
|
|
if [ -d "$item" ]; then
|
|
sudo tar -C "$path" -cf "$path"/"$item_name".tar --exclude="$item_name"/* "$item_name"
|
|
else
|
|
sudo tar -C "$path" -cf "$path"/"$item_name".tar "$item_name"
|
|
fi
|
|
|
|
}
|
|
|
|
# Creates an empty manifest in the specified path
|
|
# Parameters:
|
|
# - PATH: the path where the manifest will be created
|
|
# - BUNDLE_NAME: the name of the bundle which this manifest will be for
|
|
create_manifest() {
|
|
|
|
local path=$1
|
|
local name=$2
|
|
local version
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
create_manifest <path> <bundle_name>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_path "$path"
|
|
validate_param "$name"
|
|
|
|
version=$(basename "$path")
|
|
{
|
|
printf 'MANIFEST\t1\n'
|
|
printf 'version:\t%s\n' "$version"
|
|
printf 'previous:\t0\n'
|
|
printf 'filecount:\t0\n'
|
|
printf 'timestamp:\t%s\n' "$(date +"%s")"
|
|
printf 'contentsize:\t0\n'
|
|
printf '\n'
|
|
} | sudo tee "$path"/Manifest."$name" > /dev/null
|
|
echo "$path/Manifest.$name"
|
|
|
|
}
|
|
|
|
# Adds the specified item to an existing bundle manifest
|
|
# Parameters:
|
|
# - --skip-validation: if this flag is set (as first parameter) the other parameters
|
|
# are not validated, so use this option carefully
|
|
# - MANIFEST: the relative path to the manifest file
|
|
# - ITEM: the relative path to the item (file, directory, symlink) to be added
|
|
# - ITEM_PATH: the absolute path of the item in the target system when installed
|
|
add_to_manifest() {
|
|
|
|
local item_type
|
|
local item_size
|
|
local name
|
|
local version
|
|
local filecount
|
|
local contentsize
|
|
local linked_file
|
|
local boot_type="."
|
|
local file_path
|
|
local skip_param_validation=false
|
|
[ "$1" = "--skip-validation" ] && { skip_param_validation=true ; shift ; }
|
|
local manifest=$1
|
|
local item=$2
|
|
local item_path=$3
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
add_to_manifest [--skip-validation] <manifest> <item> <item_path_in_fs>
|
|
|
|
Options:
|
|
--skip-validation If set, the validation of parameters will be skipped
|
|
EOM
|
|
return
|
|
fi
|
|
|
|
if [ "$skip_param_validation" = false ]; then
|
|
validate_item "$manifest"
|
|
validate_item "$item"
|
|
validate_param "$item_path"
|
|
fi
|
|
|
|
item_size=$(stat -c "%s" "$item")
|
|
name=$(basename "$item")
|
|
version=$(basename "$(dirname "$manifest")")
|
|
# add to filecount
|
|
filecount=$(awk '/filecount/ { print $2}' "$manifest")
|
|
filecount=$((filecount + 1))
|
|
sudo sed -i "s/filecount:.*/filecount:\\t$filecount/" "$manifest"
|
|
# add to contentsize
|
|
contentsize=$(sudo cat "$manifest" | grep contentsize | awk '{ print $2 }')
|
|
contentsize=$((contentsize + item_size))
|
|
# get the item type
|
|
if [ "$(basename "$manifest")" = Manifest.MoM ]; then
|
|
item_type=M
|
|
# MoM has a contentsize of 0, so don't increase this for MoM
|
|
contentsize=0
|
|
# files, directories and links are stored already hashed, but since
|
|
# manifests are not stored hashed, we need to calculate the hash
|
|
# of the manifest before adding it to the MoM
|
|
name=$(sudo "$SWUPD" hashdump "$item" 2> /dev/null)
|
|
elif [ -L "$item" ]; then
|
|
item_type=L
|
|
# when adding a link to a bundle, we need to make sure we add
|
|
# its associated file too, unless it is a dangling link
|
|
linked_file=$(readlink "$item")
|
|
if [ -e "$(dirname "$item")"/"$linked_file" ]; then
|
|
if [ ! "$(sudo cat "$manifest" | grep "$linked_file")" ]; then
|
|
file_path="$(dirname "$item_path")"
|
|
if [ "$file_path" = "/" ]; then
|
|
file_path=""
|
|
fi
|
|
add_to_manifest "$manifest" "$(dirname "$item")"/"$linked_file" "$file_path"/"$(generate_random_name test-file-)"
|
|
fi
|
|
fi
|
|
elif [ -f "$item" ]; then
|
|
item_type=F
|
|
elif [ -d "$item" ]; then
|
|
item_type=D
|
|
fi
|
|
# if the file is in the /usr/lib/kernel dir then it is a boot file
|
|
if [ "$(dirname "$item_path")" = "/usr/lib/kernel" ] || [ "$(dirname "$item_path")" = "/usr/lib/modules/" ]; then
|
|
boot_type="b"
|
|
fi
|
|
sudo sed -i "s/contentsize:.*/contentsize:\\t$contentsize/" "$manifest"
|
|
# add to manifest content
|
|
write_to_protected_file -a "$manifest" "$item_type.$boot_type.\\t$name\\t$version\\t$item_path\\n"
|
|
# If a manifest tar already exists for that manifest, renew the manifest tar
|
|
sudo rm -f "$manifest".tar
|
|
create_tar "$manifest"
|
|
# if the modified manifest is the MoM, sign it again since it has changed
|
|
if [ "$(basename "$manifest")" = Manifest.MoM ]; then
|
|
sudo rm -f "$manifest".sig
|
|
sign_manifest "$manifest"
|
|
fi
|
|
|
|
}
|
|
|
|
# Adds the specified bundle dependency to an existing bundle manifest
|
|
# Parameters:
|
|
# - MANIFEST: the relative path to the manifest file
|
|
# - DEPENDENCY: the name of the bundle to be included as a dependency
|
|
add_dependency_to_manifest() {
|
|
|
|
local manifest=$1
|
|
local dependency=$2
|
|
local path
|
|
local manifest_name
|
|
local bundle_name
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
add_dependency_to_manifest <manifest> <dependency>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_item "$manifest"
|
|
path=$(dirname "$manifest")
|
|
manifest_name=$(basename "$manifest")
|
|
bundle_name=${manifest_name#Manifest.}
|
|
|
|
sudo sed -i "/contentsize:.*/a includes:\\t$dependency" "$manifest"
|
|
# If a manifest tar already exists for that manifest, renew the manifest tar
|
|
sudo rm -f "$manifest".tar
|
|
create_tar "$manifest"
|
|
|
|
}
|
|
|
|
# Removes the specified item from an existing bundle manifest
|
|
# Parameters:
|
|
# - MANIFEST: the relative path to the manifest file
|
|
# - ITEM: either the hash or filename of the item to be removed
|
|
remove_from_manifest() {
|
|
|
|
local manifest=$1
|
|
local item=$2
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
remove_from_manifest <manifest> <item>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_item "$manifest"
|
|
validate_param "$item"
|
|
|
|
# replace every / with \/ in item (if any)
|
|
item="${item////\\/}"
|
|
# remove the lines that match from the manifest
|
|
sudo sed -i "/\\t$item$/d" "$manifest"
|
|
sudo sed -i "/\\t$item\\t/d" "$manifest"
|
|
# If a manifest tar already exists for that manifest, renew the manifest tar
|
|
sudo rm -f "$manifest".tar
|
|
create_tar "$manifest"
|
|
# if the modified manifest is the MoM, sign it again
|
|
if [ "$(basename "$manifest")" = Manifest.MoM ]; then
|
|
sudo rm -f "$manifest".sig
|
|
sign_manifest "$manifest"
|
|
fi
|
|
|
|
}
|
|
|
|
# Recalculate the hashes of the elements in the specified MoM and updates it
|
|
# if there are changes in hashes.
|
|
# Parameters:
|
|
# - MANIFEST: the path to the MoM to be updated
|
|
update_hashes_in_mom() {
|
|
|
|
local manifest=$1
|
|
local path
|
|
local bundle
|
|
local manifest_name
|
|
validate_item "$manifest"
|
|
path=$(dirname "$manifest")
|
|
|
|
IFS=$'\n'
|
|
if [ $(basename "$manifest") = Manifest.MoM ]; then
|
|
bundles=($(sudo cat "$manifest" | grep -x "M\.\.\..*" | awk '{ print $4 }'))
|
|
for bundle in ${bundles[*]}; do
|
|
remove_from_manifest "$manifest" $bundle
|
|
add_to_manifest "$manifest" "$path"/Manifest.$bundle $bundle
|
|
done
|
|
# since the MoM has changed, sign it again
|
|
sudo rm -f "$manifest".sig
|
|
sign_manifest "$manifest"
|
|
else
|
|
echo "The provided manifest is not the MoM"
|
|
return 1
|
|
fi
|
|
unset IFS
|
|
|
|
}
|
|
|
|
# Signs a manifest with a PEM key and generates the signed manifest in the same location
|
|
# Parameters:
|
|
# - MANIFEST: the path to the manifest to be signed
|
|
sign_manifest() {
|
|
|
|
local manifest=$1
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
sign_manifest <manifest>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_item "$manifest"
|
|
|
|
sudo openssl smime -sign -binary -in "$manifest" \
|
|
-signer "$FUNC_DIR"/Swupd_Root.pem \
|
|
-inkey "$FUNC_DIR"/private.pem \
|
|
-outform DER -out "$(dirname "$manifest")"/Manifest.MoM.sig
|
|
}
|
|
|
|
# Retrieves the hash value of a file or directory in a manifest
|
|
# Parameters:
|
|
# - MANIFEST: the manifest in which it will be looked at
|
|
# - ITEM: the dir or file to look for in the manifest
|
|
get_hash_from_manifest() {
|
|
|
|
local manifest=$1
|
|
local item=$2
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
get_hash_from_manifest <manifest> <name_in_fs>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_item "$manifest"
|
|
validate_param "$item"
|
|
|
|
hash=$(sudo cat "$manifest" | grep "$item" | awk '{ print $2 }')
|
|
echo "$hash"
|
|
|
|
}
|
|
|
|
# Sets the current version of the target system to the desired version
|
|
# Parameters:
|
|
# - ENVIRONMENT_NAME: the name of the test environmnt to act upon
|
|
# - NEW_VERSION: the version for the target to be set to
|
|
set_current_version() {
|
|
|
|
local env_name=$1
|
|
local new_version=$2
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
echo "$(cat <<-EOM
|
|
Usage:
|
|
set_current_version <environment_name> <new_version>
|
|
EOM
|
|
)"
|
|
return
|
|
fi
|
|
validate_path "$env_name"
|
|
|
|
sudo sed -i "s/VERSION_ID=.*/VERSION_ID=$new_version/" "$env_name"/target-dir/usr/lib/os-release
|
|
|
|
}
|
|
|
|
# Sets the latest version on the "server" to the desired version
|
|
# Parameters:
|
|
# - ENVIRONMENT_NAME: the name of the test environmnt to act upon
|
|
# - NEW_VERSION: the version for the target to be set to
|
|
set_latest_version() {
|
|
|
|
local env_name=$1
|
|
local new_version=$2
|
|
validate_path "$env_name"
|
|
|
|
write_to_protected_file "$env_name"/web-dir/version/formatstaging/latest "$new_version"
|
|
|
|
}
|
|
|
|
# Creates a test environment with the basic directory structure needed to
|
|
# validate the swupd client
|
|
# Parameters:
|
|
# - ENVIRONMENT_NAME: the name of the test environment, this should be typically the test name
|
|
# - VERSION: the version to use for the test environment, if not specified the default is 10
|
|
create_test_environment() {
|
|
|
|
local env_name=$1
|
|
local version=${2:-10}
|
|
local mom
|
|
validate_param "$env_name"
|
|
|
|
# create all the files and directories needed
|
|
# web-dir files & dirs
|
|
sudo mkdir -p "$env_name"/web-dir/version/formatstaging
|
|
write_to_protected_file "$env_name"/web-dir/version/formatstaging/latest "$version"
|
|
sudo mkdir -p "$env_name"/web-dir/"$version"/files
|
|
sudo mkdir -p "$env_name"/web-dir/"$version"/staged
|
|
mom=$(create_manifest "$env_name"/web-dir/"$version" MoM)
|
|
|
|
# target-dir files & dirs
|
|
sudo mkdir -p "$env_name"/target-dir/usr/lib
|
|
{
|
|
printf 'NAME="Clear Linux Software for Intel Architecture"\n'
|
|
printf 'VERSION=1\n'
|
|
printf 'ID=clear-linux-os\n'
|
|
printf 'VERSION_ID=%s\n' "$version"
|
|
printf 'PRETTY_NAME="Clear Linux Software for Intel Architecture"\n'
|
|
printf 'ANSI_COLOR="1;35"\n'
|
|
printf 'HOME_URL="https://clearlinux.org"\n'
|
|
printf 'SUPPORT_URL="https://clearlinux.org"\n'
|
|
printf 'BUG_REPORT_URL="https://bugs.clearlinux.org/jira"\n'
|
|
} | sudo tee "$env_name"/target-dir/usr/lib/os-release > /dev/null
|
|
sudo mkdir -p "$env_name"/target-dir/usr/share/clear/bundles
|
|
sudo mkdir -p "$env_name"/target-dir/usr/share/defaults/swupd
|
|
printf '1' | sudo tee "$env_name"/target-dir/usr/share/defaults/swupd/format > /dev/null
|
|
sudo mkdir -p "$env_name"/target-dir/etc
|
|
|
|
# state files & dirs
|
|
sudo mkdir -p "$env_name"/state/{staged,download,delta,telemetry}
|
|
sudo chmod -R 0700 "$env_name"/state
|
|
|
|
# export environment variables that are dependent of the test env
|
|
set_env_variables "$env_name"
|
|
|
|
# every environment needs to have at least the os-core bundle so this should be
|
|
# added by default to every test environment
|
|
create_bundle -L -n os-core -v "$version" -f /usr/bin/core "$env_name"
|
|
|
|
}
|
|
|
|
# Destroys a test environment
|
|
# Parameters:
|
|
# - ENVIRONMENT_NAME: the name of the test environment to be deleted
|
|
destroy_test_environment() {
|
|
|
|
local env_name=$1
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
destroy_test_environment <environment_name>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_path "$env_name"
|
|
|
|
# since the action to be performed is very destructive, at least
|
|
# make sure the directory does look like a test environment
|
|
for var in "state" "target-dir" "web-dir"; do
|
|
if [ ! -d "$env_name/$var" ]; then
|
|
echo "The name provided doesn't seem to be a valid test environment"
|
|
return 1
|
|
fi
|
|
done
|
|
sudo rm -rf "$env_name"
|
|
|
|
}
|
|
|
|
# Creates a bundle in the test environment. The bundle can contain files, directories or symlinks.
|
|
create_bundle() {
|
|
|
|
cb_usage() {
|
|
cat <<-EOM
|
|
Usage:
|
|
create_bundle [-L] [-n] <bundle_name> [-v] <version> [-d] <list of dirs> [-f] <list of files> [-l] <list of links> ENV_NAME
|
|
|
|
Options:
|
|
-L When the flag is selected the bundle will be 'installed' in the target-dir, otherwise it will only be created in web-dir
|
|
-n The name of the bundle to be created, if not specified a name will be autogenerated
|
|
-v The version for the bundle, if non selected version 10 will be used
|
|
-d Comma-separated list of directories to be included in the bundle
|
|
-f Comma-separated list of files to be created and included in the bundle
|
|
-l Comma-separated list of symlinks to be created and included in the bundle
|
|
-b Comma-separated list of dangling (broken) symlinks to be created and included in the bundle
|
|
|
|
Notes:
|
|
- if no option is selected, a minimal bundle will be created with only one directory
|
|
- for every symlink created a related file will be created and added to the bundle as well (except for dangling links)
|
|
- if the '-f' or '-l' options are used, and the directories where the files live don't exist,
|
|
they will be automatically created and added to the bundle for each file
|
|
|
|
Example of usage:
|
|
|
|
The following command will create a bundle named 'test-bundle', which will include three directories,
|
|
four files, and one symlink (they will be added to the bundle's manifest), all these resources will also
|
|
be tarred. The manifest will be added to the MoM.
|
|
|
|
create_bundle -n test-bundle -f /usr/bin/test-1,/usr/bin/test-2,/etc/systemd/test-3 -l /etc/test-link my_test_env
|
|
|
|
EOM
|
|
}
|
|
|
|
local OPTIND
|
|
local opt
|
|
local dir_list
|
|
local file_list
|
|
local link_list
|
|
local dangling_link_list
|
|
local version
|
|
local bundle_name
|
|
local env_name
|
|
local files_path
|
|
local version_path
|
|
local manifest
|
|
local local_bundle=false
|
|
|
|
# If no parameters are received show help
|
|
if [ $# -eq 0 ]; then
|
|
create_bundle -h
|
|
return
|
|
fi
|
|
set -f # turn off globbing
|
|
while getopts :v:d:f:l:b:n:L opt; do
|
|
case "$opt" in
|
|
d) IFS=, read -r -a dir_list <<< "$OPTARG" ;;
|
|
f) IFS=, read -r -a file_list <<< "$OPTARG" ;;
|
|
l) IFS=, read -r -a link_list <<< "$OPTARG" ;;
|
|
b) IFS=, read -r -a dangling_link_list <<< "$OPTARG" ;;
|
|
n) bundle_name="$OPTARG" ;;
|
|
v) version="$OPTARG" ;;
|
|
L) local_bundle=true ;;
|
|
*) cb_usage
|
|
return ;;
|
|
esac
|
|
done
|
|
set +f # turn globbing back on
|
|
env_name=${@:$OPTIND:1}
|
|
|
|
# set default values
|
|
bundle_name=${bundle_name:-$(generate_random_name test-bundle-)}
|
|
version=${version:-10}
|
|
if [ -z "$dir_list" ] && [ -z "$file_list" ] && [ -z "$link_list" ] && [ -z "$dangling_link_list" ] ; then
|
|
# if nothing was specified to be created, at least create
|
|
# one directory which is the bare minimum for a bundle
|
|
dir_list=(/usr/bin)
|
|
fi
|
|
# all bundles should include their own tracking file, so append it to the
|
|
# list of files to be created in the bundle
|
|
file_list+=(/usr/share/clear/bundles/"$bundle_name")
|
|
|
|
# get useful paths
|
|
validate_path "$env_name"
|
|
version_path="$env_name"/web-dir/"$version"
|
|
files_path="$version_path"/files
|
|
target_path="$env_name"/target-dir
|
|
|
|
# 1) create the initial manifest
|
|
manifest=$(create_manifest "$version_path" "$bundle_name")
|
|
if [ "$DEBUG" == true ]; then
|
|
echo "Manifest -> $manifest"
|
|
fi
|
|
|
|
# 2) Create one directory for the bundle and add it the requested
|
|
# times to the manifest.
|
|
# Every bundle has to have at least one directory,
|
|
# hashes in directories vary depending on owner and permissions,
|
|
# so one directory hash can be reused many times
|
|
bundle_dir=$(create_dir "$files_path")
|
|
if [ "$DEBUG" == true ]; then
|
|
echo "Directory -> $bundle_dir"
|
|
fi
|
|
# Create a zero pack for the bundle and add the directory to it
|
|
sudo tar -cf "$version_path"/pack-"$bundle_name"-from-0.tar --exclude="$bundle_dir"/* "$bundle_dir"
|
|
for val in "${dir_list[@]}"; do
|
|
# the "/" directory is not allowed in the manifest
|
|
if [ val != "/" ]; then
|
|
add_to_manifest "$manifest" "$bundle_dir" "$val"
|
|
if [ "$local_bundle" = true ]; then
|
|
sudo mkdir -p "$target_path$val"
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# 3) Create the requested file(s)
|
|
for val in "${file_list[@]}"; do
|
|
# if the directories the file is don't exist, add them to the bundle
|
|
# there are 2 exceptions, the dir of the tracking file and "\"
|
|
fdir=$(dirname "$val")
|
|
if [ ! "$(sudo cat "$manifest" | grep -x "D\\.\\.\\..*$fdir")" ] && [ "$fdir" != "/usr/share/clear/bundles" ] && [ "$fdir" != "/" ]; then
|
|
bundle_dir=$(create_dir "$files_path")
|
|
add_to_manifest "$manifest" "$bundle_dir" "$fdir"
|
|
# add each one of the directories of the path if they are not in the manifest already
|
|
while [ "$(dirname "$fdir")" != "/" ]; do
|
|
fdir=$(dirname "$fdir")
|
|
if [ ! "$(sudo cat "$manifest" | grep -x "D\\.\\.\\..*$fdir")" ]; then
|
|
add_to_manifest "$manifest" "$bundle_dir" "$fdir"
|
|
fi
|
|
done
|
|
fi
|
|
bundle_file=$(create_file "$files_path")
|
|
if [ "$DEBUG" == true ]; then
|
|
echo "file -> $bundle_file"
|
|
fi
|
|
add_to_manifest "$manifest" "$bundle_file" "$val"
|
|
# Add the file to the zero pack of the bundle
|
|
sudo tar -rf "$version_path"/pack-"$bundle_name"-from-0.tar "$bundle_file"
|
|
# if the local_bundle flag is set, copy the files to the target-dir as if the
|
|
# bundle had been locally installed
|
|
if [ "$local_bundle" = true ]; then
|
|
sudo mkdir -p "$target_path$(dirname "$val")"
|
|
sudo cp "$bundle_file" "$target_path$val"
|
|
fi
|
|
done
|
|
|
|
# 4) Create the requested link(s) in the bundle
|
|
for val in "${link_list[@]}"; do
|
|
# if the directory the link is doesn't exist,
|
|
# add it to the bundle (except if the directory is "/")
|
|
fdir=$(dirname "$val")
|
|
if [ "$fdir" != "/" ]; then
|
|
if [ ! "$(sudo cat "$manifest" | grep -x "D\\.\\.\\..*$fdir")" ]; then
|
|
bundle_dir=$(create_dir "$files_path")
|
|
add_to_manifest "$manifest" "$bundle_dir" "$fdir"
|
|
fi
|
|
fi
|
|
bundle_link=$(create_link "$files_path")
|
|
sudo tar -rf "$version_path"/pack-"$bundle_name"-from-0.tar "$bundle_link"
|
|
add_to_manifest "$manifest" "$bundle_link" "$val"
|
|
# Add the file pointed by the link to the zero pack of the bundle
|
|
pfile=$(basename "$(readlink -f "$bundle_link")")
|
|
sudo tar -rf "$version_path"/pack-"$bundle_name"-from-0.tar "$files_path"/"$pfile"
|
|
if [ "$DEBUG" == true ]; then
|
|
echo "link -> $bundle_link"
|
|
echo "file pointed to -> $(readlink -f "$bundle_link")"
|
|
fi
|
|
if [ "$local_bundle" = true ]; then
|
|
sudo mkdir -p "$target_path$(dirname "$val")"
|
|
# if local_bundle is enabled copy the link to target-dir but also
|
|
# copy the file it points to
|
|
pfile_path=$(awk "/$(basename $pfile)/"'{ print $4 }' "$manifest")
|
|
sudo cp "$files_path"/"$pfile" "$target_path$pfile_path"
|
|
sudo ln -rs "$target_path$pfile_path" "$target_path$val"
|
|
fi
|
|
done
|
|
|
|
# 5) Create the requested dangling link(s) in the bundle
|
|
for val in "${dangling_link_list[@]}"; do
|
|
# if the directory the link is doesn't exist,
|
|
# add it to the bundle (except if the directory is "/")
|
|
fdir=$(dirname "$val")
|
|
if [ "$fdir" != "/" ]; then
|
|
if [ ! "$(sudo cat "$manifest" | grep -x "D\\.\\.\\..*$fdir")" ]; then
|
|
bundle_dir=$(create_dir "$files_path")
|
|
add_to_manifest "$manifest" "$bundle_dir" "$fdir"
|
|
fi
|
|
fi
|
|
# Create a link passing a file that does not exits
|
|
bundle_link=$(create_link "$files_path" "$files_path"/"$(generate_random_name does_not_exist-)")
|
|
sudo tar -rf "$version_path"/pack-"$bundle_name"-from-0.tar "$bundle_link"
|
|
add_to_manifest --skip-validation "$manifest" "$bundle_link" "$val"
|
|
# Add the file pointed by the link to the zero pack of the bundle
|
|
if [ "$DEBUG" == true ]; then
|
|
echo "dangling link -> $bundle_link"
|
|
fi
|
|
if [ "$local_bundle" = true ]; then
|
|
sudo mkdir -p "$target_path$(dirname "$val")"
|
|
# if local_bundle is enabled since we cannot copy a bad link create a new one
|
|
# in the appropriate location in target-dir with the corrent name
|
|
sudo ln -s "$(generate_random_name /does_not_exist-)" "$target_path$val"
|
|
fi
|
|
done
|
|
|
|
# 6) Add the bundle to the MoM
|
|
add_to_manifest "$version_path"/Manifest.MoM "$manifest" "$bundle_name"
|
|
|
|
# 7) Create/renew manifest tars
|
|
sudo rm -f "$version_path"/Manifest.MoM.tar
|
|
create_tar "$version_path"/Manifest.MoM
|
|
|
|
# 8) Sign the manifest MoM
|
|
sign_manifest "$version_path"/Manifest.MoM
|
|
|
|
# 9) Create the subscription to the bundle if the local_bundle flag is enabled
|
|
if [ "$local_bundle" = true ]; then
|
|
sudo touch "$target_path"/usr/share/clear/bundles/"$bundle_name"
|
|
fi
|
|
|
|
}
|
|
|
|
# Removes a bundle from the target-dir and/or the web-dir
|
|
# Parameters
|
|
# - -L: if this option is set the bundle is removed from the target-dir only,
|
|
# otherwise it is removed from target-dir and web-dir
|
|
# - BUNDLE_MANIFEST: the manifest of the bundle to be removed
|
|
remove_bundle() {
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
remove_bundle [-L] <bundle_manifest>
|
|
|
|
Options:
|
|
-L If set, the bundle will be removed from the target-dir only,
|
|
otherwise it is removed from both target-dir and web-dir
|
|
EOM
|
|
return
|
|
fi
|
|
local remove_local=false
|
|
[ "$1" = "-L" ] && { remove_local=true ; shift ; }
|
|
local bundle_manifest=$1
|
|
local target_path
|
|
local version_path
|
|
local bundle_name
|
|
local file_names
|
|
local dir_names
|
|
local manifest_file
|
|
|
|
# if the bundle's manifest is not found just return
|
|
if [ ! -e "$bundle_manifest" ]; then
|
|
echo "$(basename $bundle_manifest) not found, maybe the bundle was already removed"
|
|
return
|
|
fi
|
|
|
|
target_path=$(dirname "$bundle_manifest" | cut -d "/" -f1)/target-dir
|
|
version_path=$(dirname "$bundle_manifest")
|
|
manifest_file=$(basename "$bundle_manifest")
|
|
bundle_name=${manifest_file#Manifest.}
|
|
|
|
# remove all files that are in the manifest from target-dir first
|
|
file_names=($(awk '/^[FL]...\t/ { print $4 }' "$bundle_manifest"))
|
|
for fname in ${file_names[@]}; do
|
|
sudo rm -f "$target_path$fname"
|
|
done
|
|
# now remove all directories in the manifest (only if empty else they
|
|
# may be used by another bundle)
|
|
dir_names=($(awk '/^D...\t/ { print $4 }' "$bundle_manifest"))
|
|
for dname in ${dir_names[@]}; do
|
|
sudo rmdir "$target_path$dname" 2> /dev/null
|
|
done
|
|
if [ "$remove_local" = false ]; then
|
|
# remove all files that are in the manifest from web-dir
|
|
file_names=($(awk '/^[FL]...\t/ { print $2 }' "$bundle_manifest"))
|
|
for fname in ${file_names[@]}; do
|
|
sudo rm "$version_path"/files/"$fname"
|
|
sudo rm "$version_path"/files/"$fname".tar
|
|
done
|
|
# remove zero pack
|
|
sudo rm "$version_path"/pack-"$bundle_name"-from-0.tar
|
|
# finally remove the manifest
|
|
sudo rm "$version_path"/"$manifest_file"
|
|
sudo rm "$version_path"/"$manifest_file".tar
|
|
fi
|
|
|
|
}
|
|
|
|
# Installs a bundle in target-dir
|
|
# Parameters:
|
|
# - BUNDLE_MANIFEST: the manifest of the bundle to be installed
|
|
|
|
install_bundle() {
|
|
|
|
local bundle_manifest=$1
|
|
local target_path
|
|
local file_names
|
|
local dir_names
|
|
local link_names
|
|
local fhash
|
|
local lhash
|
|
local fdir
|
|
local manifest_file
|
|
local bundle_name
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
install_bundle <bundle_manifest>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_item "$bundle_manifest"
|
|
target_path=$(dirname "$bundle_manifest" | cut -d "/" -f1)/target-dir
|
|
files_path=$(dirname "$bundle_manifest")/files
|
|
manifest_file=$(basename "$bundle_manifest")
|
|
bundle_name=${manifest_file#Manifest.}
|
|
|
|
# make sure the bundle is not already installed
|
|
if [ -e "$target_path"/usr/share/clear/bundles/"$bundle_name" ]; then
|
|
return
|
|
fi
|
|
|
|
# iterate through the manifest and copy all the files in its
|
|
# correct place, start with directories
|
|
dir_names=($(awk '/^D...\t/ { print $4 }' "$bundle_manifest"))
|
|
for dname in ${dir_names[@]}; do
|
|
sudo mkdir -p "$target_path$dname"
|
|
done
|
|
# now files
|
|
file_names=($(awk '/^F...\t/ { print $4 }' "$bundle_manifest"))
|
|
for fname in ${file_names[@]}; do
|
|
fhash=$(get_hash_from_manifest "$bundle_manifest" "$fname")
|
|
sudo cp "$files_path"/"$fhash" "$target_path$fname"
|
|
done
|
|
# finally links
|
|
link_names=($(awk '/^L...\t/ { print $4 }' "$bundle_manifest"))
|
|
for lname in ${link_names[@]}; do
|
|
lhash=$(get_hash_from_manifest "$bundle_manifest" "$lname")
|
|
fhash=$(readlink "$files_path"/"$lhash")
|
|
# is the original link dangling?
|
|
if [[ $fhash = *"does_not_exist"* ]]; then
|
|
sudo ln -s "$fhash" "$target_path$lname"
|
|
else
|
|
fname=$(awk "/$fhash/ "'{ print $4 }' "$bundle_manifest")
|
|
sudo ln -s $(basename "$fname") "$target_path$lname"
|
|
fi
|
|
done
|
|
|
|
}
|
|
|
|
# Cleans up the directories in the state dir
|
|
# Parameters:
|
|
# - ENV_NAME: the name of the test environment to have the state dir cleaned up
|
|
clean_state_dir() {
|
|
|
|
local env_name=$1
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
clean_state_dir <environment_name>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_path "$env_name"
|
|
|
|
sudo rm -rf "$env_name"/state/{staged,download,delta,telemetry}
|
|
sudo mkdir -p "$env_name"/state/{staged,download,delta,telemetry}
|
|
sudo chmod -R 0700 "$env_name"/state
|
|
|
|
}
|
|
|
|
# Creates a new test case based on a template
|
|
# Parameters:
|
|
# - NAME: the name (and path) of the test to be generated
|
|
generate_test() {
|
|
|
|
local name=$1
|
|
local path
|
|
|
|
# If no parameters are received show usage
|
|
if [ $# -eq 0 ]; then
|
|
cat <<-EOM
|
|
Usage:
|
|
generate_test <test_name>
|
|
EOM
|
|
return
|
|
fi
|
|
validate_param "$name"
|
|
|
|
path=$(dirname "$name")/
|
|
name=$(basename "$name")
|
|
|
|
{
|
|
printf '#!/usr/bin/env bats\n\n'
|
|
printf 'load "../testlib"\n\n'
|
|
printf 'global_setup() {\n\n'
|
|
printf '\t# global setup\n\n'
|
|
printf '}\n\n'
|
|
printf 'test_setup() {\n\n'
|
|
printf '\t# create_test_environment "$TEST_NAME"\n'
|
|
printf '\t# create_bundle -n <bundle_name> -f <file_1>,<file_2>,<file_N> "$TEST_NAME"\n\n'
|
|
printf '}\n\n'
|
|
printf 'test_teardown() {\n\n'
|
|
printf '\t# destroy_test_environment "$TEST_NAME"\n\n'
|
|
printf '}\n\n'
|
|
printf 'global_teardown() {\n\n'
|
|
printf '\t# global cleanup\n\n'
|
|
printf '}\n\n'
|
|
printf '@test "<test description>" {\n\n'
|
|
printf '\trun sudo sh -c "$SWUPD <swupd_command> $SWUPD_OPTS <command_options>"\n'
|
|
printf '\t# <validations>\n\n'
|
|
printf '}\n\n'
|
|
} > "$path$name".bats
|
|
# make the test script executable
|
|
chmod +x "$path$name".bats
|
|
|
|
}
|
|
|
|
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
# The section below contains test fixtures that can be used from tests to create and
|
|
# cleanup test dependencies, these functions can be overwritten in the test script.
|
|
# The intention of these is to try reducing the amount of boilerplate included in
|
|
# tests since all tests require at least the creation of a test environment
|
|
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
setup() {
|
|
|
|
# the first time setup runs, run the global_setup
|
|
if [ "$BATS_TEST_NUMBER" -eq 1 ]; then
|
|
global_setup
|
|
fi
|
|
# in case the env was created in global_setup set environment variables
|
|
if [ -d "$TEST_NAME" ]; then
|
|
set_env_variables "$TEST_NAME"
|
|
fi
|
|
test_setup
|
|
|
|
}
|
|
|
|
teardown() {
|
|
|
|
test_teardown
|
|
# if the last test just ran, run the global teardown
|
|
if [ "$BATS_TEST_NUMBER" -eq "${#BATS_TEST_NAMES[@]}" ]; then
|
|
global_teardown
|
|
fi
|
|
|
|
}
|
|
|
|
global_setup() {
|
|
|
|
# dummy value in case function is not defined
|
|
return
|
|
|
|
}
|
|
|
|
global_teardown() {
|
|
|
|
# dummy value in case function is not defined
|
|
return
|
|
|
|
}
|
|
|
|
# Default test_setup
|
|
test_setup() {
|
|
|
|
create_test_environment "$TEST_NAME"
|
|
|
|
}
|
|
|
|
# Default test_teardown
|
|
test_teardown() {
|
|
|
|
if [ "$DEBUG_TEST" != true ]; then
|
|
destroy_test_environment "$TEST_NAME"
|
|
fi
|
|
|
|
}
|
|
|
|
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
# The section below contains functions useful for consistent test validation and output
|
|
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
sep="------------------------------------------------------------------"
|
|
|
|
print_assert_failure() {
|
|
|
|
local message=$1
|
|
validate_param "$message"
|
|
|
|
echo -e "\\nAssertion Failed"
|
|
echo -e "$message"
|
|
echo "Command output:"
|
|
echo "------------------------------------------------------------------"
|
|
echo "$output"
|
|
echo "------------------------------------------------------------------"
|
|
|
|
}
|
|
|
|
use_ignore_list() {
|
|
|
|
local ignore_enabled=$1
|
|
local filtered_output
|
|
validate_param "$ignore_enabled"
|
|
|
|
# if selected, remove things in the ignore list from the actual output
|
|
if [ "$ignore_enabled" = true ]; then
|
|
# always remove blank lines and lines with only dots
|
|
filtered_output=$(echo "$output" | sed -E '/^$/d' | sed -E '/^\.+$/d')
|
|
# now remove lines that are included in any of the ignore-lists
|
|
# there are 3 possible ignore-lists that the function is going
|
|
# to recognize (in order of precedence):
|
|
# - <functional_tests_directory>/<test_theme_directory>/<test_name>.ignore-list
|
|
# - <functional_tests_directory>/<test_theme_directory>/ignore-list
|
|
# - <functional_tests_directory>/ignore-list
|
|
if [ -f "$THEME_DIRNAME"/"$TEST_NAME".ignore-list ]; then
|
|
ignore_list="$THEME_DIRNAME"/"$TEST_NAME".ignore-list
|
|
elif [ -f "$THEME_DIRNAME"/ignore-list ]; then
|
|
ignore_list="$THEME_DIRNAME"/ignore-list
|
|
elif [ -f "$FUNC_DIR"/ignore-list ]; then
|
|
ignore_list="$FUNC_DIR"/ignore-list.global
|
|
fi
|
|
while IFS= read -r line; do
|
|
# if the pattern from the file has a "/" escape it first so it does
|
|
# not confuses the sed command
|
|
line="${line////\\/}"
|
|
filtered_output=$(echo "$filtered_output" | sed -E "/^$line$/d")
|
|
done < "$ignore_list"
|
|
else
|
|
filtered_output="$output"
|
|
fi
|
|
echo "$filtered_output"
|
|
|
|
}
|
|
|
|
assert_status_is() {
|
|
|
|
local expected_status=$1
|
|
validate_param "$expected_status"
|
|
|
|
if [ -z "$status" ]; then
|
|
echo "The \$status environment variable is empty."
|
|
echo "Please make sure this assertion is used inside a BATS test after a 'run' command."
|
|
return 1
|
|
fi
|
|
|
|
if [ ! "$status" -eq "$expected_status" ]; then
|
|
print_assert_failure "Expected status: $expected_status\\nActual status: $status"
|
|
return 1
|
|
else
|
|
# if the assertion was successful show the output only if the user
|
|
# runs the test with the -t flag
|
|
echo -e "\\nCommand output:" >&3
|
|
echo "------------------------------------------------------------------" >&3
|
|
echo "$output" >&3
|
|
echo -e "------------------------------------------------------------------\\n" >&3
|
|
fi
|
|
|
|
}
|
|
|
|
assert_status_is_not() {
|
|
|
|
local not_expected_status=$1
|
|
validate_param "$not_expected_status"
|
|
|
|
if [ -z "$status" ]; then
|
|
echo "The \$status environment variable is empty."
|
|
echo "Please make sure this assertion is used inside a BATS test after a 'run' command."
|
|
return 1
|
|
fi
|
|
|
|
if [ "$status" -eq "$not_expected_status" ]; then
|
|
print_assert_failure "Status expected to be different than: $not_expected_status\\nActual status: $status"
|
|
return 1
|
|
else
|
|
# if the assertion was successful show the output only if the user
|
|
# runs the test with the -t flag
|
|
echo -e "\\nCommand output:" >&3
|
|
echo "------------------------------------------------------------------" >&3
|
|
echo "$output" >&3
|
|
echo -e "------------------------------------------------------------------\\n" >&3
|
|
fi
|
|
|
|
}
|
|
|
|
assert_dir_exists() {
|
|
|
|
local vdir=$1
|
|
validate_param "$vdir"
|
|
|
|
if [ ! -d "$vdir" ]; then
|
|
print_assert_failure "Directory $vdir should exist, but it does not"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_dir_not_exists() {
|
|
|
|
local vdir=$1
|
|
validate_param "$vdir"
|
|
|
|
if [ -d "$vdir" ]; then
|
|
print_assert_failure "Directory $vdir should not exist, but it does"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_file_exists() {
|
|
|
|
local vfile=$1
|
|
validate_param "$vfile"
|
|
|
|
if [ ! -f "$vfile" ]; then
|
|
print_assert_failure "File $vfile should exist, but it does not"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_file_not_exists() {
|
|
|
|
local vfile=$1
|
|
validate_param "$vfile"
|
|
|
|
if [ -f "$vfile" ]; then
|
|
print_assert_failure "File $vfile should not exist, but it does"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_in_output() {
|
|
|
|
local actual_output
|
|
local ignore_switch=true
|
|
local ignore_list
|
|
[ "$1" = "--identical" ] && { ignore_switch=false ; shift ; }
|
|
local expected_output=$1
|
|
validate_param "$expected_output"
|
|
|
|
actual_output=$(use_ignore_list "$ignore_switch")
|
|
if [[ ! "$actual_output" == *"$expected_output"* ]]; then
|
|
print_assert_failure "The following text was not found in the command output:\\n$sep\\n$expected_output\\n$sep"
|
|
echo -e "Difference:\\n$sep"
|
|
echo "$(diff -u <(echo "$expected_output") <(echo "$actual_output"))"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_not_in_output() {
|
|
|
|
local actual_output
|
|
local ignore_switch=true
|
|
local ignore_list
|
|
[ "$1" = "--identical" ] && { ignore_switch=false ; shift ; }
|
|
local expected_output=$1
|
|
validate_param "$expected_output"
|
|
|
|
actual_output=$(use_ignore_list "$ignore_switch")
|
|
if [[ "$actual_output" == *"$expected_output"* ]]; then
|
|
print_assert_failure "The following text was found in the command output and should not have:\\n$sep\\n$expected_output\\n$sep"
|
|
echo -e "Difference:\\n$sep"
|
|
echo "$(diff -u <(echo "$expected_output") <(echo "$actual_output"))"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_is_output() {
|
|
|
|
local actual_output
|
|
local ignore_switch=true
|
|
local ignore_list
|
|
[ "$1" = "--identical" ] && { ignore_switch=false ; shift ; }
|
|
local expected_output=$1
|
|
validate_param "$expected_output"
|
|
|
|
actual_output=$(use_ignore_list "$ignore_switch")
|
|
if [[ ! "$actual_output" == "$expected_output" ]]; then
|
|
print_assert_failure "The following text was not the command output:\\n$sep\\n$expected_output\\n$sep"
|
|
echo -e "Difference:\\n$sep"
|
|
echo "$(diff -u <(echo "$expected_output") <(echo "$actual_output"))"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_is_not_output() {
|
|
|
|
local actual_output
|
|
local ignore_switch=true
|
|
local ignore_list
|
|
[ "$1" = "--identical" ] && { ignore_switch=false ; shift ; }
|
|
local expected_output=$1
|
|
validate_param "$expected_output"
|
|
|
|
actual_output=$(use_ignore_list "$ignore_switch")
|
|
if [[ "$actual_output" == "$expected_output" ]]; then
|
|
print_assert_failure "The following text was the command output and should not have:\\n$sep\\n$expected_output\\n$sep"
|
|
echo -e "Difference:\\n$sep"
|
|
echo "$(diff -u <(echo "$expected_output") <(echo "$actual_output"))"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_regex_in_output() {
|
|
|
|
local actual_output
|
|
local ignore_switch=true
|
|
local ignore_list
|
|
[ "$1" = "--identical" ] && { ignore_switch=false ; shift ; }
|
|
local expected_output=$1
|
|
validate_param "$expected_output"
|
|
|
|
actual_output=$(use_ignore_list "$ignore_switch")
|
|
if [[ ! "$actual_output" =~ $expected_output ]]; then
|
|
print_assert_failure "The following text (regex) was not found in the command output:\\n$sep\\n$expected_output\\n$sep"
|
|
echo -e "Difference:\\n$sep"
|
|
echo "$(diff -u <(echo "$expected_output") <(echo "$actual_output"))"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_regex_not_in_output() {
|
|
|
|
local actual_output
|
|
local ignore_switch=true
|
|
local ignore_list
|
|
[ "$1" = "--identical" ] && { ignore_switch=false ; shift ; }
|
|
local expected_output=$1
|
|
validate_param "$expected_output"
|
|
|
|
actual_output=$(use_ignore_list "$ignore_switch")
|
|
if [[ "$actual_output" =~ $expected_output ]]; then
|
|
print_assert_failure "The following text (regex) was found in the command output and should not have:\\n$sep\\n$expected_output\\n$sep"
|
|
echo -e "Difference:\\n$sep"
|
|
echo "$(diff -u <(echo "$expected_output") <(echo "$actual_output"))"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_regex_is_output() {
|
|
|
|
local actual_output
|
|
local ignore_switch=true
|
|
local ignore_list
|
|
[ "$1" = "--identical" ] && { ignore_switch=false ; shift ; }
|
|
local expected_output=$1
|
|
validate_param "$expected_output"
|
|
|
|
actual_output=$(use_ignore_list "$ignore_switch")
|
|
if [[ ! "$actual_output" =~ ^$expected_output$ ]]; then
|
|
print_assert_failure "The following text (regex) was not the command output:\\n$sep\\n$expected_output\\n$sep"
|
|
echo -e "Difference:\\n$sep"
|
|
echo "$(diff -u <(echo "$expected_output") <(echo "$actual_output"))"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_regex_is_not_output() {
|
|
|
|
local actual_output
|
|
local ignore_switch=true
|
|
local ignore_list
|
|
[ "$1" = "--identical" ] && { ignore_switch=false ; shift ; }
|
|
local expected_output=$1
|
|
validate_param "$expected_output"
|
|
|
|
actual_output=$(use_ignore_list "$ignore_switch")
|
|
if [[ "$actual_output" =~ ^$expected_output$ ]]; then
|
|
print_assert_failure "The following text (regex) was the command output and should not have:\\n$sep\\n$expected_output\\n$sep"
|
|
echo -e "Difference:\\n$sep"
|
|
echo "$(diff -u <(echo "$expected_output") <(echo "$actual_output"))"
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_equal() {
|
|
|
|
local val1=$1
|
|
local val2=$2
|
|
validate_param "$val1"
|
|
validate_param "$val2"
|
|
|
|
if [ "$val1" != "$val2" ]; then
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_not_equal() {
|
|
|
|
local val1=$1
|
|
local val2=$2
|
|
validate_param "$val1"
|
|
validate_param "$val2"
|
|
|
|
if [ "$val1" = "$val2" ]; then
|
|
return 1
|
|
fi
|
|
|
|
}
|
|
|
|
assert_files_equal() {
|
|
|
|
local val1=$1
|
|
local val2=$2
|
|
validate_item "$val1"
|
|
validate_item "$val2"
|
|
|
|
diff -q "$val1" "$val2"
|
|
|
|
}
|
|
|
|
assert_files_not_equal() {
|
|
|
|
local val1=$1
|
|
local val2=$2
|
|
validate_item "$val1"
|
|
validate_item "$val2"
|
|
|
|
if diff -q "$val1" "$val2" > /dev/null; then
|
|
echo "Files $val1 and $val2 are equal"
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
|
|
}
|