ew_base/Build/Scripts/additionalTests.sh
2024-05-26 20:41:01 +02:00

413 lines
13 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# TYPO3 core test runner based on docker or podman
#
trap 'cleanUp;exit 2' SIGINT
waitFor() {
local HOST=${1}
local PORT=${2}
local TESTCOMMAND="
COUNT=0;
while ! nc -z ${HOST} ${PORT}; do
if [ \"\${COUNT}\" -gt 10 ]; then
echo \"Can not connect to ${HOST} port ${PORT}. Aborting.\";
exit 1;
fi;
sleep 1;
COUNT=\$((COUNT + 1));
done;
"
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name wait-for-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_ALPINE} /bin/sh -c "${TESTCOMMAND}"
if [[ $? -gt 0 ]]; then
kill -SIGINT -$$
fi
}
cleanUp() {
ATTACHED_CONTAINERS=$(${CONTAINER_BIN} ps --filter network=${NETWORK} --format='{{.Names}}')
for ATTACHED_CONTAINER in ${ATTACHED_CONTAINERS}; do
${CONTAINER_BIN} kill ${ATTACHED_CONTAINER} >/dev/null
done
if [ ${CONTAINER_BIN} = "docker" ]; then
${CONTAINER_BIN} network rm ${NETWORK} >/dev/null
else
${CONTAINER_BIN} network rm -f ${NETWORK} >/dev/null
fi
}
handleDbmsOptions() {
# -a, -d, -i depend on each other. Validate input combinations and set defaults.
case ${DBMS} in
mariadb)
[ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli"
if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then
echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2
echo >&2
echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
fi
[ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10.4"
if ! [[ ${DBMS_VERSION} =~ ^(10.4|10.5|10.6|10.7|10.8|10.9|10.10|10.11|11.0|11.1)$ ]]; then
echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2
echo >&2
echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
fi
;;
mysql)
[ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli"
if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then
echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2
echo >&2
echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
fi
[ -z "${DBMS_VERSION}" ] && DBMS_VERSION="8.0"
if ! [[ ${DBMS_VERSION} =~ ^(8.0|8.1|8.2|8.3)$ ]]; then
echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2
echo >&2
echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
fi
;;
postgres)
if [ -n "${DATABASE_DRIVER}" ]; then
echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2
echo >&2
echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
fi
[ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10"
if ! [[ ${DBMS_VERSION} =~ ^(10|11|12|13|14|15|16)$ ]]; then
echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2
echo >&2
echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
fi
;;
sqlite)
if [ -n "${DATABASE_DRIVER}" ]; then
echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2
echo >&2
echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
fi
if [ -n "${DBMS_VERSION}" ]; then
echo "Invalid combination -d ${DBMS} -i ${DATABASE_DRIVER}" >&2
echo >&2
echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
fi
;;
*)
echo "Invalid option -d ${DBMS}" >&2
echo >&2
echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
;;
esac
}
cleanBuildFiles() {
echo -n "Clean builds ... "
rm -rf \
.cache \
Build/JavaScript \
Build/node_modules \
Documentation-GENERATED-temp
echo "done"
}
cleanTestFiles() {
# test related
echo -n "Clean test related files ... "
rm -rf \
bin/ \
Build/phpunit \
public/ \
typo3temp/ \
vendor/ \
var/ \
composer.lock
git checkout composer.json
echo "done"
}
getPhpImageVersion() {
case ${1} in
8.1)
echo -n "2.12"
;;
8.2)
echo -n "1.12"
;;
8.3)
echo -n "1.13"
;;
esac
}
loadHelp() {
# Load help text into $HELP
read -r -d '' HELP <<EOF
TYPO3 core test runner. Execute acceptance, unit, functional and other test suites in
a container based test environment. Handles execution of single test files, sending
xdebug information to a local IDE and more.
Usage: $0 [options] [file]
Options:
-s <...>
Specifies the test suite to run
- buildDocumentation: test build the documentation
- clean: clean up build, cache and testing related files and folders
- composerInstallPackage: install a package with composer
- lintXliff: test XLIFF language files
-b <docker|podman>
Container environment:
- podman (default)
- docker
-p <8.1|8.2|8.3>
Specifies the PHP minor version to be used
- 8.1: use PHP 8.1
- 8.2 (default): use PHP 8.2
- 8.3: use PHP 8.3
-q
package to be installed by composer
-r
parameters used with composer commands
-h
Show this help.
-v
Enable verbose script output. Shows variables and docker commands.
Examples:
# Run install a package with composer
./Build/Scripts/additionalTests.sh -p 8.2 -s composerInstallPackage "typo3/cms-core:13.0"
# Test build the documentation
./Build/Scripts/additionalTests.sh -s buildDocumentation
# Test XLIFF language files
./Build/Scripts/additionalTests.sh -s lintXliff
EOF
}
# Test if docker exists, else exit out with error
if ! type "docker" >/dev/null; then
echo "This script relies on docker. Please install" >&2
exit 1
fi
# Option defaults
TEST_SUITE="unit"
DBMS="sqlite"
DBMS_VERSION=""
PHP_VERSION="8.1"
PHP_XDEBUG_ON=0
PHP_XDEBUG_PORT=9003
ACCEPTANCE_HEADLESS=1
EXTRA_TEST_OPTIONS=""
PHPUNIT_RANDOM=""
CGLCHECK_DRY_RUN=""
DATABASE_DRIVER=""
CHUNKS=0
THISCHUNK=0
CONTAINER_BIN="docker"
SCRIPT_VERBOSE=0
COMPOSER_PACKAGE=""
COMPOSER_PARAMETER=""
# Option parsing updates above default vars
# Reset in case getopts has been used previously in the shell
OPTIND=1
# Array for invalid options
INVALID_OPTIONS=()
# Simple option parsing based on getopts (! not getopt)
while getopts ":s:p:q:r:hv" OPT; do
case ${OPT} in
s)
TEST_SUITE=${OPTARG}
;;
p)
PHP_VERSION=${OPTARG}
if ! [[ ${PHP_VERSION} =~ ^(8.1|8.2|8.3)$ ]]; then
INVALID_OPTIONS+=("${OPTARG}")
fi
;;
q)
COMPOSER_PACKAGE=${OPTARG}
;;
r)
COMPOSER_PARAMETER=${OPTARG}
;;
h)
loadHelp
echo "${HELP}"
exit 0
;;
v)
SCRIPT_VERBOSE=1
;;
\?)
INVALID_OPTIONS+=("${OPTARG}")
;;
:)
INVALID_OPTIONS+=("${OPTARG}")
;;
esac
done
# Exit on invalid options
if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then
echo "Invalid option(s):" >&2
for I in "${INVALID_OPTIONS[@]}"; do
echo "-"${I} >&2
done
echo >&2
echo "Use \"./Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
fi
handleDbmsOptions
COMPOSER_ROOT_VERSION="7.0.1"
HOST_UID=$(id -u)
HOST_PID=$(id -g)
USERSET=""
if [ $(uname) != "Darwin" ]; then
USERSET="--user $HOST_UID"
fi
# Go to the directory this script is located, so everything else is relative
# to this dir, no matter from where this script is called, then go up two dirs.
THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
cd "$THIS_SCRIPT_DIR" || exit 1
cd ../../ || exit 1
CORE_ROOT="${PWD}"
# Create .cache dir: composer and various npm jobs need this.
mkdir -p .cache
mkdir -p typo3temp/var/tests
PHPSTAN_CONFIG_FILE="phpstan.local.neon"
IMAGE_PREFIX="docker.io/"
# Non-CI fetches TYPO3 images (php and nodejs) from ghcr.io
TYPO3_IMAGE_PREFIX="ghcr.io/"
CONTAINER_INTERACTIVE="-it --init"
IS_CORE_CI=0
# ENV var "CI" is set by gitlab-ci. We use it here to distinct 'local' and 'CI' environment.
if [ "${CI}" == "true" ]; then
IS_CORE_CI=1
PHPSTAN_CONFIG_FILE="phpstan.ci.neon"
# In CI, we need to pull images from docker.io for the registry proxy to kick in.
TYPO3_IMAGE_PREFIX="docker.io/"
IMAGE_PREFIX=""
CONTAINER_INTERACTIVE=""
fi
IMAGE_APACHE="${TYPO3_IMAGE_PREFIX}typo3/core-testing-apache24:latest"
IMAGE_PHP="${TYPO3_IMAGE_PREFIX}typo3/core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):latest"
IMAGE_NODEJS="${TYPO3_IMAGE_PREFIX}typo3/core-testing-nodejs18:latest"
IMAGE_NODEJS_CHROME="${TYPO3_IMAGE_PREFIX}typo3/core-testing-nodejs18-chrome:latest"
IMAGE_ALPINE="${IMAGE_PREFIX}alpine:3.8"
IMAGE_SELENIUM="${IMAGE_PREFIX}selenium/standalone-chrome:4.11.0-20230801"
IMAGE_REDIS="${IMAGE_PREFIX}redis:4-alpine"
IMAGE_MEMCACHED="${IMAGE_PREFIX}memcached:1.5-alpine"
IMAGE_MARIADB="${IMAGE_PREFIX}mariadb:${DBMS_VERSION}"
IMAGE_MYSQL="${IMAGE_PREFIX}mysql:${DBMS_VERSION}"
IMAGE_POSTGRES="${IMAGE_PREFIX}postgres:${DBMS_VERSION}-alpine"
IMAGE_DOCUMENTATION="ghcr.io/t3docs/render-documentation:v3.0.dev30"
IMAGE_XLIFF="container.registry.gitlab.typo3.org/qa/example-extension:typo3-ci-xliff-lint"
# Detect arm64 to use seleniarm image.
ARCH=$(uname -m)
if [ ${ARCH} = "arm64" ]; then
IMAGE_SELENIUM="${IMAGE_PREFIX}seleniarm/standalone-chromium:4.1.2-20220227"
echo "Architecture" ${ARCH} "requires" ${IMAGE_SELENIUM} "to run acceptance tests."
fi
# Set $1 to first mass argument, this is the optional test file or test directory to execute
shift $((OPTIND - 1))
TEST_FILE=${1}
SUFFIX=$(echo $RANDOM)
NETWORK="typo3-core-${SUFFIX}"
${CONTAINER_BIN} network create ${NETWORK} >/dev/null
CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network $NETWORK --add-host "host.docker.internal:host-gateway" $USERSET -v ${CORE_ROOT}:${CORE_ROOT}"
if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
XDEBUG_MODE="-e XDEBUG_MODE=off"
XDEBUG_CONFIG=" "
PHP_FPM_OPTIONS="-d xdebug.mode=off"
else
XDEBUG_MODE="-e XDEBUG_MODE=debug -e XDEBUG_TRIGGER=foo"
XDEBUG_CONFIG="client_port=${PHP_XDEBUG_PORT} client_host=host.docker.internal"
PHP_FPM_OPTIONS="-d xdebug.mode=debug -d xdebug.start_with_request=yes -d xdebug.client_host=host.docker.internal -d xdebug.client_port=${PHP_XDEBUG_PORT} -d memory_limit=256M"
fi
# if host uid is root, like for example on ci we need to set additional php-fpm command line options
if [ "${HOST_UID}" = 0 ]; then
PHP_FPM_OPTIONS+=" --allow-to-run-as-root"
fi
# Suite execution
case ${TEST_SUITE} in
buildDocumentation)
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} -v ${CORE_ROOT}:/project ghcr.io/typo3-documentation/render-guides:latest render Documentation
SUITE_EXIT_CODE=$?
;;
clean)
cleanBuildFiles
cleanTestFiles
;;
composerInstallPackage)
COMMAND="[ ${SCRIPT_VERBOSE} -eq 1 ] && set -x; composer require -W -n ${COMPOSER_PARAMETER} ${COMPOSER_PACKAGE};"
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-require-package-${SUFFIX} -w ${CORE_ROOT} -e COMPOSER_CACHE_DIR=${CORE_ROOT}/Build/.cache/composer ${IMAGE_PHP} /bin/sh -c "${COMMAND}"
SUITE_EXIT_CODE=$?
;;
lintXliff)
COMMAND="[ ${SCRIPT_VERBOSE} -eq 1 ] && set -x; xmllint --schema /xliff-core-1.2-strict.xsd --noout *.xlf;"
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name lint-xliff-${SUFFIX} -w ${CORE_ROOT}/Resources/Private/Language ${IMAGE_XLIFF} /bin/sh -c "${COMMAND}"
SUITE_EXIT_CODE=$?
;;
esac
cleanUp
# Print summary
echo "" >&2
echo "###########################################################################" >&2
echo "Result of ${TEST_SUITE}" >&2
if [[ ${IS_CORE_CI} -eq 1 ]]; then
echo "Environment: CI" >&2
else
echo "Environment: local" >&2
fi
echo "PHP: ${PHP_VERSION}" >&2
if [[ "${COMPOSER_PACKAGE}" != "" ]]; then
echo "Package: ${COMPOSER_PACKAGE}" >&2
fi
if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then
echo "SUCCESS" >&2
else
echo "FAILURE" >&2
fi
echo "###########################################################################" >&2
echo "" >&2
# Exit with code of test suite - This script return non-zero if the executed test failed.
exit $SUITE_EXIT_CODE