Skip to content

Commit 52a22a7

Browse files
authored
[TASK] add functional and unit test setup (#73)
1 parent e8e5c43 commit 52a22a7

85 files changed

Lines changed: 5028 additions & 21 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/tasks.yml

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,46 @@ jobs:
4040
- run: composer update --with typo3/cms-core:^${{ matrix.typo3 }}
4141
- run: ./vendor/bin/grumphp run --ansi
4242

43+
unit-tests:
44+
name: "unit php: ${{ matrix.php }} TYPO3: ${{ matrix.typo3 }}"
45+
runs-on: ubuntu-latest
46+
strategy:
47+
fail-fast: false
48+
matrix:
49+
php: [ '8.2', '8.3', '8.4', '8.5' ]
50+
typo3: [ '13', '14' ]
51+
steps:
52+
- name: Setup PHP with PECL extension
53+
uses: shivammathur/setup-php@v2
54+
with:
55+
php-version: ${{ matrix.php }}
56+
- uses: actions/checkout@v6
57+
- run: composer update --with typo3/cms-core:^${{ matrix.typo3 }}
58+
- run: CI=true ./Build/Scripts/runTests.sh -p ${{ matrix.php }} -s unit
59+
60+
functional-tests:
61+
name: "functional php: ${{ matrix.php }} TYPO3: ${{ matrix.typo3 }} DB: ${{ matrix.dbms }}"
62+
runs-on: ubuntu-latest
63+
strategy:
64+
fail-fast: false
65+
matrix:
66+
php: [ '8.2', '8.3', '8.4', '8.5' ]
67+
typo3: [ '13', '14' ]
68+
dbms: [ 'sqlite', 'mariadb', 'mysql', 'postgres' ]
69+
steps:
70+
- name: Setup PHP with PECL extension
71+
uses: shivammathur/setup-php@v2
72+
with:
73+
php-version: ${{ matrix.php }}
74+
- uses: actions/checkout@v6
75+
- run: composer update --with typo3/cms-core:^${{ matrix.typo3 }}
76+
- run: CI=true ./Build/Scripts/runTests.sh -p ${{ matrix.php }} -d ${{ matrix.dbms }} -s functional
77+
4378
ter-release:
4479
name: Publish new version to TER
4580
runs-on: ubuntu-latest
4681
if: startsWith(github.ref, 'refs/tags/')
47-
needs: [ lint-php ]
82+
needs: [ lint-php, unit-tests, functional-tests ]
4883
env:
4984
TYPO3_API_TOKEN: ${{ secrets.TYPO3_API_TOKEN }}
5085

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/.idea/
2+
/.phpunit.cache/
23
/var/
34
/vendor/
45
/composer.lock

Build/Scripts/runTests.sh

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
#!/usr/bin/env bash
2+
3+
set -eu
4+
5+
if [ "${CI:-}" != "true" ]; then
6+
trap 'echo "runTests.sh SIGINT signal emitted"; cleanUp; exit 2' SIGINT
7+
fi
8+
9+
cleanUp() {
10+
# remove the infinite recursion symlink
11+
rm -rf ./public/typo3temp/var/tests/functional-*/typo3conf/ext/visual_editor
12+
if [ -n "${NETWORK_CREATED:-}" ]; then
13+
ATTACHED_CONTAINERS=$(${CONTAINER_BIN} ps --filter network=${NETWORK} --format='{{.Names}}' 2>/dev/null || true)
14+
for ATTACHED_CONTAINER in ${ATTACHED_CONTAINERS}; do
15+
${CONTAINER_BIN} kill ${ATTACHED_CONTAINER} >/dev/null 2>&1 || true
16+
done
17+
${CONTAINER_BIN} network rm ${NETWORK} >/dev/null 2>&1 || true
18+
fi
19+
}
20+
21+
waitFor() {
22+
local HOST=${1}
23+
local PORT=${2}
24+
[[ -n "${3:-}" ]] && echo -n "Startup wait of $3 ... " && sleep "$3" && echo "done"
25+
local TESTCOMMAND="
26+
COUNT=0;
27+
while ! nc -z ${HOST} ${PORT}; do
28+
if [ \"\${COUNT}\" -gt 30 ]; then
29+
echo \"Can not connect to ${HOST} port ${PORT}. Aborting.\";
30+
exit 1;
31+
fi;
32+
sleep 1;
33+
COUNT=\$((COUNT + 1));
34+
done;
35+
"
36+
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name wait-for-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_ALPINE} /bin/sh -c "${TESTCOMMAND}"
37+
if [[ $? -gt 0 ]]; then
38+
kill -SIGINT -$$
39+
fi
40+
}
41+
42+
handleDbmsOptions() {
43+
case ${DBMS} in
44+
mariadb)
45+
if [ -z "${DATABASE_DRIVER}" ]; then
46+
DATABASE_DRIVER="mysqli"
47+
fi
48+
if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then
49+
echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2
50+
exit 1
51+
fi
52+
if [ -z "${DBMS_VERSION}" ]; then
53+
DBMS_VERSION="10.11"
54+
fi
55+
;;
56+
mysql)
57+
if [ -z "${DATABASE_DRIVER}" ]; then
58+
DATABASE_DRIVER="mysqli"
59+
fi
60+
if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then
61+
echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2
62+
exit 1
63+
fi
64+
if [ -z "${DBMS_VERSION}" ]; then
65+
DBMS_VERSION="8.4"
66+
fi
67+
;;
68+
postgres)
69+
if [ -n "${DATABASE_DRIVER}" ]; then
70+
echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2
71+
exit 1
72+
fi
73+
if [ -z "${DBMS_VERSION}" ]; then
74+
DBMS_VERSION="16"
75+
fi
76+
;;
77+
sqlite)
78+
if [ -n "${DATABASE_DRIVER}" ]; then
79+
echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2
80+
exit 1
81+
fi
82+
if [ -n "${DBMS_VERSION}" ]; then
83+
echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2
84+
exit 1
85+
fi
86+
;;
87+
*)
88+
echo "Invalid option -d ${DBMS}" >&2
89+
exit 1
90+
;;
91+
esac
92+
}
93+
94+
loadHelp() {
95+
read -r -d '' HELP <<EOF || true
96+
Usage: $0 [options] [phpunit args]
97+
98+
Options:
99+
-s <unit|functional> Test suite to run
100+
-b <docker|podman> Container runtime
101+
-p <8.2|8.3|8.4|8.5> PHP version (default: 8.2)
102+
-d <sqlite|mariadb|mysql|postgres>
103+
Functional DBMS (default: sqlite)
104+
-a <mysqli|pdo_mysql> DB driver for mysql/mariadb
105+
-i <version> Specific DBMS version
106+
-x Enable xdebug
107+
-y <port> Xdebug port (default: 9003)
108+
-h Show help
109+
110+
Examples:
111+
./Build/Scripts/runTests.sh -s unit
112+
./Build/Scripts/runTests.sh -s functional
113+
./Build/Scripts/runTests.sh -s functional -d sqlite -- --filter LocalizationServiceTest
114+
EOF
115+
}
116+
117+
if ! type "docker" >/dev/null 2>&1 && ! type "podman" >/dev/null 2>&1; then
118+
echo "This script relies on docker or podman. Please install one of them." >&2
119+
exit 1
120+
fi
121+
122+
THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
123+
cd "$THIS_SCRIPT_DIR" || exit 1
124+
cd ../../ || exit 1
125+
CORE_ROOT="${PWD}"
126+
127+
TEST_SUITE="help"
128+
DBMS="sqlite"
129+
DBMS_VERSION=""
130+
PHP_VERSION="8.2"
131+
PHP_XDEBUG_ON=0
132+
PHP_XDEBUG_PORT=9003
133+
DATABASE_DRIVER=""
134+
CONTAINER_BIN=""
135+
CONTAINER_INTERACTIVE="-it --init"
136+
CONTAINER_HOST="host.docker.internal"
137+
HOST_UID=$(id -u)
138+
HOST_PID=$(id -g)
139+
USERSET=""
140+
SUFFIX=$(echo $RANDOM)
141+
NETWORK="visual-editor-${SUFFIX}"
142+
NETWORK_CREATED=""
143+
NETWORK_PARAM=""
144+
145+
if [ "${CI:-}" = "true" ]; then
146+
CONTAINER_INTERACTIVE=""
147+
fi
148+
149+
OPTIND=1
150+
while getopts "a:b:s:d:i:p:xy:h" OPT; do
151+
case ${OPT} in
152+
s) TEST_SUITE=${OPTARG} ;;
153+
b) CONTAINER_BIN=${OPTARG} ;;
154+
a) DATABASE_DRIVER=${OPTARG} ;;
155+
d) DBMS=${OPTARG} ;;
156+
i) DBMS_VERSION=${OPTARG} ;;
157+
p) PHP_VERSION=${OPTARG} ;;
158+
x) PHP_XDEBUG_ON=1 ;;
159+
y) PHP_XDEBUG_PORT=${OPTARG} ;;
160+
h) TEST_SUITE="help" ;;
161+
*) TEST_SUITE="help" ;;
162+
esac
163+
done
164+
165+
handleDbmsOptions
166+
167+
if [[ -z "${CONTAINER_BIN}" ]]; then
168+
if type "docker" >/dev/null 2>&1; then
169+
CONTAINER_BIN="docker"
170+
else
171+
CONTAINER_BIN="podman"
172+
fi
173+
fi
174+
175+
if ! type "${CONTAINER_BIN}" >/dev/null 2>&1; then
176+
echo "Selected container environment \"${CONTAINER_BIN}\" not found." >&2
177+
exit 1
178+
fi
179+
180+
if [ "$(uname)" != "Darwin" ] && [ "${CONTAINER_BIN}" = "docker" ]; then
181+
USERSET="--user ${HOST_UID}"
182+
fi
183+
184+
IMAGE_PHP="ghcr.io/typo3/core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):latest"
185+
IMAGE_ALPINE="docker.io/alpine:3.8"
186+
IMAGE_MARIADB="docker.io/mariadb:${DBMS_VERSION}"
187+
IMAGE_MYSQL="docker.io/mysql:${DBMS_VERSION}"
188+
IMAGE_POSTGRES="docker.io/postgres:${DBMS_VERSION}-alpine"
189+
190+
shift $((OPTIND - 1))
191+
192+
mkdir -p .cache public/typo3temp/var/tests
193+
194+
if [ "${TEST_SUITE}" = "functional" ] && [ "${DBMS}" != "sqlite" ]; then
195+
if [ "${CONTAINER_BIN}" = "docker" ]; then
196+
NETWORK_PARAM="--network bridge"
197+
else
198+
${CONTAINER_BIN} network create ${NETWORK} >/dev/null
199+
NETWORK_CREATED=1
200+
NETWORK_PARAM="--network ${NETWORK}"
201+
fi
202+
fi
203+
204+
if [ "${CONTAINER_BIN}" = "docker" ]; then
205+
CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --add-host ${CONTAINER_HOST}:host-gateway ${USERSET} -e TYPO3_PATH_ROOT=${CORE_ROOT}/public -e TYPO3_PATH_WEB=${CORE_ROOT}/public -v ${CORE_ROOT}:${CORE_ROOT} -w ${CORE_ROOT}"
206+
else
207+
CONTAINER_HOST="host.containers.internal"
208+
CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm -e TYPO3_PATH_ROOT=${CORE_ROOT}/public -e TYPO3_PATH_WEB=${CORE_ROOT}/public -v ${CORE_ROOT}:${CORE_ROOT} -w ${CORE_ROOT}"
209+
fi
210+
211+
if [ -n "${NETWORK_PARAM}" ]; then
212+
CONTAINER_COMMON_PARAMS="${CONTAINER_COMMON_PARAMS} ${NETWORK_PARAM}"
213+
fi
214+
215+
if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
216+
XDEBUG_MODE="-e XDEBUG_MODE=off"
217+
XDEBUG_CONFIG=" "
218+
else
219+
XDEBUG_MODE="-e XDEBUG_MODE=debug -e XDEBUG_TRIGGER=foo"
220+
XDEBUG_CONFIG="client_port=${PHP_XDEBUG_PORT} client_host=${CONTAINER_HOST}"
221+
fi
222+
223+
SUITE_EXIT_CODE=1
224+
case ${TEST_SUITE} in
225+
unit)
226+
${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} vendor/bin/phpunit -c Build/phpunit/UnitTests.xml "$@"
227+
SUITE_EXIT_CODE=$?
228+
;;
229+
functional)
230+
COMMAND=(vendor/bin/phpunit -c Build/phpunit/FunctionalTests.xml --exclude-group not-${DBMS} "$@")
231+
case ${DBMS} in
232+
mariadb)
233+
${CONTAINER_BIN} run --rm --name mariadb-func-${SUFFIX} ${NETWORK_PARAM} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null
234+
DATABASE_HOST=mariadb-func-${SUFFIX}
235+
if [ "${CONTAINER_BIN}" = "docker" ]; then
236+
DATABASE_HOST=$(${CONTAINER_BIN} inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mariadb-func-${SUFFIX})
237+
fi
238+
waitFor ${DATABASE_HOST} 3306
239+
CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=${DATABASE_HOST} -e typo3DatabasePassword=funcp"
240+
${CONTAINER_BIN} run --rm ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}"
241+
SUITE_EXIT_CODE=$?
242+
;;
243+
mysql)
244+
${CONTAINER_BIN} run --rm --name mysql-func-${SUFFIX} ${NETWORK_PARAM} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null
245+
DATABASE_HOST=mysql-func-${SUFFIX}
246+
if [ "${CONTAINER_BIN}" = "docker" ]; then
247+
DATABASE_HOST=$(${CONTAINER_BIN} inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mysql-func-${SUFFIX})
248+
fi
249+
waitFor ${DATABASE_HOST} 3306 2
250+
CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=${DATABASE_HOST} -e typo3DatabasePassword=funcp"
251+
${CONTAINER_BIN} run --rm ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}"
252+
SUITE_EXIT_CODE=$?
253+
;;
254+
postgres)
255+
${CONTAINER_BIN} run --rm --name postgres-func-${SUFFIX} ${NETWORK_PARAM} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null
256+
DATABASE_HOST=postgres-func-${SUFFIX}
257+
if [ "${CONTAINER_BIN}" = "docker" ]; then
258+
DATABASE_HOST=$(${CONTAINER_BIN} inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres-func-${SUFFIX})
259+
fi
260+
waitFor ${DATABASE_HOST} 5432
261+
CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=func_test -e typo3DatabaseUsername=funcu -e typo3DatabaseHost=${DATABASE_HOST} -e typo3DatabasePassword=funcp"
262+
${CONTAINER_BIN} run --rm ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}"
263+
SUITE_EXIT_CODE=$?
264+
;;
265+
sqlite)
266+
rm -rf "${CORE_ROOT}/public/typo3temp/var/tests/functional-sqlite-dbs"
267+
mkdir -p "${CORE_ROOT}/public/typo3temp/var/tests/functional-sqlite-dbs/"
268+
CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite"
269+
${CONTAINER_BIN} run --rm ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}"
270+
SUITE_EXIT_CODE=$?
271+
;;
272+
esac
273+
;;
274+
help)
275+
loadHelp
276+
echo "${HELP}"
277+
SUITE_EXIT_CODE=0
278+
;;
279+
*)
280+
loadHelp
281+
echo "${HELP}" >&2
282+
SUITE_EXIT_CODE=1
283+
;;
284+
esac
285+
286+
cleanUp
287+
exit ${SUITE_EXIT_CODE}

Build/phpunit/FunctionalTests.xml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<phpunit
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.2/phpunit.xsd"
4+
backupGlobals="true"
5+
bootstrap="FunctionalTestsBootstrap.php"
6+
cacheDirectory="../../var/.phpunit.cache"
7+
cacheResult="false"
8+
colors="true"
9+
displayDetailsOnTestsThatTriggerDeprecations="true"
10+
displayDetailsOnTestsThatTriggerErrors="true"
11+
displayDetailsOnTestsThatTriggerNotices="true"
12+
displayDetailsOnTestsThatTriggerWarnings="true"
13+
failOnDeprecation="false"
14+
failOnNotice="true"
15+
failOnRisky="true"
16+
failOnWarning="true"
17+
requireCoverageMetadata="false"
18+
>
19+
<testsuites>
20+
<testsuite name="Functional tests">
21+
<directory>../../Tests/Functional/</directory>
22+
</testsuite>
23+
</testsuites>
24+
<php>
25+
<ini name="display_errors" value="1"/>
26+
<env name="TYPO3_CONTEXT" value="Testing"/>
27+
</php>
28+
</phpunit>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use TYPO3\TestingFramework\Core\Testbase;
6+
7+
(static function (): void {
8+
$testbase = new Testbase();
9+
$testbase->defineOriginalRootPath();
10+
11+
$webRoot = $testbase->getWebRoot();
12+
$testbase->createDirectory($webRoot . 'typo3temp/var/tests');
13+
$testbase->createDirectory($webRoot . 'typo3temp/var/transient');
14+
})();

0 commit comments

Comments
 (0)