Skip to content
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
1 change: 1 addition & 0 deletions .bootstrap-registry
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ KeymanHosts.php
KeymanSentry.php
KeymanVersion.php
MarkdownHost.php
tests.inc.sh
60 changes: 58 additions & 2 deletions _common/docker.inc.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# Common docker functions.
# shellcheck shell=bash
#
# Keyman is copyright (C) SIL Global. MIT License.
#
# Common docker functions for (generally PHP-based) containers
#

source _common/tests.inc.sh

function get_docker_image_id() {
local IMAGE_NAME=$1
Expand Down Expand Up @@ -153,4 +160,53 @@ function start_docker_container() {
fi

builder_echo green "Listening on http://$HOST:$PORT"
}
}

#
# Test PHP site in a docker container
#
# Parameters:
# 1: CONTAINER_DESC desc of container to test
# 2: CONTAINER_PORT localhost http port for container
# 3: TEST_PATH path to start link check at, under http://localhost:CONTAINER_PORT
# 4..: SKIP_PATHS paths to skip crawling in link check, optional
#
# Builder options --no-unit-test, --no-lint, --no-link-check are honoured
#
function test_docker_container() {
local CONTAINER_DESC="$1"
local CONTAINER_PORT="$2"
local TEST_PATH="$3"
shift 3
local SKIP_PATHS=("$@")

local LINK_RESULT=0
echo "TIER_TEST" > tier.txt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of tier.txt ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used to specify the tier across server and script contexts consistently (as env vars may not be available in http server context). See:

# Get the site BUILDER_TIER and use that to determine builder script environment
if [[ ! -z ${KEYMANHOSTS_TIER+x} ]]; then
BUILDER_TIER="${KEYMANHOSTS_TIER}"
elif [[ -f "$(dirname "$THIS_SCRIPT")/tier.txt" ]]; then
BUILDER_TIER=$(cat "$(dirname "$THIS_SCRIPT")/tier.txt")
else
BUILDER_TIER=TIER_DEVELOPMENT
fi

if(isset($env['KEYMANHOSTS_TIER']) && in_array($env['KEYMANHOSTS_TIER'],
[KeymanHosts::TIER_DEVELOPMENT, KeymanHosts::TIER_STAGING,
KeymanHosts::TIER_PRODUCTION, KeymanHosts::TIER_TEST])) {
$this->tier = $env['KEYMANHOSTS_TIER'];
} else if(file_exists(__DIR__ . '/../tier.txt')) {
$this->tier = trim(file_get_contents(__DIR__ . '/../tier.txt'));
} else {
$this->tier = KeymanHosts::TIER_DEVELOPMENT;
}


# Similar pattern in ci.yml on sites

do_test_record_start_time

if ! builder_has_option --no-unit-test; then
builder_echo blue "---- PHP unit tests"
do_test_unit_tests "${CONTAINER_DESC}"
fi

if ! builder_has_option --no-lint; then
builder_echo blue "---- Lint PHP files"
do_test_lint "${CONTAINER_DESC}"
fi

if ! builder_has_option --no-link-check; then
builder_echo blue "---- Testing links"

do_test_links "http://localhost:${CONTAINER_PORT}" "$TEST_PATH" "${SKIP_PATHS[@]}" || LINK_RESULT=$?
builder_echo blue "Done checking links; linkinator exit code: ${LINK_RESULT}"
do_test_print_link_report

do_test_print_container_error_logs "${CONTAINER_DESC}"
fi

rm tier.txt
return "$LINK_RESULT"
}
94 changes: 94 additions & 0 deletions _common/tests.inc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# shellcheck shell=bash

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# shellcheck shell=bash
# shellcheck shell=bash
# Keyman is copyright (C) SIL Global. MIT License.

#
# Keyman is copyright (C) SIL Global. MIT License.
#
# Shared test functions for PHP lint, unit test, and general broken link checks
#

# Record the start time for unit tests for later log review
function do_test_record_start_time() {
TEST_START_TIME=$(date -Is -u)
}

# Run unit tests through phpunit
#
# Parameters
# 1: CONTAINER container_desc to run on
#
function do_test_unit_tests() {
local CONTAINER="$1"
docker exec "${CONTAINER}" sh -c "vendor/bin/phpunit --testdox ${builder_extra_params[*]}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
docker exec "${CONTAINER}" sh -c "vendor/bin/phpunit --testdox ${builder_extra_params[*]}"
docker exec "${CONTAINER}" sh -c "vendor/bin/phpunit --testdox ${builder_extra_params[@]}"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can use ${x[@]} here because that will quote at the wrong level -- it needs to remain inside the quotes for the parameter following sh -c.

}

# Lint .php files for obvious errors
#
# Parameters
# 1: CONTAINER container_desc to run on
#
function do_test_lint() {
local CONTAINER="$1"
docker exec "${CONTAINER}" sh -c "find . -name '*.php' | grep -v '/vendor/' | xargs -n 1 -d '\\n' php -l"
}

## Check links on live local server using linkinator
#
# Parameters
# 1: baseURL the top level URL for the site
# 2: testPath path under baseURL to start testing, e.g. /
# 3[,4..]: skipPaths list of paths (under baseURL) to skip crawling, optional
#
function do_test_links() {
local baseURL="$1"
local testPath="$2"
shift 2
local skipPaths=("$@")
local skip skipParams=()

for skip in "${skipPaths[@]}"; do
skipParams+=(--skip "^${baseURL}${skip}")
done

npx https://github.com/keymanapp/linkinator \
"${baseURL}${testPath}" \
--clean-urls \
--concurrency 50 \
--format json \
--output-filename linkinator-results.json \
--skip "^(?!${baseURL})" \
"${skipParams[@]}" \
--recurse \
--redirects verify \
--retry-errors \
--root-path "${baseURL}"
}

# Print summary of results from linkinator
function do_test_print_link_report() {
echo ----------------------------------------------------------------------
echo Link check summary
echo ----------------------------------------------------------------------
# Emit full JSON detail for broken links (may not be necessary)
jq '.links[] | select(.state != "OK")' < linkinator-results.json
echo
echo
# Emit a summary report
jq -r '.links[] | select(.state != "OK") | "\(.state)[\(.status)]: \(.parent) --> \(.url)"' < linkinator-results.json
}

# Scan logs recorded on container since start of tests to find any reported PHP
# errors (note, depends on '[php#:xxxx]' marker string, where # = 7 for PHP7, omitted for PHP8)
#
# Parameters
# 1: CONTAINER container_desc to run on
#
function do_test_print_container_error_logs() {
local CONTAINER="$1"
if docker container logs "${CONTAINER}" --since "${TEST_START_TIME}" 2>&1 | grep -qP '\[php7?:(error|warn|notice)\]'; then
echo 'PHP reported errors or warnings:'
docker container logs "${CONTAINER}" --since "${TEST_START_TIME}" 2>&1 | grep -P '\[php7?:(error|warn|notice)\]'
return 1
else
echo 'No PHP errors found'
return 0
fi
}
Loading