Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TASK] Streamline Build/Scripts/runTests.sh #548

Merged
merged 1 commit into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 140 additions & 71 deletions Build/Scripts/runTests.sh
Original file line number Diff line number Diff line change
@@ -1,32 +1,13 @@
#!/usr/bin/env bash

#
# Test runner based on docker and docker-compose.
#

# Function to write a .env file in Build/testing-docker/local
# This is read by docker-compose and vars defined here are
# used in Build/testing-docker/docker-compose.yml
setUpDockerComposeDotEnv() {
# Delete possibly existing local .env file if exists
[ -e .env ] && rm .env
# Set up a new .env file for docker-compose
echo "COMPOSE_PROJECT_NAME=local" >> .env
# To prevent access rights of files created by the testing, the docker image later
# runs with the same user that is currently executing the script. docker-compose can't
# use $UID directly itself since it is a shell variable and not an env variable, so
# we have to set it explicitly here.
echo "HOST_UID=`id -u`" >> .env
# Your local home directory for composer and npm caching
echo "HOST_HOME=${HOME}" >> .env
# Your local user
echo "ROOT_DIR"=${ROOT_DIR} >> .env
echo "HOST_USER=${USER}" >> .env
echo "PHP_XDEBUG_ON=${PHP_XDEBUG_ON}" >> .env
echo "DOCKER_PHP_IMAGE=${DOCKER_PHP_IMAGE}" >> .env
echo "SCRIPT_VERBOSE=${SCRIPT_VERBOSE}" >> .env
echo "CGLCHECK_DRY_RUN=${CGLCHECK_DRY_RUN}" >> .env
echo "IMAGE_PREFIX=${IMAGE_PREFIX}" >> .env
cleanUp() {
ATTACHED_CONTAINERS=$(${CONTAINER_BIN} ps --filter network=${NETWORK} --format='{{.Names}} 2>/dev/null')
if [[ -n $ATTACHED_CONTAINERS ]]; then
for ATTACHED_CONTAINER in ${ATTACHED_CONTAINERS}; do
${CONTAINER_BIN} kill ${ATTACHED_CONTAINER} >/dev/null
done
${CONTAINER_BIN} network rm ${NETWORK} >/dev/null
fi
}

# Load help text into $HELP
Expand All @@ -36,7 +17,7 @@ Also used by github actions for test execution.

Usage: $0 [options]

No arguments: Run all unit tests with PHP 7.2
No arguments: Run all unit tests with PHP 7.4

Options:
-s <...>
Expand All @@ -49,12 +30,20 @@ Options:
- phpstanGenerateBaseline: regenerate phpstan baseline, handy after phpstan updates
- unit (default): PHP unit tests

-p <7.4|8.0|8.1|8.2>
-b <docker|podman>
Container environment:
- podman
- docker

If not provided, podman will be used first if both are installed.

-p <7.4|8.0|8.1|8.2|8.3>
Specifies the PHP minor version to be used
- 7.4 (default): use PHP 7.4
- 8.0: use PHP 8.0
- 8.1: use PHP 8.1
- 8.2: use PHP 8.2
- 8.3: use PHP 8.3

-x
Only with -s cgl|unit
Expand All @@ -66,56 +55,68 @@ Options:
Only with -s cgl
Activate dry-run in CGL check that does not actively change files and only prints broken ones.

-v
Enable verbose script output. Shows variables and docker commands.

-h
Show this help.

Examples:
# Run unit tests using default PHP version
# Run unit tests using default PHP version (7.4)
./Build/Scripts/runTests.sh

# Run unit tests using PHP 7.4
./Build/Scripts/runTests.sh -p 7.4
# Run unit tests using PHP 8.1
./Build/Scripts/runTests.sh -p 8.1
EOF

# Test if docker-compose exists, else exit out with error
if ! type "docker-compose" > /dev/null; then
echo "This script relies on docker and docker-compose. Please install" >&2
exit 1
# Test if docker exists, else exit out with error
if ! type "docker" >/dev/null 2>&1 && ! type "podman" >/dev/null 2>&1; then
echo "This script relies on docker or podman. Please install" >&2
exit 1
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.
THIS_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cd "$THIS_SCRIPT_DIR" || exit 1

# Go to directory that contains the local docker-compose.yml file
cd ../testing-docker || exit 1
cd ../../ || exit 1

# Option defaults
ROOT_DIR=`readlink -f ${PWD}/../../`
ROOT_DIR=`readlink -f ${PWD}`
TEST_SUITE="unit"
PHP_VERSION="7.4"
PHP_XDEBUG_ON=0
SCRIPT_VERBOSE=0
CGLCHECK_DRY_RUN=""
IMAGE_PREFIX="ghcr.io/typo3/"
CONTAINER_BIN=""
CONTAINER_INTERACTIVE="-it --init"
HOST_UID=$(id -u)
HOST_PID=$(id -g)
USERSET=""
SUFFIX=$(echo $RANDOM)
NETWORK="testing-framework-${SUFFIX}"
CI_PARAMS=""
CONTAINER_HOST="host.docker.internal"

# Option parsing
# 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:hxnv" OPT; do
while getopts ":b:s:p:hxn" OPT; do
case ${OPT} in
s)
TEST_SUITE=${OPTARG}
;;
b)
if ! [[ ${OPTARG} =~ ^(docker|podman)$ ]]; then
INVALID_OPTIONS+=("${OPTARG}")
fi
CONTAINER_BIN=${OPTARG}
;;
p)
PHP_VERSION=${OPTARG}
if ! [[ ${PHP_VERSION} =~ ^(7.4|8.0|8.1|8.2|8.3)$ ]]; then
INVALID_OPTIONS+=("${OPTARG}")
fi
;;
h)
echo "${HELP}"
Expand All @@ -124,9 +125,6 @@ while getopts ":s:p:hxnv" OPT; do
n)
CGLCHECK_DRY_RUN="-n"
;;
v)
SCRIPT_VERBOSE=1
;;
x)
PHP_XDEBUG_ON=1
;;
Expand All @@ -146,15 +144,62 @@ if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then
echo "-"${I} >&2
done
echo >&2
echo "${HELP}" >&2
echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2
exit 1
fi

# ENV var "CI" is set by gitlab-ci. Use it to force some CI details.
if [ "${CI}" == "true" ]; then
CONTAINER_INTERACTIVE=""
# @todo Enforce pull-never once we have cached image folder similar to Core CI runner image caches.
# CI_PARAMS="--pull=never"
fi

# determine default container binary to use: 1. podman 2. docker
if [[ -z "${CONTAINER_BIN}" ]]; then
if type "podman" >/dev/null 2>&1; then
CONTAINER_BIN="podman"
elif type "docker" >/dev/null 2>&1; then
CONTAINER_BIN="docker"
fi
fi

if [ $(uname) != "Darwin" ] && [ ${CONTAINER_BIN} = "docker" ]; then
# Run docker jobs as current user to prevent permission issues. Not needed with podman.
USERSET="--user $HOST_UID"
fi

if ! type ${CONTAINER_BIN} >/dev/null 2>&1; then
echo "Selected container environment \"${CONTAINER_BIN}\" not found. Please install or use -b option to select one." >&2
exit 1
fi

# Move "7.2" to "php72", the latter is the docker container name
DOCKER_PHP_IMAGE=`echo "php${PHP_VERSION}" | sed -e 's/\.//'`
IMAGE_PHP="ghcr.io/typo3/core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):latest"

if [[ -d "../../Build/testing-docker" ]]; then
rm -rf ../../Build/testing-docker
fi

# Remove handled options and leaving the rest in the line, so it can be passed raw to commands
shift $((OPTIND - 1))

if [ ${SCRIPT_VERBOSE} -eq 1 ]; then
set -x
${CONTAINER_BIN} network create ${NETWORK} >/dev/null

if [ ${CONTAINER_BIN} = "docker" ]; then
# docker needs the add-host for xdebug remote debugging. podman has host.container.internal built in
CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}"
else
# podman
CONTAINER_HOST="host.containers.internal"
CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm --network ${NETWORK} -v ${ROOT_DIR}:${ROOT_DIR} -w ${ROOT_DIR}"
fi

if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
XDEBUG_MODE="-e XDEBUG_MODE=off"
XDEBUG_CONFIG=" "
else
XDEBUG_MODE="-e XDEBUG_MODE=debug -e XDEBUG_TRIGGER=foo"
XDEBUG_CONFIG="client_port=${PHP_XDEBUG_PORT} client_host=${CONTAINER_HOST}"
fi

# Suite execution
Expand All @@ -164,43 +209,36 @@ case ${TEST_SUITE} in
if [[ ! -z ${CGLCHECK_DRY_RUN} ]]; then
CGLCHECK_DRY_RUN="--dry-run --diff"
fi
setUpDockerComposeDotEnv
docker-compose run cgl
COMMAND="php -dxdebug.mode=off .Build/bin/php-cs-fixer fix -v ${CGLCHECK_DRY_RUN} --path-mode intersection --config=Build/php-cs-fixer/config.php"
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name cgl-${SUFFIX} ${IMAGE_PHP} ${COMMAND}
SUITE_EXIT_CODE=$?
docker-compose down
;;
clean)
rm -rf ../../composer.lock ../../.Build/ ../../public
;;
composerUpdate)
setUpDockerComposeDotEnv
docker-compose run composer_update
COMMAND=(composer update --no-progress --no-interaction)
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-update-${SUFFIX} -e COMPOSER_CACHE_DIR=.Build/.cache/composer ${IMAGE_PHP} "${COMMAND[@]}"
SUITE_EXIT_CODE=$?
docker-compose down
;;
lint)
setUpDockerComposeDotEnv
docker-compose run lint
COMMAND="php -v | grep '^PHP'; find . -name '*.php' ! -path './.Build/*' ! -path './public/*' -print0 | xargs -0 -n1 -P4 php -dxdebug.mode=off -l >/dev/null"
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name lint-php-${SUFFIX} ${IMAGE_PHP} /bin/sh -c "${COMMAND}"
SUITE_EXIT_CODE=$?
docker-compose down
;;
phpstan)
setUpDockerComposeDotEnv
docker-compose run phpstan
COMMAND=(php -dxdebug.mode=off .Build/bin/phpstan analyse -c Build/phpstan/phpstan.neon --no-progress --no-interaction --memory-limit 4G "$@")
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name phpstan-${SUFFIX} ${IMAGE_PHP} "${COMMAND[@]}"
SUITE_EXIT_CODE=$?
docker-compose down
;;
phpstanGenerateBaseline)
setUpDockerComposeDotEnv
docker-compose run phpstan_generate_baseline
COMMAND="php -dxdebug.mode=off .Build/bin/phpstan analyse -c Build/phpstan/phpstan.neon --no-progress --no-interaction --memory-limit 4G --generate-baseline=Build/phpstan/phpstan-baseline.neon"
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name phpstan-baseline-${SUFFIX} ${IMAGE_PHP} /bin/sh -c "${COMMAND}"
SUITE_EXIT_CODE=$?
docker-compose down
;;
unit)
setUpDockerComposeDotEnv
docker-compose run unit
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} .Build/bin/phpunit -c Build/phpunit/UnitTests.xml "$@"
SUITE_EXIT_CODE=$?
docker-compose down
;;
*)
echo "Invalid -s option argument ${TEST_SUITE}" >&2
Expand All @@ -209,4 +247,35 @@ case ${TEST_SUITE} in
exit 1
esac

cleanUp

# Print summary
echo "" >&2
echo "###########################################################################" >&2
echo "Result of ${TEST_SUITE}" >&2
echo "Container runtime: ${CONTAINER_BIN}" >&2
echo "PHP: ${PHP_VERSION}" >&2
if [[ ${TEST_SUITE} =~ ^(functional|functionalDeprecated|acceptance|acceptanceInstall)$ ]]; then
case "${DBMS}" in
mariadb|mysql|postgres)
echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver ${DATABASE_DRIVER}" >&2
;;
sqlite)
echo "DBMS: ${DBMS}" >&2
;;
esac
fi
if [[ -n ${EXTRA_TEST_OPTIONS} ]]; then
echo " Note: Using -e is deprecated. Simply add the options at the end of the command."
echo " Instead of: Build/Scripts/runTests.sh -s ${TEST_SUITE} -e '${EXTRA_TEST_OPTIONS}' $@"
echo " use: Build/Scripts/runTests.sh -s ${TEST_SUITE} -- ${EXTRA_TEST_OPTIONS} $@"
fi
if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then
echo "SUCCESS" >&2
else
echo "FAILURE" >&2
fi
echo "###########################################################################" >&2
echo "" >&2

exit $SUITE_EXIT_CODE
47 changes: 47 additions & 0 deletions Build/phpunit/UnitTests.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0"?>
<!--
Boilerplate for a unit test suite setup.

This file is loosely maintained within TYPO3 testing-framework, extensions
are encouraged to not use it directly, but to copy it to an own place,
for instance Build/UnitTests.xml.
Note UnitTestsBootstrap.php should be copied along the way.

Functional tests should extend \TYPO3\TestingFramework\Core\Tests\FunctionalTestCase,
take a look at this class for further documentation on how to run the suite.

TYPO3 CMS functional test suite also needs phpunit bootstrap code, the
file is located next to this .xml as FunctionalTestsBootstrap.php
-->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.1/phpunit.xsd"
backupGlobals="true"
beStrictAboutTestsThatDoNotTestAnything="false"
bootstrap="../../Resources/Core/Build/UnitTestsBootstrap.php"
cacheDirectory=".phpunit.cache"
cacheResult="false"
colors="true"
displayDetailsOnTestsThatTriggerDeprecations="true"
displayDetailsOnTestsThatTriggerErrors="true"
displayDetailsOnTestsThatTriggerNotices="true"
displayDetailsOnTestsThatTriggerWarnings="true"
failOnDeprecation="true"
failOnNotice="true"
failOnRisky="true"
failOnWarning="true"
requireCoverageMetadata="false"
>
<testsuites>
<testsuite name="Unit tests">
<!--
This path either needs an adaption in extensions, or an extension's
test location path needs to be given to phpunit.
-->
<directory>../../Tests/Unit/</directory>
</testsuite>
</testsuites>
<php>
<ini name="display_errors" value="1"/>
<env name="TYPO3_CONTEXT" value="Testing"/>
</php>
</phpunit>
Loading